├── .gitattributes ├── .github ├── CODEOWNERS ├── logo.svg └── workflows │ ├── dco.yml │ └── go.yml ├── .gitignore ├── .golangci.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CREDITS.md ├── LICENSE ├── Makefile ├── README.md ├── accounting ├── accounting.go ├── convert.go ├── grpc │ ├── service.go │ ├── service.pb.go │ ├── service_grpc.pb.go │ ├── types.go │ └── types.pb.go ├── json.go ├── marshal.go ├── message_test.go └── test │ └── generate.go ├── acl ├── bench_test.go ├── convert.go ├── filters.go ├── grpc │ ├── types.go │ └── types.pb.go ├── json.go ├── marshal.go ├── message_test.go ├── string.go ├── test │ └── generate.go └── types.go ├── audit ├── convert.go ├── grpc │ ├── types.go │ └── types.pb.go ├── json.go ├── marshal.go ├── message_test.go ├── test │ └── generate.go └── types.go ├── container ├── attributes.go ├── attributes_test.go ├── convert.go ├── grpc │ ├── service.go │ ├── service.pb.go │ ├── service_grpc.pb.go │ ├── types.go │ └── types.pb.go ├── json.go ├── marshal.go ├── message_test.go ├── status.go ├── status_test.go ├── test │ └── generate.go └── types.go ├── docs ├── .gitkeep └── release-instruction.md ├── go.mod ├── go.sum ├── internal └── random │ └── rand.go ├── lock └── grpc │ ├── types.go │ └── types.pb.go ├── netmap ├── attributes.go ├── attributes_test.go ├── convert.go ├── grpc │ ├── service.go │ ├── service.pb.go │ ├── service_grpc.pb.go │ ├── types.go │ └── types.pb.go ├── json.go ├── marshal.go ├── message_test.go ├── string.go ├── test │ └── generate.go └── types.go ├── object ├── attributes.go ├── attributes_test.go ├── bench_test.go ├── convert.go ├── filters.go ├── grpc │ ├── client.go │ ├── service.go │ ├── service.pb.go │ ├── service_grpc.pb.go │ ├── status.pb.go │ ├── types.go │ └── types.pb.go ├── json.go ├── lock.go ├── lock_test.go ├── marshal.go ├── message_test.go ├── status.go ├── status_test.go ├── string.go ├── test │ └── generate.go └── types.go ├── prepare.sh ├── refs ├── bench_test.go ├── convert.go ├── grpc │ ├── types.go │ └── types.pb.go ├── json.go ├── marshal.go ├── message_test.go ├── string.go ├── test │ └── generate.go ├── types.go └── types_test.go ├── reputation ├── convert.go ├── grpc │ ├── service.go │ ├── service.pb.go │ ├── service_grpc.pb.go │ ├── types.go │ └── types.pb.go ├── json.go ├── marshal.go ├── message_test.go ├── test │ └── generate.go └── types.go ├── rpc ├── accounting.go ├── client │ ├── call_options.go │ ├── client.go │ ├── conn.go │ ├── connect.go │ ├── flows.go │ ├── init.go │ ├── options.go │ ├── options_test.go │ ├── stream_wrapper.go │ └── util.go ├── common.go ├── common │ ├── call.go │ └── call_test.go ├── container.go ├── grpc │ └── init.go ├── message │ ├── encoding.go │ ├── message.go │ └── test │ │ └── message.go ├── netmap.go ├── object.go ├── reputation.go └── session.go ├── session ├── convert.go ├── grpc │ ├── client.go │ ├── service.go │ ├── service.pb.go │ ├── service_grpc.pb.go │ ├── types.go │ └── types.pb.go ├── json.go ├── marshal.go ├── message_test.go ├── status.go ├── status_test.go ├── string.go ├── test │ └── generate.go ├── types.go ├── util.go └── xheaders.go ├── signature ├── sign.go └── sign_test.go ├── status ├── convert.go ├── details.go ├── grpc │ ├── types.go │ └── types.pb.go ├── marshal.go ├── message_test.go ├── status.go ├── test │ ├── codes.go │ └── generate.go └── types.go ├── storagegroup ├── convert.go ├── grpc │ ├── types.go │ └── types.pb.go ├── json.go ├── marshal.go ├── message_test.go ├── test │ └── generate.go └── types.go ├── subnet ├── encoding_test.go ├── grpc │ ├── types.go │ └── types.pb.go ├── info.go └── test │ └── generate.go ├── tombstone ├── convert.go ├── grpc │ ├── types.go │ └── types.pb.go ├── json.go ├── marshal.go ├── message_test.go ├── test │ └── generate.go └── types.go └── util ├── proto ├── marshal.go ├── marshal_test.go └── test │ ├── test.pb.go │ └── test.proto ├── protogen └── main.go └── signature ├── data.go ├── options.go ├── sign_test.go └── walletconnect ├── sign.go └── sign_test.go /.gitattributes: -------------------------------------------------------------------------------- 1 | /**/*.pb.go -diff -merge 2 | /**/*.pb.go linguist-generated=true 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @TrueCloudLab/storage-core @TrueCloudLab/storage-services @TrueCloudLab/committers 2 | -------------------------------------------------------------------------------- /.github/workflows/dco.yml: -------------------------------------------------------------------------------- 1 | name: DCO check 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | commits_check_job: 10 | runs-on: ubuntu-latest 11 | name: Commits Check 12 | steps: 13 | - name: Get PR Commits 14 | id: 'get-pr-commits' 15 | uses: tim-actions/get-pr-commits@master 16 | with: 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | - name: DCO Check 19 | uses: tim-actions/dco@master 20 | with: 21 | commits: ${{ steps.get-pr-commits.outputs.commits }} 22 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: neofs-api-go tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths-ignore: 8 | - '*.md' 9 | pull_request: 10 | branches: 11 | - master 12 | paths-ignore: 13 | - '*.md' 14 | 15 | jobs: 16 | test: 17 | runs-on: ubuntu-20.04 18 | strategy: 19 | matrix: 20 | go: [ '1.17.x', '1.18.x', '1.19.x' ] 21 | steps: 22 | - name: Setup go 23 | uses: actions/setup-go@v3 24 | with: 25 | go-version: ${{ matrix.go }} 26 | 27 | - name: Check out code 28 | uses: actions/checkout@v3 29 | 30 | - name: Cache go mod 31 | uses: actions/cache@v3 32 | with: 33 | path: ~/go/pkg/mod 34 | key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('**/go.sum') }} 35 | restore-keys: | 36 | ${{ runner.os }}-go-${{ matrix.go }}- 37 | 38 | - name: Get dependencies 39 | run: make dep 40 | 41 | - name: Run go test 42 | run: go test -coverprofile=coverage.txt -covermode=atomic ./... 43 | 44 | - name: Codecov 45 | env: 46 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 47 | run: bash <(curl -s https://codecov.io/bash) 48 | 49 | lint: 50 | runs-on: ubuntu-20.04 51 | steps: 52 | - name: Setup go 53 | uses: actions/setup-go@v3 54 | with: 55 | go-version: 1.19 56 | 57 | - name: Check out code 58 | uses: actions/checkout@v3 59 | 60 | - name: golangci-lint 61 | uses: golangci/golangci-lint-action@v3 62 | with: 63 | version: v1.48.0 64 | only-new-issues: true 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | bin 3 | temp 4 | /vendor/ 5 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | # This file contains all available configuration options 2 | # with their default values. 3 | 4 | # options for analysis running 5 | run: 6 | # timeout for analysis, e.g. 30s, 5m, default is 1m 7 | timeout: 2m 8 | 9 | # include test files or not, default is true 10 | tests: false 11 | 12 | skip-files: 13 | - (^|.*/)grpc/(.*) 14 | 15 | # output configuration options 16 | output: 17 | # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" 18 | format: tab 19 | 20 | # all available settings of specific linters 21 | linters-settings: 22 | exhaustive: 23 | # indicates that switch statements are to be considered exhaustive if a 24 | # 'default' case is present, even if all enum members aren't listed in the 25 | # switch 26 | default-signifies-exhaustive: true 27 | govet: 28 | # report about shadowed variables 29 | check-shadowing: false 30 | 31 | linters: 32 | enable: 33 | # mandatory linters 34 | - govet 35 | - revive 36 | 37 | # some default golangci-lint linters 38 | - errcheck 39 | - gosimple 40 | - ineffassign 41 | - staticcheck 42 | - typecheck 43 | 44 | # extra linters 45 | - exhaustive 46 | - gofmt 47 | - whitespace 48 | - goimports 49 | disable-all: true 50 | fast: false 51 | 52 | issues: 53 | # Excluding configuration per-path, per-linter, per-text and per-source 54 | exclude-rules: 55 | - path: v2 # ignore stutters in universal structures due to protobuf compatibility 56 | linters: 57 | - golint 58 | text: "stutters;" 59 | 60 | - path: grpc # ignore case errors in grpc setters due to protobuf compatibility 61 | linters: 62 | - golint 63 | text: "should be" 64 | 65 | - linters: # ignore SA6002 since we use pool of []byte, however we can switch to *bytes.Buffer 66 | - staticcheck 67 | text: "SA6002:" -------------------------------------------------------------------------------- /CREDITS.md: -------------------------------------------------------------------------------- 1 | # Credits 2 | 3 | FrostFS continues the development of NeoFS. 4 | 5 | Initial NeoFS research and development (2018-2020) was done by 6 | [NeoSPCC](https://nspcc.ru) team. 7 | 8 | In alphabetical order: 9 | 10 | - Alexey Vanin 11 | - Anastasia Prasolova 12 | - Anatoly Bogatyrev 13 | - Evgeny Kulikov 14 | - Evgeny Stratonikov 15 | - Leonard Liubich 16 | - Sergei Liubich 17 | - Stanislav Bogatyrev 18 | 19 | # Contributors 20 | 21 | In chronological order: 22 | - Pavel Korotkov 23 | - Pavel Karpy 24 | - Angira Kekteeva 25 | 26 | # Special Thanks 27 | 28 | For product development support: 29 | 30 | - Fabian Wahle 31 | - Neo Global Development 32 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | SHELL = bash 3 | 4 | VERSION ?= $(shell git describe --tags --match "v*" --abbrev=8 --dirty --always) 5 | 6 | .PHONY: dep fmts fmt imports protoc test lint version help 7 | 8 | # Pull go dependencies 9 | dep: 10 | @printf "⇒ Tidy requirements : " 11 | CGO_ENABLED=0 \ 12 | GO111MODULE=on \ 13 | go mod tidy -v && echo OK 14 | @printf "⇒ Download requirements: " 15 | CGO_ENABLED=0 \ 16 | GO111MODULE=on \ 17 | go mod download && echo OK 18 | @printf "⇒ Install test requirements: " 19 | CGO_ENABLED=0 \ 20 | GO111MODULE=on \ 21 | go test ./... && echo OK 22 | 23 | # Run all code formatters 24 | fmts: fmt imports 25 | 26 | # Reformat code 27 | fmt: 28 | @echo "⇒ Processing gofmt check" 29 | @for f in `find . -type f -name '*.go' -not -path './vendor/*' -not -name '*.pb.go' -prune`; do \ 30 | GO111MODULE=on gofmt -s -w $$f; \ 31 | done 32 | 33 | # Reformat imports 34 | imports: 35 | @echo "⇒ Processing goimports check" 36 | @for f in `find . -type f -name '*.go' -not -path './vendor/*' -not -name '*.pb.go' -prune`; do \ 37 | GO111MODULE=on goimports -w $$f; \ 38 | done 39 | 40 | # Regenerate code for proto files 41 | protoc: 42 | @GOPRIVATE=github.com/TrueCloudLab go mod vendor 43 | # Install specific version for protobuf lib 44 | @go list -f '{{.Path}}/...@{{.Version}}' -m google.golang.org/protobuf | xargs go install -v 45 | # Protoc generate 46 | @for f in `find . -type f -name '*.proto' -not -path './vendor/*'`; do \ 47 | echo "⇒ Processing $$f "; \ 48 | protoc \ 49 | --proto_path=.:./vendor:/usr/local/include \ 50 | --go_out=. --go_opt=paths=source_relative \ 51 | --go-grpc_opt=require_unimplemented_servers=false \ 52 | --go-grpc_out=. --go-grpc_opt=paths=source_relative $$f; \ 53 | done 54 | rm -rf vendor 55 | 56 | # Run Unit Test with go test 57 | test: 58 | @echo "⇒ Running go test" 59 | @GO111MODULE=on go test ./... 60 | 61 | # Run linters 62 | lint: 63 | @golangci-lint run 64 | 65 | # Print version 66 | version: 67 | @echo $(VERSION) 68 | 69 | # Show this help prompt 70 | help: 71 | @echo ' Usage:' 72 | @echo '' 73 | @echo ' make ' 74 | @echo '' 75 | @echo ' Targets:' 76 | @echo '' 77 | @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 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | FrostFS 3 |

4 |

5 | Low-level Golang API for FrostFS 6 |

7 | 8 | --- 9 | ![Tests](https://github.com/TrueCloudLab/frostfs-api-go/workflows/frostfs-api-go%20tests/badge.svg) 10 | [![codecov](https://codecov.io/gh/TrueCloudLab/frostfs-api-go/branch/master/graph/badge.svg)](https://codecov.io/gh/TrueCloudLab/frostfs-api-go) 11 | [![Report](https://goreportcard.com/badge/github.com/TrueCloudLab/frostfs-api-go)](https://goreportcard.com/report/github.com/TrueCloudLab/frostfs-api-go) 12 | [![GitHub release](https://img.shields.io/github/release/TrueCloudLab/frostfs-api-go.svg)](https://github.com/TrueCloudLab/frostfs-api-go) 13 | ![GitHub license](https://img.shields.io/github/license/TrueCloudLab/frostfs-api-go.svg?style=popout) 14 | 15 | # Overview 16 | 17 | Go implementation of recent [FrostFS API](https://github.com/TrueCloudLab/frostfs-api) 18 | versions. For a more high-level SDK see [FrostFS SDK](https://github.com/TrueCloudLab/frostfs-sdk-go). 19 | 20 | ## Frostfs-Api compatibility 21 | 22 | |frostfs-api-go version|supported frostfs-api versions| 23 | |:------------------:|:--------------------------:| 24 | |v2.14.x|[v2.14.0](https://github.com/TrueCloudLab/frostfs-api/releases/tag/v2.14.0)| 25 | 26 | ## Contributing 27 | 28 | Feel free to contribute to this project after reading the [contributing 29 | guidelines](CONTRIBUTING.md). 30 | 31 | Before starting to work on a certain topic, create a new issue first, describing 32 | the feature/topic you are going to implement. 33 | 34 | ## License 35 | 36 | This project is licensed under the Apache 2.0 License - 37 | see the [LICENSE](LICENSE) file for details 38 | -------------------------------------------------------------------------------- /accounting/accounting.go: -------------------------------------------------------------------------------- 1 | package accounting 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/refs" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/session" 6 | ) 7 | 8 | type BalanceRequestBody struct { 9 | ownerID *refs.OwnerID 10 | } 11 | 12 | type BalanceResponseBody struct { 13 | bal *Decimal 14 | } 15 | 16 | type Decimal struct { 17 | val int64 18 | 19 | prec uint32 20 | } 21 | 22 | type BalanceRequest struct { 23 | body *BalanceRequestBody 24 | 25 | session.RequestHeaders 26 | } 27 | 28 | type BalanceResponse struct { 29 | body *BalanceResponseBody 30 | 31 | session.ResponseHeaders 32 | } 33 | 34 | func (b *BalanceRequestBody) GetOwnerID() *refs.OwnerID { 35 | if b != nil { 36 | return b.ownerID 37 | } 38 | 39 | return nil 40 | } 41 | 42 | func (b *BalanceRequestBody) SetOwnerID(v *refs.OwnerID) { 43 | b.ownerID = v 44 | } 45 | 46 | func (b *BalanceRequest) GetBody() *BalanceRequestBody { 47 | if b != nil { 48 | return b.body 49 | } 50 | 51 | return nil 52 | } 53 | 54 | func (b *BalanceRequest) SetBody(v *BalanceRequestBody) { 55 | b.body = v 56 | } 57 | 58 | func (d *Decimal) GetValue() int64 { 59 | if d != nil { 60 | return d.val 61 | } 62 | 63 | return 0 64 | } 65 | 66 | func (d *Decimal) SetValue(v int64) { 67 | d.val = v 68 | } 69 | 70 | func (d *Decimal) GetPrecision() uint32 { 71 | if d != nil { 72 | return d.prec 73 | } 74 | 75 | return 0 76 | } 77 | 78 | func (d *Decimal) SetPrecision(v uint32) { 79 | d.prec = v 80 | } 81 | 82 | func (br *BalanceResponseBody) GetBalance() *Decimal { 83 | if br != nil { 84 | return br.bal 85 | } 86 | 87 | return nil 88 | } 89 | 90 | func (br *BalanceResponseBody) SetBalance(v *Decimal) { 91 | br.bal = v 92 | } 93 | 94 | func (br *BalanceResponse) GetBody() *BalanceResponseBody { 95 | if br != nil { 96 | return br.body 97 | } 98 | 99 | return nil 100 | } 101 | 102 | func (br *BalanceResponse) SetBody(v *BalanceResponseBody) { 103 | br.body = v 104 | } 105 | -------------------------------------------------------------------------------- /accounting/convert.go: -------------------------------------------------------------------------------- 1 | package accounting 2 | 3 | import ( 4 | accounting "github.com/TrueCloudLab/frostfs-api-go/v2/accounting/grpc" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/refs" 6 | refsGRPC "github.com/TrueCloudLab/frostfs-api-go/v2/refs/grpc" 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/grpc" 8 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 9 | ) 10 | 11 | func (b *BalanceRequestBody) ToGRPCMessage() grpc.Message { 12 | var m *accounting.BalanceRequest_Body 13 | 14 | if b != nil { 15 | m = new(accounting.BalanceRequest_Body) 16 | 17 | m.SetOwnerId(b.ownerID.ToGRPCMessage().(*refsGRPC.OwnerID)) 18 | } 19 | 20 | return m 21 | } 22 | 23 | func (b *BalanceRequestBody) FromGRPCMessage(m grpc.Message) error { 24 | v, ok := m.(*accounting.BalanceRequest_Body) 25 | if !ok { 26 | return message.NewUnexpectedMessageType(m, v) 27 | } 28 | 29 | var err error 30 | 31 | ownerID := v.GetOwnerId() 32 | if ownerID == nil { 33 | b.ownerID = nil 34 | } else { 35 | if b.ownerID == nil { 36 | b.ownerID = new(refs.OwnerID) 37 | } 38 | 39 | err = b.ownerID.FromGRPCMessage(ownerID) 40 | } 41 | 42 | return err 43 | } 44 | 45 | func (b *BalanceRequest) ToGRPCMessage() grpc.Message { 46 | var m *accounting.BalanceRequest 47 | 48 | if b != nil { 49 | m = new(accounting.BalanceRequest) 50 | 51 | m.SetBody(b.body.ToGRPCMessage().(*accounting.BalanceRequest_Body)) 52 | b.RequestHeaders.ToMessage(m) 53 | } 54 | 55 | return m 56 | } 57 | 58 | func (b *BalanceRequest) FromGRPCMessage(m grpc.Message) error { 59 | v, ok := m.(*accounting.BalanceRequest) 60 | if !ok { 61 | return message.NewUnexpectedMessageType(m, v) 62 | } 63 | 64 | var err error 65 | 66 | body := v.GetBody() 67 | if body == nil { 68 | b.body = nil 69 | } else { 70 | if b.body == nil { 71 | b.body = new(BalanceRequestBody) 72 | } 73 | 74 | err = b.body.FromGRPCMessage(body) 75 | if err != nil { 76 | return err 77 | } 78 | } 79 | 80 | return b.RequestHeaders.FromMessage(v) 81 | } 82 | 83 | func (d *Decimal) ToGRPCMessage() grpc.Message { 84 | var m *accounting.Decimal 85 | 86 | if d != nil { 87 | m = new(accounting.Decimal) 88 | 89 | m.SetValue(d.val) 90 | m.SetPrecision(d.prec) 91 | } 92 | 93 | return m 94 | } 95 | 96 | func (d *Decimal) FromGRPCMessage(m grpc.Message) error { 97 | v, ok := m.(*accounting.Decimal) 98 | if !ok { 99 | return message.NewUnexpectedMessageType(m, v) 100 | } 101 | 102 | d.val = v.GetValue() 103 | d.prec = v.GetPrecision() 104 | 105 | return nil 106 | } 107 | 108 | func (br *BalanceResponseBody) ToGRPCMessage() grpc.Message { 109 | var m *accounting.BalanceResponse_Body 110 | 111 | if br != nil { 112 | m = new(accounting.BalanceResponse_Body) 113 | 114 | m.SetBalance(br.bal.ToGRPCMessage().(*accounting.Decimal)) 115 | } 116 | 117 | return m 118 | } 119 | 120 | func (br *BalanceResponseBody) FromGRPCMessage(m grpc.Message) error { 121 | v, ok := m.(*accounting.BalanceResponse_Body) 122 | if !ok { 123 | return message.NewUnexpectedMessageType(m, v) 124 | } 125 | 126 | var err error 127 | 128 | bal := v.GetBalance() 129 | if bal == nil { 130 | br.bal = nil 131 | } else { 132 | if br.bal == nil { 133 | br.bal = new(Decimal) 134 | } 135 | 136 | err = br.bal.FromGRPCMessage(bal) 137 | } 138 | 139 | return err 140 | } 141 | 142 | func (br *BalanceResponse) ToGRPCMessage() grpc.Message { 143 | var m *accounting.BalanceResponse 144 | 145 | if br != nil { 146 | m = new(accounting.BalanceResponse) 147 | 148 | m.SetBody(br.body.ToGRPCMessage().(*accounting.BalanceResponse_Body)) 149 | br.ResponseHeaders.ToMessage(m) 150 | } 151 | 152 | return m 153 | } 154 | 155 | func (br *BalanceResponse) FromGRPCMessage(m grpc.Message) error { 156 | v, ok := m.(*accounting.BalanceResponse) 157 | if !ok { 158 | return message.NewUnexpectedMessageType(m, v) 159 | } 160 | 161 | var err error 162 | 163 | body := v.GetBody() 164 | if body == nil { 165 | br.body = nil 166 | } else { 167 | if br.body == nil { 168 | br.body = new(BalanceResponseBody) 169 | } 170 | 171 | err = br.body.FromGRPCMessage(body) 172 | if err != nil { 173 | return err 174 | } 175 | } 176 | 177 | return br.ResponseHeaders.FromMessage(v) 178 | } 179 | -------------------------------------------------------------------------------- /accounting/grpc/service.go: -------------------------------------------------------------------------------- 1 | package accounting 2 | 3 | import ( 4 | refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs/grpc" 5 | session "github.com/TrueCloudLab/frostfs-api-go/v2/session/grpc" 6 | ) 7 | 8 | // SetOwnerId sets identifier of the account owner. 9 | func (m *BalanceRequest_Body) SetOwnerId(v *refs.OwnerID) { 10 | m.OwnerId = v 11 | } 12 | 13 | // SetBody sets body of the request. 14 | func (m *BalanceRequest) SetBody(v *BalanceRequest_Body) { 15 | m.Body = v 16 | } 17 | 18 | // SetMetaHeader sets meta header of the request. 19 | func (m *BalanceRequest) SetMetaHeader(v *session.RequestMetaHeader) { 20 | m.MetaHeader = v 21 | } 22 | 23 | // SetVerifyHeader sets verification header of the request. 24 | func (m *BalanceRequest) SetVerifyHeader(v *session.RequestVerificationHeader) { 25 | m.VerifyHeader = v 26 | } 27 | 28 | // SetBalance sets balance value of the response. 29 | func (m *BalanceResponse_Body) SetBalance(v *Decimal) { 30 | m.Balance = v 31 | } 32 | 33 | // SetBody sets body of the response. 34 | func (m *BalanceResponse) SetBody(v *BalanceResponse_Body) { 35 | m.Body = v 36 | } 37 | 38 | // SetMetaHeader sets meta header of the response. 39 | func (m *BalanceResponse) SetMetaHeader(v *session.ResponseMetaHeader) { 40 | m.MetaHeader = v 41 | } 42 | 43 | // SetVerifyHeader sets verification header of the response. 44 | func (m *BalanceResponse) SetVerifyHeader(v *session.ResponseVerificationHeader) { 45 | m.VerifyHeader = v 46 | } 47 | -------------------------------------------------------------------------------- /accounting/grpc/types.go: -------------------------------------------------------------------------------- 1 | package accounting 2 | 3 | // SetValue sets value of the decimal number. 4 | func (m *Decimal) SetValue(v int64) { 5 | m.Value = v 6 | } 7 | 8 | // SetPrecision sets precision of the decimal number. 9 | func (m *Decimal) SetPrecision(v uint32) { 10 | m.Precision = v 11 | } 12 | -------------------------------------------------------------------------------- /accounting/json.go: -------------------------------------------------------------------------------- 1 | package accounting 2 | 3 | import ( 4 | accounting "github.com/TrueCloudLab/frostfs-api-go/v2/accounting/grpc" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 6 | ) 7 | 8 | func (d *Decimal) MarshalJSON() ([]byte, error) { 9 | return message.MarshalJSON(d) 10 | } 11 | 12 | func (d *Decimal) UnmarshalJSON(data []byte) error { 13 | return message.UnmarshalJSON(d, data, new(accounting.Decimal)) 14 | } 15 | -------------------------------------------------------------------------------- /accounting/marshal.go: -------------------------------------------------------------------------------- 1 | package accounting 2 | 3 | import ( 4 | accounting "github.com/TrueCloudLab/frostfs-api-go/v2/accounting/grpc" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 6 | protoutil "github.com/TrueCloudLab/frostfs-api-go/v2/util/proto" 7 | ) 8 | 9 | const ( 10 | decimalValueField = 1 11 | decimalPrecisionField = 2 12 | 13 | balanceReqBodyOwnerField = 1 14 | 15 | balanceRespBodyDecimalField = 1 16 | ) 17 | 18 | func (d *Decimal) StableMarshal(buf []byte) []byte { 19 | if d == nil { 20 | return []byte{} 21 | } 22 | 23 | if buf == nil { 24 | buf = make([]byte, d.StableSize()) 25 | } 26 | 27 | var offset int 28 | 29 | offset += protoutil.Int64Marshal(decimalValueField, buf[offset:], d.val) 30 | protoutil.UInt32Marshal(decimalPrecisionField, buf[offset:], d.prec) 31 | 32 | return buf 33 | } 34 | 35 | func (d *Decimal) StableSize() (size int) { 36 | if d == nil { 37 | return 0 38 | } 39 | 40 | size += protoutil.Int64Size(decimalValueField, d.val) 41 | size += protoutil.UInt32Size(decimalPrecisionField, d.prec) 42 | 43 | return size 44 | } 45 | 46 | func (d *Decimal) Unmarshal(data []byte) error { 47 | return message.Unmarshal(d, data, new(accounting.Decimal)) 48 | } 49 | 50 | func (b *BalanceRequestBody) StableMarshal(buf []byte) []byte { 51 | if b == nil { 52 | return []byte{} 53 | } 54 | 55 | if buf == nil { 56 | buf = make([]byte, b.StableSize()) 57 | } 58 | 59 | protoutil.NestedStructureMarshal(balanceReqBodyOwnerField, buf, b.ownerID) 60 | 61 | return buf 62 | } 63 | 64 | func (b *BalanceRequestBody) StableSize() (size int) { 65 | if b == nil { 66 | return 0 67 | } 68 | 69 | size = protoutil.NestedStructureSize(balanceReqBodyOwnerField, b.ownerID) 70 | 71 | return size 72 | } 73 | 74 | func (b *BalanceRequestBody) Unmarshal(data []byte) error { 75 | return message.Unmarshal(b, data, new(accounting.BalanceRequest_Body)) 76 | } 77 | 78 | func (br *BalanceResponseBody) StableMarshal(buf []byte) []byte { 79 | if br == nil { 80 | return []byte{} 81 | } 82 | 83 | if buf == nil { 84 | buf = make([]byte, br.StableSize()) 85 | } 86 | 87 | protoutil.NestedStructureMarshal(balanceRespBodyDecimalField, buf, br.bal) 88 | 89 | return buf 90 | } 91 | 92 | func (br *BalanceResponseBody) StableSize() (size int) { 93 | if br == nil { 94 | return 0 95 | } 96 | 97 | size = protoutil.NestedStructureSize(balanceRespBodyDecimalField, br.bal) 98 | 99 | return size 100 | } 101 | 102 | func (br *BalanceResponseBody) Unmarshal(data []byte) error { 103 | return message.Unmarshal(br, data, new(accounting.BalanceResponse_Body)) 104 | } 105 | -------------------------------------------------------------------------------- /accounting/message_test.go: -------------------------------------------------------------------------------- 1 | package accounting_test 2 | 3 | import ( 4 | "testing" 5 | 6 | accountingtest "github.com/TrueCloudLab/frostfs-api-go/v2/accounting/test" 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 8 | messagetest "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message/test" 9 | ) 10 | 11 | func TestMessage(t *testing.T) { 12 | messagetest.TestRPCMessage(t, 13 | func(empty bool) message.Message { return accountingtest.GenerateDecimal(empty) }, 14 | func(empty bool) message.Message { return accountingtest.GenerateBalanceRequestBody(empty) }, 15 | func(empty bool) message.Message { return accountingtest.GenerateBalanceRequest(empty) }, 16 | func(empty bool) message.Message { return accountingtest.GenerateBalanceResponseBody(empty) }, 17 | func(empty bool) message.Message { return accountingtest.GenerateBalanceResponse(empty) }, 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /accounting/test/generate.go: -------------------------------------------------------------------------------- 1 | package accountingtest 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/accounting" 5 | accountingtest "github.com/TrueCloudLab/frostfs-api-go/v2/refs/test" 6 | sessiontest "github.com/TrueCloudLab/frostfs-api-go/v2/session/test" 7 | ) 8 | 9 | func GenerateBalanceRequest(empty bool) *accounting.BalanceRequest { 10 | m := new(accounting.BalanceRequest) 11 | 12 | if !empty { 13 | m.SetBody(GenerateBalanceRequestBody(false)) 14 | } 15 | 16 | m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) 17 | m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) 18 | 19 | return m 20 | } 21 | 22 | func GenerateBalanceRequestBody(empty bool) *accounting.BalanceRequestBody { 23 | m := new(accounting.BalanceRequestBody) 24 | 25 | if !empty { 26 | m.SetOwnerID(accountingtest.GenerateOwnerID(false)) 27 | } 28 | 29 | return m 30 | } 31 | 32 | func GenerateBalanceResponse(empty bool) *accounting.BalanceResponse { 33 | m := new(accounting.BalanceResponse) 34 | 35 | if !empty { 36 | m.SetBody(GenerateBalanceResponseBody(false)) 37 | } 38 | 39 | m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) 40 | m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) 41 | 42 | return m 43 | } 44 | 45 | func GenerateBalanceResponseBody(empty bool) *accounting.BalanceResponseBody { 46 | m := new(accounting.BalanceResponseBody) 47 | 48 | if !empty { 49 | m.SetBalance(GenerateDecimal(false)) 50 | } 51 | 52 | return m 53 | } 54 | 55 | func GenerateDecimal(empty bool) *accounting.Decimal { 56 | m := new(accounting.Decimal) 57 | 58 | if !empty { 59 | m.SetValue(1) 60 | m.SetPrecision(2) 61 | } 62 | 63 | return m 64 | } 65 | -------------------------------------------------------------------------------- /acl/bench_test.go: -------------------------------------------------------------------------------- 1 | package acl_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/acl" 7 | aclGrpc "github.com/TrueCloudLab/frostfs-api-go/v2/acl/grpc" 8 | acltest "github.com/TrueCloudLab/frostfs-api-go/v2/acl/test" 9 | ) 10 | 11 | func BenchmarkTable_ToGRPCMessage(b *testing.B) { 12 | const size = 4 13 | 14 | tb := new(acl.Table) 15 | rs := make([]acl.Record, size) 16 | for i := range rs { 17 | fs := make([]acl.HeaderFilter, size) 18 | for j := range fs { 19 | fs[j] = *acltest.GenerateFilter(false) 20 | } 21 | ts := make([]acl.Target, size) 22 | for j := range ts { 23 | ts[j] = *acltest.GenerateTarget(false) 24 | } 25 | 26 | rs[i].SetFilters(fs) 27 | rs[i].SetTargets(ts) 28 | } 29 | tb.SetRecords(rs) 30 | 31 | raw := tb.ToGRPCMessage() 32 | 33 | b.Run("to grpc message", func(b *testing.B) { 34 | b.ReportAllocs() 35 | for i := 0; i < b.N; i++ { 36 | raw := tb.ToGRPCMessage() 37 | if len(tb.GetRecords()) != len(raw.(*aclGrpc.EACLTable).Records) { 38 | b.FailNow() 39 | } 40 | } 41 | }) 42 | b.Run("from grpc message", func(b *testing.B) { 43 | b.ReportAllocs() 44 | for i := 0; i < b.N; i++ { 45 | tb := new(acl.Table) 46 | if tb.FromGRPCMessage(raw) != nil { 47 | b.FailNow() 48 | } 49 | } 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /acl/filters.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | // ObjectFilterPrefix is a prefix of key to object header value or property. 4 | const ObjectFilterPrefix = "$Object:" 5 | 6 | const ( 7 | // FilterObjectVersion is a filter key to "version" field of the object header. 8 | FilterObjectVersion = ObjectFilterPrefix + "version" 9 | 10 | // FilterObjectID is a filter key to "object_id" field of the object. 11 | FilterObjectID = ObjectFilterPrefix + "objectID" 12 | 13 | // FilterObjectContainerID is a filter key to "container_id" field of the object header. 14 | FilterObjectContainerID = ObjectFilterPrefix + "containerID" 15 | 16 | // FilterObjectOwnerID is a filter key to "owner_id" field of the object header. 17 | FilterObjectOwnerID = ObjectFilterPrefix + "ownerID" 18 | 19 | // FilterObjectCreationEpoch is a filter key to "creation_epoch" field of the object header. 20 | FilterObjectCreationEpoch = ObjectFilterPrefix + "creationEpoch" 21 | 22 | // FilterObjectPayloadLength is a filter key to "payload_length" field of the object header. 23 | FilterObjectPayloadLength = ObjectFilterPrefix + "payloadLength" 24 | 25 | // FilterObjectPayloadHash is a filter key to "payload_hash" field of the object header. 26 | FilterObjectPayloadHash = ObjectFilterPrefix + "payloadHash" 27 | 28 | // FilterObjectType is a filter key to "object_type" field of the object header. 29 | FilterObjectType = ObjectFilterPrefix + "objectType" 30 | 31 | // FilterObjectHomomorphicHash is a filter key to "homomorphic_hash" field of the object header. 32 | FilterObjectHomomorphicHash = ObjectFilterPrefix + "homomorphicHash" 33 | ) 34 | -------------------------------------------------------------------------------- /acl/json.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | import ( 4 | acl "github.com/TrueCloudLab/frostfs-api-go/v2/acl/grpc" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 6 | ) 7 | 8 | func (f *HeaderFilter) MarshalJSON() ([]byte, error) { 9 | return message.MarshalJSON(f) 10 | } 11 | 12 | func (f *HeaderFilter) UnmarshalJSON(data []byte) error { 13 | return message.UnmarshalJSON(f, data, new(acl.EACLRecord_Filter)) 14 | } 15 | 16 | func (t *Target) MarshalJSON() ([]byte, error) { 17 | return message.MarshalJSON(t) 18 | } 19 | 20 | func (t *Target) UnmarshalJSON(data []byte) error { 21 | return message.UnmarshalJSON(t, data, new(acl.EACLRecord_Target)) 22 | } 23 | 24 | func (r *Record) MarshalJSON() ([]byte, error) { 25 | return message.MarshalJSON(r) 26 | } 27 | 28 | func (r *Record) UnmarshalJSON(data []byte) error { 29 | return message.UnmarshalJSON(r, data, new(acl.EACLRecord)) 30 | } 31 | 32 | func (t *Table) MarshalJSON() ([]byte, error) { 33 | return message.MarshalJSON(t) 34 | } 35 | 36 | func (t *Table) UnmarshalJSON(data []byte) error { 37 | return message.UnmarshalJSON(t, data, new(acl.EACLTable)) 38 | } 39 | 40 | func (l *TokenLifetime) MarshalJSON() ([]byte, error) { 41 | return message.MarshalJSON(l) 42 | } 43 | 44 | func (l *TokenLifetime) UnmarshalJSON(data []byte) error { 45 | return message.UnmarshalJSON(l, data, new(acl.BearerToken_Body_TokenLifetime)) 46 | } 47 | 48 | func (bt *BearerTokenBody) MarshalJSON() ([]byte, error) { 49 | return message.MarshalJSON(bt) 50 | } 51 | 52 | func (bt *BearerTokenBody) UnmarshalJSON(data []byte) error { 53 | return message.UnmarshalJSON(bt, data, new(acl.BearerToken_Body)) 54 | } 55 | 56 | func (bt *BearerToken) MarshalJSON() ([]byte, error) { 57 | return message.MarshalJSON(bt) 58 | } 59 | 60 | func (bt *BearerToken) UnmarshalJSON(data []byte) error { 61 | return message.UnmarshalJSON(bt, data, new(acl.BearerToken)) 62 | } 63 | -------------------------------------------------------------------------------- /acl/message_test.go: -------------------------------------------------------------------------------- 1 | package acl_test 2 | 3 | import ( 4 | "testing" 5 | 6 | acltest "github.com/TrueCloudLab/frostfs-api-go/v2/acl/test" 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 8 | messagetest "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message/test" 9 | ) 10 | 11 | func TestMessageConvert(t *testing.T) { 12 | messagetest.TestRPCMessage(t, 13 | func(empty bool) message.Message { return acltest.GenerateFilter(empty) }, 14 | func(empty bool) message.Message { return acltest.GenerateTarget(empty) }, 15 | func(empty bool) message.Message { return acltest.GenerateRecord(empty) }, 16 | func(empty bool) message.Message { return acltest.GenerateTable(empty) }, 17 | func(empty bool) message.Message { return acltest.GenerateTokenLifetime(empty) }, 18 | func(empty bool) message.Message { return acltest.GenerateBearerTokenBody(empty) }, 19 | func(empty bool) message.Message { return acltest.GenerateBearerToken(empty) }, 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /acl/string.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | import ( 4 | acl "github.com/TrueCloudLab/frostfs-api-go/v2/acl/grpc" 5 | ) 6 | 7 | // String returns string representation of Action. 8 | func (x Action) String() string { 9 | return ActionToGRPCField(x).String() 10 | } 11 | 12 | // FromString parses Action from a string representation. 13 | // It is a reverse action to String(). 14 | // 15 | // Returns true if s was parsed successfully. 16 | func (x *Action) FromString(s string) bool { 17 | var g acl.Action 18 | 19 | ok := g.FromString(s) 20 | 21 | if ok { 22 | *x = ActionFromGRPCField(g) 23 | } 24 | 25 | return ok 26 | } 27 | 28 | // String returns string representation of Role. 29 | func (x Role) String() string { 30 | return RoleToGRPCField(x).String() 31 | } 32 | 33 | // FromString parses Role from a string representation. 34 | // It is a reverse action to String(). 35 | // 36 | // Returns true if s was parsed successfully. 37 | func (x *Role) FromString(s string) bool { 38 | var g acl.Role 39 | 40 | ok := g.FromString(s) 41 | 42 | if ok { 43 | *x = RoleFromGRPCField(g) 44 | } 45 | 46 | return ok 47 | } 48 | 49 | // String returns string representation of Operation. 50 | func (x Operation) String() string { 51 | return OperationToGRPCField(x).String() 52 | } 53 | 54 | // FromString parses Operation from a string representation. 55 | // It is a reverse action to String(). 56 | // 57 | // Returns true if s was parsed successfully. 58 | func (x *Operation) FromString(s string) bool { 59 | var g acl.Operation 60 | 61 | ok := g.FromString(s) 62 | 63 | if ok { 64 | *x = OperationFromGRPCField(g) 65 | } 66 | 67 | return ok 68 | } 69 | 70 | // String returns string representation of MatchType. 71 | func (x MatchType) String() string { 72 | return MatchTypeToGRPCField(x).String() 73 | } 74 | 75 | // FromString parses MatchType from a string representation. 76 | // It is a reverse action to String(). 77 | // 78 | // Returns true if s was parsed successfully. 79 | func (x *MatchType) FromString(s string) bool { 80 | var g acl.MatchType 81 | 82 | ok := g.FromString(s) 83 | 84 | if ok { 85 | *x = MatchTypeFromGRPCField(g) 86 | } 87 | 88 | return ok 89 | } 90 | 91 | // String returns string representation of HeaderType. 92 | func (x HeaderType) String() string { 93 | return HeaderTypeToGRPCField(x).String() 94 | } 95 | 96 | // FromString parses HeaderType from a string representation. 97 | // It is a reverse action to String(). 98 | // 99 | // Returns true if s was parsed successfully. 100 | func (x *HeaderType) FromString(s string) bool { 101 | var g acl.HeaderType 102 | 103 | ok := g.FromString(s) 104 | 105 | if ok { 106 | *x = HeaderTypeFromGRPCField(g) 107 | } 108 | 109 | return ok 110 | } 111 | -------------------------------------------------------------------------------- /acl/test/generate.go: -------------------------------------------------------------------------------- 1 | package acltest 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/acl" 5 | accountingtest "github.com/TrueCloudLab/frostfs-api-go/v2/refs/test" 6 | ) 7 | 8 | func GenerateBearerToken(empty bool) *acl.BearerToken { 9 | m := new(acl.BearerToken) 10 | 11 | if !empty { 12 | m.SetBody(GenerateBearerTokenBody(false)) 13 | } 14 | 15 | m.SetSignature(accountingtest.GenerateSignature(empty)) 16 | 17 | return m 18 | } 19 | 20 | func GenerateBearerTokenBody(empty bool) *acl.BearerTokenBody { 21 | m := new(acl.BearerTokenBody) 22 | 23 | if !empty { 24 | m.SetOwnerID(accountingtest.GenerateOwnerID(false)) 25 | m.SetEACL(GenerateTable(false)) 26 | m.SetLifetime(GenerateTokenLifetime(false)) 27 | } 28 | 29 | return m 30 | } 31 | 32 | func GenerateTable(empty bool) *acl.Table { 33 | m := new(acl.Table) 34 | 35 | if !empty { 36 | m.SetRecords(GenerateRecords(false)) 37 | m.SetContainerID(accountingtest.GenerateContainerID(false)) 38 | } 39 | 40 | m.SetVersion(accountingtest.GenerateVersion(empty)) 41 | 42 | return m 43 | } 44 | 45 | func GenerateRecords(empty bool) []acl.Record { 46 | var rs []acl.Record 47 | 48 | if !empty { 49 | rs = append(rs, 50 | *GenerateRecord(false), 51 | *GenerateRecord(false), 52 | ) 53 | } 54 | 55 | return rs 56 | } 57 | 58 | func GenerateRecord(empty bool) *acl.Record { 59 | m := new(acl.Record) 60 | 61 | if !empty { 62 | m.SetAction(acl.ActionAllow) 63 | m.SetOperation(acl.OperationGet) 64 | m.SetFilters(GenerateFilters(false)) 65 | m.SetTargets(GenerateTargets(false)) 66 | } 67 | 68 | return m 69 | } 70 | 71 | func GenerateFilters(empty bool) []acl.HeaderFilter { 72 | var fs []acl.HeaderFilter 73 | 74 | if !empty { 75 | fs = append(fs, 76 | *GenerateFilter(false), 77 | *GenerateFilter(false), 78 | ) 79 | } 80 | 81 | return fs 82 | } 83 | 84 | func GenerateFilter(empty bool) *acl.HeaderFilter { 85 | m := new(acl.HeaderFilter) 86 | 87 | if !empty { 88 | m.SetKey("key") 89 | m.SetValue("val") 90 | m.SetHeaderType(acl.HeaderTypeRequest) 91 | m.SetMatchType(acl.MatchTypeStringEqual) 92 | } 93 | 94 | return m 95 | } 96 | 97 | func GenerateTargets(empty bool) []acl.Target { 98 | var ts []acl.Target 99 | 100 | if !empty { 101 | ts = append(ts, 102 | *GenerateTarget(false), 103 | *GenerateTarget(false), 104 | ) 105 | } 106 | 107 | return ts 108 | } 109 | 110 | func GenerateTarget(empty bool) *acl.Target { 111 | m := new(acl.Target) 112 | 113 | if !empty { 114 | m.SetRole(acl.RoleSystem) 115 | m.SetKeys([][]byte{{1}, {2}}) 116 | } 117 | 118 | return m 119 | } 120 | 121 | func GenerateTokenLifetime(empty bool) *acl.TokenLifetime { 122 | m := new(acl.TokenLifetime) 123 | 124 | if !empty { 125 | m.SetExp(1) 126 | m.SetIat(2) 127 | m.SetExp(3) 128 | } 129 | 130 | return m 131 | } 132 | -------------------------------------------------------------------------------- /audit/convert.go: -------------------------------------------------------------------------------- 1 | package audit 2 | 3 | import ( 4 | audit "github.com/TrueCloudLab/frostfs-api-go/v2/audit/grpc" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/refs" 6 | refsGRPC "github.com/TrueCloudLab/frostfs-api-go/v2/refs/grpc" 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/grpc" 8 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 9 | ) 10 | 11 | func (a *DataAuditResult) ToGRPCMessage() grpc.Message { 12 | var m *audit.DataAuditResult 13 | 14 | if a != nil { 15 | m = new(audit.DataAuditResult) 16 | 17 | m.SetAuditEpoch(a.auditEpoch) 18 | m.SetPublicKey(a.pubKey) 19 | m.SetContainerId(a.cid.ToGRPCMessage().(*refsGRPC.ContainerID)) 20 | m.SetComplete(a.complete) 21 | m.SetVersion(a.version.ToGRPCMessage().(*refsGRPC.Version)) 22 | m.SetPassNodes(a.passNodes) 23 | m.SetFailNodes(a.failNodes) 24 | m.SetRetries(a.retries) 25 | m.SetRequests(a.requests) 26 | m.SetHit(a.hit) 27 | m.SetMiss(a.miss) 28 | m.SetFail(a.fail) 29 | m.SetPassSg(refs.ObjectIDListToGRPCMessage(a.passSG)) 30 | m.SetFailSg(refs.ObjectIDListToGRPCMessage(a.failSG)) 31 | } 32 | 33 | return m 34 | } 35 | 36 | func (a *DataAuditResult) FromGRPCMessage(m grpc.Message) error { 37 | v, ok := m.(*audit.DataAuditResult) 38 | if !ok { 39 | return message.NewUnexpectedMessageType(m, v) 40 | } 41 | 42 | var err error 43 | 44 | cid := v.GetContainerId() 45 | if cid == nil { 46 | a.cid = nil 47 | } else { 48 | if a.cid == nil { 49 | a.cid = new(refs.ContainerID) 50 | } 51 | 52 | err = a.cid.FromGRPCMessage(cid) 53 | if err != nil { 54 | return err 55 | } 56 | } 57 | 58 | version := v.GetVersion() 59 | if version == nil { 60 | a.version = nil 61 | } else { 62 | if a.version == nil { 63 | a.version = new(refs.Version) 64 | } 65 | 66 | err = a.version.FromGRPCMessage(version) 67 | if err != nil { 68 | return err 69 | } 70 | } 71 | 72 | a.passSG, err = refs.ObjectIDListFromGRPCMessage(v.GetPassSg()) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | a.failSG, err = refs.ObjectIDListFromGRPCMessage(v.GetFailSg()) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | a.auditEpoch = v.GetAuditEpoch() 83 | a.pubKey = v.GetPublicKey() 84 | a.complete = v.GetComplete() 85 | a.passNodes = v.GetPassNodes() 86 | a.failNodes = v.GetFailNodes() 87 | a.retries = v.GetRetries() 88 | a.requests = v.GetRequests() 89 | a.hit = v.GetHit() 90 | a.miss = v.GetMiss() 91 | a.fail = v.GetFail() 92 | 93 | return err 94 | } 95 | -------------------------------------------------------------------------------- /audit/grpc/types.go: -------------------------------------------------------------------------------- 1 | package audit 2 | 3 | import ( 4 | refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs/grpc" 5 | ) 6 | 7 | // SetVersion is a Version field setter. 8 | func (x *DataAuditResult) SetVersion(v *refs.Version) { 9 | x.Version = v 10 | } 11 | 12 | // SetAuditEpoch is an AuditEpoch field setter. 13 | func (x *DataAuditResult) SetAuditEpoch(v uint64) { 14 | x.AuditEpoch = v 15 | } 16 | 17 | // SetContainerId is a ContainerId field setter. 18 | func (x *DataAuditResult) SetContainerId(v *refs.ContainerID) { 19 | x.ContainerId = v 20 | } 21 | 22 | // SetPublicKey is a PublicKey field setter. 23 | func (x *DataAuditResult) SetPublicKey(v []byte) { 24 | x.PublicKey = v 25 | } 26 | 27 | // SetComplete is a Complete field setter. 28 | func (x *DataAuditResult) SetComplete(v bool) { 29 | x.Complete = v 30 | } 31 | 32 | // SetRequests is a Requests field setter. 33 | func (x *DataAuditResult) SetRequests(v uint32) { 34 | x.Requests = v 35 | } 36 | 37 | // SetRetries is a Retries field setter. 38 | func (x *DataAuditResult) SetRetries(v uint32) { 39 | x.Retries = v 40 | } 41 | 42 | // SetPassSg is a PassSg field setter. 43 | func (x *DataAuditResult) SetPassSg(v []*refs.ObjectID) { 44 | x.PassSg = v 45 | } 46 | 47 | // SetFailSg is a FailSg field setter. 48 | func (x *DataAuditResult) SetFailSg(v []*refs.ObjectID) { 49 | x.FailSg = v 50 | } 51 | 52 | // SetHit is a Hit field setter. 53 | func (x *DataAuditResult) SetHit(v uint32) { 54 | x.Hit = v 55 | } 56 | 57 | // SetMiss is a Miss field setter. 58 | func (x *DataAuditResult) SetMiss(v uint32) { 59 | x.Miss = v 60 | } 61 | 62 | // SetFail is a Fail field setter. 63 | func (x *DataAuditResult) SetFail(v uint32) { 64 | x.Fail = v 65 | } 66 | 67 | // SetPassNodes is a PassNodes field setter. 68 | func (x *DataAuditResult) SetPassNodes(v [][]byte) { 69 | x.PassNodes = v 70 | } 71 | 72 | // SetFailNodes is a FailNodes field setter. 73 | func (x *DataAuditResult) SetFailNodes(v [][]byte) { 74 | x.FailNodes = v 75 | } 76 | -------------------------------------------------------------------------------- /audit/json.go: -------------------------------------------------------------------------------- 1 | package audit 2 | 3 | import ( 4 | audit "github.com/TrueCloudLab/frostfs-api-go/v2/audit/grpc" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 6 | ) 7 | 8 | func (a *DataAuditResult) MarshalJSON() ([]byte, error) { 9 | return message.MarshalJSON(a) 10 | } 11 | 12 | func (a *DataAuditResult) UnmarshalJSON(data []byte) error { 13 | return message.UnmarshalJSON(a, data, new(audit.DataAuditResult)) 14 | } 15 | -------------------------------------------------------------------------------- /audit/marshal.go: -------------------------------------------------------------------------------- 1 | package audit 2 | 3 | import ( 4 | audit "github.com/TrueCloudLab/frostfs-api-go/v2/audit/grpc" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/refs" 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/util/proto" 8 | ) 9 | 10 | const ( 11 | _ = iota 12 | versionFNum 13 | auditEpochFNum 14 | cidFNum 15 | pubKeyFNum 16 | completeFNum 17 | requestsFNum 18 | retriesFNum 19 | passSGFNum 20 | failSGFNum 21 | hitFNum 22 | missFNum 23 | failFNum 24 | passNodesFNum 25 | failNodesFNum 26 | ) 27 | 28 | // StableMarshal marshals unified DataAuditResult structure into a protobuf 29 | // binary format without field order shuffle. 30 | func (a *DataAuditResult) StableMarshal(buf []byte) []byte { 31 | if a == nil { 32 | return []byte{} 33 | } 34 | 35 | if buf == nil { 36 | buf = make([]byte, a.StableSize()) 37 | } 38 | 39 | var offset int 40 | 41 | offset += proto.NestedStructureMarshal(versionFNum, buf[offset:], a.version) 42 | offset += proto.Fixed64Marshal(auditEpochFNum, buf[offset:], a.auditEpoch) 43 | offset += proto.NestedStructureMarshal(cidFNum, buf[offset:], a.cid) 44 | offset += proto.BytesMarshal(pubKeyFNum, buf[offset:], a.pubKey) 45 | offset += proto.BoolMarshal(completeFNum, buf[offset:], a.complete) 46 | offset += proto.UInt32Marshal(requestsFNum, buf[offset:], a.requests) 47 | offset += proto.UInt32Marshal(retriesFNum, buf[offset:], a.retries) 48 | offset += refs.ObjectIDNestedListMarshal(passSGFNum, buf[offset:], a.passSG) 49 | offset += refs.ObjectIDNestedListMarshal(failSGFNum, buf[offset:], a.failSG) 50 | offset += proto.UInt32Marshal(hitFNum, buf[offset:], a.hit) 51 | offset += proto.UInt32Marshal(missFNum, buf[offset:], a.miss) 52 | offset += proto.UInt32Marshal(failFNum, buf[offset:], a.fail) 53 | offset += proto.RepeatedBytesMarshal(passNodesFNum, buf[offset:], a.passNodes) 54 | proto.RepeatedBytesMarshal(failNodesFNum, buf[offset:], a.failNodes) 55 | 56 | return buf 57 | } 58 | 59 | // StableSize returns byte length of DataAuditResult structure 60 | // marshaled by StableMarshal function. 61 | func (a *DataAuditResult) StableSize() (size int) { 62 | if a == nil { 63 | return 0 64 | } 65 | 66 | size += proto.NestedStructureSize(versionFNum, a.version) 67 | size += proto.Fixed64Size(auditEpochFNum, a.auditEpoch) 68 | size += proto.NestedStructureSize(cidFNum, a.cid) 69 | size += proto.BytesSize(pubKeyFNum, a.pubKey) 70 | size += proto.BoolSize(completeFNum, a.complete) 71 | size += proto.UInt32Size(requestsFNum, a.requests) 72 | size += proto.UInt32Size(retriesFNum, a.retries) 73 | size += refs.ObjectIDNestedListSize(passSGFNum, a.passSG) 74 | size += refs.ObjectIDNestedListSize(failSGFNum, a.failSG) 75 | size += proto.UInt32Size(hitFNum, a.hit) 76 | size += proto.UInt32Size(missFNum, a.miss) 77 | size += proto.UInt32Size(failFNum, a.fail) 78 | size += proto.RepeatedBytesSize(passNodesFNum, a.passNodes) 79 | size += proto.RepeatedBytesSize(failNodesFNum, a.failNodes) 80 | 81 | return size 82 | } 83 | 84 | // Unmarshal unmarshals DataAuditResult structure from its protobuf 85 | // binary representation. 86 | func (a *DataAuditResult) Unmarshal(data []byte) error { 87 | return message.Unmarshal(a, data, new(audit.DataAuditResult)) 88 | } 89 | -------------------------------------------------------------------------------- /audit/message_test.go: -------------------------------------------------------------------------------- 1 | package audit_test 2 | 3 | import ( 4 | "testing" 5 | 6 | audittest "github.com/TrueCloudLab/frostfs-api-go/v2/audit/test" 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 8 | messagetest "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message/test" 9 | ) 10 | 11 | func TestMessageConvert(t *testing.T) { 12 | messagetest.TestRPCMessage(t, 13 | func(empty bool) message.Message { return audittest.GenerateDataAuditResult(empty) }, 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /audit/test/generate.go: -------------------------------------------------------------------------------- 1 | package audittest 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/audit" 5 | refstest "github.com/TrueCloudLab/frostfs-api-go/v2/refs/test" 6 | ) 7 | 8 | func GenerateDataAuditResult(empty bool) *audit.DataAuditResult { 9 | m := new(audit.DataAuditResult) 10 | 11 | if !empty { 12 | m.SetPublicKey([]byte{1, 2, 3}) 13 | m.SetAuditEpoch(13) 14 | m.SetHit(100) 15 | m.SetMiss(200) 16 | m.SetFail(300) 17 | m.SetComplete(true) 18 | m.SetPassNodes([][]byte{{1}, {2}}) 19 | m.SetFailNodes([][]byte{{3}, {4}}) 20 | m.SetRequests(666) 21 | m.SetRetries(777) 22 | m.SetVersion(refstest.GenerateVersion(false)) 23 | m.SetContainerID(refstest.GenerateContainerID(false)) 24 | m.SetPassSG(refstest.GenerateObjectIDs(false)) 25 | m.SetFailSG(refstest.GenerateObjectIDs(false)) 26 | } 27 | 28 | return m 29 | } 30 | -------------------------------------------------------------------------------- /container/attributes.go: -------------------------------------------------------------------------------- 1 | package container 2 | 3 | // SysAttributePrefix is a prefix of key to system attribute. 4 | const SysAttributePrefix = "__NEOFS__" 5 | 6 | const ( 7 | // SysAttributeSubnet is a string ID of container's storage subnet. 8 | SysAttributeSubnet = SysAttributePrefix + "SUBNET" 9 | 10 | // SysAttributeName is a string of human-friendly container name registered as the domain in NNS contract. 11 | SysAttributeName = SysAttributePrefix + "NAME" 12 | 13 | // SysAttributeZone is a string of zone for container name. 14 | SysAttributeZone = SysAttributePrefix + "ZONE" 15 | 16 | // SysAttributeHomomorphicHashing is a container's homomorphic hashing state. 17 | SysAttributeHomomorphicHashing = SysAttributePrefix + "DISABLE_HOMOMORPHIC_HASHING" 18 | ) 19 | 20 | // SysAttributeZoneDefault is a default value for SysAttributeZone attribute. 21 | const SysAttributeZoneDefault = "container" 22 | 23 | const disabledHomomorphicHashingValue = "true" 24 | 25 | // HomomorphicHashingState returns container's homomorphic 26 | // hashing state: 27 | // - true if hashing is enabled; 28 | // - false if hashing is disabled. 29 | // 30 | // All container's attributes must be unique, otherwise behavior 31 | // is undefined. 32 | // 33 | // See also SetHomomorphicHashingState. 34 | func (c Container) HomomorphicHashingState() bool { 35 | for i := range c.attr { 36 | if c.attr[i].GetKey() == SysAttributeHomomorphicHashing { 37 | return c.attr[i].GetValue() != disabledHomomorphicHashingValue 38 | } 39 | } 40 | 41 | return true 42 | } 43 | 44 | // SetHomomorphicHashingState sets homomorphic hashing state for 45 | // container. 46 | // 47 | // All container's attributes must be unique, otherwise behavior 48 | // is undefined. 49 | // 50 | // See also HomomorphicHashingState. 51 | func (c *Container) SetHomomorphicHashingState(enable bool) { 52 | for i := range c.attr { 53 | if c.attr[i].GetKey() == SysAttributeHomomorphicHashing { 54 | if enable { 55 | // approach without allocation/waste 56 | // coping works since the attributes 57 | // order is not important 58 | c.attr[i] = c.attr[len(c.attr)-1] 59 | c.attr = c.attr[:len(c.attr)-1] 60 | } else { 61 | c.attr[i].SetValue(disabledHomomorphicHashingValue) 62 | } 63 | 64 | return 65 | } 66 | } 67 | 68 | if !enable { 69 | attr := Attribute{} 70 | attr.SetKey(SysAttributeHomomorphicHashing) 71 | attr.SetValue(disabledHomomorphicHashingValue) 72 | 73 | c.attr = append(c.attr, attr) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /container/attributes_test.go: -------------------------------------------------------------------------------- 1 | package container_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/container" 7 | containertest "github.com/TrueCloudLab/frostfs-api-go/v2/container/test" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestContainer_HomomorphicHashingDisabled(t *testing.T) { 12 | cnr := containertest.GenerateContainer(false) 13 | 14 | t.Run("defaults", func(t *testing.T) { 15 | require.True(t, cnr.HomomorphicHashingState()) 16 | }) 17 | 18 | t.Run("disabled", func(t *testing.T) { 19 | attr := container.Attribute{} 20 | attr.SetKey(container.SysAttributeHomomorphicHashing) 21 | attr.SetValue("NOT_true") 22 | 23 | cnr.SetAttributes(append(cnr.GetAttributes(), attr)) 24 | require.True(t, cnr.HomomorphicHashingState()) 25 | 26 | attr.SetValue("true") 27 | 28 | cnr.SetAttributes([]container.Attribute{attr}) 29 | require.False(t, cnr.HomomorphicHashingState()) 30 | }) 31 | } 32 | 33 | func TestContainer_SetHomomorphicHashingState(t *testing.T) { 34 | cnr := containertest.GenerateContainer(false) 35 | attrs := cnr.GetAttributes() 36 | attrLen := len(attrs) 37 | 38 | cnr.SetHomomorphicHashingState(true) 39 | 40 | // enabling hashing should not add any new attributes 41 | require.Equal(t, attrLen, len(cnr.GetAttributes())) 42 | require.True(t, cnr.HomomorphicHashingState()) 43 | 44 | cnr.SetHomomorphicHashingState(false) 45 | 46 | // disabling hashing should add exactly one attribute 47 | require.Equal(t, attrLen+1, len(cnr.GetAttributes())) 48 | require.False(t, cnr.HomomorphicHashingState()) 49 | 50 | cnr.SetHomomorphicHashingState(true) 51 | 52 | // enabling hashing should remove 1 attribute if 53 | // hashing was disabled before 54 | require.Equal(t, attrLen, len(cnr.GetAttributes())) 55 | require.True(t, cnr.HomomorphicHashingState()) 56 | 57 | // hashing operations should not change any other attributes 58 | require.ElementsMatch(t, attrs, cnr.GetAttributes()) 59 | } 60 | -------------------------------------------------------------------------------- /container/grpc/types.go: -------------------------------------------------------------------------------- 1 | package container 2 | 3 | import ( 4 | netmap "github.com/TrueCloudLab/frostfs-api-go/v2/netmap/grpc" 5 | refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs/grpc" 6 | ) 7 | 8 | // SetKey sets key to the container attribute. 9 | func (m *Container_Attribute) SetKey(v string) { 10 | m.Key = v 11 | } 12 | 13 | // SetValue sets value of the container attribute. 14 | func (m *Container_Attribute) SetValue(v string) { 15 | m.Value = v 16 | } 17 | 18 | // SetOwnerId sets identifier of the container owner, 19 | func (m *Container) SetOwnerId(v *refs.OwnerID) { 20 | m.OwnerId = v 21 | } 22 | 23 | // SetNonce sets nonce of the container structure. 24 | func (m *Container) SetNonce(v []byte) { 25 | m.Nonce = v 26 | } 27 | 28 | // SetBasicAcl sets basic ACL of the container. 29 | func (m *Container) SetBasicAcl(v uint32) { 30 | m.BasicAcl = v 31 | } 32 | 33 | // SetAttributes sets list of the container attributes. 34 | func (m *Container) SetAttributes(v []*Container_Attribute) { 35 | m.Attributes = v 36 | } 37 | 38 | // SetPlacementPolicy sets placement policy of the container. 39 | func (m *Container) SetPlacementPolicy(v *netmap.PlacementPolicy) { 40 | m.PlacementPolicy = v 41 | } 42 | 43 | // SetVersion sets version of the container. 44 | func (m *Container) SetVersion(v *refs.Version) { 45 | m.Version = v 46 | } 47 | -------------------------------------------------------------------------------- /container/json.go: -------------------------------------------------------------------------------- 1 | package container 2 | 3 | import ( 4 | container "github.com/TrueCloudLab/frostfs-api-go/v2/container/grpc" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 6 | ) 7 | 8 | func (a *Attribute) MarshalJSON() ([]byte, error) { 9 | return message.MarshalJSON(a) 10 | } 11 | 12 | func (a *Attribute) UnmarshalJSON(data []byte) error { 13 | return message.UnmarshalJSON(a, data, new(container.Container_Attribute)) 14 | } 15 | 16 | func (c *Container) MarshalJSON() ([]byte, error) { 17 | return message.MarshalJSON(c) 18 | } 19 | 20 | func (c *Container) UnmarshalJSON(data []byte) error { 21 | return message.UnmarshalJSON(c, data, new(container.Container)) 22 | } 23 | -------------------------------------------------------------------------------- /container/message_test.go: -------------------------------------------------------------------------------- 1 | package container_test 2 | 3 | import ( 4 | "testing" 5 | 6 | containertest "github.com/TrueCloudLab/frostfs-api-go/v2/container/test" 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 8 | messagetest "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message/test" 9 | ) 10 | 11 | func TestMessageConvert(t *testing.T) { 12 | messagetest.TestRPCMessage(t, 13 | func(empty bool) message.Message { return containertest.GenerateAttribute(empty) }, 14 | func(empty bool) message.Message { return containertest.GenerateContainer(empty) }, 15 | func(empty bool) message.Message { return containertest.GeneratePutRequestBody(empty) }, 16 | func(empty bool) message.Message { return containertest.GeneratePutRequest(empty) }, 17 | func(empty bool) message.Message { return containertest.GeneratePutResponseBody(empty) }, 18 | func(empty bool) message.Message { return containertest.GeneratePutResponse(empty) }, 19 | func(empty bool) message.Message { return containertest.GenerateGetRequestBody(empty) }, 20 | func(empty bool) message.Message { return containertest.GenerateGetRequest(empty) }, 21 | func(empty bool) message.Message { return containertest.GenerateGetResponseBody(empty) }, 22 | func(empty bool) message.Message { return containertest.GenerateGetResponse(empty) }, 23 | func(empty bool) message.Message { return containertest.GenerateDeleteRequestBody(empty) }, 24 | func(empty bool) message.Message { return containertest.GenerateDeleteRequest(empty) }, 25 | func(empty bool) message.Message { return containertest.GenerateDeleteResponseBody(empty) }, 26 | func(empty bool) message.Message { return containertest.GenerateDeleteResponse(empty) }, 27 | func(empty bool) message.Message { return containertest.GenerateListRequestBody(empty) }, 28 | func(empty bool) message.Message { return containertest.GenerateListRequest(empty) }, 29 | func(empty bool) message.Message { return containertest.GenerateListResponseBody(empty) }, 30 | func(empty bool) message.Message { return containertest.GenerateListResponse(empty) }, 31 | func(empty bool) message.Message { return containertest.GenerateSetExtendedACLRequestBody(empty) }, 32 | func(empty bool) message.Message { return containertest.GenerateSetExtendedACLRequest(empty) }, 33 | func(empty bool) message.Message { return containertest.GenerateGetRequestBody(empty) }, 34 | func(empty bool) message.Message { return containertest.GenerateGetRequest(empty) }, 35 | func(empty bool) message.Message { return containertest.GenerateGetResponseBody(empty) }, 36 | func(empty bool) message.Message { return containertest.GenerateGetResponse(empty) }, 37 | func(empty bool) message.Message { return containertest.GenerateGetExtendedACLRequestBody(empty) }, 38 | func(empty bool) message.Message { return containertest.GenerateGetExtendedACLRequest(empty) }, 39 | func(empty bool) message.Message { return containertest.GenerateGetExtendedACLResponseBody(empty) }, 40 | func(empty bool) message.Message { return containertest.GenerateGetExtendedACLResponse(empty) }, 41 | func(empty bool) message.Message { return containertest.GenerateUsedSpaceAnnouncement(empty) }, 42 | func(empty bool) message.Message { return containertest.GenerateAnnounceUsedSpaceRequestBody(empty) }, 43 | func(empty bool) message.Message { return containertest.GenerateAnnounceUsedSpaceRequest(empty) }, 44 | func(empty bool) message.Message { return containertest.GenerateAnnounceUsedSpaceResponseBody(empty) }, 45 | func(empty bool) message.Message { return containertest.GenerateAnnounceUsedSpaceResponse(empty) }, 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /container/status.go: -------------------------------------------------------------------------------- 1 | package container 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/status" 5 | statusgrpc "github.com/TrueCloudLab/frostfs-api-go/v2/status/grpc" 6 | ) 7 | 8 | // LocalizeFailStatus checks if passed global status.Code is related to container failure and: 9 | // 10 | // then localizes the code and returns true, 11 | // else leaves the code unchanged and returns false. 12 | // 13 | // Arg must not be nil. 14 | func LocalizeFailStatus(c *status.Code) bool { 15 | return status.LocalizeIfInSection(c, uint32(statusgrpc.Section_SECTION_CONTAINER)) 16 | } 17 | 18 | // GlobalizeFail globalizes local code of container failure. 19 | // 20 | // Arg must not be nil. 21 | func GlobalizeFail(c *status.Code) { 22 | c.GlobalizeSection(uint32(statusgrpc.Section_SECTION_CONTAINER)) 23 | } 24 | 25 | const ( 26 | // StatusNotFound is a local status.Code value for 27 | // CONTAINER_NOT_FOUND container failure. 28 | StatusNotFound status.Code = iota 29 | 30 | // StatusEACLNotFound is a local status.Code value for 31 | // EACL_NOT_FOUND failure. 32 | StatusEACLNotFound 33 | ) 34 | -------------------------------------------------------------------------------- /container/status_test.go: -------------------------------------------------------------------------------- 1 | package container_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/container" 7 | statustest "github.com/TrueCloudLab/frostfs-api-go/v2/status/test" 8 | ) 9 | 10 | func TestStatusCodes(t *testing.T) { 11 | statustest.TestCodes(t, container.LocalizeFailStatus, container.GlobalizeFail, 12 | container.StatusNotFound, 3072, 13 | container.StatusEACLNotFound, 3073, 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /docs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrueCloudLab/frostfs-api-go/196241c4e79a823249282a3ee42b4b87a4ab7aa9/docs/.gitkeep -------------------------------------------------------------------------------- /docs/release-instruction.md: -------------------------------------------------------------------------------- 1 | # Release instructions 2 | 3 | ## Pre-release checks 4 | 5 | These should run successfully: 6 | * `go test ./...`; 7 | * `golangci-lint run ./...`; 8 | * `go fmt ./...` (should not change any files); 9 | * `go mog tidy` (should not change any files); 10 | * `./prepare.sh /path/to/frostfs-api/on/your/machine` (should not change any files). 11 | 12 | ## Writing changelog 13 | 14 | Add an entry to the `CHANGELOG.md` following the style established there. Add an 15 | optional codename(for not patch releases), version and release date in the heading. 16 | Write a paragraph describing the most significant changes done in this release. Add 17 | `Fixed`, `Added`, `Removed` and `Updated` sections with fixed bug, new features and 18 | other changes. 19 | 20 | Open Pull Request (must receive at least one approval) and merge this changes. 21 | 22 | ## Update README 23 | 24 | Actualize compatibility table in `README.md` with relevant information. 25 | 26 | ## Tag a release 27 | 28 | Use `vX.Y.Z` tag for releases and `vX.Y.Z-rc.N` for release candidates 29 | following the [semantic versioning](https://semver.org/) standard. 30 | 31 | Update your local `master` branch after approved and merged `CHANGELOG.md` changes. 32 | Tag a release (must be signed) and push it: 33 | 34 | ``` 35 | $ git tag -s vX.Y.Z[-rc.N] && git push origin vX.Y.Z[-rc.N] 36 | ``` 37 | 38 | ## Make a Github release 39 | 40 | Using Github's web interface create a new release based on just created tag 41 | with the same changes from changelog and publish it. 42 | 43 | ## Close github milestone 44 | 45 | Close corresponding vX.Y.Z github milestone. 46 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/TrueCloudLab/frostfs-api-go/v2 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/TrueCloudLab/frostfs-crypto v0.5.0 7 | github.com/stretchr/testify v1.7.0 8 | google.golang.org/grpc v1.48.0 9 | google.golang.org/protobuf v1.28.0 10 | ) 11 | 12 | require ( 13 | github.com/TrueCloudLab/rfc6979 v0.3.0 // indirect 14 | github.com/davecgh/go-spew v1.1.0 // indirect 15 | github.com/golang/protobuf v1.5.2 // indirect 16 | github.com/mr-tron/base58 v1.2.0 // indirect 17 | github.com/pmezard/go-difflib v1.0.0 // indirect 18 | golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect 19 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 // indirect 20 | golang.org/x/text v0.3.3 // indirect 21 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect 22 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /internal/random/rand.go: -------------------------------------------------------------------------------- 1 | package random 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | func init() { 9 | rand.Seed(time.Now().UnixNano()) 10 | } 11 | 12 | // Uint32 returns random uint32 value [0, max). 13 | func Uint32(max uint32) uint32 { 14 | return rand.Uint32() % max 15 | } 16 | -------------------------------------------------------------------------------- /lock/grpc/types.go: -------------------------------------------------------------------------------- 1 | package lock 2 | 3 | import refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs/grpc" 4 | 5 | // SetMembers sets `members` field. 6 | func (x *Lock) SetMembers(ids []*refs.ObjectID) { 7 | x.Members = ids 8 | } 9 | -------------------------------------------------------------------------------- /netmap/grpc/service.go: -------------------------------------------------------------------------------- 1 | package netmap 2 | 3 | import ( 4 | refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs/grpc" 5 | session "github.com/TrueCloudLab/frostfs-api-go/v2/session/grpc" 6 | ) 7 | 8 | // SetBody sets body of the request. 9 | func (m *LocalNodeInfoRequest) SetBody(v *LocalNodeInfoRequest_Body) { 10 | m.Body = v 11 | } 12 | 13 | // SetMetaHeader sets meta header of the request. 14 | func (m *LocalNodeInfoRequest) SetMetaHeader(v *session.RequestMetaHeader) { 15 | m.MetaHeader = v 16 | } 17 | 18 | // SetVerifyHeader sets verification header of the request. 19 | func (m *LocalNodeInfoRequest) SetVerifyHeader(v *session.RequestVerificationHeader) { 20 | m.VerifyHeader = v 21 | } 22 | 23 | // SetVersion sets version of response body. 24 | func (m *LocalNodeInfoResponse_Body) SetVersion(v *refs.Version) { 25 | m.Version = v 26 | } 27 | 28 | // SetNodeInfo sets node info of response body. 29 | func (m *LocalNodeInfoResponse_Body) SetNodeInfo(v *NodeInfo) { 30 | m.NodeInfo = v 31 | } 32 | 33 | // SetBody sets body of the response. 34 | func (m *LocalNodeInfoResponse) SetBody(v *LocalNodeInfoResponse_Body) { 35 | m.Body = v 36 | } 37 | 38 | // SetMetaHeader sets meta header of the response. 39 | func (m *LocalNodeInfoResponse) SetMetaHeader(v *session.ResponseMetaHeader) { 40 | m.MetaHeader = v 41 | } 42 | 43 | // SetVerifyHeader sets verification header of the response. 44 | func (m *LocalNodeInfoResponse) SetVerifyHeader(v *session.ResponseVerificationHeader) { 45 | m.VerifyHeader = v 46 | } 47 | 48 | // SetBody sets body of the request. 49 | func (x *NetworkInfoRequest) SetBody(v *NetworkInfoRequest_Body) { 50 | x.Body = v 51 | } 52 | 53 | // SetMetaHeader sets meta header of the request. 54 | func (x *NetworkInfoRequest) SetMetaHeader(v *session.RequestMetaHeader) { 55 | x.MetaHeader = v 56 | } 57 | 58 | // SetVerifyHeader sets verification header of the request. 59 | func (x *NetworkInfoRequest) SetVerifyHeader(v *session.RequestVerificationHeader) { 60 | x.VerifyHeader = v 61 | } 62 | 63 | // SetNetworkInfo sets information about the network. 64 | func (x *NetworkInfoResponse_Body) SetNetworkInfo(v *NetworkInfo) { 65 | x.NetworkInfo = v 66 | } 67 | 68 | // SetBody sets body of the response. 69 | func (x *NetworkInfoResponse) SetBody(v *NetworkInfoResponse_Body) { 70 | x.Body = v 71 | } 72 | 73 | // SetMetaHeader sets meta header of the response. 74 | func (x *NetworkInfoResponse) SetMetaHeader(v *session.ResponseMetaHeader) { 75 | x.MetaHeader = v 76 | } 77 | 78 | // SetVerifyHeader sets verification header of the response. 79 | func (x *NetworkInfoResponse) SetVerifyHeader(v *session.ResponseVerificationHeader) { 80 | x.VerifyHeader = v 81 | } 82 | 83 | // SetBody sets body of the request. 84 | func (x *NetmapSnapshotRequest) SetBody(v *NetmapSnapshotRequest_Body) { 85 | x.Body = v 86 | } 87 | 88 | // SetMetaHeader sets meta header of the request. 89 | func (x *NetmapSnapshotRequest) SetMetaHeader(v *session.RequestMetaHeader) { 90 | x.MetaHeader = v 91 | } 92 | 93 | // SetVerifyHeader sets verification header of the request. 94 | func (x *NetmapSnapshotRequest) SetVerifyHeader(v *session.RequestVerificationHeader) { 95 | x.VerifyHeader = v 96 | } 97 | 98 | // SetNetmap sets current Netmap. 99 | func (x *NetmapSnapshotResponse_Body) SetNetmap(v *Netmap) { 100 | x.Netmap = v 101 | } 102 | 103 | // SetBody sets body of the response. 104 | func (x *NetmapSnapshotResponse) SetBody(v *NetmapSnapshotResponse_Body) { 105 | x.Body = v 106 | } 107 | 108 | // SetMetaHeader sets meta header of the response. 109 | func (x *NetmapSnapshotResponse) SetMetaHeader(v *session.ResponseMetaHeader) { 110 | x.MetaHeader = v 111 | } 112 | 113 | // SetVerifyHeader sets verification header of the response. 114 | func (x *NetmapSnapshotResponse) SetVerifyHeader(v *session.ResponseVerificationHeader) { 115 | x.VerifyHeader = v 116 | } 117 | -------------------------------------------------------------------------------- /netmap/json.go: -------------------------------------------------------------------------------- 1 | package netmap 2 | 3 | import ( 4 | netmap "github.com/TrueCloudLab/frostfs-api-go/v2/netmap/grpc" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 6 | ) 7 | 8 | func (p *PlacementPolicy) MarshalJSON() ([]byte, error) { 9 | return message.MarshalJSON(p) 10 | } 11 | 12 | func (p *PlacementPolicy) UnmarshalJSON(data []byte) error { 13 | return message.UnmarshalJSON(p, data, new(netmap.PlacementPolicy)) 14 | } 15 | 16 | func (f *Filter) MarshalJSON() ([]byte, error) { 17 | return message.MarshalJSON(f) 18 | } 19 | 20 | func (f *Filter) UnmarshalJSON(data []byte) error { 21 | return message.UnmarshalJSON(f, data, new(netmap.Filter)) 22 | } 23 | 24 | func (s *Selector) MarshalJSON() ([]byte, error) { 25 | return message.MarshalJSON(s) 26 | } 27 | 28 | func (s *Selector) UnmarshalJSON(data []byte) error { 29 | return message.UnmarshalJSON(s, data, new(netmap.Selector)) 30 | } 31 | 32 | func (r *Replica) MarshalJSON() ([]byte, error) { 33 | return message.MarshalJSON(r) 34 | } 35 | 36 | func (r *Replica) UnmarshalJSON(data []byte) error { 37 | return message.UnmarshalJSON(r, data, new(netmap.Replica)) 38 | } 39 | 40 | func (a *Attribute) MarshalJSON() ([]byte, error) { 41 | return message.MarshalJSON(a) 42 | } 43 | 44 | func (a *Attribute) UnmarshalJSON(data []byte) error { 45 | return message.UnmarshalJSON(a, data, new(netmap.NodeInfo_Attribute)) 46 | } 47 | 48 | func (ni *NodeInfo) MarshalJSON() ([]byte, error) { 49 | return message.MarshalJSON(ni) 50 | } 51 | 52 | func (ni *NodeInfo) UnmarshalJSON(data []byte) error { 53 | return message.UnmarshalJSON(ni, data, new(netmap.NodeInfo)) 54 | } 55 | 56 | func (i *NetworkInfo) MarshalJSON() ([]byte, error) { 57 | return message.MarshalJSON(i) 58 | } 59 | 60 | func (i *NetworkInfo) UnmarshalJSON(data []byte) error { 61 | return message.UnmarshalJSON(i, data, new(netmap.NetworkInfo)) 62 | } 63 | -------------------------------------------------------------------------------- /netmap/message_test.go: -------------------------------------------------------------------------------- 1 | package netmap_test 2 | 3 | import ( 4 | "testing" 5 | 6 | netmaptest "github.com/TrueCloudLab/frostfs-api-go/v2/netmap/test" 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 8 | messagetest "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message/test" 9 | ) 10 | 11 | func TestMessageConvert(t *testing.T) { 12 | messagetest.TestRPCMessage(t, 13 | func(empty bool) message.Message { return netmaptest.GenerateFilter(empty) }, 14 | func(empty bool) message.Message { return netmaptest.GenerateSelector(empty) }, 15 | func(empty bool) message.Message { return netmaptest.GenerateReplica(empty) }, 16 | func(empty bool) message.Message { return netmaptest.GeneratePlacementPolicy(empty) }, 17 | func(empty bool) message.Message { return netmaptest.GenerateAttribute(empty) }, 18 | func(empty bool) message.Message { return netmaptest.GenerateNodeInfo(empty) }, 19 | func(empty bool) message.Message { return netmaptest.GenerateLocalNodeInfoRequest(empty) }, 20 | func(empty bool) message.Message { return netmaptest.GenerateLocalNodeInfoResponseBody(empty) }, 21 | func(empty bool) message.Message { return netmaptest.GenerateNetworkParameter(empty) }, 22 | func(empty bool) message.Message { return netmaptest.GenerateNetworkConfig(empty) }, 23 | func(empty bool) message.Message { return netmaptest.GenerateNetworkInfo(empty) }, 24 | func(empty bool) message.Message { return netmaptest.GenerateNetworkInfoRequest(empty) }, 25 | func(empty bool) message.Message { return netmaptest.GenerateNetworkInfoResponseBody(empty) }, 26 | func(empty bool) message.Message { return netmaptest.GenerateNetMap(empty) }, 27 | func(empty bool) message.Message { return netmaptest.GenerateSnapshotRequestBody(empty) }, 28 | func(empty bool) message.Message { return netmaptest.GenerateSnapshotRequest(empty) }, 29 | func(empty bool) message.Message { return netmaptest.GenerateSnapshotResponseBody(empty) }, 30 | func(empty bool) message.Message { return netmaptest.GenerateSnapshotResponse(empty) }, 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /netmap/string.go: -------------------------------------------------------------------------------- 1 | package netmap 2 | 3 | import ( 4 | netmap "github.com/TrueCloudLab/frostfs-api-go/v2/netmap/grpc" 5 | ) 6 | 7 | // String returns string representation of Clause. 8 | func (x Clause) String() string { 9 | return ClauseToGRPCMessage(x).String() 10 | } 11 | 12 | // FromString parses Clause from a string representation. 13 | // It is a reverse action to String(). 14 | // 15 | // Returns true if s was parsed successfully. 16 | func (x *Clause) FromString(s string) bool { 17 | var g netmap.Clause 18 | 19 | ok := g.FromString(s) 20 | 21 | if ok { 22 | *x = ClauseFromGRPCMessage(g) 23 | } 24 | 25 | return ok 26 | } 27 | 28 | // String returns string representation of Operation. 29 | func (x Operation) String() string { 30 | return OperationToGRPCMessage(x).String() 31 | } 32 | 33 | // FromString parses Operation from a string representation. 34 | // It is a reverse action to String(). 35 | // 36 | // Returns true if s was parsed successfully. 37 | func (x *Operation) FromString(s string) bool { 38 | var g netmap.Operation 39 | 40 | ok := g.FromString(s) 41 | 42 | if ok { 43 | *x = OperationFromGRPCMessage(g) 44 | } 45 | 46 | return ok 47 | } 48 | 49 | // String returns string representation of NodeState. 50 | func (x NodeState) String() string { 51 | return NodeStateToGRPCMessage(x).String() 52 | } 53 | 54 | // FromString parses NodeState from a string representation. 55 | // It is a reverse action to String(). 56 | // 57 | // Returns true if s was parsed successfully. 58 | func (x *NodeState) FromString(s string) bool { 59 | var g netmap.NodeInfo_State 60 | 61 | ok := g.FromString(s) 62 | 63 | if ok { 64 | *x = NodeStateFromRPCMessage(g) 65 | } 66 | 67 | return ok 68 | } 69 | -------------------------------------------------------------------------------- /object/attributes.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // SysAttributePrefix is a prefix of key to system attribute. 10 | const SysAttributePrefix = "__NEOFS__" 11 | 12 | const ( 13 | // SysAttributeUploadID marks smaller parts of a split bigger object. 14 | SysAttributeUploadID = SysAttributePrefix + "UPLOAD_ID" 15 | 16 | // SysAttributeExpEpoch tells GC to delete object after that epoch. 17 | SysAttributeExpEpoch = SysAttributePrefix + "EXPIRATION_EPOCH" 18 | 19 | // SysAttributeTickEpoch defines what epoch must produce object 20 | // notification. 21 | SysAttributeTickEpoch = SysAttributePrefix + "TICK_EPOCH" 22 | 23 | // SysAttributeTickTopic defines what topic object notification 24 | // must be sent to. 25 | SysAttributeTickTopic = SysAttributePrefix + "TICK_TOPIC" 26 | ) 27 | 28 | // NotificationInfo groups information about object notification 29 | // that can be written to object. 30 | // 31 | // Topic is an optional field. 32 | type NotificationInfo struct { 33 | epoch uint64 34 | topic string 35 | } 36 | 37 | // Epoch returns object notification tick 38 | // epoch. 39 | func (n NotificationInfo) Epoch() uint64 { 40 | return n.epoch 41 | } 42 | 43 | // SetEpoch sets object notification tick 44 | // epoch. 45 | func (n *NotificationInfo) SetEpoch(epoch uint64) { 46 | n.epoch = epoch 47 | } 48 | 49 | // Topic return optional object notification 50 | // topic. 51 | func (n NotificationInfo) Topic() string { 52 | return n.topic 53 | } 54 | 55 | // SetTopic sets optional object notification 56 | // topic. 57 | func (n *NotificationInfo) SetTopic(topic string) { 58 | n.topic = topic 59 | } 60 | 61 | // WriteNotificationInfo writes NotificationInfo to the Object via attributes. Object must not be nil. 62 | // 63 | // Existing notification attributes are expected to be key-unique, otherwise undefined behavior. 64 | func WriteNotificationInfo(o *Object, ni NotificationInfo) { 65 | h := o.GetHeader() 66 | if h == nil { 67 | h = new(Header) 68 | o.SetHeader(h) 69 | } 70 | 71 | var ( 72 | attrs = h.GetAttributes() 73 | 74 | epoch = strconv.FormatUint(ni.Epoch(), 10) 75 | topic = ni.Topic() 76 | 77 | changedEpoch bool 78 | changedTopic bool 79 | deleteIndex = -1 80 | ) 81 | 82 | for i := range attrs { 83 | switch attrs[i].GetKey() { 84 | case SysAttributeTickEpoch: 85 | attrs[i].SetValue(epoch) 86 | changedEpoch = true 87 | case SysAttributeTickTopic: 88 | changedTopic = true 89 | 90 | if topic == "" { 91 | deleteIndex = i 92 | break 93 | } 94 | 95 | attrs[i].SetValue(topic) 96 | } 97 | 98 | if changedEpoch && changedTopic { 99 | break 100 | } 101 | } 102 | 103 | if deleteIndex != -1 { 104 | // approach without allocation/waste 105 | // coping works since the attributes 106 | // order is not important 107 | attrs[deleteIndex] = attrs[len(attrs)-1] 108 | attrs = attrs[:len(attrs)-1] 109 | } 110 | 111 | if !changedEpoch { 112 | index := len(attrs) 113 | attrs = append(attrs, Attribute{}) 114 | attrs[index].SetKey(SysAttributeTickEpoch) 115 | attrs[index].SetValue(epoch) 116 | } 117 | 118 | if !changedTopic && topic != "" { 119 | index := len(attrs) 120 | attrs = append(attrs, Attribute{}) 121 | attrs[index].SetKey(SysAttributeTickTopic) 122 | attrs[index].SetValue(topic) 123 | } 124 | 125 | h.SetAttributes(attrs) 126 | } 127 | 128 | // ErrNotificationNotSet means that object does not have notification. 129 | var ErrNotificationNotSet = errors.New("notification for object is not set") 130 | 131 | // GetNotificationInfo looks for object notification attributes. Object must not be nil. 132 | // Returns ErrNotificationNotSet if no corresponding attributes 133 | // were found. 134 | // 135 | // Existing notification attributes are expected to be key-unique, otherwise undefined behavior. 136 | func GetNotificationInfo(o *Object) (*NotificationInfo, error) { 137 | var ( 138 | foundEpoch bool 139 | ni = new(NotificationInfo) 140 | ) 141 | 142 | for _, attr := range o.GetHeader().GetAttributes() { 143 | switch key := attr.GetKey(); key { 144 | case SysAttributeTickEpoch: 145 | epoch, err := strconv.ParseUint(attr.GetValue(), 10, 64) 146 | if err != nil { 147 | return nil, fmt.Errorf("could not parse epoch: %w", err) 148 | } 149 | 150 | ni.SetEpoch(epoch) 151 | 152 | foundEpoch = true 153 | case SysAttributeTickTopic: 154 | ni.SetTopic(attr.GetValue()) 155 | } 156 | } 157 | 158 | if !foundEpoch { 159 | return nil, ErrNotificationNotSet 160 | } 161 | 162 | return ni, nil 163 | } 164 | -------------------------------------------------------------------------------- /object/attributes_test.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestSetNotification(t *testing.T) { 11 | o := new(Object) 12 | 13 | var ni = NotificationInfo{ 14 | epoch: 10, 15 | topic: "test", 16 | } 17 | 18 | WriteNotificationInfo(o, ni) 19 | 20 | var foundEpoch, foundTopic bool 21 | 22 | for _, attr := range o.GetHeader().GetAttributes() { 23 | switch key := attr.GetKey(); key { 24 | case SysAttributeTickEpoch: 25 | require.Equal(t, false, foundEpoch) 26 | 27 | uEpoch, err := strconv.ParseUint(attr.GetValue(), 10, 64) 28 | require.NoError(t, err) 29 | 30 | require.Equal(t, ni.Epoch(), uEpoch) 31 | foundEpoch = true 32 | case SysAttributeTickTopic: 33 | require.Equal(t, false, foundTopic) 34 | require.Equal(t, ni.Topic(), attr.GetValue()) 35 | foundTopic = true 36 | } 37 | } 38 | 39 | require.Equal(t, true, foundEpoch && foundTopic) 40 | } 41 | 42 | func TestGetNotification(t *testing.T) { 43 | o := new(Object) 44 | 45 | attr := []Attribute{ 46 | {SysAttributeTickEpoch, "10"}, 47 | {SysAttributeTickTopic, "test"}, 48 | } 49 | 50 | h := new(Header) 51 | h.SetAttributes(attr) 52 | 53 | o.SetHeader(h) 54 | 55 | t.Run("No error", func(t *testing.T) { 56 | ni, err := GetNotificationInfo(o) 57 | require.NoError(t, err) 58 | 59 | require.Equal(t, uint64(10), ni.Epoch()) 60 | require.Equal(t, "test", ni.Topic()) 61 | }) 62 | } 63 | 64 | func TestIntegration(t *testing.T) { 65 | o := new(Object) 66 | 67 | var ( 68 | ni1 = NotificationInfo{ 69 | epoch: 10, 70 | topic: "", 71 | } 72 | ni2 = NotificationInfo{ 73 | epoch: 11, 74 | topic: "test", 75 | } 76 | ) 77 | 78 | WriteNotificationInfo(o, ni1) 79 | WriteNotificationInfo(o, ni2) 80 | 81 | t.Run("double set", func(t *testing.T) { 82 | ni, err := GetNotificationInfo(o) 83 | require.NoError(t, err) 84 | 85 | require.Equal(t, ni2.epoch, ni.Epoch()) 86 | require.Equal(t, ni2.topic, ni.Topic()) 87 | require.Equal(t, 2, len(o.GetHeader().GetAttributes())) 88 | }) 89 | } 90 | -------------------------------------------------------------------------------- /object/bench_test.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func randString(n int) string { 11 | x := make([]byte, n) 12 | for i := range x { 13 | x[i] = byte('a' + rand.Intn('z'-'a')) 14 | } 15 | return string(x) 16 | } 17 | 18 | func BenchmarkAttributesMarshal(b *testing.B) { 19 | attrs := make([]Attribute, 50) 20 | for i := range attrs { 21 | attrs[i].key = SysAttributePrefix + randString(10) 22 | attrs[i].val = randString(10) 23 | } 24 | raw := AttributesToGRPC(attrs) 25 | require.Equal(b, len(raw), len(attrs)) 26 | 27 | b.Run("marshal", func(b *testing.B) { 28 | b.ReportAllocs() 29 | for i := 0; i < b.N; i++ { 30 | res := AttributesToGRPC(attrs) 31 | if len(res) != len(raw) { 32 | b.FailNow() 33 | } 34 | } 35 | }) 36 | b.Run("unmarshal", func(b *testing.B) { 37 | b.ReportAllocs() 38 | for i := 0; i < b.N; i++ { 39 | res, err := AttributesFromGRPC(raw) 40 | if err != nil || len(res) != len(raw) { 41 | b.FailNow() 42 | } 43 | } 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /object/filters.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | // ReservedFilterPrefix is a prefix of key to object header value or property. 4 | const ReservedFilterPrefix = "$Object:" 5 | 6 | const ( 7 | // FilterHeaderVersion is a filter key to "version" field of the object header. 8 | FilterHeaderVersion = ReservedFilterPrefix + "version" 9 | 10 | // FilterHeaderObjectID is a filter key to "object_id" field of the object. 11 | FilterHeaderObjectID = ReservedFilterPrefix + "objectID" 12 | 13 | // FilterHeaderContainerID is a filter key to "container_id" field of the object header. 14 | FilterHeaderContainerID = ReservedFilterPrefix + "containerID" 15 | 16 | // FilterHeaderOwnerID is a filter key to "owner_id" field of the object header. 17 | FilterHeaderOwnerID = ReservedFilterPrefix + "ownerID" 18 | 19 | // FilterHeaderCreationEpoch is a filter key to "creation_epoch" field of the object header. 20 | FilterHeaderCreationEpoch = ReservedFilterPrefix + "creationEpoch" 21 | 22 | // FilterHeaderPayloadLength is a filter key to "payload_length" field of the object header. 23 | FilterHeaderPayloadLength = ReservedFilterPrefix + "payloadLength" 24 | 25 | // FilterHeaderPayloadHash is a filter key to "payload_hash" field of the object header. 26 | FilterHeaderPayloadHash = ReservedFilterPrefix + "payloadHash" 27 | 28 | // FilterHeaderObjectType is a filter key to "object_type" field of the object header. 29 | FilterHeaderObjectType = ReservedFilterPrefix + "objectType" 30 | 31 | // FilterHeaderHomomorphicHash is a filter key to "homomorphic_hash" field of the object header. 32 | FilterHeaderHomomorphicHash = ReservedFilterPrefix + "homomorphicHash" 33 | 34 | // FilterHeaderParent is a filter key to "split.parent" field of the object header. 35 | FilterHeaderParent = ReservedFilterPrefix + "split.parent" 36 | 37 | // FilterHeaderSplitID is a filter key to "split.splitID" field of the object header. 38 | FilterHeaderSplitID = ReservedFilterPrefix + "split.splitID" 39 | ) 40 | 41 | const ( 42 | // FilterPropertyRoot is a filter key to check if regular object is on top of split hierarchy. 43 | FilterPropertyRoot = ReservedFilterPrefix + "ROOT" 44 | 45 | // FilterPropertyPhy is a filter key to check if an object physically stored on a node. 46 | FilterPropertyPhy = ReservedFilterPrefix + "PHY" 47 | ) 48 | 49 | const ( 50 | // BooleanPropertyValueTrue is a true value for boolean property filters. 51 | BooleanPropertyValueTrue = "true" 52 | 53 | // BooleanPropertyValueFalse is a false value for boolean property filters. 54 | BooleanPropertyValueFalse = "" 55 | ) 56 | -------------------------------------------------------------------------------- /object/grpc/client.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "google.golang.org/grpc" 8 | ) 9 | 10 | // Client wraps ObjectServiceClient 11 | // with pre-defined configurations. 12 | type Client struct { 13 | *cfg 14 | 15 | client ObjectServiceClient 16 | } 17 | 18 | // Option represents Client option. 19 | type Option func(*cfg) 20 | 21 | type cfg struct { 22 | callOpts []grpc.CallOption 23 | } 24 | 25 | // ErrNilObjectServiceClient is returned by functions that expect 26 | // a non-nil ObjectServiceClient, but received nil. 27 | var ErrNilObjectServiceClient = errors.New("object gRPC client is nil") 28 | 29 | func defaultCfg() *cfg { 30 | return new(cfg) 31 | } 32 | 33 | // NewClient creates, initializes and returns a new Client instance. 34 | // 35 | // Options are applied one by one in order. 36 | func NewClient(c ObjectServiceClient, opts ...Option) (*Client, error) { 37 | if c == nil { 38 | return nil, ErrNilObjectServiceClient 39 | } 40 | 41 | cfg := defaultCfg() 42 | for i := range opts { 43 | opts[i](cfg) 44 | } 45 | 46 | return &Client{ 47 | cfg: cfg, 48 | client: c, 49 | }, nil 50 | } 51 | 52 | func (c *Client) Get(ctx context.Context, req *GetRequest) (ObjectService_GetClient, error) { 53 | return c.client.Get(ctx, req, c.callOpts...) 54 | } 55 | 56 | func (c *Client) Put(ctx context.Context) (ObjectService_PutClient, error) { 57 | return c.client.Put(ctx, c.callOpts...) 58 | } 59 | 60 | func (c *Client) Head(ctx context.Context, req *HeadRequest) (*HeadResponse, error) { 61 | return c.client.Head(ctx, req, c.callOpts...) 62 | } 63 | 64 | func (c *Client) Search(ctx context.Context, req *SearchRequest) (ObjectService_SearchClient, error) { 65 | return c.client.Search(ctx, req, c.callOpts...) 66 | } 67 | 68 | func (c *Client) Delete(ctx context.Context, req *DeleteRequest) (*DeleteResponse, error) { 69 | return c.client.Delete(ctx, req, c.callOpts...) 70 | } 71 | 72 | func (c *Client) GetRange(ctx context.Context, req *GetRangeRequest) (ObjectService_GetRangeClient, error) { 73 | return c.client.GetRange(ctx, req, c.callOpts...) 74 | } 75 | 76 | func (c *Client) GetRangeHash(ctx context.Context, req *GetRangeHashRequest) (*GetRangeHashResponse, error) { 77 | return c.client.GetRangeHash(ctx, req, c.callOpts...) 78 | } 79 | 80 | // WithCallOptions returns Option that configures 81 | // Client to attach call options to each rpc call. 82 | func WithCallOptions(opts []grpc.CallOption) Option { 83 | return func(c *cfg) { 84 | c.callOpts = opts 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /object/json.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | object "github.com/TrueCloudLab/frostfs-api-go/v2/object/grpc" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 6 | ) 7 | 8 | func (h *ShortHeader) MarshalJSON() ([]byte, error) { 9 | return message.MarshalJSON(h) 10 | } 11 | 12 | func (h *ShortHeader) UnmarshalJSON(data []byte) error { 13 | return message.UnmarshalJSON(h, data, new(object.ShortHeader)) 14 | } 15 | 16 | func (a *Attribute) MarshalJSON() ([]byte, error) { 17 | return message.MarshalJSON(a) 18 | } 19 | 20 | func (a *Attribute) UnmarshalJSON(data []byte) error { 21 | return message.UnmarshalJSON(a, data, new(object.Header_Attribute)) 22 | } 23 | 24 | func (h *SplitHeader) MarshalJSON() ([]byte, error) { 25 | return message.MarshalJSON(h) 26 | } 27 | 28 | func (h *SplitHeader) UnmarshalJSON(data []byte) error { 29 | return message.UnmarshalJSON(h, data, new(object.Header_Split)) 30 | } 31 | 32 | func (h *Header) MarshalJSON() ([]byte, error) { 33 | return message.MarshalJSON(h) 34 | } 35 | 36 | func (h *Header) UnmarshalJSON(data []byte) error { 37 | return message.UnmarshalJSON(h, data, new(object.Header)) 38 | } 39 | 40 | func (h *HeaderWithSignature) MarshalJSON() ([]byte, error) { 41 | return message.MarshalJSON(h) 42 | } 43 | 44 | func (h *HeaderWithSignature) UnmarshalJSON(data []byte) error { 45 | return message.UnmarshalJSON(h, data, new(object.HeaderWithSignature)) 46 | } 47 | 48 | func (o *Object) MarshalJSON() ([]byte, error) { 49 | return message.MarshalJSON(o) 50 | } 51 | 52 | func (o *Object) UnmarshalJSON(data []byte) error { 53 | return message.UnmarshalJSON(o, data, new(object.Object)) 54 | } 55 | 56 | func (s *SplitInfo) MarshalJSON() ([]byte, error) { 57 | return message.MarshalJSON(s) 58 | } 59 | 60 | func (s *SplitInfo) UnmarshalJSON(data []byte) error { 61 | return message.UnmarshalJSON(s, data, new(object.SplitInfo)) 62 | } 63 | 64 | func (f *SearchFilter) MarshalJSON() ([]byte, error) { 65 | return message.MarshalJSON(f) 66 | } 67 | 68 | func (f *SearchFilter) UnmarshalJSON(data []byte) error { 69 | return message.UnmarshalJSON(f, data, new(object.SearchRequest_Body_Filter)) 70 | } 71 | 72 | func (r *Range) MarshalJSON() ([]byte, error) { 73 | return message.MarshalJSON(r) 74 | } 75 | 76 | func (r *Range) UnmarshalJSON(data []byte) error { 77 | return message.UnmarshalJSON(r, data, new(object.Range)) 78 | } 79 | -------------------------------------------------------------------------------- /object/lock.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | lock "github.com/TrueCloudLab/frostfs-api-go/v2/lock/grpc" 8 | "github.com/TrueCloudLab/frostfs-api-go/v2/refs" 9 | refsGRPC "github.com/TrueCloudLab/frostfs-api-go/v2/refs/grpc" 10 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/grpc" 11 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 12 | "github.com/TrueCloudLab/frostfs-api-go/v2/util/proto" 13 | ) 14 | 15 | // Lock represents object Lock message from NeoFS API V2 protocol. 16 | type Lock struct { 17 | members []refs.ObjectID 18 | } 19 | 20 | // NumberOfMembers returns length of lock list. 21 | func (x *Lock) NumberOfMembers() int { 22 | if x != nil { 23 | return len(x.members) 24 | } 25 | 26 | return 0 27 | } 28 | 29 | // IterateMembers passes members of the lock list to f. 30 | func (x *Lock) IterateMembers(f func(refs.ObjectID)) { 31 | if x != nil { 32 | for i := range x.members { 33 | f(x.members[i]) 34 | } 35 | } 36 | } 37 | 38 | // SetMembers sets list of locked members. 39 | // Arg must not be mutated for the duration of the Lock. 40 | func (x *Lock) SetMembers(ids []refs.ObjectID) { 41 | x.members = ids 42 | } 43 | 44 | const ( 45 | _ = iota 46 | fNumLockMembers 47 | ) 48 | 49 | // StableMarshal encodes the Lock into Protocol Buffers binary format 50 | // with direct field order. 51 | func (x *Lock) StableMarshal(buf []byte) []byte { 52 | if x == nil || len(x.members) == 0 { 53 | return []byte{} 54 | } 55 | 56 | if buf == nil { 57 | buf = make([]byte, x.StableSize()) 58 | } 59 | 60 | var offset int 61 | 62 | for i := range x.members { 63 | offset += proto.NestedStructureMarshal(fNumLockMembers, buf[offset:], &x.members[i]) 64 | } 65 | 66 | return buf 67 | } 68 | 69 | // StableSize size of the buffer required to write the Lock in Protocol Buffers 70 | // binary format. 71 | func (x *Lock) StableSize() (sz int) { 72 | if x != nil { 73 | for i := range x.members { 74 | sz += proto.NestedStructureSize(fNumLockMembers, &x.members[i]) 75 | } 76 | } 77 | 78 | return 79 | } 80 | 81 | // Unmarshal decodes the Lock from its Protocol Buffers binary format. 82 | func (x *Lock) Unmarshal(data []byte) error { 83 | return message.Unmarshal(x, data, new(lock.Lock)) 84 | } 85 | 86 | func (x *Lock) ToGRPCMessage() grpc.Message { 87 | var m *lock.Lock 88 | 89 | if x != nil { 90 | m = new(lock.Lock) 91 | 92 | var members []*refsGRPC.ObjectID 93 | 94 | if x.members != nil { 95 | members = make([]*refsGRPC.ObjectID, len(x.members)) 96 | 97 | for i := range x.members { 98 | members[i] = x.members[i].ToGRPCMessage().(*refsGRPC.ObjectID) 99 | } 100 | } 101 | 102 | m.SetMembers(members) 103 | } 104 | 105 | return m 106 | } 107 | 108 | func (x *Lock) FromGRPCMessage(m grpc.Message) error { 109 | v, ok := m.(*lock.Lock) 110 | if !ok { 111 | return message.NewUnexpectedMessageType(m, v) 112 | } 113 | 114 | members := v.GetMembers() 115 | if members == nil { 116 | x.members = nil 117 | } else { 118 | x.members = make([]refs.ObjectID, len(members)) 119 | var err error 120 | 121 | for i := range x.members { 122 | err = x.members[i].FromGRPCMessage(members[i]) 123 | if err != nil { 124 | return err 125 | } 126 | } 127 | } 128 | 129 | return nil 130 | } 131 | 132 | // WriteLock writes Lock to the Object as a payload content. 133 | // The object must not be nil. 134 | func WriteLock(obj *Object, lock Lock) { 135 | hdr := obj.GetHeader() 136 | if hdr == nil { 137 | hdr = new(Header) 138 | obj.SetHeader(hdr) 139 | } 140 | 141 | hdr.SetObjectType(TypeLock) 142 | 143 | payload := lock.StableMarshal(nil) 144 | obj.SetPayload(payload) 145 | } 146 | 147 | // ReadLock reads Lock from the Object payload content. 148 | func ReadLock(lock *Lock, obj Object) error { 149 | payload := obj.GetPayload() 150 | if len(payload) == 0 { 151 | return errors.New("empty payload") 152 | } 153 | 154 | err := lock.Unmarshal(payload) 155 | if err != nil { 156 | return fmt.Errorf("decode lock content from payload: %w", err) 157 | } 158 | 159 | return nil 160 | } 161 | -------------------------------------------------------------------------------- /object/lock_test.go: -------------------------------------------------------------------------------- 1 | package object_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/object" 7 | objecttest "github.com/TrueCloudLab/frostfs-api-go/v2/object/test" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestLockRW(t *testing.T) { 12 | var l object.Lock 13 | var obj object.Object 14 | 15 | require.Error(t, object.ReadLock(&l, obj)) 16 | 17 | l = *objecttest.GenerateLock(false) 18 | 19 | object.WriteLock(&obj, l) 20 | 21 | var l2 object.Lock 22 | 23 | require.NoError(t, object.ReadLock(&l2, obj)) 24 | 25 | require.Equal(t, l, l2) 26 | } 27 | -------------------------------------------------------------------------------- /object/message_test.go: -------------------------------------------------------------------------------- 1 | package object_test 2 | 3 | import ( 4 | "testing" 5 | 6 | objecttest "github.com/TrueCloudLab/frostfs-api-go/v2/object/test" 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 8 | messagetest "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message/test" 9 | ) 10 | 11 | func TestMessageConvert(t *testing.T) { 12 | messagetest.TestRPCMessage(t, 13 | func(empty bool) message.Message { return objecttest.GenerateShortHeader(empty) }, 14 | func(empty bool) message.Message { return objecttest.GenerateAttribute(empty) }, 15 | func(empty bool) message.Message { return objecttest.GenerateSplitHeader(empty) }, 16 | func(empty bool) message.Message { return objecttest.GenerateHeader(empty) }, 17 | func(empty bool) message.Message { return objecttest.GenerateObject(empty) }, 18 | func(empty bool) message.Message { return objecttest.GenerateSplitInfo(empty) }, 19 | func(empty bool) message.Message { return objecttest.GenerateGetRequestBody(empty) }, 20 | func(empty bool) message.Message { return objecttest.GenerateGetRequest(empty) }, 21 | func(empty bool) message.Message { return objecttest.GenerateGetObjectPartInit(empty) }, 22 | func(empty bool) message.Message { return objecttest.GenerateGetObjectPartChunk(empty) }, 23 | func(empty bool) message.Message { return objecttest.GenerateGetResponseBody(empty) }, 24 | func(empty bool) message.Message { return objecttest.GenerateGetResponse(empty) }, 25 | func(empty bool) message.Message { return objecttest.GeneratePutObjectPartInit(empty) }, 26 | func(empty bool) message.Message { return objecttest.GeneratePutObjectPartChunk(empty) }, 27 | func(empty bool) message.Message { return objecttest.GeneratePutRequestBody(empty) }, 28 | func(empty bool) message.Message { return objecttest.GeneratePutRequest(empty) }, 29 | func(empty bool) message.Message { return objecttest.GeneratePutResponseBody(empty) }, 30 | func(empty bool) message.Message { return objecttest.GeneratePutResponse(empty) }, 31 | func(empty bool) message.Message { return objecttest.GenerateDeleteRequestBody(empty) }, 32 | func(empty bool) message.Message { return objecttest.GenerateDeleteRequest(empty) }, 33 | func(empty bool) message.Message { return objecttest.GenerateDeleteResponseBody(empty) }, 34 | func(empty bool) message.Message { return objecttest.GenerateDeleteResponse(empty) }, 35 | func(empty bool) message.Message { return objecttest.GenerateHeadRequestBody(empty) }, 36 | func(empty bool) message.Message { return objecttest.GenerateHeadRequest(empty) }, 37 | func(empty bool) message.Message { return objecttest.GenerateHeadResponseBody(empty) }, 38 | func(empty bool) message.Message { return objecttest.GenerateHeadResponse(empty) }, 39 | func(empty bool) message.Message { return objecttest.GenerateSearchFilter(empty) }, 40 | func(empty bool) message.Message { return objecttest.GenerateSearchRequestBody(empty) }, 41 | func(empty bool) message.Message { return objecttest.GenerateSearchRequest(empty) }, 42 | func(empty bool) message.Message { return objecttest.GenerateSearchResponseBody(empty) }, 43 | func(empty bool) message.Message { return objecttest.GenerateSearchResponse(empty) }, 44 | func(empty bool) message.Message { return objecttest.GenerateRange(empty) }, 45 | func(empty bool) message.Message { return objecttest.GenerateGetRangeRequestBody(empty) }, 46 | func(empty bool) message.Message { return objecttest.GenerateGetRangeRequest(empty) }, 47 | func(empty bool) message.Message { return objecttest.GenerateGetRangeResponseBody(empty) }, 48 | func(empty bool) message.Message { return objecttest.GenerateGetRangeResponse(empty) }, 49 | func(empty bool) message.Message { return objecttest.GenerateGetRangeHashRequestBody(empty) }, 50 | func(empty bool) message.Message { return objecttest.GenerateGetRangeHashRequest(empty) }, 51 | func(empty bool) message.Message { return objecttest.GenerateGetRangeHashResponseBody(empty) }, 52 | func(empty bool) message.Message { return objecttest.GenerateGetRangeHashResponse(empty) }, 53 | func(empty bool) message.Message { return objecttest.GenerateLock(empty) }, 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /object/status.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/status" 5 | statusgrpc "github.com/TrueCloudLab/frostfs-api-go/v2/status/grpc" 6 | ) 7 | 8 | // LocalizeFailStatus checks if passed global status.Code is related to object failure and: 9 | // 10 | // then localizes the code and returns true, 11 | // else leaves the code unchanged and returns false. 12 | // 13 | // Arg must not be nil. 14 | func LocalizeFailStatus(c *status.Code) bool { 15 | return status.LocalizeIfInSection(c, uint32(statusgrpc.Section_SECTION_OBJECT)) 16 | } 17 | 18 | // GlobalizeFail globalizes local code of object failure. 19 | // 20 | // Arg must not be nil. 21 | func GlobalizeFail(c *status.Code) { 22 | c.GlobalizeSection(uint32(statusgrpc.Section_SECTION_OBJECT)) 23 | } 24 | 25 | const ( 26 | // StatusAccessDenied is a local status.Code value for 27 | // ACCESS_DENIED object failure. 28 | StatusAccessDenied status.Code = iota 29 | // StatusNotFound is a local status.Code value for 30 | // OBJECT_NOT_FOUND object failure. 31 | StatusNotFound 32 | // StatusLocked is a local status.Code value for 33 | // LOCKED object failure. 34 | StatusLocked 35 | // StatusLockNonRegularObject is a local status.Code value for 36 | // LOCK_NON_REGULAR_OBJECT object failure. 37 | StatusLockNonRegularObject 38 | // StatusAlreadyRemoved is a local status.Code value for 39 | // OBJECT_ALREADY_REMOVED object failure. 40 | StatusAlreadyRemoved 41 | // StatusOutOfRange is a local status.Code value for 42 | // OUT_OF_RANGE object failure. 43 | StatusOutOfRange 44 | ) 45 | 46 | const ( 47 | // detailAccessDeniedDesc is a StatusAccessDenied detail ID for 48 | // human-readable description. 49 | detailAccessDeniedDesc = iota 50 | ) 51 | 52 | // WriteAccessDeniedDesc writes human-readable description of StatusAccessDenied 53 | // into status.Status as a detail. The status must not be nil. 54 | // 55 | // Existing details are expected to be ID-unique, otherwise undefined behavior. 56 | func WriteAccessDeniedDesc(st *status.Status, desc string) { 57 | var found bool 58 | 59 | st.IterateDetails(func(d *status.Detail) bool { 60 | if d.ID() == detailAccessDeniedDesc { 61 | found = true 62 | d.SetValue([]byte(desc)) 63 | } 64 | 65 | return found 66 | }) 67 | 68 | if !found { 69 | var d status.Detail 70 | 71 | d.SetID(detailAccessDeniedDesc) 72 | d.SetValue([]byte(desc)) 73 | 74 | st.AppendDetails(d) 75 | } 76 | } 77 | 78 | // ReadAccessDeniedDesc looks up for status detail with human-readable description 79 | // of StatusAccessDenied. Returns empty string if detail is missing. 80 | func ReadAccessDeniedDesc(st status.Status) (desc string) { 81 | st.IterateDetails(func(d *status.Detail) bool { 82 | if d.ID() == detailAccessDeniedDesc { 83 | desc = string(d.Value()) 84 | return true 85 | } 86 | 87 | return false 88 | }) 89 | 90 | return 91 | } 92 | -------------------------------------------------------------------------------- /object/status_test.go: -------------------------------------------------------------------------------- 1 | package object_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/object" 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/status" 8 | statustest "github.com/TrueCloudLab/frostfs-api-go/v2/status/test" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestStatusCodes(t *testing.T) { 13 | statustest.TestCodes(t, object.LocalizeFailStatus, object.GlobalizeFail, 14 | object.StatusAccessDenied, 2048, 15 | object.StatusNotFound, 2049, 16 | object.StatusLocked, 2050, 17 | object.StatusLockNonRegularObject, 2051, 18 | object.StatusAlreadyRemoved, 2052, 19 | object.StatusOutOfRange, 2053, 20 | ) 21 | } 22 | 23 | func TestAccessDeniedDesc(t *testing.T) { 24 | var st status.Status 25 | 26 | require.Empty(t, object.ReadAccessDeniedDesc(st)) 27 | 28 | const desc = "some description" 29 | 30 | object.WriteAccessDeniedDesc(&st, desc) 31 | require.Equal(t, desc, object.ReadAccessDeniedDesc(st)) 32 | 33 | object.WriteAccessDeniedDesc(&st, desc+"1") 34 | require.Equal(t, desc+"1", object.ReadAccessDeniedDesc(st)) 35 | } 36 | -------------------------------------------------------------------------------- /object/string.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | object "github.com/TrueCloudLab/frostfs-api-go/v2/object/grpc" 5 | ) 6 | 7 | // String returns string representation of Type. 8 | func (t Type) String() string { 9 | return TypeToGRPCField(t).String() 10 | } 11 | 12 | // FromString parses Type from a string representation. 13 | // It is a reverse action to String(). 14 | // 15 | // Returns true if s was parsed successfully. 16 | func (t *Type) FromString(s string) bool { 17 | var g object.ObjectType 18 | 19 | ok := g.FromString(s) 20 | 21 | if ok { 22 | *t = TypeFromGRPCField(g) 23 | } 24 | 25 | return ok 26 | } 27 | 28 | // TypeFromString converts string to Type. 29 | // 30 | // Deprecated: use FromString method. 31 | func TypeFromString(s string) (t Type) { 32 | t.FromString(s) 33 | return 34 | } 35 | 36 | // String returns string representation of MatchType. 37 | func (t MatchType) String() string { 38 | return MatchTypeToGRPCField(t).String() 39 | } 40 | 41 | // FromString parses MatchType from a string representation. 42 | // It is a reverse action to String(). 43 | // 44 | // Returns true if s was parsed successfully. 45 | func (t *MatchType) FromString(s string) bool { 46 | var g object.MatchType 47 | 48 | ok := g.FromString(s) 49 | 50 | if ok { 51 | *t = MatchTypeFromGRPCField(g) 52 | } 53 | 54 | return ok 55 | } 56 | -------------------------------------------------------------------------------- /prepare.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$1" ]; then 4 | echo "usage: ./prepare.sh path/to/frostfs-api" 5 | exit 1 6 | fi 7 | 8 | API_GO_PATH=$(pwd) 9 | API_PATH=$1 10 | 11 | # MOVE FILES FROM API REPO 12 | cd "$API_PATH" || exit 1 13 | ARGS=$(find ./ -name '*.proto' -not -path './vendor/*') 14 | for file in $ARGS; do 15 | dir=$(dirname "$file") 16 | mkdir -p "$API_GO_PATH/$dir/grpc" 17 | cp -r "$dir"/* "$API_GO_PATH/$dir/grpc" 18 | done 19 | 20 | # MODIFY FILES 21 | cd "$API_GO_PATH" || exit 1 22 | ARGS2=$(find ./ -name '*.proto') 23 | for file in $ARGS2; do 24 | echo "$file" 25 | sed -i "s/import\ \"\(.*\)\/\(.*\)\.proto\";/import\ \"\1\/grpc\/\2\.proto\";/" $file 26 | done 27 | 28 | cd "$API_GO_PATH" || exit 1 29 | # COMPILE 30 | make protoc 31 | 32 | # REMOVE PROTO DEFINITIONS 33 | ARGS=$(find ./$prefix -name '*.proto' -not -path './vendor/*' -not -path './util/*') 34 | for file in $ARGS; do 35 | rm "$file" 36 | done 37 | -------------------------------------------------------------------------------- /refs/bench_test.go: -------------------------------------------------------------------------------- 1 | package refs 2 | 3 | import ( 4 | "math/rand" 5 | "strconv" 6 | "testing" 7 | ) 8 | 9 | func BenchmarkObjectIDSlice(b *testing.B) { 10 | for _, size := range []int{0, 1, 50} { 11 | b.Run(strconv.Itoa(size)+" elements", func(b *testing.B) { 12 | benchmarkObjectIDSlice(b, size) 13 | }) 14 | } 15 | } 16 | 17 | func benchmarkObjectIDSlice(b *testing.B, size int) { 18 | ids := make([]ObjectID, size) 19 | for i := range ids { 20 | ids[i].val = make([]byte, 32) 21 | rand.Read(ids[i].val) 22 | } 23 | raw := ObjectIDListToGRPCMessage(ids) 24 | 25 | b.Run("to grpc message", func(b *testing.B) { 26 | b.ReportAllocs() 27 | for i := 0; i < b.N; i++ { 28 | raw := ObjectIDListToGRPCMessage(ids) 29 | if len(raw) != len(ids) { 30 | b.FailNow() 31 | } 32 | } 33 | }) 34 | b.Run("from grpc message", func(b *testing.B) { 35 | b.ReportAllocs() 36 | for i := 0; i < b.N; i++ { 37 | ids, err := ObjectIDListFromGRPCMessage(raw) 38 | if err != nil || len(raw) != len(ids) { 39 | b.FailNow() 40 | } 41 | } 42 | }) 43 | b.Run("marshal", func(b *testing.B) { 44 | b.ReportAllocs() 45 | for i := 0; i < b.N; i++ { 46 | buf := make([]byte, ObjectIDNestedListSize(1, ids)) 47 | n := ObjectIDNestedListMarshal(1, buf, ids) 48 | if n != len(buf) { 49 | b.FailNow() 50 | } 51 | } 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /refs/grpc/types.go: -------------------------------------------------------------------------------- 1 | package refs 2 | 3 | // SetValue sets container identifier in a binary format. 4 | func (x *ContainerID) SetValue(v []byte) { 5 | x.Value = v 6 | } 7 | 8 | // SetValue sets object identifier in a binary format. 9 | func (x *ObjectID) SetValue(v []byte) { 10 | x.Value = v 11 | } 12 | 13 | // SetValue sets owner identifier in a binary format. 14 | func (x *OwnerID) SetValue(v []byte) { 15 | x.Value = v 16 | } 17 | 18 | // SetContainerId sets container identifier of the address. 19 | func (x *Address) SetContainerId(v *ContainerID) { 20 | x.ContainerId = v 21 | } 22 | 23 | // SetObjectId sets object identifier of the address. 24 | func (x *Address) SetObjectId(v *ObjectID) { 25 | x.ObjectId = v 26 | } 27 | 28 | // SetChecksumType in generic checksum structure. 29 | func (x *Checksum) SetChecksumType(v ChecksumType) { 30 | x.Type = v 31 | } 32 | 33 | // SetSum in generic checksum structure. 34 | func (x *Checksum) SetSum(v []byte) { 35 | x.Sum = v 36 | } 37 | 38 | // SetMajor sets major version number. 39 | func (x *Version) SetMajor(v uint32) { 40 | x.Major = v 41 | } 42 | 43 | // SetMinor sets minor version number. 44 | func (x *Version) SetMinor(v uint32) { 45 | x.Minor = v 46 | } 47 | 48 | // SetKey sets public key in a binary format. 49 | func (x *Signature) SetKey(v []byte) { 50 | x.Key = v 51 | } 52 | 53 | // SetSign sets signature. 54 | func (x *Signature) SetSign(v []byte) { 55 | x.Sign = v 56 | } 57 | 58 | // SetScheme sets signature scheme. 59 | func (x *Signature) SetScheme(s SignatureScheme) { 60 | x.Scheme = s 61 | } 62 | 63 | // SetKey sets public key in a binary format. 64 | func (x *SignatureRFC6979) SetKey(v []byte) { 65 | x.Key = v 66 | } 67 | 68 | // SetSign sets signature. 69 | func (x *SignatureRFC6979) SetSign(v []byte) { 70 | x.Sign = v 71 | } 72 | 73 | // FromString parses SignatureScheme from a string representation, 74 | // It is a reverse action to String(). 75 | // 76 | // Returns true if s was parsed successfully. 77 | func (x *SignatureScheme) FromString(s string) bool { 78 | i, ok := SignatureScheme_value[s] 79 | if ok { 80 | *x = SignatureScheme(i) 81 | } 82 | 83 | return ok 84 | } 85 | 86 | // FromString parses ChecksumType from a string representation, 87 | // It is a reverse action to String(). 88 | // 89 | // Returns true if s was parsed successfully. 90 | func (x *ChecksumType) FromString(s string) bool { 91 | i, ok := ChecksumType_value[s] 92 | if ok { 93 | *x = ChecksumType(i) 94 | } 95 | 96 | return ok 97 | } 98 | 99 | // SetValue sets subnet identifier in a base-10 integer format. 100 | func (x *SubnetID) SetValue(v uint32) { 101 | x.Value = v 102 | } 103 | -------------------------------------------------------------------------------- /refs/json.go: -------------------------------------------------------------------------------- 1 | package refs 2 | 3 | import ( 4 | refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs/grpc" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 6 | ) 7 | 8 | func (a *Address) MarshalJSON() ([]byte, error) { 9 | return message.MarshalJSON(a) 10 | } 11 | 12 | func (a *Address) UnmarshalJSON(data []byte) error { 13 | return message.UnmarshalJSON(a, data, new(refs.Address)) 14 | } 15 | 16 | func (o *ObjectID) MarshalJSON() ([]byte, error) { 17 | return message.MarshalJSON(o) 18 | } 19 | 20 | func (o *ObjectID) UnmarshalJSON(data []byte) error { 21 | return message.UnmarshalJSON(o, data, new(refs.ObjectID)) 22 | } 23 | 24 | func (c *ContainerID) MarshalJSON() ([]byte, error) { 25 | return message.MarshalJSON(c) 26 | } 27 | 28 | func (c *ContainerID) UnmarshalJSON(data []byte) error { 29 | return message.UnmarshalJSON(c, data, new(refs.ContainerID)) 30 | } 31 | 32 | func (o *OwnerID) MarshalJSON() ([]byte, error) { 33 | return message.MarshalJSON(o) 34 | } 35 | 36 | func (o *OwnerID) UnmarshalJSON(data []byte) error { 37 | return message.UnmarshalJSON(o, data, new(refs.OwnerID)) 38 | } 39 | 40 | func (v *Version) MarshalJSON() ([]byte, error) { 41 | return message.MarshalJSON(v) 42 | } 43 | 44 | func (v *Version) UnmarshalJSON(data []byte) error { 45 | return message.UnmarshalJSON(v, data, new(refs.Version)) 46 | } 47 | 48 | func (s *Signature) MarshalJSON() ([]byte, error) { 49 | return message.MarshalJSON(s) 50 | } 51 | 52 | func (s *Signature) UnmarshalJSON(data []byte) error { 53 | return message.UnmarshalJSON(s, data, new(refs.Signature)) 54 | } 55 | 56 | func (c *Checksum) MarshalJSON() ([]byte, error) { 57 | return message.MarshalJSON(c) 58 | } 59 | 60 | func (c *Checksum) UnmarshalJSON(data []byte) error { 61 | return message.UnmarshalJSON(c, data, new(refs.Checksum)) 62 | } 63 | -------------------------------------------------------------------------------- /refs/message_test.go: -------------------------------------------------------------------------------- 1 | package refs_test 2 | 3 | import ( 4 | "testing" 5 | 6 | refstest "github.com/TrueCloudLab/frostfs-api-go/v2/refs/test" 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 8 | messagetest "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message/test" 9 | ) 10 | 11 | func TestMessageConvert(t *testing.T) { 12 | messagetest.TestRPCMessage(t, 13 | func(empty bool) message.Message { return refstest.GenerateOwnerID(empty) }, 14 | func(empty bool) message.Message { return refstest.GenerateObjectID(empty) }, 15 | func(empty bool) message.Message { return refstest.GenerateContainerID(empty) }, 16 | func(empty bool) message.Message { return refstest.GenerateAddress(empty) }, 17 | func(empty bool) message.Message { return refstest.GenerateChecksum(empty) }, 18 | func(empty bool) message.Message { return refstest.GenerateSignature(empty) }, 19 | func(empty bool) message.Message { return refstest.GenerateVersion(empty) }, 20 | func(empty bool) message.Message { return refstest.GenerateSubnetID(empty) }, 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /refs/string.go: -------------------------------------------------------------------------------- 1 | package refs 2 | 3 | import ( 4 | refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs/grpc" 5 | ) 6 | 7 | // String returns string representation of ChecksumType. 8 | func (t ChecksumType) String() string { 9 | return ChecksumTypeToGRPC(t).String() 10 | } 11 | 12 | // FromString parses ChecksumType from a string representation. 13 | // It is a reverse action to String(). 14 | // 15 | // Returns true if s was parsed successfully. 16 | func (t *ChecksumType) FromString(s string) bool { 17 | var g refs.ChecksumType 18 | 19 | ok := g.FromString(s) 20 | 21 | if ok { 22 | *t = ChecksumTypeFromGRPC(g) 23 | } 24 | 25 | return ok 26 | } 27 | 28 | // String returns string representation of SignatureScheme. 29 | func (t SignatureScheme) String() string { 30 | return refs.SignatureScheme(t).String() 31 | } 32 | 33 | // FromString parses SignatureScheme from a string representation. 34 | // It is a reverse action to String(). 35 | // 36 | // Returns true if s was parsed successfully. 37 | func (t *SignatureScheme) FromString(s string) bool { 38 | var g refs.SignatureScheme 39 | 40 | ok := g.FromString(s) 41 | 42 | if ok { 43 | *t = SignatureScheme(g) 44 | } 45 | 46 | return ok 47 | } 48 | -------------------------------------------------------------------------------- /refs/test/generate.go: -------------------------------------------------------------------------------- 1 | package refstest 2 | 3 | import ( 4 | "math/rand" 5 | 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/refs" 7 | ) 8 | 9 | func GenerateVersion(empty bool) *refs.Version { 10 | m := new(refs.Version) 11 | 12 | if !empty { 13 | m.SetMajor(2) 14 | m.SetMinor(1) 15 | } 16 | 17 | return m 18 | } 19 | 20 | func GenerateOwnerID(empty bool) *refs.OwnerID { 21 | m := new(refs.OwnerID) 22 | 23 | if !empty { 24 | m.SetValue([]byte{1, 2, 3}) 25 | } 26 | 27 | return m 28 | } 29 | 30 | func GenerateAddress(empty bool) *refs.Address { 31 | m := new(refs.Address) 32 | 33 | if !empty { 34 | m.SetObjectID(GenerateObjectID(false)) 35 | m.SetContainerID(GenerateContainerID(false)) 36 | } 37 | 38 | return m 39 | } 40 | 41 | func GenerateObjectID(empty bool) *refs.ObjectID { 42 | m := new(refs.ObjectID) 43 | 44 | if !empty { 45 | m.SetValue([]byte{1, 2, 3}) 46 | } 47 | 48 | return m 49 | } 50 | 51 | func GenerateObjectIDs(empty bool) []refs.ObjectID { 52 | var ids []refs.ObjectID 53 | 54 | if !empty { 55 | ids = append(ids, 56 | *GenerateObjectID(false), 57 | *GenerateObjectID(false), 58 | ) 59 | } 60 | 61 | return ids 62 | } 63 | 64 | func GenerateContainerID(empty bool) *refs.ContainerID { 65 | m := new(refs.ContainerID) 66 | 67 | if !empty { 68 | m.SetValue([]byte{1, 2, 3}) 69 | } 70 | 71 | return m 72 | } 73 | 74 | func GenerateContainerIDs(empty bool) []refs.ContainerID { 75 | var res []refs.ContainerID 76 | 77 | if !empty { 78 | res = append(res, 79 | *GenerateContainerID(false), 80 | *GenerateContainerID(false), 81 | ) 82 | } 83 | 84 | return res 85 | } 86 | 87 | func GenerateSignature(empty bool) *refs.Signature { 88 | m := new(refs.Signature) 89 | 90 | if !empty { 91 | m.SetKey([]byte{1}) 92 | m.SetSign([]byte{2}) 93 | m.SetScheme(refs.SignatureScheme(rand.Int31() % 3)) 94 | } 95 | 96 | return m 97 | } 98 | 99 | func GenerateChecksum(empty bool) *refs.Checksum { 100 | m := new(refs.Checksum) 101 | 102 | if !empty { 103 | m.SetType(1) 104 | m.SetSum([]byte{1, 2, 3}) 105 | } 106 | 107 | return m 108 | } 109 | 110 | func GenerateSubnetID(empty bool) *refs.SubnetID { 111 | m := new(refs.SubnetID) 112 | 113 | if !empty { 114 | m.SetValue(666) 115 | } 116 | 117 | return m 118 | } 119 | -------------------------------------------------------------------------------- /refs/types_test.go: -------------------------------------------------------------------------------- 1 | package refs_test 2 | 3 | import ( 4 | "math" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/TrueCloudLab/frostfs-api-go/v2/refs" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestZeroSubnet(t *testing.T) { 13 | id := new(refs.SubnetID) 14 | 15 | require.True(t, refs.IsZeroSubnet(id)) 16 | 17 | id.SetValue(1) 18 | require.False(t, refs.IsZeroSubnet(id)) 19 | 20 | refs.MakeZeroSubnet(id) 21 | require.True(t, refs.IsZeroSubnet(id)) 22 | } 23 | 24 | func TestSubnetID_MarshalText(t *testing.T) { 25 | var id refs.SubnetID 26 | 27 | const val = 15 28 | 29 | id.SetValue(val) 30 | 31 | txt, err := id.MarshalText() 32 | require.NoError(t, err) 33 | 34 | res, err := strconv.ParseUint(string(txt), 10, 32) 35 | require.NoError(t, err) 36 | 37 | require.EqualValues(t, val, res) 38 | 39 | t.Run("nil", func(t *testing.T) { 40 | var id *refs.SubnetID 41 | 42 | txt, err := id.MarshalText() 43 | require.NoError(t, err) 44 | 45 | res, err := strconv.ParseUint(string(txt), 10, 32) 46 | require.NoError(t, err) 47 | 48 | require.Zero(t, res) 49 | }) 50 | } 51 | 52 | func TestSubnetID_UnmarshalText(t *testing.T) { 53 | const val = 15 54 | 55 | str := strconv.FormatUint(val, 10) 56 | 57 | var id refs.SubnetID 58 | 59 | err := id.UnmarshalText([]byte(str)) 60 | require.NoError(t, err) 61 | 62 | require.EqualValues(t, val, id.GetValue()) 63 | 64 | t.Run("uint32 overflow", func(t *testing.T) { 65 | txt := strconv.FormatUint(math.MaxUint32+1, 10) 66 | 67 | var id refs.SubnetID 68 | 69 | err := id.UnmarshalText([]byte(txt)) 70 | require.ErrorIs(t, err, strconv.ErrRange) 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /reputation/grpc/service.go: -------------------------------------------------------------------------------- 1 | package reputation 2 | 3 | import ( 4 | session "github.com/TrueCloudLab/frostfs-api-go/v2/session/grpc" 5 | ) 6 | 7 | // SetEpoch sets epoch in which the trust was assessed. 8 | func (x *AnnounceLocalTrustRequest_Body) SetEpoch(v uint64) { 9 | x.Epoch = v 10 | } 11 | 12 | // SetTrusts sets list of normalized trust values. 13 | func (x *AnnounceLocalTrustRequest_Body) SetTrusts(v []*Trust) { 14 | x.Trusts = v 15 | } 16 | 17 | // SetBody sets body of the request. 18 | func (x *AnnounceLocalTrustRequest) SetBody(v *AnnounceLocalTrustRequest_Body) { 19 | x.Body = v 20 | } 21 | 22 | // SetMetaHeader sets meta header of the request. 23 | func (x *AnnounceLocalTrustRequest) SetMetaHeader(v *session.RequestMetaHeader) { 24 | x.MetaHeader = v 25 | } 26 | 27 | // SetVerifyHeader sets verification header of the request. 28 | func (x *AnnounceLocalTrustRequest) SetVerifyHeader(v *session.RequestVerificationHeader) { 29 | x.VerifyHeader = v 30 | } 31 | 32 | // SetBody sets body of the response. 33 | func (x *AnnounceLocalTrustResponse) SetBody(v *AnnounceLocalTrustResponse_Body) { 34 | x.Body = v 35 | } 36 | 37 | // SetMetaHeader sets meta header of the response. 38 | func (x *AnnounceLocalTrustResponse) SetMetaHeader(v *session.ResponseMetaHeader) { 39 | x.MetaHeader = v 40 | } 41 | 42 | // SetVerifyHeader sets verification header of the response. 43 | func (x *AnnounceLocalTrustResponse) SetVerifyHeader(v *session.ResponseVerificationHeader) { 44 | x.VerifyHeader = v 45 | } 46 | 47 | // SetEpoch sets epoch in which the intermediate trust was assessed. 48 | func (x *AnnounceIntermediateResultRequest_Body) SetEpoch(v uint64) { 49 | x.Epoch = v 50 | } 51 | 52 | // SetIteration sets sequence number of the iteration. 53 | func (x *AnnounceIntermediateResultRequest_Body) SetIteration(v uint32) { 54 | x.Iteration = v 55 | } 56 | 57 | // SetTrust sets current global trust value. 58 | func (x *AnnounceIntermediateResultRequest_Body) SetTrust(v *PeerToPeerTrust) { 59 | x.Trust = v 60 | } 61 | 62 | // SetBody sets body of the request. 63 | func (x *AnnounceIntermediateResultRequest) SetBody(v *AnnounceIntermediateResultRequest_Body) { 64 | x.Body = v 65 | } 66 | 67 | // SetMetaHeader sets meta header of the request. 68 | func (x *AnnounceIntermediateResultRequest) SetMetaHeader(v *session.RequestMetaHeader) { 69 | x.MetaHeader = v 70 | } 71 | 72 | // SetVerifyHeader sets verification header of the request. 73 | func (x *AnnounceIntermediateResultRequest) SetVerifyHeader(v *session.RequestVerificationHeader) { 74 | x.VerifyHeader = v 75 | } 76 | 77 | // SetBody sets body of the response. 78 | func (x *AnnounceIntermediateResultResponse) SetBody(v *AnnounceIntermediateResultResponse_Body) { 79 | x.Body = v 80 | } 81 | 82 | // SetMetaHeader sets meta header of the response. 83 | func (x *AnnounceIntermediateResultResponse) SetMetaHeader(v *session.ResponseMetaHeader) { 84 | x.MetaHeader = v 85 | } 86 | 87 | // SetVerifyHeader sets verification header of the response. 88 | func (x *AnnounceIntermediateResultResponse) SetVerifyHeader(v *session.ResponseVerificationHeader) { 89 | x.VerifyHeader = v 90 | } 91 | -------------------------------------------------------------------------------- /reputation/grpc/types.go: -------------------------------------------------------------------------------- 1 | package reputation 2 | 3 | import ( 4 | refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs/grpc" 5 | ) 6 | 7 | // SetPublicKey sets binary public key of ID. 8 | func (x *PeerID) SetPublicKey(v []byte) { 9 | x.PublicKey = v 10 | } 11 | 12 | // SetPeer sets trusted peer's ID. 13 | func (x *Trust) SetPeer(v *PeerID) { 14 | x.Peer = v 15 | } 16 | 17 | // SetValue sets trust value. 18 | func (x *Trust) SetValue(v float64) { 19 | x.Value = v 20 | } 21 | 22 | // SetTrustingPeer sets trusting peer ID. 23 | func (x *PeerToPeerTrust) SetTrustingPeer(v *PeerID) { 24 | x.TrustingPeer = v 25 | } 26 | 27 | // SetTrust sets trust value of trusting peer to the trusted one. 28 | func (x *PeerToPeerTrust) SetTrust(v *Trust) { 29 | x.Trust = v 30 | } 31 | 32 | // SetManager sets manager ID. 33 | func (x *GlobalTrust_Body) SetManager(v *PeerID) { 34 | x.Manager = v 35 | } 36 | 37 | // SetTrust sets global trust value. 38 | func (x *GlobalTrust_Body) SetTrust(v *Trust) { 39 | x.Trust = v 40 | } 41 | 42 | // SetVersion sets message format version. 43 | func (x *GlobalTrust) SetVersion(v *refs.Version) { 44 | x.Version = v 45 | } 46 | 47 | // SetBody sets message body. 48 | func (x *GlobalTrust) SetBody(v *GlobalTrust_Body) { 49 | x.Body = v 50 | } 51 | 52 | // SetSignature sets body signature. 53 | func (x *GlobalTrust) SetSignature(v *refs.Signature) { 54 | x.Signature = v 55 | } 56 | -------------------------------------------------------------------------------- /reputation/json.go: -------------------------------------------------------------------------------- 1 | package reputation 2 | 3 | import ( 4 | reputation "github.com/TrueCloudLab/frostfs-api-go/v2/reputation/grpc" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 6 | ) 7 | 8 | func (x *PeerID) MarshalJSON() ([]byte, error) { 9 | return message.MarshalJSON(x) 10 | } 11 | 12 | func (x *PeerID) UnmarshalJSON(data []byte) error { 13 | return message.UnmarshalJSON(x, data, new(reputation.PeerID)) 14 | } 15 | 16 | func (x *Trust) MarshalJSON() ([]byte, error) { 17 | return message.MarshalJSON(x) 18 | } 19 | 20 | func (x *Trust) UnmarshalJSON(data []byte) error { 21 | return message.UnmarshalJSON(x, data, new(reputation.Trust)) 22 | } 23 | 24 | func (x *PeerToPeerTrust) MarshalJSON() ([]byte, error) { 25 | return message.MarshalJSON(x) 26 | } 27 | 28 | func (x *PeerToPeerTrust) UnmarshalJSON(data []byte) error { 29 | return message.UnmarshalJSON(x, data, new(reputation.PeerToPeerTrust)) 30 | } 31 | 32 | func (x *GlobalTrust) MarshalJSON() ([]byte, error) { 33 | return message.MarshalJSON(x) 34 | } 35 | 36 | func (x *GlobalTrust) UnmarshalJSON(data []byte) error { 37 | return message.UnmarshalJSON(x, data, new(reputation.GlobalTrust)) 38 | } 39 | -------------------------------------------------------------------------------- /reputation/message_test.go: -------------------------------------------------------------------------------- 1 | package reputation_test 2 | 3 | import ( 4 | "testing" 5 | 6 | reputationtest "github.com/TrueCloudLab/frostfs-api-go/v2/reputation/test" 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 8 | messagetest "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message/test" 9 | ) 10 | 11 | func TestMessageConvert(t *testing.T) { 12 | messagetest.TestRPCMessage(t, 13 | func(empty bool) message.Message { return reputationtest.GenerateTrust(empty) }, 14 | func(empty bool) message.Message { return reputationtest.GenerateAnnounceLocalTrustRequestBody(empty) }, 15 | func(empty bool) message.Message { return reputationtest.GenerateAnnounceLocalTrustRequest(empty) }, 16 | func(empty bool) message.Message { return reputationtest.GenerateAnnounceLocalTrustResponseBody(empty) }, 17 | func(empty bool) message.Message { return reputationtest.GenerateAnnounceLocalTrustResponse(empty) }, 18 | func(empty bool) message.Message { 19 | return reputationtest.GenerateAnnounceIntermediateResultRequestBody(empty) 20 | }, 21 | func(empty bool) message.Message { 22 | return reputationtest.GenerateAnnounceIntermediateResultRequest(empty) 23 | }, 24 | func(empty bool) message.Message { 25 | return reputationtest.GenerateAnnounceIntermediateResultResponseBody(empty) 26 | }, 27 | func(empty bool) message.Message { 28 | return reputationtest.GenerateAnnounceIntermediateResultResponse(empty) 29 | }, 30 | func(empty bool) message.Message { return reputationtest.GenerateGlobalTrustBody(empty) }, 31 | func(empty bool) message.Message { return reputationtest.GenerateGlobalTrust(empty) }, 32 | func(empty bool) message.Message { return reputationtest.GeneratePeerToPeerTrust(empty) }, 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /reputation/test/generate.go: -------------------------------------------------------------------------------- 1 | package reputationtest 2 | 3 | import ( 4 | refstest "github.com/TrueCloudLab/frostfs-api-go/v2/refs/test" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/reputation" 6 | sessiontest "github.com/TrueCloudLab/frostfs-api-go/v2/session/test" 7 | ) 8 | 9 | func GeneratePeerID(empty bool) *reputation.PeerID { 10 | m := new(reputation.PeerID) 11 | 12 | if !empty { 13 | m.SetPublicKey([]byte{1, 2, 3}) 14 | } 15 | 16 | return m 17 | } 18 | 19 | func GenerateTrust(empty bool) *reputation.Trust { 20 | m := new(reputation.Trust) 21 | 22 | if !empty { 23 | m.SetValue(1) 24 | m.SetPeer(GeneratePeerID(false)) 25 | } 26 | 27 | return m 28 | } 29 | 30 | func GeneratePeerToPeerTrust(empty bool) *reputation.PeerToPeerTrust { 31 | m := new(reputation.PeerToPeerTrust) 32 | 33 | if !empty { 34 | m.SetTrustingPeer(GeneratePeerID(false)) 35 | m.SetTrust(GenerateTrust(false)) 36 | } 37 | 38 | return m 39 | } 40 | 41 | func GenerateGlobalTrustBody(empty bool) *reputation.GlobalTrustBody { 42 | m := new(reputation.GlobalTrustBody) 43 | 44 | if !empty { 45 | m.SetManager(GeneratePeerID(false)) 46 | m.SetTrust(GenerateTrust(false)) 47 | } 48 | 49 | return m 50 | } 51 | 52 | func GenerateGlobalTrust(empty bool) *reputation.GlobalTrust { 53 | m := new(reputation.GlobalTrust) 54 | 55 | if !empty { 56 | m.SetVersion(refstest.GenerateVersion(false)) 57 | m.SetBody(GenerateGlobalTrustBody(false)) 58 | m.SetSignature(refstest.GenerateSignature(empty)) 59 | } 60 | 61 | return m 62 | } 63 | 64 | func GenerateTrusts(empty bool) []reputation.Trust { 65 | var res []reputation.Trust 66 | 67 | if !empty { 68 | res = append(res, 69 | *GenerateTrust(false), 70 | *GenerateTrust(false), 71 | ) 72 | } 73 | 74 | return res 75 | } 76 | 77 | func GenerateAnnounceLocalTrustRequestBody(empty bool) *reputation.AnnounceLocalTrustRequestBody { 78 | m := new(reputation.AnnounceLocalTrustRequestBody) 79 | 80 | if !empty { 81 | m.SetEpoch(13) 82 | m.SetTrusts(GenerateTrusts(false)) 83 | } 84 | 85 | return m 86 | } 87 | 88 | func GenerateAnnounceLocalTrustRequest(empty bool) *reputation.AnnounceLocalTrustRequest { 89 | m := new(reputation.AnnounceLocalTrustRequest) 90 | 91 | if !empty { 92 | m.SetBody(GenerateAnnounceLocalTrustRequestBody(false)) 93 | m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) 94 | m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) 95 | } 96 | 97 | return m 98 | } 99 | 100 | func GenerateAnnounceLocalTrustResponseBody(empty bool) *reputation.AnnounceLocalTrustResponseBody { 101 | m := new(reputation.AnnounceLocalTrustResponseBody) 102 | 103 | return m 104 | } 105 | 106 | func GenerateAnnounceLocalTrustResponse(empty bool) *reputation.AnnounceLocalTrustResponse { 107 | m := new(reputation.AnnounceLocalTrustResponse) 108 | 109 | if !empty { 110 | m.SetBody(GenerateAnnounceLocalTrustResponseBody(false)) 111 | m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) 112 | m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) 113 | } 114 | 115 | return m 116 | } 117 | 118 | func GenerateAnnounceIntermediateResultRequestBody(empty bool) *reputation.AnnounceIntermediateResultRequestBody { 119 | m := new(reputation.AnnounceIntermediateResultRequestBody) 120 | 121 | if !empty { 122 | m.SetEpoch(123) 123 | m.SetIteration(564) 124 | m.SetTrust(GeneratePeerToPeerTrust(false)) 125 | } 126 | 127 | return m 128 | } 129 | 130 | func GenerateAnnounceIntermediateResultRequest(empty bool) *reputation.AnnounceIntermediateResultRequest { 131 | m := new(reputation.AnnounceIntermediateResultRequest) 132 | 133 | if !empty { 134 | m.SetBody(GenerateAnnounceIntermediateResultRequestBody(false)) 135 | } 136 | 137 | m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) 138 | m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) 139 | 140 | return m 141 | } 142 | 143 | func GenerateAnnounceIntermediateResultResponseBody(empty bool) *reputation.AnnounceIntermediateResultResponseBody { 144 | m := new(reputation.AnnounceIntermediateResultResponseBody) 145 | 146 | return m 147 | } 148 | 149 | func GenerateAnnounceIntermediateResultResponse(empty bool) *reputation.AnnounceIntermediateResultResponse { 150 | m := new(reputation.AnnounceIntermediateResultResponse) 151 | 152 | if !empty { 153 | m.SetBody(GenerateAnnounceIntermediateResultResponseBody(false)) 154 | } 155 | 156 | m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) 157 | m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) 158 | 159 | return m 160 | } 161 | -------------------------------------------------------------------------------- /rpc/accounting.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/accounting" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client" 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/common" 7 | ) 8 | 9 | const serviceAccounting = serviceNamePrefix + "accounting.AccountingService" 10 | 11 | const ( 12 | rpcAccountingBalance = "Balance" 13 | ) 14 | 15 | // Balance executes AccountingService.Balance RPC. 16 | func Balance( 17 | cli *client.Client, 18 | req *accounting.BalanceRequest, 19 | opts ...client.CallOption, 20 | ) (*accounting.BalanceResponse, error) { 21 | resp := new(accounting.BalanceResponse) 22 | 23 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceAccounting, rpcAccountingBalance), req, resp, opts...) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | return resp, nil 29 | } 30 | -------------------------------------------------------------------------------- /rpc/client/call_options.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // CallOption is a messaging session option within Protobuf RPC. 8 | type CallOption func(*callParameters) 9 | 10 | type callParameters struct { 11 | ctx context.Context 12 | } 13 | 14 | func defaultCallParameters() *callParameters { 15 | return &callParameters{ 16 | ctx: context.Background(), 17 | } 18 | } 19 | 20 | // WithContext returns option to specify call context. If provided, all network 21 | // communications will be based on this context. Otherwise, context.Background() 22 | // is used. 23 | // 24 | // Context SHOULD NOT be nil. 25 | func WithContext(ctx context.Context) CallOption { 26 | return func(prm *callParameters) { 27 | prm.ctx = ctx 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /rpc/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "google.golang.org/grpc" 5 | "google.golang.org/grpc/credentials" 6 | ) 7 | 8 | // Client represents client for exchanging messages 9 | // with a remote server using Protobuf RPC. 10 | type Client struct { 11 | cfg 12 | } 13 | 14 | // New creates, configures via options and returns new Client instance. 15 | func New(opts ...Option) *Client { 16 | var c Client 17 | c.initDefault() 18 | 19 | for _, opt := range opts { 20 | opt(&c.cfg) 21 | } 22 | 23 | if c.tlsCfg != nil { 24 | c.grpcDialOpts = append(c.grpcDialOpts, grpc.WithTransportCredentials(credentials.NewTLS(c.tlsCfg))) 25 | } 26 | 27 | return &c 28 | } 29 | -------------------------------------------------------------------------------- /rpc/client/conn.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // Conn returns underlying connection. 8 | // 9 | // Returns non-nil result after the first Init() call 10 | // completed without a connection error. 11 | // 12 | // Client should not be used after Close() call 13 | // on the connection: behavior is undefined. 14 | func (c *Client) Conn() io.Closer { 15 | return c.conn 16 | } 17 | -------------------------------------------------------------------------------- /rpc/client/connect.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "net" 8 | "net/url" 9 | 10 | grpcstd "google.golang.org/grpc" 11 | ) 12 | 13 | var errInvalidEndpoint = errors.New("invalid endpoint options") 14 | 15 | func (c *Client) openGRPCConn(ctx context.Context) error { 16 | if c.conn != nil { 17 | return nil 18 | } 19 | 20 | if c.addr == "" { 21 | return errInvalidEndpoint 22 | } 23 | 24 | dialCtx, cancel := context.WithTimeout(ctx, c.dialTimeout) 25 | var err error 26 | 27 | c.conn, err = grpcstd.DialContext(dialCtx, c.addr, c.grpcDialOpts...) 28 | 29 | cancel() 30 | 31 | if err != nil { 32 | return fmt.Errorf("gRPC dial: %w", err) 33 | } 34 | 35 | return nil 36 | } 37 | 38 | // ParseURI parses s as address and returns a host and a flag 39 | // indicating that TLS is enabled. If multi-address is provided 40 | // the argument is returned unchanged. 41 | func ParseURI(s string) (string, bool, error) { 42 | uri, err := url.ParseRequestURI(s) 43 | if err != nil { 44 | return s, false, nil 45 | } 46 | 47 | // check if passed string was parsed correctly 48 | // URIs that do not start with a slash after the scheme are interpreted as: 49 | // `scheme:opaque` => if `opaque` is not empty, then it is supposed that URI 50 | // is in `host:port` format 51 | if uri.Host == "" { 52 | uri.Host = uri.Scheme 53 | uri.Scheme = grpcScheme // assume GRPC by default 54 | if uri.Opaque != "" { 55 | uri.Host = net.JoinHostPort(uri.Host, uri.Opaque) 56 | } 57 | } 58 | 59 | switch uri.Scheme { 60 | case grpcTLSScheme, grpcScheme: 61 | default: 62 | return "", false, fmt.Errorf("unsupported scheme: %s", uri.Scheme) 63 | } 64 | 65 | return uri.Host, uri.Scheme == grpcTLSScheme, nil 66 | } 67 | -------------------------------------------------------------------------------- /rpc/client/flows.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "sync" 7 | 8 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/common" 9 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 10 | ) 11 | 12 | // SendUnary initializes communication session by RPC info, performs unary RPC 13 | // and closes the session. 14 | func SendUnary(cli *Client, info common.CallMethodInfo, req, resp message.Message, opts ...CallOption) error { 15 | rw, err := cli.Init(info, opts...) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | err = rw.WriteMessage(req) 21 | if err != nil { 22 | return err 23 | } 24 | 25 | err = rw.ReadMessage(resp) 26 | if err != nil { 27 | return err 28 | } 29 | 30 | return rw.Close() 31 | } 32 | 33 | // MessageWriterCloser wraps MessageWriter 34 | // and io.Closer interfaces. 35 | type MessageWriterCloser interface { 36 | MessageWriter 37 | io.Closer 38 | } 39 | 40 | type clientStreamWriterCloser struct { 41 | MessageReadWriter 42 | 43 | resp message.Message 44 | } 45 | 46 | func (c *clientStreamWriterCloser) Close() error { 47 | err := c.MessageReadWriter.Close() 48 | if err != nil { 49 | return err 50 | } 51 | 52 | return c.ReadMessage(c.resp) 53 | } 54 | 55 | // OpenClientStream initializes communication session by RPC info, opens client-side stream 56 | // and returns its interface. 57 | // 58 | // All stream writes must be performed before the closing. Close must be called once. 59 | func OpenClientStream(cli *Client, info common.CallMethodInfo, resp message.Message, opts ...CallOption) (MessageWriterCloser, error) { 60 | rw, err := cli.Init(info, opts...) 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | return &clientStreamWriterCloser{ 66 | MessageReadWriter: rw, 67 | resp: resp, 68 | }, nil 69 | } 70 | 71 | // MessageReaderCloser wraps MessageReader 72 | // and io.Closer interface. 73 | type MessageReaderCloser interface { 74 | MessageReader 75 | io.Closer 76 | } 77 | 78 | type serverStreamReaderCloser struct { 79 | rw MessageReadWriter 80 | 81 | once sync.Once 82 | 83 | req message.Message 84 | } 85 | 86 | func (s *serverStreamReaderCloser) ReadMessage(msg message.Message) error { 87 | var err error 88 | 89 | s.once.Do(func() { 90 | err = s.rw.WriteMessage(s.req) 91 | }) 92 | 93 | if err != nil { 94 | return err 95 | } 96 | 97 | err = s.rw.ReadMessage(msg) 98 | if !errors.Is(err, io.EOF) { 99 | return err 100 | } 101 | 102 | err = s.rw.Close() 103 | if err != nil { 104 | return err 105 | } 106 | 107 | return io.EOF 108 | } 109 | 110 | // OpenServerStream initializes communication session by RPC info, opens server-side stream 111 | // and returns its interface. 112 | // 113 | // All stream reads must be performed before the closing. Close must be called once. 114 | func OpenServerStream(cli *Client, info common.CallMethodInfo, req message.Message, opts ...CallOption) (MessageReader, error) { 115 | rw, err := cli.Init(info, opts...) 116 | if err != nil { 117 | return nil, err 118 | } 119 | 120 | return &serverStreamReaderCloser{ 121 | rw: rw, 122 | req: req, 123 | }, nil 124 | } 125 | -------------------------------------------------------------------------------- /rpc/client/init.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "io" 6 | 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/common" 8 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 9 | "google.golang.org/grpc" 10 | ) 11 | 12 | // MessageReader is an interface of the Message reader. 13 | type MessageReader interface { 14 | // ReadMessage reads the next Message. 15 | // 16 | // Returns io.EOF if there are no more messages to read. 17 | // ReadMessage should not be called after io.EOF occasion. 18 | ReadMessage(message.Message) error 19 | } 20 | 21 | // MessageWriter is an interface of the Message writer. 22 | type MessageWriter interface { 23 | // WriteMessage writers the next Message. 24 | // 25 | // WriteMessage should not be called after any error. 26 | WriteMessage(message.Message) error 27 | } 28 | 29 | // MessageReadWriter is a component interface 30 | // for transmitting raw Protobuf messages. 31 | type MessageReadWriter interface { 32 | MessageReader 33 | MessageWriter 34 | 35 | // Closes the communication session. 36 | // 37 | // All calls to send/receive messages must be done before closing. 38 | io.Closer 39 | } 40 | 41 | // Init initiates a messaging session and returns the interface for message transmitting. 42 | func (c *Client) Init(info common.CallMethodInfo, opts ...CallOption) (MessageReadWriter, error) { 43 | prm := defaultCallParameters() 44 | 45 | for _, opt := range opts { 46 | opt(prm) 47 | } 48 | 49 | if err := c.openGRPCConn(prm.ctx); err != nil { 50 | return nil, err 51 | } 52 | 53 | ctx, cancel := context.WithCancel(prm.ctx) 54 | stream, err := c.conn.NewStream(ctx, &grpc.StreamDesc{ 55 | StreamName: info.Name, 56 | ServerStreams: info.ServerStream(), 57 | ClientStreams: info.ClientStream(), 58 | }, toMethodName(info)) 59 | if err != nil { 60 | cancel() 61 | return nil, err 62 | } 63 | 64 | return &streamWrapper{ 65 | ClientStream: stream, 66 | cancel: cancel, 67 | timeout: c.rwTimeout, 68 | }, nil 69 | } 70 | -------------------------------------------------------------------------------- /rpc/client/options.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "crypto/tls" 5 | "time" 6 | 7 | "google.golang.org/grpc" 8 | "google.golang.org/grpc/credentials/insecure" 9 | "google.golang.org/grpc/keepalive" 10 | ) 11 | 12 | const ( 13 | grpcScheme = "grpc" 14 | grpcTLSScheme = "grpcs" 15 | ) 16 | 17 | // Option is a Client's option. 18 | type Option func(*cfg) 19 | 20 | type cfg struct { 21 | addr string 22 | 23 | dialTimeout time.Duration 24 | rwTimeout time.Duration 25 | 26 | tlsCfg *tls.Config 27 | grpcDialOpts []grpc.DialOption 28 | 29 | conn *grpc.ClientConn 30 | } 31 | 32 | const ( 33 | defaultDialTimeout = 5 * time.Second 34 | defaultKeepAliveTimeout = 5 * time.Second 35 | defaultRWTimeout = 1 * time.Minute 36 | ) 37 | 38 | func (c *cfg) initDefault() { 39 | c.dialTimeout = defaultDialTimeout 40 | c.rwTimeout = defaultRWTimeout 41 | c.grpcDialOpts = []grpc.DialOption{ 42 | grpc.WithBlock(), 43 | grpc.WithTransportCredentials(insecure.NewCredentials()), 44 | grpc.WithKeepaliveParams(keepalive.ClientParameters{ 45 | Timeout: defaultKeepAliveTimeout, 46 | }), 47 | } 48 | } 49 | 50 | // WithNetworkAddress returns option to specify 51 | // network address of the remote server. 52 | // 53 | // Ignored if WithGRPCConn is provided. 54 | func WithNetworkAddress(v string) Option { 55 | return func(c *cfg) { 56 | if v != "" { 57 | c.addr = v 58 | } 59 | } 60 | } 61 | 62 | // WithNetworkURIAddress combines WithNetworkAddress and WithTLSCfg options 63 | // based on arguments. 64 | // 65 | // Do not use along with WithNetworkAddress and WithTLSCfg. 66 | // 67 | // Ignored if WithGRPCConn is provided. 68 | func WithNetworkURIAddress(addr string, tlsCfg *tls.Config) []Option { 69 | host, isTLS, err := ParseURI(addr) 70 | if err != nil { 71 | return nil 72 | } 73 | 74 | opts := make([]Option, 2) 75 | opts[0] = WithNetworkAddress(host) 76 | if isTLS { 77 | if tlsCfg == nil { 78 | tlsCfg = &tls.Config{} 79 | } 80 | opts[1] = WithTLSCfg(tlsCfg) 81 | } else { 82 | opts[1] = WithTLSCfg(nil) 83 | } 84 | 85 | return opts 86 | } 87 | 88 | // WithDialTimeout returns option to specify 89 | // dial timeout of the remote server connection. 90 | // 91 | // Ignored if WithGRPCConn is provided. 92 | func WithDialTimeout(v time.Duration) Option { 93 | return func(c *cfg) { 94 | if v > 0 { 95 | c.dialTimeout = v 96 | } 97 | } 98 | } 99 | 100 | // WithRWTimeout returns option to specify timeout 101 | // for reading and writing single gRPC message. 102 | func WithRWTimeout(v time.Duration) Option { 103 | return func(c *cfg) { 104 | if v > 0 { 105 | c.rwTimeout = v 106 | } 107 | } 108 | } 109 | 110 | // WithTLSCfg returns option to specify 111 | // TLS configuration. 112 | // 113 | // Ignored if WithGRPCConn is provided. 114 | func WithTLSCfg(v *tls.Config) Option { 115 | return func(c *cfg) { 116 | c.tlsCfg = v 117 | } 118 | } 119 | 120 | // WithGRPCConn returns option to specify 121 | // gRPC virtual connection. 122 | func WithGRPCConn(v *grpc.ClientConn) Option { 123 | return func(c *cfg) { 124 | if v != nil { 125 | c.conn = v 126 | } 127 | } 128 | } 129 | 130 | // WithGRPCDialOptions returns an option to specify grpc.DialOption. 131 | func WithGRPCDialOptions(opts []grpc.DialOption) Option { 132 | return func(c *cfg) { 133 | c.grpcDialOpts = append(c.grpcDialOpts, opts...) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /rpc/client/stream_wrapper.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 8 | "google.golang.org/grpc" 9 | ) 10 | 11 | type streamWrapper struct { 12 | grpc.ClientStream 13 | timeout time.Duration 14 | cancel context.CancelFunc 15 | } 16 | 17 | func (w streamWrapper) ReadMessage(m message.Message) error { 18 | // Can be optimized: we can create blank message here. 19 | gm := m.ToGRPCMessage() 20 | 21 | err := w.withTimeout(func() error { 22 | return w.ClientStream.RecvMsg(gm) 23 | }) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | return m.FromGRPCMessage(gm) 29 | } 30 | 31 | func (w streamWrapper) WriteMessage(m message.Message) error { 32 | return w.withTimeout(func() error { 33 | return w.ClientStream.SendMsg(m.ToGRPCMessage()) 34 | }) 35 | } 36 | 37 | func (w *streamWrapper) Close() error { 38 | return w.withTimeout(w.ClientStream.CloseSend) 39 | } 40 | 41 | func (w *streamWrapper) withTimeout(closure func() error) error { 42 | ch := make(chan error, 1) 43 | go func() { 44 | ch <- closure() 45 | close(ch) 46 | }() 47 | 48 | tt := time.NewTimer(w.timeout) 49 | 50 | select { 51 | case err := <-ch: 52 | tt.Stop() 53 | return err 54 | case <-tt.C: 55 | w.cancel() 56 | return context.DeadlineExceeded 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /rpc/client/util.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/common" 7 | ) 8 | 9 | const methodNameFmt = "/%s/%s" 10 | 11 | func toMethodName(p common.CallMethodInfo) string { 12 | return fmt.Sprintf(methodNameFmt, p.Service, p.Name) 13 | } 14 | -------------------------------------------------------------------------------- /rpc/common.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | const serviceNamePrefix = "neo.fs.v2." 4 | -------------------------------------------------------------------------------- /rpc/common/call.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | type callType uint8 4 | 5 | const ( 6 | _ callType = iota 7 | callUnary 8 | callClientStream 9 | callServerStream 10 | callBidirStream 11 | ) 12 | 13 | // CallMethodInfo is an information about the RPC. 14 | type CallMethodInfo struct { 15 | // Name of the service. 16 | Service string 17 | 18 | // Name of the RPC. 19 | Name string 20 | 21 | t callType 22 | } 23 | 24 | // ServerStream checks if CallMethodInfo contains 25 | // information about the server-side streaming RPC. 26 | func (c CallMethodInfo) ServerStream() bool { 27 | return c.t == callServerStream || c.t == callBidirStream 28 | } 29 | 30 | // ClientStream checks if CallMethodInfo contains 31 | // information about the client-side streaming RPC. 32 | func (c CallMethodInfo) ClientStream() bool { 33 | return c.t == callClientStream || c.t == callBidirStream 34 | } 35 | 36 | func (c *CallMethodInfo) setCommon(service, name string) { 37 | c.Service = service 38 | c.Name = name 39 | } 40 | 41 | // CallMethodInfoUnary returns CallMethodInfo structure 42 | // initialized for the unary RPC. 43 | func CallMethodInfoUnary(service, name string) (info CallMethodInfo) { 44 | info.setCommon(service, name) 45 | info.t = callUnary 46 | 47 | return 48 | } 49 | 50 | // CallMethodInfoClientStream returns CallMethodInfo structure 51 | // initialized for the client-side streaming RPC. 52 | func CallMethodInfoClientStream(service, name string) (info CallMethodInfo) { 53 | info.setCommon(service, name) 54 | info.t = callClientStream 55 | 56 | return 57 | } 58 | 59 | // CallMethodInfoServerStream returns CallMethodInfo structure 60 | // initialized for the server-side streaming RPC. 61 | func CallMethodInfoServerStream(service, name string) (info CallMethodInfo) { 62 | info.setCommon(service, name) 63 | info.t = callServerStream 64 | 65 | return 66 | } 67 | 68 | // CallMethodInfoBidirectionalStream returns CallMethodInfo structure 69 | // initialized for the bidirectional streaming RPC. 70 | func CallMethodInfoBidirectionalStream(service, name string) (info CallMethodInfo) { 71 | info.setCommon(service, name) 72 | info.t = callBidirStream 73 | 74 | return 75 | } 76 | -------------------------------------------------------------------------------- /rpc/common/call_test.go: -------------------------------------------------------------------------------- 1 | package common_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/common" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | const ( 11 | testServiceName = "test service" 12 | testRPCName = "test RPC" 13 | ) 14 | 15 | func TestCallMethodInfoUnary(t *testing.T) { 16 | i := common.CallMethodInfoUnary(testServiceName, testRPCName) 17 | 18 | require.Equal(t, testServiceName, i.Service) 19 | require.Equal(t, testRPCName, i.Name) 20 | require.False(t, i.ClientStream()) 21 | require.False(t, i.ServerStream()) 22 | } 23 | 24 | func TestCallMethodInfoServerStream(t *testing.T) { 25 | i := common.CallMethodInfoServerStream(testServiceName, testRPCName) 26 | 27 | require.Equal(t, testServiceName, i.Service) 28 | require.Equal(t, testRPCName, i.Name) 29 | require.False(t, i.ClientStream()) 30 | require.True(t, i.ServerStream()) 31 | } 32 | 33 | func TestCallMethodInfoClientStream(t *testing.T) { 34 | i := common.CallMethodInfoClientStream(testServiceName, testRPCName) 35 | 36 | require.Equal(t, testServiceName, i.Service) 37 | require.Equal(t, testRPCName, i.Name) 38 | require.True(t, i.ClientStream()) 39 | require.False(t, i.ServerStream()) 40 | } 41 | 42 | func TestCallMethodInfoBidirectionalStream(t *testing.T) { 43 | i := common.CallMethodInfoBidirectionalStream(testServiceName, testRPCName) 44 | 45 | require.Equal(t, testServiceName, i.Service) 46 | require.Equal(t, testRPCName, i.Name) 47 | require.True(t, i.ClientStream()) 48 | require.True(t, i.ServerStream()) 49 | } 50 | -------------------------------------------------------------------------------- /rpc/container.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/container" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client" 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/common" 7 | ) 8 | 9 | const serviceContainer = serviceNamePrefix + "container.ContainerService" 10 | 11 | const ( 12 | rpcContainerPut = "Put" 13 | rpcContainerGet = "Get" 14 | rpcContainerDel = "Delete" 15 | rpcContainerList = "List" 16 | rpcContainerSetEACL = "SetExtendedACL" 17 | rpcContainerGetEACL = "GetExtendedACL" 18 | rpcContainerUsedSpace = "AnnounceUsedSpace" 19 | ) 20 | 21 | // PutContainer executes ContainerService.Put RPC. 22 | func PutContainer( 23 | cli *client.Client, 24 | req *container.PutRequest, 25 | opts ...client.CallOption, 26 | ) (*container.PutResponse, error) { 27 | resp := new(container.PutResponse) 28 | 29 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceContainer, rpcContainerPut), req, resp, opts...) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | return resp, nil 35 | } 36 | 37 | // GetContainer executes ContainerService.Get RPC. 38 | func GetContainer( 39 | cli *client.Client, 40 | req *container.GetRequest, 41 | opts ...client.CallOption, 42 | ) (*container.GetResponse, error) { 43 | resp := new(container.GetResponse) 44 | 45 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceContainer, rpcContainerGet), req, resp, opts...) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | return resp, nil 51 | } 52 | 53 | // DeleteContainer executes ContainerService.Delete RPC. 54 | func DeleteContainer( 55 | cli *client.Client, 56 | req *container.DeleteRequest, 57 | opts ...client.CallOption, 58 | ) (*container.PutResponse, error) { 59 | resp := new(container.PutResponse) 60 | 61 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceContainer, rpcContainerDel), req, resp, opts...) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | return resp, nil 67 | } 68 | 69 | // ListContainers executes ContainerService.List RPC. 70 | func ListContainers( 71 | cli *client.Client, 72 | req *container.ListRequest, 73 | opts ...client.CallOption, 74 | ) (*container.ListResponse, error) { 75 | resp := new(container.ListResponse) 76 | 77 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceContainer, rpcContainerList), req, resp, opts...) 78 | if err != nil { 79 | return nil, err 80 | } 81 | 82 | return resp, nil 83 | } 84 | 85 | // SetEACL executes ContainerService.SetExtendedACL RPC. 86 | func SetEACL( 87 | cli *client.Client, 88 | req *container.SetExtendedACLRequest, 89 | opts ...client.CallOption, 90 | ) (*container.PutResponse, error) { 91 | resp := new(container.PutResponse) 92 | 93 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceContainer, rpcContainerSetEACL), req, resp, opts...) 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | return resp, nil 99 | } 100 | 101 | // GetEACL executes ContainerService.GetExtendedACL RPC. 102 | func GetEACL( 103 | cli *client.Client, 104 | req *container.GetExtendedACLRequest, 105 | opts ...client.CallOption, 106 | ) (*container.GetExtendedACLResponse, error) { 107 | resp := new(container.GetExtendedACLResponse) 108 | 109 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceContainer, rpcContainerGetEACL), req, resp, opts...) 110 | if err != nil { 111 | return nil, err 112 | } 113 | 114 | return resp, nil 115 | } 116 | 117 | // AnnounceUsedSpace executes ContainerService.AnnounceUsedSpace RPC. 118 | func AnnounceUsedSpace( 119 | cli *client.Client, 120 | req *container.AnnounceUsedSpaceRequest, 121 | opts ...client.CallOption, 122 | ) (*container.PutResponse, error) { 123 | resp := new(container.PutResponse) 124 | 125 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceContainer, rpcContainerUsedSpace), req, resp, opts...) 126 | if err != nil { 127 | return nil, err 128 | } 129 | 130 | return resp, nil 131 | } 132 | -------------------------------------------------------------------------------- /rpc/grpc/init.go: -------------------------------------------------------------------------------- 1 | package grpc 2 | 3 | // Message represents raw gRPC message. 4 | type Message interface{} 5 | -------------------------------------------------------------------------------- /rpc/message/encoding.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/grpc" 5 | "google.golang.org/protobuf/encoding/protojson" 6 | "google.golang.org/protobuf/proto" 7 | ) 8 | 9 | // GRPCConvertedMessage is an interface 10 | // of the gRPC message that is used 11 | // for Message encoding/decoding. 12 | type GRPCConvertedMessage interface { 13 | grpc.Message 14 | proto.Message 15 | } 16 | 17 | // Unmarshal decodes m from its Protobuf binary representation 18 | // via related gRPC message. 19 | // 20 | // gm should be tof the same type as the m.ToGRPCMessage() return. 21 | func Unmarshal(m Message, data []byte, gm GRPCConvertedMessage) error { 22 | if err := proto.Unmarshal(data, gm); err != nil { 23 | return err 24 | } 25 | 26 | return m.FromGRPCMessage(gm) 27 | } 28 | 29 | // MarshalJSON encodes m to Protobuf JSON representation. 30 | func MarshalJSON(m Message) ([]byte, error) { 31 | return protojson.MarshalOptions{ 32 | EmitUnpopulated: true, 33 | }.Marshal( 34 | m.ToGRPCMessage().(proto.Message), 35 | ) 36 | } 37 | 38 | // UnmarshalJSON decodes m from its Protobuf JSON representation 39 | // via related gRPC message. 40 | // 41 | // gm should be tof the same type as the m.ToGRPCMessage() return. 42 | func UnmarshalJSON(m Message, data []byte, gm GRPCConvertedMessage) error { 43 | if err := protojson.Unmarshal(data, gm); err != nil { 44 | return err 45 | } 46 | 47 | return m.FromGRPCMessage(gm) 48 | } 49 | -------------------------------------------------------------------------------- /rpc/message/message.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/grpc" 7 | ) 8 | 9 | // Message represents raw Protobuf message 10 | // that can be transmitted via several 11 | // transport protocols. 12 | type Message interface { 13 | // Must return gRPC message that can 14 | // be used for gRPC protocol transmission. 15 | ToGRPCMessage() grpc.Message 16 | 17 | // Must restore the message from related 18 | // gRPC message. 19 | // 20 | // If gRPC message is not a related one, 21 | // ErrUnexpectedMessageType can be returned 22 | // to indicate this. 23 | FromGRPCMessage(grpc.Message) error 24 | } 25 | 26 | // ErrUnexpectedMessageType is an error that 27 | // is used to indicate message mismatch. 28 | type ErrUnexpectedMessageType struct { 29 | exp, act interface{} 30 | } 31 | 32 | // NewUnexpectedMessageType initializes an error about message mismatch 33 | // between act and exp. 34 | func NewUnexpectedMessageType(act, exp interface{}) ErrUnexpectedMessageType { 35 | return ErrUnexpectedMessageType{ 36 | exp: exp, 37 | act: act, 38 | } 39 | } 40 | 41 | func (e ErrUnexpectedMessageType) Error() string { 42 | return fmt.Sprintf("unexpected message type %T: expected %T", e.act, e.exp) 43 | } 44 | -------------------------------------------------------------------------------- /rpc/message/test/message.go: -------------------------------------------------------------------------------- 1 | package messagetest 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "testing" 8 | 9 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | type jsonMessage interface { 14 | json.Marshaler 15 | json.Unmarshaler 16 | } 17 | 18 | type binaryMessage interface { 19 | StableMarshal([]byte) []byte 20 | Unmarshal([]byte) error 21 | } 22 | 23 | func TestRPCMessage(t *testing.T, msgGens ...func(empty bool) message.Message) { 24 | for _, msgGen := range msgGens { 25 | msg := msgGen(false) 26 | 27 | t.Run(fmt.Sprintf("convert_%T", msg), func(t *testing.T) { 28 | msg := msgGen(false) 29 | 30 | err := msg.FromGRPCMessage(100) 31 | 32 | require.True(t, errors.As(err, new(message.ErrUnexpectedMessageType))) 33 | 34 | msg2 := msgGen(true) 35 | 36 | err = msg2.FromGRPCMessage(msg.ToGRPCMessage()) 37 | require.NoError(t, err) 38 | 39 | require.Equal(t, msg, msg2) 40 | }) 41 | 42 | t.Run("encoding", func(t *testing.T) { 43 | if jm, ok := msg.(jsonMessage); ok { 44 | t.Run(fmt.Sprintf("JSON_%T", msg), func(t *testing.T) { 45 | data, err := jm.MarshalJSON() 46 | require.NoError(t, err) 47 | 48 | jm2 := msgGen(true).(jsonMessage) 49 | require.NoError(t, jm2.UnmarshalJSON(data)) 50 | 51 | require.Equal(t, jm, jm2) 52 | }) 53 | } 54 | 55 | if bm, ok := msg.(binaryMessage); ok { 56 | t.Run(fmt.Sprintf("Binary_%T", msg), func(t *testing.T) { 57 | data := bm.StableMarshal(nil) 58 | 59 | bm2 := msgGen(true).(binaryMessage) 60 | require.NoError(t, bm2.Unmarshal(data)) 61 | 62 | require.Equal(t, bm, bm2) 63 | }) 64 | } 65 | }) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /rpc/netmap.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/netmap" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client" 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/common" 7 | ) 8 | 9 | const serviceNetmap = serviceNamePrefix + "netmap.NetmapService" 10 | 11 | const ( 12 | rpcNetmapNodeInfo = "LocalNodeInfo" 13 | rpcNetmapNetInfo = "NetworkInfo" 14 | rpcNetmapSnapshot = "NetmapSnapshot" 15 | ) 16 | 17 | // LocalNodeInfo executes NetmapService.LocalNodeInfo RPC. 18 | func LocalNodeInfo( 19 | cli *client.Client, 20 | req *netmap.LocalNodeInfoRequest, 21 | opts ...client.CallOption, 22 | ) (*netmap.LocalNodeInfoResponse, error) { 23 | resp := new(netmap.LocalNodeInfoResponse) 24 | 25 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceNetmap, rpcNetmapNodeInfo), req, resp, opts...) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | return resp, nil 31 | } 32 | 33 | // NetworkInfo executes NetmapService.NetworkInfo RPC. 34 | func NetworkInfo( 35 | cli *client.Client, 36 | req *netmap.NetworkInfoRequest, 37 | opts ...client.CallOption, 38 | ) (*netmap.NetworkInfoResponse, error) { 39 | resp := new(netmap.NetworkInfoResponse) 40 | 41 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceNetmap, rpcNetmapNetInfo), req, resp, opts...) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | return resp, nil 47 | } 48 | 49 | // NetMapSnapshot executes NetmapService.NetmapSnapshot RPC. 50 | func NetMapSnapshot( 51 | cli *client.Client, 52 | req *netmap.SnapshotRequest, 53 | opts ...client.CallOption, 54 | ) (*netmap.SnapshotResponse, error) { 55 | resp := new(netmap.SnapshotResponse) 56 | 57 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceNetmap, rpcNetmapSnapshot), req, resp, opts...) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | return resp, nil 63 | } 64 | -------------------------------------------------------------------------------- /rpc/reputation.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/reputation" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client" 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/common" 7 | ) 8 | 9 | const serviceReputation = serviceNamePrefix + "reputation.ReputationService" 10 | 11 | const ( 12 | rpcReputationAnnounceLocalTrust = "AnnounceLocalTrust" 13 | rpcReputationAnnounceIntermediateResult = "AnnounceIntermediateResult" 14 | ) 15 | 16 | // AnnounceLocalTrust executes ReputationService.AnnounceLocalTrust RPC. 17 | func AnnounceLocalTrust( 18 | cli *client.Client, 19 | req *reputation.AnnounceLocalTrustRequest, 20 | opts ...client.CallOption, 21 | ) (*reputation.AnnounceLocalTrustResponse, error) { 22 | resp := new(reputation.AnnounceLocalTrustResponse) 23 | 24 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceReputation, rpcReputationAnnounceLocalTrust), req, resp, opts...) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | return resp, nil 30 | } 31 | 32 | // AnnounceIntermediateResult executes ReputationService.AnnounceIntermediateResult RPC. 33 | func AnnounceIntermediateResult( 34 | cli *client.Client, 35 | req *reputation.AnnounceIntermediateResultRequest, 36 | opts ...client.CallOption, 37 | ) (*reputation.AnnounceIntermediateResultResponse, error) { 38 | resp := new(reputation.AnnounceIntermediateResultResponse) 39 | 40 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceReputation, rpcReputationAnnounceIntermediateResult), req, resp, opts...) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | return resp, nil 46 | } 47 | -------------------------------------------------------------------------------- /rpc/session.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/client" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/common" 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/session" 7 | ) 8 | 9 | const serviceSession = serviceNamePrefix + "session.SessionService" 10 | 11 | const ( 12 | rpcSessionCreate = "Create" 13 | ) 14 | 15 | func CreateSession( 16 | cli *client.Client, 17 | req *session.CreateRequest, 18 | opts ...client.CallOption, 19 | ) (*session.CreateResponse, error) { 20 | resp := new(session.CreateResponse) 21 | 22 | err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceSession, rpcSessionCreate), req, resp, opts...) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return resp, nil 28 | } 29 | -------------------------------------------------------------------------------- /session/grpc/client.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "google.golang.org/grpc" 8 | ) 9 | 10 | // Client wraps SessionServiceClient 11 | // with pre-defined configurations. 12 | type Client struct { 13 | *cfg 14 | 15 | client SessionServiceClient 16 | } 17 | 18 | // Option represents Client option. 19 | type Option func(*cfg) 20 | 21 | type cfg struct { 22 | callOpts []grpc.CallOption 23 | } 24 | 25 | // ErrNilSessionServiceClient is returned by functions that expect 26 | // a non-nil SessionServiceClient, but received nil. 27 | var ErrNilSessionServiceClient = errors.New("session gRPC client is nil") 28 | 29 | func defaultCfg() *cfg { 30 | return new(cfg) 31 | } 32 | 33 | // NewClient creates, initializes and returns a new Client instance. 34 | // 35 | // Options are applied one by one in order. 36 | func NewClient(c SessionServiceClient, opts ...Option) (*Client, error) { 37 | if c == nil { 38 | return nil, ErrNilSessionServiceClient 39 | } 40 | 41 | cfg := defaultCfg() 42 | for i := range opts { 43 | opts[i](cfg) 44 | } 45 | 46 | return &Client{ 47 | cfg: cfg, 48 | client: c, 49 | }, nil 50 | } 51 | 52 | func (c *Client) Create(ctx context.Context, req *CreateRequest) (*CreateResponse, error) { 53 | return c.client.Create(ctx, req, c.callOpts...) 54 | } 55 | 56 | // WithCallOptions returns Option that configures 57 | // Client to attach call options to each rpc call. 58 | func WithCallOptions(opts []grpc.CallOption) Option { 59 | return func(c *cfg) { 60 | c.callOpts = opts 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /session/grpc/service.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | import ( 4 | refs "github.com/TrueCloudLab/frostfs-api-go/v2/refs/grpc" 5 | ) 6 | 7 | // SetOwnerId sets identifier of the session initiator. 8 | func (m *CreateRequest_Body) SetOwnerId(v *refs.OwnerID) { 9 | m.OwnerId = v 10 | } 11 | 12 | // SetExpiration sets lifetime of the session. 13 | func (m *CreateRequest_Body) SetExpiration(v uint64) { 14 | m.Expiration = v 15 | } 16 | 17 | // SetBody sets body of the request. 18 | func (m *CreateRequest) SetBody(v *CreateRequest_Body) { 19 | m.Body = v 20 | } 21 | 22 | // SetMetaHeader sets meta header of the request. 23 | func (m *CreateRequest) SetMetaHeader(v *RequestMetaHeader) { 24 | m.MetaHeader = v 25 | } 26 | 27 | // SetVerifyHeader sets verification header of the request. 28 | func (m *CreateRequest) SetVerifyHeader(v *RequestVerificationHeader) { 29 | m.VerifyHeader = v 30 | } 31 | 32 | // SetId sets identifier of the session token. 33 | func (m *CreateResponse_Body) SetId(v []byte) { 34 | m.Id = v 35 | } 36 | 37 | // SetSessionKey sets session public key in a binary format. 38 | func (m *CreateResponse_Body) SetSessionKey(v []byte) { 39 | m.SessionKey = v 40 | } 41 | 42 | // SetBody sets body of the response. 43 | func (m *CreateResponse) SetBody(v *CreateResponse_Body) { 44 | m.Body = v 45 | } 46 | 47 | // SetMetaHeader sets meta header of the response. 48 | func (m *CreateResponse) SetMetaHeader(v *ResponseMetaHeader) { 49 | m.MetaHeader = v 50 | } 51 | 52 | // SetVerifyHeader sets verification header of the response. 53 | func (m *CreateResponse) SetVerifyHeader(v *ResponseVerificationHeader) { 54 | m.VerifyHeader = v 55 | } 56 | -------------------------------------------------------------------------------- /session/grpc/service_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.2.0 4 | // - protoc v3.21.9 5 | // source: session/grpc/service.proto 6 | 7 | package session 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | // SessionServiceClient is the client API for SessionService service. 22 | // 23 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 24 | type SessionServiceClient interface { 25 | // Open a new session between two peers. 26 | // 27 | // Statuses: 28 | // - **OK** (0, SECTION_SUCCESS): 29 | // session has been successfully opened; 30 | // - Common failures (SECTION_FAILURE_COMMON). 31 | Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) 32 | } 33 | 34 | type sessionServiceClient struct { 35 | cc grpc.ClientConnInterface 36 | } 37 | 38 | func NewSessionServiceClient(cc grpc.ClientConnInterface) SessionServiceClient { 39 | return &sessionServiceClient{cc} 40 | } 41 | 42 | func (c *sessionServiceClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) { 43 | out := new(CreateResponse) 44 | err := c.cc.Invoke(ctx, "/neo.fs.v2.session.SessionService/Create", in, out, opts...) 45 | if err != nil { 46 | return nil, err 47 | } 48 | return out, nil 49 | } 50 | 51 | // SessionServiceServer is the server API for SessionService service. 52 | // All implementations should embed UnimplementedSessionServiceServer 53 | // for forward compatibility 54 | type SessionServiceServer interface { 55 | // Open a new session between two peers. 56 | // 57 | // Statuses: 58 | // - **OK** (0, SECTION_SUCCESS): 59 | // session has been successfully opened; 60 | // - Common failures (SECTION_FAILURE_COMMON). 61 | Create(context.Context, *CreateRequest) (*CreateResponse, error) 62 | } 63 | 64 | // UnimplementedSessionServiceServer should be embedded to have forward compatible implementations. 65 | type UnimplementedSessionServiceServer struct { 66 | } 67 | 68 | func (UnimplementedSessionServiceServer) Create(context.Context, *CreateRequest) (*CreateResponse, error) { 69 | return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") 70 | } 71 | 72 | // UnsafeSessionServiceServer may be embedded to opt out of forward compatibility for this service. 73 | // Use of this interface is not recommended, as added methods to SessionServiceServer will 74 | // result in compilation errors. 75 | type UnsafeSessionServiceServer interface { 76 | mustEmbedUnimplementedSessionServiceServer() 77 | } 78 | 79 | func RegisterSessionServiceServer(s grpc.ServiceRegistrar, srv SessionServiceServer) { 80 | s.RegisterService(&SessionService_ServiceDesc, srv) 81 | } 82 | 83 | func _SessionService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 84 | in := new(CreateRequest) 85 | if err := dec(in); err != nil { 86 | return nil, err 87 | } 88 | if interceptor == nil { 89 | return srv.(SessionServiceServer).Create(ctx, in) 90 | } 91 | info := &grpc.UnaryServerInfo{ 92 | Server: srv, 93 | FullMethod: "/neo.fs.v2.session.SessionService/Create", 94 | } 95 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 96 | return srv.(SessionServiceServer).Create(ctx, req.(*CreateRequest)) 97 | } 98 | return interceptor(ctx, in, info, handler) 99 | } 100 | 101 | // SessionService_ServiceDesc is the grpc.ServiceDesc for SessionService service. 102 | // It's only intended for direct use with grpc.RegisterService, 103 | // and not to be introspected or modified (even as a copy) 104 | var SessionService_ServiceDesc = grpc.ServiceDesc{ 105 | ServiceName: "neo.fs.v2.session.SessionService", 106 | HandlerType: (*SessionServiceServer)(nil), 107 | Methods: []grpc.MethodDesc{ 108 | { 109 | MethodName: "Create", 110 | Handler: _SessionService_Create_Handler, 111 | }, 112 | }, 113 | Streams: []grpc.StreamDesc{}, 114 | Metadata: "session/grpc/service.proto", 115 | } 116 | -------------------------------------------------------------------------------- /session/json.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 5 | session "github.com/TrueCloudLab/frostfs-api-go/v2/session/grpc" 6 | "google.golang.org/protobuf/encoding/protojson" 7 | ) 8 | 9 | func (c *ObjectSessionContext) MarshalJSON() ([]byte, error) { 10 | return message.MarshalJSON(c) 11 | } 12 | 13 | func (c *ObjectSessionContext) UnmarshalJSON(data []byte) error { 14 | return message.UnmarshalJSON(c, data, new(session.ObjectSessionContext)) 15 | } 16 | 17 | func (l *TokenLifetime) MarshalJSON() ([]byte, error) { 18 | return message.MarshalJSON(l) 19 | } 20 | 21 | func (l *TokenLifetime) UnmarshalJSON(data []byte) error { 22 | return message.UnmarshalJSON(l, data, new(session.SessionToken_Body_TokenLifetime)) 23 | } 24 | 25 | func (t *TokenBody) MarshalJSON() ([]byte, error) { 26 | return message.MarshalJSON(t) 27 | } 28 | 29 | func (t *TokenBody) UnmarshalJSON(data []byte) error { 30 | msg := new(session.SessionToken_Body) 31 | 32 | if err := protojson.Unmarshal(data, msg); err != nil { 33 | return err 34 | } 35 | 36 | return t.FromGRPCMessage(msg) 37 | } 38 | 39 | func (t *Token) MarshalJSON() ([]byte, error) { 40 | return message.MarshalJSON(t) 41 | } 42 | 43 | func (t *Token) UnmarshalJSON(data []byte) error { 44 | msg := new(session.SessionToken) 45 | 46 | if err := protojson.Unmarshal(data, msg); err != nil { 47 | return err 48 | } 49 | 50 | return t.FromGRPCMessage(msg) 51 | } 52 | 53 | func (x *XHeader) MarshalJSON() ([]byte, error) { 54 | return message.MarshalJSON(x) 55 | } 56 | 57 | func (x *XHeader) UnmarshalJSON(data []byte) error { 58 | msg := new(session.XHeader) 59 | 60 | if err := protojson.Unmarshal(data, msg); err != nil { 61 | return err 62 | } 63 | 64 | return x.FromGRPCMessage(msg) 65 | } 66 | 67 | func (r *RequestMetaHeader) MarshalJSON() ([]byte, error) { 68 | return message.MarshalJSON(r) 69 | } 70 | 71 | func (r *RequestMetaHeader) UnmarshalJSON(data []byte) error { 72 | msg := new(session.RequestMetaHeader) 73 | 74 | if err := protojson.Unmarshal(data, msg); err != nil { 75 | return err 76 | } 77 | 78 | return r.FromGRPCMessage(msg) 79 | } 80 | 81 | func (r *RequestVerificationHeader) MarshalJSON() ([]byte, error) { 82 | return message.MarshalJSON(r) 83 | } 84 | 85 | func (r *RequestVerificationHeader) UnmarshalJSON(data []byte) error { 86 | msg := new(session.RequestVerificationHeader) 87 | 88 | if err := protojson.Unmarshal(data, msg); err != nil { 89 | return err 90 | } 91 | 92 | return r.FromGRPCMessage(msg) 93 | } 94 | 95 | func (r *ResponseMetaHeader) MarshalJSON() ([]byte, error) { 96 | return message.MarshalJSON(r) 97 | } 98 | 99 | func (r *ResponseMetaHeader) UnmarshalJSON(data []byte) error { 100 | msg := new(session.ResponseMetaHeader) 101 | 102 | if err := protojson.Unmarshal(data, msg); err != nil { 103 | return err 104 | } 105 | 106 | return r.FromGRPCMessage(msg) 107 | } 108 | 109 | func (r *ResponseVerificationHeader) MarshalJSON() ([]byte, error) { 110 | return message.MarshalJSON(r) 111 | } 112 | 113 | func (r *ResponseVerificationHeader) UnmarshalJSON(data []byte) error { 114 | msg := new(session.ResponseVerificationHeader) 115 | 116 | if err := protojson.Unmarshal(data, msg); err != nil { 117 | return err 118 | } 119 | 120 | return r.FromGRPCMessage(msg) 121 | } 122 | 123 | func (x *ContainerSessionContext) MarshalJSON() ([]byte, error) { 124 | return message.MarshalJSON(x) 125 | } 126 | 127 | func (x *ContainerSessionContext) UnmarshalJSON(data []byte) error { 128 | return message.UnmarshalJSON(x, data, new(session.ContainerSessionContext)) 129 | } 130 | -------------------------------------------------------------------------------- /session/message_test.go: -------------------------------------------------------------------------------- 1 | package session_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 7 | rpctest "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message/test" 8 | sessiontest "github.com/TrueCloudLab/frostfs-api-go/v2/session/test" 9 | ) 10 | 11 | func TestMessageConvert(t *testing.T) { 12 | rpctest.TestRPCMessage(t, 13 | func(empty bool) message.Message { return sessiontest.GenerateCreateRequestBody(empty) }, 14 | func(empty bool) message.Message { return sessiontest.GenerateCreateRequest(empty) }, 15 | func(empty bool) message.Message { return sessiontest.GenerateCreateResponseBody(empty) }, 16 | func(empty bool) message.Message { return sessiontest.GenerateCreateResponse(empty) }, 17 | func(empty bool) message.Message { return sessiontest.GenerateTokenLifetime(empty) }, 18 | func(empty bool) message.Message { return sessiontest.GenerateXHeader(empty) }, 19 | func(empty bool) message.Message { return sessiontest.GenerateSessionTokenBody(empty) }, 20 | func(empty bool) message.Message { return sessiontest.GenerateSessionToken(empty) }, 21 | func(empty bool) message.Message { return sessiontest.GenerateRequestMetaHeader(empty) }, 22 | func(empty bool) message.Message { return sessiontest.GenerateRequestVerificationHeader(empty) }, 23 | func(empty bool) message.Message { return sessiontest.GenerateResponseMetaHeader(empty) }, 24 | func(empty bool) message.Message { return sessiontest.GenerateResponseVerificationHeader(empty) }, 25 | func(empty bool) message.Message { return sessiontest.GenerateContainerSessionContext(empty) }, 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /session/status.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/status" 5 | statusgrpc "github.com/TrueCloudLab/frostfs-api-go/v2/status/grpc" 6 | ) 7 | 8 | // LocalizeFailStatus checks if passed global status.Code is related to session failure and: 9 | // 10 | // then localizes the code and returns true, 11 | // else leaves the code unchanged and returns false. 12 | // 13 | // Arg must not be nil. 14 | func LocalizeFailStatus(c *status.Code) bool { 15 | return status.LocalizeIfInSection(c, uint32(statusgrpc.Section_SECTION_SESSION)) 16 | } 17 | 18 | // GlobalizeFail globalizes local code of session failure. 19 | // 20 | // Arg must not be nil. 21 | func GlobalizeFail(c *status.Code) { 22 | c.GlobalizeSection(uint32(statusgrpc.Section_SECTION_SESSION)) 23 | } 24 | 25 | const ( 26 | // StatusTokenNotFound is a local status.Code value for 27 | // TOKEN_NOT_FOUND session failure. 28 | StatusTokenNotFound status.Code = iota 29 | // StatusTokenExpired is a local status.Code value for 30 | // TOKEN_EXPIRED session failure. 31 | StatusTokenExpired 32 | ) 33 | -------------------------------------------------------------------------------- /session/status_test.go: -------------------------------------------------------------------------------- 1 | package session_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/session" 7 | statustest "github.com/TrueCloudLab/frostfs-api-go/v2/status/test" 8 | ) 9 | 10 | func TestStatusCodes(t *testing.T) { 11 | statustest.TestCodes(t, session.LocalizeFailStatus, session.GlobalizeFail, 12 | session.StatusTokenNotFound, 4096, 13 | session.StatusTokenExpired, 4097, 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /session/string.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | import ( 4 | session "github.com/TrueCloudLab/frostfs-api-go/v2/session/grpc" 5 | ) 6 | 7 | // String returns string representation of ObjectSessionVerb. 8 | func (x ObjectSessionVerb) String() string { 9 | return ObjectSessionVerbToGRPCField(x).String() 10 | } 11 | 12 | // FromString parses ObjectSessionVerb from a string representation. 13 | // It is a reverse action to String(). 14 | // 15 | // Returns true if s was parsed successfully. 16 | func (x *ObjectSessionVerb) FromString(s string) bool { 17 | var g session.ObjectSessionContext_Verb 18 | 19 | ok := g.FromString(s) 20 | 21 | if ok { 22 | *x = ObjectSessionVerbFromGRPCField(g) 23 | } 24 | 25 | return ok 26 | } 27 | 28 | // String returns string representation of ContainerSessionVerb. 29 | func (x ContainerSessionVerb) String() string { 30 | return ContainerSessionVerbToGRPCField(x).String() 31 | } 32 | 33 | // FromString parses ContainerSessionVerb from a string representation. 34 | // It is a reverse action to String(). 35 | // 36 | // Returns true if s was parsed successfully. 37 | func (x *ContainerSessionVerb) FromString(s string) bool { 38 | var g session.ContainerSessionContext_Verb 39 | 40 | ok := g.FromString(s) 41 | 42 | if ok { 43 | *x = ContainerSessionVerbFromGRPCField(g) 44 | } 45 | 46 | return ok 47 | } 48 | -------------------------------------------------------------------------------- /session/util.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | import ( 4 | session "github.com/TrueCloudLab/frostfs-api-go/v2/session/grpc" 5 | ) 6 | 7 | // RequestHeaders represents common part of 8 | // all NeoFS requests including headers. 9 | type RequestHeaders struct { 10 | metaHeader *RequestMetaHeader 11 | 12 | verifyHeader *RequestVerificationHeader 13 | } 14 | 15 | // GetMetaHeader returns meta header of the request. 16 | func (c *RequestHeaders) GetMetaHeader() *RequestMetaHeader { 17 | if c != nil { 18 | return c.metaHeader 19 | } 20 | 21 | return nil 22 | } 23 | 24 | // SetMetaHeader sets meta header of the request. 25 | func (c *RequestHeaders) SetMetaHeader(v *RequestMetaHeader) { 26 | c.metaHeader = v 27 | } 28 | 29 | // GetVerificationHeader returns verification header of the request. 30 | func (c *RequestHeaders) GetVerificationHeader() *RequestVerificationHeader { 31 | if c != nil { 32 | return c.verifyHeader 33 | } 34 | 35 | return nil 36 | } 37 | 38 | // SetVerificationHeader sets verification header of the request. 39 | func (c *RequestHeaders) SetVerificationHeader(v *RequestVerificationHeader) { 40 | c.verifyHeader = v 41 | } 42 | 43 | func (c *RequestHeaders) ToMessage(m interface { 44 | SetMetaHeader(*session.RequestMetaHeader) 45 | SetVerifyHeader(*session.RequestVerificationHeader) 46 | }) { 47 | m.SetMetaHeader(c.metaHeader.ToGRPCMessage().(*session.RequestMetaHeader)) 48 | m.SetVerifyHeader(c.verifyHeader.ToGRPCMessage().(*session.RequestVerificationHeader)) 49 | } 50 | 51 | func (c *RequestHeaders) FromMessage(m interface { 52 | GetMetaHeader() *session.RequestMetaHeader 53 | GetVerifyHeader() *session.RequestVerificationHeader 54 | }) error { 55 | metaHdr := m.GetMetaHeader() 56 | if metaHdr == nil { 57 | c.metaHeader = nil 58 | } else { 59 | if c.metaHeader == nil { 60 | c.metaHeader = new(RequestMetaHeader) 61 | } 62 | 63 | err := c.metaHeader.FromGRPCMessage(metaHdr) 64 | if err != nil { 65 | return err 66 | } 67 | } 68 | 69 | verifyHdr := m.GetVerifyHeader() 70 | if verifyHdr == nil { 71 | c.verifyHeader = nil 72 | } else { 73 | if c.verifyHeader == nil { 74 | c.verifyHeader = new(RequestVerificationHeader) 75 | } 76 | 77 | err := c.verifyHeader.FromGRPCMessage(verifyHdr) 78 | if err != nil { 79 | return err 80 | } 81 | } 82 | 83 | return nil 84 | } 85 | 86 | // ResponseHeaders represents common part of 87 | // all NeoFS responses including headers. 88 | type ResponseHeaders struct { 89 | metaHeader *ResponseMetaHeader 90 | 91 | verifyHeader *ResponseVerificationHeader 92 | } 93 | 94 | // GetMetaHeader returns meta header of the response. 95 | func (c *ResponseHeaders) GetMetaHeader() *ResponseMetaHeader { 96 | if c != nil { 97 | return c.metaHeader 98 | } 99 | 100 | return nil 101 | } 102 | 103 | // SetMetaHeader sets meta header of the response. 104 | func (c *ResponseHeaders) SetMetaHeader(v *ResponseMetaHeader) { 105 | c.metaHeader = v 106 | } 107 | 108 | // GetVerificationHeader returns verification header of the response. 109 | func (c *ResponseHeaders) GetVerificationHeader() *ResponseVerificationHeader { 110 | if c != nil { 111 | return c.verifyHeader 112 | } 113 | 114 | return nil 115 | } 116 | 117 | // SetVerificationHeader sets verification header of the response. 118 | func (c *ResponseHeaders) SetVerificationHeader(v *ResponseVerificationHeader) { 119 | c.verifyHeader = v 120 | } 121 | 122 | func (c *ResponseHeaders) ToMessage(m interface { 123 | SetMetaHeader(*session.ResponseMetaHeader) 124 | SetVerifyHeader(*session.ResponseVerificationHeader) 125 | }) { 126 | m.SetMetaHeader(c.metaHeader.ToGRPCMessage().(*session.ResponseMetaHeader)) 127 | m.SetVerifyHeader(c.verifyHeader.ToGRPCMessage().(*session.ResponseVerificationHeader)) 128 | } 129 | 130 | func (c *ResponseHeaders) FromMessage(m interface { 131 | GetMetaHeader() *session.ResponseMetaHeader 132 | GetVerifyHeader() *session.ResponseVerificationHeader 133 | }) error { 134 | metaHdr := m.GetMetaHeader() 135 | if metaHdr == nil { 136 | c.metaHeader = nil 137 | } else { 138 | if c.metaHeader == nil { 139 | c.metaHeader = new(ResponseMetaHeader) 140 | } 141 | 142 | err := c.metaHeader.FromGRPCMessage(metaHdr) 143 | if err != nil { 144 | return err 145 | } 146 | } 147 | 148 | verifyHdr := m.GetVerifyHeader() 149 | if verifyHdr == nil { 150 | c.verifyHeader = nil 151 | } else { 152 | if c.verifyHeader == nil { 153 | c.verifyHeader = new(ResponseVerificationHeader) 154 | } 155 | 156 | err := c.verifyHeader.FromGRPCMessage(verifyHdr) 157 | if err != nil { 158 | return err 159 | } 160 | } 161 | 162 | return nil 163 | } 164 | -------------------------------------------------------------------------------- /session/xheaders.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | // ReservedXHeaderPrefix is a prefix of keys to "well-known" X-headers. 4 | const ReservedXHeaderPrefix = "__NEOFS__" 5 | 6 | const ( 7 | // XHeaderNetmapEpoch is a key to the reserved X-header that specifies netmap epoch 8 | // to use for object placement calculation. If set to '0' or not set, the current 9 | // epoch only will be used. 10 | XHeaderNetmapEpoch = ReservedXHeaderPrefix + "NETMAP_EPOCH" 11 | 12 | // XHeaderNetmapLookupDepth is a key to the reserved X-header that limits 13 | // how many past epochs back the node will can lookup. If set to '0' or not 14 | // set, the current epoch only will be used. 15 | XHeaderNetmapLookupDepth = ReservedXHeaderPrefix + "NETMAP_LOOKUP_DEPTH" 16 | ) 17 | -------------------------------------------------------------------------------- /signature/sign_test.go: -------------------------------------------------------------------------------- 1 | package signature 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/accounting" 7 | "github.com/TrueCloudLab/frostfs-api-go/v2/session" 8 | crypto "github.com/TrueCloudLab/frostfs-crypto" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestBalanceResponse(t *testing.T) { 13 | dec := new(accounting.Decimal) 14 | dec.SetValue(100) 15 | 16 | body := new(accounting.BalanceResponseBody) 17 | body.SetBalance(dec) 18 | 19 | meta := new(session.ResponseMetaHeader) 20 | meta.SetTTL(1) 21 | 22 | req := new(accounting.BalanceResponse) 23 | req.SetBody(body) 24 | req.SetMetaHeader(meta) 25 | 26 | // verify unsigned request 27 | require.Error(t, VerifyServiceMessage(req)) 28 | 29 | key, err := crypto.LoadPrivateKey("Kwk6k2eC3L3QuPvD8aiaNyoSXgQ2YL1bwS5CP1oKoA9waeAze97s") 30 | require.NoError(t, err) 31 | 32 | // sign request 33 | require.NoError(t, SignServiceMessage(key, req)) 34 | 35 | // verification must pass 36 | require.NoError(t, VerifyServiceMessage(req)) 37 | 38 | // add level to meta header matryoshka 39 | meta = new(session.ResponseMetaHeader) 40 | meta.SetOrigin(req.GetMetaHeader()) 41 | req.SetMetaHeader(meta) 42 | 43 | // sign request 44 | require.NoError(t, SignServiceMessage(key, req)) 45 | 46 | // verification must pass 47 | require.NoError(t, VerifyServiceMessage(req)) 48 | 49 | // corrupt body 50 | dec.SetValue(dec.GetValue() + 1) 51 | 52 | // verification must fail 53 | require.Error(t, VerifyServiceMessage(req)) 54 | 55 | // restore body 56 | dec.SetValue(dec.GetValue() - 1) 57 | 58 | // corrupt meta header 59 | meta.SetTTL(meta.GetTTL() + 1) 60 | 61 | // verification must fail 62 | require.Error(t, VerifyServiceMessage(req)) 63 | 64 | // restore meta header 65 | meta.SetTTL(meta.GetTTL() - 1) 66 | 67 | // corrupt origin verification header 68 | req.GetVerificationHeader().SetOrigin(nil) 69 | 70 | // verification must fail 71 | require.Error(t, VerifyServiceMessage(req)) 72 | } 73 | -------------------------------------------------------------------------------- /status/convert.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/grpc" 5 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 6 | status "github.com/TrueCloudLab/frostfs-api-go/v2/status/grpc" 7 | ) 8 | 9 | func (x *Detail) ToGRPCMessage() grpc.Message { 10 | var m *status.Status_Detail 11 | 12 | if x != nil { 13 | m = new(status.Status_Detail) 14 | 15 | m.SetId(x.id) 16 | m.SetValue(x.val) 17 | } 18 | 19 | return m 20 | } 21 | 22 | func (x *Detail) FromGRPCMessage(m grpc.Message) error { 23 | v, ok := m.(*status.Status_Detail) 24 | if !ok { 25 | return message.NewUnexpectedMessageType(m, v) 26 | } 27 | 28 | x.id = v.GetId() 29 | x.val = v.GetValue() 30 | 31 | return nil 32 | } 33 | 34 | func CodeFromGRPC(v uint32) Code { 35 | return Code(v) 36 | } 37 | 38 | func CodeToGRPC(v Code) uint32 { 39 | return uint32(v) 40 | } 41 | 42 | func (x *Status) ToGRPCMessage() grpc.Message { 43 | var m *status.Status 44 | 45 | if x != nil { 46 | m = new(status.Status) 47 | 48 | m.SetCode(CodeToGRPC(x.code)) 49 | m.SetMessage(x.msg) 50 | 51 | var ds []*status.Status_Detail 52 | 53 | if ln := len(x.details); ln > 0 { 54 | ds = make([]*status.Status_Detail, 0, ln) 55 | 56 | for i := 0; i < ln; i++ { 57 | ds = append(ds, x.details[i].ToGRPCMessage().(*status.Status_Detail)) 58 | } 59 | } 60 | 61 | m.SetDetails(ds) 62 | } 63 | 64 | return m 65 | } 66 | 67 | func (x *Status) FromGRPCMessage(m grpc.Message) error { 68 | v, ok := m.(*status.Status) 69 | if !ok { 70 | return message.NewUnexpectedMessageType(m, v) 71 | } 72 | 73 | var ( 74 | ds []Detail 75 | dsV2 = v.GetDetails() 76 | ) 77 | 78 | if dsV2 != nil { 79 | ln := len(dsV2) 80 | 81 | ds = make([]Detail, ln) 82 | 83 | for i := 0; i < ln; i++ { 84 | if dsV2[i] != nil { 85 | if err := ds[i].FromGRPCMessage(dsV2[i]); err != nil { 86 | return err 87 | } 88 | } 89 | } 90 | } 91 | 92 | x.details = ds 93 | x.msg = v.GetMessage() 94 | x.code = CodeFromGRPC(v.GetCode()) 95 | 96 | return nil 97 | } 98 | -------------------------------------------------------------------------------- /status/details.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | // details for WrongMagicNumber code. 4 | const ( 5 | // DetailIDCorrectMagic is an identifier of details with correct network magic 6 | // which can be attached to WrongMagicNumber code. 7 | DetailIDCorrectMagic = iota 8 | ) 9 | -------------------------------------------------------------------------------- /status/grpc/types.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | // SetId sets identifier of the Status_Detail. 4 | func (x *Status_Detail) SetId(v uint32) { 5 | x.Id = v 6 | } 7 | 8 | // SetValue sets value of the Status_Detail. 9 | func (x *Status_Detail) SetValue(v []byte) { 10 | x.Value = v 11 | } 12 | 13 | // SetCode sets code of the Status. 14 | func (x *Status) SetCode(v uint32) { 15 | x.Code = v 16 | } 17 | 18 | // SetMessage sets message about the Status. 19 | func (x *Status) SetMessage(v string) { 20 | x.Message = v 21 | } 22 | 23 | // SetDetails sets details of the Status. 24 | func (x *Status) SetDetails(v []*Status_Detail) { 25 | x.Details = v 26 | } 27 | -------------------------------------------------------------------------------- /status/marshal.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 5 | status "github.com/TrueCloudLab/frostfs-api-go/v2/status/grpc" 6 | protoutil "github.com/TrueCloudLab/frostfs-api-go/v2/util/proto" 7 | ) 8 | 9 | const ( 10 | _ = iota 11 | detailIDFNum 12 | detailValueFNum 13 | ) 14 | 15 | func (x *Detail) StableMarshal(buf []byte) []byte { 16 | if x == nil { 17 | return []byte{} 18 | } 19 | 20 | if buf == nil { 21 | buf = make([]byte, x.StableSize()) 22 | } 23 | 24 | var offset int 25 | 26 | offset += protoutil.UInt32Marshal(detailIDFNum, buf[offset:], x.id) 27 | protoutil.BytesMarshal(detailValueFNum, buf[offset:], x.val) 28 | 29 | return buf 30 | } 31 | 32 | func (x *Detail) StableSize() (size int) { 33 | size += protoutil.UInt32Size(detailIDFNum, x.id) 34 | size += protoutil.BytesSize(detailValueFNum, x.val) 35 | 36 | return size 37 | } 38 | 39 | func (x *Detail) Unmarshal(data []byte) error { 40 | return message.Unmarshal(x, data, new(status.Status_Detail)) 41 | } 42 | 43 | const ( 44 | _ = iota 45 | statusCodeFNum 46 | statusMsgFNum 47 | statusDetailsFNum 48 | ) 49 | 50 | func (x *Status) StableMarshal(buf []byte) []byte { 51 | if x == nil { 52 | return []byte{} 53 | } 54 | 55 | if buf == nil { 56 | buf = make([]byte, x.StableSize()) 57 | } 58 | 59 | var offset int 60 | 61 | offset += protoutil.UInt32Marshal(statusCodeFNum, buf[offset:], CodeToGRPC(x.code)) 62 | offset += protoutil.StringMarshal(statusMsgFNum, buf[offset:], x.msg) 63 | 64 | for i := range x.details { 65 | offset += protoutil.NestedStructureMarshal(statusDetailsFNum, buf[offset:], &x.details[i]) 66 | } 67 | 68 | return buf 69 | } 70 | 71 | func (x *Status) StableSize() (size int) { 72 | size += protoutil.UInt32Size(statusCodeFNum, CodeToGRPC(x.code)) 73 | size += protoutil.StringSize(statusMsgFNum, x.msg) 74 | 75 | for i := range x.details { 76 | size += protoutil.NestedStructureSize(statusDetailsFNum, &x.details[i]) 77 | } 78 | 79 | return size 80 | } 81 | 82 | func (x *Status) Unmarshal(data []byte) error { 83 | return message.Unmarshal(x, data, new(status.Status)) 84 | } 85 | -------------------------------------------------------------------------------- /status/message_test.go: -------------------------------------------------------------------------------- 1 | package status_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message" 7 | messagetest "github.com/TrueCloudLab/frostfs-api-go/v2/rpc/message/test" 8 | statustest "github.com/TrueCloudLab/frostfs-api-go/v2/status/test" 9 | ) 10 | 11 | func TestMessageConvert(t *testing.T) { 12 | messagetest.TestRPCMessage(t, 13 | func(empty bool) message.Message { return statustest.Detail(empty) }, 14 | func(empty bool) message.Message { return statustest.Status(empty) }, 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /status/status.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | const sectionBitSize = 10 4 | 5 | // InSections checks if the Code is in [i,j] section list. 6 | func (x Code) InSections(i, j uint32) bool { 7 | return uint32(x) >= i<