forked from TrueCloudLab/frostfs-sdk-go
Compare commits
1 commit
master
...
KirillovDe
Author | SHA1 | Date | |
---|---|---|---|
9424a67fb1 |
247 changed files with 6692 additions and 9819 deletions
|
@ -1,20 +0,0 @@
|
||||||
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
|
|
|
@ -1,31 +0,0 @@
|
||||||
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 @@
|
||||||
* @TrueCloudLab/storage-core @TrueCloudLab/storage-services
|
* @alexvanin @fyrchik @cthulhu-rider
|
||||||
|
|
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,45 +0,0 @@
|
||||||
---
|
|
||||||
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
1
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -1 +0,0 @@
|
||||||
blank_issues_enabled: false
|
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,20 +0,0 @@
|
||||||
---
|
|
||||||
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
Normal file
21
.github/workflows/dco.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
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
Normal file
52
.github/workflows/tests.yml
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
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,10 +20,4 @@ vendor/
|
||||||
|
|
||||||
# coverage
|
# coverage
|
||||||
coverage.txt
|
coverage.txt
|
||||||
coverage.html
|
coverage.html
|
||||||
|
|
||||||
# antlr tool jar
|
|
||||||
antlr-*.jar
|
|
||||||
|
|
||||||
# tempfiles
|
|
||||||
.cache
|
|
10
.gitlint
10
.gitlint
|
@ -1,10 +0,0 @@
|
||||||
[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
|
# options for analysis running
|
||||||
run:
|
run:
|
||||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||||
timeout: 10m
|
timeout: 5m
|
||||||
|
|
||||||
# include test files or not, default is true
|
# include test files or not, default is true
|
||||||
tests: false
|
tests: true
|
||||||
|
|
||||||
# output configuration options
|
# output configuration options
|
||||||
output:
|
output:
|
||||||
|
@ -24,13 +24,6 @@ linters-settings:
|
||||||
govet:
|
govet:
|
||||||
# report about shadowed variables
|
# report about shadowed variables
|
||||||
check-shadowing: false
|
check-shadowing: false
|
||||||
staticcheck:
|
|
||||||
checks: ["all", "-SA1019"] # TODO Enable SA1019 after deprecated warning are fixed.
|
|
||||||
funlen:
|
|
||||||
lines: 80 # default 60
|
|
||||||
statements: 60 # default 40
|
|
||||||
gocognit:
|
|
||||||
min-complexity: 40 # default 30
|
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
enable:
|
enable:
|
||||||
|
@ -39,28 +32,28 @@ linters:
|
||||||
- revive
|
- revive
|
||||||
|
|
||||||
# some default golangci-lint linters
|
# some default golangci-lint linters
|
||||||
|
- deadcode
|
||||||
- errcheck
|
- errcheck
|
||||||
- gosimple
|
- gosimple
|
||||||
- godot
|
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- staticcheck
|
- staticcheck
|
||||||
|
- structcheck
|
||||||
- typecheck
|
- typecheck
|
||||||
- unused
|
- unused
|
||||||
|
- varcheck
|
||||||
|
|
||||||
# extra linters
|
# extra linters
|
||||||
- bidichk
|
|
||||||
- durationcheck
|
|
||||||
- exhaustive
|
- exhaustive
|
||||||
- exportloopref
|
- godot
|
||||||
- gofmt
|
- gofmt
|
||||||
- goimports
|
|
||||||
- misspell
|
|
||||||
- predeclared
|
|
||||||
- reassign
|
|
||||||
- whitespace
|
- whitespace
|
||||||
- containedctx
|
- goimports
|
||||||
- funlen
|
|
||||||
- gocognit
|
|
||||||
- contextcheck
|
|
||||||
disable-all: true
|
disable-all: true
|
||||||
fast: false
|
fast: false
|
||||||
|
|
||||||
|
issues:
|
||||||
|
include:
|
||||||
|
- EXC0002 # should have a comment
|
||||||
|
- EXC0003 # test/Test ... consider calling this
|
||||||
|
- EXC0004 # govet
|
||||||
|
- EXC0005 # C-style breaks
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
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]
|
|
|
@ -1,4 +0,0 @@
|
||||||
FROM golang:1.19
|
|
||||||
|
|
||||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install make openjdk-11-jre -y
|
|
||||||
WORKDIR /work
|
|
21
Makefile
Executable file → Normal file
21
Makefile
Executable file → Normal file
|
@ -1,7 +1,5 @@
|
||||||
#!/usr/bin/make -f
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
ANTLR_VERSION="4.13.0"
|
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
test:
|
test:
|
||||||
@go test ./... -cover
|
@go test ./... -cover
|
||||||
|
@ -31,23 +29,6 @@ format:
|
||||||
@echo "⇒ Processing goimports check"
|
@echo "⇒ Processing goimports check"
|
||||||
@goimports -w ./
|
@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
|
# Show this help prompt
|
||||||
help:
|
help:
|
||||||
@echo ' Usage:'
|
@echo ' Usage:'
|
||||||
|
@ -56,4 +37,4 @@ help:
|
||||||
@echo ''
|
@echo ''
|
||||||
@echo ' Targets:'
|
@echo ' Targets:'
|
||||||
@echo ''
|
@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
|
# frostfs-sdk-go
|
||||||
Go implementation of FrostFS SDK. It contains high-level version-independent wrappers
|
Go implementation of FrostFS SDK. It contains high-level version-independent wrappers
|
||||||
for structures from [frostfs-api-go](https://git.frostfs.info/TrueCloudLab/frostfs-api-go) as well as
|
for structures from [frostfs-api-go](https://github.com/TrueCloudLab/frostfs-api-go) as well as
|
||||||
helper functions for simplifying node/dApp implementations.
|
helper functions for simplifying node/dApp implementations.
|
||||||
|
|
||||||
## Repository structure
|
## Repository structure
|
||||||
|
@ -14,7 +14,7 @@ There is also a reference implementation of checking algorithm which is used in
|
||||||
|
|
||||||
### checksum
|
### checksum
|
||||||
Contains `Checksum` type encapsulating checksum as well as it's kind.
|
Contains `Checksum` type encapsulating checksum as well as it's kind.
|
||||||
Currently Sha256 and [Tillich-Zemor hashsum](https://git.frostfs.info/TrueCloudLab/tzhash) are in use.
|
Currently Sha256 and [Tillich-Zemor hashsum](https://github.com/TrueCloudLab/tzhash) are in use.
|
||||||
|
|
||||||
### owner
|
### owner
|
||||||
`owner.ID` type represents single account interacting with FrostFS. In v2 version of protocol
|
`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
|
### ns
|
||||||
In FrostFS there are 2 types of name resolution: DNS and NNS. NNS stands for Neo Name Service
|
In FrostFS there are 2 types of name resolution: DNS and NNS. NNS stands for Neo Name Service
|
||||||
is just a [contract](https://git.frostfs.info/TrueCloudLab/frostfs-contract) deployed on a Neo blockchain.
|
is just a [contract](https://github.com/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
|
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)
|
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.
|
for the example of how NNS can be integrated in DNS.
|
||||||
|
@ -54,7 +54,7 @@ err := c.Dial(prmDial)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
|
||||||
defer cancel()
|
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.
|
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.
|
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
|
The set of reserved status codes can be found in
|
||||||
[FrostFS API](https://git.frostfs.info/TrueCloudLab/frostfs-api/src/branch/master/status/types.proto). There is also
|
[FrostFS API](https://github.com/TrueCloudLab/frostfs-api/blob/master/status/types.proto). There is also
|
||||||
a `client.PrmInit.ResolveFrostFSFailures()` to seamlessly convert erroneous statuses into Go error type.
|
a `client.PrmInit.ResolveFrostFSFailures()` to seamlessly convert erroneous statuses into Go error type.
|
||||||
|
|
||||||
### policy
|
### 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
|
are described in this paper http://ceur-ws.org/Vol-2344/short10.pdf . Note that it can be
|
||||||
outdated in some details.
|
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
|
```go
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
"github.com/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
)
|
)
|
||||||
|
|
||||||
func placementNodes(addr *object.Address, p *netmap.PlacementPolicy, frostfsNodes []netmap.NodeInfo) {
|
func placementNodes(addr *object.Address, p *netmap.PlacementPolicy, frostfsNodes []netmap.NodeInfo) {
|
||||||
// Convert list of nodes in FrostFS API format to the intermediate representation.
|
// Convert list of nodes in FrostFS API format to the intermediate representation.
|
||||||
nodes := netmap.NodesFromInfo(nodes)
|
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)
|
nm, _ := NewNetmap(nodes)
|
||||||
|
|
||||||
// Calculate nodes of container.
|
// Calculate nodes of container.
|
||||||
|
@ -131,4 +131,4 @@ Contain simple API wrappers.
|
||||||
Wrapper over `zap.Logger` which is used across FrostFS codebase.
|
Wrapper over `zap.Logger` which is used across FrostFS codebase.
|
||||||
|
|
||||||
### util
|
### util
|
||||||
Utilities for working with signature-related code.
|
Utilities for working with signature-related code.
|
|
@ -1,10 +1,10 @@
|
||||||
package accounting
|
package accounting
|
||||||
|
|
||||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
|
import "github.com/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||||
|
|
||||||
// Decimal represents decimal number for accounting operations.
|
// Decimal represents decimal number for accounting operations.
|
||||||
//
|
//
|
||||||
// Decimal is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting.Decimal
|
// Decimal is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/accounting.Decimal
|
||||||
// message. See ReadFromV2 / WriteToV2 methods.
|
// message. See ReadFromV2 / WriteToV2 methods.
|
||||||
//
|
//
|
||||||
// Instances can be created using built-in var declaration.
|
// Instances can be created using built-in var declaration.
|
||||||
|
|
|
@ -3,8 +3,8 @@ package accounting_test
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
v2accounting "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
|
v2accounting "github.com/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
|
"github.com/TrueCloudLab/frostfs-sdk-go/accounting"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,11 @@ working with Fixed8 balance precision:
|
||||||
dec.SetPrecision(8)
|
dec.SetPrecision(8)
|
||||||
|
|
||||||
Instances can be also used to process FrostFS API V2 protocol messages
|
Instances can be also used to process FrostFS API V2 protocol messages
|
||||||
(see neo.fs.v2.accounting package in https://git.frostfs.info/TrueCloudLab/frostfs-api).
|
(see neo.fs.v2.accounting package in https://github.com/TrueCloudLab/frostfs-api).
|
||||||
|
|
||||||
On client side:
|
On client side:
|
||||||
|
|
||||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
|
import "github.com/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||||
|
|
||||||
var msg accounting.Decimal
|
var msg accounting.Decimal
|
||||||
dec.WriteToV2(&msg)
|
dec.WriteToV2(&msg)
|
||||||
|
|
|
@ -3,7 +3,7 @@ package accountingtest
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
|
"github.com/TrueCloudLab/frostfs-sdk-go/accounting"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Decimal returns random accounting.Decimal.
|
// 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.:
|
Random instance generation functions can be useful when testing expects any value, e.g.:
|
||||||
|
|
||||||
import accountingtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting/test"
|
import accountingtest "github.com/TrueCloudLab/frostfs-sdk-go/accounting/test"
|
||||||
|
|
||||||
dec := accountingtest.Decimal()
|
dec := accountingtest.Decimal()
|
||||||
// test the value
|
// test the value
|
||||||
|
|
26
audit/doc.go
Normal file
26
audit/doc.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
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
Normal file
377
audit/result.go
Normal file
|
@ -0,0 +1,377 @@
|
||||||
|
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)
|
||||||
|
}
|
191
audit/result_test.go
Normal file
191
audit/result_test.go
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
13
audit/test/doc.go
Normal file
13
audit/test/doc.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
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
|
36
audit/test/generate.go
Normal file
36
audit/test/generate.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
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"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||||
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
frostfsecdsa "github.com/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
"github.com/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Token represents bearer token for object service operations.
|
// Token represents bearer token for object service operations.
|
||||||
//
|
//
|
||||||
// Token is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl.BearerToken
|
// Token is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/acl.BearerToken
|
||||||
// message. See ReadFromV2 / WriteToV2 methods.
|
// message. See ReadFromV2 / WriteToV2 methods.
|
||||||
//
|
//
|
||||||
// Instances can be created using built-in var declaration.
|
// Instances can be created using built-in var declaration.
|
||||||
|
@ -46,12 +46,10 @@ func (b *Token) readFromV2(m acl.BearerToken, checkFieldPresence bool) error {
|
||||||
return errors.New("missing token body")
|
return errors.New("missing token body")
|
||||||
}
|
}
|
||||||
|
|
||||||
b.impersonate = body.GetImpersonate()
|
|
||||||
|
|
||||||
eaclTable := body.GetEACL()
|
eaclTable := body.GetEACL()
|
||||||
if b.eaclTableSet = eaclTable != nil; b.eaclTableSet {
|
if b.eaclTableSet = eaclTable != nil; b.eaclTableSet {
|
||||||
b.eaclTable = *eacl.NewTableFromV2(eaclTable)
|
b.eaclTable = *eacl.NewTableFromV2(eaclTable)
|
||||||
} else if checkFieldPresence && !b.impersonate {
|
} else if checkFieldPresence {
|
||||||
return errors.New("missing eACL table")
|
return errors.New("missing eACL table")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +70,8 @@ func (b *Token) readFromV2(m acl.BearerToken, checkFieldPresence bool) error {
|
||||||
return errors.New("missing token lifetime")
|
return errors.New("missing token lifetime")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.impersonate = body.GetImpersonate()
|
||||||
|
|
||||||
sig := m.GetSignature()
|
sig := m.GetSignature()
|
||||||
if b.sigSet = sig != nil; sig != nil {
|
if b.sigSet = sig != nil; sig != nil {
|
||||||
b.sig = *sig
|
b.sig = *sig
|
||||||
|
@ -90,7 +90,7 @@ func (b *Token) ReadFromV2(m acl.BearerToken) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Token) fillBody() *acl.BearerTokenBody {
|
func (b Token) fillBody() *acl.BearerTokenBody {
|
||||||
if !b.eaclTableSet && !b.targetUserSet && !b.lifetimeSet && !b.impersonate {
|
if !b.eaclTableSet && !b.targetUserSet && !b.lifetimeSet {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,13 +214,10 @@ func (b Token) EACLTable() eacl.Table {
|
||||||
return 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) {
|
func (b *Token) SetImpersonate(v bool) {
|
||||||
b.impersonate = v
|
b.impersonate = v
|
||||||
}
|
}
|
||||||
|
|
||||||
// Impersonate returns true if token is impersonated.
|
|
||||||
func (b Token) Impersonate() bool {
|
func (b Token) Impersonate() bool {
|
||||||
return b.impersonate
|
return b.impersonate
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,17 +5,17 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||||
bearertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer/test"
|
bearertest "github.com/TrueCloudLab/frostfs-sdk-go/bearer/test"
|
||||||
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||||
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
frostfsecdsa "github.com/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
"github.com/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
eacltest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl/test"
|
eacltest "github.com/TrueCloudLab/frostfs-sdk-go/eacl/test"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
|
usertest "github.com/TrueCloudLab/frostfs-sdk-go/user/test"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -323,10 +323,6 @@ func TestToken_ReadFromV2(t *testing.T) {
|
||||||
|
|
||||||
require.NoError(t, val.ReadFromV2(m))
|
require.NoError(t, val.ReadFromV2(m))
|
||||||
|
|
||||||
body.SetEACL(nil)
|
|
||||||
body.SetImpersonate(true)
|
|
||||||
require.NoError(t, val.ReadFromV2(m))
|
|
||||||
|
|
||||||
var m2 acl.BearerToken
|
var m2 acl.BearerToken
|
||||||
|
|
||||||
val.WriteToV2(&m2)
|
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
|
Provide signed token in JSON or binary format to the request sender. Request
|
||||||
sender can attach this bearer token to the object service requests:
|
sender can attach this bearer token to the object service requests:
|
||||||
|
|
||||||
import sdkClient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
import sdkClient "github.com/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
|
|
||||||
var headParams sdkClient.PrmObjectHead
|
var headParams sdkClient.PrmObjectHead
|
||||||
headParams.WithBearerToken(bearerToken)
|
headParams.WithBearerToken(bearerToken)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package bearertest
|
package bearertest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||||
eacltest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl/test"
|
eacltest "github.com/TrueCloudLab/frostfs-sdk-go/eacl/test"
|
||||||
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
|
usertest "github.com/TrueCloudLab/frostfs-sdk-go/user/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Token returns random bearer.Token.
|
// Token returns random bearer.Token.
|
||||||
|
|
|
@ -6,13 +6,13 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
"git.frostfs.info/TrueCloudLab/tzhash/tz"
|
"github.com/TrueCloudLab/tzhash/tz"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Checksum represents checksum of some digital data.
|
// Checksum represents checksum of some digital data.
|
||||||
//
|
//
|
||||||
// Checksum is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.Checksum
|
// Checksum is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/refs.Checksum
|
||||||
// message. See ReadFromV2 / WriteToV2 methods.
|
// message. See ReadFromV2 / WriteToV2 methods.
|
||||||
//
|
//
|
||||||
// Instances can be created using built-in var declaration.
|
// Instances can be created using built-in var declaration.
|
||||||
|
|
|
@ -5,8 +5,8 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
"git.frostfs.info/TrueCloudLab/tzhash/tz"
|
"github.com/TrueCloudLab/tzhash/tz"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleCalculate() {
|
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.:
|
Random instance generation functions can be useful when testing expects any value, e.g.:
|
||||||
|
|
||||||
import checksumtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum/test"
|
import checksumtest "github.com/TrueCloudLab/frostfs-sdk-go/checksum/test"
|
||||||
|
|
||||||
cs := checksumtest.Checksum()
|
cs := checksumtest.Checksum()
|
||||||
// test the value
|
// test the value
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
|
"github.com/TrueCloudLab/frostfs-sdk-go/checksum"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Checksum returns random checksum.Checksum.
|
// Checksum returns random checksum.Checksum.
|
||||||
|
|
|
@ -3,12 +3,12 @@ package client
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
v2accounting "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
|
v2accounting "github.com/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
|
"github.com/TrueCloudLab/frostfs-sdk-go/accounting"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrmBalanceGet groups parameters of BalanceGet operation.
|
// PrmBalanceGet groups parameters of BalanceGet operation.
|
||||||
|
@ -46,14 +46,17 @@ func (x ResBalanceGet) Amount() accounting.Decimal {
|
||||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||||
// in the returned result structure.
|
// in the returned result structure.
|
||||||
//
|
//
|
||||||
// Returns an error if parameters are set incorrectly (see PrmBalanceGet docs).
|
// Immediately panics if parameters are set incorrectly (see PrmBalanceGet docs).
|
||||||
// Context is required and must not be nil. It is used for network communication.
|
// Context is required and must not be nil. It is used for network communication.
|
||||||
//
|
//
|
||||||
// Return statuses:
|
// Return statuses:
|
||||||
// - global (see Client docs).
|
// - global (see Client docs).
|
||||||
func (c *Client) BalanceGet(ctx context.Context, prm PrmBalanceGet) (*ResBalanceGet, error) {
|
func (c *Client) BalanceGet(ctx context.Context, prm PrmBalanceGet) (*ResBalanceGet, error) {
|
||||||
if !prm.accountSet {
|
switch {
|
||||||
return nil, errorAccountNotSet
|
case ctx == nil:
|
||||||
|
panic(panicMsgMissingContext)
|
||||||
|
case !prm.accountSet:
|
||||||
|
panic("account not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
// form request body
|
// form request body
|
||||||
|
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
v2netmap "github.com/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
// interface of FrostFS API server. Exists for test purposes only.
|
// interface of FrostFS API server. Exists for test purposes only.
|
||||||
|
|
|
@ -7,10 +7,9 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
v2accounting "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
|
v2accounting "github.com/TrueCloudLab/frostfs-api-go/v2/accounting"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client represents virtual connection to the FrostFS network to communicate
|
// Client represents virtual connection to the FrostFS network to communicate
|
||||||
|
@ -70,21 +69,21 @@ func (c *Client) Init(prm PrmInit) {
|
||||||
// argument, otherwise context.Background() is used. Dial returns context
|
// argument, otherwise context.Background() is used. Dial returns context
|
||||||
// errors, see context package docs for details.
|
// errors, see context package docs for details.
|
||||||
//
|
//
|
||||||
// Returns an error if required parameters are set incorrectly, look carefully
|
// Panics if required parameters are set incorrectly, look carefully
|
||||||
// at the method documentation.
|
// at the method documentation.
|
||||||
//
|
//
|
||||||
// One-time method call during application start-up stage (after Init ) is expected.
|
// One-time method call during application start-up stage (after Init ) is expected.
|
||||||
// Calling multiple times leads to undefined behavior.
|
// Calling multiple times leads to undefined behavior.
|
||||||
//
|
//
|
||||||
// See also Init / Close.
|
// See also Init / Close.
|
||||||
func (c *Client) Dial(ctx context.Context, prm PrmDial) error {
|
func (c *Client) Dial(prm PrmDial) error {
|
||||||
if prm.endpoint == "" {
|
if prm.endpoint == "" {
|
||||||
return errorServerAddrUnset
|
panic("server address is unset or empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
if prm.timeoutDialSet {
|
if prm.timeoutDialSet {
|
||||||
if prm.timeoutDial <= 0 {
|
if prm.timeoutDial <= 0 {
|
||||||
return errorNonPositiveTimeout
|
panic("non-positive timeout")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
prm.timeoutDial = 5 * time.Second
|
prm.timeoutDial = 5 * time.Second
|
||||||
|
@ -92,7 +91,7 @@ func (c *Client) Dial(ctx context.Context, prm PrmDial) error {
|
||||||
|
|
||||||
if prm.streamTimeoutSet {
|
if prm.streamTimeoutSet {
|
||||||
if prm.streamTimeout <= 0 {
|
if prm.streamTimeout <= 0 {
|
||||||
return errorNonPositiveTimeout
|
panic("non-positive timeout")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
prm.streamTimeout = 10 * time.Second
|
prm.streamTimeout = 10 * time.Second
|
||||||
|
@ -102,14 +101,17 @@ func (c *Client) Dial(ctx context.Context, prm PrmDial) error {
|
||||||
client.WithNetworkURIAddress(prm.endpoint, prm.tlsConfig),
|
client.WithNetworkURIAddress(prm.endpoint, prm.tlsConfig),
|
||||||
client.WithDialTimeout(prm.timeoutDial),
|
client.WithDialTimeout(prm.timeoutDial),
|
||||||
client.WithRWTimeout(prm.streamTimeout),
|
client.WithRWTimeout(prm.streamTimeout),
|
||||||
client.WithGRPCDialOptions(prm.dialOptions),
|
|
||||||
)...)
|
)...)
|
||||||
|
|
||||||
c.setFrostFSAPIServer((*coreServer)(&c.c))
|
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
|
// TODO: (neofs-api-go#382) perform generic dial stage of the client.Client
|
||||||
_, err := rpc.Balance(&c.c, new(v2accounting.BalanceRequest),
|
_, err := rpc.Balance(&c.c, new(v2accounting.BalanceRequest),
|
||||||
client.WithContext(ctx),
|
client.WithContext(prm.parentCtx),
|
||||||
)
|
)
|
||||||
// return context errors since they signal about dial problem
|
// return context errors since they signal about dial problem
|
||||||
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
||||||
|
@ -121,7 +123,7 @@ func (c *Client) Dial(ctx context.Context, prm PrmDial) error {
|
||||||
|
|
||||||
// sets underlying provider of frostFSAPIServer. The method is used for testing as an approach
|
// 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.
|
// to skip Dial stage and override FrostFS API server. MUST NOT be used outside test code.
|
||||||
// In real applications wrapper over git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client
|
// In real applications wrapper over github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client
|
||||||
// is statically used.
|
// is statically used.
|
||||||
func (c *Client) setFrostFSAPIServer(server frostFSAPIServer) {
|
func (c *Client) setFrostFSAPIServer(server frostFSAPIServer) {
|
||||||
c.server = server
|
c.server = server
|
||||||
|
@ -189,7 +191,7 @@ type PrmDial struct {
|
||||||
streamTimeoutSet bool
|
streamTimeoutSet bool
|
||||||
streamTimeout time.Duration
|
streamTimeout time.Duration
|
||||||
|
|
||||||
dialOptions []grpc.DialOption
|
parentCtx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetServerURI sets server URI in the FrostFS network.
|
// SetServerURI sets server URI in the FrostFS network.
|
||||||
|
@ -231,7 +233,10 @@ func (x *PrmDial) SetStreamTimeout(timeout time.Duration) {
|
||||||
x.streamTimeout = timeout
|
x.streamTimeout = timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetGRPCDialOptions sets the gRPC dial options for new gRPC client connection.
|
// SetContext allows to specify optional base context within which connection
|
||||||
func (x *PrmDial) SetGRPCDialOptions(opts ...grpc.DialOption) {
|
// should be established.
|
||||||
x.dialOptions = opts
|
//
|
||||||
|
// Context SHOULD NOT be nil.
|
||||||
|
func (x *PrmDial) SetContext(ctx context.Context) {
|
||||||
|
x.parentCtx = ctx
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,8 +47,11 @@ func TestClient_DialContext(t *testing.T) {
|
||||||
prm.SetServerURI("localhost:8080")
|
prm.SetServerURI("localhost:8080")
|
||||||
|
|
||||||
assert := func(ctx context.Context, errExpected error) {
|
assert := func(ctx context.Context, errExpected error) {
|
||||||
|
// use the particular context
|
||||||
|
prm.SetContext(ctx)
|
||||||
|
|
||||||
// expect particular context error according to Dial docs
|
// expect particular context error according to Dial docs
|
||||||
require.ErrorIs(t, c.Dial(ctx, prm), errExpected)
|
require.ErrorIs(t, c.Dial(prm), errExpected)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create pre-abandoned context
|
// create pre-abandoned context
|
||||||
|
|
|
@ -2,15 +2,14 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
"github.com/TrueCloudLab/frostfs-sdk-go/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// common interface of resulting structures with API status.
|
// common interface of resulting structures with API status.
|
||||||
|
@ -71,17 +70,11 @@ func writeXHeadersToMeta(xHeaders []string, h *v2session.RequestMetaHeader) {
|
||||||
h.SetXHeaders(hs)
|
h.SetXHeaders(hs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// error messages.
|
// panic messages.
|
||||||
var (
|
const (
|
||||||
errorMissingContainer = errors.New("missing container")
|
panicMsgMissingContext = "missing context"
|
||||||
errorMissingObject = errors.New("missing object")
|
panicMsgMissingContainer = "missing container"
|
||||||
errorAccountNotSet = errors.New("account not set")
|
panicMsgMissingObject = "missing object"
|
||||||
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.
|
// groups all the details required to send a single request and process a response to it.
|
||||||
|
@ -257,16 +250,6 @@ func (x *contextCall) processResponse() bool {
|
||||||
|
|
||||||
// processResponse verifies response signature and converts status to an error if needed.
|
// processResponse verifies response signature and converts status to an error if needed.
|
||||||
func (c *Client) processResponse(resp responseV2) (apistatus.Status, error) {
|
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)
|
err := signature.VerifyServiceMessage(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid response signature: %w", err)
|
return nil, fmt.Errorf("invalid response signature: %w", err)
|
||||||
|
@ -351,7 +334,7 @@ func (c *Client) initCallContext(ctx *contextCall) {
|
||||||
ctx.netMagic = c.prm.netMagic
|
ctx.netMagic = c.prm.netMagic
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecRaw executes f with underlying git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client.Client
|
// ExecRaw executes f with underlying github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client.Client
|
||||||
// instance. Communicate over the Protocol Buffers protocol in a more flexible way:
|
// 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
|
// most often used to transmit data over a fixed version of the FrostFS protocol, as well
|
||||||
// as to support custom services.
|
// as to support custom services.
|
||||||
|
@ -362,7 +345,7 @@ func (c *Client) initCallContext(ctx *contextCall) {
|
||||||
// before closing the connection.
|
// before closing the connection.
|
||||||
//
|
//
|
||||||
// See also Dial and Close.
|
// See also Dial and Close.
|
||||||
// See also git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client package docs.
|
// See also github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client package docs.
|
||||||
func (c *Client) ExecRaw(f func(client *client.Client) error) error {
|
func (c *Client) ExecRaw(f func(client *client.Client) error) error {
|
||||||
return f(&c.c)
|
return f(&c.c)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,803 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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
|
// SyncContainerWithNetwork requests network configuration using passed client
|
||||||
// and applies it to the container. Container MUST not be nil.
|
// and applies it to the container. Container MUST not be nil.
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,134 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,155 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
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);
|
rpc CustomRPC(CustomRPCRequest) returns (CustomRPCResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
import "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/common"
|
import "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/common"
|
||||||
|
|
||||||
req := new(CustomRPCRequest)
|
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
|
read-only. To be able to override client behavior (e.g. for tests), abstract it
|
||||||
with an interface:
|
with an interface:
|
||||||
|
|
||||||
import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
import "github.com/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
|
|
||||||
type FrostFSClient interface {
|
type FrostFSClient interface {
|
||||||
// Operations according to the application needs
|
// Operations according to the application needs
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// unwraps err using errors.Unwrap and returns the result.
|
// unwraps err using errors.Unwrap and returns the result.
|
||||||
|
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
"github.com/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
198
client/netmap.go
198
client/netmap.go
|
@ -4,14 +4,14 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
v2netmap "github.com/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
"github.com/TrueCloudLab/frostfs-sdk-go/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrmEndpointInfo groups parameters of EndpointInfo operation.
|
// PrmEndpointInfo groups parameters of EndpointInfo operation.
|
||||||
|
@ -19,16 +19,6 @@ type PrmEndpointInfo struct {
|
||||||
prmCommonMeta
|
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.
|
// ResEndpointInfo group resulting values of EndpointInfo operation.
|
||||||
type ResEndpointInfo struct {
|
type ResEndpointInfo struct {
|
||||||
statusRes
|
statusRes
|
||||||
|
@ -57,7 +47,7 @@ func (x ResEndpointInfo) NodeInfo() netmap.NodeInfo {
|
||||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||||
// in the returned result structure.
|
// in the returned result structure.
|
||||||
//
|
//
|
||||||
// Returns an error if parameters are set incorrectly (see PrmEndpointInfo docs).
|
// Immediately panics if parameters are set incorrectly (see PrmEndpointInfo docs).
|
||||||
// Context is required and must not be nil. It is used for network communication.
|
// 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.
|
// Exactly one return value is non-nil. Server status return is returned in ResEndpointInfo.
|
||||||
|
@ -66,47 +56,67 @@ func (x ResEndpointInfo) NodeInfo() netmap.NodeInfo {
|
||||||
// Return statuses:
|
// Return statuses:
|
||||||
// - global (see Client docs).
|
// - global (see Client docs).
|
||||||
func (c *Client) EndpointInfo(ctx context.Context, prm PrmEndpointInfo) (*ResEndpointInfo, error) {
|
func (c *Client) EndpointInfo(ctx context.Context, prm PrmEndpointInfo) (*ResEndpointInfo, error) {
|
||||||
req, err := prm.buildRequest(c)
|
// check context
|
||||||
if err != nil {
|
if ctx == nil {
|
||||||
return nil, err
|
panic(panicMsgMissingContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
|
// form request
|
||||||
return nil, fmt.Errorf("sign request: %w", err)
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := rpcapi.LocalNodeInfo(&c.c, req, client.WithContext(ctx))
|
// process call
|
||||||
if err != nil {
|
if !cc.processCall() {
|
||||||
return nil, err
|
return nil, cc.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
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,16 +125,6 @@ type PrmNetworkInfo struct {
|
||||||
prmCommonMeta
|
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.
|
// ResNetworkInfo groups resulting values of NetworkInfo operation.
|
||||||
type ResNetworkInfo struct {
|
type ResNetworkInfo struct {
|
||||||
statusRes
|
statusRes
|
||||||
|
@ -144,7 +144,7 @@ func (x ResNetworkInfo) Info() netmap.NetworkInfo {
|
||||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||||
// in the returned result structure.
|
// in the returned result structure.
|
||||||
//
|
//
|
||||||
// Returns an error if parameters are set incorrectly (see PrmNetworkInfo docs).
|
// Immediately panics if parameters are set incorrectly (see PrmNetworkInfo docs).
|
||||||
// Context is required and must not be nil. It is used for network communication.
|
// 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.
|
// Exactly one return value is non-nil. Server status return is returned in ResNetworkInfo.
|
||||||
|
@ -153,35 +153,51 @@ func (x ResNetworkInfo) Info() netmap.NetworkInfo {
|
||||||
// Return statuses:
|
// Return statuses:
|
||||||
// - global (see Client docs).
|
// - global (see Client docs).
|
||||||
func (c *Client) NetworkInfo(ctx context.Context, prm PrmNetworkInfo) (*ResNetworkInfo, error) {
|
func (c *Client) NetworkInfo(ctx context.Context, prm PrmNetworkInfo) (*ResNetworkInfo, error) {
|
||||||
req, err := prm.buildRequest(c)
|
// check context
|
||||||
if err != nil {
|
if ctx == nil {
|
||||||
return nil, err
|
panic(panicMsgMissingContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
|
// form request
|
||||||
return nil, fmt.Errorf("sign request: %w", err)
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := rpcapi.NetworkInfo(&c.c, req, client.WithContext(ctx))
|
// process call
|
||||||
if err != nil {
|
if !cc.processCall() {
|
||||||
return nil, err
|
return nil, cc.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
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +224,6 @@ func (x ResNetMapSnapshot) NetMap() netmap.NetMap {
|
||||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||||
// in the returned result structure.
|
// 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.
|
// 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.
|
// Exactly one return value is non-nil. Server status return is returned in ResNetMapSnapshot.
|
||||||
|
@ -217,6 +232,11 @@ func (x ResNetMapSnapshot) NetMap() netmap.NetMap {
|
||||||
// Return statuses:
|
// Return statuses:
|
||||||
// - global (see Client docs).
|
// - global (see Client docs).
|
||||||
func (c *Client) NetMapSnapshot(ctx context.Context, _ PrmNetMapSnapshot) (*ResNetMapSnapshot, error) {
|
func (c *Client) NetMapSnapshot(ctx context.Context, _ PrmNetMapSnapshot) (*ResNetMapSnapshot, error) {
|
||||||
|
// check context
|
||||||
|
if ctx == nil {
|
||||||
|
panic(panicMsgMissingContext)
|
||||||
|
}
|
||||||
|
|
||||||
// form request body
|
// form request body
|
||||||
var body v2netmap.SnapshotRequestBody
|
var body v2netmap.SnapshotRequestBody
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
v2netmap "github.com/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -69,6 +69,12 @@ func TestClient_NetMapSnapshot(t *testing.T) {
|
||||||
c := newClient(&srv)
|
c := newClient(&srv)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// missing context
|
||||||
|
require.PanicsWithValue(t, panicMsgMissingContext, func() {
|
||||||
|
//nolint:staticcheck
|
||||||
|
_, _ = c.NetMapSnapshot(nil, prm)
|
||||||
|
})
|
||||||
|
|
||||||
// request signature
|
// request signature
|
||||||
srv.errTransport = errors.New("any error")
|
srv.errTransport = errors.New("any error")
|
||||||
|
|
||||||
|
|
|
@ -5,18 +5,18 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
v2refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
"github.com/TrueCloudLab/frostfs-sdk-go/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrmObjectDelete groups parameters of ObjectDelete operation.
|
// 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
|
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||||
// in the returned result structure.
|
// in the returned result structure.
|
||||||
//
|
//
|
||||||
// Returns an error if parameters are set incorrectly (see PrmObjectDelete docs).
|
// Immediately panics if parameters are set incorrectly (see PrmObjectDelete docs).
|
||||||
// Context is required and must not be nil. It is used for network communication.
|
// Context is required and must not be nil. It is used for network communication.
|
||||||
//
|
//
|
||||||
// Return statuses:
|
// Return statuses:
|
||||||
|
@ -125,10 +125,12 @@ func (x ResObjectDelete) Tombstone() oid.ID {
|
||||||
// - *apistatus.SessionTokenExpired.
|
// - *apistatus.SessionTokenExpired.
|
||||||
func (c *Client) ObjectDelete(ctx context.Context, prm PrmObjectDelete) (*ResObjectDelete, error) {
|
func (c *Client) ObjectDelete(ctx context.Context, prm PrmObjectDelete) (*ResObjectDelete, error) {
|
||||||
switch {
|
switch {
|
||||||
|
case ctx == nil:
|
||||||
|
panic(panicMsgMissingContext)
|
||||||
case prm.addr.GetContainerID() == nil:
|
case prm.addr.GetContainerID() == nil:
|
||||||
return nil, errorMissingContainer
|
panic(panicMsgMissingContainer)
|
||||||
case prm.addr.GetObjectID() == nil:
|
case prm.addr.GetObjectID() == nil:
|
||||||
return nil, errorMissingObject
|
panic(panicMsgMissingObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
// form request body
|
// form request body
|
||||||
|
|
|
@ -7,19 +7,19 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
v2refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
"github.com/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
"github.com/TrueCloudLab/frostfs-sdk-go/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
// shared parameters of GET/HEAD/RANGE.
|
// shared parameters of GET/HEAD/RANGE.
|
||||||
|
@ -296,15 +296,17 @@ func (x *ObjectReader) Read(p []byte) (int, error) {
|
||||||
// The call only opens the transmission channel, explicit fetching is done using the ObjectReader.
|
// 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.
|
// Exactly one return value is non-nil. Resulting reader must be finally closed.
|
||||||
//
|
//
|
||||||
// Returns an error if parameters are set incorrectly (see PrmObjectGet docs).
|
// Immediately panics if parameters are set incorrectly (see PrmObjectGet docs).
|
||||||
// Context is required and must not be nil. It is used for network communication.
|
// 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) {
|
func (c *Client) ObjectGetInit(ctx context.Context, prm PrmObjectGet) (*ObjectReader, error) {
|
||||||
// check parameters
|
// check parameters
|
||||||
switch {
|
switch {
|
||||||
|
case ctx == nil:
|
||||||
|
panic(panicMsgMissingContext)
|
||||||
case prm.addr.GetContainerID() == nil:
|
case prm.addr.GetContainerID() == nil:
|
||||||
return nil, errorMissingContainer
|
panic(panicMsgMissingContainer)
|
||||||
case prm.addr.GetObjectID() == nil:
|
case prm.addr.GetObjectID() == nil:
|
||||||
return nil, errorMissingObject
|
panic(panicMsgMissingObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
// form request body
|
// form request body
|
||||||
|
@ -398,7 +400,7 @@ func (x *ResObjectHead) ReadHeader(dst *object.Object) bool {
|
||||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||||
// in the returned result structure.
|
// in the returned result structure.
|
||||||
//
|
//
|
||||||
// Returns an error if parameters are set incorrectly (see PrmObjectHead docs).
|
// Immediately panics if parameters are set incorrectly (see PrmObjectHead docs).
|
||||||
// Context is required and must not be nil. It is used for network communication.
|
// Context is required and must not be nil. It is used for network communication.
|
||||||
//
|
//
|
||||||
// Return errors:
|
// Return errors:
|
||||||
|
@ -414,10 +416,12 @@ func (x *ResObjectHead) ReadHeader(dst *object.Object) bool {
|
||||||
// - *apistatus.SessionTokenExpired.
|
// - *apistatus.SessionTokenExpired.
|
||||||
func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectHead, error) {
|
func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectHead, error) {
|
||||||
switch {
|
switch {
|
||||||
|
case ctx == nil:
|
||||||
|
panic(panicMsgMissingContext)
|
||||||
case prm.addr.GetContainerID() == nil:
|
case prm.addr.GetContainerID() == nil:
|
||||||
return nil, errorMissingContainer
|
panic(panicMsgMissingContainer)
|
||||||
case prm.addr.GetObjectID() == nil:
|
case prm.addr.GetObjectID() == nil:
|
||||||
return nil, errorMissingObject
|
panic(panicMsgMissingObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
var body v2object.HeadRequestBody
|
var body v2object.HeadRequestBody
|
||||||
|
@ -659,17 +663,19 @@ func (x *ObjectRangeReader) Read(p []byte) (int, error) {
|
||||||
// The call only opens the transmission channel, explicit fetching is done using the ObjectRangeReader.
|
// 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.
|
// Exactly one return value is non-nil. Resulting reader must be finally closed.
|
||||||
//
|
//
|
||||||
// Returns an error if parameters are set incorrectly (see PrmObjectRange docs).
|
// Immediately panics if parameters are set incorrectly (see PrmObjectRange docs).
|
||||||
// Context is required and must not be nil. It is used for network communication.
|
// 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) {
|
func (c *Client) ObjectRangeInit(ctx context.Context, prm PrmObjectRange) (*ObjectRangeReader, error) {
|
||||||
// check parameters
|
// check parameters
|
||||||
switch {
|
switch {
|
||||||
|
case ctx == nil:
|
||||||
|
panic(panicMsgMissingContext)
|
||||||
case prm.addr.GetContainerID() == nil:
|
case prm.addr.GetContainerID() == nil:
|
||||||
return nil, errorMissingContainer
|
panic(panicMsgMissingContainer)
|
||||||
case prm.addr.GetObjectID() == nil:
|
case prm.addr.GetObjectID() == nil:
|
||||||
return nil, errorMissingObject
|
panic(panicMsgMissingObject)
|
||||||
case prm.rng.GetLength() == 0:
|
case prm.rng.GetLength() == 0:
|
||||||
return nil, errorZeroRangeLength
|
panic("zero range length")
|
||||||
}
|
}
|
||||||
|
|
||||||
// form request body
|
// form request body
|
||||||
|
|
|
@ -5,18 +5,18 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
v2refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
"github.com/TrueCloudLab/frostfs-sdk-go/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrmObjectHash groups parameters of ObjectHash operation.
|
// 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
|
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||||
// in the returned result structure.
|
// in the returned result structure.
|
||||||
//
|
//
|
||||||
// Returns an error if parameters are set incorrectly (see PrmObjectHash docs).
|
// Immediately panics if parameters are set incorrectly (see PrmObjectHash docs).
|
||||||
// Context is required and must not be nil. It is used for network communication.
|
// Context is required and must not be nil. It is used for network communication.
|
||||||
//
|
//
|
||||||
// Return statuses:
|
// Return statuses:
|
||||||
|
@ -166,12 +166,14 @@ func (x ResObjectHash) Checksums() [][]byte {
|
||||||
// - *apistatus.SessionTokenExpired.
|
// - *apistatus.SessionTokenExpired.
|
||||||
func (c *Client) ObjectHash(ctx context.Context, prm PrmObjectHash) (*ResObjectHash, error) {
|
func (c *Client) ObjectHash(ctx context.Context, prm PrmObjectHash) (*ResObjectHash, error) {
|
||||||
switch {
|
switch {
|
||||||
|
case ctx == nil:
|
||||||
|
panic(panicMsgMissingContext)
|
||||||
case prm.addr.GetContainerID() == nil:
|
case prm.addr.GetContainerID() == nil:
|
||||||
return nil, errorMissingContainer
|
panic(panicMsgMissingContainer)
|
||||||
case prm.addr.GetObjectID() == nil:
|
case prm.addr.GetObjectID() == nil:
|
||||||
return nil, errorMissingObject
|
panic(panicMsgMissingObject)
|
||||||
case len(prm.body.GetRanges()) == 0:
|
case len(prm.body.GetRanges()) == 0:
|
||||||
return nil, errorMissingRanges
|
panic("missing ranges")
|
||||||
}
|
}
|
||||||
|
|
||||||
prm.body.SetAddress(&prm.addr)
|
prm.body.SetAddress(&prm.addr)
|
||||||
|
|
|
@ -3,50 +3,33 @@ package client
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/transformer"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// defaultGRPCPayloadChunkLen default value for maxChunkLen.
|
|
||||||
// See PrmObjectPutInit.SetGRPCPayloadChunkLen for details.
|
|
||||||
const defaultGRPCPayloadChunkLen = 3 << 20
|
|
||||||
|
|
||||||
// PrmObjectPutInit groups parameters of ObjectPutInit operation.
|
// PrmObjectPutInit groups parameters of ObjectPutInit operation.
|
||||||
type PrmObjectPutInit struct {
|
type PrmObjectPutInit struct {
|
||||||
copyNum []uint32
|
copyNum uint32
|
||||||
key *ecdsa.PrivateKey
|
key *ecdsa.PrivateKey
|
||||||
meta v2session.RequestMetaHeader
|
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.
|
// SetCopiesNumber sets number of object copies that is enough to consider put successful.
|
||||||
func (x *PrmObjectPutInit) SetCopiesNumber(copiesNumber uint32) {
|
func (x *PrmObjectPutInit) SetCopiesNumber(copiesNumber uint32) {
|
||||||
x.copyNum = []uint32{copiesNumber}
|
x.copyNum = 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.
|
// ResObjectPut groups the final result values of ObjectPutInit operation.
|
||||||
|
@ -61,35 +44,29 @@ func (x ResObjectPut) StoredObjectID() oid.ID {
|
||||||
return x.obj
|
return x.obj
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObjectWriter is designed to write one object or
|
// ObjectWriter is designed to write one object to FrostFS system.
|
||||||
// multiple parts of one object to FrostFS system.
|
|
||||||
//
|
//
|
||||||
// Must be initialized using Client.ObjectPutInit, any other
|
// Must be initialized using Client.ObjectPutInit, any other
|
||||||
// usage is unsafe.
|
// usage is unsafe.
|
||||||
type ObjectWriter interface {
|
type ObjectWriter struct {
|
||||||
// WriteHeader writes header of the object. Result means success.
|
cancelCtxStream context.CancelFunc
|
||||||
// Failure reason can be received via Close.
|
|
||||||
WriteHeader(context.Context, object.Object) bool
|
client *Client
|
||||||
// WritePayloadChunk writes chunk of the object payload. Result means success.
|
stream interface {
|
||||||
// Failure reason can be received via Close.
|
Write(*v2object.PutRequest) error
|
||||||
WritePayloadChunk(context.Context, []byte) bool
|
Close() error
|
||||||
// Close ends writing the object and returns the result of the operation
|
}
|
||||||
// along with the final results. Must be called after using the ObjectWriter.
|
|
||||||
//
|
key *ecdsa.PrivateKey
|
||||||
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
res ResObjectPut
|
||||||
// Any client's internal or transport errors are returned as Go built-in error.
|
err error
|
||||||
// If Client is tuned to resolve FrostFS API statuses, then FrostFS failures
|
|
||||||
// codes are returned as error.
|
chunkCalled bool
|
||||||
//
|
|
||||||
// Return statuses:
|
respV2 v2object.PutResponse
|
||||||
// - global (see Client docs);
|
req v2object.PutRequest
|
||||||
// - *apistatus.ContainerNotFound;
|
partInit v2object.PutObjectPartInit
|
||||||
// - *apistatus.ObjectAccessDenied;
|
partChunk v2object.PutObjectPartChunk
|
||||||
// - *apistatus.ObjectLocked;
|
|
||||||
// - *apistatus.LockNonRegularObject;
|
|
||||||
// - *apistatus.SessionTokenNotFound;
|
|
||||||
// - *apistatus.SessionTokenExpired.
|
|
||||||
Close(context.Context) (*ResObjectPut, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UseKey specifies private key to sign the requests.
|
// UseKey specifies private key to sign the requests.
|
||||||
|
@ -128,21 +105,129 @@ func (x *PrmObjectPutInit) WithXHeaders(hs ...string) {
|
||||||
writeXHeadersToMeta(hs, &x.meta)
|
writeXHeadersToMeta(hs, &x.meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithObjectMaxSize specifies max object size value and use it during object splitting.
|
// WriteHeader writes header of the object. Result means success.
|
||||||
// When specified, start writing to the stream only after the object is formed.
|
// Failure reason can be received via Close.
|
||||||
// Continue processing the input only when the previous formed object has been successfully written.
|
func (x *ObjectWriter) WriteHeader(hdr object.Object) bool {
|
||||||
func (x *PrmObjectPutInit) WithObjectMaxSize(maxSize uint64) {
|
v2Hdr := hdr.ToV2()
|
||||||
x.maxSize = maxSize
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithoutHomomorphicHash if set to true do not use Tillich-Zémor hash for payload.
|
// WritePayloadChunk writes chunk of the object payload. Result means success.
|
||||||
func (x *PrmObjectPutInit) WithoutHomomorphicHash(v bool) {
|
// Failure reason can be received via Close.
|
||||||
x.withoutHomomorphicHash = v
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithEpochSource specifies epoch for object when split it on client side.
|
// Close ends writing the object and returns the result of the operation
|
||||||
func (x *PrmObjectPutInit) WithEpochSource(es transformer.EpochSource) {
|
// along with the final results. Must be called after using the ObjectWriter.
|
||||||
x.epochSource = es
|
//
|
||||||
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObjectPutInit initiates writing an object through a remote server using FrostFS API protocol.
|
// ObjectPutInit initiates writing an object through a remote server using FrostFS API protocol.
|
||||||
|
@ -150,11 +235,32 @@ func (x *PrmObjectPutInit) WithEpochSource(es transformer.EpochSource) {
|
||||||
// The call only opens the transmission channel, explicit recording is done using the ObjectWriter.
|
// 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.
|
// 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.
|
// 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) {
|
func (c *Client) ObjectPutInit(ctx context.Context, prm PrmObjectPutInit) (*ObjectWriter, error) {
|
||||||
if prm.maxSize > 0 {
|
// check parameters
|
||||||
return c.objectPutInitTransformer(prm)
|
if ctx == nil {
|
||||||
|
panic(panicMsgMissingContext)
|
||||||
}
|
}
|
||||||
return c.objectPutInitRaw(ctx, 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,154 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
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"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
v2refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
"github.com/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
"github.com/TrueCloudLab/frostfs-sdk-go/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrmObjectSearch groups parameters of ObjectSearch operation.
|
// PrmObjectSearch groups parameters of ObjectSearch operation.
|
||||||
|
@ -218,12 +218,15 @@ func (x *ObjectListReader) Close() (*ResObjectSearch, error) {
|
||||||
// is done using the ObjectListReader. Exactly one return value is non-nil.
|
// is done using the ObjectListReader. Exactly one return value is non-nil.
|
||||||
// Resulting reader must be finally closed.
|
// Resulting reader must be finally closed.
|
||||||
//
|
//
|
||||||
// Returns an error if parameters are set incorrectly (see PrmObjectSearch docs).
|
// Immediately panics if parameters are set incorrectly (see PrmObjectSearch docs).
|
||||||
// Context is required and must not be nil. It is used for network communication.
|
// 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) {
|
func (c *Client) ObjectSearchInit(ctx context.Context, prm PrmObjectSearch) (*ObjectListReader, error) {
|
||||||
// check parameters
|
// check parameters
|
||||||
if !prm.cnrSet {
|
switch {
|
||||||
return nil, errorMissingContainer
|
case ctx == nil:
|
||||||
|
panic(panicMsgMissingContext)
|
||||||
|
case !prm.cnrSet:
|
||||||
|
panic(panicMsgMissingContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
var cidV2 v2refs.ContainerID
|
var cidV2 v2refs.ContainerID
|
||||||
|
|
|
@ -7,11 +7,11 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
signatureV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
signatureV2 "github.com/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
oidtest "github.com/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
200
client/reputation.go
Normal file
200
client/reputation.go
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
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
|
package client
|
||||||
|
|
||||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
import "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
|
|
||||||
// ResponseMetaInfo groups meta information about any FrostFS API response.
|
// ResponseMetaInfo groups meta information about any FrostFS API response.
|
||||||
type ResponseMetaInfo struct {
|
type ResponseMetaInfo struct {
|
||||||
|
|
|
@ -3,15 +3,12 @@ package client
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
rpcapi "github.com/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
v2session "github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrmSessionCreate groups parameters of SessionCreate operation.
|
// PrmSessionCreate groups parameters of SessionCreate operation.
|
||||||
|
@ -36,30 +33,6 @@ func (x *PrmSessionCreate) UseKey(key ecdsa.PrivateKey) {
|
||||||
x.key = key
|
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.
|
// ResSessionCreate groups resulting values of SessionCreate operation.
|
||||||
type ResSessionCreate struct {
|
type ResSessionCreate struct {
|
||||||
statusRes
|
statusRes
|
||||||
|
@ -69,6 +42,10 @@ type ResSessionCreate struct {
|
||||||
sessionKey []byte
|
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.
|
// ID returns identifier of the opened session in a binary FrostFS API protocol format.
|
||||||
//
|
//
|
||||||
// Client doesn't retain value so modification is safe.
|
// Client doesn't retain value so modification is safe.
|
||||||
|
@ -76,6 +53,10 @@ func (x ResSessionCreate) ID() []byte {
|
||||||
return x.id
|
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.
|
// PublicKey returns public key of the opened session in a binary FrostFS API protocol format.
|
||||||
func (x ResSessionCreate) PublicKey() []byte {
|
func (x ResSessionCreate) PublicKey() []byte {
|
||||||
return x.sessionKey
|
return x.sessionKey
|
||||||
|
@ -91,34 +72,68 @@ func (x ResSessionCreate) PublicKey() []byte {
|
||||||
// FrostFS status codes are returned as `error`, otherwise, are included
|
// FrostFS status codes are returned as `error`, otherwise, are included
|
||||||
// in the returned result structure.
|
// in the returned result structure.
|
||||||
//
|
//
|
||||||
// Returns an error if parameters are set incorrectly (see PrmSessionCreate docs).
|
// Immediately panics if parameters are set incorrectly (see PrmSessionCreate docs).
|
||||||
// Context is required and must not be nil. It is used for network communication.
|
// Context is required and must not be nil. It is used for network communication.
|
||||||
//
|
//
|
||||||
// Return statuses:
|
// Return statuses:
|
||||||
// - global (see Client docs).
|
// - global (see Client docs).
|
||||||
func (c *Client) SessionCreate(ctx context.Context, prm PrmSessionCreate) (*ResSessionCreate, error) {
|
func (c *Client) SessionCreate(ctx context.Context, prm PrmSessionCreate) (*ResSessionCreate, error) {
|
||||||
req, err := prm.buildRequest(c)
|
// check context
|
||||||
if err != nil {
|
if ctx == nil {
|
||||||
return nil, err
|
panic(panicMsgMissingContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
|
ownerKey := c.prm.key.PublicKey
|
||||||
return nil, fmt.Errorf("sign request: %w", err)
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := rpcapi.CreateSession(&c.c, req, client.WithContext(ctx))
|
cc.meta = prm.prmCommonMeta
|
||||||
if err != nil {
|
cc.req = &req
|
||||||
return nil, err
|
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())
|
||||||
}
|
}
|
||||||
|
|
||||||
var res ResSessionCreate
|
// process call
|
||||||
res.st, err = c.processResponse(resp)
|
if !cc.processCall() {
|
||||||
if err != nil || !apistatus.IsSuccessful(res.st) {
|
return nil, cc.err
|
||||||
return &res, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body := resp.GetBody()
|
|
||||||
res.id = body.GetID()
|
|
||||||
res.sessionKey = body.GetSessionKey()
|
|
||||||
return &res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package apistatus
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServerInternal describes failure statuses related to internal server errors.
|
// ServerInternal describes failure statuses related to internal server errors.
|
||||||
|
|
|
@ -3,8 +3,8 @@ package apistatus_test
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package apistatus
|
package apistatus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContainerNotFound describes status of the failure because of the missing container.
|
// ContainerNotFound describes status of the failure because of the missing container.
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package apistatus
|
package apistatus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ObjectLocked describes status of the failure because of the locked object.
|
// ObjectLocked describes status of the failure because of the locked object.
|
||||||
|
|
|
@ -3,7 +3,7 @@ package apistatus_test
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package apistatus
|
package apistatus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SessionTokenNotFound describes status of the failure because of the missing session token.
|
// 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.
|
// 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.
|
// To transport statuses using the FrostFS API V2 protocol, see StatusV2 interface and FromStatusV2 and ToStatusV2 functions.
|
||||||
type Status any
|
type Status interface{}
|
||||||
|
|
||||||
// ErrFromStatus converts Status instance to error if it is failed. Returns nil on successful Status.
|
// ErrFromStatus converts Status instance to error if it is failed. Returns nil on successful Status.
|
||||||
//
|
//
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package apistatus
|
package apistatus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SuccessDefaultV2 represents Status instance of default success. Implements StatusV2.
|
// SuccessDefaultV2 represents Status instance of default success. Implements StatusV2.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package apistatus
|
package apistatus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
type unrecognizedStatusV2 struct {
|
type unrecognizedStatusV2 struct {
|
||||||
|
|
|
@ -3,10 +3,10 @@ package apistatus
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StatusV2 defines a variety of Status instances compatible with FrostFS API V2 protocol.
|
// StatusV2 defines a variety of Status instances compatible with FrostFS API V2 protocol.
|
||||||
|
@ -15,7 +15,7 @@ import (
|
||||||
type StatusV2 interface {
|
type StatusV2 interface {
|
||||||
Status
|
Status
|
||||||
|
|
||||||
// ToStatusV2 returns the status as git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status.Status message structure.
|
// ToStatusV2 returns the status as github.com/TrueCloudLab/frostfs-api-go/v2/status.Status message structure.
|
||||||
ToStatusV2() *status.Status
|
ToStatusV2() *status.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ func ToStatusV2(st Status) *status.Status {
|
||||||
return internalErrorStatus
|
return internalErrorStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func errMessageStatusV2(code any, msg string) string {
|
func errMessageStatusV2(code interface{}, msg string) string {
|
||||||
const (
|
const (
|
||||||
noMsgFmt = "status: code = %v"
|
noMsgFmt = "status: code = %v"
|
||||||
msgFmt = noMsgFmt + " message = %s"
|
msgFmt = noMsgFmt + " message = %s"
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "github.com/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ func TestToStatusV2(t *testing.T) {
|
||||||
type statusConstructor func() apistatus.Status
|
type statusConstructor func() apistatus.Status
|
||||||
|
|
||||||
for _, testItem := range [...]struct {
|
for _, testItem := range [...]struct {
|
||||||
status any // Status or statusConstructor
|
status interface{} // Status or statusConstructor
|
||||||
codeV2 uint64
|
codeV2 uint64
|
||||||
messageV2 string
|
messageV2 string
|
||||||
}{
|
}{
|
||||||
|
@ -165,7 +165,7 @@ func TestFromStatusV2(t *testing.T) {
|
||||||
type statusConstructor func() apistatus.Status
|
type statusConstructor func() apistatus.Status
|
||||||
|
|
||||||
for _, testItem := range [...]struct {
|
for _, testItem := range [...]struct {
|
||||||
status any // Status or statusConstructor
|
status interface{} // Status or statusConstructor
|
||||||
codeV2 uint64
|
codeV2 uint64
|
||||||
messageV2 string
|
messageV2 string
|
||||||
}{
|
}{
|
||||||
|
|
|
@ -9,7 +9,6 @@ import "strconv"
|
||||||
// use corresponding constants and/or methods instead.
|
// use corresponding constants and/or methods instead.
|
||||||
type Op uint32
|
type Op uint32
|
||||||
|
|
||||||
// nolint: unused
|
|
||||||
const (
|
const (
|
||||||
opZero Op = iota // extreme value for testing
|
opZero Op = iota // extreme value for testing
|
||||||
|
|
||||||
|
@ -54,7 +53,6 @@ func (x Op) String() string {
|
||||||
// use corresponding constants and/or methods instead.
|
// use corresponding constants and/or methods instead.
|
||||||
type Role uint32
|
type Role uint32
|
||||||
|
|
||||||
// nolint: unused
|
|
||||||
const (
|
const (
|
||||||
roleZero Role = iota // extreme value for testing
|
roleZero Role = iota // extreme value for testing
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,14 @@ func setBit(num *uint32, n uint8) {
|
||||||
*num |= 1 << n
|
*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).
|
// checks if n-th bit in num is set (starting at 0).
|
||||||
func isBitSet(num uint32, n uint8) bool {
|
func isBitSet(num uint32, n uint8) bool {
|
||||||
mask := uint32(1 << n)
|
mask := uint32(1 << n)
|
||||||
|
|
|
@ -22,6 +22,9 @@ func TestBits(t *testing.T) {
|
||||||
|
|
||||||
setBit(&num, 6)
|
setBit(&num, 6)
|
||||||
require.EqualValues(t, 0b1011110, num)
|
require.EqualValues(t, 0b1011110, num)
|
||||||
|
|
||||||
|
resetBit(&num, 1)
|
||||||
|
require.EqualValues(t, 0b1011100, num)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOpBits(t *testing.T) {
|
func TestOpBits(t *testing.T) {
|
||||||
|
|
|
@ -8,16 +8,17 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
v2netmap "github.com/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
"github.com/TrueCloudLab/frostfs-sdk-go/container/acl"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||||
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
frostfsecdsa "github.com/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
subnetid "github.com/TrueCloudLab/frostfs-sdk-go/subnet/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
|
"github.com/TrueCloudLab/frostfs-sdk-go/version"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ import (
|
||||||
// Instances for existing containers can be initialized using decoding methods
|
// Instances for existing containers can be initialized using decoding methods
|
||||||
// (e.g Unmarshal).
|
// (e.g Unmarshal).
|
||||||
//
|
//
|
||||||
// Container is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container.Container
|
// Container is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/container.Container
|
||||||
// message. See ReadFromV2 / WriteToV2 methods.
|
// message. See ReadFromV2 / WriteToV2 methods.
|
||||||
type Container struct {
|
type Container struct {
|
||||||
v2 container.Container
|
v2 container.Container
|
||||||
|
@ -95,16 +96,6 @@ func (x *Container) readFromV2(m container.Container, checkFieldPresence bool) e
|
||||||
return errors.New("missing placement policy")
|
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()
|
attrs := m.GetAttributes()
|
||||||
mAttr := make(map[string]struct{}, len(attrs))
|
mAttr := make(map[string]struct{}, len(attrs))
|
||||||
var key, val string
|
var key, val string
|
||||||
|
@ -126,8 +117,10 @@ func checkAttributes(m container.Container) error {
|
||||||
return fmt.Errorf("empty attribute value %s", key)
|
return fmt.Errorf("empty attribute value %s", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
switch key {
|
||||||
if key == attributeTimestamp {
|
case container.SysAttributeSubnet:
|
||||||
|
err = new(subnetid.ID).DecodeString(val)
|
||||||
|
case attributeTimestamp:
|
||||||
_, err = strconv.ParseInt(val, 10, 64)
|
_, err = strconv.ParseInt(val, 10, 64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +130,9 @@ func checkAttributes(m container.Container) error {
|
||||||
|
|
||||||
mAttr[key] = struct{}{}
|
mAttr[key] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x.v2 = m
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,6 +383,28 @@ func CreatedAt(cnr Container) time.Time {
|
||||||
return time.Unix(sec, 0)
|
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"
|
const attributeHomoHashEnabled = "true"
|
||||||
|
|
||||||
// DisableHomomorphicHashing sets flag to disable homomorphic hashing of the
|
// DisableHomomorphicHashing sets flag to disable homomorphic hashing of the
|
||||||
|
@ -401,8 +419,7 @@ func DisableHomomorphicHashing(cnr *Container) {
|
||||||
//
|
//
|
||||||
// Zero Container has enabled hashing.
|
// Zero Container has enabled hashing.
|
||||||
func IsHomomorphicHashingDisabled(cnr Container) bool {
|
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
|
// Domain represents information about container domain registered in the NNS
|
||||||
|
@ -448,12 +465,10 @@ func WriteDomain(cnr *Container, domain Domain) {
|
||||||
// ReadDomain reads Domain from the Container. Returns value with empty name
|
// ReadDomain reads Domain from the Container. Returns value with empty name
|
||||||
// if domain is not specified.
|
// if domain is not specified.
|
||||||
func ReadDomain(cnr Container) (res Domain) {
|
func ReadDomain(cnr Container) (res Domain) {
|
||||||
if name := cnr.Attribute(container.SysAttributeName); name != "" {
|
name := cnr.Attribute(container.SysAttributeName)
|
||||||
|
if name != "" {
|
||||||
res.SetName(name)
|
res.SetName(name)
|
||||||
res.SetZone(cnr.Attribute(container.SysAttributeZone))
|
res.SetZone(cnr.Attribute(container.SysAttributeZone))
|
||||||
} else if name = cnr.Attribute(container.SysAttributeNameNeoFS); name != "" {
|
|
||||||
res.SetName(name)
|
|
||||||
res.SetZone(cnr.Attribute(container.SysAttributeZoneNeoFS))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -6,17 +6,19 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
v2container "github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
v2netmap "github.com/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
"github.com/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||||
containertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/test"
|
containertest "github.com/TrueCloudLab/frostfs-sdk-go/container/test"
|
||||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||||
netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test"
|
netmaptest "github.com/TrueCloudLab/frostfs-sdk-go/netmap/test"
|
||||||
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
|
subnetid "github.com/TrueCloudLab/frostfs-sdk-go/subnet/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
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"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -231,6 +233,28 @@ func TestSetCreationTime(t *testing.T) {
|
||||||
require.Equal(t, creat.Unix(), container.CreatedAt(val2).Unix())
|
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) {
|
func TestDisableHomomorphicHashing(t *testing.T) {
|
||||||
var val container.Container
|
var val container.Container
|
||||||
|
|
||||||
|
|
|
@ -23,11 +23,11 @@ it using the instance of Container types
|
||||||
// process the container data
|
// process the container data
|
||||||
|
|
||||||
Instances can be also used to process FrostFS API V2 protocol messages
|
Instances can be also used to process FrostFS API V2 protocol messages
|
||||||
(see neo.fs.v2.container package in https://git.frostfs.info/TrueCloudLab/frostfs-api).
|
(see neo.fs.v2.container package in https://github.com/TrueCloudLab/frostfs-api).
|
||||||
|
|
||||||
On client side:
|
On client side:
|
||||||
|
|
||||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
import "github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
|
|
||||||
var msg container.Container
|
var msg container.Container
|
||||||
cnr.WriteToV2(&msg)
|
cnr.WriteToV2(&msg)
|
||||||
|
|
|
@ -4,13 +4,13 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
"github.com/mr-tron/base58"
|
"github.com/mr-tron/base58"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ID represents FrostFS container identifier.
|
// ID represents FrostFS container identifier.
|
||||||
//
|
//
|
||||||
// ID is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.ContainerID
|
// ID is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/refs.ContainerID
|
||||||
// message. See ReadFromV2 / WriteToV2 methods.
|
// message. See ReadFromV2 / WriteToV2 methods.
|
||||||
//
|
//
|
||||||
// Instances can be created using built-in var declaration.
|
// Instances can be created using built-in var declaration.
|
||||||
|
|
|
@ -5,9 +5,9 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||||
"github.com/mr-tron/base58"
|
"github.com/mr-tron/base58"
|
||||||
"github.com/stretchr/testify/require"
|
"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.:
|
Random instance generation functions can be useful when testing expects any value, e.g.:
|
||||||
|
|
||||||
import cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
import cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||||
|
|
||||||
cid := cidtest.ID()
|
cid := cidtest.ID()
|
||||||
// test the value
|
// test the value
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ID returns random cid.ID.
|
// ID returns random cid.ID.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"github.com/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ApplyNetworkConfig applies network configuration to the
|
// ApplyNetworkConfig applies network configuration to the
|
||||||
|
|
|
@ -3,9 +3,9 @@ package container_test
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
"github.com/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
containertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/test"
|
containertest "github.com/TrueCloudLab/frostfs-sdk-go/container/test"
|
||||||
netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test"
|
netmaptest "github.com/TrueCloudLab/frostfs-sdk-go/netmap/test"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -4,15 +4,15 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SizeEstimation groups information about estimation of the size of the data
|
// SizeEstimation groups information about estimation of the size of the data
|
||||||
// stored in the FrostFS container.
|
// stored in the FrostFS container.
|
||||||
//
|
//
|
||||||
// SizeEstimation is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container.UsedSpaceAnnouncement
|
// SizeEstimation is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/container.UsedSpaceAnnouncement
|
||||||
// message. See ReadFromV2 / WriteToV2 methods.
|
// message. See ReadFromV2 / WriteToV2 methods.
|
||||||
type SizeEstimation struct {
|
type SizeEstimation struct {
|
||||||
m container.UsedSpaceAnnouncement
|
m container.UsedSpaceAnnouncement
|
||||||
|
|
|
@ -4,11 +4,11 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
v2container "github.com/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
"github.com/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,11 @@ package containertest
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
"github.com/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
"github.com/TrueCloudLab/frostfs-sdk-go/container/acl"
|
||||||
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
cidtest "github.com/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||||
netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test"
|
netmaptest "github.com/TrueCloudLab/frostfs-sdk-go/netmap/test"
|
||||||
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
|
usertest "github.com/TrueCloudLab/frostfs-sdk-go/user/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Container returns random container.Container.
|
// Container returns random container.Container.
|
||||||
|
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||||
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
frostfsecdsa "github.com/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/stretchr/testify/require"
|
"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
|
Signature can be also used to process FrostFS API V2 protocol messages
|
||||||
(see neo.fs.v2.refs package in https://git.frostfs.info/TrueCloudLab/frostfs-api).
|
(see neo.fs.v2.refs package in https://github.com/TrueCloudLab/frostfs-api).
|
||||||
|
|
||||||
On client side:
|
On client side:
|
||||||
|
|
||||||
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
import "github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
|
|
||||||
var msg refs.Signature
|
var msg refs.Signature
|
||||||
sig.WriteToV2(&msg)
|
sig.WriteToV2(&msg)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package frostfsecdsa
|
package frostfsecdsa
|
||||||
|
|
||||||
import frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
import frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
frostfscrypto.RegisterScheme(frostfscrypto.ECDSA_SHA512, func() frostfscrypto.PublicKey {
|
frostfscrypto.RegisterScheme(frostfscrypto.ECDSA_SHA512, func() frostfscrypto.PublicKey {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
|
|
||||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/signature/walletconnect"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/util/signature/walletconnect"
|
||||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,13 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Signature represents a confirmation of data integrity received by the
|
// Signature represents a confirmation of data integrity received by the
|
||||||
// digital signature mechanism.
|
// digital signature mechanism.
|
||||||
//
|
//
|
||||||
// Signature is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.Signature
|
// Signature is mutually compatible with github.com/TrueCloudLab/frostfs-api-go/v2/refs.Signature
|
||||||
// message. See ReadFromV2 / WriteToV2 methods.
|
// message. See ReadFromV2 / WriteToV2 methods.
|
||||||
//
|
//
|
||||||
// Note that direct typecast is not safe and may result in loss of compatibility:
|
// Note that direct typecast is not safe and may result in loss of compatibility:
|
||||||
|
|
|
@ -3,7 +3,7 @@ package frostfscrypto
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
"github.com/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Scheme represents digital signature algorithm with fixed cryptographic hash function.
|
// Scheme represents digital signature algorithm with fixed cryptographic hash function.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package eacl
|
package eacl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
v2acl "github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Action taken if ContainerEACL record matched request.
|
// Action taken if ContainerEACL record matched request.
|
||||||
|
|
|
@ -3,8 +3,8 @@ package eacl_test
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
v2acl "github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
"github.com/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ package eacl
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
v2acl "github.com/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Filter defines check conditions if request header is matched or not. Matched
|
// 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