forked from TrueCloudLab/frostfs-sdk-go
Compare commits
100 commits
KirillovDe
...
master
Author | SHA1 | Date | |
---|---|---|---|
5d62cef27e | |||
c0c0c588b5 | |||
2f88460172 | |||
66cb5dcf34 | |||
91e80ba743 | |||
c243b443bc | |||
aa8ffebc63 | |||
9d40228cec | |||
af40dc68f0 | |||
981d24a493 | |||
19adb4dffa | |||
51e022ab8c | |||
0d3dacb515 | |||
b2e302624d | |||
fcbf96add6 | |||
4f48f6c9e0 | |||
ec59ebfd88 | |||
030ff2f122 | |||
0f7455ff7a | |||
e6b662cfa6 | |||
406c2324d4 | |||
10482ffbed | |||
f5b23eb225 | |||
70f23dd1ea | |||
57f874048b | |||
a397d1fd15 | |||
9803c2816a | |||
d04d96b42e | |||
9a072a8f49 | |||
15b4287092 | |||
d4fe9a193d | |||
c42a6119ff | |||
29b188db57 | |||
38b03ff28b | |||
0fa23a9b14 | |||
d0762d037d | |||
db5b89496d | |||
7c75db2f2d | |||
dce55a436a | |||
cae2f37cdd | |||
f60bea4be5 | |||
a16fc40c39 | |||
40d966bec2 | |||
|
d0c5d837d2 | ||
237b90f744 | |||
c8e620ad24 | |||
591dd1247d | |||
57619fbbe4 | |||
8bc8f1f365 | |||
09ed2863fc | |||
8e2f77890f | |||
8852b262f2 | |||
b2c66cb99e | |||
6c9b92c9dc | |||
772fa90983 | |||
1bfa9ecdb0 | |||
55b06cd764 | |||
|
f41860f9bd | ||
|
423b320f91 | ||
f8c34b45f3 | |||
fa9573e857 | |||
bc62e2f712 | |||
31271ad8b1 | |||
25e9336d68 | |||
4cd755877c | |||
1395b282fe | |||
708d933fe3 | |||
4fa52312c7 | |||
552219b8e1 | |||
cfb8a7b914 | |||
4438f115fb | |||
bec77f280a | |||
df2090c2be | |||
7e6592b28e | |||
d589d51509 | |||
25588ee3be | |||
9407f30248 | |||
94c0a607b5 | |||
e45647de3c | |||
611e20587b | |||
eba6831125 | |||
7e3810d654 | |||
cc0fef2c55 | |||
b696d3c70e | |||
1c94309d7a | |||
f43f18ecda | |||
ac8442bf99 | |||
0ad877288e | |||
0e1999c965 | |||
b461aa64b8 | |||
b761fd8070 | |||
94476f9055 | |||
5e759bf089 | |||
d4f5bba459 | |||
e9c1a2ab2b | |||
2cbc585edd | |||
e355e5eeba | |||
f08069ceeb | |||
dad99bad48 | |||
0d3a238d9c |
247 changed files with 9833 additions and 6690 deletions
20
.forgejo/workflows/dco.yml
Normal file
20
.forgejo/workflows/dco.yml
Normal file
|
@ -0,0 +1,20 @@
|
|||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
dco:
|
||||
name: DCO
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
|
||||
- name: Run commit format checker
|
||||
uses: https://git.alexvan.in/alexvanin/dco-go@v1
|
||||
with:
|
||||
from: 406c2324
|
31
.forgejo/workflows/tests.yml
Normal file
31
.forgejo/workflows/tests.yml
Normal file
|
@ -0,0 +1,31 @@
|
|||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: golangci-lint
|
||||
uses: https://github.com/golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: latest
|
||||
|
||||
tests:
|
||||
name: Tests
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go_versions: [ '1.19', '1.20' ]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '${{ matrix.go_versions }}'
|
||||
|
||||
- name: Run tests
|
||||
run: make test
|
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
|
@ -1 +1 @@
|
|||
* @alexvanin @fyrchik @cthulhu-rider
|
||||
* @TrueCloudLab/storage-core @TrueCloudLab/storage-services
|
||||
|
|
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: community, triage, bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Expected Behavior
|
||||
<!--- If you're describing a bug, tell us what should happen -->
|
||||
<!--- If you're suggesting a change/improvement, tell us how it should work -->
|
||||
|
||||
## Current Behavior
|
||||
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
|
||||
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
|
||||
|
||||
## Possible Solution
|
||||
<!--- Not obligatory -->
|
||||
<!--- If no reason/fix/additions for the bug can be suggested, -->
|
||||
<!--- uncomment the following phrase: -->
|
||||
|
||||
<!--- No fix can be suggested by a QA engineer. Further solutions shall be up to developers. -->
|
||||
|
||||
## Steps to Reproduce (for bugs)
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||
<!--- reproduce this bug. -->
|
||||
|
||||
1.
|
||||
|
||||
## Context
|
||||
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
||||
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
|
||||
|
||||
## Regression
|
||||
<!-- Is this issue a regression? (Yes / No) -->
|
||||
<!-- If Yes, optionally please include version or commit id or PR# that caused this regression, if you have these details. -->
|
||||
|
||||
## Your Environment
|
||||
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
||||
* Version used:
|
||||
* Server setup and configuration:
|
||||
* Operating System and version (`uname -a`):
|
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
blank_issues_enabled: false
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: community, triage
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Is your feature request related to a problem? Please describe.
|
||||
<!--- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||
|
||||
## Describe the solution you'd like
|
||||
<!--- A clear and concise description of what you want to happen. -->
|
||||
|
||||
## Describe alternatives you've considered
|
||||
<!--- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
## Additional context
|
||||
<!--- Add any other context or screenshots about the feature request here. -->
|
21
.github/workflows/dco.yml
vendored
21
.github/workflows/dco.yml
vendored
|
@ -1,21 +0,0 @@
|
|||
name: DCO check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
commits_check_job:
|
||||
runs-on: ubuntu-latest
|
||||
name: Commits Check
|
||||
steps:
|
||||
- name: Get PR Commits
|
||||
id: 'get-pr-commits'
|
||||
uses: tim-actions/get-pr-commits@master
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: DCO Check
|
||||
uses: tim-actions/dco@master
|
||||
with:
|
||||
commits: ${{ steps.get-pr-commits.outputs.commits }}
|
52
.github/workflows/tests.yml
vendored
52
.github/workflows/tests.yml
vendored
|
@ -1,52 +0,0 @@
|
|||
name: neofs-sdk-go tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
types: [opened, synchronize]
|
||||
paths-ignore:
|
||||
- '**/*.md'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Tests
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
go_versions: [ '1.17.x', '1.18.x', '1.19.x' ]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '${{ matrix.go_versions }}'
|
||||
|
||||
- name: Restore Go modules from cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /home/runner/go/pkg/mod
|
||||
key: deps-${{ hashFiles('go.sum') }}
|
||||
|
||||
- name: Update Go modules
|
||||
run: make dep
|
||||
|
||||
- name: Run tests
|
||||
run: make test
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: latest
|
||||
only-new-issues: true
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -20,4 +20,10 @@ vendor/
|
|||
|
||||
# coverage
|
||||
coverage.txt
|
||||
coverage.html
|
||||
coverage.html
|
||||
|
||||
# antlr tool jar
|
||||
antlr-*.jar
|
||||
|
||||
# tempfiles
|
||||
.cache
|
||||
|
|
10
.gitlint
Normal file
10
.gitlint
Normal file
|
@ -0,0 +1,10 @@
|
|||
[general]
|
||||
fail-without-commits=true
|
||||
contrib=CC1
|
||||
|
||||
[title-match-regex]
|
||||
regex=^\[\#[0-9Xx]+\]\s
|
||||
|
||||
[ignore-by-title]
|
||||
regex=^Release(.*)
|
||||
ignore=title-match-regex
|
|
@ -4,10 +4,10 @@
|
|||
# options for analysis running
|
||||
run:
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
timeout: 5m
|
||||
timeout: 10m
|
||||
|
||||
# include test files or not, default is true
|
||||
tests: true
|
||||
tests: false
|
||||
|
||||
# output configuration options
|
||||
output:
|
||||
|
@ -24,6 +24,13 @@ linters-settings:
|
|||
govet:
|
||||
# report about shadowed variables
|
||||
check-shadowing: false
|
||||
staticcheck:
|
||||
checks: ["all", "-SA1019"] # TODO Enable SA1019 after deprecated warning are fixed.
|
||||
funlen:
|
||||
lines: 80 # default 60
|
||||
statements: 60 # default 40
|
||||
gocognit:
|
||||
min-complexity: 40 # default 30
|
||||
|
||||
linters:
|
||||
enable:
|
||||
|
@ -32,28 +39,28 @@ linters:
|
|||
- revive
|
||||
|
||||
# some default golangci-lint linters
|
||||
- deadcode
|
||||
- errcheck
|
||||
- gosimple
|
||||
- godot
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- typecheck
|
||||
- unused
|
||||
- varcheck
|
||||
|
||||
# extra linters
|
||||
- bidichk
|
||||
- durationcheck
|
||||
- exhaustive
|
||||
- godot
|
||||
- exportloopref
|
||||
- gofmt
|
||||
- whitespace
|
||||
- goimports
|
||||
- misspell
|
||||
- predeclared
|
||||
- reassign
|
||||
- whitespace
|
||||
- containedctx
|
||||
- funlen
|
||||
- gocognit
|
||||
- contextcheck
|
||||
disable-all: true
|
||||
fast: false
|
||||
|
||||
issues:
|
||||
include:
|
||||
- EXC0002 # should have a comment
|
||||
- EXC0003 # test/Test ... consider calling this
|
||||
- EXC0004 # govet
|
||||
- EXC0005 # C-style breaks
|
||||
|
|
30
.pre-commit-config.yaml
Normal file
30
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
ci:
|
||||
autofix_prs: false
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-case-conflict
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-shebang-scripts-are-executable
|
||||
- id: check-merge-conflict
|
||||
- id: check-json
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
- id: trailing-whitespace
|
||||
args: [--markdown-linebreak-ext=md]
|
||||
- id: end-of-file-fixer
|
||||
exclude: "(.key|.interp|.tokens)$"
|
||||
|
||||
- repo: https://github.com/golangci/golangci-lint
|
||||
rev: v1.51.2
|
||||
hooks:
|
||||
- id: golangci-lint
|
||||
|
||||
- repo: https://github.com/jorisroovers/gitlint
|
||||
rev: v0.18.0
|
||||
hooks:
|
||||
- id: gitlint
|
||||
stages: [commit-msg]
|
4
Dockerfile
Normal file
4
Dockerfile
Normal file
|
@ -0,0 +1,4 @@
|
|||
FROM golang:1.19
|
||||
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install make openjdk-11-jre -y
|
||||
WORKDIR /work
|
21
Makefile
Normal file → Executable file
21
Makefile
Normal file → Executable file
|
@ -1,5 +1,7 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
ANTLR_VERSION="4.13.0"
|
||||
|
||||
# Run tests
|
||||
test:
|
||||
@go test ./... -cover
|
||||
|
@ -29,6 +31,23 @@ format:
|
|||
@echo "⇒ Processing goimports check"
|
||||
@goimports -w ./
|
||||
|
||||
policy:
|
||||
@wget -q https://www.antlr.org/download/antlr-${ANTLR_VERSION}-complete.jar -O antlr4-tool.jar
|
||||
@java -Xmx500M -cp "`pwd`/antlr4-tool.jar" "org.antlr.v4.Tool" -o `pwd`/netmap/parser/ -Dlanguage=Go -no-listener -visitor `pwd`/netmap/parser/Query.g4 `pwd`/netmap/parser/QueryLexer.g4
|
||||
|
||||
# Run `make %` in truecloudlab/frostfs-sdk-go container(Golang+Java)
|
||||
docker/%:
|
||||
@docker build -t truecloudlab/frostfs-sdk-go --platform linux/amd64 . > /dev/null
|
||||
@docker run --rm -t \
|
||||
-v `pwd`:/work \
|
||||
-u "$$(id -u):$$(id -g)" \
|
||||
--env HOME=/work \
|
||||
truecloudlab/frostfs-sdk-go make $*
|
||||
|
||||
# Synchronize tree service
|
||||
sync-tree:
|
||||
@./syncTree.sh
|
||||
|
||||
# Show this help prompt
|
||||
help:
|
||||
@echo ' Usage:'
|
||||
|
@ -37,4 +56,4 @@ help:
|
|||
@echo ''
|
||||
@echo ' Targets:'
|
||||
@echo ''
|
||||
@awk '/^#/{ comment = substr($$0,3) } comment && /^[a-zA-Z][a-zA-Z0-9_-]+ ?:/{ print " ", $$1, comment }' $(MAKEFILE_LIST) | column -t -s ':' | grep -v 'IGNORE' | sort -u
|
||||
@awk '/^#/{ comment = substr($$0,3) } comment && /^[a-zA-Z][a-zA-Z0-9_-]+ ?:/{ print " ", $$1, comment }' $(MAKEFILE_LIST) | column -t -s ':' | grep -v 'IGNORE' | sort -u
|
||||
|
|
20
README.md
20
README.md
|
@ -1,6 +1,6 @@
|
|||
# frostfs-sdk-go
|
||||
Go implementation of FrostFS SDK. It contains high-level version-independent wrappers
|
||||
for structures from [frostfs-api-go](https://github.com/TrueCloudLab/frostfs-api-go) as well as
|
||||
for structures from [frostfs-api-go](https://git.frostfs.info/TrueCloudLab/frostfs-api-go) as well as
|
||||
helper functions for simplifying node/dApp implementations.
|
||||
|
||||
## Repository structure
|
||||
|
@ -14,7 +14,7 @@ There is also a reference implementation of checking algorithm which is used in
|
|||
|
||||
### checksum
|
||||
Contains `Checksum` type encapsulating checksum as well as it's kind.
|
||||
Currently Sha256 and [Tillich-Zemor hashsum](https://github.com/TrueCloudLab/tzhash) are in use.
|
||||
Currently Sha256 and [Tillich-Zemor hashsum](https://git.frostfs.info/TrueCloudLab/tzhash) are in use.
|
||||
|
||||
### owner
|
||||
`owner.ID` type represents single account interacting with FrostFS. In v2 version of protocol
|
||||
|
@ -27,7 +27,7 @@ Contains Bearer token type with several FrostFS-specific methods.
|
|||
|
||||
### ns
|
||||
In FrostFS there are 2 types of name resolution: DNS and NNS. NNS stands for Neo Name Service
|
||||
is just a [contract](https://github.com/TrueCloudLab/frostfs-contract) deployed on a Neo blockchain.
|
||||
is just a [contract](https://git.frostfs.info/TrueCloudLab/frostfs-contract) deployed on a Neo blockchain.
|
||||
Basically, NNS is just a DNS-on-chain which can be used for resolving container nice-names as well
|
||||
as any other name in dApps. See our [CoreDNS plugin](https://github.com/nspcc-dev/coredns/tree/master/plugin/nns)
|
||||
for the example of how NNS can be integrated in DNS.
|
||||
|
@ -54,7 +54,7 @@ err := c.Dial(prmDial)
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
|
||||
defer cancel()
|
||||
|
||||
|
@ -77,7 +77,7 @@ if needed and perform any desired action. In the case above we may want to repor
|
|||
these details to the user as well as retry an operation, possibly with different parameters.
|
||||
Status wire-format is extendable and each node can report any set of details it wants.
|
||||
The set of reserved status codes can be found in
|
||||
[FrostFS API](https://github.com/TrueCloudLab/frostfs-api/blob/master/status/types.proto). There is also
|
||||
[FrostFS API](https://git.frostfs.info/TrueCloudLab/frostfs-api/src/branch/master/status/types.proto). There is also
|
||||
a `client.PrmInit.ResolveFrostFSFailures()` to seamlessly convert erroneous statuses into Go error type.
|
||||
|
||||
### policy
|
||||
|
@ -98,19 +98,19 @@ Contains CRUSH-like implementation of container node selection algorithm. Releva
|
|||
are described in this paper http://ceur-ws.org/Vol-2344/short10.pdf . Note that it can be
|
||||
outdated in some details.
|
||||
|
||||
`netmap/json_tests` subfolder contains language-agnostic tests for selection algorithm.
|
||||
`netmap/json_tests` subfolder contains language-agnostic tests for selection algorithm.
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
)
|
||||
|
||||
func placementNodes(addr *object.Address, p *netmap.PlacementPolicy, frostfsNodes []netmap.NodeInfo) {
|
||||
// Convert list of nodes in FrostFS API format to the intermediate representation.
|
||||
nodes := netmap.NodesFromInfo(nodes)
|
||||
|
||||
// Create new netmap (errors are skipped for the sake of clarity).
|
||||
// Create new netmap (errors are skipped for the sake of clarity).
|
||||
nm, _ := NewNetmap(nodes)
|
||||
|
||||
// Calculate nodes of container.
|
||||
|
@ -131,4 +131,4 @@ Contain simple API wrappers.
|
|||
Wrapper over `zap.Logger` which is used across FrostFS codebase.
|
||||
|
||||
### util
|
||||
Utilities for working with signature-related code.
|
||||
Utilities for working with signature-related code.
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package accounting
|
||||
|
||||
import "github.com/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||
|
||||
// Decimal represents decimal number for accounting operations.
|
||||
//
|
||||
// Decimal is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/accounting.Decimal
|
||||
// Decimal is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting.Decimal
|
||||
// message. See ReadFromV2 / WriteToV2 methods.
|
||||
//
|
||||
// Instances can be created using built-in var declaration.
|
||||
|
|
|
@ -3,8 +3,8 @@ package accounting_test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
v2accounting "github.com/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/accounting"
|
||||
v2accounting "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
|
@ -9,11 +9,11 @@ working with Fixed8 balance precision:
|
|||
dec.SetPrecision(8)
|
||||
|
||||
Instances can be also used to process FrostFS API V2 protocol messages
|
||||
(see neo.fs.v2.accounting package in https://github.com/TrueCloudLab/frostfs-api).
|
||||
(see neo.fs.v2.accounting package in https://git.frostfs.info/TrueCloudLab/frostfs-api).
|
||||
|
||||
On client side:
|
||||
|
||||
import "github.com/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||
|
||||
var msg accounting.Decimal
|
||||
dec.WriteToV2(&msg)
|
||||
|
|
|
@ -3,7 +3,7 @@ package accountingtest
|
|||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/accounting"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
|
||||
)
|
||||
|
||||
// Decimal returns random accounting.Decimal.
|
||||
|
|
|
@ -5,7 +5,7 @@ Note that importing the package into source files is highly discouraged.
|
|||
|
||||
Random instance generation functions can be useful when testing expects any value, e.g.:
|
||||
|
||||
import accountingtest "github.com/TrueCloudLab/frostfs-sdk-go/accounting/test"
|
||||
import accountingtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting/test"
|
||||
|
||||
dec := accountingtest.Decimal()
|
||||
// test the value
|
||||
|
|
26
audit/doc.go
26
audit/doc.go
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
Package audit provides features to process data audit in FrostFS system.
|
||||
|
||||
Result type groups values which can be gathered during data audit process:
|
||||
|
||||
var res audit.Result
|
||||
res.ForEpoch(32)
|
||||
res.ForContainer(cnr)
|
||||
// ...
|
||||
res.Complete()
|
||||
|
||||
Result instances can be stored in a binary format. On reporter side:
|
||||
|
||||
data := res.Marshal()
|
||||
// send data
|
||||
|
||||
On receiver side:
|
||||
|
||||
var res audit.Result
|
||||
err := res.Unmarshal(data)
|
||||
// ...
|
||||
|
||||
Using package types in an application is recommended to potentially work with
|
||||
different protocol versions with which these types are compatible.
|
||||
*/
|
||||
package audit
|
377
audit/result.go
377
audit/result.go
|
@ -1,377 +0,0 @@
|
|||
package audit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/audit"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/version"
|
||||
)
|
||||
|
||||
// Result represents report on the results of the data audit in FrostFS system.
|
||||
//
|
||||
// Result is mutually binary-compatible with github.com/TrueCloudLab/frostfs-api-go/v2/audit.DataAuditResult
|
||||
// message. See Marshal / Unmarshal methods.
|
||||
//
|
||||
// Instances can be created using built-in var declaration.
|
||||
type Result struct {
|
||||
versionEncoded bool
|
||||
|
||||
v2 audit.DataAuditResult
|
||||
}
|
||||
|
||||
// Marshal encodes Result into a canonical FrostFS binary format (Protocol Buffers
|
||||
// with direct field order).
|
||||
//
|
||||
// Writes version.Current() protocol version into the resulting message if Result
|
||||
// hasn't been already decoded from such a message using Unmarshal.
|
||||
//
|
||||
// See also Unmarshal.
|
||||
func (r *Result) Marshal() []byte {
|
||||
if !r.versionEncoded {
|
||||
var verV2 refs.Version
|
||||
version.Current().WriteToV2(&verV2)
|
||||
r.v2.SetVersion(&verV2)
|
||||
r.versionEncoded = true
|
||||
}
|
||||
|
||||
return r.v2.StableMarshal(nil)
|
||||
}
|
||||
|
||||
var errCIDNotSet = errors.New("container ID is not set")
|
||||
|
||||
// Unmarshal decodes Result from its canonical FrostFS binary format (Protocol Buffers
|
||||
// with direct field order). Returns an error describing a format violation.
|
||||
//
|
||||
// See also Marshal.
|
||||
func (r *Result) Unmarshal(data []byte) error {
|
||||
err := r.v2.Unmarshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.versionEncoded = true
|
||||
|
||||
// format checks
|
||||
|
||||
var cID cid.ID
|
||||
|
||||
cidV2 := r.v2.GetContainerID()
|
||||
if cidV2 == nil {
|
||||
return errCIDNotSet
|
||||
}
|
||||
|
||||
err = cID.ReadFromV2(*cidV2)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not convert V2 container ID: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
oID oid.ID
|
||||
oidV2 refs.ObjectID
|
||||
)
|
||||
|
||||
for _, oidV2 = range r.v2.GetPassSG() {
|
||||
err = oID.ReadFromV2(oidV2)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid passed storage group ID: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, oidV2 = range r.v2.GetFailSG() {
|
||||
err = oID.ReadFromV2(oidV2)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid failed storage group ID: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Epoch returns FrostFS epoch when the data associated with the Result was audited.
|
||||
//
|
||||
// Zero Result has zero epoch.
|
||||
//
|
||||
// See also ForEpoch.
|
||||
func (r Result) Epoch() uint64 {
|
||||
return r.v2.GetAuditEpoch()
|
||||
}
|
||||
|
||||
// ForEpoch specifies FrostFS epoch when the data associated with the Result was audited.
|
||||
//
|
||||
// See also Epoch.
|
||||
func (r *Result) ForEpoch(epoch uint64) {
|
||||
r.v2.SetAuditEpoch(epoch)
|
||||
}
|
||||
|
||||
// Container returns identifier of the container with which the data audit Result
|
||||
// is associated and a bool that indicates container ID field presence in the Result.
|
||||
//
|
||||
// Zero Result does not have container ID.
|
||||
//
|
||||
// See also ForContainer.
|
||||
func (r Result) Container() (cid.ID, bool) {
|
||||
var cID cid.ID
|
||||
|
||||
cidV2 := r.v2.GetContainerID()
|
||||
if cidV2 != nil {
|
||||
_ = cID.ReadFromV2(*cidV2)
|
||||
return cID, true
|
||||
}
|
||||
|
||||
return cID, false
|
||||
}
|
||||
|
||||
// ForContainer sets identifier of the container with which the data audit Result
|
||||
// is associated.
|
||||
//
|
||||
// See also Container.
|
||||
func (r *Result) ForContainer(cnr cid.ID) {
|
||||
var cidV2 refs.ContainerID
|
||||
cnr.WriteToV2(&cidV2)
|
||||
|
||||
r.v2.SetContainerID(&cidV2)
|
||||
}
|
||||
|
||||
// AuditorKey returns public key of the auditing FrostFS Inner Ring node in
|
||||
// a FrostFS binary key format.
|
||||
//
|
||||
// Zero Result has nil key. Return value MUST NOT be mutated: to do this,
|
||||
// first make a copy.
|
||||
//
|
||||
// See also SetAuditorPublicKey.
|
||||
func (r Result) AuditorKey() []byte {
|
||||
return r.v2.GetPublicKey()
|
||||
}
|
||||
|
||||
// SetAuditorKey specifies public key of the auditing FrostFS Inner Ring node in
|
||||
// a FrostFS binary key format.
|
||||
//
|
||||
// Argument MUST NOT be mutated at least until the end of using the Result.
|
||||
//
|
||||
// See also AuditorKey.
|
||||
func (r *Result) SetAuditorKey(key []byte) {
|
||||
r.v2.SetPublicKey(key)
|
||||
}
|
||||
|
||||
// Completed returns completion state of the data audit associated with the Result.
|
||||
//
|
||||
// Zero Result corresponds to incomplete data audit.
|
||||
//
|
||||
// See also Complete.
|
||||
func (r Result) Completed() bool {
|
||||
return r.v2.GetComplete()
|
||||
}
|
||||
|
||||
// Complete marks the data audit associated with the Result as completed.
|
||||
//
|
||||
// See also Completed.
|
||||
func (r *Result) Complete() {
|
||||
r.v2.SetComplete(true)
|
||||
}
|
||||
|
||||
// RequestsPoR returns number of requests made by Proof-of-Retrievability
|
||||
// audit check to get all headers of the objects inside storage groups.
|
||||
//
|
||||
// Zero Result has zero requests.
|
||||
//
|
||||
// See also SetRequestsPoR.
|
||||
func (r Result) RequestsPoR() uint32 {
|
||||
return r.v2.GetRequests()
|
||||
}
|
||||
|
||||
// SetRequestsPoR sets number of requests made by Proof-of-Retrievability
|
||||
// audit check to get all headers of the objects inside storage groups.
|
||||
//
|
||||
// See also RequestsPoR.
|
||||
func (r *Result) SetRequestsPoR(v uint32) {
|
||||
r.v2.SetRequests(v)
|
||||
}
|
||||
|
||||
// RetriesPoR returns number of retries made by Proof-of-Retrievability
|
||||
// audit check to get all headers of the objects inside storage groups.
|
||||
//
|
||||
// Zero Result has zero retries.
|
||||
//
|
||||
// See also SetRetriesPoR.
|
||||
func (r Result) RetriesPoR() uint32 {
|
||||
return r.v2.GetRetries()
|
||||
}
|
||||
|
||||
// SetRetriesPoR sets number of retries made by Proof-of-Retrievability
|
||||
// audit check to get all headers of the objects inside storage groups.
|
||||
//
|
||||
// See also RetriesPoR.
|
||||
func (r *Result) SetRetriesPoR(v uint32) {
|
||||
r.v2.SetRetries(v)
|
||||
}
|
||||
|
||||
// IteratePassedStorageGroups iterates over all storage groups that passed
|
||||
// Proof-of-Retrievability audit check and passes them into f. Breaks on f's
|
||||
// false return, f MUST NOT be nil.
|
||||
//
|
||||
// Zero Result has no passed storage groups and doesn't call f.
|
||||
//
|
||||
// See also SubmitPassedStorageGroup.
|
||||
func (r Result) IteratePassedStorageGroups(f func(oid.ID) bool) {
|
||||
r2 := r.v2.GetPassSG()
|
||||
|
||||
var id oid.ID
|
||||
|
||||
for i := range r2 {
|
||||
_ = id.ReadFromV2(r2[i])
|
||||
|
||||
if !f(id) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SubmitPassedStorageGroup marks storage group as passed Proof-of-Retrievability
|
||||
// audit check.
|
||||
//
|
||||
// See also IteratePassedStorageGroups.
|
||||
func (r *Result) SubmitPassedStorageGroup(sg oid.ID) {
|
||||
var idV2 refs.ObjectID
|
||||
sg.WriteToV2(&idV2)
|
||||
|
||||
r.v2.SetPassSG(append(r.v2.GetPassSG(), idV2))
|
||||
}
|
||||
|
||||
// IterateFailedStorageGroups is similar to IteratePassedStorageGroups but for failed groups.
|
||||
//
|
||||
// See also SubmitFailedStorageGroup.
|
||||
func (r Result) IterateFailedStorageGroups(f func(oid.ID) bool) {
|
||||
v := r.v2.GetFailSG()
|
||||
var id oid.ID
|
||||
|
||||
for i := range v {
|
||||
_ = id.ReadFromV2(v[i])
|
||||
if !f(id) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SubmitFailedStorageGroup is similar to SubmitPassedStorageGroup but for failed groups.
|
||||
//
|
||||
// See also IterateFailedStorageGroups.
|
||||
func (r *Result) SubmitFailedStorageGroup(sg oid.ID) {
|
||||
var idV2 refs.ObjectID
|
||||
sg.WriteToV2(&idV2)
|
||||
|
||||
r.v2.SetFailSG(append(r.v2.GetFailSG(), idV2))
|
||||
}
|
||||
|
||||
// Hits returns number of sampled objects under audit placed
|
||||
// in an optimal way according to the container's placement policy
|
||||
// when checking Proof-of-Placement.
|
||||
//
|
||||
// Zero result has zero hits.
|
||||
//
|
||||
// See also SetHits.
|
||||
func (r Result) Hits() uint32 {
|
||||
return r.v2.GetHit()
|
||||
}
|
||||
|
||||
// SetHits sets number of sampled objects under audit placed
|
||||
// in an optimal way according to the containers placement policy
|
||||
// when checking Proof-of-Placement.
|
||||
//
|
||||
// See also Hits.
|
||||
func (r *Result) SetHits(hit uint32) {
|
||||
r.v2.SetHit(hit)
|
||||
}
|
||||
|
||||
// Misses returns number of sampled objects under audit placed
|
||||
// in suboptimal way according to the container's placement policy,
|
||||
// but still at a satisfactory level when checking Proof-of-Placement.
|
||||
//
|
||||
// Zero Result has zero misses.
|
||||
//
|
||||
// See also SetMisses.
|
||||
func (r Result) Misses() uint32 {
|
||||
return r.v2.GetMiss()
|
||||
}
|
||||
|
||||
// SetMisses sets number of sampled objects under audit placed
|
||||
// in suboptimal way according to the container's placement policy,
|
||||
// but still at a satisfactory level when checking Proof-of-Placement.
|
||||
//
|
||||
// See also Misses.
|
||||
func (r *Result) SetMisses(miss uint32) {
|
||||
r.v2.SetMiss(miss)
|
||||
}
|
||||
|
||||
// Failures returns number of sampled objects under audit stored
|
||||
// in a way not confirming placement policy or not found at all
|
||||
// when checking Proof-of-Placement.
|
||||
//
|
||||
// Zero result has zero failures.
|
||||
//
|
||||
// See also SetFailures.
|
||||
func (r Result) Failures() uint32 {
|
||||
return r.v2.GetFail()
|
||||
}
|
||||
|
||||
// SetFailures sets number of sampled objects under audit stored
|
||||
// in a way not confirming placement policy or not found at all
|
||||
// when checking Proof-of-Placement.
|
||||
//
|
||||
// See also Failures.
|
||||
func (r *Result) SetFailures(fail uint32) {
|
||||
r.v2.SetFail(fail)
|
||||
}
|
||||
|
||||
// IteratePassedStorageNodes iterates over all storage nodes that passed at least one
|
||||
// Proof-of-Data-Possession audit check and passes their public keys into f. Breaks on
|
||||
// f's false return.
|
||||
//
|
||||
// f MUST NOT be nil and MUST NOT mutate parameter passed into it at least until
|
||||
// the end of using the Result.
|
||||
//
|
||||
// Zero Result has no passed storage nodes and doesn't call f.
|
||||
//
|
||||
// See also SubmitPassedStorageNode.
|
||||
func (r Result) IteratePassedStorageNodes(f func([]byte) bool) {
|
||||
v := r.v2.GetPassNodes()
|
||||
|
||||
for i := range v {
|
||||
if !f(v[i]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SubmitPassedStorageNodes marks storage node list as passed Proof-of-Data-Possession
|
||||
// audit check. The list contains public keys.
|
||||
//
|
||||
// Argument and its elements MUST NOT be mutated at least until the end of using the Result.
|
||||
//
|
||||
// See also IteratePassedStorageNodes.
|
||||
func (r *Result) SubmitPassedStorageNodes(list [][]byte) {
|
||||
r.v2.SetPassNodes(list)
|
||||
}
|
||||
|
||||
// IterateFailedStorageNodes is similar to IteratePassedStorageNodes but for failed nodes.
|
||||
//
|
||||
// See also SubmitPassedStorageNodes.
|
||||
func (r Result) IterateFailedStorageNodes(f func([]byte) bool) {
|
||||
v := r.v2.GetFailNodes()
|
||||
|
||||
for i := range v {
|
||||
if !f(v[i]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SubmitFailedStorageNodes is similar to SubmitPassedStorageNodes but for failed nodes.
|
||||
//
|
||||
// See also IterateFailedStorageNodes.
|
||||
func (r *Result) SubmitFailedStorageNodes(list [][]byte) {
|
||||
r.v2.SetFailNodes(list)
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
package audit_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/audit"
|
||||
audittest "github.com/TrueCloudLab/frostfs-sdk-go/audit/test"
|
||||
cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
oidtest "github.com/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestResultData(t *testing.T) {
|
||||
var r audit.Result
|
||||
|
||||
countSG := func(passed bool, f func(oid.ID)) int {
|
||||
called := 0
|
||||
|
||||
ff := func(arg oid.ID) bool {
|
||||
called++
|
||||
|
||||
if f != nil {
|
||||
f(arg)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if passed {
|
||||
r.IteratePassedStorageGroups(ff)
|
||||
} else {
|
||||
r.IterateFailedStorageGroups(ff)
|
||||
}
|
||||
|
||||
return called
|
||||
}
|
||||
|
||||
countPassSG := func(f func(oid.ID)) int { return countSG(true, f) }
|
||||
countFailSG := func(f func(oid.ID)) int { return countSG(false, f) }
|
||||
|
||||
countNodes := func(passed bool, f func([]byte)) int {
|
||||
called := 0
|
||||
|
||||
ff := func(arg []byte) bool {
|
||||
called++
|
||||
|
||||
if f != nil {
|
||||
f(arg)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if passed {
|
||||
r.IteratePassedStorageNodes(ff)
|
||||
} else {
|
||||
r.IterateFailedStorageNodes(ff)
|
||||
}
|
||||
|
||||
return called
|
||||
}
|
||||
|
||||
countPassNodes := func(f func([]byte)) int { return countNodes(true, f) }
|
||||
countFailNodes := func(f func([]byte)) int { return countNodes(false, f) }
|
||||
|
||||
require.Zero(t, r.Epoch())
|
||||
_, set := r.Container()
|
||||
require.False(t, set)
|
||||
require.Nil(t, r.AuditorKey())
|
||||
require.False(t, r.Completed())
|
||||
require.Zero(t, r.RequestsPoR())
|
||||
require.Zero(t, r.RetriesPoR())
|
||||
require.Zero(t, countPassSG(nil))
|
||||
require.Zero(t, countFailSG(nil))
|
||||
require.Zero(t, countPassNodes(nil))
|
||||
require.Zero(t, countFailNodes(nil))
|
||||
|
||||
epoch := uint64(13)
|
||||
r.ForEpoch(epoch)
|
||||
require.Equal(t, epoch, r.Epoch())
|
||||
|
||||
cnr := cidtest.ID()
|
||||
r.ForContainer(cnr)
|
||||
cID, set := r.Container()
|
||||
require.True(t, set)
|
||||
require.Equal(t, cnr, cID)
|
||||
|
||||
key := []byte{1, 2, 3}
|
||||
r.SetAuditorKey(key)
|
||||
require.Equal(t, key, r.AuditorKey())
|
||||
|
||||
r.Complete()
|
||||
require.True(t, r.Completed())
|
||||
|
||||
requests := uint32(2)
|
||||
r.SetRequestsPoR(requests)
|
||||
require.Equal(t, requests, r.RequestsPoR())
|
||||
|
||||
retries := uint32(1)
|
||||
r.SetRetriesPoR(retries)
|
||||
require.Equal(t, retries, r.RetriesPoR())
|
||||
|
||||
passSG1, passSG2 := oidtest.ID(), oidtest.ID()
|
||||
r.SubmitPassedStorageGroup(passSG1)
|
||||
r.SubmitPassedStorageGroup(passSG2)
|
||||
|
||||
called1, called2 := false, false
|
||||
|
||||
require.EqualValues(t, 2, countPassSG(func(id oid.ID) {
|
||||
if id.Equals(passSG1) {
|
||||
called1 = true
|
||||
} else if id.Equals(passSG2) {
|
||||
called2 = true
|
||||
}
|
||||
}))
|
||||
require.True(t, called1)
|
||||
require.True(t, called2)
|
||||
|
||||
failSG1, failSG2 := oidtest.ID(), oidtest.ID()
|
||||
r.SubmitFailedStorageGroup(failSG1)
|
||||
r.SubmitFailedStorageGroup(failSG2)
|
||||
|
||||
called1, called2 = false, false
|
||||
|
||||
require.EqualValues(t, 2, countFailSG(func(id oid.ID) {
|
||||
if id.Equals(failSG1) {
|
||||
called1 = true
|
||||
} else if id.Equals(failSG2) {
|
||||
called2 = true
|
||||
}
|
||||
}))
|
||||
require.True(t, called1)
|
||||
require.True(t, called2)
|
||||
|
||||
hit := uint32(1)
|
||||
r.SetHits(hit)
|
||||
require.Equal(t, hit, r.Hits())
|
||||
|
||||
miss := uint32(2)
|
||||
r.SetMisses(miss)
|
||||
require.Equal(t, miss, r.Misses())
|
||||
|
||||
fail := uint32(3)
|
||||
r.SetFailures(fail)
|
||||
require.Equal(t, fail, r.Failures())
|
||||
|
||||
passNodes := [][]byte{{1}, {2}}
|
||||
r.SubmitPassedStorageNodes(passNodes)
|
||||
|
||||
called1, called2 = false, false
|
||||
|
||||
require.EqualValues(t, 2, countPassNodes(func(arg []byte) {
|
||||
if bytes.Equal(arg, passNodes[0]) {
|
||||
called1 = true
|
||||
} else if bytes.Equal(arg, passNodes[1]) {
|
||||
called2 = true
|
||||
}
|
||||
}))
|
||||
require.True(t, called1)
|
||||
require.True(t, called2)
|
||||
|
||||
failNodes := [][]byte{{3}, {4}}
|
||||
r.SubmitFailedStorageNodes(failNodes)
|
||||
|
||||
called1, called2 = false, false
|
||||
|
||||
require.EqualValues(t, 2, countFailNodes(func(arg []byte) {
|
||||
if bytes.Equal(arg, failNodes[0]) {
|
||||
called1 = true
|
||||
} else if bytes.Equal(arg, failNodes[1]) {
|
||||
called2 = true
|
||||
}
|
||||
}))
|
||||
require.True(t, called1)
|
||||
require.True(t, called2)
|
||||
}
|
||||
|
||||
func TestResultEncoding(t *testing.T) {
|
||||
r := *audittest.Result()
|
||||
|
||||
t.Run("binary", func(t *testing.T) {
|
||||
data := r.Marshal()
|
||||
|
||||
var r2 audit.Result
|
||||
require.NoError(t, r2.Unmarshal(data))
|
||||
|
||||
require.Equal(t, r, r2)
|
||||
})
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
Package audittest provides functions for convenient testing of audit package API.
|
||||
|
||||
Note that importing the package into source files is highly discouraged.
|
||||
|
||||
Random instance generation functions can be useful when testing expects any value, e.g.:
|
||||
|
||||
import audittest "github.com/TrueCloudLab/frostfs-sdk-go/audit/test"
|
||||
|
||||
dec := audittest.Result()
|
||||
// test the value
|
||||
*/
|
||||
package audittest
|
|
@ -1,36 +0,0 @@
|
|||
package audittest
|
||||
|
||||
import (
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/audit"
|
||||
cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
oidtest "github.com/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
||||
)
|
||||
|
||||
// Result returns random audit.Result.
|
||||
func Result() *audit.Result {
|
||||
var x audit.Result
|
||||
|
||||
x.ForContainer(cidtest.ID())
|
||||
x.SetAuditorKey([]byte("key"))
|
||||
x.Complete()
|
||||
x.ForEpoch(44)
|
||||
x.SetHits(55)
|
||||
x.SetMisses(66)
|
||||
x.SetFailures(77)
|
||||
x.SetRequestsPoR(88)
|
||||
x.SetRequestsPoR(99)
|
||||
x.SubmitFailedStorageNodes([][]byte{
|
||||
[]byte("node1"),
|
||||
[]byte("node2"),
|
||||
})
|
||||
x.SubmitPassedStorageNodes([][]byte{
|
||||
[]byte("node3"),
|
||||
[]byte("node4"),
|
||||
})
|
||||
x.SubmitPassedStorageGroup(oidtest.ID())
|
||||
x.SubmitPassedStorageGroup(oidtest.ID())
|
||||
x.SubmitFailedStorageGroup(oidtest.ID())
|
||||
x.SubmitFailedStorageGroup(oidtest.ID())
|
||||
|
||||
return &x
|
||||
}
|
|
@ -5,18 +5,18 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
frostfsecdsa "github.com/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
)
|
||||
|
||||
// Token represents bearer token for object service operations.
|
||||
//
|
||||
// Token is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/acl.BearerToken
|
||||
// Token is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl.BearerToken
|
||||
// message. See ReadFromV2 / WriteToV2 methods.
|
||||
//
|
||||
// Instances can be created using built-in var declaration.
|
||||
|
@ -32,6 +32,8 @@ type Token struct {
|
|||
|
||||
sigSet bool
|
||||
sig refs.Signature
|
||||
|
||||
impersonate bool
|
||||
}
|
||||
|
||||
// reads Token from the acl.BearerToken message. If checkFieldPresence is set,
|
||||
|
@ -44,10 +46,12 @@ func (b *Token) readFromV2(m acl.BearerToken, checkFieldPresence bool) error {
|
|||
return errors.New("missing token body")
|
||||
}
|
||||
|
||||
b.impersonate = body.GetImpersonate()
|
||||
|
||||
eaclTable := body.GetEACL()
|
||||
if b.eaclTableSet = eaclTable != nil; b.eaclTableSet {
|
||||
b.eaclTable = *eacl.NewTableFromV2(eaclTable)
|
||||
} else if checkFieldPresence {
|
||||
} else if checkFieldPresence && !b.impersonate {
|
||||
return errors.New("missing eACL table")
|
||||
}
|
||||
|
||||
|
@ -86,7 +90,7 @@ func (b *Token) ReadFromV2(m acl.BearerToken) error {
|
|||
}
|
||||
|
||||
func (b Token) fillBody() *acl.BearerTokenBody {
|
||||
if !b.eaclTableSet && !b.targetUserSet && !b.lifetimeSet {
|
||||
if !b.eaclTableSet && !b.targetUserSet && !b.lifetimeSet && !b.impersonate {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -112,6 +116,8 @@ func (b Token) fillBody() *acl.BearerTokenBody {
|
|||
body.SetLifetime(&lifetime)
|
||||
}
|
||||
|
||||
body.SetImpersonate(b.impersonate)
|
||||
|
||||
return &body
|
||||
}
|
||||
|
||||
|
@ -208,6 +214,17 @@ func (b Token) EACLTable() eacl.Table {
|
|||
return eacl.Table{}
|
||||
}
|
||||
|
||||
// SetImpersonate mark token as impersonate to consider token signer as request owner.
|
||||
// If this field is true extended EACLTable in token body isn't processed.
|
||||
func (b *Token) SetImpersonate(v bool) {
|
||||
b.impersonate = v
|
||||
}
|
||||
|
||||
// Impersonate returns true if token is impersonated.
|
||||
func (b Token) Impersonate() bool {
|
||||
return b.impersonate
|
||||
}
|
||||
|
||||
// AssertContainer checks if the token is valid within the given container.
|
||||
//
|
||||
// Note: cnr is assumed to refer to the issuer's container, otherwise the check
|
||||
|
|
|
@ -5,17 +5,17 @@ import (
|
|||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
bearertest "github.com/TrueCloudLab/frostfs-sdk-go/bearer/test"
|
||||
cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
frostfsecdsa "github.com/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||
eacltest "github.com/TrueCloudLab/frostfs-sdk-go/eacl/test"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||
usertest "github.com/TrueCloudLab/frostfs-sdk-go/user/test"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
bearertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer/test"
|
||||
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||
eacltest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl/test"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -323,6 +323,10 @@ func TestToken_ReadFromV2(t *testing.T) {
|
|||
|
||||
require.NoError(t, val.ReadFromV2(m))
|
||||
|
||||
body.SetEACL(nil)
|
||||
body.SetImpersonate(true)
|
||||
require.NoError(t, val.ReadFromV2(m))
|
||||
|
||||
var m2 acl.BearerToken
|
||||
|
||||
val.WriteToV2(&m2)
|
||||
|
|
|
@ -22,7 +22,7 @@ Bearer token must be signed by owner of the container.
|
|||
Provide signed token in JSON or binary format to the request sender. Request
|
||||
sender can attach this bearer token to the object service requests:
|
||||
|
||||
import sdkClient "github.com/TrueCloudLab/frostfs-sdk-go/client"
|
||||
import sdkClient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||
|
||||
var headParams sdkClient.PrmObjectHead
|
||||
headParams.WithBearerToken(bearerToken)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package bearertest
|
||||
|
||||
import (
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
eacltest "github.com/TrueCloudLab/frostfs-sdk-go/eacl/test"
|
||||
usertest "github.com/TrueCloudLab/frostfs-sdk-go/user/test"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
eacltest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl/test"
|
||||
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
|
||||
)
|
||||
|
||||
// Token returns random bearer.Token.
|
||||
|
|
|
@ -6,13 +6,13 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"github.com/TrueCloudLab/tzhash/tz"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/tzhash/tz"
|
||||
)
|
||||
|
||||
// Checksum represents checksum of some digital data.
|
||||
//
|
||||
// Checksum is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/refs.Checksum
|
||||
// Checksum is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.Checksum
|
||||
// message. See ReadFromV2 / WriteToV2 methods.
|
||||
//
|
||||
// Instances can be created using built-in var declaration.
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"crypto/sha256"
|
||||
"testing"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"github.com/TrueCloudLab/tzhash/tz"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/tzhash/tz"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
)
|
||||
|
||||
func ExampleCalculate() {
|
||||
|
|
|
@ -5,7 +5,7 @@ Note that importing the package into source files is highly discouraged.
|
|||
|
||||
Random instance generation functions can be useful when testing expects any value, e.g.:
|
||||
|
||||
import checksumtest "github.com/TrueCloudLab/frostfs-sdk-go/checksum/test"
|
||||
import checksumtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum/test"
|
||||
|
||||
cs := checksumtest.Checksum()
|
||||
// test the value
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"crypto/sha256"
|
||||
"math/rand"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/checksum"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
|
||||
)
|
||||
|
||||
// Checksum returns random checksum.Checksum.
|
||||
|
|
|
@ -3,12 +3,12 @@ package client
|
|||
import (
|
||||
"context"
|
||||
|
||||
v2accounting "github.com/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/accounting"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||
v2accounting "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
)
|
||||
|
||||
// PrmBalanceGet groups parameters of BalanceGet operation.
|
||||
|
@ -46,17 +46,14 @@ func (x ResBalanceGet) Amount() accounting.Decimal {
|
|||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmBalanceGet docs).
|
||||
// Returns an error if parameters are set incorrectly (see PrmBalanceGet docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) BalanceGet(ctx context.Context, prm PrmBalanceGet) (*ResBalanceGet, error) {
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case !prm.accountSet:
|
||||
panic("account not set")
|
||||
if !prm.accountSet {
|
||||
return nil, errorAccountNotSet
|
||||
}
|
||||
|
||||
// form request body
|
||||
|
|
|
@ -4,9 +4,9 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
v2netmap "github.com/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
)
|
||||
|
||||
// interface of FrostFS API server. Exists for test purposes only.
|
||||
|
|
|
@ -7,9 +7,10 @@ import (
|
|||
"errors"
|
||||
"time"
|
||||
|
||||
v2accounting "github.com/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2accounting "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Client represents virtual connection to the FrostFS network to communicate
|
||||
|
@ -69,21 +70,21 @@ func (c *Client) Init(prm PrmInit) {
|
|||
// argument, otherwise context.Background() is used. Dial returns context
|
||||
// errors, see context package docs for details.
|
||||
//
|
||||
// Panics if required parameters are set incorrectly, look carefully
|
||||
// Returns an error if required parameters are set incorrectly, look carefully
|
||||
// at the method documentation.
|
||||
//
|
||||
// One-time method call during application start-up stage (after Init ) is expected.
|
||||
// Calling multiple times leads to undefined behavior.
|
||||
//
|
||||
// See also Init / Close.
|
||||
func (c *Client) Dial(prm PrmDial) error {
|
||||
func (c *Client) Dial(ctx context.Context, prm PrmDial) error {
|
||||
if prm.endpoint == "" {
|
||||
panic("server address is unset or empty")
|
||||
return errorServerAddrUnset
|
||||
}
|
||||
|
||||
if prm.timeoutDialSet {
|
||||
if prm.timeoutDial <= 0 {
|
||||
panic("non-positive timeout")
|
||||
return errorNonPositiveTimeout
|
||||
}
|
||||
} else {
|
||||
prm.timeoutDial = 5 * time.Second
|
||||
|
@ -91,7 +92,7 @@ func (c *Client) Dial(prm PrmDial) error {
|
|||
|
||||
if prm.streamTimeoutSet {
|
||||
if prm.streamTimeout <= 0 {
|
||||
panic("non-positive timeout")
|
||||
return errorNonPositiveTimeout
|
||||
}
|
||||
} else {
|
||||
prm.streamTimeout = 10 * time.Second
|
||||
|
@ -101,17 +102,14 @@ func (c *Client) Dial(prm PrmDial) error {
|
|||
client.WithNetworkURIAddress(prm.endpoint, prm.tlsConfig),
|
||||
client.WithDialTimeout(prm.timeoutDial),
|
||||
client.WithRWTimeout(prm.streamTimeout),
|
||||
client.WithGRPCDialOptions(prm.dialOptions),
|
||||
)...)
|
||||
|
||||
c.setFrostFSAPIServer((*coreServer)(&c.c))
|
||||
|
||||
if prm.parentCtx == nil {
|
||||
prm.parentCtx = context.Background()
|
||||
}
|
||||
|
||||
// TODO: (neofs-api-go#382) perform generic dial stage of the client.Client
|
||||
_, err := rpc.Balance(&c.c, new(v2accounting.BalanceRequest),
|
||||
client.WithContext(prm.parentCtx),
|
||||
client.WithContext(ctx),
|
||||
)
|
||||
// return context errors since they signal about dial problem
|
||||
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
||||
|
@ -123,7 +121,7 @@ func (c *Client) Dial(prm PrmDial) error {
|
|||
|
||||
// sets underlying provider of frostFSAPIServer. The method is used for testing as an approach
|
||||
// to skip Dial stage and override FrostFS API server. MUST NOT be used outside test code.
|
||||
// In real applications wrapper over github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client
|
||||
// In real applications wrapper over git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client
|
||||
// is statically used.
|
||||
func (c *Client) setFrostFSAPIServer(server frostFSAPIServer) {
|
||||
c.server = server
|
||||
|
@ -191,7 +189,7 @@ type PrmDial struct {
|
|||
streamTimeoutSet bool
|
||||
streamTimeout time.Duration
|
||||
|
||||
parentCtx context.Context
|
||||
dialOptions []grpc.DialOption
|
||||
}
|
||||
|
||||
// SetServerURI sets server URI in the FrostFS network.
|
||||
|
@ -233,10 +231,7 @@ func (x *PrmDial) SetStreamTimeout(timeout time.Duration) {
|
|||
x.streamTimeout = timeout
|
||||
}
|
||||
|
||||
// SetContext allows to specify optional base context within which connection
|
||||
// should be established.
|
||||
//
|
||||
// Context SHOULD NOT be nil.
|
||||
func (x *PrmDial) SetContext(ctx context.Context) {
|
||||
x.parentCtx = ctx
|
||||
// SetGRPCDialOptions sets the gRPC dial options for new gRPC client connection.
|
||||
func (x *PrmDial) SetGRPCDialOptions(opts ...grpc.DialOption) {
|
||||
x.dialOptions = opts
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -47,11 +47,8 @@ func TestClient_DialContext(t *testing.T) {
|
|||
prm.SetServerURI("localhost:8080")
|
||||
|
||||
assert := func(ctx context.Context, errExpected error) {
|
||||
// use the particular context
|
||||
prm.SetContext(ctx)
|
||||
|
||||
// expect particular context error according to Dial docs
|
||||
require.ErrorIs(t, c.Dial(prm), errExpected)
|
||||
require.ErrorIs(t, c.Dial(ctx, prm), errExpected)
|
||||
}
|
||||
|
||||
// create pre-abandoned context
|
||||
|
|
|
@ -2,14 +2,15 @@ package client
|
|||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/version"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
||||
)
|
||||
|
||||
// common interface of resulting structures with API status.
|
||||
|
@ -70,11 +71,17 @@ func writeXHeadersToMeta(xHeaders []string, h *v2session.RequestMetaHeader) {
|
|||
h.SetXHeaders(hs)
|
||||
}
|
||||
|
||||
// panic messages.
|
||||
const (
|
||||
panicMsgMissingContext = "missing context"
|
||||
panicMsgMissingContainer = "missing container"
|
||||
panicMsgMissingObject = "missing object"
|
||||
// error messages.
|
||||
var (
|
||||
errorMissingContainer = errors.New("missing container")
|
||||
errorMissingObject = errors.New("missing object")
|
||||
errorAccountNotSet = errors.New("account not set")
|
||||
errorServerAddrUnset = errors.New("server address is unset or empty")
|
||||
errorNonPositiveTimeout = errors.New("non-positive timeout")
|
||||
errorEACLTableNotSet = errors.New("eACL table not set")
|
||||
errorMissingAnnouncements = errors.New("missing announcements")
|
||||
errorZeroRangeLength = errors.New("zero range length")
|
||||
errorMissingRanges = errors.New("missing ranges")
|
||||
)
|
||||
|
||||
// groups all the details required to send a single request and process a response to it.
|
||||
|
@ -250,6 +257,16 @@ func (x *contextCall) processResponse() bool {
|
|||
|
||||
// processResponse verifies response signature and converts status to an error if needed.
|
||||
func (c *Client) processResponse(resp responseV2) (apistatus.Status, error) {
|
||||
if c.prm.cbRespInfo != nil {
|
||||
rmi := ResponseMetaInfo{
|
||||
key: resp.GetVerificationHeader().GetBodySignature().GetKey(),
|
||||
epoch: resp.GetMetaHeader().GetEpoch(),
|
||||
}
|
||||
if err := c.prm.cbRespInfo(rmi); err != nil {
|
||||
return nil, fmt.Errorf("response callback error: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
err := signature.VerifyServiceMessage(resp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid response signature: %w", err)
|
||||
|
@ -334,7 +351,7 @@ func (c *Client) initCallContext(ctx *contextCall) {
|
|||
ctx.netMagic = c.prm.netMagic
|
||||
}
|
||||
|
||||
// ExecRaw executes f with underlying github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client.Client
|
||||
// ExecRaw executes f with underlying git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client.Client
|
||||
// instance. Communicate over the Protocol Buffers protocol in a more flexible way:
|
||||
// most often used to transmit data over a fixed version of the FrostFS protocol, as well
|
||||
// as to support custom services.
|
||||
|
@ -345,7 +362,7 @@ func (c *Client) initCallContext(ctx *contextCall) {
|
|||
// before closing the connection.
|
||||
//
|
||||
// See also Dial and Close.
|
||||
// See also github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client package docs.
|
||||
// See also git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client package docs.
|
||||
func (c *Client) ExecRaw(f func(client *client.Client) error) error {
|
||||
return f(&c.c)
|
||||
}
|
||||
|
|
|
@ -2,803 +2,11 @@ package client
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
v2container "github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/container"
|
||||
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
frostfsecdsa "github.com/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/session"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
)
|
||||
|
||||
// PrmContainerPut groups parameters of ContainerPut operation.
|
||||
type PrmContainerPut struct {
|
||||
prmCommonMeta
|
||||
|
||||
cnrSet bool
|
||||
cnr container.Container
|
||||
|
||||
sessionSet bool
|
||||
session session.Container
|
||||
}
|
||||
|
||||
// SetContainer sets structured information about new FrostFS container.
|
||||
// Required parameter.
|
||||
func (x *PrmContainerPut) SetContainer(cnr container.Container) {
|
||||
x.cnr = cnr
|
||||
x.cnrSet = true
|
||||
}
|
||||
|
||||
// WithinSession specifies session within which container should be saved.
|
||||
//
|
||||
// Creator of the session acquires the authorship of the request. This affects
|
||||
// the execution of an operation (e.g. access control).
|
||||
//
|
||||
// Session is optional, if set the following requirements apply:
|
||||
// - session operation MUST be session.VerbContainerPut (ForVerb)
|
||||
// - token MUST be signed using private key of the owner of the container to be saved
|
||||
func (x *PrmContainerPut) WithinSession(s session.Container) {
|
||||
x.session = s
|
||||
x.sessionSet = true
|
||||
}
|
||||
|
||||
// ResContainerPut groups resulting values of ContainerPut operation.
|
||||
type ResContainerPut struct {
|
||||
statusRes
|
||||
|
||||
id cid.ID
|
||||
}
|
||||
|
||||
// ID returns identifier of the container declared to be stored in the system.
|
||||
// Used as a link to information about the container (in particular, you can
|
||||
// asynchronously check if the save was successful).
|
||||
func (x ResContainerPut) ID() cid.ID {
|
||||
return x.id
|
||||
}
|
||||
|
||||
// ContainerPut sends request to save container in FrostFS.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// 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 identifier (see ResContainerPut.ID).
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmContainerPut docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) ContainerPut(ctx context.Context, prm PrmContainerPut) (*ResContainerPut, error) {
|
||||
// check parameters
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case !prm.cnrSet:
|
||||
panic(panicMsgMissingContainer)
|
||||
}
|
||||
|
||||
// TODO: check private key is set before forming the request
|
||||
// sign container
|
||||
var cnr v2container.Container
|
||||
prm.cnr.WriteToV2(&cnr)
|
||||
|
||||
var sig frostfscrypto.Signature
|
||||
|
||||
err := container.CalculateSignature(&sig, prm.cnr, c.prm.key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("calculate container signature: %w", err)
|
||||
}
|
||||
|
||||
var sigv2 refs.Signature
|
||||
|
||||
sig.WriteToV2(&sigv2)
|
||||
|
||||
// form request body
|
||||
reqBody := new(v2container.PutRequestBody)
|
||||
reqBody.SetContainer(&cnr)
|
||||
reqBody.SetSignature(&sigv2)
|
||||
|
||||
// form meta header
|
||||
var meta v2session.RequestMetaHeader
|
||||
writeXHeadersToMeta(prm.prmCommonMeta.xHeaders, &meta)
|
||||
|
||||
if prm.sessionSet {
|
||||
var tokv2 v2session.Token
|
||||
prm.session.WriteToV2(&tokv2)
|
||||
|
||||
meta.SetSessionToken(&tokv2)
|
||||
}
|
||||
|
||||
// form request
|
||||
var req v2container.PutRequest
|
||||
|
||||
req.SetBody(reqBody)
|
||||
req.SetMetaHeader(&meta)
|
||||
|
||||
// init call context
|
||||
|
||||
var (
|
||||
cc contextCall
|
||||
res ResContainerPut
|
||||
)
|
||||
|
||||
c.initCallContext(&cc)
|
||||
cc.req = &req
|
||||
cc.statusRes = &res
|
||||
cc.call = func() (responseV2, error) {
|
||||
return rpcapi.PutContainer(&c.c, &req, client.WithContext(ctx))
|
||||
}
|
||||
cc.result = func(r responseV2) {
|
||||
resp := r.(*v2container.PutResponse)
|
||||
|
||||
const fieldCnrID = "container ID"
|
||||
|
||||
cidV2 := resp.GetBody().GetContainerID()
|
||||
if cidV2 == nil {
|
||||
cc.err = newErrMissingResponseField(fieldCnrID)
|
||||
return
|
||||
}
|
||||
|
||||
cc.err = res.id.ReadFromV2(*cidV2)
|
||||
if cc.err != nil {
|
||||
cc.err = newErrInvalidResponseField(fieldCnrID, cc.err)
|
||||
}
|
||||
}
|
||||
|
||||
// process call
|
||||
if !cc.processCall() {
|
||||
return nil, cc.err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// PrmContainerGet groups parameters of ContainerGet operation.
|
||||
type PrmContainerGet struct {
|
||||
prmCommonMeta
|
||||
|
||||
idSet bool
|
||||
id cid.ID
|
||||
}
|
||||
|
||||
// SetContainer sets identifier of the container to be read.
|
||||
// Required parameter.
|
||||
func (x *PrmContainerGet) SetContainer(id cid.ID) {
|
||||
x.id = id
|
||||
x.idSet = true
|
||||
}
|
||||
|
||||
// ResContainerGet groups resulting values of ContainerGet operation.
|
||||
type ResContainerGet struct {
|
||||
statusRes
|
||||
|
||||
cnr container.Container
|
||||
}
|
||||
|
||||
// Container returns structured information about the requested container.
|
||||
//
|
||||
// Client doesn't retain value so modification is safe.
|
||||
func (x ResContainerGet) Container() container.Container {
|
||||
return x.cnr
|
||||
}
|
||||
|
||||
// ContainerGet reads FrostFS container by ID.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmContainerGet docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs);
|
||||
// - *apistatus.ContainerNotFound.
|
||||
func (c *Client) ContainerGet(ctx context.Context, prm PrmContainerGet) (*ResContainerGet, error) {
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case !prm.idSet:
|
||||
panic(panicMsgMissingContainer)
|
||||
}
|
||||
|
||||
var cidV2 refs.ContainerID
|
||||
prm.id.WriteToV2(&cidV2)
|
||||
|
||||
// form request body
|
||||
reqBody := new(v2container.GetRequestBody)
|
||||
reqBody.SetContainerID(&cidV2)
|
||||
|
||||
// form request
|
||||
var req v2container.GetRequest
|
||||
|
||||
req.SetBody(reqBody)
|
||||
|
||||
// init call context
|
||||
|
||||
var (
|
||||
cc contextCall
|
||||
res ResContainerGet
|
||||
)
|
||||
|
||||
c.initCallContext(&cc)
|
||||
cc.meta = prm.prmCommonMeta
|
||||
cc.req = &req
|
||||
cc.statusRes = &res
|
||||
cc.call = func() (responseV2, error) {
|
||||
return rpcapi.GetContainer(&c.c, &req, client.WithContext(ctx))
|
||||
}
|
||||
cc.result = func(r responseV2) {
|
||||
resp := r.(*v2container.GetResponse)
|
||||
|
||||
cnrV2 := resp.GetBody().GetContainer()
|
||||
if cnrV2 == nil {
|
||||
cc.err = errors.New("missing container in response")
|
||||
return
|
||||
}
|
||||
|
||||
cc.err = res.cnr.ReadFromV2(*cnrV2)
|
||||
if cc.err != nil {
|
||||
cc.err = fmt.Errorf("invalid container in response: %w", cc.err)
|
||||
}
|
||||
}
|
||||
|
||||
// process call
|
||||
if !cc.processCall() {
|
||||
return nil, cc.err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// PrmContainerList groups parameters of ContainerList operation.
|
||||
type PrmContainerList struct {
|
||||
prmCommonMeta
|
||||
|
||||
ownerSet bool
|
||||
ownerID user.ID
|
||||
}
|
||||
|
||||
// SetAccount sets identifier of the FrostFS account to list the containers.
|
||||
// Required parameter.
|
||||
func (x *PrmContainerList) SetAccount(id user.ID) {
|
||||
x.ownerID = id
|
||||
x.ownerSet = true
|
||||
}
|
||||
|
||||
// ResContainerList groups resulting values of ContainerList operation.
|
||||
type ResContainerList struct {
|
||||
statusRes
|
||||
|
||||
ids []cid.ID
|
||||
}
|
||||
|
||||
// Containers returns list of identifiers of the account-owned containers.
|
||||
//
|
||||
// Client doesn't retain value so modification is safe.
|
||||
func (x ResContainerList) Containers() []cid.ID {
|
||||
return x.ids
|
||||
}
|
||||
|
||||
// ContainerList requests identifiers of the account-owned containers.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmContainerList docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) ContainerList(ctx context.Context, prm PrmContainerList) (*ResContainerList, error) {
|
||||
// check parameters
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case !prm.ownerSet:
|
||||
panic("account not set")
|
||||
}
|
||||
|
||||
// form request body
|
||||
var ownerV2 refs.OwnerID
|
||||
prm.ownerID.WriteToV2(&ownerV2)
|
||||
|
||||
reqBody := new(v2container.ListRequestBody)
|
||||
reqBody.SetOwnerID(&ownerV2)
|
||||
|
||||
// form request
|
||||
var req v2container.ListRequest
|
||||
|
||||
req.SetBody(reqBody)
|
||||
|
||||
// init call context
|
||||
|
||||
var (
|
||||
cc contextCall
|
||||
res ResContainerList
|
||||
)
|
||||
|
||||
c.initCallContext(&cc)
|
||||
cc.meta = prm.prmCommonMeta
|
||||
cc.req = &req
|
||||
cc.statusRes = &res
|
||||
cc.call = func() (responseV2, error) {
|
||||
return rpcapi.ListContainers(&c.c, &req, client.WithContext(ctx))
|
||||
}
|
||||
cc.result = func(r responseV2) {
|
||||
resp := r.(*v2container.ListResponse)
|
||||
|
||||
res.ids = make([]cid.ID, len(resp.GetBody().GetContainerIDs()))
|
||||
|
||||
for i, cidV2 := range resp.GetBody().GetContainerIDs() {
|
||||
cc.err = res.ids[i].ReadFromV2(cidV2)
|
||||
if cc.err != nil {
|
||||
cc.err = fmt.Errorf("invalid ID in the response: %w", cc.err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// process call
|
||||
if !cc.processCall() {
|
||||
return nil, cc.err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// PrmContainerDelete groups parameters of ContainerDelete operation.
|
||||
type PrmContainerDelete struct {
|
||||
prmCommonMeta
|
||||
|
||||
idSet bool
|
||||
id cid.ID
|
||||
|
||||
tokSet bool
|
||||
tok session.Container
|
||||
}
|
||||
|
||||
// SetContainer sets identifier of the FrostFS container to be removed.
|
||||
// Required parameter.
|
||||
func (x *PrmContainerDelete) SetContainer(id cid.ID) {
|
||||
x.id = id
|
||||
x.idSet = true
|
||||
}
|
||||
|
||||
// WithinSession specifies session within which container should be removed.
|
||||
//
|
||||
// Creator of the session acquires the authorship of the request.
|
||||
// This may affect the execution of an operation (e.g. access control).
|
||||
//
|
||||
// Must be signed.
|
||||
func (x *PrmContainerDelete) WithinSession(tok session.Container) {
|
||||
x.tok = tok
|
||||
x.tokSet = true
|
||||
}
|
||||
|
||||
// ResContainerDelete groups resulting values of ContainerDelete operation.
|
||||
type ResContainerDelete struct {
|
||||
statusRes
|
||||
}
|
||||
|
||||
// ContainerDelete sends request to remove the FrostFS container.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// 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 identifier (see GetContainer).
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmContainerDelete docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Exactly one return value is non-nil. Server status return is returned in ResContainerDelete.
|
||||
// Reflects all internal errors in second return value (transport problems, response processing, etc.).
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) ContainerDelete(ctx context.Context, prm PrmContainerDelete) (*ResContainerDelete, error) {
|
||||
// check parameters
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case !prm.idSet:
|
||||
panic(panicMsgMissingContainer)
|
||||
}
|
||||
|
||||
// sign container ID
|
||||
var cidV2 refs.ContainerID
|
||||
prm.id.WriteToV2(&cidV2)
|
||||
|
||||
// container contract expects signature of container ID value
|
||||
// don't get confused with stable marshaled protobuf container.ID structure
|
||||
data := cidV2.GetValue()
|
||||
|
||||
var sig frostfscrypto.Signature
|
||||
|
||||
err := sig.Calculate(frostfsecdsa.SignerRFC6979(c.prm.key), data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("calculate signature: %w", err)
|
||||
}
|
||||
|
||||
var sigv2 refs.Signature
|
||||
|
||||
sig.WriteToV2(&sigv2)
|
||||
|
||||
// form request body
|
||||
reqBody := new(v2container.DeleteRequestBody)
|
||||
reqBody.SetContainerID(&cidV2)
|
||||
reqBody.SetSignature(&sigv2)
|
||||
|
||||
// form meta header
|
||||
var meta v2session.RequestMetaHeader
|
||||
writeXHeadersToMeta(prm.prmCommonMeta.xHeaders, &meta)
|
||||
|
||||
if prm.tokSet {
|
||||
var tokv2 v2session.Token
|
||||
prm.tok.WriteToV2(&tokv2)
|
||||
|
||||
meta.SetSessionToken(&tokv2)
|
||||
}
|
||||
|
||||
// form request
|
||||
var req v2container.DeleteRequest
|
||||
|
||||
req.SetBody(reqBody)
|
||||
req.SetMetaHeader(&meta)
|
||||
|
||||
// init call context
|
||||
|
||||
var (
|
||||
cc contextCall
|
||||
res ResContainerDelete
|
||||
)
|
||||
|
||||
c.initCallContext(&cc)
|
||||
cc.req = &req
|
||||
cc.statusRes = &res
|
||||
cc.call = func() (responseV2, error) {
|
||||
return rpcapi.DeleteContainer(&c.c, &req, client.WithContext(ctx))
|
||||
}
|
||||
|
||||
// process call
|
||||
if !cc.processCall() {
|
||||
return nil, cc.err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// PrmContainerEACL groups parameters of ContainerEACL operation.
|
||||
type PrmContainerEACL struct {
|
||||
prmCommonMeta
|
||||
|
||||
idSet bool
|
||||
id cid.ID
|
||||
}
|
||||
|
||||
// SetContainer sets identifier of the FrostFS container to read the eACL table.
|
||||
// Required parameter.
|
||||
func (x *PrmContainerEACL) SetContainer(id cid.ID) {
|
||||
x.id = id
|
||||
x.idSet = true
|
||||
}
|
||||
|
||||
// ResContainerEACL groups resulting values of ContainerEACL operation.
|
||||
type ResContainerEACL struct {
|
||||
statusRes
|
||||
|
||||
table eacl.Table
|
||||
}
|
||||
|
||||
// Table returns eACL table of the requested container.
|
||||
func (x ResContainerEACL) Table() eacl.Table {
|
||||
return x.table
|
||||
}
|
||||
|
||||
// ContainerEACL reads eACL table of the FrostFS container.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmContainerEACL docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs);
|
||||
// - *apistatus.ContainerNotFound;
|
||||
// - *apistatus.EACLNotFound.
|
||||
func (c *Client) ContainerEACL(ctx context.Context, prm PrmContainerEACL) (*ResContainerEACL, error) {
|
||||
// check parameters
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case !prm.idSet:
|
||||
panic(panicMsgMissingContainer)
|
||||
}
|
||||
|
||||
var cidV2 refs.ContainerID
|
||||
prm.id.WriteToV2(&cidV2)
|
||||
|
||||
// form request body
|
||||
reqBody := new(v2container.GetExtendedACLRequestBody)
|
||||
reqBody.SetContainerID(&cidV2)
|
||||
|
||||
// form request
|
||||
var req v2container.GetExtendedACLRequest
|
||||
|
||||
req.SetBody(reqBody)
|
||||
|
||||
// init call context
|
||||
|
||||
var (
|
||||
cc contextCall
|
||||
res ResContainerEACL
|
||||
)
|
||||
|
||||
c.initCallContext(&cc)
|
||||
cc.meta = prm.prmCommonMeta
|
||||
cc.req = &req
|
||||
cc.statusRes = &res
|
||||
cc.call = func() (responseV2, error) {
|
||||
return rpcapi.GetEACL(&c.c, &req, client.WithContext(ctx))
|
||||
}
|
||||
cc.result = func(r responseV2) {
|
||||
resp := r.(*v2container.GetExtendedACLResponse)
|
||||
|
||||
eACL := resp.GetBody().GetEACL()
|
||||
if eACL == nil {
|
||||
cc.err = newErrMissingResponseField("eACL")
|
||||
return
|
||||
}
|
||||
|
||||
res.table = *eacl.NewTableFromV2(eACL)
|
||||
}
|
||||
|
||||
// process call
|
||||
if !cc.processCall() {
|
||||
return nil, cc.err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// PrmContainerSetEACL groups parameters of ContainerSetEACL operation.
|
||||
type PrmContainerSetEACL struct {
|
||||
prmCommonMeta
|
||||
|
||||
tableSet bool
|
||||
table eacl.Table
|
||||
|
||||
sessionSet bool
|
||||
session session.Container
|
||||
}
|
||||
|
||||
// SetTable sets eACL table structure to be set for the container.
|
||||
// Required parameter.
|
||||
func (x *PrmContainerSetEACL) SetTable(table eacl.Table) {
|
||||
x.table = table
|
||||
x.tableSet = true
|
||||
}
|
||||
|
||||
// WithinSession specifies session within which extended ACL of the container
|
||||
// should be saved.
|
||||
//
|
||||
// Creator of the session acquires the authorship of the request. This affects
|
||||
// the execution of an operation (e.g. access control).
|
||||
//
|
||||
// Session is optional, if set the following requirements apply:
|
||||
// - if particular container is specified (ApplyOnlyTo), it MUST equal the container
|
||||
// for which extended ACL is going to be set
|
||||
// - session operation MUST be session.VerbContainerSetEACL (ForVerb)
|
||||
// - token MUST be signed using private key of the owner of the container to be saved
|
||||
func (x *PrmContainerSetEACL) WithinSession(s session.Container) {
|
||||
x.session = s
|
||||
x.sessionSet = true
|
||||
}
|
||||
|
||||
// ResContainerSetEACL groups resulting values of ContainerSetEACL operation.
|
||||
type ResContainerSetEACL struct {
|
||||
statusRes
|
||||
}
|
||||
|
||||
// ContainerSetEACL sends request to update eACL table of the FrostFS container.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// 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 identifier (see EACL).
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmContainerSetEACL docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) ContainerSetEACL(ctx context.Context, prm PrmContainerSetEACL) (*ResContainerSetEACL, error) {
|
||||
// check parameters
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case !prm.tableSet:
|
||||
panic("eACL table not set")
|
||||
}
|
||||
|
||||
// sign the eACL table
|
||||
eaclV2 := prm.table.ToV2()
|
||||
|
||||
var sig frostfscrypto.Signature
|
||||
|
||||
err := sig.Calculate(frostfsecdsa.SignerRFC6979(c.prm.key), eaclV2.StableMarshal(nil))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("calculate signature: %w", err)
|
||||
}
|
||||
|
||||
var sigv2 refs.Signature
|
||||
|
||||
sig.WriteToV2(&sigv2)
|
||||
|
||||
// form request body
|
||||
reqBody := new(v2container.SetExtendedACLRequestBody)
|
||||
reqBody.SetEACL(eaclV2)
|
||||
reqBody.SetSignature(&sigv2)
|
||||
|
||||
// form meta header
|
||||
var meta v2session.RequestMetaHeader
|
||||
writeXHeadersToMeta(prm.prmCommonMeta.xHeaders, &meta)
|
||||
|
||||
if prm.sessionSet {
|
||||
var tokv2 v2session.Token
|
||||
prm.session.WriteToV2(&tokv2)
|
||||
|
||||
meta.SetSessionToken(&tokv2)
|
||||
}
|
||||
|
||||
// form request
|
||||
var req v2container.SetExtendedACLRequest
|
||||
|
||||
req.SetBody(reqBody)
|
||||
req.SetMetaHeader(&meta)
|
||||
|
||||
// init call context
|
||||
|
||||
var (
|
||||
cc contextCall
|
||||
res ResContainerSetEACL
|
||||
)
|
||||
|
||||
c.initCallContext(&cc)
|
||||
cc.req = &req
|
||||
cc.statusRes = &res
|
||||
cc.call = func() (responseV2, error) {
|
||||
return rpcapi.SetEACL(&c.c, &req, client.WithContext(ctx))
|
||||
}
|
||||
|
||||
// process call
|
||||
if !cc.processCall() {
|
||||
return nil, cc.err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// PrmAnnounceSpace groups parameters of ContainerAnnounceUsedSpace operation.
|
||||
type PrmAnnounceSpace struct {
|
||||
prmCommonMeta
|
||||
|
||||
announcements []container.SizeEstimation
|
||||
}
|
||||
|
||||
// SetValues sets values describing volume of space that is used for the container objects.
|
||||
// Required parameter. Must not be empty.
|
||||
//
|
||||
// Must not be mutated before the end of the operation.
|
||||
func (x *PrmAnnounceSpace) SetValues(vs []container.SizeEstimation) {
|
||||
x.announcements = vs
|
||||
}
|
||||
|
||||
// ResAnnounceSpace groups resulting values of ContainerAnnounceUsedSpace operation.
|
||||
type ResAnnounceSpace struct {
|
||||
statusRes
|
||||
}
|
||||
|
||||
// ContainerAnnounceUsedSpace sends request to announce volume of the space used for the container objects.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Operation is asynchronous and no guaranteed even in the absence of errors.
|
||||
// The required time is also not predictable.
|
||||
//
|
||||
// At this moment success can not be checked.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmAnnounceSpace docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) ContainerAnnounceUsedSpace(ctx context.Context, prm PrmAnnounceSpace) (*ResAnnounceSpace, error) {
|
||||
// check parameters
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case len(prm.announcements) == 0:
|
||||
panic("missing announcements")
|
||||
}
|
||||
|
||||
// convert list of SDK announcement structures into FrostFS-API v2 list
|
||||
v2announce := make([]v2container.UsedSpaceAnnouncement, len(prm.announcements))
|
||||
for i := range prm.announcements {
|
||||
prm.announcements[i].WriteToV2(&v2announce[i])
|
||||
}
|
||||
|
||||
// prepare body of the FrostFS-API v2 request and request itself
|
||||
reqBody := new(v2container.AnnounceUsedSpaceRequestBody)
|
||||
reqBody.SetAnnouncements(v2announce)
|
||||
|
||||
// form request
|
||||
var req v2container.AnnounceUsedSpaceRequest
|
||||
|
||||
req.SetBody(reqBody)
|
||||
|
||||
// init call context
|
||||
|
||||
var (
|
||||
cc contextCall
|
||||
res ResAnnounceSpace
|
||||
)
|
||||
|
||||
c.initCallContext(&cc)
|
||||
cc.meta = prm.prmCommonMeta
|
||||
cc.req = &req
|
||||
cc.statusRes = &res
|
||||
cc.call = func() (responseV2, error) {
|
||||
return rpcapi.AnnounceUsedSpace(&c.c, &req, client.WithContext(ctx))
|
||||
}
|
||||
|
||||
// process call
|
||||
if !cc.processCall() {
|
||||
return nil, cc.err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// SyncContainerWithNetwork requests network configuration using passed client
|
||||
// and applies it to the container. Container MUST not be nil.
|
||||
//
|
||||
|
|
134
client/container_delete.go
Normal file
134
client/container_delete.go
Normal file
|
@ -0,0 +1,134 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||
)
|
||||
|
||||
// PrmContainerDelete groups parameters of ContainerDelete operation.
|
||||
type PrmContainerDelete struct {
|
||||
prmCommonMeta
|
||||
|
||||
idSet bool
|
||||
id cid.ID
|
||||
|
||||
tokSet bool
|
||||
tok session.Container
|
||||
}
|
||||
|
||||
// SetContainer sets identifier of the FrostFS container to be removed.
|
||||
// Required parameter.
|
||||
func (x *PrmContainerDelete) SetContainer(id cid.ID) {
|
||||
x.id = id
|
||||
x.idSet = true
|
||||
}
|
||||
|
||||
func (x *PrmContainerDelete) buildRequest(c *Client) (*v2container.DeleteRequest, error) {
|
||||
if !x.idSet {
|
||||
return nil, errorMissingContainer
|
||||
}
|
||||
|
||||
var cidV2 refs.ContainerID
|
||||
x.id.WriteToV2(&cidV2)
|
||||
|
||||
// Container contract expects signature of container ID value,
|
||||
// don't get confused with stable marshaled protobuf container.ID structure.
|
||||
data := cidV2.GetValue()
|
||||
|
||||
var sig frostfscrypto.Signature
|
||||
|
||||
err := sig.Calculate(frostfsecdsa.SignerRFC6979(c.prm.key), data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("calculate signature: %w", err)
|
||||
}
|
||||
|
||||
var sigv2 refs.Signature
|
||||
sig.WriteToV2(&sigv2)
|
||||
|
||||
reqBody := new(v2container.DeleteRequestBody)
|
||||
reqBody.SetContainerID(&cidV2)
|
||||
reqBody.SetSignature(&sigv2)
|
||||
|
||||
var meta v2session.RequestMetaHeader
|
||||
writeXHeadersToMeta(x.prmCommonMeta.xHeaders, &meta)
|
||||
|
||||
if x.tokSet {
|
||||
var tokv2 v2session.Token
|
||||
x.tok.WriteToV2(&tokv2)
|
||||
|
||||
meta.SetSessionToken(&tokv2)
|
||||
}
|
||||
|
||||
var req v2container.DeleteRequest
|
||||
req.SetBody(reqBody)
|
||||
c.prepareRequest(&req, &meta)
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
// WithinSession specifies session within which container should be removed.
|
||||
//
|
||||
// Creator of the session acquires the authorship of the request.
|
||||
// This may affect the execution of an operation (e.g. access control).
|
||||
//
|
||||
// Must be signed.
|
||||
func (x *PrmContainerDelete) WithinSession(tok session.Container) {
|
||||
x.tok = tok
|
||||
x.tokSet = true
|
||||
}
|
||||
|
||||
// ResContainerDelete groups resulting values of ContainerDelete operation.
|
||||
type ResContainerDelete struct {
|
||||
statusRes
|
||||
}
|
||||
|
||||
// ContainerDelete sends request to remove the FrostFS container.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// 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 identifier (see GetContainer).
|
||||
//
|
||||
// Returns an error if parameters are set incorrectly (see PrmContainerDelete docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Exactly one return value is non-nil. Server status return is returned in ResContainerDelete.
|
||||
// Reflects all internal errors in second return value (transport problems, response processing, etc.).
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) ContainerDelete(ctx context.Context, prm PrmContainerDelete) (*ResContainerDelete, error) {
|
||||
req, err := prm.buildRequest(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
|
||||
return nil, fmt.Errorf("sign request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := rpcapi.DeleteContainer(&c.c, req, client.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res ResContainerDelete
|
||||
res.st, err = c.processResponse(resp)
|
||||
return &res, err
|
||||
}
|
105
client/container_eacl.go
Normal file
105
client/container_eacl.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||
)
|
||||
|
||||
// PrmContainerEACL groups parameters of ContainerEACL operation.
|
||||
type PrmContainerEACL struct {
|
||||
prmCommonMeta
|
||||
|
||||
idSet bool
|
||||
id cid.ID
|
||||
}
|
||||
|
||||
// SetContainer sets identifier of the FrostFS container to read the eACL table.
|
||||
// Required parameter.
|
||||
func (x *PrmContainerEACL) SetContainer(id cid.ID) {
|
||||
x.id = id
|
||||
x.idSet = true
|
||||
}
|
||||
|
||||
func (x *PrmContainerEACL) buildRequest(c *Client) (*v2container.GetExtendedACLRequest, error) {
|
||||
if !x.idSet {
|
||||
return nil, errorMissingContainer
|
||||
}
|
||||
|
||||
var cidV2 refs.ContainerID
|
||||
x.id.WriteToV2(&cidV2)
|
||||
|
||||
reqBody := new(v2container.GetExtendedACLRequestBody)
|
||||
reqBody.SetContainerID(&cidV2)
|
||||
|
||||
var req v2container.GetExtendedACLRequest
|
||||
req.SetBody(reqBody)
|
||||
c.prepareRequest(&req, new(v2session.RequestMetaHeader))
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
// ResContainerEACL groups resulting values of ContainerEACL operation.
|
||||
type ResContainerEACL struct {
|
||||
statusRes
|
||||
|
||||
table eacl.Table
|
||||
}
|
||||
|
||||
// Table returns eACL table of the requested container.
|
||||
func (x ResContainerEACL) Table() eacl.Table {
|
||||
return x.table
|
||||
}
|
||||
|
||||
// ContainerEACL reads eACL table of the FrostFS container.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Returns an error if parameters are set incorrectly (see PrmContainerEACL docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs);
|
||||
// - *apistatus.ContainerNotFound;
|
||||
// - *apistatus.EACLNotFound.
|
||||
func (c *Client) ContainerEACL(ctx context.Context, prm PrmContainerEACL) (*ResContainerEACL, error) {
|
||||
req, err := prm.buildRequest(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
|
||||
return nil, fmt.Errorf("sign request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := rpcapi.GetEACL(&c.c, req, client.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res ResContainerEACL
|
||||
res.st, err = c.processResponse(resp)
|
||||
if err != nil || !apistatus.IsSuccessful(res.st) {
|
||||
return &res, err
|
||||
}
|
||||
|
||||
eACL := resp.GetBody().GetEACL()
|
||||
if eACL == nil {
|
||||
return &res, newErrMissingResponseField("eACL")
|
||||
}
|
||||
|
||||
res.table = *eacl.NewTableFromV2(eACL)
|
||||
return &res, nil
|
||||
}
|
108
client/container_get.go
Normal file
108
client/container_get.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
)
|
||||
|
||||
// PrmContainerGet groups parameters of ContainerGet operation.
|
||||
type PrmContainerGet struct {
|
||||
prmCommonMeta
|
||||
|
||||
idSet bool
|
||||
id cid.ID
|
||||
}
|
||||
|
||||
// SetContainer sets identifier of the container to be read.
|
||||
// Required parameter.
|
||||
func (x *PrmContainerGet) SetContainer(id cid.ID) {
|
||||
x.id = id
|
||||
x.idSet = true
|
||||
}
|
||||
|
||||
func (x *PrmContainerGet) buildRequest(c *Client) (*v2container.GetRequest, error) {
|
||||
if !x.idSet {
|
||||
return nil, errorMissingContainer
|
||||
}
|
||||
|
||||
var cidV2 refs.ContainerID
|
||||
x.id.WriteToV2(&cidV2)
|
||||
|
||||
reqBody := new(v2container.GetRequestBody)
|
||||
reqBody.SetContainerID(&cidV2)
|
||||
|
||||
var req v2container.GetRequest
|
||||
req.SetBody(reqBody)
|
||||
c.prepareRequest(&req, new(v2session.RequestMetaHeader))
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
// ResContainerGet groups resulting values of ContainerGet operation.
|
||||
type ResContainerGet struct {
|
||||
statusRes
|
||||
|
||||
cnr container.Container
|
||||
}
|
||||
|
||||
// Container returns structured information about the requested container.
|
||||
//
|
||||
// Client doesn't retain value so modification is safe.
|
||||
func (x ResContainerGet) Container() container.Container {
|
||||
return x.cnr
|
||||
}
|
||||
|
||||
// ContainerGet reads FrostFS container by ID.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Returns an error if parameters are set incorrectly (see PrmContainerGet docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs);
|
||||
// - *apistatus.ContainerNotFound.
|
||||
func (c *Client) ContainerGet(ctx context.Context, prm PrmContainerGet) (*ResContainerGet, error) {
|
||||
req, err := prm.buildRequest(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
|
||||
return nil, fmt.Errorf("sign request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := rpcapi.GetContainer(&c.c, req, client.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res ResContainerGet
|
||||
res.st, err = c.processResponse(resp)
|
||||
if err != nil || !apistatus.IsSuccessful(res.st) {
|
||||
return &res, err
|
||||
}
|
||||
|
||||
cnrV2 := resp.GetBody().GetContainer()
|
||||
if cnrV2 == nil {
|
||||
return &res, errors.New("missing container in response")
|
||||
}
|
||||
if err := res.cnr.ReadFromV2(*cnrV2); err != nil {
|
||||
return &res, fmt.Errorf("invalid container in response: %w", err)
|
||||
}
|
||||
return &res, nil
|
||||
}
|
106
client/container_list.go
Normal file
106
client/container_list.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
)
|
||||
|
||||
// PrmContainerList groups parameters of ContainerList operation.
|
||||
type PrmContainerList struct {
|
||||
prmCommonMeta
|
||||
|
||||
ownerSet bool
|
||||
ownerID user.ID
|
||||
}
|
||||
|
||||
// SetAccount sets identifier of the FrostFS account to list the containers.
|
||||
// Required parameter.
|
||||
func (x *PrmContainerList) SetAccount(id user.ID) {
|
||||
x.ownerID = id
|
||||
x.ownerSet = true
|
||||
}
|
||||
|
||||
func (x *PrmContainerList) buildRequest(c *Client) (*v2container.ListRequest, error) {
|
||||
if !x.ownerSet {
|
||||
return nil, errorAccountNotSet
|
||||
}
|
||||
|
||||
var ownerV2 refs.OwnerID
|
||||
x.ownerID.WriteToV2(&ownerV2)
|
||||
|
||||
reqBody := new(v2container.ListRequestBody)
|
||||
reqBody.SetOwnerID(&ownerV2)
|
||||
|
||||
var req v2container.ListRequest
|
||||
req.SetBody(reqBody)
|
||||
c.prepareRequest(&req, new(v2session.RequestMetaHeader))
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
// ResContainerList groups resulting values of ContainerList operation.
|
||||
type ResContainerList struct {
|
||||
statusRes
|
||||
|
||||
ids []cid.ID
|
||||
}
|
||||
|
||||
// Containers returns list of identifiers of the account-owned containers.
|
||||
//
|
||||
// Client doesn't retain value so modification is safe.
|
||||
func (x ResContainerList) Containers() []cid.ID {
|
||||
return x.ids
|
||||
}
|
||||
|
||||
// ContainerList requests identifiers of the account-owned containers.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Returns an error if parameters are set incorrectly (see PrmContainerList docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) ContainerList(ctx context.Context, prm PrmContainerList) (*ResContainerList, error) {
|
||||
req, err := prm.buildRequest(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
|
||||
return nil, fmt.Errorf("sign request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := rpcapi.ListContainers(&c.c, req, client.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res ResContainerList
|
||||
res.st, err = c.processResponse(resp)
|
||||
if err != nil || !apistatus.IsSuccessful(res.st) {
|
||||
return &res, err
|
||||
}
|
||||
|
||||
res.ids = make([]cid.ID, len(resp.GetBody().GetContainerIDs()))
|
||||
for i, cidV2 := range resp.GetBody().GetContainerIDs() {
|
||||
if err := res.ids[i].ReadFromV2(cidV2); err != nil {
|
||||
return &res, fmt.Errorf("invalid ID in the response: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
155
client/container_put.go
Normal file
155
client/container_put.go
Normal file
|
@ -0,0 +1,155 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||
)
|
||||
|
||||
// PrmContainerPut groups parameters of ContainerPut operation.
|
||||
type PrmContainerPut struct {
|
||||
prmCommonMeta
|
||||
|
||||
cnrSet bool
|
||||
cnr container.Container
|
||||
|
||||
sessionSet bool
|
||||
session session.Container
|
||||
}
|
||||
|
||||
// SetContainer sets structured information about new FrostFS container.
|
||||
// Required parameter.
|
||||
func (x *PrmContainerPut) SetContainer(cnr container.Container) {
|
||||
x.cnr = cnr
|
||||
x.cnrSet = true
|
||||
}
|
||||
|
||||
// WithinSession specifies session within which container should be saved.
|
||||
//
|
||||
// Creator of the session acquires the authorship of the request. This affects
|
||||
// the execution of an operation (e.g. access control).
|
||||
//
|
||||
// Session is optional, if set the following requirements apply:
|
||||
// - session operation MUST be session.VerbContainerPut (ForVerb)
|
||||
// - token MUST be signed using private key of the owner of the container to be saved
|
||||
func (x *PrmContainerPut) WithinSession(s session.Container) {
|
||||
x.session = s
|
||||
x.sessionSet = true
|
||||
}
|
||||
|
||||
func (x *PrmContainerPut) buildRequest(c *Client) (*v2container.PutRequest, error) {
|
||||
if !x.cnrSet {
|
||||
return nil, errorMissingContainer
|
||||
}
|
||||
|
||||
// TODO: check private key is set before forming the request
|
||||
var cnr v2container.Container
|
||||
x.cnr.WriteToV2(&cnr)
|
||||
|
||||
var sig frostfscrypto.Signature
|
||||
|
||||
err := container.CalculateSignature(&sig, x.cnr, c.prm.key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("calculate container signature: %w", err)
|
||||
}
|
||||
|
||||
var sigv2 refs.Signature
|
||||
sig.WriteToV2(&sigv2)
|
||||
|
||||
reqBody := new(v2container.PutRequestBody)
|
||||
reqBody.SetContainer(&cnr)
|
||||
reqBody.SetSignature(&sigv2)
|
||||
|
||||
var meta v2session.RequestMetaHeader
|
||||
writeXHeadersToMeta(x.prmCommonMeta.xHeaders, &meta)
|
||||
|
||||
if x.sessionSet {
|
||||
var tokv2 v2session.Token
|
||||
x.session.WriteToV2(&tokv2)
|
||||
|
||||
meta.SetSessionToken(&tokv2)
|
||||
}
|
||||
|
||||
var req v2container.PutRequest
|
||||
req.SetBody(reqBody)
|
||||
c.prepareRequest(&req, &meta)
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
// ResContainerPut groups resulting values of ContainerPut operation.
|
||||
type ResContainerPut struct {
|
||||
statusRes
|
||||
|
||||
id cid.ID
|
||||
}
|
||||
|
||||
// ID returns identifier of the container declared to be stored in the system.
|
||||
// Used as a link to information about the container (in particular, you can
|
||||
// asynchronously check if the save was successful).
|
||||
func (x ResContainerPut) ID() cid.ID {
|
||||
return x.id
|
||||
}
|
||||
|
||||
// ContainerPut sends request to save container in FrostFS.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// 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 identifier (see ResContainerPut.ID).
|
||||
//
|
||||
// Returns an error if parameters are set incorrectly (see PrmContainerPut docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
//
|
||||
// nolint: funlen
|
||||
func (c *Client) ContainerPut(ctx context.Context, prm PrmContainerPut) (*ResContainerPut, error) {
|
||||
req, err := prm.buildRequest(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
|
||||
return nil, fmt.Errorf("sign request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := rpcapi.PutContainer(&c.c, req, client.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res ResContainerPut
|
||||
res.st, err = c.processResponse(resp)
|
||||
if err != nil || !apistatus.IsSuccessful(res.st) {
|
||||
return &res, err
|
||||
}
|
||||
|
||||
const fieldCnrID = "container ID"
|
||||
|
||||
cidV2 := resp.GetBody().GetContainerID()
|
||||
if cidV2 == nil {
|
||||
return &res, newErrMissingResponseField(fieldCnrID)
|
||||
}
|
||||
if err := res.id.ReadFromV2(*cidV2); err != nil {
|
||||
return &res, newErrInvalidResponseField(fieldCnrID, err)
|
||||
}
|
||||
return &res, nil
|
||||
}
|
131
client/container_set_eacl.go
Normal file
131
client/container_set_eacl.go
Normal file
|
@ -0,0 +1,131 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||
)
|
||||
|
||||
// PrmContainerSetEACL groups parameters of ContainerSetEACL operation.
|
||||
type PrmContainerSetEACL struct {
|
||||
prmCommonMeta
|
||||
|
||||
tableSet bool
|
||||
table eacl.Table
|
||||
|
||||
sessionSet bool
|
||||
session session.Container
|
||||
}
|
||||
|
||||
// SetTable sets eACL table structure to be set for the container.
|
||||
// Required parameter.
|
||||
func (x *PrmContainerSetEACL) SetTable(table eacl.Table) {
|
||||
x.table = table
|
||||
x.tableSet = true
|
||||
}
|
||||
|
||||
// WithinSession specifies session within which extended ACL of the container
|
||||
// should be saved.
|
||||
//
|
||||
// Creator of the session acquires the authorship of the request. This affects
|
||||
// the execution of an operation (e.g. access control).
|
||||
//
|
||||
// Session is optional, if set the following requirements apply:
|
||||
// - if particular container is specified (ApplyOnlyTo), it MUST equal the container
|
||||
// for which extended ACL is going to be set
|
||||
// - session operation MUST be session.VerbContainerSetEACL (ForVerb)
|
||||
// - token MUST be signed using private key of the owner of the container to be saved
|
||||
func (x *PrmContainerSetEACL) WithinSession(s session.Container) {
|
||||
x.session = s
|
||||
x.sessionSet = true
|
||||
}
|
||||
|
||||
func (x *PrmContainerSetEACL) buildRequest(c *Client) (*v2container.SetExtendedACLRequest, error) {
|
||||
if !x.tableSet {
|
||||
return nil, errorEACLTableNotSet
|
||||
}
|
||||
|
||||
eaclV2 := x.table.ToV2()
|
||||
|
||||
var sig frostfscrypto.Signature
|
||||
|
||||
err := sig.Calculate(frostfsecdsa.SignerRFC6979(c.prm.key), eaclV2.StableMarshal(nil))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("calculate signature: %w", err)
|
||||
}
|
||||
|
||||
var sigv2 refs.Signature
|
||||
sig.WriteToV2(&sigv2)
|
||||
|
||||
reqBody := new(v2container.SetExtendedACLRequestBody)
|
||||
reqBody.SetEACL(eaclV2)
|
||||
reqBody.SetSignature(&sigv2)
|
||||
|
||||
var meta v2session.RequestMetaHeader
|
||||
writeXHeadersToMeta(x.prmCommonMeta.xHeaders, &meta)
|
||||
|
||||
if x.sessionSet {
|
||||
var tokv2 v2session.Token
|
||||
x.session.WriteToV2(&tokv2)
|
||||
|
||||
meta.SetSessionToken(&tokv2)
|
||||
}
|
||||
|
||||
var req v2container.SetExtendedACLRequest
|
||||
req.SetBody(reqBody)
|
||||
c.prepareRequest(&req, &meta)
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
// ResContainerSetEACL groups resulting values of ContainerSetEACL operation.
|
||||
type ResContainerSetEACL struct {
|
||||
statusRes
|
||||
}
|
||||
|
||||
// ContainerSetEACL sends request to update eACL table of the FrostFS container.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// 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 identifier (see EACL).
|
||||
//
|
||||
// Returns an error if parameters are set incorrectly (see PrmContainerSetEACL docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) ContainerSetEACL(ctx context.Context, prm PrmContainerSetEACL) (*ResContainerSetEACL, error) {
|
||||
req, err := prm.buildRequest(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
|
||||
return nil, fmt.Errorf("sign request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := rpcapi.SetEACL(&c.c, req, client.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res ResContainerSetEACL
|
||||
res.st, err = c.processResponse(resp)
|
||||
return &res, err
|
||||
}
|
90
client/container_space.go
Normal file
90
client/container_space.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
)
|
||||
|
||||
// PrmAnnounceSpace groups parameters of ContainerAnnounceUsedSpace operation.
|
||||
type PrmAnnounceSpace struct {
|
||||
prmCommonMeta
|
||||
|
||||
announcements []container.SizeEstimation
|
||||
}
|
||||
|
||||
// SetValues sets values describing volume of space that is used for the container objects.
|
||||
// Required parameter. Must not be empty.
|
||||
//
|
||||
// Must not be mutated before the end of the operation.
|
||||
func (x *PrmAnnounceSpace) SetValues(vs []container.SizeEstimation) {
|
||||
x.announcements = vs
|
||||
}
|
||||
|
||||
func (x *PrmAnnounceSpace) buildRequest(c *Client) (*v2container.AnnounceUsedSpaceRequest, error) {
|
||||
if len(x.announcements) == 0 {
|
||||
return nil, errorMissingAnnouncements
|
||||
}
|
||||
|
||||
v2announce := make([]v2container.UsedSpaceAnnouncement, len(x.announcements))
|
||||
for i := range x.announcements {
|
||||
x.announcements[i].WriteToV2(&v2announce[i])
|
||||
}
|
||||
|
||||
reqBody := new(v2container.AnnounceUsedSpaceRequestBody)
|
||||
reqBody.SetAnnouncements(v2announce)
|
||||
|
||||
var req v2container.AnnounceUsedSpaceRequest
|
||||
req.SetBody(reqBody)
|
||||
c.prepareRequest(&req, new(v2session.RequestMetaHeader))
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
// ResAnnounceSpace groups resulting values of ContainerAnnounceUsedSpace operation.
|
||||
type ResAnnounceSpace struct {
|
||||
statusRes
|
||||
}
|
||||
|
||||
// ContainerAnnounceUsedSpace sends request to announce volume of the space used for the container objects.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Operation is asynchronous and no guaranteed even in the absence of errors.
|
||||
// The required time is also not predictable.
|
||||
//
|
||||
// At this moment success can not be checked.
|
||||
//
|
||||
// Returns an error if parameters are set incorrectly (see PrmAnnounceSpace docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) ContainerAnnounceUsedSpace(ctx context.Context, prm PrmAnnounceSpace) (*ResAnnounceSpace, error) {
|
||||
req, err := prm.buildRequest(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
|
||||
return nil, fmt.Errorf("sign request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := rpcapi.AnnounceUsedSpace(&c.c, req, client.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res ResAnnounceSpace
|
||||
res.st, err = c.processResponse(resp)
|
||||
return &res, err
|
||||
}
|
|
@ -47,8 +47,8 @@ Consume custom service of the server:
|
|||
rpc CustomRPC(CustomRPCRequest) returns (CustomRPCResponse);
|
||||
}
|
||||
|
||||
import "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
import "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/common"
|
||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/common"
|
||||
|
||||
req := new(CustomRPCRequest)
|
||||
// ...
|
||||
|
@ -72,7 +72,7 @@ for the all operations are write-only and the results of the all operations are
|
|||
read-only. To be able to override client behavior (e.g. for tests), abstract it
|
||||
with an interface:
|
||||
|
||||
import "github.com/TrueCloudLab/frostfs-sdk-go/client"
|
||||
import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||
|
||||
type FrostFSClient interface {
|
||||
// Operations according to the application needs
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
)
|
||||
|
||||
// unwraps err using errors.Unwrap and returns the result.
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/client"
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
198
client/netmap.go
198
client/netmap.go
|
@ -4,14 +4,14 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
v2netmap "github.com/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/version"
|
||||
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
||||
)
|
||||
|
||||
// PrmEndpointInfo groups parameters of EndpointInfo operation.
|
||||
|
@ -19,6 +19,16 @@ type PrmEndpointInfo struct {
|
|||
prmCommonMeta
|
||||
}
|
||||
|
||||
func (x *PrmEndpointInfo) buildRequest(c *Client) (*v2netmap.LocalNodeInfoRequest, error) {
|
||||
meta := new(v2session.RequestMetaHeader)
|
||||
writeXHeadersToMeta(x.xHeaders, meta)
|
||||
|
||||
req := new(v2netmap.LocalNodeInfoRequest)
|
||||
req.SetBody(new(v2netmap.LocalNodeInfoRequestBody))
|
||||
c.prepareRequest(req, meta)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// ResEndpointInfo group resulting values of EndpointInfo operation.
|
||||
type ResEndpointInfo struct {
|
||||
statusRes
|
||||
|
@ -47,7 +57,7 @@ func (x ResEndpointInfo) NodeInfo() netmap.NodeInfo {
|
|||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmEndpointInfo docs).
|
||||
// Returns an error if parameters are set incorrectly (see PrmEndpointInfo docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Exactly one return value is non-nil. Server status return is returned in ResEndpointInfo.
|
||||
|
@ -56,67 +66,47 @@ func (x ResEndpointInfo) NodeInfo() netmap.NodeInfo {
|
|||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) EndpointInfo(ctx context.Context, prm PrmEndpointInfo) (*ResEndpointInfo, error) {
|
||||
// check context
|
||||
if ctx == nil {
|
||||
panic(panicMsgMissingContext)
|
||||
req, err := prm.buildRequest(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// form request
|
||||
var req v2netmap.LocalNodeInfoRequest
|
||||
|
||||
// init call context
|
||||
|
||||
var (
|
||||
cc contextCall
|
||||
res ResEndpointInfo
|
||||
)
|
||||
|
||||
c.initCallContext(&cc)
|
||||
cc.meta = prm.prmCommonMeta
|
||||
cc.req = &req
|
||||
cc.statusRes = &res
|
||||
cc.call = func() (responseV2, error) {
|
||||
return rpcapi.LocalNodeInfo(&c.c, &req, client.WithContext(ctx))
|
||||
}
|
||||
cc.result = func(r responseV2) {
|
||||
resp := r.(*v2netmap.LocalNodeInfoResponse)
|
||||
|
||||
body := resp.GetBody()
|
||||
|
||||
const fieldVersion = "version"
|
||||
|
||||
verV2 := body.GetVersion()
|
||||
if verV2 == nil {
|
||||
cc.err = newErrMissingResponseField(fieldVersion)
|
||||
return
|
||||
}
|
||||
|
||||
cc.err = res.version.ReadFromV2(*verV2)
|
||||
if cc.err != nil {
|
||||
cc.err = newErrInvalidResponseField(fieldVersion, cc.err)
|
||||
return
|
||||
}
|
||||
|
||||
const fieldNodeInfo = "node info"
|
||||
|
||||
nodeInfoV2 := body.GetNodeInfo()
|
||||
if nodeInfoV2 == nil {
|
||||
cc.err = newErrMissingResponseField(fieldNodeInfo)
|
||||
return
|
||||
}
|
||||
|
||||
cc.err = res.ni.ReadFromV2(*nodeInfoV2)
|
||||
if cc.err != nil {
|
||||
cc.err = newErrInvalidResponseField(fieldNodeInfo, cc.err)
|
||||
return
|
||||
}
|
||||
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
|
||||
return nil, fmt.Errorf("sign request: %w", err)
|
||||
}
|
||||
|
||||
// process call
|
||||
if !cc.processCall() {
|
||||
return nil, cc.err
|
||||
resp, err := rpcapi.LocalNodeInfo(&c.c, req, client.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res ResEndpointInfo
|
||||
res.st, err = c.processResponse(resp)
|
||||
if err != nil || !apistatus.IsSuccessful(res.st) {
|
||||
return &res, err
|
||||
}
|
||||
|
||||
body := resp.GetBody()
|
||||
|
||||
const fieldVersion = "version"
|
||||
|
||||
verV2 := body.GetVersion()
|
||||
if verV2 == nil {
|
||||
return nil, newErrMissingResponseField(fieldVersion)
|
||||
}
|
||||
if err := res.version.ReadFromV2(*verV2); err != nil {
|
||||
return nil, newErrInvalidResponseField(fieldVersion, err)
|
||||
}
|
||||
|
||||
const fieldNodeInfo = "node info"
|
||||
|
||||
nodeInfoV2 := body.GetNodeInfo()
|
||||
if nodeInfoV2 == nil {
|
||||
return nil, newErrMissingResponseField(fieldNodeInfo)
|
||||
}
|
||||
if err := res.ni.ReadFromV2(*nodeInfoV2); err != nil {
|
||||
return nil, newErrInvalidResponseField(fieldNodeInfo, err)
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
|
@ -125,6 +115,16 @@ type PrmNetworkInfo struct {
|
|||
prmCommonMeta
|
||||
}
|
||||
|
||||
func (x PrmNetworkInfo) buildRequest(c *Client) (*v2netmap.NetworkInfoRequest, error) {
|
||||
meta := new(v2session.RequestMetaHeader)
|
||||
writeXHeadersToMeta(x.xHeaders, meta)
|
||||
|
||||
var req v2netmap.NetworkInfoRequest
|
||||
req.SetBody(new(v2netmap.NetworkInfoRequestBody))
|
||||
c.prepareRequest(&req, meta)
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
// ResNetworkInfo groups resulting values of NetworkInfo operation.
|
||||
type ResNetworkInfo struct {
|
||||
statusRes
|
||||
|
@ -144,7 +144,7 @@ func (x ResNetworkInfo) Info() netmap.NetworkInfo {
|
|||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmNetworkInfo docs).
|
||||
// Returns an error if parameters are set incorrectly (see PrmNetworkInfo docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Exactly one return value is non-nil. Server status return is returned in ResNetworkInfo.
|
||||
|
@ -153,51 +153,35 @@ func (x ResNetworkInfo) Info() netmap.NetworkInfo {
|
|||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) NetworkInfo(ctx context.Context, prm PrmNetworkInfo) (*ResNetworkInfo, error) {
|
||||
// check context
|
||||
if ctx == nil {
|
||||
panic(panicMsgMissingContext)
|
||||
req, err := prm.buildRequest(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// form request
|
||||
var req v2netmap.NetworkInfoRequest
|
||||
|
||||
// init call context
|
||||
|
||||
var (
|
||||
cc contextCall
|
||||
res ResNetworkInfo
|
||||
)
|
||||
|
||||
c.initCallContext(&cc)
|
||||
cc.meta = prm.prmCommonMeta
|
||||
cc.req = &req
|
||||
cc.statusRes = &res
|
||||
cc.call = func() (responseV2, error) {
|
||||
return rpcapi.NetworkInfo(&c.c, &req, client.WithContext(ctx))
|
||||
}
|
||||
cc.result = func(r responseV2) {
|
||||
resp := r.(*v2netmap.NetworkInfoResponse)
|
||||
|
||||
const fieldNetInfo = "network info"
|
||||
|
||||
netInfoV2 := resp.GetBody().GetNetworkInfo()
|
||||
if netInfoV2 == nil {
|
||||
cc.err = newErrMissingResponseField(fieldNetInfo)
|
||||
return
|
||||
}
|
||||
|
||||
cc.err = res.info.ReadFromV2(*netInfoV2)
|
||||
if cc.err != nil {
|
||||
cc.err = newErrInvalidResponseField(fieldNetInfo, cc.err)
|
||||
return
|
||||
}
|
||||
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
|
||||
return nil, fmt.Errorf("sign request: %w", err)
|
||||
}
|
||||
|
||||
// process call
|
||||
if !cc.processCall() {
|
||||
return nil, cc.err
|
||||
resp, err := rpcapi.NetworkInfo(&c.c, req, client.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res ResNetworkInfo
|
||||
res.st, err = c.processResponse(resp)
|
||||
if err != nil || !apistatus.IsSuccessful(res.st) {
|
||||
return &res, err
|
||||
}
|
||||
|
||||
const fieldNetInfo = "network info"
|
||||
|
||||
netInfoV2 := resp.GetBody().GetNetworkInfo()
|
||||
if netInfoV2 == nil {
|
||||
return nil, newErrMissingResponseField(fieldNetInfo)
|
||||
}
|
||||
if err := res.info.ReadFromV2(*netInfoV2); err != nil {
|
||||
return nil, newErrInvalidResponseField(fieldNetInfo, err)
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
|
@ -224,6 +208,7 @@ func (x ResNetMapSnapshot) NetMap() netmap.NetMap {
|
|||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Returns an error if parameters are set incorrectly.
|
||||
// Context is required and MUST NOT be nil. It is used for network communication.
|
||||
//
|
||||
// Exactly one return value is non-nil. Server status return is returned in ResNetMapSnapshot.
|
||||
|
@ -232,11 +217,6 @@ func (x ResNetMapSnapshot) NetMap() netmap.NetMap {
|
|||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) NetMapSnapshot(ctx context.Context, _ PrmNetMapSnapshot) (*ResNetMapSnapshot, error) {
|
||||
// check context
|
||||
if ctx == nil {
|
||||
panic(panicMsgMissingContext)
|
||||
}
|
||||
|
||||
// form request body
|
||||
var body v2netmap.SnapshotRequestBody
|
||||
|
||||
|
|
|
@ -6,11 +6,11 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
v2netmap "github.com/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -69,12 +69,6 @@ func TestClient_NetMapSnapshot(t *testing.T) {
|
|||
c := newClient(&srv)
|
||||
ctx := context.Background()
|
||||
|
||||
// missing context
|
||||
require.PanicsWithValue(t, panicMsgMissingContext, func() {
|
||||
//nolint:staticcheck
|
||||
_, _ = c.NetMapSnapshot(nil, prm)
|
||||
})
|
||||
|
||||
// request signature
|
||||
srv.errTransport = errors.New("any error")
|
||||
|
||||
|
|
|
@ -5,18 +5,18 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
v2refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||
)
|
||||
|
||||
// PrmObjectDelete groups parameters of ObjectDelete operation.
|
||||
|
@ -114,7 +114,7 @@ func (x ResObjectDelete) Tombstone() oid.ID {
|
|||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmObjectDelete docs).
|
||||
// Returns an error if parameters are set incorrectly (see PrmObjectDelete docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
|
@ -125,12 +125,10 @@ func (x ResObjectDelete) Tombstone() oid.ID {
|
|||
// - *apistatus.SessionTokenExpired.
|
||||
func (c *Client) ObjectDelete(ctx context.Context, prm PrmObjectDelete) (*ResObjectDelete, error) {
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case prm.addr.GetContainerID() == nil:
|
||||
panic(panicMsgMissingContainer)
|
||||
return nil, errorMissingContainer
|
||||
case prm.addr.GetObjectID() == nil:
|
||||
panic(panicMsgMissingObject)
|
||||
return nil, errorMissingObject
|
||||
}
|
||||
|
||||
// form request body
|
||||
|
|
|
@ -7,19 +7,19 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
v2refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||
)
|
||||
|
||||
// shared parameters of GET/HEAD/RANGE.
|
||||
|
@ -296,17 +296,15 @@ func (x *ObjectReader) Read(p []byte) (int, error) {
|
|||
// The call only opens the transmission channel, explicit fetching is done using the ObjectReader.
|
||||
// Exactly one return value is non-nil. Resulting reader must be finally closed.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmObjectGet docs).
|
||||
// Returns an error if parameters are set incorrectly (see PrmObjectGet docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
func (c *Client) ObjectGetInit(ctx context.Context, prm PrmObjectGet) (*ObjectReader, error) {
|
||||
// check parameters
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case prm.addr.GetContainerID() == nil:
|
||||
panic(panicMsgMissingContainer)
|
||||
return nil, errorMissingContainer
|
||||
case prm.addr.GetObjectID() == nil:
|
||||
panic(panicMsgMissingObject)
|
||||
return nil, errorMissingObject
|
||||
}
|
||||
|
||||
// form request body
|
||||
|
@ -400,7 +398,7 @@ func (x *ResObjectHead) ReadHeader(dst *object.Object) bool {
|
|||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmObjectHead docs).
|
||||
// Returns an error if parameters are set incorrectly (see PrmObjectHead docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return errors:
|
||||
|
@ -416,12 +414,10 @@ func (x *ResObjectHead) ReadHeader(dst *object.Object) bool {
|
|||
// - *apistatus.SessionTokenExpired.
|
||||
func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectHead, error) {
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case prm.addr.GetContainerID() == nil:
|
||||
panic(panicMsgMissingContainer)
|
||||
return nil, errorMissingContainer
|
||||
case prm.addr.GetObjectID() == nil:
|
||||
panic(panicMsgMissingObject)
|
||||
return nil, errorMissingObject
|
||||
}
|
||||
|
||||
var body v2object.HeadRequestBody
|
||||
|
@ -663,19 +659,17 @@ func (x *ObjectRangeReader) Read(p []byte) (int, error) {
|
|||
// The call only opens the transmission channel, explicit fetching is done using the ObjectRangeReader.
|
||||
// Exactly one return value is non-nil. Resulting reader must be finally closed.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmObjectRange docs).
|
||||
// Returns an error if parameters are set incorrectly (see PrmObjectRange docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
func (c *Client) ObjectRangeInit(ctx context.Context, prm PrmObjectRange) (*ObjectRangeReader, error) {
|
||||
// check parameters
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case prm.addr.GetContainerID() == nil:
|
||||
panic(panicMsgMissingContainer)
|
||||
return nil, errorMissingContainer
|
||||
case prm.addr.GetObjectID() == nil:
|
||||
panic(panicMsgMissingObject)
|
||||
return nil, errorMissingObject
|
||||
case prm.rng.GetLength() == 0:
|
||||
panic("zero range length")
|
||||
return nil, errorZeroRangeLength
|
||||
}
|
||||
|
||||
// form request body
|
||||
|
|
|
@ -5,18 +5,18 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
v2refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||
)
|
||||
|
||||
// PrmObjectHash groups parameters of ObjectHash operation.
|
||||
|
@ -154,7 +154,7 @@ func (x ResObjectHash) Checksums() [][]byte {
|
|||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmObjectHash docs).
|
||||
// Returns an error if parameters are set incorrectly (see PrmObjectHash docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
|
@ -166,14 +166,12 @@ func (x ResObjectHash) Checksums() [][]byte {
|
|||
// - *apistatus.SessionTokenExpired.
|
||||
func (c *Client) ObjectHash(ctx context.Context, prm PrmObjectHash) (*ResObjectHash, error) {
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case prm.addr.GetContainerID() == nil:
|
||||
panic(panicMsgMissingContainer)
|
||||
return nil, errorMissingContainer
|
||||
case prm.addr.GetObjectID() == nil:
|
||||
panic(panicMsgMissingObject)
|
||||
return nil, errorMissingObject
|
||||
case len(prm.body.GetRanges()) == 0:
|
||||
panic("missing ranges")
|
||||
return nil, errorMissingRanges
|
||||
}
|
||||
|
||||
prm.body.SetAddress(&prm.addr)
|
||||
|
|
|
@ -3,33 +3,50 @@ package client
|
|||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/transformer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||
)
|
||||
|
||||
// defaultGRPCPayloadChunkLen default value for maxChunkLen.
|
||||
// See PrmObjectPutInit.SetGRPCPayloadChunkLen for details.
|
||||
const defaultGRPCPayloadChunkLen = 3 << 20
|
||||
|
||||
// PrmObjectPutInit groups parameters of ObjectPutInit operation.
|
||||
type PrmObjectPutInit struct {
|
||||
copyNum uint32
|
||||
key *ecdsa.PrivateKey
|
||||
meta v2session.RequestMetaHeader
|
||||
copyNum []uint32
|
||||
key *ecdsa.PrivateKey
|
||||
meta v2session.RequestMetaHeader
|
||||
maxChunkLen int
|
||||
maxSize uint64
|
||||
epochSource transformer.EpochSource
|
||||
withoutHomomorphicHash bool
|
||||
}
|
||||
|
||||
// SetCopiesNumber sets number of object copies that is enough to consider put successful.
|
||||
func (x *PrmObjectPutInit) SetCopiesNumber(copiesNumber uint32) {
|
||||
x.copyNum = copiesNumber
|
||||
x.copyNum = []uint32{copiesNumber}
|
||||
}
|
||||
|
||||
// SetCopiesNumberByVectors sets ordered list of minimal required object copies numbers
|
||||
// per placement vector. List's length MUST equal container's placement vector number,
|
||||
// otherwise request will fail.
|
||||
func (x *PrmObjectPutInit) SetCopiesNumberByVectors(copiesNumbers []uint32) {
|
||||
x.copyNum = copiesNumbers
|
||||
}
|
||||
|
||||
// SetGRPCPayloadChunkLen sets maximum chunk length value for gRPC Put request.
|
||||
// Maximum chunk length restricts maximum byte length of the chunk
|
||||
// transmitted in a single stream message. It depends on
|
||||
// server settings and other message fields.
|
||||
// If not specified or negative value set, default value of 3MiB will be used.
|
||||
func (x *PrmObjectPutInit) SetGRPCPayloadChunkLen(v int) {
|
||||
x.maxChunkLen = v
|
||||
}
|
||||
|
||||
// ResObjectPut groups the final result values of ObjectPutInit operation.
|
||||
|
@ -44,29 +61,35 @@ func (x ResObjectPut) StoredObjectID() oid.ID {
|
|||
return x.obj
|
||||
}
|
||||
|
||||
// ObjectWriter is designed to write one object to FrostFS system.
|
||||
// ObjectWriter is designed to write one object or
|
||||
// multiple parts of one object to FrostFS system.
|
||||
//
|
||||
// Must be initialized using Client.ObjectPutInit, any other
|
||||
// usage is unsafe.
|
||||
type ObjectWriter struct {
|
||||
cancelCtxStream context.CancelFunc
|
||||
|
||||
client *Client
|
||||
stream interface {
|
||||
Write(*v2object.PutRequest) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
key *ecdsa.PrivateKey
|
||||
res ResObjectPut
|
||||
err error
|
||||
|
||||
chunkCalled bool
|
||||
|
||||
respV2 v2object.PutResponse
|
||||
req v2object.PutRequest
|
||||
partInit v2object.PutObjectPartInit
|
||||
partChunk v2object.PutObjectPartChunk
|
||||
type ObjectWriter interface {
|
||||
// WriteHeader writes header of the object. Result means success.
|
||||
// Failure reason can be received via Close.
|
||||
WriteHeader(context.Context, object.Object) bool
|
||||
// WritePayloadChunk writes chunk of the object payload. Result means success.
|
||||
// Failure reason can be received via Close.
|
||||
WritePayloadChunk(context.Context, []byte) bool
|
||||
// Close ends writing the object and returns the result of the operation
|
||||
// along with the final results. Must be called after using the ObjectWriter.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as Go built-in error.
|
||||
// If Client is tuned to resolve FrostFS API statuses, then FrostFS failures
|
||||
// codes are returned as error.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs);
|
||||
// - *apistatus.ContainerNotFound;
|
||||
// - *apistatus.ObjectAccessDenied;
|
||||
// - *apistatus.ObjectLocked;
|
||||
// - *apistatus.LockNonRegularObject;
|
||||
// - *apistatus.SessionTokenNotFound;
|
||||
// - *apistatus.SessionTokenExpired.
|
||||
Close(context.Context) (*ResObjectPut, error)
|
||||
}
|
||||
|
||||
// UseKey specifies private key to sign the requests.
|
||||
|
@ -105,129 +128,21 @@ func (x *PrmObjectPutInit) WithXHeaders(hs ...string) {
|
|||
writeXHeadersToMeta(hs, &x.meta)
|
||||
}
|
||||
|
||||
// WriteHeader writes header of the object. Result means success.
|
||||
// Failure reason can be received via Close.
|
||||
func (x *ObjectWriter) WriteHeader(hdr object.Object) bool {
|
||||
v2Hdr := hdr.ToV2()
|
||||
|
||||
x.partInit.SetObjectID(v2Hdr.GetObjectID())
|
||||
x.partInit.SetHeader(v2Hdr.GetHeader())
|
||||
x.partInit.SetSignature(v2Hdr.GetSignature())
|
||||
|
||||
x.req.GetBody().SetObjectPart(&x.partInit)
|
||||
x.req.SetVerificationHeader(nil)
|
||||
|
||||
x.err = signature.SignServiceMessage(x.key, &x.req)
|
||||
if x.err != nil {
|
||||
x.err = fmt.Errorf("sign message: %w", x.err)
|
||||
return false
|
||||
}
|
||||
|
||||
x.err = x.stream.Write(&x.req)
|
||||
return x.err == nil
|
||||
// WithObjectMaxSize specifies max object size value and use it during object splitting.
|
||||
// When specified, start writing to the stream only after the object is formed.
|
||||
// Continue processing the input only when the previous formed object has been successfully written.
|
||||
func (x *PrmObjectPutInit) WithObjectMaxSize(maxSize uint64) {
|
||||
x.maxSize = maxSize
|
||||
}
|
||||
|
||||
// WritePayloadChunk writes chunk of the object payload. Result means success.
|
||||
// Failure reason can be received via Close.
|
||||
func (x *ObjectWriter) WritePayloadChunk(chunk []byte) bool {
|
||||
if !x.chunkCalled {
|
||||
x.chunkCalled = true
|
||||
x.req.GetBody().SetObjectPart(&x.partChunk)
|
||||
}
|
||||
|
||||
for ln := len(chunk); ln > 0; ln = len(chunk) {
|
||||
// maxChunkLen restricts maximum byte length of the chunk
|
||||
// transmitted in a single stream message. It depends on
|
||||
// server settings and other message fields, but for now
|
||||
// we simply assume that 3MB is large enough to reduce the
|
||||
// number of messages, and not to exceed the limit
|
||||
// (4MB by default for gRPC servers).
|
||||
const maxChunkLen = 3 << 20
|
||||
if ln > maxChunkLen {
|
||||
ln = maxChunkLen
|
||||
}
|
||||
|
||||
// we deal with size limit overflow above, but there is another case:
|
||||
// what if method is called with "small" chunk many times? We write
|
||||
// a message to the stream on each call. Alternatively, we could use buffering.
|
||||
// In most cases, the chunk length does not vary between calls. Given this
|
||||
// assumption, as well as the length of the payload from the header, it is
|
||||
// possible to buffer the data of intermediate chunks, and send a message when
|
||||
// the allocated buffer is filled, or when the last chunk is received.
|
||||
// It is mentally assumed that allocating and filling the buffer is better than
|
||||
// synchronous sending, but this needs to be tested.
|
||||
x.partChunk.SetChunk(chunk[:ln])
|
||||
x.req.SetVerificationHeader(nil)
|
||||
|
||||
x.err = signature.SignServiceMessage(x.key, &x.req)
|
||||
if x.err != nil {
|
||||
x.err = fmt.Errorf("sign message: %w", x.err)
|
||||
return false
|
||||
}
|
||||
|
||||
x.err = x.stream.Write(&x.req)
|
||||
if x.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
chunk = chunk[ln:]
|
||||
}
|
||||
|
||||
return true
|
||||
// WithoutHomomorphicHash if set to true do not use Tillich-Zémor hash for payload.
|
||||
func (x *PrmObjectPutInit) WithoutHomomorphicHash(v bool) {
|
||||
x.withoutHomomorphicHash = v
|
||||
}
|
||||
|
||||
// Close ends writing the object and returns the result of the operation
|
||||
// along with the final results. Must be called after using the ObjectWriter.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as Go built-in error.
|
||||
// If Client is tuned to resolve FrostFS API statuses, then FrostFS failures
|
||||
// codes are returned as error.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs);
|
||||
// - *apistatus.ContainerNotFound;
|
||||
// - *apistatus.ObjectAccessDenied;
|
||||
// - *apistatus.ObjectLocked;
|
||||
// - *apistatus.LockNonRegularObject;
|
||||
// - *apistatus.SessionTokenNotFound;
|
||||
// - *apistatus.SessionTokenExpired.
|
||||
func (x *ObjectWriter) Close() (*ResObjectPut, error) {
|
||||
defer x.cancelCtxStream()
|
||||
|
||||
// Ignore io.EOF error, because it is expected error for client-side
|
||||
// stream termination by the server. E.g. when stream contains invalid
|
||||
// message. Server returns an error in response message (in status).
|
||||
if x.err != nil && !errors.Is(x.err, io.EOF) {
|
||||
return nil, x.err
|
||||
}
|
||||
|
||||
if x.err = x.stream.Close(); x.err != nil {
|
||||
return nil, x.err
|
||||
}
|
||||
|
||||
x.res.st, x.err = x.client.processResponse(&x.respV2)
|
||||
if x.err != nil {
|
||||
return nil, x.err
|
||||
}
|
||||
|
||||
if !apistatus.IsSuccessful(x.res.st) {
|
||||
return &x.res, nil
|
||||
}
|
||||
|
||||
const fieldID = "ID"
|
||||
|
||||
idV2 := x.respV2.GetBody().GetObjectID()
|
||||
if idV2 == nil {
|
||||
return nil, newErrMissingResponseField(fieldID)
|
||||
}
|
||||
|
||||
x.err = x.res.obj.ReadFromV2(*idV2)
|
||||
if x.err != nil {
|
||||
x.err = newErrInvalidResponseField(fieldID, x.err)
|
||||
}
|
||||
|
||||
return &x.res, nil
|
||||
// WithEpochSource specifies epoch for object when split it on client side.
|
||||
func (x *PrmObjectPutInit) WithEpochSource(es transformer.EpochSource) {
|
||||
x.epochSource = es
|
||||
}
|
||||
|
||||
// ObjectPutInit initiates writing an object through a remote server using FrostFS API protocol.
|
||||
|
@ -235,32 +150,11 @@ func (x *ObjectWriter) Close() (*ResObjectPut, error) {
|
|||
// The call only opens the transmission channel, explicit recording is done using the ObjectWriter.
|
||||
// Exactly one return value is non-nil. Resulting writer must be finally closed.
|
||||
//
|
||||
// Returns an error if parameters are set incorrectly.
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
func (c *Client) ObjectPutInit(ctx context.Context, prm PrmObjectPutInit) (*ObjectWriter, error) {
|
||||
// check parameters
|
||||
if ctx == nil {
|
||||
panic(panicMsgMissingContext)
|
||||
func (c *Client) ObjectPutInit(ctx context.Context, prm PrmObjectPutInit) (ObjectWriter, error) {
|
||||
if prm.maxSize > 0 {
|
||||
return c.objectPutInitTransformer(prm)
|
||||
}
|
||||
|
||||
var w ObjectWriter
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
stream, err := rpcapi.PutObject(&c.c, &w.respV2, client.WithContext(ctx))
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, fmt.Errorf("open stream: %w", err)
|
||||
}
|
||||
|
||||
w.key = &c.prm.key
|
||||
if prm.key != nil {
|
||||
w.key = prm.key
|
||||
}
|
||||
w.cancelCtxStream = cancel
|
||||
w.client = c
|
||||
w.stream = stream
|
||||
w.partInit.SetCopiesNumber(prm.copyNum)
|
||||
w.req.SetBody(new(v2object.PutRequestBody))
|
||||
c.prepareRequest(&w.req, &prm.meta)
|
||||
|
||||
return &w, nil
|
||||
return c.objectPutInitRaw(ctx, prm)
|
||||
}
|
||||
|
|
154
client/object_put_raw.go
Normal file
154
client/object_put_raw.go
Normal file
|
@ -0,0 +1,154 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
)
|
||||
|
||||
func (c *Client) objectPutInitRaw(ctx context.Context, prm PrmObjectPutInit) (*objectWriterRaw, error) {
|
||||
var w objectWriterRaw
|
||||
stream, err := rpcapi.PutObject(&c.c, &w.respV2, client.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open stream: %w", err)
|
||||
}
|
||||
|
||||
w.key = &c.prm.key
|
||||
if prm.key != nil {
|
||||
w.key = prm.key
|
||||
}
|
||||
w.client = c
|
||||
w.stream = stream
|
||||
w.partInit.SetCopiesNumber(prm.copyNum)
|
||||
w.req.SetBody(new(v2object.PutRequestBody))
|
||||
if prm.maxChunkLen > 0 {
|
||||
w.maxChunkLen = prm.maxChunkLen
|
||||
} else {
|
||||
w.maxChunkLen = defaultGRPCPayloadChunkLen
|
||||
}
|
||||
c.prepareRequest(&w.req, &prm.meta)
|
||||
return &w, nil
|
||||
}
|
||||
|
||||
type objectWriterRaw struct {
|
||||
client *Client
|
||||
stream interface {
|
||||
Write(*v2object.PutRequest) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
key *ecdsa.PrivateKey
|
||||
res ResObjectPut
|
||||
err error
|
||||
chunkCalled bool
|
||||
respV2 v2object.PutResponse
|
||||
req v2object.PutRequest
|
||||
partInit v2object.PutObjectPartInit
|
||||
partChunk v2object.PutObjectPartChunk
|
||||
maxChunkLen int
|
||||
}
|
||||
|
||||
func (x *objectWriterRaw) WriteHeader(_ context.Context, hdr object.Object) bool {
|
||||
v2Hdr := hdr.ToV2()
|
||||
|
||||
x.partInit.SetObjectID(v2Hdr.GetObjectID())
|
||||
x.partInit.SetHeader(v2Hdr.GetHeader())
|
||||
x.partInit.SetSignature(v2Hdr.GetSignature())
|
||||
|
||||
x.req.GetBody().SetObjectPart(&x.partInit)
|
||||
x.req.SetVerificationHeader(nil)
|
||||
|
||||
x.err = signature.SignServiceMessage(x.key, &x.req)
|
||||
if x.err != nil {
|
||||
x.err = fmt.Errorf("sign message: %w", x.err)
|
||||
return false
|
||||
}
|
||||
|
||||
x.err = x.stream.Write(&x.req)
|
||||
return x.err == nil
|
||||
}
|
||||
|
||||
func (x *objectWriterRaw) WritePayloadChunk(_ context.Context, chunk []byte) bool {
|
||||
if !x.chunkCalled {
|
||||
x.chunkCalled = true
|
||||
x.req.GetBody().SetObjectPart(&x.partChunk)
|
||||
}
|
||||
|
||||
for ln := len(chunk); ln > 0; ln = len(chunk) {
|
||||
if ln > x.maxChunkLen {
|
||||
ln = x.maxChunkLen
|
||||
}
|
||||
|
||||
// we deal with size limit overflow above, but there is another case:
|
||||
// what if method is called with "small" chunk many times? We write
|
||||
// a message to the stream on each call. Alternatively, we could use buffering.
|
||||
// In most cases, the chunk length does not vary between calls. Given this
|
||||
// assumption, as well as the length of the payload from the header, it is
|
||||
// possible to buffer the data of intermediate chunks, and send a message when
|
||||
// the allocated buffer is filled, or when the last chunk is received.
|
||||
// It is mentally assumed that allocating and filling the buffer is better than
|
||||
// synchronous sending, but this needs to be tested.
|
||||
x.partChunk.SetChunk(chunk[:ln])
|
||||
x.req.SetVerificationHeader(nil)
|
||||
|
||||
x.err = signature.SignServiceMessage(x.key, &x.req)
|
||||
if x.err != nil {
|
||||
x.err = fmt.Errorf("sign message: %w", x.err)
|
||||
return false
|
||||
}
|
||||
|
||||
x.err = x.stream.Write(&x.req)
|
||||
if x.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
chunk = chunk[ln:]
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (x *objectWriterRaw) Close(_ context.Context) (*ResObjectPut, error) {
|
||||
// Ignore io.EOF error, because it is expected error for client-side
|
||||
// stream termination by the server. E.g. when stream contains invalid
|
||||
// message. Server returns an error in response message (in status).
|
||||
if x.err != nil && !errors.Is(x.err, io.EOF) {
|
||||
return nil, x.err
|
||||
}
|
||||
|
||||
if x.err = x.stream.Close(); x.err != nil {
|
||||
return nil, x.err
|
||||
}
|
||||
|
||||
x.res.st, x.err = x.client.processResponse(&x.respV2)
|
||||
if x.err != nil {
|
||||
return nil, x.err
|
||||
}
|
||||
|
||||
if !apistatus.IsSuccessful(x.res.st) {
|
||||
return &x.res, nil
|
||||
}
|
||||
|
||||
const fieldID = "ID"
|
||||
|
||||
idV2 := x.respV2.GetBody().GetObjectID()
|
||||
if idV2 == nil {
|
||||
return nil, newErrMissingResponseField(fieldID)
|
||||
}
|
||||
|
||||
x.err = x.res.obj.ReadFromV2(*idV2)
|
||||
if x.err != nil {
|
||||
x.err = newErrInvalidResponseField(fieldID, x.err)
|
||||
}
|
||||
|
||||
return &x.res, nil
|
||||
}
|
88
client/object_put_transformer.go
Normal file
88
client/object_put_transformer.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/transformer"
|
||||
)
|
||||
|
||||
func (c *Client) objectPutInitTransformer(prm PrmObjectPutInit) (*objectWriterTransformer, error) {
|
||||
var w objectWriterTransformer
|
||||
w.it = internalTarget{
|
||||
client: c,
|
||||
prm: prm,
|
||||
}
|
||||
key := &c.prm.key
|
||||
if prm.key != nil {
|
||||
key = prm.key
|
||||
}
|
||||
w.ot = transformer.NewPayloadSizeLimiter(transformer.Params{
|
||||
Key: key,
|
||||
NextTargetInit: func() transformer.ObjectTarget { return &w.it },
|
||||
MaxSize: prm.maxSize,
|
||||
WithoutHomomorphicHash: prm.withoutHomomorphicHash,
|
||||
NetworkState: prm.epochSource,
|
||||
})
|
||||
return &w, nil
|
||||
}
|
||||
|
||||
type objectWriterTransformer struct {
|
||||
ot transformer.ObjectTarget
|
||||
it internalTarget
|
||||
err error
|
||||
}
|
||||
|
||||
func (x *objectWriterTransformer) WriteHeader(ctx context.Context, hdr object.Object) bool {
|
||||
x.err = x.ot.WriteHeader(ctx, &hdr)
|
||||
return x.err == nil
|
||||
}
|
||||
|
||||
func (x *objectWriterTransformer) WritePayloadChunk(ctx context.Context, chunk []byte) bool {
|
||||
_, x.err = x.ot.Write(ctx, chunk)
|
||||
return x.err == nil
|
||||
}
|
||||
|
||||
func (x *objectWriterTransformer) Close(ctx context.Context) (*ResObjectPut, error) {
|
||||
if ai, err := x.ot.Close(ctx); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if ai != nil && ai.ParentID != nil {
|
||||
x.it.res.obj = *ai.ParentID
|
||||
}
|
||||
return x.it.res, nil
|
||||
}
|
||||
}
|
||||
|
||||
type internalTarget struct {
|
||||
current *object.Object
|
||||
client *Client
|
||||
res *ResObjectPut
|
||||
prm PrmObjectPutInit
|
||||
payload []byte
|
||||
}
|
||||
|
||||
func (it *internalTarget) WriteHeader(_ context.Context, object *object.Object) error {
|
||||
it.current = object
|
||||
return nil
|
||||
}
|
||||
|
||||
func (it *internalTarget) Write(_ context.Context, p []byte) (n int, err error) {
|
||||
it.payload = append(it.payload, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (it *internalTarget) Close(ctx context.Context) (*transformer.AccessIdentifiers, error) {
|
||||
it.current.SetPayload(it.payload)
|
||||
wrt, err := it.client.objectPutInitRaw(ctx, it.prm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if wrt.WriteHeader(ctx, *it.current) {
|
||||
wrt.WritePayloadChunk(ctx, it.current.Payload())
|
||||
}
|
||||
it.res, err = wrt.Close(ctx)
|
||||
it.current = nil
|
||||
it.payload = nil
|
||||
return nil, err
|
||||
}
|
|
@ -7,19 +7,19 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
v2refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||
)
|
||||
|
||||
// PrmObjectSearch groups parameters of ObjectSearch operation.
|
||||
|
@ -218,15 +218,12 @@ func (x *ObjectListReader) Close() (*ResObjectSearch, error) {
|
|||
// is done using the ObjectListReader. Exactly one return value is non-nil.
|
||||
// Resulting reader must be finally closed.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmObjectSearch docs).
|
||||
// Returns an error if parameters are set incorrectly (see PrmObjectSearch docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
func (c *Client) ObjectSearchInit(ctx context.Context, prm PrmObjectSearch) (*ObjectListReader, error) {
|
||||
// check parameters
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case !prm.cnrSet:
|
||||
panic(panicMsgMissingContainer)
|
||||
if !prm.cnrSet {
|
||||
return nil, errorMissingContainer
|
||||
}
|
||||
|
||||
var cidV2 v2refs.ContainerID
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
"io"
|
||||
"testing"
|
||||
|
||||
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
signatureV2 "github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
oidtest "github.com/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
signatureV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
|
@ -1,200 +0,0 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v2reputation "github.com/TrueCloudLab/frostfs-api-go/v2/reputation"
|
||||
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/reputation"
|
||||
)
|
||||
|
||||
// PrmAnnounceLocalTrust groups parameters of AnnounceLocalTrust operation.
|
||||
type PrmAnnounceLocalTrust struct {
|
||||
prmCommonMeta
|
||||
|
||||
epoch uint64
|
||||
|
||||
trusts []reputation.Trust
|
||||
}
|
||||
|
||||
// SetEpoch sets number of FrostFS epoch in which the trust was assessed.
|
||||
// Required parameter, must not be zero.
|
||||
func (x *PrmAnnounceLocalTrust) SetEpoch(epoch uint64) {
|
||||
x.epoch = epoch
|
||||
}
|
||||
|
||||
// SetValues sets values describing trust of the client to the FrostFS network participants.
|
||||
// Required parameter. Must not be empty.
|
||||
//
|
||||
// Must not be mutated before the end of the operation.
|
||||
func (x *PrmAnnounceLocalTrust) SetValues(trusts []reputation.Trust) {
|
||||
x.trusts = trusts
|
||||
}
|
||||
|
||||
// ResAnnounceLocalTrust groups results of AnnounceLocalTrust operation.
|
||||
type ResAnnounceLocalTrust struct {
|
||||
statusRes
|
||||
}
|
||||
|
||||
// AnnounceLocalTrust sends client's trust values to the FrostFS network participants.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmAnnounceLocalTrust docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) AnnounceLocalTrust(ctx context.Context, prm PrmAnnounceLocalTrust) (*ResAnnounceLocalTrust, error) {
|
||||
// check parameters
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case prm.epoch == 0:
|
||||
panic("zero epoch")
|
||||
case len(prm.trusts) == 0:
|
||||
panic("missing trusts")
|
||||
}
|
||||
|
||||
// form request body
|
||||
reqBody := new(v2reputation.AnnounceLocalTrustRequestBody)
|
||||
reqBody.SetEpoch(prm.epoch)
|
||||
|
||||
trusts := make([]v2reputation.Trust, len(prm.trusts))
|
||||
|
||||
for i := range prm.trusts {
|
||||
prm.trusts[i].WriteToV2(&trusts[i])
|
||||
}
|
||||
|
||||
reqBody.SetTrusts(trusts)
|
||||
|
||||
// form request
|
||||
var req v2reputation.AnnounceLocalTrustRequest
|
||||
|
||||
req.SetBody(reqBody)
|
||||
|
||||
// init call context
|
||||
|
||||
var (
|
||||
cc contextCall
|
||||
res ResAnnounceLocalTrust
|
||||
)
|
||||
|
||||
c.initCallContext(&cc)
|
||||
cc.meta = prm.prmCommonMeta
|
||||
cc.req = &req
|
||||
cc.statusRes = &res
|
||||
cc.call = func() (responseV2, error) {
|
||||
return rpcapi.AnnounceLocalTrust(&c.c, &req, client.WithContext(ctx))
|
||||
}
|
||||
|
||||
// process call
|
||||
if !cc.processCall() {
|
||||
return nil, cc.err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// PrmAnnounceIntermediateTrust groups parameters of AnnounceIntermediateTrust operation.
|
||||
type PrmAnnounceIntermediateTrust struct {
|
||||
prmCommonMeta
|
||||
|
||||
epoch uint64
|
||||
|
||||
iter uint32
|
||||
|
||||
trustSet bool
|
||||
trust reputation.PeerToPeerTrust
|
||||
}
|
||||
|
||||
// SetEpoch sets number of FrostFS epoch with which client's calculation algorithm is initialized.
|
||||
// Required parameter, must not be zero.
|
||||
func (x *PrmAnnounceIntermediateTrust) SetEpoch(epoch uint64) {
|
||||
x.epoch = epoch
|
||||
}
|
||||
|
||||
// SetIteration sets current sequence number of the client's calculation algorithm.
|
||||
// By default, corresponds to initial (zero) iteration.
|
||||
func (x *PrmAnnounceIntermediateTrust) SetIteration(iter uint32) {
|
||||
x.iter = iter
|
||||
}
|
||||
|
||||
// SetCurrentValue sets current global trust value computed at the specified iteration
|
||||
// of the client's calculation algorithm. Required parameter.
|
||||
func (x *PrmAnnounceIntermediateTrust) SetCurrentValue(trust reputation.PeerToPeerTrust) {
|
||||
x.trust = trust
|
||||
x.trustSet = true
|
||||
}
|
||||
|
||||
// ResAnnounceIntermediateTrust groups results of AnnounceIntermediateTrust operation.
|
||||
type ResAnnounceIntermediateTrust struct {
|
||||
statusRes
|
||||
}
|
||||
|
||||
// AnnounceIntermediateTrust sends global trust values calculated for the specified FrostFS network participants
|
||||
// at some stage of client's calculation algorithm.
|
||||
//
|
||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||
// Any client's internal or transport errors are returned as `error`.
|
||||
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
|
||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmAnnounceIntermediateTrust docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) AnnounceIntermediateTrust(ctx context.Context, prm PrmAnnounceIntermediateTrust) (*ResAnnounceIntermediateTrust, error) {
|
||||
// check parameters
|
||||
switch {
|
||||
case ctx == nil:
|
||||
panic(panicMsgMissingContext)
|
||||
case prm.epoch == 0:
|
||||
panic("zero epoch")
|
||||
case !prm.trustSet:
|
||||
panic("current trust value not set")
|
||||
}
|
||||
|
||||
var trust v2reputation.PeerToPeerTrust
|
||||
prm.trust.WriteToV2(&trust)
|
||||
|
||||
// form request body
|
||||
reqBody := new(v2reputation.AnnounceIntermediateResultRequestBody)
|
||||
reqBody.SetEpoch(prm.epoch)
|
||||
reqBody.SetIteration(prm.iter)
|
||||
reqBody.SetTrust(&trust)
|
||||
|
||||
// form request
|
||||
var req v2reputation.AnnounceIntermediateResultRequest
|
||||
|
||||
req.SetBody(reqBody)
|
||||
|
||||
// init call context
|
||||
|
||||
var (
|
||||
cc contextCall
|
||||
res ResAnnounceIntermediateTrust
|
||||
)
|
||||
|
||||
c.initCallContext(&cc)
|
||||
cc.meta = prm.prmCommonMeta
|
||||
cc.req = &req
|
||||
cc.statusRes = &res
|
||||
cc.call = func() (responseV2, error) {
|
||||
return rpcapi.AnnounceIntermediateResult(&c.c, &req, client.WithContext(ctx))
|
||||
}
|
||||
|
||||
// process call
|
||||
if !cc.processCall() {
|
||||
return nil, cc.err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package client
|
||||
|
||||
import "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
|
||||
// ResponseMetaInfo groups meta information about any FrostFS API response.
|
||||
type ResponseMetaInfo struct {
|
||||
|
|
|
@ -3,12 +3,15 @@ package client
|
|||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
)
|
||||
|
||||
// PrmSessionCreate groups parameters of SessionCreate operation.
|
||||
|
@ -33,6 +36,30 @@ func (x *PrmSessionCreate) UseKey(key ecdsa.PrivateKey) {
|
|||
x.key = key
|
||||
}
|
||||
|
||||
func (x *PrmSessionCreate) buildRequest(c *Client) (*v2session.CreateRequest, error) {
|
||||
ownerKey := c.prm.key.PublicKey
|
||||
if x.keySet {
|
||||
ownerKey = x.key.PublicKey
|
||||
}
|
||||
var ownerID user.ID
|
||||
user.IDFromKey(&ownerID, ownerKey)
|
||||
|
||||
var ownerIDV2 refs.OwnerID
|
||||
ownerID.WriteToV2(&ownerIDV2)
|
||||
|
||||
reqBody := new(v2session.CreateRequestBody)
|
||||
reqBody.SetOwnerID(&ownerIDV2)
|
||||
reqBody.SetExpiration(x.exp)
|
||||
|
||||
var meta v2session.RequestMetaHeader
|
||||
writeXHeadersToMeta(x.xHeaders, &meta)
|
||||
|
||||
var req v2session.CreateRequest
|
||||
req.SetBody(reqBody)
|
||||
c.prepareRequest(&req, &meta)
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
// ResSessionCreate groups resulting values of SessionCreate operation.
|
||||
type ResSessionCreate struct {
|
||||
statusRes
|
||||
|
@ -42,10 +69,6 @@ type ResSessionCreate struct {
|
|||
sessionKey []byte
|
||||
}
|
||||
|
||||
func (x *ResSessionCreate) setID(id []byte) {
|
||||
x.id = id
|
||||
}
|
||||
|
||||
// ID returns identifier of the opened session in a binary FrostFS API protocol format.
|
||||
//
|
||||
// Client doesn't retain value so modification is safe.
|
||||
|
@ -53,10 +76,6 @@ func (x ResSessionCreate) ID() []byte {
|
|||
return x.id
|
||||
}
|
||||
|
||||
func (x *ResSessionCreate) setSessionKey(key []byte) {
|
||||
x.sessionKey = key
|
||||
}
|
||||
|
||||
// PublicKey returns public key of the opened session in a binary FrostFS API protocol format.
|
||||
func (x ResSessionCreate) PublicKey() []byte {
|
||||
return x.sessionKey
|
||||
|
@ -72,68 +91,34 @@ func (x ResSessionCreate) PublicKey() []byte {
|
|||
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||
// in the returned result structure.
|
||||
//
|
||||
// Immediately panics if parameters are set incorrectly (see PrmSessionCreate docs).
|
||||
// Returns an error if parameters are set incorrectly (see PrmSessionCreate docs).
|
||||
// Context is required and must not be nil. It is used for network communication.
|
||||
//
|
||||
// Return statuses:
|
||||
// - global (see Client docs).
|
||||
func (c *Client) SessionCreate(ctx context.Context, prm PrmSessionCreate) (*ResSessionCreate, error) {
|
||||
// check context
|
||||
if ctx == nil {
|
||||
panic(panicMsgMissingContext)
|
||||
req, err := prm.buildRequest(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ownerKey := c.prm.key.PublicKey
|
||||
if prm.keySet {
|
||||
ownerKey = prm.key.PublicKey
|
||||
}
|
||||
var ownerID user.ID
|
||||
user.IDFromKey(&ownerID, ownerKey)
|
||||
|
||||
var ownerIDV2 refs.OwnerID
|
||||
ownerID.WriteToV2(&ownerIDV2)
|
||||
|
||||
// form request body
|
||||
reqBody := new(v2session.CreateRequestBody)
|
||||
reqBody.SetOwnerID(&ownerIDV2)
|
||||
reqBody.SetExpiration(prm.exp)
|
||||
|
||||
// for request
|
||||
var req v2session.CreateRequest
|
||||
|
||||
req.SetBody(reqBody)
|
||||
|
||||
// init call context
|
||||
|
||||
var (
|
||||
cc contextCall
|
||||
res ResSessionCreate
|
||||
)
|
||||
|
||||
c.initCallContext(&cc)
|
||||
if prm.keySet {
|
||||
cc.key = prm.key
|
||||
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
|
||||
return nil, fmt.Errorf("sign request: %w", err)
|
||||
}
|
||||
|
||||
cc.meta = prm.prmCommonMeta
|
||||
cc.req = &req
|
||||
cc.statusRes = &res
|
||||
cc.call = func() (responseV2, error) {
|
||||
return rpcapi.CreateSession(&c.c, &req, client.WithContext(ctx))
|
||||
}
|
||||
cc.result = func(r responseV2) {
|
||||
resp := r.(*v2session.CreateResponse)
|
||||
|
||||
body := resp.GetBody()
|
||||
|
||||
res.setID(body.GetID())
|
||||
res.setSessionKey(body.GetSessionKey())
|
||||
resp, err := rpcapi.CreateSession(&c.c, req, client.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// process call
|
||||
if !cc.processCall() {
|
||||
return nil, cc.err
|
||||
var res ResSessionCreate
|
||||
res.st, err = c.processResponse(resp)
|
||||
if err != nil || !apistatus.IsSuccessful(res.st) {
|
||||
return &res, err
|
||||
}
|
||||
|
||||
body := resp.GetBody()
|
||||
res.id = body.GetID()
|
||||
res.sessionKey = body.GetSessionKey()
|
||||
return &res, nil
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package apistatus
|
|||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
)
|
||||
|
||||
// ServerInternal describes failure statuses related to internal server errors.
|
||||
|
|
|
@ -3,8 +3,8 @@ package apistatus_test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package apistatus
|
||||
|
||||
import (
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
)
|
||||
|
||||
// ContainerNotFound describes status of the failure because of the missing container.
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package apistatus
|
||||
|
||||
import (
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
)
|
||||
|
||||
// ObjectLocked describes status of the failure because of the locked object.
|
||||
|
|
|
@ -3,7 +3,7 @@ package apistatus_test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package apistatus
|
||||
|
||||
import (
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
)
|
||||
|
||||
// SessionTokenNotFound describes status of the failure because of the missing session token.
|
||||
|
|
|
@ -15,7 +15,7 @@ package apistatus
|
|||
// It should be noted that using direct typecasting is not a compatible approach.
|
||||
//
|
||||
// To transport statuses using the FrostFS API V2 protocol, see StatusV2 interface and FromStatusV2 and ToStatusV2 functions.
|
||||
type Status interface{}
|
||||
type Status any
|
||||
|
||||
// ErrFromStatus converts Status instance to error if it is failed. Returns nil on successful Status.
|
||||
//
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"errors"
|
||||
"testing"
|
||||
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package apistatus
|
||||
|
||||
import (
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
)
|
||||
|
||||
// SuccessDefaultV2 represents Status instance of default success. Implements StatusV2.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package apistatus
|
||||
|
||||
import (
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
)
|
||||
|
||||
type unrecognizedStatusV2 struct {
|
||||
|
|
|
@ -3,10 +3,10 @@ package apistatus
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
||||
)
|
||||
|
||||
// StatusV2 defines a variety of Status instances compatible with FrostFS API V2 protocol.
|
||||
|
@ -15,7 +15,7 @@ import (
|
|||
type StatusV2 interface {
|
||||
Status
|
||||
|
||||
// ToStatusV2 returns the status as github.com/TrueCloudLab/frostfs-api-go/v2/status.Status message structure.
|
||||
// ToStatusV2 returns the status as git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status.Status message structure.
|
||||
ToStatusV2() *status.Status
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ func ToStatusV2(st Status) *status.Status {
|
|||
return internalErrorStatus
|
||||
}
|
||||
|
||||
func errMessageStatusV2(code interface{}, msg string) string {
|
||||
func errMessageStatusV2(code any, msg string) string {
|
||||
const (
|
||||
noMsgFmt = "status: code = %v"
|
||||
msgFmt = noMsgFmt + " message = %s"
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"errors"
|
||||
"testing"
|
||||
|
||||
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -12,7 +12,7 @@ func TestToStatusV2(t *testing.T) {
|
|||
type statusConstructor func() apistatus.Status
|
||||
|
||||
for _, testItem := range [...]struct {
|
||||
status interface{} // Status or statusConstructor
|
||||
status any // Status or statusConstructor
|
||||
codeV2 uint64
|
||||
messageV2 string
|
||||
}{
|
||||
|
@ -165,7 +165,7 @@ func TestFromStatusV2(t *testing.T) {
|
|||
type statusConstructor func() apistatus.Status
|
||||
|
||||
for _, testItem := range [...]struct {
|
||||
status interface{} // Status or statusConstructor
|
||||
status any // Status or statusConstructor
|
||||
codeV2 uint64
|
||||
messageV2 string
|
||||
}{
|
||||
|
|
|
@ -9,6 +9,7 @@ import "strconv"
|
|||
// use corresponding constants and/or methods instead.
|
||||
type Op uint32
|
||||
|
||||
// nolint: unused
|
||||
const (
|
||||
opZero Op = iota // extreme value for testing
|
||||
|
||||
|
@ -53,6 +54,7 @@ func (x Op) String() string {
|
|||
// use corresponding constants and/or methods instead.
|
||||
type Role uint32
|
||||
|
||||
// nolint: unused
|
||||
const (
|
||||
roleZero Role = iota // extreme value for testing
|
||||
|
||||
|
|
|
@ -7,14 +7,6 @@ func setBit(num *uint32, n uint8) {
|
|||
*num |= 1 << n
|
||||
}
|
||||
|
||||
// resets n-th bit in num (starting at 0).
|
||||
func resetBit(num *uint32, n uint8) {
|
||||
var mask uint32
|
||||
setBit(&mask, n)
|
||||
|
||||
*num &= ^mask
|
||||
}
|
||||
|
||||
// checks if n-th bit in num is set (starting at 0).
|
||||
func isBitSet(num uint32, n uint8) bool {
|
||||
mask := uint32(1 << n)
|
||||
|
|
|
@ -22,9 +22,6 @@ func TestBits(t *testing.T) {
|
|||
|
||||
setBit(&num, 6)
|
||||
require.EqualValues(t, 0b1011110, num)
|
||||
|
||||
resetBit(&num, 1)
|
||||
require.EqualValues(t, 0b1011100, num)
|
||||
}
|
||||
|
||||
func TestOpBits(t *testing.T) {
|
||||
|
|
|
@ -8,17 +8,16 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
v2netmap "github.com/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/container/acl"
|
||||
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
frostfsecdsa "github.com/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
subnetid "github.com/TrueCloudLab/frostfs-sdk-go/subnet/id"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/version"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
|
@ -37,7 +36,7 @@ import (
|
|||
// Instances for existing containers can be initialized using decoding methods
|
||||
// (e.g Unmarshal).
|
||||
//
|
||||
// Container is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/container.Container
|
||||
// Container is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container.Container
|
||||
// message. See ReadFromV2 / WriteToV2 methods.
|
||||
type Container struct {
|
||||
v2 container.Container
|
||||
|
@ -96,6 +95,16 @@ func (x *Container) readFromV2(m container.Container, checkFieldPresence bool) e
|
|||
return errors.New("missing placement policy")
|
||||
}
|
||||
|
||||
if err := checkAttributes(m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x.v2 = m
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkAttributes(m container.Container) error {
|
||||
attrs := m.GetAttributes()
|
||||
mAttr := make(map[string]struct{}, len(attrs))
|
||||
var key, val string
|
||||
|
@ -117,10 +126,8 @@ func (x *Container) readFromV2(m container.Container, checkFieldPresence bool) e
|
|||
return fmt.Errorf("empty attribute value %s", key)
|
||||
}
|
||||
|
||||
switch key {
|
||||
case container.SysAttributeSubnet:
|
||||
err = new(subnetid.ID).DecodeString(val)
|
||||
case attributeTimestamp:
|
||||
var err error
|
||||
if key == attributeTimestamp {
|
||||
_, err = strconv.ParseInt(val, 10, 64)
|
||||
}
|
||||
|
||||
|
@ -130,9 +137,6 @@ func (x *Container) readFromV2(m container.Container, checkFieldPresence bool) e
|
|||
|
||||
mAttr[key] = struct{}{}
|
||||
}
|
||||
|
||||
x.v2 = m
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -383,28 +387,6 @@ func CreatedAt(cnr Container) time.Time {
|
|||
return time.Unix(sec, 0)
|
||||
}
|
||||
|
||||
// SetSubnet places the Container on the specified FrostFS subnet. If called,
|
||||
// container nodes will only be selected from the given subnet, otherwise from
|
||||
// the entire network.
|
||||
func SetSubnet(cnr *Container, subNet subnetid.ID) {
|
||||
cnr.SetAttribute(container.SysAttributeSubnet, subNet.EncodeToString())
|
||||
}
|
||||
|
||||
// Subnet return container subnet set using SetSubnet.
|
||||
//
|
||||
// Zero Container is bound to zero subnet.
|
||||
func Subnet(cnr Container) (res subnetid.ID) {
|
||||
val := cnr.Attribute(container.SysAttributeSubnet)
|
||||
if val != "" {
|
||||
err := res.DecodeString(val)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("invalid subnet attribute: %s (%v)", val, err))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const attributeHomoHashEnabled = "true"
|
||||
|
||||
// DisableHomomorphicHashing sets flag to disable homomorphic hashing of the
|
||||
|
@ -419,7 +401,8 @@ func DisableHomomorphicHashing(cnr *Container) {
|
|||
//
|
||||
// Zero Container has enabled hashing.
|
||||
func IsHomomorphicHashingDisabled(cnr Container) bool {
|
||||
return cnr.Attribute(container.SysAttributeHomomorphicHashing) == attributeHomoHashEnabled
|
||||
return cnr.Attribute(container.SysAttributeHomomorphicHashing) == attributeHomoHashEnabled ||
|
||||
cnr.Attribute(container.SysAttributeHomomorphicHashingNeoFS) == attributeHomoHashEnabled
|
||||
}
|
||||
|
||||
// Domain represents information about container domain registered in the NNS
|
||||
|
@ -465,10 +448,12 @@ func WriteDomain(cnr *Container, domain Domain) {
|
|||
// ReadDomain reads Domain from the Container. Returns value with empty name
|
||||
// if domain is not specified.
|
||||
func ReadDomain(cnr Container) (res Domain) {
|
||||
name := cnr.Attribute(container.SysAttributeName)
|
||||
if name != "" {
|
||||
if name := cnr.Attribute(container.SysAttributeName); name != "" {
|
||||
res.SetName(name)
|
||||
res.SetZone(cnr.Attribute(container.SysAttributeZone))
|
||||
} else if name = cnr.Attribute(container.SysAttributeNameNeoFS); name != "" {
|
||||
res.SetName(name)
|
||||
res.SetZone(cnr.Attribute(container.SysAttributeZoneNeoFS))
|
||||
}
|
||||
|
||||
return
|
||||
|
|
|
@ -6,19 +6,17 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
v2container "github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
v2netmap "github.com/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/container"
|
||||
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
containertest "github.com/TrueCloudLab/frostfs-sdk-go/container/test"
|
||||
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
netmaptest "github.com/TrueCloudLab/frostfs-sdk-go/netmap/test"
|
||||
subnetid "github.com/TrueCloudLab/frostfs-sdk-go/subnet/id"
|
||||
subnetidtest "github.com/TrueCloudLab/frostfs-sdk-go/subnet/id/test"
|
||||
usertest "github.com/TrueCloudLab/frostfs-sdk-go/user/test"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/version"
|
||||
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
containertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/test"
|
||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test"
|
||||
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
||||
"github.com/google/uuid"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -233,28 +231,6 @@ func TestSetCreationTime(t *testing.T) {
|
|||
require.Equal(t, creat.Unix(), container.CreatedAt(val2).Unix())
|
||||
}
|
||||
|
||||
func TestSetSubnet(t *testing.T) {
|
||||
var val container.Container
|
||||
|
||||
require.True(t, subnetid.IsZero(container.Subnet(val)))
|
||||
|
||||
val = containertest.Container()
|
||||
|
||||
sub := subnetidtest.ID()
|
||||
|
||||
container.SetSubnet(&val, sub)
|
||||
|
||||
var msg v2container.Container
|
||||
val.WriteToV2(&msg)
|
||||
|
||||
assertContainsAttribute(t, msg, v2container.SysAttributeSubnet, sub.EncodeToString())
|
||||
|
||||
var val2 container.Container
|
||||
require.NoError(t, val2.ReadFromV2(msg))
|
||||
|
||||
require.Equal(t, sub, container.Subnet(val))
|
||||
}
|
||||
|
||||
func TestDisableHomomorphicHashing(t *testing.T) {
|
||||
var val container.Container
|
||||
|
||||
|
|
|
@ -23,11 +23,11 @@ it using the instance of Container types
|
|||
// process the container data
|
||||
|
||||
Instances can be also used to process FrostFS API V2 protocol messages
|
||||
(see neo.fs.v2.container package in https://github.com/TrueCloudLab/frostfs-api).
|
||||
(see neo.fs.v2.container package in https://git.frostfs.info/TrueCloudLab/frostfs-api).
|
||||
|
||||
On client side:
|
||||
|
||||
import "github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
|
||||
var msg container.Container
|
||||
cnr.WriteToV2(&msg)
|
||||
|
|
|
@ -4,13 +4,13 @@ import (
|
|||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"github.com/mr-tron/base58"
|
||||
)
|
||||
|
||||
// ID represents FrostFS container identifier.
|
||||
//
|
||||
// ID is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/refs.ContainerID
|
||||
// ID is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.ContainerID
|
||||
// message. See ReadFromV2 / WriteToV2 methods.
|
||||
//
|
||||
// Instances can be created using built-in var declaration.
|
||||
|
|
|
@ -5,9 +5,9 @@ import (
|
|||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
"github.com/mr-tron/base58"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@ Note that importing the package into source files is highly discouraged.
|
|||
|
||||
Random instance generation functions can be useful when testing expects any value, e.g.:
|
||||
|
||||
import cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
import cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
|
||||
cid := cidtest.ID()
|
||||
// test the value
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"crypto/sha256"
|
||||
"math/rand"
|
||||
|
||||
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
)
|
||||
|
||||
// ID returns random cid.ID.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
)
|
||||
|
||||
// ApplyNetworkConfig applies network configuration to the
|
||||
|
|
|
@ -3,9 +3,9 @@ package container_test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/container"
|
||||
containertest "github.com/TrueCloudLab/frostfs-sdk-go/container/test"
|
||||
netmaptest "github.com/TrueCloudLab/frostfs-sdk-go/netmap/test"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
containertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/test"
|
||||
netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
|
@ -4,15 +4,15 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
)
|
||||
|
||||
// SizeEstimation groups information about estimation of the size of the data
|
||||
// stored in the FrostFS container.
|
||||
//
|
||||
// SizeEstimation is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/container.UsedSpaceAnnouncement
|
||||
// SizeEstimation is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container.UsedSpaceAnnouncement
|
||||
// message. See ReadFromV2 / WriteToV2 methods.
|
||||
type SizeEstimation struct {
|
||||
m container.UsedSpaceAnnouncement
|
||||
|
|
|
@ -4,11 +4,11 @@ import (
|
|||
"crypto/sha256"
|
||||
"testing"
|
||||
|
||||
v2container "github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/container"
|
||||
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@ package containertest
|
|||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/container"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/container/acl"
|
||||
cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
netmaptest "github.com/TrueCloudLab/frostfs-sdk-go/netmap/test"
|
||||
usertest "github.com/TrueCloudLab/frostfs-sdk-go/user/test"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
||||
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test"
|
||||
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
|
||||
)
|
||||
|
||||
// Container returns random container.Container.
|
||||
|
|
|
@ -4,9 +4,9 @@ import (
|
|||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
frostfsecdsa "github.com/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
|
@ -25,11 +25,11 @@ PublicKey allows to verify signatures.
|
|||
// ...
|
||||
|
||||
Signature can be also used to process FrostFS API V2 protocol messages
|
||||
(see neo.fs.v2.refs package in https://github.com/TrueCloudLab/frostfs-api).
|
||||
(see neo.fs.v2.refs package in https://git.frostfs.info/TrueCloudLab/frostfs-api).
|
||||
|
||||
On client side:
|
||||
|
||||
import "github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
|
||||
var msg refs.Signature
|
||||
sig.WriteToV2(&msg)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package frostfsecdsa
|
||||
|
||||
import frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
import frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
|
||||
func init() {
|
||||
frostfscrypto.RegisterScheme(frostfscrypto.ECDSA_SHA512, func() frostfscrypto.PublicKey {
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
|
||||
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
)
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/util/signature/walletconnect"
|
||||
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/signature/walletconnect"
|
||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
)
|
||||
|
||||
|
|
|
@ -4,13 +4,13 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
)
|
||||
|
||||
// Signature represents a confirmation of data integrity received by the
|
||||
// digital signature mechanism.
|
||||
//
|
||||
// Signature is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/refs.Signature
|
||||
// Signature is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.Signature
|
||||
// message. See ReadFromV2 / WriteToV2 methods.
|
||||
//
|
||||
// Note that direct typecast is not safe and may result in loss of compatibility:
|
||||
|
|
|
@ -3,7 +3,7 @@ package frostfscrypto
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
)
|
||||
|
||||
// Scheme represents digital signature algorithm with fixed cryptographic hash function.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package eacl
|
||||
|
||||
import (
|
||||
v2acl "github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
)
|
||||
|
||||
// Action taken if ContainerEACL record matched request.
|
||||
|
|
|
@ -3,8 +3,8 @@ package eacl_test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
v2acl "github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
"github.com/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||
v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package eacl
|
|||
import (
|
||||
"strconv"
|
||||
|
||||
v2acl "github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
)
|
||||
|
||||
// Filter defines check conditions if request header is matched or not. Matched
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue