forked from TrueCloudLab/frostfs-node
Compare commits
658 commits
bugfix/upd
...
master
Author | SHA1 | Date | |
---|---|---|---|
d7fcc5ce30 | |||
c0221d76e6 | |||
242f0095d0 | |||
6fe34d266a | |||
fa08bfa553 | |||
0da998ef50 | |||
e44782473a | |||
9cd1bcef06 | |||
ca0a33ea0f | |||
f6c5222952 | |||
ea868e09f8 | |||
31d3d299bf | |||
b5b4f78b49 | |||
2832f44437 | |||
7c3bcb0f44 | |||
e64871c3fd | |||
303cd35a01 | |||
bb9ba1bce2 | |||
db03742d33 | |||
148d68933b | |||
51ee132ea3 | |||
226dd25dd0 | |||
bd0197eaa8 | |||
e44b84c18c | |||
bed49e6ace | |||
df05057ed4 | |||
b6c8ebf493 | |||
6e82661c35 | |||
1a091ea7bb | |||
7ac3542714 | |||
f0c43c8d80 | |||
8ba9f31fca | |||
2af3409d39 | |||
d165ac042c | |||
7151c71d51 | |||
91d9dc2676 | |||
7853dbc315 | |||
3821645085 | |||
72470d6b48 | |||
e9837bbcf9 | |||
a641c91594 | |||
b1614a284d | |||
d0ce835fbf | |||
dfa51048a8 | |||
670305a721 | |||
1f6cf57e30 | |||
386a12eea4 | |||
15139d80c9 | |||
41da27dad5 | |||
ac0511d214 | |||
7e542906ef | |||
d1bc4351c3 | |||
1c12f23b84 | |||
a353d45742 | |||
d5c46d812a | |||
d5d5ce2074 | |||
7df3520d48 | |||
5fe78e51d1 | |||
84b4051b4d | |||
6a51086030 | |||
5c3b2d95ba | |||
2d5d4093be | |||
e3487d5af5 | |||
e37dcdf88b | |||
6c679d1535 | |||
281d65435e | |||
b348b20289 | |||
748edd1999 | |||
47dfd8840c | |||
432042c534 | |||
9cabca9dfe | |||
60feed3b5f | |||
635a292ae4 | |||
edfa3f4825 | |||
e0ac3a583f | |||
00c608c05e | |||
bba1892fa1 | |||
01acec708f | |||
aac65001e5 | |||
1170370753 | |||
9e275d44c8 | |||
2469e0c683 | |||
a6ef4ab524 | |||
49959c4166 | |||
61ee1b5610 | |||
b10c954377 | |||
1605391628 | |||
b1766e47c7 | |||
caa4253249 | |||
7eac5fb18b | |||
0e5524dac7 | |||
3ebd560f42 | |||
1ed7ab75fb | |||
99f9e59de9 | |||
256f96e252 | |||
e5ea95c045 | |||
9073e555db | |||
2771fdb8c7 | |||
efa4ce00b8 | |||
f12f04199e | |||
2e2c62147d | |||
6ae8667fb4 | |||
49a4e727fd | |||
2e974f734c | |||
3042490340 | |||
a339b52a60 | |||
4ab4ed6f96 | |||
3b1364e4cf | |||
daff77b273 | |||
7a7ee71a4d | |||
ffe9906266 | |||
ae31ef3602 | |||
e2cb0640f1 | |||
9f4ce600ac | |||
d82f0d1926 | |||
acd5babd86 | |||
b65874d1c3 | |||
69c63006da | |||
d77a218f7c | |||
44df67492f | |||
1e6f132b4e | |||
6dc0dc6691 | |||
f7cb6b4d87 | |||
7fc6101bec | |||
7ef36749d0 | |||
c6066d6ee4 | |||
612b34d570 | |||
7429553266 | |||
6921a89061 | |||
16598553d9 | |||
c139892117 | |||
62b5181618 | |||
6db46257c0 | |||
c16dae8b4d | |||
fd004add00 | |||
8ed7a676d5 | |||
b451de94c8 | |||
f1556e3c42 | |||
e122ff6013 | |||
e2658c7519 | |||
c00f4bab18 | |||
46fef276b4 | |||
9bd05e94c8 | |||
16830033f8 | |||
1cf51a8079 | |||
3324c26fd8 | |||
a692298533 | |||
be2753de00 | |||
b543569c3f | |||
80f8a8fd3a | |||
2f3bc6eb84 | |||
8a57c78f5f | |||
ad01fb958a | |||
d336f2d487 | |||
c82c753e9f | |||
f666898e5d | |||
b1a31281e4 | |||
764450d04a | |||
755cae3f19 | |||
9b13a18aac | |||
ef64930fef | |||
c8fb154151 | |||
7edec9193c | |||
3cf6ea745d | |||
9a77527f46 | |||
5b1ba8e23d | |||
9902965ff4 | |||
33ad753302 | |||
15102e6dfd | |||
17ec84151b | |||
6c45a17af6 | |||
d19ab43500 | |||
5bcf81d1cc | |||
c2effcc61c | |||
2285cfc36f | |||
e74d05c03f | |||
48862e0e63 | |||
89892d9754 | |||
7ac0852364 | |||
d28a5d2d7a | |||
87ac3c5279 | |||
d5ee6d3039 | |||
433aab12bb | |||
81f4cdbb91 | |||
3cd7d23f10 | |||
012af5cc38 | |||
eb5336d5ff | |||
bc8d79ddf9 | |||
29708b78d7 | |||
b9284604d9 | |||
65a4320c75 | |||
9a260c2e64 | |||
6f798b9c4b | |||
e515dd4582 | |||
8b6ec57c61 | |||
ed13387c0e | |||
5afea62ec0 | |||
c0a2f20eee | |||
2d064d0bd8 | |||
ef38420623 | |||
f7caef355b | |||
fbdfd503e4 | |||
67798bb50e | |||
5b653aa65f | |||
e314f328c4 | |||
6c96cc2af6 | |||
74db735265 | |||
3304afa9d1 | |||
b42bcdc6fa | |||
b0c5def2d9 | |||
90f3669399 | |||
07ce40e119 | |||
41038b2ec0 | |||
d83879d4b8 | |||
f6582081a4 | |||
00b1cecfb7 | |||
63466d71b2 | |||
d53732f663 | |||
3012286452 | |||
714ff784fa | |||
d2a59b2de8 | |||
acd6eb1815 | |||
42bf03e5cc | |||
5992ee901a | |||
dfb00083d0 | |||
1134760271 | |||
02bb7159a5 | |||
94302235d0 | |||
cc5360a578 | |||
4190fba86d | |||
936ebbb8e5 | |||
c065d55ca3 | |||
fe9f664b57 | |||
87f4b934d1 | |||
8093e145b3 | |||
3da168f8cf | |||
4572fa4874 | |||
1efa64ee72 | |||
be744ae3e6 | |||
1b520f7973 | |||
899cd55c27 | |||
0c49bca19c | |||
5fbb2657ca | |||
a5de74a249 | |||
fc032838c0 | |||
2f710d8f94 | |||
4dc9a1b300 | |||
963faa615a | |||
9a87acb87a | |||
9206ce5cd2 | |||
6c46044c9c | |||
01e3944b31 | |||
434048e8d9 | |||
f83f7feb8c | |||
62028cd7ee | |||
f45e75e3eb | |||
57c31e9802 | |||
9c5ddc4dfe | |||
54eb005822 | |||
a13219808a | |||
7f8a1dcf8e | |||
d0ed29b3c7 | |||
5f22ba6f38 | |||
a5e1aa22c9 | |||
772b471aab | |||
4fbfffd44c | |||
29e4cf7ba1 | |||
a2ab6d4942 | |||
bdd57c8b6b | |||
d1d6e3471c | |||
401c398704 | |||
004ff9e9bf | |||
63a567a1de | |||
580cd55180 | |||
e319bf403e | |||
aedb55f913 | |||
b69e07da7a | |||
2bd560e528 | |||
95597d3437 | |||
fd18aa363b | |||
76268e3ea2 | |||
8434f3dbfc | |||
34e6a309c6 | |||
bdf386366c | |||
839dead226 | |||
3bb65ba820 | |||
d4493a6d08 | |||
0b87be804a | |||
f71418b73c | |||
c34b8acedd | |||
c290d079fd | |||
53a90634fc | |||
5a53f9c4fd | |||
1361db91ee | |||
945b7c740b | |||
61d5e140e0 | |||
3441fff05d | |||
ac1eee091d | |||
a603d14d08 | |||
d4be2f20d4 | |||
e5c8f7ff9f | |||
1e7f9909da | |||
b807d8c400 | |||
d4bec24c9f | |||
ea48e928c8 | |||
96308a26c6 | |||
74a6a1da7f | |||
2be1aa781d | |||
89d0435b1d | |||
54fe8383a4 | |||
944160427b | |||
bb44867491 | |||
546d09660f | |||
e3764c51df | |||
b33559754d | |||
f345fe9a58 | |||
3b236160a6 | |||
25d2ae8aaf | |||
e39378b1c3 | |||
8a6e3025a0 | |||
2dd3a6f7a8 | |||
b142b6f48e | |||
5f6c7cbdb1 | |||
66e17f4b8e | |||
99be4c83a7 | |||
ec8da40567 | |||
dea6f031f9 | |||
5fac4058e8 | |||
2220f6a809 | |||
a812932984 | |||
92fe5d90f5 | |||
4668efc0bf | |||
654d970fad | |||
d3b209c8e1 | |||
edb1747af7 | |||
a61201a987 | |||
6b6eabe41c | |||
d508da8397 | |||
007827255e | |||
f652518c24 | |||
273980cfb9 | |||
108e4e07be | |||
b3deb893ba | |||
7768a482b5 | |||
371d97f61a | |||
e655336390 | |||
ed396448ac | |||
9cbd32bce8 | |||
1ae86f35a8 | |||
b9043433a0 | |||
a4fb7f085b | |||
a685fcdc96 | |||
2b3fc50681 | |||
98fe24cdb7 | |||
882c068410 | |||
6c2146bbc1 | |||
03976c6ed5 | |||
7e97df4878 | |||
01b6f1733c | |||
7abbdca064 | |||
6488ddee88 | |||
d6b42972a8 | |||
5e9a97fd3e | |||
fa7f9fbce2 | |||
806ea37101 | |||
80099d9a2f | |||
a059a7dcf0 | |||
bd24beecf8 | |||
dfe825b81b | |||
76f67ea34e | |||
7d0d781db1 | |||
0f08a2efba | |||
7bf20c9f1f | |||
2542d4f5df | |||
15dae8685e | |||
7bca428db0 | |||
a345c972bf | |||
8c1082b31a | |||
cfda9003a7 | |||
6ff0b0996b | |||
8319b59238 | |||
b7acb34fa4 | |||
41104f2383 | |||
eeab417dcf | |||
5ed317e24c | |||
e890f1b4b1 | |||
a4a1c3f18b | |||
ec1509de4e | |||
5da41f1fe5 | |||
d055168e2a | |||
80ce7c3a00 | |||
93d63e1632 | |||
68029d756e | |||
fa82854af4 | |||
c985b1198f | |||
08b1f18bca | |||
36efccd862 | |||
5c01bd5be8 | |||
8e51d7849a | |||
10602b55b1 | |||
eeca796d2e | |||
327d364f34 | |||
dc3dcabadc | |||
8021bacc43 | |||
ef4cea6d19 | |||
a55600893e | |||
c49982d22a | |||
7e04083c27 | |||
a12c39667d | |||
85a77b7c21 | |||
8377372a40 | |||
dd459d399f | |||
7fd7961dfa | |||
8398a8b4b3 | |||
9c2c76ca32 | |||
1032075a21 | |||
94f07b4778 | |||
7ddba70030 | |||
658e3cb92f | |||
9ee1bd3669 | |||
ca4d6df1cc | |||
18182e578e | |||
3119f2fbc3 | |||
e377a92d97 | |||
eadcea8df0 | |||
5e5ee545b6 | |||
27caa8a72f | |||
e83d39e33f | |||
fc383ea6ae | |||
00a88b9936 | |||
3940bc17c1 | |||
e5767c9002 | |||
286df198c9 | |||
3a48b282b6 | |||
21431f22c0 | |||
d5dc14c639 | |||
39866a957c | |||
d02a7c2d38 | |||
08953a2f94 | |||
0308835d48 | |||
36956db123 | |||
3bf6e6dde6 | |||
b027a7f91e | |||
6ace2f597e | |||
784e8ef857 | |||
ca974b8b4c | |||
84ecd61dfd | |||
6ef38c07bd | |||
d90aab5454 | |||
0c2b6f3dac | |||
47bcd346d3 | |||
8eb591d668 | |||
cfd5e3d403 | |||
62cbb72a5e | |||
78b1d9b18d | |||
40c9ddb6ba | |||
3a797e4682 | |||
2bac82cd6f | |||
bbe95dac8b | |||
80d7459560 | |||
4bd4667791 | |||
f3a861806e | |||
a378ff9cf6 | |||
91bed3b0ba | |||
74842e7f43 | |||
259007540f | |||
56eeb630b6 | |||
36eab4059c | |||
72ab373b71 | |||
7a8ac4907a | |||
10497e9136 | |||
dc2867682f | |||
dc6778f385 | |||
7085723c6b | |||
4c7ff159ec | |||
81070ada01 | |||
452faa3c89 | |||
87a4a6e8d0 | |||
4f7d76c9ef | |||
4951babd5f | |||
a0e5fc733e | |||
df894fbac7 | |||
81ea91de52 | |||
11a38a0a84 | |||
0b87388c18 | |||
621dbf58ab | |||
a1f7615b7e | |||
46732b61d7 | |||
a83eeddb1d | |||
9ac74efc41 | |||
ec76689ab7 | |||
68eb68f59a | |||
11e880de7f | |||
40b68bcb6c | |||
fd28461def | |||
ecd1ed7a5e | |||
ac1f014747 | |||
7b8937ec35 | |||
75eedf71f3 | |||
5b100699d7 | |||
76cf7a051b | |||
b9d6c9d10c | |||
3fc8e0e08c | |||
dbd3b238f7 | |||
96fe271bab | |||
a0e49fa5a5 | |||
42ecc2f2b9 | |||
68ac490729 | |||
6a39c3d15e | |||
9d73f9c2c6 | |||
41e670c9ba | |||
d4f425f86a | |||
b9fcaad21f | |||
069c1559cc | |||
6cf512e574 | |||
e7d479f4c2 | |||
a0c588263b | |||
239323eeef | |||
04a3f891fd | |||
4edff5eea6 | |||
a90310335d | |||
a849236b68 | |||
67b3002743 | |||
3f1961157e | |||
2e074d3846 | |||
8fcd0f8f8d | |||
806236da78 | |||
6f2187a420 | |||
cc2449beaf | |||
643480d6fa | |||
f2d2908745 | |||
5aacb8fc86 | |||
f8e33f8e3a | |||
f0edebea18 | |||
0b367007fc | |||
92e19feb57 | |||
c1af13b47e | |||
9f80d6d9a2 | |||
6130650bb6 | |||
88b8ddd902 | |||
a82c8cc5b8 | |||
d355274cd0 | |||
3555c73225 | |||
e43e7bec3a | |||
50923ed81c | |||
4a34d0d40e | |||
cc3f762cf2 | |||
3627b44e92 | |||
482c5129ac | |||
43625e7536 | |||
2b02f52cd9 | |||
542d3adcb2 | |||
51ade979e8 | |||
2697d4d1fe | |||
40b04c00ef | |||
89a80e9a0f | |||
8fd678e269 | |||
436c9f5558 | |||
44f2e8f27f | |||
0e42126ddc | |||
1cd8562db8 | |||
4ab6c404f7 | |||
cbe9757490 | |||
d45d086acd | |||
af57d5a6a1 | |||
2f04ce2f79 | |||
b078fe5ba1 | |||
f3e09cb09b | |||
5c582e96fd | |||
b3eaa8a9bc | |||
0924b62a95 | |||
300654b045 | |||
b4cfc80579 | |||
d0f64c23a5 | |||
6e71ae3bda | |||
bf9bdde8ea | |||
952d13cd2b | |||
20baf6e112 | |||
74135776c7 | |||
0144117cc9 | |||
368218f0cc | |||
a45b548a6f | |||
654384990c | |||
ada1b9f737 | |||
5c730de96e | |||
854200a874 | |||
fe2c1c926f | |||
3e782527b8 | |||
21a490da8f | |||
93c0ccad4f | |||
00b2b77b26 | |||
a23d53b2d4 | |||
fc7b07f314 | |||
9cc51f86b7 | |||
b60a51b862 | |||
6c76c9b457 | |||
45f4e6939d | |||
e07869a8cf | |||
ec2873caa7 | |||
71789676d5 | |||
c9efaa5819 | |||
4730ecfdb8 | |||
52bebe9452 | |||
4b514f5ba0 | |||
c8e2ca2ab4 | |||
3b9d12b11c | |||
411a8d0245 | |||
112a7c690f | |||
167c52a1a9 | |||
700e891b85 | |||
1f02ac2566 | |||
7bc3003803 | |||
6d4583f5de | |||
10ee865e98 | |||
c21d72ac23 | |||
6772976657 | |||
97e54066d0 | |||
46bc6a7930 | |||
3ea1d7b729 | |||
0094186299 | |||
91e79c98ba | |||
e5e0542482 | |||
5be36924e3 | |||
6a46c6d229 | |||
f4dcb418f2 | |||
661faa639e | |||
cdae227f82 | |||
40781b3a20 | |||
5ef5734c4e | |||
669103a33e | |||
2c4b50a71e | |||
2ca5dfc2f6 | |||
3dc81cb4fc | |||
d864945961 | |||
43fdb1da45 | |||
1b17258c04 | |||
e3d9dd6ee8 | |||
57466594fb | |||
1005bf4f56 | |||
76398c06b0 | |||
7b1adfed3e | |||
e74bdaa5d5 | |||
338d8cbebd | |||
5b8200de88 | |||
2b88361849 | |||
f5b67c6735 | |||
bdf4990904 | |||
8668cbf147 | |||
1c5e0f90aa | |||
39da643354 | |||
92569b9bbf | |||
17f7adb640 | |||
0290f86579 | |||
ffb1a6f81a | |||
9aa533e59a | |||
d614f04a0a | |||
531542ce60 | |||
4738508ce2 |
1013 changed files with 33519 additions and 18828 deletions
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.21 as builder
|
FROM golang:1.23 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
|
FROM golang:1.23
|
||||||
|
|
||||||
WORKDIR /tmp
|
WORKDIR /tmp
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.21 as builder
|
FROM golang:1.23 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.23 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.23 AS builder
|
||||||
ARG BUILD=now
|
ARG BUILD=now
|
||||||
ARG VERSION=dev
|
ARG VERSION=dev
|
||||||
ARG REPO=repository
|
ARG REPO=repository
|
||||||
|
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
@ -1,6 +1,10 @@
|
||||||
name: Build
|
name: Build
|
||||||
|
|
||||||
on: [pull_request]
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -8,7 +12,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go_versions: [ '1.20', '1.21' ]
|
go_versions: [ '1.22', '1.23' ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
|
@ -13,7 +13,7 @@ jobs:
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
|
|
||||||
- name: Run commit format checker
|
- name: Run commit format checker
|
||||||
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3
|
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3
|
||||||
|
|
30
.forgejo/workflows/pre-commit.yml
Normal file
30
.forgejo/workflows/pre-commit.yml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
name: Pre-commit hooks
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
precommit:
|
||||||
|
name: Pre-commit
|
||||||
|
env:
|
||||||
|
# Skip pre-commit hooks which are executed by other actions.
|
||||||
|
SKIP: make-lint,go-staticcheck-repo-mod,go-unit-tests,gofumpt
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
# If we use actions/setup-python from either Github or Gitea,
|
||||||
|
# the line above fails with a cryptic error about not being able to find python.
|
||||||
|
# So install everything manually.
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.23
|
||||||
|
- name: Set up Python
|
||||||
|
run: |
|
||||||
|
apt update
|
||||||
|
apt install -y pre-commit
|
||||||
|
- name: Run pre-commit
|
||||||
|
run: pre-commit run --color=always --hook-stage manual --all-files
|
|
@ -1,5 +1,10 @@
|
||||||
name: Tests and linters
|
name: Tests and linters
|
||||||
on: [pull_request]
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
|
@ -11,7 +16,7 @@ jobs:
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.23'
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: Install linters
|
- name: Install linters
|
||||||
|
@ -25,7 +30,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go_versions: [ '1.20', '1.21' ]
|
go_versions: [ '1.22', '1.23' ]
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
@ -48,7 +53,7 @@ jobs:
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
@ -63,7 +68,7 @@ jobs:
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.23'
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: Install staticcheck
|
- name: Install staticcheck
|
||||||
|
@ -81,7 +86,7 @@ jobs:
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: Install gopls
|
- name: Install gopls
|
||||||
|
@ -89,3 +94,23 @@ jobs:
|
||||||
|
|
||||||
- name: Run gopls
|
- name: Run gopls
|
||||||
run: make gopls-run
|
run: make gopls-run
|
||||||
|
|
||||||
|
fumpt:
|
||||||
|
name: Run gofumpt
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: '1.23'
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
- name: Install gofumpt
|
||||||
|
run: make fumpt-install
|
||||||
|
|
||||||
|
- name: Run gofumpt
|
||||||
|
run: |
|
||||||
|
make fumpt
|
||||||
|
git diff --exit-code --quiet
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
name: Vulncheck
|
name: Vulncheck
|
||||||
on: [pull_request]
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
vulncheck:
|
vulncheck:
|
||||||
|
@ -13,7 +18,7 @@ jobs:
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.23'
|
||||||
|
|
||||||
- name: Install govulncheck
|
- name: Install govulncheck
|
||||||
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||||
|
|
|
@ -12,7 +12,8 @@ run:
|
||||||
# output configuration options
|
# output configuration options
|
||||||
output:
|
output:
|
||||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
||||||
format: tab
|
formats:
|
||||||
|
- format: tab
|
||||||
|
|
||||||
# all available settings of specific linters
|
# all available settings of specific linters
|
||||||
linters-settings:
|
linters-settings:
|
||||||
|
@ -37,6 +38,10 @@ linters-settings:
|
||||||
alias:
|
alias:
|
||||||
pkg: git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object
|
pkg: git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object
|
||||||
alias: objectSDK
|
alias: objectSDK
|
||||||
|
unused:
|
||||||
|
field-writes-are-uses: false
|
||||||
|
exported-fields-are-used: false
|
||||||
|
local-variables-are-used: false
|
||||||
custom:
|
custom:
|
||||||
truecloudlab-linters:
|
truecloudlab-linters:
|
||||||
path: bin/linters/external_linters.so
|
path: bin/linters/external_linters.so
|
||||||
|
@ -66,7 +71,7 @@ linters:
|
||||||
- bidichk
|
- bidichk
|
||||||
- durationcheck
|
- durationcheck
|
||||||
- exhaustive
|
- exhaustive
|
||||||
- exportloopref
|
- copyloopvar
|
||||||
- gofmt
|
- gofmt
|
||||||
- goimports
|
- goimports
|
||||||
- misspell
|
- misspell
|
||||||
|
@ -82,5 +87,7 @@ linters:
|
||||||
- perfsprint
|
- perfsprint
|
||||||
- testifylint
|
- testifylint
|
||||||
- protogetter
|
- protogetter
|
||||||
|
- intrange
|
||||||
|
- tenv
|
||||||
disable-all: true
|
disable-all: true
|
||||||
fast: false
|
fast: false
|
||||||
|
|
|
@ -16,7 +16,7 @@ repos:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
args: [--markdown-linebreak-ext=md]
|
args: [--markdown-linebreak-ext=md]
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
exclude: ".key$"
|
exclude: "(.key|.svg)$"
|
||||||
|
|
||||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||||
rev: v0.9.0.6
|
rev: v0.9.0.6
|
||||||
|
|
|
@ -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
|
|
92
CHANGELOG.md
92
CHANGELOG.md
|
@ -9,6 +9,98 @@ Changelog for FrostFS Node
|
||||||
### Removed
|
### Removed
|
||||||
### Updated
|
### Updated
|
||||||
|
|
||||||
|
## [v0.44.0] - 2024-25-11 - Rongbuk
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Allow to prioritize nodes during GET traversal via attributes (#1439)
|
||||||
|
- Add metrics for the frostfsid cache (#1464)
|
||||||
|
- Customize constant attributes attached to every tracing span (#1488)
|
||||||
|
- Manage additional keys in the `frostfsid` contract (#1505)
|
||||||
|
- Describe `--rule` flag in detail for `frostfs-cli ape-manager` subcommands (#1519)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Support richer interaction with the console in `frostfs-cli container policy-playground` (#1396)
|
||||||
|
- Print address in base58 format in `frostfs-adm morph policy set-admin` (#1515)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix EC object search (#1408)
|
||||||
|
- Fix EC object put when one of the nodes is unavailable (#1427)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Drop most of the eACL-related code (#1425)
|
||||||
|
- Remove `--basic-acl` flag from `frostfs-cli container create` (#1483)
|
||||||
|
|
||||||
|
### Upgrading from v0.43.0
|
||||||
|
The metabase schema has changed completely, resync is required.
|
||||||
|
|
||||||
|
## [v0.42.0]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Add audit logs for gRPC requests (#1184)
|
||||||
|
- Add CLI command to convert eACL to APE (#1189)
|
||||||
|
- Add `--await` flag to `control set-status` (#60)
|
||||||
|
- `app_info` metric for binary version (#1154)
|
||||||
|
- `--quiet` flag for healthcheck command (#1209)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Deprecate Container.SetEACL RPC (#1219)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Take groups into account during APE processing (#1190)
|
||||||
|
- Handle double SIGHUP correctly (#1145)
|
||||||
|
- Handle empty filenames in tree listing (#1074)
|
||||||
|
- Handle duplicate tree nodes in the split-brain scenario (#1234, #1251)
|
||||||
|
- Remove APE pre-check in Object.GET/HEAD/RANGE RPC (#1249)
|
||||||
|
- Delete EC gc marks and split info (#1257)
|
||||||
|
- Do not search for non-existent objects on deletion (#1261)
|
||||||
|
|
||||||
|
### Updated
|
||||||
|
- Make putting EC chunks more robust (#1233)
|
||||||
|
|
||||||
|
## [v0.41.0]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Support mTLS for morph client (#1170)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Update shard state metric during shard init (#1174)
|
||||||
|
- Handle ENOSPC in blobovnicza (#1166)
|
||||||
|
- Handle multiple split-infos for EC objects (#1163)
|
||||||
|
- Set `Disabled` mode as the default for components (#1168)
|
||||||
|
|
||||||
|
## [v0.40.0]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Support EC chunk reconstruction in policer (#1129)
|
||||||
|
- Support LOCK, DELETE and SEARCH methods on EC objects (#1147, 1144)
|
||||||
|
- apemanager service to manage APE chains (#1105)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Properly verify GetRangeHash response (#1083)
|
||||||
|
- Send `MONOTONIC_USEC` in sdnotify on reload (#1135)
|
||||||
|
|
||||||
|
### Updated
|
||||||
|
- neo-go to `v0.106.0`
|
||||||
|
|
||||||
|
## [v0.39.0]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Preliminary erasure coding support (#1065, #1112, #1103, #1120)
|
||||||
|
- TTL cache for blobovnicza tree (#1004)
|
||||||
|
- Cache for frostfsid and policy contracts (#1117)
|
||||||
|
- Writecache path to metric labels (#966)
|
||||||
|
- Documentation for authentication mechanisms (#1097, #1104)
|
||||||
|
- Metrics for metabase resync status (#1029)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Speed up metabase resync (#1024)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Possible panic in GET_RANGE (#1077)
|
||||||
|
|
||||||
|
### Updated
|
||||||
|
- Minimum required Go version to 1.21
|
||||||
|
|
||||||
## [v0.38.0]
|
## [v0.38.0]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
3
CODEOWNERS
Normal file
3
CODEOWNERS
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.* @TrueCloudLab/storage-core-committers @TrueCloudLab/storage-core-developers
|
||||||
|
.forgejo/.* @potyarkin
|
||||||
|
Makefile @potyarkin
|
85
Makefile
85
Makefile
|
@ -4,20 +4,19 @@ SHELL = bash
|
||||||
REPO ?= $(shell go list -m)
|
REPO ?= $(shell go list -m)
|
||||||
VERSION ?= $(shell git describe --tags --dirty --match "v*" --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
|
VERSION ?= $(shell git describe --tags --dirty --match "v*" --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
|
||||||
|
|
||||||
HUB_IMAGE ?= truecloudlab/frostfs
|
HUB_IMAGE ?= git.frostfs.info/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.22
|
||||||
LINT_VERSION ?= 1.56.1
|
LINT_VERSION ?= 1.62.0
|
||||||
TRUECLOUDLAB_LINT_VERSION ?= 0.0.5
|
TRUECLOUDLAB_LINT_VERSION ?= 0.0.8
|
||||||
PROTOC_VERSION ?= 25.0
|
PROTOC_VERSION ?= 25.0
|
||||||
PROTOC_GEN_GO_VERSION ?= $(shell go list -f '{{.Version}}' -m google.golang.org/protobuf)
|
PROTOGEN_FROSTFS_VERSION ?= $(shell go list -f '{{.Version}}' -m git.frostfs.info/TrueCloudLab/frostfs-sdk-go)
|
||||||
PROTOGEN_FROSTFS_VERSION ?= $(shell go list -f '{{.Version}}' -m git.frostfs.info/TrueCloudLab/frostfs-api-go/v2)
|
|
||||||
PROTOC_OS_VERSION=osx-x86_64
|
PROTOC_OS_VERSION=osx-x86_64
|
||||||
ifeq ($(shell uname), Linux)
|
ifeq ($(shell uname), Linux)
|
||||||
PROTOC_OS_VERSION=linux-x86_64
|
PROTOC_OS_VERSION=linux-x86_64
|
||||||
endif
|
endif
|
||||||
STATICCHECK_VERSION ?= 2023.1.6
|
STATICCHECK_VERSION ?= 2024.1.1
|
||||||
ARCH = amd64
|
ARCH = amd64
|
||||||
|
|
||||||
BIN = bin
|
BIN = bin
|
||||||
|
@ -28,32 +27,32 @@ DIRS = $(BIN) $(RELEASE)
|
||||||
CMDS = $(notdir $(basename $(wildcard cmd/frostfs-*)))
|
CMDS = $(notdir $(basename $(wildcard cmd/frostfs-*)))
|
||||||
BINS = $(addprefix $(BIN)/, $(CMDS))
|
BINS = $(addprefix $(BIN)/, $(CMDS))
|
||||||
|
|
||||||
# .deb package versioning
|
|
||||||
OS_RELEASE = $(shell lsb_release -cs)
|
|
||||||
PKG_VERSION ?= $(shell echo $(VERSION) | sed "s/^v//" | \
|
|
||||||
sed -E "s/(.*)-(g[a-fA-F0-9]{6,8})(.*)/\1\3~\2/" | \
|
|
||||||
sed "s/-/~/")-${OS_RELEASE}
|
|
||||||
|
|
||||||
OUTPUT_LINT_DIR ?= $(abspath $(BIN))/linters
|
OUTPUT_LINT_DIR ?= $(abspath $(BIN))/linters
|
||||||
LINT_DIR = $(OUTPUT_LINT_DIR)/golangci-lint-$(LINT_VERSION)-v$(TRUECLOUDLAB_LINT_VERSION)
|
LINT_DIR = $(OUTPUT_LINT_DIR)/golangci-lint-$(LINT_VERSION)-v$(TRUECLOUDLAB_LINT_VERSION)
|
||||||
TMP_DIR := .cache
|
TMP_DIR := .cache
|
||||||
PROTOBUF_DIR ?= $(abspath $(BIN))/protobuf
|
PROTOBUF_DIR ?= $(abspath $(BIN))/protobuf
|
||||||
PROTOC_DIR ?= $(PROTOBUF_DIR)/protoc-v$(PROTOC_VERSION)
|
PROTOC_DIR ?= $(PROTOBUF_DIR)/protoc-v$(PROTOC_VERSION)
|
||||||
PROTOC_GEN_GO_DIR ?= $(PROTOBUF_DIR)/protoc-gen-go-$(PROTOC_GEN_GO_VERSION)
|
|
||||||
PROTOGEN_FROSTFS_DIR ?= $(PROTOBUF_DIR)/protogen-$(PROTOGEN_FROSTFS_VERSION)
|
PROTOGEN_FROSTFS_DIR ?= $(PROTOBUF_DIR)/protogen-$(PROTOGEN_FROSTFS_VERSION)
|
||||||
STATICCHECK_DIR ?= $(abspath $(BIN))/staticcheck
|
STATICCHECK_DIR ?= $(abspath $(BIN))/staticcheck
|
||||||
STATICCHECK_VERSION_DIR ?= $(STATICCHECK_DIR)/$(STATICCHECK_VERSION)
|
STATICCHECK_VERSION_DIR ?= $(STATICCHECK_DIR)/$(STATICCHECK_VERSION)
|
||||||
|
|
||||||
|
SOURCES = $(shell find . -type f -name "*.go" -print)
|
||||||
|
|
||||||
|
GOFUMPT_VERSION ?= v0.7.0
|
||||||
|
GOFUMPT_DIR ?= $(abspath $(BIN))/gofumpt
|
||||||
|
GOFUMPT_VERSION_DIR ?= $(GOFUMPT_DIR)/$(GOFUMPT_VERSION)
|
||||||
|
|
||||||
GOPLS_VERSION ?= v0.15.1
|
GOPLS_VERSION ?= v0.15.1
|
||||||
GOPLS_DIR ?= $(abspath $(BIN))/gopls
|
GOPLS_DIR ?= $(abspath $(BIN))/gopls
|
||||||
GOPLS_VERSION_DIR ?= $(GOPLS_DIR)/$(GOPLS_VERSION)
|
GOPLS_VERSION_DIR ?= $(GOPLS_DIR)/$(GOPLS_VERSION)
|
||||||
|
GOPLS_TEMP_FILE := $(shell mktemp)
|
||||||
|
|
||||||
FROSTFS_CONTRACTS_PATH=$(abspath ./../frostfs-contract)
|
FROSTFS_CONTRACTS_PATH=$(abspath ./../frostfs-contract)
|
||||||
LOCODE_DB_PATH=$(abspath ./.cache/locode_db)
|
LOCODE_DB_PATH=$(abspath ./.cache/locode_db)
|
||||||
LOCODE_DB_VERSION=v0.4.0
|
LOCODE_DB_VERSION=v0.4.0
|
||||||
|
|
||||||
.PHONY: help all images dep clean fmts fumpt imports test lint docker/lint
|
.PHONY: help all images dep clean fmts fumpt imports test lint docker/lint
|
||||||
prepare-release debpackage pre-commit unpre-commit
|
prepare-release pre-commit unpre-commit
|
||||||
|
|
||||||
# To build a specific binary, use it's name prefix with bin/ as a target
|
# 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
|
||||||
|
@ -100,17 +99,15 @@ export-metrics: dep
|
||||||
|
|
||||||
# Regenerate proto files:
|
# Regenerate proto files:
|
||||||
protoc:
|
protoc:
|
||||||
@if [ ! -d "$(PROTOC_DIR)" ] || [ ! -d "$(PROTOC_GEN_GO_DIR)" ] || [ ! -d "$(PROTOGEN_FROSTFS_DIR)" ]; then \
|
@if [ ! -d "$(PROTOC_DIR)" ] || [ ! -d "$(PROTOGEN_FROSTFS_DIR)" ]; then \
|
||||||
make protoc-install; \
|
make protoc-install; \
|
||||||
fi
|
fi
|
||||||
@for f in `find . -type f -name '*.proto' -not -path './bin/*'`; do \
|
@for f in `find . -type f -name '*.proto' -not -path './bin/*'`; do \
|
||||||
echo "⇒ Processing $$f "; \
|
echo "⇒ Processing $$f "; \
|
||||||
$(PROTOC_DIR)/bin/protoc \
|
$(PROTOC_DIR)/bin/protoc \
|
||||||
--proto_path=.:$(PROTOC_DIR)/include:/usr/local/include \
|
--proto_path=.:$(PROTOC_DIR)/include:/usr/local/include \
|
||||||
--plugin=protoc-gen-go=$(PROTOC_GEN_GO_DIR)/protoc-gen-go \
|
|
||||||
--plugin=protoc-gen-go-frostfs=$(PROTOGEN_FROSTFS_DIR)/protogen \
|
--plugin=protoc-gen-go-frostfs=$(PROTOGEN_FROSTFS_DIR)/protogen \
|
||||||
--go-frostfs_out=. --go-frostfs_opt=paths=source_relative \
|
--go-frostfs_out=. --go-frostfs_opt=paths=source_relative \
|
||||||
--go_out=. --go_opt=paths=source_relative \
|
|
||||||
--go-grpc_opt=require_unimplemented_servers=false \
|
--go-grpc_opt=require_unimplemented_servers=false \
|
||||||
--go-grpc_out=. --go-grpc_opt=paths=source_relative $$f; \
|
--go-grpc_out=. --go-grpc_opt=paths=source_relative $$f; \
|
||||||
done
|
done
|
||||||
|
@ -123,10 +120,8 @@ protoc-install:
|
||||||
@wget -q -O $(PROTOBUF_DIR)/protoc-$(PROTOC_VERSION).zip 'https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/protoc-$(PROTOC_VERSION)-$(PROTOC_OS_VERSION).zip'
|
@wget -q -O $(PROTOBUF_DIR)/protoc-$(PROTOC_VERSION).zip 'https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/protoc-$(PROTOC_VERSION)-$(PROTOC_OS_VERSION).zip'
|
||||||
@unzip -q -o $(PROTOBUF_DIR)/protoc-$(PROTOC_VERSION).zip -d $(PROTOC_DIR)
|
@unzip -q -o $(PROTOBUF_DIR)/protoc-$(PROTOC_VERSION).zip -d $(PROTOC_DIR)
|
||||||
@rm $(PROTOBUF_DIR)/protoc-$(PROTOC_VERSION).zip
|
@rm $(PROTOBUF_DIR)/protoc-$(PROTOC_VERSION).zip
|
||||||
@echo "⇒ Installing protoc-gen-go..."
|
|
||||||
@GOBIN=$(PROTOC_GEN_GO_DIR) go install -v google.golang.org/protobuf/...@$(PROTOC_GEN_GO_VERSION)
|
|
||||||
@echo "⇒ Instaling protogen FrostFS plugin..."
|
@echo "⇒ Instaling protogen FrostFS plugin..."
|
||||||
@GOBIN=$(PROTOGEN_FROSTFS_DIR) go install -mod=mod -v git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/protogen@$(PROTOGEN_FROSTFS_VERSION)
|
@GOBIN=$(PROTOGEN_FROSTFS_DIR) go install -mod=mod -v git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/util/protogen@$(PROTOGEN_FROSTFS_VERSION)
|
||||||
|
|
||||||
# Build FrostFS component's docker image
|
# Build FrostFS component's docker image
|
||||||
image-%:
|
image-%:
|
||||||
|
@ -162,10 +157,19 @@ imports:
|
||||||
@echo "⇒ Processing goimports check"
|
@echo "⇒ Processing goimports check"
|
||||||
@goimports -w cmd/ pkg/ misc/
|
@goimports -w cmd/ pkg/ misc/
|
||||||
|
|
||||||
|
# Install gofumpt
|
||||||
|
fumpt-install:
|
||||||
|
@rm -rf $(GOFUMPT_DIR)
|
||||||
|
@mkdir $(GOFUMPT_DIR)
|
||||||
|
@GOBIN=$(GOFUMPT_VERSION_DIR) go install mvdan.cc/gofumpt@$(GOFUMPT_VERSION)
|
||||||
|
|
||||||
# Run gofumpt
|
# Run gofumpt
|
||||||
fumpt:
|
fumpt:
|
||||||
|
@if [ ! -d "$(GOFUMPT_VERSION_DIR)" ]; then \
|
||||||
|
make fumpt-install; \
|
||||||
|
fi
|
||||||
@echo "⇒ Processing gofumpt check"
|
@echo "⇒ Processing gofumpt check"
|
||||||
@gofumpt -l -w cmd/ pkg/ misc/
|
$(GOFUMPT_VERSION_DIR)/gofumpt -l -w cmd/ pkg/ misc/
|
||||||
|
|
||||||
# Run Unit Test with go test
|
# Run Unit Test with go test
|
||||||
test: GOFLAGS ?= "-count=1"
|
test: GOFLAGS ?= "-count=1"
|
||||||
|
@ -187,7 +191,7 @@ lint-install:
|
||||||
@@make -C $(TMP_DIR)/linters lib CGO_ENABLED=1 OUT_DIR=$(OUTPUT_LINT_DIR)
|
@@make -C $(TMP_DIR)/linters lib CGO_ENABLED=1 OUT_DIR=$(OUTPUT_LINT_DIR)
|
||||||
@rm -rf $(TMP_DIR)/linters
|
@rm -rf $(TMP_DIR)/linters
|
||||||
@rmdir $(TMP_DIR) 2>/dev/null || true
|
@rmdir $(TMP_DIR) 2>/dev/null || true
|
||||||
@CGO_ENABLED=1 GOBIN=$(LINT_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v$(LINT_VERSION)
|
@CGO_ENABLED=1 GOBIN=$(LINT_DIR) go install -trimpath github.com/golangci/golangci-lint/cmd/golangci-lint@v$(LINT_VERSION)
|
||||||
|
|
||||||
# Run linters
|
# Run linters
|
||||||
lint:
|
lint:
|
||||||
|
@ -220,9 +224,12 @@ gopls-run:
|
||||||
@if [ ! -d "$(GOPLS_VERSION_DIR)" ]; then \
|
@if [ ! -d "$(GOPLS_VERSION_DIR)" ]; then \
|
||||||
make gopls-install; \
|
make gopls-install; \
|
||||||
fi
|
fi
|
||||||
@if [[ $$(find . -type f -name "*.go" -print | xargs $(GOPLS_VERSION_DIR)/gopls check | tee /dev/tty | wc -l) -ne 0 ]]; then \
|
$(GOPLS_VERSION_DIR)/gopls check $(SOURCES) 2>&1 >$(GOPLS_TEMP_FILE)
|
||||||
|
@if [[ $$(wc -l < $(GOPLS_TEMP_FILE)) -ne 0 ]]; then \
|
||||||
|
cat $(GOPLS_TEMP_FILE); \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
rm $(GOPLS_TEMP_FILE)
|
||||||
|
|
||||||
# Run linters in Docker
|
# Run linters in Docker
|
||||||
docker/lint:
|
docker/lint:
|
||||||
|
@ -250,19 +257,6 @@ clean:
|
||||||
rm -rf $(BIN)
|
rm -rf $(BIN)
|
||||||
rm -rf $(RELEASE)
|
rm -rf $(RELEASE)
|
||||||
|
|
||||||
# Package for Debian
|
|
||||||
debpackage:
|
|
||||||
dch -b --package frostfs-node \
|
|
||||||
--controlmaint \
|
|
||||||
--newversion $(PKG_VERSION) \
|
|
||||||
--distribution $(OS_RELEASE) \
|
|
||||||
"Please see CHANGELOG.md for code changes for $(VERSION)"
|
|
||||||
dpkg-buildpackage --no-sign -b
|
|
||||||
|
|
||||||
# Cleanup deb package build directories
|
|
||||||
debclean:
|
|
||||||
dh clean
|
|
||||||
|
|
||||||
# Download locode database
|
# Download locode database
|
||||||
locode-download:
|
locode-download:
|
||||||
mkdir -p $(TMP_DIR)
|
mkdir -p $(TMP_DIR)
|
||||||
|
@ -276,15 +270,20 @@ env-up: all
|
||||||
echo "Frostfs contracts not found"; exit 1; \
|
echo "Frostfs contracts not found"; exit 1; \
|
||||||
fi
|
fi
|
||||||
${BIN}/frostfs-adm --config ./dev/adm/frostfs-adm.yml morph init --contracts ${FROSTFS_CONTRACTS_PATH}
|
${BIN}/frostfs-adm --config ./dev/adm/frostfs-adm.yml morph init --contracts ${FROSTFS_CONTRACTS_PATH}
|
||||||
${BIN}/frostfs-adm --config ./dev/adm/frostfs-adm.yml morph refill-gas --storage-wallet ./dev/storage/wallet.json --gas 10.0
|
${BIN}/frostfs-adm --config ./dev/adm/frostfs-adm.yml morph refill-gas --gas 10.0 \
|
||||||
|
--storage-wallet ./dev/storage/wallet01.json \
|
||||||
|
--storage-wallet ./dev/storage/wallet02.json \
|
||||||
|
--storage-wallet ./dev/storage/wallet03.json \
|
||||||
|
--storage-wallet ./dev/storage/wallet04.json
|
||||||
|
|
||||||
@if [ ! -f "$(LOCODE_DB_PATH)" ]; then \
|
@if [ ! -f "$(LOCODE_DB_PATH)" ]; then \
|
||||||
make locode-download; \
|
make locode-download; \
|
||||||
fi
|
fi
|
||||||
|
mkdir -p ./$(TMP_DIR)/state
|
||||||
|
mkdir -p ./$(TMP_DIR)/storage
|
||||||
|
|
||||||
# Shutdown dev environment
|
# Shutdown dev environment
|
||||||
env-down:
|
env-down:
|
||||||
docker compose -f dev/docker-compose.yml down
|
docker compose -f dev/docker-compose.yml down -v
|
||||||
docker volume rm -f frostfs-node_neo-go
|
rm -rf ./$(TMP_DIR)/state
|
||||||
rm -f ./.cache/.frostfs-ir-state
|
rm -rf ./$(TMP_DIR)/storage
|
||||||
rm -f ./.cache/.frostfs-node-state
|
|
||||||
rm -rf ./.cache/storage
|
|
||||||
|
|
21
README.md
21
README.md
|
@ -1,5 +1,5 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./.github/logo.svg" width="500px" alt="FrostFS">
|
<img src="./.forgejo/logo.svg" width="500px" alt="FrostFS">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
@ -7,9 +7,8 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
---
|
---
|
||||||
[![Report](https://goreportcard.com/badge/github.com/TrueCloudLab/frostfs-node)](https://goreportcard.com/report/github.com/TrueCloudLab/frostfs-node)
|
[![Report](https://goreportcard.com/badge/git.frostfs.info/TrueCloudLab/frostfs-node)](https://goreportcard.com/report/git.frostfs.info/TrueCloudLab/frostfs-node)
|
||||||
![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/TrueCloudLab/frostfs-node?sort=semver)
|
![Release (latest)](https://git.frostfs.info/TrueCloudLab/frostfs-node/badges/release.svg)
|
||||||
![License](https://img.shields.io/github/license/TrueCloudLab/frostfs-node.svg?style=popout)
|
|
||||||
|
|
||||||
# Overview
|
# Overview
|
||||||
|
|
||||||
|
@ -33,8 +32,8 @@ manipulate large amounts of data without paying a prohibitive price.
|
||||||
|
|
||||||
FrostFS has a native [gRPC API](https://git.frostfs.info/TrueCloudLab/frostfs-api) and has
|
FrostFS has a native [gRPC API](https://git.frostfs.info/TrueCloudLab/frostfs-api) and has
|
||||||
protocol gateways for popular protocols such as [AWS
|
protocol gateways for popular protocols such as [AWS
|
||||||
S3](https://github.com/TrueCloudLab/frostfs-s3-gw),
|
S3](https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw),
|
||||||
[HTTP](https://github.com/TrueCloudLab/frostfs-http-gw),
|
[HTTP](https://git.frostfs.info/TrueCloudLab/frostfs-http-gw),
|
||||||
[FUSE](https://wikipedia.org/wiki/Filesystem_in_Userspace) and
|
[FUSE](https://wikipedia.org/wiki/Filesystem_in_Userspace) and
|
||||||
[sFTP](https://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol) allowing
|
[sFTP](https://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol) allowing
|
||||||
developers to integrate applications without rewriting their code.
|
developers to integrate applications without rewriting their code.
|
||||||
|
@ -45,11 +44,11 @@ Now, we only support GNU/Linux on amd64 CPUs with AVX/AVX2 instructions. More
|
||||||
platforms will be officially supported after release `1.0`.
|
platforms will be officially supported after release `1.0`.
|
||||||
|
|
||||||
The latest version of frostfs-node works with frostfs-contract
|
The latest version of frostfs-node works with frostfs-contract
|
||||||
[v0.16.0](https://github.com/TrueCloudLab/frostfs-contract/releases/tag/v0.16.0).
|
[v0.19.2](https://git.frostfs.info/TrueCloudLab/frostfs-contract/releases/tag/v0.19.2).
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
|
||||||
To make all binaries you need Go 1.20+ and `make`:
|
To make all binaries you need Go 1.22+ and `make`:
|
||||||
```
|
```
|
||||||
make all
|
make all
|
||||||
```
|
```
|
||||||
|
@ -71,7 +70,7 @@ make docker/bin/frostfs-<name> # build a specific binary
|
||||||
|
|
||||||
## Docker images
|
## Docker images
|
||||||
|
|
||||||
To make docker images suitable for use in [frostfs-dev-env](https://github.com/TrueCloudLab/frostfs-dev-env/) use:
|
To make docker images suitable for use in [frostfs-dev-env](https://git.frostfs.info/TrueCloudLab/frostfs-dev-env/) use:
|
||||||
```
|
```
|
||||||
make images
|
make images
|
||||||
```
|
```
|
||||||
|
@ -99,7 +98,7 @@ See `frostfs-contract`'s README.md for build instructions.
|
||||||
4. To create container and put object into it run (container and object IDs will be different):
|
4. To create container and put object into it run (container and object IDs will be different):
|
||||||
|
|
||||||
```
|
```
|
||||||
./bin/frostfs-cli container create -r 127.0.0.1:8080 --wallet ./dev/wallet.json --policy "REP 1 IN X CBF 1 SELECT 1 FROM * AS X" --basic-acl public-read-write --await
|
./bin/frostfs-cli container create -r 127.0.0.1:8080 --wallet ./dev/wallet.json --policy "REP 1 IN X CBF 1 SELECT 1 FROM * AS X" --await
|
||||||
Enter password > <- press ENTER, the is no password for wallet
|
Enter password > <- press ENTER, the is no password for wallet
|
||||||
CID: CfPhEuHQ2PRvM4gfBQDC4dWZY3NccovyfcnEdiq2ixju
|
CID: CfPhEuHQ2PRvM4gfBQDC4dWZY3NccovyfcnEdiq2ixju
|
||||||
|
|
||||||
|
@ -125,7 +124,7 @@ the feature/topic you are going to implement.
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
|
|
||||||
FrostFS is maintained by [True Cloud Lab](https://github.com/TrueCloudLab/) with the help and
|
FrostFS is maintained by [True Cloud Lab](https://git.frostfs.info/TrueCloudLab/) with the help and
|
||||||
contributions from community members.
|
contributions from community members.
|
||||||
|
|
||||||
Please see [CREDITS](CREDITS.md) for details.
|
Please see [CREDITS](CREDITS.md) for details.
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
v0.38.0
|
v0.44.0
|
||||||
|
|
|
@ -56,7 +56,8 @@ 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. The list of the name for alphabet wallets(no gaps between names allowed, order is important):
|
||||||
|
- az, buky, vedi, glagoli, dobro, yest, zhivete, dzelo, zemlja, izhe, izhei, gerv, kako, ljudi, mislete, nash, on, pokoj, rtsi, slovo, tverdo, uk
|
||||||
|
|
||||||
- `init` initializes the sidechain by deploying smart contracts and
|
- `init` initializes the sidechain by deploying smart contracts and
|
||||||
setting provided FrostFS network configuration.
|
setting provided FrostFS network configuration.
|
||||||
|
|
|
@ -9,8 +9,8 @@ related configuration details.
|
||||||
|
|
||||||
To follow this guide you need:
|
To follow this guide you need:
|
||||||
- latest released version of [neo-go](https://github.com/nspcc-dev/neo-go/releases) (v0.97.2 at the moment),
|
- latest released version of [neo-go](https://github.com/nspcc-dev/neo-go/releases) (v0.97.2 at the moment),
|
||||||
- latest released version of [frostfs-adm](https://github.com/TrueCloudLab/frostfs-node/releases) utility (v0.25.1 at the moment),
|
- latest released version of [frostfs-adm](https://git.frostfs.info/TrueCloudLab/frostfs-node/releases) utility (v0.42.9 at the moment),
|
||||||
- latest released version of compiled [frostfs-contract](https://github.com/TrueCloudLab/frostfs-contract/releases) (v0.11.0 at the moment).
|
- latest released version of compiled [frostfs-contract](https://git.frostfs.info/TrueCloudLab/frostfs-contract/releases) (v0.19.2 at the moment).
|
||||||
|
|
||||||
## Step 1: Prepare network configuration
|
## Step 1: Prepare network configuration
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@ alphabet-wallets: /home/user/deploy/alphabet-wallets
|
||||||
network:
|
network:
|
||||||
max_object_size: 67108864
|
max_object_size: 67108864
|
||||||
epoch_duration: 240
|
epoch_duration: 240
|
||||||
|
max_ec_data_count: 12
|
||||||
|
max_ec_parity_count: 4
|
||||||
fee:
|
fee:
|
||||||
candidate: 0
|
candidate: 0
|
||||||
container: 0
|
container: 0
|
||||||
|
@ -62,6 +64,11 @@ alphabet-wallets: /home/user/deploy/alphabet-wallets
|
||||||
wallet[0]: hunter2
|
wallet[0]: hunter2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This command generates wallets with the following names:
|
||||||
|
- az, buky, vedi, glagoli, dobro, yest, zhivete, dzelo, zemlja, izhe, izhei, gerv, kako, ljudi, mislete, nash, on, pokoj, rtsi, slovo, tverdo, uk
|
||||||
|
|
||||||
|
No gaps between names allowed, order is important.
|
||||||
|
|
||||||
Do not lose wallet files and network config. Store it in an encrypted backed up
|
Do not lose wallet files and network config. Store it in an encrypted backed up
|
||||||
storage.
|
storage.
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,15 @@ const (
|
||||||
AlphabetWalletsFlagDesc = "Path to alphabet wallets dir"
|
AlphabetWalletsFlagDesc = "Path to alphabet wallets dir"
|
||||||
|
|
||||||
LocalDumpFlag = "local-dump"
|
LocalDumpFlag = "local-dump"
|
||||||
|
ProtoConfigPath = "protocol"
|
||||||
ContractsInitFlag = "contracts"
|
ContractsInitFlag = "contracts"
|
||||||
ContractsInitFlagDesc = "Path to archive with compiled FrostFS contracts (the default is to fetch the latest release from the official repository)"
|
ContractsInitFlagDesc = "Path to archive with compiled FrostFS contracts (the default is to fetch the latest release from the official repository)"
|
||||||
ContractsURLFlag = "contracts-url"
|
ContractsURLFlag = "contracts-url"
|
||||||
ContractsURLFlagDesc = "URL to archive with compiled FrostFS contracts"
|
ContractsURLFlagDesc = "URL to archive with compiled FrostFS contracts"
|
||||||
EpochDurationInitFlag = "network.epoch_duration"
|
EpochDurationInitFlag = "network.epoch_duration"
|
||||||
MaxObjectSizeInitFlag = "network.max_object_size"
|
MaxObjectSizeInitFlag = "network.max_object_size"
|
||||||
|
MaxECDataCountFlag = "network.max_ec_data_count"
|
||||||
|
MaxECParityCounFlag = "network.max_ec_parity_count"
|
||||||
RefillGasAmountFlag = "gas"
|
RefillGasAmountFlag = "gas"
|
||||||
StorageWalletFlag = "storage-wallet"
|
StorageWalletFlag = "storage-wallet"
|
||||||
ContainerFeeInitFlag = "network.fee.container"
|
ContainerFeeInitFlag = "network.fee.container"
|
||||||
|
|
|
@ -21,6 +21,8 @@ type configTemplate struct {
|
||||||
CandidateFee int
|
CandidateFee int
|
||||||
ContainerFee int
|
ContainerFee int
|
||||||
ContainerAliasFee int
|
ContainerAliasFee int
|
||||||
|
MaxECDataCount int
|
||||||
|
MaxECParityCount int
|
||||||
WithdrawFee int
|
WithdrawFee int
|
||||||
Glagolitics []string
|
Glagolitics []string
|
||||||
HomomorphicHashDisabled bool
|
HomomorphicHashDisabled bool
|
||||||
|
@ -31,6 +33,8 @@ alphabet-wallets: {{ .AlphabetDir}}
|
||||||
network:
|
network:
|
||||||
max_object_size: {{ .MaxObjectSize}}
|
max_object_size: {{ .MaxObjectSize}}
|
||||||
epoch_duration: {{ .EpochDuration}}
|
epoch_duration: {{ .EpochDuration}}
|
||||||
|
max_ec_data_count: {{ .MaxECDataCount}}
|
||||||
|
max_ec_parity_count: {{ .MaxECParityCount}}
|
||||||
homomorphic_hash_disabled: {{ .HomomorphicHashDisabled}}
|
homomorphic_hash_disabled: {{ .HomomorphicHashDisabled}}
|
||||||
fee:
|
fee:
|
||||||
candidate: {{ .CandidateFee}}
|
candidate: {{ .CandidateFee}}
|
||||||
|
@ -106,6 +110,8 @@ func generateConfigExample(appDir string, credSize int) (string, error) {
|
||||||
tmpl := configTemplate{
|
tmpl := configTemplate{
|
||||||
Endpoint: "https://neo.rpc.node:30333",
|
Endpoint: "https://neo.rpc.node:30333",
|
||||||
MaxObjectSize: 67108864, // 64 MiB
|
MaxObjectSize: 67108864, // 64 MiB
|
||||||
|
MaxECDataCount: 12, // Tested with 16-node networks, assuming 12 data + 4 parity nodes.
|
||||||
|
MaxECParityCount: 4, // Maximum 4 parity chunks, typically <= 3 for most policies.
|
||||||
EpochDuration: 240, // 1 hour with 15s per block
|
EpochDuration: 240, // 1 hour with 15s per block
|
||||||
HomomorphicHashDisabled: false, // object homomorphic hash is enabled
|
HomomorphicHashDisabled: false, // object homomorphic hash is enabled
|
||||||
CandidateFee: 100_0000_0000, // 100.0 GAS (Fixed8)
|
CandidateFee: 100_0000_0000, // 100.0 GAS (Fixed8)
|
||||||
|
@ -122,7 +128,7 @@ func generateConfigExample(appDir string, credSize int) (string, error) {
|
||||||
tmpl.AlphabetDir = filepath.Join(appDir, "alphabet-wallets")
|
tmpl.AlphabetDir = filepath.Join(appDir, "alphabet-wallets")
|
||||||
|
|
||||||
var i innerring.GlagoliticLetter
|
var i innerring.GlagoliticLetter
|
||||||
for i = 0; i < innerring.GlagoliticLetter(credSize); i++ {
|
for i = range innerring.GlagoliticLetter(credSize) {
|
||||||
tmpl.Glagolitics = append(tmpl.Glagolitics, i.String())
|
tmpl.Glagolitics = append(tmpl.Glagolitics, i.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,8 @@ func TestGenerateConfigExample(t *testing.T) {
|
||||||
require.Equal(t, "https://neo.rpc.node:30333", v.GetString("rpc-endpoint"))
|
require.Equal(t, "https://neo.rpc.node:30333", v.GetString("rpc-endpoint"))
|
||||||
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, 12, v.GetInt("network.max_ec_data_count"))
|
||||||
|
require.Equal(t, 4, v.GetInt("network.max_ec_parity_count"))
|
||||||
require.Equal(t, 240, v.GetInt("network.epoch_duration"))
|
require.Equal(t, 240, v.GetInt("network.epoch_duration"))
|
||||||
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"))
|
||||||
|
|
15
cmd/frostfs-adm/internal/modules/metabase/root.go
Normal file
15
cmd/frostfs-adm/internal/modules/metabase/root.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package metabase
|
||||||
|
|
||||||
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
|
// RootCmd is a root command of config section.
|
||||||
|
var RootCmd = &cobra.Command{
|
||||||
|
Use: "metabase",
|
||||||
|
Short: "Section for metabase commands",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RootCmd.AddCommand(UpgradeCmd)
|
||||||
|
|
||||||
|
initUpgradeCommand()
|
||||||
|
}
|
150
cmd/frostfs-adm/internal/modules/metabase/upgrade.go
Normal file
150
cmd/frostfs-adm/internal/modules/metabase/upgrade.go
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
package metabase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||||
|
engineconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine"
|
||||||
|
shardconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard"
|
||||||
|
morphconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/morph"
|
||||||
|
nodeconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/node"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
||||||
|
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
||||||
|
morphcontainer "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
noCompactFlag = "no-compact"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNoPathsFound = errors.New("no metabase paths found")
|
||||||
|
errNoMorphEndpointsFound = errors.New("no morph endpoints found")
|
||||||
|
)
|
||||||
|
|
||||||
|
var UpgradeCmd = &cobra.Command{
|
||||||
|
Use: "upgrade",
|
||||||
|
Short: "Upgrade metabase to latest version",
|
||||||
|
RunE: upgrade,
|
||||||
|
}
|
||||||
|
|
||||||
|
func upgrade(cmd *cobra.Command, _ []string) error {
|
||||||
|
configFile, err := cmd.Flags().GetString(commonflags.ConfigFlag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
configDir, err := cmd.Flags().GetString(commonflags.ConfigDirFlag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
appCfg := config.New(configFile, configDir, config.EnvPrefix)
|
||||||
|
paths, err := getMetabasePaths(appCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(paths) == 0 {
|
||||||
|
return errNoPathsFound
|
||||||
|
}
|
||||||
|
cmd.Println("found", len(paths), "metabases:")
|
||||||
|
for i, path := range paths {
|
||||||
|
cmd.Println(i+1, ":", path)
|
||||||
|
}
|
||||||
|
mc, err := createMorphClient(cmd.Context(), appCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer mc.Close()
|
||||||
|
civ, err := createContainerInfoProvider(mc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
noCompact, _ := cmd.Flags().GetBool(noCompactFlag)
|
||||||
|
result := make(map[string]bool)
|
||||||
|
var resultGuard sync.Mutex
|
||||||
|
eg, ctx := errgroup.WithContext(cmd.Context())
|
||||||
|
for _, path := range paths {
|
||||||
|
eg.Go(func() error {
|
||||||
|
var success bool
|
||||||
|
cmd.Println("upgrading metabase", path, "...")
|
||||||
|
if err := meta.Upgrade(ctx, path, !noCompact, civ, func(a ...any) {
|
||||||
|
cmd.Println(append([]any{time.Now().Format(time.RFC3339), ":", path, ":"}, a...)...)
|
||||||
|
}); err != nil {
|
||||||
|
cmd.Println("error: failed to upgrade metabase", path, ":", err)
|
||||||
|
} else {
|
||||||
|
success = true
|
||||||
|
cmd.Println("metabase", path, "upgraded successfully")
|
||||||
|
}
|
||||||
|
resultGuard.Lock()
|
||||||
|
result[path] = success
|
||||||
|
resultGuard.Unlock()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := eg.Wait(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for mb, ok := range result {
|
||||||
|
if ok {
|
||||||
|
cmd.Println(mb, ": success")
|
||||||
|
} else {
|
||||||
|
cmd.Println(mb, ": failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMetabasePaths(appCfg *config.Config) ([]string, error) {
|
||||||
|
var paths []string
|
||||||
|
if err := engineconfig.IterateShards(appCfg, false, func(sc *shardconfig.Config) error {
|
||||||
|
paths = append(paths, sc.Metabase().Path())
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return nil, fmt.Errorf("get metabase paths: %w", err)
|
||||||
|
}
|
||||||
|
return paths, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMorphClient(ctx context.Context, appCfg *config.Config) (*client.Client, error) {
|
||||||
|
addresses := morphconfig.RPCEndpoint(appCfg)
|
||||||
|
if len(addresses) == 0 {
|
||||||
|
return nil, errNoMorphEndpointsFound
|
||||||
|
}
|
||||||
|
key := nodeconfig.Key(appCfg)
|
||||||
|
cli, err := client.New(ctx,
|
||||||
|
key,
|
||||||
|
client.WithDialTimeout(morphconfig.DialTimeout(appCfg)),
|
||||||
|
client.WithEndpoints(addresses...),
|
||||||
|
client.WithSwitchInterval(morphconfig.SwitchInterval(appCfg)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create morph client:%w", err)
|
||||||
|
}
|
||||||
|
return cli, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createContainerInfoProvider(cli *client.Client) (container.InfoProvider, error) {
|
||||||
|
sh, err := cli.NNSContractAddress(client.NNSContainerContractName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("resolve container contract hash: %w", err)
|
||||||
|
}
|
||||||
|
cc, err := morphcontainer.NewFromMorph(cli, sh, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create morph container client: %w", err)
|
||||||
|
}
|
||||||
|
return container.NewInfoProvider(func() (container.Source, error) {
|
||||||
|
return morphcontainer.AsContainerSource(cc), nil
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initUpgradeCommand() {
|
||||||
|
flags := UpgradeCmd.Flags()
|
||||||
|
flags.Bool(noCompactFlag, false, "Do not compact upgraded metabase file")
|
||||||
|
}
|
|
@ -5,33 +5,19 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
parseutil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
apeCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/ape"
|
||||||
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
namespaceTarget = "namespace"
|
|
||||||
containerTarget = "container"
|
|
||||||
jsonFlag = "json"
|
jsonFlag = "json"
|
||||||
jsonFlagDesc = "Output rule chains in JSON format"
|
jsonFlagDesc = "Output rule chains in JSON format"
|
||||||
chainIDFlag = "chain-id"
|
|
||||||
chainIDDesc = "Rule chain ID"
|
|
||||||
ruleFlag = "rule"
|
|
||||||
ruleFlagDesc = "Rule chain in text format"
|
|
||||||
pathFlag = "path"
|
|
||||||
pathFlagDesc = "path to encoded chain in JSON or binary format"
|
|
||||||
targetNameFlag = "target-name"
|
|
||||||
targetNameDesc = "Resource name in APE resource name format"
|
|
||||||
targetTypeFlag = "target-type"
|
|
||||||
targetTypeDesc = "Resource type(container/namespace)"
|
|
||||||
addrAdminFlag = "addr"
|
addrAdminFlag = "addr"
|
||||||
addrAdminDesc = "The address of the admins wallet"
|
addrAdminDesc = "The address of the admins wallet"
|
||||||
chainNameFlag = "chain-name"
|
|
||||||
chainNameFlagDesc = "Chain name(ingress|s3)"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -99,17 +85,17 @@ func initAddRuleChainCmd() {
|
||||||
addRuleChainCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
addRuleChainCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
addRuleChainCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
addRuleChainCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||||
|
|
||||||
addRuleChainCmd.Flags().String(targetTypeFlag, "", targetTypeDesc)
|
addRuleChainCmd.Flags().String(apeCmd.TargetTypeFlag, "", apeCmd.TargetTypeFlagDesc)
|
||||||
_ = addRuleChainCmd.MarkFlagRequired(targetTypeFlag)
|
_ = addRuleChainCmd.MarkFlagRequired(apeCmd.TargetTypeFlag)
|
||||||
addRuleChainCmd.Flags().String(targetNameFlag, "", targetNameDesc)
|
addRuleChainCmd.Flags().String(apeCmd.TargetNameFlag, "", apeCmd.TargetTypeFlagDesc)
|
||||||
_ = addRuleChainCmd.MarkFlagRequired(targetNameFlag)
|
_ = addRuleChainCmd.MarkFlagRequired(apeCmd.TargetNameFlag)
|
||||||
|
|
||||||
addRuleChainCmd.Flags().String(chainIDFlag, "", chainIDDesc)
|
addRuleChainCmd.Flags().String(apeCmd.ChainIDFlag, "", apeCmd.ChainIDFlagDesc)
|
||||||
_ = addRuleChainCmd.MarkFlagRequired(chainIDFlag)
|
_ = addRuleChainCmd.MarkFlagRequired(apeCmd.ChainIDFlag)
|
||||||
addRuleChainCmd.Flags().StringArray(ruleFlag, []string{}, ruleFlagDesc)
|
addRuleChainCmd.Flags().StringArray(apeCmd.RuleFlag, []string{}, apeCmd.RuleFlagDesc)
|
||||||
addRuleChainCmd.Flags().String(pathFlag, "", pathFlagDesc)
|
addRuleChainCmd.Flags().String(apeCmd.PathFlag, "", apeCmd.PathFlagDesc)
|
||||||
addRuleChainCmd.Flags().String(chainNameFlag, ingress, chainNameFlagDesc)
|
addRuleChainCmd.Flags().String(apeCmd.ChainNameFlag, apeCmd.Ingress, apeCmd.ChainNameFlagDesc)
|
||||||
addRuleChainCmd.MarkFlagsMutuallyExclusive(ruleFlag, pathFlag)
|
addRuleChainCmd.MarkFlagsMutuallyExclusive(apeCmd.RuleFlag, apeCmd.PathFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initRemoveRuleChainCmd() {
|
func initRemoveRuleChainCmd() {
|
||||||
|
@ -118,26 +104,25 @@ func initRemoveRuleChainCmd() {
|
||||||
removeRuleChainCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
removeRuleChainCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
removeRuleChainCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
removeRuleChainCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||||
|
|
||||||
removeRuleChainCmd.Flags().String(targetTypeFlag, "", targetTypeDesc)
|
removeRuleChainCmd.Flags().String(apeCmd.TargetTypeFlag, "", apeCmd.TargetTypeFlagDesc)
|
||||||
_ = removeRuleChainCmd.MarkFlagRequired(targetTypeFlag)
|
_ = removeRuleChainCmd.MarkFlagRequired(apeCmd.TargetTypeFlag)
|
||||||
removeRuleChainCmd.Flags().String(targetNameFlag, "", targetNameDesc)
|
removeRuleChainCmd.Flags().String(apeCmd.TargetNameFlag, "", apeCmd.TargetNameFlagDesc)
|
||||||
_ = removeRuleChainCmd.MarkFlagRequired(targetNameFlag)
|
_ = removeRuleChainCmd.MarkFlagRequired(apeCmd.TargetNameFlag)
|
||||||
removeRuleChainCmd.Flags().String(chainIDFlag, "", chainIDDesc)
|
removeRuleChainCmd.Flags().String(apeCmd.ChainIDFlag, "", apeCmd.ChainIDFlagDesc)
|
||||||
removeRuleChainCmd.Flags().String(chainNameFlag, ingress, chainNameFlagDesc)
|
removeRuleChainCmd.Flags().String(apeCmd.ChainNameFlag, apeCmd.Ingress, apeCmd.ChainNameFlagDesc)
|
||||||
removeRuleChainCmd.Flags().Bool(commonflags.AllFlag, false, "Remove all chains for target")
|
removeRuleChainCmd.Flags().Bool(commonflags.AllFlag, false, "Remove all chains for target")
|
||||||
removeRuleChainCmd.MarkFlagsMutuallyExclusive(commonflags.AllFlag, chainIDFlag)
|
removeRuleChainCmd.MarkFlagsMutuallyExclusive(commonflags.AllFlag, apeCmd.ChainIDFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initListRuleChainsCmd() {
|
func initListRuleChainsCmd() {
|
||||||
Cmd.AddCommand(listRuleChainsCmd)
|
Cmd.AddCommand(listRuleChainsCmd)
|
||||||
|
|
||||||
listRuleChainsCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
listRuleChainsCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
listRuleChainsCmd.Flags().StringP(targetTypeFlag, "t", "", targetTypeDesc)
|
listRuleChainsCmd.Flags().StringP(apeCmd.TargetTypeFlag, "t", "", apeCmd.TargetTypeFlagDesc)
|
||||||
_ = listRuleChainsCmd.MarkFlagRequired(targetTypeFlag)
|
_ = listRuleChainsCmd.MarkFlagRequired(apeCmd.TargetTypeFlag)
|
||||||
listRuleChainsCmd.Flags().String(targetNameFlag, "", targetNameDesc)
|
listRuleChainsCmd.Flags().String(apeCmd.TargetNameFlag, "", apeCmd.TargetNameFlagDesc)
|
||||||
_ = listRuleChainsCmd.MarkFlagRequired(targetNameFlag)
|
|
||||||
listRuleChainsCmd.Flags().Bool(jsonFlag, false, jsonFlagDesc)
|
listRuleChainsCmd.Flags().Bool(jsonFlag, false, jsonFlagDesc)
|
||||||
listRuleChainsCmd.Flags().String(chainNameFlag, ingress, chainNameFlagDesc)
|
listRuleChainsCmd.Flags().String(apeCmd.ChainNameFlag, apeCmd.Ingress, apeCmd.ChainNameFlagDesc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initSetAdminCmd() {
|
func initSetAdminCmd() {
|
||||||
|
@ -159,15 +144,15 @@ func initListTargetsCmd() {
|
||||||
Cmd.AddCommand(listTargetsCmd)
|
Cmd.AddCommand(listTargetsCmd)
|
||||||
|
|
||||||
listTargetsCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
listTargetsCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
listTargetsCmd.Flags().StringP(targetTypeFlag, "t", "", targetTypeDesc)
|
listTargetsCmd.Flags().StringP(apeCmd.TargetTypeFlag, "t", "", apeCmd.TargetTypeFlagDesc)
|
||||||
_ = listTargetsCmd.MarkFlagRequired(targetTypeFlag)
|
_ = listTargetsCmd.MarkFlagRequired(apeCmd.TargetTypeFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addRuleChain(cmd *cobra.Command, _ []string) {
|
func addRuleChain(cmd *cobra.Command, _ []string) {
|
||||||
chain := parseChain(cmd)
|
chain := apeCmd.ParseChain(cmd)
|
||||||
target := parseTarget(cmd)
|
target := parseTarget(cmd)
|
||||||
pci, ac := newPolicyContractInterface(cmd)
|
pci, ac := newPolicyContractInterface(cmd)
|
||||||
h, vub, err := pci.AddMorphRuleChain(parseChainName(cmd), target, chain)
|
h, vub, err := pci.AddMorphRuleChain(apeCmd.ParseChainName(cmd), target, chain)
|
||||||
cmd.Println("Waiting for transaction to persist...")
|
cmd.Println("Waiting for transaction to persist...")
|
||||||
_, err = ac.Wait(h, vub, err)
|
_, err = ac.Wait(h, vub, err)
|
||||||
commonCmd.ExitOnErr(cmd, "add rule chain error: %w", err)
|
commonCmd.ExitOnErr(cmd, "add rule chain error: %w", err)
|
||||||
|
@ -179,14 +164,14 @@ func removeRuleChain(cmd *cobra.Command, _ []string) {
|
||||||
pci, ac := newPolicyContractInterface(cmd)
|
pci, ac := newPolicyContractInterface(cmd)
|
||||||
removeAll, _ := cmd.Flags().GetBool(commonflags.AllFlag)
|
removeAll, _ := cmd.Flags().GetBool(commonflags.AllFlag)
|
||||||
if removeAll {
|
if removeAll {
|
||||||
h, vub, err := pci.RemoveMorphRuleChainsByTarget(parseChainName(cmd), target)
|
h, vub, err := pci.RemoveMorphRuleChainsByTarget(apeCmd.ParseChainName(cmd), target)
|
||||||
cmd.Println("Waiting for transaction to persist...")
|
cmd.Println("Waiting for transaction to persist...")
|
||||||
_, err = ac.Wait(h, vub, err)
|
_, err = ac.Wait(h, vub, err)
|
||||||
commonCmd.ExitOnErr(cmd, "remove rule chain error: %w", err)
|
commonCmd.ExitOnErr(cmd, "remove rule chain error: %w", err)
|
||||||
cmd.Println("All chains for target removed successfully")
|
cmd.Println("All chains for target removed successfully")
|
||||||
} else {
|
} else {
|
||||||
chainID := parseChainID(cmd)
|
chainID := apeCmd.ParseChainID(cmd)
|
||||||
h, vub, err := pci.RemoveMorphRuleChain(parseChainName(cmd), target, chainID)
|
h, vub, err := pci.RemoveMorphRuleChain(apeCmd.ParseChainName(cmd), target, chainID)
|
||||||
cmd.Println("Waiting for transaction to persist...")
|
cmd.Println("Waiting for transaction to persist...")
|
||||||
_, err = ac.Wait(h, vub, err)
|
_, err = ac.Wait(h, vub, err)
|
||||||
commonCmd.ExitOnErr(cmd, "remove rule chain error: %w", err)
|
commonCmd.ExitOnErr(cmd, "remove rule chain error: %w", err)
|
||||||
|
@ -197,7 +182,7 @@ func removeRuleChain(cmd *cobra.Command, _ []string) {
|
||||||
func listRuleChains(cmd *cobra.Command, _ []string) {
|
func listRuleChains(cmd *cobra.Command, _ []string) {
|
||||||
target := parseTarget(cmd)
|
target := parseTarget(cmd)
|
||||||
pci, _ := newPolicyContractReaderInterface(cmd)
|
pci, _ := newPolicyContractReaderInterface(cmd)
|
||||||
chains, err := pci.ListMorphRuleChains(parseChainName(cmd), target)
|
chains, err := pci.ListMorphRuleChains(apeCmd.ParseChainName(cmd), target)
|
||||||
commonCmd.ExitOnErr(cmd, "list rule chains error: %w", err)
|
commonCmd.ExitOnErr(cmd, "list rule chains error: %w", err)
|
||||||
if len(chains) == 0 {
|
if len(chains) == 0 {
|
||||||
return
|
return
|
||||||
|
@ -208,14 +193,14 @@ func listRuleChains(cmd *cobra.Command, _ []string) {
|
||||||
prettyJSONFormat(cmd, chains)
|
prettyJSONFormat(cmd, chains)
|
||||||
} else {
|
} else {
|
||||||
for _, c := range chains {
|
for _, c := range chains {
|
||||||
parseutil.PrintHumanReadableAPEChain(cmd, c)
|
apeCmd.PrintHumanReadableAPEChain(cmd, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAdmin(cmd *cobra.Command, _ []string) {
|
func setAdmin(cmd *cobra.Command, _ []string) {
|
||||||
s, _ := cmd.Flags().GetString(addrAdminFlag)
|
s, _ := cmd.Flags().GetString(addrAdminFlag)
|
||||||
addr, err := util.Uint160DecodeStringLE(s)
|
addr, err := address.StringToUint160(s)
|
||||||
commonCmd.ExitOnErr(cmd, "can't decode admin addr: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't decode admin addr: %w", err)
|
||||||
pci, ac := newPolicyContractInterface(cmd)
|
pci, ac := newPolicyContractInterface(cmd)
|
||||||
h, vub, err := pci.SetAdmin(addr)
|
h, vub, err := pci.SetAdmin(addr)
|
||||||
|
@ -229,12 +214,11 @@ func getAdmin(cmd *cobra.Command, _ []string) {
|
||||||
pci, _ := newPolicyContractReaderInterface(cmd)
|
pci, _ := newPolicyContractReaderInterface(cmd)
|
||||||
addr, err := pci.GetAdmin()
|
addr, err := pci.GetAdmin()
|
||||||
commonCmd.ExitOnErr(cmd, "unable to get admin: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to get admin: %w", err)
|
||||||
cmd.Println(addr.StringLE())
|
cmd.Println(address.Uint160ToString(addr))
|
||||||
}
|
}
|
||||||
|
|
||||||
func listTargets(cmd *cobra.Command, _ []string) {
|
func listTargets(cmd *cobra.Command, _ []string) {
|
||||||
typ, err := parseTargetType(cmd)
|
typ := apeCmd.ParseTargetType(cmd)
|
||||||
commonCmd.ExitOnErr(cmd, "parse target type error: %w", err)
|
|
||||||
pci, inv := newPolicyContractReaderInterface(cmd)
|
pci, inv := newPolicyContractReaderInterface(cmd)
|
||||||
|
|
||||||
sid, it, err := pci.ListTargetsIterator(typ)
|
sid, it, err := pci.ListTargetsIterator(typ)
|
||||||
|
|
|
@ -2,13 +2,12 @@ package ape
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
parseutil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
apeCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/ape"
|
||||||
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
||||||
morph "git.frostfs.info/TrueCloudLab/policy-engine/pkg/morph/policy"
|
morph "git.frostfs.info/TrueCloudLab/policy-engine/pkg/morph/policy"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
|
@ -18,108 +17,71 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var errUnknownTargetType = errors.New("unknown target type")
|
||||||
ingress = "ingress"
|
|
||||||
s3 = "s3"
|
|
||||||
)
|
|
||||||
|
|
||||||
var mChainName = map[string]apechain.Name{
|
|
||||||
ingress: apechain.Ingress,
|
|
||||||
s3: apechain.S3,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
errUnknownTargetType = errors.New("unknown target type")
|
|
||||||
errChainIDCannotBeEmpty = errors.New("chain id cannot be empty")
|
|
||||||
errRuleIsNotParsed = errors.New("rule is not passed")
|
|
||||||
errUnsupportedChainName = errors.New("unsupported chain name")
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseTarget(cmd *cobra.Command) policyengine.Target {
|
func parseTarget(cmd *cobra.Command) policyengine.Target {
|
||||||
name, _ := cmd.Flags().GetString(targetNameFlag)
|
typ := apeCmd.ParseTargetType(cmd)
|
||||||
typ, err := parseTargetType(cmd)
|
name, _ := cmd.Flags().GetString(apeCmd.TargetNameFlag)
|
||||||
commonCmd.ExitOnErr(cmd, "read target type error: %w", err)
|
|
||||||
|
|
||||||
return policyengine.Target{
|
|
||||||
Name: name,
|
|
||||||
Type: typ,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseTargetType(cmd *cobra.Command) (policyengine.TargetType, error) {
|
|
||||||
typ, _ := cmd.Flags().GetString(targetTypeFlag)
|
|
||||||
switch typ {
|
switch typ {
|
||||||
case namespaceTarget:
|
case policyengine.Namespace:
|
||||||
return policyengine.Namespace, nil
|
if name == "root" {
|
||||||
case containerTarget:
|
name = ""
|
||||||
return policyengine.Container, nil
|
|
||||||
}
|
}
|
||||||
return -1, errUnknownTargetType
|
return policyengine.NamespaceTarget(name)
|
||||||
|
case policyengine.Container:
|
||||||
|
var cnr cid.ID
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't decode container ID: %w", cnr.DecodeString(name))
|
||||||
|
return policyengine.ContainerTarget(name)
|
||||||
|
case policyengine.User:
|
||||||
|
return policyengine.UserTarget(name)
|
||||||
|
case policyengine.Group:
|
||||||
|
return policyengine.GroupTarget(name)
|
||||||
|
default:
|
||||||
|
commonCmd.ExitOnErr(cmd, "read target type error: %w", errUnknownTargetType)
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseChainID(cmd *cobra.Command) apechain.ID {
|
// invokerAdapter adapats invoker.Invoker to ContractStorageInvoker interface.
|
||||||
chainID, _ := cmd.Flags().GetString(chainIDFlag)
|
type invokerAdapter struct {
|
||||||
if chainID == "" {
|
*invoker.Invoker
|
||||||
commonCmd.ExitOnErr(cmd, "read chain id error: %w",
|
rpcActor invoker.RPCInvoke
|
||||||
errChainIDCannotBeEmpty)
|
|
||||||
}
|
|
||||||
return apechain.ID(chainID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseChain(cmd *cobra.Command) *apechain.Chain {
|
func (n *invokerAdapter) GetRPCInvoker() invoker.RPCInvoke {
|
||||||
chain := new(apechain.Chain)
|
return n.rpcActor
|
||||||
|
|
||||||
if rules, _ := cmd.Flags().GetStringArray(ruleFlag); len(rules) > 0 {
|
|
||||||
commonCmd.ExitOnErr(cmd, "parser error: %w", parseutil.ParseAPEChain(chain, rules))
|
|
||||||
} else if encPath, _ := cmd.Flags().GetString(pathFlag); encPath != "" {
|
|
||||||
commonCmd.ExitOnErr(cmd, "decode binary or json error: %w", parseutil.ParseAPEChainBinaryOrJSON(chain, encPath))
|
|
||||||
} else {
|
|
||||||
commonCmd.ExitOnErr(cmd, "parser error: %w", errRuleIsNotParsed)
|
|
||||||
}
|
|
||||||
|
|
||||||
chain.ID = parseChainID(cmd)
|
|
||||||
|
|
||||||
cmd.Println("Parsed chain:")
|
|
||||||
parseutil.PrintHumanReadableAPEChain(cmd, chain)
|
|
||||||
|
|
||||||
return chain
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseChainName(cmd *cobra.Command) apechain.Name {
|
|
||||||
chainName, _ := cmd.Flags().GetString(chainNameFlag)
|
|
||||||
apeChainName, ok := mChainName[strings.ToLower(chainName)]
|
|
||||||
if !ok {
|
|
||||||
commonCmd.ExitOnErr(cmd, "", errUnsupportedChainName)
|
|
||||||
}
|
|
||||||
return apeChainName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPolicyContractReaderInterface(cmd *cobra.Command) (*morph.ContractStorageReader, *invoker.Invoker) {
|
func newPolicyContractReaderInterface(cmd *cobra.Command) (*morph.ContractStorageReader, *invoker.Invoker) {
|
||||||
c, err := helper.GetN3Client(viper.GetViper())
|
c, err := helper.NewRemoteClient(viper.GetViper())
|
||||||
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
||||||
|
|
||||||
inv := invoker.New(c, nil)
|
inv := invoker.New(c, nil)
|
||||||
var ch util.Uint160
|
|
||||||
r := management.NewReader(inv)
|
r := management.NewReader(inv)
|
||||||
nnsCs, err := r.GetContractByID(1)
|
nnsCs, err := helper.GetContractByID(r, 1)
|
||||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||||
|
|
||||||
ch, err = helper.NNSResolveHash(inv, nnsCs.Hash, helper.DomainOf(constants.PolicyContract))
|
ch, err := helper.NNSResolveHash(inv, nnsCs.Hash, helper.DomainOf(constants.PolicyContract))
|
||||||
commonCmd.ExitOnErr(cmd, "unable to resolve policy contract hash: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to resolve policy contract hash: %w", err)
|
||||||
|
|
||||||
return morph.NewContractStorageReader(inv, ch), inv
|
invokerAdapter := &invokerAdapter{
|
||||||
|
Invoker: inv,
|
||||||
|
rpcActor: c,
|
||||||
|
}
|
||||||
|
|
||||||
|
return morph.NewContractStorageReader(invokerAdapter, ch), inv
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPolicyContractInterface(cmd *cobra.Command) (*morph.ContractStorage, *helper.LocalActor) {
|
func newPolicyContractInterface(cmd *cobra.Command) (*morph.ContractStorage, *helper.LocalActor) {
|
||||||
c, err := helper.GetN3Client(viper.GetViper())
|
c, err := helper.NewRemoteClient(viper.GetViper())
|
||||||
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
||||||
|
|
||||||
ac, err := helper.NewLocalActor(cmd, c)
|
ac, err := helper.NewLocalActor(cmd, c, constants.ConsensusAccountName)
|
||||||
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
||||||
|
|
||||||
var ch util.Uint160
|
var ch util.Uint160
|
||||||
r := management.NewReader(ac.Invoker)
|
r := management.NewReader(ac.Invoker)
|
||||||
nnsCs, err := r.GetContractByID(1)
|
nnsCs, err := helper.GetContractByID(r, 1)
|
||||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||||
|
|
||||||
ch, err = helper.NNSResolveHash(ac.Invoker, nnsCs.Hash, helper.DomainOf(constants.PolicyContract))
|
ch, err = helper.NNSResolveHash(ac.Invoker, nnsCs.Hash, helper.DomainOf(constants.PolicyContract))
|
||||||
|
|
|
@ -51,7 +51,7 @@ func dumpBalances(cmd *cobra.Command, _ []string) error {
|
||||||
nmHash util.Uint160
|
nmHash util.Uint160
|
||||||
)
|
)
|
||||||
|
|
||||||
c, err := helper.GetN3Client(viper.GetViper())
|
c, err := helper.NewRemoteClient(viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ func dumpBalances(cmd *cobra.Command, _ []string) error {
|
||||||
|
|
||||||
if dumpStorage || dumpAlphabet || dumpProxy {
|
if dumpStorage || dumpAlphabet || dumpProxy {
|
||||||
r := management.NewReader(inv)
|
r := management.NewReader(inv)
|
||||||
nnsCs, err = r.GetContractByID(1)
|
nnsCs, err = helper.GetContractByID(r, 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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ import (
|
||||||
const forceConfigSet = "force"
|
const forceConfigSet = "force"
|
||||||
|
|
||||||
func dumpNetworkConfig(cmd *cobra.Command, _ []string) error {
|
func dumpNetworkConfig(cmd *cobra.Command, _ []string) error {
|
||||||
c, err := helper.GetN3Client(viper.GetViper())
|
c, err := helper.NewRemoteClient(viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't create N3 client: %w", err)
|
return fmt.Errorf("can't create N3 client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ func dumpNetworkConfig(cmd *cobra.Command, _ []string) error {
|
||||||
inv := invoker.New(c, nil)
|
inv := invoker.New(c, nil)
|
||||||
r := management.NewReader(inv)
|
r := management.NewReader(inv)
|
||||||
|
|
||||||
cs, err := r.GetContractByID(1)
|
cs, err := helper.GetContractByID(r, 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)
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,8 @@ func dumpNetworkConfig(cmd *cobra.Command, _ []string) error {
|
||||||
switch k {
|
switch k {
|
||||||
case netmap.ContainerFeeConfig, netmap.ContainerAliasFeeConfig,
|
case netmap.ContainerFeeConfig, netmap.ContainerAliasFeeConfig,
|
||||||
netmap.EpochDurationConfig, netmap.IrCandidateFeeConfig,
|
netmap.EpochDurationConfig, netmap.IrCandidateFeeConfig,
|
||||||
netmap.MaxObjectSizeConfig, netmap.WithdrawFeeConfig:
|
netmap.MaxObjectSizeConfig, netmap.WithdrawFeeConfig,
|
||||||
|
netmap.MaxECDataCountConfig, netmap.MaxECParityCountConfig:
|
||||||
nbuf := make([]byte, 8)
|
nbuf := make([]byte, 8)
|
||||||
copy(nbuf[:], v)
|
copy(nbuf[:], v)
|
||||||
n := binary.LittleEndian.Uint64(nbuf)
|
n := binary.LittleEndian.Uint64(nbuf)
|
||||||
|
@ -92,7 +93,7 @@ func SetConfigCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||||
cs, err := r.GetContractByID(1)
|
cs, err := helper.GetContractByID(r, 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)
|
||||||
}
|
}
|
||||||
|
@ -103,14 +104,22 @@ func SetConfigCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
forceFlag, _ := cmd.Flags().GetBool(forceConfigSet)
|
forceFlag, _ := cmd.Flags().GetBool(forceConfigSet)
|
||||||
|
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
|
prm := make(map[string]any)
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
k, v, err := parseConfigPair(arg, forceFlag)
|
k, v, err := parseConfigPair(arg, forceFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prm[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateConfig(prm, forceFlag); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range prm {
|
||||||
// In NeoFS this is done via Notary contract. Here, however, we can form the
|
// In NeoFS this is done via Notary contract. Here, however, we can form the
|
||||||
// transaction locally. The first `nil` argument is required only for notary
|
// transaction locally. The first `nil` argument is required only for notary
|
||||||
// disabled environment which is not supported by that command.
|
// disabled environment which is not supported by that command.
|
||||||
|
@ -128,6 +137,50 @@ func SetConfigCmd(cmd *cobra.Command, args []string) error {
|
||||||
return wCtx.AwaitTx()
|
return wCtx.AwaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxECSum = 256
|
||||||
|
|
||||||
|
func validateConfig(args map[string]any, forceFlag bool) error {
|
||||||
|
var sumEC int64
|
||||||
|
_, okData := args[netmap.MaxECDataCountConfig]
|
||||||
|
_, okParity := args[netmap.MaxECParityCountConfig]
|
||||||
|
if okData != okParity {
|
||||||
|
return fmt.Errorf("both %s and %s must be present in the configuration",
|
||||||
|
netmap.MaxECDataCountConfig, netmap.MaxECParityCountConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range args {
|
||||||
|
switch k {
|
||||||
|
case netmap.ContainerFeeConfig, netmap.ContainerAliasFeeConfig,
|
||||||
|
netmap.EpochDurationConfig, netmap.IrCandidateFeeConfig,
|
||||||
|
netmap.MaxObjectSizeConfig, netmap.WithdrawFeeConfig,
|
||||||
|
netmap.MaxECDataCountConfig, netmap.MaxECParityCountConfig:
|
||||||
|
value, ok := v.(int64)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("%s has an invalid type. Expected type: int", k)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value < 0 {
|
||||||
|
return fmt.Errorf("%s must be >= 0, got %v", k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if k == netmap.MaxECDataCountConfig || k == netmap.MaxECParityCountConfig {
|
||||||
|
sumEC += value
|
||||||
|
}
|
||||||
|
case netmap.HomomorphicHashingDisabledKey, netmap.MaintenanceModeAllowedConfig:
|
||||||
|
_, ok := v.(bool)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("%s has an invalid type. Expected type: bool", k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sumEC > maxECSum && !forceFlag {
|
||||||
|
return fmt.Errorf("the sum of %s and %s must be <= %d, got %d",
|
||||||
|
netmap.MaxECDataCountConfig, netmap.MaxECParityCountConfig, maxECSum, sumEC)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func parseConfigPair(kvStr string, force bool) (key string, val any, err error) {
|
func parseConfigPair(kvStr string, force bool) (key string, val any, err error) {
|
||||||
k, v, found := strings.Cut(kvStr, "=")
|
k, v, found := strings.Cut(kvStr, "=")
|
||||||
if !found {
|
if !found {
|
||||||
|
@ -140,7 +193,8 @@ func parseConfigPair(kvStr string, force bool) (key string, val any, err error)
|
||||||
switch key {
|
switch key {
|
||||||
case netmap.ContainerFeeConfig, netmap.ContainerAliasFeeConfig,
|
case netmap.ContainerFeeConfig, netmap.ContainerAliasFeeConfig,
|
||||||
netmap.EpochDurationConfig, netmap.IrCandidateFeeConfig,
|
netmap.EpochDurationConfig, netmap.IrCandidateFeeConfig,
|
||||||
netmap.MaxObjectSizeConfig, netmap.WithdrawFeeConfig:
|
netmap.MaxObjectSizeConfig, netmap.WithdrawFeeConfig,
|
||||||
|
netmap.MaxECDataCountConfig, netmap.MaxECParityCountConfig:
|
||||||
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)
|
||||||
|
|
34
cmd/frostfs-adm/internal/modules/morph/config/config_test.go
Normal file
34
cmd/frostfs-adm/internal/modules/morph/config/config_test.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ValidateConfig(t *testing.T) {
|
||||||
|
testArgs := make(map[string]any)
|
||||||
|
|
||||||
|
testArgs[netmap.MaxECDataCountConfig] = int64(11)
|
||||||
|
require.Error(t, validateConfig(testArgs, false))
|
||||||
|
|
||||||
|
testArgs[netmap.MaxECParityCountConfig] = int64(256)
|
||||||
|
require.Error(t, validateConfig(testArgs, false))
|
||||||
|
require.NoError(t, validateConfig(testArgs, true))
|
||||||
|
|
||||||
|
testArgs[netmap.MaxECParityCountConfig] = int64(-1)
|
||||||
|
require.Error(t, validateConfig(testArgs, false))
|
||||||
|
|
||||||
|
testArgs[netmap.MaxECParityCountConfig] = int64(55)
|
||||||
|
require.NoError(t, validateConfig(testArgs, false))
|
||||||
|
|
||||||
|
testArgs[netmap.HomomorphicHashingDisabledKey] = "1"
|
||||||
|
require.Error(t, validateConfig(testArgs, false))
|
||||||
|
|
||||||
|
testArgs[netmap.HomomorphicHashingDisabledKey] = true
|
||||||
|
require.NoError(t, validateConfig(testArgs, false))
|
||||||
|
|
||||||
|
testArgs["not-well-known-configuration-key"] = "key"
|
||||||
|
require.NoError(t, validateConfig(testArgs, false))
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ import "time"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ConsensusAccountName = "consensus"
|
ConsensusAccountName = "consensus"
|
||||||
ProtoConfigPath = "protocol"
|
|
||||||
|
|
||||||
// MaxAlphabetNodes is the maximum number of candidates allowed, which is currently limited by the size
|
// MaxAlphabetNodes is the maximum number of candidates allowed, which is currently limited by the size
|
||||||
// of the invocation script.
|
// of the invocation script.
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
|
@ -33,7 +34,7 @@ func getContainerContractHash(cmd *cobra.Command, inv *invoker.Invoker) (util.Ui
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r := management.NewReader(inv)
|
r := management.NewReader(inv)
|
||||||
nnsCs, err := r.GetContractByID(1)
|
nnsCs, err := helper.GetContractByID(r, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err)
|
return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -75,7 +76,7 @@ func dumpContainers(cmd *cobra.Command, _ []string) error {
|
||||||
return fmt.Errorf("invalid filename: %w", err)
|
return fmt.Errorf("invalid filename: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := helper.GetN3Client(viper.GetViper())
|
c, err := helper.NewRemoteClient(viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't create N3 client: %w", err)
|
return fmt.Errorf("can't create N3 client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -138,13 +139,12 @@ func dumpContainers(cmd *cobra.Command, _ []string) error {
|
||||||
func dumpSingleContainer(bw *io.BufBinWriter, ch util.Uint160, inv *invoker.Invoker, id []byte) (*Container, error) {
|
func dumpSingleContainer(bw *io.BufBinWriter, ch util.Uint160, inv *invoker.Invoker, id []byte) (*Container, error) {
|
||||||
bw.Reset()
|
bw.Reset()
|
||||||
emit.AppCall(bw.BinWriter, ch, "get", callflag.All, id)
|
emit.AppCall(bw.BinWriter, ch, "get", callflag.All, id)
|
||||||
emit.AppCall(bw.BinWriter, ch, "eACL", callflag.All, id)
|
|
||||||
res, err := inv.Run(bw.Bytes())
|
res, err := inv.Run(bw.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't get container info: %w", err)
|
return nil, fmt.Errorf("can't get container info: %w", err)
|
||||||
}
|
}
|
||||||
if len(res.Stack) != 2 {
|
if len(res.Stack) != 1 {
|
||||||
return nil, fmt.Errorf("%w: expected 2 items on stack", errInvalidContainerResponse)
|
return nil, fmt.Errorf("%w: expected 1 items on stack", errInvalidContainerResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
cnt := new(Container)
|
cnt := new(Container)
|
||||||
|
@ -153,19 +153,11 @@ func dumpSingleContainer(bw *io.BufBinWriter, ch util.Uint160, inv *invoker.Invo
|
||||||
return nil, fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
return nil, fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ea := new(EACL)
|
|
||||||
err = ea.FromStackItem(res.Stack[1])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
|
||||||
}
|
|
||||||
if len(ea.Value) != 0 {
|
|
||||||
cnt.EACL = ea
|
|
||||||
}
|
|
||||||
return cnt, nil
|
return cnt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func listContainers(cmd *cobra.Command, _ []string) error {
|
func listContainers(cmd *cobra.Command, _ []string) error {
|
||||||
c, err := helper.GetN3Client(viper.GetViper())
|
c, err := helper.NewRemoteClient(viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't create N3 client: %w", err)
|
return fmt.Errorf("can't create N3 client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -257,10 +249,6 @@ func restoreOrPutContainers(containers []Container, isOK func([]byte) bool, cmd
|
||||||
func putContainer(bw *io.BufBinWriter, ch util.Uint160, cnt Container) {
|
func putContainer(bw *io.BufBinWriter, ch util.Uint160, cnt Container) {
|
||||||
emit.AppCall(bw.BinWriter, ch, "put", callflag.All,
|
emit.AppCall(bw.BinWriter, ch, "put", callflag.All,
|
||||||
cnt.Value, cnt.Signature, cnt.PublicKey, cnt.Token)
|
cnt.Value, cnt.Signature, cnt.PublicKey, cnt.Token)
|
||||||
if ea := cnt.EACL; ea != nil {
|
|
||||||
emit.AppCall(bw.BinWriter, ch, "setEACL", callflag.All,
|
|
||||||
ea.Value, ea.Signature, ea.PublicKey, ea.Token)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isContainerRestored(cmd *cobra.Command, wCtx *helper.InitializeContext, containerHash util.Uint160, bw *io.BufBinWriter, hashValue util.Uint256) (bool, error) {
|
func isContainerRestored(cmd *cobra.Command, wCtx *helper.InitializeContext, containerHash util.Uint160, bw *io.BufBinWriter, hashValue util.Uint256) (bool, error) {
|
||||||
|
@ -303,7 +291,7 @@ func parseContainers(filename string) ([]Container, error) {
|
||||||
|
|
||||||
func fetchContainerContractHash(wCtx *helper.InitializeContext) (util.Uint160, error) {
|
func fetchContainerContractHash(wCtx *helper.InitializeContext) (util.Uint160, error) {
|
||||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||||
nnsCs, err := r.GetContractByID(1)
|
nnsCs, err := helper.GetContractByID(r, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err)
|
return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -321,15 +309,6 @@ type Container struct {
|
||||||
Signature []byte `json:"signature"`
|
Signature []byte `json:"signature"`
|
||||||
PublicKey []byte `json:"public_key"`
|
PublicKey []byte `json:"public_key"`
|
||||||
Token []byte `json:"token"`
|
Token []byte `json:"token"`
|
||||||
EACL *EACL `json:"eacl"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// EACL represents extended ACL struct in contract storage.
|
|
||||||
type EACL struct {
|
|
||||||
Value []byte `json:"value"`
|
|
||||||
Signature []byte `json:"signature"`
|
|
||||||
PublicKey []byte `json:"public_key"`
|
|
||||||
Token []byte `json:"token"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToStackItem implements stackitem.Convertible.
|
// ToStackItem implements stackitem.Convertible.
|
||||||
|
@ -376,50 +355,6 @@ func (c *Container) FromStackItem(item stackitem.Item) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToStackItem implements stackitem.Convertible.
|
|
||||||
func (c *EACL) ToStackItem() (stackitem.Item, error) {
|
|
||||||
return stackitem.NewStruct([]stackitem.Item{
|
|
||||||
stackitem.NewByteArray(c.Value),
|
|
||||||
stackitem.NewByteArray(c.Signature),
|
|
||||||
stackitem.NewByteArray(c.PublicKey),
|
|
||||||
stackitem.NewByteArray(c.Token),
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromStackItem implements stackitem.Convertible.
|
|
||||||
func (c *EACL) FromStackItem(item stackitem.Item) error {
|
|
||||||
arr, ok := item.Value().([]stackitem.Item)
|
|
||||||
if !ok || len(arr) != 4 {
|
|
||||||
return errors.New("invalid stack item type")
|
|
||||||
}
|
|
||||||
|
|
||||||
value, err := arr[0].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("invalid eACL value")
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := arr[1].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("invalid eACL signature")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub, err := arr[2].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("invalid eACL public key")
|
|
||||||
}
|
|
||||||
|
|
||||||
tok, err := arr[3].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("invalid eACL token")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Value = value
|
|
||||||
c.Signature = sig
|
|
||||||
c.PublicKey = pub
|
|
||||||
c.Token = tok
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getCIDFilterFunc returns filtering function for container IDs.
|
// getCIDFilterFunc returns filtering function for container IDs.
|
||||||
// Raw byte slices are used because it works with structures returned
|
// Raw byte slices are used because it works with structures returned
|
||||||
// from contract.
|
// from contract.
|
||||||
|
@ -446,7 +381,7 @@ func getCIDFilterFunc(cmd *cobra.Command) (func([]byte) bool, error) {
|
||||||
var id cid.ID
|
var id cid.ID
|
||||||
id.SetSHA256(v)
|
id.SetSHA256(v)
|
||||||
idStr := id.EncodeToString()
|
idStr := id.EncodeToString()
|
||||||
n := sort.Search(len(rawIDs), func(i int) bool { return rawIDs[i] >= idStr })
|
_, found := slices.BinarySearch(rawIDs, idStr)
|
||||||
return n < len(rawIDs) && rawIDs[n] == idStr
|
return found
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ func deployContractCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
r := management.NewReader(c.ReadOnlyInvoker)
|
r := management.NewReader(c.ReadOnlyInvoker)
|
||||||
nnsCs, err := r.GetContractByID(1)
|
nnsCs, err := helper.GetContractByID(r, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't fetch NNS contract state: %w", err)
|
return fmt.Errorf("can't fetch NNS contract state: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,13 +36,13 @@ type contractDumpInfo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func dumpContractHashes(cmd *cobra.Command, _ []string) error {
|
func dumpContractHashes(cmd *cobra.Command, _ []string) error {
|
||||||
c, err := helper.GetN3Client(viper.GetViper())
|
c, err := helper.NewRemoteClient(viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't create N3 client: %w", err)
|
return fmt.Errorf("can't create N3 client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := management.NewReader(invoker.New(c, nil))
|
r := management.NewReader(invoker.New(c, nil))
|
||||||
cs, err := r.GetContractByID(1)
|
cs, err := helper.GetContractByID(r, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ func dumpContractHashes(cmd *cobra.Command, _ []string) error {
|
||||||
|
|
||||||
if irSize != 0 {
|
if irSize != 0 {
|
||||||
bw.Reset()
|
bw.Reset()
|
||||||
for i := 0; i < irSize; i++ {
|
for i := range irSize {
|
||||||
emit.AppCall(bw.BinWriter, cs.Hash, "resolve", callflag.ReadOnly,
|
emit.AppCall(bw.BinWriter, cs.Hash, "resolve", callflag.ReadOnly,
|
||||||
helper.GetAlphabetNNSDomain(i),
|
helper.GetAlphabetNNSDomain(i),
|
||||||
int64(nns.TXT))
|
int64(nns.TXT))
|
||||||
|
@ -79,7 +79,7 @@ func dumpContractHashes(cmd *cobra.Command, _ []string) error {
|
||||||
return fmt.Errorf("can't fetch info from NNS: %w", err)
|
return fmt.Errorf("can't fetch info from NNS: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < irSize; i++ {
|
for i := range irSize {
|
||||||
info := contractDumpInfo{name: fmt.Sprintf("alphabet %d", i)}
|
info := contractDumpInfo{name: fmt.Sprintf("alphabet %d", i)}
|
||||||
if h, err := helper.ParseNNSResolveResult(alphaRes.Stack[i]); err == nil {
|
if h, err := helper.ParseNNSResolveResult(alphaRes.Stack[i]); err == nil {
|
||||||
info.hash = h
|
info.hash = h
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
package frostfsid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
frostfsidAddSubjectKeyCmd = &cobra.Command{
|
||||||
|
Use: "add-subject-key",
|
||||||
|
Short: "Add a public key to the subject in frostfsid contract",
|
||||||
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
||||||
|
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||||
|
},
|
||||||
|
Run: frostfsidAddSubjectKey,
|
||||||
|
}
|
||||||
|
frostfsidRemoveSubjectKeyCmd = &cobra.Command{
|
||||||
|
Use: "remove-subject-key",
|
||||||
|
Short: "Remove a public key from the subject in frostfsid contract",
|
||||||
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
||||||
|
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||||
|
},
|
||||||
|
Run: frostfsidRemoveSubjectKey,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func initFrostfsIDAddSubjectKeyCmd() {
|
||||||
|
Cmd.AddCommand(frostfsidAddSubjectKeyCmd)
|
||||||
|
|
||||||
|
ff := frostfsidAddSubjectKeyCmd.Flags()
|
||||||
|
ff.StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
|
ff.String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||||
|
|
||||||
|
ff.String(subjectAddressFlag, "", "Subject address")
|
||||||
|
_ = frostfsidAddSubjectKeyCmd.MarkFlagRequired(subjectAddressFlag)
|
||||||
|
|
||||||
|
ff.String(subjectKeyFlag, "", "Public key to add")
|
||||||
|
_ = frostfsidAddSubjectKeyCmd.MarkFlagRequired(subjectKeyFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initFrostfsIDRemoveSubjectKeyCmd() {
|
||||||
|
Cmd.AddCommand(frostfsidRemoveSubjectKeyCmd)
|
||||||
|
|
||||||
|
ff := frostfsidRemoveSubjectKeyCmd.Flags()
|
||||||
|
ff.StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
|
ff.String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||||
|
|
||||||
|
ff.String(subjectAddressFlag, "", "Subject address")
|
||||||
|
_ = frostfsidAddSubjectKeyCmd.MarkFlagRequired(subjectAddressFlag)
|
||||||
|
|
||||||
|
ff.String(subjectKeyFlag, "", "Public key to remove")
|
||||||
|
_ = frostfsidAddSubjectKeyCmd.MarkFlagRequired(subjectKeyFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func frostfsidAddSubjectKey(cmd *cobra.Command, _ []string) {
|
||||||
|
addr := getFrostfsIDSubjectAddress(cmd)
|
||||||
|
pub := getFrostfsIDSubjectKey(cmd)
|
||||||
|
|
||||||
|
ffsid, err := newFrostfsIDClient(cmd)
|
||||||
|
commonCmd.ExitOnErr(cmd, "init contract client: %w", err)
|
||||||
|
|
||||||
|
ffsid.addCall(ffsid.roCli.AddSubjectKeyCall(addr, pub))
|
||||||
|
|
||||||
|
err = ffsid.sendWait()
|
||||||
|
commonCmd.ExitOnErr(cmd, "add subject key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func frostfsidRemoveSubjectKey(cmd *cobra.Command, _ []string) {
|
||||||
|
addr := getFrostfsIDSubjectAddress(cmd)
|
||||||
|
pub := getFrostfsIDSubjectKey(cmd)
|
||||||
|
|
||||||
|
ffsid, err := newFrostfsIDClient(cmd)
|
||||||
|
commonCmd.ExitOnErr(cmd, "init contract client: %w", err)
|
||||||
|
|
||||||
|
ffsid.addCall(ffsid.roCli.RemoveSubjectKeyCall(addr, pub))
|
||||||
|
|
||||||
|
err = ffsid.sendWait()
|
||||||
|
commonCmd.ExitOnErr(cmd, "remove subject key: %w", err)
|
||||||
|
}
|
|
@ -1,26 +1,33 @@
|
||||||
package frostfsid
|
package frostfsid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
frostfsidclient "git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
|
frostfsidclient "git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
|
||||||
|
frostfsidrpclient "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/frostfsid"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const iteratorBatchSize = 1
|
||||||
|
|
||||||
const (
|
const (
|
||||||
namespaceFlag = "namespace"
|
namespaceFlag = "namespace"
|
||||||
subjectNameFlag = "subject-name"
|
subjectNameFlag = "subject-name"
|
||||||
|
@ -53,7 +60,6 @@ var (
|
||||||
Use: "list-namespaces",
|
Use: "list-namespaces",
|
||||||
Short: "List all namespaces in frostfsid",
|
Short: "List all namespaces in frostfsid",
|
||||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
|
||||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||||
},
|
},
|
||||||
Run: frostfsidListNamespaces,
|
Run: frostfsidListNamespaces,
|
||||||
|
@ -83,7 +89,6 @@ var (
|
||||||
Use: "list-subjects",
|
Use: "list-subjects",
|
||||||
Short: "List subjects in namespace",
|
Short: "List subjects in namespace",
|
||||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
|
||||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||||
},
|
},
|
||||||
Run: frostfsidListSubjects,
|
Run: frostfsidListSubjects,
|
||||||
|
@ -113,7 +118,6 @@ var (
|
||||||
Use: "list-groups",
|
Use: "list-groups",
|
||||||
Short: "List groups in namespace",
|
Short: "List groups in namespace",
|
||||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
|
||||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||||
},
|
},
|
||||||
Run: frostfsidListGroups,
|
Run: frostfsidListGroups,
|
||||||
|
@ -143,7 +147,6 @@ var (
|
||||||
Use: "list-group-subjects",
|
Use: "list-group-subjects",
|
||||||
Short: "List subjects in group",
|
Short: "List subjects in group",
|
||||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
|
||||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||||
},
|
},
|
||||||
Run: frostfsidListGroupSubjects,
|
Run: frostfsidListGroupSubjects,
|
||||||
|
@ -161,7 +164,6 @@ func initFrostfsIDCreateNamespaceCmd() {
|
||||||
func initFrostfsIDListNamespacesCmd() {
|
func initFrostfsIDListNamespacesCmd() {
|
||||||
Cmd.AddCommand(frostfsidListNamespacesCmd)
|
Cmd.AddCommand(frostfsidListNamespacesCmd)
|
||||||
frostfsidListNamespacesCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
frostfsidListNamespacesCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
frostfsidListNamespacesCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initFrostfsIDCreateSubjectCmd() {
|
func initFrostfsIDCreateSubjectCmd() {
|
||||||
|
@ -185,7 +187,6 @@ func initFrostfsIDListSubjectsCmd() {
|
||||||
frostfsidListSubjectsCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
frostfsidListSubjectsCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
frostfsidListSubjectsCmd.Flags().String(namespaceFlag, "", "Namespace to list subjects")
|
frostfsidListSubjectsCmd.Flags().String(namespaceFlag, "", "Namespace to list subjects")
|
||||||
frostfsidListSubjectsCmd.Flags().Bool(includeNamesFlag, false, "Whether include subject name (require additional requests)")
|
frostfsidListSubjectsCmd.Flags().Bool(includeNamesFlag, false, "Whether include subject name (require additional requests)")
|
||||||
frostfsidListSubjectsCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initFrostfsIDCreateGroupCmd() {
|
func initFrostfsIDCreateGroupCmd() {
|
||||||
|
@ -194,6 +195,7 @@ func initFrostfsIDCreateGroupCmd() {
|
||||||
frostfsidCreateGroupCmd.Flags().String(namespaceFlag, "", "Namespace where create group")
|
frostfsidCreateGroupCmd.Flags().String(namespaceFlag, "", "Namespace where create group")
|
||||||
frostfsidCreateGroupCmd.Flags().String(groupNameFlag, "", "Group name, must be unique in namespace")
|
frostfsidCreateGroupCmd.Flags().String(groupNameFlag, "", "Group name, must be unique in namespace")
|
||||||
frostfsidCreateGroupCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
frostfsidCreateGroupCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||||
|
_ = frostfsidCreateGroupCmd.MarkFlagRequired(groupNameFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initFrostfsIDDeleteGroupCmd() {
|
func initFrostfsIDDeleteGroupCmd() {
|
||||||
|
@ -208,7 +210,6 @@ func initFrostfsIDListGroupsCmd() {
|
||||||
Cmd.AddCommand(frostfsidListGroupsCmd)
|
Cmd.AddCommand(frostfsidListGroupsCmd)
|
||||||
frostfsidListGroupsCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
frostfsidListGroupsCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
frostfsidListGroupsCmd.Flags().String(namespaceFlag, "", "Namespace to list groups")
|
frostfsidListGroupsCmd.Flags().String(namespaceFlag, "", "Namespace to list groups")
|
||||||
frostfsidListGroupsCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initFrostfsIDAddSubjectToGroupCmd() {
|
func initFrostfsIDAddSubjectToGroupCmd() {
|
||||||
|
@ -233,7 +234,6 @@ func initFrostfsIDListGroupSubjectsCmd() {
|
||||||
frostfsidListGroupSubjectsCmd.Flags().String(namespaceFlag, "", "Namespace name")
|
frostfsidListGroupSubjectsCmd.Flags().String(namespaceFlag, "", "Namespace name")
|
||||||
frostfsidListGroupSubjectsCmd.Flags().Int64(groupIDFlag, 0, "Group id")
|
frostfsidListGroupSubjectsCmd.Flags().Int64(groupIDFlag, 0, "Group id")
|
||||||
frostfsidListGroupSubjectsCmd.Flags().Bool(includeNamesFlag, false, "Whether include subject name (require additional requests)")
|
frostfsidListGroupSubjectsCmd.Flags().Bool(includeNamesFlag, false, "Whether include subject name (require additional requests)")
|
||||||
frostfsidListGroupSubjectsCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func frostfsidCreateNamespace(cmd *cobra.Command, _ []string) {
|
func frostfsidCreateNamespace(cmd *cobra.Command, _ []string) {
|
||||||
|
@ -249,12 +249,15 @@ func frostfsidCreateNamespace(cmd *cobra.Command, _ []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func frostfsidListNamespaces(cmd *cobra.Command, _ []string) {
|
func frostfsidListNamespaces(cmd *cobra.Command, _ []string) {
|
||||||
ffsid, err := newFrostfsIDClient(cmd)
|
inv, _, hash := initInvoker(cmd)
|
||||||
commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err)
|
reader := frostfsidrpclient.NewReader(inv, hash)
|
||||||
|
sessionID, it, err := reader.ListNamespaces()
|
||||||
namespaces, err := ffsid.roCli.ListNamespaces()
|
commonCmd.ExitOnErr(cmd, "can't get namespace: %w", err)
|
||||||
commonCmd.ExitOnErr(cmd, "list namespaces: %w", err)
|
items, err := readIterator(inv, &it, iteratorBatchSize, sessionID)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
||||||
|
|
||||||
|
namespaces, err := frostfsidclient.ParseNamespaces(items)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't parse namespace: %w", err)
|
||||||
sort.Slice(namespaces, func(i, j int) bool { return namespaces[i].Name < namespaces[j].Name })
|
sort.Slice(namespaces, func(i, j int) bool { return namespaces[i].Name < namespaces[j].Name })
|
||||||
|
|
||||||
for _, namespace := range namespaces {
|
for _, namespace := range namespaces {
|
||||||
|
@ -295,14 +298,15 @@ func frostfsidDeleteSubject(cmd *cobra.Command, _ []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func frostfsidListSubjects(cmd *cobra.Command, _ []string) {
|
func frostfsidListSubjects(cmd *cobra.Command, _ []string) {
|
||||||
ns := getFrostfsIDNamespace(cmd)
|
|
||||||
includeNames, _ := cmd.Flags().GetBool(includeNamesFlag)
|
includeNames, _ := cmd.Flags().GetBool(includeNamesFlag)
|
||||||
|
ns := getFrostfsIDNamespace(cmd)
|
||||||
|
inv, _, hash := initInvoker(cmd)
|
||||||
|
reader := frostfsidrpclient.NewReader(inv, hash)
|
||||||
|
sessionID, it, err := reader.ListNamespaceSubjects(ns)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't get namespace: %w", err)
|
||||||
|
|
||||||
ffsid, err := newFrostfsIDClient(cmd)
|
subAddresses, err := frostfsidclient.UnwrapArrayOfUint160(readIterator(inv, &it, iteratorBatchSize, sessionID))
|
||||||
commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't unwrap: %w", err)
|
||||||
|
|
||||||
subAddresses, err := ffsid.roCli.ListNamespaceSubjects(ns)
|
|
||||||
commonCmd.ExitOnErr(cmd, "list subjects: %w", err)
|
|
||||||
|
|
||||||
sort.Slice(subAddresses, func(i, j int) bool { return subAddresses[i].Less(subAddresses[j]) })
|
sort.Slice(subAddresses, func(i, j int) bool { return subAddresses[i].Less(subAddresses[j]) })
|
||||||
|
|
||||||
|
@ -312,8 +316,14 @@ func frostfsidListSubjects(cmd *cobra.Command, _ []string) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
subj, err := ffsid.roCli.GetSubject(addr)
|
sessionID, it, err := reader.ListSubjects()
|
||||||
commonCmd.ExitOnErr(cmd, "get subject: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't get subject: %w", err)
|
||||||
|
|
||||||
|
items, err := readIterator(inv, &it, iteratorBatchSize, sessionID)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
||||||
|
|
||||||
|
subj, err := frostfsidclient.ParseSubject(items)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't parse subject: %w", err)
|
||||||
|
|
||||||
cmd.Printf("%s (%s)\n", address.Uint160ToString(addr), subj.Name)
|
cmd.Printf("%s (%s)\n", address.Uint160ToString(addr), subj.Name)
|
||||||
}
|
}
|
||||||
|
@ -348,13 +358,17 @@ func frostfsidDeleteGroup(cmd *cobra.Command, _ []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func frostfsidListGroups(cmd *cobra.Command, _ []string) {
|
func frostfsidListGroups(cmd *cobra.Command, _ []string) {
|
||||||
|
inv, _, hash := initInvoker(cmd)
|
||||||
ns := getFrostfsIDNamespace(cmd)
|
ns := getFrostfsIDNamespace(cmd)
|
||||||
|
|
||||||
ffsid, err := newFrostfsIDClient(cmd)
|
reader := frostfsidrpclient.NewReader(inv, hash)
|
||||||
commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err)
|
sessionID, it, err := reader.ListGroups(ns)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't get namespace: %w", err)
|
||||||
|
|
||||||
groups, err := ffsid.roCli.ListGroups(ns)
|
items, err := readIterator(inv, &it, iteratorBatchSize, sessionID)
|
||||||
commonCmd.ExitOnErr(cmd, "list groups: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't list groups: %w", err)
|
||||||
|
groups, err := frostfsidclient.ParseGroups(items)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't parse groups: %w", err)
|
||||||
|
|
||||||
sort.Slice(groups, func(i, j int) bool { return groups[i].Name < groups[j].Name })
|
sort.Slice(groups, func(i, j int) bool { return groups[i].Name < groups[j].Name })
|
||||||
|
|
||||||
|
@ -393,12 +407,19 @@ func frostfsidListGroupSubjects(cmd *cobra.Command, _ []string) {
|
||||||
ns := getFrostfsIDNamespace(cmd)
|
ns := getFrostfsIDNamespace(cmd)
|
||||||
groupID := getFrostfsIDGroupID(cmd)
|
groupID := getFrostfsIDGroupID(cmd)
|
||||||
includeNames, _ := cmd.Flags().GetBool(includeNamesFlag)
|
includeNames, _ := cmd.Flags().GetBool(includeNamesFlag)
|
||||||
|
inv, cs, hash := initInvoker(cmd)
|
||||||
|
_, err := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.FrostfsIDContract))
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't get netmap contract hash: %w", err)
|
||||||
|
|
||||||
ffsid, err := newFrostfsIDClient(cmd)
|
reader := frostfsidrpclient.NewReader(inv, hash)
|
||||||
commonCmd.ExitOnErr(cmd, "init contract client: %w", err)
|
sessionID, it, err := reader.ListGroupSubjects(ns, big.NewInt(groupID))
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't list groups: %w", err)
|
||||||
|
|
||||||
subjects, err := ffsid.roCli.ListGroupSubjects(ns, groupID)
|
items, err := readIterator(inv, &it, iteratorBatchSize, sessionID)
|
||||||
commonCmd.ExitOnErr(cmd, "list group subjects: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
||||||
|
|
||||||
|
subjects, err := frostfsidclient.UnwrapArrayOfUint160(items, err)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't unwrap: %w", err)
|
||||||
|
|
||||||
sort.Slice(subjects, func(i, j int) bool { return subjects[i].Less(subjects[j]) })
|
sort.Slice(subjects, func(i, j int) bool { return subjects[i].Less(subjects[j]) })
|
||||||
|
|
||||||
|
@ -408,9 +429,10 @@ func frostfsidListGroupSubjects(cmd *cobra.Command, _ []string) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
subj, err := ffsid.roCli.GetSubject(subjAddr)
|
items, err := reader.GetSubject(subjAddr)
|
||||||
commonCmd.ExitOnErr(cmd, "get subject: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't get subject: %w", err)
|
||||||
|
subj, err := frostfsidclient.ParseSubject(items)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't parse subject: %w", err)
|
||||||
cmd.Printf("%s (%s)\n", address.Uint160ToString(subjAddr), subj.Name)
|
cmd.Printf("%s (%s)\n", address.Uint160ToString(subjAddr), subj.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -425,11 +447,11 @@ type frostfsidClient struct {
|
||||||
func newFrostfsIDClient(cmd *cobra.Command) (*frostfsidClient, error) {
|
func newFrostfsIDClient(cmd *cobra.Command) (*frostfsidClient, error) {
|
||||||
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't to initialize context: %w", err)
|
return nil, fmt.Errorf("can't initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||||
cs, err := r.GetContractByID(1)
|
cs, err := helper.GetContractByID(r, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't get NNS contract info: %w", err)
|
return nil, fmt.Errorf("can't get NNS contract info: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -466,10 +488,38 @@ func (f *frostfsidClient) sendWaitRes() (*state.AppExecResult, error) {
|
||||||
}
|
}
|
||||||
f.bw.Reset()
|
f.bw.Reset()
|
||||||
|
|
||||||
if len(f.wCtx.SentTxs) == 0 {
|
|
||||||
return nil, errors.New("no transactions to wait")
|
|
||||||
}
|
|
||||||
|
|
||||||
f.wCtx.Command.Println("Waiting for transactions to persist...")
|
f.wCtx.Command.Println("Waiting for transactions to persist...")
|
||||||
return f.roCli.Wait(f.wCtx.SentTxs[0].Hash, f.wCtx.SentTxs[0].Vub, nil)
|
return f.roCli.Wait(f.wCtx.SentTxs[0].Hash, f.wCtx.SentTxs[0].Vub, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readIterator(inv *invoker.Invoker, iter *result.Iterator, batchSize int, sessionID uuid.UUID) ([]stackitem.Item, error) {
|
||||||
|
var shouldStop bool
|
||||||
|
res := make([]stackitem.Item, 0)
|
||||||
|
for !shouldStop {
|
||||||
|
items, err := inv.TraverseIterator(sessionID, iter, batchSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(res, items...)
|
||||||
|
shouldStop = len(items) < batchSize
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initInvoker(cmd *cobra.Command) (*invoker.Invoker, *state.Contract, util.Uint160) {
|
||||||
|
c, err := helper.NewRemoteClient(viper.GetViper())
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't create N3 client: %w", err)
|
||||||
|
|
||||||
|
inv := invoker.New(c, nil)
|
||||||
|
r := management.NewReader(inv)
|
||||||
|
|
||||||
|
cs, err := r.GetContractByID(1)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract info: %w", err)
|
||||||
|
|
||||||
|
nmHash, err := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.FrostfsIDContract))
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't get netmap contract hash: %w", err)
|
||||||
|
|
||||||
|
return inv, cs, nmHash
|
||||||
|
}
|
||||||
|
|
|
@ -1,59 +1,12 @@
|
||||||
package frostfsid
|
package frostfsid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/ape"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/ape"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFrostfsIDConfig(t *testing.T) {
|
|
||||||
pks := make([]*keys.PrivateKey, 4)
|
|
||||||
for i := range pks {
|
|
||||||
pk, err := keys.NewPrivateKey()
|
|
||||||
require.NoError(t, err)
|
|
||||||
pks[i] = pk
|
|
||||||
}
|
|
||||||
|
|
||||||
fmts := []string{
|
|
||||||
pks[0].GetScriptHash().StringLE(),
|
|
||||||
address.Uint160ToString(pks[1].GetScriptHash()),
|
|
||||||
hex.EncodeToString(pks[2].PublicKey().UncompressedBytes()),
|
|
||||||
hex.EncodeToString(pks[3].PublicKey().Bytes()),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range fmts {
|
|
||||||
v := viper.New()
|
|
||||||
v.Set("frostfsid.admin", fmts[i])
|
|
||||||
|
|
||||||
actual, found, err := helper.GetFrostfsIDAdmin(v)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, found)
|
|
||||||
require.Equal(t, pks[i].GetScriptHash(), actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("bad key", func(t *testing.T) {
|
|
||||||
v := viper.New()
|
|
||||||
v.Set("frostfsid.admin", "abc")
|
|
||||||
|
|
||||||
_, found, err := helper.GetFrostfsIDAdmin(v)
|
|
||||||
require.Error(t, err)
|
|
||||||
require.True(t, found)
|
|
||||||
})
|
|
||||||
t.Run("missing key", func(t *testing.T) {
|
|
||||||
v := viper.New()
|
|
||||||
|
|
||||||
_, found, err := helper.GetFrostfsIDAdmin(v)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.False(t, found)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNamespaceRegexp(t *testing.T) {
|
func TestNamespaceRegexp(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
|
|
|
@ -12,4 +12,6 @@ func init() {
|
||||||
initFrostfsIDAddSubjectToGroupCmd()
|
initFrostfsIDAddSubjectToGroupCmd()
|
||||||
initFrostfsIDRemoveSubjectFromGroupCmd()
|
initFrostfsIDRemoveSubjectFromGroupCmd()
|
||||||
initFrostfsIDListGroupSubjectsCmd()
|
initFrostfsIDListGroupSubjectsCmd()
|
||||||
|
initFrostfsIDAddSubjectKeyCmd()
|
||||||
|
initFrostfsIDRemoveSubjectKeyCmd()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
"git.frostfs.info/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/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
@ -65,41 +64,47 @@ func initializeWallets(v *viper.Viper, walletDir string, size int) ([]string, er
|
||||||
pubs := make(keys.PublicKeys, size)
|
pubs := make(keys.PublicKeys, size)
|
||||||
passwords := make([]string, size)
|
passwords := make([]string, size)
|
||||||
|
|
||||||
|
var errG errgroup.Group
|
||||||
|
|
||||||
for i := range wallets {
|
for i := range wallets {
|
||||||
password, err := config.GetPassword(v, innerring.GlagoliticLetter(i).String())
|
password, err := config.GetPassword(v, innerring.GlagoliticLetter(i).String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't fetch password: %w", err)
|
return nil, fmt.Errorf("can't fetch password: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errG.Go(func() error {
|
||||||
p := filepath.Join(walletDir, innerring.GlagoliticLetter(i).String()+".json")
|
p := filepath.Join(walletDir, innerring.GlagoliticLetter(i).String()+".json")
|
||||||
f, err := os.OpenFile(p, os.O_CREATE, 0o644)
|
f, err := os.OpenFile(p, os.O_CREATE, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't create wallet file: %w", err)
|
return fmt.Errorf("can't create wallet file: %w", err)
|
||||||
}
|
}
|
||||||
if err := f.Close(); err != nil {
|
if err := f.Close(); err != nil {
|
||||||
return nil, fmt.Errorf("can't close wallet file: %w", err)
|
return fmt.Errorf("can't close wallet file: %w", err)
|
||||||
}
|
}
|
||||||
w, err := wallet.NewWallet(p)
|
w, err := wallet.NewWallet(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't create wallet: %w", err)
|
return fmt.Errorf("can't create wallet: %w", err)
|
||||||
}
|
}
|
||||||
if err := w.CreateAccount(constants.SingleAccountName, password); err != nil {
|
if err := w.CreateAccount(constants.SingleAccountName, password); err != nil {
|
||||||
return nil, fmt.Errorf("can't create account: %w", err)
|
return fmt.Errorf("can't create account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
passwords[i] = password
|
passwords[i] = password
|
||||||
wallets[i] = w
|
wallets[i] = w
|
||||||
pubs[i] = w.Accounts[0].PrivateKey().PublicKey()
|
pubs[i] = w.Accounts[0].PrivateKey().PublicKey()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var errG errgroup.Group
|
if err := errG.Wait(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
// Create consensus account with 2*N/3+1 multi-signature.
|
// Create consensus account with 2*N/3+1 multi-signature.
|
||||||
bftCount := smartcontract.GetDefaultHonestNodeCount(size)
|
bftCount := smartcontract.GetDefaultHonestNodeCount(size)
|
||||||
for i := range wallets {
|
for i := range wallets {
|
||||||
i := i
|
|
||||||
ps := pubs.Copy()
|
ps := pubs.Copy()
|
||||||
errG.Go(func() error {
|
errG.Go(func() error {
|
||||||
if err := addMultisigAccount(wallets[i], majCount, constants.CommitteeAccountName, passwords[i], ps); err != nil {
|
if err := addMultisigAccount(wallets[i], majCount, constants.CommitteeAccountName, passwords[i], ps); err != nil {
|
||||||
|
@ -135,41 +140,11 @@ func addMultisigAccount(w *wallet.Wallet, m int, name, password string, pubs key
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateStorageCreds(cmd *cobra.Command, _ []string) error {
|
func generateStorageCreds(cmd *cobra.Command, _ []string) error {
|
||||||
return refillGas(cmd, storageGasConfigFlag, true)
|
walletPath, _ := cmd.Flags().GetString(commonflags.StorageWalletFlag)
|
||||||
}
|
w, err := wallet.NewWallet(walletPath)
|
||||||
|
|
||||||
func refillGas(cmd *cobra.Command, gasFlag string, createWallet bool) (err error) {
|
|
||||||
// storage wallet path is not part of the config
|
|
||||||
storageWalletPath, _ := cmd.Flags().GetString(commonflags.StorageWalletFlag)
|
|
||||||
// wallet address is not part of the config
|
|
||||||
walletAddress, _ := cmd.Flags().GetString(walletAddressFlag)
|
|
||||||
|
|
||||||
var gasReceiver util.Uint160
|
|
||||||
|
|
||||||
if len(walletAddress) != 0 {
|
|
||||||
gasReceiver, err = address.StringToUint160(walletAddress)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid wallet address %s: %w", walletAddress, err)
|
return fmt.Errorf("create wallet: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if storageWalletPath == "" {
|
|
||||||
return fmt.Errorf("missing wallet path (use '--%s <out.json>')", commonflags.StorageWalletFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
var w *wallet.Wallet
|
|
||||||
|
|
||||||
if createWallet {
|
|
||||||
w, err = wallet.NewWallet(storageWalletPath)
|
|
||||||
} else {
|
|
||||||
w, err = wallet.NewWalletFromFile(storageWalletPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't create wallet: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if createWallet {
|
|
||||||
var password string
|
|
||||||
|
|
||||||
label, _ := cmd.Flags().GetString(storageWalletLabelFlag)
|
label, _ := cmd.Flags().GetString(storageWalletLabelFlag)
|
||||||
password, err := config.GetStoragePassword(viper.GetViper(), label)
|
password, err := config.GetStoragePassword(viper.GetViper(), label)
|
||||||
|
@ -184,11 +159,10 @@ func refillGas(cmd *cobra.Command, gasFlag string, createWallet bool) (err error
|
||||||
if err := w.CreateAccount(label, password); err != nil {
|
if err := w.CreateAccount(label, password); err != nil {
|
||||||
return fmt.Errorf("can't create account: %w", err)
|
return fmt.Errorf("can't create account: %w", err)
|
||||||
}
|
}
|
||||||
|
return refillGas(cmd, storageGasConfigFlag, w.Accounts[0].ScriptHash())
|
||||||
}
|
}
|
||||||
|
|
||||||
gasReceiver = w.Accounts[0].Contract.ScriptHash()
|
func refillGas(cmd *cobra.Command, gasFlag string, gasReceivers ...util.Uint160) (err error) {
|
||||||
}
|
|
||||||
|
|
||||||
gasStr := viper.GetString(gasFlag)
|
gasStr := viper.GetString(gasFlag)
|
||||||
|
|
||||||
gasAmount, err := helper.ParseGASAmount(gasStr)
|
gasAmount, err := helper.ParseGASAmount(gasStr)
|
||||||
|
@ -202,9 +176,11 @@ func refillGas(cmd *cobra.Command, gasFlag string, createWallet bool) (err error
|
||||||
}
|
}
|
||||||
|
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
|
for _, gasReceiver := range gasReceivers {
|
||||||
emit.AppCall(bw.BinWriter, gas.Hash, "transfer", callflag.All,
|
emit.AppCall(bw.BinWriter, gas.Hash, "transfer", callflag.All,
|
||||||
wCtx.CommitteeAcc.Contract.ScriptHash(), gasReceiver, int64(gasAmount), nil)
|
wCtx.CommitteeAcc.Contract.ScriptHash(), gasReceiver, int64(gasAmount), nil)
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
|
}
|
||||||
if bw.Err != nil {
|
if bw.Err != nil {
|
||||||
return fmt.Errorf("BUG: invalid transfer arguments: %w", bw.Err)
|
return fmt.Errorf("BUG: invalid transfer arguments: %w", bw.Err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerateAlphabet(t *testing.T) {
|
func TestGenerateAlphabet(t *testing.T) {
|
||||||
const size = 4
|
|
||||||
|
|
||||||
walletDir := t.TempDir()
|
walletDir := t.TempDir()
|
||||||
buf := setupTestTerminal(t)
|
buf := setupTestTerminal(t)
|
||||||
|
|
||||||
|
@ -55,17 +53,17 @@ func TestGenerateAlphabet(t *testing.T) {
|
||||||
t.Run("no password for contract group wallet", func(t *testing.T) {
|
t.Run("no password for contract group wallet", func(t *testing.T) {
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
v.Set(commonflags.AlphabetWalletsFlag, walletDir)
|
v.Set(commonflags.AlphabetWalletsFlag, walletDir)
|
||||||
require.NoError(t, cmd.Flags().Set(commonflags.AlphabetSizeFlag, strconv.FormatUint(size, 10)))
|
require.NoError(t, cmd.Flags().Set(commonflags.AlphabetSizeFlag, "1"))
|
||||||
for i := uint64(0); i < size; i++ {
|
buf.WriteString("pass\r")
|
||||||
buf.WriteString(strconv.FormatUint(i, 10) + "\r")
|
|
||||||
}
|
|
||||||
require.Error(t, AlphabetCreds(cmd, nil))
|
require.Error(t, AlphabetCreds(cmd, nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const size = 4
|
||||||
|
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
v.Set(commonflags.AlphabetWalletsFlag, walletDir)
|
v.Set(commonflags.AlphabetWalletsFlag, walletDir)
|
||||||
require.NoError(t, GenerateAlphabetCmd.Flags().Set(commonflags.AlphabetSizeFlag, strconv.FormatUint(size, 10)))
|
require.NoError(t, GenerateAlphabetCmd.Flags().Set(commonflags.AlphabetSizeFlag, strconv.FormatUint(size, 10)))
|
||||||
for i := uint64(0); i < size; i++ {
|
for i := range uint64(size) {
|
||||||
buf.WriteString(strconv.FormatUint(i, 10) + "\r")
|
buf.WriteString(strconv.FormatUint(i, 10) + "\r")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
package generate
|
package generate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
@ -33,7 +38,27 @@ var (
|
||||||
_ = viper.BindPFlag(commonflags.RefillGasAmountFlag, cmd.Flags().Lookup(commonflags.RefillGasAmountFlag))
|
_ = viper.BindPFlag(commonflags.RefillGasAmountFlag, cmd.Flags().Lookup(commonflags.RefillGasAmountFlag))
|
||||||
},
|
},
|
||||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
return refillGas(cmd, commonflags.RefillGasAmountFlag, false)
|
storageWalletPaths, _ := cmd.Flags().GetStringArray(commonflags.StorageWalletFlag)
|
||||||
|
walletAddresses, _ := cmd.Flags().GetStringArray(walletAddressFlag)
|
||||||
|
|
||||||
|
var gasReceivers []util.Uint160
|
||||||
|
for _, walletAddress := range walletAddresses {
|
||||||
|
addr, err := address.StringToUint160(walletAddress)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid wallet address %s: %w", walletAddress, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gasReceivers = append(gasReceivers, addr)
|
||||||
|
}
|
||||||
|
for _, storageWalletPath := range storageWalletPaths {
|
||||||
|
w, err := wallet.NewWalletFromFile(storageWalletPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't create wallet: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gasReceivers = append(gasReceivers, w.Accounts[0].Contract.ScriptHash())
|
||||||
|
}
|
||||||
|
return refillGas(cmd, commonflags.RefillGasAmountFlag, gasReceivers...)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
GenerateAlphabetCmd = &cobra.Command{
|
GenerateAlphabetCmd = &cobra.Command{
|
||||||
|
@ -50,10 +75,10 @@ var (
|
||||||
func initRefillGasCmd() {
|
func initRefillGasCmd() {
|
||||||
RefillGasCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
RefillGasCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||||
RefillGasCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
RefillGasCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
RefillGasCmd.Flags().String(commonflags.StorageWalletFlag, "", "Path to storage node wallet")
|
RefillGasCmd.Flags().StringArray(commonflags.StorageWalletFlag, nil, "Path to storage node wallet")
|
||||||
RefillGasCmd.Flags().String(walletAddressFlag, "", "Address of wallet")
|
RefillGasCmd.Flags().StringArray(walletAddressFlag, nil, "Address of wallet")
|
||||||
RefillGasCmd.Flags().String(commonflags.RefillGasAmountFlag, "", "Additional amount of GAS to transfer")
|
RefillGasCmd.Flags().String(commonflags.RefillGasAmountFlag, "", "Additional amount of GAS to transfer")
|
||||||
RefillGasCmd.MarkFlagsMutuallyExclusive(walletAddressFlag, commonflags.StorageWalletFlag)
|
RefillGasCmd.MarkFlagsOneRequired(walletAddressFlag, commonflags.StorageWalletFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initGenerateStorageCmd() {
|
func initGenerateStorageCmd() {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
@ -26,34 +25,26 @@ type LocalActor struct {
|
||||||
neoActor *actor.Actor
|
neoActor *actor.Actor
|
||||||
accounts []*wallet.Account
|
accounts []*wallet.Account
|
||||||
Invoker *invoker.Invoker
|
Invoker *invoker.Invoker
|
||||||
|
rpcInvoker invoker.RPCInvoke
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLocalActor create LocalActor with accounts form provided wallets.
|
// NewLocalActor create LocalActor with accounts form provided wallets.
|
||||||
// In case of empty wallets provided created actor with dummy account only for read operation.
|
// In case of empty wallets provided created actor with dummy account only for read operation.
|
||||||
func NewLocalActor(cmd *cobra.Command, c actor.RPCActor) (*LocalActor, error) {
|
//
|
||||||
|
// If wallets are provided, the contract client will use accounts with accName name from these wallets.
|
||||||
|
// To determine which account name should be used in a contract client, refer to how the contract
|
||||||
|
// verifies the transaction signature.
|
||||||
|
func NewLocalActor(cmd *cobra.Command, c actor.RPCActor, accName string) (*LocalActor, error) {
|
||||||
walletDir := config.ResolveHomePath(viper.GetString(commonflags.AlphabetWalletsFlag))
|
walletDir := config.ResolveHomePath(viper.GetString(commonflags.AlphabetWalletsFlag))
|
||||||
var act *actor.Actor
|
var act *actor.Actor
|
||||||
var accounts []*wallet.Account
|
var accounts []*wallet.Account
|
||||||
if walletDir == "" {
|
|
||||||
account, err := wallet.NewAccount()
|
|
||||||
commonCmd.ExitOnErr(cmd, "unable to create dummy account: %w", err)
|
|
||||||
act, err = actor.New(c, []actor.SignerAccount{{
|
|
||||||
Signer: transaction.Signer{
|
|
||||||
Account: account.Contract.ScriptHash(),
|
|
||||||
Scopes: transaction.Global,
|
|
||||||
},
|
|
||||||
Account: account,
|
|
||||||
}})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wallets, err := GetAlphabetWallets(viper.GetViper(), walletDir)
|
wallets, err := GetAlphabetWallets(viper.GetViper(), walletDir)
|
||||||
commonCmd.ExitOnErr(cmd, "unable to get alphabet wallets: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to get alphabet wallets: %w", err)
|
||||||
|
|
||||||
for _, w := range wallets {
|
for _, w := range wallets {
|
||||||
acc, err := GetWalletAccount(w, constants.CommitteeAccountName)
|
acc, err := GetWalletAccount(w, accName)
|
||||||
commonCmd.ExitOnErr(cmd, "can't find committee account: %w", err)
|
commonCmd.ExitOnErr(cmd, fmt.Sprintf("can't find %s account: %%w", accName), err)
|
||||||
accounts = append(accounts, acc)
|
accounts = append(accounts, acc)
|
||||||
}
|
}
|
||||||
act, err = actor.New(c, []actor.SignerAccount{{
|
act, err = actor.New(c, []actor.SignerAccount{{
|
||||||
|
@ -66,11 +57,11 @@ func NewLocalActor(cmd *cobra.Command, c actor.RPCActor) (*LocalActor, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return &LocalActor{
|
return &LocalActor{
|
||||||
neoActor: act,
|
neoActor: act,
|
||||||
accounts: accounts,
|
accounts: accounts,
|
||||||
Invoker: &act.Invoker,
|
Invoker: &act.Invoker,
|
||||||
|
rpcInvoker: c,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,3 +158,7 @@ func (a *LocalActor) MakeUnsignedRun(_ []byte, _ []transaction.Attribute) (*tran
|
||||||
func (a *LocalActor) MakeCall(_ util.Uint160, _ string, _ ...any) (*transaction.Transaction, error) {
|
func (a *LocalActor) MakeCall(_ util.Uint160, _ string, _ ...any) (*transaction.Transaction, error) {
|
||||||
panic("unimplemented")
|
panic("unimplemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *LocalActor) GetRPCInvoker() invoker.RPCInvoke {
|
||||||
|
return a.rpcInvoker
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
|
|
||||||
func getFrostfsIDAdminFromContract(roInvoker *invoker.Invoker) (util.Uint160, bool, error) {
|
func getFrostfsIDAdminFromContract(roInvoker *invoker.Invoker) (util.Uint160, bool, error) {
|
||||||
r := management.NewReader(roInvoker)
|
r := management.NewReader(roInvoker)
|
||||||
cs, err := r.GetContractByID(1)
|
cs, err := GetContractByID(r, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.Uint160{}, false, fmt.Errorf("get nns contract: %w", err)
|
return util.Uint160{}, false, fmt.Errorf("get nns contract: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ func GetContractDeployData(c *InitializeContext, ctrName string, keysParam []any
|
||||||
// In case if NNS is updated multiple times, we can't calculate
|
// In case if NNS is updated multiple times, we can't calculate
|
||||||
// it's actual hash based on local data, thus query chain.
|
// it's actual hash based on local data, thus query chain.
|
||||||
r := management.NewReader(c.ReadOnlyInvoker)
|
r := management.NewReader(c.ReadOnlyInvoker)
|
||||||
nnsCs, err := r.GetContractByID(1)
|
nnsCs, err := GetContractByID(r, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("get nns contract: %w", err)
|
return nil, fmt.Errorf("get nns contract: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ func GetContractDeployData(c *InitializeContext, ctrName string, keysParam []any
|
||||||
h, found, err = getFrostfsIDAdminFromContract(c.ReadOnlyInvoker)
|
h, found, err = getFrostfsIDAdminFromContract(c.ReadOnlyInvoker)
|
||||||
}
|
}
|
||||||
if method != constants.UpdateMethodName || err == nil && !found {
|
if method != constants.UpdateMethodName || err == nil && !found {
|
||||||
h, found, err = GetFrostfsIDAdmin(viper.GetViper())
|
h, found, err = getFrostfsIDAdmin(viper.GetViper())
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -166,5 +166,6 @@ func DeployNNS(c *InitializeContext, method string) error {
|
||||||
return fmt.Errorf("can't send deploy transaction: %w", err)
|
return fmt.Errorf("can't send deploy transaction: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.Command.Println("NNS hash:", invokeHash.StringLE())
|
||||||
return c.AwaitTx()
|
return c.AwaitTx()
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
const frostfsIDAdminConfigKey = "frostfsid.admin"
|
const frostfsIDAdminConfigKey = "frostfsid.admin"
|
||||||
|
|
||||||
func GetFrostfsIDAdmin(v *viper.Viper) (util.Uint160, bool, error) {
|
func getFrostfsIDAdmin(v *viper.Viper) (util.Uint160, bool, error) {
|
||||||
admin := v.GetString(frostfsIDAdminConfigKey)
|
admin := v.GetString(frostfsIDAdminConfigKey)
|
||||||
if admin == "" {
|
if admin == "" {
|
||||||
return util.Uint160{}, false, nil
|
return util.Uint160{}, false, nil
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFrostfsIDConfig(t *testing.T) {
|
||||||
|
pks := make([]*keys.PrivateKey, 4)
|
||||||
|
for i := range pks {
|
||||||
|
pk, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
pks[i] = pk
|
||||||
|
}
|
||||||
|
|
||||||
|
fmts := []string{
|
||||||
|
pks[0].GetScriptHash().StringLE(),
|
||||||
|
address.Uint160ToString(pks[1].GetScriptHash()),
|
||||||
|
hex.EncodeToString(pks[2].PublicKey().UncompressedBytes()),
|
||||||
|
hex.EncodeToString(pks[3].PublicKey().Bytes()),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range fmts {
|
||||||
|
v := viper.New()
|
||||||
|
v.Set("frostfsid.admin", fmts[i])
|
||||||
|
|
||||||
|
actual, found, err := getFrostfsIDAdmin(v)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, found)
|
||||||
|
require.Equal(t, pks[i].GetScriptHash(), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("bad key", func(t *testing.T) {
|
||||||
|
v := viper.New()
|
||||||
|
v.Set("frostfsid.admin", "abc")
|
||||||
|
|
||||||
|
_, found, err := getFrostfsIDAdmin(v)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.True(t, found)
|
||||||
|
})
|
||||||
|
t.Run("missing key", func(t *testing.T) {
|
||||||
|
v := viper.New()
|
||||||
|
|
||||||
|
_, found, err := getFrostfsIDAdmin(v)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, found)
|
||||||
|
})
|
||||||
|
}
|
|
@ -134,12 +134,12 @@ func NewInitializeContext(cmd *cobra.Command, v *viper.Viper) (*InitializeContex
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
accounts, err := createWalletAccounts(wallets)
|
accounts, err := getSingleAccounts(wallets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cliCtx, err := DefaultClientContext(c, committeeAcc)
|
cliCtx, err := defaultClientContext(c, committeeAcc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("client context: %w", err)
|
return nil, fmt.Errorf("client context: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,7 @@ func createClient(cmd *cobra.Command, v *viper.Viper, wallets []*wallet.Wallet)
|
||||||
}
|
}
|
||||||
c, err = NewLocalClient(cmd, v, wallets, ldf.Value.String())
|
c, err = NewLocalClient(cmd, v, wallets, ldf.Value.String())
|
||||||
} else {
|
} else {
|
||||||
c, err = GetN3Client(v)
|
c, err = NewRemoteClient(v)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't create N3 client: %w", err)
|
return nil, fmt.Errorf("can't create N3 client: %w", err)
|
||||||
|
@ -211,7 +211,7 @@ func getContractsPath(cmd *cobra.Command, needContracts bool) (string, error) {
|
||||||
return ctrPath, nil
|
return ctrPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createWalletAccounts(wallets []*wallet.Wallet) ([]*wallet.Account, error) {
|
func getSingleAccounts(wallets []*wallet.Wallet) ([]*wallet.Account, error) {
|
||||||
accounts := make([]*wallet.Account, len(wallets))
|
accounts := make([]*wallet.Account, len(wallets))
|
||||||
for i, w := range wallets {
|
for i, w := range wallets {
|
||||||
acc, err := GetWalletAccount(w, constants.SingleAccountName)
|
acc, err := GetWalletAccount(w, constants.SingleAccountName)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
|
@ -44,11 +45,10 @@ type LocalClient struct {
|
||||||
transactions []*transaction.Transaction
|
transactions []*transaction.Transaction
|
||||||
dumpPath string
|
dumpPath string
|
||||||
accounts []*wallet.Account
|
accounts []*wallet.Account
|
||||||
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, dumpPath string) (*LocalClient, error) {
|
||||||
cfg, err := config.LoadFile(v.GetString(constants.ProtoConfigPath))
|
cfg, err := config.LoadFile(v.GetString(commonflags.ProtoConfigPath))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -58,35 +58,30 @@ func NewLocalClient(cmd *cobra.Command, v *viper.Viper, wallets []*wallet.Wallet
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m := smartcontract.GetDefaultHonestNodeCount(int(cfg.ProtocolConfiguration.ValidatorsCount))
|
go bc.Run()
|
||||||
accounts := make([]*wallet.Account, len(wallets))
|
|
||||||
for i := range accounts {
|
accounts, err := getBlockSigningAccounts(cfg.ProtocolConfiguration, wallets)
|
||||||
accounts[i], err = GetWalletAccount(wallets[i], constants.ConsensusAccountName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
indexMap := make(map[string]int)
|
|
||||||
for i, pub := range cfg.ProtocolConfiguration.StandbyCommittee {
|
|
||||||
indexMap[pub] = i
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(accounts, func(i, j int) bool {
|
|
||||||
pi := accounts[i].PrivateKey().PublicKey().Bytes()
|
|
||||||
pj := accounts[j].PrivateKey().PublicKey().Bytes()
|
|
||||||
return indexMap[string(pi)] < indexMap[string(pj)]
|
|
||||||
})
|
|
||||||
sort.Slice(accounts[:cfg.ProtocolConfiguration.ValidatorsCount], func(i, j int) bool {
|
|
||||||
return accounts[i].PublicKey().Cmp(accounts[j].PublicKey()) == -1
|
|
||||||
})
|
|
||||||
|
|
||||||
go bc.Run()
|
|
||||||
|
|
||||||
if cmd.Name() != "init" {
|
if cmd.Name() != "init" {
|
||||||
|
if err := restoreDump(bc, dumpPath); err != nil {
|
||||||
|
return nil, fmt.Errorf("restore dump: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &LocalClient{
|
||||||
|
bc: bc,
|
||||||
|
dumpPath: dumpPath,
|
||||||
|
accounts: accounts,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreDump(bc *core.Blockchain, dumpPath string) error {
|
||||||
f, err := os.OpenFile(dumpPath, os.O_RDONLY, 0o600)
|
f, err := os.OpenFile(dumpPath, os.O_RDONLY, 0o600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't open local dump: %w", err)
|
return fmt.Errorf("can't open local dump: %w", err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
|
@ -99,23 +94,44 @@ func NewLocalClient(cmd *cobra.Command, v *viper.Viper, wallets []*wallet.Wallet
|
||||||
|
|
||||||
count := r.ReadU32LE() - skip
|
count := r.ReadU32LE() - skip
|
||||||
if err := chaindump.Restore(bc, r, skip, count, nil); err != nil {
|
if err := chaindump.Restore(bc, r, skip, count, nil); err != nil {
|
||||||
return nil, fmt.Errorf("can't restore local dump: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return &LocalClient{
|
func getBlockSigningAccounts(cfg config.ProtocolConfiguration, wallets []*wallet.Wallet) ([]*wallet.Account, error) {
|
||||||
bc: bc,
|
accounts := make([]*wallet.Account, len(wallets))
|
||||||
dumpPath: dumpPath,
|
for i := range accounts {
|
||||||
accounts: accounts[:m],
|
acc, err := GetWalletAccount(wallets[i], constants.ConsensusAccountName)
|
||||||
maxGasInvoke: 15_0000_0000,
|
if err != nil {
|
||||||
}, nil
|
return nil, err
|
||||||
|
}
|
||||||
|
accounts[i] = acc
|
||||||
|
}
|
||||||
|
|
||||||
|
indexMap := make(map[string]int)
|
||||||
|
for i, pub := range cfg.StandbyCommittee {
|
||||||
|
indexMap[pub] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(accounts, func(i, j int) bool {
|
||||||
|
pi := accounts[i].PrivateKey().PublicKey().Bytes()
|
||||||
|
pj := accounts[j].PrivateKey().PublicKey().Bytes()
|
||||||
|
return indexMap[string(pi)] < indexMap[string(pj)]
|
||||||
|
})
|
||||||
|
sort.Slice(accounts[:cfg.ValidatorsCount], func(i, j int) bool {
|
||||||
|
return accounts[i].PublicKey().Cmp(accounts[j].PublicKey()) == -1
|
||||||
|
})
|
||||||
|
|
||||||
|
m := smartcontract.GetDefaultHonestNodeCount(int(cfg.ValidatorsCount))
|
||||||
|
return accounts[:m], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LocalClient) GetBlockCount() (uint32, error) {
|
func (l *LocalClient) GetBlockCount() (uint32, error) {
|
||||||
return l.bc.BlockHeight(), nil
|
return l.bc.BlockHeight(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LocalClient) GetNativeContracts() ([]state.NativeContract, error) {
|
func (l *LocalClient) GetNativeContracts() ([]state.Contract, error) {
|
||||||
return l.bc.GetNatives(), nil
|
return l.bc.GetNatives(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,11 +145,6 @@ func (l *LocalClient) GetApplicationLog(h util.Uint256, t *trigger.Type) (*resul
|
||||||
return &a, nil
|
return &a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LocalClient) GetCommittee() (keys.PublicKeys, error) {
|
|
||||||
// not used by `morph init` command
|
|
||||||
panic("unexpected call")
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvokeFunction is implemented via `InvokeScript`.
|
// InvokeFunction is implemented via `InvokeScript`.
|
||||||
func (l *LocalClient) InvokeFunction(h util.Uint160, method string, sPrm []smartcontract.Parameter, ss []transaction.Signer) (*result.Invoke, error) {
|
func (l *LocalClient) InvokeFunction(h util.Uint160, method string, sPrm []smartcontract.Parameter, ss []transaction.Signer) (*result.Invoke, error) {
|
||||||
var err error
|
var err error
|
||||||
|
@ -226,7 +237,7 @@ func (l *LocalClient) CalculateNetworkFee(tx *transaction.Transaction) (int64, e
|
||||||
paramz = []manifest.Parameter{{Type: smartcontract.SignatureType}}
|
paramz = []manifest.Parameter{{Type: smartcontract.SignatureType}}
|
||||||
} else if nSigs, _, ok := vm.ParseMultiSigContract(w.VerificationScript); ok {
|
} else if nSigs, _, ok := vm.ParseMultiSigContract(w.VerificationScript); ok {
|
||||||
paramz = make([]manifest.Parameter, nSigs)
|
paramz = make([]manifest.Parameter, nSigs)
|
||||||
for j := 0; j < nSigs; j++ {
|
for j := range nSigs {
|
||||||
paramz[j] = manifest.Parameter{Type: smartcontract.SignatureType}
|
paramz[j] = manifest.Parameter{Type: smartcontract.SignatureType}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,13 +308,7 @@ func (l *LocalClient) InvokeScript(script []byte, signers []transaction.Signer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LocalClient) SendRawTransaction(tx *transaction.Transaction) (util.Uint256, error) {
|
func (l *LocalClient) SendRawTransaction(tx *transaction.Transaction) (util.Uint256, error) {
|
||||||
// We need to test that transaction was formed correctly to catch as many errors as we can.
|
tx = tx.Copy()
|
||||||
bs := tx.Bytes()
|
|
||||||
_, err := transaction.NewTransactionFromBytes(bs)
|
|
||||||
if err != nil {
|
|
||||||
return tx.Hash(), fmt.Errorf("invalid transaction: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.transactions = append(l.transactions, tx)
|
l.transactions = append(l.transactions, tx)
|
||||||
return tx.Hash(), nil
|
return tx.Hash(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package helper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
@ -9,7 +10,6 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||||
"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"
|
||||||
|
@ -24,15 +24,10 @@ import (
|
||||||
// Client represents N3 client interface capable of test-invoking scripts
|
// Client represents N3 client interface capable of test-invoking scripts
|
||||||
// and sending signed transactions to chain.
|
// and sending signed transactions to chain.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
invoker.RPCInvoke
|
actor.RPCActor
|
||||||
|
|
||||||
GetBlockCount() (uint32, error)
|
GetNativeContracts() ([]state.Contract, error)
|
||||||
GetNativeContracts() ([]state.NativeContract, error)
|
|
||||||
GetApplicationLog(util.Uint256, *trigger.Type) (*result.ApplicationLog, error)
|
GetApplicationLog(util.Uint256, *trigger.Type) (*result.ApplicationLog, error)
|
||||||
GetVersion() (*result.Version, error)
|
|
||||||
SendRawTransaction(*transaction.Transaction) (util.Uint256, error)
|
|
||||||
GetCommittee() (keys.PublicKeys, error)
|
|
||||||
CalculateNetworkFee(tx *transaction.Transaction) (int64, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HashVUBPair struct {
|
type HashVUBPair struct {
|
||||||
|
@ -47,7 +42,7 @@ type ClientContext struct {
|
||||||
SentTxs []HashVUBPair
|
SentTxs []HashVUBPair
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetN3Client(v *viper.Viper) (Client, error) {
|
func NewRemoteClient(v *viper.Viper) (Client, error) {
|
||||||
// number of opened connections
|
// number of opened connections
|
||||||
// by neo-go client per one host
|
// by neo-go client per one host
|
||||||
const (
|
const (
|
||||||
|
@ -60,9 +55,23 @@ func GetN3Client(v *viper.Viper) (Client, error) {
|
||||||
if endpoint == "" {
|
if endpoint == "" {
|
||||||
return nil, errors.New("missing endpoint")
|
return nil, errors.New("missing endpoint")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cfg *tls.Config
|
||||||
|
if rootCAs := v.GetStringSlice("tls.trusted_ca_list"); len(rootCAs) != 0 {
|
||||||
|
certFile := v.GetString("tls.certificate")
|
||||||
|
keyFile := v.GetString("tls.key")
|
||||||
|
|
||||||
|
tlsConfig, err := rpcclient.TLSClientConfig(rootCAs, certFile, keyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg = tlsConfig
|
||||||
|
}
|
||||||
c, err := rpcclient.New(ctx, endpoint, rpcclient.Options{
|
c, err := rpcclient.New(ctx, endpoint, rpcclient.Options{
|
||||||
MaxConnsPerHost: maxConnsPerHost,
|
MaxConnsPerHost: maxConnsPerHost,
|
||||||
RequestTimeout: requestTimeout,
|
RequestTimeout: requestTimeout,
|
||||||
|
TLSClientConfig: cfg,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -73,8 +82,14 @@ func GetN3Client(v *viper.Viper) (Client, error) {
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultClientContext(c Client, committeeAcc *wallet.Account) (*ClientContext, error) {
|
func defaultClientContext(c Client, committeeAcc *wallet.Account) (*ClientContext, error) {
|
||||||
commAct, err := NewActor(c, committeeAcc)
|
commAct, err := actor.New(c, []actor.SignerAccount{{
|
||||||
|
Signer: transaction.Signer{
|
||||||
|
Account: committeeAcc.Contract.ScriptHash(),
|
||||||
|
Scopes: transaction.Global,
|
||||||
|
},
|
||||||
|
Account: committeeAcc,
|
||||||
|
}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@ func GetDefaultNetmapContractConfigMap() map[string]any {
|
||||||
m := make(map[string]any)
|
m := make(map[string]any)
|
||||||
m[netmap.EpochDurationConfig] = viper.GetInt64(commonflags.EpochDurationInitFlag)
|
m[netmap.EpochDurationConfig] = viper.GetInt64(commonflags.EpochDurationInitFlag)
|
||||||
m[netmap.MaxObjectSizeConfig] = viper.GetInt64(commonflags.MaxObjectSizeInitFlag)
|
m[netmap.MaxObjectSizeConfig] = viper.GetInt64(commonflags.MaxObjectSizeInitFlag)
|
||||||
|
m[netmap.MaxECDataCountConfig] = viper.GetInt64(commonflags.MaxECDataCountFlag)
|
||||||
|
m[netmap.MaxECParityCountConfig] = viper.GetInt64(commonflags.MaxECParityCounFlag)
|
||||||
m[netmap.ContainerFeeConfig] = viper.GetInt64(commonflags.ContainerFeeInitFlag)
|
m[netmap.ContainerFeeConfig] = viper.GetInt64(commonflags.ContainerFeeInitFlag)
|
||||||
m[netmap.ContainerAliasFeeConfig] = viper.GetInt64(commonflags.ContainerAliasFeeInitFlag)
|
m[netmap.ContainerAliasFeeConfig] = viper.GetInt64(commonflags.ContainerAliasFeeInitFlag)
|
||||||
m[netmap.IrCandidateFeeConfig] = viper.GetInt64(commonflags.CandidateFeeInitFlag)
|
m[netmap.IrCandidateFeeConfig] = viper.GetInt64(commonflags.CandidateFeeInitFlag)
|
||||||
|
@ -70,13 +72,17 @@ func InvalidConfigValueErr(key string) error {
|
||||||
return fmt.Errorf("invalid %s config value from netmap contract", key)
|
return fmt.Errorf("invalid %s config value from netmap contract", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func EmitNewEpochCall(bw *io.BufBinWriter, wCtx *InitializeContext, nmHash util.Uint160) error {
|
func EmitNewEpochCall(bw *io.BufBinWriter, wCtx *InitializeContext, nmHash util.Uint160, countEpoch int64) error {
|
||||||
|
if countEpoch <= 0 {
|
||||||
|
return errors.New("number of epochs cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
curr, err := unwrap.Int64(wCtx.ReadOnlyInvoker.Call(nmHash, "epoch"))
|
curr, err := unwrap.Int64(wCtx.ReadOnlyInvoker.Call(nmHash, "epoch"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("can't fetch current epoch from the netmap contract")
|
return errors.New("can't fetch current epoch from the netmap contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
newEpoch := curr + 1
|
newEpoch := curr + countEpoch
|
||||||
wCtx.Command.Printf("Current epoch: %d, increase to %d.\n", curr, newEpoch)
|
wCtx.Command.Printf("Current epoch: %d, increase to %d.\n", curr, newEpoch)
|
||||||
|
|
||||||
// In NeoFS this is done via Notary contract. Here, however, we can form the
|
// In NeoFS this is done via Notary contract. Here, however, we can form the
|
||||||
|
@ -87,7 +93,7 @@ func EmitNewEpochCall(bw *io.BufBinWriter, wCtx *InitializeContext, nmHash util.
|
||||||
|
|
||||||
func GetNetConfigFromNetmapContract(roInvoker *invoker.Invoker) ([]stackitem.Item, error) {
|
func GetNetConfigFromNetmapContract(roInvoker *invoker.Invoker) ([]stackitem.Item, error) {
|
||||||
r := management.NewReader(roInvoker)
|
r := management.NewReader(roInvoker)
|
||||||
cs, err := r.GetContractByID(1)
|
cs, err := GetContractByID(r, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("get nns contract: %w", err)
|
return nil, fmt.Errorf("get nns contract: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,10 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"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/fixedn"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
@ -40,58 +40,51 @@ func openAlphabetWallets(v *viper.Viper, walletDir string) ([]*wallet.Wallet, er
|
||||||
return nil, fmt.Errorf("can't read alphabet wallets dir: %w", err)
|
return nil, fmt.Errorf("can't read alphabet wallets dir: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var size int
|
var wallets []*wallet.Wallet
|
||||||
loop:
|
var letter string
|
||||||
for i := 0; i < len(walletFiles); i++ {
|
for i := range constants.MaxAlphabetNodes {
|
||||||
name := innerring.GlagoliticLetter(i).String() + ".json"
|
letter = innerring.GlagoliticLetter(i).String()
|
||||||
for j := range walletFiles {
|
p := filepath.Join(walletDir, letter+".json")
|
||||||
if walletFiles[j].Name() == name {
|
var w *wallet.Wallet
|
||||||
size++
|
w, err = wallet.NewWalletFromFile(p)
|
||||||
continue loop
|
if err != nil {
|
||||||
}
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
err = nil
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("can't open wallet: %w", err)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if size == 0 {
|
|
||||||
return nil, errors.New("alphabet wallets dir is empty (run `generate-alphabet` command first)")
|
|
||||||
}
|
|
||||||
|
|
||||||
wallets := make([]*wallet.Wallet, size)
|
var password string
|
||||||
for i := 0; i < size; i++ {
|
password, err = config.GetPassword(v, letter)
|
||||||
letter := innerring.GlagoliticLetter(i).String()
|
|
||||||
p := filepath.Join(walletDir, letter+".json")
|
|
||||||
w, err := wallet.NewWalletFromFile(p)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't open wallet: %w", err)
|
err = fmt.Errorf("can't fetch password: %w", err)
|
||||||
}
|
break
|
||||||
|
|
||||||
password, err := config.GetPassword(v, letter)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't fetch password: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range w.Accounts {
|
for i := range w.Accounts {
|
||||||
if err := w.Accounts[i].Decrypt(password, keys.NEP2ScryptParams()); err != nil {
|
if err = w.Accounts[i].Decrypt(password, keys.NEP2ScryptParams()); err != nil {
|
||||||
return nil, fmt.Errorf("can't unlock wallet: %w", err)
|
err = fmt.Errorf("can't unlock wallet: %w", err)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wallets[i] = w
|
wallets = append(wallets, w)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't read wallet for letter '%s': %w", letter, err)
|
||||||
|
}
|
||||||
|
if len(wallets) == 0 {
|
||||||
|
err = errors.New("there are no alphabet wallets in dir (run `generate-alphabet` command first)")
|
||||||
|
if len(walletFiles) > 0 {
|
||||||
|
err = fmt.Errorf("use glagolitic names for wallets(run `print-alphabet`): %w", err)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return wallets, nil
|
return wallets, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewActor(c actor.RPCActor, committeeAcc *wallet.Account) (*actor.Actor, error) {
|
|
||||||
return actor.New(c, []actor.SignerAccount{{
|
|
||||||
Signer: transaction.Signer{
|
|
||||||
Account: committeeAcc.Contract.ScriptHash(),
|
|
||||||
Scopes: transaction.Global,
|
|
||||||
},
|
|
||||||
Account: committeeAcc,
|
|
||||||
}})
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadContract(ctrPath, ctrName string) (*ContractState, error) {
|
func ReadContract(ctrPath, ctrName string) (*ContractState, error) {
|
||||||
rawNef, err := os.ReadFile(filepath.Join(ctrPath, ctrName+"_contract.nef"))
|
rawNef, err := os.ReadFile(filepath.Join(ctrPath, ctrName+"_contract.nef"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -122,11 +115,11 @@ func readContractsFromArchive(file io.Reader, names []string) (map[string]*Contr
|
||||||
}
|
}
|
||||||
|
|
||||||
r := tar.NewReader(gr)
|
r := tar.NewReader(gr)
|
||||||
for h, err := r.Next(); ; h, err = r.Next() {
|
var h *tar.Header
|
||||||
if err != nil {
|
for h, err = r.Next(); err == nil && h != nil; h, err = r.Next() {
|
||||||
break
|
if h.Typeflag != tar.TypeReg {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, _ := filepath.Split(h.Name)
|
dir, _ := filepath.Split(h.Name)
|
||||||
ctrName := filepath.Base(dir)
|
ctrName := filepath.Base(dir)
|
||||||
|
|
||||||
|
@ -149,6 +142,9 @@ func readContractsFromArchive(file io.Reader, names []string) (map[string]*Contr
|
||||||
}
|
}
|
||||||
m[ctrName] = cs
|
m[ctrName] = cs
|
||||||
}
|
}
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return nil, fmt.Errorf("can't read contracts from archive: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
for ctrName, cs := range m {
|
for ctrName, cs := range m {
|
||||||
if cs.RawNEF == nil {
|
if cs.RawNEF == nil {
|
||||||
|
@ -175,3 +171,18 @@ func ParseGASAmount(s string) (fixedn.Fixed8, error) {
|
||||||
}
|
}
|
||||||
return gasAmount, nil
|
return gasAmount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetContractByID retrieves a contract by its ID using the standard GetContractByID method.
|
||||||
|
// However, if the returned state.Contract is nil, it returns an error indicating that the contract was not found.
|
||||||
|
// See https://git.frostfs.info/TrueCloudLab/frostfs-node/issues/1210
|
||||||
|
func GetContractByID(r *management.ContractReader, id int32) (*state.Contract, error) {
|
||||||
|
cs, err := r.GetContractByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cs == nil {
|
||||||
|
return nil, errors.New("contract not found")
|
||||||
|
}
|
||||||
|
return cs, nil
|
||||||
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import (
|
||||||
|
|
||||||
func setNNS(c *helper.InitializeContext) error {
|
func setNNS(c *helper.InitializeContext) error {
|
||||||
r := management.NewReader(c.ReadOnlyInvoker)
|
r := management.NewReader(c.ReadOnlyInvoker)
|
||||||
nnsCs, err := r.GetContractByID(1)
|
nnsCs, err := helper.GetContractByID(r, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,10 +100,7 @@ func registerCandidates(c *helper.InitializeContext) error {
|
||||||
// Register candidates in batches in order to overcome the signers amount limit.
|
// 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
|
// See: https://github.com/nspcc-dev/neo-go/blob/master/pkg/core/transaction/transaction.go#L27
|
||||||
for i := 0; i < need; i += registerBatchSize {
|
for i := 0; i < need; i += registerBatchSize {
|
||||||
start, end := i, i+registerBatchSize
|
start, end := i, min(i+registerBatchSize, need)
|
||||||
if end > need {
|
|
||||||
end = need
|
|
||||||
}
|
|
||||||
// This check is sound because transactions are accepted/rejected atomically.
|
// This check is sound because transactions are accepted/rejected atomically.
|
||||||
if have >= end {
|
if have >= end {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package initialize
|
package initialize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
@ -29,10 +31,14 @@ func setNotaryAndAlphabetNodes(c *helper.InitializeContext) error {
|
||||||
callflag.States|callflag.AllowNotify, int64(noderoles.NeoFSAlphabet), pubs)
|
callflag.States|callflag.AllowNotify, int64(noderoles.NeoFSAlphabet), pubs)
|
||||||
|
|
||||||
if err := c.SendCommitteeTx(w.Bytes(), false); err != nil {
|
if err := c.SendCommitteeTx(w.Bytes(), false); err != nil {
|
||||||
return err
|
return fmt.Errorf("send committee transaction: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.AwaitTx()
|
err := c.AwaitTx()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("await committee transaction: %w", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func setRolesFinished(c *helper.InitializeContext) (bool, error) {
|
func setRolesFinished(c *helper.InitializeContext) (bool, error) {
|
||||||
|
|
|
@ -62,7 +62,7 @@ func testInitialize(t *testing.T, committeeSize int) {
|
||||||
v := viper.GetViper()
|
v := viper.GetViper()
|
||||||
|
|
||||||
require.NoError(t, generateTestData(testdataDir, committeeSize))
|
require.NoError(t, generateTestData(testdataDir, committeeSize))
|
||||||
v.Set(constants.ProtoConfigPath, filepath.Join(testdataDir, protoFileName))
|
v.Set(commonflags.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.
|
||||||
require.NoError(t, Cmd.Flags().Set(commonflags.ContractsInitFlag, contractsPath))
|
require.NoError(t, Cmd.Flags().Set(commonflags.ContractsInitFlag, contractsPath))
|
||||||
|
@ -113,7 +113,7 @@ func generateTestData(dir string, size int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var pubs []string
|
var pubs []string
|
||||||
for i := 0; i < size; i++ {
|
for i := range size {
|
||||||
p := filepath.Join(dir, innerring.GlagoliticLetter(i).String()+".json")
|
p := filepath.Join(dir, innerring.GlagoliticLetter(i).String()+".json")
|
||||||
w, err := wallet.NewWalletFromFile(p)
|
w, err := wallet.NewWalletFromFile(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -148,7 +148,7 @@ func generateTestData(dir string, size int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTestCredentials(v *viper.Viper, size int) {
|
func setTestCredentials(v *viper.Viper, size int) {
|
||||||
for i := 0; i < size; i++ {
|
for i := range size {
|
||||||
v.Set("credentials."+innerring.GlagoliticLetter(i).String(), strconv.FormatUint(uint64(i), 10))
|
v.Set("credentials."+innerring.GlagoliticLetter(i).String(), strconv.FormatUint(uint64(i), 10))
|
||||||
}
|
}
|
||||||
v.Set("credentials.contract", constants.TestContractPassword)
|
v.Set("credentials.contract", constants.TestContractPassword)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package initialize
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
|
@ -28,6 +29,10 @@ const (
|
||||||
initialProxyGASAmount = 50_000 * native.GASFactor
|
initialProxyGASAmount = 50_000 * native.GASFactor
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func initialCommitteeGASAmount(c *helper.InitializeContext) int64 {
|
||||||
|
return (gasInitialTotalSupply - initialAlphabetGASAmount*int64(len(c.Wallets))) / 2
|
||||||
|
}
|
||||||
|
|
||||||
func transferFunds(c *helper.InitializeContext) error {
|
func transferFunds(c *helper.InitializeContext) error {
|
||||||
ok, err := transferFundsFinished(c)
|
ok, err := transferFundsFinished(c)
|
||||||
if ok || err != nil {
|
if ok || err != nil {
|
||||||
|
@ -54,7 +59,7 @@ func transferFunds(c *helper.InitializeContext) error {
|
||||||
transferTarget{
|
transferTarget{
|
||||||
Token: gas.Hash,
|
Token: gas.Hash,
|
||||||
Address: c.CommitteeAcc.Contract.ScriptHash(),
|
Address: c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
Amount: (gasInitialTotalSupply - initialAlphabetGASAmount*int64(len(c.Wallets))) / 2,
|
Amount: initialCommitteeGASAmount(c),
|
||||||
},
|
},
|
||||||
transferTarget{
|
transferTarget{
|
||||||
Token: neo.Hash,
|
Token: neo.Hash,
|
||||||
|
@ -75,12 +80,19 @@ func transferFunds(c *helper.InitializeContext) error {
|
||||||
return c.AwaitTx()
|
return c.AwaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// transferFundsFinished checks balances of accounts we transfer GAS to.
|
||||||
|
// The stage is considered finished if the balance is greater than the half of what we need to transfer.
|
||||||
func transferFundsFinished(c *helper.InitializeContext) (bool, error) {
|
func transferFundsFinished(c *helper.InitializeContext) (bool, error) {
|
||||||
acc := c.Accounts[0]
|
acc := c.Accounts[0]
|
||||||
|
|
||||||
r := nep17.NewReader(c.ReadOnlyInvoker, gas.Hash)
|
r := nep17.NewReader(c.ReadOnlyInvoker, gas.Hash)
|
||||||
res, err := r.BalanceOf(acc.Contract.ScriptHash())
|
res, err := r.BalanceOf(acc.Contract.ScriptHash())
|
||||||
return res.Cmp(big.NewInt(initialAlphabetGASAmount/2)) == 1, err
|
if err != nil || res.Cmp(big.NewInt(initialAlphabetGASAmount/2)) != 1 {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err = r.BalanceOf(c.CommitteeAcc.ScriptHash())
|
||||||
|
return res != nil && res.Cmp(big.NewInt(initialCommitteeGASAmount(c)/2)) == 1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func transferGASToProxy(c *helper.InitializeContext) error {
|
func transferGASToProxy(c *helper.InitializeContext) error {
|
||||||
|
@ -140,5 +152,17 @@ func createNEP17MultiTransferTx(c helper.Client, acc *wallet.Account, recipients
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't create actor: %w", err)
|
return nil, fmt.Errorf("can't create actor: %w", err)
|
||||||
}
|
}
|
||||||
return act.MakeRun(w.Bytes())
|
tx, err := act.MakeRun(w.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
sum := make(map[util.Uint160]int64)
|
||||||
|
for _, recipient := range recipients {
|
||||||
|
sum[recipient.Token] += recipient.Amount
|
||||||
|
}
|
||||||
|
detail := make([]string, 0, len(sum))
|
||||||
|
for _, value := range sum {
|
||||||
|
detail = append(detail, fmt.Sprintf("amount=%v", value))
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("transfer failed: from=%s(%s) %s: %w", acc.Label, acc.Address, strings.Join(detail, " "), err)
|
||||||
|
}
|
||||||
|
return tx, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package initialize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
@ -25,12 +24,14 @@ var Cmd = &cobra.Command{
|
||||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||||
_ = viper.BindPFlag(commonflags.EpochDurationInitFlag, cmd.Flags().Lookup(epochDurationCLIFlag))
|
_ = viper.BindPFlag(commonflags.EpochDurationInitFlag, cmd.Flags().Lookup(epochDurationCLIFlag))
|
||||||
_ = viper.BindPFlag(commonflags.MaxObjectSizeInitFlag, cmd.Flags().Lookup(maxObjectSizeCLIFlag))
|
_ = viper.BindPFlag(commonflags.MaxObjectSizeInitFlag, cmd.Flags().Lookup(maxObjectSizeCLIFlag))
|
||||||
|
_ = viper.BindPFlag(commonflags.MaxECDataCountFlag, cmd.Flags().Lookup(commonflags.MaxECDataCountFlag))
|
||||||
|
_ = viper.BindPFlag(commonflags.MaxECParityCounFlag, cmd.Flags().Lookup(commonflags.MaxECParityCounFlag))
|
||||||
_ = viper.BindPFlag(commonflags.HomomorphicHashDisabledInitFlag, cmd.Flags().Lookup(homomorphicHashDisabledCLIFlag))
|
_ = viper.BindPFlag(commonflags.HomomorphicHashDisabledInitFlag, cmd.Flags().Lookup(homomorphicHashDisabledCLIFlag))
|
||||||
_ = viper.BindPFlag(commonflags.CandidateFeeInitFlag, cmd.Flags().Lookup(candidateFeeCLIFlag))
|
_ = viper.BindPFlag(commonflags.CandidateFeeInitFlag, cmd.Flags().Lookup(candidateFeeCLIFlag))
|
||||||
_ = viper.BindPFlag(commonflags.ContainerFeeInitFlag, cmd.Flags().Lookup(containerFeeCLIFlag))
|
_ = viper.BindPFlag(commonflags.ContainerFeeInitFlag, cmd.Flags().Lookup(containerFeeCLIFlag))
|
||||||
_ = viper.BindPFlag(commonflags.ContainerAliasFeeInitFlag, cmd.Flags().Lookup(containerAliasFeeCLIFlag))
|
_ = viper.BindPFlag(commonflags.ContainerAliasFeeInitFlag, cmd.Flags().Lookup(containerAliasFeeCLIFlag))
|
||||||
_ = viper.BindPFlag(commonflags.WithdrawFeeInitFlag, cmd.Flags().Lookup(withdrawFeeCLIFlag))
|
_ = viper.BindPFlag(commonflags.WithdrawFeeInitFlag, cmd.Flags().Lookup(withdrawFeeCLIFlag))
|
||||||
_ = viper.BindPFlag(constants.ProtoConfigPath, cmd.Flags().Lookup(constants.ProtoConfigPath))
|
_ = viper.BindPFlag(commonflags.ProtoConfigPath, cmd.Flags().Lookup(commonflags.ProtoConfigPath))
|
||||||
},
|
},
|
||||||
RunE: initializeSideChainCmd,
|
RunE: initializeSideChainCmd,
|
||||||
}
|
}
|
||||||
|
@ -46,7 +47,7 @@ func initInitCmd() {
|
||||||
// Defaults are taken from neo-preodolenie.
|
// Defaults are taken from neo-preodolenie.
|
||||||
Cmd.Flags().Uint64(containerFeeCLIFlag, 1000, "Container registration fee")
|
Cmd.Flags().Uint64(containerFeeCLIFlag, 1000, "Container registration fee")
|
||||||
Cmd.Flags().Uint64(containerAliasFeeCLIFlag, 500, "Container alias fee")
|
Cmd.Flags().Uint64(containerAliasFeeCLIFlag, 500, "Container alias fee")
|
||||||
Cmd.Flags().String(constants.ProtoConfigPath, "", "Path to the consensus node configuration")
|
Cmd.Flags().String(commonflags.ProtoConfigPath, "", "Path to the consensus node configuration")
|
||||||
Cmd.Flags().String(commonflags.LocalDumpFlag, "", "Path to the blocks dump file")
|
Cmd.Flags().String(commonflags.LocalDumpFlag, "", "Path to the blocks dump file")
|
||||||
Cmd.MarkFlagsMutuallyExclusive(commonflags.ContractsInitFlag, commonflags.ContractsURLFlag)
|
Cmd.MarkFlagsMutuallyExclusive(commonflags.ContractsInitFlag, commonflags.ContractsURLFlag)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,14 +12,16 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const deltaFlag = "delta"
|
||||||
|
|
||||||
func ForceNewEpochCmd(cmd *cobra.Command, _ []string) error {
|
func ForceNewEpochCmd(cmd *cobra.Command, _ []string) error {
|
||||||
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't to initialize context: %w", err)
|
return fmt.Errorf("can't initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||||
cs, err := r.GetContractByID(1)
|
cs, err := helper.GetContractByID(r, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't get NNS contract info: %w", err)
|
return fmt.Errorf("can't get NNS contract info: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -30,7 +32,8 @@ func ForceNewEpochCmd(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
if err := helper.EmitNewEpochCall(bw, wCtx, nmHash); err != nil {
|
delta, _ := cmd.Flags().GetInt64(deltaFlag)
|
||||||
|
if err := helper.EmitNewEpochCall(bw, wCtx, nmHash, delta); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func listNetmapCandidatesNodes(cmd *cobra.Command, _ []string) {
|
func listNetmapCandidatesNodes(cmd *cobra.Command, _ []string) {
|
||||||
c, err := helper.GetN3Client(viper.GetViper())
|
c, err := helper.NewRemoteClient(viper.GetViper())
|
||||||
commonCmd.ExitOnErr(cmd, "can't create N3 client: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't create N3 client: %w", err)
|
||||||
|
|
||||||
inv := invoker.New(c, nil)
|
inv := invoker.New(c, nil)
|
||||||
r := management.NewReader(inv)
|
r := management.NewReader(inv)
|
||||||
|
|
||||||
cs, err := r.GetContractByID(1)
|
cs, err := helper.GetContractByID(r, 1)
|
||||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract info: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract info: %w", err)
|
||||||
|
|
||||||
nmHash, err := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.NetmapContract))
|
nmHash, err := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.NetmapContract))
|
||||||
|
|
|
@ -12,7 +12,6 @@ var (
|
||||||
Short: "List netmap candidates nodes",
|
Short: "List netmap candidates nodes",
|
||||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
|
||||||
},
|
},
|
||||||
Run: listNetmapCandidatesNodes,
|
Run: listNetmapCandidatesNodes,
|
||||||
}
|
}
|
||||||
|
@ -35,6 +34,7 @@ func initForceNewEpochCmd() {
|
||||||
ForceNewEpoch.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
ForceNewEpoch.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||||
ForceNewEpoch.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
ForceNewEpoch.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
ForceNewEpoch.Flags().String(commonflags.LocalDumpFlag, "", "Path to the blocks dump file")
|
ForceNewEpoch.Flags().String(commonflags.LocalDumpFlag, "", "Path to the blocks dump file")
|
||||||
|
ForceNewEpoch.Flags().Int64(deltaFlag, 1, "Number of epochs to increase the current epoch")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -24,7 +24,7 @@ func initRegisterCmd() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerDomain(cmd *cobra.Command, _ []string) {
|
func registerDomain(cmd *cobra.Command, _ []string) {
|
||||||
c, actor, _ := getRPCClient(cmd)
|
c, actor := nnsWriter(cmd)
|
||||||
|
|
||||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||||
email, _ := cmd.Flags().GetString(nnsEmailFlag)
|
email, _ := cmd.Flags().GetString(nnsEmailFlag)
|
||||||
|
@ -42,3 +42,23 @@ func registerDomain(cmd *cobra.Command, _ []string) {
|
||||||
commonCmd.ExitOnErr(cmd, "register domain error: %w", err)
|
commonCmd.ExitOnErr(cmd, "register domain error: %w", err)
|
||||||
cmd.Println("Domain registered successfully")
|
cmd.Println("Domain registered successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initDeleteCmd() {
|
||||||
|
Cmd.AddCommand(deleteCmd)
|
||||||
|
deleteCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
|
deleteCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||||
|
deleteCmd.Flags().String(nnsNameFlag, "", nnsNameFlagDesc)
|
||||||
|
|
||||||
|
_ = cobra.MarkFlagRequired(deleteCmd.Flags(), nnsNameFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteDomain(cmd *cobra.Command, _ []string) {
|
||||||
|
c, actor := nnsWriter(cmd)
|
||||||
|
|
||||||
|
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||||
|
h, vub, err := c.DeleteDomain(name)
|
||||||
|
|
||||||
|
_, err = actor.Wait(h, vub, err)
|
||||||
|
commonCmd.ExitOnErr(cmd, "delete domain error: %w", err)
|
||||||
|
cmd.Println("Domain deleted successfully")
|
||||||
|
}
|
|
@ -2,24 +2,37 @@ package nns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
client "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/nns"
|
client "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/nns"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getRPCClient(cmd *cobra.Command) (*client.Contract, *helper.LocalActor, util.Uint160) {
|
func nnsWriter(cmd *cobra.Command) (*client.Contract, *helper.LocalActor) {
|
||||||
v := viper.GetViper()
|
v := viper.GetViper()
|
||||||
c, err := helper.GetN3Client(v)
|
c, err := helper.NewRemoteClient(v)
|
||||||
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
||||||
|
|
||||||
ac, err := helper.NewLocalActor(cmd, c)
|
ac, err := helper.NewLocalActor(cmd, c, constants.CommitteeAccountName)
|
||||||
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
||||||
|
|
||||||
r := management.NewReader(ac.Invoker)
|
r := management.NewReader(ac.Invoker)
|
||||||
nnsCs, err := r.GetContractByID(1)
|
nnsCs, err := helper.GetContractByID(r, 1)
|
||||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||||
return client.New(ac, nnsCs.Hash), ac, nnsCs.Hash
|
return client.New(ac, nnsCs.Hash), ac
|
||||||
|
}
|
||||||
|
|
||||||
|
func nnsReader(cmd *cobra.Command) (*client.ContractReader, *invoker.Invoker) {
|
||||||
|
c, err := helper.NewRemoteClient(viper.GetViper())
|
||||||
|
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
||||||
|
|
||||||
|
inv := invoker.New(c, nil)
|
||||||
|
r := management.NewReader(inv)
|
||||||
|
nnsCs, err := helper.GetContractByID(r, 1)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||||
|
|
||||||
|
return client.NewReader(inv, nnsCs.Hash), inv
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
@ -29,7 +28,6 @@ func initAddRecordCmd() {
|
||||||
func initGetRecordsCmd() {
|
func initGetRecordsCmd() {
|
||||||
Cmd.AddCommand(getRecordsCmd)
|
Cmd.AddCommand(getRecordsCmd)
|
||||||
getRecordsCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
getRecordsCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
getRecordsCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
|
||||||
getRecordsCmd.Flags().String(nnsNameFlag, "", nnsNameFlagDesc)
|
getRecordsCmd.Flags().String(nnsNameFlag, "", nnsNameFlagDesc)
|
||||||
getRecordsCmd.Flags().String(nnsRecordTypeFlag, "", nnsRecordTypeFlagDesc)
|
getRecordsCmd.Flags().String(nnsRecordTypeFlag, "", nnsRecordTypeFlagDesc)
|
||||||
|
|
||||||
|
@ -47,8 +45,21 @@ func initDelRecordsCmd() {
|
||||||
_ = cobra.MarkFlagRequired(delRecordsCmd.Flags(), nnsRecordTypeFlag)
|
_ = cobra.MarkFlagRequired(delRecordsCmd.Flags(), nnsRecordTypeFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initDelRecordCmd() {
|
||||||
|
Cmd.AddCommand(delRecordCmd)
|
||||||
|
delRecordCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
|
delRecordCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||||
|
delRecordCmd.Flags().String(nnsNameFlag, "", nnsNameFlagDesc)
|
||||||
|
delRecordCmd.Flags().String(nnsRecordTypeFlag, "", nnsRecordTypeFlagDesc)
|
||||||
|
delRecordCmd.Flags().String(nnsRecordDataFlag, "", nnsRecordDataFlagDesc)
|
||||||
|
|
||||||
|
_ = cobra.MarkFlagRequired(delRecordCmd.Flags(), nnsNameFlag)
|
||||||
|
_ = cobra.MarkFlagRequired(delRecordCmd.Flags(), nnsRecordTypeFlag)
|
||||||
|
_ = cobra.MarkFlagRequired(delRecordCmd.Flags(), nnsRecordDataFlag)
|
||||||
|
}
|
||||||
|
|
||||||
func addRecord(cmd *cobra.Command, _ []string) {
|
func addRecord(cmd *cobra.Command, _ []string) {
|
||||||
c, actor, _ := getRPCClient(cmd)
|
c, actor := nnsWriter(cmd)
|
||||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||||
data, _ := cmd.Flags().GetString(nnsRecordDataFlag)
|
data, _ := cmd.Flags().GetString(nnsRecordDataFlag)
|
||||||
recordType, _ := cmd.Flags().GetString(nnsRecordTypeFlag)
|
recordType, _ := cmd.Flags().GetString(nnsRecordTypeFlag)
|
||||||
|
@ -64,16 +75,16 @@ func addRecord(cmd *cobra.Command, _ []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRecords(cmd *cobra.Command, _ []string) {
|
func getRecords(cmd *cobra.Command, _ []string) {
|
||||||
c, act, hash := getRPCClient(cmd)
|
c, inv := nnsReader(cmd)
|
||||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||||
recordType, _ := cmd.Flags().GetString(nnsRecordTypeFlag)
|
recordType, _ := cmd.Flags().GetString(nnsRecordTypeFlag)
|
||||||
if recordType == "" {
|
if recordType == "" {
|
||||||
sid, r, err := unwrap.SessionIterator(act.Invoker.Call(hash, "getAllRecords", name))
|
sid, r, err := c.GetAllRecords(name)
|
||||||
commonCmd.ExitOnErr(cmd, "unable to get records: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to get records: %w", err)
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = act.Invoker.TerminateSession(sid)
|
_ = inv.TerminateSession(sid)
|
||||||
}()
|
}()
|
||||||
items, err := act.Invoker.TraverseIterator(sid, &r, 0)
|
items, err := inv.TraverseIterator(sid, &r, 0)
|
||||||
commonCmd.ExitOnErr(cmd, "unable to get records: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to get records: %w", err)
|
||||||
for len(items) != 0 {
|
for len(items) != 0 {
|
||||||
for j := range items {
|
for j := range items {
|
||||||
|
@ -84,7 +95,7 @@ func getRecords(cmd *cobra.Command, _ []string) {
|
||||||
recordTypeToString(nns.RecordType(rs[1].Value().(*big.Int).Int64())),
|
recordTypeToString(nns.RecordType(rs[1].Value().(*big.Int).Int64())),
|
||||||
string(bs))
|
string(bs))
|
||||||
}
|
}
|
||||||
items, err = act.Invoker.TraverseIterator(sid, &r, 0)
|
items, err = inv.TraverseIterator(sid, &r, 0)
|
||||||
commonCmd.ExitOnErr(cmd, "unable to get records: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to get records: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -101,7 +112,7 @@ func getRecords(cmd *cobra.Command, _ []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func delRecords(cmd *cobra.Command, _ []string) {
|
func delRecords(cmd *cobra.Command, _ []string) {
|
||||||
c, actor, _ := getRPCClient(cmd)
|
c, actor := nnsWriter(cmd)
|
||||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||||
recordType, _ := cmd.Flags().GetString(nnsRecordTypeFlag)
|
recordType, _ := cmd.Flags().GetString(nnsRecordTypeFlag)
|
||||||
typ, err := getRecordType(recordType)
|
typ, err := getRecordType(recordType)
|
||||||
|
@ -115,6 +126,22 @@ func delRecords(cmd *cobra.Command, _ []string) {
|
||||||
cmd.Println("Records removed successfully")
|
cmd.Println("Records removed successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func delRecord(cmd *cobra.Command, _ []string) {
|
||||||
|
c, actor := nnsWriter(cmd)
|
||||||
|
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||||
|
data, _ := cmd.Flags().GetString(nnsRecordDataFlag)
|
||||||
|
recordType, _ := cmd.Flags().GetString(nnsRecordTypeFlag)
|
||||||
|
typ, err := getRecordType(recordType)
|
||||||
|
commonCmd.ExitOnErr(cmd, "unable to parse record type: %w", err)
|
||||||
|
h, vub, err := c.DeleteRecord(name, typ, data)
|
||||||
|
commonCmd.ExitOnErr(cmd, "unable to delete record: %w", err)
|
||||||
|
|
||||||
|
cmd.Println("Waiting for transaction to persist...")
|
||||||
|
_, err = actor.Wait(h, vub, err)
|
||||||
|
commonCmd.ExitOnErr(cmd, "delete records error: %w", err)
|
||||||
|
cmd.Println("Record removed successfully")
|
||||||
|
}
|
||||||
|
|
||||||
func getRecordType(recordType string) (*big.Int, error) {
|
func getRecordType(recordType string) (*big.Int, error) {
|
||||||
switch strings.ToUpper(recordType) {
|
switch strings.ToUpper(recordType) {
|
||||||
case "A":
|
case "A":
|
||||||
|
|
|
@ -14,7 +14,7 @@ func initRenewCmd() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func renewDomain(cmd *cobra.Command, _ []string) {
|
func renewDomain(cmd *cobra.Command, _ []string) {
|
||||||
c, actor, _ := getRPCClient(cmd)
|
c, actor := nnsWriter(cmd)
|
||||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||||
h, vub, err := c.Renew(name)
|
h, vub, err := c.Renew(name)
|
||||||
commonCmd.ExitOnErr(cmd, "unable to renew domain: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to renew domain: %w", err)
|
||||||
|
|
|
@ -42,6 +42,15 @@ var (
|
||||||
},
|
},
|
||||||
Run: registerDomain,
|
Run: registerDomain,
|
||||||
}
|
}
|
||||||
|
deleteCmd = &cobra.Command{
|
||||||
|
Use: "delete",
|
||||||
|
Short: "Delete a domain by name",
|
||||||
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||||
|
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
||||||
|
},
|
||||||
|
Run: deleteDomain,
|
||||||
|
}
|
||||||
renewCmd = &cobra.Command{
|
renewCmd = &cobra.Command{
|
||||||
Use: "renew",
|
Use: "renew",
|
||||||
Short: "Increases domain expiration date",
|
Short: "Increases domain expiration date",
|
||||||
|
@ -86,14 +95,25 @@ var (
|
||||||
},
|
},
|
||||||
Run: delRecords,
|
Run: delRecords,
|
||||||
}
|
}
|
||||||
|
delRecordCmd = &cobra.Command{
|
||||||
|
Use: "delete-record",
|
||||||
|
Short: "Removes domain record with the specified type and data",
|
||||||
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||||
|
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
||||||
|
},
|
||||||
|
Run: delRecord,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
initTokensCmd()
|
initTokensCmd()
|
||||||
initRegisterCmd()
|
initRegisterCmd()
|
||||||
|
initDeleteCmd()
|
||||||
initRenewCmd()
|
initRenewCmd()
|
||||||
initUpdateCmd()
|
initUpdateCmd()
|
||||||
initAddRecordCmd()
|
initAddRecordCmd()
|
||||||
initGetRecordsCmd()
|
initGetRecordsCmd()
|
||||||
initDelRecordsCmd()
|
initDelRecordsCmd()
|
||||||
|
initDelRecordCmd()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,65 @@
|
||||||
package nns
|
package nns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
||||||
|
client "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/nns"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
verboseDesc = "Include additional information about CNAME record."
|
||||||
|
)
|
||||||
|
|
||||||
func initTokensCmd() {
|
func initTokensCmd() {
|
||||||
Cmd.AddCommand(tokensCmd)
|
Cmd.AddCommand(tokensCmd)
|
||||||
tokensCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
tokensCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
tokensCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
tokensCmd.Flags().BoolP(commonflags.Verbose, commonflags.VerboseShorthand, false, verboseDesc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func listTokens(cmd *cobra.Command, _ []string) {
|
func listTokens(cmd *cobra.Command, _ []string) {
|
||||||
c, _, _ := getRPCClient(cmd)
|
c, _ := nnsReader(cmd)
|
||||||
it, err := c.Tokens()
|
it, err := c.Tokens()
|
||||||
commonCmd.ExitOnErr(cmd, "unable to get tokens: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to get tokens: %w", err)
|
||||||
for toks, err := it.Next(10); err == nil && len(toks) > 0; toks, err = it.Next(10) {
|
for toks, err := it.Next(10); err == nil && len(toks) > 0; toks, err = it.Next(10) {
|
||||||
for _, token := range toks {
|
for _, token := range toks {
|
||||||
cmd.Println(string(token))
|
output := string(token)
|
||||||
|
if verbose, _ := cmd.Flags().GetBool(commonflags.Verbose); verbose {
|
||||||
|
cname, err := getCnameRecord(c, token)
|
||||||
|
commonCmd.ExitOnErr(cmd, "", err)
|
||||||
|
if cname != "" {
|
||||||
|
output += " (CNAME: " + cname + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd.Println(output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCnameRecord(c *client.ContractReader, token []byte) (string, error) {
|
||||||
|
items, err := c.GetRecords(string(token), big.NewInt(int64(nns.CNAME)))
|
||||||
|
|
||||||
|
// GetRecords returns the error "not an array" if the domain does not contain records.
|
||||||
|
if err != nil && strings.Contains(err.Error(), "not an array") {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(items) == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
record, err := items[0].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(record), nil
|
||||||
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ func initUpdateCmd() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSOA(cmd *cobra.Command, _ []string) {
|
func updateSOA(cmd *cobra.Command, _ []string) {
|
||||||
c, actor, _ := getRPCClient(cmd)
|
c, actor := nnsWriter(cmd)
|
||||||
|
|
||||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||||
email, _ := cmd.Flags().GetString(nnsEmailFlag)
|
email, _ := cmd.Flags().GetString(nnsEmailFlag)
|
||||||
|
|
|
@ -37,7 +37,7 @@ func RemoveNodesCmd(cmd *cobra.Command, args []string) error {
|
||||||
defer wCtx.Close()
|
defer wCtx.Close()
|
||||||
|
|
||||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||||
cs, err := r.GetContractByID(1)
|
cs, err := helper.GetContractByID(r, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't get NNS contract info: %w", err)
|
return fmt.Errorf("can't get NNS contract info: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ func RemoveNodesCmd(cmd *cobra.Command, args []string) error {
|
||||||
int64(netmapcontract.NodeStateOffline), nodeKeys[i].Bytes())
|
int64(netmapcontract.NodeStateOffline), nodeKeys[i].Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := helper.EmitNewEpochCall(bw, wCtx, nmHash); err != nil {
|
if err := helper.EmitNewEpochCall(bw, wCtx, nmHash, 1); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
|
@ -41,7 +40,8 @@ func depositNotary(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
accHash := w.GetChangeAddress()
|
accHash := w.GetChangeAddress()
|
||||||
if addr, err := cmd.Flags().GetString(walletAccountFlag); err == nil {
|
addr, _ := cmd.Flags().GetString(walletAccountFlag)
|
||||||
|
if addr != "" {
|
||||||
accHash, err = address.StringToUint160(addr)
|
accHash, err = address.StringToUint160(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid address: %s", addr)
|
return fmt.Errorf("invalid address: %s", addr)
|
||||||
|
@ -73,23 +73,16 @@ func depositNotary(cmd *cobra.Command, _ []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
till := int64(defaultNotaryDepositLifetime)
|
till, _ := cmd.Flags().GetInt64(notaryDepositTillFlag)
|
||||||
tillStr, err := cmd.Flags().GetString(notaryDepositTillFlag)
|
if till <= 0 {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if tillStr != "" {
|
|
||||||
till, err = strconv.ParseInt(tillStr, 10, 64)
|
|
||||||
if err != nil || till <= 0 {
|
|
||||||
return errInvalidNotaryDepositLifetime
|
return errInvalidNotaryDepositLifetime
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return transferGas(cmd, acc, accHash, gasAmount, till)
|
return transferGas(cmd, acc, accHash, gasAmount, till)
|
||||||
}
|
}
|
||||||
|
|
||||||
func transferGas(cmd *cobra.Command, acc *wallet.Account, accHash util.Uint160, gasAmount fixedn.Fixed8, till int64) error {
|
func transferGas(cmd *cobra.Command, acc *wallet.Account, accHash util.Uint160, gasAmount fixedn.Fixed8, till int64) error {
|
||||||
c, err := helper.GetN3Client(viper.GetViper())
|
c, err := helper.NewRemoteClient(viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ func initDepositoryNotaryCmd() {
|
||||||
DepositCmd.Flags().String(commonflags.StorageWalletFlag, "", "Path to storage node wallet")
|
DepositCmd.Flags().String(commonflags.StorageWalletFlag, "", "Path to storage node wallet")
|
||||||
DepositCmd.Flags().String(walletAccountFlag, "", "Wallet account address")
|
DepositCmd.Flags().String(walletAccountFlag, "", "Wallet account address")
|
||||||
DepositCmd.Flags().String(commonflags.RefillGasAmountFlag, "", "Amount of GAS to deposit")
|
DepositCmd.Flags().String(commonflags.RefillGasAmountFlag, "", "Amount of GAS to deposit")
|
||||||
DepositCmd.Flags().String(notaryDepositTillFlag, "", "Notary deposit duration in blocks")
|
DepositCmd.Flags().Int64(notaryDepositTillFlag, defaultNotaryDepositLifetime, "Notary deposit duration in blocks")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -30,7 +30,7 @@ var errInvalidParameterFormat = errors.New("invalid parameter format, must be Pa
|
||||||
func SetPolicyCmd(cmd *cobra.Command, args []string) error {
|
func SetPolicyCmd(cmd *cobra.Command, args []string) error {
|
||||||
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't to initialize context: %w", err)
|
return fmt.Errorf("can't initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
|
@ -48,7 +48,7 @@ func SetPolicyCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
value, err := strconv.ParseUint(v, 10, 32)
|
value, err := strconv.ParseUint(v, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't parse parameter value '%s': %w", args[1], err)
|
return fmt.Errorf("can't parse parameter value '%s': %w", args[i], err)
|
||||||
}
|
}
|
||||||
|
|
||||||
emit.AppCall(bw.BinWriter, policy.Hash, "set"+k, callflag.All, int64(value))
|
emit.AppCall(bw.BinWriter, policy.Hash, "set"+k, callflag.All, int64(value))
|
||||||
|
@ -62,7 +62,7 @@ func SetPolicyCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func dumpPolicyCmd(cmd *cobra.Command, _ []string) error {
|
func dumpPolicyCmd(cmd *cobra.Command, _ []string) error {
|
||||||
c, err := helper.GetN3Client(viper.GetViper())
|
c, err := helper.NewRemoteClient(viper.GetViper())
|
||||||
commonCmd.ExitOnErr(cmd, "can't create N3 client:", err)
|
commonCmd.ExitOnErr(cmd, "can't create N3 client:", err)
|
||||||
|
|
||||||
inv := invoker.New(c, nil)
|
inv := invoker.New(c, nil)
|
||||||
|
|
|
@ -39,11 +39,11 @@ func removeProxyAccount(cmd *cobra.Command, _ []string) {
|
||||||
func processAccount(cmd *cobra.Command, addr util.Uint160, method string) error {
|
func processAccount(cmd *cobra.Command, addr util.Uint160, method string) error {
|
||||||
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't to initialize context: %w", err)
|
return fmt.Errorf("can't initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||||
cs, err := r.GetContractByID(1)
|
cs, err := helper.GetContractByID(r, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't get NNS contract info: %w", err)
|
return fmt.Errorf("can't get NNS contract info: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,13 @@ var (
|
||||||
func initProxyAddAccount() {
|
func initProxyAddAccount() {
|
||||||
AddAccountCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
AddAccountCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
AddAccountCmd.Flags().String(accountAddressFlag, "", "Wallet address string")
|
AddAccountCmd.Flags().String(accountAddressFlag, "", "Wallet address string")
|
||||||
|
AddAccountCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initProxyRemoveAccount() {
|
func initProxyRemoveAccount() {
|
||||||
RemoveAccountCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
RemoveAccountCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
RemoveAccountCmd.Flags().String(accountAddressFlag, "", "Wallet address string")
|
RemoveAccountCmd.Flags().String(accountAddressFlag, "", "Wallet address string")
|
||||||
|
RemoveAccountCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/metabase"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/storagecfg"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/storagecfg"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/misc"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/misc"
|
||||||
|
@ -41,6 +42,7 @@ func init() {
|
||||||
rootCmd.AddCommand(config.RootCmd)
|
rootCmd.AddCommand(config.RootCmd)
|
||||||
rootCmd.AddCommand(morph.RootCmd)
|
rootCmd.AddCommand(morph.RootCmd)
|
||||||
rootCmd.AddCommand(storagecfg.RootCmd)
|
rootCmd.AddCommand(storagecfg.RootCmd)
|
||||||
|
rootCmd.AddCommand(metabase.RootCmd)
|
||||||
|
|
||||||
rootCmd.AddCommand(autocomplete.Command("frostfs-adm"))
|
rootCmd.AddCommand(autocomplete.Command("frostfs-adm"))
|
||||||
rootCmd.AddCommand(gendoc.Command(rootCmd, gendoc.Options{}))
|
rootCmd.AddCommand(gendoc.Command(rootCmd, gendoc.Options{}))
|
||||||
|
|
|
@ -61,6 +61,8 @@ storage:
|
||||||
depth: 1 # max depth of object tree storage in key-value DB
|
depth: 1 # max depth of object tree storage in key-value DB
|
||||||
width: 4 # max width of object tree storage in key-value DB
|
width: 4 # max width of object tree storage in key-value DB
|
||||||
opened_cache_capacity: 50 # maximum number of opened database files
|
opened_cache_capacity: 50 # maximum number of opened database files
|
||||||
|
opened_cache_ttl: 5m # ttl for opened database file
|
||||||
|
opened_cache_exp_interval: 15s # cache cleanup interval for expired blobovnicza's
|
||||||
|
|
||||||
gc:
|
gc:
|
||||||
remover_batch_size: 200 # number of objects to be removed by the garbage collector
|
remover_batch_size: 200 # number of objects to be removed by the garbage collector
|
||||||
|
|
|
@ -105,7 +105,7 @@ func storageConfig(cmd *cobra.Command, args []string) {
|
||||||
fatalOnErr(errors.New("can't find account in wallet"))
|
fatalOnErr(errors.New("can't find account in wallet"))
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Wallet.Password, err = input.ReadPassword(fmt.Sprintf("Account password for %s: ", c.Wallet.Account))
|
c.Wallet.Password, err = input.ReadPassword(fmt.Sprintf("Enter password for %s > ", c.Wallet.Account))
|
||||||
fatalOnErr(err)
|
fatalOnErr(err)
|
||||||
|
|
||||||
err = acc.Decrypt(c.Wallet.Password, keys.NEP2ScryptParams())
|
err = acc.Decrypt(c.Wallet.Password, keys.NEP2ScryptParams())
|
||||||
|
|
|
@ -72,4 +72,3 @@ All other `object` sub-commands support only static sessions (2).
|
||||||
List of commands supporting sessions (static only):
|
List of commands supporting sessions (static only):
|
||||||
- `create`
|
- `create`
|
||||||
- `delete`
|
- `delete`
|
||||||
- `set-eacl`
|
|
||||||
|
|
|
@ -2,11 +2,13 @@ package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sort"
|
"os"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
|
||||||
|
@ -14,7 +16,6 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
@ -76,13 +77,31 @@ func ListContainers(ctx context.Context, prm ListContainersPrm) (res ListContain
|
||||||
// SortedIDList returns sorted list of identifiers of user's containers.
|
// SortedIDList returns sorted list of identifiers of user's containers.
|
||||||
func (x ListContainersRes) SortedIDList() []cid.ID {
|
func (x ListContainersRes) SortedIDList() []cid.ID {
|
||||||
list := x.cliRes.Containers()
|
list := x.cliRes.Containers()
|
||||||
sort.Slice(list, func(i, j int) bool {
|
slices.SortFunc(list, func(lhs, rhs cid.ID) int {
|
||||||
lhs, rhs := list[i].EncodeToString(), list[j].EncodeToString()
|
return strings.Compare(lhs.EncodeToString(), rhs.EncodeToString())
|
||||||
return strings.Compare(lhs, rhs) < 0
|
|
||||||
})
|
})
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ListContainersStream(ctx context.Context, prm ListContainersPrm, processCnr func(id cid.ID) bool) (err error) {
|
||||||
|
cliPrm := &client.PrmContainerListStream{
|
||||||
|
XHeaders: prm.XHeaders,
|
||||||
|
OwnerID: prm.OwnerID,
|
||||||
|
Session: prm.Session,
|
||||||
|
}
|
||||||
|
rdr, err := prm.cli.ContainerListInit(ctx, *cliPrm)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("init container list: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rdr.Iterate(processCnr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("read container list: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// PutContainerPrm groups parameters of PutContainer operation.
|
// PutContainerPrm groups parameters of PutContainer operation.
|
||||||
type PutContainerPrm struct {
|
type PutContainerPrm struct {
|
||||||
Client *client.Client
|
Client *client.Client
|
||||||
|
@ -189,54 +208,6 @@ func DeleteContainer(ctx context.Context, prm DeleteContainerPrm) (res DeleteCon
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// EACLPrm groups parameters of EACL operation.
|
|
||||||
type EACLPrm struct {
|
|
||||||
Client *client.Client
|
|
||||||
ClientParams client.PrmContainerEACL
|
|
||||||
}
|
|
||||||
|
|
||||||
// EACLRes groups the resulting values of EACL operation.
|
|
||||||
type EACLRes struct {
|
|
||||||
cliRes *client.ResContainerEACL
|
|
||||||
}
|
|
||||||
|
|
||||||
// EACL returns requested eACL table.
|
|
||||||
func (x EACLRes) EACL() eacl.Table {
|
|
||||||
return x.cliRes.Table()
|
|
||||||
}
|
|
||||||
|
|
||||||
// EACL reads eACL table from FrostFS by container ID.
|
|
||||||
//
|
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
|
||||||
func EACL(ctx context.Context, prm EACLPrm) (res EACLRes, err error) {
|
|
||||||
res.cliRes, err = prm.Client.ContainerEACL(ctx, prm.ClientParams)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetEACLPrm groups parameters of SetEACL operation.
|
|
||||||
type SetEACLPrm struct {
|
|
||||||
Client *client.Client
|
|
||||||
ClientParams client.PrmContainerSetEACL
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetEACLRes groups the resulting values of SetEACL operation.
|
|
||||||
type SetEACLRes struct{}
|
|
||||||
|
|
||||||
// SetEACL requests to save an eACL table in FrostFS.
|
|
||||||
//
|
|
||||||
// Operation is asynchronous and no guaranteed even in the absence of errors.
|
|
||||||
// The required time is also not predictable.
|
|
||||||
//
|
|
||||||
// Success can be verified by reading by container identifier.
|
|
||||||
//
|
|
||||||
// Returns any error which prevented the operation from completing correctly in error return.
|
|
||||||
func SetEACL(ctx context.Context, prm SetEACLPrm) (res SetEACLRes, err error) {
|
|
||||||
_, err = prm.Client.ContainerSetEACL(ctx, prm.ClientParams)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkInfoPrm groups parameters of NetworkInfo operation.
|
// NetworkInfoPrm groups parameters of NetworkInfo operation.
|
||||||
type NetworkInfoPrm struct {
|
type NetworkInfoPrm struct {
|
||||||
Client *client.Client
|
Client *client.Client
|
||||||
|
@ -611,13 +582,6 @@ type HeadObjectPrm struct {
|
||||||
commonObjectPrm
|
commonObjectPrm
|
||||||
objectAddressPrm
|
objectAddressPrm
|
||||||
rawPrm
|
rawPrm
|
||||||
|
|
||||||
mainOnly bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMainOnlyFlag sets flag to get only main fields of an object header in terms of FrostFS API.
|
|
||||||
func (x *HeadObjectPrm) SetMainOnlyFlag(v bool) {
|
|
||||||
x.mainOnly = v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HeadObjectRes groups the resulting values of HeadObject operation.
|
// HeadObjectRes groups the resulting values of HeadObject operation.
|
||||||
|
@ -712,9 +676,7 @@ func SearchObjects(ctx context.Context, prm SearchObjectsPrm) (*SearchObjectsRes
|
||||||
|
|
||||||
for {
|
for {
|
||||||
n, ok = rdr.Read(buf)
|
n, ok = rdr.Read(buf)
|
||||||
for i := 0; i < n; i++ {
|
list = append(list, buf[:n]...)
|
||||||
list = append(list, buf[i])
|
|
||||||
}
|
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -725,9 +687,8 @@ 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 {
|
slices.SortFunc(list, func(a, b oid.ID) int {
|
||||||
lhs, rhs := list[i].EncodeToString(), list[j].EncodeToString()
|
return strings.Compare(a.EncodeToString(), b.EncodeToString())
|
||||||
return strings.Compare(lhs, rhs) < 0
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return &SearchObjectsRes{
|
return &SearchObjectsRes{
|
||||||
|
@ -892,3 +853,65 @@ func SyncContainerSettings(ctx context.Context, prm SyncContainerPrm) (*SyncCont
|
||||||
|
|
||||||
return new(SyncContainerRes), nil
|
return new(SyncContainerRes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PatchObjectPrm groups parameters of PatchObject operation.
|
||||||
|
type PatchObjectPrm struct {
|
||||||
|
commonObjectPrm
|
||||||
|
objectAddressPrm
|
||||||
|
|
||||||
|
NewAttributes []objectSDK.Attribute
|
||||||
|
|
||||||
|
ReplaceAttribute bool
|
||||||
|
|
||||||
|
PayloadPatches []PayloadPatch
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayloadPatch struct {
|
||||||
|
Range objectSDK.Range
|
||||||
|
|
||||||
|
PayloadPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PatchRes struct {
|
||||||
|
OID oid.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func Patch(ctx context.Context, prm PatchObjectPrm) (*PatchRes, error) {
|
||||||
|
patchPrm := client.PrmObjectPatch{
|
||||||
|
XHeaders: prm.xHeaders,
|
||||||
|
BearerToken: prm.bearerToken,
|
||||||
|
Session: prm.sessionToken,
|
||||||
|
Address: prm.objAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(prm.PayloadPatches, func(a, b PayloadPatch) int {
|
||||||
|
return cmp.Compare(a.Range.GetOffset(), b.Range.GetOffset())
|
||||||
|
})
|
||||||
|
|
||||||
|
patcher, err := prm.cli.ObjectPatchInit(ctx, patchPrm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("init payload reading: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if patcher.PatchAttributes(ctx, prm.NewAttributes, prm.ReplaceAttribute) {
|
||||||
|
for _, pp := range prm.PayloadPatches {
|
||||||
|
payloadFile, err := os.OpenFile(pp.PayloadPath, os.O_RDONLY, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
applied := patcher.PatchPayload(ctx, &pp.Range, payloadFile)
|
||||||
|
_ = payloadFile.Close()
|
||||||
|
if !applied {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := patcher.Close(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &PatchRes{
|
||||||
|
OID: res.ObjectID(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ func GetSDKClient(ctx context.Context, cmd *cobra.Command, key *ecdsa.PrivateKey
|
||||||
GRPCDialOptions: []grpc.DialOption{
|
GRPCDialOptions: []grpc.DialOption{
|
||||||
grpc.WithChainUnaryInterceptor(tracing.NewUnaryClientInteceptor()),
|
grpc.WithChainUnaryInterceptor(tracing.NewUnaryClientInteceptor()),
|
||||||
grpc.WithChainStreamInterceptor(tracing.NewStreamClientInterceptor()),
|
grpc.WithChainStreamInterceptor(tracing.NewStreamClientInterceptor()),
|
||||||
|
grpc.WithDefaultCallOptions(grpc.WaitForReady(true)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if timeout := viper.GetDuration(commonflags.Timeout); timeout > 0 {
|
if timeout := viper.GetDuration(commonflags.Timeout); timeout > 0 {
|
||||||
|
|
|
@ -2,7 +2,7 @@ package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"sort"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
|
@ -45,15 +45,11 @@ func StartClientCommandSpan(cmd *cobra.Command) {
|
||||||
})
|
})
|
||||||
commonCmd.ExitOnErr(cmd, "init tracing: %w", err)
|
commonCmd.ExitOnErr(cmd, "init tracing: %w", err)
|
||||||
|
|
||||||
var components sort.StringSlice
|
var components []string
|
||||||
for c := cmd; c != nil; c = c.Parent() {
|
for c := cmd; c != nil; c = c.Parent() {
|
||||||
components = append(components, c.Name())
|
components = append(components, c.Name())
|
||||||
}
|
}
|
||||||
for i, j := 0, len(components)-1; i < j; {
|
slices.Reverse(components)
|
||||||
components.Swap(i, j)
|
|
||||||
i++
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
|
|
||||||
operation := strings.Join(components, ".")
|
operation := strings.Join(components, ".")
|
||||||
ctx, span := tracing.StartSpanFromContext(cmd.Context(), operation)
|
ctx, span := tracing.StartSpanFromContext(cmd.Context(), operation)
|
||||||
|
|
|
@ -11,9 +11,9 @@ import (
|
||||||
// values and their usage descriptions.
|
// values and their usage descriptions.
|
||||||
const (
|
const (
|
||||||
GenerateKey = "generate-key"
|
GenerateKey = "generate-key"
|
||||||
generateKeyShorthand = "g"
|
GenerateKeyShorthand = "g"
|
||||||
generateKeyDefault = false
|
GenerateKeyDefault = false
|
||||||
generateKeyUsage = "Generate new private key"
|
GenerateKeyUsage = "Generate new private key"
|
||||||
|
|
||||||
WalletPath = "wallet"
|
WalletPath = "wallet"
|
||||||
WalletPathShorthand = "w"
|
WalletPathShorthand = "w"
|
||||||
|
@ -50,6 +50,13 @@ const (
|
||||||
|
|
||||||
TracingFlag = "trace"
|
TracingFlag = "trace"
|
||||||
TracingFlagUsage = "Generate trace ID and print it."
|
TracingFlagUsage = "Generate trace ID and print it."
|
||||||
|
|
||||||
|
AwaitFlag = "await"
|
||||||
|
AwaitFlagUsage = "Wait for the operation to complete"
|
||||||
|
|
||||||
|
QuietFlag = "quiet"
|
||||||
|
QuietFlagShorthand = "q"
|
||||||
|
QuietFlagUsage = "Print nothing and exit with non-zero code on failure"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init adds common flags to the command:
|
// Init adds common flags to the command:
|
||||||
|
@ -72,7 +79,7 @@ func Init(cmd *cobra.Command) {
|
||||||
func InitWithoutRPC(cmd *cobra.Command) {
|
func InitWithoutRPC(cmd *cobra.Command) {
|
||||||
ff := cmd.Flags()
|
ff := cmd.Flags()
|
||||||
|
|
||||||
ff.BoolP(GenerateKey, generateKeyShorthand, generateKeyDefault, generateKeyUsage)
|
ff.BoolP(GenerateKey, GenerateKeyShorthand, GenerateKeyDefault, GenerateKeyUsage)
|
||||||
ff.StringP(WalletPath, WalletPathShorthand, WalletPathDefault, WalletPathUsage)
|
ff.StringP(WalletPath, WalletPathShorthand, WalletPathDefault, WalletPathUsage)
|
||||||
ff.StringP(Account, AccountShorthand, AccountDefault, AccountUsage)
|
ff.StringP(Account, AccountShorthand, AccountDefault, AccountUsage)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,8 @@ var testCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_getOrGenerate(t *testing.T) {
|
func Test_getOrGenerate(t *testing.T) {
|
||||||
|
t.Cleanup(viper.Reset)
|
||||||
|
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
|
|
||||||
wallPath := filepath.Join(dir, "wallet.json")
|
wallPath := filepath.Join(dir, "wallet.json")
|
||||||
|
|
86
cmd/frostfs-cli/modules/ape_manager/add_chain.go
Normal file
86
cmd/frostfs-cli/modules/ape_manager/add_chain.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package apemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
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"
|
||||||
|
apeCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/ape"
|
||||||
|
apeSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape"
|
||||||
|
client_sdk "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
|
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var addCmd = &cobra.Command{
|
||||||
|
Use: "add",
|
||||||
|
Short: "Add rule chain for a target",
|
||||||
|
Run: add,
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
commonflags.Bind(cmd)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTarget(cmd *cobra.Command) (ct apeSDK.ChainTarget) {
|
||||||
|
t := apeCmd.ParseTarget(cmd)
|
||||||
|
|
||||||
|
ct.Name = t.Name
|
||||||
|
|
||||||
|
switch t.Type {
|
||||||
|
case engine.Namespace:
|
||||||
|
ct.TargetType = apeSDK.TargetTypeNamespace
|
||||||
|
case engine.Container:
|
||||||
|
ct.TargetType = apeSDK.TargetTypeContainer
|
||||||
|
case engine.User:
|
||||||
|
ct.TargetType = apeSDK.TargetTypeUser
|
||||||
|
case engine.Group:
|
||||||
|
ct.TargetType = apeSDK.TargetTypeGroup
|
||||||
|
default:
|
||||||
|
commonCmd.ExitOnErr(cmd, "conversion error: %w", fmt.Errorf("unknown type '%c'", t.Type))
|
||||||
|
}
|
||||||
|
return ct
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseChain(cmd *cobra.Command) apeSDK.Chain {
|
||||||
|
c := apeCmd.ParseChain(cmd)
|
||||||
|
serialized := c.Bytes()
|
||||||
|
return apeSDK.Chain{
|
||||||
|
Raw: serialized,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func add(cmd *cobra.Command, _ []string) {
|
||||||
|
c := parseChain(cmd)
|
||||||
|
|
||||||
|
target := parseTarget(cmd)
|
||||||
|
|
||||||
|
key := key.Get(cmd)
|
||||||
|
cli := internalclient.GetSDKClientByFlag(cmd, key, commonflags.RPC)
|
||||||
|
|
||||||
|
res, err := cli.APEManagerAddChain(cmd.Context(), client_sdk.PrmAPEManagerAddChain{
|
||||||
|
ChainTarget: target,
|
||||||
|
Chain: c,
|
||||||
|
})
|
||||||
|
|
||||||
|
commonCmd.ExitOnErr(cmd, "add chain error: %w", err)
|
||||||
|
|
||||||
|
cmd.Println("Rule has been added.")
|
||||||
|
cmd.Println("Chain ID: ", string(res.ChainID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func initAddCmd() {
|
||||||
|
commonflags.Init(addCmd)
|
||||||
|
|
||||||
|
ff := addCmd.Flags()
|
||||||
|
ff.StringArray(apeCmd.RuleFlag, []string{}, apeCmd.RuleFlagDesc)
|
||||||
|
ff.String(apeCmd.PathFlag, "", apeCmd.PathFlagDesc)
|
||||||
|
ff.String(apeCmd.ChainIDFlag, "", apeCmd.ChainIDFlagDesc)
|
||||||
|
ff.String(apeCmd.TargetNameFlag, "", apeCmd.TargetNameFlagDesc)
|
||||||
|
ff.String(apeCmd.TargetTypeFlag, "", apeCmd.TargetTypeFlagDesc)
|
||||||
|
_ = addCmd.MarkFlagRequired(apeCmd.TargetTypeFlag)
|
||||||
|
ff.Bool(apeCmd.ChainIDHexFlag, false, apeCmd.ChainIDHexFlagDesc)
|
||||||
|
|
||||||
|
addCmd.MarkFlagsMutuallyExclusive(apeCmd.PathFlag, apeCmd.RuleFlag)
|
||||||
|
}
|
49
cmd/frostfs-cli/modules/ape_manager/list_chain.go
Normal file
49
cmd/frostfs-cli/modules/ape_manager/list_chain.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package apemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
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"
|
||||||
|
apeCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/ape"
|
||||||
|
client_sdk "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
|
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var listCmd = &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "List rule chains defined on target",
|
||||||
|
Run: list,
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
commonflags.Bind(cmd)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func list(cmd *cobra.Command, _ []string) {
|
||||||
|
target := parseTarget(cmd)
|
||||||
|
|
||||||
|
key := key.Get(cmd)
|
||||||
|
cli := internalclient.GetSDKClientByFlag(cmd, key, commonflags.RPC)
|
||||||
|
|
||||||
|
resp, err := cli.APEManagerListChains(cmd.Context(),
|
||||||
|
client_sdk.PrmAPEManagerListChains{
|
||||||
|
ChainTarget: target,
|
||||||
|
})
|
||||||
|
commonCmd.ExitOnErr(cmd, "list chains call error: %w", err)
|
||||||
|
|
||||||
|
for _, respChain := range resp.Chains {
|
||||||
|
var chain apechain.Chain
|
||||||
|
commonCmd.ExitOnErr(cmd, "decode error: %w", chain.DecodeBytes(respChain.Raw))
|
||||||
|
apeCmd.PrintHumanReadableAPEChain(cmd, &chain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initListCmd() {
|
||||||
|
commonflags.Init(listCmd)
|
||||||
|
|
||||||
|
ff := listCmd.Flags()
|
||||||
|
ff.String(apeCmd.TargetNameFlag, "", apeCmd.TargetNameFlagDesc)
|
||||||
|
ff.String(apeCmd.TargetTypeFlag, "", apeCmd.TargetTypeFlagDesc)
|
||||||
|
_ = listCmd.MarkFlagRequired(apeCmd.TargetTypeFlag)
|
||||||
|
}
|
51
cmd/frostfs-cli/modules/ape_manager/remove_chain.go
Normal file
51
cmd/frostfs-cli/modules/ape_manager/remove_chain.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package apemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
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"
|
||||||
|
apeCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/ape"
|
||||||
|
client_sdk "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var removeCmd = &cobra.Command{
|
||||||
|
Use: "remove",
|
||||||
|
Short: "Remove rule chain for a target",
|
||||||
|
Run: remove,
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
commonflags.Bind(cmd)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func remove(cmd *cobra.Command, _ []string) {
|
||||||
|
target := parseTarget(cmd)
|
||||||
|
|
||||||
|
key := key.Get(cmd)
|
||||||
|
cli := internalclient.GetSDKClientByFlag(cmd, key, commonflags.RPC)
|
||||||
|
|
||||||
|
chainID := apeCmd.ParseChainID(cmd)
|
||||||
|
chainIDRaw := []byte(chainID)
|
||||||
|
|
||||||
|
_, err := cli.APEManagerRemoveChain(cmd.Context(), client_sdk.PrmAPEManagerRemoveChain{
|
||||||
|
ChainTarget: target,
|
||||||
|
ChainID: chainIDRaw,
|
||||||
|
})
|
||||||
|
|
||||||
|
commonCmd.ExitOnErr(cmd, "remove chain error: %w", err)
|
||||||
|
|
||||||
|
cmd.Println("\nRule has been removed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initRemoveCmd() {
|
||||||
|
commonflags.Init(removeCmd)
|
||||||
|
|
||||||
|
ff := removeCmd.Flags()
|
||||||
|
ff.String(apeCmd.TargetNameFlag, "", apeCmd.TargetNameFlagDesc)
|
||||||
|
ff.String(apeCmd.TargetTypeFlag, "", apeCmd.TargetTypeFlagDesc)
|
||||||
|
_ = removeCmd.MarkFlagRequired(apeCmd.TargetTypeFlag)
|
||||||
|
ff.String(apeCmd.ChainIDFlag, "", apeCmd.ChainIDFlagDesc)
|
||||||
|
_ = removeCmd.MarkFlagRequired(apeCmd.ChainIDFlag)
|
||||||
|
ff.Bool(apeCmd.ChainIDHexFlag, false, apeCmd.ChainIDHexFlagDesc)
|
||||||
|
}
|
21
cmd/frostfs-cli/modules/ape_manager/root.go
Normal file
21
cmd/frostfs-cli/modules/ape_manager/root.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package apemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Cmd = &cobra.Command{
|
||||||
|
Use: "ape-manager",
|
||||||
|
Short: "Operations with APE manager",
|
||||||
|
Long: `Operations with APE manager`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Cmd.AddCommand(addCmd)
|
||||||
|
Cmd.AddCommand(removeCmd)
|
||||||
|
Cmd.AddCommand(listCmd)
|
||||||
|
|
||||||
|
initAddCmd()
|
||||||
|
initRemoveCmd()
|
||||||
|
initListCmd()
|
||||||
|
}
|
|
@ -15,10 +15,12 @@ import (
|
||||||
eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
eaclFlag = "eacl"
|
eaclFlag = "eacl"
|
||||||
|
apeFlag = "ape"
|
||||||
issuedAtFlag = "issued-at"
|
issuedAtFlag = "issued-at"
|
||||||
notValidBeforeFlag = "not-valid-before"
|
notValidBeforeFlag = "not-valid-before"
|
||||||
ownerFlag = "owner"
|
ownerFlag = "owner"
|
||||||
|
@ -37,10 +39,17 @@ In this case --` + commonflags.RPC + ` flag should be specified and the epoch in
|
||||||
is set to current epoch + n.
|
is set to current epoch + n.
|
||||||
`,
|
`,
|
||||||
Run: createToken,
|
Run: createToken,
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
ff := cmd.Flags()
|
||||||
|
|
||||||
|
_ = viper.BindPFlag(commonflags.WalletPath, ff.Lookup(commonflags.WalletPath))
|
||||||
|
_ = viper.BindPFlag(commonflags.Account, ff.Lookup(commonflags.Account))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createCmd.Flags().StringP(eaclFlag, "e", "", "Path to the extended ACL table (mutually exclusive with --impersonate flag)")
|
createCmd.Flags().StringP(eaclFlag, "e", "", "Path to the extended ACL table (mutually exclusive with --impersonate and --ape flag)")
|
||||||
|
createCmd.Flags().StringP(apeFlag, "a", "", "Path to the JSON-encoded APE override (mutually exclusive with --impersonate and --eacl flag)")
|
||||||
createCmd.Flags().StringP(issuedAtFlag, "i", "+0", "Epoch to issue token at")
|
createCmd.Flags().StringP(issuedAtFlag, "i", "+0", "Epoch to issue token at")
|
||||||
createCmd.Flags().StringP(notValidBeforeFlag, "n", "+0", "Not valid before epoch")
|
createCmd.Flags().StringP(notValidBeforeFlag, "n", "+0", "Not valid before epoch")
|
||||||
createCmd.Flags().StringP(commonflags.ExpireAt, "x", "", "The last active epoch for the token")
|
createCmd.Flags().StringP(commonflags.ExpireAt, "x", "", "The last active epoch for the token")
|
||||||
|
@ -49,13 +58,15 @@ func init() {
|
||||||
createCmd.Flags().Bool(jsonFlag, false, "Output token in JSON")
|
createCmd.Flags().Bool(jsonFlag, false, "Output token in JSON")
|
||||||
createCmd.Flags().Bool(impersonateFlag, false, "Mark token as impersonate to consider the token signer as the request owner (mutually exclusive with --eacl flag)")
|
createCmd.Flags().Bool(impersonateFlag, false, "Mark token as impersonate to consider the token signer as the request owner (mutually exclusive with --eacl flag)")
|
||||||
createCmd.Flags().StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage)
|
createCmd.Flags().StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage)
|
||||||
|
createCmd.Flags().StringP(commonflags.WalletPath, commonflags.WalletPathShorthand, commonflags.WalletPathDefault, commonflags.WalletPathUsage)
|
||||||
|
createCmd.Flags().StringP(commonflags.Account, commonflags.AccountShorthand, commonflags.AccountDefault, commonflags.AccountUsage)
|
||||||
|
|
||||||
createCmd.MarkFlagsMutuallyExclusive(eaclFlag, impersonateFlag)
|
createCmd.MarkFlagsMutuallyExclusive(eaclFlag, apeFlag, impersonateFlag)
|
||||||
|
|
||||||
_ = cobra.MarkFlagFilename(createCmd.Flags(), eaclFlag)
|
_ = cobra.MarkFlagFilename(createCmd.Flags(), eaclFlag)
|
||||||
|
_ = cobra.MarkFlagFilename(createCmd.Flags(), apeFlag)
|
||||||
|
|
||||||
_ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.ExpireAt)
|
_ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.ExpireAt)
|
||||||
_ = cobra.MarkFlagRequired(createCmd.Flags(), ownerFlag)
|
|
||||||
_ = cobra.MarkFlagRequired(createCmd.Flags(), outFlag)
|
_ = cobra.MarkFlagRequired(createCmd.Flags(), outFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,16 +107,16 @@ func createToken(cmd *cobra.Command, _ []string) {
|
||||||
fmt.Errorf("expiration epoch is less than not-valid-before epoch: %d < %d", exp, nvb))
|
fmt.Errorf("expiration epoch is less than not-valid-before epoch: %d < %d", exp, nvb))
|
||||||
}
|
}
|
||||||
|
|
||||||
ownerStr, _ := cmd.Flags().GetString(ownerFlag)
|
|
||||||
|
|
||||||
var ownerID user.ID
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't parse recipient: %w", ownerID.DecodeString(ownerStr))
|
|
||||||
|
|
||||||
var b bearer.Token
|
var b bearer.Token
|
||||||
b.SetExp(exp)
|
b.SetExp(exp)
|
||||||
b.SetNbf(nvb)
|
b.SetNbf(nvb)
|
||||||
b.SetIat(iat)
|
b.SetIat(iat)
|
||||||
|
|
||||||
|
if ownerStr, _ := cmd.Flags().GetString(ownerFlag); ownerStr != "" {
|
||||||
|
var ownerID user.ID
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't parse recipient: %w", ownerID.DecodeString(ownerStr))
|
||||||
b.ForUser(ownerID)
|
b.ForUser(ownerID)
|
||||||
|
}
|
||||||
|
|
||||||
impersonate, _ := cmd.Flags().GetBool(impersonateFlag)
|
impersonate, _ := cmd.Flags().GetBool(impersonateFlag)
|
||||||
b.SetImpersonate(impersonate)
|
b.SetImpersonate(impersonate)
|
||||||
|
@ -119,6 +130,14 @@ func createToken(cmd *cobra.Command, _ []string) {
|
||||||
b.SetEACLTable(*table)
|
b.SetEACLTable(*table)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apePath, _ := cmd.Flags().GetString(apeFlag)
|
||||||
|
if apePath != "" {
|
||||||
|
var apeOverride bearer.APEOverride
|
||||||
|
raw, err := os.ReadFile(apePath)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't read APE rules: %w", err)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't parse APE rules: %w", json.Unmarshal(raw, &apeOverride))
|
||||||
|
b.SetAPEOverride(apeOverride)
|
||||||
|
}
|
||||||
var data []byte
|
var data []byte
|
||||||
|
|
||||||
toJSON, _ := cmd.Flags().GetBool(jsonFlag)
|
toJSON, _ := cmd.Flags().GetBool(jsonFlag)
|
||||||
|
|
76
cmd/frostfs-cli/modules/bearer/generate_override.go
Normal file
76
cmd/frostfs-cli/modules/bearer/generate_override.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package bearer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
apeCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/ape"
|
||||||
|
apeSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||||
|
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
outputFlag = "output"
|
||||||
|
)
|
||||||
|
|
||||||
|
var generateAPEOverrideCmd = &cobra.Command{
|
||||||
|
Use: "generate-ape-override",
|
||||||
|
Short: "Generate APE override.",
|
||||||
|
Long: `Generate APE override by target and APE chains. Util command.
|
||||||
|
|
||||||
|
Generated APE override can be dumped to a file in JSON format that is passed to
|
||||||
|
"create" command.
|
||||||
|
`,
|
||||||
|
Run: genereateAPEOverride,
|
||||||
|
}
|
||||||
|
|
||||||
|
func genereateAPEOverride(cmd *cobra.Command, _ []string) {
|
||||||
|
c := apeCmd.ParseChain(cmd)
|
||||||
|
|
||||||
|
targetCID, _ := cmd.Flags().GetString(commonflags.CIDFlag)
|
||||||
|
var cid cidSDK.ID
|
||||||
|
commonCmd.ExitOnErr(cmd, "invalid cid format: %w", cid.DecodeString(targetCID))
|
||||||
|
|
||||||
|
override := &bearer.APEOverride{
|
||||||
|
Target: apeSDK.ChainTarget{
|
||||||
|
TargetType: apeSDK.TargetTypeContainer,
|
||||||
|
Name: targetCID,
|
||||||
|
},
|
||||||
|
Chains: []apeSDK.Chain{
|
||||||
|
{
|
||||||
|
Raw: c.Bytes(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
overrideMarshalled, err := override.MarshalJSON()
|
||||||
|
commonCmd.ExitOnErr(cmd, "failed to marshal APE override: %w", err)
|
||||||
|
|
||||||
|
outputPath, _ := cmd.Flags().GetString(outputFlag)
|
||||||
|
if outputPath != "" {
|
||||||
|
err := os.WriteFile(outputPath, []byte(overrideMarshalled), 0o644)
|
||||||
|
commonCmd.ExitOnErr(cmd, "dump error: %w", err)
|
||||||
|
} else {
|
||||||
|
fmt.Print("\n")
|
||||||
|
fmt.Println(string(overrideMarshalled))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ff := generateAPEOverrideCmd.Flags()
|
||||||
|
|
||||||
|
ff.StringP(commonflags.CIDFlag, "", "", "Target container ID.")
|
||||||
|
_ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.CIDFlag)
|
||||||
|
|
||||||
|
ff.StringArray(apeCmd.RuleFlag, []string{}, "Rule statement")
|
||||||
|
ff.String(apeCmd.PathFlag, "", "Path to encoded chain in JSON or binary format")
|
||||||
|
ff.String(apeCmd.ChainIDFlag, "", "Assign ID to the parsed chain")
|
||||||
|
ff.Bool(apeCmd.ChainIDHexFlag, false, "Flag to parse chain ID as hex")
|
||||||
|
|
||||||
|
ff.String(outputFlag, "", "Output path to dump result JSON-encoded APE override")
|
||||||
|
_ = cobra.MarkFlagFilename(createCmd.Flags(), outputFlag)
|
||||||
|
}
|
|
@ -11,4 +11,5 @@ var Cmd = &cobra.Command{
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Cmd.AddCommand(createCmd)
|
Cmd.AddCommand(createCmd)
|
||||||
|
Cmd.AddCommand(generateAPEOverrideCmd)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,22 +7,20 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
containerApi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
containerApi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
containerACL string
|
|
||||||
containerPolicy string
|
containerPolicy string
|
||||||
containerAttributes []string
|
containerAttributes []string
|
||||||
containerAwait bool
|
containerAwait bool
|
||||||
|
@ -58,14 +56,28 @@ It will be stored in sidechain when inner ring will accepts it.`,
|
||||||
"use --force option to skip this check: %w", err)
|
"use --force option to skip this check: %w", err)
|
||||||
|
|
||||||
for i, nodes := range nodesByRep {
|
for i, nodes := range nodesByRep {
|
||||||
if placementPolicy.ReplicaNumberByIndex(i) > uint32(len(nodes)) {
|
if repNum := placementPolicy.ReplicaDescriptor(i).NumberOfObjects(); repNum > 0 {
|
||||||
|
if repNum > uint32(len(nodes)) {
|
||||||
commonCmd.ExitOnErr(cmd, "", fmt.Errorf(
|
commonCmd.ExitOnErr(cmd, "", fmt.Errorf(
|
||||||
"the number of nodes '%d' in selector is not enough for the number of replicas '%d', "+
|
"the number of nodes '%d' in selector is not enough for the number of replicas '%d', "+
|
||||||
"use --force option to skip this check",
|
"use --force option to skip this check",
|
||||||
len(nodes),
|
len(nodes),
|
||||||
placementPolicy.ReplicaNumberByIndex(i),
|
repNum,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
} else if ecParts := placementPolicy.ReplicaDescriptor(i).TotalECPartCount(); ecParts > 0 {
|
||||||
|
if ecParts > uint32(len(nodes)) {
|
||||||
|
commonCmd.ExitOnErr(cmd, "", fmt.Errorf(
|
||||||
|
"the number of nodes '%d' in selector is not enough for EC placement '%d.%d', "+
|
||||||
|
"use --force option to skip this check",
|
||||||
|
len(nodes),
|
||||||
|
placementPolicy.ReplicaDescriptor(i).GetECDataCount(),
|
||||||
|
placementPolicy.ReplicaDescriptor(i).GetECParityCount(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
commonCmd.ExitOnErr(cmd, "%w", errors.New("no replication policy is set"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,9 +87,6 @@ It will be stored in sidechain when inner ring will accepts it.`,
|
||||||
err = parseAttributes(&cnr, containerAttributes)
|
err = parseAttributes(&cnr, containerAttributes)
|
||||||
commonCmd.ExitOnErr(cmd, "", err)
|
commonCmd.ExitOnErr(cmd, "", err)
|
||||||
|
|
||||||
var basicACL acl.Basic
|
|
||||||
commonCmd.ExitOnErr(cmd, "decode basic ACL string: %w", basicACL.DecodeString(containerACL))
|
|
||||||
|
|
||||||
tok := getSession(cmd)
|
tok := getSession(cmd)
|
||||||
|
|
||||||
if tok != nil {
|
if tok != nil {
|
||||||
|
@ -91,7 +100,6 @@ It will be stored in sidechain when inner ring will accepts it.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
cnr.SetPlacementPolicy(*placementPolicy)
|
cnr.SetPlacementPolicy(*placementPolicy)
|
||||||
cnr.SetBasicACL(basicACL)
|
|
||||||
|
|
||||||
var syncContainerPrm internalclient.SyncContainerPrm
|
var syncContainerPrm internalclient.SyncContainerPrm
|
||||||
syncContainerPrm.SetClient(cli)
|
syncContainerPrm.SetClient(cli)
|
||||||
|
@ -125,7 +133,7 @@ It will be stored in sidechain when inner ring will accepts it.`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < awaitTimeout; i++ {
|
for range awaitTimeout {
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
_, err := internalclient.GetContainer(cmd.Context(), getPrm)
|
_, err := internalclient.GetContainer(cmd.Context(), getPrm)
|
||||||
|
@ -149,10 +157,6 @@ func initContainerCreateCmd() {
|
||||||
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)
|
||||||
|
|
||||||
flags.StringVar(&containerACL, "basic-acl", acl.NamePrivate, fmt.Sprintf("HEX encoded basic ACL value or keywords like '%s', '%s', '%s'",
|
|
||||||
acl.NamePublicRW, acl.NamePrivate, acl.NamePublicROExtended,
|
|
||||||
))
|
|
||||||
flags.StringVarP(&containerPolicy, "policy", "p", "", "QL-encoded or JSON-encoded placement policy or path to file with it")
|
flags.StringVarP(&containerPolicy, "policy", "p", "", "QL-encoded or JSON-encoded placement policy or path to file with it")
|
||||||
flags.StringSliceVarP(&containerAttributes, "attributes", "a", nil, "Comma separated pairs of container attributes in form of Key1=Value1,Key2=Value2")
|
flags.StringSliceVarP(&containerAttributes, "attributes", "a", nil, "Comma separated pairs of container attributes in form of Key1=Value1,Key2=Value2")
|
||||||
flags.BoolVar(&containerAwait, "await", false, "Block execution until container is persisted")
|
flags.BoolVar(&containerAwait, "await", false, "Block execution until container is persisted")
|
||||||
|
|
|
@ -110,7 +110,7 @@ Only owner of the container has a permission to remove container.`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < awaitTimeout; i++ {
|
for range awaitTimeout {
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
_, err := internalclient.GetContainer(cmd.Context(), getPrm)
|
_, err := internalclient.GetContainer(cmd.Context(), getPrm)
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
package container
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
var getExtendedACLCmd = &cobra.Command{
|
|
||||||
Use: "get-eacl",
|
|
||||||
Short: "Get extended ACL table of container",
|
|
||||||
Long: `Get extended ACL table of container`,
|
|
||||||
Run: func(cmd *cobra.Command, _ []string) {
|
|
||||||
id := parseContainerID(cmd)
|
|
||||||
pk := key.GetOrGenerate(cmd)
|
|
||||||
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
|
||||||
|
|
||||||
eaclPrm := internalclient.EACLPrm{
|
|
||||||
Client: cli,
|
|
||||||
ClientParams: client.PrmContainerEACL{
|
|
||||||
ContainerID: &id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := internalclient.EACL(cmd.Context(), eaclPrm)
|
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
|
||||||
|
|
||||||
eaclTable := res.EACL()
|
|
||||||
|
|
||||||
if containerPathTo == "" {
|
|
||||||
cmd.Println("eACL: ")
|
|
||||||
common.PrettyPrintJSON(cmd, &eaclTable, "eACL")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var data []byte
|
|
||||||
|
|
||||||
if containerJSON {
|
|
||||||
data, err = eaclTable.MarshalJSON()
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't encode to JSON: %w", err)
|
|
||||||
} else {
|
|
||||||
data, err = eaclTable.Marshal()
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't encode to binary: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Println("dumping data to file:", containerPathTo)
|
|
||||||
|
|
||||||
err = os.WriteFile(containerPathTo, data, 0o644)
|
|
||||||
commonCmd.ExitOnErr(cmd, "could not write eACL to file: %w", err)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func initContainerGetEACLCmd() {
|
|
||||||
commonflags.Init(getExtendedACLCmd)
|
|
||||||
|
|
||||||
flags := getExtendedACLCmd.Flags()
|
|
||||||
|
|
||||||
flags.StringVar(&containerID, commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
|
|
||||||
flags.StringVar(&containerPathTo, "to", "", "Path to dump encoded container (default: binary encoded)")
|
|
||||||
flags.BoolVar(&containerJSON, commonflags.JSON, false, "Encode EACL table in json format")
|
|
||||||
}
|
|
|
@ -1,16 +1,16 @@
|
||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// flags of list command.
|
// flags of list command.
|
||||||
|
@ -18,6 +18,8 @@ const (
|
||||||
flagListPrintAttr = "with-attr"
|
flagListPrintAttr = "with-attr"
|
||||||
flagListContainerOwner = "owner"
|
flagListContainerOwner = "owner"
|
||||||
flagListName = "name"
|
flagListName = "name"
|
||||||
|
|
||||||
|
generateKeyContainerUsage = commonflags.GenerateKeyUsage + ", should be used with --owner flag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// flag vars of list command.
|
// flag vars of list command.
|
||||||
|
@ -34,6 +36,11 @@ var listContainersCmd = &cobra.Command{
|
||||||
Run: func(cmd *cobra.Command, _ []string) {
|
Run: func(cmd *cobra.Command, _ []string) {
|
||||||
var idUser user.ID
|
var idUser user.ID
|
||||||
|
|
||||||
|
generateKey, _ := cmd.Flags().GetBool(commonflags.GenerateKey)
|
||||||
|
if flagVarListContainerOwner == "" && generateKey {
|
||||||
|
cmd.PrintErrln("WARN: using -g without --owner - output will be empty")
|
||||||
|
}
|
||||||
|
|
||||||
key := key.GetOrGenerate(cmd)
|
key := key.GetOrGenerate(cmd)
|
||||||
|
|
||||||
if flagVarListContainerOwner == "" {
|
if flagVarListContainerOwner == "" {
|
||||||
|
@ -47,48 +54,59 @@ var listContainersCmd = &cobra.Command{
|
||||||
|
|
||||||
var prm internalclient.ListContainersPrm
|
var prm internalclient.ListContainersPrm
|
||||||
prm.SetClient(cli)
|
prm.SetClient(cli)
|
||||||
prm.Account = idUser
|
prm.OwnerID = idUser
|
||||||
|
|
||||||
res, err := internalclient.ListContainers(cmd.Context(), prm)
|
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
|
||||||
|
|
||||||
prmGet := internalclient.GetContainerPrm{
|
prmGet := internalclient.GetContainerPrm{
|
||||||
Client: cli,
|
Client: cli,
|
||||||
}
|
}
|
||||||
|
var containerIDs []cid.ID
|
||||||
|
|
||||||
containerIDs := res.SortedIDList()
|
err := internalclient.ListContainersStream(cmd.Context(), prm, func(id cid.ID) bool {
|
||||||
for _, cnrID := range containerIDs {
|
printContainer(cmd, prmGet, id)
|
||||||
if flagVarListName == "" && !flagVarListPrintAttr {
|
return false
|
||||||
cmd.Println(cnrID.String())
|
})
|
||||||
continue
|
if err == nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cnrID := cnrID
|
if e, ok := status.FromError(err); ok && e.Code() == codes.Unimplemented {
|
||||||
prmGet.ClientParams.ContainerID = &cnrID
|
res, err := internalclient.ListContainers(cmd.Context(), prm)
|
||||||
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
containerIDs = res.SortedIDList()
|
||||||
|
} else {
|
||||||
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cnrID := range containerIDs {
|
||||||
|
printContainer(cmd, prmGet, cnrID)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func printContainer(cmd *cobra.Command, prmGet internalclient.GetContainerPrm, id cid.ID) {
|
||||||
|
if flagVarListName == "" && !flagVarListPrintAttr {
|
||||||
|
cmd.Println(id.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
prmGet.ClientParams.ContainerID = &id
|
||||||
res, err := internalclient.GetContainer(cmd.Context(), prmGet)
|
res, err := internalclient.GetContainer(cmd.Context(), prmGet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.Printf(" failed to read attributes: %v\n", err)
|
cmd.Printf(" failed to read attributes: %v\n", err)
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cnr := res.Container()
|
cnr := res.Container()
|
||||||
if cnrName := containerSDK.Name(cnr); flagVarListName != "" && cnrName != flagVarListName {
|
if cnrName := containerSDK.Name(cnr); flagVarListName != "" && cnrName != flagVarListName {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
cmd.Println(cnrID.String())
|
cmd.Println(id.String())
|
||||||
|
|
||||||
if flagVarListPrintAttr {
|
if flagVarListPrintAttr {
|
||||||
cnr.IterateAttributes(func(key, val string) {
|
cnr.IterateUserAttributes(func(key, val string) {
|
||||||
if !strings.HasPrefix(key, container.SysAttributePrefix) && !strings.HasPrefix(key, container.SysAttributePrefixNeoFS) {
|
|
||||||
// FIXME(@cthulhu-rider): https://git.frostfs.info/TrueCloudLab/frostfs-sdk-go/issues/97
|
|
||||||
// Use dedicated method to skip system attributes.
|
|
||||||
cmd.Printf(" %s: %s\n", key, val)
|
cmd.Printf(" %s: %s\n", key, val)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func initContainerListContainersCmd() {
|
func initContainerListContainersCmd() {
|
||||||
commonflags.Init(listContainersCmd)
|
commonflags.Init(listContainersCmd)
|
||||||
|
@ -104,4 +122,5 @@ func initContainerListContainersCmd() {
|
||||||
flags.BoolVar(&flagVarListPrintAttr, flagListPrintAttr, false,
|
flags.BoolVar(&flagVarListPrintAttr, flagListPrintAttr, false,
|
||||||
"Request and print attributes of each container",
|
"Request and print attributes of each container",
|
||||||
)
|
)
|
||||||
|
flags.Lookup(commonflags.GenerateKey).Usage = generateKeyContainerUsage
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
|
@ -67,14 +64,8 @@ var listContainerObjectsCmd = &cobra.Command{
|
||||||
|
|
||||||
resHead, err := internalclient.HeadObject(cmd.Context(), prmHead)
|
resHead, err := internalclient.HeadObject(cmd.Context(), prmHead)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
attrs := resHead.Header().Attributes()
|
for _, attr := range resHead.Header().UserAttributes() {
|
||||||
for i := range attrs {
|
cmd.Printf(" %s: %s\n", attr.Key(), attr.Value())
|
||||||
attrKey := attrs[i].Key()
|
|
||||||
if !strings.HasPrefix(attrKey, v2object.SysAttributePrefix) && !strings.HasPrefix(attrKey, v2object.SysAttributePrefixNeoFS) {
|
|
||||||
// FIXME(@cthulhu-rider): https://git.frostfs.info/TrueCloudLab/frostfs-sdk-go/issues/97
|
|
||||||
// Use dedicated method to skip system attributes.
|
|
||||||
cmd.Printf(" %s: %s\n", attrKey, attrs[i].Value())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cmd.Printf(" failed to read attributes: %v\n", err)
|
cmd.Printf(" failed to read attributes: %v\n", err)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
|
@ -46,7 +47,14 @@ var containerNodesCmd = &cobra.Command{
|
||||||
commonCmd.ExitOnErr(cmd, "could not build container nodes for given container: %w", err)
|
commonCmd.ExitOnErr(cmd, "could not build container nodes for given container: %w", err)
|
||||||
|
|
||||||
for i := range cnrNodes {
|
for i := range cnrNodes {
|
||||||
cmd.Printf("Descriptor #%d, REP %d:\n", i+1, policy.ReplicaNumberByIndex(i))
|
if repNum := policy.ReplicaDescriptor(i).NumberOfObjects(); repNum > 0 {
|
||||||
|
cmd.Printf("Descriptor #%d, REP %d:\n", i+1, repNum)
|
||||||
|
} else if ecParts := policy.ReplicaDescriptor(i).TotalECPartCount(); ecParts > 0 {
|
||||||
|
cmd.Printf("Descriptor #%d, EC %d.%d:\n", i+1, policy.ReplicaDescriptor(i).GetECDataCount(),
|
||||||
|
policy.ReplicaDescriptor(i).GetECParityCount())
|
||||||
|
} else {
|
||||||
|
commonCmd.ExitOnErr(cmd, "%w", errors.New("no replication policy is set"))
|
||||||
|
}
|
||||||
for j := range cnrNodes[i] {
|
for j := range cnrNodes[i] {
|
||||||
commonCmd.PrettyPrintNodeInfo(cmd, cnrNodes[i][j], j, "\t", short)
|
commonCmd.PrettyPrintNodeInfo(cmd, cnrNodes[i][j], j, "\t", short)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -14,20 +13,19 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
|
"github.com/chzyer/readline"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type policyPlaygroundREPL struct {
|
type policyPlaygroundREPL struct {
|
||||||
cmd *cobra.Command
|
cmd *cobra.Command
|
||||||
args []string
|
|
||||||
nodes map[string]netmap.NodeInfo
|
nodes map[string]netmap.NodeInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPolicyPlaygroundREPL(cmd *cobra.Command, args []string) (*policyPlaygroundREPL, error) {
|
func newPolicyPlaygroundREPL(cmd *cobra.Command) (*policyPlaygroundREPL, error) {
|
||||||
return &policyPlaygroundREPL{
|
return &policyPlaygroundREPL{
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
args: args,
|
|
||||||
nodes: map[string]netmap.NodeInfo{},
|
nodes: map[string]netmap.NodeInfo{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -165,6 +163,16 @@ func (repl *policyPlaygroundREPL) netMap() netmap.NetMap {
|
||||||
return nm
|
return nm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var policyPlaygroundCompleter = readline.NewPrefixCompleter(
|
||||||
|
readline.PcItem("list"),
|
||||||
|
readline.PcItem("ls"),
|
||||||
|
readline.PcItem("add"),
|
||||||
|
readline.PcItem("load"),
|
||||||
|
readline.PcItem("remove"),
|
||||||
|
readline.PcItem("rm"),
|
||||||
|
readline.PcItem("eval"),
|
||||||
|
)
|
||||||
|
|
||||||
func (repl *policyPlaygroundREPL) run() error {
|
func (repl *policyPlaygroundREPL) run() error {
|
||||||
if len(viper.GetString(commonflags.RPC)) > 0 {
|
if len(viper.GetString(commonflags.RPC)) > 0 {
|
||||||
key := key.GetOrGenerate(repl.cmd)
|
key := key.GetOrGenerate(repl.cmd)
|
||||||
|
@ -191,22 +199,38 @@ func (repl *policyPlaygroundREPL) run() error {
|
||||||
"rm": repl.handleRemove,
|
"rm": repl.handleRemove,
|
||||||
"eval": repl.handleEval,
|
"eval": repl.handleEval,
|
||||||
}
|
}
|
||||||
for reader := bufio.NewReader(os.Stdin); ; {
|
|
||||||
fmt.Print("> ")
|
rl, err := readline.NewEx(&readline.Config{
|
||||||
line, err := reader.ReadString('\n')
|
Prompt: "> ",
|
||||||
|
InterruptPrompt: "^C",
|
||||||
|
AutoComplete: policyPlaygroundCompleter,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
return fmt.Errorf("error initializing readline: %w", err)
|
||||||
|
}
|
||||||
|
defer rl.Close()
|
||||||
|
|
||||||
|
var exit bool
|
||||||
|
for {
|
||||||
|
line, err := rl.Readline()
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, readline.ErrInterrupt) {
|
||||||
|
if exit {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("reading line: %v", err)
|
exit = true
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
return fmt.Errorf("reading line: %w", err)
|
||||||
|
}
|
||||||
|
exit = false
|
||||||
|
|
||||||
parts := strings.Fields(line)
|
parts := strings.Fields(line)
|
||||||
if len(parts) == 0 {
|
if len(parts) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cmd := parts[0]
|
cmd := parts[0]
|
||||||
handler, exists := cmdHandlers[cmd]
|
if handler, exists := cmdHandlers[cmd]; exists {
|
||||||
if exists {
|
|
||||||
if err := handler(parts[1:]); err != nil {
|
if err := handler(parts[1:]); err != nil {
|
||||||
fmt.Printf("error: %v\n", err)
|
fmt.Printf("error: %v\n", err)
|
||||||
}
|
}
|
||||||
|
@ -221,8 +245,8 @@ var policyPlaygroundCmd = &cobra.Command{
|
||||||
Short: "A REPL for testing placement policies",
|
Short: "A REPL for testing placement policies",
|
||||||
Long: `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.`,
|
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) {
|
Run: func(cmd *cobra.Command, _ []string) {
|
||||||
repl, err := newPolicyPlaygroundREPL(cmd, args)
|
repl, err := newPolicyPlaygroundREPL(cmd)
|
||||||
commonCmd.ExitOnErr(cmd, "could not create policy playground: %w", err)
|
commonCmd.ExitOnErr(cmd, "could not create policy playground: %w", err)
|
||||||
commonCmd.ExitOnErr(cmd, "policy playground failed: %w", repl.run())
|
commonCmd.ExitOnErr(cmd, "policy playground failed: %w", repl.run())
|
||||||
},
|
},
|
||||||
|
|
|
@ -25,8 +25,6 @@ func init() {
|
||||||
deleteContainerCmd,
|
deleteContainerCmd,
|
||||||
listContainerObjectsCmd,
|
listContainerObjectsCmd,
|
||||||
getContainerInfoCmd,
|
getContainerInfoCmd,
|
||||||
getExtendedACLCmd,
|
|
||||||
setExtendedACLCmd,
|
|
||||||
containerNodesCmd,
|
containerNodesCmd,
|
||||||
policyPlaygroundCmd,
|
policyPlaygroundCmd,
|
||||||
}
|
}
|
||||||
|
@ -38,8 +36,6 @@ func init() {
|
||||||
initContainerDeleteCmd()
|
initContainerDeleteCmd()
|
||||||
initContainerListObjectsCmd()
|
initContainerListObjectsCmd()
|
||||||
initContainerInfoCmd()
|
initContainerInfoCmd()
|
||||||
initContainerGetEACLCmd()
|
|
||||||
initContainerSetEACLCmd()
|
|
||||||
initContainerNodesCmd()
|
initContainerNodesCmd()
|
||||||
initContainerPolicyPlaygroundCmd()
|
initContainerPolicyPlaygroundCmd()
|
||||||
|
|
||||||
|
@ -53,7 +49,6 @@ func init() {
|
||||||
}{
|
}{
|
||||||
{createContainerCmd, "PUT"},
|
{createContainerCmd, "PUT"},
|
||||||
{deleteContainerCmd, "DELETE"},
|
{deleteContainerCmd, "DELETE"},
|
||||||
{setExtendedACLCmd, "SETEACL"},
|
|
||||||
} {
|
} {
|
||||||
commonflags.InitSession(el.cmd, "container "+el.verb)
|
commonflags.InitSession(el.cmd, "container "+el.verb)
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue