Compare commits

..

16 commits

Author SHA1 Message Date
557267a07b [#126] Refine CODEOWNERS settings
Signed-off-by: Vitaliy Potyarkin <v.potyarkin@yadro.com>
2024-12-10 12:37:53 +03:00
2bdee4c9e6 [#126] Stop using obsolete .github directory
This commit is a part of multi-repo cleanup effort:
TrueCloudLab/frostfs-infra#136

Signed-off-by: Vitaliy Potyarkin <v.potyarkin@yadro.com>
2024-11-06 15:14:57 +03:00
f0fc40e116
[#123] Resolve funlen linter issue
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-10-11 14:40:54 +03:00
29f2157563
[#123] protogen: Treat bytes field as non-nullable
In protobuf 3.12 they have added an support for `optional` keyword,
which has made it into the main branch in 3.15.
https://github.com/protocolbuffers/protobuf/blob/main/docs/implementing_proto3_presence.md
https://github.com/protocolbuffers/protobuf/blob/v3.12.0/docs/field_presence.md#presence-in-proto3-apis

This means that without an explicit `optional` keyword field presence
for scalars is not tracked, thus empty string in JSON should be
unmarshaled to a nil byte slice. Relevant decoding code and tests from
protojson:
fb995f184a/internal/impl/message_reflect_field.go (L327)
fb995f184a/encoding/protojson/decode_test.go (L134)
fb995f184a/encoding/protojson/decode_test.go (L156)

We do not support `optional` keyword and the generator will fail if it sees on.
So only implement the default behaviour.

Refs #122

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-10-11 14:40:54 +03:00
29c522d5d8 [#122] protogen: Always marshal empty fields
This is how it was done previously:
a0a9b765f3/rpc/message/encoding.go (L31)

The tricky part is `[]byte` which is marshaled as `null` by easyjson
helper, but as `""` by protojson.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-10-07 15:05:43 +03:00
3e705a3cbe [#121] .golangci.yml: Replace exportloopref with copyloopvar
Fix linter warning:
```
WARN The linter 'exportloopref' is deprecated (since v1.60.2) due to: Since Go1.22 (loopvar) this linter is no longer relevant. Replaced by copyloopvar.
```

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-10-02 09:48:11 +03:00
d9a604fbc1 [#120] proto/test: Unskip protojson compatibility test
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-10-01 14:18:52 +03:00
b06dad731c [#120] protogen: Marshal 64-bit integers as strings
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-10-01 14:18:46 +03:00
d94b9c6d0d [#120] protogen: Unmarshal stringified integers from JSON
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-10-01 14:18:45 +03:00
eeb754c327 [#120] protogen: Omit empty fields from JSON output
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-10-01 14:11:43 +03:00
805da79319 [#120] protogen: Marshal enum as string
Be compatible with protojson.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-10-01 14:11:43 +03:00
f812b1ae5b [#120] Regenerate proto files
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-10-01 14:11:43 +03:00
287e98ad67 [#120] proto/test: Add protojson compatibility test
It is failing, thus is skipped.
But implement it now to make it easier to see it failing.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-10-01 14:11:43 +03:00
c3dbbc5eab
[#118] status: Support INVALID_ARGUMENT status
Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
2024-09-24 14:38:14 +03:00
13fa0da374 [#117] rpc: Allow to specify custom gRPC dialer
After grpc upgrade there is no DialContext call.
So connection is not actually established after created.

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2024-09-16 12:35:37 +03:00
c9782cf3ef [#117] go.mod: Upgrade grpc to v1.66.2
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2024-09-13 17:22:52 +03:00
40 changed files with 255 additions and 79 deletions

70
.forgejo/logo.svg Normal file
View file

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 184.2 51.8" style="enable-background:new 0 0 184.2 51.8;" xml:space="preserve">
<style type="text/css">
.st0{display:none;}
.st1{display:inline;}
.st2{fill:#01E397;}
.st3{display:inline;fill:#010032;}
.st4{display:inline;fill:#00E599;}
.st5{display:inline;fill:#00AF92;}
.st6{fill:#00C3E5;}
</style>
<g id="Layer_2">
<g id="Layer_1-2" class="st0">
<g class="st1">
<path class="st2" d="M146.6,18.3v7.2h10.9V29h-10.9v10.7h-4V14.8h18v3.5H146.6z"/>
<path class="st2" d="M180,15.7c1.7,0.9,3,2.2,4,3.8l-3,2.7c-0.6-1.3-1.5-2.4-2.6-3.3c-1.3-0.7-2.8-1-4.3-1
c-1.4-0.1-2.8,0.3-4,1.1c-0.9,0.5-1.5,1.5-1.4,2.6c0,1,0.5,1.9,1.4,2.4c1.5,0.8,3.2,1.3,4.9,1.5c1.9,0.3,3.7,0.8,5.4,1.6
c1.2,0.5,2.2,1.3,2.9,2.3c0.6,1,1,2.2,0.9,3.4c0,1.4-0.5,2.7-1.3,3.8c-0.9,1.2-2.1,2.1-3.5,2.6c-1.7,0.6-3.4,0.9-5.2,0.8
c-5,0-8.6-1.6-10.7-5l2.9-2.8c0.7,1.4,1.8,2.5,3.1,3.3c1.5,0.7,3.1,1.1,4.7,1c1.5,0.1,2.9-0.2,4.2-0.9c0.9-0.5,1.5-1.5,1.5-2.6
c0-0.9-0.5-1.8-1.3-2.2c-1.5-0.7-3.1-1.2-4.8-1.5c-1.9-0.3-3.7-0.8-5.5-1.5c-1.2-0.5-2.2-1.4-3-2.4c-0.6-1-1-2.2-0.9-3.4
c0-1.4,0.4-2.7,1.2-3.8c0.8-1.2,2-2.2,3.3-2.8c1.6-0.7,3.4-1.1,5.2-1C176.1,14.3,178.2,14.8,180,15.7z"/>
</g>
<path class="st3" d="M73.3,16.3c1.9,1.9,2.9,4.5,2.7,7.1v15.9h-4V24.8c0-2.6-0.5-4.5-1.6-5.7c-1.2-1.2-2.8-1.8-4.5-1.7
c-1.3,0-2.5,0.3-3.7,0.8c-1.2,0.7-2.2,1.7-2.9,2.9c-0.8,1.5-1.1,3.2-1.1,4.9v13.3h-4V15.1l3.6,1.5v1.7c0.8-1.5,2.1-2.6,3.6-3.3
c1.5-0.8,3.2-1.2,4.9-1.1C68.9,13.8,71.3,14.7,73.3,16.3z"/>
<path class="st3" d="M104.4,28.3H85.6c0.1,2.2,1,4.3,2.5,5.9c1.5,1.4,3.5,2.2,5.6,2.1c1.6,0.1,3.2-0.2,4.6-0.9
c1.1-0.6,2-1.6,2.5-2.8l3.3,1.8c-0.9,1.7-2.3,3.1-4,4c-2,1-4.2,1.5-6.4,1.4c-3.7,0-6.7-1.1-8.8-3.4s-3.2-5.5-3.2-9.6s1-7.2,3-9.5
s5-3.4,8.7-3.4c2.1-0.1,4.2,0.5,6.1,1.5c1.6,1,3,2.5,3.8,4.2c0.9,1.8,1.3,3.9,1.3,5.9C104.6,26.4,104.6,27.4,104.4,28.3z
M88.1,19.3c-1.4,1.5-2.2,3.4-2.4,5.5h15.1c-0.2-2-1-3.9-2.3-5.5c-1.4-1.3-3.2-2-5.1-1.9C91.5,17.3,89.6,18,88.1,19.3z"/>
<path class="st3" d="M131,17.3c2.2,2.3,3.2,5.5,3.2,9.5s-1,7.3-3.2,9.6s-5.1,3.4-8.8,3.4s-6.7-1.1-8.9-3.4s-3.2-5.5-3.2-9.6
s1.1-7.2,3.2-9.5s5.1-3.4,8.9-3.4S128.9,15,131,17.3z M116.2,19.9c-1.5,2-2.2,4.4-2.1,6.9c-0.2,2.5,0.6,5,2.1,7
c1.5,1.7,3.7,2.7,6,2.6c2.3,0.1,4.4-0.9,5.9-2.6c1.5-2,2.3-4.5,2.1-7c0.1-2.5-0.6-4.9-2.1-6.9c-1.5-1.7-3.6-2.7-5.9-2.6
C119.9,17.2,117.7,18.2,116.2,19.9z"/>
<polygon class="st4" points="0,9.1 0,43.7 22.5,51.8 22.5,16.9 46.8,7.9 24.8,0 "/>
<polygon class="st5" points="24.3,17.9 24.3,36.8 46.8,44.9 46.8,9.6 "/>
</g>
<g>
<g>
<path class="st6" d="M41.6,17.5H28.2v6.9h10.4v3.3H28.2v10.2h-3.9V14.2h17.2V17.5z"/>
<path class="st6" d="M45.8,37.9v-18h3.3l0.4,3.2c0.5-1.2,1.2-2.1,2.1-2.7c0.9-0.6,2.1-0.9,3.5-0.9c0.4,0,0.7,0,1.1,0.1
c0.4,0.1,0.7,0.2,0.9,0.3l-0.5,3.4c-0.3-0.1-0.6-0.2-0.9-0.2C55.4,23,54.9,23,54.4,23c-0.7,0-1.5,0.2-2.2,0.6
c-0.7,0.4-1.3,1-1.8,1.8s-0.7,1.8-0.7,3v9.5H45.8z"/>
<path class="st6" d="M68.6,19.6c1.8,0,3.3,0.4,4.6,1.1c1.3,0.7,2.4,1.8,3.1,3.2s1.1,3.1,1.1,5c0,1.9-0.4,3.6-1.1,5
c-0.8,1.4-1.8,2.5-3.1,3.2c-1.3,0.7-2.9,1.1-4.6,1.1s-3.3-0.4-4.6-1.1c-1.3-0.7-2.4-1.8-3.2-3.2c-0.8-1.4-1.2-3.1-1.2-5
c0-1.9,0.4-3.6,1.2-5s1.8-2.5,3.2-3.2C65.3,19.9,66.8,19.6,68.6,19.6z M68.6,22.6c-1.1,0-2,0.2-2.8,0.7c-0.8,0.5-1.3,1.2-1.7,2.1
s-0.6,2.1-0.6,3.5c0,1.3,0.2,2.5,0.6,3.4s1,1.7,1.7,2.2s1.7,0.7,2.8,0.7c1.1,0,2-0.2,2.7-0.7c0.7-0.5,1.3-1.2,1.7-2.2
s0.6-2.1,0.6-3.4c0-1.4-0.2-2.5-0.6-3.5s-1-1.6-1.7-2.1C70.6,22.8,69.6,22.6,68.6,22.6z"/>
<path class="st6" d="M89.2,38.3c-1.8,0-3.4-0.3-4.9-1c-1.5-0.7-2.7-1.7-3.5-3l2.7-2.3c0.5,1,1.3,1.8,2.3,2.4
c1,0.6,2.2,0.9,3.6,0.9c1.1,0,2-0.2,2.6-0.6c0.6-0.4,1-0.9,1-1.6c0-0.5-0.2-0.9-0.5-1.2s-0.9-0.6-1.7-0.8l-3.8-0.8
c-1.9-0.4-3.3-1-4.1-1.9c-0.8-0.9-1.2-1.9-1.2-3.3c0-1,0.3-1.9,0.9-2.7c0.6-0.8,1.4-1.5,2.5-2s2.5-0.8,4-0.8c1.8,0,3.3,0.3,4.6,1
c1.3,0.6,2.2,1.5,2.9,2.7l-2.7,2.2c-0.5-1-1.1-1.7-2-2.1c-0.9-0.5-1.8-0.7-2.8-0.7c-0.8,0-1.4,0.1-2,0.3c-0.6,0.2-1,0.5-1.3,0.8
c-0.3,0.3-0.4,0.7-0.4,1.2c0,0.5,0.2,0.9,0.5,1.3s1,0.6,1.9,0.8l4.1,0.9c1.7,0.3,2.9,0.9,3.7,1.7c0.7,0.8,1.1,1.8,1.1,2.9
c0,1.2-0.3,2.2-0.9,3c-0.6,0.9-1.5,1.6-2.6,2C92.1,38.1,90.7,38.3,89.2,38.3z"/>
<path class="st6" d="M112.8,19.9v3H99.3v-3H112.8z M106.6,14.6v17.9c0,0.9,0.2,1.5,0.7,1.9c0.5,0.4,1.1,0.6,1.9,0.6
c0.6,0,1.2-0.1,1.7-0.3c0.5-0.2,0.9-0.5,1.3-0.8l0.9,2.8c-0.6,0.5-1.2,0.9-2,1.1c-0.8,0.3-1.7,0.4-2.7,0.4c-1,0-2-0.2-2.8-0.5
s-1.5-0.9-2-1.6c-0.5-0.8-0.7-1.7-0.8-3V15.7L106.6,14.6z"/>
<path d="M137.9,17.5h-13.3v6.9h10.4v3.3h-10.4v10.2h-3.9V14.2h17.2V17.5z"/>
<path d="M150.9,13.8c2.1,0,4,0.4,5.5,1.2c1.6,0.8,2.9,2,4,3.5l-2.6,2.5c-0.9-1.4-1.9-2.4-3.1-3c-1.1-0.6-2.5-0.9-4-0.9
c-1.2,0-2.1,0.2-2.8,0.5c-0.7,0.3-1.3,0.7-1.6,1.2c-0.3,0.5-0.5,1.1-0.5,1.7c0,0.7,0.3,1.4,0.8,1.9c0.5,0.6,1.5,1,2.9,1.3
l4.8,1.1c2.3,0.5,3.9,1.3,4.9,2.3c1,1,1.4,2.3,1.4,3.9c0,1.5-0.4,2.7-1.2,3.8c-0.8,1.1-1.9,1.9-3.3,2.5s-3.1,0.9-5,0.9
c-1.7,0-3.2-0.2-4.5-0.6c-1.3-0.4-2.5-1-3.5-1.8c-1-0.7-1.8-1.6-2.5-2.6l2.7-2.7c0.5,0.8,1.1,1.6,1.9,2.2
c0.8,0.7,1.7,1.2,2.7,1.5c1,0.4,2.2,0.5,3.4,0.5c1.1,0,2.1-0.1,2.9-0.4c0.8-0.3,1.4-0.7,1.8-1.2c0.4-0.5,0.6-1.1,0.6-1.9
c0-0.7-0.2-1.3-0.7-1.8c-0.5-0.5-1.3-0.9-2.6-1.2l-5.2-1.2c-1.4-0.3-2.6-0.8-3.6-1.3c-0.9-0.6-1.6-1.3-2.1-2.1s-0.7-1.8-0.7-2.8
c0-1.3,0.4-2.6,1.1-3.7c0.7-1.1,1.8-2,3.2-2.6C147.3,14.1,148.9,13.8,150.9,13.8z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

1
.github/CODEOWNERS vendored
View file

@ -1 +0,0 @@
* @TrueCloudLab/storage-core @TrueCloudLab/storage-services @TrueCloudLab/committers

View file

@ -50,7 +50,7 @@ linters:
- bidichk
- durationcheck
- exhaustive
- exportloopref
- copyloopvar
- gofmt
- goimports
- misspell

2
CODEOWNERS Normal file
View file

@ -0,0 +1,2 @@
.* @TrueCloudLab/storage-core-committers @TrueCloudLab/storage-core-developers @TrueCloudLab/storage-services-committers @TrueCloudLab/storage-services-developers
.forgejo/.* @potyarkin

View file

@ -25,7 +25,8 @@ Start by forking the `frostfs-api-go` repository, make changes in a branch and t
send a pull request. We encourage pull requests to discuss code changes. Here
are the steps in details:
### Set up your GitHub Repository
### Set up your repository
Fork [FrostFS node upstream](https://git.frostfs.info/TrueCloudLab/frostfs-api-go/fork) source
repository to your own personal repository. Copy the URL of your fork (you will
need it for the `git clone` command below).
@ -86,7 +87,7 @@ $ git push origin feature/123-something_awesome
```
### Create a Pull Request
Pull requests can be created via GitHub. Refer to [this
Pull requests can be created via git.frostfs.info. Refer to [this
document](https://help.github.com/articles/creating-a-pull-request/) for
detailed steps on how to create a pull request. After a Pull Request gets peer
reviewed and approved, it will be merged.

View file

@ -1,5 +1,5 @@
<p align="center">
<img src="./.github/logo.svg" width="500px" alt="FrostFS">
<img src="./.forgejo/logo.svg" width="500px" alt="FrostFS">
</p>
<p align="center">
Low-level Golang API for <a href="https://frostfs.info">FrostFS</a>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -35,11 +35,11 @@ Tag a release (must be signed) and push it:
$ git tag -s vX.Y.Z[-rc.N] && git push origin vX.Y.Z[-rc.N]
```
## Make a Github release
## Make a proper release
Using Github's web interface create a new release based on just created tag
Using git.frostfs.info web interface create a new release based on just created tag
with the same changes from changelog and publish it.
## Close github milestone
## Close milestone
Close corresponding vX.Y.Z github milestone.
Close corresponding vX.Y.Z milestone.

14
go.mod
View file

@ -7,9 +7,9 @@ require (
github.com/VictoriaMetrics/easyproto v0.1.4
github.com/mailru/easyjson v0.7.7
github.com/stretchr/testify v1.8.3
golang.org/x/sync v0.6.0
google.golang.org/grpc v1.63.2
google.golang.org/protobuf v1.33.0
golang.org/x/sync v0.7.0
google.golang.org/grpc v1.66.2
google.golang.org/protobuf v1.34.1
)
require (
@ -19,10 +19,10 @@ require (
github.com/kr/pretty v0.1.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

28
go.sum
View file

@ -26,20 +26,20 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -2,13 +2,16 @@ package client
import (
"context"
"google.golang.org/grpc"
)
// CallOption is a messaging session option within Protobuf RPC.
type CallOption func(*callParameters)
type callParameters struct {
ctx context.Context // nolint:containedctx
ctx context.Context // nolint:containedctx
dialer func(context.Context, grpc.ClientConnInterface) error
}
func defaultCallParameters() *callParameters {
@ -27,3 +30,11 @@ func WithContext(ctx context.Context) CallOption {
prm.ctx = ctx
}
}
// WithDialer returns option to specify grpc dialer. If passed, it will be
// called after the connection is successfully created.
func WithDialer(dialer func(context.Context, grpc.ClientConnInterface) error) CallOption {
return func(prm *callParameters) {
prm.dialer = dialer
}
}

View file

@ -12,7 +12,7 @@ import (
var errInvalidEndpoint = errors.New("invalid endpoint options")
func (c *Client) openGRPCConn(ctx context.Context) error {
func (c *Client) openGRPCConn(ctx context.Context, dialer func(ctx context.Context, cc grpcstd.ClientConnInterface) error) error {
if c.conn != nil {
return nil
}
@ -21,15 +21,21 @@ func (c *Client) openGRPCConn(ctx context.Context) error {
return errInvalidEndpoint
}
dialCtx, cancel := context.WithTimeout(ctx, c.dialTimeout)
var err error
c.conn, err = grpcstd.DialContext(dialCtx, c.addr, c.grpcDialOpts...)
cancel()
c.conn, err = grpcstd.NewClient(c.addr, c.grpcDialOpts...)
if err != nil {
return fmt.Errorf("gRPC dial: %w", err)
return fmt.Errorf("gRPC new client: %w", err)
}
if dialer != nil {
ctx, cancel := context.WithTimeout(ctx, c.dialTimeout)
defer cancel()
if err := dialer(ctx, c.conn); err != nil {
_ = c.conn.Close()
return fmt.Errorf("gRPC dial: %w", err)
}
}
return nil

View file

@ -46,7 +46,7 @@ func (c *Client) Init(info common.CallMethodInfo, opts ...CallOption) (MessageRe
opt(prm)
}
if err := c.openGRPCConn(prm.ctx); err != nil {
if err := c.openGRPCConn(prm.ctx, prm.dialer); err != nil {
return nil, err
}

View file

@ -37,7 +37,6 @@ func (c *cfg) initDefault() {
c.dialTimeout = defaultDialTimeout
c.rwTimeout = defaultRWTimeout
c.grpcDialOpts = []grpc.DialOption{
grpc.WithBlock(),
grpc.WithTransportCredentials(insecure.NewCredentials()),
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -63,6 +63,8 @@ const (
SignatureVerificationFail
// NodeUnderMaintenance is a local Code value for NODE_UNDER_MAINTENANCE status.
NodeUnderMaintenance
// InvalidArgument is a local Code value for INVALID_ARGUMENT status.
InvalidArgument
)
const (

Binary file not shown.

View file

@ -26,11 +26,34 @@ func nonZero[T protoInt]() T {
func TestStableMarshalSingle(t *testing.T) {
t.Run("empty", func(t *testing.T) {
input := &generated.Primitives{}
require.Zero(t, input.StableSize())
t.Run("proto", func(t *testing.T) {
input := &generated.Primitives{}
require.Zero(t, input.StableSize())
r := input.MarshalProtobuf(nil)
require.Empty(t, r)
r := input.MarshalProtobuf(nil)
require.Empty(t, r)
})
t.Run("json", func(t *testing.T) {
input := &generated.Primitives{}
r, err := input.MarshalJSON()
require.NoError(t, err)
require.NotEmpty(t, r)
var actual test.Primitives
require.NoError(t, protojson.Unmarshal(r, &actual))
t.Run("protojson compatibility", func(t *testing.T) {
data, err := protojson.MarshalOptions{EmitUnpopulated: true}.Marshal(&actual)
require.NoError(t, err)
require.JSONEq(t, string(data), string(r))
})
var actualFrostfs generated.Primitives
require.NoError(t, actualFrostfs.UnmarshalJSON(r))
require.Equal(t, input, &actualFrostfs)
primitivesEqual(t, input, &actual)
})
})
marshalCases := []struct {
@ -76,6 +99,12 @@ func TestStableMarshalSingle(t *testing.T) {
var actual test.Primitives
require.NoError(t, protojson.Unmarshal(r, &actual))
t.Run("protojson compatibility", func(t *testing.T) {
data, err := protojson.MarshalOptions{EmitUnpopulated: true}.Marshal(&actual)
require.NoError(t, err)
require.JSONEq(t, string(data), string(r))
})
var actualFrostfs generated.Primitives
require.NoError(t, actualFrostfs.UnmarshalJSON(r))
require.Equal(t, tc.input, &actualFrostfs)
@ -88,6 +117,7 @@ func TestStableMarshalSingle(t *testing.T) {
func primitivesEqual(t *testing.T, a *generated.Primitives, b *test.Primitives) {
// Compare each field directly, because proto-generated code has private fields.
require.Equal(t, len(a.FieldA), len(b.FieldA))
require.Equal(t, a.FieldA, b.FieldA)
require.Equal(t, a.FieldB, b.FieldB)
require.Equal(t, a.FieldC, b.FieldC)

Binary file not shown.

View file

@ -59,6 +59,35 @@ func emitJSONUnmarshal(g *protogen.GeneratedFile, msg *protogen.Message) {
g.P("}")
}
func emitJSONParseInteger(g *protogen.GeneratedFile, ident string, method string, bitSize int, typ string) {
g.P("r := in.JsonNumber()")
g.P("n := r.String()")
g.P("v, err := ", strconvPackage.Ident(method), "(n, 10, ", bitSize, ")")
g.P("if err != nil {")
g.P(" in.AddError(err)")
g.P(" return")
g.P("}")
g.P(ident, " := ", typ, "(v)")
}
func emitJSONReadEnum(g *protogen.GeneratedFile, name string, enumType string) {
g.P(`switch v := in.Interface().(type) {
case string:
if vv, ok := `+enumType+`_value[v]; ok {
`+name+` = `+enumType+`(vv)
break
}
vv, err := `, strconvPackage.Ident("ParseInt"), `(v, 10, 32)
if err != nil {
in.AddError(err)
return
}
`+name+` = `+enumType+`(vv)
case float64:
`+name+` = `+enumType+`(v)
}`)
}
func emitJSONFieldRead(g *protogen.GeneratedFile, f *protogen.Field, name string) {
g.P("{")
defer g.P("}")
@ -83,30 +112,20 @@ func emitJSONFieldRead(g *protogen.GeneratedFile, f *protogen.Field, name string
enumType := fieldType(g, f).String()
g.P("var parsedValue " + enumType)
g.P(`switch v := in.Interface().(type) {
case string:
if vv, ok := `+enumType+`_value[v]; ok {
parsedValue = `+enumType+`(vv)
break
}
vv, err := `, strconvPackage.Ident("ParseInt"), `(v, 10, 32)
if err != nil {
in.AddError(err)
return
}
parsedValue = `+enumType+`(vv)
case float64:
parsedValue = `+enumType+`(v)
}`)
emitJSONReadEnum(g, "parsedValue", enumType)
template = "%s = parsedValue"
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
template = "%s = in.Int32()"
emitJSONParseInteger(g, "pv", "ParseInt", 32, "int32")
template = "%s = pv"
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
template = "%s = in.Uint32()"
emitJSONParseInteger(g, "pv", "ParseUint", 32, "uint32")
template = "%s = pv"
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
template = "%s = in.Int64()"
emitJSONParseInteger(g, "pv", "ParseInt", 64, "int64")
template = "%s = pv"
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
template = "%s = in.Uint64()"
emitJSONParseInteger(g, "pv", "ParseUint", 64, "uint64")
template = "%s = pv"
case protoreflect.FloatKind:
template = "%s = in.Float32()"
case protoreflect.DoubleKind:
@ -114,7 +133,17 @@ func emitJSONFieldRead(g *protogen.GeneratedFile, f *protogen.Field, name string
case protoreflect.StringKind:
template = "%s = in.String()"
case protoreflect.BytesKind:
template = "%s = in.Bytes()"
// Since some time ago proto3 support optional keyword, thus the presence is not tracked by default:
// https://github.com/protocolbuffers/protobuf-go/blob/fb995f184a1719ec42b247a3771d1036d92adf67/internal/impl/message_reflect_field.go#L327
// We do not explicitly support `optional` keyword, protoc will fail on such fileds.
// Thus, treat empty string as `[]byte(nil)`.
template = `{
tmp := in.Bytes()
if len(tmp) == 0 {
tmp = nil
}
%s = tmp
}`
case protoreflect.MessageKind:
if f.Desc.IsList() {
g.P("f = ", fieldType(g, f)[2:], "{}")
@ -147,8 +176,11 @@ func emitJSONMarshal(g *protogen.GeneratedFile, msg *protogen.Message) {
g.P("func (x *", msg.GoIdent.GoName, ") MarshalEasyJSON(out *", jwriterPackage.Ident("Writer"), ") {")
g.P(`if x == nil { out.RawString("null"); return }`)
if len(msg.Fields) != 0 {
g.P("first := true")
}
g.P("out.RawByte('{')")
for i, f := range msg.Fields {
for _, f := range msg.Fields {
if f.Oneof != nil {
if f.Oneof.Fields[0] != f {
continue
@ -157,29 +189,38 @@ func emitJSONMarshal(g *protogen.GeneratedFile, msg *protogen.Message) {
g.P("switch xx := x.", f.Oneof.GoName, ".(type) {")
for _, ff := range f.Oneof.Fields {
g.P("case *", ff.GoIdent, ":")
emitJSONFieldWrite(g, ff, "xx", i == 0)
emitJSONFieldWrite(g, ff, "xx")
}
g.P("}")
continue
}
emitJSONFieldWrite(g, f, "x", i == 0)
emitJSONFieldWrite(g, f, "x")
}
g.P("out.RawByte('}')")
g.P("}")
}
func emitJSONFieldWrite(g *protogen.GeneratedFile, f *protogen.Field, name string, first bool) {
func emitJSONFieldWrite(g *protogen.GeneratedFile, f *protogen.Field, name string) {
g.P("{")
defer g.P("}")
g.P("const prefix string = ", `",\"`, fieldJSONName(f), `\":"`)
if first {
g.P("out.RawString(prefix[1:])")
} else {
g.P("out.RawString(prefix)")
}
selector := name + "." + f.GoName
// This code is responsible for ignoring default values.
// We will restore it after having parametrized JSON marshaling.
//
// isNotDefault := notNil
// if f.Desc.IsList() {
// isNotDefault = notEmpty
// } else if f.Desc.Kind() != protoreflect.MessageKind {
// _, isNotDefault = easyprotoKindInfo(f.Desc.Kind())
// }
// g.P("if ", isNotDefault(selector), "{")
// defer g.P("}")
g.P("if !first { out.RawByte(','); } else { first = false; }")
g.P("const prefix string = ", `"\"`, fieldJSONName(f), `\":"`)
g.P("out.RawString(prefix)")
if f.Desc.IsList() {
selector += "[i]"
g.P("out.RawByte('[')")
@ -195,15 +236,27 @@ func emitJSONFieldWrite(g *protogen.GeneratedFile, f *protogen.Field, name strin
case protoreflect.BoolKind:
template = "out.Bool(%s)"
case protoreflect.EnumKind:
template = "out.Int32(int32(%s))"
enumType := fieldType(g, f).String()
template = `v := int32(%s)
if vv, ok := ` + enumType + `_name[v]; ok {
out.String(vv)
} else {
out.Int32(v)
}`
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
template = "out.Int32(%s)"
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
template = "out.Uint32(%s)"
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
template = "out.Int64(%s)"
g.P("out.RawByte('\"')")
g.P("out.Buffer.Buf = ", strconvPackage.Ident("AppendInt"), "(out.Buffer.Buf, ", selector, ", 10)")
g.P("out.RawByte('\"')")
return
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
template = "out.Uint64(%s)"
g.P("out.RawByte('\"')")
g.P("out.Buffer.Buf = ", strconvPackage.Ident("AppendUint"), "(out.Buffer.Buf, ", selector, ", 10)")
g.P("out.RawByte('\"')")
return
case protoreflect.FloatKind:
template = "out.Float32(%s)"
case protoreflect.DoubleKind:
@ -211,7 +264,10 @@ func emitJSONFieldWrite(g *protogen.GeneratedFile, f *protogen.Field, name strin
case protoreflect.StringKind:
template = "out.String(%s)"
case protoreflect.BytesKind:
template = "out.Base64Bytes(%s)"
g.P("if ", selector, "!= nil {")
g.P("out.Base64Bytes(", selector, ")")
g.P("} else { out.String(\"\") }")
return
case protoreflect.MessageKind:
template = "%s.MarshalEasyJSON(out)"
case protoreflect.GroupKind: