forked from TrueCloudLab/frostfs-node
Compare commits
18 commits
f1f3c80dbf
...
b4582239bf
Author | SHA1 | Date | |
---|---|---|---|
b4582239bf | |||
4e244686cf | |||
6cd806f998 | |||
3e6fd4c611 | |||
5ae4446280 | |||
5890cd4d7d | |||
365adb4ebd | |||
bce5827f64 | |||
05471d3827 | |||
8226d49376 | |||
0893689c6a | |||
a4931ea4c7 | |||
861e9ab59a | |||
|
24a540caa8 | ||
6226c3ba86 | |||
f2250a316f | |||
9929dcf50b | |||
7486c02bbc |
53 changed files with 450 additions and 351 deletions
|
@ -5,4 +5,4 @@ docker-compose.yml
|
|||
Dockerfile
|
||||
temp
|
||||
.dockerignore
|
||||
docker
|
||||
docker
|
||||
|
|
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -1 +0,0 @@
|
|||
* @TrueCloudLab/storage-core @TrueCloudLab/committers
|
29
.github/workflows/changelog.yml
vendored
29
.github/workflows/changelog.yml
vendored
|
@ -1,29 +0,0 @@
|
|||
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
37
.github/workflows/config-update.yml
vendored
|
@ -1,37 +0,0 @@
|
|||
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
22
.github/workflows/dco.yml
vendored
|
@ -1,22 +0,0 @@
|
|||
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
60
.github/workflows/go.yml
vendored
|
@ -1,60 +0,0 @@
|
|||
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
|
10
.gitlint
Normal file
10
.gitlint
Normal file
|
@ -0,0 +1,10 @@
|
|||
[general]
|
||||
fail-without-commits=true
|
||||
contrib=CC1
|
||||
|
||||
[title-match-regex]
|
||||
regex=^\[\#[0-9]+\]\s
|
||||
|
||||
[ignore-by-title]
|
||||
regex=^Release(.*)
|
||||
ignore=title-match-regex
|
|
@ -24,6 +24,8 @@ linters-settings:
|
|||
govet:
|
||||
# report about shadowed variables
|
||||
check-shadowing: false
|
||||
staticcheck:
|
||||
checks: ["all", "-SA1019"] # TODO Enable SA1019 after deprecated warning are fixed.
|
||||
|
||||
linters:
|
||||
enable:
|
||||
|
@ -53,4 +55,3 @@ linters:
|
|||
- whitespace
|
||||
disable-all: true
|
||||
fast: false
|
||||
|
||||
|
|
36
.pre-commit-config.yaml
Normal file
36
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,36 @@
|
|||
ci:
|
||||
autofix_prs: false
|
||||
|
||||
repos:
|
||||
- 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/golangci/golangci-lint
|
||||
rev: v1.51.2
|
||||
hooks:
|
||||
- id: golangci-lint
|
||||
|
||||
- repo: https://github.com/jorisroovers/gitlint
|
||||
rev: v0.18.0
|
||||
hooks:
|
||||
- id: gitlint
|
||||
stages: [commit-msg]
|
||||
|
||||
- repo: https://github.com/koalaman/shellcheck-precommit
|
||||
rev: v0.9.0
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
# args: ["--severity=warning"] # Optionally only show errors and warnings
|
123
CHANGELOG.md
123
CHANGELOG.md
|
@ -13,6 +13,7 @@ Changelog for FrostFS Node
|
|||
- Reload config for pprof and metrics on SIGHUP in `neofs-node` (#1868)
|
||||
- Multiple configs support (#44)
|
||||
- Parameters `nns-name` and `nns-zone` for command `frostfs-cli container create` (#37)
|
||||
- Tree service now saves the last synchronization height which persists across restarts (#82)
|
||||
|
||||
### Changed
|
||||
- Change `frostfs_node_engine_container_size` to counting sizes of logical objects
|
||||
|
@ -46,6 +47,8 @@ Changelog for FrostFS Node
|
|||
- Do not fetch an object if `meta` is missing it (#61)
|
||||
- Create contract wallet only by `init` and `update-config` command (#63)
|
||||
- Actually use `object.put.pool_size_local` and independent pool for local puts (#64).
|
||||
- Pretty printer of basic ACL in the NeoFS CLI (#2259)
|
||||
- Adding of public key for nns group `group.frostfs` at init step (#130)
|
||||
|
||||
### Removed
|
||||
### Updated
|
||||
|
@ -198,7 +201,7 @@ Tree service network replication can now be fine-tuned with `tree.replication_ti
|
|||
- `neo-go` to `v0.99.4`
|
||||
- `protoc` to `v3.21.7`
|
||||
- `neofs-sdk` to `v1.0.0-rc.7`
|
||||
|
||||
|
||||
### Updating from v0.33.0
|
||||
Now storage node serves Control API `SetNemapStatus` request with `MAINTENANCE`
|
||||
status only if the mode is allowed in the network settings. To force starting the local
|
||||
|
@ -236,7 +239,7 @@ command.
|
|||
- Policer marks nodes under maintenance as OK without requests (#1680)
|
||||
- Unify help messages in CLI (#1854)
|
||||
- `evacuate`, `set-mode` and `flush-cache` control subcommands now accept a list of shard ids (#1867)
|
||||
- Reading `object` commands of NeoFS CLI don't open remote sessions (#1865)
|
||||
- Reading `object` commands of NeoFS CLI don't open remote sessions (#1865)
|
||||
- Use hex format to print storage node ID (#1765)
|
||||
|
||||
### Fixed
|
||||
|
@ -264,7 +267,7 @@ command.
|
|||
- `neofs-contract` to `v0.16.0`
|
||||
- `neofs-api-go` to `v2.14.0`
|
||||
|
||||
### Updating from v0.32.0
|
||||
### Updating from v0.32.0
|
||||
Replace using the `control netmap-snapshot` command with `netmap snapshot` one in NeoFS CLI.
|
||||
Node can now specify additional addresses in `ExternalAddr` attribute. To allow a node to dial
|
||||
other nodes external address, use `apiclient.allow_external` config setting.
|
||||
|
@ -274,7 +277,7 @@ Pass `maintenance` state to `neofs-cli control set-status` to enter maintenance
|
|||
If network allows maintenance state (*), it will be reflected in the network map.
|
||||
Storage nodes under maintenance are not excluded from the network map, but don't
|
||||
serve object operations. (*) can be fetched from network configuration via
|
||||
`neofs-cli netmap netinfo` command.
|
||||
`neofs-cli netmap netinfo` command.
|
||||
|
||||
To allow maintenance mode during neofs-adm deployments, set
|
||||
`network.maintenance_mode_allowed` parameter in config.
|
||||
|
@ -569,15 +572,15 @@ Clean up all metabases and re-sync them using `resync_metabase` config flag.
|
|||
- Reduced amount of slices with pointers (#1239)
|
||||
|
||||
### Updating from v0.28.0-rc.2
|
||||
Remove `NEOFS_IR_MAINNET_ENDPOINT_NOTIFICATION`,
|
||||
Remove `NEOFS_IR_MAINNET_ENDPOINT_NOTIFICATION`,
|
||||
`NEOFS_IR_MORPH_ENDPOINT_NOTIFICATION`, and `NEOFS_MORPH_NOTIFICATION_ENDPOINT`
|
||||
from Inner Ring and Storage configurations.
|
||||
from Inner Ring and Storage configurations.
|
||||
|
||||
Specify _WebSocket_ endpoints in `NEOFS_IR_MAINNET_ENDPOINT_CLIENT`,
|
||||
`NEOFS_IR_MORPH_ENDPOINT_CLIENT`, and `NEOFS_MORPH_RPC_ENDPOINT` at Inner Ring
|
||||
and Storage configurations.
|
||||
|
||||
Specify path to persistent session token db in Storage configuration with
|
||||
Specify path to persistent session token db in Storage configuration with
|
||||
`NEOFS_NODE_PERSISTENT_SESSIONS_PATH`.
|
||||
|
||||
## [0.28.0-rc.2] - 2022-03-24
|
||||
|
@ -593,7 +596,7 @@ Specify path to persistent session token db in Storage configuration with
|
|||
|
||||
## [0.28.0-rc.1] - 2022-03-18
|
||||
|
||||
Native RFC-6979 signatures of messages and tokens, LOCK object types,
|
||||
Native RFC-6979 signatures of messages and tokens, LOCK object types,
|
||||
experimental notifications over NATS with NeoFS API v2.12 support
|
||||
|
||||
### Fixed
|
||||
|
@ -629,8 +632,8 @@ experimental notifications over NATS with NeoFS API v2.12 support
|
|||
- Deprecated structures from SDK v1.0.0 rc (#1181)
|
||||
|
||||
### Updating from neofs-node v0.27.5
|
||||
Set shard error threshold for read-only mode switch with
|
||||
`NEOFS_STORAGE_SHARD_RO_ERROR_THRESHOLD` (default: 0, deactivated).
|
||||
Set shard error threshold for read-only mode switch with
|
||||
`NEOFS_STORAGE_SHARD_RO_ERROR_THRESHOLD` (default: 0, deactivated).
|
||||
|
||||
Set NATS configuration for notifications in `NEOFS_NODE_NOTIFICATION` section.
|
||||
See example config for more details.
|
||||
|
@ -696,7 +699,7 @@ See example config for more details.
|
|||
Use `--wallet` key in CLI to provide WIF or binary key file instead of `--wif`
|
||||
and `--binary-key`.
|
||||
|
||||
Replace `NEOFS_STORAGE_SHARD_N_USE_WRITE_CACHE` with
|
||||
Replace `NEOFS_STORAGE_SHARD_N_USE_WRITE_CACHE` with
|
||||
`NEOFS_STORAGE_SHARD_N_WRITECACHE_ENABLED` in Storage node config.
|
||||
|
||||
Specify `password: xxx` in config file for NeoFS CLI to avoid password input.
|
||||
|
@ -773,7 +776,7 @@ NeoFS API v2.11.0 support with response status codes and storage subnetworks.
|
|||
- CLI now opens LOCODE database in read-only mode for listing command (#958)
|
||||
- Tombstone owner now is always set (#842)
|
||||
- Node in relay mode does not require shard config anymore (#969)
|
||||
- Alphabet nodes now ignore notary notifications with non-HALT main tx (#976)
|
||||
- Alphabet nodes now ignore notary notifications with non-HALT main tx (#976)
|
||||
- neofs-adm now prints version of NNS contract (#1014)
|
||||
- Possible NPE in blobovnicza (#1007)
|
||||
- More precise calculation of blobovnicza size (#915)
|
||||
|
@ -790,13 +793,13 @@ NeoFS API v2.11.0 support with response status codes and storage subnetworks.
|
|||
- Alphabet nodes resign `AddPeer` request if it updates Storage node info (#938)
|
||||
- All applications now use client from neofs-sdk-go library (#966)
|
||||
- Some shard configuration records were renamed, see upgrading section (#859)
|
||||
- `Nonce` and `VUB` values of notary transactions generated from notification
|
||||
- `Nonce` and `VUB` values of notary transactions generated from notification
|
||||
hash (#844)
|
||||
- Non alphabet notary invocations now have 4 witnesses (#975)
|
||||
- Object replication is now async and continuous (#965)
|
||||
- NeoFS ADM updated for the neofs-contract v0.13.0 deploy (#984)
|
||||
- Minimal TLS version is set to v1.2 (#878)
|
||||
- Alphabet nodes now invoke `netmap.Register` to add node to the network map
|
||||
- Alphabet nodes now invoke `netmap.Register` to add node to the network map
|
||||
candidates in notary enabled environment (#1008)
|
||||
|
||||
### Upgrading from v0.26.1
|
||||
|
@ -826,7 +829,7 @@ with `NEOFS_IR_FEE_NAMED_CONTAINER_REGISTER`.
|
|||
### Fixed
|
||||
- Storage Node handles requests before its initialization is finished (#934)
|
||||
- Release worker pools gracefully (#901)
|
||||
- Metabase ignored containers of storage group and tombstone objects
|
||||
- Metabase ignored containers of storage group and tombstone objects
|
||||
in listing (#945)
|
||||
- CLI missed endpoint flag in `control netmap-snapshot` command (#942)
|
||||
- Write cache object persisting (#866)
|
||||
|
@ -840,16 +843,16 @@ with `NEOFS_IR_FEE_NAMED_CONTAINER_REGISTER`.
|
|||
|
||||
### Changed
|
||||
- Use FSTree counter in write cache (#821)
|
||||
- Calculate notary deposit `till` parameter depending on available
|
||||
- Calculate notary deposit `till` parameter depending on available
|
||||
deposit (#910)
|
||||
- Storage node returns session token error if attached token's private key
|
||||
- Storage node returns session token error if attached token's private key
|
||||
is not available (#943)
|
||||
- Refactor of NeoFS API client in inner ring (#946)
|
||||
- LOCODE generator tries to find the closest continent if there are
|
||||
- LOCODE generator tries to find the closest continent if there are
|
||||
no exact match (#955)
|
||||
|
||||
### Upgrading from v0.26.0
|
||||
You can specify default section in storage engine configuration.
|
||||
You can specify default section in storage engine configuration.
|
||||
See [example](./config/example/node.yaml) for more details.
|
||||
|
||||
## [0.26.0] - 2021-10-19 - Udo (우도, 牛島)
|
||||
|
@ -859,7 +862,7 @@ NeoFS API v2.10 support
|
|||
### Fixed
|
||||
- Check remote node public key in every response message (#645)
|
||||
- Do not lose local container size estimations (#872)
|
||||
- Compressed and uncompressed objects are always available for reading
|
||||
- Compressed and uncompressed objects are always available for reading
|
||||
regardless of compression configuration (#868)
|
||||
- Use request session token in ACL check of object.Put (#881)
|
||||
- Parse URI in neofs-cli properly (#883)
|
||||
|
@ -913,7 +916,7 @@ instead.
|
|||
### Added
|
||||
- Support of multiple Neo RPC endpoints in Inner Ring node (#792)
|
||||
|
||||
`mainchain` section of storage node config is left unused by the application.
|
||||
`mainchain` section of storage node config is left unused by the application.
|
||||
|
||||
## [0.25.0] - 2021-09-27 - Mungapdo (문갑도, 文甲島)
|
||||
|
||||
|
@ -921,7 +924,7 @@ instead.
|
|||
- Work of a storage node with one Neo RPC endpoint instead of a list (#746)
|
||||
- Lack of support for HEAD operation on the object write cache (#762)
|
||||
- Storage node attribute parsing is stable now (#787)
|
||||
- Inner Ring node now logs transaction hashes of Deposit and Withdraw events
|
||||
- Inner Ring node now logs transaction hashes of Deposit and Withdraw events
|
||||
in LittleEndian encoding (#794)
|
||||
- Storage node uses public keys of the remote nodes in placement traverser
|
||||
checks (#645)
|
||||
|
@ -929,7 +932,7 @@ instead.
|
|||
(#816)
|
||||
- neofs-adm supports update and deploy of neofs-contract v0.11.0 (#834, #836)
|
||||
- Possible NPE in public key conversion (#848)
|
||||
- Object assembly routine do not forward existing request instead of creating
|
||||
- Object assembly routine do not forward existing request instead of creating
|
||||
new one (#839)
|
||||
- Shard now returns only physical stored objects for replication (#840)
|
||||
|
||||
|
@ -938,7 +941,7 @@ instead.
|
|||
- Smart contract address auto negotiation with NNS contract (#736)
|
||||
- Detailed logs for all data writing operations in storage engine (#790)
|
||||
- Docker build and release targets in Makefile (#785)
|
||||
- Metabase restore option in the shard config (#789)
|
||||
- Metabase restore option in the shard config (#789)
|
||||
- Write cache used size limit in bytes (#776)
|
||||
|
||||
### Changed
|
||||
|
@ -973,7 +976,7 @@ Added `NEOFS_STORAGE_SHARD_<N>_WRITECACHE_SIZE_LIMIT` where `<N>` is shard ID.
|
|||
This is the size limit for the all write cache storages combined in bytes. Default
|
||||
size limit is 1 GiB.
|
||||
|
||||
Added `NEOFS_STORAGE_SHARD_<N>_REFILL_METABASE` bool flag where `<N>` is shard
|
||||
Added `NEOFS_STORAGE_SHARD_<N>_REFILL_METABASE` bool flag where `<N>` is shard
|
||||
ID. This flag purges metabase instance at the application start and reinitialize
|
||||
it with available objects from the blobstor.
|
||||
|
||||
|
@ -982,12 +985,12 @@ Object service pool size now split into `NEOFS_OBJECT_PUT_POOL_SIZE_REMOTE` and
|
|||
|
||||
## [0.24.1] - 2021-09-07
|
||||
|
||||
### Fixed
|
||||
### Fixed
|
||||
- Storage and Inner Ring will not start until Neo RPC node will have the height
|
||||
of the latest processed block by the nodes (#795)
|
||||
|
||||
### Upgrading from v0.24.0
|
||||
Specify path to the local state DB in Inner Ring node config with
|
||||
Specify path to the local state DB in Inner Ring node config with
|
||||
`NEOFS_IR_NODE_PERSISTENT_STATE_PATH`. Specify path to the local state DB in
|
||||
Storage node config with `NEOFS_NODE_PERSISTENT_STATE_PATH`.
|
||||
|
||||
|
@ -1006,7 +1009,7 @@ Storage node config with `NEOFS_NODE_PERSISTENT_STATE_PATH`.
|
|||
- Contract update support in `neofs-adm` utility (#748)
|
||||
- Container transferring support in `neofs-adm` utility (#755)
|
||||
- Storage Node's balance refilling support in `neofs-adm` utility (#758)
|
||||
- Support `COMMON_PREFIX` filter for object attributes in storage engine and `neofs-cli` (#760)
|
||||
- Support `COMMON_PREFIX` filter for object attributes in storage engine and `neofs-cli` (#760)
|
||||
- Node's and IR's notary status debug message on startup (#758)
|
||||
- Go `1.17` unit tests in CI (#766)
|
||||
- Supporting all eACL filter fields from the specification (#768)
|
||||
|
@ -1068,7 +1071,7 @@ Improved stability for notary disabled environment.
|
|||
- Storage Node configuration example contains usable parameters (#699)
|
||||
|
||||
### Fixed
|
||||
- Do not use side chain RoleManagement contract as source of Inner Ring list
|
||||
- Do not use side chain RoleManagement contract as source of Inner Ring list
|
||||
when notary disabled in side chain (#672)
|
||||
- Alphabet list transition is even more effective (#697)
|
||||
- Inner Ring node does not require proxy and processing contracts if notary
|
||||
|
@ -1135,9 +1138,9 @@ Storage nodes with a group of network endpoints.
|
|||
- Control service with healthcheck RPC in IR and CLI support ([#414](https://github.com/nspcc-dev/neofs-node/issues/414)).
|
||||
|
||||
### Fixed
|
||||
- Approval of objects with with duplicate attribute keys or empty values ([#633](https://github.com/nspcc-dev/neofs-node/issues/633)).
|
||||
- Approval of objects with with duplicate attribute keys or empty values ([#633](https://github.com/nspcc-dev/neofs-node/issues/633)).
|
||||
- Approval of containers with with duplicate attribute keys or empty values ([#634](https://github.com/nspcc-dev/neofs-node/issues/634)).
|
||||
- Default path for CLI config ([#626](https://github.com/nspcc-dev/neofs-node/issues/626)).
|
||||
- Default path for CLI config ([#626](https://github.com/nspcc-dev/neofs-node/issues/626)).
|
||||
|
||||
### Changed
|
||||
- `version` command replaced with `--version` flag in CLI ([#571](https://github.com/nspcc-dev/neofs-node/issues/571)).
|
||||
|
@ -1165,7 +1168,7 @@ Storage nodes with a group of network endpoints.
|
|||
- grpc: [v1.38.0](https://github.com/grpc/grpc-go/releases/tag/v1.38.0).
|
||||
- cast: [v1.3.1](https://github.com/spf13/cast/releases/tag/v1.3.1).
|
||||
- cobra: [1.1.3](https://github.com/spf13/cobra/releases/tag/v1.1.3).
|
||||
- viper: [v1.8.1](https://github.com/spf13/viper/releases/tag/v1.8.1).
|
||||
- viper: [v1.8.1](https://github.com/spf13/viper/releases/tag/v1.8.1).
|
||||
|
||||
## [0.21.1] - 2021-06-10
|
||||
|
||||
|
@ -1185,7 +1188,7 @@ Session token support in container service, refactored config in storage node,
|
|||
TLS support on gRPC servers.
|
||||
|
||||
### Fixed
|
||||
- ACL service traverses over all RequestMetaHeader chain to find
|
||||
- ACL service traverses over all RequestMetaHeader chain to find
|
||||
bearer and session tokens (#548).
|
||||
- Object service correctly resends complete objects without attached
|
||||
session token (#501).
|
||||
|
@ -1193,7 +1196,7 @@ TLS support on gRPC servers.
|
|||
- Client cache now gracefully closes all available connections (#567).
|
||||
|
||||
### Added
|
||||
- Session token support in container service for `container.Put`,
|
||||
- Session token support in container service for `container.Put`,
|
||||
`container.Delete` and `container.SetEACL` operations.
|
||||
- Session token support in container and sign command of NeoFS CLI.
|
||||
- TLS encryption support of gRPC service in storage node.
|
||||
|
@ -1203,8 +1206,8 @@ TLS support on gRPC servers.
|
|||
update earlier.
|
||||
- Inner ring processes extended ACL changes.
|
||||
- Inner ring makes signature checks of containers and extended ACLs.
|
||||
- Refactored config of storage node.
|
||||
- Static clients from `morph/client` do not process notary invocations
|
||||
- Refactored config of storage node.
|
||||
- Static clients from `morph/client` do not process notary invocations
|
||||
explicitly anymore. Now notary support specified at static client creation.
|
||||
- Updated neo-go to v0.95.1 release.
|
||||
- Updated neofs-api-go to v1.27.0 release.
|
||||
|
@ -1215,7 +1218,7 @@ TLS support on gRPC servers.
|
|||
|
||||
## [0.20.0] - 2021-05-21 - Dolsando (돌산도, 突山島)
|
||||
|
||||
NeoFS is N3 RC2 compatible.
|
||||
NeoFS is N3 RC2 compatible.
|
||||
|
||||
### Fixed
|
||||
- Calculations in EigenTrust algorithm (#527).
|
||||
|
@ -1228,7 +1231,7 @@ NeoFS is N3 RC2 compatible.
|
|||
- Client for NeoFSID contract.
|
||||
|
||||
### Changed
|
||||
- Reorganized and removed plenty of application configuration records
|
||||
- Reorganized and removed plenty of application configuration records
|
||||
(#510, #511, #512, #514).
|
||||
- Nodes do not resolve remote addresses manually.
|
||||
- Presets for basic ACL in CLI are `private` ,`public-read` and
|
||||
|
@ -1246,11 +1249,11 @@ NeoFS is N3 RC2 compatible.
|
|||
|
||||
Storage nodes exchange, calculate, aggregate and store reputation information
|
||||
in reputation contract. Inner ring nodes support workflows with and without
|
||||
notary subsystem in chains.
|
||||
notary subsystem in chains.
|
||||
|
||||
### Fixed
|
||||
- Build with go1.16.
|
||||
- Notary deposits last more blocks.
|
||||
- Notary deposits last more blocks.
|
||||
- TX hashes now prints in little endian in logs.
|
||||
- Metabase deletes graves regardless of the presence of objects.
|
||||
- SplitInfo error created from all shards instead of first matched shard.
|
||||
|
@ -1258,7 +1261,7 @@ notary subsystem in chains.
|
|||
- Storage node does not send rebootstrap messages after it went offline.
|
||||
|
||||
### Added
|
||||
- Reputation subsystem that includes reputation collection, exchange,
|
||||
- Reputation subsystem that includes reputation collection, exchange,
|
||||
calculation and storage components.
|
||||
- Notary and non notary workflows in inner ring.
|
||||
- Audit fee transfer for inner ring nodes that performed audit.
|
||||
|
@ -1268,7 +1271,7 @@ calculation and storage components.
|
|||
|
||||
### Changed
|
||||
- Metabase puts data in batches.
|
||||
- Network related new epoch handlers in storage node executed asynchronously.
|
||||
- Network related new epoch handlers in storage node executed asynchronously.
|
||||
- Storage node gets epoch duration from global config.
|
||||
- Storage node resign and resend Search, Range, Head, Get requests of object
|
||||
service without modification.
|
||||
|
@ -1285,7 +1288,7 @@ alphabet keys are synchronized with main chain.
|
|||
### Fixed
|
||||
- Metabase does not store object payloads anymore.
|
||||
- TTLNetCache now always evict data after a timeout.
|
||||
- NeoFS CLI keyer could misinterpret hex value as base58.
|
||||
- NeoFS CLI keyer could misinterpret hex value as base58.
|
||||
|
||||
### Added
|
||||
- Local trust controller in storage node.
|
||||
|
@ -1297,7 +1300,7 @@ alphabet keys are synchronized with main chain.
|
|||
|
||||
## [0.17.0] - 2021-03-22 - Jebudo (제부도, 濟扶島)
|
||||
|
||||
Notary contract support, updated neofs-api-go with raw client, some performance
|
||||
Notary contract support, updated neofs-api-go with raw client, some performance
|
||||
tweaks with extra caches and enhanced metrics.
|
||||
|
||||
### Added
|
||||
|
@ -1316,7 +1319,7 @@ tweaks with extra caches and enhanced metrics.
|
|||
|
||||
Garbage collector is now running inside storage engine. It is accessed
|
||||
via Control API, from `policer` component and through object expiration
|
||||
scrubbers.
|
||||
scrubbers.
|
||||
|
||||
Inner ring configuration now supports single chain mode with any number of
|
||||
alphabet contracts.
|
||||
|
@ -1347,39 +1350,39 @@ Storage node now supports NetworkInfo method in netmap service.
|
|||
|
||||
## [0.15.0] - 2021-02-12 - Seonyudo (선유도, 仙遊島)
|
||||
|
||||
NeoFS nodes are now preview5-compatible.
|
||||
NeoFS nodes are now preview5-compatible.
|
||||
|
||||
IR nodes are now engaged in the distribution of funds to the storage nodes:
|
||||
for the passed audit and for the amount of stored information. All timers
|
||||
of the IR nodes related to the generation and processing of global system
|
||||
events are decoupled from astronomical time, and are measured in the number
|
||||
for the passed audit and for the amount of stored information. All timers
|
||||
of the IR nodes related to the generation and processing of global system
|
||||
events are decoupled from astronomical time, and are measured in the number
|
||||
of blockchain blocks.
|
||||
|
||||
For the geographic positioning of storage nodes, a global NeoFS location
|
||||
database is now used, the key in which is a UN/LOCODE, and the base itself
|
||||
database is now used, the key in which is a UN/LOCODE, and the base itself
|
||||
is generated on the basis of the UN/LOCODE and OpenFlights databases.
|
||||
|
||||
### Added
|
||||
- Timers with time in blocks of the chain.
|
||||
- Subscriptions to new blocks in blockchain event `Listener`.
|
||||
- Tracking the volume of stored information by containers in the
|
||||
- Tracking the volume of stored information by containers in the
|
||||
storage engine and an external interface for obtaining this data.
|
||||
- `TransferX` operation in sidechain client.
|
||||
- Calculators of audit and basic settlements.
|
||||
- Distribution of funds to storage nodes for audit and for the amount
|
||||
- Distribution of funds to storage nodes for audit and for the amount
|
||||
of stored information (settlement processors of IR).
|
||||
- NeoFS API `Container.AnnounceUsedSpace` RPC service.
|
||||
- Exchange of information about container volumes between storage nodes
|
||||
- Exchange of information about container volumes between storage nodes
|
||||
controlled by IR through sidechain notifications.
|
||||
- Support of new search matchers (`STRING_NOT_EQUAL`, `NOT_PRESENT`).
|
||||
- Functional for the formation of NeoFS location database.
|
||||
- CLI commands for generating and reading the location database.
|
||||
- Checking the locode attribute and generating geographic attributes
|
||||
- Checking the locode attribute and generating geographic attributes
|
||||
for candidates for a network map on IR side.
|
||||
- Verification of the eACL signature when checking Object ACL rules.
|
||||
|
||||
### Fixed
|
||||
- Overwriting the local configuration of node attributes when updating
|
||||
- Overwriting the local configuration of node attributes when updating
|
||||
the network map.
|
||||
- Ignoring the X-headers CLI `storagegroup` commands.
|
||||
- Inability to attach bearer token in CLI `storagegroup` commands.
|
||||
|
@ -1397,7 +1400,7 @@ is generated on the basis of the UN/LOCODE and OpenFlights databases.
|
|||
### Fixed
|
||||
- Upload of objects bigger than single gRPC message.
|
||||
- Inconsistent placement issues (#347, #349).
|
||||
- Bug when ACL request classifier failed to classify `RoleOthers` in
|
||||
- Bug when ACL request classifier failed to classify `RoleOthers` in
|
||||
first epoch.
|
||||
|
||||
### Added
|
||||
|
@ -1411,13 +1414,13 @@ is generated on the basis of the UN/LOCODE and OpenFlights databases.
|
|||
|
||||
Testnet4 related bugfixes.
|
||||
|
||||
### Fixed
|
||||
- Default values for blobovnicza object size limit and blobstor small object
|
||||
### Fixed
|
||||
- Default values for blobovnicza object size limit and blobstor small object
|
||||
size are not zero.
|
||||
- Various storage engine log messages.
|
||||
- Bug when inner ring node ignored bootstrap messages from restarted storage
|
||||
nodes.
|
||||
|
||||
nodes.
|
||||
|
||||
### Added
|
||||
- Timeout for reading boltDB files at storage node initialization.
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ In alphabetical order:
|
|||
- Alexey Vanin
|
||||
- Anastasia Prasolova
|
||||
- Anatoly Bogatyrev
|
||||
- Evgeny Kulikov
|
||||
- Evgeny Kulikov
|
||||
- Evgeny Stratonikov
|
||||
- Leonard Liubich
|
||||
- Sergei Liubich
|
||||
|
|
11
Makefile
Normal file → Executable file
11
Makefile
Normal file → Executable file
|
@ -26,7 +26,7 @@ PKG_VERSION ?= $(shell echo $(VERSION) | sed "s/^v//" | \
|
|||
sed "s/-/~/")-${OS_RELEASE}
|
||||
|
||||
.PHONY: help all images dep clean fmts fmt imports test lint docker/lint
|
||||
prepare-release debpackage
|
||||
prepare-release debpackage pre-commit unpre-commit
|
||||
|
||||
# To build a specific binary, use it's name prefix with bin/ as a target
|
||||
# For example `make bin/frostfs-node` will build only storage node binary
|
||||
|
@ -140,10 +140,19 @@ docker/lint:
|
|||
--env HOME=/src \
|
||||
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
|
||||
version:
|
||||
@echo $(VERSION)
|
||||
|
||||
# Delete built artifacts
|
||||
clean:
|
||||
rm -rf vendor
|
||||
rm -rf .cache
|
||||
|
|
|
@ -3,31 +3,30 @@
|
|||
## Overview
|
||||
|
||||
Admin tool provides an easier way to deploy and maintain private installation
|
||||
of FrostFS. Private installation provides a set of N3 consensus nodes, FrostFS
|
||||
Alphabet, and Storage nodes. Admin tool generates consensus keys, initializes
|
||||
of FrostFS. Private installation provides a set of N3 consensus nodes, FrostFS
|
||||
Alphabet, and Storage nodes. Admin tool generates consensus keys, initializes
|
||||
the sidechain, and provides functions to update the network and register new
|
||||
Storage nodes.
|
||||
|
||||
## 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`.
|
||||
|
||||
At FrostFS private install deployment, frostfs-adm requires compiled FrostFS
|
||||
contracts. Find them in the latest release of
|
||||
At FrostFS private install deployment, frostfs-adm requires compiled FrostFS
|
||||
contracts. Find them in the latest release of
|
||||
[frostfs-contract repository](https://git.frostfs.info/TrueCloudLab/frostfs-contract/releases).
|
||||
|
||||
|
||||
## Commands
|
||||
|
||||
### Config
|
||||
|
||||
Config section provides `init` command that creates a configuration file for
|
||||
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).
|
||||
|
||||
Config example:
|
||||
|
@ -58,14 +57,14 @@ credentials: # passwords for consensus node / alphabet wallets
|
|||
|
||||
#### Network deployment
|
||||
|
||||
- `generate-alphabet` generates a set of wallets for consensus and
|
||||
Alphabet nodes.
|
||||
- `generate-alphabet` generates a set of wallets for consensus and
|
||||
Alphabet nodes.
|
||||
|
||||
- `init` initializes the sidechain by deploying smart contracts and
|
||||
setting provided FrostFS network configuration.
|
||||
|
||||
- `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
|
||||
- `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
|
||||
wallet can be used for FrostFS bootstrap.
|
||||
|
||||
#### Network maintenance
|
||||
|
@ -75,7 +74,7 @@ credentials: # passwords for consensus node / alphabet wallets
|
|||
- `force-new-epoch` increments FrostFS epoch number and executes new epoch
|
||||
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.
|
||||
|
||||
|
@ -87,7 +86,7 @@ info. These commands **do not migrate actual objects**.
|
|||
- `dump-containers` saves all containers and metadata registered in the container
|
||||
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.
|
||||
|
||||
- `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
|
||||
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.
|
||||
|
||||
## Prerequisites
|
||||
|
@ -12,11 +12,11 @@ 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 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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
It is easier to use`frostfs-adm` with a predefined configuration. First, create
|
||||
|
@ -27,7 +27,7 @@ consensus / Alphabet node in the network.
|
|||
$ frostfs-adm config init --path 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
|
||||
alphabet-wallets: /home/user/deploy/alphabet-wallets
|
||||
network:
|
||||
|
@ -43,17 +43,17 @@ credentials:
|
|||
az: hunter2
|
||||
```
|
||||
|
||||
For private installation, it is recommended to set all **fees** and **basic
|
||||
income rate** to 0.
|
||||
For private installation, it is recommended to set all **fees** and **basic
|
||||
income rate** to 0.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
```
|
||||
|
@ -69,14 +69,14 @@ storage.
|
|||
## Step 2: Launch consensus nodes
|
||||
|
||||
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).
|
||||
|
||||
Gather public keys from **all** generated wallets. We are interested in the first
|
||||
`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):
|
||||
02c1cc85f9c856dbe2d02017349bcb7b4e5defa78b8056a09b3240ba2a8c078869
|
||||
|
||||
|
@ -87,10 +87,10 @@ NiMKabp3ddi3xShmLAXhTfbnuWb4cSJT6E (1 out of 1 multisig contract):
|
|||
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`
|
||||
and `ApplicationConfiguration.UnlockWallet` sections. If config includes
|
||||
`ProtocolConfiguration.NativeActivations` section, add notary
|
||||
`ProtocolConfiguration.NativeActivations` section, add notary
|
||||
contract `Notary: [0]`.
|
||||
|
||||
```yaml
|
||||
|
@ -121,7 +121,7 @@ and possible overload issues.
|
|||
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
|
||||
Stage 1: transfer GAS to alphabet nodes.
|
||||
|
@ -153,8 +153,8 @@ Waiting for transactions to persist...
|
|||
|
||||
## Step 4: Launch Alphabet nodes
|
||||
|
||||
Configure Alphabet nodes with the wallets generated in step 1. For
|
||||
`morph.validators` use a list of public keys from
|
||||
Configure Alphabet nodes with the wallets generated in step 1. For
|
||||
`morph.validators` use a list of public keys from
|
||||
`ProtocolConfiguration.StandbyCommittee`.
|
||||
|
||||
```yaml
|
||||
|
@ -178,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
|
||||
New password >
|
||||
New password >
|
||||
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):
|
||||
0355eccb72cd46f09a3e5237eaa0f4949cceb5ecfa5a225bd3bb9fd021c4d75b85
|
||||
```
|
||||
|
@ -205,7 +205,7 @@ Current epoch: 8, increase to 9.
|
|||
Waiting for transactions to persist...
|
||||
```
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
After that, FrostFS Storage is ready to work. You can access it directly or
|
||||
with protocol gates.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# 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
|
||||
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
|
||||
|
|
|
@ -88,11 +88,11 @@ 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
|
||||
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:
|
||||
For bootstrapping, you need to specify the ID of the subnetwork in the node's
|
||||
configuration:
|
||||
|
||||
```yaml
|
||||
...
|
||||
|
@ -106,7 +106,7 @@ node:
|
|||
```
|
||||
|
||||
**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
|
||||
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:
|
||||
|
||||
|
@ -122,7 +122,7 @@ node:
|
|||
|
||||
# Creating container in non-zero subnetwork
|
||||
|
||||
Creating containers without using `--subnet` flag is equivalent to
|
||||
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
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"text/tabwriter"
|
||||
|
||||
"git.frostfs.info/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/rpcclient/invoker"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||
|
@ -123,7 +124,7 @@ func dumpCustomZoneHashes(cmd *cobra.Command, nnsHash util.Uint160, zone string,
|
|||
return
|
||||
}
|
||||
|
||||
if !bytes.HasSuffix(bs, []byte(zone)) {
|
||||
if !bytes.HasSuffix(bs, []byte(zone)) || bytes.HasPrefix(bs, []byte(morphClient.NNSGroupKeyName)) {
|
||||
// Related https://github.com/nspcc-dev/neofs-contract/issues/316.
|
||||
return
|
||||
}
|
||||
|
|
|
@ -82,13 +82,13 @@ func (c *initializeContext) setNNS() error {
|
|||
|
||||
func (c *initializeContext) updateNNSGroup(nnsHash util.Uint160, pub *keys.PublicKey) error {
|
||||
bw := io.NewBufBinWriter()
|
||||
needUpdate, needRegister, err := c.emitUpdateNNSGroupScript(bw, nnsHash, pub)
|
||||
if !needUpdate || err != nil {
|
||||
keyAlreadyAdded, domainRegCodeEmitted, err := c.emitUpdateNNSGroupScript(bw, nnsHash, pub)
|
||||
if keyAlreadyAdded || err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
script := bw.Bytes()
|
||||
if needRegister {
|
||||
if domainRegCodeEmitted {
|
||||
w := io.NewBufBinWriter()
|
||||
emit.Instruction(w.BinWriter, opcode.INITSSLOT, []byte{1})
|
||||
wrapRegisterScriptWithPrice(w, nnsHash, script)
|
||||
|
@ -228,20 +228,27 @@ func nnsResolve(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (stac
|
|||
}
|
||||
|
||||
func nnsResolveKey(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (*keys.PublicKey, error) {
|
||||
item, err := nnsResolve(inv, nnsHash, domain)
|
||||
res, err := nnsResolve(inv, nnsHash, domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, ok := item.Value().(stackitem.Null)
|
||||
if ok {
|
||||
if _, ok := res.Value().(stackitem.Null); ok {
|
||||
return nil, errors.New("NNS record is missing")
|
||||
}
|
||||
bs, err := v.TryBytes()
|
||||
if err != nil {
|
||||
return nil, errors.New("malformed response")
|
||||
arr, ok := res.Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return nil, errors.New("API of the NNS contract method `resolve` has changed")
|
||||
}
|
||||
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.
|
||||
|
|
|
@ -26,18 +26,18 @@ Action is 'allow' or 'deny'.
|
|||
Operation is an object service verb: 'get', 'head', 'put', 'search', 'delete', 'getrange', or 'getrangehash'.
|
||||
|
||||
Filter consists of <typ>:<key><match><value>
|
||||
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.
|
||||
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.
|
||||
Well-known system object headers start with '$Object:' 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
|
||||
Match is '=' for matching and '!=' for non-matching filter.
|
||||
Value is a valid unicode string corresponding to object or request header value.
|
||||
|
||||
Target is
|
||||
'user' for container owner,
|
||||
Target is
|
||||
'user' for container owner,
|
||||
'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.
|
||||
|
||||
When both '--rule' and '--file' arguments are used, '--rule' records will be placed higher in resulting extended ACL table.
|
||||
|
|
|
@ -37,7 +37,7 @@ var (
|
|||
var createContainerCmd = &cobra.Command{
|
||||
Use: "create",
|
||||
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.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
placementPolicy, err := parseContainerPolicy(cmd, containerPolicy)
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
var deleteContainerCmd = &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "Delete existing container",
|
||||
Long: `Delete existing container.
|
||||
Long: `Delete existing container.
|
||||
Only owner of the container has a permission to remove container.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
id := parseContainerID(cmd)
|
||||
|
|
|
@ -24,7 +24,7 @@ func PrettyPrintTableBACL(cmd *cobra.Command, bacl *acl.Basic) {
|
|||
fmt.Fprintln(w, "\tRangeHASH\tRange\tSearch\tDelete\tPut\tHead\tGet")
|
||||
// Bits
|
||||
bits := []string{
|
||||
boolToString(bacl.Sticky()) + " " + boolToString(bacl.Extendable()),
|
||||
boolToString(bacl.Sticky()) + " " + boolToString(!bacl.Extendable()),
|
||||
getRoleBitsForOperation(bacl, acl.OpObjectHash), getRoleBitsForOperation(bacl, acl.OpObjectRange),
|
||||
getRoleBitsForOperation(bacl, acl.OpObjectSearch), getRoleBitsForOperation(bacl, acl.OpObjectDelete),
|
||||
getRoleBitsForOperation(bacl, acl.OpObjectPut), getRoleBitsForOperation(bacl, acl.OpObjectHead),
|
||||
|
@ -47,7 +47,7 @@ func getRoleBitsForOperation(bacl *acl.Basic, op acl.Op) string {
|
|||
return boolToString(bacl.IsOpAllowed(op, acl.RoleOwner)) + " " +
|
||||
boolToString(bacl.IsOpAllowed(op, acl.RoleContainer)) + " " +
|
||||
boolToString(bacl.IsOpAllowed(op, acl.RoleOthers)) + " " +
|
||||
boolToString(bacl.IsOpAllowed(op, acl.RoleInnerRing))
|
||||
boolToString(bacl.AllowedBearerRules(op))
|
||||
}
|
||||
|
||||
func boolToString(b bool) string {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
# Examples of correct configuration file structures
|
||||
# Examples of correct configuration file structures
|
||||
|
||||
Here are files in all supported formats and with all possible configuration values
|
||||
of FrostFS applications. See [node.yaml](node.yaml) for configuration notes.
|
||||
|
||||
All parameters are correct, however, they are for informational purposes only.
|
||||
All parameters are correct, however, they are for informational purposes only.
|
||||
It is not recommended transferring these configs for real application launches.
|
||||
|
||||
## Config files
|
||||
|
@ -15,7 +15,7 @@ It is not recommended transferring these configs for real application launches.
|
|||
- YAML: `ir.yaml`
|
||||
- CLI
|
||||
- YAML: `cli.yaml`
|
||||
|
||||
|
||||
### Multiple configs
|
||||
|
||||
You can split your configuration to several files.
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
# N3 Mainnet Storage node configuration
|
||||
|
||||
Here is a template for simple storage node configuration in N3 Mainnet.
|
||||
Make sure to specify correct values instead of `<...>` placeholders.
|
||||
Make sure to specify correct values instead of `<...>` placeholders.
|
||||
Do not change `contracts` section. Run the latest frostfs-node release with
|
||||
the fixed config `frostfs-node -c config.yml`
|
||||
|
||||
To use NeoFS in the Mainnet, you need to deposit assets to NeoFS contract.
|
||||
The contract sript hash is `2cafa46838e8b564468ebd868dcafdd99dce6221`
|
||||
The contract sript hash is `2cafa46838e8b564468ebd868dcafdd99dce6221`
|
||||
(N3 address `NNxVrKjLsRkWsmGgmuNXLcMswtxTGaNQLk`)
|
||||
|
||||
## Tips
|
||||
|
|
|
@ -6,8 +6,8 @@ docker image and run it with docker-compose.
|
|||
|
||||
## Build image
|
||||
|
||||
Prepared **frostfs-storage-testnet** image is available at Docker Hub.
|
||||
However, if you need to rebuild it for some reason, run
|
||||
Prepared **frostfs-storage-testnet** image is available at Docker Hub.
|
||||
However, if you need to rebuild it for some reason, run
|
||||
`make image-storage-testnet` command.
|
||||
|
||||
```
|
||||
|
@ -19,13 +19,13 @@ Successfully tagged nspccdev/neofs-storage-testnet:0.25.1
|
|||
|
||||
## Deploy node
|
||||
|
||||
To run a storage node in N3 Testnet environment, you should deposit GAS assets,
|
||||
To run a storage node in N3 Testnet environment, you should deposit GAS assets,
|
||||
update docker-compose file and start the node.
|
||||
|
||||
### Deposit
|
||||
|
||||
The Storage Node owner should deposit GAS to NeoFS smart contract. It generates a
|
||||
bit of sidechain GAS in the node's wallet. Sidechain GAS is used to send bootstrap tx.
|
||||
The Storage Node owner should deposit GAS to NeoFS smart contract. It generates a
|
||||
bit of sidechain GAS in the node's wallet. Sidechain GAS is used to send bootstrap tx.
|
||||
|
||||
First, obtain GAS in N3 Testnet chain. You can do that with
|
||||
[faucet](https://neowish.ngd.network) service.
|
||||
|
@ -34,7 +34,7 @@ Then, make a deposit by transferring GAS to NeoFS contract in N3 Testnet.
|
|||
You can provide scripthash in the `data` argument of transfer tx to make a
|
||||
deposit to a specified account. Otherwise, deposit is made to the tx sender.
|
||||
|
||||
NeoFS contract scripthash in N3 Testnet is `b65d8243ac63983206d17e5221af0653a7266fa1`,
|
||||
NeoFS contract scripthash in N3 Testnet is `b65d8243ac63983206d17e5221af0653a7266fa1`,
|
||||
so the address is `NadZ8YfvkddivcFFkztZgfwxZyKf1acpRF`.
|
||||
|
||||
See a deposit example with `neo-go`.
|
||||
|
@ -57,7 +57,7 @@ NEOFS_GRPC_0_ENDPOINT=65.52.183.157:36512
|
|||
NEOFS_NODE_ADDRESSES=65.52.183.157:36512
|
||||
```
|
||||
|
||||
Set up your [UN/LOCODE](https://unece.org/trade/cefact/unlocode-code-list-country-and-territory)
|
||||
Set up your [UN/LOCODE](https://unece.org/trade/cefact/unlocode-code-list-country-and-territory)
|
||||
attribute.
|
||||
|
||||
```
|
||||
|
@ -66,7 +66,7 @@ NEOFS_NODE_ADDRESSES=65.52.183.157:36512
|
|||
NEOFS_NODE_ATTRIBUTE_2=UN-LOCODE:RU LED
|
||||
```
|
||||
|
||||
You can validate UN/LOCODE attribute in
|
||||
You can validate UN/LOCODE attribute in
|
||||
[NeoFS LOCODE database](https://github.com/TrueCloudLab/frostfs-locode-db/releases/tag/v0.1.0)
|
||||
with frostfs-cli.
|
||||
|
||||
|
@ -79,7 +79,7 @@ Subdivision: [SPE] Sankt-Peterburg
|
|||
Coordinates: 59.53, 30.15
|
||||
```
|
||||
|
||||
It is recommended to pass the node's key as a file. To do so, convert your wallet
|
||||
It is recommended to pass the node's key as a file. To do so, convert your wallet
|
||||
WIF to 32-byte hex (via `frostfs-cli` for example) and save it to a file.
|
||||
|
||||
```
|
||||
|
@ -104,8 +104,8 @@ Then, specify the path to this file in `docker-compose.yml`
|
|||
```
|
||||
|
||||
|
||||
NeoFS objects will be stored on your machine. By default, docker-compose
|
||||
is configured to store objects in named docker volume `frostfs_storage`. You can
|
||||
NeoFS objects will be stored on your machine. By default, docker-compose
|
||||
is configured to store objects in named docker volume `frostfs_storage`. You can
|
||||
specify a directory on the filesystem to store objects there.
|
||||
|
||||
```yaml
|
||||
|
@ -120,7 +120,7 @@ Run the node with `docker-compose up` command and stop it with `docker-compose d
|
|||
|
||||
### Debug
|
||||
|
||||
To print node logs, use `docker logs frostfs-testnet`. To print debug messages in
|
||||
To print node logs, use `docker logs frostfs-testnet`. To print debug messages in
|
||||
log, set up log level to debug with this env:
|
||||
|
||||
```yaml
|
||||
|
|
2
debian/clean
vendored
2
debian/clean
vendored
|
@ -1,2 +1,2 @@
|
|||
man/
|
||||
man/
|
||||
debian/*.bash-completion
|
||||
|
|
4
debian/copyright
vendored
4
debian/copyright
vendored
|
@ -19,5 +19,5 @@ License: GPL-3
|
|||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program or at /usr/share/common-licenses/GPL-3
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
along with this program or at /usr/share/common-licenses/GPL-3
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
|
|
6
debian/frostfs-ir.postinst
vendored
Normal file → Executable file
6
debian/frostfs-ir.postinst
vendored
Normal file → Executable file
|
@ -28,9 +28,9 @@ case "$1" in
|
|||
chmod -f 0640 /etc/frostfs/$USERNAME/config.yml || true
|
||||
chmod -f 0640 /etc/frostfs/$USERNAME/control.yml || true
|
||||
fi
|
||||
USERDIR=$(getent passwd "frostfs-$USERNAME" | cut -d: -f6)
|
||||
if ! dpkg-statoverride --list frostfs-$USERDIR >/dev/null; then
|
||||
chown -f frostfs-$USERNAME: $USERDIR
|
||||
USERDIR="$(getent passwd frostfs-$USERNAME | cut -d: -f6)"
|
||||
if ! dpkg-statoverride --list frostfs-"$USERDIR" >/dev/null; then
|
||||
chown -f frostfs-$USERNAME: "$USERDIR"
|
||||
fi
|
||||
;;
|
||||
|
||||
|
|
0
debian/frostfs-ir.postrm
vendored
Normal file → Executable file
0
debian/frostfs-ir.postrm
vendored
Normal file → Executable file
0
debian/frostfs-ir.preinst
vendored
Normal file → Executable file
0
debian/frostfs-ir.preinst
vendored
Normal file → Executable file
0
debian/frostfs-ir.prerm
vendored
Normal file → Executable file
0
debian/frostfs-ir.prerm
vendored
Normal file → Executable file
6
debian/frostfs-storage.postinst
vendored
Normal file → Executable file
6
debian/frostfs-storage.postinst
vendored
Normal file → Executable file
|
@ -28,9 +28,9 @@ case "$1" in
|
|||
chmod -f 0640 /etc/frostfs/$USERNAME/config.yml || true
|
||||
chmod -f 0640 /etc/frostfs/$USERNAME/control.yml || true
|
||||
fi
|
||||
USERDIR=$(getent passwd "frostfs-$USERNAME" | cut -d: -f6)
|
||||
if ! dpkg-statoverride --list frostfs-$USERDIR >/dev/null; then
|
||||
chown -f frostfs-$USERNAME: $USERDIR
|
||||
USERDIR=$(getent passwd frostfs-$USERNAME | cut -d: -f6)
|
||||
if ! dpkg-statoverride --list frostfs-"$USERDIR" >/dev/null; then
|
||||
chown -f frostfs-$USERNAME: "$USERDIR"
|
||||
fi
|
||||
USERDIR=/srv/frostfs
|
||||
if ! dpkg-statoverride --list frostfs-$USERDIR >/dev/null; then
|
||||
|
|
0
debian/frostfs-storage.postrm
vendored
Normal file → Executable file
0
debian/frostfs-storage.postrm
vendored
Normal file → Executable file
0
debian/frostfs-storage.preinst
vendored
Normal file → Executable file
0
debian/frostfs-storage.preinst
vendored
Normal file → Executable file
0
debian/frostfs-storage.prerm
vendored
Normal file → Executable file
0
debian/frostfs-storage.prerm
vendored
Normal file → Executable file
|
@ -4,7 +4,7 @@
|
|||
|
||||
Each mode is characterized by two important properties:
|
||||
1. Whether modifying operations are allowed.
|
||||
2. Whether metabase and write-cache is available.
|
||||
2. Whether metabase and write-cache is available.
|
||||
The expected deployment scenario is to place both metabase and write-cache on an SSD drive thus these modes
|
||||
can be approximately described as no-SSD modes.
|
||||
|
||||
|
@ -45,4 +45,4 @@ However, all mode changing operations are idempotent.
|
|||
Shard can automatically switch to a `degraded-read-only` mode in 3 cases:
|
||||
1. If the metabase was not available or couldn't be opened/initialized during shard startup.
|
||||
2. If shard error counter exceeds threshold.
|
||||
3. If the metabase couldn't be reopened during SIGHUP handling.
|
||||
3. If the metabase couldn't be reopened during SIGHUP handling.
|
||||
|
|
|
@ -45,8 +45,8 @@ control:
|
|||
grpc:
|
||||
- endpoint: localhost:8080
|
||||
tls:
|
||||
enabled: true
|
||||
certificate: /path/to/cert.pem
|
||||
enabled: true
|
||||
certificate: /path/to/cert.pem
|
||||
key: /path/to/key.pem
|
||||
- endpoint: internal.ip:8080
|
||||
- endpoint: external.ip:8080
|
||||
|
@ -237,7 +237,7 @@ gc:
|
|||
| Parameter | Type | Default value | Description |
|
||||
|--------------------------|------------|---------------|----------------------------------------------|
|
||||
| `remover_batch_size` | `int` | `100` | Amount of objects to grab in a single batch. |
|
||||
| `remover_sleep_interval` | `duration` | `1m` | Time to sleep between iterations. |
|
||||
| `remover_sleep_interval` | `duration` | `1m` | Time to sleep between iterations. |
|
||||
|
||||
### `metabase` subsection
|
||||
|
||||
|
@ -271,7 +271,7 @@ writecache:
|
|||
| Parameter | Type | Default value | Description |
|
||||
|----------------------|------------|---------------|----------------------------------------------------------------------------------------------------------------------|
|
||||
| `path` | `string` | | Path to the metabase file. |
|
||||
| `capacity` | `size` | unrestricted | Approximate maximum size of the writecache. If the writecache is full, objects are written to the blobstor directly. |
|
||||
| `capacity` | `size` | unrestricted | Approximate maximum size of the writecache. If the writecache is full, objects are written to the blobstor directly. |
|
||||
| `small_object_size` | `size` | `32K` | Maximum object size for "small" objects. This objects are stored in a key-value database instead of a file-system. |
|
||||
| `max_object_size` | `size` | `64M` | Maximum object size allowed to be stored in the writecache. |
|
||||
| `workers_number` | `int` | `20` | Amount of background workers that move data from the writecache to the blobstor. |
|
||||
|
|
|
@ -30,5 +30,5 @@ Update `GO_VERSION` variable in `./Makefile`.
|
|||
|
||||
## Apply language changes
|
||||
|
||||
Open PR that fixes/updates repository's code according to
|
||||
Open PR that fixes/updates repository's code according to
|
||||
language improvements.
|
||||
|
|
|
@ -925,7 +925,7 @@ func New(ctx context.Context, log *logger.Logger, cfg *viper.Viper, errChan chan
|
|||
queueSize: cfg.GetUint32("workers.subnet"),
|
||||
})
|
||||
|
||||
if cfg.GetString("metrics.address") != "" {
|
||||
if cfg.GetString("prometheus.address") != "" {
|
||||
m := metrics.NewInnerRingMetrics()
|
||||
server.metrics = &m
|
||||
}
|
||||
|
|
|
@ -167,6 +167,9 @@ func (s *Server) ResetEpochTimer(h uint32) error {
|
|||
|
||||
func (s *Server) setHealthStatus(hs control.HealthStatus) {
|
||||
s.healthStatus.Store(hs)
|
||||
if s.metrics != nil {
|
||||
s.metrics.SetHealth(int32(hs))
|
||||
}
|
||||
}
|
||||
|
||||
// HealthStatus returns the current health status of the IR application.
|
||||
|
|
|
@ -213,6 +213,44 @@ func (e *StorageEngine) TreeExists(cid cidSDK.ID, treeID string) (bool, error) {
|
|||
return err == nil, err
|
||||
}
|
||||
|
||||
// TreeUpdateLastSyncHeight implements the pilorama.Forest interface.
|
||||
func (e *StorageEngine) TreeUpdateLastSyncHeight(cid cidSDK.ID, treeID string, height uint64) error {
|
||||
index, lst, err := e.getTreeShard(cid, treeID)
|
||||
if err != nil && !errors.Is(err, pilorama.ErrTreeNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
err = lst[index].TreeUpdateLastSyncHeight(cid, treeID, height)
|
||||
if err != nil && !errors.Is(err, shard.ErrReadOnlyMode) && err != shard.ErrPiloramaDisabled {
|
||||
e.reportShardError(lst[index], "can't update tree synchronization height", err,
|
||||
zap.Stringer("cid", cid),
|
||||
zap.String("tree", treeID))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// TreeLastSyncHeight implements the pilorama.Forest interface.
|
||||
func (e *StorageEngine) TreeLastSyncHeight(cid cidSDK.ID, treeID string) (uint64, error) {
|
||||
var err error
|
||||
var height uint64
|
||||
for _, sh := range e.sortShardsByWeight(cid) {
|
||||
height, err = sh.TreeLastSyncHeight(cid, treeID)
|
||||
if err != nil {
|
||||
if err == shard.ErrPiloramaDisabled {
|
||||
break
|
||||
}
|
||||
if !errors.Is(err, pilorama.ErrTreeNotFound) {
|
||||
e.reportShardError(sh, "can't read tree synchronization height", err,
|
||||
zap.Stringer("cid", cid),
|
||||
zap.String("tree", treeID))
|
||||
}
|
||||
continue
|
||||
}
|
||||
return height, err
|
||||
}
|
||||
return height, err
|
||||
}
|
||||
|
||||
func (e *StorageEngine) getTreeShard(cid cidSDK.ID, treeID string) (int, []hashedShard, error) {
|
||||
lst := e.sortShardsByWeight(cid)
|
||||
for i, sh := range lst {
|
||||
|
|
|
@ -7,7 +7,7 @@ This file describes changes between the metabase versions.
|
|||
### Primary buckets
|
||||
- Graveyard bucket
|
||||
- Name: `_Graveyard`
|
||||
- Key: object address
|
||||
- Key: object address
|
||||
- Value: tombstone address
|
||||
- Garbage bucket
|
||||
- Name: `_Garbage`
|
||||
|
@ -23,7 +23,7 @@ This file describes changes between the metabase versions.
|
|||
- Key: container ID
|
||||
- Value: container size in bytes as little-endian uint64
|
||||
- Bucket for storing locked objects information
|
||||
- Name: `_Locked`
|
||||
- Name: `_Locked`
|
||||
- Key: container ID
|
||||
- Value: bucket mapping objects locked to the list of corresponding LOCK objects
|
||||
- Bucket containing auxilliary information. All keys are custom and are not connected to the container
|
||||
|
@ -102,4 +102,4 @@ This file describes changes between the metabase versions.
|
|||
|
||||
- Container ID is encoded as base58 string
|
||||
- Object ID is encoded as base58 string
|
||||
- Address is encoded as container ID + "/" + object ID
|
||||
- Address is encoded as container ID + "/" + object ID
|
||||
|
|
|
@ -192,6 +192,46 @@ func (t *boltForest) TreeExists(cid cidSDK.ID, treeID string) (bool, error) {
|
|||
return exists, err
|
||||
}
|
||||
|
||||
var syncHeightKey = []byte{'h'}
|
||||
|
||||
// TreeUpdateLastSyncHeight implements the pilorama.Forest interface.
|
||||
func (t *boltForest) TreeUpdateLastSyncHeight(cid cidSDK.ID, treeID string, height uint64) error {
|
||||
rawHeight := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(rawHeight, height)
|
||||
|
||||
buck := bucketName(cid, treeID)
|
||||
return t.db.Batch(func(tx *bbolt.Tx) error {
|
||||
treeRoot := tx.Bucket(buck)
|
||||
if treeRoot == nil {
|
||||
return ErrTreeNotFound
|
||||
}
|
||||
|
||||
b := treeRoot.Bucket(dataBucket)
|
||||
return b.Put(syncHeightKey, rawHeight)
|
||||
})
|
||||
}
|
||||
|
||||
// TreeLastSyncHeight implements the pilorama.Forest interface.
|
||||
func (t *boltForest) TreeLastSyncHeight(cid cidSDK.ID, treeID string) (uint64, error) {
|
||||
var height uint64
|
||||
|
||||
buck := bucketName(cid, treeID)
|
||||
err := t.db.View(func(tx *bbolt.Tx) error {
|
||||
treeRoot := tx.Bucket(buck)
|
||||
if treeRoot == nil {
|
||||
return ErrTreeNotFound
|
||||
}
|
||||
|
||||
b := treeRoot.Bucket(dataBucket)
|
||||
data := b.Get(syncHeightKey)
|
||||
if len(data) == 8 {
|
||||
height = binary.LittleEndian.Uint64(data)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return height, err
|
||||
}
|
||||
|
||||
// TreeAddByPath implements the Forest interface.
|
||||
func (t *boltForest) TreeAddByPath(d CIDDescriptor, treeID string, attr string, path []string, meta []KeyValue) ([]Move, error) {
|
||||
if !d.checkValid() {
|
||||
|
|
|
@ -226,3 +226,24 @@ func (f *memoryForest) TreeExists(cid cidSDK.ID, treeID string) (bool, error) {
|
|||
_, ok := f.treeMap[fullID]
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
// TreeUpdateLastSyncHeight implements the pilorama.Forest interface.
|
||||
func (f *memoryForest) TreeUpdateLastSyncHeight(cid cidSDK.ID, treeID string, height uint64) error {
|
||||
fullID := cid.EncodeToString() + "/" + treeID
|
||||
t, ok := f.treeMap[fullID]
|
||||
if !ok {
|
||||
return ErrTreeNotFound
|
||||
}
|
||||
t.syncHeight = height
|
||||
return nil
|
||||
}
|
||||
|
||||
// TreeLastSyncHeight implements the pilorama.Forest interface.
|
||||
func (f *memoryForest) TreeLastSyncHeight(cid cidSDK.ID, treeID string) (uint64, error) {
|
||||
fullID := cid.EncodeToString() + "/" + treeID
|
||||
t, ok := f.treeMap[fullID]
|
||||
if !ok {
|
||||
return 0, ErrTreeNotFound
|
||||
}
|
||||
return t.syncHeight, nil
|
||||
}
|
||||
|
|
|
@ -1030,3 +1030,52 @@ func testTreeGetTrees(t *testing.T, s Forest) {
|
|||
require.ElementsMatch(t, treeIDs[cid], trees)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeLastSyncHeight(t *testing.T) {
|
||||
for i := range providers {
|
||||
t.Run(providers[i].name, func(t *testing.T) {
|
||||
testTreeLastSyncHeight(t, providers[i].construct(t))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testTreeLastSyncHeight(t *testing.T, f Forest) {
|
||||
cnr := cidtest.ID()
|
||||
treeID := "someTree"
|
||||
|
||||
t.Run("ErrNotFound if no log operations are stored for a tree", func(t *testing.T) {
|
||||
_, err := f.TreeLastSyncHeight(cnr, treeID)
|
||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||
|
||||
err = f.TreeUpdateLastSyncHeight(cnr, treeID, 1)
|
||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||
})
|
||||
|
||||
_, err := f.TreeMove(CIDDescriptor{CID: cnr, Size: 1}, treeID, &Move{
|
||||
Parent: RootID,
|
||||
Child: 1,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
h, err := f.TreeLastSyncHeight(cnr, treeID)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 0, h)
|
||||
|
||||
t.Run("separate storages for separate containers", func(t *testing.T) {
|
||||
_, err := f.TreeLastSyncHeight(cidtest.ID(), treeID)
|
||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||
})
|
||||
|
||||
require.NoError(t, f.TreeUpdateLastSyncHeight(cnr, treeID, 10))
|
||||
|
||||
h, err = f.TreeLastSyncHeight(cnr, treeID)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 10, h)
|
||||
|
||||
t.Run("removed correctly", func(t *testing.T) {
|
||||
require.NoError(t, f.TreeDrop(cnr, treeID))
|
||||
|
||||
_, err := f.TreeLastSyncHeight(cnr, treeID)
|
||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -143,8 +143,9 @@ func (s *state) findSpareID() Node {
|
|||
|
||||
// tree is a mapping from the child nodes to their parent and metadata.
|
||||
type tree struct {
|
||||
infoMap map[Node]nodeInfo
|
||||
childMap map[Node][]Node
|
||||
syncHeight uint64
|
||||
infoMap map[Node]nodeInfo
|
||||
childMap map[Node][]Node
|
||||
}
|
||||
|
||||
func newTree() *tree {
|
||||
|
|
|
@ -44,6 +44,10 @@ type Forest interface {
|
|||
// TreeExists checks if a tree exists locally.
|
||||
// If the tree is not found, false and a nil error should be returned.
|
||||
TreeExists(cid cidSDK.ID, treeID string) (bool, error)
|
||||
// TreeUpdateLastSyncHeight updates last log height synchronized with _all_ container nodes.
|
||||
TreeUpdateLastSyncHeight(cid cidSDK.ID, treeID string, height uint64) error
|
||||
// TreeLastSyncHeight returns last log height synchronized with _all_ container nodes.
|
||||
TreeLastSyncHeight(cid cidSDK.ID, treeID string) (uint64, error)
|
||||
}
|
||||
|
||||
type ForestStorage interface {
|
||||
|
|
|
@ -111,3 +111,19 @@ func (s *Shard) TreeExists(cid cidSDK.ID, treeID string) (bool, error) {
|
|||
}
|
||||
return s.pilorama.TreeExists(cid, treeID)
|
||||
}
|
||||
|
||||
// TreeUpdateLastSyncHeight implements the pilorama.Forest interface.
|
||||
func (s *Shard) TreeUpdateLastSyncHeight(cid cidSDK.ID, treeID string, height uint64) error {
|
||||
if s.pilorama == nil {
|
||||
return ErrPiloramaDisabled
|
||||
}
|
||||
return s.pilorama.TreeUpdateLastSyncHeight(cid, treeID, height)
|
||||
}
|
||||
|
||||
// TreeLastSyncHeight implements the pilorama.Forest interface.
|
||||
func (s *Shard) TreeLastSyncHeight(cid cidSDK.ID, treeID string) (uint64, error) {
|
||||
if s.pilorama == nil {
|
||||
return 0, ErrPiloramaDisabled
|
||||
}
|
||||
return s.pilorama.TreeLastSyncHeight(cid, treeID)
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@ package metrics
|
|||
|
||||
import "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
const innerRingSubsystem = "object"
|
||||
const innerRingSubsystem = "ir"
|
||||
|
||||
// InnerRingServiceMetrics contains metrics collected by inner ring.
|
||||
type InnerRingServiceMetrics struct {
|
||||
epoch prometheus.Gauge
|
||||
epoch prometheus.Gauge
|
||||
health prometheus.Gauge
|
||||
}
|
||||
|
||||
// NewInnerRingMetrics returns new instance of metrics collectors for inner ring.
|
||||
|
@ -18,12 +19,20 @@ func NewInnerRingMetrics() InnerRingServiceMetrics {
|
|||
Name: "epoch",
|
||||
Help: "Current epoch as seen by inner-ring node.",
|
||||
})
|
||||
health = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: innerRingSubsystem,
|
||||
Name: "health",
|
||||
Help: "Current inner-ring node state.",
|
||||
})
|
||||
)
|
||||
|
||||
prometheus.MustRegister(epoch)
|
||||
prometheus.MustRegister(health)
|
||||
|
||||
return InnerRingServiceMetrics{
|
||||
epoch: epoch,
|
||||
epoch: epoch,
|
||||
health: health,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,3 +40,8 @@ func NewInnerRingMetrics() InnerRingServiceMetrics {
|
|||
func (m InnerRingServiceMetrics) SetEpoch(epoch uint64) {
|
||||
m.epoch.Set(float64(epoch))
|
||||
}
|
||||
|
||||
// SetHealth updates health metrics.
|
||||
func (m InnerRingServiceMetrics) SetHealth(s int32) {
|
||||
m.health.Set(float64(s))
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ func (p *Policer) processObject(ctx context.Context, addrWithType objectcore.Add
|
|||
p.processNodes(c, addrWithType, nn[i], policy.ReplicaNumberByIndex(i), checkedNodes)
|
||||
}
|
||||
|
||||
if !c.needLocalCopy {
|
||||
if !c.needLocalCopy && c.removeLocalCopy {
|
||||
p.log.Info("redundant local object copy detected",
|
||||
zap.Stringer("object", addr),
|
||||
)
|
||||
|
@ -139,7 +139,11 @@ func (p *Policer) processObject(ctx context.Context, addrWithType objectcore.Add
|
|||
type processPlacementContext struct {
|
||||
context.Context
|
||||
|
||||
// needLocalCopy is true if the current node must store an object according to the storage policy.
|
||||
needLocalCopy bool
|
||||
// removeLocalCopy is true if all copies are stored according to the storage policy
|
||||
// and the current node doesn't need to store an object.
|
||||
removeLocalCopy bool
|
||||
}
|
||||
|
||||
func (p *Policer) processNodes(ctx *processPlacementContext, addrWithType objectcore.AddressWithType,
|
||||
|
@ -242,9 +246,11 @@ func (p *Policer) processNodes(ctx *processPlacementContext, addrWithType object
|
|||
} else if uncheckedCopies > 0 {
|
||||
// If we have more copies than needed, but some of them are from the maintenance nodes,
|
||||
// save the local copy.
|
||||
ctx.needLocalCopy = true
|
||||
p.log.Debug("some of the copies are stored on nodes under maintenance, save local copy",
|
||||
zap.Int("count", uncheckedCopies))
|
||||
} else if uncheckedCopies == 0 {
|
||||
// Safe to remove: checked all copies, shortage == 0.
|
||||
ctx.removeLocalCopy = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,10 +31,8 @@ type Service struct {
|
|||
syncChan chan struct{}
|
||||
syncPool *ants.Pool
|
||||
|
||||
// cnrMap maps contrainer and tree ID to the minimum height which was fetched from _each_ client.
|
||||
// This allows us to better handle split-brain scenario, because we always synchronize
|
||||
// from the last seen height. The inner map is read-only and should not be modified in-place.
|
||||
cnrMap map[cidSDK.ID]map[string]uint64
|
||||
// cnrMap contains existing (used) container IDs.
|
||||
cnrMap map[cidSDK.ID]struct{}
|
||||
// cnrMapMtx protects cnrMap
|
||||
cnrMapMtx sync.Mutex
|
||||
}
|
||||
|
@ -63,7 +61,7 @@ func New(opts ...Option) *Service {
|
|||
s.replicateLocalCh = make(chan applyOp)
|
||||
s.replicationTasks = make(chan replicationTask, s.replicatorWorkerCount)
|
||||
s.containerCache.init(s.containerCacheSize)
|
||||
s.cnrMap = make(map[cidSDK.ID]map[string]uint64)
|
||||
s.cnrMap = make(map[cidSDK.ID]struct{})
|
||||
s.syncChan = make(chan struct{})
|
||||
s.syncPool, _ = ants.NewPool(defaultSyncWorkerCount)
|
||||
|
||||
|
|
|
@ -86,31 +86,24 @@ func (s *Service) synchronizeAllTrees(ctx context.Context, cid cid.ID) error {
|
|||
return fmt.Errorf("could not fetch tree ID list: %w", outErr)
|
||||
}
|
||||
|
||||
s.cnrMapMtx.Lock()
|
||||
oldStatus := s.cnrMap[cid]
|
||||
s.cnrMapMtx.Unlock()
|
||||
|
||||
syncStatus := map[string]uint64{}
|
||||
for i := range treesToSync {
|
||||
syncStatus[treesToSync[i]] = 0
|
||||
}
|
||||
for tid := range oldStatus {
|
||||
if _, ok := syncStatus[tid]; ok {
|
||||
syncStatus[tid] = oldStatus[tid]
|
||||
}
|
||||
}
|
||||
|
||||
for _, tid := range treesToSync {
|
||||
h := s.synchronizeTree(ctx, d, syncStatus[tid], tid, nodes)
|
||||
if syncStatus[tid] < h {
|
||||
syncStatus[tid] = h
|
||||
h, err := s.forest.TreeLastSyncHeight(d.CID, tid)
|
||||
if err != nil && !errors.Is(err, pilorama.ErrTreeNotFound) {
|
||||
s.log.Warn("could not get last synchronized height for a tree",
|
||||
zap.Stringer("cid", d.CID),
|
||||
zap.String("tree", tid))
|
||||
continue
|
||||
}
|
||||
newHeight := s.synchronizeTree(ctx, d, h, tid, nodes)
|
||||
if h < newHeight {
|
||||
if err := s.forest.TreeUpdateLastSyncHeight(d.CID, tid, newHeight); err != nil {
|
||||
s.log.Warn("could not update last synchronized height for a tree",
|
||||
zap.Stringer("cid", d.CID),
|
||||
zap.String("tree", tid))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.cnrMapMtx.Lock()
|
||||
s.cnrMap[cid] = syncStatus
|
||||
s.cnrMapMtx.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -288,7 +281,6 @@ func (s *Service) syncLoop(ctx context.Context) {
|
|||
s.log.Error("could not calculate container nodes",
|
||||
zap.Stringer("cid", cnr),
|
||||
zap.Error(err))
|
||||
removed = append(removed, cnr)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -21,15 +21,15 @@ Zsh:
|
|||
If shell completion is not already enabled in your environment you will need
|
||||
to enable it. You can execute the following once:
|
||||
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
|
||||
|
||||
|
||||
To load completions for each session, execute once:
|
||||
$ %s completion zsh > "${fpath[1]}/_%s"
|
||||
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
|
||||
Fish:
|
||||
$ %s completion fish | source
|
||||
|
||||
|
||||
To load completions for each session, execute once:
|
||||
$ %s completion fish > ~/.config/fish/completions/%s.fish
|
||||
`
|
||||
|
@ -43,7 +43,7 @@ func Command(name string) *cobra.Command {
|
|||
name, name, name, name, name, name, name, name, name, name),
|
||||
DisableFlagsInUseLine: true,
|
||||
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
|
||||
Args: cobra.ExactValidArgs(1),
|
||||
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
switch args[0] {
|
||||
case "bash":
|
||||
|
|
Loading…
Reference in a new issue