Compare commits

..

47 commits

Author SHA1 Message Date
552219b8e1 [#16] pool: Fix counting context canceled error
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-03-29 15:58:04 +03:00
cfb8a7b914 [#41] .gitlint: Synchronize settings across FrostFS repos
This change allows to use `[#xx]` placeholders for issue number.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-24 22:07:13 +03:00
4438f115fb [#39] Add Issue Template
Add bug report and feature request templates

Signed-off-by: Liza <e.chichindaeva@yadro.com>
2023-03-23 12:25:57 +03:00
bec77f280a [#37] container: Support legacy sys attributes
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-03-16 11:14:42 +03:00
df2090c2be [#37] netmap: Update tests for new sys attributes
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-03-16 11:14:36 +03:00
7e6592b28e [#37] go.mod: Update api-go for new sys attributes
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-03-16 11:14:27 +03:00
d589d51509 [#19] transformer: Fix dependencies
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-03-16 09:51:03 +03:00
25588ee3be [#19] transformer: Do not allocate intermeate slice for hashers
```
name                 old time/op    new time/op    delta
Transformer/small-8    73.7µs ±15%    72.4µs ±16%    ~     (p=0.604 n=10+9)
Transformer/big-8       1.36s ± 4%     1.36s ± 8%    ~     (p=0.579 n=10+10)

name                 old alloc/op   new alloc/op   delta
Transformer/small-8    7.67kB ± 0%    7.57kB ± 0%  -1.36%  (p=0.000 n=10+10)
Transformer/big-8      49.0kB ± 0%    48.3kB ± 0%  -1.48%  (p=0.000 n=10+10)

name                 old allocs/op  new allocs/op  delta
Transformer/small-8       101 ± 0%        98 ± 0%  -2.97%  (p=0.000 n=10+10)
Transformer/big-8         609 ± 0%       591 ± 1%  -3.00%  (p=0.000 n=10+9)
```

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-15 05:39:09 +00:00
9407f30248 [#19] transformer: Optimize payload hashers a bit
```
name                 old time/op    new time/op    delta
Transformer/small-8    74.8µs ±11%    73.7µs ±15%    ~     (p=0.529 n=10+10)
Transformer/big-8       1.38s ±11%     1.36s ± 4%    ~     (p=0.796 n=10+10)

name                 old alloc/op   new alloc/op   delta
Transformer/small-8    7.69kB ± 0%    7.67kB ± 0%  -0.21%  (p=0.000 n=10+10)
Transformer/big-8      49.2kB ± 0%    49.0kB ± 0%  -0.48%  (p=0.004 n=10+10)

name                 old allocs/op  new allocs/op  delta
Transformer/small-8       102 ± 0%       101 ± 0%  -0.98%  (p=0.000 n=9+10)
Transformer/big-8         620 ± 1%       609 ± 0%  -1.66%  (p=0.000 n=10+10)
```

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-15 05:39:09 +00:00
94c0a607b5 [#19] transformer: Add a target which sends parts to a channel
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-15 05:39:09 +00:00
e45647de3c [#19] transformer: Do not reuse memory of sent objects
Slower, but more correct.
```
name                 old time/op    new time/op    delta
Transformer/small-8    72.4µs ± 8%    74.8µs ±11%     ~     (p=0.278 n=9+10)
Transformer/big-8       1.31s ± 8%     1.38s ±11%   +5.50%  (p=0.035 n=10+10)

name                 old alloc/op   new alloc/op   delta
Transformer/small-8    7.39kB ± 0%    7.69kB ± 0%   +4.04%  (p=0.000 n=10+10)
Transformer/big-8      46.9kB ± 0%    49.2kB ± 0%   +4.87%  (p=0.000 n=10+10)

name                 old allocs/op  new allocs/op  delta
Transformer/small-8      94.6 ± 1%     102.0 ± 0%   +7.82%  (p=0.000 n=10+9)
Transformer/big-8         560 ± 0%       620 ± 1%  +10.66%  (p=0.000 n=10+10)
```

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-15 05:39:09 +00:00
611e20587b [#19] transformer/test: Check owner ID and payload hash for parts
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-15 05:39:09 +00:00
eba6831125 [#19] transformer/test: Add helper functions
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-15 05:39:09 +00:00
7e3810d654 [#19] transformer: Move EpochSource to other types
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-15 05:39:09 +00:00
cc0fef2c55 [#19] transformer: Merge formatter and payload splitter
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-15 05:39:09 +00:00
b696d3c70e [#19] transformer: Make writeChunk non-recursive
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-15 05:39:09 +00:00
1c94309d7a [#19] transformer: Simplify AccessIdentifiers
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-15 05:39:09 +00:00
f43f18ecda [#19] transformer: Cover with unit-tests
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-15 05:39:09 +00:00
ac8442bf99 [#19] object: Move transformer implementation from node
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-15 05:39:09 +00:00
0ad877288e [TrueCloudLab#16] pool: Don't count grpc canceled error
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-03-10 06:58:34 +00:00
0e1999c965 [#23] pre-commit: Add gitlint hook
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-09 22:53:16 +03:00
b461aa64b8 [#23] pre-commit: Add golangci-lint hook
Also, fix minor issues. Skip deprecated warning for now.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-09 22:53:14 +03:00
b761fd8070 [#23] pre-commit: Add initial configuration
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-09 22:52:51 +03:00
94476f9055 Rename package name
Due to source code relocation from GitHub.

Signed-off-by: Alex Vanin <a.vanin@yadro.com>
2023-03-07 15:47:21 +03:00
5e759bf089 [#2] Remove panic from RPCs
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2023-03-01 10:29:23 +03:00
d4f5bba459 [#2] Update lint config, fix lint errors
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2023-03-01 10:29:23 +03:00
e9c1a2ab2b [TrueCloudLab/hrw#2] sdk-go: Optimize node hash
Compute node hash by node initialization

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-02-28 18:07:14 +03:00
2cbc585edd [TrueCloudLab/hrw#2] sdk-go: Use typed HRW methods
Update HRW pkg and use typed HRW methods to sort nodes

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-02-28 18:07:14 +03:00
e355e5eeba [TrueCloudLab#22] .github: Fix CODEOWNERS
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-02-28 16:35:38 +03:00
f08069ceeb [#20] .github: Update CODEOWNERS
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-02-27 17:47:21 +03:00
dad99bad48 [#20] .github: Drop go1.17, add go1.20
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-02-27 17:47:21 +03:00
0d3a238d9c [#5] pool: Update hashicorp/lru to v2
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-02-24 17:32:40 +03:00
cf64ddfb14 [TrueCloudLab#13] pool: Renew token before it expired
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-01-30 15:06:02 +03:00
cf9a54dcda [TrueCloudLab#11] pool: Fix handling SplitInfoError
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-01-23 11:07:29 +03:00
Denis Kirillov
b2a37543d3 [#362] pool: Don't use default session token for read
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
2023-01-13 13:21:29 +03:00
f0ac49b8f0 [TrueCloudLab#6] pool: Decrease rebalanceInterval
Set default rebalance interval to 15s

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-01-13 13:16:22 +03:00
54696acf48 [TrueCloudLab#6] pool: Log node healthy changing
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-01-13 13:16:22 +03:00
dd88a5c5e0 [#4] go.mod: Bump supported go version to 1.18
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2022-12-30 13:40:50 +03:00
4ff9c00de3 [#4] Rename NeoFS mentions in comments and method names
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2022-12-30 13:40:50 +03:00
b204a62da1 [#4] go.mod: Update ANTLR
Current `go:generate` command was, probably, executed only on my laptop.
Replace it with explicit version, because package in the generated code
depends on in.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2022-12-30 13:40:50 +03:00
377a5a0517 [#4] go.mod: Update dependencies
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2022-12-30 13:40:50 +03:00
43c046f343 [#4] go.mod: Update neo-go to v0.100.1
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2022-12-30 13:40:50 +03:00
Denis Kirillov
8c0c7789ca [#365] pool: Add request callback
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2022-12-29 16:34:13 +03:00
Evgenii Stratonikov
339e2702f8 [#367] client: Allow to override key in Object.Hash RPC
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2022-12-29 16:34:13 +03:00
Pavel Karpy
a1748ae0e7 [#370] bearer, session: Clarify expiration epoch
The expiration epoch is the _last_ valid epoch for an entity. Also, clarify
the expiration epoch meaning for tombstones and regular objects.

Signed-off-by: Pavel Karpy <p.karpy@yadro.com>
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2022-12-29 16:34:13 +03:00
Pavel Karpy
4c779423f5 Move to frostfs-sdk-go
Signed-off-by: Pavel Karpy <p.karpy@yadro.com>
2022-12-14 09:59:29 +03:00
Pavel Karpy
45a6e7a7c2 [#369] status: Make errors return default messages
Use default messages in `Error` methods like in `ToStatusV2`.

Signed-off-by: Pavel Karpy <p.karpy@yadro.com>
2022-12-14 09:59:29 +03:00
243 changed files with 5402 additions and 5901 deletions

2
.github/CODEOWNERS vendored
View file

@ -1 +1 @@
* @roman-khimov @cthulhu-rider @smallhive @notimetoname
* @TrueCloudLab/storage-core @TrueCloudLab/storage-services

45
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,45 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: community, triage, bug
assignees: ''
---
<!--- Provide a general summary of the issue in the Title above -->
## Expected Behavior
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->
## Current Behavior
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
## Possible Solution
<!--- Not obligatory -->
<!--- If no reason/fix/additions for the bug can be suggested, -->
<!--- uncomment the following phrase: -->
<!--- No fix can be suggested by a QA engineer. Further solutions shall be up to developers. -->
## Steps to Reproduce (for bugs)
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. -->
1.
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
## Regression
<!-- Is this issue a regression? (Yes / No) -->
<!-- If Yes, optionally please include version or commit id or PR# that caused this regression, if you have these details. -->
## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->
* Version used:
* Server setup and configuration:
* Operating System and version (`uname -a`):

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1 @@
blank_issues_enabled: false

View file

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: community, triage
assignees: ''
---
## Is your feature request related to a problem? Please describe.
<!--- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
## Describe the solution you'd like
<!--- A clear and concise description of what you want to happen. -->
## Describe alternatives you've considered
<!--- A clear and concise description of any alternative solutions or features you've considered. -->
## Additional context
<!--- Add any other context or screenshots about the feature request here. -->

1
.gitignore vendored
View file

@ -17,7 +17,6 @@ vendor/
# IDE
.idea
.vscode
*~
# coverage
coverage.txt

10
.gitlint Normal file
View file

@ -0,0 +1,10 @@
[general]
fail-without-commits=true
contrib=CC1
[title-match-regex]
regex=^\[\#[0-9Xx]+\]\s
[ignore-by-title]
regex=^Release(.*)
ignore=title-match-regex

View file

@ -24,6 +24,9 @@ linters-settings:
govet:
# report about shadowed variables
check-shadowing: false
staticcheck:
checks: ["all", "-SA1019"] # TODO Enable SA1019 after deprecated warning are fixed.
linters:
enable:
@ -32,15 +35,12 @@ linters:
- revive
# some default golangci-lint linters
- deadcode
- errcheck
- gosimple
- ineffassign
- staticcheck
- structcheck
- typecheck
- unused
- varcheck
# extra linters
- exhaustive

30
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,30 @@
ci:
autofix_prs: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-executables-have-shebangs
- id: check-shebang-scripts-are-executable
- id: check-merge-conflict
- id: check-json
- id: check-xml
- id: check-yaml
- id: trailing-whitespace
args: [--markdown-linebreak-ext=md]
- id: end-of-file-fixer
exclude: ".key$"
- 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]

View file

@ -1,60 +0,0 @@
# Changelog
## [1.0.0-rc.8] - 2023-04-27
We've reworked the way we handle keys in the SDK and added an experimental API
simplifying object creation. Pools were also improved and a number of other
minor enhancements are included in this release as well. Try it in your
applications and leave your feedback in issues, we need it to make 1.0.0 a
really good release for all NeoFS use cases.
New features:
* object/relations package for large (splitted) object data handling (#360, #348)
* pool can now accept stream operations timeout parameter (#364)
* pool can also accept callbacks to be invoked after every client response (#365)
* additional marshaling/unmarshaling methods for accounting.Decimal,
netmap.NetworkInfo and version.Version (#378)
* it's possible to get a single client from a pool now (#388)
* object.SearchFilters now provide more convenient APIs to search for object
by their hashes (#386)
* experimental client-side API for object creation (and slicing large inputs
into split objects, #382)
Behaviour changes:
* ns.ResolveContainerName changed to ns.ResolveContainerName with more
specific input type (#356)
* pool.ErrPoolClientUnhealthy is no longer exported (#358)
* deprecated object.RawObject type is gone (#387)
* pools no longer use default session tokens for read operations (they're not
required technically, #363)
* Go 1.18+ is required now (#394)
* enumeration types in eacl and object packages now have
EncodeToString/DecodeString methods replacing String/FromString for
serialization; String is still available, but it's not guaranteed to be
compatible across versions (#393)
* signing APIs no longer require a specific key, it can be provided in a
Signer wrapper (with different schemes) or a crypto.StaticSigner can be
used for signatures calculated outside of the SDK (#398, #401)
* (NetMap).PlacementVectors and (NetMap).ContainerNodes APIs now use more
specific types simplifying their use (#396)
Improvements:
* pool can work with some nodes being inaccessible now (#358)
* unified error messages in the client/status package (#369)
* documentation updates (#370, #392, #396)
* updated NeoGo, ANTLR, zap, golang-lru dependencies (#372)
* (*Client).ContainerSetEACL method now checks CID internally before
generating a request to send to the network (#389)
* neofs-api-go signature package is no longer required (#401)
Bugs fixed:
* pool.DeleteObject now correctly handles large (splitted) objects (#360)
* object structure problems are no longer considered as connection problems
by the pool (#374)
* private key used as a part of a map key in the pool (#401)
## pre-v1.0.0-rc.8
See git log.
[1.0.0-rc.8]: https://github.com/nspcc-dev/neofs-sdk-go/compare/v1.0.0-rc.7...v1.0.0-rc.8

2
Makefile Normal file → Executable file
View file

@ -37,4 +37,4 @@ help:
@echo ''
@echo ' Targets:'
@echo ''
@awk '/^#/{ comment = substr($$0,3) } comment && /^[a-zA-Z][a-zA-Z0-9_-]+ ?:/{ print " ", $$1, comment }' $(MAKEFILE_LIST) | column -t -s ':' | grep -v 'IGNORE' | sort -u
@awk '/^#/{ comment = substr($$0,3) } comment && /^[a-zA-Z][a-zA-Z0-9_-]+ ?:/{ print " ", $$1, comment }' $(MAKEFILE_LIST) | column -t -s ':' | grep -v 'IGNORE' | sort -u

View file

@ -1,6 +1,6 @@
# neofs-sdk-go
Go implementation of NeoFS SDK. It contains high-level version-independent wrappers
for structures from [neofs-api-go](https://github.com/nspcc-dev/neofs-api-go) as well as
# frostfs-sdk-go
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
helper functions for simplifying node/dApp implementations.
## Repository structure
@ -10,43 +10,42 @@ Contains fixed-point `Decimal` type for performing balance calculations.
### eacl
Contains Extended ACL types for fine-grained access control.
There is also a reference implementation of checking algorithm which is used in NeoFS node.
There is also a reference implementation of checking algorithm which is used in FrostFS node.
### checksum
Contains `Checksum` type encapsulating checksum as well as it's kind.
Currently Sha256 and [Tillich-Zemor hashsum](https://github.com/nspcc-dev/tzhash) are in use.
Currently Sha256 and [Tillich-Zemor hashsum](https://git.frostfs.info/TrueCloudLab/tzhash) are in use.
### owner
`owner.ID` type represents single account interacting with NeoFS. In v2 version of protocol
`owner.ID` type represents single account interacting with FrostFS. In v2 version of protocol
it is just raw bytes behing [base58-encoded address](https://docs.neo.org/docs/en-us/basic/concept/wallets.html#address)
in Neo blockchain. Note that for historical reasons it contains
version prefix and checksum in addition to script-hash.
### token
Contains Bearer token type with several NeoFS-specific methods.
Contains Bearer token type with several FrostFS-specific methods.
### ns
In NeoFS there are 2 types of name resolution: DNS and NNS. NNS stands for Neo Name Service
is just a [contract](https://github.com/nspcc-dev/neofs-contract/) deployed on a Neo blockchain.
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.
Basically, NNS is just a DNS-on-chain which can be used for resolving container nice-names as well
as any other name in dApps. See our [CoreDNS plugin](https://github.com/nspcc-dev/coredns/tree/master/plugin/nns)
for the example of how NNS can be integrated in DNS.
### session
To help lightweight clients interact with NeoFS without sacrificing trust, NeoFS has a concept
To help lightweight clients interact with FrostFS without sacrificing trust, FrostFS has a concept
of session token. It is signed by client and allows any node with which a session is established
to perform certain actions on behalf of the user.
### client
Contains client for working with NeoFS.
Contains client for working with FrostFS.
```go
var prmInit client.PrmInit
prmInit.SetDefaultPrivateKey(key) // private key for request signing
prmInit.ResolveFrostFSFailures() // enable erroneous status parsing
c, err := client.New(prmInit)
if err != nil {
return
}
var c client.Client
c.Init(prmInit)
var prmDial client.PrmDial
prmDial.SetServerURI("grpcs://localhost:40005") // endpoint address
@ -55,7 +54,7 @@ err := c.Dial(prmDial)
if err != nil {
return
}
ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
defer cancel()
@ -71,14 +70,15 @@ fmt.Printf("Balance for %s: %v\n", acc, res.Amount())
```
#### Response status
In NeoFS every operation can fail on multiple levels, so a single `error` doesn't suffice,
In FrostFS every operation can fail on multiple levels, so a single `error` doesn't suffice,
e.g. consider a case when object was put on 4 out of 5 replicas. Thus, all request execution
details are contained in `Status` returned from every RPC call. dApp can inspect them
if needed and perform any desired action. In the case above we may want to report
these details to the user as well as retry an operation, possibly with different parameters.
Status wire-format is extendable and each node can report any set of details it wants.
The set of reserved status codes can be found in
[NeoFS API](https://github.com/nspcc-dev/neofs-api/blob/master/status/types.proto).
[FrostFS API](https://git.frostfs.info/TrueCloudLab/frostfs-api/src/branch/master/status/types.proto). There is also
a `client.PrmInit.ResolveFrostFSFailures()` to seamlessly convert erroneous statuses into Go error type.
### policy
Contains helpers allowing conversion of placing policy from/to JSON representation
@ -98,19 +98,19 @@ Contains CRUSH-like implementation of container node selection algorithm. Releva
are described in this paper http://ceur-ws.org/Vol-2344/short10.pdf . Note that it can be
outdated in some details.
`netmap/json_tests` subfolder contains language-agnostic tests for selection algorithm.
`netmap/json_tests` subfolder contains language-agnostic tests for selection algorithm.
```go
import (
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/object"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
)
func placementNodes(addr *object.Address, p *netmap.PlacementPolicy, neofsNodes []netmap.NodeInfo) {
// Convert list of nodes in NeoFS API format to the intermediate representation.
func placementNodes(addr *object.Address, p *netmap.PlacementPolicy, frostfsNodes []netmap.NodeInfo) {
// Convert list of nodes in FrostFS API format to the intermediate representation.
nodes := netmap.NodesFromInfo(nodes)
// Create new netmap (errors are skipped for the sake of clarity).
// Create new netmap (errors are skipped for the sake of clarity).
nm, _ := NewNetmap(nodes)
// Calculate nodes of container.
@ -122,13 +122,13 @@ func placementNodes(addr *object.Address, p *netmap.PlacementPolicy, neofsNodes
```
### pool
Simple pool for managing connections to NeoFS nodes.
Simple pool for managing connections to FrostFS nodes.
### acl, checksum, version, signature
Contain simple API wrappers.
### logger
Wrapper over `zap.Logger` which is used across NeoFS codebase.
Wrapper over `zap.Logger` which is used across FrostFS codebase.
### util
Utilities for working with signature-related code.

View file

@ -1,12 +1,10 @@
package accounting
import (
"github.com/nspcc-dev/neofs-api-go/v2/accounting"
)
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
// Decimal represents decimal number for accounting operations.
//
// Decimal is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/accounting.Decimal
// Decimal is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting.Decimal
// message. See ReadFromV2 / WriteToV2 methods.
//
// Instances can be created using built-in var declaration.
@ -17,7 +15,7 @@ import (
type Decimal accounting.Decimal
// ReadFromV2 reads Decimal from the accounting.Decimal message. Checks if the
// message conforms to NeoFS API V2 protocol.
// message conforms to FrostFS API V2 protocol.
//
// See also WriteToV2.
func (d *Decimal) ReadFromV2(m accounting.Decimal) error {
@ -64,30 +62,3 @@ func (d Decimal) Precision() uint32 {
func (d *Decimal) SetPrecision(p uint32) {
(*accounting.Decimal)(d).SetPrecision(p)
}
// Marshal encodes Decimal into a binary format of the NeoFS API protocol
// (Protocol Buffers with direct field order).
//
// See also Unmarshal.
func (d Decimal) Marshal() []byte {
var m accounting.Decimal
d.WriteToV2(&m)
return m.StableMarshal(nil)
}
// Unmarshal decodes NeoFS API protocol binary format into the Decimal
// (Protocol Buffers with direct field order). Returns an error describing
// a format violation.
//
// See also Marshal.
func (d *Decimal) Unmarshal(data []byte) error {
var m accounting.Decimal
err := m.Unmarshal(data)
if err != nil {
return err
}
return d.ReadFromV2(m)
}

View file

@ -3,9 +3,8 @@ package accounting_test
import (
"testing"
v2accounting "github.com/nspcc-dev/neofs-api-go/v2/accounting"
"github.com/nspcc-dev/neofs-sdk-go/accounting"
accountingtest "github.com/nspcc-dev/neofs-sdk-go/accounting/test"
v2accounting "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
"github.com/stretchr/testify/require"
)
@ -45,12 +44,3 @@ func TestDecimalMessageV2(t *testing.T) {
require.EqualValues(t, d.Value(), m2.GetValue())
require.EqualValues(t, d.Precision(), m2.GetPrecision())
}
func TestDecimal_Marshal(t *testing.T) {
d := *accountingtest.Decimal()
var d2 accounting.Decimal
require.NoError(t, d2.Unmarshal(d.Marshal()))
require.Equal(t, d, d2)
}

View file

@ -1,5 +1,5 @@
/*
Package accounting provides primitives to perform accounting operations in NeoFS.
Package accounting provides primitives to perform accounting operations in FrostFS.
Decimal type provides functionality to process user balances. For example, when
working with Fixed8 balance precision:
@ -8,12 +8,12 @@ working with Fixed8 balance precision:
dec.SetValue(val)
dec.SetPrecision(8)
Instances can be also used to process NeoFS API V2 protocol messages
(see neo.fs.v2.accounting package in https://github.com/nspcc-dev/neofs-api).
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).
On client side:
import "github.com/nspcc-dev/neofs-api-go/v2/accounting"
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
var msg accounting.Decimal
dec.WriteToV2(&msg)

View file

@ -3,7 +3,7 @@ package accountingtest
import (
"math/rand"
"github.com/nspcc-dev/neofs-sdk-go/accounting"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
)
// Decimal returns random accounting.Decimal.

View file

@ -5,7 +5,7 @@ Note that importing the package into source files is highly discouraged.
Random instance generation functions can be useful when testing expects any value, e.g.:
import accountingtest "github.com/nspcc-dev/neofs-sdk-go/accounting/test"
import accountingtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting/test"
dec := accountingtest.Decimal()
// test the value

View file

@ -1,76 +0,0 @@
package audit
import (
"context"
"fmt"
"github.com/nspcc-dev/neofs-sdk-go/checksum"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/object/relations"
"github.com/nspcc-dev/neofs-sdk-go/storagegroup"
"github.com/nspcc-dev/tzhash/tz"
)
type Collector interface {
Head(ctx context.Context, addr oid.Address) (*object.Object, error)
relations.Relations
}
// CollectMembers creates new storage group structure and fills it
// with information about members collected via HeadReceiver.
//
// Resulting storage group consists of physically stored objects only.
func CollectMembers(ctx context.Context, collector Collector, cnr cid.ID, members []oid.ID, tokens relations.Tokens, calcHomoHash bool) (*storagegroup.StorageGroup, error) {
var (
err error
sumPhySize uint64
phyMembers []oid.ID
phyHashes [][]byte
addr oid.Address
sg storagegroup.StorageGroup
)
addr.SetContainer(cnr)
for i := range members {
if phyMembers, err = relations.ListRelations(ctx, collector, cnr, members[i], tokens, false); err != nil {
return nil, err
}
for _, phyMember := range phyMembers {
addr.SetObject(phyMember)
leaf, err := collector.Head(ctx, addr)
if err != nil {
return nil, fmt.Errorf("head phy member '%s': %w", phyMember.EncodeToString(), err)
}
sumPhySize += leaf.PayloadSize()
cs, _ := leaf.PayloadHomomorphicHash()
if calcHomoHash {
phyHashes = append(phyHashes, cs.Value())
}
}
}
sg.SetMembers(phyMembers)
sg.SetValidationDataSize(sumPhySize)
if calcHomoHash {
sumHash, err := tz.Concat(phyHashes)
if err != nil {
return nil, err
}
var cs checksum.Checksum
tzHash := [64]byte{}
copy(tzHash[:], sumHash)
cs.SetTillichZemor(tzHash)
sg.SetValidationDataHash(cs)
}
return &sg, nil
}

View file

@ -1,5 +1,5 @@
/*
Package audit provides features to process data audit in NeoFS system.
Package audit provides features to process data audit in FrostFS system.
Result type groups values which can be gathered during data audit process:

View file

@ -4,16 +4,16 @@ import (
"errors"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/audit"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/version"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/audit"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
)
// Result represents report on the results of the data audit in NeoFS system.
// Result represents report on the results of the data audit in FrostFS system.
//
// Result is mutually binary-compatible with github.com/nspcc-dev/neofs-api-go/v2/audit.DataAuditResult
// Result is mutually binary-compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/audit.DataAuditResult
// message. See Marshal / Unmarshal methods.
//
// Instances can be created using built-in var declaration.
@ -23,7 +23,7 @@ type Result struct {
v2 audit.DataAuditResult
}
// Marshal encodes Result into a canonical NeoFS binary format (Protocol Buffers
// 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
@ -43,7 +43,7 @@ func (r *Result) Marshal() []byte {
var errCIDNotSet = errors.New("container ID is not set")
// Unmarshal decodes Result from its canonical NeoFS binary format (Protocol Buffers
// 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.
@ -91,7 +91,7 @@ func (r *Result) Unmarshal(data []byte) error {
return nil
}
// Epoch returns NeoFS epoch when the data associated with the Result was audited.
// Epoch returns FrostFS epoch when the data associated with the Result was audited.
//
// Zero Result has zero epoch.
//
@ -100,7 +100,7 @@ func (r Result) Epoch() uint64 {
return r.v2.GetAuditEpoch()
}
// ForEpoch specifies NeoFS epoch when the data associated with the Result was audited.
// ForEpoch specifies FrostFS epoch when the data associated with the Result was audited.
//
// See also Epoch.
func (r *Result) ForEpoch(epoch uint64) {
@ -136,8 +136,8 @@ func (r *Result) ForContainer(cnr cid.ID) {
r.v2.SetContainerID(&cidV2)
}
// AuditorKey returns public key of the auditing NeoFS Inner Ring node in
// a NeoFS binary key format.
// 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.
@ -147,8 +147,8 @@ func (r Result) AuditorKey() []byte {
return r.v2.GetPublicKey()
}
// SetAuditorKey specifies public key of the auditing NeoFS Inner Ring node in
// a NeoFS binary key format.
// 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.
//

View file

@ -4,11 +4,11 @@ import (
"bytes"
"testing"
"github.com/nspcc-dev/neofs-sdk-go/audit"
audittest "github.com/nspcc-dev/neofs-sdk-go/audit/test"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/audit"
audittest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/audit/test"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
"github.com/stretchr/testify/require"
)

View file

@ -5,7 +5,7 @@ Note that importing the package into source files is highly discouraged.
Random instance generation functions can be useful when testing expects any value, e.g.:
import audittest "github.com/nspcc-dev/neofs-sdk-go/audit/test"
import audittest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/audit/test"
dec := audittest.Result()
// test the value

View file

@ -1,9 +1,9 @@
package audittest
import (
"github.com/nspcc-dev/neofs-sdk-go/audit"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/audit"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
)
// Result returns random audit.Result.

View file

@ -1,20 +1,22 @@
package bearer
import (
"crypto/ecdsa"
"errors"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/acl"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/nspcc-dev/neofs-sdk-go/user"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
)
// Token represents bearer token for object service operations.
//
// Token is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/acl.BearerToken
// Token is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl.BearerToken
// message. See ReadFromV2 / WriteToV2 methods.
//
// Instances can be created using built-in var declaration.
@ -134,7 +136,7 @@ func (b Token) WriteToV2(m *acl.BearerToken) {
}
// SetExp sets "exp" (expiration time) claim which identifies the
// expiration time (in NeoFS epochs) after which the Token MUST NOT be
// expiration time (in FrostFS epochs) after which the Token MUST NOT be
// accepted for processing. The processing of the "exp" claim requires
// that the current epoch MUST be before or equal to the expiration epoch
// listed in the "exp" claim.
@ -148,7 +150,7 @@ func (b *Token) SetExp(exp uint64) {
}
// SetNbf sets "nbf" (not before) claim which identifies the time (in
// NeoFS epochs) before which the Token MUST NOT be accepted for processing. The
// FrostFS epochs) before which the Token MUST NOT be accepted for processing. The
// processing of the "nbf" claim requires that the current epoch MUST be
// after or equal to the not-before epoch listed in the "nbf" claim.
//
@ -160,7 +162,7 @@ func (b *Token) SetNbf(nbf uint64) {
b.lifetimeSet = true
}
// SetIat sets "iat" (issued at) claim which identifies the time (in NeoFS
// SetIat sets "iat" (issued at) claim which identifies the time (in FrostFS
// epochs) at which the Token was issued. This claim can be used to determine
// the age of the Token.
//
@ -187,7 +189,7 @@ func (b Token) InvalidAt(epoch uint64) bool {
// within any issuer's container.
//
// SetEACLTable MUST be called if Token is going to be transmitted over
// NeoFS API V2 protocol.
// FrostFS API V2 protocol.
//
// See also EACLTable, AssertContainer.
func (b *Token) SetEACLTable(table eacl.Table) {
@ -243,20 +245,20 @@ func (b Token) AssertUser(id user.ID) bool {
return !b.targetUserSet || b.targetUser.Equals(id)
}
// Sign calculates and writes signature of the Token data using issuer's signer.
// Sign calculates and writes signature of the Token data using issuer's secret.
// Returns signature calculation errors.
//
// Sign MUST be called if Token is going to be transmitted over
// NeoFS API V2 protocol.
// FrostFS API V2 protocol.
//
// Note that any Token mutation is likely to break the signature, so it is
// expected to be calculated as a final stage of Token formation.
//
// See also VerifySignature, Issuer.
func (b *Token) Sign(signer neofscrypto.Signer) error {
var sig neofscrypto.Signature
func (b *Token) Sign(key ecdsa.PrivateKey) error {
var sig frostfscrypto.Signature
err := sig.Calculate(signer, b.signedData())
err := sig.Calculate(frostfsecdsa.Signer(key), b.signedData())
if err != nil {
return err
}
@ -277,13 +279,13 @@ func (b Token) VerifySignature() bool {
return false
}
var sig neofscrypto.Signature
var sig frostfscrypto.Signature
// TODO: (#233) check owner<->key relation
return sig.ReadFromV2(b.sig) == nil && sig.Verify(b.signedData())
}
// Marshal encodes Token into a binary format of the NeoFS API protocol
// Marshal encodes Token into a binary format of the FrostFS API protocol
// (Protocol Buffers V3 with direct field order).
//
// See also Unmarshal.
@ -294,7 +296,7 @@ func (b Token) Marshal() []byte {
return m.StableMarshal(nil)
}
// Unmarshal decodes NeoFS API protocol binary data into the Token
// Unmarshal decodes FrostFS API protocol binary data into the Token
// (Protocol Buffers V3 with direct field order). Returns an error describing
// a format violation.
//
@ -310,7 +312,7 @@ func (b *Token) Unmarshal(data []byte) error {
return b.readFromV2(m, false)
}
// MarshalJSON encodes Token into a JSON format of the NeoFS API protocol
// MarshalJSON encodes Token into a JSON format of the FrostFS API protocol
// (Protocol Buffers V3 JSON).
//
// See also UnmarshalJSON.
@ -321,7 +323,7 @@ func (b Token) MarshalJSON() ([]byte, error) {
return m.MarshalJSON()
}
// UnmarshalJSON decodes NeoFS API protocol JSON data into the Token
// UnmarshalJSON decodes FrostFS API protocol JSON data into the Token
// (Protocol Buffers V3 JSON). Returns an error describing a format violation.
//
// See also MarshalJSON.
@ -337,7 +339,7 @@ func (b *Token) UnmarshalJSON(data []byte) error {
}
// SigningKeyBytes returns issuer's public key in a binary format of
// NeoFS API protocol.
// FrostFS API protocol.
//
// Unsigned Token has empty key.
//
@ -358,8 +360,9 @@ func ResolveIssuer(b Token) (usr user.ID) {
binKey := b.SigningKeyBytes()
if len(binKey) != 0 {
if err := user.IDFromKey(&usr, binKey); err != nil {
usr = user.ID{}
var key frostfsecdsa.PublicKey
if key.Decode(binKey) == nil {
user.IDFromKey(&usr, ecdsa.PublicKey(key))
}
}

View file

@ -5,17 +5,18 @@ import (
"math/rand"
"testing"
"github.com/nspcc-dev/neofs-api-go/v2/acl"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
bearertest "github.com/nspcc-dev/neofs-sdk-go/bearer/test"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/crypto/test"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
eacltest "github.com/nspcc-dev/neofs-sdk-go/eacl/test"
"github.com/nspcc-dev/neofs-sdk-go/user"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
bearertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer/test"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
eacltest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require"
)
@ -37,7 +38,7 @@ func isEqualEACLTables(t1, t2 eacl.Table) bool {
func TestToken_SetEACLTable(t *testing.T) {
var val bearer.Token
var m acl.BearerToken
filled := bearertest.Token(t)
filled := bearertest.Token()
val.WriteToV2(&m)
require.Zero(t, m.GetBody())
@ -57,7 +58,7 @@ func TestToken_SetEACLTable(t *testing.T) {
// set value
eaclTable := *eacltest.Table(t)
eaclTable := *eacltest.Table()
val.SetEACLTable(eaclTable)
require.True(t, isEqualEACLTables(eaclTable, val.EACLTable()))
@ -83,7 +84,7 @@ func TestToken_SetEACLTable(t *testing.T) {
func TestToken_ForUser(t *testing.T) {
var val bearer.Token
var m acl.BearerToken
filled := bearertest.Token(t)
filled := bearertest.Token()
val.WriteToV2(&m)
require.Zero(t, m.GetBody())
@ -106,7 +107,7 @@ func TestToken_ForUser(t *testing.T) {
require.Zero(t, m.GetBody())
// set value
usr := *usertest.ID(t)
usr := *usertest.ID()
var usrV2 refs.OwnerID
usr.WriteToV2(&usrV2)
@ -137,7 +138,7 @@ func TestToken_ForUser(t *testing.T) {
func testLifetimeClaim(t *testing.T, setter func(*bearer.Token, uint64), getter func(*acl.BearerToken) uint64) {
var val bearer.Token
var m acl.BearerToken
filled := bearertest.Token(t)
filled := bearertest.Token()
val.WriteToV2(&m)
require.Zero(t, m.GetBody())
@ -229,7 +230,7 @@ func TestToken_AssertContainer(t *testing.T) {
require.True(t, val.AssertContainer(cnr))
eaclTable := *eacltest.Table(t)
eaclTable := *eacltest.Table()
eaclTable.SetCID(cidtest.ID())
val.SetEACLTable(eaclTable)
@ -242,11 +243,11 @@ func TestToken_AssertContainer(t *testing.T) {
func TestToken_AssertUser(t *testing.T) {
var val bearer.Token
usr := *usertest.ID(t)
usr := *usertest.ID()
require.True(t, val.AssertUser(usr))
val.ForUser(*usertest.ID(t))
val.ForUser(*usertest.ID())
require.False(t, val.AssertUser(usr))
val.ForUser(usr)
@ -258,11 +259,13 @@ func TestToken_Sign(t *testing.T) {
require.False(t, val.VerifySignature())
signer := test.RandomSigner(t)
k, err := keys.NewPrivateKey()
require.NoError(t, err)
val = bearertest.Token(t)
key := k.PrivateKey
val = bearertest.Token()
require.NoError(t, val.Sign(signer))
require.NoError(t, val.Sign(key))
require.True(t, val.VerifySignature())
@ -272,7 +275,7 @@ func TestToken_Sign(t *testing.T) {
require.NotZero(t, m.GetSignature().GetKey())
require.NotZero(t, m.GetSignature().GetSign())
val2 := bearertest.Token(t)
val2 := bearertest.Token()
require.NoError(t, val2.Unmarshal(val.Marshal()))
require.True(t, val2.VerifySignature())
@ -280,7 +283,7 @@ func TestToken_Sign(t *testing.T) {
jd, err := val.MarshalJSON()
require.NoError(t, err)
val2 = bearertest.Token(t)
val2 = bearertest.Token()
require.NoError(t, val2.UnmarshalJSON(jd))
require.True(t, val2.VerifySignature())
}
@ -296,7 +299,7 @@ func TestToken_ReadFromV2(t *testing.T) {
require.Error(t, val.ReadFromV2(m))
eaclTable := eacltest.Table(t).ToV2()
eaclTable := eacltest.Table().ToV2()
body.SetEACL(eaclTable)
require.Error(t, val.ReadFromV2(m))
@ -325,7 +328,7 @@ func TestToken_ReadFromV2(t *testing.T) {
val.WriteToV2(&m2)
require.Equal(t, m, m2)
usr, usr2 := *usertest.ID(t), *usertest.ID(t)
usr, usr2 := *usertest.ID(), *usertest.ID()
require.True(t, val.AssertUser(usr))
require.True(t, val.AssertUser(usr2))
@ -343,9 +346,12 @@ func TestToken_ReadFromV2(t *testing.T) {
require.True(t, val.AssertUser(usr))
require.False(t, val.AssertUser(usr2))
signer := test.RandomSigner(t)
k, err := keys.NewPrivateKey()
require.NoError(t, err)
var s neofscrypto.Signature
signer := frostfsecdsa.Signer(k.PrivateKey)
var s frostfscrypto.Signature
require.NoError(t, s.Calculate(signer, body.StableMarshal(nil)))
@ -357,7 +363,8 @@ func TestToken_ReadFromV2(t *testing.T) {
}
func TestResolveIssuer(t *testing.T) {
signer := test.RandomSigner(t)
k, err := keys.NewPrivateKey()
require.NoError(t, err)
var val bearer.Token
@ -374,10 +381,10 @@ func TestResolveIssuer(t *testing.T) {
require.Zero(t, bearer.ResolveIssuer(val))
require.NoError(t, val.Sign(signer))
require.NoError(t, val.Sign(k.PrivateKey))
var usr user.ID
require.NoError(t, user.IDFromSigner(&usr, signer))
user.IDFromKey(&usr, k.PrivateKey.PublicKey)
require.Equal(t, usr, bearer.ResolveIssuer(val))
}

View file

@ -22,7 +22,7 @@ Bearer token must be signed by owner of the container.
Provide signed token in JSON or binary format to the request sender. Request
sender can attach this bearer token to the object service requests:
import sdkClient "github.com/nspcc-dev/neofs-sdk-go/client"
import sdkClient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
var headParams sdkClient.PrmObjectHead
headParams.WithBearerToken(bearerToken)

View file

@ -1,22 +1,20 @@
package bearertest
import (
"testing"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
eacltest "github.com/nspcc-dev/neofs-sdk-go/eacl/test"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
eacltest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl/test"
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
)
// Token returns random bearer.Token.
//
// Resulting token is unsigned.
func Token(t testing.TB) (tok bearer.Token) {
tok.SetExp(3)
tok.SetNbf(2)
tok.SetIat(1)
tok.ForUser(*usertest.ID(t))
tok.SetEACLTable(*eacltest.Table(t))
func Token() (t bearer.Token) {
t.SetExp(3)
t.SetNbf(2)
t.SetIat(1)
t.ForUser(*usertest.ID())
t.SetEACLTable(*eacltest.Table())
return tok
return t
}

View file

@ -6,13 +6,13 @@ import (
"errors"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/tzhash/tz"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/tzhash/tz"
)
// Checksum represents checksum of some digital data.
//
// Checksum is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.Checksum
// Checksum is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.Checksum
// message. See ReadFromV2 / WriteToV2 methods.
//
// Instances can be created using built-in var declaration.
@ -38,7 +38,7 @@ const (
)
// ReadFromV2 reads Checksum from the refs.Checksum message. Checks if the
// message conforms to NeoFS API V2 protocol.
// message conforms to FrostFS API V2 protocol.
//
// See also WriteToV2.
func (c *Checksum) ReadFromV2(m refs.Checksum) error {

View file

@ -5,8 +5,8 @@ import (
"crypto/sha256"
"testing"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/tzhash/tz"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/tzhash/tz"
"github.com/stretchr/testify/require"
)

View file

@ -6,7 +6,7 @@ import (
"fmt"
"math/rand"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
)
func ExampleCalculate() {

View file

@ -5,7 +5,7 @@ Note that importing the package into source files is highly discouraged.
Random instance generation functions can be useful when testing expects any value, e.g.:
import checksumtest "github.com/nspcc-dev/neofs-sdk-go/checksum/test"
import checksumtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum/test"
cs := checksumtest.Checksum()
// test the value

View file

@ -4,7 +4,7 @@ import (
"crypto/sha256"
"math/rand"
"github.com/nspcc-dev/neofs-sdk-go/checksum"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
)
// Checksum returns random checksum.Checksum.

View file

@ -3,12 +3,12 @@ package client
import (
"context"
v2accounting "github.com/nspcc-dev/neofs-api-go/v2/accounting"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
"github.com/nspcc-dev/neofs-sdk-go/accounting"
"github.com/nspcc-dev/neofs-sdk-go/user"
v2accounting "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
)
// PrmBalanceGet groups parameters of BalanceGet operation.
@ -19,7 +19,7 @@ type PrmBalanceGet struct {
account user.ID
}
// SetAccount sets identifier of the NeoFS account for which the balance is requested.
// SetAccount sets identifier of the FrostFS account for which the balance is requested.
// Required parameter.
func (x *PrmBalanceGet) SetAccount(id user.ID) {
x.account = id
@ -28,27 +28,35 @@ func (x *PrmBalanceGet) SetAccount(id user.ID) {
// ResBalanceGet groups resulting values of BalanceGet operation.
type ResBalanceGet struct {
statusRes
amount accounting.Decimal
}
// Amount returns current amount of funds on the NeoFS account as decimal number.
// Amount returns current amount of funds on the FrostFS account as decimal number.
func (x ResBalanceGet) Amount() accounting.Decimal {
return x.amount
}
// BalanceGet requests current balance of the NeoFS account.
// BalanceGet requests current balance of the FrostFS account.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
// 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 PrmBalanceGet docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingAccount]
// Return statuses:
// - global (see Client docs).
func (c *Client) BalanceGet(ctx context.Context, prm PrmBalanceGet) (*ResBalanceGet, error) {
switch {
case ctx == nil:
return nil, errorMissingContext
case !prm.accountSet:
return nil, ErrMissingAccount
return nil, errorAccountNotSet
}
// form request body
@ -73,6 +81,7 @@ func (c *Client) BalanceGet(ctx context.Context, prm PrmBalanceGet) (*ResBalance
c.initCallContext(&cc)
cc.meta = prm.prmCommonMeta
cc.req = &req
cc.statusRes = &res
cc.call = func() (responseV2, error) {
return rpcapi.Balance(&c.c, &req, client.WithContext(ctx))
}

View file

@ -4,21 +4,18 @@ import (
"context"
"fmt"
v2netmap "github.com/nspcc-dev/neofs-api-go/v2/netmap"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
"github.com/nspcc-dev/neofs-api-go/v2/session"
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
)
// interface of NeoFS API server. Exists for test purposes only.
type neoFSAPIServer interface {
createSession(cli *client.Client, req *session.CreateRequest, opts ...client.CallOption) (*session.CreateResponse, error)
// interface of FrostFS API server. Exists for test purposes only.
type frostFSAPIServer interface {
netMapSnapshot(context.Context, v2netmap.SnapshotRequest) (*v2netmap.SnapshotResponse, error)
}
// wrapper over real client connection which communicates over NeoFS API protocol.
// Provides neoFSAPIServer for Client instances used in real applications.
// wrapper over real client connection which communicates over FrostFS API protocol.
// Provides frostFSAPIServer for Client instances used in real applications.
type coreServer client.Client
// unifies errors of all RPC.
@ -26,7 +23,7 @@ func rpcErr(e error) error {
return fmt.Errorf("rpc failure: %w", e)
}
// executes NetmapService.NetmapSnapshot RPC declared in NeoFS API protocol
// executes NetmapService.NetmapSnapshot RPC declared in FrostFS API protocol
// using underlying client.Client.
func (x *coreServer) netMapSnapshot(ctx context.Context, req v2netmap.SnapshotRequest) (*v2netmap.SnapshotResponse, error) {
resp, err := rpcapi.NetMapSnapshot((*client.Client)(x), &req, client.WithContext(ctx))
@ -36,12 +33,3 @@ func (x *coreServer) netMapSnapshot(ctx context.Context, req v2netmap.SnapshotRe
return resp, nil
}
func (x *coreServer) createSession(cli *client.Client, req *session.CreateRequest, opts ...client.CallOption) (*session.CreateResponse, error) {
resp, err := rpcapi.CreateSession(cli, req, opts...)
if err != nil {
return nil, rpcErr(err)
}
return resp, nil
}

View file

@ -2,42 +2,43 @@ package client
import (
"context"
"crypto/ecdsa"
"crypto/tls"
"errors"
"fmt"
"time"
v2accounting "github.com/nspcc-dev/neofs-api-go/v2/accounting"
"github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
v2accounting "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
)
// Client represents virtual connection to the NeoFS network to communicate
// with NeoFS server using NeoFS API protocol. It is designed to provide
// Client represents virtual connection to the FrostFS network to communicate
// with FrostFS server using FrostFS API protocol. It is designed to provide
// an abstraction interface from the protocol details of data transfer over
// a network in NeoFS.
// a network in FrostFS.
//
// Client can be created using [New].
// Before executing the NeoFS operations using the Client, connection to the
// Client can be created using simple Go variable declaration. Before starting
// work with the Client, it SHOULD BE correctly initialized (see Init method).
// Before executing the FrostFS operations using the Client, connection to the
// server MUST BE correctly established (see Dial method and pay attention
// to the mandatory parameters). Using the Client before connecting have
// been established can lead to a panic. After the work, the Client SHOULD BE
// closed (see Close method): it frees internal and system resources which were
// allocated for the period of work of the Client. Calling [Client.Dial]/[Client.Close] method
// allocated for the period of work of the Client. Calling Init/Dial/Close method
// during the communication process step strongly discouraged as it leads to
// undefined behavior.
//
// Each method which produces a NeoFS API call may return a server response.
// Each method which produces a FrostFS API call may return a server response.
// Status responses are returned in the result structure, and can be cast
// to built-in error instance (or in the returned error if the client is
// configured accordingly). Certain statuses can be checked using [apistatus]
// and standard [errors] packages.
// configured accordingly). Certain statuses can be checked using `apistatus`
// and standard `errors` packages. Note that package provides some helper
// functions to work with status returns (e.g. IsErrContainerNotFound).
// All possible responses are documented in methods, however, some may be
// returned from all of them (pay attention to the presence of the pointer sign):
// - *[apistatus.ServerInternal] on internal server error;
// - *[apistatus.NodeUnderMaintenance] if a server is under maintenance;
// - *[apistatus.SuccessDefaultV2] on default success.
// - *apistatus.ServerInternal on internal server error;
// - *apistatus.NodeUnderMaintenance if a server is under maintenance;
// - *apistatus.SuccessDefaultV2 on default success.
//
// Client MUST NOT be copied by value: use pointer to Client instead.
//
@ -47,27 +48,20 @@ type Client struct {
c client.Client
server neoFSAPIServer
server frostFSAPIServer
}
var errNonNeoSigner = fmt.Errorf("%w: expected ECDSA_DETERMINISTIC_SHA256 scheme", neofscrypto.ErrIncorrectSigner)
// New creates an instance of Client initialized with the given parameters.
// Init brings the Client instance to its initial state.
//
// See docs of [PrmInit] methods for details. See also [Client.Dial]/[Client.Close].
// One-time method call during application init stage (before Dial) is expected.
// Calling multiple times leads to undefined behavior.
//
// Returned errors:
// - [neofscrypto.ErrIncorrectSigner]
func New(prm PrmInit) (*Client, error) {
var c = new(Client)
if prm.signer != nil && prm.signer.Scheme() != neofscrypto.ECDSA_DETERMINISTIC_SHA256 {
return nil, errNonNeoSigner
}
// See docs of PrmInit methods for details. See also Dial / Close.
func (c *Client) Init(prm PrmInit) {
c.prm = prm
return c, nil
}
// Dial establishes a connection to the server from the NeoFS network.
// Dial establishes a connection to the server from the FrostFS network.
// Returns an error describing failure reason. If failed, the Client
// SHOULD NOT be used.
//
@ -75,25 +69,21 @@ func New(prm PrmInit) (*Client, error) {
// argument, otherwise context.Background() is used. Dial returns context
// errors, see context package docs for details.
//
// Panics if required parameters are set incorrectly, look carefully
// Returns an error if required parameters are set incorrectly, look carefully
// at the method documentation.
//
// One-time method call during application start-up stage is expected.
// One-time method call during application start-up stage (after Init ) is expected.
// Calling multiple times leads to undefined behavior.
//
// Return client errors:
// - [ErrMissingServer]
// - [ErrNonPositiveTimeout]
//
// See also [Client.Close].
// See also Init / Close.
func (c *Client) Dial(prm PrmDial) error {
if prm.endpoint == "" {
return ErrMissingServer
return errorServerAddrUnset
}
if prm.timeoutDialSet {
if prm.timeoutDial <= 0 {
return ErrNonPositiveTimeout
return errorNonPositiveTimeout
}
} else {
prm.timeoutDial = 5 * time.Second
@ -101,7 +91,7 @@ func (c *Client) Dial(prm PrmDial) error {
if prm.streamTimeoutSet {
if prm.streamTimeout <= 0 {
return ErrNonPositiveTimeout
return errorNonPositiveTimeout
}
} else {
prm.streamTimeout = 10 * time.Second
@ -113,7 +103,7 @@ func (c *Client) Dial(prm PrmDial) error {
client.WithRWTimeout(prm.streamTimeout),
)...)
c.setNeoFSAPIServer((*coreServer)(&c.c))
c.setFrostFSAPIServer((*coreServer)(&c.c))
if prm.parentCtx == nil {
prm.parentCtx = context.Background()
@ -131,51 +121,58 @@ func (c *Client) Dial(prm PrmDial) error {
return nil
}
// sets underlying provider of neoFSAPIServer. The method is used for testing as an approach
// to skip Dial stage and override NeoFS API server. MUST NOT be used outside test code.
// In real applications wrapper over github.com/nspcc-dev/neofs-api-go/v2/rpc/client
// sets underlying provider of frostFSAPIServer. The method is used for testing as an approach
// to skip Dial stage and override FrostFS API server. MUST NOT be used outside test code.
// In real applications wrapper over git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client
// is statically used.
func (c *Client) setNeoFSAPIServer(server neoFSAPIServer) {
func (c *Client) setFrostFSAPIServer(server frostFSAPIServer) {
c.server = server
}
// Close closes underlying connection to the NeoFS server. Implements io.Closer.
// Close closes underlying connection to the FrostFS server. Implements io.Closer.
// MUST NOT be called before successful Dial. Can be called concurrently
// with server operations processing on running goroutines: in this case
// they are likely to fail due to a connection error.
//
// One-time method call during application shutdown stage (after [Client.Dial])
// One-time method call during application shutdown stage (after Init and Dial)
// is expected. Calling multiple times leads to undefined behavior.
//
// See also [Client.Dial].
// See also Init / Dial.
func (c *Client) Close() error {
return c.c.Conn().Close()
}
// PrmInit groups initialization parameters of Client instances.
//
// See also [New].
// See also Init.
type PrmInit struct {
signer neofscrypto.Signer
resolveFrostFSErrors bool
key ecdsa.PrivateKey
cbRespInfo func(ResponseMetaInfo) error
netMagic uint64
}
// SetDefaultSigner sets Client private signer to be used for the protocol
// SetDefaultPrivateKey sets Client private key to be used for the protocol
// communication by default.
//
// Optional if you intend to sign every request separately (see Prm* docs), but
// required if you'd like to use this signer for all operations implicitly.
// If specified, MUST be of [neofscrypto.ECDSA_DETERMINISTIC_SHA256] scheme,
// for example, [neofsecdsa.SignerRFC6979] can be used.
func (x *PrmInit) SetDefaultSigner(signer neofscrypto.Signer) {
x.signer = signer
// Required for operations without custom key parametrization (see corresponding Prm* docs).
func (x *PrmInit) SetDefaultPrivateKey(key ecdsa.PrivateKey) {
x.key = key
}
// ResolveFrostFSFailures makes the Client to resolve failure statuses of the
// FrostFS protocol into Go built-in errors. These errors are returned from
// each protocol operation. By default, statuses aren't resolved and written
// to the resulting structure (see corresponding Res* docs).
func (x *PrmInit) ResolveFrostFSFailures() {
x.resolveFrostFSErrors = true
}
// SetResponseInfoCallback makes the Client to pass ResponseMetaInfo from each
// NeoFS server response to f. Nil (default) means ignore response meta info.
// FrostFS server response to f. Nil (default) means ignore response meta info.
func (x *PrmInit) SetResponseInfoCallback(f func(ResponseMetaInfo) error) {
x.cbRespInfo = f
}
@ -197,7 +194,7 @@ type PrmDial struct {
parentCtx context.Context
}
// SetServerURI sets server URI in the NeoFS network.
// SetServerURI sets server URI in the FrostFS network.
// Required parameter.
//
// Format of the URI:
@ -215,7 +212,7 @@ func (x *PrmDial) SetServerURI(endpoint string) {
}
// SetTLSConfig sets tls.Config to open TLS client connection
// to the NeoFS server. Nil (default) means insecure connection.
// to the FrostFS server. Nil (default) means insecure connection.
//
// See also SetServerURI.
func (x *PrmDial) SetTLSConfig(tlsConfig *tls.Config) {

View file

@ -2,10 +2,12 @@ package client
import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"testing"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"github.com/stretchr/testify/require"
)
@ -13,21 +15,28 @@ import (
File contains common functionality used for client package testing.
*/
var key, _ = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
var statusErr apistatus.ServerInternal
func init() {
statusErr.SetMessage("test status error")
}
func newClient(t *testing.T, signer neofscrypto.Signer, server neoFSAPIServer) *Client {
func assertStatusErr(tb testing.TB, res interface{ Status() apistatus.Status }) {
require.IsType(tb, &statusErr, res.Status())
require.Equal(tb, statusErr.Message(), res.Status().(*apistatus.ServerInternal).Message())
}
func newClient(server frostFSAPIServer) *Client {
var prm PrmInit
prm.SetDefaultSigner(signer)
prm.SetDefaultPrivateKey(*key)
c, err := New(prm)
require.NoError(t, err)
c.setNeoFSAPIServer(server)
var c Client
c.Init(prm)
c.setFrostFSAPIServer(server)
return c
return &c
}
func TestClient_DialContext(t *testing.T) {

View file

@ -1,19 +1,43 @@
package client
import (
"crypto/ecdsa"
"errors"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/version"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
)
// common interface of resulting structures with API status.
type resCommon interface {
setStatus(apistatus.Status)
}
// structure is embedded to all resulting types in order to inherit status-related methods.
type statusRes struct {
st apistatus.Status
}
// setStatus implements resCommon interface method.
func (x *statusRes) setStatus(st apistatus.Status) {
x.st = st
}
// Status returns server's status return.
//
// Use apistatus package functionality to handle the status.
func (x statusRes) Status() apistatus.Status {
return x.st
}
// groups meta parameters shared between all Client operations.
type prmCommonMeta struct {
// NeoFS request X-Headers
// FrostFS request X-Headers
xHeaders []string
}
@ -47,6 +71,23 @@ func writeXHeadersToMeta(xHeaders []string, h *v2session.RequestMetaHeader) {
h.SetXHeaders(hs)
}
// error messages.
var (
errorMissingContext = errors.New("missing context")
errorMissingContainer = errors.New("missing container")
errorMissingObject = errors.New("missing object")
errorAccountNotSet = errors.New("account not set")
errorServerAddrUnset = errors.New("server address is unset or empty")
errorNonPositiveTimeout = errors.New("non-positive timeout")
errorEACLTableNotSet = errors.New("eACL table not set")
errorMissingAnnouncements = errors.New("missing announcements")
errorZeroRangeLength = errors.New("zero range length")
errorMissingRanges = errors.New("missing ranges")
errorZeroEpoch = errors.New("zero epoch")
errorMissingTrusts = errors.New("missing trusts")
errorTrustNotSet = errors.New("current trust value not set")
)
// groups all the details required to send a single request and process a response to it.
type contextCall struct {
// ==================================================
@ -61,13 +102,16 @@ type contextCall struct {
// ==================================================
// shared parameters which are set uniformly on all calls
// request signer
signer neofscrypto.Signer
// request signing key
key ecdsa.PrivateKey
// callback prior to processing the response by the client
callbackResp func(ResponseMetaInfo) error
// NeoFS network magic
// if set, protocol errors will be expanded into a final error
resolveAPIFailures bool
// FrostFS network magic
netMagic uint64
// Meta parameters
@ -76,7 +120,10 @@ type contextCall struct {
// ==================================================
// custom call parameters
// request to be signed with a signer and sent
// structure of the call result
statusRes resCommon
// request to be signed with a key and sent
req request
// function to send a request (unary) and receive a response
@ -151,7 +198,7 @@ func (x *contextCall) writeRequest() bool {
x.req.SetVerificationHeader(nil)
// sign the request
x.err = signServiceMessage(x.signer, x.req)
x.err = signature.SignServiceMessage(&x.key, x.req)
if x.err != nil {
x.err = fmt.Errorf("sign request: %w", x.err)
return false
@ -190,28 +237,44 @@ func (x *contextCall) processResponse() bool {
// while verification needs marshaling
// verify response signature
x.err = verifyServiceMessage(x.resp)
x.err = signature.VerifyServiceMessage(x.resp)
if x.err != nil {
x.err = fmt.Errorf("invalid response signature: %w", x.err)
return false
}
// get result status
x.err = apistatus.ErrorFromV2(x.resp.GetMetaHeader().GetStatus())
return x.err == nil
}
st := apistatus.FromStatusV2(x.resp.GetMetaHeader().GetStatus())
// processResponse verifies response signature.
func (c *Client) processResponse(resp responseV2) error {
if err := verifyServiceMessage(resp); err != nil {
return fmt.Errorf("invalid response signature: %w", err)
// unwrap unsuccessful status and return it
// as error if client has been configured so
successfulStatus := apistatus.IsSuccessful(st)
if x.resolveAPIFailures {
x.err = apistatus.ErrFromStatus(st)
} else {
x.statusRes.setStatus(st)
}
return apistatus.ErrorFromV2(resp.GetMetaHeader().GetStatus())
return successfulStatus
}
// processResponse verifies response signature and converts status to an error if needed.
func (c *Client) processResponse(resp responseV2) (apistatus.Status, error) {
err := signature.VerifyServiceMessage(resp)
if err != nil {
return nil, fmt.Errorf("invalid response signature: %w", err)
}
st := apistatus.FromStatusV2(resp.GetMetaHeader().GetStatus())
if c.prm.resolveFrostFSErrors {
return st, apistatus.ErrFromStatus(st)
}
return st, nil
}
// reads response (if rResp is set) and processes it. Result means success.
// If failed, contextCall.err contains the reason.
// If failed, contextCall.err (or statusRes if resolveAPIFailures is set) contains the reason.
func (x *contextCall) readResponse() bool {
if x.rResp != nil {
x.err = x.rResp()
@ -276,14 +339,15 @@ func (x *contextCall) processCall() bool {
// initializes static cross-call parameters inherited from client.
func (c *Client) initCallContext(ctx *contextCall) {
ctx.signer = c.prm.signer
ctx.key = c.prm.key
ctx.resolveAPIFailures = c.prm.resolveFrostFSErrors
ctx.callbackResp = c.prm.cbRespInfo
ctx.netMagic = c.prm.netMagic
}
// ExecRaw executes f with underlying github.com/nspcc-dev/neofs-api-go/v2/rpc/client.Client
// ExecRaw executes f with underlying git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client.Client
// instance. Communicate over the Protocol Buffers protocol in a more flexible way:
// most often used to transmit data over a fixed version of the NeoFS protocol, as well
// most often used to transmit data over a fixed version of the FrostFS protocol, as well
// as to support custom services.
//
// The f must not manipulate the client connection passed into it.
@ -292,7 +356,7 @@ func (c *Client) initCallContext(ctx *contextCall) {
// before closing the connection.
//
// See also Dial and Close.
// See also github.com/nspcc-dev/neofs-api-go/v2/rpc/client package docs.
// See also git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client package docs.
func (c *Client) ExecRaw(f func(client *client.Client) error) error {
return f(&c.c)
}

View file

@ -5,17 +5,18 @@ import (
"errors"
"fmt"
v2container "github.com/nspcc-dev/neofs-api-go/v2/container"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-sdk-go/container"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/user"
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-sdk-go/container"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
)
// PrmContainerPut groups parameters of ContainerPut operation.
@ -27,24 +28,15 @@ type PrmContainerPut struct {
sessionSet bool
session session.Container
signer neofscrypto.Signer
}
// SetContainer sets structured information about new NeoFS container.
// SetContainer sets structured information about new FrostFS container.
// Required parameter.
func (x *PrmContainerPut) SetContainer(cnr container.Container) {
x.cnr = cnr
x.cnrSet = true
}
// SetSigner sets signer to sign request payload.
// Signer's scheme MUST be neofscrypto.ECDSA_DETERMINISTIC_SHA256. For example, you can use neofsecdsa.SignerRFC6979.
// Optional parameter: defaults to internal Client signer.
func (x *PrmContainerPut) SetSigner(signer neofscrypto.Signer) {
x.signer = signer
}
// WithinSession specifies session within which container should be saved.
//
// Creator of the session acquires the authorship of the request. This affects
@ -52,7 +44,7 @@ func (x *PrmContainerPut) SetSigner(signer neofscrypto.Signer) {
//
// Session is optional, if set the following requirements apply:
// - session operation MUST be session.VerbContainerPut (ForVerb)
// - token MUST be signed using private signer of the owner of the container to be saved
// - 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
@ -60,6 +52,8 @@ func (x *PrmContainerPut) WithinSession(s session.Container) {
// ResContainerPut groups resulting values of ContainerPut operation.
type ResContainerPut struct {
statusRes
id cid.ID
}
@ -70,43 +64,41 @@ func (x ResContainerPut) ID() cid.ID {
return x.id
}
func (c *Client) defaultSigner() neofscrypto.Signer {
return c.prm.signer
}
// ContainerPut sends request to save container in NeoFS.
// ContainerPut sends request to save container in FrostFS.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
// 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 errors:
// - [ErrMissingContainer]
// Return statuses:
// - global (see Client docs).
func (c *Client) ContainerPut(ctx context.Context, prm PrmContainerPut) (*ResContainerPut, error) {
// check parameters
switch {
case ctx == nil:
return nil, errorMissingContext
case !prm.cnrSet:
return nil, ErrMissingContainer
return nil, errorMissingContainer
}
// TODO: check private signer is set before forming the request
// TODO: check private key is set before forming the request
// sign container
var cnr v2container.Container
prm.cnr.WriteToV2(&cnr)
var sig neofscrypto.Signature
signer := prm.signer
if signer == nil {
signer = c.defaultSigner()
}
var sig frostfscrypto.Signature
err := container.CalculateSignature(&sig, prm.cnr, signer)
err := container.CalculateSignature(&sig, prm.cnr, c.prm.key)
if err != nil {
return nil, fmt.Errorf("calculate container signature: %w", err)
}
@ -146,6 +138,7 @@ func (c *Client) ContainerPut(ctx context.Context, prm PrmContainerPut) (*ResCon
c.initCallContext(&cc)
cc.req = &req
cc.statusRes = &res
cc.call = func() (responseV2, error) {
return rpcapi.PutContainer(&c.c, &req, client.WithContext(ctx))
}
@ -191,6 +184,8 @@ func (x *PrmContainerGet) SetContainer(id cid.ID) {
// ResContainerGet groups resulting values of ContainerGet operation.
type ResContainerGet struct {
statusRes
cnr container.Container
}
@ -201,19 +196,26 @@ func (x ResContainerGet) Container() container.Container {
return x.cnr
}
// ContainerGet reads NeoFS container by ID.
// ContainerGet reads FrostFS container by ID.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
// 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 errors:
// - [ErrMissingContainer]
// Return statuses:
// - global (see Client docs);
// - *apistatus.ContainerNotFound.
func (c *Client) ContainerGet(ctx context.Context, prm PrmContainerGet) (*ResContainerGet, error) {
switch {
case ctx == nil:
return nil, errorMissingContext
case !prm.idSet:
return nil, ErrMissingContainer
return nil, errorMissingContainer
}
var cidV2 refs.ContainerID
@ -238,6 +240,7 @@ func (c *Client) ContainerGet(ctx context.Context, prm PrmContainerGet) (*ResCon
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))
}
@ -272,7 +275,7 @@ type PrmContainerList struct {
ownerID user.ID
}
// SetAccount sets identifier of the NeoFS account to list the containers.
// SetAccount sets identifier of the FrostFS account to list the containers.
// Required parameter.
func (x *PrmContainerList) SetAccount(id user.ID) {
x.ownerID = id
@ -281,6 +284,8 @@ func (x *PrmContainerList) SetAccount(id user.ID) {
// ResContainerList groups resulting values of ContainerList operation.
type ResContainerList struct {
statusRes
ids []cid.ID
}
@ -293,18 +298,24 @@ func (x ResContainerList) Containers() []cid.ID {
// ContainerList requests identifiers of the account-owned containers.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
// 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 errors:
// - [ErrMissingAccount]
// Return statuses:
// - global (see Client docs).
func (c *Client) ContainerList(ctx context.Context, prm PrmContainerList) (*ResContainerList, error) {
// check parameters
switch {
case ctx == nil:
return nil, errorMissingContext
case !prm.ownerSet:
return nil, ErrMissingAccount
return nil, errorAccountNotSet
}
// form request body
@ -329,6 +340,7 @@ func (c *Client) ContainerList(ctx context.Context, prm PrmContainerList) (*ResC
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))
}
@ -363,24 +375,15 @@ type PrmContainerDelete struct {
tokSet bool
tok session.Container
signer neofscrypto.Signer
}
// SetContainer sets identifier of the NeoFS container to be removed.
// 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
}
// SetSigner sets signer to sign request payload.
// Signer's scheme MUST be neofscrypto.ECDSA_DETERMINISTIC_SHA256. For example, you can use neofsecdsa.SignerRFC6979.
// Optional parameter: defaults to internal Client signer.
func (x *PrmContainerDelete) SetSigner(signer neofscrypto.Signer) {
x.signer = signer
}
// WithinSession specifies session within which container should be removed.
//
// Creator of the session acquires the authorship of the request.
@ -392,28 +395,39 @@ func (x *PrmContainerDelete) WithinSession(tok session.Container) {
x.tokSet = true
}
// ContainerDelete sends request to remove the NeoFS container.
// ResContainerDelete groups resulting values of ContainerDelete operation.
type ResContainerDelete struct {
statusRes
}
// ContainerDelete sends request to remove the FrostFS container.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
// 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.
//
// Return errors:
// - [ErrMissingContainer]
// - [neofscrypto.ErrIncorrectSigner]
//
// 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.).
func (c *Client) ContainerDelete(ctx context.Context, prm PrmContainerDelete) error {
//
// Return statuses:
// - global (see Client docs).
func (c *Client) ContainerDelete(ctx context.Context, prm PrmContainerDelete) (*ResContainerDelete, error) {
// check parameters
switch {
case ctx == nil:
return nil, errorMissingContext
case !prm.idSet:
return ErrMissingContainer
return nil, errorMissingContainer
}
// sign container ID
@ -424,18 +438,11 @@ func (c *Client) ContainerDelete(ctx context.Context, prm PrmContainerDelete) er
// don't get confused with stable marshaled protobuf container.ID structure
data := cidV2.GetValue()
var sig neofscrypto.Signature
signer := prm.signer
if signer == nil {
signer = c.defaultSigner()
}
var sig frostfscrypto.Signature
if signer.Scheme() != neofscrypto.ECDSA_DETERMINISTIC_SHA256 {
return errNonNeoSigner
}
err := sig.Calculate(signer, data)
err := sig.Calculate(frostfsecdsa.SignerRFC6979(c.prm.key), data)
if err != nil {
return fmt.Errorf("calculate signature: %w", err)
return nil, fmt.Errorf("calculate signature: %w", err)
}
var sigv2 refs.Signature
@ -467,21 +474,23 @@ func (c *Client) ContainerDelete(ctx context.Context, prm PrmContainerDelete) er
// init call context
var (
cc contextCall
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 cc.err
return nil, cc.err
}
return nil
return &res, nil
}
// PrmContainerEACL groups parameters of ContainerEACL operation.
@ -492,7 +501,7 @@ type PrmContainerEACL struct {
id cid.ID
}
// SetContainer sets identifier of the NeoFS container to read the eACL table.
// SetContainer sets identifier of the FrostFS container to read the eACL table.
// Required parameter.
func (x *PrmContainerEACL) SetContainer(id cid.ID) {
x.id = id
@ -501,6 +510,8 @@ func (x *PrmContainerEACL) SetContainer(id cid.ID) {
// ResContainerEACL groups resulting values of ContainerEACL operation.
type ResContainerEACL struct {
statusRes
table eacl.Table
}
@ -509,20 +520,28 @@ func (x ResContainerEACL) Table() eacl.Table {
return x.table
}
// ContainerEACL reads eACL table of the NeoFS container.
// ContainerEACL reads eACL table of the FrostFS container.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
// 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 errors:
// - [ErrMissingContainer]
// 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:
return nil, errorMissingContext
case !prm.idSet:
return nil, ErrMissingContainer
return nil, errorMissingContainer
}
var cidV2 refs.ContainerID
@ -547,6 +566,7 @@ func (c *Client) ContainerEACL(ctx context.Context, prm PrmContainerEACL) (*ResC
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))
}
@ -579,24 +599,15 @@ type PrmContainerSetEACL struct {
sessionSet bool
session session.Container
signer neofscrypto.Signer
}
// SetTable sets eACL table structure to be set for the container.
// Required parameter and CID must be set inside the table.
// Required parameter.
func (x *PrmContainerSetEACL) SetTable(table eacl.Table) {
x.table = table
x.tableSet = true
}
// SetSigner sets signer to sign request payload.
// Signer's scheme MUST be neofscrypto.ECDSA_DETERMINISTIC_SHA256. For example, you can use neofsecdsa.SignerRFC6979.
// Optional parameter: defaults to internal Client signer.
func (x *PrmContainerSetEACL) SetSigner(signer neofscrypto.Signer) {
x.signer = signer
}
// WithinSession specifies session within which extended ACL of the container
// should be saved.
//
@ -607,56 +618,52 @@ func (x *PrmContainerSetEACL) SetSigner(signer neofscrypto.Signer) {
// - 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 signer of the owner of the container to be saved
// - 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
}
// ContainerSetEACL sends request to update eACL table of the NeoFS container.
// ResContainerSetEACL groups resulting values of ContainerSetEACL operation.
type ResContainerSetEACL struct {
statusRes
}
// ContainerSetEACL sends request to update eACL table of the FrostFS container.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
// 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).
//
// Return errors:
// - [ErrMissingEACL]
// - [ErrMissingEACLContainer]
// - [neofscrypto.ErrIncorrectSigner]
//
// 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.
func (c *Client) ContainerSetEACL(ctx context.Context, prm PrmContainerSetEACL) error {
//
// Return statuses:
// - global (see Client docs).
func (c *Client) ContainerSetEACL(ctx context.Context, prm PrmContainerSetEACL) (*ResContainerSetEACL, error) {
// check parameters
switch {
case ctx == nil:
return nil, errorMissingContext
case !prm.tableSet:
return ErrMissingEACL
}
_, isCIDSet := prm.table.CID()
if !isCIDSet {
return ErrMissingEACLContainer
return nil, errorEACLTableNotSet
}
// sign the eACL table
eaclV2 := prm.table.ToV2()
var sig neofscrypto.Signature
signer := prm.signer
if signer == nil {
signer = c.defaultSigner()
}
var sig frostfscrypto.Signature
if signer.Scheme() != neofscrypto.ECDSA_DETERMINISTIC_SHA256 {
return errNonNeoSigner
}
err := sig.Calculate(signer, eaclV2.StableMarshal(nil))
err := sig.Calculate(frostfsecdsa.SignerRFC6979(c.prm.key), eaclV2.StableMarshal(nil))
if err != nil {
return fmt.Errorf("calculate signature: %w", err)
return nil, fmt.Errorf("calculate signature: %w", err)
}
var sigv2 refs.Signature
@ -688,21 +695,23 @@ func (c *Client) ContainerSetEACL(ctx context.Context, prm PrmContainerSetEACL)
// init call context
var (
cc contextCall
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 cc.err
return nil, cc.err
}
return nil
return &res, nil
}
// PrmAnnounceSpace groups parameters of ContainerAnnounceUsedSpace operation.
@ -720,34 +729,45 @@ 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.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
// 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 errors:
// - [ErrMissingAnnouncements]
func (c *Client) ContainerAnnounceUsedSpace(ctx context.Context, prm PrmAnnounceSpace) error {
// Return statuses:
// - global (see Client docs).
func (c *Client) ContainerAnnounceUsedSpace(ctx context.Context, prm PrmAnnounceSpace) (*ResAnnounceSpace, error) {
// check parameters
switch {
case ctx == nil:
return nil, errorMissingContext
case len(prm.announcements) == 0:
return ErrMissingAnnouncements
return nil, errorMissingAnnouncements
}
// convert list of SDK announcement structures into NeoFS-API v2 list
// 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 NeoFS-API v2 request and request itself
// prepare body of the FrostFS-API v2 request and request itself
reqBody := new(v2container.AnnounceUsedSpaceRequestBody)
reqBody.SetAnnouncements(v2announce)
@ -759,22 +779,24 @@ func (c *Client) ContainerAnnounceUsedSpace(ctx context.Context, prm PrmAnnounce
// init call context
var (
cc contextCall
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 cc.err
return nil, cc.err
}
return nil
return &res, nil
}
// SyncContainerWithNetwork requests network configuration using passed client

View file

@ -1,28 +1,32 @@
/*
Package client provides NeoFS API client implementation.
Package client provides FrostFS API client implementation.
The main component is Client type. It is a virtual connection to the network
and provides methods for executing operations on the server.
Create client instance:
var c client.Client
Initialize client state:
var prm client.PrmInit
prm.SetDefaultSigner(signer)
prm.SetDefaultPrivateKey(key)
// ...
c, err := client.New(prm)
c.Init(prm)
Connect to the NeoFS server:
Connect to the FrostFS server:
var prm client.PrmDial
prm.SetServerURI("localhost:8080")
prm.SetDefaultSigner(signer)
prm.SetDefaultPrivateKey(key)
// ...
err := c.Dial(prm)
// ...
Execute NeoFS operation on the server:
Execute FrostFS operation on the server:
var prm client.PrmContainerPut
prm.SetContainer(cnr)
@ -43,8 +47,8 @@ Consume custom service of the server:
rpc CustomRPC(CustomRPCRequest) returns (CustomRPCResponse);
}
import "github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
import "github.com/nspcc-dev/neofs-api-go/v2/rpc/common"
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/common"
req := new(CustomRPCRequest)
// ...
@ -68,9 +72,9 @@ for the all operations are write-only and the results of the all operations are
read-only. To be able to override client behavior (e.g. for tests), abstract it
with an interface:
import "github.com/nspcc-dev/neofs-sdk-go/client"
import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
type NeoFSClient interface {
type FrostFSClient interface {
// Operations according to the application needs
CreateContainer(context.Context, container.Container) error
// ...

View file

@ -3,106 +3,104 @@ package client
import (
"errors"
"fmt"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
)
var (
// ErrMissingServer is returned when server endpoint is empty in parameters.
ErrMissingServer = errors.New("server address is unset or empty")
// ErrNonPositiveTimeout is returned when any timeout is below zero in parameters.
ErrNonPositiveTimeout = errors.New("non-positive timeout")
// unwraps err using errors.Unwrap and returns the result.
func unwrapErr(err error) error {
for e := errors.Unwrap(err); e != nil; e = errors.Unwrap(err) {
err = e
}
// ErrMissingContainer is returned when container is not provided.
ErrMissingContainer = errors.New("missing container")
// ErrMissingObject is returned when object is not provided.
ErrMissingObject = errors.New("missing object")
// ErrMissingAccount is returned when account/owner is not provided.
ErrMissingAccount = errors.New("missing account")
// ErrMissingSigner is returned when signer is not provided.
ErrMissingSigner = errors.New("missing signer")
// ErrMissingEACL is returned when eACL table is not provided.
ErrMissingEACL = errors.New("missing eACL table")
// ErrMissingEACLContainer is returned when container info is not provided in eACL table.
ErrMissingEACLContainer = errors.New("missing container in eACL table")
// ErrMissingAnnouncements is returned when announcements are not provided.
ErrMissingAnnouncements = errors.New("missing announcements")
// ErrZeroRangeLength is returned when range parameter has zero length.
ErrZeroRangeLength = errors.New("zero range length")
// ErrMissingRanges is returned when empty ranges list is provided.
ErrMissingRanges = errors.New("missing ranges")
// ErrZeroEpoch is returned when zero epoch is provided.
ErrZeroEpoch = errors.New("zero epoch")
// ErrMissingTrusts is returned when empty slice of trusts is provided.
ErrMissingTrusts = errors.New("missing trusts")
// ErrMissingTrust is returned when empty trust is not provided.
ErrMissingTrust = errors.New("missing trust")
// ErrUnexpectedReadCall is returned when we already got all data but truing to get more.
ErrUnexpectedReadCall = errors.New("unexpected call to `Read`")
// ErrSign is returned when unable to sign service message.
ErrSign SignError
// ErrMissingResponseField is returned when required field is not exists in NeoFS api response.
ErrMissingResponseField MissingResponseFieldErr
)
// MissingResponseFieldErr contains field name which should be in NeoFS API response.
type MissingResponseFieldErr struct {
name string
return err
}
// Error implements the error interface.
func (e MissingResponseFieldErr) Error() string {
return fmt.Sprintf("missing %s field in the response", e.name)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (e MissingResponseFieldErr) Is(target error) bool {
switch target.(type) {
// IsErrContainerNotFound checks if err corresponds to FrostFS status
// return corresponding to missing container. Supports wrapped errors.
func IsErrContainerNotFound(err error) bool {
switch unwrapErr(err).(type) {
default:
return false
case MissingResponseFieldErr, *MissingResponseFieldErr:
case
apistatus.ContainerNotFound,
*apistatus.ContainerNotFound:
return true
}
}
// IsErrEACLNotFound checks if err corresponds to FrostFS status
// return corresponding to missing eACL table. Supports wrapped errors.
func IsErrEACLNotFound(err error) bool {
switch unwrapErr(err).(type) {
default:
return false
case
apistatus.EACLNotFound,
*apistatus.EACLNotFound:
return true
}
}
// IsErrObjectNotFound checks if err corresponds to FrostFS status
// return corresponding to missing object. Supports wrapped errors.
func IsErrObjectNotFound(err error) bool {
switch unwrapErr(err).(type) {
default:
return false
case
apistatus.ObjectNotFound,
*apistatus.ObjectNotFound:
return true
}
}
// IsErrObjectAlreadyRemoved checks if err corresponds to FrostFS status
// return corresponding to already removed object. Supports wrapped errors.
func IsErrObjectAlreadyRemoved(err error) bool {
switch unwrapErr(err).(type) {
default:
return false
case
apistatus.ObjectAlreadyRemoved,
*apistatus.ObjectAlreadyRemoved:
return true
}
}
// IsErrSessionExpired checks if err corresponds to FrostFS status return
// corresponding to expired session. Supports wrapped errors.
func IsErrSessionExpired(err error) bool {
switch unwrapErr(err).(type) {
default:
return false
case
apistatus.SessionTokenExpired,
*apistatus.SessionTokenExpired:
return true
}
}
// IsErrSessionNotFound checks if err corresponds to FrostFS status return
// corresponding to missing session. Supports wrapped errors.
func IsErrSessionNotFound(err error) bool {
switch unwrapErr(err).(type) {
default:
return false
case
apistatus.SessionTokenNotFound,
*apistatus.SessionTokenNotFound:
return true
}
}
// returns error describing missing field with the given name.
func newErrMissingResponseField(name string) error {
return MissingResponseFieldErr{name: name}
return fmt.Errorf("missing %s field in the response", name)
}
// returns error describing invalid field (according to the NeoFS protocol)
// returns error describing invalid field (according to the FrostFS protocol)
// with the given name and format violation err.
func newErrInvalidResponseField(name string, err error) error {
return fmt.Errorf("invalid %s field in the response: %w", name, err)
}
// SignError wraps another error with reason why sign process was failed.
type SignError struct {
err error
}
// NewSignError is a constructor for [SignError].
func NewSignError(err error) SignError {
return SignError{err: err}
}
// Error implements the error interface.
func (e SignError) Error() string {
return fmt.Sprintf("sign: %v", e.err)
}
// Unwrap implements the error interface.
func (e SignError) Unwrap() error {
return e.err
}
// Is implements interface for correct checking current error type with [errors.Is].
func (e SignError) Is(target error) bool {
switch target.(type) {
default:
return false
case SignError, *SignError:
return true
}
}

View file

@ -1,17 +1,68 @@
package client_test
import (
"errors"
"fmt"
"testing"
"github.com/nspcc-dev/neofs-sdk-go/client"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"github.com/stretchr/testify/require"
)
func Test_SignError(t *testing.T) {
someErr := errors.New("some error")
signErr := client.NewSignError(someErr)
func TestErrors(t *testing.T) {
for _, tc := range []struct {
check func(error) bool
errs []error
}{
{
check: client.IsErrContainerNotFound,
errs: []error{
apistatus.ContainerNotFound{},
new(apistatus.ContainerNotFound),
},
},
{
check: client.IsErrEACLNotFound,
errs: []error{
apistatus.EACLNotFound{},
new(apistatus.EACLNotFound),
},
},
{
check: client.IsErrObjectNotFound,
errs: []error{
apistatus.ObjectNotFound{},
new(apistatus.ObjectNotFound),
},
},
{
check: client.IsErrObjectAlreadyRemoved,
errs: []error{
apistatus.ObjectAlreadyRemoved{},
new(apistatus.ObjectAlreadyRemoved),
},
},
{
check: client.IsErrSessionExpired,
errs: []error{
apistatus.SessionTokenExpired{},
new(apistatus.SessionTokenExpired),
},
}, {
check: client.IsErrSessionNotFound,
errs: []error{
apistatus.SessionTokenNotFound{},
new(apistatus.SessionTokenNotFound),
},
},
} {
require.NotEmpty(t, tc.errs)
require.ErrorIs(t, signErr, someErr)
require.ErrorIs(t, signErr, client.ErrSign)
for i := range tc.errs {
require.True(t, tc.check(tc.errs[i]), tc.errs[i])
require.True(t, tc.check(fmt.Errorf("top-level context: :%w",
fmt.Errorf("inner context: %w", tc.errs[i])),
), tc.errs[i])
}
}
}

View file

@ -1,118 +0,0 @@
package client_test
import (
"context"
"fmt"
"time"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
netmapv2 "github.com/nspcc-dev/neofs-api-go/v2/netmap"
"github.com/nspcc-dev/neofs-sdk-go/client"
"github.com/nspcc-dev/neofs-sdk-go/container"
"github.com/nspcc-dev/neofs-sdk-go/container/acl"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/user"
)
func ExampleClient_ContainerPut() {
ctx := context.Background()
var accountID user.ID
// The account was taken from https://github.com/nspcc-dev/neofs-aio
key, err := keys.NEP2Decrypt("6PYM8VdX2BSm7BSXKzV4Fz6S3R9cDLLWNrD9nMjxW352jEv3fsC8N3wNLY", "one", keys.NEP2ScryptParams())
if err != nil {
panic(err)
}
signer := neofsecdsa.SignerRFC6979(key.PrivateKey)
// decode account from user's signer
if err = user.IDFromSigner(&accountID, signer); err != nil {
panic(err)
}
// prepare client
var prmInit client.PrmInit
prmInit.SetDefaultSigner(signer) // private signer for request signing
c, err := client.New(prmInit)
if err != nil {
panic(fmt.Errorf("New: %w", err))
}
// connect to NeoFS gateway
var prmDial client.PrmDial
prmDial.SetServerURI("grpc://localhost:8080") // endpoint address
prmDial.SetTimeout(15 * time.Second)
prmDial.SetStreamTimeout(15 * time.Second)
if err = c.Dial(prmDial); err != nil {
panic(fmt.Errorf("dial %v", err))
}
// describe new container
cont := container.Container{}
// set version and nonce
cont.Init()
cont.SetOwner(accountID)
cont.SetBasicACL(acl.PublicRW)
// set reserved attributes
container.SetName(&cont, "name-1")
container.SetCreationTime(&cont, time.Now().UTC())
// init placement policy
var containerID cid.ID
var placementPolicyV2 netmapv2.PlacementPolicy
var replicas []netmapv2.Replica
replica := netmapv2.Replica{}
replica.SetCount(1)
replicas = append(replicas, replica)
placementPolicyV2.SetReplicas(replicas)
var placementPolicy netmap.PlacementPolicy
if err = placementPolicy.ReadFromV2(placementPolicyV2); err != nil {
panic(fmt.Errorf("ReadFromV2 %w", err))
}
placementPolicy.SetContainerBackupFactor(1)
cont.SetPlacementPolicy(placementPolicy)
// prepare command to create container
var prmCon client.PrmContainerPut
prmCon.SetContainer(cont)
putResult, err := c.ContainerPut(ctx, prmCon)
if err != nil {
panic(fmt.Errorf("ContainerPut %w", err))
}
// containerID already exists
containerID = putResult.ID()
fmt.Println(containerID)
// example output: 76wa5UNiT8gk8Q5rdCVCV4pKuZSmYsifh6g84BcL6Hqs
// but container creation is async operation. We should wait some time or make polling to ensure container created
// for simplifying we just wait
<-time.After(2 * time.Second)
// take our created container
var cmdGet client.PrmContainerGet
cmdGet.SetContainer(containerID)
getResp, err := c.ContainerGet(ctx, cmdGet)
if err != nil {
panic(fmt.Errorf("ContainerGet %w", err))
}
jsonData, err := getResp.Container().MarshalJSON()
if err != nil {
panic(fmt.Errorf("MarshalJSON %w", err))
}
fmt.Println(string(jsonData))
// example output: {"version":{"major":2,"minor":13},"ownerID":{"value":"Ne6eoiwn40vQFI/EEI4I906PUEiy8ZXKcw=="},"nonce":"rPVd/iw2RW6Q6d66FVnIqg==","basicACL":532660223,"attributes":[{"key":"Name","value":"name-1"},{"key":"Timestamp","value":"1681738627"}],"placementPolicy":{"replicas":[{"count":1,"selector":""}],"containerBackupFactor":1,"selectors":[],"filters":[],"subnetId":{"value":0}}}
}

View file

@ -4,12 +4,14 @@ import (
"context"
"fmt"
v2netmap "github.com/nspcc-dev/neofs-api-go/v2/netmap"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/version"
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
)
// PrmEndpointInfo groups parameters of EndpointInfo operation.
@ -19,17 +21,19 @@ type PrmEndpointInfo struct {
// ResEndpointInfo group resulting values of EndpointInfo operation.
type ResEndpointInfo struct {
statusRes
version version.Version
ni netmap.NodeInfo
}
// LatestVersion returns latest NeoFS API protocol's version in use.
// LatestVersion returns latest FrostFS API protocol's version in use.
func (x ResEndpointInfo) LatestVersion() version.Version {
return x.version
}
// NodeInfo returns information about the NeoFS node served on the remote endpoint.
// NodeInfo returns information about the FrostFS node served on the remote endpoint.
func (x ResEndpointInfo) NodeInfo() netmap.NodeInfo {
return x.ni
}
@ -38,14 +42,25 @@ func (x ResEndpointInfo) NodeInfo() netmap.NodeInfo {
//
// Method can be used as a health check to see if node is alive and responds to requests.
//
// Any client's internal or transport errors are returned as `error`,
// see [apistatus] package for NeoFS-specific error types.
// 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 PrmEndpointInfo docs).
// Context is required and must not be nil. It is used for network communication.
//
// Exactly one return value is non-nil. Server status return is returned in ResEndpointInfo.
// Reflects all internal errors in second return value (transport problems, response processing, etc.).
//
// Return statuses:
// - global (see Client docs).
func (c *Client) EndpointInfo(ctx context.Context, prm PrmEndpointInfo) (*ResEndpointInfo, error) {
// check context
if ctx == nil {
return nil, errorMissingContext
}
// form request
var req v2netmap.LocalNodeInfoRequest
@ -59,6 +74,7 @@ func (c *Client) EndpointInfo(ctx context.Context, prm PrmEndpointInfo) (*ResEnd
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))
}
@ -111,24 +127,37 @@ type PrmNetworkInfo struct {
// ResNetworkInfo groups resulting values of NetworkInfo operation.
type ResNetworkInfo struct {
statusRes
info netmap.NetworkInfo
}
// Info returns structured information about the NeoFS network.
// Info returns structured information about the FrostFS network.
func (x ResNetworkInfo) Info() netmap.NetworkInfo {
return x.info
}
// NetworkInfo requests information about the NeoFS network of which the remote server is a part.
// NetworkInfo requests information about the FrostFS network of which the remote server is a part.
//
// Any client's internal or transport errors are returned as `error`,
// see [apistatus] package for NeoFS-specific error types.
// 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 PrmNetworkInfo docs).
// Context is required and must not be nil. It is used for network communication.
//
// Exactly one return value is non-nil. Server status return is returned in ResNetworkInfo.
// Reflects all internal errors in second return value (transport problems, response processing, etc.).
//
// Return statuses:
// - global (see Client docs).
func (c *Client) NetworkInfo(ctx context.Context, prm PrmNetworkInfo) (*ResNetworkInfo, error) {
// check context
if ctx == nil {
return nil, errorMissingContext
}
// form request
var req v2netmap.NetworkInfoRequest
@ -142,6 +171,7 @@ func (c *Client) NetworkInfo(ctx context.Context, prm PrmNetworkInfo) (*ResNetwo
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))
}
@ -177,6 +207,8 @@ type PrmNetMapSnapshot struct {
// ResNetMapSnapshot groups resulting values of NetMapSnapshot operation.
type ResNetMapSnapshot struct {
statusRes
netMap netmap.NetMap
}
@ -187,14 +219,25 @@ func (x ResNetMapSnapshot) NetMap() netmap.NetMap {
// NetMapSnapshot requests current network view of the remote server.
//
// Any client's internal or transport errors are returned as `error`,
// see [apistatus] package for NeoFS-specific error types.
// 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.
// 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.
// Reflects all internal errors in second return value (transport problems, response processing, etc.).
//
// Return statuses:
// - global (see Client docs).
func (c *Client) NetMapSnapshot(ctx context.Context, _ PrmNetMapSnapshot) (*ResNetMapSnapshot, error) {
// check context
if ctx == nil {
return nil, errorMissingContext
}
// form request body
var body v2netmap.SnapshotRequestBody
@ -206,7 +249,7 @@ func (c *Client) NetMapSnapshot(ctx context.Context, _ PrmNetMapSnapshot) (*ResN
req.SetBody(&body)
c.prepareRequest(&req, &meta)
err := signServiceMessage(c.prm.signer, &req)
err := signature.SignServiceMessage(&c.prm.key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}
@ -217,10 +260,15 @@ func (c *Client) NetMapSnapshot(ctx context.Context, _ PrmNetMapSnapshot) (*ResN
}
var res ResNetMapSnapshot
if err = c.processResponse(resp); err != nil {
res.st, err = c.processResponse(resp)
if err != nil {
return nil, err
}
if !apistatus.IsSuccessful(res.st) {
return &res, nil
}
const fieldNetMap = "network map"
netMapV2 := resp.GetBody().NetMap()

View file

@ -6,13 +6,11 @@ import (
"fmt"
"testing"
v2netmap "github.com/nspcc-dev/neofs-api-go/v2/netmap"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
"github.com/nspcc-dev/neofs-api-go/v2/session"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/crypto/test"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"github.com/stretchr/testify/require"
)
@ -25,16 +23,10 @@ type serverNetMap struct {
setNetMap bool
netMap v2netmap.NetMap
signer neofscrypto.Signer
}
func (x *serverNetMap) createSession(*client.Client, *session.CreateRequest, ...client.CallOption) (*session.CreateResponse, error) {
return nil, nil
}
func (x *serverNetMap) netMapSnapshot(_ context.Context, req v2netmap.SnapshotRequest) (*v2netmap.SnapshotResponse, error) {
err := verifyServiceMessage(&req)
func (x *serverNetMap) netMapSnapshot(ctx context.Context, req v2netmap.SnapshotRequest) (*v2netmap.SnapshotResponse, error) {
err := signature.VerifyServiceMessage(&req)
if err != nil {
return nil, err
}
@ -52,7 +44,7 @@ func (x *serverNetMap) netMapSnapshot(_ context.Context, req v2netmap.SnapshotRe
var meta session.ResponseMetaHeader
if !x.statusOK {
meta.SetStatus(statusErr.ErrorToV2())
meta.SetStatus(statusErr.ToStatusV2())
}
var resp v2netmap.SnapshotResponse
@ -60,7 +52,7 @@ func (x *serverNetMap) netMapSnapshot(_ context.Context, req v2netmap.SnapshotRe
resp.SetMetaHeader(&meta)
if x.signResponse {
err = signServiceMessage(x.signer, &resp)
err = signature.SignServiceMessage(key, &resp)
if err != nil {
panic(fmt.Sprintf("sign response: %v", err))
}
@ -74,14 +66,14 @@ func TestClient_NetMapSnapshot(t *testing.T) {
var prm PrmNetMapSnapshot
var res *ResNetMapSnapshot
var srv serverNetMap
signer := test.RandomSignerRFC6979(t)
srv.signer = signer
c := newClient(t, signer, &srv)
c := newClient(&srv)
ctx := context.Background()
// missing context
//nolint:staticcheck
_, err = c.NetMapSnapshot(nil, prm)
require.ErrorIs(t, err, errorMissingContext, "")
// request signature
srv.errTransport = errors.New("any error")
@ -96,10 +88,10 @@ func TestClient_NetMapSnapshot(t *testing.T) {
srv.signResponse = true
// failure error
_, err = c.NetMapSnapshot(ctx, prm)
require.Error(t, err)
require.ErrorIs(t, err, apistatus.ErrServerInternal)
// status failure
res, err = c.NetMapSnapshot(ctx, prm)
require.NoError(t, err)
assertStatusErr(t, res)
srv.statusOK = true
@ -139,5 +131,6 @@ func TestClient_NetMapSnapshot(t *testing.T) {
res, err = c.NetMapSnapshot(ctx, prm)
require.NoError(t, err)
require.True(t, apistatus.IsSuccessful(res.Status()))
require.Equal(t, netMap, res.NetMap())
}

View file

@ -2,19 +2,21 @@ package client
import (
"context"
"crypto/ecdsa"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/acl"
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
)
// PrmObjectDelete groups parameters of ObjectDelete operation.
@ -26,7 +28,7 @@ type PrmObjectDelete struct {
addr v2refs.Address
keySet bool
signer neofscrypto.Signer
key ecdsa.PrivateKey
}
// WithinSession specifies session within which object should be read.
@ -53,8 +55,8 @@ func (x *PrmObjectDelete) WithBearerToken(t bearer.Token) {
x.meta.SetBearerToken(&v2token)
}
// FromContainer specifies NeoFS container of the object.
// Required parameter. It is an alternative to [PrmObjectDelete.ByAddress].
// FromContainer specifies FrostFS container of the object.
// Required parameter.
func (x *PrmObjectDelete) FromContainer(id cid.ID) {
var cidV2 v2refs.ContainerID
id.WriteToV2(&cidV2)
@ -63,7 +65,7 @@ func (x *PrmObjectDelete) FromContainer(id cid.ID) {
}
// ByID specifies identifier of the requested object.
// Required parameter. It is an alternative to [PrmObjectDelete.ByAddress].
// Required parameter.
func (x *PrmObjectDelete) ByID(id oid.ID) {
var idV2 v2refs.ObjectID
id.WriteToV2(&idV2)
@ -71,17 +73,11 @@ func (x *PrmObjectDelete) ByID(id oid.ID) {
x.addr.SetObjectID(&idV2)
}
// ByAddress specifies address of the requested object.
// Required parameter. It is an alternative to [PrmObjectDelete.ByID], [PrmObjectDelete.FromContainer].
func (x *PrmObjectDelete) ByAddress(addr oid.Address) {
addr.WriteToV2(&x.addr)
}
// UseSigner specifies private signer to sign the requests.
// If signer is not provided, then Client default signer is used.
func (x *PrmObjectDelete) UseSigner(signer neofscrypto.Signer) {
// UseKey specifies private key to sign the requests.
// If key is not provided, then Client default key is used.
func (x *PrmObjectDelete) UseKey(key ecdsa.PrivateKey) {
x.keySet = true
x.signer = signer
x.key = key
}
// WithXHeaders specifies list of extended headers (string key-value pairs)
@ -94,6 +90,8 @@ func (x *PrmObjectDelete) WithXHeaders(hs ...string) {
// ResObjectDelete groups resulting values of ObjectDelete operation.
type ResObjectDelete struct {
statusRes
tomb oid.ID
}
@ -102,7 +100,7 @@ func (x ResObjectDelete) Tombstone() oid.ID {
return x.tomb
}
// ObjectDelete marks an object for deletion from the container using NeoFS API protocol.
// ObjectDelete marks an object for deletion from the container using FrostFS API protocol.
// As a marker, a special unit called a tombstone is placed in the container.
// It confirms the user's intent to delete the object, and is itself a container object.
// Explicit deletion is done asynchronously, and is generally not guaranteed.
@ -112,24 +110,27 @@ func (x ResObjectDelete) Tombstone() oid.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`,
// see [apistatus] package for NeoFS-specific error types.
// 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 PrmObjectDelete docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// Return statuses:
// - global (see Client docs)
// - [ErrMissingContainer];
// - [ErrMissingObject];
// - [apistatus.ErrContainerNotFound];
// - [apistatus.ErrObjectAccessDenied];
// - [apistatus.ErrObjectLocked];
// - [apistatus.ErrSessionTokenExpired].
// - *apistatus.ContainerNotFound;
// - *apistatus.ObjectAccessDenied;
// - *apistatus.ObjectLocked;
// - *apistatus.SessionTokenExpired.
func (c *Client) ObjectDelete(ctx context.Context, prm PrmObjectDelete) (*ResObjectDelete, error) {
switch {
case ctx == nil:
return nil, errorMissingContext
case prm.addr.GetContainerID() == nil:
return nil, ErrMissingContainer
return nil, errorMissingContainer
case prm.addr.GetObjectID() == nil:
return nil, ErrMissingObject
return nil, errorMissingObject
}
// form request body
@ -140,12 +141,12 @@ func (c *Client) ObjectDelete(ctx context.Context, prm PrmObjectDelete) (*ResObj
req.SetBody(&prm.body)
c.prepareRequest(&req, &prm.meta)
signer := prm.signer
if signer == nil {
signer = c.prm.signer
key := c.prm.key
if prm.keySet {
key = prm.key
}
err := signServiceMessage(signer, &req)
err := signature.SignServiceMessage(&key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}
@ -156,10 +157,15 @@ func (c *Client) ObjectDelete(ctx context.Context, prm PrmObjectDelete) (*ResObj
}
var res ResObjectDelete
if err = c.processResponse(resp); err != nil {
res.st, err = c.processResponse(resp)
if err != nil {
return nil, err
}
if !apistatus.IsSuccessful(res.st) {
return &res, nil
}
const fieldTombstone = "tombstone"
idTombV2 := resp.GetBody().GetTombstone().GetObjectID()

View file

@ -1,73 +0,0 @@
package client
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"testing"
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/stretchr/testify/require"
)
func randOID(t *testing.T) oid.ID {
var id oid.ID
id.SetSHA256(randSHA256Checksum(t))
return id
}
func randCID(t *testing.T) cid.ID {
var id cid.ID
id.SetSHA256(randSHA256Checksum(t))
return id
}
func randSHA256Checksum(t *testing.T) (cs [sha256.Size]byte) {
_, err := rand.Read(cs[:])
require.NoError(t, err)
return
}
func TestPrmObjectDelete_ByAddress(t *testing.T) {
var prm PrmObjectDelete
var (
objID oid.ID
contID cid.ID
oidV2 v2refs.ObjectID
cidV2 v2refs.ContainerID
)
t.Run("ByID", func(t *testing.T) {
objID = randOID(t)
prm.ByID(objID)
objID.WriteToV2(&oidV2)
require.True(t, bytes.Equal(oidV2.GetValue(), prm.addr.GetObjectID().GetValue()))
})
t.Run("FromContainer", func(t *testing.T) {
contID = randCID(t)
prm.FromContainer(contID)
contID.WriteToV2(&cidV2)
require.True(t, bytes.Equal(cidV2.GetValue(), prm.addr.GetContainerID().GetValue()))
})
t.Run("ByAddress", func(t *testing.T) {
var addr oid.Address
addr.SetObject(objID)
addr.SetContainer(contID)
prm.ByAddress(addr)
require.True(t, bytes.Equal(oidV2.GetValue(), prm.addr.GetObjectID().GetValue()))
require.True(t, bytes.Equal(cidV2.GetValue(), prm.addr.GetContainerID().GetValue()))
})
}

View file

@ -2,22 +2,24 @@ package client
import (
"context"
"crypto/ecdsa"
"errors"
"fmt"
"io"
"github.com/nspcc-dev/neofs-api-go/v2/acl"
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
)
// shared parameters of GET/HEAD/RANGE.
@ -70,8 +72,8 @@ func (x *prmObjectRead) WithBearerToken(t bearer.Token) {
x.meta.SetBearerToken(&v2token)
}
// FromContainer specifies NeoFS container of the object.
// Required parameter. It is an alternative to ByAddress.
// FromContainer specifies FrostFS container of the object.
// Required parameter.
func (x *prmObjectRead) FromContainer(id cid.ID) {
var cnrV2 v2refs.ContainerID
id.WriteToV2(&cnrV2)
@ -79,27 +81,26 @@ func (x *prmObjectRead) FromContainer(id cid.ID) {
}
// ByID specifies identifier of the requested object.
// Required parameter. It is an alternative to ByAddress.
// Required parameter.
func (x *prmObjectRead) ByID(id oid.ID) {
var objV2 v2refs.ObjectID
id.WriteToV2(&objV2)
x.addr.SetObjectID(&objV2)
}
// ByAddress specifies address of the requested object.
// Required parameter. It is an alternative to ByID, FromContainer.
func (x *prmObjectRead) ByAddress(addr oid.Address) {
addr.WriteToV2(&x.addr)
}
// PrmObjectGet groups parameters of ObjectGetInit operation.
type PrmObjectGet struct {
prmObjectRead
signer neofscrypto.Signer
key *ecdsa.PrivateKey
}
// ObjectReader is designed to read one object from NeoFS system.
// ResObjectGet groups the final result values of ObjectGetInit operation.
type ResObjectGet struct {
statusRes
}
// ObjectReader is designed to read one object from FrostFS system.
//
// Must be initialized using Client.ObjectGetInit, any other
// usage is unsafe.
@ -111,6 +112,7 @@ type ObjectReader struct {
Read(resp *v2object.GetResponse) error
}
res ResObjectGet
err error
tailPayload []byte
@ -118,10 +120,10 @@ type ObjectReader struct {
remainingPayloadLen int
}
// UseSigner specifies private signer to sign the requests.
// If signer is not provided, then Client default signer is used.
func (x *PrmObjectGet) UseSigner(signer neofscrypto.Signer) {
x.signer = signer
// UseKey specifies private key to sign the requests.
// If key is not provided, then Client default key is used.
func (x *PrmObjectGet) UseKey(key ecdsa.PrivateKey) {
x.key = &key
}
// ReadHeader reads header of the object. Result means success.
@ -133,8 +135,8 @@ func (x *ObjectReader) ReadHeader(dst *object.Object) bool {
return false
}
x.err = x.client.processResponse(&resp)
if x.err != nil {
x.res.st, x.err = x.client.processResponse(&resp)
if x.err != nil || !apistatus.IsSuccessful(x.res.st) {
return false
}
@ -186,8 +188,8 @@ func (x *ObjectReader) readChunk(buf []byte) (int, bool) {
return read, false
}
x.err = x.client.processResponse(&resp)
if x.err != nil {
x.res.st, x.err = x.client.processResponse(&resp)
if x.err != nil || !apistatus.IsSuccessful(x.res.st) {
return read, false
}
@ -226,40 +228,44 @@ func (x *ObjectReader) ReadChunk(buf []byte) (int, bool) {
return x.readChunk(buf)
}
func (x *ObjectReader) close(ignoreEOF bool) error {
func (x *ObjectReader) close(ignoreEOF bool) (*ResObjectGet, error) {
defer x.cancelCtxStream()
if x.err != nil {
if !errors.Is(x.err, io.EOF) {
return x.err
return nil, x.err
} else if !ignoreEOF {
if x.remainingPayloadLen > 0 {
return io.ErrUnexpectedEOF
return nil, io.ErrUnexpectedEOF
}
return io.EOF
return nil, io.EOF
}
}
return nil
return &x.res, nil
}
// Close ends reading the object and returns the result of the operation
// along with the final results. Must be called after using the ObjectReader.
//
// 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 NeoFS API statuses, then NeoFS failures
// If Client is tuned to resolve FrostFS API statuses, then FrostFS failures
// codes are returned as error.
//
// Return errors:
//
// *object.SplitInfoError (returned on virtual objects with PrmObjectGet.MakeRaw).
//
// Return statuses:
// - global (see Client docs);
// - *[object.SplitInfoError] (returned on virtual objects with PrmObjectGet.MakeRaw).
// - [apistatus.ErrContainerNotFound];
// - [apistatus.ErrObjectNotFound];
// - [apistatus.ErrObjectAccessDenied];
// - [apistatus.ErrObjectAlreadyRemoved];
// - [apistatus.ErrSessionTokenExpired].
func (x *ObjectReader) Close() error {
// - *apistatus.ContainerNotFound;
// - *apistatus.ObjectNotFound;
// - *apistatus.ObjectAccessDenied;
// - *apistatus.ObjectAlreadyRemoved;
// - *apistatus.SessionTokenExpired.
func (x *ObjectReader) Close() (*ResObjectGet, error) {
return x.close(true)
}
@ -270,11 +276,12 @@ func (x *ObjectReader) Read(p []byte) (int, error) {
x.remainingPayloadLen -= n
if !ok {
if err := x.close(false); err != nil {
res, err := x.close(false)
if err != nil {
return n, err
}
return n, x.err
return n, apistatus.ErrFromStatus(res.Status())
}
if x.remainingPayloadLen < 0 {
@ -284,23 +291,22 @@ func (x *ObjectReader) Read(p []byte) (int, error) {
return n, nil
}
// ObjectGetInit initiates reading an object through a remote server using NeoFS API protocol.
// ObjectGetInit initiates reading an object through a remote server using FrostFS API protocol.
//
// 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.
//
// Returns an error if parameters are set incorrectly (see PrmObjectGet docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingContainer]
// - [ErrMissingObject]
func (c *Client) ObjectGetInit(ctx context.Context, prm PrmObjectGet) (*ObjectReader, error) {
// check parameters
switch {
case ctx == nil:
return nil, errorMissingContext
case prm.addr.GetContainerID() == nil:
return nil, ErrMissingContainer
return nil, errorMissingContainer
case prm.addr.GetObjectID() == nil:
return nil, ErrMissingObject
return nil, errorMissingObject
}
// form request body
@ -315,12 +321,12 @@ func (c *Client) ObjectGetInit(ctx context.Context, prm PrmObjectGet) (*ObjectRe
req.SetBody(&body)
c.prepareRequest(&req, &prm.meta)
signer := prm.signer
if signer == nil {
signer = c.prm.signer
key := prm.key
if key == nil {
key = &c.prm.key
}
err := signServiceMessage(signer, &req)
err := signature.SignServiceMessage(key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}
@ -345,17 +351,21 @@ func (c *Client) ObjectGetInit(ctx context.Context, prm PrmObjectGet) (*ObjectRe
type PrmObjectHead struct {
prmObjectRead
signer neofscrypto.Signer
keySet bool
key ecdsa.PrivateKey
}
// UseSigner specifies private signer to sign the requests.
// If signer is not provided, then Client default signer is used.
func (x *PrmObjectHead) UseSigner(signer neofscrypto.Signer) {
x.signer = signer
// UseKey specifies private key to sign the requests.
// If key is not provided, then Client default key is used.
func (x *PrmObjectHead) UseKey(key ecdsa.PrivateKey) {
x.keySet = true
x.key = key
}
// ResObjectHead groups resulting values of ObjectHead operation.
type ResObjectHead struct {
statusRes
// requested object (response doesn't carry the ID)
idObj oid.ID
@ -382,30 +392,36 @@ func (x *ResObjectHead) ReadHeader(dst *object.Object) bool {
return true
}
// ObjectHead reads object header through a remote server using NeoFS API protocol.
// ObjectHead reads object header through a remote server using FrostFS API protocol.
//
// 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`,
// see [apistatus] package for NeoFS-specific error types.
// 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 PrmObjectHead docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
//
// *object.SplitInfoError (returned on virtual objects with PrmObjectHead.MakeRaw).
//
// Return statuses:
// - global (see Client docs);
// - [ErrMissingContainer];
// - [ErrMissingObject];
// - *[object.SplitInfoError] (returned on virtual objects with PrmObjectHead.MakeRaw).
// - [apistatus.ErrContainerNotFound];
// - [apistatus.ErrObjectNotFound];
// - [apistatus.ErrObjectAccessDenied];
// - [apistatus.ErrObjectAlreadyRemoved];
// - [apistatus.ErrSessionTokenExpired].
// - *apistatus.ContainerNotFound;
// - *apistatus.ObjectNotFound;
// - *apistatus.ObjectAccessDenied;
// - *apistatus.ObjectAlreadyRemoved;
// - *apistatus.SessionTokenExpired.
func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectHead, error) {
switch {
case ctx == nil:
return nil, errorMissingContext
case prm.addr.GetContainerID() == nil:
return nil, ErrMissingContainer
return nil, errorMissingContainer
case prm.addr.GetObjectID() == nil:
return nil, ErrMissingObject
return nil, errorMissingObject
}
var body v2object.HeadRequestBody
@ -416,13 +432,13 @@ func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectH
req.SetBody(&body)
c.prepareRequest(&req, &prm.meta)
signer := prm.signer
if signer == nil {
signer = c.prm.signer
key := c.prm.key
if prm.keySet {
key = prm.key
}
// sign the request
err := signServiceMessage(signer, &req)
err := signature.SignServiceMessage(&key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}
@ -433,10 +449,15 @@ func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectH
}
var res ResObjectHead
if err = c.processResponse(resp); err != nil {
res.st, err = c.processResponse(resp)
if err != nil {
return nil, err
}
if !apistatus.IsSuccessful(res.st) {
return &res, nil
}
_ = res.idObj.ReadFromV2(*prm.addr.GetObjectID())
switch v := resp.GetBody().GetHeaderPart().(type) {
@ -455,37 +476,36 @@ func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectH
type PrmObjectRange struct {
prmObjectRead
signer neofscrypto.Signer
key *ecdsa.PrivateKey
rng v2object.Range
}
// SetOffset sets offset of the payload range to be read.
// Zero by default. It is an alternative to [PrmObjectRange.SetRange].
// Zero by default.
func (x *PrmObjectRange) SetOffset(off uint64) {
x.rng.SetOffset(off)
}
// SetLength sets length of the payload range to be read.
// Must be positive. It is an alternative to [PrmObjectRange.SetRange].
// Must be positive.
func (x *PrmObjectRange) SetLength(ln uint64) {
x.rng.SetLength(ln)
}
// SetRange sets range of the payload to be read.
// It is an alternative to [PrmObjectRange.SetOffset], [PrmObjectRange.SetLength].
func (x *PrmObjectRange) SetRange(rng object.Range) {
x.rng = *rng.ToV2()
// UseKey specifies private key to sign the requests.
// If key is not provided, then Client default key is used.
func (x *PrmObjectRange) UseKey(key ecdsa.PrivateKey) {
x.key = &key
}
// UseSigner specifies private signer to sign the requests.
// If signer is not provided, then Client default signer is used.
func (x *PrmObjectRange) UseSigner(signer neofscrypto.Signer) {
x.signer = signer
// ResObjectRange groups the final result values of ObjectRange operation.
type ResObjectRange struct {
statusRes
}
// ObjectRangeReader is designed to read payload range of one object
// from NeoFS system.
// from FrostFS system.
//
// Must be initialized using Client.ObjectRangeInit, any other
// usage is unsafe.
@ -494,6 +514,7 @@ type ObjectRangeReader struct {
client *Client
res ResObjectRange
err error
stream interface {
@ -528,8 +549,8 @@ func (x *ObjectRangeReader) readChunk(buf []byte) (int, bool) {
return read, false
}
x.err = x.client.processResponse(&resp)
if x.err != nil {
x.res.st, x.err = x.client.processResponse(&resp)
if x.err != nil || !apistatus.IsSuccessful(x.res.st) {
return read, false
}
@ -572,41 +593,45 @@ func (x *ObjectRangeReader) ReadChunk(buf []byte) (int, bool) {
return x.readChunk(buf)
}
func (x *ObjectRangeReader) close(ignoreEOF bool) error {
func (x *ObjectRangeReader) close(ignoreEOF bool) (*ResObjectRange, error) {
defer x.cancelCtxStream()
if x.err != nil {
if !errors.Is(x.err, io.EOF) {
return x.err
return nil, x.err
} else if !ignoreEOF {
if x.remainingPayloadLen > 0 {
return io.ErrUnexpectedEOF
return nil, io.ErrUnexpectedEOF
}
return io.EOF
return nil, io.EOF
}
}
return nil
return &x.res, nil
}
// Close ends reading the payload range and returns the result of the operation
// along with the final results. Must be called after using the ObjectRangeReader.
//
// 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 NeoFS API statuses, then NeoFS failures
// If Client is tuned to resolve FrostFS API statuses, then FrostFS failures
// codes are returned as error.
//
// Return errors:
//
// *object.SplitInfoError (returned on virtual objects with PrmObjectRange.MakeRaw).
//
// Return statuses:
// - global (see Client docs);
// - *[object.SplitInfoError] (returned on virtual objects with PrmObjectRange.MakeRaw).
// - [apistatus.ErrContainerNotFound];
// - [apistatus.ErrObjectNotFound];
// - [apistatus.ErrObjectAccessDenied];
// - [apistatus.ErrObjectAlreadyRemoved];
// - [apistatus.ErrObjectOutOfRange];
// - [apistatus.ErrSessionTokenExpired].
func (x *ObjectRangeReader) Close() error {
// - *apistatus.ContainerNotFound;
// - *apistatus.ObjectNotFound;
// - *apistatus.ObjectAccessDenied;
// - *apistatus.ObjectAlreadyRemoved;
// - *apistatus.ObjectOutOfRange;
// - *apistatus.SessionTokenExpired.
func (x *ObjectRangeReader) Close() (*ResObjectRange, error) {
return x.close(true)
}
@ -617,12 +642,12 @@ func (x *ObjectRangeReader) Read(p []byte) (int, error) {
x.remainingPayloadLen -= n
if !ok {
err := x.close(false)
res, err := x.close(false)
if err != nil {
return n, err
}
return n, x.err
return n, apistatus.ErrFromStatus(res.Status())
}
if x.remainingPayloadLen < 0 {
@ -633,26 +658,24 @@ func (x *ObjectRangeReader) Read(p []byte) (int, error) {
}
// ObjectRangeInit initiates reading an object's payload range through a remote
// server using NeoFS API protocol.
// server using FrostFS API protocol.
//
// 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.
//
// Returns an error if parameters are set incorrectly (see PrmObjectRange docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingContainer]
// - [ErrMissingObject]
// - [ErrZeroRangeLength]
func (c *Client) ObjectRangeInit(ctx context.Context, prm PrmObjectRange) (*ObjectRangeReader, error) {
// check parameters
switch {
case ctx == nil:
return nil, errorMissingContext
case prm.addr.GetContainerID() == nil:
return nil, ErrMissingContainer
return nil, errorMissingContainer
case prm.addr.GetObjectID() == nil:
return nil, ErrMissingObject
return nil, errorMissingObject
case prm.rng.GetLength() == 0:
return nil, ErrZeroRangeLength
return nil, errorZeroRangeLength
}
// form request body
@ -668,12 +691,12 @@ func (c *Client) ObjectRangeInit(ctx context.Context, prm PrmObjectRange) (*Obje
req.SetBody(&body)
c.prepareRequest(&req, &prm.meta)
signer := prm.signer
if signer == nil {
signer = c.prm.signer
key := prm.key
if key == nil {
key = &c.prm.key
}
err := signServiceMessage(signer, &req)
err := signature.SignServiceMessage(key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}

View file

@ -1,86 +0,0 @@
package client
import (
"bytes"
"math/rand"
"testing"
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/stretchr/testify/require"
)
func TestPrmObjectRead_ByAddress(t *testing.T) {
var prm PrmObjectHead
var (
objID oid.ID
contID cid.ID
oidV2 v2refs.ObjectID
cidV2 v2refs.ContainerID
)
t.Run("ByID", func(t *testing.T) {
objID = randOID(t)
prm.ByID(objID)
objID.WriteToV2(&oidV2)
require.True(t, bytes.Equal(oidV2.GetValue(), prm.addr.GetObjectID().GetValue()))
})
t.Run("FromContainer", func(t *testing.T) {
contID = randCID(t)
prm.FromContainer(contID)
contID.WriteToV2(&cidV2)
require.True(t, bytes.Equal(cidV2.GetValue(), prm.addr.GetContainerID().GetValue()))
})
t.Run("ByAddress", func(t *testing.T) {
var addr oid.Address
addr.SetObject(objID)
addr.SetContainer(contID)
prm.ByAddress(addr)
require.True(t, bytes.Equal(oidV2.GetValue(), prm.addr.GetObjectID().GetValue()))
require.True(t, bytes.Equal(cidV2.GetValue(), prm.addr.GetContainerID().GetValue()))
})
}
func TestPrmObjectRange_SetRange(t *testing.T) {
var prm PrmObjectRange
var (
ln = rand.Uint64()
off = rand.Uint64()
rng *object.Range
)
t.Run("SetLength", func(t *testing.T) {
prm.SetLength(ln)
rng = object.NewRangeFromV2(&prm.rng)
require.Equal(t, ln, rng.GetLength())
})
t.Run("SetOffset", func(t *testing.T) {
prm.SetOffset(off)
rng = object.NewRangeFromV2(&prm.rng)
require.Equal(t, off, rng.GetOffset())
})
t.Run("SetRange", func(t *testing.T) {
var tmp object.Range
tmp.SetLength(ln)
tmp.SetOffset(off)
prm.SetRange(tmp)
require.Equal(t, ln, tmp.ToV2().GetLength())
require.Equal(t, off, tmp.ToV2().GetOffset())
})
}

View file

@ -2,19 +2,21 @@ package client
import (
"context"
"crypto/ecdsa"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/acl"
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
)
// PrmObjectHash groups parameters of ObjectHash operation.
@ -27,13 +29,15 @@ type PrmObjectHash struct {
addr v2refs.Address
signer neofscrypto.Signer
keySet bool
key ecdsa.PrivateKey
}
// UseSigner specifies private signer to sign the requests.
// If signer is not provided, then Client default signer is used.
func (x *PrmObjectHash) UseSigner(signer neofscrypto.Signer) {
x.signer = signer
// UseKey specifies private key to sign the requests.
// If key is not provided, then Client default key is used.
func (x *PrmObjectHash) UseKey(key ecdsa.PrivateKey) {
x.keySet = true
x.key = key
}
// MarkLocal tells the server to execute the operation locally.
@ -65,8 +69,8 @@ func (x *PrmObjectHash) WithBearerToken(t bearer.Token) {
x.meta.SetBearerToken(&v2token)
}
// FromContainer specifies NeoFS container of the object.
// Required parameter. It is an alternative to [PrmObjectHash.ByAddress].
// FromContainer specifies FrostFS container of the object.
// Required parameter.
func (x *PrmObjectHash) FromContainer(id cid.ID) {
var cidV2 v2refs.ContainerID
id.WriteToV2(&cidV2)
@ -75,7 +79,7 @@ func (x *PrmObjectHash) FromContainer(id cid.ID) {
}
// ByID specifies identifier of the requested object.
// Required parameter. It is an alternative to [PrmObjectHash.ByAddress].
// Required parameter.
func (x *PrmObjectHash) ByID(id oid.ID) {
var idV2 v2refs.ObjectID
id.WriteToV2(&idV2)
@ -83,12 +87,6 @@ func (x *PrmObjectHash) ByID(id oid.ID) {
x.addr.SetObjectID(&idV2)
}
// ByAddress specifies address of the requested object.
// Required parameter. It is an alternative to [PrmObjectHash.ByID], [PrmObjectHash.FromContainer].
func (x *PrmObjectHash) ByAddress(addr oid.Address) {
addr.WriteToV2(&x.addr)
}
// SetRangeList sets list of ranges in (offset, length) pair format.
// Required parameter.
//
@ -134,6 +132,8 @@ func (x *PrmObjectHash) WithXHeaders(hs ...string) {
// ResObjectHash groups resulting values of ObjectHash operation.
type ResObjectHash struct {
statusRes
checksums [][]byte
}
@ -143,29 +143,37 @@ func (x ResObjectHash) Checksums() [][]byte {
}
// ObjectHash requests checksum of the range list of the object payload using
// NeoFS API protocol.
// FrostFS API protocol.
//
// Returns a list of checksums in raw form: the format of hashes and their number
// is left for the caller to check. Client preserves the order of the server's response.
//
// 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`,
// see [apistatus] package for NeoFS-specific error types.
// 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 PrmObjectHash docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingContainer]
// - [ErrMissingObject]
// - [ErrMissingRanges]
// Return statuses:
// - global (see Client docs);
// - *apistatus.ContainerNotFound;
// - *apistatus.ObjectNotFound;
// - *apistatus.ObjectAccessDenied;
// - *apistatus.ObjectOutOfRange;
// - *apistatus.SessionTokenExpired.
func (c *Client) ObjectHash(ctx context.Context, prm PrmObjectHash) (*ResObjectHash, error) {
switch {
case ctx == nil:
return nil, errorMissingContext
case prm.addr.GetContainerID() == nil:
return nil, ErrMissingContainer
return nil, errorMissingContainer
case prm.addr.GetObjectID() == nil:
return nil, ErrMissingObject
return nil, errorMissingObject
case len(prm.body.GetRanges()) == 0:
return nil, ErrMissingRanges
return nil, errorMissingRanges
}
prm.body.SetAddress(&prm.addr)
@ -179,12 +187,12 @@ func (c *Client) ObjectHash(ctx context.Context, prm PrmObjectHash) (*ResObjectH
c.prepareRequest(&req, &prm.meta)
req.SetBody(&prm.body)
signer := prm.signer
if signer == nil {
signer = c.prm.signer
key := c.prm.key
if prm.keySet {
key = prm.key
}
err := signServiceMessage(signer, &req)
err := signature.SignServiceMessage(&key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}
@ -195,10 +203,15 @@ func (c *Client) ObjectHash(ctx context.Context, prm PrmObjectHash) (*ResObjectH
}
var res ResObjectHash
if err = c.processResponse(resp); err != nil {
res.st, err = c.processResponse(resp)
if err != nil {
return nil, err
}
if !apistatus.IsSuccessful(res.st) {
return &res, nil
}
res.checksums = resp.GetBody().GetHashList()
if len(res.checksums) == 0 {
return nil, newErrMissingResponseField("hash list")

View file

@ -1,50 +0,0 @@
package client
import (
"bytes"
"testing"
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/stretchr/testify/require"
)
func TestPrmObjectHash_ByAddress(t *testing.T) {
var prm PrmObjectHash
var (
objID oid.ID
contID cid.ID
oidV2 v2refs.ObjectID
cidV2 v2refs.ContainerID
)
t.Run("ByID", func(t *testing.T) {
objID = randOID(t)
prm.ByID(objID)
objID.WriteToV2(&oidV2)
require.True(t, bytes.Equal(oidV2.GetValue(), prm.addr.GetObjectID().GetValue()))
})
t.Run("FromContainer", func(t *testing.T) {
contID = randCID(t)
prm.FromContainer(contID)
contID.WriteToV2(&cidV2)
require.True(t, bytes.Equal(cidV2.GetValue(), prm.addr.GetContainerID().GetValue()))
})
t.Run("ByAddress", func(t *testing.T) {
var addr oid.Address
addr.SetObject(objID)
addr.SetContainer(contID)
prm.ByAddress(addr)
require.True(t, bytes.Equal(oidV2.GetValue(), prm.addr.GetObjectID().GetValue()))
require.True(t, bytes.Equal(cidV2.GetValue(), prm.addr.GetContainerID().GetValue()))
})
}

View file

@ -2,29 +2,28 @@ package client
import (
"context"
"crypto/ecdsa"
"errors"
"fmt"
"io"
"github.com/nspcc-dev/neofs-api-go/v2/acl"
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/object/slicer"
"github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/user"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
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"
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
)
// PrmObjectPutInit groups parameters of ObjectPutInit operation.
type PrmObjectPutInit struct {
copyNum uint32
signer neofscrypto.Signer
key *ecdsa.PrivateKey
meta v2session.RequestMetaHeader
}
@ -35,6 +34,8 @@ func (x *PrmObjectPutInit) SetCopiesNumber(copiesNumber uint32) {
// ResObjectPut groups the final result values of ObjectPutInit operation.
type ResObjectPut struct {
statusRes
obj oid.ID
}
@ -43,7 +44,7 @@ func (x ResObjectPut) StoredObjectID() oid.ID {
return x.obj
}
// ObjectWriter is designed to write one object to NeoFS system.
// ObjectWriter is designed to write one object to FrostFS system.
//
// Must be initialized using Client.ObjectPutInit, any other
// usage is unsafe.
@ -56,9 +57,9 @@ type ObjectWriter struct {
Close() error
}
signer neofscrypto.Signer
res ResObjectPut
err error
key *ecdsa.PrivateKey
res ResObjectPut
err error
chunkCalled bool
@ -68,10 +69,10 @@ type ObjectWriter struct {
partChunk v2object.PutObjectPartChunk
}
// UseSigner specifies private signer to sign the requests.
// If signer is not provided, then Client default signer is used.
func (x *PrmObjectPutInit) UseSigner(signer neofscrypto.Signer) {
x.signer = signer
// UseKey specifies private key to sign the requests.
// If key is not provided, then Client default key is used.
func (x *PrmObjectPutInit) UseKey(key ecdsa.PrivateKey) {
x.key = &key
}
// WithBearerToken attaches bearer token to be used for the operation.
@ -116,7 +117,7 @@ func (x *ObjectWriter) WriteHeader(hdr object.Object) bool {
x.req.GetBody().SetObjectPart(&x.partInit)
x.req.SetVerificationHeader(nil)
x.err = signServiceMessage(x.signer, &x.req)
x.err = signature.SignServiceMessage(x.key, &x.req)
if x.err != nil {
x.err = fmt.Errorf("sign message: %w", x.err)
return false
@ -158,7 +159,7 @@ func (x *ObjectWriter) WritePayloadChunk(chunk []byte) bool {
x.partChunk.SetChunk(chunk[:ln])
x.req.SetVerificationHeader(nil)
x.err = signServiceMessage(x.signer, &x.req)
x.err = signature.SignServiceMessage(x.key, &x.req)
if x.err != nil {
x.err = fmt.Errorf("sign message: %w", x.err)
return false
@ -180,17 +181,17 @@ func (x *ObjectWriter) WritePayloadChunk(chunk []byte) bool {
//
// 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 NeoFS API statuses, then NeoFS failures
// If Client is tuned to resolve FrostFS API statuses, then FrostFS failures
// codes are returned as error.
//
// Return errors:
// Return statuses:
// - global (see Client docs);
// - [apistatus.ErrContainerNotFound];
// - [apistatus.ErrObjectAccessDenied];
// - [apistatus.ErrObjectLocked];
// - [apistatus.ErrLockNonRegularObject];
// - [apistatus.ErrSessionTokenNotFound];
// - [apistatus.ErrSessionTokenExpired].
// - *apistatus.ContainerNotFound;
// - *apistatus.ObjectAccessDenied;
// - *apistatus.ObjectLocked;
// - *apistatus.LockNonRegularObject;
// - *apistatus.SessionTokenNotFound;
// - *apistatus.SessionTokenExpired.
func (x *ObjectWriter) Close() (*ResObjectPut, error) {
defer x.cancelCtxStream()
@ -205,10 +206,15 @@ func (x *ObjectWriter) Close() (*ResObjectPut, error) {
return nil, x.err
}
if x.err = x.client.processResponse(&x.respV2); x.err != nil {
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()
@ -224,13 +230,19 @@ func (x *ObjectWriter) Close() (*ResObjectPut, error) {
return &x.res, nil
}
// ObjectPutInit initiates writing an object through a remote server using NeoFS API protocol.
// ObjectPutInit initiates writing an object through a remote server using FrostFS API protocol.
//
// The call only opens the transmission channel, explicit recording is done using the ObjectWriter.
// Exactly one return value is non-nil. Resulting writer must be finally closed.
//
// Returns an error if parameters are set incorrectly.
// Context is required and must not be nil. It is used for network communication.
func (c *Client) ObjectPutInit(ctx context.Context, prm PrmObjectPutInit) (*ObjectWriter, error) {
// check parameters
if ctx == nil {
return nil, errorMissingContext
}
var w ObjectWriter
ctx, cancel := context.WithCancel(ctx)
@ -240,9 +252,9 @@ func (c *Client) ObjectPutInit(ctx context.Context, prm PrmObjectPutInit) (*Obje
return nil, fmt.Errorf("open stream: %w", err)
}
w.signer = prm.signer
if w.signer == nil {
w.signer = c.prm.signer
w.key = &c.prm.key
if prm.key != nil {
w.key = prm.key
}
w.cancelCtxStream = cancel
w.client = c
@ -253,106 +265,3 @@ func (c *Client) ObjectPutInit(ctx context.Context, prm PrmObjectPutInit) (*Obje
return &w, nil
}
type objectWriter struct {
context context.Context
client *Client
}
func (x *objectWriter) InitDataStream(header object.Object) (io.Writer, error) {
var prm PrmObjectPutInit
stream, err := x.client.ObjectPutInit(x.context, prm)
if err != nil {
return nil, fmt.Errorf("init object stream: %w", err)
}
if stream.WriteHeader(header) {
return &payloadWriter{
stream: stream,
}, nil
}
_, err = stream.Close()
if err != nil {
return nil, err
}
return nil, errors.New("unexpected error")
}
type payloadWriter struct {
stream *ObjectWriter
}
func (x *payloadWriter) Write(p []byte) (int, error) {
if !x.stream.WritePayloadChunk(p) {
return 0, x.Close()
}
return len(p), nil
}
func (x *payloadWriter) Close() error {
_, err := x.stream.Close()
if err != nil {
return err
}
return nil
}
// CreateObject creates new NeoFS object with given payload data and stores it
// in specified container of the NeoFS network using provided Client connection.
// The object is created on behalf of provided neofscrypto.Signer, and owned by
// the specified user.ID.
//
// In terms of NeoFS, parameterized neofscrypto.Signer represents object owner,
// object signer and request sender. Container SHOULD be public-write or sender
// SHOULD have corresponding rights.
//
// Client connection MUST be opened in advance, see Dial method for details.
// Network communication is carried out within a given context, so it MUST NOT
// be nil.
//
// Notice: This API is EXPERIMENTAL and is planned to be replaced/changed in the
// future. Be ready to refactor your code regarding imports and call mechanics,
// in essence the operation will not change.
func CreateObject(ctx context.Context, cli *Client, signer neofscrypto.Signer, cnr cid.ID, owner user.ID, data io.Reader, attributes ...string) (oid.ID, error) {
s, err := NewDataSlicer(ctx, cli, signer, cnr, owner)
if err != nil {
return oid.ID{}, err
}
return s.Slice(data, attributes...)
}
// NewDataSlicer creates slicer.Slicer that saves data in the NeoFS network
// through provided Client. The data is packaged into NeoFS objects stored in
// the specified container. Provided signer is being used to sign the resulting
// objects as a system requirement. Produced objects are owned by the
// parameterized NeoFS user.
//
// Notice: This API is EXPERIMENTAL and is planned to be replaced/changed in the
// future. Be ready to refactor your code regarding imports and call mechanics,
// in essence the operation will not change.
func NewDataSlicer(ctx context.Context, cli *Client, signer neofscrypto.Signer, cnr cid.ID, owner user.ID) (*slicer.Slicer, error) {
resNetInfo, err := cli.NetworkInfo(ctx, PrmNetworkInfo{})
if err != nil {
return nil, fmt.Errorf("read current network info: %w", err)
}
netInfo := resNetInfo.Info()
var opts slicer.Options
opts.SetObjectPayloadLimit(netInfo.MaxObjectSize())
opts.SetCurrentNeoFSEpoch(netInfo.CurrentEpoch())
if !netInfo.HomomorphicHashingDisabled() {
opts.CalculateHomomorphicChecksum()
}
return slicer.New(signer, cnr, owner, &objectWriter{
context: ctx,
client: cli,
}, opts), nil
}

View file

@ -2,29 +2,31 @@ package client
import (
"context"
"crypto/ecdsa"
"errors"
"fmt"
"io"
"github.com/nspcc-dev/neofs-api-go/v2/acl"
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
)
// PrmObjectSearch groups parameters of ObjectSearch operation.
type PrmObjectSearch struct {
meta v2session.RequestMetaHeader
signer neofscrypto.Signer
key *ecdsa.PrivateKey
cnrSet bool
cnrID cid.ID
@ -68,10 +70,10 @@ func (x *PrmObjectSearch) WithXHeaders(hs ...string) {
writeXHeadersToMeta(hs, &x.meta)
}
// UseSigner specifies private signer to sign the requests.
// If signer is not provided, then Client default signer is used.
func (x *PrmObjectSearch) UseSigner(signer neofscrypto.Signer) {
x.signer = signer
// UseKey specifies private key to sign the requests.
// If key is not provided, then Client default key is used.
func (x *PrmObjectSearch) UseKey(key ecdsa.PrivateKey) {
x.key = &key
}
// InContainer specifies the container in which to look for objects.
@ -87,13 +89,19 @@ func (x *PrmObjectSearch) SetFilters(filters object.SearchFilters) {
x.filters = filters
}
// ObjectListReader is designed to read list of object identifiers from NeoFS system.
// ResObjectSearch groups the final result values of ObjectSearch operation.
type ResObjectSearch struct {
statusRes
}
// ObjectListReader is designed to read list of object identifiers from FrostFS system.
//
// Must be initialized using Client.ObjectSearch, any other usage is unsafe.
type ObjectListReader struct {
client *Client
cancelCtxStream context.CancelFunc
err error
res ResObjectSearch
stream interface {
Read(resp *v2object.SearchResponse) error
}
@ -125,8 +133,8 @@ func (x *ObjectListReader) Read(buf []oid.ID) (int, bool) {
return read, false
}
x.err = x.client.processResponse(&resp)
if x.err != nil {
x.res.st, x.err = x.client.processResponse(&resp)
if x.err != nil || !apistatus.IsSuccessful(x.res.st) {
return read, false
}
@ -169,7 +177,11 @@ func (x *ObjectListReader) Iterate(f func(oid.ID) bool) error {
// so false means nothing was read.
_, ok := x.Read(buf)
if !ok {
return x.Close()
res, err := x.Close()
if err != nil {
return err
}
return apistatus.ErrFromStatus(res.Status())
}
if f(buf[0]) {
return nil
@ -180,40 +192,41 @@ func (x *ObjectListReader) Iterate(f func(oid.ID) bool) error {
// Close ends reading list of the matched objects and returns the result of the operation
// along with the final results. Must be called after using the ObjectListReader.
//
// 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 NeoFS API statuses, then NeoFS failures
// If Client is tuned to resolve FrostFS API statuses, then FrostFS failures
// codes are returned as error.
//
// Return errors:
// Return statuses:
// - global (see Client docs);
// - [apistatus.ErrContainerNotFound];
// - [apistatus.ErrObjectAccessDenied];
// - [apistatus.ErrSessionTokenExpired].
func (x *ObjectListReader) Close() error {
// - *apistatus.ContainerNotFound;
// - *apistatus.ObjectAccessDenied;
// - *apistatus.SessionTokenExpired.
func (x *ObjectListReader) Close() (*ResObjectSearch, error) {
defer x.cancelCtxStream()
if x.err != nil && !errors.Is(x.err, io.EOF) {
return x.err
return nil, x.err
}
return nil
return &x.res, nil
}
// ObjectSearchInit initiates object selection through a remote server using NeoFS API protocol.
// ObjectSearchInit initiates object selection through a remote server using FrostFS API protocol.
//
// The call only opens the transmission channel, explicit fetching of matched objects
// is done using the ObjectListReader. Exactly one return value is non-nil.
// Resulting reader must be finally closed.
//
// Returns an error if parameters are set incorrectly (see PrmObjectSearch docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingContainer]
func (c *Client) ObjectSearchInit(ctx context.Context, prm PrmObjectSearch) (*ObjectListReader, error) {
// check parameters
switch {
case ctx == nil:
return nil, errorMissingContext
case !prm.cnrSet:
return nil, ErrMissingContainer
return nil, errorMissingContainer
}
var cidV2 v2refs.ContainerID
@ -229,12 +242,12 @@ func (c *Client) ObjectSearchInit(ctx context.Context, prm PrmObjectSearch) (*Ob
req.SetBody(&body)
c.prepareRequest(&req, &prm.meta)
signer := prm.signer
if signer == nil {
signer = c.prm.signer
key := prm.key
if key == nil {
key = &c.prm.key
}
err := signServiceMessage(signer, &req)
err := signature.SignServiceMessage(key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}

View file

@ -1,16 +1,18 @@
package client
import (
"crypto/ecdsa"
"errors"
"fmt"
"io"
"testing"
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/crypto/test"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
signatureV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require"
)
@ -84,7 +86,7 @@ func TestObjectIterate(t *testing.T) {
p, resp := testListReaderResponse(t)
var actual []oid.ID
resp.stream = &singleStreamResponder{signer: p, idList: [][]oid.ID{ids}}
resp.stream = &singleStreamResponder{key: p, idList: [][]oid.ID{ids}}
require.NoError(t, resp.Iterate(func(id oid.ID) bool {
actual = append(actual, id)
return len(actual) == 2
@ -107,24 +109,27 @@ func TestObjectIterate(t *testing.T) {
})
}
func testListReaderResponse(t *testing.T) (neofscrypto.Signer, *ObjectListReader) {
return test.RandomSigner(t), &ObjectListReader{
func testListReaderResponse(t *testing.T) (*ecdsa.PrivateKey, *ObjectListReader) {
p, err := keys.NewPrivateKey()
require.NoError(t, err)
return &p.PrivateKey, &ObjectListReader{
cancelCtxStream: func() {},
client: &Client{},
tail: nil,
}
}
func newSearchStream(signer neofscrypto.Signer, endError error, idList ...[]oid.ID) *singleStreamResponder {
func newSearchStream(key *ecdsa.PrivateKey, endError error, idList ...[]oid.ID) *singleStreamResponder {
return &singleStreamResponder{
signer: signer,
key: key,
endError: endError,
idList: idList,
}
}
type singleStreamResponder struct {
signer neofscrypto.Signer
key *ecdsa.PrivateKey
n int
endError error
idList [][]oid.ID
@ -135,7 +140,7 @@ func (s *singleStreamResponder) Read(resp *v2object.SearchResponse) error {
if s.endError != nil {
return s.endError
}
return ErrUnexpectedReadCall
panic("unexpected call to `Read`")
}
var body v2object.SearchResponseBody
@ -149,9 +154,9 @@ func (s *singleStreamResponder) Read(resp *v2object.SearchResponse) error {
}
resp.SetBody(&body)
err := signServiceMessage(s.signer, resp)
err := signatureV2.SignServiceMessage(s.key, resp)
if err != nil {
return err
panic(fmt.Errorf("error: %w", err))
}
s.n++

View file

@ -3,10 +3,10 @@ package client
import (
"context"
v2reputation "github.com/nspcc-dev/neofs-api-go/v2/reputation"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
"github.com/nspcc-dev/neofs-sdk-go/reputation"
v2reputation "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/reputation"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/reputation"
)
// PrmAnnounceLocalTrust groups parameters of AnnounceLocalTrust operation.
@ -18,13 +18,13 @@ type PrmAnnounceLocalTrust struct {
trusts []reputation.Trust
}
// SetEpoch sets number of NeoFS epoch in which the trust was assessed.
// 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 NeoFS network participants.
// 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.
@ -32,23 +32,33 @@ func (x *PrmAnnounceLocalTrust) SetValues(trusts []reputation.Trust) {
x.trusts = trusts
}
// AnnounceLocalTrust sends client's trust values to the NeoFS network participants.
// ResAnnounceLocalTrust groups results of AnnounceLocalTrust operation.
type ResAnnounceLocalTrust struct {
statusRes
}
// AnnounceLocalTrust sends client's trust values to the FrostFS network participants.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
// 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 PrmAnnounceLocalTrust docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrZeroEpoch]
// - [ErrMissingTrusts]
func (c *Client) AnnounceLocalTrust(ctx context.Context, prm PrmAnnounceLocalTrust) error {
// Return statuses:
// - global (see Client docs).
func (c *Client) AnnounceLocalTrust(ctx context.Context, prm PrmAnnounceLocalTrust) (*ResAnnounceLocalTrust, error) {
// check parameters
switch {
case ctx == nil:
return nil, errorMissingContext
case prm.epoch == 0:
return ErrZeroEpoch
return nil, errorZeroEpoch
case len(prm.trusts) == 0:
return ErrMissingTrusts
return nil, errorMissingTrusts
}
// form request body
@ -71,22 +81,24 @@ func (c *Client) AnnounceLocalTrust(ctx context.Context, prm PrmAnnounceLocalTru
// init call context
var (
cc contextCall
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 cc.err
return nil, cc.err
}
return nil
return &res, nil
}
// PrmAnnounceIntermediateTrust groups parameters of AnnounceIntermediateTrust operation.
@ -101,7 +113,7 @@ type PrmAnnounceIntermediateTrust struct {
trust reputation.PeerToPeerTrust
}
// SetEpoch sets number of NeoFS epoch with which client's calculation algorithm is initialized.
// 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
@ -120,24 +132,34 @@ func (x *PrmAnnounceIntermediateTrust) SetCurrentValue(trust reputation.PeerToPe
x.trustSet = true
}
// AnnounceIntermediateTrust sends global trust values calculated for the specified NeoFS network participants
// 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.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
// 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 PrmAnnounceIntermediateTrust docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrZeroEpoch]
// - [ErrMissingTrust]
func (c *Client) AnnounceIntermediateTrust(ctx context.Context, prm PrmAnnounceIntermediateTrust) error {
// Return statuses:
// - global (see Client docs).
func (c *Client) AnnounceIntermediateTrust(ctx context.Context, prm PrmAnnounceIntermediateTrust) (*ResAnnounceIntermediateTrust, error) {
// check parameters
switch {
case ctx == nil:
return nil, errorMissingContext
case prm.epoch == 0:
return ErrZeroEpoch
return nil, errorZeroEpoch
case !prm.trustSet:
return ErrMissingTrust
return nil, errorTrustNotSet
}
var trust v2reputation.PeerToPeerTrust
@ -157,20 +179,22 @@ func (c *Client) AnnounceIntermediateTrust(ctx context.Context, prm PrmAnnounceI
// init call context
var (
cc contextCall
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 cc.err
return nil, cc.err
}
return nil
return &res, nil
}

View file

@ -1,8 +1,8 @@
package client
import "github.com/nspcc-dev/neofs-api-go/v2/session"
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
// ResponseMetaInfo groups meta information about any NeoFS API response.
// ResponseMetaInfo groups meta information about any FrostFS API response.
type ResponseMetaInfo struct {
key []byte
@ -21,7 +21,7 @@ func (x ResponseMetaInfo) ResponderKey() []byte {
return x.key
}
// Epoch returns local NeoFS epoch of the server.
// Epoch returns local FrostFS epoch of the server.
func (x ResponseMetaInfo) Epoch() uint64 {
return x.epoch
}

View file

@ -2,13 +2,13 @@ package client
import (
"context"
"fmt"
"crypto/ecdsa"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/user"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
)
// PrmSessionCreate groups parameters of SessionCreate operation.
@ -17,7 +17,8 @@ type PrmSessionCreate struct {
exp uint64
signer neofscrypto.Signer
keySet bool
key ecdsa.PrivateKey
}
// SetExp sets number of the last NepFS epoch in the lifetime of the session after which it will be expired.
@ -25,14 +26,17 @@ func (x *PrmSessionCreate) SetExp(exp uint64) {
x.exp = exp
}
// UseSigner specifies private signer to sign the requests and compute token owner.
// If signer is not provided, then Client default signer is used.
func (x *PrmSessionCreate) UseSigner(signer neofscrypto.Signer) {
x.signer = signer
// UseKey specifies private key to sign the requests and compute token owner.
// If key is not provided, then Client default key is used.
func (x *PrmSessionCreate) UseKey(key ecdsa.PrivateKey) {
x.keySet = true
x.key = key
}
// ResSessionCreate groups resulting values of SessionCreate operation.
type ResSessionCreate struct {
statusRes
id []byte
sessionKey []byte
@ -42,7 +46,7 @@ func (x *ResSessionCreate) setID(id []byte) {
x.id = id
}
// ID returns identifier of the opened session in a binary NeoFS 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.
func (x ResSessionCreate) ID() []byte {
@ -53,7 +57,7 @@ func (x *ResSessionCreate) setSessionKey(key []byte) {
x.sessionKey = key
}
// PublicKey returns public key of the opened session in a binary NeoFS API protocol format.
// PublicKey returns public key of the opened session in a binary FrostFS API protocol format.
func (x ResSessionCreate) PublicKey() []byte {
return x.sessionKey
}
@ -62,27 +66,29 @@ func (x ResSessionCreate) PublicKey() []byte {
// The session lifetime coincides with the server lifetime. Results can be written
// to session token which can be later attached to the requests.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
// see [apistatus] package for NeoFS-specific error types.
// 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 PrmSessionCreate docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return errors:
// - [ErrMissingSigner]
// Return statuses:
// - global (see Client docs).
func (c *Client) SessionCreate(ctx context.Context, prm PrmSessionCreate) (*ResSessionCreate, error) {
signer := prm.signer
if signer == nil {
signer = c.prm.signer
// check context
if ctx == nil {
return nil, errorMissingContext
}
if signer == nil {
return nil, ErrMissingSigner
ownerKey := c.prm.key.PublicKey
if prm.keySet {
ownerKey = prm.key.PublicKey
}
var ownerID user.ID
if err := user.IDFromSigner(&ownerID, signer); err != nil {
return nil, fmt.Errorf("IDFromSigner: %w", err)
}
user.IDFromKey(&ownerID, ownerKey)
var ownerIDV2 refs.OwnerID
ownerID.WriteToV2(&ownerIDV2)
@ -105,27 +111,21 @@ func (c *Client) SessionCreate(ctx context.Context, prm PrmSessionCreate) (*ResS
)
c.initCallContext(&cc)
cc.signer = signer
if prm.keySet {
cc.key = prm.key
}
cc.meta = prm.prmCommonMeta
cc.req = &req
cc.statusRes = &res
cc.call = func() (responseV2, error) {
return c.server.createSession(&c.c, &req, client.WithContext(ctx))
return rpcapi.CreateSession(&c.c, &req, client.WithContext(ctx))
}
cc.result = func(r responseV2) {
resp := r.(*v2session.CreateResponse)
body := resp.GetBody()
if len(body.GetID()) == 0 {
cc.err = newErrMissingResponseField("session id")
return
}
if len(body.GetSessionKey()) == 0 {
cc.err = newErrMissingResponseField("session key")
return
}
res.setID(body.GetID())
res.setSessionKey(body.GetSessionKey())
}

View file

@ -1,69 +0,0 @@
package client
import (
"context"
"testing"
v2netmap "github.com/nspcc-dev/neofs-api-go/v2/netmap"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
"github.com/nspcc-dev/neofs-api-go/v2/session"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/crypto/test"
"github.com/stretchr/testify/require"
)
type sessionAPIServer struct {
signer neofscrypto.Signer
setBody func(body *session.CreateResponseBody)
}
func (m sessionAPIServer) netMapSnapshot(context.Context, v2netmap.SnapshotRequest) (*v2netmap.SnapshotResponse, error) {
return nil, nil
}
func (m sessionAPIServer) createSession(*client.Client, *session.CreateRequest, ...client.CallOption) (*session.CreateResponse, error) {
var body session.CreateResponseBody
m.setBody(&body)
var resp session.CreateResponse
resp.SetBody(&body)
if err := signServiceMessage(m.signer, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func TestClient_SessionCreate(t *testing.T) {
ctx := context.Background()
signer := test.RandomSignerRFC6979(t)
c := newClient(t, signer, nil)
var prmSessionCreate PrmSessionCreate
prmSessionCreate.UseSigner(signer)
prmSessionCreate.SetExp(1)
t.Run("missing session id", func(t *testing.T) {
c.setNeoFSAPIServer(&sessionAPIServer{signer: signer, setBody: func(body *session.CreateResponseBody) {
body.SetSessionKey([]byte{1})
}})
result, err := c.SessionCreate(ctx, prmSessionCreate)
require.Nil(t, result)
require.ErrorIs(t, err, ErrMissingResponseField)
require.Equal(t, "missing session id field in the response", err.Error())
})
t.Run("missing session key", func(t *testing.T) {
c.setNeoFSAPIServer(&sessionAPIServer{signer: signer, setBody: func(body *session.CreateResponseBody) {
body.SetID([]byte{1})
}})
result, err := c.SessionCreate(ctx, prmSessionCreate)
require.Nil(t, result)
require.ErrorIs(t, err, ErrMissingResponseField)
require.Equal(t, "missing session key field in the response", err.Error())
})
}

View file

@ -1,396 +0,0 @@
package client
import (
"errors"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/accounting"
"github.com/nspcc-dev/neofs-api-go/v2/container"
"github.com/nspcc-dev/neofs-api-go/v2/netmap"
"github.com/nspcc-dev/neofs-api-go/v2/object"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/reputation"
"github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-api-go/v2/util/signature"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
)
type serviceRequest interface {
GetMetaHeader() *session.RequestMetaHeader
GetVerificationHeader() *session.RequestVerificationHeader
SetVerificationHeader(*session.RequestVerificationHeader)
}
type serviceResponse interface {
GetMetaHeader() *session.ResponseMetaHeader
GetVerificationHeader() *session.ResponseVerificationHeader
SetVerificationHeader(*session.ResponseVerificationHeader)
}
type stableMarshaler interface {
StableMarshal([]byte) []byte
StableSize() int
}
type stableMarshalerWrapper struct {
SM stableMarshaler
}
type metaHeader interface {
stableMarshaler
getOrigin() metaHeader
}
type verificationHeader interface {
stableMarshaler
GetBodySignature() *refs.Signature
SetBodySignature(*refs.Signature)
GetMetaSignature() *refs.Signature
SetMetaSignature(*refs.Signature)
GetOriginSignature() *refs.Signature
SetOriginSignature(*refs.Signature)
setOrigin(stableMarshaler)
getOrigin() verificationHeader
}
type requestMetaHeader struct {
*session.RequestMetaHeader
}
type responseMetaHeader struct {
*session.ResponseMetaHeader
}
type requestVerificationHeader struct {
*session.RequestVerificationHeader
}
type responseVerificationHeader struct {
*session.ResponseVerificationHeader
}
func (h *requestMetaHeader) getOrigin() metaHeader {
return &requestMetaHeader{
RequestMetaHeader: h.GetOrigin(),
}
}
func (h *responseMetaHeader) getOrigin() metaHeader {
return &responseMetaHeader{
ResponseMetaHeader: h.GetOrigin(),
}
}
func (h *requestVerificationHeader) getOrigin() verificationHeader {
if origin := h.GetOrigin(); origin != nil {
return &requestVerificationHeader{
RequestVerificationHeader: origin,
}
}
return nil
}
func (h *requestVerificationHeader) setOrigin(m stableMarshaler) {
if m != nil {
h.SetOrigin(m.(*session.RequestVerificationHeader))
}
}
func (r *responseVerificationHeader) getOrigin() verificationHeader {
if origin := r.GetOrigin(); origin != nil {
return &responseVerificationHeader{
ResponseVerificationHeader: origin,
}
}
return nil
}
func (r *responseVerificationHeader) setOrigin(m stableMarshaler) {
if m != nil {
r.SetOrigin(m.(*session.ResponseVerificationHeader))
}
}
func (s stableMarshalerWrapper) ReadSignedData(buf []byte) ([]byte, error) {
if s.SM != nil {
return s.SM.StableMarshal(buf), nil
}
return nil, nil
}
func (s stableMarshalerWrapper) SignedDataSize() int {
if s.SM != nil {
return s.SM.StableSize()
}
return 0
}
// signServiceMessage signing request or response messages which can be sent or received from neofs endpoint.
// Return errors:
// - [ErrSign]
func signServiceMessage(signer neofscrypto.Signer, msg interface{}) error {
var (
body, meta, verifyOrigin stableMarshaler
verifyHdr verificationHeader
verifyHdrSetter func(verificationHeader)
)
switch v := msg.(type) {
case nil:
return nil
case serviceRequest:
body = serviceMessageBody(v)
meta = v.GetMetaHeader()
verifyHdr = &requestVerificationHeader{new(session.RequestVerificationHeader)}
verifyHdrSetter = func(h verificationHeader) {
v.SetVerificationHeader(h.(*requestVerificationHeader).RequestVerificationHeader)
}
if h := v.GetVerificationHeader(); h != nil {
verifyOrigin = h
}
case serviceResponse:
body = serviceMessageBody(v)
meta = v.GetMetaHeader()
verifyHdr = &responseVerificationHeader{new(session.ResponseVerificationHeader)}
verifyHdrSetter = func(h verificationHeader) {
v.SetVerificationHeader(h.(*responseVerificationHeader).ResponseVerificationHeader)
}
if h := v.GetVerificationHeader(); h != nil {
verifyOrigin = h
}
default:
return NewSignError(fmt.Errorf("unsupported session message %T", v))
}
if verifyOrigin == nil {
// sign session message body
if err := signServiceMessagePart(signer, body, verifyHdr.SetBodySignature); err != nil {
return NewSignError(fmt.Errorf("body: %w", err))
}
}
// sign meta header
if err := signServiceMessagePart(signer, meta, verifyHdr.SetMetaSignature); err != nil {
return NewSignError(fmt.Errorf("meta header: %w", err))
}
// sign verification header origin
if err := signServiceMessagePart(signer, verifyOrigin, verifyHdr.SetOriginSignature); err != nil {
return NewSignError(fmt.Errorf("origin of verification header: %w", err))
}
// wrap origin verification header
verifyHdr.setOrigin(verifyOrigin)
// update matryoshka verification header
verifyHdrSetter(verifyHdr)
return nil
}
func signServiceMessagePart(signer neofscrypto.Signer, part stableMarshaler, sigWrite func(*refs.Signature)) error {
var sig neofscrypto.Signature
var sigv2 refs.Signature
m := &stableMarshalerWrapper{part}
data, err := m.ReadSignedData(nil)
if err != nil {
return fmt.Errorf("ReadSignedData %w", err)
}
if err = sig.Calculate(signer, data); err != nil {
return fmt.Errorf("calculate %w", err)
}
sig.WriteToV2(&sigv2)
sigWrite(&sigv2)
return nil
}
func verifyServiceMessage(msg interface{}) error {
var (
meta metaHeader
verify verificationHeader
)
switch v := msg.(type) {
case nil:
return nil
case serviceRequest:
meta = &requestMetaHeader{
RequestMetaHeader: v.GetMetaHeader(),
}
verify = &requestVerificationHeader{
RequestVerificationHeader: v.GetVerificationHeader(),
}
case serviceResponse:
meta = &responseMetaHeader{
ResponseMetaHeader: v.GetMetaHeader(),
}
verify = &responseVerificationHeader{
ResponseVerificationHeader: v.GetVerificationHeader(),
}
default:
return fmt.Errorf("unsupported session message %T", v)
}
body := serviceMessageBody(msg)
size := body.StableSize()
if sz := meta.StableSize(); sz > size {
size = sz
}
if sz := verify.StableSize(); sz > size {
size = sz
}
buf := make([]byte, 0, size)
return verifyMatryoshkaLevel(body, meta, verify, buf)
}
func verifyMatryoshkaLevel(body stableMarshaler, meta metaHeader, verify verificationHeader, buf []byte) error {
if err := verifyServiceMessagePart(meta, verify.GetMetaSignature, buf); err != nil {
return fmt.Errorf("could not verify meta header: %w", err)
}
origin := verify.getOrigin()
if err := verifyServiceMessagePart(origin, verify.GetOriginSignature, buf); err != nil {
return fmt.Errorf("could not verify origin of verification header: %w", err)
}
if origin == nil {
if err := verifyServiceMessagePart(body, verify.GetBodySignature, buf); err != nil {
return fmt.Errorf("could not verify body: %w", err)
}
return nil
}
if verify.GetBodySignature() != nil {
return errors.New("body signature at the matryoshka upper level")
}
return verifyMatryoshkaLevel(body, meta.getOrigin(), origin, buf)
}
func verifyServiceMessagePart(part stableMarshaler, sigRdr func() *refs.Signature, buf []byte) error {
return signature.VerifyDataWithSource(
&stableMarshalerWrapper{part},
sigRdr,
signature.WithBuffer(buf),
)
}
func serviceMessageBody(req any) stableMarshaler {
switch v := req.(type) {
default:
panic(fmt.Sprintf("unsupported session message %T", req))
/* Accounting */
case *accounting.BalanceRequest:
return v.GetBody()
case *accounting.BalanceResponse:
return v.GetBody()
/* Session */
case *session.CreateRequest:
return v.GetBody()
case *session.CreateResponse:
return v.GetBody()
/* Container */
case *container.PutRequest:
return v.GetBody()
case *container.PutResponse:
return v.GetBody()
case *container.DeleteRequest:
return v.GetBody()
case *container.DeleteResponse:
return v.GetBody()
case *container.GetRequest:
return v.GetBody()
case *container.GetResponse:
return v.GetBody()
case *container.ListRequest:
return v.GetBody()
case *container.ListResponse:
return v.GetBody()
case *container.SetExtendedACLRequest:
return v.GetBody()
case *container.SetExtendedACLResponse:
return v.GetBody()
case *container.GetExtendedACLRequest:
return v.GetBody()
case *container.GetExtendedACLResponse:
return v.GetBody()
case *container.AnnounceUsedSpaceRequest:
return v.GetBody()
case *container.AnnounceUsedSpaceResponse:
return v.GetBody()
/* Object */
case *object.PutRequest:
return v.GetBody()
case *object.PutResponse:
return v.GetBody()
case *object.GetRequest:
return v.GetBody()
case *object.GetResponse:
return v.GetBody()
case *object.HeadRequest:
return v.GetBody()
case *object.HeadResponse:
return v.GetBody()
case *object.SearchRequest:
return v.GetBody()
case *object.SearchResponse:
return v.GetBody()
case *object.DeleteRequest:
return v.GetBody()
case *object.DeleteResponse:
return v.GetBody()
case *object.GetRangeRequest:
return v.GetBody()
case *object.GetRangeResponse:
return v.GetBody()
case *object.GetRangeHashRequest:
return v.GetBody()
case *object.GetRangeHashResponse:
return v.GetBody()
/* Netmap */
case *netmap.LocalNodeInfoRequest:
return v.GetBody()
case *netmap.LocalNodeInfoResponse:
return v.GetBody()
case *netmap.NetworkInfoRequest:
return v.GetBody()
case *netmap.NetworkInfoResponse:
return v.GetBody()
case *netmap.SnapshotRequest:
return v.GetBody()
case *netmap.SnapshotResponse:
return v.GetBody()
/* Reputation */
case *reputation.AnnounceLocalTrustRequest:
return v.GetBody()
case *reputation.AnnounceLocalTrustResponse:
return v.GetBody()
case *reputation.AnnounceIntermediateResultRequest:
return v.GetBody()
case *reputation.AnnounceIntermediateResultResponse:
return v.GetBody()
}
}

View file

@ -1,247 +0,0 @@
package client
import (
"crypto/rand"
"testing"
"github.com/nspcc-dev/neofs-api-go/v2/accounting"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/session"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/crypto/test"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/stretchr/testify/require"
)
type testResponse interface {
SetMetaHeader(*session.ResponseMetaHeader)
GetMetaHeader() *session.ResponseMetaHeader
}
func testOwner(t *testing.T, owner *refs.OwnerID, req any) {
originalValue := owner.GetValue()
owner.SetValue([]byte{1, 2, 3})
// verification must fail
require.Error(t, verifyServiceMessage(req))
owner.SetValue(originalValue)
require.NoError(t, verifyServiceMessage(req))
}
func testRequestSign(t *testing.T, signer neofscrypto.Signer, meta *session.RequestMetaHeader, req request) {
require.Error(t, verifyServiceMessage(req))
// sign request
require.NoError(t, signServiceMessage(signer, req))
// verification must pass
require.NoError(t, verifyServiceMessage(req))
meta.SetOrigin(req.GetMetaHeader())
req.SetMetaHeader(meta)
// sign request
require.NoError(t, signServiceMessage(signer, req))
// verification must pass
require.NoError(t, verifyServiceMessage(req))
}
func testRequestMeta(t *testing.T, meta *session.RequestMetaHeader, req serviceRequest) {
// corrupt meta header
meta.SetTTL(meta.GetTTL() + 1)
// verification must fail
require.Error(t, verifyServiceMessage(req))
// restore meta header
meta.SetTTL(meta.GetTTL() - 1)
// corrupt origin verification header
req.GetVerificationHeader().SetOrigin(nil)
// verification must fail
require.Error(t, verifyServiceMessage(req))
}
func testResponseSign(t *testing.T, signer neofscrypto.Signer, meta *session.ResponseMetaHeader, resp testResponse) {
require.Error(t, verifyServiceMessage(resp))
// sign request
require.NoError(t, signServiceMessage(signer, resp))
// verification must pass
require.NoError(t, verifyServiceMessage(resp))
meta.SetOrigin(resp.GetMetaHeader())
resp.SetMetaHeader(meta)
// sign request
require.NoError(t, signServiceMessage(signer, resp))
// verification must pass
require.NoError(t, verifyServiceMessage(resp))
}
func testResponseMeta(t *testing.T, meta *session.ResponseMetaHeader, req serviceResponse) {
// corrupt meta header
meta.SetTTL(meta.GetTTL() + 1)
// verification must fail
require.Error(t, verifyServiceMessage(req))
// restore meta header
meta.SetTTL(meta.GetTTL() - 1)
// corrupt origin verification header
req.GetVerificationHeader().SetOrigin(nil)
// verification must fail
require.Error(t, verifyServiceMessage(req))
}
func TestEmptyMessage(t *testing.T) {
signer := test.RandomSignerRFC6979(t)
require.NoError(t, verifyServiceMessage(nil))
require.NoError(t, signServiceMessage(signer, nil))
}
func TestBalanceRequest(t *testing.T) {
signer := test.RandomSignerRFC6979(t)
var id user.ID
require.NoError(t, user.IDFromSigner(&id, signer))
var ownerID refs.OwnerID
id.WriteToV2(&ownerID)
body := accounting.BalanceRequestBody{}
body.SetOwnerID(&ownerID)
meta := &session.RequestMetaHeader{}
meta.SetTTL(1)
req := &accounting.BalanceRequest{}
req.SetBody(&body)
req.SetMetaHeader(meta)
// add level to meta header matryoshka
meta = &session.RequestMetaHeader{}
testRequestSign(t, signer, meta, req)
testOwner(t, &ownerID, req)
testRequestMeta(t, meta, req)
}
func TestBalanceResponse(t *testing.T) {
signer := test.RandomSignerRFC6979(t)
dec := new(accounting.Decimal)
dec.SetValue(100)
body := new(accounting.BalanceResponseBody)
body.SetBalance(dec)
meta := new(session.ResponseMetaHeader)
meta.SetTTL(1)
resp := new(accounting.BalanceResponse)
resp.SetBody(body)
resp.SetMetaHeader(meta)
// add level to meta header matryoshka
meta = new(session.ResponseMetaHeader)
testResponseSign(t, signer, meta, resp)
// corrupt body
dec.SetValue(dec.GetValue() + 1)
// verification must fail
require.Error(t, verifyServiceMessage(resp))
// restore body
dec.SetValue(dec.GetValue() - 1)
testResponseMeta(t, meta, resp)
}
func TestCreateRequest(t *testing.T) {
signer := test.RandomSignerRFC6979(t)
var id user.ID
require.NoError(t, user.IDFromSigner(&id, signer))
var ownerID refs.OwnerID
id.WriteToV2(&ownerID)
body := session.CreateRequestBody{}
body.SetOwnerID(&ownerID)
body.SetExpiration(100)
meta := &session.RequestMetaHeader{}
meta.SetTTL(1)
req := &session.CreateRequest{}
req.SetBody(&body)
req.SetMetaHeader(meta)
// add level to meta header matryoshka
meta = &session.RequestMetaHeader{}
testRequestSign(t, signer, meta, req)
testOwner(t, &ownerID, req)
// corrupt body
body.SetExpiration(body.GetExpiration() + 1)
// verification must fail
require.Error(t, verifyServiceMessage(req))
// restore body
body.SetExpiration(body.GetExpiration() - 1)
testRequestMeta(t, meta, req)
}
func TestCreateResponse(t *testing.T) {
signer := test.RandomSignerRFC6979(t)
id := make([]byte, 8)
_, err := rand.Read(id)
require.NoError(t, err)
sessionKey := make([]byte, 8)
_, err = rand.Read(sessionKey)
require.NoError(t, err)
body := session.CreateResponseBody{}
body.SetID(id)
body.SetSessionKey(sessionKey)
meta := &session.ResponseMetaHeader{}
meta.SetTTL(1)
req := &session.CreateResponse{}
req.SetBody(&body)
req.SetMetaHeader(meta)
// add level to meta header matryoshka
meta = &session.ResponseMetaHeader{}
testResponseSign(t, signer, meta, req)
// corrupt body
body.SetID([]byte{1})
// verification must fail
require.Error(t, verifyServiceMessage(req))
// restore body
body.SetID(id)
// corrupt body
body.SetSessionKey([]byte{1})
// verification must fail
require.Error(t, verifyServiceMessage(req))
// restore body
body.SetSessionKey(id)
testResponseMeta(t, meta, req)
}

View file

@ -2,32 +2,12 @@ package apistatus
import (
"encoding/binary"
"errors"
"github.com/nspcc-dev/neofs-api-go/v2/status"
)
// Error describes common error which is a grouping type for any [apistatus] errors. Any [apistatus] error may be checked
// explicitly via it's type of just check the group via errors.Is(err, [apistatus.Error]).
var Error = errors.New("api error")
var (
// ErrServerInternal is an instance of ServerInternal error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrServerInternal ServerInternal
// ErrWrongMagicNumber is an instance of WrongMagicNumber error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrWrongMagicNumber WrongMagicNumber
// ErrSignatureVerification is an instance of SignatureVerification error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrSignatureVerification SignatureVerification
// ErrNodeUnderMaintenance is an instance of NodeUnderMaintenance error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrNodeUnderMaintenance NodeUnderMaintenance
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
)
// ServerInternal describes failure statuses related to internal server errors.
// Instances provide [StatusV2] and error interfaces.
// Instances provide Status and StatusV2 interfaces.
//
// The status is purely informative, the client should not go into details of the error except for debugging needs.
type ServerInternal struct {
@ -41,28 +21,18 @@ func (x ServerInternal) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x ServerInternal) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case ServerInternal, *ServerInternal:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
// implements local interface defined in FromStatusV2 func.
func (x *ServerInternal) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: INTERNAL;
// - string message: empty;
// - details: empty.
func (x ServerInternal) ErrorToV2() *status.Status {
func (x ServerInternal) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(status.Internal, status.GlobalizeCommonFail))
return &x.v2
}
@ -87,7 +57,7 @@ func WriteInternalServerErr(x *ServerInternal, err error) {
}
// WrongMagicNumber describes failure status related to incorrect network magic.
// Instances provide [StatusV2] and error interfaces.
// Instances provide Status and StatusV2 interfaces.
type WrongMagicNumber struct {
v2 status.Status
}
@ -99,28 +69,18 @@ func (x WrongMagicNumber) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x WrongMagicNumber) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case WrongMagicNumber, *WrongMagicNumber:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
// implements local interface defined in FromStatusV2 func.
func (x *WrongMagicNumber) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: WRONG_MAGIC_NUMBER;
// - string message: empty;
// - details: empty.
func (x WrongMagicNumber) ErrorToV2() *status.Status {
func (x WrongMagicNumber) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(status.WrongMagicNumber, status.GlobalizeCommonFail))
return &x.v2
}
@ -165,7 +125,7 @@ func (x WrongMagicNumber) CorrectMagic() (magic uint64, ok int8) {
}
// SignatureVerification describes failure status related to signature verification.
// Instances provide [StatusV2] and error interfaces.
// Instances provide Status and StatusV2 interfaces.
type SignatureVerification struct {
v2 status.Status
}
@ -184,29 +144,19 @@ func (x SignatureVerification) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x SignatureVerification) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case SignatureVerification, *SignatureVerification:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
// implements local interface defined in FromStatusV2 func.
func (x *SignatureVerification) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: SIGNATURE_VERIFICATION_FAIL;
// - string message: written message via [SignatureVerification.SetMessage] or
// - string message: written message via SetMessage or
// "signature verification failed" as a default message;
// - details: empty.
func (x SignatureVerification) ErrorToV2() *status.Status {
func (x SignatureVerification) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(status.SignatureVerificationFail, status.GlobalizeCommonFail))
if x.v2.Message() == "" {
@ -233,7 +183,7 @@ func (x SignatureVerification) Message() string {
}
// NodeUnderMaintenance describes failure status for nodes being under maintenance.
// Instances provide [StatusV2] and error interfaces.
// Instances provide Status and StatusV2 interfaces.
type NodeUnderMaintenance struct {
v2 status.Status
}
@ -253,28 +203,18 @@ func (x NodeUnderMaintenance) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x NodeUnderMaintenance) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case NodeUnderMaintenance, *NodeUnderMaintenance:
return true
}
}
func (x *NodeUnderMaintenance) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: NODE_UNDER_MAINTENANCE;
// - string message: written message via [NodeUnderMaintenance.SetMessage] or
// - string message: written message via SetMessage or
// "node is under maintenance" as a default message;
// - details: empty.
func (x NodeUnderMaintenance) ErrorToV2() *status.Status {
func (x NodeUnderMaintenance) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(status.NodeUnderMaintenance, status.GlobalizeCommonFail))
if x.v2.Message() == "" {
x.v2.SetMessage(defaultNodeUnderMaintenanceMsg)

View file

@ -3,8 +3,8 @@ package apistatus_test
import (
"testing"
"github.com/nspcc-dev/neofs-api-go/v2/status"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"github.com/stretchr/testify/require"
)
@ -14,14 +14,14 @@ func TestServerInternal_Message(t *testing.T) {
var st apistatus.ServerInternal
res := st.Message()
resv2 := apistatus.ErrorToV2(st).Message()
resv2 := apistatus.ToStatusV2(st).Message()
require.Empty(t, res)
require.Empty(t, resv2)
st.SetMessage(msg)
res = st.Message()
resv2 = apistatus.ErrorToV2(st).Message()
resv2 = apistatus.ToStatusV2(st).Message()
require.Equal(t, msg, res)
require.Equal(t, msg, resv2)
}
@ -42,7 +42,7 @@ func TestWrongMagicNumber_CorrectMagic(t *testing.T) {
require.EqualValues(t, 1, ok)
// corrupt the value
apistatus.ErrorToV2(st).IterateDetails(func(d *status.Detail) bool {
apistatus.ToStatusV2(st).IterateDetails(func(d *status.Detail) bool {
d.SetValue([]byte{1, 2, 3}) // any slice with len != 8
return true
})
@ -64,7 +64,7 @@ func TestSignatureVerification(t *testing.T) {
st.SetMessage(msg)
stV2 := st.ErrorToV2()
stV2 := st.ToStatusV2()
require.Equal(t, msg, st.Message())
require.Equal(t, msg, stV2.Message())
@ -73,7 +73,7 @@ func TestSignatureVerification(t *testing.T) {
t.Run("empty to V2", func(t *testing.T) {
var st apistatus.SignatureVerification
stV2 := st.ErrorToV2()
stV2 := st.ToStatusV2()
require.Equal(t, "signature verification failed", stV2.Message())
})
@ -84,7 +84,7 @@ func TestSignatureVerification(t *testing.T) {
st.SetMessage(msg)
stV2 := st.ErrorToV2()
stV2 := st.ToStatusV2()
require.Equal(t, msg, stV2.Message())
})
@ -103,7 +103,7 @@ func TestNodeUnderMaintenance(t *testing.T) {
st.SetMessage(msg)
stV2 := st.ErrorToV2()
stV2 := st.ToStatusV2()
require.Equal(t, msg, st.Message())
require.Equal(t, msg, stV2.Message())
@ -112,7 +112,7 @@ func TestNodeUnderMaintenance(t *testing.T) {
t.Run("empty to V2", func(t *testing.T) {
var st apistatus.NodeUnderMaintenance
stV2 := st.ErrorToV2()
stV2 := st.ToStatusV2()
require.Empty(t, "", stV2.Message())
})
@ -123,7 +123,7 @@ func TestNodeUnderMaintenance(t *testing.T) {
st.SetMessage(msg)
stV2 := st.ErrorToV2()
stV2 := st.ToStatusV2()
require.Equal(t, msg, stV2.Message())
})

View file

@ -1,23 +1,12 @@
package apistatus
import (
"errors"
"github.com/nspcc-dev/neofs-api-go/v2/container"
"github.com/nspcc-dev/neofs-api-go/v2/status"
)
var (
// ErrEACLNotFound is an instance of EACLNotFound error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrEACLNotFound EACLNotFound
// ErrContainerNotFound is an instance of ContainerNotFound error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrContainerNotFound ContainerNotFound
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
)
// ContainerNotFound describes status of the failure because of the missing container.
// Instances provide [StatusV2] and error interfaces.
// Instances provide Status and StatusV2 interfaces.
type ContainerNotFound struct {
v2 status.Status
}
@ -36,28 +25,18 @@ func (x ContainerNotFound) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x ContainerNotFound) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case ContainerNotFound, *ContainerNotFound:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
// implements local interface defined in FromStatusV2 func.
func (x *ContainerNotFound) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: CONTAINER_NOT_FOUND;
// - string message: "container not found";
// - details: empty.
func (x ContainerNotFound) ErrorToV2() *status.Status {
func (x ContainerNotFound) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(container.StatusNotFound, container.GlobalizeFail))
x.v2.SetMessage(defaultContainerNotFoundMsg)
return &x.v2
@ -65,7 +44,7 @@ func (x ContainerNotFound) ErrorToV2() *status.Status {
// EACLNotFound describes status of the failure because of the missing eACL
// table.
// Instances provide [StatusV2] and error interfaces.
// Instances provide Status and StatusV2 interfaces.
type EACLNotFound struct {
v2 status.Status
}
@ -84,28 +63,18 @@ func (x EACLNotFound) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x EACLNotFound) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case EACLNotFound, *EACLNotFound:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
// implements local interface defined in FromStatusV2 func.
func (x *EACLNotFound) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: EACL_NOT_FOUND;
// - string message: "eACL not found";
// - details: empty.
func (x EACLNotFound) ErrorToV2() *status.Status {
func (x EACLNotFound) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(container.StatusEACLNotFound, container.GlobalizeFail))
x.v2.SetMessage(defaultEACLNotFoundMsg)
return &x.v2

View file

@ -1,93 +0,0 @@
package apistatus
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestErrors(t *testing.T) {
for _, tc := range []struct {
errs []error
errVariable error
}{
{
errs: []error{ServerInternal{}, new(ServerInternal)},
errVariable: ErrServerInternal,
},
{
errs: []error{WrongMagicNumber{}, new(WrongMagicNumber)},
errVariable: ErrWrongMagicNumber,
},
{
errs: []error{SignatureVerification{}, new(SignatureVerification)},
errVariable: ErrSignatureVerification,
},
{
errs: []error{NodeUnderMaintenance{}, new(NodeUnderMaintenance)},
errVariable: ErrNodeUnderMaintenance,
},
{
errs: []error{ObjectLocked{}, new(ObjectLocked)},
errVariable: ErrObjectLocked,
},
{
errs: []error{LockNonRegularObject{}, new(LockNonRegularObject)},
errVariable: ErrLockNonRegularObject,
},
{
errs: []error{ObjectAccessDenied{}, new(ObjectAccessDenied)},
errVariable: ErrObjectAccessDenied,
},
{
errs: []error{ObjectNotFound{}, new(ObjectNotFound)},
errVariable: ErrObjectNotFound,
},
{
errs: []error{ObjectAlreadyRemoved{}, new(ObjectAlreadyRemoved)},
errVariable: ErrObjectAlreadyRemoved,
},
{
errs: []error{ObjectOutOfRange{}, new(ObjectOutOfRange)},
errVariable: ErrObjectOutOfRange,
},
{
errs: []error{ContainerNotFound{}, new(ContainerNotFound)},
errVariable: ErrContainerNotFound,
},
{
errs: []error{EACLNotFound{}, new(EACLNotFound)},
errVariable: ErrEACLNotFound,
},
{
errs: []error{SessionTokenExpired{}, new(SessionTokenExpired)},
errVariable: ErrSessionTokenExpired,
},
{
errs: []error{SessionTokenNotFound{}, new(SessionTokenNotFound)},
errVariable: ErrSessionTokenNotFound,
},
{
errs: []error{UnrecognizedStatusV2{}, new(UnrecognizedStatusV2)},
errVariable: ErrUnrecognizedStatusV2,
},
} {
require.NotEmpty(t, tc.errs)
require.NotNil(t, tc.errVariable)
for i := range tc.errs {
require.ErrorIs(t, tc.errs[i], tc.errVariable)
wrapped := fmt.Errorf("some message %w", tc.errs[i])
require.ErrorIs(t, wrapped, tc.errVariable)
wrappedTwice := fmt.Errorf("another message %w", wrapped)
require.ErrorIs(t, wrappedTwice, tc.errVariable)
}
}
}

View file

@ -1,35 +1,12 @@
package apistatus
import (
"errors"
"github.com/nspcc-dev/neofs-api-go/v2/object"
"github.com/nspcc-dev/neofs-api-go/v2/status"
)
var (
// ErrObjectLocked is an instance of ObjectLocked error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrObjectLocked ObjectLocked
// ErrObjectAlreadyRemoved is an instance of ObjectAlreadyRemoved error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrObjectAlreadyRemoved ObjectAlreadyRemoved
// ErrLockNonRegularObject is an instance of LockNonRegularObject error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrLockNonRegularObject LockNonRegularObject
// ErrObjectAccessDenied is an instance of ObjectAccessDenied error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrObjectAccessDenied ObjectAccessDenied
// ErrObjectNotFound is an instance of ObjectNotFound error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrObjectNotFound ObjectNotFound
// ErrObjectOutOfRange is an instance of ObjectOutOfRange error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrObjectOutOfRange ObjectOutOfRange
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
)
// ObjectLocked describes status of the failure because of the locked object.
// Instances provide [StatusV2] and error interfaces.
// Instances provide Status and StatusV2 interfaces.
type ObjectLocked struct {
v2 status.Status
}
@ -48,35 +25,25 @@ func (x ObjectLocked) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x ObjectLocked) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case ObjectLocked, *ObjectLocked:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
// implements local interface defined in FromStatusV2 func.
func (x *ObjectLocked) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: LOCKED;
// - string message: "object is locked";
// - details: empty.
func (x ObjectLocked) ErrorToV2() *status.Status {
func (x ObjectLocked) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(object.StatusLocked, object.GlobalizeFail))
x.v2.SetMessage(defaultObjectLockedMsg)
return &x.v2
}
// LockNonRegularObject describes status returned on locking the non-regular object.
// Instances provide [StatusV2] and error interfaces.
// Instances provide Status and StatusV2 interfaces.
type LockNonRegularObject struct {
v2 status.Status
}
@ -95,35 +62,25 @@ func (x LockNonRegularObject) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x LockNonRegularObject) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case LockNonRegularObject, *LockNonRegularObject:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
// implements local interface defined in FromStatusV2 func.
func (x *LockNonRegularObject) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: LOCK_NON_REGULAR_OBJECT;
// - string message: "locking non-regular object is forbidden";
// - details: empty.
func (x LockNonRegularObject) ErrorToV2() *status.Status {
func (x LockNonRegularObject) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(object.StatusLockNonRegularObject, object.GlobalizeFail))
x.v2.SetMessage(defaultLockNonRegularObjectMsg)
return &x.v2
}
// ObjectAccessDenied describes status of the failure because of the access control violation.
// Instances provide [StatusV2] and error interfaces.
// Instances provide Status and StatusV2 interfaces.
type ObjectAccessDenied struct {
v2 status.Status
}
@ -142,28 +99,18 @@ func (x ObjectAccessDenied) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x ObjectAccessDenied) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case ObjectAccessDenied, *ObjectAccessDenied:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
// implements local interface defined in FromStatusV2 func.
func (x *ObjectAccessDenied) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: ACCESS_DENIED;
// - string message: "access to object operation denied";
// - details: empty.
func (x ObjectAccessDenied) ErrorToV2() *status.Status {
func (x ObjectAccessDenied) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(object.StatusAccessDenied, object.GlobalizeFail))
x.v2.SetMessage(defaultObjectAccessDeniedMsg)
return &x.v2
@ -181,7 +128,7 @@ func (x ObjectAccessDenied) Reason() string {
}
// ObjectNotFound describes status of the failure because of the missing object.
// Instances provide [StatusV2] and error interfaces.
// Instances provide Status and StatusV2 interfaces.
type ObjectNotFound struct {
v2 status.Status
}
@ -200,28 +147,18 @@ func (x ObjectNotFound) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x ObjectNotFound) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case ObjectNotFound, *ObjectNotFound:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
// implements local interface defined in FromStatusV2 func.
func (x *ObjectNotFound) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: OBJECT_NOT_FOUND;
// - string message: "object not found";
// - details: empty.
func (x ObjectNotFound) ErrorToV2() *status.Status {
func (x ObjectNotFound) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(object.StatusNotFound, object.GlobalizeFail))
x.v2.SetMessage(defaultObjectNotFoundMsg)
return &x.v2
@ -247,28 +184,18 @@ func (x ObjectAlreadyRemoved) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x ObjectAlreadyRemoved) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case ObjectAlreadyRemoved, *ObjectAlreadyRemoved:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
// implements local interface defined in FromStatusV2 func.
func (x *ObjectAlreadyRemoved) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: OBJECT_ALREADY_REMOVED;
// - string message: "object already removed";
// - details: empty.
func (x ObjectAlreadyRemoved) ErrorToV2() *status.Status {
func (x ObjectAlreadyRemoved) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(object.StatusAlreadyRemoved, object.GlobalizeFail))
x.v2.SetMessage(defaultObjectAlreadyRemovedMsg)
return &x.v2
@ -276,7 +203,7 @@ func (x ObjectAlreadyRemoved) ErrorToV2() *status.Status {
// ObjectOutOfRange describes status of the failure because of the incorrect
// provided object ranges.
// Instances provide [StatusV2] and error interfaces.
// Instances provide Status and StatusV2 interfaces.
type ObjectOutOfRange struct {
v2 status.Status
}
@ -295,28 +222,18 @@ func (x ObjectOutOfRange) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x ObjectOutOfRange) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case ObjectOutOfRange, *ObjectOutOfRange:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
// implements local interface defined in FromStatusV2 func.
func (x *ObjectOutOfRange) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: OUT_OF_RANGE;
// - string message: "out of range";
// - details: empty.
func (x ObjectOutOfRange) ErrorToV2() *status.Status {
func (x ObjectOutOfRange) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(object.StatusOutOfRange, object.GlobalizeFail))
x.v2.SetMessage(defaultObjectOutOfRangeMsg)
return &x.v2

View file

@ -3,7 +3,7 @@ package apistatus_test
import (
"testing"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"github.com/stretchr/testify/require"
)
@ -14,13 +14,13 @@ func TestObjectAccessDenied_WriteReason(t *testing.T) {
res := st.Reason()
require.Empty(t, res)
detailNum := apistatus.ErrorToV2(st).NumberOfDetails()
detailNum := apistatus.ToStatusV2(st).NumberOfDetails()
require.Zero(t, detailNum)
st.WriteReason(reason)
res = st.Reason()
require.Equal(t, reason, res)
detailNum = apistatus.ErrorToV2(st).NumberOfDetails()
detailNum = apistatus.ToStatusV2(st).NumberOfDetails()
require.EqualValues(t, 1, detailNum)
}

View file

@ -1,23 +1,12 @@
package apistatus
import (
"errors"
"github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-api-go/v2/status"
)
var (
// ErrSessionTokenNotFound is an instance of SessionTokenNotFound error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrSessionTokenNotFound SessionTokenNotFound
// ErrSessionTokenExpired is an instance of SessionTokenExpired error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
ErrSessionTokenExpired SessionTokenExpired
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
)
// SessionTokenNotFound describes status of the failure because of the missing session token.
// Instances provide [StatusV2] and error interfaces.
// Instances provide Status and StatusV2 interfaces.
type SessionTokenNotFound struct {
v2 status.Status
}
@ -36,35 +25,25 @@ func (x SessionTokenNotFound) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x SessionTokenNotFound) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case SessionTokenNotFound, *SessionTokenNotFound:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
// implements local interface defined in FromStatusV2 func.
func (x *SessionTokenNotFound) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: TOKEN_NOT_FOUND;
// - string message: "session token not found";
// - details: empty.
func (x SessionTokenNotFound) ErrorToV2() *status.Status {
func (x SessionTokenNotFound) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(session.StatusTokenNotFound, session.GlobalizeFail))
x.v2.SetMessage(defaultSessionTokenNotFoundMsg)
return &x.v2
}
// SessionTokenExpired describes status of the failure because of the expired session token.
// Instances provide [StatusV2] and error interfaces.
// Instances provide Status and StatusV2 interfaces.
type SessionTokenExpired struct {
v2 status.Status
}
@ -83,28 +62,18 @@ func (x SessionTokenExpired) Error() string {
)
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x SessionTokenExpired) Is(target error) bool {
switch target.(type) {
default:
return errors.Is(Error, target)
case SessionTokenExpired, *SessionTokenExpired:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
// implements local interface defined in FromStatusV2 func.
func (x *SessionTokenExpired) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ErrorToV2 implements [StatusV2] interface method.
// If the value was returned by [ErrorFromV2], returns the source message.
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: TOKEN_EXPIRED;
// - string message: "expired session token";
// - details: empty.
func (x SessionTokenExpired) ErrorToV2() *status.Status {
func (x SessionTokenExpired) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(session.StatusTokenExpired, session.GlobalizeFail))
x.v2.SetMessage(defaultSessionTokenExpiredMsg)
return &x.v2

44
client/status/status.go Normal file
View file

@ -0,0 +1,44 @@
package apistatus
// Status defines a variety of FrostFS API status returns.
//
// All statuses are split into two disjoint subsets: successful and failed, and:
// - statuses that implement the build-in error interface are considered failed statuses;
// - all other value types are considered successes (nil is a default success).
//
// In Go code type of success can be determined by a type switch, failure - by a switch with errors.As calls.
// Nil should be considered as a success, and default switch section - as an unrecognized Status.
//
// To convert statuses into errors and vice versa, use functions ErrToStatus and ErrFromStatus, respectively.
// ErrFromStatus function returns nil for successful statuses. However, to simplify the check of statuses for success,
// IsSuccessful function should be used (try to avoid nil comparison).
// It should be noted that using direct typecasting is not a compatible approach.
//
// To transport statuses using the FrostFS API V2 protocol, see StatusV2 interface and FromStatusV2 and ToStatusV2 functions.
type Status interface{}
// ErrFromStatus converts Status instance to error if it is failed. Returns nil on successful Status.
//
// Note: direct assignment may not be compatibility-safe.
func ErrFromStatus(st Status) error {
if err, ok := st.(error); ok {
return err
}
return nil
}
// ErrToStatus converts the error instance to Status instance.
//
// Note: direct assignment may not be compatibility-safe.
func ErrToStatus(err error) Status {
return err
}
// IsSuccessful checks if status is successful.
//
// Note: direct cast may not be compatibility-safe.
func IsSuccessful(st Status) bool {
_, ok := st.(error)
return !ok
}

View file

@ -0,0 +1,35 @@
package apistatus_test
import (
"errors"
"testing"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"github.com/stretchr/testify/require"
)
func TestErrors(t *testing.T) {
t.Run("error source", func(t *testing.T) {
err := errors.New("some error")
st := apistatus.ErrToStatus(err)
success := apistatus.IsSuccessful(st)
require.False(t, success)
res := apistatus.ErrFromStatus(st)
require.Equal(t, err, res)
})
t.Run("non-error source", func(t *testing.T) {
var st apistatus.Status = "any non-error type"
success := apistatus.IsSuccessful(st)
require.True(t, success)
res := apistatus.ErrFromStatus(st)
require.Nil(t, res)
})
}

32
client/status/success.go Normal file
View file

@ -0,0 +1,32 @@
package apistatus
import (
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
)
// SuccessDefaultV2 represents Status instance of default success. Implements StatusV2.
type SuccessDefaultV2 struct {
isNil bool
v2 *status.Status
}
// implements local interface defined in FromStatusV2 func.
func (x *SuccessDefaultV2) fromStatusV2(st *status.Status) {
x.isNil = st == nil
x.v2 = st
}
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: OK;
// - string message: empty;
// - details: empty.
func (x SuccessDefaultV2) ToStatusV2() *status.Status {
if x.isNil || x.v2 != nil {
return x.v2
}
return newStatusV2WithLocalCode(status.OK, status.GlobalizeSuccess)
}

View file

@ -1,34 +1,18 @@
package apistatus
import (
"github.com/nspcc-dev/neofs-api-go/v2/status"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
)
// ErrUnrecognizedStatusV2 is an instance of UnrecognizedStatusV2 error status. It's expected to be used for [errors.Is]
// and MUST NOT be changed.
var ErrUnrecognizedStatusV2 UnrecognizedStatusV2
// UnrecognizedStatusV2 describes status of the uncertain failure.
// Instances provide [StatusV2] and error interfaces.
type UnrecognizedStatusV2 struct {
type unrecognizedStatusV2 struct {
v2 status.Status
}
func (x UnrecognizedStatusV2) Error() string {
func (x unrecognizedStatusV2) Error() string {
return errMessageStatusV2("unrecognized", x.v2.Message())
}
// Is implements interface for correct checking current error type with [errors.Is].
func (x UnrecognizedStatusV2) Is(target error) bool {
switch target.(type) {
default:
return false
case UnrecognizedStatusV2, *UnrecognizedStatusV2:
return true
}
}
// implements local interface defined in [ErrorFromV2] func.
func (x *UnrecognizedStatusV2) fromStatusV2(st *status.Status) {
// implements local interface defined in FromStatusV2 func.
func (x *unrecognizedStatusV2) fromStatusV2(st *status.Status) {
x.v2 = *st
}

View file

@ -1,59 +1,46 @@
package apistatus
import (
"errors"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/container"
"github.com/nspcc-dev/neofs-api-go/v2/object"
"github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-api-go/v2/status"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
)
// StatusV2 defines a variety of status instances compatible with NeoFS API V2 protocol.
// StatusV2 defines a variety of Status instances compatible with FrostFS API V2 protocol.
//
// Note: it is not recommended to use this type directly, it is intended for documentation of the library functionality.
type StatusV2 interface {
// ErrorToV2 returns the status as github.com/nspcc-dev/neofs-api-go/v2/status.Status message structure.
ErrorToV2() *status.Status
Status
// ToStatusV2 returns the status as git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status.Status message structure.
ToStatusV2() *status.Status
}
// ErrorFromV2 converts [status.Status] message structure to error. Inverse to [ErrorToV2] operation.
// FromStatusV2 converts status.Status message structure to Status instance. Inverse to ToStatusV2 operation.
//
// If result is not nil, it implements [StatusV2]. This fact should be taken into account only when passing
// the result to the inverse function [ErrorToV2], casts are not compatibility-safe.
// If result is not nil, it implements StatusV2. This fact should be taken into account only when passing
// the result to the inverse function ToStatusV2, casts are not compatibility-safe.
//
// Below is the mapping of return codes to status instance types (with a description of parsing details).
// Below is the mapping of return codes to Status instance types (with a description of parsing details).
// Note: notice if the return type is a pointer.
//
// Successes:
// - [status.OK]: nil (this also includes nil argument).
// - status.OK: *SuccessDefaultV2 (this also includes nil argument).
//
// Common failures:
// - [status.Internal]: *[ServerInternal];
// - [status.SignatureVerificationFail]: *[SignatureVerification].
// - [status.WrongMagicNumber]: *[WrongMagicNumber].
// - [status.NodeUnderMaintenance]: *[NodeUnderMaintenance].
// - status.Internal: *ServerInternal;
// - status.SignatureVerificationFail: *SignatureVerification.
//
// Object failures:
// - [object.StatusLocked]: *[ObjectLocked];
// - [object.StatusLockNonRegularObject]: *[LockNonRegularObject].
// - [object.StatusAccessDenied]: *[ObjectAccessDenied].
// - [object.StatusNotFound]: *[ObjectNotFound].
// - [object.StatusAlreadyRemoved]: *[ObjectAlreadyRemoved].
// - [object.StatusOutOfRange]: *[ObjectOutOfRange].
//
// Container failures:
// - [container.StatusNotFound]: *[ContainerNotFound];
// - [container.StatusEACLNotFound]: *[EACLNotFound];
//
// Session failures:
// - [session.StatusTokenNotFound]: *[SessionTokenNotFound];
// - [session.StatusTokenExpired]: *[SessionTokenExpired];
func ErrorFromV2(st *status.Status) error {
// - object.StatusLocked: *ObjectLocked;
// - object.StatusLockNonRegularObject: *LockNonRegularObject.
// - object.StatusAccessDenied: *ObjectAccessDenied.
func FromStatusV2(st *status.Status) Status {
var decoder interface {
fromStatusV2(*status.Status)
Error() string
}
switch code := st.Code(); {
@ -61,7 +48,7 @@ func ErrorFromV2(st *status.Status) error {
//nolint:exhaustive
switch status.LocalizeSuccess(&code); code {
case status.OK:
return nil
decoder = new(SuccessDefaultV2)
}
case status.IsCommonFail(code):
switch status.LocalizeCommonFail(&code); code {
@ -108,7 +95,7 @@ func ErrorFromV2(st *status.Status) error {
}
if decoder == nil {
decoder = new(UnrecognizedStatusV2)
decoder = new(unrecognizedStatusV2)
}
decoder.fromStatusV2(st)
@ -116,28 +103,27 @@ func ErrorFromV2(st *status.Status) error {
return decoder
}
// ErrorToV2 converts error to status.Status message structure. Inverse to [ErrorFromV2] operation.
// ToStatusV2 converts Status instance to status.Status message structure. Inverse to FromStatusV2 operation.
//
// If argument is the [StatusV2] instance, it is converted directly.
// Otherwise, successes are converted with [status.OK] code w/o details and message,
// failures - with [status.Internal] and error text message w/o details.
func ErrorToV2(err error) *status.Status {
if err == nil {
// If argument is the StatusV2 instance, it is converted directly.
// Otherwise, successes are converted with status.OK code w/o details and message,
// failures - with status.Internal and error text message w/o details.
func ToStatusV2(st Status) *status.Status {
if v, ok := st.(StatusV2); ok {
return v.ToStatusV2()
}
if IsSuccessful(st) {
return newStatusV2WithLocalCode(status.OK, status.GlobalizeSuccess)
}
var instance StatusV2
if errors.As(err, &instance) {
return instance.ErrorToV2()
}
internalErrorStatus := newStatusV2WithLocalCode(status.Internal, status.GlobalizeCommonFail)
internalErrorStatus.SetMessage(err.Error())
internalErrorStatus.SetMessage(st.(error).Error()) // type cast never panics because IsSuccessful() checks cast
return internalErrorStatus
}
func errMessageStatusV2(code any, msg string) string {
func errMessageStatusV2(code interface{}, msg string) string {
const (
noMsgFmt = "status: code = %v"
msgFmt = noMsgFmt + " message = %s"

View file

@ -4,193 +4,143 @@ import (
"errors"
"testing"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"github.com/stretchr/testify/require"
)
func TestFromStatusV2(t *testing.T) {
type statusConstructor func() error
func TestToStatusV2(t *testing.T) {
type statusConstructor func() apistatus.Status
for _, testItem := range [...]struct {
status any // Status or statusConstructor
codeV2 uint64
messageV2 string
compatibleErrs []error
checkAsErr func(error) bool
status interface{} // Status or statusConstructor
codeV2 uint64
messageV2 string
}{
{
status: (statusConstructor)(func() error {
return errors.New("some error")
}),
status: errors.New("some error"),
codeV2: 1024,
messageV2: "some error",
},
{
status: (statusConstructor)(func() error {
return nil
}),
status: 1,
codeV2: 0,
},
{
status: (statusConstructor)(func() error {
st := new(apistatus.ServerInternal)
status: "text",
codeV2: 0,
},
{
status: true,
codeV2: 0,
},
{
status: true,
codeV2: 0,
},
{
status: nil,
codeV2: 0,
},
{
status: (statusConstructor)(func() apistatus.Status {
var st apistatus.ServerInternal
st.SetMessage("internal error message")
return st
}),
codeV2: 1024,
compatibleErrs: []error{apistatus.ErrServerInternal, apistatus.ServerInternal{}, &apistatus.ServerInternal{}, apistatus.Error},
checkAsErr: func(err error) bool {
var target *apistatus.ServerInternal
return errors.As(err, &target)
},
codeV2: 1024,
},
{
status: (statusConstructor)(func() error {
st := new(apistatus.WrongMagicNumber)
status: (statusConstructor)(func() apistatus.Status {
var st apistatus.WrongMagicNumber
st.WriteCorrectMagic(322)
return st
}),
codeV2: 1025,
compatibleErrs: []error{apistatus.ErrWrongMagicNumber, apistatus.WrongMagicNumber{}, &apistatus.WrongMagicNumber{}, apistatus.Error},
checkAsErr: func(err error) bool {
var target *apistatus.WrongMagicNumber
return errors.As(err, &target)
},
codeV2: 1025,
},
{
status: (statusConstructor)(func() error {
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.ObjectLocked)
}),
codeV2: 2050,
compatibleErrs: []error{apistatus.ErrObjectLocked, apistatus.ObjectLocked{}, &apistatus.ObjectLocked{}, apistatus.Error},
checkAsErr: func(err error) bool {
var target *apistatus.ObjectLocked
return errors.As(err, &target)
},
codeV2: 2050,
},
{
status: (statusConstructor)(func() error {
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.LockNonRegularObject)
}),
codeV2: 2051,
compatibleErrs: []error{apistatus.ErrLockNonRegularObject, apistatus.LockNonRegularObject{}, &apistatus.LockNonRegularObject{}, apistatus.Error},
checkAsErr: func(err error) bool {
var target *apistatus.LockNonRegularObject
return errors.As(err, &target)
},
codeV2: 2051,
},
{
status: (statusConstructor)(func() error {
st := new(apistatus.ObjectAccessDenied)
status: (statusConstructor)(func() apistatus.Status {
var st apistatus.ObjectAccessDenied
st.WriteReason("any reason")
return st
}),
codeV2: 2048,
compatibleErrs: []error{apistatus.ErrObjectAccessDenied, apistatus.ObjectAccessDenied{}, &apistatus.ObjectAccessDenied{}, apistatus.Error},
checkAsErr: func(err error) bool {
var target *apistatus.ObjectAccessDenied
return errors.As(err, &target)
},
codeV2: 2048,
},
{
status: (statusConstructor)(func() error {
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.ObjectNotFound)
}),
codeV2: 2049,
compatibleErrs: []error{apistatus.ErrObjectNotFound, apistatus.ObjectNotFound{}, &apistatus.ObjectNotFound{}, apistatus.Error},
checkAsErr: func(err error) bool {
var target *apistatus.ObjectNotFound
return errors.As(err, &target)
},
codeV2: 2049,
},
{
status: (statusConstructor)(func() error {
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.ObjectAlreadyRemoved)
}),
codeV2: 2052,
compatibleErrs: []error{apistatus.ErrObjectAlreadyRemoved, apistatus.ObjectAlreadyRemoved{}, &apistatus.ObjectAlreadyRemoved{}, apistatus.Error},
checkAsErr: func(err error) bool {
var target *apistatus.ObjectAlreadyRemoved
return errors.As(err, &target)
},
codeV2: 2052,
},
{
status: statusConstructor(func() error {
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.ObjectOutOfRange)
}),
codeV2: 2053,
compatibleErrs: []error{apistatus.ErrObjectOutOfRange, apistatus.ObjectOutOfRange{}, &apistatus.ObjectOutOfRange{}, apistatus.Error},
checkAsErr: func(err error) bool {
var target *apistatus.ObjectOutOfRange
return errors.As(err, &target)
},
codeV2: 2053,
},
{
status: (statusConstructor)(func() error {
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.ContainerNotFound)
}),
codeV2: 3072,
compatibleErrs: []error{apistatus.ErrContainerNotFound, apistatus.ContainerNotFound{}, &apistatus.ContainerNotFound{}, apistatus.Error},
checkAsErr: func(err error) bool {
var target *apistatus.ContainerNotFound
return errors.As(err, &target)
},
codeV2: 3072,
},
{
status: (statusConstructor)(func() error {
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.EACLNotFound)
}),
codeV2: 3073,
compatibleErrs: []error{apistatus.ErrEACLNotFound, apistatus.EACLNotFound{}, &apistatus.EACLNotFound{}, apistatus.Error},
checkAsErr: func(err error) bool {
var target *apistatus.EACLNotFound
return errors.As(err, &target)
},
codeV2: 3073,
},
{
status: (statusConstructor)(func() error {
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.SessionTokenNotFound)
}),
codeV2: 4096,
compatibleErrs: []error{apistatus.ErrSessionTokenNotFound, apistatus.SessionTokenNotFound{}, &apistatus.SessionTokenNotFound{}, apistatus.Error},
checkAsErr: func(err error) bool {
var target *apistatus.SessionTokenNotFound
return errors.As(err, &target)
},
codeV2: 4096,
},
{
status: (statusConstructor)(func() error {
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.SessionTokenExpired)
}),
codeV2: 4097,
compatibleErrs: []error{apistatus.ErrSessionTokenExpired, apistatus.SessionTokenExpired{}, &apistatus.SessionTokenExpired{}, apistatus.Error},
checkAsErr: func(err error) bool {
var target *apistatus.SessionTokenExpired
return errors.As(err, &target)
},
codeV2: 4097,
},
{
status: (statusConstructor)(func() error {
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.NodeUnderMaintenance)
}),
codeV2: 1027,
compatibleErrs: []error{apistatus.ErrNodeUnderMaintenance, apistatus.NodeUnderMaintenance{}, &apistatus.NodeUnderMaintenance{}, apistatus.Error},
checkAsErr: func(err error) bool {
var target *apistatus.NodeUnderMaintenance
return errors.As(err, &target)
},
codeV2: 1027,
},
} {
var st error
cons, ok := testItem.status.(statusConstructor)
require.True(t, ok)
var st apistatus.Status
st = cons()
if cons, ok := testItem.status.(statusConstructor); ok {
st = cons()
} else {
st = testItem.status
}
stv2 := apistatus.ErrorToV2(st)
stv2 := apistatus.ToStatusV2(st)
// must generate the same status.Status message
require.EqualValues(t, testItem.codeV2, stv2.Code())
@ -198,25 +148,168 @@ func TestFromStatusV2(t *testing.T) {
require.Equal(t, testItem.messageV2, stv2.Message())
}
_, ok = st.(apistatus.StatusV2)
_, ok := st.(apistatus.StatusV2)
if ok {
// restore and convert again
restored := apistatus.ErrorFromV2(stv2)
restored := apistatus.FromStatusV2(stv2)
res := apistatus.ErrorToV2(restored)
res := apistatus.ToStatusV2(restored)
// must generate the same status.Status message
require.Equal(t, stv2, res)
}
}
}
randomError := errors.New("garbage")
for _, err := range testItem.compatibleErrs {
require.ErrorIs(t, st, err)
require.NotErrorIs(t, randomError, err)
func TestFromStatusV2(t *testing.T) {
type statusConstructor func() apistatus.Status
for _, testItem := range [...]struct {
status interface{} // Status or statusConstructor
codeV2 uint64
messageV2 string
}{
{
status: errors.New("some error"),
codeV2: 1024,
messageV2: "some error",
},
{
status: 1,
codeV2: 0,
},
{
status: "text",
codeV2: 0,
},
{
status: true,
codeV2: 0,
},
{
status: true,
codeV2: 0,
},
{
status: nil,
codeV2: 0,
},
{
status: (statusConstructor)(func() apistatus.Status {
var st apistatus.ServerInternal
st.SetMessage("internal error message")
return st
}),
codeV2: 1024,
},
{
status: (statusConstructor)(func() apistatus.Status {
var st apistatus.WrongMagicNumber
st.WriteCorrectMagic(322)
return st
}),
codeV2: 1025,
},
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.ObjectLocked)
}),
codeV2: 2050,
},
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.LockNonRegularObject)
}),
codeV2: 2051,
},
{
status: (statusConstructor)(func() apistatus.Status {
var st apistatus.ObjectAccessDenied
st.WriteReason("any reason")
return st
}),
codeV2: 2048,
},
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.ObjectNotFound)
}),
codeV2: 2049,
},
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.ObjectAlreadyRemoved)
}),
codeV2: 2052,
},
{
status: statusConstructor(func() apistatus.Status {
return new(apistatus.ObjectOutOfRange)
}),
codeV2: 2053,
},
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.ContainerNotFound)
}),
codeV2: 3072,
},
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.EACLNotFound)
}),
codeV2: 3073,
},
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.SessionTokenNotFound)
}),
codeV2: 4096,
},
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.SessionTokenExpired)
}),
codeV2: 4097,
},
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.NodeUnderMaintenance)
}),
codeV2: 1027,
},
} {
var st apistatus.Status
if cons, ok := testItem.status.(statusConstructor); ok {
st = cons()
} else {
st = testItem.status
}
if testItem.checkAsErr != nil {
require.True(t, testItem.checkAsErr(st))
stv2 := apistatus.ToStatusV2(st)
// must generate the same status.Status message
require.EqualValues(t, testItem.codeV2, stv2.Code())
if len(testItem.messageV2) > 0 {
require.Equal(t, testItem.messageV2, stv2.Message())
}
_, ok := st.(apistatus.StatusV2)
if ok {
// restore and convert again
restored := apistatus.FromStatusV2(stv2)
res := apistatus.ToStatusV2(restored)
// must generate the same status.Status message
require.Equal(t, stv2, res)
}
}
}

View file

@ -6,9 +6,9 @@ import (
"strings"
)
// Basic represents basic part of the NeoFS container's ACL. It includes
// Basic represents basic part of the FrostFS container's ACL. It includes
// common (pretty simple) access rules for operations inside the container.
// See NeoFS Specification for details.
// See FrostFS Specification for details.
//
// One can find some similarities with the traditional Unix permission, such as
//
@ -236,7 +236,7 @@ const (
NamePublicAppendExtended = "eacl-public-append"
)
// Frequently used Basic values. Bitmasks are taken from the NeoFS Specification.
// Frequently used Basic values. Bitmasks are taken from the FrostFS Specification.
const (
Private = Basic(0x1C8C8CCC) // private
PrivateExtended = Basic(0x0C8C8CCC) // eacl-private

View file

@ -1,7 +1,7 @@
/*
Package acl provides functionality to control access to data and operations on them in NeoFS containers.
Package acl provides functionality to control access to data and operations on them in FrostFS containers.
Type Basic represents basic ACL of the NeoFS container which specifies the general order of data access.
Type Basic represents basic ACL of the FrostFS container which specifies the general order of data access.
Basic provides interface of rule composition. Package acl also exports some frequently used settings like
private or public.
*/

View file

@ -1,40 +1,43 @@
package container
import (
"crypto/ecdsa"
"crypto/sha256"
"errors"
"fmt"
"strconv"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
"github.com/google/uuid"
"github.com/nspcc-dev/neofs-api-go/v2/container"
v2netmap "github.com/nspcc-dev/neofs-api-go/v2/netmap"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-sdk-go/container/acl"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/nspcc-dev/neofs-sdk-go/version"
)
// Container represents descriptor of the NeoFS container. Container logically
// stores NeoFS objects. Container is one of the basic and at the same time
// necessary data storage units in the NeoFS. Container includes data about the
// Container represents descriptor of the FrostFS container. Container logically
// stores FrostFS objects. Container is one of the basic and at the same time
// necessary data storage units in the FrostFS. Container includes data about the
// owner, rules for placing objects and other information necessary for the
// system functioning.
//
// Container type instances can represent different container states in the
// system, depending on the context. To create new container in NeoFS zero
// system, depending on the context. To create new container in FrostFS zero
// instance SHOULD be declared, initialized using Init method and filled using
// dedicated methods. Once container is saved in the NeoFS network, it can't be
// changed: containers stored in the system are immutable, and NeoFS is a CAS
// dedicated methods. Once container is saved in the FrostFS network, it can't be
// changed: containers stored in the system are immutable, and FrostFS is a CAS
// of containers that are identified by a fixed length value (see cid.ID type).
// Instances for existing containers can be initialized using decoding methods
// (e.g Unmarshal).
//
// Container is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/container.Container
// Container is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container.Container
// message. See ReadFromV2 / WriteToV2 methods.
type Container struct {
v2 container.Container
@ -115,6 +118,8 @@ func (x *Container) readFromV2(m container.Container, checkFieldPresence bool) e
}
switch key {
case container.SysAttributeSubnet:
err = new(subnetid.ID).DecodeString(val)
case attributeTimestamp:
_, err = strconv.ParseInt(val, 10, 64)
}
@ -132,7 +137,7 @@ func (x *Container) readFromV2(m container.Container, checkFieldPresence bool) e
}
// ReadFromV2 reads Container from the container.Container message. Checks if the
// message conforms to NeoFS API V2 protocol.
// message conforms to FrostFS API V2 protocol.
//
// See also WriteToV2.
func (x *Container) ReadFromV2(m container.Container) error {
@ -147,7 +152,7 @@ func (x Container) WriteToV2(m *container.Container) {
*m = x.v2
}
// Marshal encodes Container into a binary format of the NeoFS API protocol
// Marshal encodes Container into a binary format of the FrostFS API protocol
// (Protocol Buffers with direct field order).
//
// See also Unmarshal.
@ -155,7 +160,7 @@ func (x Container) Marshal() []byte {
return x.v2.StableMarshal(nil)
}
// Unmarshal decodes NeoFS API protocol binary format into the Container
// Unmarshal decodes FrostFS API protocol binary format into the Container
// (Protocol Buffers with direct field order). Returns an error describing
// a format violation.
//
@ -171,7 +176,7 @@ func (x *Container) Unmarshal(data []byte) error {
return x.readFromV2(m, false)
}
// MarshalJSON encodes Container into a JSON format of the NeoFS API protocol
// MarshalJSON encodes Container into a JSON format of the FrostFS API protocol
// (Protocol Buffers JSON).
//
// See also UnmarshalJSON.
@ -179,7 +184,7 @@ func (x Container) MarshalJSON() ([]byte, error) {
return x.v2.MarshalJSON()
}
// UnmarshalJSON decodes NeoFS API protocol JSON format into the Container
// UnmarshalJSON decodes FrostFS API protocol JSON format into the Container
// (Protocol Buffers JSON). Returns an error describing a format violation.
//
// See also MarshalJSON.
@ -187,7 +192,7 @@ func (x *Container) UnmarshalJSON(data []byte) error {
return x.v2.UnmarshalJSON(data)
}
// Init initializes all internal data of the Container required by NeoFS API
// Init initializes all internal data of the Container required by FrostFS API
// protocol. Init MUST be called when creating a new container. Init SHOULD NOT
// be called multiple times. Init SHOULD NOT be called if the Container instance
// is used for decoding only.
@ -207,7 +212,7 @@ func (x *Container) Init() {
// SetOwner specifies the owner of the Container. Each Container has exactly
// one owner, so SetOwner MUST be called for instances to be saved in the
// NeoFS.
// FrostFS.
//
// See also Owner.
func (x *Container) SetOwner(owner user.ID) {
@ -219,7 +224,7 @@ func (x *Container) SetOwner(owner user.ID) {
// Owner returns owner of the Container set using SetOwner.
//
// Zero Container has no owner which is incorrect according to NeoFS API
// Zero Container has no owner which is incorrect according to FrostFS API
// protocol.
func (x Container) Owner() (res user.ID) {
m := x.v2.GetOwnerID()
@ -251,7 +256,7 @@ func (x Container) BasicACL() (res acl.Basic) {
}
// SetPlacementPolicy sets placement policy for the objects within the Container.
// NeoFS storage layer strives to follow the specified policy.
// FrostFS storage layer strives to follow the specified policy.
//
// See also PlacementPolicy.
func (x *Container) SetPlacementPolicy(policy netmap.PlacementPolicy) {
@ -264,7 +269,7 @@ func (x *Container) SetPlacementPolicy(policy netmap.PlacementPolicy) {
// PlacementPolicy returns placement policy set using SetPlacementPolicy.
//
// Zero Container has no placement policy which is incorrect according to
// NeoFS API protocol.
// FrostFS API protocol.
func (x Container) PlacementPolicy() (res netmap.PlacementPolicy) {
m := x.v2.GetPlacementPolicy()
if m != nil {
@ -279,7 +284,7 @@ func (x Container) PlacementPolicy() (res netmap.PlacementPolicy) {
// SetAttribute sets Container attribute value by key. Both key and value
// MUST NOT be empty. Attributes set by the creator (owner) are most commonly
// ignored by the NeoFS system and used for application layer. Some attributes
// ignored by the FrostFS system and used for application layer. Some attributes
// are so-called system or well-known attributes: they are reserved for system
// needs. System attributes SHOULD NOT be modified using SetAttribute, use
// corresponding methods/functions. List of the reserved keys is documented
@ -378,6 +383,28 @@ func CreatedAt(cnr Container) time.Time {
return time.Unix(sec, 0)
}
// SetSubnet places the Container on the specified FrostFS subnet. If called,
// container nodes will only be selected from the given subnet, otherwise from
// the entire network.
func SetSubnet(cnr *Container, subNet subnetid.ID) {
cnr.SetAttribute(container.SysAttributeSubnet, subNet.EncodeToString())
}
// Subnet return container subnet set using SetSubnet.
//
// Zero Container is bound to zero subnet.
func Subnet(cnr Container) (res subnetid.ID) {
val := cnr.Attribute(container.SysAttributeSubnet)
if val != "" {
err := res.DecodeString(val)
if err != nil {
panic(fmt.Sprintf("invalid subnet attribute: %s (%v)", val, err))
}
}
return
}
const attributeHomoHashEnabled = "true"
// DisableHomomorphicHashing sets flag to disable homomorphic hashing of the
@ -392,11 +419,12 @@ func DisableHomomorphicHashing(cnr *Container) {
//
// Zero Container has enabled hashing.
func IsHomomorphicHashingDisabled(cnr Container) bool {
return cnr.Attribute(container.SysAttributeHomomorphicHashing) == attributeHomoHashEnabled
return cnr.Attribute(container.SysAttributeHomomorphicHashing) == attributeHomoHashEnabled ||
cnr.Attribute(container.SysAttributeHomomorphicHashingNeoFS) == attributeHomoHashEnabled
}
// Domain represents information about container domain registered in the NNS
// contract deployed in the NeoFS network.
// contract deployed in the FrostFS network.
type Domain struct {
name, zone string
}
@ -438,10 +466,12 @@ func WriteDomain(cnr *Container, domain Domain) {
// ReadDomain reads Domain from the Container. Returns value with empty name
// if domain is not specified.
func ReadDomain(cnr Container) (res Domain) {
name := cnr.Attribute(container.SysAttributeName)
if name != "" {
if name := cnr.Attribute(container.SysAttributeName); name != "" {
res.SetName(name)
res.SetZone(cnr.Attribute(container.SysAttributeZone))
} else if name = cnr.Attribute(container.SysAttributeNameNeoFS); name != "" {
res.SetName(name)
res.SetZone(cnr.Attribute(container.SysAttributeZoneNeoFS))
}
return
@ -450,30 +480,22 @@ func ReadDomain(cnr Container) (res Domain) {
// CalculateSignature calculates signature of the Container using provided signer
// and writes it into dst. Signature instance MUST NOT be nil. CalculateSignature
// is expected to be called after all the Container data is filled and before
// saving the Container in the NeoFS network. Note that мany subsequent change
// will most likely break the signature. signer MUST be of
// [neofscrypto.ECDSA_DETERMINISTIC_SHA256] scheme, for example, [neofsecdsa.SignerRFC6979]
// can be used.
// saving the Container in the FrostFS network. Note that мany subsequent change
// will most likely break the signature.
//
// See also VerifySignature.
//
// Returned errors:
// - [neofscrypto.ErrIncorrectSigner]
func CalculateSignature(dst *neofscrypto.Signature, cnr Container, signer neofscrypto.Signer) error {
if signer.Scheme() != neofscrypto.ECDSA_DETERMINISTIC_SHA256 {
return fmt.Errorf("%w: expected ECDSA_DETERMINISTIC_SHA256 scheme", neofscrypto.ErrIncorrectSigner)
}
return dst.Calculate(signer, cnr.Marshal())
func CalculateSignature(dst *frostfscrypto.Signature, cnr Container, signer ecdsa.PrivateKey) error {
return dst.Calculate(frostfsecdsa.SignerRFC6979(signer), cnr.Marshal())
}
// VerifySignature verifies Container signature calculated using CalculateSignature.
// Result means signature correctness.
func VerifySignature(sig neofscrypto.Signature, cnr Container) bool {
func VerifySignature(sig frostfscrypto.Signature, cnr Container) bool {
return sig.Verify(cnr.Marshal())
}
// CalculateIDFromBinary calculates identifier of the binary-encoded container
// in CAS of the NeoFS containers and writes it into dst. ID instance MUST NOT
// in CAS of the FrostFS containers and writes it into dst. ID instance MUST NOT
// be nil.
//
// See also CalculateID, AssertID.
@ -490,7 +512,7 @@ func CalculateID(dst *cid.ID, cnr Container) {
}
// AssertID checks if the given Container matches its identifier in CAS of the
// NeoFS containers.
// FrostFS containers.
//
// See also CalculateID.
func AssertID(id cid.ID, cnr Container) bool {

View file

@ -6,24 +6,26 @@ import (
"testing"
"time"
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
containertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/test"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test"
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
subnetidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id/test"
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
"github.com/google/uuid"
v2container "github.com/nspcc-dev/neofs-api-go/v2/container"
v2netmap "github.com/nspcc-dev/neofs-api-go/v2/netmap"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-sdk-go/container"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
containertest "github.com/nspcc-dev/neofs-sdk-go/container/test"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/crypto/test"
netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
"github.com/nspcc-dev/neofs-sdk-go/version"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require"
)
func TestPlacementPolicyEncoding(t *testing.T) {
v := containertest.Container(t)
v := containertest.Container()
t.Run("binary", func(t *testing.T) {
var v2 container.Container
@ -44,7 +46,7 @@ func TestPlacementPolicyEncoding(t *testing.T) {
}
func TestContainer_Init(t *testing.T) {
val := containertest.Container(t)
val := containertest.Container()
val.Init()
@ -76,9 +78,9 @@ func TestContainer_Owner(t *testing.T) {
require.Zero(t, val.Owner())
val = containertest.Container(t)
val = containertest.Container()
owner := *usertest.ID(t)
owner := *usertest.ID()
val.SetOwner(owner)
@ -101,7 +103,7 @@ func TestContainer_BasicACL(t *testing.T) {
require.Zero(t, val.BasicACL())
val = containertest.Container(t)
val = containertest.Container()
basicACL := containertest.BasicACL()
val.SetBasicACL(basicACL)
@ -122,7 +124,7 @@ func TestContainer_PlacementPolicy(t *testing.T) {
require.Zero(t, val.PlacementPolicy())
val = containertest.Container(t)
val = containertest.Container()
pp := netmaptest.PlacementPolicy()
val.SetPlacementPolicy(pp)
@ -153,7 +155,7 @@ func TestContainer_Attribute(t *testing.T) {
const attrKey1, attrKey2 = "key1", "key2"
const attrVal1, attrVal2 = "val1", "val2"
val := containertest.Container(t)
val := containertest.Container()
val.SetAttribute(attrKey1, attrVal1)
val.SetAttribute(attrKey2, attrVal2)
@ -192,7 +194,7 @@ func TestSetName(t *testing.T) {
container.SetName(&val, "")
})
val = containertest.Container(t)
val = containertest.Container()
const name = "some name"
@ -214,7 +216,7 @@ func TestSetCreationTime(t *testing.T) {
require.Zero(t, container.CreatedAt(val).Unix())
val = containertest.Container(t)
val = containertest.Container()
creat := time.Now()
@ -231,12 +233,34 @@ func TestSetCreationTime(t *testing.T) {
require.Equal(t, creat.Unix(), container.CreatedAt(val2).Unix())
}
func TestSetSubnet(t *testing.T) {
var val container.Container
require.True(t, subnetid.IsZero(container.Subnet(val)))
val = containertest.Container()
sub := subnetidtest.ID()
container.SetSubnet(&val, sub)
var msg v2container.Container
val.WriteToV2(&msg)
assertContainsAttribute(t, msg, v2container.SysAttributeSubnet, sub.EncodeToString())
var val2 container.Container
require.NoError(t, val2.ReadFromV2(msg))
require.Equal(t, sub, container.Subnet(val))
}
func TestDisableHomomorphicHashing(t *testing.T) {
var val container.Container
require.False(t, container.IsHomomorphicHashingDisabled(val))
val = containertest.Container(t)
val = containertest.Container()
container.DisableHomomorphicHashing(&val)
@ -256,7 +280,7 @@ func TestWriteDomain(t *testing.T) {
require.Zero(t, container.ReadDomain(val).Name())
val = containertest.Container(t)
val = containertest.Container()
const name = "domain name"
@ -288,7 +312,7 @@ func TestWriteDomain(t *testing.T) {
}
func TestCalculateID(t *testing.T) {
val := containertest.Container(t)
val := containertest.Container()
require.False(t, container.AssertID(cidtest.ID(), val))
@ -308,17 +332,19 @@ func TestCalculateID(t *testing.T) {
}
func TestCalculateSignature(t *testing.T) {
val := containertest.Container(t)
key, err := keys.NewPrivateKey()
require.NoError(t, err)
var sig neofscrypto.Signature
val := containertest.Container()
require.Error(t, container.CalculateSignature(&sig, val, test.RandomSigner(t)))
require.NoError(t, container.CalculateSignature(&sig, val, test.RandomSignerRFC6979(t)))
var sig frostfscrypto.Signature
require.NoError(t, container.CalculateSignature(&sig, val, key.PrivateKey))
var msg refs.Signature
sig.WriteToV2(&msg)
var sig2 neofscrypto.Signature
var sig2 frostfscrypto.Signature
require.NoError(t, sig2.ReadFromV2(msg))
require.True(t, container.VerifySignature(sig2, val))

View file

@ -1,7 +1,7 @@
/*
Package container provides functionality related to the NeoFS containers.
Package container provides functionality related to the FrostFS containers.
The base type is Container. To create new container in the NeoFS network
The base type is Container. To create new container in the FrostFS network
Container instance should be initialized
var cnr Container
@ -10,7 +10,7 @@ Container instance should be initialized
// encode cnr and send
After the container is persisted in the NeoFS network, applications can process
After the container is persisted in the FrostFS network, applications can process
it using the instance of Container types
// recv binary container
@ -22,12 +22,12 @@ it using the instance of Container types
// process the container data
Instances can be also used to process NeoFS API V2 protocol messages
(see neo.fs.v2.container package in https://github.com/nspcc-dev/neofs-api).
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).
On client side:
import "github.com/nspcc-dev/neofs-api-go/v2/container"
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
var msg container.Container
cnr.WriteToV2(&msg)

View file

@ -1,5 +1,5 @@
/*
Package cid provides primitives to work with container identification in NeoFS.
Package cid provides primitives to work with container identification in FrostFS.
Using package types in an application is recommended to potentially work with
different protocol versions with which these types are compatible.

View file

@ -4,13 +4,13 @@ import (
"crypto/sha256"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
)
// ID represents NeoFS container identifier.
// ID represents FrostFS container identifier.
//
// ID is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.ContainerID
// ID is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.ContainerID
// message. See ReadFromV2 / WriteToV2 methods.
//
// Instances can be created using built-in var declaration.
@ -22,7 +22,7 @@ type ID [sha256.Size]byte
// ReadFromV2 reads ID from the refs.ContainerID message.
// Returns an error if the message is malformed according
// to the NeoFS API V2 protocol.
// to the FrostFS API V2 protocol.
//
// See also WriteToV2.
func (id *ID) ReadFromV2(m refs.ContainerID) error {
@ -83,7 +83,7 @@ func (id ID) Equals(id2 ID) bool {
return id == id2
}
// EncodeToString encodes ID into NeoFS API protocol string.
// EncodeToString encodes ID into FrostFS API protocol string.
//
// Zero ID is base58 encoding of 32 zeros.
//
@ -92,7 +92,7 @@ func (id ID) EncodeToString() string {
return base58.Encode(id[:])
}
// DecodeString decodes string into ID according to NeoFS API protocol. Returns
// DecodeString decodes string into ID according to FrostFS API protocol. Returns
// an error if s is malformed.
//
// See also DecodeString.
@ -109,7 +109,7 @@ func (id *ID) DecodeString(s string) error {
//
// String is designed to be human-readable, and its format MAY differ between
// SDK versions. String MAY return same result as EncodeToString. String MUST NOT
// be used to encode ID into NeoFS protocol string.
// be used to encode ID into FrostFS protocol string.
func (id ID) String() string {
return id.EncodeToString()
}

View file

@ -5,10 +5,10 @@ import (
"math/rand"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
"github.com/stretchr/testify/require"
)

View file

@ -5,7 +5,7 @@ Note that importing the package into source files is highly discouraged.
Random instance generation functions can be useful when testing expects any value, e.g.:
import cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
import cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
cid := cidtest.ID()
// test the value

View file

@ -4,7 +4,7 @@ import (
"crypto/sha256"
"math/rand"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
)
// ID returns random cid.ID.

View file

@ -1,7 +1,7 @@
package container
import (
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
)
// ApplyNetworkConfig applies network configuration to the

View file

@ -3,14 +3,14 @@ package container_test
import (
"testing"
"github.com/nspcc-dev/neofs-sdk-go/container"
containertest "github.com/nspcc-dev/neofs-sdk-go/container/test"
netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
containertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/test"
netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test"
"github.com/stretchr/testify/require"
)
func TestContainer_NetworkConfig(t *testing.T) {
c := containertest.Container(t)
c := containertest.Container()
nc := netmaptest.NetworkInfo()
t.Run("default", func(t *testing.T) {

View file

@ -4,22 +4,22 @@ import (
"errors"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/container"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
)
// SizeEstimation groups information about estimation of the size of the data
// stored in the NeoFS container.
// stored in the FrostFS container.
//
// SizeEstimation is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/container.UsedSpaceAnnouncement
// SizeEstimation is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container.UsedSpaceAnnouncement
// message. See ReadFromV2 / WriteToV2 methods.
type SizeEstimation struct {
m container.UsedSpaceAnnouncement
}
// ReadFromV2 reads SizeEstimation from the container.UsedSpaceAnnouncement message.
// Checks if the message conforms to NeoFS API V2 protocol.
// Checks if the message conforms to FrostFS API V2 protocol.
//
// See also WriteToV2.
func (x *SizeEstimation) ReadFromV2(m container.UsedSpaceAnnouncement) error {
@ -63,7 +63,7 @@ func (x SizeEstimation) Epoch() uint64 {
}
// SetContainer specifies the container for which the amount of data is estimated.
// Required by the NeoFS API protocol.
// Required by the FrostFS API protocol.
//
// See also Container.
func (x *SizeEstimation) SetContainer(cnr cid.ID) {
@ -76,7 +76,7 @@ func (x *SizeEstimation) SetContainer(cnr cid.ID) {
// Container returns container set using SetContainer.
//
// Zero SizeEstimation is not bound to any container (returns zero) which is
// incorrect according to NeoFS API protocol.
// incorrect according to FrostFS API protocol.
func (x SizeEstimation) Container() (res cid.ID) {
m := x.m.GetContainerID()
if m != nil {

View file

@ -4,11 +4,11 @@ import (
"crypto/sha256"
"testing"
v2container "github.com/nspcc-dev/neofs-api-go/v2/container"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-sdk-go/container"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
"github.com/stretchr/testify/require"
)

View file

@ -2,18 +2,17 @@ package containertest
import (
"math/rand"
"testing"
"github.com/nspcc-dev/neofs-sdk-go/container"
"github.com/nspcc-dev/neofs-sdk-go/container/acl"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test"
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
)
// Container returns random container.Container.
func Container(t *testing.T) (x container.Container) {
owner := usertest.ID(t)
func Container() (x container.Container) {
owner := usertest.ID()
x.Init()
x.SetAttribute("some attribute", "value")

View file

@ -1,13 +1,13 @@
package neofscrypto_test
package frostfscrypto_test
import (
"math/rand"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/stretchr/testify/require"
)
@ -18,18 +18,18 @@ func TestSignature(t *testing.T) {
k, err := keys.NewPrivateKey()
require.NoError(t, err)
var s neofscrypto.Signature
var s frostfscrypto.Signature
var m refs.Signature
for _, f := range []func() neofscrypto.Signer{
func() neofscrypto.Signer {
return neofsecdsa.Signer(k.PrivateKey)
for _, f := range []func() frostfscrypto.Signer{
func() frostfscrypto.Signer {
return frostfsecdsa.Signer(k.PrivateKey)
},
func() neofscrypto.Signer {
return neofsecdsa.SignerRFC6979(k.PrivateKey)
func() frostfscrypto.Signer {
return frostfsecdsa.SignerRFC6979(k.PrivateKey)
},
func() neofscrypto.Signer {
return neofsecdsa.SignerWalletConnect(k.PrivateKey)
func() frostfscrypto.Signer {
return frostfsecdsa.SignerWalletConnect(k.PrivateKey)
},
} {
signer := f()

View file

@ -1,7 +1,7 @@
/*
Package neofscrypto collects NeoFS cryptographic primitives.
Package frostfscrypto collects FrostFS cryptographic primitives.
Signer type unifies entities for signing NeoFS data.
Signer type unifies entities for signing FrostFS data.
// instantiate Signer
// select data to be signed
@ -24,12 +24,12 @@ PublicKey allows to verify signatures.
isValid := sig.Verify(data)
// ...
Signature can be also used to process NeoFS API V2 protocol messages
(see neo.fs.v2.refs package in https://github.com/nspcc-dev/neofs-api).
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).
On client side:
import "github.com/nspcc-dev/neofs-api-go/v2/refs"
import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
var msg refs.Signature
sig.WriteToV2(&msg)
@ -40,7 +40,7 @@ On server side:
// recv msg
var sig neofscrypto.Signature
var sig frostfscrypto.Signature
sig.ReadFromV2(msg)
// process sig
@ -48,4 +48,4 @@ On server side:
Using package types in an application is recommended to potentially work with
different protocol versions with which these types are compatible.
*/
package neofscrypto
package frostfscrypto

View file

@ -1,12 +1,12 @@
/*
Package neofsecdsa collects ECDSA primitives for NeoFS cryptography.
Package frostfsecdsa collects ECDSA primitives for FrostFS cryptography.
Signer and PublicKey support ECDSA signature algorithm with SHA-512 hashing.
SignerRFC6979 and PublicKeyRFC6979 implement signature algorithm described in RFC 6979.
All these types provide corresponding interfaces from neofscrypto package.
All these types provide corresponding interfaces from frostfscrypto package.
Package import causes registration of next signature schemes via neofscrypto.RegisterScheme:
- neofscrypto.ECDSA_SHA512
- neofscrypto.ECDSA_DETERMINISTIC_SHA256
Package import causes registration of next signature schemes via frostfscrypto.RegisterScheme:
- frostfscrypto.ECDSA_SHA512
- frostfscrypto.ECDSA_DETERMINISTIC_SHA256
*/
package neofsecdsa
package frostfsecdsa

View file

@ -1,17 +1,17 @@
package neofsecdsa
package frostfsecdsa
import neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
import frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
func init() {
neofscrypto.RegisterScheme(neofscrypto.ECDSA_SHA512, func() neofscrypto.PublicKey {
frostfscrypto.RegisterScheme(frostfscrypto.ECDSA_SHA512, func() frostfscrypto.PublicKey {
return new(PublicKey)
})
neofscrypto.RegisterScheme(neofscrypto.ECDSA_DETERMINISTIC_SHA256, func() neofscrypto.PublicKey {
frostfscrypto.RegisterScheme(frostfscrypto.ECDSA_DETERMINISTIC_SHA256, func() frostfscrypto.PublicKey {
return new(PublicKeyRFC6979)
})
neofscrypto.RegisterScheme(neofscrypto.ECDSA_WALLETCONNECT, func() neofscrypto.PublicKey {
frostfscrypto.RegisterScheme(frostfscrypto.ECDSA_WALLETCONNECT, func() frostfscrypto.PublicKey {
return new(PublicKeyWalletConnect)
})
}

View file

@ -1,4 +1,4 @@
package neofsecdsa
package frostfsecdsa
import (
"crypto/ecdsa"
@ -11,8 +11,8 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
)
// PublicKey is a wrapper over ecdsa.PublicKey used for NeoFS needs.
// Provides neofscrypto.PublicKey interface.
// PublicKey is a wrapper over ecdsa.PublicKey used for FrostFS needs.
// Provides frostfscrypto.PublicKey interface.
//
// Instances MUST be initialized from ecdsa.PublicKey using type conversion.
type PublicKey ecdsa.PublicKey
@ -77,8 +77,8 @@ func (x PublicKey) Verify(data, signature []byte) bool {
return r != nil && s != nil && ecdsa.Verify((*ecdsa.PublicKey)(&x), h[:], r, s)
}
// PublicKeyRFC6979 is a wrapper over ecdsa.PublicKey used for NeoFS needs.
// Provides neofscrypto.PublicKey interface.
// PublicKeyRFC6979 is a wrapper over ecdsa.PublicKey used for FrostFS needs.
// Provides frostfscrypto.PublicKey interface.
//
// Instances MUST be initialized from ecdsa.PublicKey using type conversion.
type PublicKeyRFC6979 ecdsa.PublicKey

View file

@ -1,4 +1,4 @@
package neofsecdsa
package frostfsecdsa
import (
"crypto/ecdsa"
@ -6,24 +6,24 @@ import (
"crypto/rand"
"crypto/sha512"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
)
// Signer wraps ecdsa.PrivateKey and represents signer based on ECDSA with
// SHA-512 hashing. Provides neofscrypto.Signer interface.
// SHA-512 hashing. Provides frostfscrypto.Signer interface.
//
// Instances MUST be initialized from ecdsa.PrivateKey using type conversion.
type Signer ecdsa.PrivateKey
// Scheme returns neofscrypto.ECDSA_SHA512.
// Implements neofscrypto.Signer.
func (x Signer) Scheme() neofscrypto.Scheme {
return neofscrypto.ECDSA_SHA512
// Scheme returns frostfscrypto.ECDSA_SHA512.
// Implements frostfscrypto.Signer.
func (x Signer) Scheme() frostfscrypto.Scheme {
return frostfscrypto.ECDSA_SHA512
}
// Sign signs data using ECDSA algorithm with SHA-512 hashing.
// Implements neofscrypto.Signer.
// Implements frostfscrypto.Signer.
func (x Signer) Sign(data []byte) ([]byte, error) {
h := sha512.Sum512(data)
r, s, err := ecdsa.Sign(rand.Reader, (*ecdsa.PrivateKey)(&x), h[:])
@ -43,26 +43,26 @@ func (x Signer) Sign(data []byte) ([]byte, error) {
return buf, nil
}
// Public initializes PublicKey and returns it as neofscrypto.PublicKey.
// Implements neofscrypto.Signer.
func (x Signer) Public() neofscrypto.PublicKey {
// Public initializes PublicKey and returns it as frostfscrypto.PublicKey.
// Implements frostfscrypto.Signer.
func (x Signer) Public() frostfscrypto.PublicKey {
return (*PublicKey)(&x.PublicKey)
}
// SignerRFC6979 wraps ecdsa.PrivateKey and represents signer based on deterministic
// ECDSA with SHA-256 hashing (RFC 6979). Provides neofscrypto.Signer interface.
// ECDSA with SHA-256 hashing (RFC 6979). Provides frostfscrypto.Signer interface.
//
// Instances SHOULD be initialized from ecdsa.PrivateKey using type conversion.
type SignerRFC6979 ecdsa.PrivateKey
// Scheme returns neofscrypto.ECDSA_DETERMINISTIC_SHA256.
// Implements neofscrypto.Signer.
func (x SignerRFC6979) Scheme() neofscrypto.Scheme {
return neofscrypto.ECDSA_DETERMINISTIC_SHA256
// Scheme returns frostfscrypto.ECDSA_DETERMINISTIC_SHA256.
// Implements frostfscrypto.Signer.
func (x SignerRFC6979) Scheme() frostfscrypto.Scheme {
return frostfscrypto.ECDSA_DETERMINISTIC_SHA256
}
// Sign signs data using deterministic ECDSA algorithm with SHA-256 hashing.
// Implements neofscrypto.Signer.
// Implements frostfscrypto.Signer.
//
// See also RFC 6979.
func (x SignerRFC6979) Sign(data []byte) ([]byte, error) {
@ -70,8 +70,8 @@ func (x SignerRFC6979) Sign(data []byte) ([]byte, error) {
return p.Sign(data), nil
}
// Public initializes PublicKeyRFC6979 and returns it as neofscrypto.PublicKey.
// Implements neofscrypto.Signer.
func (x SignerRFC6979) Public() neofscrypto.PublicKey {
// Public initializes PublicKeyRFC6979 and returns it as frostfscrypto.PublicKey.
// Implements frostfscrypto.Signer.
func (x SignerRFC6979) Public() frostfscrypto.PublicKey {
return (*PublicKeyRFC6979)(&x.PublicKey)
}

View file

@ -1,4 +1,4 @@
package neofsecdsa
package frostfsecdsa
import (
"crypto/ecdsa"
@ -6,9 +6,9 @@ import (
"encoding/base64"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/signature/walletconnect"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-api-go/v2/util/signature/walletconnect"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
)
// SignerWalletConnect is similar to SignerRFC6979 with 2 changes:
@ -18,28 +18,28 @@ import (
// Instances MUST be initialized from ecdsa.PrivateKey using type conversion.
type SignerWalletConnect ecdsa.PrivateKey
// Scheme returns neofscrypto.ECDSA_WALLETCONNECT.
// Implements neofscrypto.Signer.
func (x SignerWalletConnect) Scheme() neofscrypto.Scheme {
return neofscrypto.ECDSA_WALLETCONNECT
// Scheme returns frostfscrypto.ECDSA_WALLETCONNECT.
// Implements frostfscrypto.Signer.
func (x SignerWalletConnect) Scheme() frostfscrypto.Scheme {
return frostfscrypto.ECDSA_WALLETCONNECT
}
// Sign signs data using ECDSA algorithm with SHA-512 hashing.
// Implements neofscrypto.Signer.
// Implements frostfscrypto.Signer.
func (x SignerWalletConnect) Sign(data []byte) ([]byte, error) {
b64 := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
base64.StdEncoding.Encode(b64, data)
return walletconnect.Sign((*ecdsa.PrivateKey)(&x), b64)
}
// Public initializes PublicKey and returns it as neofscrypto.PublicKey.
// Implements neofscrypto.Signer.
func (x SignerWalletConnect) Public() neofscrypto.PublicKey {
// Public initializes PublicKey and returns it as frostfscrypto.PublicKey.
// Implements frostfscrypto.Signer.
func (x SignerWalletConnect) Public() frostfscrypto.PublicKey {
return (*PublicKeyWalletConnect)(&x.PublicKey)
}
// PublicKeyWalletConnect is a wrapper over ecdsa.PublicKey used for NeoFS needs.
// Provides neofscrypto.PublicKey interface.
// PublicKeyWalletConnect is a wrapper over ecdsa.PublicKey used for FrostFS needs.
// Provides frostfscrypto.PublicKey interface.
//
// Instances MUST be initialized from ecdsa.PublicKey using type conversion.
type PublicKeyWalletConnect ecdsa.PublicKey

View file

@ -1,16 +1,16 @@
package neofscrypto
package frostfscrypto
import (
"errors"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
)
// Signature represents a confirmation of data integrity received by the
// digital signature mechanism.
//
// Signature is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.Signature
// Signature is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.Signature
// message. See ReadFromV2 / WriteToV2 methods.
//
// Note that direct typecast is not safe and may result in loss of compatibility:
@ -19,7 +19,7 @@ import (
type Signature refs.Signature
// ReadFromV2 reads Signature from the refs.Signature message. Checks if the
// message conforms to NeoFS API V2 protocol.
// message conforms to FrostFS API V2 protocol.
//
// See also WriteToV2.
func (x *Signature) ReadFromV2(m refs.Signature) error {

View file

@ -1,18 +1,11 @@
package neofscrypto
package frostfscrypto
import (
"errors"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
)
// ErrIncorrectSigner is returned from function when the signer passed to it
// is incompatible with the function requirements. This variable is intended
// to be used as documentation and for [errors.Is] purposes and MUST NOT be
// changed.
var ErrIncorrectSigner = errors.New("incorrect signer")
// Scheme represents digital signature algorithm with fixed cryptographic hash function.
//
// Negative values are reserved and depend on context (e.g. unsupported scheme).
@ -52,7 +45,7 @@ func RegisterScheme(scheme Scheme, f func() PublicKey) {
}
// Signer is an interface of entities that can be used for signing operations
// in NeoFS. Unites secret and public parts. For example, an ECDSA private key
// in FrostFS. Unites secret and public parts. For example, an ECDSA private key
// or external auth service.
//
// See also PublicKey.
@ -70,7 +63,7 @@ type Signer interface {
}
// PublicKey represents a public key using fixed signature scheme supported by
// NeoFS.
// FrostFS.
//
// See also Signer.
type PublicKey interface {
@ -99,38 +92,3 @@ type PublicKey interface {
// Verify checks signature of the given data. True means correct signature.
Verify(data, signature []byte) bool
}
// StaticSigner emulates real sign and contains already precalculated hash.
// Provides neofscrypto.Signer interface.
type StaticSigner struct {
scheme Scheme
sig []byte
pubKey PublicKey
}
// NewStaticSigner creates new StaticSigner.
func NewStaticSigner(scheme Scheme, sig []byte, pubKey PublicKey) *StaticSigner {
return &StaticSigner{
scheme: scheme,
sig: sig,
pubKey: pubKey,
}
}
// Scheme returns neofscrypto.ECDSA_DETERMINISTIC_SHA256.
// Implements neofscrypto.Signer.
func (s *StaticSigner) Scheme() Scheme {
return s.scheme
}
// Sign returns precalculated hash.
// Implements neofscrypto.Signer.
func (s *StaticSigner) Sign(_ []byte) ([]byte, error) {
return s.sig, nil
}
// Public returns neofscrypto.PublicKey.
// Implements neofscrypto.Signer.
func (s *StaticSigner) Public() PublicKey {
return s.pubKey
}

View file

@ -1,33 +0,0 @@
/*
Package tests provides special help functions for testing NeoFS API and its environment.
All functions accepting `t *testing.T` that emphasize there are only for tests purposes.
*/
package test
import (
"testing"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/stretchr/testify/require"
)
// RandomSigner return neofscrypto.Signer ONLY for TESTs purposes.
// It may be used like helper to get new neofscrypto.Signer if you need it in yours tests.
func RandomSigner(tb testing.TB) neofscrypto.Signer {
p, err := keys.NewPrivateKey()
require.NoError(tb, err)
return neofsecdsa.Signer(p.PrivateKey)
}
// RandomSignerRFC6979 return neofscrypto.Signer ONLY for TESTs purposes.
// It may be used like helper to get new neofscrypto.Signer if you need it in yours tests.
func RandomSignerRFC6979(tb testing.TB) neofscrypto.Signer {
p, err := keys.NewPrivateKey()
require.NoError(tb, err)
return neofsecdsa.SignerRFC6979(p.PrivateKey)
}

View file

@ -1,4 +1,4 @@
package neofscrypto
package frostfscrypto
import "encoding/hex"

View file

@ -1,7 +1,7 @@
package eacl
import (
v2acl "github.com/nspcc-dev/neofs-api-go/v2/acl"
v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
)
// Action taken if ContainerEACL record matched request.
@ -127,30 +127,21 @@ func ActionFromV2(action v2acl.Action) (a Action) {
return a
}
// EncodeToString returns string representation of Action.
// String returns string representation of Action.
//
// String mapping:
// - ActionAllow: ALLOW;
// - ActionDeny: DENY;
// - ActionUnknown, default: ACTION_UNSPECIFIED.
func (a Action) EncodeToString() string {
func (a Action) String() string {
return a.ToV2().String()
}
// String implements fmt.Stringer.
//
// String is designed to be human-readable, and its format MAY differ between
// SDK versions. String MAY return same result as EncodeToString. String MUST NOT
// be used to encode ID into NeoFS protocol string.
func (a Action) String() string {
return a.EncodeToString()
}
// DecodeString parses Action from a string representation.
// It is a reverse action to EncodeToString().
// FromString parses Action from a string representation.
// It is a reverse action to String().
//
// Returns true if s was parsed successfully.
func (a *Action) DecodeString(s string) bool {
func (a *Action) FromString(s string) bool {
var g v2acl.Action
ok := g.FromString(s)
@ -208,7 +199,7 @@ func OperationFromV2(operation v2acl.Operation) (o Operation) {
return o
}
// EncodeToString returns string representation of Operation.
// String returns string representation of Operation.
//
// String mapping:
// - OperationGet: GET;
@ -219,24 +210,15 @@ func OperationFromV2(operation v2acl.Operation) (o Operation) {
// - OperationRange: GETRANGE;
// - OperationRangeHash: GETRANGEHASH;
// - OperationUnknown, default: OPERATION_UNSPECIFIED.
func (o Operation) EncodeToString() string {
func (o Operation) String() string {
return o.ToV2().String()
}
// String implements fmt.Stringer.
//
// String is designed to be human-readable, and its format MAY differ between
// SDK versions. String MAY return same result as EncodeToString. String MUST NOT
// be used to encode ID into NeoFS protocol string.
func (o Operation) String() string {
return o.EncodeToString()
}
// DecodeString parses Operation from a string representation.
// It is a reverse action to EncodeToString().
// FromString parses Operation from a string representation.
// It is a reverse action to String().
//
// Returns true if s was parsed successfully.
func (o *Operation) DecodeString(s string) bool {
func (o *Operation) FromString(s string) bool {
var g v2acl.Operation
ok := g.FromString(s)
@ -278,31 +260,22 @@ func RoleFromV2(role v2acl.Role) (r Role) {
return r
}
// EncodeToString returns string representation of Role.
// String returns string representation of Role.
//
// String mapping:
// - RoleUser: USER;
// - RoleSystem: SYSTEM;
// - RoleOthers: OTHERS;
// - RoleUnknown, default: ROLE_UNKNOWN.
func (r Role) EncodeToString() string {
func (r Role) String() string {
return r.ToV2().String()
}
// String implements fmt.Stringer.
//
// String is designed to be human-readable, and its format MAY differ between
// SDK versions. String MAY return same result as EncodeToString. String MUST NOT
// be used to encode ID into NeoFS protocol string.
func (r Role) String() string {
return r.EncodeToString()
}
// DecodeString parses Role from a string representation.
// It is a reverse action to EncodeToString().
// FromString parses Role from a string representation.
// It is a reverse action to String().
//
// Returns true if s was parsed successfully.
func (r *Role) DecodeString(s string) bool {
func (r *Role) FromString(s string) bool {
var g v2acl.Role
ok := g.FromString(s)
@ -340,30 +313,21 @@ func MatchFromV2(match v2acl.MatchType) (m Match) {
return m
}
// EncodeToString returns string representation of Match.
// String returns string representation of Match.
//
// String mapping:
// - MatchStringEqual: STRING_EQUAL;
// - MatchStringNotEqual: STRING_NOT_EQUAL;
// - MatchUnknown, default: MATCH_TYPE_UNSPECIFIED.
func (m Match) EncodeToString() string {
func (m Match) String() string {
return m.ToV2().String()
}
// String implements fmt.Stringer.
//
// String is designed to be human-readable, and its format MAY differ between
// SDK versions. String MAY return same result as EncodeToString. String MUST NOT
// be used to encode ID into NeoFS protocol string.
func (m Match) String() string {
return m.EncodeToString()
}
// DecodeString parses Match from a string representation.
// It is a reverse action to EncodeToString().
// FromString parses Match from a string representation.
// It is a reverse action to String().
//
// Returns true if s was parsed successfully.
func (m *Match) DecodeString(s string) bool {
func (m *Match) FromString(s string) bool {
var g v2acl.MatchType
ok := g.FromString(s)
@ -405,30 +369,21 @@ func FilterHeaderTypeFromV2(header v2acl.HeaderType) (h FilterHeaderType) {
return h
}
// EncodeToString returns string representation of FilterHeaderType.
// String returns string representation of FilterHeaderType.
//
// String mapping:
// - HeaderFromRequest: REQUEST;
// - HeaderFromObject: OBJECT;
// - HeaderTypeUnknown, default: HEADER_UNSPECIFIED.
func (h FilterHeaderType) EncodeToString() string {
func (h FilterHeaderType) String() string {
return h.ToV2().String()
}
// String implements fmt.Stringer.
//
// String is designed to be human-readable, and its format MAY differ between
// SDK versions. String MAY return same result as EncodeToString. String MUST NOT
// be used to encode ID into NeoFS protocol string.
func (h FilterHeaderType) String() string {
return h.EncodeToString()
}
// DecodeString parses FilterHeaderType from a string representation.
// It is a reverse action to EncodeToString().
// FromString parses FilterHeaderType from a string representation.
// It is a reverse action to String().
//
// Returns true if s was parsed successfully.
func (h *FilterHeaderType) DecodeString(s string) bool {
func (h *FilterHeaderType) FromString(s string) bool {
var g v2acl.HeaderType
ok := g.FromString(s)

View file

@ -3,8 +3,8 @@ package eacl_test
import (
"testing"
v2acl "github.com/nspcc-dev/neofs-api-go/v2/acl"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"github.com/stretchr/testify/require"
)
@ -118,8 +118,8 @@ func TestFilterHeaderType(t *testing.T) {
}
type enumIface interface {
DecodeString(string) bool
EncodeToString() string
FromString(string) bool
String() string
}
type enumStringItem struct {
@ -129,11 +129,11 @@ type enumStringItem struct {
func testEnumStrings(t *testing.T, e enumIface, items []enumStringItem) {
for _, item := range items {
require.Equal(t, item.str, item.val.EncodeToString())
require.Equal(t, item.str, item.val.String())
s := item.val.EncodeToString()
s := item.val.String()
require.True(t, e.DecodeString(s), s)
require.True(t, e.FromString(s), s)
require.EqualValues(t, item.val, e, item.val)
}
@ -143,7 +143,7 @@ func testEnumStrings(t *testing.T, e enumIface, items []enumStringItem) {
"some string",
"UNSPECIFIED",
} {
require.False(t, e.DecodeString(str))
require.False(t, e.FromString(str))
}
}

View file

@ -3,7 +3,7 @@ package eacl
import (
"strconv"
v2acl "github.com/nspcc-dev/neofs-api-go/v2/acl"
v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
)
// Filter defines check conditions if request header is matched or not. Matched

Some files were not shown because too many files have changed in this diff Show more