Compare commits
16 commits
master
...
fyrchik/si
Author | SHA1 | Date | |
---|---|---|---|
47d81e0a06 | |||
4f14360212 | |||
339fccde4c | |||
632da4d916 | |||
8a186a2d9b | |||
167f7826a7 | |||
45d74df9ff | |||
a08b11cdad | |||
8269c67701 | |||
f762855a5d | |||
82f84662e5 | |||
7318b70ba7 | |||
8b50b042c4 | |||
617988460b | |||
826294209b | |||
eb48a5bb33 |
1137 changed files with 37815 additions and 38127 deletions
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.21 as builder
|
FROM golang:1.18 as builder
|
||||||
ARG BUILD=now
|
ARG BUILD=now
|
||||||
ARG VERSION=dev
|
ARG VERSION=dev
|
||||||
ARG REPO=repository
|
ARG REPO=repository
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
FROM golang:1.21
|
|
||||||
|
|
||||||
WORKDIR /tmp
|
|
||||||
|
|
||||||
# Install apt packages
|
|
||||||
RUN apt-get update && apt-get install --no-install-recommends -y \
|
|
||||||
pip \
|
|
||||||
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Dash → Bash
|
|
||||||
RUN echo "dash dash/sh boolean false" | debconf-set-selections
|
|
||||||
RUN DEBIAN_FRONTEND=noninteractive dpkg-reconfigure dash
|
|
||||||
|
|
||||||
RUN useradd -u 1234 -d /home/ci -m ci
|
|
||||||
USER ci
|
|
||||||
|
|
||||||
ENV PATH="$PATH:/home/ci/.local/bin"
|
|
||||||
|
|
||||||
COPY .pre-commit-config.yaml .
|
|
||||||
|
|
||||||
RUN pip install "pre-commit==3.1.1" \
|
|
||||||
&& git init . \
|
|
||||||
&& pre-commit install-hooks \
|
|
||||||
&& rm -rf /tmp/*
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.21 as builder
|
FROM golang:1.18 as builder
|
||||||
ARG BUILD=now
|
ARG BUILD=now
|
||||||
ARG VERSION=dev
|
ARG VERSION=dev
|
||||||
ARG REPO=repository
|
ARG REPO=repository
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.21 as builder
|
FROM golang:1.18 as builder
|
||||||
ARG BUILD=now
|
ARG BUILD=now
|
||||||
ARG VERSION=dev
|
ARG VERSION=dev
|
||||||
ARG REPO=repository
|
ARG REPO=repository
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.21 as builder
|
FROM golang:1.18 as builder
|
||||||
ARG BUILD=now
|
ARG BUILD=now
|
||||||
ARG VERSION=dev
|
ARG VERSION=dev
|
||||||
ARG REPO=repository
|
ARG REPO=repository
|
||||||
|
|
19
.docker/Dockerfile.storage-testnet
Normal file
19
.docker/Dockerfile.storage-testnet
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
FROM golang:1.18 as builder
|
||||||
|
ARG BUILD=now
|
||||||
|
ARG VERSION=dev
|
||||||
|
ARG REPO=repository
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . /src
|
||||||
|
|
||||||
|
RUN make bin/frostfs-node
|
||||||
|
|
||||||
|
# Executable image
|
||||||
|
FROM alpine AS frostfs-node
|
||||||
|
RUN apk add --no-cache bash
|
||||||
|
|
||||||
|
WORKDIR /
|
||||||
|
|
||||||
|
COPY --from=builder /src/bin/frostfs-node /bin/frostfs-node
|
||||||
|
COPY --from=builder /src/config/testnet/config.yml /config.yml
|
||||||
|
|
||||||
|
CMD ["frostfs-node", "--config", "/config.yml"]
|
|
@ -5,5 +5,4 @@ docker-compose.yml
|
||||||
Dockerfile
|
Dockerfile
|
||||||
temp
|
temp
|
||||||
.dockerignore
|
.dockerignore
|
||||||
docker
|
docker
|
||||||
.cache
|
|
|
@ -1,41 +0,0 @@
|
||||||
name: Build
|
|
||||||
|
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build Components
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
go_versions: [ '1.20', '1.21' ]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
# Allows to fetch all history for all branches and tags.
|
|
||||||
# Need this for proper versioning.
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '${{ matrix.go_versions }}'
|
|
||||||
|
|
||||||
- name: Build CLI
|
|
||||||
run: make bin/frostfs-cli
|
|
||||||
- run: bin/frostfs-cli --version
|
|
||||||
|
|
||||||
- name: Build NODE
|
|
||||||
run: make bin/frostfs-node
|
|
||||||
|
|
||||||
- name: Build IR
|
|
||||||
run: make bin/frostfs-ir
|
|
||||||
|
|
||||||
- name: Build ADM
|
|
||||||
run: make bin/frostfs-adm
|
|
||||||
- run: bin/frostfs-adm --version
|
|
||||||
|
|
||||||
- name: Build LENS
|
|
||||||
run: make bin/frostfs-lens
|
|
||||||
- run: bin/frostfs-lens --version
|
|
|
@ -1,21 +0,0 @@
|
||||||
name: DCO action
|
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
dco:
|
|
||||||
name: DCO
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.21'
|
|
||||||
|
|
||||||
- name: Run commit format checker
|
|
||||||
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v2
|
|
||||||
with:
|
|
||||||
from: 'origin/${{ github.event.pull_request.base.ref }}'
|
|
|
@ -1,73 +0,0 @@
|
||||||
name: Tests and linters
|
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lint:
|
|
||||||
name: Lint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.21'
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Install linters
|
|
||||||
run: make lint-install
|
|
||||||
|
|
||||||
- name: Run linters
|
|
||||||
run: make lint
|
|
||||||
|
|
||||||
tests:
|
|
||||||
name: Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
go_versions: [ '1.20', '1.21' ]
|
|
||||||
fail-fast: false
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '${{ matrix.go_versions }}'
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: make test
|
|
||||||
|
|
||||||
tests-race:
|
|
||||||
name: Tests with -race
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.21'
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: go test ./... -count=1 -race
|
|
||||||
|
|
||||||
staticcheck:
|
|
||||||
name: Staticcheck
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.21'
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Install staticcheck
|
|
||||||
run: make staticcheck-install
|
|
||||||
|
|
||||||
- name: Run staticcheck
|
|
||||||
run: make staticcheck-run
|
|
|
@ -1,22 +0,0 @@
|
||||||
name: Vulncheck
|
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
vulncheck:
|
|
||||||
name: Vulncheck
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.21'
|
|
||||||
|
|
||||||
- name: Install govulncheck
|
|
||||||
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
|
||||||
|
|
||||||
- name: Run govulncheck
|
|
||||||
run: govulncheck ./...
|
|
1
.gitattributes
vendored
1
.gitattributes
vendored
|
@ -1,3 +1,2 @@
|
||||||
/**/*.pb.go -diff -merge
|
/**/*.pb.go -diff -merge
|
||||||
/**/*.pb.go linguist-generated=true
|
/**/*.pb.go linguist-generated=true
|
||||||
/go.sum -diff
|
|
||||||
|
|
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
* @TrueCloudLab/storage-core @TrueCloudLab/committers
|
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -2,7 +2,7 @@
|
||||||
name: Bug report
|
name: Bug report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: ''
|
||||||
labels: community, triage, bug
|
labels: community, triage
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -18,11 +18,8 @@ assignees: ''
|
||||||
If suggesting a change/improvement, explain the difference from current behavior -->
|
If suggesting a change/improvement, explain the difference from current behavior -->
|
||||||
|
|
||||||
## Possible Solution
|
## Possible Solution
|
||||||
<!-- Not obligatory
|
<!-- Not obligatory, but suggest a fix/reason for the bug,
|
||||||
If no reason/fix/additions for the bug can be suggested,
|
or ideas how to implement the addition or change -->
|
||||||
uncomment the following phrase:
|
|
||||||
|
|
||||||
No fix can be suggested by a QA engineer. Further solutions shall be up to developers. -->
|
|
||||||
|
|
||||||
## Steps to Reproduce (for bugs)
|
## Steps to Reproduce (for bugs)
|
||||||
<!-- Provide a link to a live example, or an unambiguous set of steps
|
<!-- Provide a link to a live example, or an unambiguous set of steps
|
||||||
|
@ -44,3 +41,10 @@ assignees: ''
|
||||||
* Version used:
|
* Version used:
|
||||||
* Server setup and configuration:
|
* Server setup and configuration:
|
||||||
* Operating System and version (`uname -a`):
|
* Operating System and version (`uname -a`):
|
||||||
|
|
||||||
|
## Don't forget to add labels!
|
||||||
|
- component label (`frostfs-adm`, `frostfs-storage`, ...)
|
||||||
|
- `goodfirstissue`, `helpwanted` if needed
|
||||||
|
- does this issue belong to an epic?
|
||||||
|
- priority (`P0`-`P4`) if already triaged
|
||||||
|
- quarter label (`202XQY`) if possible
|
||||||
|
|
29
.github/workflows/changelog.yml
vendored
Normal file
29
.github/workflows/changelog.yml
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
name: CHANGELOG check
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- support/**
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Check for updates
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Get changed CHANGELOG
|
||||||
|
id: changelog-diff
|
||||||
|
uses: tj-actions/changed-files@v29
|
||||||
|
with:
|
||||||
|
files: CHANGELOG.md
|
||||||
|
|
||||||
|
- name: Fail if changelog not updated
|
||||||
|
if: steps.changelog-diff.outputs.any_changed == 'false'
|
||||||
|
uses: actions/github-script@v3
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
core.setFailed('CHANGELOG.md has not been updated')
|
37
.github/workflows/config-update.yml
vendored
Normal file
37
.github/workflows/config-update.yml
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
name: Configuration check
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- support/**
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: config-check
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Get changed config-related files
|
||||||
|
id: config-diff
|
||||||
|
uses: tj-actions/changed-files@v29
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
config/**
|
||||||
|
cmd/neofs-node/config/**
|
||||||
|
|
||||||
|
- name: Get changed doc files
|
||||||
|
id: docs-diff
|
||||||
|
uses: tj-actions/changed-files@v29
|
||||||
|
with:
|
||||||
|
files: docs/**
|
||||||
|
|
||||||
|
- name: Fail if config files are changed but the documentation is not updated
|
||||||
|
if: steps.config-diff.outputs.any_changed == 'true' && steps.docs-diff.outputs.any_changed == 'false'
|
||||||
|
uses: actions/github-script@v3
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
core.setFailed('Documentation has not been updated')
|
22
.github/workflows/dco.yml
vendored
Normal file
22
.github/workflows/dco.yml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
name: DCO check
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- support/**
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
commits_check_job:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Commits Check
|
||||||
|
steps:
|
||||||
|
- name: Get PR Commits
|
||||||
|
id: 'get-pr-commits'
|
||||||
|
uses: tim-actions/get-pr-commits@master
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: DCO Check
|
||||||
|
uses: tim-actions/dco@master
|
||||||
|
with:
|
||||||
|
commits: ${{ steps.get-pr-commits.outputs.commits }}
|
60
.github/workflows/go.yml
vendored
Normal file
60
.github/workflows/go.yml
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
name: frostfs-node tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- support/**
|
||||||
|
paths-ignore:
|
||||||
|
- '*.md'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- support/**
|
||||||
|
paths-ignore:
|
||||||
|
- '*.md'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go: [ '1.18.x', '1.19.x' ]
|
||||||
|
steps:
|
||||||
|
- name: Setup go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go }}
|
||||||
|
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Cache go mod
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-${{ matrix.go }}-
|
||||||
|
|
||||||
|
- name: Run go test
|
||||||
|
run: go test -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
|
|
||||||
|
- name: Codecov
|
||||||
|
env:
|
||||||
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
run: bash <(curl -s https://codecov.io/bash)
|
||||||
|
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v3
|
||||||
|
with:
|
||||||
|
version: v1.50.0
|
||||||
|
args: --timeout=5m
|
||||||
|
only-new-issues: true
|
11
.gitlint
11
.gitlint
|
@ -1,11 +0,0 @@
|
||||||
[general]
|
|
||||||
fail-without-commits=True
|
|
||||||
regex-style-search=True
|
|
||||||
contrib=CC1
|
|
||||||
|
|
||||||
[title-match-regex]
|
|
||||||
regex=^\[\#[0-9Xx]+\]\s
|
|
||||||
|
|
||||||
[ignore-by-title]
|
|
||||||
regex=^Release(.*)
|
|
||||||
ignore=title-match-regex
|
|
|
@ -4,7 +4,7 @@
|
||||||
# options for analysis running
|
# options for analysis running
|
||||||
run:
|
run:
|
||||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||||
timeout: 20m
|
timeout: 5m
|
||||||
|
|
||||||
# include test files or not, default is true
|
# include test files or not, default is true
|
||||||
tests: false
|
tests: false
|
||||||
|
@ -24,28 +24,6 @@ linters-settings:
|
||||||
govet:
|
govet:
|
||||||
# report about shadowed variables
|
# report about shadowed variables
|
||||||
check-shadowing: false
|
check-shadowing: false
|
||||||
staticcheck:
|
|
||||||
checks: ["all", "-SA1019"] # TODO Enable SA1019 after deprecated warning are fixed.
|
|
||||||
funlen:
|
|
||||||
lines: 80 # default 60
|
|
||||||
statements: 60 # default 40
|
|
||||||
gocognit:
|
|
||||||
min-complexity: 40 # default 30
|
|
||||||
importas:
|
|
||||||
no-unaliased: true
|
|
||||||
no-extra-aliases: false
|
|
||||||
alias:
|
|
||||||
pkg: git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object
|
|
||||||
alias: objectSDK
|
|
||||||
custom:
|
|
||||||
truecloudlab-linters:
|
|
||||||
path: bin/external_linters.so
|
|
||||||
original-url: git.frostfs.info/TrueCloudLab/linters.git
|
|
||||||
settings:
|
|
||||||
noliteral:
|
|
||||||
target-methods : ["reportFlushError", "reportError"]
|
|
||||||
disable-packages: ["codes", "err", "res","exec"]
|
|
||||||
constants-package: "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
enable:
|
enable:
|
||||||
|
@ -73,11 +51,6 @@ linters:
|
||||||
- predeclared
|
- predeclared
|
||||||
- reassign
|
- reassign
|
||||||
- whitespace
|
- whitespace
|
||||||
- containedctx
|
|
||||||
- funlen
|
|
||||||
- gocognit
|
|
||||||
- contextcheck
|
|
||||||
- importas
|
|
||||||
- truecloudlab-linters
|
|
||||||
disable-all: true
|
disable-all: true
|
||||||
fast: false
|
fast: false
|
||||||
|
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
ci:
|
|
||||||
autofix_prs: false
|
|
||||||
|
|
||||||
repos:
|
|
||||||
- repo: https://github.com/jorisroovers/gitlint
|
|
||||||
rev: v0.19.1
|
|
||||||
hooks:
|
|
||||||
- id: gitlint
|
|
||||||
stages: [commit-msg]
|
|
||||||
- id: gitlint-ci
|
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
||||||
rev: v4.4.0
|
|
||||||
hooks:
|
|
||||||
- id: check-added-large-files
|
|
||||||
- id: check-case-conflict
|
|
||||||
- id: check-executables-have-shebangs
|
|
||||||
- id: check-shebang-scripts-are-executable
|
|
||||||
- id: check-merge-conflict
|
|
||||||
- id: check-json
|
|
||||||
- id: check-xml
|
|
||||||
- id: check-yaml
|
|
||||||
- id: trailing-whitespace
|
|
||||||
args: [--markdown-linebreak-ext=md]
|
|
||||||
- id: end-of-file-fixer
|
|
||||||
exclude: ".key$"
|
|
||||||
|
|
||||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
|
||||||
rev: v0.9.0.5
|
|
||||||
hooks:
|
|
||||||
- id: shellcheck
|
|
||||||
|
|
||||||
- repo: local
|
|
||||||
hooks:
|
|
||||||
- id: make-lint
|
|
||||||
name: Run Make Lint
|
|
||||||
entry: make lint
|
|
||||||
language: system
|
|
||||||
pass_filenames: false
|
|
||||||
|
|
||||||
- repo: local
|
|
||||||
hooks:
|
|
||||||
- id: go-unit-tests
|
|
||||||
name: go unit tests
|
|
||||||
entry: make test
|
|
||||||
pass_filenames: false
|
|
||||||
types: [go]
|
|
||||||
language: system
|
|
||||||
|
|
||||||
- repo: https://github.com/TekWizely/pre-commit-golang
|
|
||||||
rev: v1.0.0-rc.1
|
|
||||||
hooks:
|
|
||||||
- id: go-staticcheck-repo-mod
|
|
||||||
- id: go-mod-tidy
|
|
|
@ -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
|
|
1630
CHANGELOG.md
1630
CHANGELOG.md
File diff suppressed because it is too large
Load diff
|
@ -3,8 +3,8 @@
|
||||||
First, thank you for contributing! We love and encourage pull requests from
|
First, thank you for contributing! We love and encourage pull requests from
|
||||||
everyone. Please follow the guidelines:
|
everyone. Please follow the guidelines:
|
||||||
|
|
||||||
- Check the open [issues](https://git.frostfs.info/TrueCloudLab/frostfs-node/issues) and
|
- Check the open [issues](https://github.com/TrueCloudLab/frostfs-node/issues) and
|
||||||
[pull requests](https://git.frostfs.info/TrueCloudLab/frostfs-node/pulls) for existing
|
[pull requests](https://github.com/TrueCloudLab/frostfs-node/pulls) for existing
|
||||||
discussions.
|
discussions.
|
||||||
|
|
||||||
- Open an issue first, to discuss a new feature or enhancement.
|
- Open an issue first, to discuss a new feature or enhancement.
|
||||||
|
@ -27,19 +27,19 @@ Start by forking the `frostfs-node` repository, make changes in a branch and the
|
||||||
send a pull request. We encourage pull requests to discuss code changes. Here
|
send a pull request. We encourage pull requests to discuss code changes. Here
|
||||||
are the steps in details:
|
are the steps in details:
|
||||||
|
|
||||||
### Set up your Forgejo repository
|
### Set up your GitHub Repository
|
||||||
Fork [FrostFS node upstream](https://git.frostfs.info/TrueCloudLab/frostfs-node) source
|
Fork [FrostFS node upstream](https://github.com/TrueCloudLab/frostfs-node/fork) source
|
||||||
repository to your own personal repository. Copy the URL of your fork (you will
|
repository to your own personal repository. Copy the URL of your fork (you will
|
||||||
need it for the `git clone` command below).
|
need it for the `git clone` command below).
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ git clone https://git.frostfs.info/TrueCloudLab/frostfs-node
|
$ git clone https://github.com/TrueCloudLab/frostfs-node
|
||||||
```
|
```
|
||||||
|
|
||||||
### Set up git remote as ``upstream``
|
### Set up git remote as ``upstream``
|
||||||
```sh
|
```sh
|
||||||
$ cd frostfs-node
|
$ cd frostfs-node
|
||||||
$ git remote add upstream https://git.frostfs.info/TrueCloudLab/frostfs-node
|
$ git remote add upstream https://github.com/TrueCloudLab/frostfs-node
|
||||||
$ git fetch upstream
|
$ git fetch upstream
|
||||||
$ git merge upstream/master
|
$ git merge upstream/master
|
||||||
...
|
...
|
||||||
|
@ -58,7 +58,7 @@ $ git checkout -b feature/123-something_awesome
|
||||||
After your code changes, make sure
|
After your code changes, make sure
|
||||||
|
|
||||||
- To add test cases for the new code.
|
- To add test cases for the new code.
|
||||||
- To run `make lint` and `make staticcheck-run`
|
- To run `make lint`
|
||||||
- To squash your commits into a single commit or a series of logically separated
|
- To squash your commits into a single commit or a series of logically separated
|
||||||
commits run `git rebase -i`. It's okay to force update your pull request.
|
commits run `git rebase -i`. It's okay to force update your pull request.
|
||||||
- To run `make test` and `make all` completes.
|
- To run `make test` and `make all` completes.
|
||||||
|
@ -89,8 +89,8 @@ $ git push origin feature/123-something_awesome
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create a Pull Request
|
### Create a Pull Request
|
||||||
Pull requests can be created via Forgejo. Refer to [this
|
Pull requests can be created via GitHub. Refer to [this
|
||||||
document](https://docs.codeberg.org/collaborating/pull-requests-and-git-flow/) for
|
document](https://help.github.com/articles/creating-a-pull-request/) for
|
||||||
detailed steps on how to create a pull request. After a Pull Request gets peer
|
detailed steps on how to create a pull request. After a Pull Request gets peer
|
||||||
reviewed and approved, it will be merged.
|
reviewed and approved, it will be merged.
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ In alphabetical order:
|
||||||
- Alexey Vanin
|
- Alexey Vanin
|
||||||
- Anastasia Prasolova
|
- Anastasia Prasolova
|
||||||
- Anatoly Bogatyrev
|
- Anatoly Bogatyrev
|
||||||
- Evgeny Kulikov
|
- Evgeny Kulikov
|
||||||
- Evgeny Stratonikov
|
- Evgeny Stratonikov
|
||||||
- Leonard Liubich
|
- Leonard Liubich
|
||||||
- Sergei Liubich
|
- Sergei Liubich
|
||||||
|
|
59
Makefile
Executable file → Normal file
59
Makefile
Executable file → Normal file
|
@ -7,9 +7,8 @@ VERSION ?= $(shell git describe --tags --dirty --match "v*" --always --abbrev=8
|
||||||
HUB_IMAGE ?= truecloudlab/frostfs
|
HUB_IMAGE ?= truecloudlab/frostfs
|
||||||
HUB_TAG ?= "$(shell echo ${VERSION} | sed 's/^v//')"
|
HUB_TAG ?= "$(shell echo ${VERSION} | sed 's/^v//')"
|
||||||
|
|
||||||
GO_VERSION ?= 1.21
|
GO_VERSION ?= 1.19
|
||||||
LINT_VERSION ?= 1.54.0
|
LINT_VERSION ?= 1.50.0
|
||||||
TRUECLOUDLAB_LINT_VERSION ?= 0.0.2
|
|
||||||
ARCH = amd64
|
ARCH = amd64
|
||||||
|
|
||||||
BIN = bin
|
BIN = bin
|
||||||
|
@ -26,12 +25,8 @@ PKG_VERSION ?= $(shell echo $(VERSION) | sed "s/^v//" | \
|
||||||
sed -E "s/(.*)-(g[a-fA-F0-9]{6,8})(.*)/\1\3~\2/" | \
|
sed -E "s/(.*)-(g[a-fA-F0-9]{6,8})(.*)/\1\3~\2/" | \
|
||||||
sed "s/-/~/")-${OS_RELEASE}
|
sed "s/-/~/")-${OS_RELEASE}
|
||||||
|
|
||||||
OUTPUT_LINT_DIR ?= $(shell pwd)/bin
|
|
||||||
LINT_DIR = $(OUTPUT_LINT_DIR)/golangci-lint-$(LINT_VERSION)-v$(TRUECLOUDLAB_LINT_VERSION)
|
|
||||||
TMP_DIR := .cache
|
|
||||||
|
|
||||||
.PHONY: help all images dep clean fmts fmt imports test lint docker/lint
|
.PHONY: help all images dep clean fmts fmt imports test lint docker/lint
|
||||||
prepare-release debpackage pre-commit unpre-commit
|
prepare-release debpackage
|
||||||
|
|
||||||
# To build a specific binary, use it's name prefix with bin/ as a target
|
# To build a specific binary, use it's name prefix with bin/ as a target
|
||||||
# For example `make bin/frostfs-node` will build only storage node binary
|
# For example `make bin/frostfs-node` will build only storage node binary
|
||||||
|
@ -70,18 +65,12 @@ dep:
|
||||||
CGO_ENABLED=0 \
|
CGO_ENABLED=0 \
|
||||||
go mod tidy -v && echo OK
|
go mod tidy -v && echo OK
|
||||||
|
|
||||||
# Build export-metrics
|
|
||||||
export-metrics: dep
|
|
||||||
@printf "⇒ Build export-metrics\n"
|
|
||||||
CGO_ENABLED=0 \
|
|
||||||
go build -v -trimpath -o bin/export-metrics ./scripts/export-metrics
|
|
||||||
|
|
||||||
# Regenerate proto files:
|
# Regenerate proto files:
|
||||||
protoc:
|
protoc:
|
||||||
@GOPRIVATE=github.com/TrueCloudLab go mod vendor
|
@GOPRIVATE=github.com/TrueCloudLab go mod vendor
|
||||||
# Install specific version for protobuf lib
|
# Install specific version for protobuf lib
|
||||||
@go list -f '{{.Path}}/...@{{.Version}}' -m github.com/golang/protobuf | xargs go install -v
|
@go list -f '{{.Path}}/...@{{.Version}}' -m github.com/golang/protobuf | xargs go install -v
|
||||||
@GOBIN=$(abspath $(BIN)) go install -mod=mod -v git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/protogen
|
@GOBIN=$(abspath $(BIN)) go install -mod=mod -v github.com/TrueCloudLab/frostfs-api-go/v2/util/protogen
|
||||||
# Protoc generate
|
# Protoc generate
|
||||||
@for f in `find . -type f -name '*.proto' -not -path './vendor/*'`; do \
|
@for f in `find . -type f -name '*.proto' -not -path './vendor/*'`; do \
|
||||||
echo "⇒ Processing $$f "; \
|
echo "⇒ Processing $$f "; \
|
||||||
|
@ -106,7 +95,7 @@ image-%:
|
||||||
-t $(HUB_IMAGE)-$*:$(HUB_TAG) .
|
-t $(HUB_IMAGE)-$*:$(HUB_TAG) .
|
||||||
|
|
||||||
# Build all Docker images
|
# Build all Docker images
|
||||||
images: image-storage image-ir image-cli image-adm
|
images: image-storage image-ir image-cli image-adm image-storage-testnet
|
||||||
|
|
||||||
# Build dirty local Docker images
|
# Build dirty local Docker images
|
||||||
dirty-images: image-dirty-storage image-dirty-ir image-dirty-cli image-dirty-adm
|
dirty-images: image-dirty-storage image-dirty-ir image-dirty-cli image-dirty-adm
|
||||||
|
@ -137,36 +126,11 @@ imports:
|
||||||
# Run Unit Test with go test
|
# Run Unit Test with go test
|
||||||
test:
|
test:
|
||||||
@echo "⇒ Running go test"
|
@echo "⇒ Running go test"
|
||||||
@go test ./... -count=1
|
@go test ./...
|
||||||
|
|
||||||
pre-commit-run:
|
|
||||||
@pre-commit run -a --hook-stage manual
|
|
||||||
|
|
||||||
# Install linters
|
|
||||||
lint-install:
|
|
||||||
@mkdir -p $(TMP_DIR)
|
|
||||||
@rm -rf $(TMP_DIR)/linters
|
|
||||||
@git -c advice.detachedHead=false clone --branch v$(TRUECLOUDLAB_LINT_VERSION) https://git.frostfs.info/TrueCloudLab/linters.git $(TMP_DIR)/linters
|
|
||||||
@@make -C $(TMP_DIR)/linters lib CGO_ENABLED=1 OUT_DIR=$(OUTPUT_LINT_DIR)
|
|
||||||
@rm -rf $(TMP_DIR)/linters
|
|
||||||
@rmdir $(TMP_DIR) 2>/dev/null || true
|
|
||||||
@CGO_ENABLED=1 GOBIN=$(LINT_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v$(LINT_VERSION)
|
|
||||||
|
|
||||||
# Run linters
|
# Run linters
|
||||||
lint:
|
lint:
|
||||||
@if [ ! -d "$(LINT_DIR)" ]; then \
|
@golangci-lint --timeout=5m run
|
||||||
echo "Run make lint-install"; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
|
||||||
$(LINT_DIR)/golangci-lint run
|
|
||||||
|
|
||||||
# Install staticcheck
|
|
||||||
staticcheck-install:
|
|
||||||
@go install honnef.co/go/tools/cmd/staticcheck@latest
|
|
||||||
|
|
||||||
# Run staticcheck
|
|
||||||
staticcheck-run:
|
|
||||||
@staticcheck ./...
|
|
||||||
|
|
||||||
# Run linters in Docker
|
# Run linters in Docker
|
||||||
docker/lint:
|
docker/lint:
|
||||||
|
@ -176,19 +140,10 @@ docker/lint:
|
||||||
--env HOME=/src \
|
--env HOME=/src \
|
||||||
golangci/golangci-lint:v$(LINT_VERSION) bash -c 'cd /src/ && make lint'
|
golangci/golangci-lint:v$(LINT_VERSION) bash -c 'cd /src/ && make lint'
|
||||||
|
|
||||||
# Activate pre-commit hooks
|
|
||||||
pre-commit:
|
|
||||||
pre-commit install -t pre-commit -t commit-msg
|
|
||||||
|
|
||||||
# Deactivate pre-commit hooks
|
|
||||||
unpre-commit:
|
|
||||||
pre-commit uninstall -t pre-commit -t commit-msg
|
|
||||||
|
|
||||||
# Print version
|
# Print version
|
||||||
version:
|
version:
|
||||||
@echo $(VERSION)
|
@echo $(VERSION)
|
||||||
|
|
||||||
# Delete built artifacts
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf vendor
|
rm -rf vendor
|
||||||
rm -rf .cache
|
rm -rf .cache
|
||||||
|
|
|
@ -31,7 +31,7 @@ dApps directly from
|
||||||
code level. This way dApps are not limited to on-chain storage and can
|
code level. This way dApps are not limited to on-chain storage and can
|
||||||
manipulate large amounts of data without paying a prohibitive price.
|
manipulate large amounts of data without paying a prohibitive price.
|
||||||
|
|
||||||
FrostFS has a native [gRPC API](https://git.frostfs.info/TrueCloudLab/frostfs-api) and has
|
FrostFS has a native [gRPC API](https://github.com/TrueCloudLab/frostfs-api) and has
|
||||||
protocol gateways for popular protocols such as [AWS
|
protocol gateways for popular protocols such as [AWS
|
||||||
S3](https://github.com/TrueCloudLab/frostfs-s3-gw),
|
S3](https://github.com/TrueCloudLab/frostfs-s3-gw),
|
||||||
[HTTP](https://github.com/TrueCloudLab/frostfs-http-gw),
|
[HTTP](https://github.com/TrueCloudLab/frostfs-http-gw),
|
||||||
|
@ -49,7 +49,7 @@ The latest version of frostfs-node works with frostfs-contract
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
|
||||||
To make all binaries you need Go 1.20+ and `make`:
|
To make all binaries you need Go 1.18+ and `make`:
|
||||||
```
|
```
|
||||||
make all
|
make all
|
||||||
```
|
```
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
v0.36.0
|
v0.35.0
|
||||||
|
|
|
@ -3,22 +3,23 @@
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Admin tool provides an easier way to deploy and maintain private installation
|
Admin tool provides an easier way to deploy and maintain private installation
|
||||||
of FrostFS. Private installation provides a set of N3 consensus nodes, FrostFS
|
of FrostFS. Private installation provides a set of N3 consensus nodes, FrostFS
|
||||||
Alphabet, and Storage nodes. Admin tool generates consensus keys, initializes
|
Alphabet, and Storage nodes. Admin tool generates consensus keys, initializes
|
||||||
the sidechain, and provides functions to update the network and register new
|
the sidechain, and provides functions to update the network and register new
|
||||||
Storage nodes.
|
Storage nodes.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
To build binary locally, use `make bin/frostfs-adm` command.
|
To build binary locally, use `make bin/frostfs-adm` command.
|
||||||
|
|
||||||
For clean build inside a docker container, use `make docker/bin/frostfs-adm`.
|
For clean build inside a docker container, use `make docker/bin/frostfs-adm`.
|
||||||
|
|
||||||
Build docker image with `make image-adm`.
|
Build docker image with `make image-adm`.
|
||||||
|
|
||||||
At FrostFS private install deployment, frostfs-adm requires compiled FrostFS
|
At FrostFS private install deployment, frostfs-adm requires compiled FrostFS
|
||||||
contracts. Find them in the latest release of
|
contracts. Find them in the latest release of
|
||||||
[frostfs-contract repository](https://git.frostfs.info/TrueCloudLab/frostfs-contract/releases).
|
[frostfs-contract repository](https://github.com/TrueCloudLab/frostfs-contract/releases).
|
||||||
|
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ contracts. Find them in the latest release of
|
||||||
|
|
||||||
Config section provides `init` command that creates a configuration file for
|
Config section provides `init` command that creates a configuration file for
|
||||||
private installation deployment and updates. Config file is optional, all
|
private installation deployment and updates. Config file is optional, all
|
||||||
parameters can be passed by arguments or read from standard input (wallet
|
parameters can be passed by arguments or read from standard input (wallet
|
||||||
passwords).
|
passwords).
|
||||||
|
|
||||||
Config example:
|
Config example:
|
||||||
|
@ -36,7 +37,9 @@ alphabet-wallets: /path # path to consensus node / alphabet wallets s
|
||||||
network:
|
network:
|
||||||
max_object_size: 67108864 # max size of a single FrostFS object, bytes
|
max_object_size: 67108864 # max size of a single FrostFS object, bytes
|
||||||
epoch_duration: 240 # duration of a FrostFS epoch in blocks, consider block generation frequency in the sidechain
|
epoch_duration: 240 # duration of a FrostFS epoch in blocks, consider block generation frequency in the sidechain
|
||||||
|
basic_income_rate: 0 # basic income rate, for private consider 0
|
||||||
fee:
|
fee:
|
||||||
|
audit: 0 # network audit fee, for private installation consider 0
|
||||||
candidate: 0 # inner ring candidate registration fee, for private installation consider 0
|
candidate: 0 # inner ring candidate registration fee, for private installation consider 0
|
||||||
container: 0 # container creation fee, for private installation consider 0
|
container: 0 # container creation fee, for private installation consider 0
|
||||||
container_alias: 0 # container nice-name registration fee, for private installation consider 0
|
container_alias: 0 # container nice-name registration fee, for private installation consider 0
|
||||||
|
@ -55,14 +58,14 @@ credentials: # passwords for consensus node / alphabet wallets
|
||||||
|
|
||||||
#### Network deployment
|
#### Network deployment
|
||||||
|
|
||||||
- `generate-alphabet` generates a set of wallets for consensus and
|
- `generate-alphabet` generates a set of wallets for consensus and
|
||||||
Alphabet nodes.
|
Alphabet nodes.
|
||||||
|
|
||||||
- `init` initializes the sidechain by deploying smart contracts and
|
- `init` initializes the sidechain by deploying smart contracts and
|
||||||
setting provided FrostFS network configuration.
|
setting provided FrostFS network configuration.
|
||||||
|
|
||||||
- `generate-storage-wallet` generates a wallet for the Storage node that
|
- `generate-storage-wallet` generates a wallet for the Storage node that
|
||||||
is ready for deployment. It also transfers a bit of sidechain GAS, so this
|
is ready for deployment. It also transfers a bit of sidechain GAS, so this
|
||||||
wallet can be used for FrostFS bootstrap.
|
wallet can be used for FrostFS bootstrap.
|
||||||
|
|
||||||
#### Network maintenance
|
#### Network maintenance
|
||||||
|
@ -72,7 +75,7 @@ credentials: # passwords for consensus node / alphabet wallets
|
||||||
- `force-new-epoch` increments FrostFS epoch number and executes new epoch
|
- `force-new-epoch` increments FrostFS epoch number and executes new epoch
|
||||||
handlers in FrostFS nodes.
|
handlers in FrostFS nodes.
|
||||||
|
|
||||||
- `refill-gas` transfers sidechain GAS to the specified wallet.
|
- `refill-gas` transfers sidechain GAS to the specified wallet.
|
||||||
|
|
||||||
- `update-contracts` updates contracts to a new version.
|
- `update-contracts` updates contracts to a new version.
|
||||||
|
|
||||||
|
@ -84,7 +87,7 @@ info. These commands **do not migrate actual objects**.
|
||||||
- `dump-containers` saves all containers and metadata registered in the container
|
- `dump-containers` saves all containers and metadata registered in the container
|
||||||
contract to a file.
|
contract to a file.
|
||||||
|
|
||||||
- `restore-containers` restores previously saved containers by their repeated registration in
|
- `restore-containers` restores previously saved containers by their repeated registration in
|
||||||
the container contract.
|
the container contract.
|
||||||
|
|
||||||
- `list-containers` output all containers ids.
|
- `list-containers` output all containers ids.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
This is a short guide on how to deploy a private FrostFS storage network on bare
|
This is a short guide on how to deploy a private FrostFS storage network on bare
|
||||||
metal without docker images. This guide does not cover details on how to start
|
metal without docker images. This guide does not cover details on how to start
|
||||||
consensus, Alphabet, or Storage nodes. This guide covers only `frostfs-adm`
|
consensus, Alphabet, or Storage nodes. This guide covers only `frostfs-adm`
|
||||||
related configuration details.
|
related configuration details.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
@ -12,13 +12,12 @@ To follow this guide you need:
|
||||||
- latest released version of [frostfs-adm](https://github.com/TrueCloudLab/frostfs-node/releases) utility (v0.25.1 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 compiled [frostfs-contract](https://github.com/TrueCloudLab/frostfs-contract/releases) (v0.11.0 at the moment).
|
||||||
|
|
||||||
## Step 1: Prepare network configuration
|
## Step 1: Prepare network configuration
|
||||||
|
|
||||||
To start a network, you need a set of consensus nodes, the same number of
|
To start a network, you need a set of consensus nodes, the same number of
|
||||||
Alphabet nodes and any number of Storage nodes. While the number of Storage
|
Alphabet nodes and any number of Storage nodes. While the number of Storage
|
||||||
nodes can be scaled almost infinitely, the number of consensus and Alphabet
|
nodes can be scaled almost infinitely, the number of consensus and Alphabet
|
||||||
nodes can't be changed so easily right now. Consider this before going any further.
|
nodes can't be changed so easily right now. Consider this before going any further.
|
||||||
Note also that there is an upper limit on the number of alphabet nodes (currently 22).
|
|
||||||
|
|
||||||
It is easier to use`frostfs-adm` with a predefined configuration. First, create
|
It is easier to use`frostfs-adm` with a predefined configuration. First, create
|
||||||
a network configuration file. In this example, there is going to be only one
|
a network configuration file. In this example, there is going to be only one
|
||||||
|
@ -28,13 +27,15 @@ consensus / Alphabet node in the network.
|
||||||
$ frostfs-adm config init --path foo.network.yml
|
$ frostfs-adm config init --path foo.network.yml
|
||||||
Initial config file saved to foo.network.yml
|
Initial config file saved to foo.network.yml
|
||||||
|
|
||||||
$ cat foo.network.yml
|
$ cat foo.network.yml
|
||||||
rpc-endpoint: https://neo.rpc.node:30333
|
rpc-endpoint: https://neo.rpc.node:30333
|
||||||
alphabet-wallets: /home/user/deploy/alphabet-wallets
|
alphabet-wallets: /home/user/deploy/alphabet-wallets
|
||||||
network:
|
network:
|
||||||
max_object_size: 67108864
|
max_object_size: 67108864
|
||||||
epoch_duration: 240
|
epoch_duration: 240
|
||||||
|
basic_income_rate: 0
|
||||||
fee:
|
fee:
|
||||||
|
audit: 0
|
||||||
candidate: 0
|
candidate: 0
|
||||||
container: 0
|
container: 0
|
||||||
withdraw: 0
|
withdraw: 0
|
||||||
|
@ -42,17 +43,17 @@ credentials:
|
||||||
az: hunter2
|
az: hunter2
|
||||||
```
|
```
|
||||||
|
|
||||||
For private installation, it is recommended to set all **fees** and **basic
|
For private installation, it is recommended to set all **fees** and **basic
|
||||||
income rate** to 0.
|
income rate** to 0.
|
||||||
|
|
||||||
As for **epoch duration**, consider consensus node block generation frequency.
|
As for **epoch duration**, consider consensus node block generation frequency.
|
||||||
With default 15 seconds per block, 240 blocks are going to be a 1-hour epoch.
|
With default 15 seconds per block, 240 blocks are going to be a 1-hour epoch.
|
||||||
|
|
||||||
For **max object size**, 67108864 (64 MiB) or 134217728 (128 MiB) should provide
|
For **max object size**, 67108864 (64 MiB) or 134217728 (128 MiB) should provide
|
||||||
good chunk distribution in most cases.
|
good chunk distribution in most cases.
|
||||||
|
|
||||||
With this config, generate wallets (private keys) of consensus nodes. The same
|
With this config, generate wallets (private keys) of consensus nodes. The same
|
||||||
wallets will be used for Alphabet nodes. Make sure, that dir for alphabet
|
wallets will be used for Alphabet nodes. Make sure, that dir for alphabet
|
||||||
wallets already exists.
|
wallets already exists.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -68,14 +69,14 @@ storage.
|
||||||
## Step 2: Launch consensus nodes
|
## Step 2: Launch consensus nodes
|
||||||
|
|
||||||
Configure blockchain nodes with the generated wallets from the previous step.
|
Configure blockchain nodes with the generated wallets from the previous step.
|
||||||
Config examples can be found in
|
Config examples can be found in
|
||||||
[neo-go repository](https://github.com/nspcc-dev/neo-go/tree/master/config).
|
[neo-go repository](https://github.com/nspcc-dev/neo-go/tree/master/config).
|
||||||
|
|
||||||
Gather public keys from **all** generated wallets. We are interested in the first
|
Gather public keys from **all** generated wallets. We are interested in the first
|
||||||
`simple signature contract` public key.
|
`simple signature contract` public key.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ neo-go wallet dump-keys -w alphabet-wallets/az.json
|
$ neo-go wallet dump-keys -w alphabet-wallets/az.json
|
||||||
NitdS4k4f1Hh5mbLJhAswBK3WC2gQgPN1o (simple signature contract):
|
NitdS4k4f1Hh5mbLJhAswBK3WC2gQgPN1o (simple signature contract):
|
||||||
02c1cc85f9c856dbe2d02017349bcb7b4e5defa78b8056a09b3240ba2a8c078869
|
02c1cc85f9c856dbe2d02017349bcb7b4e5defa78b8056a09b3240ba2a8c078869
|
||||||
|
|
||||||
|
@ -86,10 +87,10 @@ NiMKabp3ddi3xShmLAXhTfbnuWb4cSJT6E (1 out of 1 multisig contract):
|
||||||
02c1cc85f9c856dbe2d02017349bcb7b4e5defa78b8056a09b3240ba2a8c078869
|
02c1cc85f9c856dbe2d02017349bcb7b4e5defa78b8056a09b3240ba2a8c078869
|
||||||
```
|
```
|
||||||
|
|
||||||
Put the list of public keys into `ProtocolConfiguration.StandbyCommittee`
|
Put the list of public keys into `ProtocolConfiguration.StandbyCommittee`
|
||||||
section. Specify the wallet path and the password in `ApplicationConfiguration.P2PNotary`
|
section. Specify the wallet path and the password in `ApplicationConfiguration.P2PNotary`
|
||||||
and `ApplicationConfiguration.UnlockWallet` sections. If config includes
|
and `ApplicationConfiguration.UnlockWallet` sections. If config includes
|
||||||
`ProtocolConfiguration.NativeActivations` section, add notary
|
`ProtocolConfiguration.NativeActivations` section, add notary
|
||||||
contract `Notary: [0]`.
|
contract `Notary: [0]`.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -120,7 +121,7 @@ and possible overload issues.
|
||||||
Use archive with compiled FrostFS contracts to initialize the sidechain.
|
Use archive with compiled FrostFS contracts to initialize the sidechain.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ tar -xzvf frostfs-contract-v0.11.0.tar.gz
|
$ tar -xzvf frostfs-contract-v0.11.0.tar.gz
|
||||||
|
|
||||||
$ ./frostfs-adm -c foo.network.yml morph init --contracts ./frostfs-contract-v0.11.0
|
$ ./frostfs-adm -c foo.network.yml morph init --contracts ./frostfs-contract-v0.11.0
|
||||||
Stage 1: transfer GAS to alphabet nodes.
|
Stage 1: transfer GAS to alphabet nodes.
|
||||||
|
@ -140,18 +141,20 @@ Waiting for transactions to persist...
|
||||||
Stage 7: set addresses in NNS.
|
Stage 7: set addresses in NNS.
|
||||||
Waiting for transactions to persist...
|
Waiting for transactions to persist...
|
||||||
NNS: Set alphabet0.frostfs -> f692dfb4d43a15b464eb51a7041160fb29c44b6a
|
NNS: Set alphabet0.frostfs -> f692dfb4d43a15b464eb51a7041160fb29c44b6a
|
||||||
|
NNS: Set audit.frostfs -> 7df847b993affb3852074345a7c2bd622171ee0d
|
||||||
NNS: Set balance.frostfs -> 103519b3067a66307080a66570c0491ee8f68879
|
NNS: Set balance.frostfs -> 103519b3067a66307080a66570c0491ee8f68879
|
||||||
NNS: Set container.frostfs -> cae60bdd689d185901e495352d0247752ce50846
|
NNS: Set container.frostfs -> cae60bdd689d185901e495352d0247752ce50846
|
||||||
NNS: Set frostfsid.frostfs -> c421fb60a3895865a8f24d197d6a80ef686041d2
|
NNS: Set frostfsid.frostfs -> c421fb60a3895865a8f24d197d6a80ef686041d2
|
||||||
NNS: Set netmap.frostfs -> 894eb854632f50fb124412ce7951ebc00763525e
|
NNS: Set netmap.frostfs -> 894eb854632f50fb124412ce7951ebc00763525e
|
||||||
NNS: Set proxy.frostfs -> ac6e6fe4b373d0ca0ca4969d1e58fa0988724e7d
|
NNS: Set proxy.frostfs -> ac6e6fe4b373d0ca0ca4969d1e58fa0988724e7d
|
||||||
|
NNS: Set reputation.frostfs -> 6eda57c9d93d990573646762d1fea327ce41191f
|
||||||
Waiting for transactions to persist...
|
Waiting for transactions to persist...
|
||||||
```
|
```
|
||||||
|
|
||||||
## Step 4: Launch Alphabet nodes
|
## Step 4: Launch Alphabet nodes
|
||||||
|
|
||||||
Configure Alphabet nodes with the wallets generated in step 1. For
|
Configure Alphabet nodes with the wallets generated in step 1. For
|
||||||
`morph.validators` use a list of public keys from
|
`morph.validators` use a list of public keys from
|
||||||
`ProtocolConfiguration.StandbyCommittee`.
|
`ProtocolConfiguration.StandbyCommittee`.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -175,10 +178,10 @@ Generate a new wallet for a Storage node.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ frostfs-adm -c foo.network.yml morph generate-storage-wallet --storage-wallet ./sn01.json --initial-gas 10.0
|
$ frostfs-adm -c foo.network.yml morph generate-storage-wallet --storage-wallet ./sn01.json --initial-gas 10.0
|
||||||
New password >
|
New password >
|
||||||
Waiting for transactions to persist...
|
Waiting for transactions to persist...
|
||||||
|
|
||||||
$ neo-go wallet dump-keys -w sn01.json
|
$ neo-go wallet dump-keys -w sn01.json
|
||||||
Ngr7p8Z9S22XDH6VkUG9oXobv8zZRAWwwv (simple signature contract):
|
Ngr7p8Z9S22XDH6VkUG9oXobv8zZRAWwwv (simple signature contract):
|
||||||
0355eccb72cd46f09a3e5237eaa0f4949cceb5ecfa5a225bd3bb9fd021c4d75b85
|
0355eccb72cd46f09a3e5237eaa0f4949cceb5ecfa5a225bd3bb9fd021c4d75b85
|
||||||
```
|
```
|
||||||
|
@ -202,7 +205,7 @@ Current epoch: 8, increase to 9.
|
||||||
Waiting for transactions to persist...
|
Waiting for transactions to persist...
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
After that, FrostFS Storage is ready to work. You can access it directly or
|
After that, FrostFS Storage is ready to work. You can access it directly or
|
||||||
with protocol gates.
|
with protocol gates.
|
||||||
|
|
39
cmd/frostfs-adm/docs/subnetwork-creation.md
Normal file
39
cmd/frostfs-adm/docs/subnetwork-creation.md
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
# FrostFS subnetwork creation
|
||||||
|
|
||||||
|
This is a short guide on how to create FrostFS subnetworks. This guide
|
||||||
|
considers that the sidechain and the inner ring (alphabet nodes) have already been
|
||||||
|
deployed and the sidechain contains a deployed `subnet` contract.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
To follow this guide, you need:
|
||||||
|
- neo-go sidechain RPC endpoint;
|
||||||
|
- latest released version of [frostfs-adm](https://github.com/TrueCloudLab/frostfs-node/releases);
|
||||||
|
- wallet with FrostFS account.
|
||||||
|
|
||||||
|
## Creation
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ frostfs-adm morph subnet create \
|
||||||
|
-r <side_chain_RPC_endpoint> \
|
||||||
|
-w </path/to/owner/wallet> \
|
||||||
|
--notary
|
||||||
|
Create subnet request sent successfully. ID: 4223489767.
|
||||||
|
```
|
||||||
|
|
||||||
|
**NOTE:** in notary-enabled environment you should have a sufficient
|
||||||
|
notary deposit (not expired, with enough GAS balance). Your subnet ID
|
||||||
|
will differ from the example.
|
||||||
|
|
||||||
|
The default account in the wallet that has been passed with `-w` flag is the owner
|
||||||
|
of the just created subnetwork.
|
||||||
|
|
||||||
|
You can check if your subnetwork was created successfully:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ frostfs-adm morph subnet get \
|
||||||
|
-r <side_chain_RPC_endpoint> \
|
||||||
|
--subnet <subnet_ID>
|
||||||
|
Owner: NUc734PMJXiqa2J9jRtvskU3kCdyyuSN8Q
|
||||||
|
```
|
||||||
|
Your owner will differ from the example.
|
137
cmd/frostfs-adm/docs/subnetwork-usage.md
Normal file
137
cmd/frostfs-adm/docs/subnetwork-usage.md
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
# Managing Subnetworks
|
||||||
|
|
||||||
|
This is a short guide on how to manage FrostFS subnetworks. This guide
|
||||||
|
considers that the sidechain and the inner ring (alphabet nodes) have already been
|
||||||
|
deployed, and the sidechain contains a deployed `subnet` contract.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- neo-go sidechain RPC endpoint;
|
||||||
|
- latest released version of [frostfs-adm](https://github.com/TrueCloudLab/frostfs-node/releases);
|
||||||
|
- [created](subnetwork-creation.md) subnetwork;
|
||||||
|
- wallet with the account that owns the subnetwork;
|
||||||
|
- public key of the Storage Node;
|
||||||
|
- public keys of the node and client administrators;
|
||||||
|
- owner IDs of the FrostFS users.
|
||||||
|
|
||||||
|
## Add node administrator
|
||||||
|
|
||||||
|
Node administrators are accounts that can manage (add and delete nodes)
|
||||||
|
the whitelist of the nodes which can be included to a subnetwork. Only the subnet
|
||||||
|
owner is allowed to add and remove node administrators from the subnetwork.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ frostfs-adm morph subnet admin add \
|
||||||
|
-r <side_chain_RPC_endpoint> \
|
||||||
|
-w </path/to/owner/wallet> \
|
||||||
|
--admin <HEX_admin_public_key> \
|
||||||
|
--subnet <subnet_ID>
|
||||||
|
Add admin request sent successfully.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Add node
|
||||||
|
|
||||||
|
Adding a node to a subnetwork means that the node becomes able to service
|
||||||
|
containers that have been created in that subnetwork. Addition only changes
|
||||||
|
the list of the allowed nodes. Node is not required to be bootstrapped at the
|
||||||
|
moment of its inclusion.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ frostfs-adm morph subnet node add \
|
||||||
|
-r <side_chain_RPC_endpoint> \
|
||||||
|
-w </path/to/node_admin/wallet> \
|
||||||
|
--node <HEX_node_public_key> \
|
||||||
|
--subnet <subnet_ID>
|
||||||
|
Add node request sent successfully.
|
||||||
|
```
|
||||||
|
|
||||||
|
**NOTE:** the owner of the subnetwork is also allowed to add nodes.
|
||||||
|
|
||||||
|
## Add client administrator
|
||||||
|
|
||||||
|
Client administrators are accounts that can manage (add and delete
|
||||||
|
nodes) the whitelist of the clients that can create containers in the
|
||||||
|
subnetwork. Only the subnet owner is allowed to add and remove client
|
||||||
|
administrators from the subnetwork.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ frostfs-adm morph subnet admin add \
|
||||||
|
-r <side_chain_RPC_endpoint> \
|
||||||
|
-w </path/to/owner/wallet> \
|
||||||
|
--admin <HEX_admin_public_key> \
|
||||||
|
--subnet <subnet_ID> \
|
||||||
|
--client \
|
||||||
|
--group <group_ID>
|
||||||
|
Add admin request sent successfully.
|
||||||
|
```
|
||||||
|
|
||||||
|
**NOTE:** you do not need to create a group explicitly, it will be created
|
||||||
|
right after the first client admin is added. Group ID is a 4-byte
|
||||||
|
positive integer number.
|
||||||
|
|
||||||
|
## Add client
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ frostfs-adm morph subnet client add \
|
||||||
|
-r <side_chain_RPC_endpoint> \
|
||||||
|
-w </path/to/client_admin/wallet> \
|
||||||
|
--client <client_ownerID> \
|
||||||
|
--subnet <subnet_ID> \
|
||||||
|
--group <group_ID>
|
||||||
|
Add client request sent successfully.
|
||||||
|
```
|
||||||
|
|
||||||
|
**NOTE:** the owner of the subnetwork is also allowed to add clients. This is
|
||||||
|
the only one command that accepts `ownerID`, not the public key.
|
||||||
|
Administrator can manage only their group (a group where that administrator
|
||||||
|
has been added by the subnet owner).
|
||||||
|
|
||||||
|
# Bootstrapping Storage Node
|
||||||
|
|
||||||
|
After a subnetwork [is created](subnetwork-creation.md) and a node is included into it, the
|
||||||
|
node could be bootstrapped and service subnetwork containers.
|
||||||
|
|
||||||
|
For bootstrapping, you need to specify the ID of the subnetwork in the node's
|
||||||
|
configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
...
|
||||||
|
node:
|
||||||
|
...
|
||||||
|
subnet:
|
||||||
|
entries: # list of IDs of subnets to enter in a text format of FrostFS API protocol (overrides corresponding attributes)
|
||||||
|
- <subnetwork_ID>
|
||||||
|
...
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
**NOTE:** specifying subnetwork that is denied for the node is not an error:
|
||||||
|
that configuration value would be ignored. You do not need to specify zero
|
||||||
|
(with 0 ID) subnetwork: its inclusion is implicit. On the contrary, to exclude
|
||||||
|
a node from the default zero subnetwork, you need to specify it explicitly:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
...
|
||||||
|
node:
|
||||||
|
...
|
||||||
|
subnet:
|
||||||
|
exit_zero: true # toggle entrance to zero subnet (overrides corresponding attribute and occurrence in `entries`)
|
||||||
|
...
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
# Creating container in non-zero subnetwork
|
||||||
|
|
||||||
|
Creating containers without using `--subnet` flag is equivalent to
|
||||||
|
creating container in the zero subnetwork.
|
||||||
|
|
||||||
|
To create a container in a private network, your wallet must be added to
|
||||||
|
the client whitelist by the client admins or the subnet owners:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ frostfs-cli container create \
|
||||||
|
--policy 'REP 1' \
|
||||||
|
-w </path/to/wallet> \
|
||||||
|
-r s01.frostfs.devenv:8080 \
|
||||||
|
--subnet <subnet_ID>
|
||||||
|
```
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
"github.com/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||||
"github.com/nspcc-dev/neo-go/cli/input"
|
"github.com/nspcc-dev/neo-go/cli/input"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
@ -18,6 +18,8 @@ type configTemplate struct {
|
||||||
AlphabetDir string
|
AlphabetDir string
|
||||||
MaxObjectSize int
|
MaxObjectSize int
|
||||||
EpochDuration int
|
EpochDuration int
|
||||||
|
BasicIncomeRate int
|
||||||
|
AuditFee int
|
||||||
CandidateFee int
|
CandidateFee int
|
||||||
ContainerFee int
|
ContainerFee int
|
||||||
ContainerAliasFee int
|
ContainerAliasFee int
|
||||||
|
@ -31,8 +33,10 @@ alphabet-wallets: {{ .AlphabetDir}}
|
||||||
network:
|
network:
|
||||||
max_object_size: {{ .MaxObjectSize}}
|
max_object_size: {{ .MaxObjectSize}}
|
||||||
epoch_duration: {{ .EpochDuration}}
|
epoch_duration: {{ .EpochDuration}}
|
||||||
|
basic_income_rate: {{ .BasicIncomeRate}}
|
||||||
homomorphic_hash_disabled: {{ .HomomorphicHashDisabled}}
|
homomorphic_hash_disabled: {{ .HomomorphicHashDisabled}}
|
||||||
fee:
|
fee:
|
||||||
|
audit: {{ .AuditFee}}
|
||||||
candidate: {{ .CandidateFee}}
|
candidate: {{ .CandidateFee}}
|
||||||
container: {{ .ContainerFee}}
|
container: {{ .ContainerFee}}
|
||||||
container_alias: {{ .ContainerAliasFee }}
|
container_alias: {{ .ContainerAliasFee }}
|
||||||
|
@ -43,7 +47,7 @@ credentials:
|
||||||
{{.}}: password{{end}}
|
{{.}}: password{{end}}
|
||||||
`
|
`
|
||||||
|
|
||||||
func initConfig(cmd *cobra.Command, _ []string) error {
|
func initConfig(cmd *cobra.Command, args []string) error {
|
||||||
configPath, err := readConfigPathFromArgs(cmd)
|
configPath, err := readConfigPathFromArgs(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -107,7 +111,9 @@ func generateConfigExample(appDir string, credSize int) (string, error) {
|
||||||
Endpoint: "https://neo.rpc.node:30333",
|
Endpoint: "https://neo.rpc.node:30333",
|
||||||
MaxObjectSize: 67108864, // 64 MiB
|
MaxObjectSize: 67108864, // 64 MiB
|
||||||
EpochDuration: 240, // 1 hour with 15s per block
|
EpochDuration: 240, // 1 hour with 15s per block
|
||||||
|
BasicIncomeRate: 1_0000_0000, // 0.0001 GAS per GiB (Fixed12)
|
||||||
HomomorphicHashDisabled: false, // object homomorphic hash is enabled
|
HomomorphicHashDisabled: false, // object homomorphic hash is enabled
|
||||||
|
AuditFee: 1_0000, // 0.00000001 GAS per audit (Fixed12)
|
||||||
CandidateFee: 100_0000_0000, // 100.0 GAS (Fixed8)
|
CandidateFee: 100_0000_0000, // 100.0 GAS (Fixed8)
|
||||||
ContainerFee: 1000, // 0.000000001 * 7 GAS per container (Fixed12)
|
ContainerFee: 1000, // 0.000000001 * 7 GAS per container (Fixed12)
|
||||||
ContainerAliasFee: 500, // ContainerFee / 2
|
ContainerAliasFee: 500, // ContainerFee / 2
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
"github.com/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -28,6 +28,8 @@ func TestGenerateConfigExample(t *testing.T) {
|
||||||
require.Equal(t, filepath.Join(appDir, "alphabet-wallets"), v.GetString("alphabet-wallets"))
|
require.Equal(t, filepath.Join(appDir, "alphabet-wallets"), v.GetString("alphabet-wallets"))
|
||||||
require.Equal(t, 67108864, v.GetInt("network.max_object_size"))
|
require.Equal(t, 67108864, v.GetInt("network.max_object_size"))
|
||||||
require.Equal(t, 240, v.GetInt("network.epoch_duration"))
|
require.Equal(t, 240, v.GetInt("network.epoch_duration"))
|
||||||
|
require.Equal(t, 100000000, v.GetInt("network.basic_income_rate"))
|
||||||
|
require.Equal(t, 10000, v.GetInt("network.fee.audit"))
|
||||||
require.Equal(t, 10000000000, v.GetInt("network.fee.candidate"))
|
require.Equal(t, 10000000000, v.GetInt("network.fee.candidate"))
|
||||||
require.Equal(t, 1000, v.GetInt("network.fee.container"))
|
require.Equal(t, 1000, v.GetInt("network.fee.container"))
|
||||||
require.Equal(t, 100000000, v.GetInt("network.fee.withdraw"))
|
require.Equal(t, 100000000, v.GetInt("network.fee.withdraw"))
|
||||||
|
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
"github.com/TrueCloudLab/frostfs-contract/nns"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -37,6 +37,11 @@ const (
|
||||||
dumpBalancesAlphabetFlag = "alphabet"
|
dumpBalancesAlphabetFlag = "alphabet"
|
||||||
dumpBalancesProxyFlag = "proxy"
|
dumpBalancesProxyFlag = "proxy"
|
||||||
dumpBalancesUseScriptHashFlag = "script-hash"
|
dumpBalancesUseScriptHashFlag = "script-hash"
|
||||||
|
|
||||||
|
// notaryEnabled signifies whether contracts were deployed in a notary-enabled environment.
|
||||||
|
// The setting is here to simplify testing and building the command for testnet (notary currently disabled).
|
||||||
|
// It will be removed eventually.
|
||||||
|
notaryEnabled = true
|
||||||
)
|
)
|
||||||
|
|
||||||
func dumpBalances(cmd *cobra.Command, _ []string) error {
|
func dumpBalances(cmd *cobra.Command, _ []string) error {
|
||||||
|
@ -55,7 +60,7 @@ func dumpBalances(cmd *cobra.Command, _ []string) error {
|
||||||
|
|
||||||
inv := invoker.New(c, nil)
|
inv := invoker.New(c, nil)
|
||||||
|
|
||||||
if dumpStorage || dumpAlphabet || dumpProxy {
|
if !notaryEnabled || dumpStorage || dumpAlphabet || dumpProxy {
|
||||||
nnsCs, err = c.GetContractStateByID(1)
|
nnsCs, err = c.GetContractStateByID(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't get NNS contract info: %w", err)
|
return fmt.Errorf("can't get NNS contract info: %w", err)
|
||||||
|
@ -67,7 +72,7 @@ func dumpBalances(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
irList, err := fetchIRNodes(c, rolemgmt.Hash)
|
irList, err := fetchIRNodes(c, nmHash, rolemgmt.Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -78,126 +83,120 @@ func dumpBalances(cmd *cobra.Command, _ []string) error {
|
||||||
printBalances(cmd, "Inner ring nodes balances:", irList)
|
printBalances(cmd, "Inner ring nodes balances:", irList)
|
||||||
|
|
||||||
if dumpStorage {
|
if dumpStorage {
|
||||||
if err := printStorageNodeBalances(cmd, inv, nmHash); err != nil {
|
arr, err := unwrap.Array(inv.Call(nmHash, "netmap"))
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("can't fetch the list of storage nodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
snList := make([]accBalancePair, len(arr))
|
||||||
|
for i := range arr {
|
||||||
|
node, ok := arr[i].Value().([]stackitem.Item)
|
||||||
|
if !ok || len(node) == 0 {
|
||||||
|
return errors.New("can't parse the list of storage nodes")
|
||||||
|
}
|
||||||
|
bs, err := node[0].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("can't parse the list of storage nodes")
|
||||||
|
}
|
||||||
|
var ni netmap.NodeInfo
|
||||||
|
if err := ni.Unmarshal(bs); err != nil {
|
||||||
|
return fmt.Errorf("can't parse the list of storage nodes: %w", err)
|
||||||
|
}
|
||||||
|
pub, err := keys.NewPublicKeyFromBytes(ni.PublicKey(), elliptic.P256())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't parse storage node public key: %w", err)
|
||||||
|
}
|
||||||
|
snList[i].scriptHash = pub.GetScriptHash()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fetchBalances(inv, gas.Hash, snList); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
printBalances(cmd, "\nStorage node balances:", snList)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dumpProxy {
|
if dumpProxy {
|
||||||
if err := printProxyContractBalance(cmd, inv, nnsCs.Hash); err != nil {
|
h, err := nnsResolveHash(inv, nnsCs.Hash, proxyContract+".frostfs")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't get hash of the proxy contract: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyList := []accBalancePair{{scriptHash: h}}
|
||||||
|
if err := fetchBalances(inv, gas.Hash, proxyList); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
printBalances(cmd, "\nProxy contract balance:", proxyList)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dumpAlphabet {
|
if dumpAlphabet {
|
||||||
if err := printAlphabetContractBalances(cmd, c, inv, len(irList), nnsCs.Hash); err != nil {
|
alphaList := make([]accBalancePair, len(irList))
|
||||||
|
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
for i := range alphaList {
|
||||||
|
emit.AppCall(w.BinWriter, nnsCs.Hash, "resolve", callflag.ReadOnly,
|
||||||
|
getAlphabetNNSDomain(i),
|
||||||
|
int64(nns.TXT))
|
||||||
|
}
|
||||||
|
if w.Err != nil {
|
||||||
|
panic(w.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
alphaRes, err := c.InvokeScript(w.Bytes(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't fetch info from NNS: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range alphaList {
|
||||||
|
h, err := parseNNSResolveResult(alphaRes.Stack[i])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't fetch the alphabet contract #%d hash: %w", i, err)
|
||||||
|
}
|
||||||
|
alphaList[i].scriptHash = h
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fetchBalances(inv, gas.Hash, alphaList); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
printBalances(cmd, "\nAlphabet contracts balances:", alphaList)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func printStorageNodeBalances(cmd *cobra.Command, inv *invoker.Invoker, nmHash util.Uint160) error {
|
func fetchIRNodes(c Client, nmHash, desigHash util.Uint160) ([]accBalancePair, error) {
|
||||||
arr, err := unwrap.Array(inv.Call(nmHash, "netmap"))
|
var irList []accBalancePair
|
||||||
if err != nil {
|
|
||||||
return errors.New("can't fetch the list of storage nodes")
|
|
||||||
}
|
|
||||||
|
|
||||||
snList := make([]accBalancePair, len(arr))
|
|
||||||
for i := range arr {
|
|
||||||
node, ok := arr[i].Value().([]stackitem.Item)
|
|
||||||
if !ok || len(node) == 0 {
|
|
||||||
return errors.New("can't parse the list of storage nodes")
|
|
||||||
}
|
|
||||||
bs, err := node[0].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("can't parse the list of storage nodes")
|
|
||||||
}
|
|
||||||
var ni netmap.NodeInfo
|
|
||||||
if err := ni.Unmarshal(bs); err != nil {
|
|
||||||
return fmt.Errorf("can't parse the list of storage nodes: %w", err)
|
|
||||||
}
|
|
||||||
pub, err := keys.NewPublicKeyFromBytes(ni.PublicKey(), elliptic.P256())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't parse storage node public key: %w", err)
|
|
||||||
}
|
|
||||||
snList[i].scriptHash = pub.GetScriptHash()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fetchBalances(inv, gas.Hash, snList); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
printBalances(cmd, "\nStorage node balances:", snList)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func printProxyContractBalance(cmd *cobra.Command, inv *invoker.Invoker, nnsHash util.Uint160) error {
|
|
||||||
h, err := nnsResolveHash(inv, nnsHash, proxyContract+".frostfs")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't get hash of the proxy contract: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
proxyList := []accBalancePair{{scriptHash: h}}
|
|
||||||
if err := fetchBalances(inv, gas.Hash, proxyList); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
printBalances(cmd, "\nProxy contract balance:", proxyList)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func printAlphabetContractBalances(cmd *cobra.Command, c Client, inv *invoker.Invoker, count int, nnsHash util.Uint160) error {
|
|
||||||
alphaList := make([]accBalancePair, count)
|
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
|
||||||
for i := range alphaList {
|
|
||||||
emit.AppCall(w.BinWriter, nnsHash, "resolve", callflag.ReadOnly,
|
|
||||||
getAlphabetNNSDomain(i),
|
|
||||||
int64(nns.TXT))
|
|
||||||
}
|
|
||||||
if w.Err != nil {
|
|
||||||
panic(w.Err)
|
|
||||||
}
|
|
||||||
|
|
||||||
alphaRes, err := c.InvokeScript(w.Bytes(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't fetch info from NNS: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range alphaList {
|
|
||||||
h, err := parseNNSResolveResult(alphaRes.Stack[i])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't fetch the alphabet contract #%d hash: %w", i, err)
|
|
||||||
}
|
|
||||||
alphaList[i].scriptHash = h
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fetchBalances(inv, gas.Hash, alphaList); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
printBalances(cmd, "\nAlphabet contracts balances:", alphaList)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchIRNodes(c Client, desigHash util.Uint160) ([]accBalancePair, error) {
|
|
||||||
inv := invoker.New(c, nil)
|
inv := invoker.New(c, nil)
|
||||||
|
|
||||||
height, err := c.GetBlockCount()
|
if notaryEnabled {
|
||||||
if err != nil {
|
height, err := c.GetBlockCount()
|
||||||
return nil, fmt.Errorf("can't get block height: %w", err)
|
if err != nil {
|
||||||
}
|
return nil, fmt.Errorf("can't get block height: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
arr, err := getDesignatedByRole(inv, desigHash, noderoles.NeoFSAlphabet, height)
|
arr, err := getDesignatedByRole(inv, desigHash, noderoles.NeoFSAlphabet, height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("can't fetch list of IR nodes from the netmap contract")
|
return nil, errors.New("can't fetch list of IR nodes from the netmap contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
irList := make([]accBalancePair, len(arr))
|
irList = make([]accBalancePair, len(arr))
|
||||||
for i := range arr {
|
for i := range arr {
|
||||||
irList[i].scriptHash = arr[i].GetScriptHash()
|
irList[i].scriptHash = arr[i].GetScriptHash()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
arr, err := unwrap.ArrayOfBytes(inv.Call(nmHash, "innerRingList"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("can't fetch list of IR nodes from the netmap contract")
|
||||||
|
}
|
||||||
|
|
||||||
|
irList = make([]accBalancePair, len(arr))
|
||||||
|
for i := range arr {
|
||||||
|
pub, err := keys.NewPublicKeyFromBytes(arr[i], elliptic.P256())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't parse IR node public key: %w", err)
|
||||||
|
}
|
||||||
|
irList[i].scriptHash = pub.GetScriptHash()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return irList, nil
|
return irList, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,12 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
@ -48,24 +48,41 @@ func dumpNetworkConfig(cmd *cobra.Command, _ []string) error {
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
tw := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
|
tw := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
|
||||||
|
|
||||||
m, err := parseConfigFromNetmapContract(arr)
|
for _, param := range arr {
|
||||||
if err != nil {
|
tuple, ok := param.Value().([]stackitem.Item)
|
||||||
return err
|
if !ok || len(tuple) != 2 {
|
||||||
}
|
return errors.New("invalid ListConfig response from netmap contract")
|
||||||
for k, v := range m {
|
}
|
||||||
switch k {
|
|
||||||
case netmap.ContainerFeeConfig, netmap.ContainerAliasFeeConfig,
|
k, err := tuple[0].TryBytes()
|
||||||
netmap.EpochDurationConfig, netmap.IrCandidateFeeConfig,
|
if err != nil {
|
||||||
netmap.MaxObjectSizeConfig, netmap.WithdrawFeeConfig:
|
return errors.New("invalid config key from netmap contract")
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := tuple[1].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return invalidConfigValueErr(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch string(k) {
|
||||||
|
case netmapAuditFeeKey, netmapBasicIncomeRateKey,
|
||||||
|
netmapContainerFeeKey, netmapContainerAliasFeeKey,
|
||||||
|
netmapEigenTrustIterationsKey,
|
||||||
|
netmapEpochKey, netmapInnerRingCandidateFeeKey,
|
||||||
|
netmapMaxObjectSizeKey, netmapWithdrawFeeKey:
|
||||||
nbuf := make([]byte, 8)
|
nbuf := make([]byte, 8)
|
||||||
copy(nbuf[:], v)
|
copy(nbuf[:], v)
|
||||||
n := binary.LittleEndian.Uint64(nbuf)
|
n := binary.LittleEndian.Uint64(nbuf)
|
||||||
_, _ = tw.Write([]byte(fmt.Sprintf("%s:\t%d (int)\n", k, n)))
|
_, _ = tw.Write([]byte(fmt.Sprintf("%s:\t%d (int)\n", k, n)))
|
||||||
case netmap.HomomorphicHashingDisabledKey, netmap.MaintenanceModeAllowedConfig:
|
case netmapEigenTrustAlphaKey:
|
||||||
if len(v) == 0 || len(v) > 1 {
|
_, _ = tw.Write([]byte(fmt.Sprintf("%s:\t%s (str)\n", k, v)))
|
||||||
|
case netmapHomomorphicHashDisabledKey, netmapMaintenanceAllowedKey:
|
||||||
|
vBool, err := tuple[1].TryBool()
|
||||||
|
if err != nil {
|
||||||
return invalidConfigValueErr(k)
|
return invalidConfigValueErr(k)
|
||||||
}
|
}
|
||||||
_, _ = tw.Write([]byte(fmt.Sprintf("%s:\t%t (bool)\n", k, v[0] == 1)))
|
|
||||||
|
_, _ = tw.Write([]byte(fmt.Sprintf("%s:\t%t (bool)\n", k, vBool)))
|
||||||
default:
|
default:
|
||||||
_, _ = tw.Write([]byte(fmt.Sprintf("%s:\t%s (hex)\n", k, hex.EncodeToString(v))))
|
_, _ = tw.Write([]byte(fmt.Sprintf("%s:\t%s (hex)\n", k, hex.EncodeToString(v))))
|
||||||
}
|
}
|
||||||
|
@ -133,14 +150,25 @@ func parseConfigPair(kvStr string, force bool) (key string, val any, err error)
|
||||||
valRaw := v
|
valRaw := v
|
||||||
|
|
||||||
switch key {
|
switch key {
|
||||||
case netmap.ContainerFeeConfig, netmap.ContainerAliasFeeConfig,
|
case netmapAuditFeeKey, netmapBasicIncomeRateKey,
|
||||||
netmap.EpochDurationConfig, netmap.IrCandidateFeeConfig,
|
netmapContainerFeeKey, netmapContainerAliasFeeKey,
|
||||||
netmap.MaxObjectSizeConfig, netmap.WithdrawFeeConfig:
|
netmapEigenTrustIterationsKey,
|
||||||
|
netmapEpochKey, netmapInnerRingCandidateFeeKey,
|
||||||
|
netmapMaxObjectSizeKey, netmapWithdrawFeeKey:
|
||||||
val, err = strconv.ParseInt(valRaw, 10, 64)
|
val, err = strconv.ParseInt(valRaw, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("could not parse %s's value '%s' as int: %w", key, valRaw, err)
|
err = fmt.Errorf("could not parse %s's value '%s' as int: %w", key, valRaw, err)
|
||||||
}
|
}
|
||||||
case netmap.HomomorphicHashingDisabledKey, netmap.MaintenanceModeAllowedConfig:
|
case netmapEigenTrustAlphaKey:
|
||||||
|
// just check that it could
|
||||||
|
// be parsed correctly
|
||||||
|
_, err = strconv.ParseFloat(v, 64)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("could not parse %s's value '%s' as float: %w", key, valRaw, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
val = valRaw
|
||||||
|
case netmapHomomorphicHashDisabledKey, netmapMaintenanceAllowedKey:
|
||||||
val, err = strconv.ParseBool(valRaw)
|
val, err = strconv.ParseBool(valRaw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("could not parse %s's value '%s' as bool: %w", key, valRaw, err)
|
err = fmt.Errorf("could not parse %s's value '%s' as bool: %w", key, valRaw, err)
|
||||||
|
@ -159,6 +187,6 @@ func parseConfigPair(kvStr string, force bool) (key string, val any, err error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func invalidConfigValueErr(key string) error {
|
func invalidConfigValueErr(key []byte) error {
|
||||||
return fmt.Errorf("invalid %s config value from netmap contract", key)
|
return fmt.Errorf("invalid %s config value from netmap contract", key)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
|
@ -41,28 +41,16 @@ func getContainerContractHash(cmd *cobra.Command, inv *invoker.Invoker, c Client
|
||||||
return ch, nil
|
return ch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func iterateContainerList(inv *invoker.Invoker, ch util.Uint160, f func([]byte) error) error {
|
func getContainersList(inv *invoker.Invoker, ch util.Uint160) ([][]byte, error) {
|
||||||
sid, r, err := unwrap.SessionIterator(inv.Call(ch, "containersOf", ""))
|
res, err := inv.Call(ch, "list", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
return nil, fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
||||||
}
|
}
|
||||||
// Nothing bad, except live session on the server, do not report to the user.
|
itm, err := unwrap.Item(res, err)
|
||||||
defer func() { _ = inv.TerminateSession(sid) }()
|
if _, ok := itm.(stackitem.Null); !ok {
|
||||||
|
return unwrap.ArrayOfBytes(res, err)
|
||||||
items, err := inv.TraverseIterator(sid, &r, 0)
|
|
||||||
for err == nil && len(items) != 0 {
|
|
||||||
for j := range items {
|
|
||||||
b, err := items[j].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
|
||||||
}
|
|
||||||
if err := f(b); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
items, err = inv.TraverseIterator(sid, &r, 0)
|
|
||||||
}
|
}
|
||||||
return err
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func dumpContainers(cmd *cobra.Command, _ []string) error {
|
func dumpContainers(cmd *cobra.Command, _ []string) error {
|
||||||
|
@ -83,81 +71,56 @@ func dumpContainers(cmd *cobra.Command, _ []string) error {
|
||||||
return fmt.Errorf("unable to get contaract hash: %w", err)
|
return fmt.Errorf("unable to get contaract hash: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cids, err := getContainersList(inv, ch)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
||||||
|
}
|
||||||
|
|
||||||
isOK, err := getCIDFilterFunc(cmd)
|
isOK, err := getCIDFilterFunc(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := os.OpenFile(filename, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0o660)
|
var containers []*Container
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
_, err = f.Write([]byte{'['})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
written := 0
|
|
||||||
enc := json.NewEncoder(f)
|
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
iterErr := iterateContainerList(inv, ch, func(id []byte) error {
|
for _, id := range cids {
|
||||||
if !isOK(id) {
|
if !isOK(id) {
|
||||||
return nil
|
continue
|
||||||
}
|
}
|
||||||
|
bw.Reset()
|
||||||
cnt, err := dumpSingleContainer(bw, ch, inv, id)
|
emit.AppCall(bw.BinWriter, ch, "get", callflag.All, id)
|
||||||
|
emit.AppCall(bw.BinWriter, ch, "eACL", callflag.All, id)
|
||||||
|
res, err := inv.Run(bw.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("can't get container info: %w", err)
|
||||||
|
}
|
||||||
|
if len(res.Stack) != 2 {
|
||||||
|
return fmt.Errorf("%w: expected 2 items on stack", errInvalidContainerResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writing directly to the file is ok, because json.Encoder does no internal buffering.
|
cnt := new(Container)
|
||||||
if written != 0 {
|
err = cnt.FromStackItem(res.Stack[0])
|
||||||
_, err = f.Write([]byte{','})
|
if err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
written++
|
ea := new(EACL)
|
||||||
return enc.Encode(cnt)
|
err = ea.FromStackItem(res.Stack[1])
|
||||||
})
|
if err != nil {
|
||||||
if iterErr != nil {
|
return fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
||||||
return iterErr
|
}
|
||||||
|
if len(ea.Value) != 0 {
|
||||||
|
cnt.EACL = ea
|
||||||
|
}
|
||||||
|
|
||||||
|
containers = append(containers, cnt)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = f.Write([]byte{']'})
|
out, err := json.Marshal(containers)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't get container info: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
if len(res.Stack) != 2 {
|
return os.WriteFile(filename, out, 0o660)
|
||||||
return nil, fmt.Errorf("%w: expected 2 items on stack", errInvalidContainerResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
cnt := new(Container)
|
|
||||||
err = cnt.FromStackItem(res.Stack[0])
|
|
||||||
if err != nil {
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func listContainers(cmd *cobra.Command, _ []string) error {
|
func listContainers(cmd *cobra.Command, _ []string) error {
|
||||||
|
@ -173,15 +136,20 @@ func listContainers(cmd *cobra.Command, _ []string) error {
|
||||||
return fmt.Errorf("unable to get contaract hash: %w", err)
|
return fmt.Errorf("unable to get contaract hash: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return iterateContainerList(inv, ch, func(id []byte) error {
|
cids, err := getContainersList(inv, ch)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range cids {
|
||||||
var idCnr cid.ID
|
var idCnr cid.ID
|
||||||
err = idCnr.Decode(id)
|
err = idCnr.Decode(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to decode container id: %w", err)
|
return fmt.Errorf("unable to decode container id: %w", err)
|
||||||
}
|
}
|
||||||
cmd.Println(idCnr)
|
cmd.Println(idCnr)
|
||||||
return nil
|
}
|
||||||
})
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func restoreContainers(cmd *cobra.Command, _ []string) error {
|
func restoreContainers(cmd *cobra.Command, _ []string) error {
|
||||||
|
@ -196,14 +164,25 @@ func restoreContainers(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
defer wCtx.close()
|
defer wCtx.close()
|
||||||
|
|
||||||
containers, err := parseContainers(filename)
|
nnsCs, err := wCtx.Client.GetContractStateByID(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("can't get NNS contract state: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ch, err := fetchContainerContractHash(wCtx)
|
ch, err := nnsResolveHash(wCtx.ReadOnlyInvoker, nnsCs.Hash, containerContract+".frostfs")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("can't fetch container contract hash: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't read dump file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var containers []Container
|
||||||
|
err = json.Unmarshal(data, &containers)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't parse dump file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
isOK, err := getCIDFilterFunc(cmd)
|
isOK, err := getCIDFilterFunc(cmd)
|
||||||
|
@ -211,15 +190,6 @@ func restoreContainers(cmd *cobra.Command, _ []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = restoreOrPutContainers(containers, isOK, cmd, wCtx, ch)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return wCtx.awaitTx()
|
|
||||||
}
|
|
||||||
|
|
||||||
func restoreOrPutContainers(containers []Container, isOK func([]byte) bool, cmd *cobra.Command, wCtx *initializeContext, ch util.Uint160) error {
|
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
for _, cnt := range containers {
|
for _, cnt := range containers {
|
||||||
hv := hash.Sha256(cnt.Value)
|
hv := hash.Sha256(cnt.Value)
|
||||||
|
@ -227,18 +197,33 @@ func restoreOrPutContainers(containers []Container, isOK func([]byte) bool, cmd
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
bw.Reset()
|
bw.Reset()
|
||||||
restored, err := isContainerRestored(cmd, wCtx, ch, bw, hv)
|
emit.AppCall(bw.BinWriter, ch, "get", callflag.All, hv.BytesBE())
|
||||||
|
res, err := wCtx.Client.InvokeScript(bw.Bytes(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("can't check if container is already restored: %w", err)
|
||||||
}
|
}
|
||||||
if restored {
|
if len(res.Stack) == 0 {
|
||||||
|
return errors.New("empty stack")
|
||||||
|
}
|
||||||
|
|
||||||
|
old := new(Container)
|
||||||
|
if err := old.FromStackItem(res.Stack[0]); err != nil {
|
||||||
|
return fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
||||||
|
}
|
||||||
|
if len(old.Value) != 0 {
|
||||||
|
var id cid.ID
|
||||||
|
id.SetSHA256(hv)
|
||||||
|
cmd.Printf("Container %s is already deployed.\n", id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
bw.Reset()
|
bw.Reset()
|
||||||
|
emit.AppCall(bw.BinWriter, ch, "put", callflag.All,
|
||||||
putContainer(bw, ch, cnt)
|
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)
|
||||||
|
}
|
||||||
if bw.Err != nil {
|
if bw.Err != nil {
|
||||||
panic(bw.Err)
|
panic(bw.Err)
|
||||||
}
|
}
|
||||||
|
@ -247,67 +232,8 @@ func restoreOrPutContainers(containers []Container, isOK func([]byte) bool, cmd
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func putContainer(bw *io.BufBinWriter, ch util.Uint160, cnt Container) {
|
return wCtx.awaitTx()
|
||||||
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 *initializeContext, containerHash util.Uint160, bw *io.BufBinWriter, hashValue util.Uint256) (bool, error) {
|
|
||||||
emit.AppCall(bw.BinWriter, containerHash, "get", callflag.All, hashValue.BytesBE())
|
|
||||||
res, err := wCtx.Client.InvokeScript(bw.Bytes(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("can't check if container is already restored: %w", err)
|
|
||||||
}
|
|
||||||
if len(res.Stack) == 0 {
|
|
||||||
return false, errors.New("empty stack")
|
|
||||||
}
|
|
||||||
|
|
||||||
old := new(Container)
|
|
||||||
if err := old.FromStackItem(res.Stack[0]); err != nil {
|
|
||||||
return false, fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
|
||||||
}
|
|
||||||
if len(old.Value) != 0 {
|
|
||||||
var id cid.ID
|
|
||||||
id.SetSHA256(hashValue)
|
|
||||||
cmd.Printf("Container %s is already deployed.\n", id)
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseContainers(filename string) ([]Container, error) {
|
|
||||||
data, err := os.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't read dump file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var containers []Container
|
|
||||||
err = json.Unmarshal(data, &containers)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't parse dump file: %w", err)
|
|
||||||
}
|
|
||||||
return containers, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchContainerContractHash(wCtx *initializeContext) (util.Uint160, error) {
|
|
||||||
nnsCs, err := wCtx.Client.GetContractStateByID(1)
|
|
||||||
if err != nil {
|
|
||||||
return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ch, err := nnsResolveHash(wCtx.ReadOnlyInvoker, nnsCs.Hash, containerContract+".frostfs")
|
|
||||||
if err != nil {
|
|
||||||
return util.Uint160{}, fmt.Errorf("can't fetch container contract hash: %w", err)
|
|
||||||
}
|
|
||||||
return ch, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container represents container struct in contract storage.
|
// Container represents container struct in contract storage.
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
"github.com/TrueCloudLab/frostfs-contract/nns"
|
||||||
"github.com/nspcc-dev/neo-go/cli/cmdargs"
|
"github.com/nspcc-dev/neo-go/cli/cmdargs"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
|
@ -100,88 +100,80 @@ func deployContractCmd(cmd *cobra.Command, args []string) error {
|
||||||
cs.Manifest.Name)
|
cs.Manifest.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
writer := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
if err := emitDeploymentArguments(writer.BinWriter, args); err != nil {
|
if err := emitDeploymentArguments(w.BinWriter, args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
emit.Bytes(writer.BinWriter, cs.RawManifest)
|
emit.Bytes(w.BinWriter, cs.RawManifest)
|
||||||
emit.Bytes(writer.BinWriter, cs.RawNEF)
|
emit.Bytes(w.BinWriter, cs.RawNEF)
|
||||||
emit.Int(writer.BinWriter, 3)
|
emit.Int(w.BinWriter, 3)
|
||||||
emit.Opcodes(writer.BinWriter, opcode.PACK)
|
emit.Opcodes(w.BinWriter, opcode.PACK)
|
||||||
emit.AppCallNoArgs(writer.BinWriter, callHash, method, callflag.All)
|
emit.AppCallNoArgs(w.BinWriter, callHash, method, callflag.All)
|
||||||
emit.Opcodes(writer.BinWriter, opcode.DROP) // contract state on stack
|
emit.Opcodes(w.BinWriter, opcode.DROP) // contract state on stack
|
||||||
if !isUpdate {
|
if !isUpdate {
|
||||||
err := registerNNS(nnsCs, c, zone, domain, cs, writer)
|
bw := io.NewBufBinWriter()
|
||||||
|
emit.Instruction(bw.BinWriter, opcode.INITSSLOT, []byte{1})
|
||||||
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "getPrice", callflag.All)
|
||||||
|
emit.Opcodes(bw.BinWriter, opcode.STSFLD0)
|
||||||
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "setPrice", callflag.All, 1)
|
||||||
|
|
||||||
|
start := bw.Len()
|
||||||
|
needRecord := false
|
||||||
|
|
||||||
|
ok, err := c.nnsRootRegistered(nnsCs.Hash, zone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
} else if !ok {
|
||||||
|
needRecord = true
|
||||||
|
|
||||||
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
||||||
|
zone, c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
|
"ops@nspcc.ru", int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
||||||
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
|
|
||||||
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
||||||
|
domain, c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
|
"ops@nspcc.ru", int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
||||||
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
|
} else {
|
||||||
|
s, ok, err := c.nnsRegisterDomainScript(nnsCs.Hash, cs.Hash, domain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
needRecord = !ok
|
||||||
|
if len(s) != 0 {
|
||||||
|
bw.WriteBytes(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if needRecord {
|
||||||
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "deleteRecords", callflag.All, domain, int64(nns.TXT))
|
||||||
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "addRecord", callflag.All,
|
||||||
|
domain, int64(nns.TXT), address.Uint160ToString(cs.Hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
if bw.Err != nil {
|
||||||
|
panic(fmt.Errorf("BUG: can't create deployment script: %w", w.Err))
|
||||||
|
} else if bw.Len() != start {
|
||||||
|
w.WriteBytes(bw.Bytes())
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.PUSH1, opcode.PACK)
|
||||||
|
emit.AppCallNoArgs(w.BinWriter, nnsCs.Hash, "setPrice", callflag.All)
|
||||||
|
|
||||||
|
if needRecord {
|
||||||
|
c.Command.Printf("NNS: Set %s -> %s\n", domain, cs.Hash.StringLE())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if writer.Err != nil {
|
if w.Err != nil {
|
||||||
panic(fmt.Errorf("BUG: can't create deployment script: %w", writer.Err))
|
panic(fmt.Errorf("BUG: can't create deployment script: %w", w.Err))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.sendCommitteeTx(writer.Bytes(), false); err != nil {
|
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return c.awaitTx()
|
return c.awaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerNNS(nnsCs *state.Contract, c *initializeContext, zone string, domain string, cs *contractState, writer *io.BufBinWriter) error {
|
|
||||||
bw := io.NewBufBinWriter()
|
|
||||||
emit.Instruction(bw.BinWriter, opcode.INITSSLOT, []byte{1})
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "getPrice", callflag.All)
|
|
||||||
emit.Opcodes(bw.BinWriter, opcode.STSFLD0)
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "setPrice", callflag.All, 1)
|
|
||||||
|
|
||||||
start := bw.Len()
|
|
||||||
needRecord := false
|
|
||||||
|
|
||||||
ok, err := c.nnsRootRegistered(nnsCs.Hash, zone)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if !ok {
|
|
||||||
needRecord = true
|
|
||||||
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
|
||||||
zone, c.CommitteeAcc.Contract.ScriptHash(),
|
|
||||||
frostfsOpsEmail, int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
|
||||||
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
|
||||||
domain, c.CommitteeAcc.Contract.ScriptHash(),
|
|
||||||
frostfsOpsEmail, int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
|
||||||
} else {
|
|
||||||
s, ok, err := c.nnsRegisterDomainScript(nnsCs.Hash, cs.Hash, domain)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
needRecord = !ok
|
|
||||||
if len(s) != 0 {
|
|
||||||
bw.WriteBytes(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if needRecord {
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "deleteRecords", callflag.All, domain, int64(nns.TXT))
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "addRecord", callflag.All,
|
|
||||||
domain, int64(nns.TXT), address.Uint160ToString(cs.Hash))
|
|
||||||
}
|
|
||||||
|
|
||||||
if bw.Err != nil {
|
|
||||||
panic(fmt.Errorf("BUG: can't create deployment script: %w", writer.Err))
|
|
||||||
} else if bw.Len() != start {
|
|
||||||
writer.WriteBytes(bw.Bytes())
|
|
||||||
emit.Opcodes(writer.BinWriter, opcode.LDSFLD0, opcode.PUSH1, opcode.PACK)
|
|
||||||
emit.AppCallNoArgs(writer.BinWriter, nnsCs.Hash, "setPrice", callflag.All)
|
|
||||||
|
|
||||||
if needRecord {
|
|
||||||
c.Command.Printf("NNS: Set %s -> %s\n", domain, cs.Hash.StringLE())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func emitDeploymentArguments(w *io.BinWriter, args []string) error {
|
func emitDeploymentArguments(w *io.BinWriter, args []string) error {
|
||||||
_, ps, err := cmdargs.ParseParams(args, true)
|
_, ps, err := cmdargs.ParseParams(args, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
40
cmd/frostfs-adm/internal/modules/morph/download.go
Normal file
40
cmd/frostfs-adm/internal/modules/morph/download.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package morph
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/go-github/v39/github"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func downloadContractsFromGithub(cmd *cobra.Command) (io.ReadCloser, error) {
|
||||||
|
gcl := github.NewClient(nil)
|
||||||
|
release, _, err := gcl.Repositories.GetLatestRelease(context.Background(), "nspcc-dev", "frostfs-contract")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't fetch release info: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Printf("Found %s (%s), downloading...\n", release.GetTagName(), release.GetName())
|
||||||
|
|
||||||
|
var url string
|
||||||
|
for _, a := range release.Assets {
|
||||||
|
if strings.HasPrefix(a.GetName(), "frostfs-contract") {
|
||||||
|
url = a.GetBrowserDownloadURL()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if url == "" {
|
||||||
|
return nil, errors.New("can't find contracts archive in release assets")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't fetch contracts archive: %w", err)
|
||||||
|
}
|
||||||
|
return resp.Body, nil
|
||||||
|
}
|
|
@ -7,8 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
"github.com/TrueCloudLab/frostfs-contract/nns"
|
||||||
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||||
|
@ -124,7 +123,7 @@ func dumpCustomZoneHashes(cmd *cobra.Command, nnsHash util.Uint160, zone string,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.HasSuffix(bs, []byte(zone)) || bytes.HasPrefix(bs, []byte(morphClient.NNSGroupKeyName)) {
|
if !bytes.HasSuffix(bs, []byte(zone)) {
|
||||||
// Related https://github.com/nspcc-dev/neofs-contract/issues/316.
|
// Related https://github.com/nspcc-dev/neofs-contract/issues/316.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package morph
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||||
|
@ -14,7 +13,7 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func forceNewEpochCmd(cmd *cobra.Command, _ []string) error {
|
func forceNewEpochCmd(cmd *cobra.Command, args []string) error {
|
||||||
wCtx, err := newInitializeContext(cmd, viper.GetViper())
|
wCtx, err := newInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't to initialize context: %w", err)
|
return fmt.Errorf("can't to initialize context: %w", err)
|
||||||
|
@ -39,14 +38,7 @@ func forceNewEpochCmd(cmd *cobra.Command, _ []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := wCtx.awaitTx(); err != nil {
|
return wCtx.awaitTx()
|
||||||
if strings.Contains(err.Error(), "invalid epoch") {
|
|
||||||
cmd.Println("Epoch has already ticked.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func emitNewEpochCall(bw *io.BufBinWriter, wCtx *initializeContext, nmHash util.Uint160) error {
|
func emitNewEpochCall(bw *io.BufBinWriter, wCtx *initializeContext, nmHash util.Uint160) error {
|
||||||
|
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
"github.com/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||||
|
@ -21,7 +21,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -30,7 +29,7 @@ const (
|
||||||
consensusAccountName = "consensus"
|
consensusAccountName = "consensus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func generateAlphabetCreds(cmd *cobra.Command, _ []string) error {
|
func generateAlphabetCreds(cmd *cobra.Command, args []string) error {
|
||||||
// alphabet size is not part of the config
|
// alphabet size is not part of the config
|
||||||
size, err := cmd.Flags().GetUint(alphabetSizeFlag)
|
size, err := cmd.Flags().GetUint(alphabetSizeFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -39,9 +38,6 @@ func generateAlphabetCreds(cmd *cobra.Command, _ []string) error {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return errors.New("size must be > 0")
|
return errors.New("size must be > 0")
|
||||||
}
|
}
|
||||||
if size > maxAlphabetNodes {
|
|
||||||
return ErrTooManyAlphabetNodes
|
|
||||||
}
|
|
||||||
|
|
||||||
v := viper.GetViper()
|
v := viper.GetViper()
|
||||||
walletDir := config.ResolveHomePath(viper.GetString(alphabetWalletsFlag))
|
walletDir := config.ResolveHomePath(viper.GetString(alphabetWalletsFlag))
|
||||||
|
@ -96,31 +92,28 @@ func initializeWallets(v *viper.Viper, walletDir string, size int) ([]string, er
|
||||||
pubs[i] = w.Accounts[0].PrivateKey().PublicKey()
|
pubs[i] = w.Accounts[0].PrivateKey().PublicKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
var errG errgroup.Group
|
|
||||||
|
|
||||||
// Create committee account with N/2+1 multi-signature.
|
// Create committee account with N/2+1 multi-signature.
|
||||||
majCount := smartcontract.GetMajorityHonestNodeCount(size)
|
majCount := smartcontract.GetMajorityHonestNodeCount(size)
|
||||||
|
for i, w := range wallets {
|
||||||
|
if err := addMultisigAccount(w, majCount, committeeAccountName, passwords[i], pubs); err != nil {
|
||||||
|
return nil, fmt.Errorf("can't create committee account: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create consensus account with 2*N/3+1 multi-signature.
|
// Create consensus account with 2*N/3+1 multi-signature.
|
||||||
bftCount := smartcontract.GetDefaultHonestNodeCount(size)
|
bftCount := smartcontract.GetDefaultHonestNodeCount(size)
|
||||||
for i := range wallets {
|
for i, w := range wallets {
|
||||||
i := i
|
if err := addMultisigAccount(w, bftCount, consensusAccountName, passwords[i], pubs); err != nil {
|
||||||
ps := pubs.Copy()
|
return nil, fmt.Errorf("can't create consensus account: %w", err)
|
||||||
errG.Go(func() error {
|
}
|
||||||
if err := addMultisigAccount(wallets[i], majCount, committeeAccountName, passwords[i], ps); err != nil {
|
|
||||||
return fmt.Errorf("can't create committee account: %w", err)
|
|
||||||
}
|
|
||||||
if err := addMultisigAccount(wallets[i], bftCount, consensusAccountName, passwords[i], ps); err != nil {
|
|
||||||
return fmt.Errorf("can't create consentus account: %w", err)
|
|
||||||
}
|
|
||||||
if err := wallets[i].SavePretty(); err != nil {
|
|
||||||
return fmt.Errorf("can't save wallet: %w", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
if err := errG.Wait(); err != nil {
|
|
||||||
return nil, err
|
for _, w := range wallets {
|
||||||
|
if err := w.SavePretty(); err != nil {
|
||||||
|
return nil, fmt.Errorf("can't save wallet: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return passwords, nil
|
return passwords, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,9 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
"github.com/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||||
"github.com/nspcc-dev/neo-go/cli/input"
|
"github.com/nspcc-dev/neo-go/cli/input"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
@ -72,32 +71,24 @@ func TestGenerateAlphabet(t *testing.T) {
|
||||||
buf.WriteString(testContractPassword + "\r")
|
buf.WriteString(testContractPassword + "\r")
|
||||||
require.NoError(t, generateAlphabetCreds(generateAlphabetCmd, nil))
|
require.NoError(t, generateAlphabetCreds(generateAlphabetCmd, nil))
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
for i := uint64(0); i < size; i++ {
|
for i := uint64(0); i < size; i++ {
|
||||||
i := i
|
p := filepath.Join(walletDir, innerring.GlagoliticLetter(i).String()+".json")
|
||||||
wg.Add(1)
|
w, err := wallet.NewWalletFromFile(p)
|
||||||
go func() {
|
require.NoError(t, err, "wallet doesn't exist")
|
||||||
defer wg.Done()
|
require.Equal(t, 3, len(w.Accounts), "not all accounts were created")
|
||||||
p := filepath.Join(walletDir, innerring.GlagoliticLetter(i).String()+".json")
|
for _, a := range w.Accounts {
|
||||||
w, err := wallet.NewWalletFromFile(p)
|
err := a.Decrypt(strconv.FormatUint(i, 10), keys.NEP2ScryptParams())
|
||||||
require.NoError(t, err, "wallet doesn't exist")
|
require.NoError(t, err, "can't decrypt account")
|
||||||
require.Equal(t, 3, len(w.Accounts), "not all accounts were created")
|
switch a.Label {
|
||||||
|
case consensusAccountName:
|
||||||
for _, a := range w.Accounts {
|
require.Equal(t, smartcontract.GetDefaultHonestNodeCount(size), len(a.Contract.Parameters))
|
||||||
err := a.Decrypt(strconv.FormatUint(i, 10), keys.NEP2ScryptParams())
|
case committeeAccountName:
|
||||||
require.NoError(t, err, "can't decrypt account")
|
require.Equal(t, smartcontract.GetMajorityHonestNodeCount(size), len(a.Contract.Parameters))
|
||||||
switch a.Label {
|
default:
|
||||||
case consensusAccountName:
|
require.Equal(t, singleAccountName, a.Label)
|
||||||
require.Equal(t, smartcontract.GetDefaultHonestNodeCount(size), len(a.Contract.Parameters))
|
|
||||||
case committeeAccountName:
|
|
||||||
require.Equal(t, smartcontract.GetMajorityHonestNodeCount(size), len(a.Contract.Parameters))
|
|
||||||
default:
|
|
||||||
require.Equal(t, singleAccountName, a.Label)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
}
|
||||||
}
|
}
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
t.Run("check contract group wallet", func(t *testing.T) {
|
t.Run("check contract group wallet", func(t *testing.T) {
|
||||||
p := filepath.Join(walletDir, contractWalletFilename)
|
p := filepath.Join(walletDir, contractWalletFilename)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
"github.com/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||||
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
morphClient "github.com/TrueCloudLab/frostfs-node/pkg/morph/client"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
@ -23,13 +23,6 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// maxAlphabetNodes is the maximum number of candidates allowed, which is currently limited by the size
|
|
||||||
// of the invocation script.
|
|
||||||
// See: https://github.com/nspcc-dev/neo-go/blob/740488f7f35e367eaa99a71c0a609c315fe2b0fc/pkg/core/transaction/witness.go#L10
|
|
||||||
maxAlphabetNodes = 22
|
|
||||||
)
|
|
||||||
|
|
||||||
type cache struct {
|
type cache struct {
|
||||||
nnsCs *state.Contract
|
nnsCs *state.Contract
|
||||||
groupKey *keys.PublicKey
|
groupKey *keys.PublicKey
|
||||||
|
@ -52,9 +45,7 @@ type initializeContext struct {
|
||||||
ContractPath string
|
ContractPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrTooManyAlphabetNodes = fmt.Errorf("too many alphabet nodes (maximum allowed is %d)", maxAlphabetNodes)
|
func initializeSideChainCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
func initializeSideChainCmd(cmd *cobra.Command, _ []string) error {
|
|
||||||
initCtx, err := newInitializeContext(cmd, viper.GetViper())
|
initCtx, err := newInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("initialization error: %w", err)
|
return fmt.Errorf("initialization error: %w", err)
|
||||||
|
@ -100,7 +91,11 @@ func initializeSideChainCmd(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Println("Stage 7: set addresses in NNS.")
|
cmd.Println("Stage 7: set addresses in NNS.")
|
||||||
return initCtx.setNNS()
|
if err := initCtx.setNNS(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *initializeContext) close() {
|
func (c *initializeContext) close() {
|
||||||
|
@ -120,21 +115,27 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(wallets) > maxAlphabetNodes {
|
|
||||||
return nil, ErrTooManyAlphabetNodes
|
|
||||||
}
|
|
||||||
|
|
||||||
needContracts := cmd.Name() == "update-contracts" || cmd.Name() == "init"
|
needContracts := cmd.Name() == "update-contracts" || cmd.Name() == "init"
|
||||||
|
|
||||||
var w *wallet.Wallet
|
var w *wallet.Wallet
|
||||||
w, err = getWallet(cmd, v, needContracts, walletDir)
|
if needContracts {
|
||||||
if err != nil {
|
w, err = openContractWallet(v, cmd, walletDir)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := createClient(cmd, v, wallets)
|
var c Client
|
||||||
|
if v.GetString(localDumpFlag) != "" {
|
||||||
|
if v.GetString(endpointFlag) != "" {
|
||||||
|
return nil, fmt.Errorf("`%s` and `%s` flags are mutually exclusive", endpointFlag, localDumpFlag)
|
||||||
|
}
|
||||||
|
c, err = newLocalClient(cmd, v, wallets)
|
||||||
|
} else {
|
||||||
|
c, err = getN3Client(v)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("can't create N3 client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
committeeAcc, err := getWalletAccount(wallets[0], committeeAccountName)
|
committeeAcc, err := getWalletAccount(wallets[0], committeeAccountName)
|
||||||
|
@ -147,22 +148,35 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex
|
||||||
return nil, fmt.Errorf("can't find consensus account: %w", err)
|
return nil, fmt.Errorf("can't find consensus account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateInit(cmd); err != nil {
|
var ctrPath string
|
||||||
return nil, err
|
if cmd.Name() == "init" {
|
||||||
|
if viper.GetInt64(epochDurationInitFlag) <= 0 {
|
||||||
|
return nil, fmt.Errorf("epoch duration must be positive")
|
||||||
|
}
|
||||||
|
|
||||||
|
if viper.GetInt64(maxObjectSizeInitFlag) <= 0 {
|
||||||
|
return nil, fmt.Errorf("max object size must be positive")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrPath, err := getContractsPath(cmd, needContracts)
|
if needContracts {
|
||||||
if err != nil {
|
ctrPath, err = cmd.Flags().GetString(contractsInitFlag)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid contracts path: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkNotaryEnabled(c); err != nil {
|
if err := checkNotaryEnabled(c); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
accounts, err := createWalletAccounts(wallets)
|
accounts := make([]*wallet.Account, len(wallets))
|
||||||
if err != nil {
|
for i, w := range wallets {
|
||||||
return nil, err
|
acc, err := getWalletAccount(w, singleAccountName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("wallet %s is invalid (no single account): %w", w.Path(), err)
|
||||||
|
}
|
||||||
|
accounts[i] = acc
|
||||||
}
|
}
|
||||||
|
|
||||||
cliCtx, err := defaultClientContext(c, committeeAcc)
|
cliCtx, err := defaultClientContext(c, committeeAcc)
|
||||||
|
@ -192,69 +206,6 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex
|
||||||
return initCtx, nil
|
return initCtx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateInit(cmd *cobra.Command) error {
|
|
||||||
if cmd.Name() != "init" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if viper.GetInt64(epochDurationInitFlag) <= 0 {
|
|
||||||
return fmt.Errorf("epoch duration must be positive")
|
|
||||||
}
|
|
||||||
|
|
||||||
if viper.GetInt64(maxObjectSizeInitFlag) <= 0 {
|
|
||||||
return fmt.Errorf("max object size must be positive")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createClient(cmd *cobra.Command, v *viper.Viper, wallets []*wallet.Wallet) (Client, error) {
|
|
||||||
var c Client
|
|
||||||
var err error
|
|
||||||
if ldf := cmd.Flags().Lookup(localDumpFlag); ldf != nil && ldf.Changed {
|
|
||||||
if cmd.Flags().Changed(endpointFlag) {
|
|
||||||
return nil, fmt.Errorf("`%s` and `%s` flags are mutually exclusive", endpointFlag, localDumpFlag)
|
|
||||||
}
|
|
||||||
c, err = newLocalClient(cmd, v, wallets, ldf.Value.String())
|
|
||||||
} else {
|
|
||||||
c, err = getN3Client(v)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't create N3 client: %w", err)
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getWallet(cmd *cobra.Command, v *viper.Viper, needContracts bool, walletDir string) (*wallet.Wallet, error) {
|
|
||||||
if !needContracts {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return openContractWallet(v, cmd, walletDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getContractsPath(cmd *cobra.Command, needContracts bool) (string, error) {
|
|
||||||
if !needContracts {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrPath, err := cmd.Flags().GetString(contractsInitFlag)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("invalid contracts path: %w", err)
|
|
||||||
}
|
|
||||||
return ctrPath, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createWalletAccounts(wallets []*wallet.Wallet) ([]*wallet.Account, error) {
|
|
||||||
accounts := make([]*wallet.Account, len(wallets))
|
|
||||||
for i, w := range wallets {
|
|
||||||
acc, err := getWalletAccount(w, singleAccountName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("wallet %s is invalid (no single account): %w", w.Path(), err)
|
|
||||||
}
|
|
||||||
accounts[i] = acc
|
|
||||||
}
|
|
||||||
return accounts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func openAlphabetWallets(v *viper.Viper, walletDir string) ([]*wallet.Wallet, error) {
|
func openAlphabetWallets(v *viper.Viper, walletDir string) ([]*wallet.Wallet, error) {
|
||||||
walletFiles, err := os.ReadDir(walletDir)
|
walletFiles, err := os.ReadDir(walletDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -12,11 +12,10 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
"github.com/TrueCloudLab/frostfs-contract/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
"github.com/TrueCloudLab/frostfs-contract/nns"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
"github.com/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||||
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
morphClient "github.com/TrueCloudLab/frostfs-node/pkg/morph/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
|
@ -24,7 +23,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
@ -32,8 +30,8 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -41,20 +39,44 @@ const (
|
||||||
frostfsContract = "frostfs" // not deployed in side-chain.
|
frostfsContract = "frostfs" // not deployed in side-chain.
|
||||||
processingContract = "processing" // not deployed in side-chain.
|
processingContract = "processing" // not deployed in side-chain.
|
||||||
alphabetContract = "alphabet"
|
alphabetContract = "alphabet"
|
||||||
|
auditContract = "audit"
|
||||||
balanceContract = "balance"
|
balanceContract = "balance"
|
||||||
containerContract = "container"
|
containerContract = "container"
|
||||||
frostfsIDContract = "frostfsid"
|
frostfsIDContract = "frostfsid"
|
||||||
netmapContract = "netmap"
|
netmapContract = "netmap"
|
||||||
proxyContract = "proxy"
|
proxyContract = "proxy"
|
||||||
|
reputationContract = "reputation"
|
||||||
|
subnetContract = "subnet"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
netmapEpochKey = "EpochDuration"
|
||||||
|
netmapMaxObjectSizeKey = "MaxObjectSize"
|
||||||
|
netmapAuditFeeKey = "AuditFee"
|
||||||
|
netmapContainerFeeKey = "ContainerFee"
|
||||||
|
netmapContainerAliasFeeKey = "ContainerAliasFee"
|
||||||
|
netmapEigenTrustIterationsKey = "EigenTrustIterations"
|
||||||
|
netmapEigenTrustAlphaKey = "EigenTrustAlpha"
|
||||||
|
netmapBasicIncomeRateKey = "BasicIncomeRate"
|
||||||
|
netmapInnerRingCandidateFeeKey = "InnerRingCandidateFee"
|
||||||
|
netmapWithdrawFeeKey = "WithdrawFee"
|
||||||
|
netmapHomomorphicHashDisabledKey = "HomomorphicHashingDisabled"
|
||||||
|
netmapMaintenanceAllowedKey = "MaintenanceModeAllowed"
|
||||||
|
|
||||||
|
defaultEigenTrustIterations = 4
|
||||||
|
defaultEigenTrustAlpha = "0.1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
contractList = []string{
|
contractList = []string{
|
||||||
|
auditContract,
|
||||||
balanceContract,
|
balanceContract,
|
||||||
containerContract,
|
containerContract,
|
||||||
frostfsIDContract,
|
frostfsIDContract,
|
||||||
netmapContract,
|
netmapContract,
|
||||||
proxyContract,
|
proxyContract,
|
||||||
|
reputationContract,
|
||||||
|
subnetContract,
|
||||||
}
|
}
|
||||||
|
|
||||||
fullContractList = append([]string{
|
fullContractList = append([]string{
|
||||||
|
@ -63,17 +85,6 @@ var (
|
||||||
nnsContract,
|
nnsContract,
|
||||||
alphabetContract,
|
alphabetContract,
|
||||||
}, contractList...)
|
}, contractList...)
|
||||||
|
|
||||||
netmapConfigKeys = []string{
|
|
||||||
netmap.EpochDurationConfig,
|
|
||||||
netmap.MaxObjectSizeConfig,
|
|
||||||
netmap.ContainerFeeConfig,
|
|
||||||
netmap.ContainerAliasFeeConfig,
|
|
||||||
netmap.IrCandidateFeeConfig,
|
|
||||||
netmap.WithdrawFeeConfig,
|
|
||||||
netmap.HomomorphicHashingDisabledKey,
|
|
||||||
netmap.MaintenanceModeAllowedConfig,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type contractState struct {
|
type contractState struct {
|
||||||
|
@ -156,6 +167,8 @@ func (c *initializeContext) updateContracts() error {
|
||||||
|
|
||||||
w := io2.NewBufBinWriter()
|
w := io2.NewBufBinWriter()
|
||||||
|
|
||||||
|
var keysParam []any
|
||||||
|
|
||||||
// Update script size for a single-node committee is close to the maximum allowed size of 65535.
|
// Update script size for a single-node committee is close to the maximum allowed size of 65535.
|
||||||
// Because of this we want to reuse alphabet contract NEF and manifest for different updates.
|
// Because of this we want to reuse alphabet contract NEF and manifest for different updates.
|
||||||
// The generated script is as following.
|
// The generated script is as following.
|
||||||
|
@ -169,36 +182,42 @@ func (c *initializeContext) updateContracts() error {
|
||||||
emit.Bytes(w.BinWriter, alphaCs.RawNEF)
|
emit.Bytes(w.BinWriter, alphaCs.RawNEF)
|
||||||
emit.Opcodes(w.BinWriter, opcode.STSFLD0)
|
emit.Opcodes(w.BinWriter, opcode.STSFLD0)
|
||||||
|
|
||||||
keysParam, err := c.deployAlphabetAccounts(nnsHash, w, alphaCs)
|
baseGroups := alphaCs.Manifest.Groups
|
||||||
if err != nil {
|
|
||||||
return err
|
// alphabet contracts should be deployed by individual nodes to get different hashes.
|
||||||
|
for i, acc := range c.Accounts {
|
||||||
|
ctrHash, err := nnsResolveHash(c.ReadOnlyInvoker, nnsHash, getAlphabetNNSDomain(i))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't resolve hash for contract update: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keysParam = append(keysParam, acc.PrivateKey().PublicKey().Bytes())
|
||||||
|
|
||||||
|
params := c.getAlphabetDeployItems(i, len(c.Wallets))
|
||||||
|
emit.Array(w.BinWriter, params...)
|
||||||
|
|
||||||
|
alphaCs.Manifest.Groups = baseGroups
|
||||||
|
err = c.addManifestGroup(ctrHash, alphaCs)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't sign manifest group: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
emit.Bytes(w.BinWriter, alphaCs.RawManifest)
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.LDSFLD0)
|
||||||
|
emit.Int(w.BinWriter, 3)
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.PACK)
|
||||||
|
emit.AppCallNoArgs(w.BinWriter, ctrHash, updateMethodName, callflag.All)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
||||||
|
if !strings.Contains(err.Error(), common.ErrAlreadyUpdated) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Command.Println("Alphabet contracts are already updated.")
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Reset()
|
w.Reset()
|
||||||
|
|
||||||
if err = c.deployOrUpdateContracts(w, nnsHash, keysParam); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
groupKey := c.ContractWallet.Accounts[0].PrivateKey().PublicKey()
|
|
||||||
_, _, err = c.emitUpdateNNSGroupScript(w, nnsHash, groupKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Command.Printf("NNS: Set %s -> %s\n", morphClient.NNSGroupKeyName, hex.EncodeToString(groupKey.Bytes()))
|
|
||||||
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.LDSFLD0)
|
|
||||||
emit.Int(w.BinWriter, 1)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
|
||||||
emit.AppCallNoArgs(w.BinWriter, nnsHash, "setPrice", callflag.All)
|
|
||||||
|
|
||||||
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return c.awaitTx()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *initializeContext) deployOrUpdateContracts(w *io2.BufBinWriter, nnsHash util.Uint160, keysParam []any) error {
|
|
||||||
emit.Instruction(w.BinWriter, opcode.INITSSLOT, []byte{1})
|
emit.Instruction(w.BinWriter, opcode.INITSSLOT, []byte{1})
|
||||||
emit.AppCall(w.BinWriter, nnsHash, "getPrice", callflag.All)
|
emit.AppCall(w.BinWriter, nnsHash, "getPrice", callflag.All)
|
||||||
emit.Opcodes(w.BinWriter, opcode.STSFLD0)
|
emit.Opcodes(w.BinWriter, opcode.STSFLD0)
|
||||||
|
@ -228,7 +247,7 @@ func (c *initializeContext) deployOrUpdateContracts(w *io2.BufBinWriter, nnsHash
|
||||||
invokeHash = ctrHash
|
invokeHash = ctrHash
|
||||||
}
|
}
|
||||||
|
|
||||||
params := getContractDeployParameters(cs, c.getContractDeployData(ctrName, keysParam, updateMethodName))
|
params := getContractDeployParameters(cs, c.getContractDeployData(ctrName, keysParam))
|
||||||
res, err := c.CommitteeAct.MakeCall(invokeHash, method, params...)
|
res, err := c.CommitteeAct.MakeCall(invokeHash, method, params...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if method != updateMethodName || !strings.Contains(err.Error(), common.ErrAlreadyUpdated) {
|
if method != updateMethodName || !strings.Contains(err.Error(), common.ErrAlreadyUpdated) {
|
||||||
|
@ -258,46 +277,23 @@ func (c *initializeContext) deployOrUpdateContracts(w *io2.BufBinWriter, nnsHash
|
||||||
c.Command.Printf("NNS: Set %s -> %s\n", domain, cs.Hash.StringLE())
|
c.Command.Printf("NNS: Set %s -> %s\n", domain, cs.Hash.StringLE())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *initializeContext) deployAlphabetAccounts(nnsHash util.Uint160, w *io2.BufBinWriter, alphaCs *contractState) ([]any, error) {
|
groupKey := c.ContractWallet.Accounts[0].PrivateKey().PublicKey()
|
||||||
var keysParam []any
|
_, _, err = c.emitUpdateNNSGroupScript(w, nnsHash, groupKey)
|
||||||
|
if err != nil {
|
||||||
baseGroups := alphaCs.Manifest.Groups
|
return err
|
||||||
|
|
||||||
// alphabet contracts should be deployed by individual nodes to get different hashes.
|
|
||||||
for i, acc := range c.Accounts {
|
|
||||||
ctrHash, err := nnsResolveHash(c.ReadOnlyInvoker, nnsHash, getAlphabetNNSDomain(i))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't resolve hash for contract update: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
keysParam = append(keysParam, acc.PrivateKey().PublicKey().Bytes())
|
|
||||||
|
|
||||||
params := c.getAlphabetDeployItems(i, len(c.Wallets))
|
|
||||||
emit.Array(w.BinWriter, params...)
|
|
||||||
|
|
||||||
alphaCs.Manifest.Groups = baseGroups
|
|
||||||
err = c.addManifestGroup(ctrHash, alphaCs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't sign manifest group: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
emit.Bytes(w.BinWriter, alphaCs.RawManifest)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.LDSFLD0)
|
|
||||||
emit.Int(w.BinWriter, 3)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
|
||||||
emit.AppCallNoArgs(w.BinWriter, ctrHash, updateMethodName, callflag.All)
|
|
||||||
}
|
}
|
||||||
|
c.Command.Printf("NNS: Set %s -> %s\n", morphClient.NNSGroupKeyName, hex.EncodeToString(groupKey.Bytes()))
|
||||||
|
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.LDSFLD0)
|
||||||
|
emit.Int(w.BinWriter, 1)
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.PACK)
|
||||||
|
emit.AppCallNoArgs(w.BinWriter, nnsHash, "setPrice", callflag.All)
|
||||||
|
|
||||||
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
||||||
if !strings.Contains(err.Error(), common.ErrAlreadyUpdated) {
|
return err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c.Command.Println("Alphabet contracts are already updated.")
|
|
||||||
}
|
}
|
||||||
|
return c.awaitTx()
|
||||||
return keysParam, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *initializeContext) deployContracts() error {
|
func (c *initializeContext) deployContracts() error {
|
||||||
|
@ -351,7 +347,7 @@ func (c *initializeContext) deployContracts() error {
|
||||||
return fmt.Errorf("can't sign manifest group: %v", err)
|
return fmt.Errorf("can't sign manifest group: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
params := getContractDeployParameters(cs, c.getContractDeployData(ctrName, keysParam, deployMethodName))
|
params := getContractDeployParameters(cs, c.getContractDeployData(ctrName, keysParam))
|
||||||
res, err := c.CommitteeAct.MakeCall(management.Hash, deployMethodName, params...)
|
res, err := c.CommitteeAct.MakeCall(management.Hash, deployMethodName, params...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't deploy %s contract: %w", ctrName, err)
|
return fmt.Errorf("can't deploy %s contract: %w", ctrName, err)
|
||||||
|
@ -397,9 +393,11 @@ func (c *initializeContext) readContracts(names []string) error {
|
||||||
} else {
|
} else {
|
||||||
var r io.ReadCloser
|
var r io.ReadCloser
|
||||||
if c.ContractPath == "" {
|
if c.ContractPath == "" {
|
||||||
return errors.New("contracts flag is missing")
|
c.Command.Println("Contracts flag is missing, latest release will be fetched from Github.")
|
||||||
|
r, err = downloadContractsFromGithub(c.Command)
|
||||||
|
} else {
|
||||||
|
r, err = os.Open(c.ContractPath)
|
||||||
}
|
}
|
||||||
r, err = os.Open(c.ContractPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't open contracts archive: %w", err)
|
return fmt.Errorf("can't open contracts archive: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -516,7 +514,7 @@ func getContractDeployParameters(cs *contractState, deployData []any) []any {
|
||||||
return []any{cs.RawNEF, cs.RawManifest, deployData}
|
return []any{cs.RawNEF, cs.RawManifest, deployData}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *initializeContext) getContractDeployData(ctrName string, keysParam []any, method string) []any {
|
func (c *initializeContext) getContractDeployData(ctrName string, keysParam []any) []any {
|
||||||
items := make([]any, 1, 6)
|
items := make([]any, 1, 6)
|
||||||
items[0] = false // notaryDisabled is false
|
items[0] = false // notaryDisabled is false
|
||||||
|
|
||||||
|
@ -529,6 +527,8 @@ func (c *initializeContext) getContractDeployData(ctrName string, keysParam []an
|
||||||
case processingContract:
|
case processingContract:
|
||||||
items = append(items, c.Contracts[frostfsContract].Hash)
|
items = append(items, c.Contracts[frostfsContract].Hash)
|
||||||
return items[1:] // no notary info
|
return items[1:] // no notary info
|
||||||
|
case auditContract:
|
||||||
|
items = append(items, c.Contracts[netmapContract].Hash)
|
||||||
case balanceContract:
|
case balanceContract:
|
||||||
items = append(items,
|
items = append(items,
|
||||||
c.Contracts[netmapContract].Hash,
|
c.Contracts[netmapContract].Hash,
|
||||||
|
@ -551,31 +551,20 @@ func (c *initializeContext) getContractDeployData(ctrName string, keysParam []an
|
||||||
c.Contracts[netmapContract].Hash,
|
c.Contracts[netmapContract].Hash,
|
||||||
c.Contracts[containerContract].Hash)
|
c.Contracts[containerContract].Hash)
|
||||||
case netmapContract:
|
case netmapContract:
|
||||||
md := getDefaultNetmapContractConfigMap()
|
configParam := []any{
|
||||||
if method == updateMethodName {
|
netmapEpochKey, viper.GetInt64(epochDurationInitFlag),
|
||||||
arr, err := c.getNetConfigFromNetmapContract()
|
netmapMaxObjectSizeKey, viper.GetInt64(maxObjectSizeInitFlag),
|
||||||
if err != nil {
|
netmapAuditFeeKey, viper.GetInt64(auditFeeInitFlag),
|
||||||
panic(err)
|
netmapContainerFeeKey, viper.GetInt64(containerFeeInitFlag),
|
||||||
}
|
netmapContainerAliasFeeKey, viper.GetInt64(containerAliasFeeInitFlag),
|
||||||
m, err := parseConfigFromNetmapContract(arr)
|
netmapEigenTrustIterationsKey, int64(defaultEigenTrustIterations),
|
||||||
if err != nil {
|
netmapEigenTrustAlphaKey, defaultEigenTrustAlpha,
|
||||||
panic(err)
|
netmapBasicIncomeRateKey, viper.GetInt64(incomeRateInitFlag),
|
||||||
}
|
netmapInnerRingCandidateFeeKey, viper.GetInt64(candidateFeeInitFlag),
|
||||||
for k, v := range m {
|
netmapWithdrawFeeKey, viper.GetInt64(withdrawFeeInitFlag),
|
||||||
for _, key := range netmapConfigKeys {
|
netmapHomomorphicHashDisabledKey, viper.GetBool(homomorphicHashDisabledInitFlag),
|
||||||
if k == key {
|
netmapMaintenanceAllowedKey, viper.GetBool(maintenanceModeAllowedInitFlag),
|
||||||
md[k] = v
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var configParam []any
|
|
||||||
for k, v := range md {
|
|
||||||
configParam = append(configParam, k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
items = append(items,
|
items = append(items,
|
||||||
c.Contracts[balanceContract].Hash,
|
c.Contracts[balanceContract].Hash,
|
||||||
c.Contracts[containerContract].Hash,
|
c.Contracts[containerContract].Hash,
|
||||||
|
@ -583,28 +572,14 @@ func (c *initializeContext) getContractDeployData(ctrName string, keysParam []an
|
||||||
configParam)
|
configParam)
|
||||||
case proxyContract:
|
case proxyContract:
|
||||||
items = nil
|
items = nil
|
||||||
|
case reputationContract:
|
||||||
|
case subnetContract:
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("invalid contract name: %s", ctrName))
|
panic(fmt.Sprintf("invalid contract name: %s", ctrName))
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *initializeContext) getNetConfigFromNetmapContract() ([]stackitem.Item, error) {
|
|
||||||
cs, err := c.Client.GetContractStateByID(1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("NNS is not yet deployed: %w", err)
|
|
||||||
}
|
|
||||||
nmHash, err := nnsResolveHash(c.ReadOnlyInvoker, cs.Hash, netmapContract+".frostfs")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't get netmap contract hash: %w", err)
|
|
||||||
}
|
|
||||||
arr, err := unwrap.Array(c.ReadOnlyInvoker.Call(nmHash, "listConfig"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't fetch list of network config keys from the netmap contract")
|
|
||||||
}
|
|
||||||
return arr, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *initializeContext) getAlphabetDeployItems(i, n int) []any {
|
func (c *initializeContext) getAlphabetDeployItems(i, n int) []any {
|
||||||
items := make([]any, 6)
|
items := make([]any, 6)
|
||||||
items[0] = false
|
items[0] = false
|
||||||
|
|
|
@ -7,15 +7,14 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
"github.com/TrueCloudLab/frostfs-contract/nns"
|
||||||
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
morphClient "github.com/TrueCloudLab/frostfs-node/pkg/morph/client"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
nnsClient "github.com/nspcc-dev/neo-go/pkg/rpcclient/nns"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
@ -27,8 +26,6 @@ import (
|
||||||
|
|
||||||
const defaultExpirationTime = 10 * 365 * 24 * time.Hour / time.Second
|
const defaultExpirationTime = 10 * 365 * 24 * time.Hour / time.Second
|
||||||
|
|
||||||
const frostfsOpsEmail = "ops@frostfs.info"
|
|
||||||
|
|
||||||
func (c *initializeContext) setNNS() error {
|
func (c *initializeContext) setNNS() error {
|
||||||
nnsCs, err := c.Client.GetContractStateByID(1)
|
nnsCs, err := c.Client.GetContractStateByID(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -42,7 +39,7 @@ func (c *initializeContext) setNNS() error {
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
||||||
"frostfs", c.CommitteeAcc.Contract.ScriptHash(),
|
"frostfs", c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
frostfsOpsEmail, int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
"ops@nspcc.ru", int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
if err := c.sendCommitteeTx(bw.Bytes(), true); err != nil {
|
if err := c.sendCommitteeTx(bw.Bytes(), true); err != nil {
|
||||||
return fmt.Errorf("can't add domain root to NNS: %w", err)
|
return fmt.Errorf("can't add domain root to NNS: %w", err)
|
||||||
|
@ -85,13 +82,13 @@ func (c *initializeContext) setNNS() error {
|
||||||
|
|
||||||
func (c *initializeContext) updateNNSGroup(nnsHash util.Uint160, pub *keys.PublicKey) error {
|
func (c *initializeContext) updateNNSGroup(nnsHash util.Uint160, pub *keys.PublicKey) error {
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
keyAlreadyAdded, domainRegCodeEmitted, err := c.emitUpdateNNSGroupScript(bw, nnsHash, pub)
|
needUpdate, needRegister, err := c.emitUpdateNNSGroupScript(bw, nnsHash, pub)
|
||||||
if keyAlreadyAdded || err != nil {
|
if !needUpdate || err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
script := bw.Bytes()
|
script := bw.Bytes()
|
||||||
if domainRegCodeEmitted {
|
if needRegister {
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.Instruction(w.BinWriter, opcode.INITSSLOT, []byte{1})
|
emit.Instruction(w.BinWriter, opcode.INITSSLOT, []byte{1})
|
||||||
wrapRegisterScriptWithPrice(w, nnsHash, script)
|
wrapRegisterScriptWithPrice(w, nnsHash, script)
|
||||||
|
@ -124,7 +121,7 @@ func (c *initializeContext) emitUpdateNNSGroupScript(bw *io.BufBinWriter, nnsHas
|
||||||
if isAvail {
|
if isAvail {
|
||||||
emit.AppCall(bw.BinWriter, nnsHash, "register", callflag.All,
|
emit.AppCall(bw.BinWriter, nnsHash, "register", callflag.All,
|
||||||
morphClient.NNSGroupKeyName, c.CommitteeAcc.Contract.ScriptHash(),
|
morphClient.NNSGroupKeyName, c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
frostfsOpsEmail, int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
"ops@nspcc.ru", int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +169,7 @@ func (c *initializeContext) nnsRegisterDomainScript(nnsHash, expectedHash util.U
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
emit.AppCall(bw.BinWriter, nnsHash, "register", callflag.All,
|
emit.AppCall(bw.BinWriter, nnsHash, "register", callflag.All,
|
||||||
domain, c.CommitteeAcc.Contract.ScriptHash(),
|
domain, c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
frostfsOpsEmail, int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
"ops@nspcc.ru", int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
|
|
||||||
if bw.Err != nil {
|
if bw.Err != nil {
|
||||||
|
@ -231,27 +228,20 @@ func nnsResolve(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (stac
|
||||||
}
|
}
|
||||||
|
|
||||||
func nnsResolveKey(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (*keys.PublicKey, error) {
|
func nnsResolveKey(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (*keys.PublicKey, error) {
|
||||||
res, err := nnsResolve(inv, nnsHash, domain)
|
item, err := nnsResolve(inv, nnsHash, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if _, ok := res.Value().(stackitem.Null); ok {
|
v, ok := item.Value().(stackitem.Null)
|
||||||
|
if ok {
|
||||||
return nil, errors.New("NNS record is missing")
|
return nil, errors.New("NNS record is missing")
|
||||||
}
|
}
|
||||||
arr, ok := res.Value().([]stackitem.Item)
|
bs, err := v.TryBytes()
|
||||||
if !ok {
|
if err != nil {
|
||||||
return nil, errors.New("API of the NNS contract method `resolve` has changed")
|
return nil, errors.New("malformed response")
|
||||||
}
|
}
|
||||||
for i := range arr {
|
|
||||||
var bs []byte
|
|
||||||
bs, err = arr[i].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return keys.NewPublicKeyFromString(string(bs))
|
return keys.NewPublicKeyFromString(string(bs))
|
||||||
}
|
|
||||||
return nil, errors.New("no valid keys are found")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseNNSResolveResult parses the result of resolving NNS record.
|
// parseNNSResolveResult parses the result of resolving NNS record.
|
||||||
|
@ -287,11 +277,9 @@ func parseNNSResolveResult(res stackitem.Item) (util.Uint160, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func nnsIsAvailable(c Client, nnsHash util.Uint160, name string) (bool, error) {
|
func nnsIsAvailable(c Client, nnsHash util.Uint160, name string) (bool, error) {
|
||||||
switch c.(type) {
|
switch ct := c.(type) {
|
||||||
case *rpcclient.Client:
|
case *rpcclient.Client:
|
||||||
inv := invoker.New(c, nil)
|
return ct.NNSIsAvailable(nnsHash, name)
|
||||||
reader := nnsClient.NewReader(inv, nnsHash)
|
|
||||||
return reader.IsAvailable(name)
|
|
||||||
default:
|
default:
|
||||||
b, err := unwrap.Bool(invokeFunction(c, nnsHash, "isAvailable", []any{name}, nil))
|
b, err := unwrap.Bool(invokeFunction(c, nnsHash, "isAvailable", []any{name}, nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
|
@ -19,24 +18,33 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// initialAlphabetNEOAmount represents the total amount of GAS distributed between alphabet nodes.
|
// initialAlphabetNEOAmount represents the total amount of GAS distributed between alphabet nodes.
|
||||||
const (
|
const initialAlphabetNEOAmount = native.NEOTotalSupply
|
||||||
initialAlphabetNEOAmount = native.NEOTotalSupply
|
|
||||||
registerBatchSize = transaction.MaxAttributes - 1
|
func (c *initializeContext) registerCandidates() error {
|
||||||
)
|
neoHash := neo.Hash
|
||||||
|
|
||||||
|
cc, err := unwrap.Array(c.ReadOnlyInvoker.Call(neoHash, "getCandidates"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("`getCandidates`: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cc) > 0 {
|
||||||
|
c.Command.Println("Candidates are already registered.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *initializeContext) registerCandidateRange(start, end int) error {
|
|
||||||
regPrice, err := c.getCandidateRegisterPrice()
|
regPrice, err := c.getCandidateRegisterPrice()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't fetch registration price: %w", err)
|
return fmt.Errorf("can't fetch registration price: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.AppCall(w.BinWriter, neo.Hash, "setRegisterPrice", callflag.States, 1)
|
emit.AppCall(w.BinWriter, neoHash, "setRegisterPrice", callflag.States, 1)
|
||||||
for _, acc := range c.Accounts[start:end] {
|
for _, acc := range c.Accounts {
|
||||||
emit.AppCall(w.BinWriter, neo.Hash, "registerCandidate", callflag.States, acc.PrivateKey().PublicKey().Bytes())
|
emit.AppCall(w.BinWriter, neoHash, "registerCandidate", callflag.States, acc.PrivateKey().PublicKey().Bytes())
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
}
|
}
|
||||||
emit.AppCall(w.BinWriter, neo.Hash, "setRegisterPrice", callflag.States, regPrice)
|
emit.AppCall(w.BinWriter, neoHash, "setRegisterPrice", callflag.States, regPrice)
|
||||||
if w.Err != nil {
|
if w.Err != nil {
|
||||||
panic(fmt.Sprintf("BUG: %v", w.Err))
|
panic(fmt.Sprintf("BUG: %v", w.Err))
|
||||||
}
|
}
|
||||||
|
@ -45,14 +53,14 @@ func (c *initializeContext) registerCandidateRange(start, end int) error {
|
||||||
Signer: c.getSigner(false, c.CommitteeAcc),
|
Signer: c.getSigner(false, c.CommitteeAcc),
|
||||||
Account: c.CommitteeAcc,
|
Account: c.CommitteeAcc,
|
||||||
}}
|
}}
|
||||||
for _, acc := range c.Accounts[start:end] {
|
for i := range c.Accounts {
|
||||||
signers = append(signers, rpcclient.SignerAccount{
|
signers = append(signers, rpcclient.SignerAccount{
|
||||||
Signer: transaction.Signer{
|
Signer: transaction.Signer{
|
||||||
Account: acc.Contract.ScriptHash(),
|
Account: c.Accounts[i].Contract.ScriptHash(),
|
||||||
Scopes: transaction.CustomContracts,
|
Scopes: transaction.CustomContracts,
|
||||||
AllowedContracts: []util.Uint160{neo.Hash},
|
AllowedContracts: []util.Uint160{neoHash},
|
||||||
},
|
},
|
||||||
Account: acc,
|
Account: c.Accounts[i],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,8 +73,8 @@ func (c *initializeContext) registerCandidateRange(start, end int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
network := c.CommitteeAct.GetNetwork()
|
network := c.CommitteeAct.GetNetwork()
|
||||||
for _, acc := range c.Accounts[start:end] {
|
for i := range c.Accounts {
|
||||||
if err := acc.SignTx(network, tx); err != nil {
|
if err := c.Accounts[i].SignTx(network, tx); err != nil {
|
||||||
return fmt.Errorf("can't sign a transaction: %w", err)
|
return fmt.Errorf("can't sign a transaction: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,39 +82,6 @@ func (c *initializeContext) registerCandidateRange(start, end int) error {
|
||||||
return c.sendTx(tx, c.Command, true)
|
return c.sendTx(tx, c.Command, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *initializeContext) registerCandidates() error {
|
|
||||||
cc, err := unwrap.Array(c.ReadOnlyInvoker.Call(neo.Hash, "getCandidates"))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("`getCandidates`: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
need := len(c.Accounts)
|
|
||||||
have := len(cc)
|
|
||||||
|
|
||||||
if need == have {
|
|
||||||
c.Command.Println("Candidates are already registered.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register candidates in batches in order to overcome the signers amount limit.
|
|
||||||
// See: https://github.com/nspcc-dev/neo-go/blob/master/pkg/core/transaction/transaction.go#L27
|
|
||||||
for i := 0; i < need; i += registerBatchSize {
|
|
||||||
start, end := i, i+registerBatchSize
|
|
||||||
if end > need {
|
|
||||||
end = need
|
|
||||||
}
|
|
||||||
// This check is sound because transactions are accepted/rejected atomically.
|
|
||||||
if have >= end {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := c.registerCandidateRange(start, end); err != nil {
|
|
||||||
return fmt.Errorf("registering candidates %d..%d: %q", start, end-1, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *initializeContext) transferNEOToAlphabetContracts() error {
|
func (c *initializeContext) transferNEOToAlphabetContracts() error {
|
||||||
neoHash := neo.Hash
|
neoHash := neo.Hash
|
||||||
|
|
||||||
|
@ -141,11 +116,9 @@ func (c *initializeContext) transferNEOFinished(neoHash util.Uint160) (bool, err
|
||||||
var errGetPriceInvalid = errors.New("`getRegisterPrice`: invalid response")
|
var errGetPriceInvalid = errors.New("`getRegisterPrice`: invalid response")
|
||||||
|
|
||||||
func (c *initializeContext) getCandidateRegisterPrice() (int64, error) {
|
func (c *initializeContext) getCandidateRegisterPrice() (int64, error) {
|
||||||
switch c.Client.(type) {
|
switch ct := c.Client.(type) {
|
||||||
case *rpcclient.Client:
|
case *rpcclient.Client:
|
||||||
inv := invoker.New(c.Client, nil)
|
return ct.GetCandidateRegisterPrice()
|
||||||
reader := neo.NewReader(inv)
|
|
||||||
return reader.GetRegisterPrice()
|
|
||||||
default:
|
default:
|
||||||
neoHash := neo.Hash
|
neoHash := neo.Hash
|
||||||
res, err := invokeFunction(c.Client, neoHash, "getRegisterPrice", nil, nil)
|
res, err := invokeFunction(c.Client, neoHash, "getRegisterPrice", nil, nil)
|
||||||
|
|
|
@ -2,14 +2,12 @@ package morph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
"github.com/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
@ -38,22 +36,13 @@ func TestInitialize(t *testing.T) {
|
||||||
t.Run("7 nodes", func(t *testing.T) {
|
t.Run("7 nodes", func(t *testing.T) {
|
||||||
testInitialize(t, 7)
|
testInitialize(t, 7)
|
||||||
})
|
})
|
||||||
t.Run("16 nodes", func(t *testing.T) {
|
|
||||||
testInitialize(t, 16)
|
|
||||||
})
|
|
||||||
t.Run("max nodes", func(t *testing.T) {
|
|
||||||
testInitialize(t, maxAlphabetNodes)
|
|
||||||
})
|
|
||||||
t.Run("too many nodes", func(t *testing.T) {
|
|
||||||
require.ErrorIs(t, generateTestData(t, t.TempDir(), maxAlphabetNodes+1), ErrTooManyAlphabetNodes)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInitialize(t *testing.T, committeeSize int) {
|
func testInitialize(t *testing.T, committeeSize int) {
|
||||||
testdataDir := t.TempDir()
|
testdataDir := t.TempDir()
|
||||||
v := viper.GetViper()
|
v := viper.GetViper()
|
||||||
|
|
||||||
require.NoError(t, generateTestData(t, testdataDir, committeeSize))
|
generateTestData(t, testdataDir, committeeSize)
|
||||||
v.Set(protoConfigPath, filepath.Join(testdataDir, protoFileName))
|
v.Set(protoConfigPath, filepath.Join(testdataDir, protoFileName))
|
||||||
|
|
||||||
// Set to the path or remove the next statement to download from the network.
|
// Set to the path or remove the next statement to download from the network.
|
||||||
|
@ -84,33 +73,25 @@ func testInitialize(t *testing.T, committeeSize int) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateTestData(t *testing.T, dir string, size int) error {
|
func generateTestData(t *testing.T, dir string, size int) {
|
||||||
v := viper.GetViper()
|
v := viper.GetViper()
|
||||||
v.Set(alphabetWalletsFlag, dir)
|
v.Set(alphabetWalletsFlag, dir)
|
||||||
|
|
||||||
sizeStr := strconv.FormatUint(uint64(size), 10)
|
sizeStr := strconv.FormatUint(uint64(size), 10)
|
||||||
if err := generateAlphabetCmd.Flags().Set(alphabetSizeFlag, sizeStr); err != nil {
|
require.NoError(t, generateAlphabetCmd.Flags().Set(alphabetSizeFlag, sizeStr))
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
setTestCredentials(v, size)
|
setTestCredentials(v, size)
|
||||||
if err := generateAlphabetCreds(generateAlphabetCmd, nil); err != nil {
|
require.NoError(t, generateAlphabetCreds(generateAlphabetCmd, nil))
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var pubs []string
|
var pubs []string
|
||||||
for i := 0; i < size; i++ {
|
for i := 0; i < size; i++ {
|
||||||
p := filepath.Join(dir, innerring.GlagoliticLetter(i).String()+".json")
|
p := filepath.Join(dir, innerring.GlagoliticLetter(i).String()+".json")
|
||||||
w, err := wallet.NewWalletFromFile(p)
|
w, err := wallet.NewWalletFromFile(p)
|
||||||
if err != nil {
|
require.NoError(t, err, "wallet doesn't exist")
|
||||||
return fmt.Errorf("wallet doesn't exist: %w", err)
|
|
||||||
}
|
|
||||||
for _, acc := range w.Accounts {
|
for _, acc := range w.Accounts {
|
||||||
if acc.Label == singleAccountName {
|
if acc.Label == singleAccountName {
|
||||||
pub, ok := vm.ParseSignatureContract(acc.Contract.Script)
|
pub, ok := vm.ParseSignatureContract(acc.Contract.Script)
|
||||||
if !ok {
|
require.True(t, ok)
|
||||||
return fmt.Errorf("could not parse signature script for %s", acc.Address)
|
|
||||||
}
|
|
||||||
pubs = append(pubs, hex.EncodeToString(pub))
|
pubs = append(pubs, hex.EncodeToString(pub))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -119,18 +100,17 @@ func generateTestData(t *testing.T, dir string, size int) error {
|
||||||
|
|
||||||
cfg := config.Config{}
|
cfg := config.Config{}
|
||||||
cfg.ProtocolConfiguration.Magic = 12345
|
cfg.ProtocolConfiguration.Magic = 12345
|
||||||
cfg.ProtocolConfiguration.ValidatorsCount = uint32(size)
|
cfg.ProtocolConfiguration.ValidatorsCount = size
|
||||||
cfg.ProtocolConfiguration.TimePerBlock = time.Second
|
cfg.ProtocolConfiguration.SecondsPerBlock = 1
|
||||||
cfg.ProtocolConfiguration.StandbyCommittee = pubs // sorted by glagolic letters
|
cfg.ProtocolConfiguration.StandbyCommittee = pubs // sorted by glagolic letters
|
||||||
cfg.ProtocolConfiguration.P2PSigExtensions = true
|
cfg.ProtocolConfiguration.P2PSigExtensions = true
|
||||||
cfg.ProtocolConfiguration.VerifyTransactions = true
|
cfg.ProtocolConfiguration.VerifyTransactions = true
|
||||||
|
cfg.ProtocolConfiguration.VerifyBlocks = true
|
||||||
data, err := yaml.Marshal(cfg)
|
data, err := yaml.Marshal(cfg)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
protoPath := filepath.Join(dir, protoFileName)
|
protoPath := filepath.Join(dir, protoFileName)
|
||||||
return os.WriteFile(protoPath, data, os.ModePerm)
|
require.NoError(t, os.WriteFile(protoPath, data, os.ModePerm))
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTestCredentials(v *viper.Viper, size int) {
|
func setTestCredentials(v *viper.Viper, size int) {
|
||||||
|
|
65
cmd/frostfs-adm/internal/modules/morph/internal/types.go
Normal file
65
cmd/frostfs-adm/internal/modules/morph/internal/types.go
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StringifySubnetClientGroupID returns string representation of SubnetClientGroupID using MarshalText.
|
||||||
|
// Returns a string with a message on error.
|
||||||
|
func StringifySubnetClientGroupID(id *SubnetClientGroupID) string {
|
||||||
|
text, err := id.MarshalText()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("<invalid> %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText encodes SubnetClientGroupID into text format according to FrostFS API V2 protocol:
|
||||||
|
// value in base-10 integer string format.
|
||||||
|
//
|
||||||
|
// It implements encoding.TextMarshaler.
|
||||||
|
func (x *SubnetClientGroupID) MarshalText() ([]byte, error) {
|
||||||
|
num := x.GetValue() // NPE safe, returns zero on nil
|
||||||
|
|
||||||
|
return []byte(strconv.FormatUint(uint64(num), 10)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText decodes the SubnetID from the text according to FrostFS API V2 protocol:
|
||||||
|
// should be base-10 integer string format with bitsize = 32.
|
||||||
|
//
|
||||||
|
// Returns strconv.ErrRange if integer overflows uint32.
|
||||||
|
//
|
||||||
|
// Must not be called on nil.
|
||||||
|
//
|
||||||
|
// Implements encoding.TextUnmarshaler.
|
||||||
|
func (x *SubnetClientGroupID) UnmarshalText(txt []byte) error {
|
||||||
|
num, err := strconv.ParseUint(string(txt), 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid numeric value: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
x.SetNumber(uint32(num))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal encodes the SubnetClientGroupID into a binary format of FrostFS API V2 protocol
|
||||||
|
// (Protocol Buffers with direct field order).
|
||||||
|
func (x *SubnetClientGroupID) Marshal() ([]byte, error) {
|
||||||
|
return proto.Marshal(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal decodes the SubnetClientGroupID from FrostFS API V2 binary format (see Marshal). Must not be called on nil.
|
||||||
|
func (x *SubnetClientGroupID) Unmarshal(data []byte) error {
|
||||||
|
return proto.Unmarshal(data, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNumber sets SubnetClientGroupID value in uint32 format. Must not be called on nil.
|
||||||
|
// By default, number is 0.
|
||||||
|
func (x *SubnetClientGroupID) SetNumber(num uint32) {
|
||||||
|
x.Value = num
|
||||||
|
}
|
BIN
cmd/frostfs-adm/internal/modules/morph/internal/types.pb.go
generated
Normal file
BIN
cmd/frostfs-adm/internal/modules/morph/internal/types.pb.go
generated
Normal file
Binary file not shown.
15
cmd/frostfs-adm/internal/modules/morph/internal/types.proto
Normal file
15
cmd/frostfs-adm/internal/modules/morph/internal/types.proto
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package neo.fs.v2.refs;
|
||||||
|
|
||||||
|
option go_package = "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/internal";
|
||||||
|
|
||||||
|
// Client group identifier in the FrostFS subnet.
|
||||||
|
//
|
||||||
|
// String representation of a value is base-10 integer.
|
||||||
|
//
|
||||||
|
// JSON representation is an object containing single `value` number field.
|
||||||
|
message SubnetClientGroupID {
|
||||||
|
// 4-byte integer identifier of the subnet client group.
|
||||||
|
fixed32 value = 1 [json_name = "value"];
|
||||||
|
}
|
|
@ -51,7 +51,7 @@ type localClient struct {
|
||||||
maxGasInvoke int64
|
maxGasInvoke int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLocalClient(cmd *cobra.Command, v *viper.Viper, wallets []*wallet.Wallet, dumpPath string) (*localClient, error) {
|
func newLocalClient(cmd *cobra.Command, v *viper.Viper, wallets []*wallet.Wallet) (*localClient, error) {
|
||||||
cfg, err := config.LoadFile(v.GetString(protoConfigPath))
|
cfg, err := config.LoadFile(v.GetString(protoConfigPath))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -62,7 +62,7 @@ func newLocalClient(cmd *cobra.Command, v *viper.Viper, wallets []*wallet.Wallet
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m := smartcontract.GetDefaultHonestNodeCount(int(cfg.ProtocolConfiguration.ValidatorsCount))
|
m := smartcontract.GetDefaultHonestNodeCount(cfg.ProtocolConfiguration.ValidatorsCount)
|
||||||
accounts := make([]*wallet.Account, len(wallets))
|
accounts := make([]*wallet.Account, len(wallets))
|
||||||
for i := range accounts {
|
for i := range accounts {
|
||||||
accounts[i], err = getWalletAccount(wallets[i], consensusAccountName)
|
accounts[i], err = getWalletAccount(wallets[i], consensusAccountName)
|
||||||
|
@ -87,6 +87,7 @@ func newLocalClient(cmd *cobra.Command, v *viper.Viper, wallets []*wallet.Wallet
|
||||||
|
|
||||||
go bc.Run()
|
go bc.Run()
|
||||||
|
|
||||||
|
dumpPath := v.GetString(localDumpFlag)
|
||||||
if cmd.Name() != "init" {
|
if cmd.Name() != "init" {
|
||||||
f, err := os.OpenFile(dumpPath, os.O_RDONLY, 0600)
|
f, err := os.OpenFile(dumpPath, os.O_RDONLY, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -247,7 +248,7 @@ func (l *localClient) GetVersion() (*result.Version, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *localClient) InvokeContractVerify(util.Uint160, []smartcontract.Parameter, []transaction.Signer, ...transaction.Witness) (*result.Invoke, error) {
|
func (l *localClient) InvokeContractVerify(contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) {
|
||||||
// not used by `morph init` command
|
// not used by `morph init` command
|
||||||
panic("unexpected call")
|
panic("unexpected call")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package morph
|
package morph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
"github.com/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
package morph
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getDefaultNetmapContractConfigMap() map[string]any {
|
|
||||||
m := make(map[string]any)
|
|
||||||
m[netmap.EpochDurationConfig] = viper.GetInt64(epochDurationInitFlag)
|
|
||||||
m[netmap.MaxObjectSizeConfig] = viper.GetInt64(maxObjectSizeInitFlag)
|
|
||||||
m[netmap.ContainerFeeConfig] = viper.GetInt64(containerFeeInitFlag)
|
|
||||||
m[netmap.ContainerAliasFeeConfig] = viper.GetInt64(containerAliasFeeInitFlag)
|
|
||||||
m[netmap.IrCandidateFeeConfig] = viper.GetInt64(candidateFeeInitFlag)
|
|
||||||
m[netmap.WithdrawFeeConfig] = viper.GetInt64(withdrawFeeInitFlag)
|
|
||||||
m[netmap.HomomorphicHashingDisabledKey] = viper.GetBool(homomorphicHashDisabledInitFlag)
|
|
||||||
m[netmap.MaintenanceModeAllowedConfig] = viper.GetBool(maintenanceModeAllowedInitFlag)
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseConfigFromNetmapContract(arr []stackitem.Item) (map[string][]byte, error) {
|
|
||||||
m := make(map[string][]byte, len(arr))
|
|
||||||
for _, param := range arr {
|
|
||||||
tuple, ok := param.Value().([]stackitem.Item)
|
|
||||||
if !ok || len(tuple) != 2 {
|
|
||||||
return nil, errors.New("invalid ListConfig response from netmap contract")
|
|
||||||
}
|
|
||||||
|
|
||||||
k, err := tuple[0].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("invalid config key from netmap contract")
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := tuple[1].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, invalidConfigValueErr(string(k))
|
|
||||||
}
|
|
||||||
m[string(k)] = v
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
|
@ -9,12 +9,10 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/notary"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/notary"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
@ -25,9 +23,16 @@ import (
|
||||||
const defaultNotaryDepositLifetime = 5760
|
const defaultNotaryDepositLifetime = 5760
|
||||||
|
|
||||||
func depositNotary(cmd *cobra.Command, _ []string) error {
|
func depositNotary(cmd *cobra.Command, _ []string) error {
|
||||||
w, err := openWallet(cmd)
|
p, err := cmd.Flags().GetString(storageWalletFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
} else if p == "" {
|
||||||
|
return fmt.Errorf("missing wallet path (use '--%s <out.json>')", storageWalletFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := wallet.NewWalletFromFile(p)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't open wallet: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
accHash := w.GetChangeAddress()
|
accHash := w.GetChangeAddress()
|
||||||
|
@ -75,10 +80,6 @@ func depositNotary(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return transferGas(cmd, acc, accHash, gasAmount, till)
|
|
||||||
}
|
|
||||||
|
|
||||||
func transferGas(cmd *cobra.Command, acc *wallet.Account, accHash util.Uint160, gasAmount fixedn.Fixed8, till int64) error {
|
|
||||||
c, err := getN3Client(viper.GetViper())
|
c, err := getN3Client(viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -118,18 +119,3 @@ func transferGas(cmd *cobra.Command, acc *wallet.Account, accHash util.Uint160,
|
||||||
|
|
||||||
return awaitTx(cmd, c, []hashVUBPair{{hash: txHash, vub: vub}})
|
return awaitTx(cmd, c, []hashVUBPair{{hash: txHash, vub: vub}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func openWallet(cmd *cobra.Command) (*wallet.Wallet, error) {
|
|
||||||
p, err := cmd.Flags().GetString(storageWalletFlag)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if p == "" {
|
|
||||||
return nil, fmt.Errorf("missing wallet path (use '--%s <out.json>')", storageWalletFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := wallet.NewWalletFromFile(p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't open wallet: %v", err)
|
|
||||||
}
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
netmapcontract "git.frostfs.info/TrueCloudLab/frostfs-contract/netmap"
|
netmapcontract "github.com/TrueCloudLab/frostfs-contract/netmap"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
|
|
|
@ -18,6 +18,10 @@ const (
|
||||||
maxObjectSizeCLIFlag = "max-object-size"
|
maxObjectSizeCLIFlag = "max-object-size"
|
||||||
epochDurationInitFlag = "network.epoch_duration"
|
epochDurationInitFlag = "network.epoch_duration"
|
||||||
epochDurationCLIFlag = "epoch-duration"
|
epochDurationCLIFlag = "epoch-duration"
|
||||||
|
incomeRateInitFlag = "network.basic_income_rate"
|
||||||
|
incomeRateCLIFlag = "basic-income-rate"
|
||||||
|
auditFeeInitFlag = "network.fee.audit"
|
||||||
|
auditFeeCLIFlag = "audit-fee"
|
||||||
containerFeeInitFlag = "network.fee.container"
|
containerFeeInitFlag = "network.fee.container"
|
||||||
containerAliasFeeInitFlag = "network.fee.container_alias"
|
containerAliasFeeInitFlag = "network.fee.container_alias"
|
||||||
containerFeeCLIFlag = "container-fee"
|
containerFeeCLIFlag = "container-fee"
|
||||||
|
@ -65,12 +69,15 @@ var (
|
||||||
_ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag))
|
_ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag))
|
||||||
_ = viper.BindPFlag(epochDurationInitFlag, cmd.Flags().Lookup(epochDurationCLIFlag))
|
_ = viper.BindPFlag(epochDurationInitFlag, cmd.Flags().Lookup(epochDurationCLIFlag))
|
||||||
_ = viper.BindPFlag(maxObjectSizeInitFlag, cmd.Flags().Lookup(maxObjectSizeCLIFlag))
|
_ = viper.BindPFlag(maxObjectSizeInitFlag, cmd.Flags().Lookup(maxObjectSizeCLIFlag))
|
||||||
|
_ = viper.BindPFlag(incomeRateInitFlag, cmd.Flags().Lookup(incomeRateCLIFlag))
|
||||||
_ = viper.BindPFlag(homomorphicHashDisabledInitFlag, cmd.Flags().Lookup(homomorphicHashDisabledCLIFlag))
|
_ = viper.BindPFlag(homomorphicHashDisabledInitFlag, cmd.Flags().Lookup(homomorphicHashDisabledCLIFlag))
|
||||||
|
_ = viper.BindPFlag(auditFeeInitFlag, cmd.Flags().Lookup(auditFeeCLIFlag))
|
||||||
_ = viper.BindPFlag(candidateFeeInitFlag, cmd.Flags().Lookup(candidateFeeCLIFlag))
|
_ = viper.BindPFlag(candidateFeeInitFlag, cmd.Flags().Lookup(candidateFeeCLIFlag))
|
||||||
_ = viper.BindPFlag(containerFeeInitFlag, cmd.Flags().Lookup(containerFeeCLIFlag))
|
_ = viper.BindPFlag(containerFeeInitFlag, cmd.Flags().Lookup(containerFeeCLIFlag))
|
||||||
_ = viper.BindPFlag(containerAliasFeeInitFlag, cmd.Flags().Lookup(containerAliasFeeCLIFlag))
|
_ = viper.BindPFlag(containerAliasFeeInitFlag, cmd.Flags().Lookup(containerAliasFeeCLIFlag))
|
||||||
_ = viper.BindPFlag(withdrawFeeInitFlag, cmd.Flags().Lookup(withdrawFeeCLIFlag))
|
_ = viper.BindPFlag(withdrawFeeInitFlag, cmd.Flags().Lookup(withdrawFeeCLIFlag))
|
||||||
_ = viper.BindPFlag(protoConfigPath, cmd.Flags().Lookup(protoConfigPath))
|
_ = viper.BindPFlag(protoConfigPath, cmd.Flags().Lookup(protoConfigPath))
|
||||||
|
_ = viper.BindPFlag(localDumpFlag, cmd.Flags().Lookup(localDumpFlag))
|
||||||
},
|
},
|
||||||
RunE: initializeSideChainCmd,
|
RunE: initializeSideChainCmd,
|
||||||
}
|
}
|
||||||
|
@ -232,140 +239,14 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
initGenerateAlphabetCmd()
|
RootCmd.AddCommand(generateAlphabetCmd)
|
||||||
initInitCmd()
|
generateAlphabetCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
initDeployCmd()
|
generateAlphabetCmd.Flags().Uint(alphabetSizeFlag, 7, "Amount of alphabet wallets to generate")
|
||||||
initGenerateStorageCmd()
|
|
||||||
initForceNewEpochCmd()
|
|
||||||
initRemoveNodesCmd()
|
|
||||||
initSetPolicyCmd()
|
|
||||||
initDumpContractHashesCmd()
|
|
||||||
initDumpNetworkConfigCmd()
|
|
||||||
initSetConfigCmd()
|
|
||||||
initDumpBalancesCmd()
|
|
||||||
initUpdateContractsCmd()
|
|
||||||
initDumpContainersCmd()
|
|
||||||
initRestoreContainersCmd()
|
|
||||||
initListContainersCmd()
|
|
||||||
initRefillGasCmd()
|
|
||||||
initDepositoryNotaryCmd()
|
|
||||||
initNetmapCandidatesCmd()
|
|
||||||
}
|
|
||||||
|
|
||||||
func initNetmapCandidatesCmd() {
|
|
||||||
RootCmd.AddCommand(netmapCandidatesCmd)
|
|
||||||
netmapCandidatesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initDepositoryNotaryCmd() {
|
|
||||||
RootCmd.AddCommand(depositNotaryCmd)
|
|
||||||
depositNotaryCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
depositNotaryCmd.Flags().String(storageWalletFlag, "", "Path to storage node wallet")
|
|
||||||
depositNotaryCmd.Flags().String(walletAccountFlag, "", "Wallet account address")
|
|
||||||
depositNotaryCmd.Flags().String(refillGasAmountFlag, "", "Amount of GAS to deposit")
|
|
||||||
depositNotaryCmd.Flags().String(notaryDepositTillFlag, "", "Notary deposit duration in blocks")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initRefillGasCmd() {
|
|
||||||
RootCmd.AddCommand(refillGasCmd)
|
|
||||||
refillGasCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
refillGasCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
refillGasCmd.Flags().String(storageWalletFlag, "", "Path to storage node wallet")
|
|
||||||
refillGasCmd.Flags().String(walletAddressFlag, "", "Address of wallet")
|
|
||||||
refillGasCmd.Flags().String(refillGasAmountFlag, "", "Additional amount of GAS to transfer")
|
|
||||||
refillGasCmd.MarkFlagsMutuallyExclusive(walletAddressFlag, storageWalletFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initListContainersCmd() {
|
|
||||||
RootCmd.AddCommand(listContainersCmd)
|
|
||||||
listContainersCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
listContainersCmd.Flags().String(containerContractFlag, "", "Container contract hash (for networks without NNS)")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initRestoreContainersCmd() {
|
|
||||||
RootCmd.AddCommand(restoreContainersCmd)
|
|
||||||
restoreContainersCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
restoreContainersCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
restoreContainersCmd.Flags().String(containerDumpFlag, "", "File to restore containers from")
|
|
||||||
restoreContainersCmd.Flags().StringSlice(containerIDsFlag, nil, "Containers to restore")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initDumpContainersCmd() {
|
|
||||||
RootCmd.AddCommand(dumpContainersCmd)
|
|
||||||
dumpContainersCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
dumpContainersCmd.Flags().String(containerDumpFlag, "", "File where to save dumped containers")
|
|
||||||
dumpContainersCmd.Flags().String(containerContractFlag, "", "Container contract hash (for networks without NNS)")
|
|
||||||
dumpContainersCmd.Flags().StringSlice(containerIDsFlag, nil, "Containers to dump")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initUpdateContractsCmd() {
|
|
||||||
RootCmd.AddCommand(updateContractsCmd)
|
|
||||||
updateContractsCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
updateContractsCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
updateContractsCmd.Flags().String(contractsInitFlag, "", "Path to archive with compiled FrostFS contracts")
|
|
||||||
_ = updateContractsCmd.MarkFlagRequired(contractsInitFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initDumpBalancesCmd() {
|
|
||||||
RootCmd.AddCommand(dumpBalancesCmd)
|
|
||||||
dumpBalancesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
dumpBalancesCmd.Flags().BoolP(dumpBalancesStorageFlag, "s", false, "Dump balances of storage nodes from the current netmap")
|
|
||||||
dumpBalancesCmd.Flags().BoolP(dumpBalancesAlphabetFlag, "a", false, "Dump balances of alphabet contracts")
|
|
||||||
dumpBalancesCmd.Flags().BoolP(dumpBalancesProxyFlag, "p", false, "Dump balances of the proxy contract")
|
|
||||||
dumpBalancesCmd.Flags().Bool(dumpBalancesUseScriptHashFlag, false, "Use script-hash format for addresses")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initSetConfigCmd() {
|
|
||||||
RootCmd.AddCommand(setConfig)
|
|
||||||
setConfig.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
setConfig.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
setConfig.Flags().Bool(forceConfigSet, false, "Force setting not well-known configuration key")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initDumpNetworkConfigCmd() {
|
|
||||||
RootCmd.AddCommand(dumpNetworkConfigCmd)
|
|
||||||
dumpNetworkConfigCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initDumpContractHashesCmd() {
|
|
||||||
RootCmd.AddCommand(dumpContractHashesCmd)
|
|
||||||
dumpContractHashesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
dumpContractHashesCmd.Flags().String(customZoneFlag, "", "Custom zone to search.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initSetPolicyCmd() {
|
|
||||||
RootCmd.AddCommand(setPolicy)
|
|
||||||
setPolicy.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
setPolicy.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initRemoveNodesCmd() {
|
|
||||||
RootCmd.AddCommand(removeNodes)
|
|
||||||
removeNodes.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
removeNodes.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initForceNewEpochCmd() {
|
|
||||||
RootCmd.AddCommand(forceNewEpoch)
|
|
||||||
forceNewEpoch.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
forceNewEpoch.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initGenerateStorageCmd() {
|
|
||||||
RootCmd.AddCommand(generateStorageCmd)
|
|
||||||
generateStorageCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
generateStorageCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
generateStorageCmd.Flags().String(storageWalletFlag, "", "Path to new storage node wallet")
|
|
||||||
generateStorageCmd.Flags().String(storageGasCLIFlag, "", "Initial amount of GAS to transfer")
|
|
||||||
generateStorageCmd.Flags().StringP(storageWalletLabelFlag, "l", "", "Wallet label")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initInitCmd() {
|
|
||||||
RootCmd.AddCommand(initCmd)
|
RootCmd.AddCommand(initCmd)
|
||||||
initCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
initCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
initCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
initCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
initCmd.Flags().String(contractsInitFlag, "", "Path to archive with compiled FrostFS contracts")
|
initCmd.Flags().String(contractsInitFlag, "", "Path to archive with compiled FrostFS contracts (default fetched from latest github release)")
|
||||||
_ = initCmd.MarkFlagRequired(contractsInitFlag)
|
|
||||||
initCmd.Flags().Uint(epochDurationCLIFlag, 240, "Amount of side chain blocks in one FrostFS epoch")
|
initCmd.Flags().Uint(epochDurationCLIFlag, 240, "Amount of side chain blocks in one FrostFS epoch")
|
||||||
initCmd.Flags().Uint(maxObjectSizeCLIFlag, 67108864, "Max single object size in bytes")
|
initCmd.Flags().Uint(maxObjectSizeCLIFlag, 67108864, "Max single object size in bytes")
|
||||||
initCmd.Flags().Bool(homomorphicHashDisabledCLIFlag, false, "Disable object homomorphic hashing")
|
initCmd.Flags().Bool(homomorphicHashDisabledCLIFlag, false, "Disable object homomorphic hashing")
|
||||||
|
@ -374,14 +255,85 @@ func initInitCmd() {
|
||||||
initCmd.Flags().Uint64(containerAliasFeeCLIFlag, 500, "Container alias fee")
|
initCmd.Flags().Uint64(containerAliasFeeCLIFlag, 500, "Container alias fee")
|
||||||
initCmd.Flags().String(protoConfigPath, "", "Path to the consensus node configuration")
|
initCmd.Flags().String(protoConfigPath, "", "Path to the consensus node configuration")
|
||||||
initCmd.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
|
initCmd.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
|
||||||
}
|
|
||||||
|
|
||||||
func initGenerateAlphabetCmd() {
|
|
||||||
RootCmd.AddCommand(generateAlphabetCmd)
|
|
||||||
generateAlphabetCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
generateAlphabetCmd.Flags().Uint(alphabetSizeFlag, 7, "Amount of alphabet wallets to generate")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initDeployCmd() {
|
|
||||||
RootCmd.AddCommand(deployCmd)
|
RootCmd.AddCommand(deployCmd)
|
||||||
|
|
||||||
|
RootCmd.AddCommand(generateStorageCmd)
|
||||||
|
generateStorageCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
generateStorageCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
generateStorageCmd.Flags().String(storageWalletFlag, "", "Path to new storage node wallet")
|
||||||
|
generateStorageCmd.Flags().String(storageGasCLIFlag, "", "Initial amount of GAS to transfer")
|
||||||
|
generateStorageCmd.Flags().StringP(storageWalletLabelFlag, "l", "", "Wallet label")
|
||||||
|
|
||||||
|
RootCmd.AddCommand(forceNewEpoch)
|
||||||
|
forceNewEpoch.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
forceNewEpoch.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
|
||||||
|
RootCmd.AddCommand(removeNodes)
|
||||||
|
removeNodes.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
removeNodes.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
|
||||||
|
RootCmd.AddCommand(setPolicy)
|
||||||
|
setPolicy.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
setPolicy.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
|
||||||
|
RootCmd.AddCommand(dumpContractHashesCmd)
|
||||||
|
dumpContractHashesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
dumpContractHashesCmd.Flags().String(customZoneFlag, "", "Custom zone to search.")
|
||||||
|
|
||||||
|
RootCmd.AddCommand(dumpNetworkConfigCmd)
|
||||||
|
dumpNetworkConfigCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
|
||||||
|
RootCmd.AddCommand(setConfig)
|
||||||
|
setConfig.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
setConfig.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
setConfig.Flags().Bool(forceConfigSet, false, "Force setting not well-known configuration key")
|
||||||
|
|
||||||
|
RootCmd.AddCommand(dumpBalancesCmd)
|
||||||
|
dumpBalancesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
dumpBalancesCmd.Flags().BoolP(dumpBalancesStorageFlag, "s", false, "Dump balances of storage nodes from the current netmap")
|
||||||
|
dumpBalancesCmd.Flags().BoolP(dumpBalancesAlphabetFlag, "a", false, "Dump balances of alphabet contracts")
|
||||||
|
dumpBalancesCmd.Flags().BoolP(dumpBalancesProxyFlag, "p", false, "Dump balances of the proxy contract")
|
||||||
|
dumpBalancesCmd.Flags().Bool(dumpBalancesUseScriptHashFlag, false, "Use script-hash format for addresses")
|
||||||
|
|
||||||
|
RootCmd.AddCommand(updateContractsCmd)
|
||||||
|
updateContractsCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
updateContractsCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
updateContractsCmd.Flags().String(contractsInitFlag, "", "Path to archive with compiled FrostFS contracts (default fetched from latest github release)")
|
||||||
|
|
||||||
|
RootCmd.AddCommand(dumpContainersCmd)
|
||||||
|
dumpContainersCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
dumpContainersCmd.Flags().String(containerDumpFlag, "", "File where to save dumped containers")
|
||||||
|
dumpContainersCmd.Flags().String(containerContractFlag, "", "Container contract hash (for networks without NNS)")
|
||||||
|
dumpContainersCmd.Flags().StringSlice(containerIDsFlag, nil, "Containers to dump")
|
||||||
|
|
||||||
|
RootCmd.AddCommand(restoreContainersCmd)
|
||||||
|
restoreContainersCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
restoreContainersCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
restoreContainersCmd.Flags().String(containerDumpFlag, "", "File to restore containers from")
|
||||||
|
restoreContainersCmd.Flags().StringSlice(containerIDsFlag, nil, "Containers to restore")
|
||||||
|
|
||||||
|
RootCmd.AddCommand(listContainersCmd)
|
||||||
|
listContainersCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
listContainersCmd.Flags().String(containerContractFlag, "", "Container contract hash (for networks without NNS)")
|
||||||
|
|
||||||
|
RootCmd.AddCommand(refillGasCmd)
|
||||||
|
refillGasCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
refillGasCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
refillGasCmd.Flags().String(storageWalletFlag, "", "Path to storage node wallet")
|
||||||
|
refillGasCmd.Flags().String(walletAddressFlag, "", "Address of wallet")
|
||||||
|
refillGasCmd.Flags().String(refillGasAmountFlag, "", "Additional amount of GAS to transfer")
|
||||||
|
refillGasCmd.MarkFlagsMutuallyExclusive(walletAddressFlag, storageWalletFlag)
|
||||||
|
|
||||||
|
RootCmd.AddCommand(cmdSubnet)
|
||||||
|
|
||||||
|
RootCmd.AddCommand(depositNotaryCmd)
|
||||||
|
depositNotaryCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
depositNotaryCmd.Flags().String(storageWalletFlag, "", "Path to storage node wallet")
|
||||||
|
depositNotaryCmd.Flags().String(walletAccountFlag, "", "Wallet account address")
|
||||||
|
depositNotaryCmd.Flags().String(refillGasAmountFlag, "", "Amount of GAS to deposit")
|
||||||
|
depositNotaryCmd.Flags().String(notaryDepositTillFlag, "", "Notary deposit duration in blocks")
|
||||||
|
|
||||||
|
RootCmd.AddCommand(netmapCandidatesCmd)
|
||||||
|
netmapCandidatesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
}
|
}
|
||||||
|
|
1064
cmd/frostfs-adm/internal/modules/morph/subnet.go
Normal file
1064
cmd/frostfs-adm/internal/modules/morph/subnet.go
Normal file
File diff suppressed because it is too large
Load diff
|
@ -3,14 +3,14 @@ package modules
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/storagecfg"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/storagecfg"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/misc"
|
"github.com/TrueCloudLab/frostfs-node/misc"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/autocomplete"
|
"github.com/TrueCloudLab/frostfs-node/pkg/util/autocomplete"
|
||||||
utilConfig "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/config"
|
utilConfig "github.com/TrueCloudLab/frostfs-node/pkg/util/config"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/gendoc"
|
"github.com/TrueCloudLab/frostfs-node/pkg/util/gendoc"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
@ -45,14 +45,14 @@ func init() {
|
||||||
rootCmd.AddCommand(storagecfg.RootCmd)
|
rootCmd.AddCommand(storagecfg.RootCmd)
|
||||||
|
|
||||||
rootCmd.AddCommand(autocomplete.Command("frostfs-adm"))
|
rootCmd.AddCommand(autocomplete.Command("frostfs-adm"))
|
||||||
rootCmd.AddCommand(gendoc.Command(rootCmd, gendoc.Options{}))
|
rootCmd.AddCommand(gendoc.Command(rootCmd))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Execute() error {
|
func Execute() error {
|
||||||
return rootCmd.Execute()
|
return rootCmd.Execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
func entryPoint(cmd *cobra.Command, _ []string) error {
|
func entryPoint(cmd *cobra.Command, args []string) error {
|
||||||
printVersion, _ := cmd.Flags().GetBool("version")
|
printVersion, _ := cmd.Flags().GetBool("version")
|
||||||
if printVersion {
|
if printVersion {
|
||||||
cmd.Print(misc.BuildInfo("FrostFS Adm"))
|
cmd.Print(misc.BuildInfo("FrostFS Adm"))
|
||||||
|
|
|
@ -12,6 +12,9 @@ node:
|
||||||
- {{ .AnnouncedAddress }}
|
- {{ .AnnouncedAddress }}
|
||||||
attribute_0: UN-LOCODE:{{ .Attribute.Locode }}
|
attribute_0: UN-LOCODE:{{ .Attribute.Locode }}
|
||||||
relay: {{ .Relay }} # start Storage node in relay mode without bootstrapping into the Network map
|
relay: {{ .Relay }} # start Storage node in relay mode without bootstrapping into the Network map
|
||||||
|
subnet:
|
||||||
|
exit_zero: false # toggle entrance to zero subnet (overrides corresponding attribute and occurrence in entries)
|
||||||
|
entries: [] # list of IDs of subnets to enter in a text format of FrostFS API protocol (overrides corresponding attributes)
|
||||||
|
|
||||||
grpc:
|
grpc:
|
||||||
num: 1 # total number of listener endpoints
|
num: 1 # total number of listener endpoints
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
netutil "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/network"
|
netutil "github.com/TrueCloudLab/frostfs-node/pkg/network"
|
||||||
"github.com/chzyer/readline"
|
"github.com/chzyer/readline"
|
||||||
"github.com/nspcc-dev/neo-go/cli/flags"
|
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||||
"github.com/nspcc-dev/neo-go/cli/input"
|
"github.com/nspcc-dev/neo-go/cli/input"
|
||||||
|
@ -80,7 +80,15 @@ type config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func storageConfig(cmd *cobra.Command, args []string) {
|
func storageConfig(cmd *cobra.Command, args []string) {
|
||||||
outPath := getOutputPath(args)
|
var outPath string
|
||||||
|
if len(args) != 0 {
|
||||||
|
outPath = args[0]
|
||||||
|
} else {
|
||||||
|
outPath = getPath("File to write config at [./config.yml]: ")
|
||||||
|
if outPath == "" {
|
||||||
|
outPath = "./config.yml"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
historyPath := filepath.Join(os.TempDir(), "frostfs-adm.history")
|
historyPath := filepath.Join(os.TempDir(), "frostfs-adm.history")
|
||||||
readline.SetHistoryPath(historyPath)
|
readline.SetHistoryPath(historyPath)
|
||||||
|
@ -95,7 +103,14 @@ func storageConfig(cmd *cobra.Command, args []string) {
|
||||||
w, err := wallet.NewWalletFromFile(c.Wallet.Path)
|
w, err := wallet.NewWalletFromFile(c.Wallet.Path)
|
||||||
fatalOnErr(err)
|
fatalOnErr(err)
|
||||||
|
|
||||||
fillWalletAccount(cmd, &c, w)
|
c.Wallet.Account, _ = cmd.Flags().GetString(accountFlag)
|
||||||
|
if c.Wallet.Account == "" {
|
||||||
|
addr := address.Uint160ToString(w.GetChangeAddress())
|
||||||
|
c.Wallet.Account = getWalletAccount(w, fmt.Sprintf("Wallet account [%s]: ", addr))
|
||||||
|
if c.Wallet.Account == "" {
|
||||||
|
c.Wallet.Account = addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
accH, err := flags.ParseAddress(c.Wallet.Account)
|
accH, err := flags.ParseAddress(c.Wallet.Account)
|
||||||
fatalOnErr(err)
|
fatalOnErr(err)
|
||||||
|
@ -113,51 +128,32 @@ func storageConfig(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
c.AuthorizedKeys = append(c.AuthorizedKeys, hex.EncodeToString(acc.PrivateKey().PublicKey().Bytes()))
|
c.AuthorizedKeys = append(c.AuthorizedKeys, hex.EncodeToString(acc.PrivateKey().PublicKey().Bytes()))
|
||||||
|
|
||||||
network := readNetwork(cmd)
|
var network string
|
||||||
|
for {
|
||||||
|
network = getString("Choose network [mainnet]/testnet: ")
|
||||||
|
switch network {
|
||||||
|
case "":
|
||||||
|
network = "mainnet"
|
||||||
|
case "testnet", "mainnet":
|
||||||
|
default:
|
||||||
|
cmd.Println(`Network must be either "mainnet" or "testnet"`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
c.MorphRPC = n3config[network].MorphRPC
|
c.MorphRPC = n3config[network].MorphRPC
|
||||||
|
|
||||||
depositGas(cmd, acc, network)
|
depositGas(cmd, acc, network)
|
||||||
|
|
||||||
c.Attribute.Locode = getString("UN-LOCODE attribute in [XX YYY] format: ")
|
c.Attribute.Locode = getString("UN-LOCODE attribute in [XX YYY] format: ")
|
||||||
|
|
||||||
endpoint := getDefaultEndpoint(cmd, &c)
|
|
||||||
c.Endpoint = getString(fmt.Sprintf("Listening address [%s]: ", endpoint))
|
|
||||||
if c.Endpoint == "" {
|
|
||||||
c.Endpoint = endpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
c.ControlEndpoint = getString(fmt.Sprintf("Listening address (control endpoint) [%s]: ", defaultControlEndpoint))
|
|
||||||
if c.ControlEndpoint == "" {
|
|
||||||
c.ControlEndpoint = defaultControlEndpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
c.TLSCert = getPath("TLS Certificate (optional): ")
|
|
||||||
if c.TLSCert != "" {
|
|
||||||
c.TLSKey = getPath("TLS Key: ")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Relay = getConfirmation(false, "Use node as a relay? yes/[no]: ")
|
|
||||||
if !c.Relay {
|
|
||||||
p := getPath("Path to the storage directory (all available storage will be used): ")
|
|
||||||
c.BlobstorPath = filepath.Join(p, "blob")
|
|
||||||
c.MetabasePath = filepath.Join(p, "meta")
|
|
||||||
}
|
|
||||||
|
|
||||||
out := applyTemplate(c)
|
|
||||||
fatalOnErr(os.WriteFile(outPath, out, 0644))
|
|
||||||
|
|
||||||
cmd.Println("Node is ready for work! Run `frostfs-node -config " + outPath + "`")
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDefaultEndpoint(cmd *cobra.Command, c *config) string {
|
|
||||||
var addr, port string
|
var addr, port string
|
||||||
for {
|
for {
|
||||||
c.AnnouncedAddress = getString("Publicly announced address: ")
|
c.AnnouncedAddress = getString("Publicly announced address: ")
|
||||||
validator := netutil.Address{}
|
validator := netutil.Address{}
|
||||||
err := validator.FromString(c.AnnouncedAddress)
|
err := validator.FromString(c.AnnouncedAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.Println("Incorrect address format. See https://git.frostfs.info/TrueCloudLab/frostfs-node/src/branch/master/pkg/network/address.go for details.")
|
cmd.Println("Incorrect address format. See https://github.com/TrueCloudLab/frostfs-node/blob/master/pkg/network/address.go for details.")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
uriAddr, err := url.Parse(validator.URIAddr())
|
uriAddr, err := url.Parse(validator.URIAddr())
|
||||||
|
@ -186,46 +182,34 @@ func getDefaultEndpoint(cmd *cobra.Command, c *config) string {
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return net.JoinHostPort(defaultDataEndpoint, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fillWalletAccount(cmd *cobra.Command, c *config, w *wallet.Wallet) {
|
defaultAddr := net.JoinHostPort(defaultDataEndpoint, port)
|
||||||
c.Wallet.Account, _ = cmd.Flags().GetString(accountFlag)
|
c.Endpoint = getString(fmt.Sprintf("Listening address [%s]: ", defaultAddr))
|
||||||
if c.Wallet.Account == "" {
|
if c.Endpoint == "" {
|
||||||
addr := address.Uint160ToString(w.GetChangeAddress())
|
c.Endpoint = defaultAddr
|
||||||
c.Wallet.Account = getWalletAccount(w, fmt.Sprintf("Wallet account [%s]: ", addr))
|
|
||||||
if c.Wallet.Account == "" {
|
|
||||||
c.Wallet.Account = addr
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func readNetwork(cmd *cobra.Command) string {
|
c.ControlEndpoint = getString(fmt.Sprintf("Listening address (control endpoint) [%s]: ", defaultControlEndpoint))
|
||||||
var network string
|
if c.ControlEndpoint == "" {
|
||||||
for {
|
c.ControlEndpoint = defaultControlEndpoint
|
||||||
network = getString("Choose network [mainnet]/testnet: ")
|
|
||||||
switch network {
|
|
||||||
case "":
|
|
||||||
network = "mainnet"
|
|
||||||
case "testnet", "mainnet":
|
|
||||||
default:
|
|
||||||
cmd.Println(`Network must be either "mainnet" or "testnet"`)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
return network
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOutputPath(args []string) string {
|
c.TLSCert = getPath("TLS Certificate (optional): ")
|
||||||
if len(args) != 0 {
|
if c.TLSCert != "" {
|
||||||
return args[0]
|
c.TLSKey = getPath("TLS Key: ")
|
||||||
}
|
}
|
||||||
outPath := getPath("File to write config at [./config.yml]: ")
|
|
||||||
if outPath == "" {
|
c.Relay = getConfirmation(false, "Use node as a relay? yes/[no]: ")
|
||||||
outPath = "./config.yml"
|
if !c.Relay {
|
||||||
|
p := getPath("Path to the storage directory (all available storage will be used): ")
|
||||||
|
c.BlobstorPath = filepath.Join(p, "blob")
|
||||||
|
c.MetabasePath = filepath.Join(p, "meta")
|
||||||
}
|
}
|
||||||
return outPath
|
|
||||||
|
out := applyTemplate(c)
|
||||||
|
fatalOnErr(os.WriteFile(outPath, out, 0644))
|
||||||
|
|
||||||
|
cmd.Println("Node is ready for work! Run `frostfs-node -config " + outPath + "`")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWalletAccount(w *wallet.Wallet, prompt string) string {
|
func getWalletAccount(w *wallet.Wallet, prompt string) string {
|
||||||
|
|
|
@ -3,7 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
@ -9,16 +9,16 @@ duplicated header names or headers with empty values are considered invalid.
|
||||||
|
|
||||||
## Existing headers
|
## Existing headers
|
||||||
|
|
||||||
There are some "well-known" headers starting with `__SYSTEM__` prefix that
|
There are some "well-known" headers starting with `__FROSTFS__` prefix that
|
||||||
affect system behaviour. For backward compatibility, the same set of
|
affect system behaviour. For backward compatibility, the same set of
|
||||||
"well-known" headers may also use `__NEOFS__` prefix:
|
"well-known" headers may also use `__NEOFS__` prefix:
|
||||||
|
|
||||||
* `__SYSTEM__NETMAP_EPOCH` - netmap epoch to use for object placement calculation. The `value` is string
|
* `__FROSTFS__NETMAP_EPOCH` - netmap epoch to use for object placement calculation. The `value` is string
|
||||||
encoded `uint64` in decimal presentation. If set to '0' or omitted, the
|
encoded `uint64` in decimal presentation. If set to '0' or omitted, the
|
||||||
current epoch only will be used.
|
current epoch only will be used.
|
||||||
* `__SYSTEM__NETMAP_LOOKUP_DEPTH` - if object can't be found using current epoch's netmap, this header limits
|
* `__FROSTFS__NETMAP_LOOKUP_DEPTH` - if object can't be found using current epoch's netmap, this header limits
|
||||||
how many past epochs the node can look up through. Depth is applied to a current epoch or the value
|
how many past epochs the node can look up through. Depth is applied to a current epoch or the value
|
||||||
of `__SYSTEM__NETMAP_EPOCH` attribute. The `value` is string encoded `uint64` in decimal presentation.
|
of `__FROSTFS__NETMAP_EPOCH` attribute. The `value` is string encoded `uint64` in decimal presentation.
|
||||||
If set to '0' or not set, only the current epoch is used.
|
If set to '0' or not set, only the current epoch is used.
|
||||||
|
|
||||||
## `frostfs-cli` commands with `--xhdr`
|
## `frostfs-cli` commands with `--xhdr`
|
||||||
|
@ -26,8 +26,9 @@ If set to '0' or not set, only the current epoch is used.
|
||||||
List of commands with support of extended headers:
|
List of commands with support of extended headers:
|
||||||
* `container list-objects`
|
* `container list-objects`
|
||||||
* `object delete/get/hash/head/lock/put/range/search`
|
* `object delete/get/hash/head/lock/put/range/search`
|
||||||
|
* `storagegroup delete/get/list/put`
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```shell
|
```shell
|
||||||
$ frostfs-cli object put -r s01.frostfs.devenv:8080 -w wallet.json --cid CID --file FILE --xhdr "__SYSTEM__NETMAP_EPOCH=777"
|
$ frostfs-cli object put -r s01.frostfs.devenv:8080 -w wallet.json --cid CID --file FILE --xhdr "__FROSTFS__NETMAP_EPOCH=777"
|
||||||
```
|
```
|
||||||
|
|
|
@ -6,19 +6,16 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
|
"github.com/TrueCloudLab/frostfs-sdk-go/accounting"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
|
"github.com/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
containerSDK "github.com/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
"github.com/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"github.com/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
"github.com/TrueCloudLab/frostfs-sdk-go/version"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// BalanceOfPrm groups parameters of BalanceOf operation.
|
// BalanceOfPrm groups parameters of BalanceOf operation.
|
||||||
|
@ -40,8 +37,8 @@ func (x BalanceOfRes) Balance() accounting.Decimal {
|
||||||
// BalanceOf requests the current balance of a FrostFS user.
|
// BalanceOf requests the current balance of a FrostFS user.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func BalanceOf(ctx context.Context, prm BalanceOfPrm) (res BalanceOfRes, err error) {
|
func BalanceOf(prm BalanceOfPrm) (res BalanceOfRes, err error) {
|
||||||
res.cliRes, err = prm.cli.BalanceGet(ctx, prm.PrmBalanceGet)
|
res.cliRes, err = prm.cli.BalanceGet(context.Background(), prm.PrmBalanceGet)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -65,26 +62,16 @@ func (x ListContainersRes) IDList() []cid.ID {
|
||||||
// ListContainers requests a list of FrostFS user's containers.
|
// ListContainers requests a list of FrostFS user's containers.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func ListContainers(ctx context.Context, prm ListContainersPrm) (res ListContainersRes, err error) {
|
func ListContainers(prm ListContainersPrm) (res ListContainersRes, err error) {
|
||||||
res.cliRes, err = prm.cli.ContainerList(ctx, prm.PrmContainerList)
|
res.cliRes, err = prm.cli.ContainerList(context.Background(), prm.PrmContainerList)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SortedIDList returns sorted list of identifiers of user's containers.
|
|
||||||
func (x ListContainersRes) SortedIDList() []cid.ID {
|
|
||||||
list := x.cliRes.Containers()
|
|
||||||
sort.Slice(list, func(i, j int) bool {
|
|
||||||
lhs, rhs := list[i].EncodeToString(), list[j].EncodeToString()
|
|
||||||
return strings.Compare(lhs, rhs) < 0
|
|
||||||
})
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
// PutContainerPrm groups parameters of PutContainer operation.
|
// PutContainerPrm groups parameters of PutContainer operation.
|
||||||
type PutContainerPrm struct {
|
type PutContainerPrm struct {
|
||||||
Client *client.Client
|
commonPrm
|
||||||
ClientParams client.PrmContainerPut
|
client.PrmContainerPut
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutContainerRes groups the resulting values of PutContainer operation.
|
// PutContainerRes groups the resulting values of PutContainer operation.
|
||||||
|
@ -105,8 +92,8 @@ func (x PutContainerRes) ID() cid.ID {
|
||||||
// Success can be verified by reading by identifier.
|
// Success can be verified by reading by identifier.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func PutContainer(ctx context.Context, prm PutContainerPrm) (res PutContainerRes, err error) {
|
func PutContainer(prm PutContainerPrm) (res PutContainerRes, err error) {
|
||||||
cliRes, err := prm.Client.ContainerPut(ctx, prm.ClientParams)
|
cliRes, err := prm.cli.ContainerPut(context.Background(), prm.PrmContainerPut)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
res.cnr = cliRes.ID()
|
res.cnr = cliRes.ID()
|
||||||
}
|
}
|
||||||
|
@ -116,15 +103,13 @@ func PutContainer(ctx context.Context, prm PutContainerPrm) (res PutContainerRes
|
||||||
|
|
||||||
// GetContainerPrm groups parameters of GetContainer operation.
|
// GetContainerPrm groups parameters of GetContainer operation.
|
||||||
type GetContainerPrm struct {
|
type GetContainerPrm struct {
|
||||||
Client *client.Client
|
commonPrm
|
||||||
ClientParams client.PrmContainerGet
|
cliPrm client.PrmContainerGet
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetContainer sets identifier of the container to be read.
|
// SetContainer sets identifier of the container to be read.
|
||||||
//
|
|
||||||
// Deprecated: Use GetContainerPrm.ClientParams.ContainerID instead.
|
|
||||||
func (x *GetContainerPrm) SetContainer(id cid.ID) {
|
func (x *GetContainerPrm) SetContainer(id cid.ID) {
|
||||||
x.ClientParams.ContainerID = &id
|
x.cliPrm.SetContainer(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetContainerRes groups the resulting values of GetContainer operation.
|
// GetContainerRes groups the resulting values of GetContainer operation.
|
||||||
|
@ -140,23 +125,20 @@ func (x GetContainerRes) Container() containerSDK.Container {
|
||||||
// GetContainer reads a container from FrostFS by ID.
|
// GetContainer reads a container from FrostFS by ID.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func GetContainer(ctx context.Context, prm GetContainerPrm) (res GetContainerRes, err error) {
|
func GetContainer(prm GetContainerPrm) (res GetContainerRes, err error) {
|
||||||
res.cliRes, err = prm.Client.ContainerGet(ctx, prm.ClientParams)
|
res.cliRes, err = prm.cli.ContainerGet(context.Background(), prm.cliPrm)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsACLExtendable checks if ACL of the container referenced by the given identifier
|
// IsACLExtendable checks if ACL of the container referenced by the given identifier
|
||||||
// can be extended. Client connection MUST BE correctly established in advance.
|
// can be extended. Client connection MUST BE correctly established in advance.
|
||||||
func IsACLExtendable(ctx context.Context, c *client.Client, cnr cid.ID) (bool, error) {
|
func IsACLExtendable(c *client.Client, cnr cid.ID) (bool, error) {
|
||||||
prm := GetContainerPrm{
|
var prm GetContainerPrm
|
||||||
Client: c,
|
prm.SetClient(c)
|
||||||
ClientParams: client.PrmContainerGet{
|
prm.SetContainer(cnr)
|
||||||
ContainerID: &cnr,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := GetContainer(ctx, prm)
|
res, err := GetContainer(prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("get container from the FrostFS: %w", err)
|
return false, fmt.Errorf("get container from the FrostFS: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -166,8 +148,8 @@ func IsACLExtendable(ctx context.Context, c *client.Client, cnr cid.ID) (bool, e
|
||||||
|
|
||||||
// DeleteContainerPrm groups parameters of DeleteContainerPrm operation.
|
// DeleteContainerPrm groups parameters of DeleteContainerPrm operation.
|
||||||
type DeleteContainerPrm struct {
|
type DeleteContainerPrm struct {
|
||||||
Client *client.Client
|
commonPrm
|
||||||
ClientParams client.PrmContainerDelete
|
client.PrmContainerDelete
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteContainerRes groups the resulting values of DeleteContainer operation.
|
// DeleteContainerRes groups the resulting values of DeleteContainer operation.
|
||||||
|
@ -181,16 +163,16 @@ type DeleteContainerRes struct{}
|
||||||
// Success can be verified by reading by identifier.
|
// Success can be verified by reading by identifier.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func DeleteContainer(ctx context.Context, prm DeleteContainerPrm) (res DeleteContainerRes, err error) {
|
func DeleteContainer(prm DeleteContainerPrm) (res DeleteContainerRes, err error) {
|
||||||
_, err = prm.Client.ContainerDelete(ctx, prm.ClientParams)
|
_, err = prm.cli.ContainerDelete(context.Background(), prm.PrmContainerDelete)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// EACLPrm groups parameters of EACL operation.
|
// EACLPrm groups parameters of EACL operation.
|
||||||
type EACLPrm struct {
|
type EACLPrm struct {
|
||||||
Client *client.Client
|
commonPrm
|
||||||
ClientParams client.PrmContainerEACL
|
client.PrmContainerEACL
|
||||||
}
|
}
|
||||||
|
|
||||||
// EACLRes groups the resulting values of EACL operation.
|
// EACLRes groups the resulting values of EACL operation.
|
||||||
|
@ -206,16 +188,16 @@ func (x EACLRes) EACL() eacl.Table {
|
||||||
// EACL reads eACL table from FrostFS by container ID.
|
// EACL reads eACL table from FrostFS by container ID.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func EACL(ctx context.Context, prm EACLPrm) (res EACLRes, err error) {
|
func EACL(prm EACLPrm) (res EACLRes, err error) {
|
||||||
res.cliRes, err = prm.Client.ContainerEACL(ctx, prm.ClientParams)
|
res.cliRes, err = prm.cli.ContainerEACL(context.Background(), prm.PrmContainerEACL)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEACLPrm groups parameters of SetEACL operation.
|
// SetEACLPrm groups parameters of SetEACL operation.
|
||||||
type SetEACLPrm struct {
|
type SetEACLPrm struct {
|
||||||
Client *client.Client
|
commonPrm
|
||||||
ClientParams client.PrmContainerSetEACL
|
client.PrmContainerSetEACL
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEACLRes groups the resulting values of SetEACL operation.
|
// SetEACLRes groups the resulting values of SetEACL operation.
|
||||||
|
@ -229,16 +211,16 @@ type SetEACLRes struct{}
|
||||||
// Success can be verified by reading by container identifier.
|
// Success can be verified by reading by container identifier.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func SetEACL(ctx context.Context, prm SetEACLPrm) (res SetEACLRes, err error) {
|
func SetEACL(prm SetEACLPrm) (res SetEACLRes, err error) {
|
||||||
_, err = prm.Client.ContainerSetEACL(ctx, prm.ClientParams)
|
_, err = prm.cli.ContainerSetEACL(context.Background(), prm.PrmContainerSetEACL)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkInfoPrm groups parameters of NetworkInfo operation.
|
// NetworkInfoPrm groups parameters of NetworkInfo operation.
|
||||||
type NetworkInfoPrm struct {
|
type NetworkInfoPrm struct {
|
||||||
Client *client.Client
|
commonPrm
|
||||||
ClientParams client.PrmNetworkInfo
|
client.PrmNetworkInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkInfoRes groups the resulting values of NetworkInfo operation.
|
// NetworkInfoRes groups the resulting values of NetworkInfo operation.
|
||||||
|
@ -254,16 +236,16 @@ func (x NetworkInfoRes) NetworkInfo() netmap.NetworkInfo {
|
||||||
// NetworkInfo reads information about the FrostFS network.
|
// NetworkInfo reads information about the FrostFS network.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func NetworkInfo(ctx context.Context, prm NetworkInfoPrm) (res NetworkInfoRes, err error) {
|
func NetworkInfo(prm NetworkInfoPrm) (res NetworkInfoRes, err error) {
|
||||||
res.cliRes, err = prm.Client.NetworkInfo(ctx, prm.ClientParams)
|
res.cliRes, err = prm.cli.NetworkInfo(context.Background(), prm.PrmNetworkInfo)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeInfoPrm groups parameters of NodeInfo operation.
|
// NodeInfoPrm groups parameters of NodeInfo operation.
|
||||||
type NodeInfoPrm struct {
|
type NodeInfoPrm struct {
|
||||||
Client *client.Client
|
commonPrm
|
||||||
ClientParams client.PrmEndpointInfo
|
client.PrmEndpointInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeInfoRes groups the resulting values of NodeInfo operation.
|
// NodeInfoRes groups the resulting values of NodeInfo operation.
|
||||||
|
@ -284,8 +266,8 @@ func (x NodeInfoRes) LatestVersion() version.Version {
|
||||||
// NodeInfo requests information about the remote server from FrostFS netmap.
|
// NodeInfo requests information about the remote server from FrostFS netmap.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func NodeInfo(ctx context.Context, prm NodeInfoPrm) (res NodeInfoRes, err error) {
|
func NodeInfo(prm NodeInfoPrm) (res NodeInfoRes, err error) {
|
||||||
res.cliRes, err = prm.Client.EndpointInfo(ctx, prm.ClientParams)
|
res.cliRes, err = prm.cli.EndpointInfo(context.Background(), prm.PrmEndpointInfo)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -308,8 +290,8 @@ func (x NetMapSnapshotRes) NetMap() netmap.NetMap {
|
||||||
// NetMapSnapshot requests current network view of the remote server.
|
// NetMapSnapshot requests current network view of the remote server.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func NetMapSnapshot(ctx context.Context, prm NetMapSnapshotPrm) (res NetMapSnapshotRes, err error) {
|
func NetMapSnapshot(prm NetMapSnapshotPrm) (res NetMapSnapshotRes, err error) {
|
||||||
res.cliRes, err = prm.cli.NetMapSnapshot(ctx, client.PrmNetMapSnapshot{})
|
res.cliRes, err = prm.cli.NetMapSnapshot(context.Background(), client.PrmNetMapSnapshot{})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,8 +319,8 @@ func (x CreateSessionRes) SessionKey() []byte {
|
||||||
// CreateSession opens a new unlimited session with the remote node.
|
// CreateSession opens a new unlimited session with the remote node.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func CreateSession(ctx context.Context, prm CreateSessionPrm) (res CreateSessionRes, err error) {
|
func CreateSession(prm CreateSessionPrm) (res CreateSessionRes, err error) {
|
||||||
res.cliRes, err = prm.cli.SessionCreate(ctx, prm.PrmSessionCreate)
|
res.cliRes, err = prm.cli.SessionCreate(context.Background(), prm.PrmSessionCreate)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -347,19 +329,15 @@ func CreateSession(ctx context.Context, prm CreateSessionPrm) (res CreateSession
|
||||||
type PutObjectPrm struct {
|
type PutObjectPrm struct {
|
||||||
commonObjectPrm
|
commonObjectPrm
|
||||||
|
|
||||||
copyNum []uint32
|
hdr *object.Object
|
||||||
|
|
||||||
hdr *objectSDK.Object
|
|
||||||
|
|
||||||
rdr io.Reader
|
rdr io.Reader
|
||||||
|
|
||||||
headerCallback func(*objectSDK.Object)
|
headerCallback func(*object.Object)
|
||||||
|
|
||||||
prepareLocally bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetHeader sets object header.
|
// SetHeader sets object header.
|
||||||
func (x *PutObjectPrm) SetHeader(hdr *objectSDK.Object) {
|
func (x *PutObjectPrm) SetHeader(hdr *object.Object) {
|
||||||
x.hdr = hdr
|
x.hdr = hdr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,51 +348,10 @@ func (x *PutObjectPrm) SetPayloadReader(rdr io.Reader) {
|
||||||
|
|
||||||
// SetHeaderCallback sets callback which is called on the object after the header is received
|
// SetHeaderCallback sets callback which is called on the object after the header is received
|
||||||
// but before the payload is written.
|
// but before the payload is written.
|
||||||
func (x *PutObjectPrm) SetHeaderCallback(f func(*objectSDK.Object)) {
|
func (x *PutObjectPrm) SetHeaderCallback(f func(*object.Object)) {
|
||||||
x.headerCallback = f
|
x.headerCallback = f
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCopiesNumberByVectors sets ordered list of minimal required object copies numbers
|
|
||||||
// per placement vector.
|
|
||||||
func (x *PutObjectPrm) SetCopiesNumberByVectors(copiesNumbers []uint32) {
|
|
||||||
x.copyNum = copiesNumbers
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrepareLocally generate object header on the client side.
|
|
||||||
// For big object - split locally too.
|
|
||||||
func (x *PutObjectPrm) PrepareLocally() {
|
|
||||||
x.prepareLocally = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PutObjectPrm) convertToSDKPrm(ctx context.Context) (client.PrmObjectPutInit, error) {
|
|
||||||
var putPrm client.PrmObjectPutInit
|
|
||||||
if !x.prepareLocally && x.sessionToken != nil {
|
|
||||||
putPrm.WithinSession(*x.sessionToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
if x.bearerToken != nil {
|
|
||||||
putPrm.WithBearerToken(*x.bearerToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
if x.local {
|
|
||||||
putPrm.MarkLocal()
|
|
||||||
}
|
|
||||||
|
|
||||||
putPrm.WithXHeaders(x.xHeaders...)
|
|
||||||
putPrm.SetCopiesNumberByVectors(x.copyNum)
|
|
||||||
|
|
||||||
if x.prepareLocally {
|
|
||||||
res, err := x.cli.NetworkInfo(ctx, client.PrmNetworkInfo{})
|
|
||||||
if err != nil {
|
|
||||||
return client.PrmObjectPutInit{}, err
|
|
||||||
}
|
|
||||||
putPrm.WithObjectMaxSize(res.Info().MaxObjectSize())
|
|
||||||
putPrm.WithEpochSource(epochSource(res.Info().CurrentEpoch()))
|
|
||||||
putPrm.WithoutHomomorphicHash(res.Info().HomomorphicHashingDisabled())
|
|
||||||
}
|
|
||||||
return putPrm, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PutObjectRes groups the resulting values of PutObject operation.
|
// PutObjectRes groups the resulting values of PutObject operation.
|
||||||
type PutObjectRes struct {
|
type PutObjectRes struct {
|
||||||
id oid.ID
|
id oid.ID
|
||||||
|
@ -425,26 +362,32 @@ func (x PutObjectRes) ID() oid.ID {
|
||||||
return x.id
|
return x.id
|
||||||
}
|
}
|
||||||
|
|
||||||
type epochSource uint64
|
|
||||||
|
|
||||||
func (s epochSource) CurrentEpoch() uint64 {
|
|
||||||
return uint64(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PutObject saves the object in FrostFS network.
|
// PutObject saves the object in FrostFS network.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func PutObject(ctx context.Context, prm PutObjectPrm) (*PutObjectRes, error) {
|
func PutObject(prm PutObjectPrm) (*PutObjectRes, error) {
|
||||||
sdkPrm, err := prm.convertToSDKPrm(ctx)
|
var putPrm client.PrmObjectPutInit
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to create parameters of object put operation: %w", err)
|
if prm.sessionToken != nil {
|
||||||
|
putPrm.WithinSession(*prm.sessionToken)
|
||||||
}
|
}
|
||||||
wrt, err := prm.cli.ObjectPutInit(ctx, sdkPrm)
|
|
||||||
|
if prm.bearerToken != nil {
|
||||||
|
putPrm.WithBearerToken(*prm.bearerToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
if prm.local {
|
||||||
|
putPrm.MarkLocal()
|
||||||
|
}
|
||||||
|
|
||||||
|
putPrm.WithXHeaders(prm.xHeaders...)
|
||||||
|
|
||||||
|
wrt, err := prm.cli.ObjectPutInit(context.Background(), putPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("init object writing: %w", err)
|
return nil, fmt.Errorf("init object writing: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if wrt.WriteHeader(ctx, *prm.hdr) {
|
if wrt.WriteHeader(*prm.hdr) {
|
||||||
if prm.headerCallback != nil {
|
if prm.headerCallback != nil {
|
||||||
prm.headerCallback(prm.hdr)
|
prm.headerCallback(prm.hdr)
|
||||||
}
|
}
|
||||||
|
@ -474,7 +417,7 @@ func PutObject(ctx context.Context, prm PutObjectPrm) (*PutObjectRes, error) {
|
||||||
for {
|
for {
|
||||||
n, err = prm.rdr.Read(buf)
|
n, err = prm.rdr.Read(buf)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
if !wrt.WritePayloadChunk(ctx, buf[:n]) {
|
if !wrt.WritePayloadChunk(buf[:n]) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,7 +433,7 @@ func PutObject(ctx context.Context, prm PutObjectPrm) (*PutObjectRes, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cliRes, err := wrt.Close(ctx)
|
cliRes, err := wrt.Close()
|
||||||
if err != nil { // here err already carries both status and client errors
|
if err != nil { // here err already carries both status and client errors
|
||||||
return nil, fmt.Errorf("client failure: %w", err)
|
return nil, fmt.Errorf("client failure: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -519,19 +462,22 @@ func (x DeleteObjectRes) Tombstone() oid.ID {
|
||||||
// DeleteObject marks an object to be removed from FrostFS through tombstone placement.
|
// DeleteObject marks an object to be removed from FrostFS through tombstone placement.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func DeleteObject(ctx context.Context, prm DeleteObjectPrm) (*DeleteObjectRes, error) {
|
func DeleteObject(prm DeleteObjectPrm) (*DeleteObjectRes, error) {
|
||||||
cnr := prm.objAddr.Container()
|
var delPrm client.PrmObjectDelete
|
||||||
obj := prm.objAddr.Object()
|
delPrm.FromContainer(prm.objAddr.Container())
|
||||||
|
delPrm.ByID(prm.objAddr.Object())
|
||||||
|
|
||||||
delPrm := client.PrmObjectDelete{
|
if prm.sessionToken != nil {
|
||||||
XHeaders: prm.xHeaders,
|
delPrm.WithinSession(*prm.sessionToken)
|
||||||
ContainerID: &cnr,
|
|
||||||
ObjectID: &obj,
|
|
||||||
Session: prm.sessionToken,
|
|
||||||
BearerToken: prm.bearerToken,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cliRes, err := prm.cli.ObjectDelete(ctx, delPrm)
|
if prm.bearerToken != nil {
|
||||||
|
delPrm.WithBearerToken(*prm.bearerToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
delPrm.WithXHeaders(prm.xHeaders...)
|
||||||
|
|
||||||
|
cliRes, err := prm.cli.ObjectDelete(context.Background(), delPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("remove object via client: %w", err)
|
return nil, fmt.Errorf("remove object via client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -547,22 +493,22 @@ type GetObjectPrm struct {
|
||||||
objectAddressPrm
|
objectAddressPrm
|
||||||
rawPrm
|
rawPrm
|
||||||
payloadWriterPrm
|
payloadWriterPrm
|
||||||
headerCallback func(*objectSDK.Object)
|
headerCallback func(*object.Object)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetHeaderCallback sets callback which is called on the object after the header is received
|
// SetHeaderCallback sets callback which is called on the object after the header is received
|
||||||
// but before the payload is written.
|
// but before the payload is written.
|
||||||
func (p *GetObjectPrm) SetHeaderCallback(f func(*objectSDK.Object)) {
|
func (p *GetObjectPrm) SetHeaderCallback(f func(*object.Object)) {
|
||||||
p.headerCallback = f
|
p.headerCallback = f
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetObjectRes groups the resulting values of GetObject operation.
|
// GetObjectRes groups the resulting values of GetObject operation.
|
||||||
type GetObjectRes struct {
|
type GetObjectRes struct {
|
||||||
hdr *objectSDK.Object
|
hdr *object.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header returns the header of the request object.
|
// Header returns the header of the request object.
|
||||||
func (x GetObjectRes) Header() *objectSDK.Object {
|
func (x GetObjectRes) Header() *object.Object {
|
||||||
return x.hdr
|
return x.hdr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,26 +518,35 @@ func (x GetObjectRes) Header() *objectSDK.Object {
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
// For raw reading, returns *object.SplitInfoError error if object is virtual.
|
// For raw reading, returns *object.SplitInfoError error if object is virtual.
|
||||||
func GetObject(ctx context.Context, prm GetObjectPrm) (*GetObjectRes, error) {
|
func GetObject(prm GetObjectPrm) (*GetObjectRes, error) {
|
||||||
cnr := prm.objAddr.Container()
|
var getPrm client.PrmObjectGet
|
||||||
obj := prm.objAddr.Object()
|
getPrm.FromContainer(prm.objAddr.Container())
|
||||||
|
getPrm.ByID(prm.objAddr.Object())
|
||||||
|
|
||||||
getPrm := client.PrmObjectGet{
|
if prm.sessionToken != nil {
|
||||||
XHeaders: prm.xHeaders,
|
getPrm.WithinSession(*prm.sessionToken)
|
||||||
BearerToken: prm.bearerToken,
|
|
||||||
Session: prm.sessionToken,
|
|
||||||
Raw: prm.raw,
|
|
||||||
Local: prm.local,
|
|
||||||
ContainerID: &cnr,
|
|
||||||
ObjectID: &obj,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rdr, err := prm.cli.ObjectGetInit(ctx, getPrm)
|
if prm.bearerToken != nil {
|
||||||
|
getPrm.WithBearerToken(*prm.bearerToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
if prm.raw {
|
||||||
|
getPrm.MarkRaw()
|
||||||
|
}
|
||||||
|
|
||||||
|
if prm.local {
|
||||||
|
getPrm.MarkLocal()
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrm.WithXHeaders(prm.xHeaders...)
|
||||||
|
|
||||||
|
rdr, err := prm.cli.ObjectGetInit(context.Background(), getPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("init object reading on client: %w", err)
|
return nil, fmt.Errorf("init object reading on client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var hdr objectSDK.Object
|
var hdr object.Object
|
||||||
|
|
||||||
if !rdr.ReadHeader(&hdr) {
|
if !rdr.ReadHeader(&hdr) {
|
||||||
_, err = rdr.Close()
|
_, err = rdr.Close()
|
||||||
|
@ -627,11 +582,11 @@ func (x *HeadObjectPrm) SetMainOnlyFlag(v bool) {
|
||||||
|
|
||||||
// HeadObjectRes groups the resulting values of HeadObject operation.
|
// HeadObjectRes groups the resulting values of HeadObject operation.
|
||||||
type HeadObjectRes struct {
|
type HeadObjectRes struct {
|
||||||
hdr *objectSDK.Object
|
hdr *object.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header returns the requested object header.
|
// Header returns the requested object header.
|
||||||
func (x HeadObjectRes) Header() *objectSDK.Object {
|
func (x HeadObjectRes) Header() *object.Object {
|
||||||
return x.hdr
|
return x.hdr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,26 +594,35 @@ func (x HeadObjectRes) Header() *objectSDK.Object {
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
// For raw reading, returns *object.SplitInfoError error if object is virtual.
|
// For raw reading, returns *object.SplitInfoError error if object is virtual.
|
||||||
func HeadObject(ctx context.Context, prm HeadObjectPrm) (*HeadObjectRes, error) {
|
func HeadObject(prm HeadObjectPrm) (*HeadObjectRes, error) {
|
||||||
cnr := prm.objAddr.Container()
|
var cliPrm client.PrmObjectHead
|
||||||
obj := prm.objAddr.Object()
|
cliPrm.FromContainer(prm.objAddr.Container())
|
||||||
|
cliPrm.ByID(prm.objAddr.Object())
|
||||||
|
|
||||||
headPrm := client.PrmObjectHead{
|
if prm.sessionToken != nil {
|
||||||
XHeaders: prm.xHeaders,
|
cliPrm.WithinSession(*prm.sessionToken)
|
||||||
BearerToken: prm.bearerToken,
|
|
||||||
Session: prm.sessionToken,
|
|
||||||
Raw: prm.raw,
|
|
||||||
Local: prm.local,
|
|
||||||
ContainerID: &cnr,
|
|
||||||
ObjectID: &obj,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := prm.cli.ObjectHead(ctx, headPrm)
|
if prm.bearerToken != nil {
|
||||||
|
cliPrm.WithBearerToken(*prm.bearerToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
if prm.raw {
|
||||||
|
cliPrm.MarkRaw()
|
||||||
|
}
|
||||||
|
|
||||||
|
if prm.local {
|
||||||
|
cliPrm.MarkLocal()
|
||||||
|
}
|
||||||
|
|
||||||
|
cliPrm.WithXHeaders(prm.xHeaders...)
|
||||||
|
|
||||||
|
res, err := prm.cli.ObjectHead(context.Background(), cliPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("read object header via client: %w", err)
|
return nil, fmt.Errorf("read object header via client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var hdr objectSDK.Object
|
var hdr object.Object
|
||||||
|
|
||||||
if !res.ReadHeader(&hdr) {
|
if !res.ReadHeader(&hdr) {
|
||||||
return nil, fmt.Errorf("missing header in response")
|
return nil, fmt.Errorf("missing header in response")
|
||||||
|
@ -674,11 +638,11 @@ type SearchObjectsPrm struct {
|
||||||
commonObjectPrm
|
commonObjectPrm
|
||||||
containerIDPrm
|
containerIDPrm
|
||||||
|
|
||||||
filters objectSDK.SearchFilters
|
filters object.SearchFilters
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFilters sets search filters.
|
// SetFilters sets search filters.
|
||||||
func (x *SearchObjectsPrm) SetFilters(filters objectSDK.SearchFilters) {
|
func (x *SearchObjectsPrm) SetFilters(filters object.SearchFilters) {
|
||||||
x.filters = filters
|
x.filters = filters
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -695,7 +659,7 @@ func (x SearchObjectsRes) IDList() []oid.ID {
|
||||||
// SearchObjects selects objects from the container which match the filters.
|
// SearchObjects selects objects from the container which match the filters.
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
func SearchObjects(ctx context.Context, prm SearchObjectsPrm) (*SearchObjectsRes, error) {
|
func SearchObjects(prm SearchObjectsPrm) (*SearchObjectsRes, error) {
|
||||||
var cliPrm client.PrmObjectSearch
|
var cliPrm client.PrmObjectSearch
|
||||||
cliPrm.InContainer(prm.cnrID)
|
cliPrm.InContainer(prm.cnrID)
|
||||||
cliPrm.SetFilters(prm.filters)
|
cliPrm.SetFilters(prm.filters)
|
||||||
|
@ -714,7 +678,7 @@ func SearchObjects(ctx context.Context, prm SearchObjectsPrm) (*SearchObjectsRes
|
||||||
|
|
||||||
cliPrm.WithXHeaders(prm.xHeaders...)
|
cliPrm.WithXHeaders(prm.xHeaders...)
|
||||||
|
|
||||||
rdr, err := prm.cli.ObjectSearchInit(ctx, cliPrm)
|
rdr, err := prm.cli.ObjectSearchInit(context.Background(), cliPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("init object search: %w", err)
|
return nil, fmt.Errorf("init object search: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -739,11 +703,6 @@ func SearchObjects(ctx context.Context, prm SearchObjectsPrm) (*SearchObjectsRes
|
||||||
return nil, fmt.Errorf("read object list: %w", err)
|
return nil, fmt.Errorf("read object list: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(list, func(i, j int) bool {
|
|
||||||
lhs, rhs := list[i].EncodeToString(), list[j].EncodeToString()
|
|
||||||
return strings.Compare(lhs, rhs) < 0
|
|
||||||
})
|
|
||||||
|
|
||||||
return &SearchObjectsRes{
|
return &SearchObjectsRes{
|
||||||
ids: list,
|
ids: list,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -756,7 +715,7 @@ type HashPayloadRangesPrm struct {
|
||||||
|
|
||||||
tz bool
|
tz bool
|
||||||
|
|
||||||
rngs []objectSDK.Range
|
rngs []*object.Range
|
||||||
|
|
||||||
salt []byte
|
salt []byte
|
||||||
}
|
}
|
||||||
|
@ -767,7 +726,7 @@ func (x *HashPayloadRangesPrm) TZ() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRanges sets a list of payload ranges to hash.
|
// SetRanges sets a list of payload ranges to hash.
|
||||||
func (x *HashPayloadRangesPrm) SetRanges(rngs []objectSDK.Range) {
|
func (x *HashPayloadRangesPrm) SetRanges(rngs []*object.Range) {
|
||||||
x.rngs = rngs
|
x.rngs = rngs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -790,27 +749,41 @@ func (x HashPayloadRangesRes) HashList() [][]byte {
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
// Returns an error if number of received hashes differs with the number of requested ranges.
|
// Returns an error if number of received hashes differs with the number of requested ranges.
|
||||||
func HashPayloadRanges(ctx context.Context, prm HashPayloadRangesPrm) (*HashPayloadRangesRes, error) {
|
func HashPayloadRanges(prm HashPayloadRangesPrm) (*HashPayloadRangesRes, error) {
|
||||||
cs := checksum.SHA256
|
var cliPrm client.PrmObjectHash
|
||||||
|
cliPrm.FromContainer(prm.objAddr.Container())
|
||||||
|
cliPrm.ByID(prm.objAddr.Object())
|
||||||
|
|
||||||
|
if prm.local {
|
||||||
|
cliPrm.MarkLocal()
|
||||||
|
}
|
||||||
|
|
||||||
|
cliPrm.UseSalt(prm.salt)
|
||||||
|
|
||||||
|
rngs := make([]uint64, 2*len(prm.rngs))
|
||||||
|
|
||||||
|
for i := range prm.rngs {
|
||||||
|
rngs[2*i] = prm.rngs[i].GetOffset()
|
||||||
|
rngs[2*i+1] = prm.rngs[i].GetLength()
|
||||||
|
}
|
||||||
|
|
||||||
|
cliPrm.SetRangeList(rngs...)
|
||||||
|
|
||||||
if prm.tz {
|
if prm.tz {
|
||||||
cs = checksum.TZ
|
cliPrm.TillichZemorAlgo()
|
||||||
}
|
}
|
||||||
|
|
||||||
cnr := prm.objAddr.Container()
|
if prm.sessionToken != nil {
|
||||||
obj := prm.objAddr.Object()
|
cliPrm.WithinSession(*prm.sessionToken)
|
||||||
cliPrm := client.PrmObjectHash{
|
|
||||||
ContainerID: &cnr,
|
|
||||||
ObjectID: &obj,
|
|
||||||
Local: prm.local,
|
|
||||||
Salt: prm.salt,
|
|
||||||
Ranges: prm.rngs,
|
|
||||||
ChecksumType: cs,
|
|
||||||
Session: prm.sessionToken,
|
|
||||||
BearerToken: prm.bearerToken,
|
|
||||||
XHeaders: prm.xHeaders,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := prm.cli.ObjectHash(ctx, cliPrm)
|
if prm.bearerToken != nil {
|
||||||
|
cliPrm.WithBearerToken(*prm.bearerToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
cliPrm.WithXHeaders(prm.xHeaders...)
|
||||||
|
|
||||||
|
res, err := prm.cli.ObjectHash(context.Background(), cliPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("read payload hashes via client: %w", err)
|
return nil, fmt.Errorf("read payload hashes via client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -827,11 +800,11 @@ type PayloadRangePrm struct {
|
||||||
rawPrm
|
rawPrm
|
||||||
payloadWriterPrm
|
payloadWriterPrm
|
||||||
|
|
||||||
rng *objectSDK.Range
|
rng *object.Range
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRange sets payload range to read.
|
// SetRange sets payload range to read.
|
||||||
func (x *PayloadRangePrm) SetRange(rng *objectSDK.Range) {
|
func (x *PayloadRangePrm) SetRange(rng *object.Range) {
|
||||||
x.rng = rng
|
x.rng = rng
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -844,23 +817,33 @@ type PayloadRangeRes struct{}
|
||||||
//
|
//
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
// For raw reading, returns *object.SplitInfoError error if object is virtual.
|
// For raw reading, returns *object.SplitInfoError error if object is virtual.
|
||||||
func PayloadRange(ctx context.Context, prm PayloadRangePrm) (*PayloadRangeRes, error) {
|
func PayloadRange(prm PayloadRangePrm) (*PayloadRangeRes, error) {
|
||||||
cnr := prm.objAddr.Container()
|
var cliPrm client.PrmObjectRange
|
||||||
obj := prm.objAddr.Object()
|
cliPrm.FromContainer(prm.objAddr.Container())
|
||||||
|
cliPrm.ByID(prm.objAddr.Object())
|
||||||
|
|
||||||
rangePrm := client.PrmObjectRange{
|
if prm.sessionToken != nil {
|
||||||
XHeaders: prm.xHeaders,
|
cliPrm.WithinSession(*prm.sessionToken)
|
||||||
BearerToken: prm.bearerToken,
|
|
||||||
Session: prm.sessionToken,
|
|
||||||
Raw: prm.raw,
|
|
||||||
Local: prm.local,
|
|
||||||
ContainerID: &cnr,
|
|
||||||
ObjectID: &obj,
|
|
||||||
Offset: prm.rng.GetOffset(),
|
|
||||||
Length: prm.rng.GetLength(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rdr, err := prm.cli.ObjectRangeInit(ctx, rangePrm)
|
if prm.bearerToken != nil {
|
||||||
|
cliPrm.WithBearerToken(*prm.bearerToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
if prm.raw {
|
||||||
|
cliPrm.MarkRaw()
|
||||||
|
}
|
||||||
|
|
||||||
|
if prm.local {
|
||||||
|
cliPrm.MarkLocal()
|
||||||
|
}
|
||||||
|
|
||||||
|
cliPrm.SetOffset(prm.rng.GetOffset())
|
||||||
|
cliPrm.SetLength(prm.rng.GetLength())
|
||||||
|
|
||||||
|
cliPrm.WithXHeaders(prm.xHeaders...)
|
||||||
|
|
||||||
|
rdr, err := prm.cli.ObjectRangeInit(context.Background(), cliPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("init payload reading: %w", err)
|
return nil, fmt.Errorf("init payload reading: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -894,12 +877,12 @@ type SyncContainerRes struct{}
|
||||||
// Interrupts on any writer error.
|
// Interrupts on any writer error.
|
||||||
//
|
//
|
||||||
// Panics if a container passed as a parameter is nil.
|
// Panics if a container passed as a parameter is nil.
|
||||||
func SyncContainerSettings(ctx context.Context, prm SyncContainerPrm) (*SyncContainerRes, error) {
|
func SyncContainerSettings(prm SyncContainerPrm) (*SyncContainerRes, error) {
|
||||||
if prm.c == nil {
|
if prm.c == nil {
|
||||||
panic("sync container settings with the network: nil container")
|
panic("sync container settings with the network: nil container")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.SyncContainerWithNetwork(ctx, prm.c, prm.cli)
|
err := client.SyncContainerWithNetwork(context.Background(), prm.c, prm.cli)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,11 @@ package internal
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
"github.com/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
"github.com/TrueCloudLab/frostfs-sdk-go/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
// here are small structures with public setters to share between parameter structures
|
// here are small structures with public setters to share between parameter structures
|
||||||
|
|
|
@ -8,15 +8,13 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/network"
|
"github.com/TrueCloudLab/frostfs-node/pkg/network"
|
||||||
tracing "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
|
"github.com/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var errInvalidEndpoint = errors.New("provided RPC endpoint is incorrect")
|
var errInvalidEndpoint = errors.New("provided RPC endpoint is incorrect")
|
||||||
|
@ -38,11 +36,11 @@ func getSDKClientByFlag(cmd *cobra.Command, key *ecdsa.PrivateKey, endpointFlag
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%v: %w", errInvalidEndpoint, err)
|
return nil, fmt.Errorf("%v: %w", errInvalidEndpoint, err)
|
||||||
}
|
}
|
||||||
return GetSDKClient(cmd.Context(), cmd, key, addr)
|
return GetSDKClient(cmd, key, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSDKClient returns default frostfs-sdk-go client.
|
// GetSDKClient returns default frostfs-sdk-go client.
|
||||||
func GetSDKClient(ctx context.Context, cmd *cobra.Command, key *ecdsa.PrivateKey, addr network.Address) (*client.Client, error) {
|
func GetSDKClient(cmd *cobra.Command, key *ecdsa.PrivateKey, addr network.Address) (*client.Client, error) {
|
||||||
var (
|
var (
|
||||||
c client.Client
|
c client.Client
|
||||||
prmInit client.PrmInit
|
prmInit client.PrmInit
|
||||||
|
@ -50,7 +48,7 @@ func GetSDKClient(ctx context.Context, cmd *cobra.Command, key *ecdsa.PrivateKey
|
||||||
)
|
)
|
||||||
|
|
||||||
prmInit.SetDefaultPrivateKey(*key)
|
prmInit.SetDefaultPrivateKey(*key)
|
||||||
prmInit.ResolveFrostFSFailures()
|
prmInit.ResolveNeoFSFailures()
|
||||||
prmDial.SetServerURI(addr.URIAddr())
|
prmDial.SetServerURI(addr.URIAddr())
|
||||||
if timeout := viper.GetDuration(commonflags.Timeout); timeout > 0 {
|
if timeout := viper.GetDuration(commonflags.Timeout); timeout > 0 {
|
||||||
// In CLI we can only set a timeout for the whole operation.
|
// In CLI we can only set a timeout for the whole operation.
|
||||||
|
@ -61,13 +59,10 @@ func GetSDKClient(ctx context.Context, cmd *cobra.Command, key *ecdsa.PrivateKey
|
||||||
|
|
||||||
common.PrintVerbose(cmd, "Set request timeout to %s.", timeout)
|
common.PrintVerbose(cmd, "Set request timeout to %s.", timeout)
|
||||||
}
|
}
|
||||||
prmDial.SetGRPCDialOptions(
|
|
||||||
grpc.WithChainUnaryInterceptor(tracing.NewUnaryClientInteceptor()),
|
|
||||||
grpc.WithChainStreamInterceptor(tracing.NewStreamClientInterceptor()))
|
|
||||||
|
|
||||||
c.Init(prmInit)
|
c.Init(prmInit)
|
||||||
|
|
||||||
if err := c.Dial(ctx, prmDial); err != nil {
|
if err := c.Dial(prmDial); err != nil {
|
||||||
return nil, fmt.Errorf("can't init SDK client: %w", err)
|
return nil, fmt.Errorf("can't init SDK client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +82,7 @@ func GetCurrentEpoch(ctx context.Context, cmd *cobra.Command, endpoint string) (
|
||||||
return 0, fmt.Errorf("can't generate key to sign query: %w", err)
|
return 0, fmt.Errorf("can't generate key to sign query: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := GetSDKClient(ctx, cmd, key, addr)
|
c, err := GetSDKClient(cmd, key, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,10 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/version"
|
"github.com/TrueCloudLab/frostfs-node/pkg/core/version"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
"github.com/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
versionSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
versionSDK "github.com/TrueCloudLab/frostfs-sdk-go/version"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"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/misc"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
|
||||||
)
|
|
||||||
|
|
||||||
type spanKey struct{}
|
|
||||||
|
|
||||||
// StopClientCommandSpan stops tracing span for the command and prints trace ID on the standard output.
|
|
||||||
func StopClientCommandSpan(cmd *cobra.Command, _ []string) {
|
|
||||||
span, ok := cmd.Context().Value(spanKey{}).(trace.Span)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
span.End()
|
|
||||||
|
|
||||||
// Noop provider cannot fail on flush.
|
|
||||||
_ = tracing.Shutdown(cmd.Context())
|
|
||||||
|
|
||||||
cmd.PrintErrf("Trace ID: %s\n", span.SpanContext().TraceID())
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartClientCommandSpan starts tracing span for the command.
|
|
||||||
func StartClientCommandSpan(cmd *cobra.Command) {
|
|
||||||
enableTracing, err := cmd.Flags().GetBool(commonflags.TracingFlag)
|
|
||||||
if err != nil || !enableTracing {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = tracing.Setup(cmd.Context(), tracing.Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: tracing.NoOpExporter,
|
|
||||||
Service: "frostfs-cli",
|
|
||||||
Version: misc.Version,
|
|
||||||
})
|
|
||||||
commonCmd.ExitOnErr(cmd, "init tracing: %w", err)
|
|
||||||
|
|
||||||
var components sort.StringSlice
|
|
||||||
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--
|
|
||||||
}
|
|
||||||
|
|
||||||
operation := strings.Join(components, ".")
|
|
||||||
ctx, span := tracing.StartSpanFromContext(cmd.Context(), operation)
|
|
||||||
ctx = context.WithValue(ctx, spanKey{}, span)
|
|
||||||
cmd.SetContext(ctx)
|
|
||||||
}
|
|
|
@ -5,8 +5,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
|
"github.com/TrueCloudLab/frostfs-sdk-go/checksum"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
|
@ -47,9 +47,6 @@ const (
|
||||||
|
|
||||||
OIDFlag = "oid"
|
OIDFlag = "oid"
|
||||||
OIDFlagUsage = "Object ID."
|
OIDFlagUsage = "Object ID."
|
||||||
|
|
||||||
TracingFlag = "trace"
|
|
||||||
TracingFlagUsage = "Generate trace ID and print it."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init adds common flags to the command:
|
// Init adds common flags to the command:
|
||||||
|
@ -57,14 +54,12 @@ const (
|
||||||
// - WalletPath,
|
// - WalletPath,
|
||||||
// - Account,
|
// - Account,
|
||||||
// - RPC,
|
// - RPC,
|
||||||
// - Tracing,
|
|
||||||
// - Timeout.
|
// - Timeout.
|
||||||
func Init(cmd *cobra.Command) {
|
func Init(cmd *cobra.Command) {
|
||||||
InitWithoutRPC(cmd)
|
InitWithoutRPC(cmd)
|
||||||
|
|
||||||
ff := cmd.Flags()
|
ff := cmd.Flags()
|
||||||
ff.StringP(RPC, RPCShorthand, RPCDefault, RPCUsage)
|
ff.StringP(RPC, RPCShorthand, RPCDefault, RPCUsage)
|
||||||
ff.Bool(TracingFlag, false, TracingFlagUsage)
|
|
||||||
ff.DurationP(Timeout, TimeoutShorthand, TimeoutDefault, TimeoutUsage)
|
ff.DurationP(Timeout, TimeoutShorthand, TimeoutDefault, TimeoutUsage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"github.com/nspcc-dev/neo-go/cli/input"
|
"github.com/nspcc-dev/neo-go/cli/input"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
|
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
||||||
"github.com/nspcc-dev/neo-go/cli/flags"
|
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||||
"github.com/nspcc-dev/neo-go/cli/input"
|
"github.com/nspcc-dev/neo-go/cli/input"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import cmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules"
|
import cmd "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cmd.Execute()
|
cmd.Execute()
|
||||||
|
|
|
@ -3,13 +3,13 @@ package accounting
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/precision"
|
"github.com/TrueCloudLab/frostfs-node/pkg/util/precision"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
|
"github.com/TrueCloudLab/frostfs-sdk-go/accounting"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
@ -41,7 +41,7 @@ var accountingBalanceCmd = &cobra.Command{
|
||||||
prm.SetClient(cli)
|
prm.SetClient(cli)
|
||||||
prm.SetAccount(idUser)
|
prm.SetAccount(idUser)
|
||||||
|
|
||||||
res, err := internalclient.BalanceOf(cmd.Context(), prm)
|
res, err := internalclient.BalanceOf(prm)
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
|
||||||
// print to stdout
|
// print to stdout
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
package accounting
|
package accounting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
@ -18,9 +17,7 @@ var Cmd = &cobra.Command{
|
||||||
_ = viper.BindPFlag(commonflags.WalletPath, flags.Lookup(commonflags.WalletPath))
|
_ = viper.BindPFlag(commonflags.WalletPath, flags.Lookup(commonflags.WalletPath))
|
||||||
_ = viper.BindPFlag(commonflags.Account, flags.Lookup(commonflags.Account))
|
_ = viper.BindPFlag(commonflags.Account, flags.Lookup(commonflags.Account))
|
||||||
_ = viper.BindPFlag(commonflags.RPC, flags.Lookup(commonflags.RPC))
|
_ = viper.BindPFlag(commonflags.RPC, flags.Lookup(commonflags.RPC))
|
||||||
common.StartClientCommandSpan(cmd)
|
|
||||||
},
|
},
|
||||||
PersistentPostRun: common.StopClientCommandSpan,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package basic
|
package basic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
"github.com/TrueCloudLab/frostfs-sdk-go/container/acl"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,11 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
"github.com/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,18 +26,18 @@ Action is 'allow' or 'deny'.
|
||||||
Operation is an object service verb: 'get', 'head', 'put', 'search', 'delete', 'getrange', or 'getrangehash'.
|
Operation is an object service verb: 'get', 'head', 'put', 'search', 'delete', 'getrange', or 'getrangehash'.
|
||||||
|
|
||||||
Filter consists of <typ>:<key><match><value>
|
Filter consists of <typ>:<key><match><value>
|
||||||
Typ is 'obj' for object applied filter or 'req' for request applied filter.
|
Typ is 'obj' for object applied filter or 'req' for request applied filter.
|
||||||
Key is a valid unicode string corresponding to object or request header key.
|
Key is a valid unicode string corresponding to object or request header key.
|
||||||
Well-known system object headers start with '$Object:' prefix.
|
Well-known system object headers start with '$Object:' prefix.
|
||||||
User defined headers start without prefix.
|
User defined headers start without prefix.
|
||||||
Read more about filter keys at git.frostfs.info.com/TrueCloudLab/frostfs-api/src/branch/master/proto-docs/acl.md#message-eaclrecordfilter
|
Read more about filter keys at github.com/TrueCloudLab/frostfs-api/blob/master/proto-docs/acl.md#message-eaclrecordfilter
|
||||||
Match is '=' for matching and '!=' for non-matching filter.
|
Match is '=' for matching and '!=' for non-matching filter.
|
||||||
Value is a valid unicode string corresponding to object or request header value.
|
Value is a valid unicode string corresponding to object or request header value.
|
||||||
|
|
||||||
Target is
|
Target is
|
||||||
'user' for container owner,
|
'user' for container owner,
|
||||||
'system' for Storage nodes in container and Inner Ring nodes,
|
'system' for Storage nodes in container and Inner Ring nodes,
|
||||||
'others' for all other request senders,
|
'others' for all other request senders,
|
||||||
'pubkey:<key1>,<key2>,...' for exact request sender, where <key> is a hex-encoded 33-byte public key.
|
'pubkey:<key1>,<key2>,...' for exact request sender, where <key> is a hex-encoded 33-byte public key.
|
||||||
|
|
||||||
When both '--rule' and '--file' arguments are used, '--rule' records will be placed higher in resulting extended ACL table.
|
When both '--rule' and '--file' arguments are used, '--rule' records will be placed higher in resulting extended ACL table.
|
||||||
|
|
|
@ -3,8 +3,8 @@ package extended
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
"github.com/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
"github.com/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package acl
|
package acl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/acl/basic"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/acl/basic"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/acl/extended"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/acl/extended"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,13 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||||
eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
eaclSDK "github.com/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ const (
|
||||||
ownerFlag = "owner"
|
ownerFlag = "owner"
|
||||||
outFlag = "out"
|
outFlag = "out"
|
||||||
jsonFlag = commonflags.JSON
|
jsonFlag = commonflags.JSON
|
||||||
impersonateFlag = "impersonate"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var createCmd = &cobra.Command{
|
var createCmd = &cobra.Command{
|
||||||
|
@ -40,20 +39,19 @@ is set to current epoch + n.
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createCmd.Flags().StringP(eaclFlag, "e", "", "Path to the extended ACL table (mutually exclusive with --impersonate flag)")
|
createCmd.Flags().StringP(eaclFlag, "e", "", "Path to the extended ACL table")
|
||||||
createCmd.Flags().StringP(issuedAtFlag, "i", "+0", "Epoch to issue token at")
|
createCmd.Flags().StringP(issuedAtFlag, "i", "", "Epoch to issue token at")
|
||||||
createCmd.Flags().StringP(notValidBeforeFlag, "n", "+0", "Not valid before epoch")
|
createCmd.Flags().StringP(notValidBeforeFlag, "n", "", "Not valid before epoch")
|
||||||
createCmd.Flags().StringP(commonflags.ExpireAt, "x", "", "The last active epoch for the token")
|
createCmd.Flags().StringP(commonflags.ExpireAt, "x", "", "The last active epoch for the token")
|
||||||
createCmd.Flags().StringP(ownerFlag, "o", "", "Token owner")
|
createCmd.Flags().StringP(ownerFlag, "o", "", "Token owner")
|
||||||
createCmd.Flags().String(outFlag, "", "File to write token to")
|
createCmd.Flags().String(outFlag, "", "File to write token to")
|
||||||
createCmd.Flags().Bool(jsonFlag, false, "Output token in JSON")
|
createCmd.Flags().Bool(jsonFlag, false, "Output token in JSON")
|
||||||
createCmd.Flags().Bool(impersonateFlag, false, "Mark token as impersonate to consider the token signer as the request owner (mutually exclusive with --eacl flag)")
|
|
||||||
createCmd.Flags().StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage)
|
createCmd.Flags().StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage)
|
||||||
|
|
||||||
createCmd.MarkFlagsMutuallyExclusive(eaclFlag, impersonateFlag)
|
|
||||||
|
|
||||||
_ = cobra.MarkFlagFilename(createCmd.Flags(), eaclFlag)
|
_ = cobra.MarkFlagFilename(createCmd.Flags(), eaclFlag)
|
||||||
|
|
||||||
|
_ = cobra.MarkFlagRequired(createCmd.Flags(), issuedAtFlag)
|
||||||
|
_ = cobra.MarkFlagRequired(createCmd.Flags(), notValidBeforeFlag)
|
||||||
_ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.ExpireAt)
|
_ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.ExpireAt)
|
||||||
_ = cobra.MarkFlagRequired(createCmd.Flags(), ownerFlag)
|
_ = cobra.MarkFlagRequired(createCmd.Flags(), ownerFlag)
|
||||||
_ = cobra.MarkFlagRequired(createCmd.Flags(), outFlag)
|
_ = cobra.MarkFlagRequired(createCmd.Flags(), outFlag)
|
||||||
|
@ -70,14 +68,10 @@ func createToken(cmd *cobra.Command, _ []string) {
|
||||||
commonCmd.ExitOnErr(cmd, "can't parse --"+notValidBeforeFlag+" flag: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't parse --"+notValidBeforeFlag+" flag: %w", err)
|
||||||
|
|
||||||
if iatRelative || expRelative || nvbRelative {
|
if iatRelative || expRelative || nvbRelative {
|
||||||
endpoint, _ := cmd.Flags().GetString(commonflags.RPC)
|
|
||||||
if len(endpoint) == 0 {
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't fetch current epoch: %w", fmt.Errorf("'%s' flag value must be specified", commonflags.RPC))
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
endpoint, _ := cmd.Flags().GetString(commonflags.RPC)
|
||||||
currEpoch, err := internalclient.GetCurrentEpoch(ctx, cmd, endpoint)
|
currEpoch, err := internalclient.GetCurrentEpoch(ctx, cmd, endpoint)
|
||||||
commonCmd.ExitOnErr(cmd, "can't fetch current epoch: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't fetch current epoch: %w", err)
|
||||||
|
|
||||||
|
@ -107,9 +101,6 @@ func createToken(cmd *cobra.Command, _ []string) {
|
||||||
b.SetIat(iat)
|
b.SetIat(iat)
|
||||||
b.ForUser(ownerID)
|
b.ForUser(ownerID)
|
||||||
|
|
||||||
impersonate, _ := cmd.Flags().GetBool(impersonateFlag)
|
|
||||||
b.SetImpersonate(impersonate)
|
|
||||||
|
|
||||||
eaclPath, _ := cmd.Flags().GetString(eaclFlag)
|
eaclPath, _ := cmd.Flags().GetString(eaclFlag)
|
||||||
if eaclPath != "" {
|
if eaclPath != "" {
|
||||||
table := eaclSDK.NewTable()
|
table := eaclSDK.NewTable()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/autocomplete"
|
"github.com/TrueCloudLab/frostfs-node/pkg/util/autocomplete"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -7,17 +7,17 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
containerApi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
containerApi "github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
"github.com/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
"github.com/TrueCloudLab/frostfs-sdk-go/container/acl"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
subnetid "github.com/TrueCloudLab/frostfs-sdk-go/subnet/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,13 +30,14 @@ var (
|
||||||
containerNnsName string
|
containerNnsName string
|
||||||
containerNnsZone string
|
containerNnsZone string
|
||||||
containerNoTimestamp bool
|
containerNoTimestamp bool
|
||||||
|
containerSubnet string
|
||||||
force bool
|
force bool
|
||||||
)
|
)
|
||||||
|
|
||||||
var createContainerCmd = &cobra.Command{
|
var createContainerCmd = &cobra.Command{
|
||||||
Use: "create",
|
Use: "create",
|
||||||
Short: "Create new container",
|
Short: "Create new container",
|
||||||
Long: `Create new container and register it in the FrostFS.
|
Long: `Create new container and register it in the FrostFS.
|
||||||
It will be stored in sidechain when inner ring will accepts it.`,
|
It will be stored in sidechain when inner ring will accepts it.`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
placementPolicy, err := parseContainerPolicy(cmd, containerPolicy)
|
placementPolicy, err := parseContainerPolicy(cmd, containerPolicy)
|
||||||
|
@ -49,7 +50,7 @@ It will be stored in sidechain when inner ring will accepts it.`,
|
||||||
var prm internalclient.NetMapSnapshotPrm
|
var prm internalclient.NetMapSnapshotPrm
|
||||||
prm.SetClient(cli)
|
prm.SetClient(cli)
|
||||||
|
|
||||||
resmap, err := internalclient.NetMapSnapshot(cmd.Context(), prm)
|
resmap, err := internalclient.NetMapSnapshot(prm)
|
||||||
commonCmd.ExitOnErr(cmd, "unable to get netmap snapshot to validate container placement, "+
|
commonCmd.ExitOnErr(cmd, "unable to get netmap snapshot to validate container placement, "+
|
||||||
"use --force option to skip this check: %w", err)
|
"use --force option to skip this check: %w", err)
|
||||||
|
|
||||||
|
@ -69,6 +70,15 @@ It will be stored in sidechain when inner ring will accepts it.`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if containerSubnet != "" {
|
||||||
|
var subnetID subnetid.ID
|
||||||
|
|
||||||
|
err = subnetID.DecodeString(containerSubnet)
|
||||||
|
commonCmd.ExitOnErr(cmd, "could not parse subnetID: %w", err)
|
||||||
|
|
||||||
|
placementPolicy.RestrictSubnet(subnetID)
|
||||||
|
}
|
||||||
|
|
||||||
var cnr container.Container
|
var cnr container.Container
|
||||||
cnr.Init()
|
cnr.Init()
|
||||||
|
|
||||||
|
@ -97,18 +107,18 @@ It will be stored in sidechain when inner ring will accepts it.`,
|
||||||
syncContainerPrm.SetClient(cli)
|
syncContainerPrm.SetClient(cli)
|
||||||
syncContainerPrm.SetContainer(&cnr)
|
syncContainerPrm.SetContainer(&cnr)
|
||||||
|
|
||||||
_, err = internalclient.SyncContainerSettings(cmd.Context(), syncContainerPrm)
|
_, err = internalclient.SyncContainerSettings(syncContainerPrm)
|
||||||
commonCmd.ExitOnErr(cmd, "syncing container's settings rpc error: %w", err)
|
commonCmd.ExitOnErr(cmd, "syncing container's settings rpc error: %w", err)
|
||||||
|
|
||||||
putPrm := internalclient.PutContainerPrm{
|
var putPrm internalclient.PutContainerPrm
|
||||||
Client: cli,
|
putPrm.SetClient(cli)
|
||||||
ClientParams: client.PrmContainerPut{
|
putPrm.SetContainer(cnr)
|
||||||
Container: &cnr,
|
|
||||||
Session: tok,
|
if tok != nil {
|
||||||
},
|
putPrm.WithinSession(*tok)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := internalclient.PutContainer(cmd.Context(), putPrm)
|
res, err := internalclient.PutContainer(putPrm)
|
||||||
commonCmd.ExitOnErr(cmd, "put container rpc error: %w", err)
|
commonCmd.ExitOnErr(cmd, "put container rpc error: %w", err)
|
||||||
|
|
||||||
id := res.ID()
|
id := res.ID()
|
||||||
|
@ -118,17 +128,14 @@ It will be stored in sidechain when inner ring will accepts it.`,
|
||||||
if containerAwait {
|
if containerAwait {
|
||||||
cmd.Println("awaiting...")
|
cmd.Println("awaiting...")
|
||||||
|
|
||||||
getPrm := internalclient.GetContainerPrm{
|
var getPrm internalclient.GetContainerPrm
|
||||||
Client: cli,
|
getPrm.SetClient(cli)
|
||||||
ClientParams: client.PrmContainerGet{
|
getPrm.SetContainer(id)
|
||||||
ContainerID: &id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < awaitTimeout; i++ {
|
for i := 0; i < awaitTimeout; i++ {
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
_, err := internalclient.GetContainer(cmd.Context(), getPrm)
|
_, err := internalclient.GetContainer(getPrm)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cmd.Println("container has been persisted on sidechain")
|
cmd.Println("container has been persisted on sidechain")
|
||||||
return
|
return
|
||||||
|
@ -145,7 +152,6 @@ func initContainerCreateCmd() {
|
||||||
|
|
||||||
// Init common flags
|
// Init common flags
|
||||||
flags.StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage)
|
flags.StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage)
|
||||||
flags.Bool(commonflags.TracingFlag, false, commonflags.TracingFlagUsage)
|
|
||||||
flags.DurationP(commonflags.Timeout, commonflags.TimeoutShorthand, commonflags.TimeoutDefault, commonflags.TimeoutUsage)
|
flags.DurationP(commonflags.Timeout, commonflags.TimeoutShorthand, commonflags.TimeoutDefault, commonflags.TimeoutUsage)
|
||||||
flags.StringP(commonflags.WalletPath, commonflags.WalletPathShorthand, commonflags.WalletPathDefault, commonflags.WalletPathUsage)
|
flags.StringP(commonflags.WalletPath, commonflags.WalletPathShorthand, commonflags.WalletPathDefault, commonflags.WalletPathUsage)
|
||||||
flags.StringP(commonflags.Account, commonflags.AccountShorthand, commonflags.AccountDefault, commonflags.AccountUsage)
|
flags.StringP(commonflags.Account, commonflags.AccountShorthand, commonflags.AccountDefault, commonflags.AccountUsage)
|
||||||
|
@ -160,6 +166,7 @@ func initContainerCreateCmd() {
|
||||||
flags.StringVar(&containerNnsName, "nns-name", "", "Container nns name attribute")
|
flags.StringVar(&containerNnsName, "nns-name", "", "Container nns name attribute")
|
||||||
flags.StringVar(&containerNnsZone, "nns-zone", "", "Container nns zone attribute")
|
flags.StringVar(&containerNnsZone, "nns-zone", "", "Container nns zone attribute")
|
||||||
flags.BoolVar(&containerNoTimestamp, "disable-timestamp", false, "Disable timestamp container attribute")
|
flags.BoolVar(&containerNoTimestamp, "disable-timestamp", false, "Disable timestamp container attribute")
|
||||||
|
flags.StringVar(&containerSubnet, "subnet", "", "String representation of container subnetwork")
|
||||||
flags.BoolVarP(&force, commonflags.ForceFlag, commonflags.ForceFlagShorthand, false,
|
flags.BoolVarP(&force, commonflags.ForceFlag, commonflags.ForceFlagShorthand, false,
|
||||||
"Skip placement validity check")
|
"Skip placement validity check")
|
||||||
}
|
}
|
||||||
|
@ -185,12 +192,12 @@ func parseContainerPolicy(cmd *cobra.Command, policyString string) (*netmap.Plac
|
||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := result.UnmarshalJSON([]byte(policyString)); err == nil {
|
if err = result.UnmarshalJSON([]byte(policyString)); err == nil {
|
||||||
common.PrintVerbose(cmd, "Parsed JSON encoded policy")
|
common.PrintVerbose(cmd, "Parsed JSON encoded policy")
|
||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("can't parse placement policy: %w", err)
|
return nil, errors.New("can't parse placement policy")
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAttributes(dst *container.Container, attributes []string) error {
|
func parseAttributes(dst *container.Container, attributes []string) error {
|
||||||
|
|
|
@ -4,21 +4,20 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
objectSDK "github.com/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteContainerCmd = &cobra.Command{
|
var deleteContainerCmd = &cobra.Command{
|
||||||
Use: "delete",
|
Use: "delete",
|
||||||
Short: "Delete existing container",
|
Short: "Delete existing container",
|
||||||
Long: `Delete existing container.
|
Long: `Delete existing container.
|
||||||
Only owner of the container has a permission to remove container.`,
|
Only owner of the container has a permission to remove container.`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
id := parseContainerID(cmd)
|
id := parseContainerID(cmd)
|
||||||
|
@ -31,14 +30,11 @@ Only owner of the container has a permission to remove container.`,
|
||||||
if force, _ := cmd.Flags().GetBool(commonflags.ForceFlag); !force {
|
if force, _ := cmd.Flags().GetBool(commonflags.ForceFlag); !force {
|
||||||
common.PrintVerbose(cmd, "Reading the container to check ownership...")
|
common.PrintVerbose(cmd, "Reading the container to check ownership...")
|
||||||
|
|
||||||
getPrm := internalclient.GetContainerPrm{
|
var getPrm internalclient.GetContainerPrm
|
||||||
Client: cli,
|
getPrm.SetClient(cli)
|
||||||
ClientParams: client.PrmContainerGet{
|
getPrm.SetContainer(id)
|
||||||
ContainerID: &id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
resGet, err := internalclient.GetContainer(cmd.Context(), getPrm)
|
resGet, err := internalclient.GetContainer(getPrm)
|
||||||
commonCmd.ExitOnErr(cmd, "can't get the container: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't get the container: %w", err)
|
||||||
|
|
||||||
owner := resGet.Container().Owner()
|
owner := resGet.Container().Owner()
|
||||||
|
@ -76,26 +72,26 @@ Only owner of the container has a permission to remove container.`,
|
||||||
|
|
||||||
common.PrintVerbose(cmd, "Searching for LOCK objects...")
|
common.PrintVerbose(cmd, "Searching for LOCK objects...")
|
||||||
|
|
||||||
res, err := internalclient.SearchObjects(cmd.Context(), searchPrm)
|
res, err := internalclient.SearchObjects(searchPrm)
|
||||||
commonCmd.ExitOnErr(cmd, "can't search for LOCK objects: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't search for LOCK objects: %w", err)
|
||||||
|
|
||||||
if len(res.IDList()) != 0 {
|
if len(res.IDList()) != 0 {
|
||||||
commonCmd.ExitOnErr(cmd, "",
|
commonCmd.ExitOnErr(cmd, "",
|
||||||
fmt.Errorf("container wasn't removed because LOCK objects were found, "+
|
fmt.Errorf("Container wasn't removed because LOCK objects were found.\n"+
|
||||||
"use --%s flag to remove anyway", commonflags.ForceFlag))
|
"Use --%s flag to remove anyway.", commonflags.ForceFlag))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delPrm := internalclient.DeleteContainerPrm{
|
var delPrm internalclient.DeleteContainerPrm
|
||||||
Client: cli,
|
delPrm.SetClient(cli)
|
||||||
ClientParams: client.PrmContainerDelete{
|
delPrm.SetContainer(id)
|
||||||
ContainerID: &id,
|
|
||||||
Session: tok,
|
if tok != nil {
|
||||||
},
|
delPrm.WithinSession(*tok)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := internalclient.DeleteContainer(cmd.Context(), delPrm)
|
_, err := internalclient.DeleteContainer(delPrm)
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
|
||||||
cmd.Println("container delete method invoked")
|
cmd.Println("container delete method invoked")
|
||||||
|
@ -103,17 +99,14 @@ Only owner of the container has a permission to remove container.`,
|
||||||
if containerAwait {
|
if containerAwait {
|
||||||
cmd.Println("awaiting...")
|
cmd.Println("awaiting...")
|
||||||
|
|
||||||
getPrm := internalclient.GetContainerPrm{
|
var getPrm internalclient.GetContainerPrm
|
||||||
Client: cli,
|
getPrm.SetClient(cli)
|
||||||
ClientParams: client.PrmContainerGet{
|
getPrm.SetContainer(id)
|
||||||
ContainerID: &id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < awaitTimeout; i++ {
|
for i := 0; i < awaitTimeout; i++ {
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
_, err := internalclient.GetContainer(cmd.Context(), getPrm)
|
_, err := internalclient.GetContainer(getPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.Println("container has been removed:", containerID)
|
cmd.Println("container has been removed:", containerID)
|
||||||
return
|
return
|
||||||
|
@ -131,7 +124,6 @@ func initContainerDeleteCmd() {
|
||||||
flags.StringP(commonflags.WalletPath, commonflags.WalletPathShorthand, commonflags.WalletPathDefault, commonflags.WalletPathUsage)
|
flags.StringP(commonflags.WalletPath, commonflags.WalletPathShorthand, commonflags.WalletPathDefault, commonflags.WalletPathUsage)
|
||||||
flags.StringP(commonflags.Account, commonflags.AccountShorthand, commonflags.AccountDefault, commonflags.AccountUsage)
|
flags.StringP(commonflags.Account, commonflags.AccountShorthand, commonflags.AccountDefault, commonflags.AccountUsage)
|
||||||
flags.StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage)
|
flags.StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage)
|
||||||
flags.Bool(commonflags.TracingFlag, false, commonflags.TracingFlagUsage)
|
|
||||||
|
|
||||||
flags.StringVar(&containerID, commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
|
flags.StringVar(&containerID, commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
|
||||||
flags.BoolVar(&containerAwait, "await", false, "Block execution until container is removed")
|
flags.BoolVar(&containerAwait, "await", false, "Block execution until container is removed")
|
||||||
|
|
|
@ -4,16 +4,15 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
"github.com/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
"github.com/TrueCloudLab/frostfs-sdk-go/container/acl"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -148,14 +147,11 @@ func getContainer(cmd *cobra.Command) (container.Container, *ecdsa.PrivateKey) {
|
||||||
pk = key.GetOrGenerate(cmd)
|
pk = key.GetOrGenerate(cmd)
|
||||||
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
||||||
|
|
||||||
prm := internalclient.GetContainerPrm{
|
var prm internalclient.GetContainerPrm
|
||||||
Client: cli,
|
prm.SetClient(cli)
|
||||||
ClientParams: client.PrmContainerGet{
|
prm.SetContainer(id)
|
||||||
ContainerID: &id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := internalclient.GetContainer(cmd.Context(), prm)
|
res, err := internalclient.GetContainer(prm)
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
|
||||||
cnr = res.Container()
|
cnr = res.Container()
|
||||||
|
|
|
@ -3,12 +3,11 @@ package container
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,14 +20,11 @@ var getExtendedACLCmd = &cobra.Command{
|
||||||
pk := key.GetOrGenerate(cmd)
|
pk := key.GetOrGenerate(cmd)
|
||||||
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
||||||
|
|
||||||
eaclPrm := internalclient.EACLPrm{
|
var eaclPrm internalclient.EACLPrm
|
||||||
Client: cli,
|
eaclPrm.SetClient(cli)
|
||||||
ClientParams: client.PrmContainerEACL{
|
eaclPrm.SetContainer(id)
|
||||||
ContainerID: &id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := internalclient.EACL(cmd.Context(), eaclPrm)
|
res, err := internalclient.EACL(eaclPrm)
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
|
||||||
eaclTable := res.EACL()
|
eaclTable := res.EACL()
|
||||||
|
|
|
@ -3,13 +3,12 @@ package container
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,14 +16,12 @@ import (
|
||||||
const (
|
const (
|
||||||
flagListPrintAttr = "with-attr"
|
flagListPrintAttr = "with-attr"
|
||||||
flagListContainerOwner = "owner"
|
flagListContainerOwner = "owner"
|
||||||
flagListName = "name"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// flag vars of list command.
|
// flag vars of list command.
|
||||||
var (
|
var (
|
||||||
flagVarListPrintAttr bool
|
flagVarListPrintAttr bool
|
||||||
flagVarListContainerOwner string
|
flagVarListContainerOwner string
|
||||||
flagVarListName string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var listContainersCmd = &cobra.Command{
|
var listContainersCmd = &cobra.Command{
|
||||||
|
@ -49,42 +46,30 @@ var listContainersCmd = &cobra.Command{
|
||||||
prm.SetClient(cli)
|
prm.SetClient(cli)
|
||||||
prm.SetAccount(idUser)
|
prm.SetAccount(idUser)
|
||||||
|
|
||||||
res, err := internalclient.ListContainers(cmd.Context(), prm)
|
res, err := internalclient.ListContainers(prm)
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
|
||||||
prmGet := internalclient.GetContainerPrm{
|
var prmGet internalclient.GetContainerPrm
|
||||||
Client: cli,
|
prmGet.SetClient(cli)
|
||||||
}
|
|
||||||
|
|
||||||
containerIDs := res.SortedIDList()
|
list := res.IDList()
|
||||||
for _, cnrID := range containerIDs {
|
for i := range list {
|
||||||
if flagVarListName == "" && !flagVarListPrintAttr {
|
cmd.Println(list[i].String())
|
||||||
cmd.Println(cnrID.String())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
cnrID := cnrID
|
|
||||||
prmGet.ClientParams.ContainerID = &cnrID
|
|
||||||
res, err := internalclient.GetContainer(cmd.Context(), prmGet)
|
|
||||||
if err != nil {
|
|
||||||
cmd.Printf(" failed to read attributes: %v\n", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
cnr := res.Container()
|
|
||||||
if cnrName := containerSDK.Name(cnr); flagVarListName != "" && cnrName != flagVarListName {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cmd.Println(cnrID.String())
|
|
||||||
|
|
||||||
if flagVarListPrintAttr {
|
if flagVarListPrintAttr {
|
||||||
cnr.IterateAttributes(func(key, val string) {
|
prmGet.SetContainer(list[i])
|
||||||
if !strings.HasPrefix(key, container.SysAttributePrefix) && !strings.HasPrefix(key, container.SysAttributePrefixNeoFS) {
|
|
||||||
// FIXME(@cthulhu-rider): https://git.frostfs.info/TrueCloudLab/frostfs-sdk-go/issues/97
|
res, err := internalclient.GetContainer(prmGet)
|
||||||
// Use dedicated method to skip system attributes.
|
if err == nil {
|
||||||
cmd.Printf(" %s: %s\n", key, val)
|
res.Container().IterateAttributes(func(key, val string) {
|
||||||
}
|
if !strings.HasPrefix(key, container.SysAttributePrefix) {
|
||||||
})
|
// FIXME(@cthulhu-rider): neofs-sdk-go#314 use dedicated method to skip system attributes
|
||||||
|
cmd.Printf(" %s: %s\n", key, val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
cmd.Printf(" failed to read attributes: %v\n", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -95,9 +80,6 @@ func initContainerListContainersCmd() {
|
||||||
|
|
||||||
flags := listContainersCmd.Flags()
|
flags := listContainersCmd.Flags()
|
||||||
|
|
||||||
flags.StringVar(&flagVarListName, flagListName, "",
|
|
||||||
"List containers by the attribute name",
|
|
||||||
)
|
|
||||||
flags.StringVar(&flagVarListContainerOwner, flagListContainerOwner, "",
|
flags.StringVar(&flagVarListContainerOwner, flagListContainerOwner, "",
|
||||||
"Owner of containers (omit to use owner from private key)",
|
"Owner of containers (omit to use owner from private key)",
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,14 +3,14 @@ package container
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
objectCli "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/object"
|
objectCli "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/object"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
"github.com/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ var listContainerObjectsCmd = &cobra.Command{
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
id := parseContainerID(cmd)
|
id := parseContainerID(cmd)
|
||||||
|
|
||||||
filters := new(objectSDK.SearchFilters)
|
filters := new(object.SearchFilters)
|
||||||
filters.AddRootFilter() // search only user created objects
|
filters.AddRootFilter() // search only user created objects
|
||||||
|
|
||||||
cli := internalclient.GetSDKClientByFlag(cmd, key.GetOrGenerate(cmd), commonflags.RPC)
|
cli := internalclient.GetSDKClientByFlag(cmd, key.GetOrGenerate(cmd), commonflags.RPC)
|
||||||
|
@ -51,7 +51,7 @@ var listContainerObjectsCmd = &cobra.Command{
|
||||||
prmSearch.SetContainerID(id)
|
prmSearch.SetContainerID(id)
|
||||||
prmSearch.SetFilters(*filters)
|
prmSearch.SetFilters(*filters)
|
||||||
|
|
||||||
res, err := internalclient.SearchObjects(cmd.Context(), prmSearch)
|
res, err := internalclient.SearchObjects(prmSearch)
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
|
||||||
objectIDs := res.IDList()
|
objectIDs := res.IDList()
|
||||||
|
@ -65,14 +65,13 @@ var listContainerObjectsCmd = &cobra.Command{
|
||||||
addr.SetObject(objectIDs[i])
|
addr.SetObject(objectIDs[i])
|
||||||
prmHead.SetAddress(addr)
|
prmHead.SetAddress(addr)
|
||||||
|
|
||||||
resHead, err := internalclient.HeadObject(cmd.Context(), prmHead)
|
resHead, err := internalclient.HeadObject(prmHead)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
attrs := resHead.Header().Attributes()
|
attrs := resHead.Header().Attributes()
|
||||||
for i := range attrs {
|
for i := range attrs {
|
||||||
attrKey := attrs[i].Key()
|
attrKey := attrs[i].Key()
|
||||||
if !strings.HasPrefix(attrKey, v2object.SysAttributePrefix) && !strings.HasPrefix(attrKey, v2object.SysAttributePrefixNeoFS) {
|
if !strings.HasPrefix(attrKey, v2object.SysAttributePrefix) {
|
||||||
// FIXME(@cthulhu-rider): https://git.frostfs.info/TrueCloudLab/frostfs-sdk-go/issues/97
|
// FIXME(@cthulhu-rider): neofs-sdk-go#226 use dedicated method to skip system attributes
|
||||||
// Use dedicated method to skip system attributes.
|
|
||||||
cmd.Printf(" %s: %s\n", attrKey, attrs[i].Value())
|
cmd.Printf(" %s: %s\n", attrKey, attrs[i].Value())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,13 @@ package container
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
containerAPI "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
containerAPI "github.com/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ var containerNodesCmd = &cobra.Command{
|
||||||
var prm internalclient.NetMapSnapshotPrm
|
var prm internalclient.NetMapSnapshotPrm
|
||||||
prm.SetClient(cli)
|
prm.SetClient(cli)
|
||||||
|
|
||||||
resmap, err := internalclient.NetMapSnapshot(cmd.Context(), prm)
|
resmap, err := internalclient.NetMapSnapshot(prm)
|
||||||
commonCmd.ExitOnErr(cmd, "unable to get netmap snapshot", err)
|
commonCmd.ExitOnErr(cmd, "unable to get netmap snapshot", err)
|
||||||
|
|
||||||
var id cid.ID
|
var id cid.ID
|
||||||
|
|
|
@ -1,233 +0,0 @@
|
||||||
package container
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"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"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
type policyPlaygroundREPL struct {
|
|
||||||
cmd *cobra.Command
|
|
||||||
args []string
|
|
||||||
nodes map[string]netmap.NodeInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPolicyPlaygroundREPL(cmd *cobra.Command, args []string) (*policyPlaygroundREPL, error) {
|
|
||||||
return &policyPlaygroundREPL{
|
|
||||||
cmd: cmd,
|
|
||||||
args: args,
|
|
||||||
nodes: map[string]netmap.NodeInfo{},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repl *policyPlaygroundREPL) handleLs(args []string) error {
|
|
||||||
if len(args) > 0 {
|
|
||||||
return fmt.Errorf("too many arguments for command 'ls': got %d, want 0", len(args))
|
|
||||||
}
|
|
||||||
i := 1
|
|
||||||
for id, node := range repl.nodes {
|
|
||||||
var attrs []string
|
|
||||||
node.IterateAttributes(func(k, v string) {
|
|
||||||
attrs = append(attrs, fmt.Sprintf("%s:%q", k, v))
|
|
||||||
})
|
|
||||||
fmt.Printf("\t%2d: id=%s attrs={%v}\n", i, id, strings.Join(attrs, " "))
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repl *policyPlaygroundREPL) handleAdd(args []string) error {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return fmt.Errorf("too few arguments for command 'add': got %d, want >0", len(args))
|
|
||||||
}
|
|
||||||
id := args[0]
|
|
||||||
key, err := hex.DecodeString(id)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("node id must be a hex string: got %q: %v", id, err)
|
|
||||||
}
|
|
||||||
node := repl.nodes[id]
|
|
||||||
node.SetPublicKey(key)
|
|
||||||
for _, attr := range args[1:] {
|
|
||||||
kv := strings.Split(attr, ":")
|
|
||||||
if len(kv) != 2 {
|
|
||||||
return fmt.Errorf("node attributes must be in the format 'KEY:VALUE': got %q", attr)
|
|
||||||
}
|
|
||||||
node.SetAttribute(kv[0], kv[1])
|
|
||||||
}
|
|
||||||
repl.nodes[id] = node
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repl *policyPlaygroundREPL) handleLoad(args []string) error {
|
|
||||||
if len(args) != 1 {
|
|
||||||
return fmt.Errorf("too few arguments for command 'add': got %d, want 1", len(args))
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonNetmap := map[string]map[string]string{}
|
|
||||||
|
|
||||||
b, err := os.ReadFile(args[0])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("reading netmap file %q: %v", args[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.Unmarshal(b, &jsonNetmap); err != nil {
|
|
||||||
return fmt.Errorf("decoding json netmap: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
repl.nodes = make(map[string]netmap.NodeInfo)
|
|
||||||
for id, attrs := range jsonNetmap {
|
|
||||||
key, err := hex.DecodeString(id)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("node id must be a hex string: got %q: %v", id, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
node := repl.nodes[id]
|
|
||||||
node.SetPublicKey(key)
|
|
||||||
for k, v := range attrs {
|
|
||||||
node.SetAttribute(k, v)
|
|
||||||
}
|
|
||||||
repl.nodes[id] = node
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repl *policyPlaygroundREPL) handleRemove(args []string) error {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return fmt.Errorf("too few arguments for command 'remove': got %d, want >0", len(args))
|
|
||||||
}
|
|
||||||
id := args[0]
|
|
||||||
if _, exists := repl.nodes[id]; exists {
|
|
||||||
delete(repl.nodes, id)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("node not found: id=%q", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repl *policyPlaygroundREPL) handleEval(args []string) error {
|
|
||||||
policyStr := strings.TrimSpace(strings.Join(args, " "))
|
|
||||||
var nodes [][]netmap.NodeInfo
|
|
||||||
nm := repl.netMap()
|
|
||||||
|
|
||||||
if strings.HasPrefix(policyStr, "CBF") || strings.HasPrefix(policyStr, "SELECT") || strings.HasPrefix(policyStr, "FILTER") {
|
|
||||||
// Assume that the input is a partial SELECT-FILTER expression.
|
|
||||||
// Full inline policies always start with UNIQUE or REP keywords,
|
|
||||||
// or different prefixes when it's the case of an external file.
|
|
||||||
sfExpr, err := netmap.DecodeSelectFilterString(policyStr)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("parsing select-filter expression: %v", err)
|
|
||||||
}
|
|
||||||
nodes, err = nm.SelectFilterNodes(sfExpr)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("building select-filter nodes: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Assume that the input is a full policy or input file otherwise.
|
|
||||||
placementPolicy, err := parseContainerPolicy(repl.cmd, policyStr)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("parsing placement policy: %v", err)
|
|
||||||
}
|
|
||||||
nodes, err = nm.ContainerNodes(*placementPolicy, nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("building container nodes: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i, ns := range nodes {
|
|
||||||
var ids []string
|
|
||||||
for _, node := range ns {
|
|
||||||
ids = append(ids, hex.EncodeToString(node.PublicKey()))
|
|
||||||
}
|
|
||||||
fmt.Printf("\t%2d: %v\n", i+1, ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repl *policyPlaygroundREPL) netMap() netmap.NetMap {
|
|
||||||
var nm netmap.NetMap
|
|
||||||
var nodes []netmap.NodeInfo
|
|
||||||
for _, node := range repl.nodes {
|
|
||||||
nodes = append(nodes, node)
|
|
||||||
}
|
|
||||||
nm.SetNodes(nodes)
|
|
||||||
return nm
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repl *policyPlaygroundREPL) run() error {
|
|
||||||
if len(viper.GetString(commonflags.RPC)) > 0 {
|
|
||||||
key := key.GetOrGenerate(repl.cmd)
|
|
||||||
cli := internalclient.GetSDKClientByFlag(repl.cmd, key, commonflags.RPC)
|
|
||||||
|
|
||||||
var prm internalclient.NetMapSnapshotPrm
|
|
||||||
prm.SetClient(cli)
|
|
||||||
|
|
||||||
resp, err := internalclient.NetMapSnapshot(repl.cmd.Context(), prm)
|
|
||||||
commonCmd.ExitOnErr(repl.cmd, "unable to get netmap snapshot to populate initial netmap: %w", err)
|
|
||||||
|
|
||||||
for _, node := range resp.NetMap().Nodes() {
|
|
||||||
id := hex.EncodeToString(node.PublicKey())
|
|
||||||
repl.nodes[id] = node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdHandlers := map[string]func([]string) error{
|
|
||||||
"list": repl.handleLs,
|
|
||||||
"ls": repl.handleLs,
|
|
||||||
"add": repl.handleAdd,
|
|
||||||
"load": repl.handleLoad,
|
|
||||||
"remove": repl.handleRemove,
|
|
||||||
"rm": repl.handleRemove,
|
|
||||||
"eval": repl.handleEval,
|
|
||||||
}
|
|
||||||
for reader := bufio.NewReader(os.Stdin); ; {
|
|
||||||
fmt.Print("> ")
|
|
||||||
line, err := reader.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("reading line: %v", err)
|
|
||||||
}
|
|
||||||
parts := strings.Fields(line)
|
|
||||||
if len(parts) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cmd := parts[0]
|
|
||||||
handler, exists := cmdHandlers[cmd]
|
|
||||||
if exists {
|
|
||||||
if err := handler(parts[1:]); err != nil {
|
|
||||||
fmt.Printf("error: %v\n", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Printf("error: unknown command %q\n", cmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var policyPlaygroundCmd = &cobra.Command{
|
|
||||||
Use: "policy-playground",
|
|
||||||
Short: "A REPL for testing placement policies",
|
|
||||||
Long: `A REPL for testing placement policies.
|
|
||||||
If a wallet and endpoint is provided, the initial netmap data will be loaded from the snapshot of the node. Otherwise, an empty playground is created.`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
repl, err := newPolicyPlaygroundREPL(cmd, args)
|
|
||||||
commonCmd.ExitOnErr(cmd, "could not create policy playground: %w", err)
|
|
||||||
commonCmd.ExitOnErr(cmd, "policy playground failed: %w", repl.run())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func initContainerPolicyPlaygroundCmd() {
|
|
||||||
commonflags.Init(policyPlaygroundCmd)
|
|
||||||
}
|
|
|
@ -1,8 +1,7 @@
|
||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,9 +15,7 @@ var Cmd = &cobra.Command{
|
||||||
// the viper before execution
|
// the viper before execution
|
||||||
commonflags.Bind(cmd)
|
commonflags.Bind(cmd)
|
||||||
commonflags.BindAPI(cmd)
|
commonflags.BindAPI(cmd)
|
||||||
common.StartClientCommandSpan(cmd)
|
|
||||||
},
|
},
|
||||||
PersistentPostRun: common.StopClientCommandSpan,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -31,7 +28,6 @@ func init() {
|
||||||
getExtendedACLCmd,
|
getExtendedACLCmd,
|
||||||
setExtendedACLCmd,
|
setExtendedACLCmd,
|
||||||
containerNodesCmd,
|
containerNodesCmd,
|
||||||
policyPlaygroundCmd,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cmd.AddCommand(containerChildCommand...)
|
Cmd.AddCommand(containerChildCommand...)
|
||||||
|
@ -44,7 +40,6 @@ func init() {
|
||||||
initContainerGetEACLCmd()
|
initContainerGetEACLCmd()
|
||||||
initContainerSetEACLCmd()
|
initContainerSetEACLCmd()
|
||||||
initContainerNodesCmd()
|
initContainerNodesCmd()
|
||||||
initContainerPolicyPlaygroundCmd()
|
|
||||||
|
|
||||||
for _, containerCommand := range containerChildCommand {
|
for _, containerCommand := range containerChildCommand {
|
||||||
commonflags.InitAPI(containerCommand)
|
commonflags.InitAPI(containerCommand)
|
||||||
|
|
|
@ -5,12 +5,11 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ Container ID in EACL table will be substituted with ID from the CLI.`,
|
||||||
if !flagVarsSetEACL.noPreCheck {
|
if !flagVarsSetEACL.noPreCheck {
|
||||||
cmd.Println("Checking the ability to modify access rights in the container...")
|
cmd.Println("Checking the ability to modify access rights in the container...")
|
||||||
|
|
||||||
extendable, err := internalclient.IsACLExtendable(cmd.Context(), cli, id)
|
extendable, err := internalclient.IsACLExtendable(cli, id)
|
||||||
commonCmd.ExitOnErr(cmd, "Extensibility check failure: %w", err)
|
commonCmd.ExitOnErr(cmd, "Extensibility check failure: %w", err)
|
||||||
|
|
||||||
if !extendable {
|
if !extendable {
|
||||||
|
@ -49,15 +48,15 @@ Container ID in EACL table will be substituted with ID from the CLI.`,
|
||||||
cmd.Println("ACL extension is enabled in the container, continue processing.")
|
cmd.Println("ACL extension is enabled in the container, continue processing.")
|
||||||
}
|
}
|
||||||
|
|
||||||
setEACLPrm := internalclient.SetEACLPrm{
|
var setEACLPrm internalclient.SetEACLPrm
|
||||||
Client: cli,
|
setEACLPrm.SetClient(cli)
|
||||||
ClientParams: client.PrmContainerSetEACL{
|
setEACLPrm.SetTable(*eaclTable)
|
||||||
Table: eaclTable,
|
|
||||||
Session: tok,
|
if tok != nil {
|
||||||
},
|
setEACLPrm.WithinSession(*tok)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := internalclient.SetEACL(cmd.Context(), setEACLPrm)
|
_, err := internalclient.SetEACL(setEACLPrm)
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
|
||||||
if containerAwait {
|
if containerAwait {
|
||||||
|
@ -66,17 +65,14 @@ Container ID in EACL table will be substituted with ID from the CLI.`,
|
||||||
|
|
||||||
cmd.Println("awaiting...")
|
cmd.Println("awaiting...")
|
||||||
|
|
||||||
getEACLPrm := internalclient.EACLPrm{
|
var getEACLPrm internalclient.EACLPrm
|
||||||
Client: cli,
|
getEACLPrm.SetClient(cli)
|
||||||
ClientParams: client.PrmContainerEACL{
|
getEACLPrm.SetContainer(id)
|
||||||
ContainerID: &id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < awaitTimeout; i++ {
|
for i := 0; i < awaitTimeout; i++ {
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
res, err := internalclient.EACL(cmd.Context(), getEACLPrm)
|
res, err := internalclient.EACL(getEACLPrm)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// compare binary values because EACL could have been set already
|
// compare binary values because EACL could have been set already
|
||||||
table := res.EACL()
|
table := res.EACL()
|
||||||
|
|
|
@ -3,11 +3,11 @@ package container
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
"github.com/TrueCloudLab/frostfs-sdk-go/session"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
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"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
concurrencyFlag = "concurrency"
|
|
||||||
removeDuplicatesFlag = "remove-duplicates"
|
|
||||||
)
|
|
||||||
|
|
||||||
var doctorCmd = &cobra.Command{
|
|
||||||
Use: "doctor",
|
|
||||||
Short: "Restructure node's storage",
|
|
||||||
Long: "Restructure node's storage",
|
|
||||||
Run: doctor,
|
|
||||||
}
|
|
||||||
|
|
||||||
func doctor(cmd *cobra.Command, _ []string) {
|
|
||||||
pk := key.Get(cmd)
|
|
||||||
|
|
||||||
req := &control.DoctorRequest{Body: new(control.DoctorRequest_Body)}
|
|
||||||
req.Body.Concurrency, _ = cmd.Flags().GetUint32(concurrencyFlag)
|
|
||||||
req.Body.RemoveDuplicates, _ = cmd.Flags().GetBool(removeDuplicatesFlag)
|
|
||||||
|
|
||||||
signRequest(cmd, pk, req)
|
|
||||||
|
|
||||||
cli := getClient(cmd, pk)
|
|
||||||
|
|
||||||
var resp *control.DoctorResponse
|
|
||||||
var err error
|
|
||||||
err = cli.ExecRaw(func(client *client.Client) error {
|
|
||||||
resp, err = control.Doctor(client, req)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
|
||||||
|
|
||||||
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
|
||||||
|
|
||||||
cmd.Println("Operation has finished.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initControlDoctorCmd() {
|
|
||||||
initControlFlags(doctorCmd)
|
|
||||||
|
|
||||||
ff := doctorCmd.Flags()
|
|
||||||
ff.Uint32(concurrencyFlag, 0, "Number of parallel threads to use")
|
|
||||||
ff.Bool(removeDuplicatesFlag, false, "Remove duplicate objects")
|
|
||||||
}
|
|
|
@ -1,10 +1,10 @@
|
||||||
package control
|
package control
|
||||||
|
|
||||||
import (
|
import (
|
||||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
rawclient "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
"github.com/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
package control
|
package control
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
"github.com/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ignoreErrorsFlag = "no-errors"
|
|
||||||
|
|
||||||
var evacuateShardCmd = &cobra.Command{
|
var evacuateShardCmd = &cobra.Command{
|
||||||
Use: "evacuate",
|
Use: "evacuate",
|
||||||
Short: "Evacuate objects from shard",
|
Short: "Evacuate objects from shard",
|
||||||
Long: "Evacuate objects from shard to other shards",
|
Long: "Evacuate objects from shard to other shards",
|
||||||
Run: evacuateShard,
|
Run: evacuateShard,
|
||||||
Deprecated: "use frostfs-cli control shards evacuation start",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func evacuateShard(cmd *cobra.Command, _ []string) {
|
func evacuateShard(cmd *cobra.Command, _ []string) {
|
||||||
|
@ -23,7 +20,7 @@ func evacuateShard(cmd *cobra.Command, _ []string) {
|
||||||
|
|
||||||
req := &control.EvacuateShardRequest{Body: new(control.EvacuateShardRequest_Body)}
|
req := &control.EvacuateShardRequest{Body: new(control.EvacuateShardRequest_Body)}
|
||||||
req.Body.Shard_ID = getShardIDList(cmd)
|
req.Body.Shard_ID = getShardIDList(cmd)
|
||||||
req.Body.IgnoreErrors, _ = cmd.Flags().GetBool(ignoreErrorsFlag)
|
req.Body.IgnoreErrors, _ = cmd.Flags().GetBool(dumpIgnoreErrorsFlag)
|
||||||
|
|
||||||
signRequest(cmd, pk, req)
|
signRequest(cmd, pk, req)
|
||||||
|
|
||||||
|
@ -50,7 +47,7 @@ func initControlEvacuateShardCmd() {
|
||||||
flags := evacuateShardCmd.Flags()
|
flags := evacuateShardCmd.Flags()
|
||||||
flags.StringSlice(shardIDFlag, nil, "List of shard IDs in base58 encoding")
|
flags.StringSlice(shardIDFlag, nil, "List of shard IDs in base58 encoding")
|
||||||
flags.Bool(shardAllFlag, false, "Process all shards")
|
flags.Bool(shardAllFlag, false, "Process all shards")
|
||||||
flags.Bool(ignoreErrorsFlag, false, "Skip invalid/unreadable objects")
|
flags.Bool(dumpIgnoreErrorsFlag, false, "Skip invalid/unreadable objects")
|
||||||
|
|
||||||
evacuateShardCmd.MarkFlagsMutuallyExclusive(shardIDFlag, shardAllFlag)
|
evacuateShardCmd.MarkFlagsMutuallyExclusive(shardIDFlag, shardAllFlag)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,315 +0,0 @@
|
||||||
package control
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"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"
|
|
||||||
clientSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
awaitFlag = "await"
|
|
||||||
noProgressFlag = "no-progress"
|
|
||||||
)
|
|
||||||
|
|
||||||
var evacuationShardCmd = &cobra.Command{
|
|
||||||
Use: "evacuation",
|
|
||||||
Short: "Objects evacuation from shard",
|
|
||||||
Long: "Objects evacuation from shard to other shards",
|
|
||||||
}
|
|
||||||
|
|
||||||
var startEvacuationShardCmd = &cobra.Command{
|
|
||||||
Use: "start",
|
|
||||||
Short: "Start evacuate objects from shard",
|
|
||||||
Long: "Start evacuate objects from shard to other shards",
|
|
||||||
Run: startEvacuateShard,
|
|
||||||
}
|
|
||||||
|
|
||||||
var getEvacuationShardStatusCmd = &cobra.Command{
|
|
||||||
Use: "status",
|
|
||||||
Short: "Get evacuate objects from shard status",
|
|
||||||
Long: "Get evacuate objects from shard to other shards status",
|
|
||||||
Run: getEvacuateShardStatus,
|
|
||||||
}
|
|
||||||
|
|
||||||
var stopEvacuationShardCmd = &cobra.Command{
|
|
||||||
Use: "stop",
|
|
||||||
Short: "Stop running evacuate process",
|
|
||||||
Long: "Stop running evacuate process from shard to other shards",
|
|
||||||
Run: stopEvacuateShardStatus,
|
|
||||||
}
|
|
||||||
|
|
||||||
func startEvacuateShard(cmd *cobra.Command, _ []string) {
|
|
||||||
pk := key.Get(cmd)
|
|
||||||
|
|
||||||
ignoreErrors, _ := cmd.Flags().GetBool(ignoreErrorsFlag)
|
|
||||||
|
|
||||||
req := &control.StartShardEvacuationRequest{
|
|
||||||
Body: &control.StartShardEvacuationRequest_Body{
|
|
||||||
Shard_ID: getShardIDList(cmd),
|
|
||||||
IgnoreErrors: ignoreErrors,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
signRequest(cmd, pk, req)
|
|
||||||
|
|
||||||
cli := getClient(cmd, pk)
|
|
||||||
|
|
||||||
var resp *control.StartShardEvacuationResponse
|
|
||||||
var err error
|
|
||||||
err = cli.ExecRaw(func(client *client.Client) error {
|
|
||||||
resp, err = control.StartShardEvacuation(client, req)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
commonCmd.ExitOnErr(cmd, "Start evacuate shards failed, rpc error: %w", err)
|
|
||||||
|
|
||||||
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
|
||||||
|
|
||||||
cmd.Println("Shard evacuation has been successfully started.")
|
|
||||||
|
|
||||||
if awaitCompletion, _ := cmd.Flags().GetBool(awaitFlag); awaitCompletion {
|
|
||||||
noProgress, _ := cmd.Flags().GetBool(noProgressFlag)
|
|
||||||
waitEvacuateCompletion(cmd, pk, cli, !noProgress, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEvacuateShardStatus(cmd *cobra.Command, _ []string) {
|
|
||||||
pk := key.Get(cmd)
|
|
||||||
req := &control.GetShardEvacuationStatusRequest{
|
|
||||||
Body: &control.GetShardEvacuationStatusRequest_Body{},
|
|
||||||
}
|
|
||||||
|
|
||||||
signRequest(cmd, pk, req)
|
|
||||||
|
|
||||||
cli := getClient(cmd, pk)
|
|
||||||
|
|
||||||
var resp *control.GetShardEvacuationStatusResponse
|
|
||||||
var err error
|
|
||||||
err = cli.ExecRaw(func(client *client.Client) error {
|
|
||||||
resp, err = control.GetShardEvacuationStatus(client, req)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
commonCmd.ExitOnErr(cmd, "Get evacuate shards status failed, rpc error: %w", err)
|
|
||||||
|
|
||||||
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
|
||||||
|
|
||||||
printStatus(cmd, resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stopEvacuateShardStatus(cmd *cobra.Command, _ []string) {
|
|
||||||
pk := key.Get(cmd)
|
|
||||||
req := &control.StopShardEvacuationRequest{
|
|
||||||
Body: &control.StopShardEvacuationRequest_Body{},
|
|
||||||
}
|
|
||||||
|
|
||||||
signRequest(cmd, pk, req)
|
|
||||||
|
|
||||||
cli := getClient(cmd, pk)
|
|
||||||
|
|
||||||
var resp *control.StopShardEvacuationResponse
|
|
||||||
var err error
|
|
||||||
err = cli.ExecRaw(func(client *client.Client) error {
|
|
||||||
resp, err = control.StopShardEvacuation(client, req)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
commonCmd.ExitOnErr(cmd, "Stop evacuate shards failed, rpc error: %w", err)
|
|
||||||
|
|
||||||
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
|
||||||
|
|
||||||
waitEvacuateCompletion(cmd, pk, cli, false, false)
|
|
||||||
|
|
||||||
cmd.Println("Evacuation stopped.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitEvacuateCompletion(cmd *cobra.Command, pk *ecdsa.PrivateKey, cli *clientSDK.Client, printProgress, printCompleted bool) {
|
|
||||||
const statusPollingInterval = 1 * time.Second
|
|
||||||
const reportIntervalSeconds = 5
|
|
||||||
var resp *control.GetShardEvacuationStatusResponse
|
|
||||||
reportResponse := new(atomic.Pointer[control.GetShardEvacuationStatusResponse])
|
|
||||||
pollingCompleted := make(chan struct{})
|
|
||||||
progressReportCompleted := make(chan struct{})
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer close(progressReportCompleted)
|
|
||||||
if !printProgress {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cmd.Printf("Progress will be reported every %d seconds.\n", reportIntervalSeconds)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-pollingCompleted:
|
|
||||||
return
|
|
||||||
case <-time.After(reportIntervalSeconds * time.Second):
|
|
||||||
r := reportResponse.Load()
|
|
||||||
if r == nil || r.GetBody().GetStatus() == control.GetShardEvacuationStatusResponse_Body_COMPLETED {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
printStatus(cmd, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
|
||||||
req := &control.GetShardEvacuationStatusRequest{
|
|
||||||
Body: &control.GetShardEvacuationStatusRequest_Body{},
|
|
||||||
}
|
|
||||||
signRequest(cmd, pk, req)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
err = cli.ExecRaw(func(client *client.Client) error {
|
|
||||||
resp, err = control.GetShardEvacuationStatus(client, req)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
|
|
||||||
reportResponse.Store(resp)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
commonCmd.ExitOnErr(cmd, "Failed to get evacuate status, rpc error: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if resp.GetBody().GetStatus() != control.GetShardEvacuationStatusResponse_Body_RUNNING {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(statusPollingInterval)
|
|
||||||
}
|
|
||||||
close(pollingCompleted)
|
|
||||||
<-progressReportCompleted
|
|
||||||
if printCompleted {
|
|
||||||
printCompletedStatusMessage(cmd, resp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func printCompletedStatusMessage(cmd *cobra.Command, resp *control.GetShardEvacuationStatusResponse) {
|
|
||||||
cmd.Println("Shard evacuation has been completed.")
|
|
||||||
sb := &strings.Builder{}
|
|
||||||
appendShardIDs(sb, resp)
|
|
||||||
appendCounts(sb, resp)
|
|
||||||
appendError(sb, resp)
|
|
||||||
appendStartedAt(sb, resp)
|
|
||||||
appendDuration(sb, resp)
|
|
||||||
cmd.Println(sb.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func printStatus(cmd *cobra.Command, resp *control.GetShardEvacuationStatusResponse) {
|
|
||||||
if resp.GetBody().GetStatus() == control.GetShardEvacuationStatusResponse_Body_EVACUATE_SHARD_STATUS_UNDEFINED {
|
|
||||||
cmd.Println("There is no running or completed evacuation.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sb := &strings.Builder{}
|
|
||||||
appendShardIDs(sb, resp)
|
|
||||||
appendStatus(sb, resp)
|
|
||||||
appendCounts(sb, resp)
|
|
||||||
appendError(sb, resp)
|
|
||||||
appendStartedAt(sb, resp)
|
|
||||||
appendDuration(sb, resp)
|
|
||||||
appendEstimation(sb, resp)
|
|
||||||
cmd.Println(sb.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendEstimation(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
|
||||||
if resp.GetBody().GetStatus() != control.GetShardEvacuationStatusResponse_Body_RUNNING ||
|
|
||||||
resp.GetBody().GetDuration() == nil ||
|
|
||||||
resp.GetBody().GetTotal() == 0 ||
|
|
||||||
resp.GetBody().GetEvacuated()+resp.GetBody().GetFailed() == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
durationSeconds := float64(resp.GetBody().GetDuration().GetSeconds())
|
|
||||||
evacuated := float64(resp.GetBody().GetEvacuated() + resp.GetBody().GetFailed())
|
|
||||||
avgObjEvacuationTimeSeconds := durationSeconds / evacuated
|
|
||||||
objectsLeft := float64(resp.GetBody().GetTotal()) - evacuated
|
|
||||||
leftSeconds := avgObjEvacuationTimeSeconds * objectsLeft
|
|
||||||
leftMinutes := int(leftSeconds / 60)
|
|
||||||
|
|
||||||
sb.WriteString(fmt.Sprintf(" Estimated time left: %d minutes.", leftMinutes))
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendDuration(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
|
||||||
if resp.GetBody().GetDuration() != nil {
|
|
||||||
duration := time.Second * time.Duration(resp.GetBody().GetDuration().GetSeconds())
|
|
||||||
hour := int(duration.Seconds() / 3600)
|
|
||||||
minute := int(duration.Seconds()/60) % 60
|
|
||||||
second := int(duration.Seconds()) % 60
|
|
||||||
sb.WriteString(fmt.Sprintf(" Duration: %02d:%02d:%02d.", hour, minute, second))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendStartedAt(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
|
||||||
if resp.GetBody().GetStartedAt() != nil {
|
|
||||||
startedAt := time.Unix(resp.GetBody().GetStartedAt().GetValue(), 0).UTC()
|
|
||||||
sb.WriteString(fmt.Sprintf(" Started at: %s UTC.", startedAt.Format(time.RFC3339)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendError(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
|
||||||
if len(resp.Body.GetErrorMessage()) > 0 {
|
|
||||||
sb.WriteString(fmt.Sprintf(" Error: %s.", resp.Body.GetErrorMessage()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendStatus(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
|
||||||
var status string
|
|
||||||
switch resp.GetBody().GetStatus() {
|
|
||||||
case control.GetShardEvacuationStatusResponse_Body_COMPLETED:
|
|
||||||
status = "completed"
|
|
||||||
case control.GetShardEvacuationStatusResponse_Body_RUNNING:
|
|
||||||
status = "running"
|
|
||||||
default:
|
|
||||||
status = "undefined"
|
|
||||||
}
|
|
||||||
sb.WriteString(fmt.Sprintf(" Status: %s.", status))
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendShardIDs(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
|
||||||
sb.WriteString("Shard IDs: ")
|
|
||||||
for idx, shardID := range resp.GetBody().GetShard_ID() {
|
|
||||||
shardIDStr := shard.NewIDFromBytes(shardID).String()
|
|
||||||
if idx > 0 {
|
|
||||||
sb.WriteString(", ")
|
|
||||||
}
|
|
||||||
sb.WriteString(shardIDStr)
|
|
||||||
if idx == len(resp.GetBody().GetShard_ID())-1 {
|
|
||||||
sb.WriteString(".")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendCounts(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
|
||||||
sb.WriteString(fmt.Sprintf(" Evacuated %d object out of %d, failed to evacuate %d objects.",
|
|
||||||
resp.GetBody().GetEvacuated(),
|
|
||||||
resp.Body.GetTotal(),
|
|
||||||
resp.Body.GetFailed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func initControlEvacuationShardCmd() {
|
|
||||||
evacuationShardCmd.AddCommand(startEvacuationShardCmd)
|
|
||||||
evacuationShardCmd.AddCommand(getEvacuationShardStatusCmd)
|
|
||||||
evacuationShardCmd.AddCommand(stopEvacuationShardCmd)
|
|
||||||
|
|
||||||
initControlStartEvacuationShardCmd()
|
|
||||||
initControlFlags(getEvacuationShardStatusCmd)
|
|
||||||
initControlFlags(stopEvacuationShardCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initControlStartEvacuationShardCmd() {
|
|
||||||
initControlFlags(startEvacuationShardCmd)
|
|
||||||
|
|
||||||
flags := startEvacuationShardCmd.Flags()
|
|
||||||
flags.StringSlice(shardIDFlag, nil, "List of shard IDs in base58 encoding")
|
|
||||||
flags.Bool(shardAllFlag, false, "Process all shards")
|
|
||||||
flags.Bool(ignoreErrorsFlag, true, "Skip invalid/unreadable objects")
|
|
||||||
flags.Bool(awaitFlag, false, "Block execution until evacuation is completed")
|
|
||||||
flags.Bool(noProgressFlag, false, fmt.Sprintf("Print progress if %s provided", awaitFlag))
|
|
||||||
|
|
||||||
startEvacuationShardCmd.MarkFlagsMutuallyExclusive(shardIDFlag, shardAllFlag)
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue