diff --git a/accounting/decimal.go b/accounting/decimal.go index bb01f28..e7acdb3 100644 --- a/accounting/decimal.go +++ b/accounting/decimal.go @@ -1,10 +1,10 @@ package accounting -import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting" +import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting" // Decimal represents decimal number for accounting operations. // -// Decimal is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting.Decimal +// Decimal is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting.Decimal // message. See ReadFromV2 / WriteToV2 methods. // // Instances can be created using built-in var declaration. diff --git a/accounting/decimal_test.go b/accounting/decimal_test.go index af46dd4..84f739f 100644 --- a/accounting/decimal_test.go +++ b/accounting/decimal_test.go @@ -3,8 +3,8 @@ package accounting_test import ( "testing" - v2accounting "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting" + v2accounting "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting" "github.com/stretchr/testify/require" ) diff --git a/accounting/doc.go b/accounting/doc.go index defcb0d..7a55f24 100644 --- a/accounting/doc.go +++ b/accounting/doc.go @@ -13,7 +13,7 @@ Instances can be also used to process FrostFS API V2 protocol messages On client side: - import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting" + import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting" var msg accounting.Decimal dec.WriteToV2(&msg) diff --git a/apiv2/accounting/accounting.go b/apiv2/accounting/accounting.go new file mode 100644 index 0000000..119882f --- /dev/null +++ b/apiv2/accounting/accounting.go @@ -0,0 +1,104 @@ +package accounting + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" +) + +type BalanceRequestBody struct { + ownerID *refs.OwnerID +} + +type BalanceResponseBody struct { + bal *Decimal +} + +type Decimal struct { + val int64 + + prec uint32 +} + +type BalanceRequest struct { + body *BalanceRequestBody + + session.RequestHeaders +} + +type BalanceResponse struct { + body *BalanceResponseBody + + session.ResponseHeaders +} + +func (b *BalanceRequestBody) GetOwnerID() *refs.OwnerID { + if b != nil { + return b.ownerID + } + + return nil +} + +func (b *BalanceRequestBody) SetOwnerID(v *refs.OwnerID) { + b.ownerID = v +} + +func (b *BalanceRequest) GetBody() *BalanceRequestBody { + if b != nil { + return b.body + } + + return nil +} + +func (b *BalanceRequest) SetBody(v *BalanceRequestBody) { + b.body = v +} + +func (d *Decimal) GetValue() int64 { + if d != nil { + return d.val + } + + return 0 +} + +func (d *Decimal) SetValue(v int64) { + d.val = v +} + +func (d *Decimal) GetPrecision() uint32 { + if d != nil { + return d.prec + } + + return 0 +} + +func (d *Decimal) SetPrecision(v uint32) { + d.prec = v +} + +func (br *BalanceResponseBody) GetBalance() *Decimal { + if br != nil { + return br.bal + } + + return nil +} + +func (br *BalanceResponseBody) SetBalance(v *Decimal) { + br.bal = v +} + +func (br *BalanceResponse) GetBody() *BalanceResponseBody { + if br != nil { + return br.body + } + + return nil +} + +func (br *BalanceResponse) SetBody(v *BalanceResponseBody) { + br.body = v +} diff --git a/apiv2/accounting/convert.go b/apiv2/accounting/convert.go new file mode 100644 index 0000000..f53eaed --- /dev/null +++ b/apiv2/accounting/convert.go @@ -0,0 +1,178 @@ +package accounting + +import ( + accounting "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + refsGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +func (b *BalanceRequestBody) ToGRPCMessage() grpc.Message { + var m *accounting.BalanceRequest_Body + + if b != nil { + m = new(accounting.BalanceRequest_Body) + + m.SetOwnerId(b.ownerID.ToGRPCMessage().(*refsGRPC.OwnerID)) + } + + return m +} + +func (b *BalanceRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*accounting.BalanceRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + ownerID := v.GetOwnerId() + if ownerID == nil { + b.ownerID = nil + } else { + if b.ownerID == nil { + b.ownerID = new(refs.OwnerID) + } + + err = b.ownerID.FromGRPCMessage(ownerID) + } + + return err +} + +func (b *BalanceRequest) ToGRPCMessage() grpc.Message { + var m *accounting.BalanceRequest + + if b != nil { + m = new(accounting.BalanceRequest) + + m.SetBody(b.body.ToGRPCMessage().(*accounting.BalanceRequest_Body)) + b.RequestHeaders.ToMessage(m) + } + + return m +} + +func (b *BalanceRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*accounting.BalanceRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + b.body = nil + } else { + if b.body == nil { + b.body = new(BalanceRequestBody) + } + + err = b.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return b.RequestHeaders.FromMessage(v) +} + +func (d *Decimal) ToGRPCMessage() grpc.Message { + var m *accounting.Decimal + + if d != nil { + m = new(accounting.Decimal) + + m.SetValue(d.val) + m.SetPrecision(d.prec) + } + + return m +} + +func (d *Decimal) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*accounting.Decimal) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + d.val = v.GetValue() + d.prec = v.GetPrecision() + + return nil +} + +func (br *BalanceResponseBody) ToGRPCMessage() grpc.Message { + var m *accounting.BalanceResponse_Body + + if br != nil { + m = new(accounting.BalanceResponse_Body) + + m.SetBalance(br.bal.ToGRPCMessage().(*accounting.Decimal)) + } + + return m +} + +func (br *BalanceResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*accounting.BalanceResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + bal := v.GetBalance() + if bal == nil { + br.bal = nil + } else { + if br.bal == nil { + br.bal = new(Decimal) + } + + err = br.bal.FromGRPCMessage(bal) + } + + return err +} + +func (br *BalanceResponse) ToGRPCMessage() grpc.Message { + var m *accounting.BalanceResponse + + if br != nil { + m = new(accounting.BalanceResponse) + + m.SetBody(br.body.ToGRPCMessage().(*accounting.BalanceResponse_Body)) + br.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (br *BalanceResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*accounting.BalanceResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + br.body = nil + } else { + if br.body == nil { + br.body = new(BalanceResponseBody) + } + + err = br.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return br.ResponseHeaders.FromMessage(v) +} diff --git a/apiv2/accounting/grpc/service_frostfs.pb.go b/apiv2/accounting/grpc/service_frostfs.pb.go new file mode 100644 index 0000000..10e7ce4 Binary files /dev/null and b/apiv2/accounting/grpc/service_frostfs.pb.go differ diff --git a/apiv2/accounting/grpc/service_frostfs_fuzz.go b/apiv2/accounting/grpc/service_frostfs_fuzz.go new file mode 100644 index 0000000..69e7174 --- /dev/null +++ b/apiv2/accounting/grpc/service_frostfs_fuzz.go @@ -0,0 +1,45 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package accounting + +func DoFuzzProtoBalanceRequest(data []byte) int { + msg := new(BalanceRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONBalanceRequest(data []byte) int { + msg := new(BalanceRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoBalanceResponse(data []byte) int { + msg := new(BalanceResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONBalanceResponse(data []byte) int { + msg := new(BalanceResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/accounting/grpc/service_frostfs_test.go b/apiv2/accounting/grpc/service_frostfs_test.go new file mode 100644 index 0000000..b97a13e --- /dev/null +++ b/apiv2/accounting/grpc/service_frostfs_test.go @@ -0,0 +1,31 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package accounting + +import ( + testing "testing" +) + +func FuzzProtoBalanceRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoBalanceRequest(data) + }) +} +func FuzzJSONBalanceRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONBalanceRequest(data) + }) +} +func FuzzProtoBalanceResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoBalanceResponse(data) + }) +} +func FuzzJSONBalanceResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONBalanceResponse(data) + }) +} diff --git a/apiv2/accounting/grpc/service_grpc.pb.go b/apiv2/accounting/grpc/service_grpc.pb.go new file mode 100644 index 0000000..63207fe Binary files /dev/null and b/apiv2/accounting/grpc/service_grpc.pb.go differ diff --git a/apiv2/accounting/grpc/types_frostfs.pb.go b/apiv2/accounting/grpc/types_frostfs.pb.go new file mode 100644 index 0000000..058e42e Binary files /dev/null and b/apiv2/accounting/grpc/types_frostfs.pb.go differ diff --git a/apiv2/accounting/grpc/types_frostfs_fuzz.go b/apiv2/accounting/grpc/types_frostfs_fuzz.go new file mode 100644 index 0000000..5eb5e97 --- /dev/null +++ b/apiv2/accounting/grpc/types_frostfs_fuzz.go @@ -0,0 +1,26 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package accounting + +func DoFuzzProtoDecimal(data []byte) int { + msg := new(Decimal) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONDecimal(data []byte) int { + msg := new(Decimal) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/accounting/grpc/types_frostfs_test.go b/apiv2/accounting/grpc/types_frostfs_test.go new file mode 100644 index 0000000..404b75e --- /dev/null +++ b/apiv2/accounting/grpc/types_frostfs_test.go @@ -0,0 +1,21 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package accounting + +import ( + testing "testing" +) + +func FuzzProtoDecimal(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoDecimal(data) + }) +} +func FuzzJSONDecimal(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONDecimal(data) + }) +} diff --git a/apiv2/accounting/json.go b/apiv2/accounting/json.go new file mode 100644 index 0000000..78ac98e --- /dev/null +++ b/apiv2/accounting/json.go @@ -0,0 +1,14 @@ +package accounting + +import ( + accounting "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +func (d *Decimal) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(d) +} + +func (d *Decimal) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(d, data, new(accounting.Decimal)) +} diff --git a/apiv2/accounting/marshal.go b/apiv2/accounting/marshal.go new file mode 100644 index 0000000..e4a0c6d --- /dev/null +++ b/apiv2/accounting/marshal.go @@ -0,0 +1,104 @@ +package accounting + +import ( + accounting "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + protoutil "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto" +) + +const ( + decimalValueField = 1 + decimalPrecisionField = 2 + + balanceReqBodyOwnerField = 1 + + balanceRespBodyDecimalField = 1 +) + +func (d *Decimal) StableMarshal(buf []byte) []byte { + if d == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, d.StableSize()) + } + + var offset int + + offset += protoutil.Int64Marshal(decimalValueField, buf[offset:], d.val) + protoutil.UInt32Marshal(decimalPrecisionField, buf[offset:], d.prec) + + return buf +} + +func (d *Decimal) StableSize() (size int) { + if d == nil { + return 0 + } + + size += protoutil.Int64Size(decimalValueField, d.val) + size += protoutil.UInt32Size(decimalPrecisionField, d.prec) + + return size +} + +func (d *Decimal) Unmarshal(data []byte) error { + return message.Unmarshal(d, data, new(accounting.Decimal)) +} + +func (b *BalanceRequestBody) StableMarshal(buf []byte) []byte { + if b == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, b.StableSize()) + } + + protoutil.NestedStructureMarshal(balanceReqBodyOwnerField, buf, b.ownerID) + + return buf +} + +func (b *BalanceRequestBody) StableSize() (size int) { + if b == nil { + return 0 + } + + size = protoutil.NestedStructureSize(balanceReqBodyOwnerField, b.ownerID) + + return size +} + +func (b *BalanceRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(b, data, new(accounting.BalanceRequest_Body)) +} + +func (br *BalanceResponseBody) StableMarshal(buf []byte) []byte { + if br == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, br.StableSize()) + } + + protoutil.NestedStructureMarshal(balanceRespBodyDecimalField, buf, br.bal) + + return buf +} + +func (br *BalanceResponseBody) StableSize() (size int) { + if br == nil { + return 0 + } + + size = protoutil.NestedStructureSize(balanceRespBodyDecimalField, br.bal) + + return size +} + +func (br *BalanceResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(br, data, new(accounting.BalanceResponse_Body)) +} diff --git a/apiv2/accounting/message_test.go b/apiv2/accounting/message_test.go new file mode 100644 index 0000000..be2a71c --- /dev/null +++ b/apiv2/accounting/message_test.go @@ -0,0 +1,19 @@ +package accounting_test + +import ( + "testing" + + accountingtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting/test" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + messagetest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message/test" +) + +func TestMessage(t *testing.T) { + messagetest.TestRPCMessage(t, + func(empty bool) message.Message { return accountingtest.GenerateDecimal(empty) }, + func(empty bool) message.Message { return accountingtest.GenerateBalanceRequestBody(empty) }, + func(empty bool) message.Message { return accountingtest.GenerateBalanceRequest(empty) }, + func(empty bool) message.Message { return accountingtest.GenerateBalanceResponseBody(empty) }, + func(empty bool) message.Message { return accountingtest.GenerateBalanceResponse(empty) }, + ) +} diff --git a/apiv2/accounting/test/generate.go b/apiv2/accounting/test/generate.go new file mode 100644 index 0000000..41d1aba --- /dev/null +++ b/apiv2/accounting/test/generate.go @@ -0,0 +1,64 @@ +package accountingtest + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting" + accountingtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/test" + sessiontest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session/test" +) + +func GenerateBalanceRequest(empty bool) *accounting.BalanceRequest { + m := new(accounting.BalanceRequest) + + if !empty { + m.SetBody(GenerateBalanceRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateBalanceRequestBody(empty bool) *accounting.BalanceRequestBody { + m := new(accounting.BalanceRequestBody) + + if !empty { + m.SetOwnerID(accountingtest.GenerateOwnerID(false)) + } + + return m +} + +func GenerateBalanceResponse(empty bool) *accounting.BalanceResponse { + m := new(accounting.BalanceResponse) + + if !empty { + m.SetBody(GenerateBalanceResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} + +func GenerateBalanceResponseBody(empty bool) *accounting.BalanceResponseBody { + m := new(accounting.BalanceResponseBody) + + if !empty { + m.SetBalance(GenerateDecimal(false)) + } + + return m +} + +func GenerateDecimal(empty bool) *accounting.Decimal { + m := new(accounting.Decimal) + + if !empty { + m.SetValue(1) + m.SetPrecision(2) + } + + return m +} diff --git a/apiv2/acl/bench_test.go b/apiv2/acl/bench_test.go new file mode 100644 index 0000000..0b45f4a --- /dev/null +++ b/apiv2/acl/bench_test.go @@ -0,0 +1,51 @@ +package acl_test + +import ( + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" + aclGrpc "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl/grpc" + acltest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl/test" +) + +func BenchmarkTable_ToGRPCMessage(b *testing.B) { + const size = 4 + + tb := new(acl.Table) + rs := make([]acl.Record, size) + for i := range rs { + fs := make([]acl.HeaderFilter, size) + for j := range fs { + fs[j] = *acltest.GenerateFilter(false) + } + ts := make([]acl.Target, size) + for j := range ts { + ts[j] = *acltest.GenerateTarget(false) + } + + rs[i].SetFilters(fs) + rs[i].SetTargets(ts) + } + tb.SetRecords(rs) + + raw := tb.ToGRPCMessage() + + b.Run("to grpc message", func(b *testing.B) { + b.ReportAllocs() + for range b.N { + raw := tb.ToGRPCMessage() + if len(tb.GetRecords()) != len(raw.(*aclGrpc.EACLTable).Records) { + b.FailNow() + } + } + }) + b.Run("from grpc message", func(b *testing.B) { + b.ReportAllocs() + for range b.N { + tb := new(acl.Table) + if tb.FromGRPCMessage(raw) != nil { + b.FailNow() + } + } + }) +} diff --git a/apiv2/acl/convert.go b/apiv2/acl/convert.go new file mode 100644 index 0000000..d2c4d97 --- /dev/null +++ b/apiv2/acl/convert.go @@ -0,0 +1,592 @@ +package acl + +import ( + acl "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape" + apeGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + refsGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +// RoleToGRPCField converts unified role enum into grpc enum. +func RoleToGRPCField(t Role) acl.Role { + switch t { + case RoleUser: + return acl.Role_USER + case RoleSystem: + return acl.Role_SYSTEM + case RoleOthers: + return acl.Role_OTHERS + default: + return acl.Role_ROLE_UNSPECIFIED + } +} + +// RoleFromGRPCField converts grpc enum into unified role enum. +func RoleFromGRPCField(t acl.Role) Role { + switch t { + case acl.Role_USER: + return RoleUser + case acl.Role_SYSTEM: + return RoleSystem + case acl.Role_OTHERS: + return RoleOthers + default: + return RoleUnknown + } +} + +// OperationToGRPCField converts unified operation enum into grpc enum. +func OperationToGRPCField(t Operation) acl.Operation { + switch t { + case OperationPut: + return acl.Operation_PUT + case OperationDelete: + return acl.Operation_DELETE + case OperationGet: + return acl.Operation_GET + case OperationHead: + return acl.Operation_HEAD + case OperationSearch: + return acl.Operation_SEARCH + case OperationRange: + return acl.Operation_GETRANGE + case OperationRangeHash: + return acl.Operation_GETRANGEHASH + default: + return acl.Operation_OPERATION_UNSPECIFIED + } +} + +// OperationFromGRPCField converts grpc enum into unified operation enum. +func OperationFromGRPCField(t acl.Operation) Operation { + switch t { + case acl.Operation_PUT: + return OperationPut + case acl.Operation_DELETE: + return OperationDelete + case acl.Operation_GET: + return OperationGet + case acl.Operation_HEAD: + return OperationHead + case acl.Operation_SEARCH: + return OperationSearch + case acl.Operation_GETRANGE: + return OperationRange + case acl.Operation_GETRANGEHASH: + return OperationRangeHash + default: + return OperationUnknown + } +} + +// ActionToGRPCField converts unified action enum into grpc enum. +func ActionToGRPCField(t Action) acl.Action { + switch t { + case ActionDeny: + return acl.Action_DENY + case ActionAllow: + return acl.Action_ALLOW + default: + return acl.Action_ACTION_UNSPECIFIED + } +} + +// ActionFromGRPCField converts grpc enum into unified action enum. +func ActionFromGRPCField(t acl.Action) Action { + switch t { + case acl.Action_DENY: + return ActionDeny + case acl.Action_ALLOW: + return ActionAllow + default: + return ActionUnknown + } +} + +// HeaderTypeToGRPCField converts unified header type enum into grpc enum. +func HeaderTypeToGRPCField(t HeaderType) acl.HeaderType { + switch t { + case HeaderTypeRequest: + return acl.HeaderType_REQUEST + case HeaderTypeObject: + return acl.HeaderType_OBJECT + case HeaderTypeService: + return acl.HeaderType_SERVICE + default: + return acl.HeaderType_HEADER_UNSPECIFIED + } +} + +// HeaderTypeFromGRPCField converts grpc enum into unified header type enum. +func HeaderTypeFromGRPCField(t acl.HeaderType) HeaderType { + switch t { + case acl.HeaderType_REQUEST: + return HeaderTypeRequest + case acl.HeaderType_OBJECT: + return HeaderTypeObject + case acl.HeaderType_SERVICE: + return HeaderTypeService + default: + return HeaderTypeUnknown + } +} + +// MatchTypeToGRPCField converts unified match type enum into grpc enum. +func MatchTypeToGRPCField(t MatchType) acl.MatchType { + switch t { + case MatchTypeStringEqual: + return acl.MatchType_STRING_EQUAL + case MatchTypeStringNotEqual: + return acl.MatchType_STRING_NOT_EQUAL + default: + return acl.MatchType_MATCH_TYPE_UNSPECIFIED + } +} + +// MatchTypeFromGRPCField converts grpc enum into unified match type enum. +func MatchTypeFromGRPCField(t acl.MatchType) MatchType { + switch t { + case acl.MatchType_STRING_EQUAL: + return MatchTypeStringEqual + case acl.MatchType_STRING_NOT_EQUAL: + return MatchTypeStringNotEqual + default: + return MatchTypeUnknown + } +} + +func (f *HeaderFilter) ToGRPCMessage() grpc.Message { + var m *acl.EACLRecord_Filter + + if f != nil { + m = new(acl.EACLRecord_Filter) + + m.SetKey(f.key) + m.SetValue(f.value) + m.SetHeaderType(HeaderTypeToGRPCField(f.hdrType)) + m.SetMatchType(MatchTypeToGRPCField(f.matchType)) + } + + return m +} + +func (f *HeaderFilter) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*acl.EACLRecord_Filter) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + f.key = v.GetKey() + f.value = v.GetValue() + f.hdrType = HeaderTypeFromGRPCField(v.GetHeaderType()) + f.matchType = MatchTypeFromGRPCField(v.GetMatchType()) + + return nil +} + +func HeaderFiltersToGRPC(fs []HeaderFilter) (res []acl.EACLRecord_Filter) { + if fs != nil { + res = make([]acl.EACLRecord_Filter, 0, len(fs)) + + for i := range fs { + res = append(res, *fs[i].ToGRPCMessage().(*acl.EACLRecord_Filter)) + } + } + + return +} + +func HeaderFiltersFromGRPC(fs []acl.EACLRecord_Filter) (res []HeaderFilter, err error) { + if fs != nil { + res = make([]HeaderFilter, len(fs)) + + for i := range fs { + err = res[i].FromGRPCMessage(&fs[i]) + if err != nil { + return + } + } + } + + return +} + +func (t *Target) ToGRPCMessage() grpc.Message { + var m *acl.EACLRecord_Target + + if t != nil { + m = new(acl.EACLRecord_Target) + + m.SetRole(RoleToGRPCField(t.role)) + m.SetKeys(t.keys) + } + + return m +} + +func (t *Target) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*acl.EACLRecord_Target) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + t.role = RoleFromGRPCField(v.GetRole()) + t.keys = v.GetKeys() + + return nil +} + +func TargetsToGRPC(ts []Target) (res []acl.EACLRecord_Target) { + if ts != nil { + res = make([]acl.EACLRecord_Target, 0, len(ts)) + + for i := range ts { + res = append(res, *ts[i].ToGRPCMessage().(*acl.EACLRecord_Target)) + } + } + + return +} + +func TargetsFromGRPC(fs []acl.EACLRecord_Target) (res []Target, err error) { + if fs != nil { + res = make([]Target, len(fs)) + + for i := range fs { + err = res[i].FromGRPCMessage(&fs[i]) + if err != nil { + return + } + } + } + + return +} + +func (r *Record) ToGRPCMessage() grpc.Message { + var m *acl.EACLRecord + + if r != nil { + m = new(acl.EACLRecord) + + m.SetOperation(OperationToGRPCField(r.op)) + m.SetAction(ActionToGRPCField(r.action)) + m.SetFilters(HeaderFiltersToGRPC(r.filters)) + m.SetTargets(TargetsToGRPC(r.targets)) + } + + return m +} + +func (r *Record) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*acl.EACLRecord) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + r.filters, err = HeaderFiltersFromGRPC(v.GetFilters()) + if err != nil { + return err + } + + r.targets, err = TargetsFromGRPC(v.GetTargets()) + if err != nil { + return err + } + + r.op = OperationFromGRPCField(v.GetOperation()) + r.action = ActionFromGRPCField(v.GetAction()) + + return nil +} + +func RecordsToGRPC(ts []Record) (res []acl.EACLRecord) { + if ts != nil { + res = make([]acl.EACLRecord, 0, len(ts)) + + for i := range ts { + res = append(res, *ts[i].ToGRPCMessage().(*acl.EACLRecord)) + } + } + + return +} + +func RecordsFromGRPC(fs []acl.EACLRecord) (res []Record, err error) { + if fs != nil { + res = make([]Record, len(fs)) + + for i := range fs { + err = res[i].FromGRPCMessage(&fs[i]) + if err != nil { + return + } + } + } + + return +} + +func (t *Table) ToGRPCMessage() grpc.Message { + var m *acl.EACLTable + + if t != nil { + m = new(acl.EACLTable) + + m.SetVersion(t.version.ToGRPCMessage().(*refsGRPC.Version)) + m.SetContainerId(t.cid.ToGRPCMessage().(*refsGRPC.ContainerID)) + m.SetRecords(RecordsToGRPC(t.records)) + } + + return m +} + +func (t *Table) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*acl.EACLTable) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + cid := v.GetContainerId() + if cid == nil { + t.cid = nil + } else { + if t.cid == nil { + t.cid = new(refs.ContainerID) + } + + err = t.cid.FromGRPCMessage(cid) + if err != nil { + return err + } + } + + version := v.GetVersion() + if version == nil { + t.version = nil + } else { + if t.version == nil { + t.version = new(refs.Version) + } + + err = t.version.FromGRPCMessage(version) + if err != nil { + return err + } + } + + t.records, err = RecordsFromGRPC(v.GetRecords()) + + return err +} + +func (l *TokenLifetime) ToGRPCMessage() grpc.Message { + var m *acl.BearerToken_Body_TokenLifetime + + if l != nil { + m = new(acl.BearerToken_Body_TokenLifetime) + + m.SetExp(l.exp) + m.SetIat(l.iat) + m.SetNbf(l.nbf) + } + + return m +} + +func (l *TokenLifetime) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*acl.BearerToken_Body_TokenLifetime) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + l.exp = v.GetExp() + l.iat = v.GetIat() + l.nbf = v.GetNbf() + + return nil +} + +func (c *APEOverride) ToGRPCMessage() grpc.Message { + var m *acl.BearerToken_Body_APEOverride + + if c != nil { + m = new(acl.BearerToken_Body_APEOverride) + + m.SetTarget(c.target.ToGRPCMessage().(*apeGRPC.ChainTarget)) + + if len(c.chains) > 0 { + apeChains := make([]apeGRPC.Chain, len(c.chains)) + for i := range c.chains { + apeChains[i] = *c.chains[i].ToGRPCMessage().(*apeGRPC.Chain) + } + m.SetChains(apeChains) + } + } + + return m +} + +func (c *APEOverride) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*acl.BearerToken_Body_APEOverride) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + if targetGRPC := v.GetTarget(); targetGRPC != nil { + if c.target == nil { + c.target = new(ape.ChainTarget) + } + if err := c.target.FromGRPCMessage(v.GetTarget()); err != nil { + return err + } + } + + if apeChains := v.GetChains(); len(apeChains) > 0 { + c.chains = make([]*ape.Chain, len(apeChains)) + for i := range apeChains { + c.chains[i] = new(ape.Chain) + if err := c.chains[i].FromGRPCMessage(&apeChains[i]); err != nil { + return err + } + } + } + + return nil +} + +func (bt *BearerTokenBody) ToGRPCMessage() grpc.Message { + var m *acl.BearerToken_Body + + if bt != nil { + m = new(acl.BearerToken_Body) + + m.SetOwnerId(bt.ownerID.ToGRPCMessage().(*refsGRPC.OwnerID)) + m.SetLifetime(bt.lifetime.ToGRPCMessage().(*acl.BearerToken_Body_TokenLifetime)) + m.SetEaclTable(bt.eacl.ToGRPCMessage().(*acl.EACLTable)) + m.SetAllowImpersonate(bt.impersonate) + m.SetApeOverride(bt.apeOverride.ToGRPCMessage().(*acl.BearerToken_Body_APEOverride)) + } + + return m +} + +func (bt *BearerTokenBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*acl.BearerToken_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + ownerID := v.GetOwnerId() + if ownerID == nil { + bt.ownerID = nil + } else { + if bt.ownerID == nil { + bt.ownerID = new(refs.OwnerID) + } + + err = bt.ownerID.FromGRPCMessage(ownerID) + if err != nil { + return err + } + } + + lifetime := v.GetLifetime() + if lifetime == nil { + bt.lifetime = nil + } else { + if bt.lifetime == nil { + bt.lifetime = new(TokenLifetime) + } + + err = bt.lifetime.FromGRPCMessage(lifetime) + if err != nil { + return err + } + } + + eacl := v.GetEaclTable() + if eacl == nil { + bt.eacl = nil + } else { + if bt.eacl == nil { + bt.eacl = new(Table) + } + + if err = bt.eacl.FromGRPCMessage(eacl); err != nil { + return err + } + } + + if apeOverrideGRPC := v.GetApeOverride(); apeOverrideGRPC != nil { + if bt.apeOverride == nil { + bt.apeOverride = new(APEOverride) + } + err = bt.apeOverride.FromGRPCMessage(apeOverrideGRPC) + if err != nil { + return err + } + } + + bt.impersonate = v.GetAllowImpersonate() + + return err +} + +func (bt *BearerToken) ToGRPCMessage() grpc.Message { + var m *acl.BearerToken + + if bt != nil { + m = new(acl.BearerToken) + + m.SetBody(bt.body.ToGRPCMessage().(*acl.BearerToken_Body)) + m.SetSignature(bt.sig.ToGRPCMessage().(*refsGRPC.Signature)) + } + + return m +} + +func (bt *BearerToken) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*acl.BearerToken) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + bt.body = nil + } else { + if bt.body == nil { + bt.body = new(BearerTokenBody) + } + + err = bt.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + sig := v.GetSignature() + if sig == nil { + bt.sig = nil + } else { + if bt.sig == nil { + bt.sig = new(refs.Signature) + } + + err = bt.sig.FromGRPCMessage(sig) + } + + return err +} diff --git a/apiv2/acl/filters.go b/apiv2/acl/filters.go new file mode 100644 index 0000000..c1d8afe --- /dev/null +++ b/apiv2/acl/filters.go @@ -0,0 +1,33 @@ +package acl + +// ObjectFilterPrefix is a prefix of key to object header value or property. +const ObjectFilterPrefix = "$Object:" + +const ( + // FilterObjectVersion is a filter key to "version" field of the object header. + FilterObjectVersion = ObjectFilterPrefix + "version" + + // FilterObjectID is a filter key to "object_id" field of the object. + FilterObjectID = ObjectFilterPrefix + "objectID" + + // FilterObjectContainerID is a filter key to "container_id" field of the object header. + FilterObjectContainerID = ObjectFilterPrefix + "containerID" + + // FilterObjectOwnerID is a filter key to "owner_id" field of the object header. + FilterObjectOwnerID = ObjectFilterPrefix + "ownerID" + + // FilterObjectCreationEpoch is a filter key to "creation_epoch" field of the object header. + FilterObjectCreationEpoch = ObjectFilterPrefix + "creationEpoch" + + // FilterObjectPayloadLength is a filter key to "payload_length" field of the object header. + FilterObjectPayloadLength = ObjectFilterPrefix + "payloadLength" + + // FilterObjectPayloadHash is a filter key to "payload_hash" field of the object header. + FilterObjectPayloadHash = ObjectFilterPrefix + "payloadHash" + + // FilterObjectType is a filter key to "object_type" field of the object header. + FilterObjectType = ObjectFilterPrefix + "objectType" + + // FilterObjectHomomorphicHash is a filter key to "homomorphic_hash" field of the object header. + FilterObjectHomomorphicHash = ObjectFilterPrefix + "homomorphicHash" +) diff --git a/apiv2/acl/grpc/types_frostfs.pb.go b/apiv2/acl/grpc/types_frostfs.pb.go new file mode 100644 index 0000000..e34267e Binary files /dev/null and b/apiv2/acl/grpc/types_frostfs.pb.go differ diff --git a/apiv2/acl/grpc/types_frostfs_fuzz.go b/apiv2/acl/grpc/types_frostfs_fuzz.go new file mode 100644 index 0000000..5d5b763 --- /dev/null +++ b/apiv2/acl/grpc/types_frostfs_fuzz.go @@ -0,0 +1,64 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package acl + +func DoFuzzProtoEACLRecord(data []byte) int { + msg := new(EACLRecord) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONEACLRecord(data []byte) int { + msg := new(EACLRecord) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoEACLTable(data []byte) int { + msg := new(EACLTable) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONEACLTable(data []byte) int { + msg := new(EACLTable) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoBearerToken(data []byte) int { + msg := new(BearerToken) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONBearerToken(data []byte) int { + msg := new(BearerToken) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/acl/grpc/types_frostfs_test.go b/apiv2/acl/grpc/types_frostfs_test.go new file mode 100644 index 0000000..c6d1c43 --- /dev/null +++ b/apiv2/acl/grpc/types_frostfs_test.go @@ -0,0 +1,41 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package acl + +import ( + testing "testing" +) + +func FuzzProtoEACLRecord(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoEACLRecord(data) + }) +} +func FuzzJSONEACLRecord(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONEACLRecord(data) + }) +} +func FuzzProtoEACLTable(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoEACLTable(data) + }) +} +func FuzzJSONEACLTable(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONEACLTable(data) + }) +} +func FuzzProtoBearerToken(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoBearerToken(data) + }) +} +func FuzzJSONBearerToken(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONBearerToken(data) + }) +} diff --git a/apiv2/acl/json.go b/apiv2/acl/json.go new file mode 100644 index 0000000..64f9cbe --- /dev/null +++ b/apiv2/acl/json.go @@ -0,0 +1,70 @@ +package acl + +import ( + acl "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +func (f *HeaderFilter) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(f) +} + +func (f *HeaderFilter) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(f, data, new(acl.EACLRecord_Filter)) +} + +func (t *Target) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(t) +} + +func (t *Target) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(t, data, new(acl.EACLRecord_Target)) +} + +func (a *APEOverride) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(a) +} + +func (a *APEOverride) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(a, data, new(acl.BearerToken_Body_APEOverride)) +} + +func (r *Record) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(r) +} + +func (r *Record) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(r, data, new(acl.EACLRecord)) +} + +func (t *Table) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(t) +} + +func (t *Table) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(t, data, new(acl.EACLTable)) +} + +func (l *TokenLifetime) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(l) +} + +func (l *TokenLifetime) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(l, data, new(acl.BearerToken_Body_TokenLifetime)) +} + +func (bt *BearerTokenBody) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(bt) +} + +func (bt *BearerTokenBody) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(bt, data, new(acl.BearerToken_Body)) +} + +func (bt *BearerToken) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(bt) +} + +func (bt *BearerToken) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(bt, data, new(acl.BearerToken)) +} diff --git a/apiv2/acl/marshal.go b/apiv2/acl/marshal.go new file mode 100644 index 0000000..e05c475 --- /dev/null +++ b/apiv2/acl/marshal.go @@ -0,0 +1,350 @@ +package acl + +import ( + acl "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + protoutil "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto" +) + +const ( + filterHeaderTypeField = 1 + filterMatchTypeField = 2 + filterNameField = 3 + filterValueField = 4 + + targetTypeField = 1 + targetKeysField = 2 + + recordOperationField = 1 + recordActionField = 2 + recordFiltersField = 3 + recordTargetsField = 4 + + tableVersionField = 1 + tableContainerIDField = 2 + tableRecordsField = 3 + + lifetimeExpirationField = 1 + lifetimeNotValidBeforeField = 2 + lifetimeIssuedAtField = 3 + + tokenAPEChainsTargetField = 1 + tokenAPEChainsChainsField = 2 + + bearerTokenBodyACLField = 1 + bearerTokenBodyOwnerField = 2 + bearerTokenBodyLifetimeField = 3 + bearerTokenBodyImpersonate = 4 + bearerTokenTokenAPEChainsField = 5 + + bearerTokenBodyField = 1 + bearerTokenSignatureField = 2 +) + +// StableMarshal marshals unified acl table structure in a protobuf +// compatible way without field order shuffle. +func (t *Table) StableMarshal(buf []byte) []byte { + if t == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, t.StableSize()) + } + + var offset int + + offset += protoutil.NestedStructureMarshal(tableVersionField, buf[offset:], t.version) + offset += protoutil.NestedStructureMarshal(tableContainerIDField, buf[offset:], t.cid) + + for i := range t.records { + offset += protoutil.NestedStructureMarshal(tableRecordsField, buf[offset:], &t.records[i]) + } + + return buf +} + +// StableSize of acl table structure marshalled by StableMarshal function. +func (t *Table) StableSize() (size int) { + if t == nil { + return 0 + } + + size += protoutil.NestedStructureSize(tableVersionField, t.version) + size += protoutil.NestedStructureSize(tableContainerIDField, t.cid) + + for i := range t.records { + size += protoutil.NestedStructureSize(tableRecordsField, &t.records[i]) + } + + return size +} + +func (t *Table) Unmarshal(data []byte) error { + return message.Unmarshal(t, data, new(acl.EACLTable)) +} + +// StableMarshal marshals unified acl record structure in a protobuf +// compatible way without field order shuffle. +func (r *Record) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += protoutil.EnumMarshal(recordOperationField, buf[offset:], int32(r.op)) + offset += protoutil.EnumMarshal(recordActionField, buf[offset:], int32(r.action)) + + for i := range r.filters { + offset += protoutil.NestedStructureMarshal(recordFiltersField, buf[offset:], &r.filters[i]) + } + + for i := range r.targets { + offset += protoutil.NestedStructureMarshal(recordTargetsField, buf[offset:], &r.targets[i]) + } + + return buf +} + +// StableSize of acl record structure marshalled by StableMarshal function. +func (r *Record) StableSize() (size int) { + if r == nil { + return 0 + } + + size += protoutil.EnumSize(recordOperationField, int32(r.op)) + size += protoutil.EnumSize(recordActionField, int32(r.action)) + + for i := range r.filters { + size += protoutil.NestedStructureSize(recordFiltersField, &r.filters[i]) + } + + for i := range r.targets { + size += protoutil.NestedStructureSize(recordTargetsField, &r.targets[i]) + } + + return size +} + +func (r *Record) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(acl.EACLRecord)) +} + +// StableMarshal marshals unified header filter structure in a protobuf +// compatible way without field order shuffle. +func (f *HeaderFilter) StableMarshal(buf []byte) []byte { + if f == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, f.StableSize()) + } + + var offset int + + offset += protoutil.EnumMarshal(filterHeaderTypeField, buf[offset:], int32(f.hdrType)) + offset += protoutil.EnumMarshal(filterMatchTypeField, buf[offset:], int32(f.matchType)) + offset += protoutil.StringMarshal(filterNameField, buf[offset:], f.key) + protoutil.StringMarshal(filterValueField, buf[offset:], f.value) + + return buf +} + +// StableSize of header filter structure marshalled by StableMarshal function. +func (f *HeaderFilter) StableSize() (size int) { + if f == nil { + return 0 + } + + size += protoutil.EnumSize(filterHeaderTypeField, int32(f.hdrType)) + size += protoutil.EnumSize(filterMatchTypeField, int32(f.matchType)) + size += protoutil.StringSize(filterNameField, f.key) + size += protoutil.StringSize(filterValueField, f.value) + + return size +} + +func (f *HeaderFilter) Unmarshal(data []byte) error { + return message.Unmarshal(f, data, new(acl.EACLRecord_Filter)) +} + +// StableMarshal marshals unified role info structure in a protobuf +// compatible way without field order shuffle. +func (t *Target) StableMarshal(buf []byte) []byte { + if t == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, t.StableSize()) + } + + var offset int + + offset += protoutil.EnumMarshal(targetTypeField, buf[offset:], int32(t.role)) + protoutil.RepeatedBytesMarshal(targetKeysField, buf[offset:], t.keys) + + return buf +} + +// StableSize of role info structure marshalled by StableMarshal function. +func (t *Target) StableSize() (size int) { + if t == nil { + return 0 + } + + size += protoutil.EnumSize(targetTypeField, int32(t.role)) + size += protoutil.RepeatedBytesSize(targetKeysField, t.keys) + + return size +} + +func (t *Target) Unmarshal(data []byte) error { + return message.Unmarshal(t, data, new(acl.EACLRecord_Target)) +} + +func (l *TokenLifetime) StableMarshal(buf []byte) []byte { + if l == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, l.StableSize()) + } + + var offset int + + offset += protoutil.UInt64Marshal(lifetimeExpirationField, buf[offset:], l.exp) + offset += protoutil.UInt64Marshal(lifetimeNotValidBeforeField, buf[offset:], l.nbf) + protoutil.UInt64Marshal(lifetimeIssuedAtField, buf[offset:], l.iat) + + return buf +} + +func (l *TokenLifetime) StableSize() (size int) { + if l == nil { + return 0 + } + + size += protoutil.UInt64Size(lifetimeExpirationField, l.exp) + size += protoutil.UInt64Size(lifetimeNotValidBeforeField, l.nbf) + size += protoutil.UInt64Size(lifetimeIssuedAtField, l.iat) + + return size +} + +func (l *TokenLifetime) Unmarshal(data []byte) error { + return message.Unmarshal(l, data, new(acl.BearerToken_Body_TokenLifetime)) +} + +func (c *APEOverride) StableMarshal(buf []byte) []byte { + if c == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, c.StableSize()) + } + + var offset int + + offset += protoutil.NestedStructureMarshal(tokenAPEChainsTargetField, buf[offset:], c.target) + for i := range c.chains { + offset += protoutil.NestedStructureMarshal(tokenAPEChainsChainsField, buf[offset:], c.chains[i]) + } + + return buf +} + +func (c *APEOverride) StableSize() (size int) { + if c == nil { + return 0 + } + + size += protoutil.NestedStructureSize(tokenAPEChainsTargetField, c.target) + for i := range c.chains { + size += protoutil.NestedStructureSize(tokenAPEChainsChainsField, c.chains[i]) + } + + return size +} + +func (c *APEOverride) Unmarshal(data []byte) error { + return message.Unmarshal(c, data, new(acl.BearerToken_Body_APEOverride)) +} + +func (bt *BearerTokenBody) StableMarshal(buf []byte) []byte { + if bt == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, bt.StableSize()) + } + + var offset int + + offset += protoutil.NestedStructureMarshal(bearerTokenBodyACLField, buf[offset:], bt.eacl) + offset += protoutil.NestedStructureMarshal(bearerTokenBodyOwnerField, buf[offset:], bt.ownerID) + offset += protoutil.NestedStructureMarshal(bearerTokenBodyLifetimeField, buf[offset:], bt.lifetime) + offset += protoutil.BoolMarshal(bearerTokenBodyImpersonate, buf[offset:], bt.impersonate) + protoutil.NestedStructureMarshal(bearerTokenTokenAPEChainsField, buf[offset:], bt.apeOverride) + + return buf +} + +func (bt *BearerTokenBody) StableSize() (size int) { + if bt == nil { + return 0 + } + + size += protoutil.NestedStructureSize(bearerTokenBodyACLField, bt.eacl) + size += protoutil.NestedStructureSize(bearerTokenBodyOwnerField, bt.ownerID) + size += protoutil.NestedStructureSize(bearerTokenBodyLifetimeField, bt.lifetime) + size += protoutil.BoolSize(bearerTokenBodyImpersonate, bt.impersonate) + size += protoutil.NestedStructureSize(bearerTokenTokenAPEChainsField, bt.apeOverride) + + return size +} + +func (bt *BearerTokenBody) Unmarshal(data []byte) error { + return message.Unmarshal(bt, data, new(acl.BearerToken_Body)) +} + +func (bt *BearerToken) StableMarshal(buf []byte) []byte { + if bt == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, bt.StableSize()) + } + + var offset int + + offset += protoutil.NestedStructureMarshal(bearerTokenBodyField, buf[offset:], bt.body) + protoutil.NestedStructureMarshal(bearerTokenSignatureField, buf[offset:], bt.sig) + + return buf +} + +func (bt *BearerToken) StableSize() (size int) { + if bt == nil { + return 0 + } + + size += protoutil.NestedStructureSize(bearerTokenBodyField, bt.body) + size += protoutil.NestedStructureSize(bearerTokenSignatureField, bt.sig) + + return size +} + +func (bt *BearerToken) Unmarshal(data []byte) error { + return message.Unmarshal(bt, data, new(acl.BearerToken)) +} diff --git a/apiv2/acl/message_test.go b/apiv2/acl/message_test.go new file mode 100644 index 0000000..bc16637 --- /dev/null +++ b/apiv2/acl/message_test.go @@ -0,0 +1,21 @@ +package acl_test + +import ( + "testing" + + acltest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl/test" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + messagetest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message/test" +) + +func TestMessageConvert(t *testing.T) { + messagetest.TestRPCMessage(t, + func(empty bool) message.Message { return acltest.GenerateFilter(empty) }, + func(empty bool) message.Message { return acltest.GenerateTarget(empty) }, + func(empty bool) message.Message { return acltest.GenerateRecord(empty) }, + func(empty bool) message.Message { return acltest.GenerateTable(empty) }, + func(empty bool) message.Message { return acltest.GenerateTokenLifetime(empty) }, + func(empty bool) message.Message { return acltest.GenerateBearerTokenBody(empty) }, + func(empty bool) message.Message { return acltest.GenerateBearerToken(empty) }, + ) +} diff --git a/apiv2/acl/string.go b/apiv2/acl/string.go new file mode 100644 index 0000000..65801b0 --- /dev/null +++ b/apiv2/acl/string.go @@ -0,0 +1,110 @@ +package acl + +import ( + acl "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl/grpc" +) + +// String returns string representation of Action. +func (x Action) String() string { + return ActionToGRPCField(x).String() +} + +// FromString parses Action from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (x *Action) FromString(s string) bool { + var g acl.Action + + ok := g.FromString(s) + + if ok { + *x = ActionFromGRPCField(g) + } + + return ok +} + +// String returns string representation of Role. +func (x Role) String() string { + return RoleToGRPCField(x).String() +} + +// FromString parses Role from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (x *Role) FromString(s string) bool { + var g acl.Role + + ok := g.FromString(s) + + if ok { + *x = RoleFromGRPCField(g) + } + + return ok +} + +// String returns string representation of Operation. +func (x Operation) String() string { + return OperationToGRPCField(x).String() +} + +// FromString parses Operation from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (x *Operation) FromString(s string) bool { + var g acl.Operation + + ok := g.FromString(s) + + if ok { + *x = OperationFromGRPCField(g) + } + + return ok +} + +// String returns string representation of MatchType. +func (x MatchType) String() string { + return MatchTypeToGRPCField(x).String() +} + +// FromString parses MatchType from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (x *MatchType) FromString(s string) bool { + var g acl.MatchType + + ok := g.FromString(s) + + if ok { + *x = MatchTypeFromGRPCField(g) + } + + return ok +} + +// String returns string representation of HeaderType. +func (x HeaderType) String() string { + return HeaderTypeToGRPCField(x).String() +} + +// FromString parses HeaderType from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (x *HeaderType) FromString(s string) bool { + var g acl.HeaderType + + ok := g.FromString(s) + + if ok { + *x = HeaderTypeFromGRPCField(g) + } + + return ok +} diff --git a/apiv2/acl/test/generate.go b/apiv2/acl/test/generate.go new file mode 100644 index 0000000..1e07c8f --- /dev/null +++ b/apiv2/acl/test/generate.go @@ -0,0 +1,144 @@ +package acltest + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" + apetest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape/test" + accountingtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/test" +) + +func GenerateBearerToken(empty bool) *acl.BearerToken { + m := new(acl.BearerToken) + + if !empty { + m.SetBody(GenerateBearerTokenBody(false)) + } + + m.SetSignature(accountingtest.GenerateSignature(empty)) + + return m +} + +func GenerateBearerTokenBody(empty bool) *acl.BearerTokenBody { + m := new(acl.BearerTokenBody) + + if !empty { + m.SetOwnerID(accountingtest.GenerateOwnerID(false)) + m.SetLifetime(GenerateTokenLifetime(false)) + m.SetAPEOverride(GenerateAPEOverride(empty)) + } + + return m +} + +func GenerateAPEOverride(empty bool) *acl.APEOverride { + var m *acl.APEOverride + + if !empty { + m = new(acl.APEOverride) + m.SetTarget(apetest.GenerateChainTarget(empty)) + m.SetChains(apetest.GenerateRawChains(false, 3)) + } + + return m +} + +func GenerateTable(empty bool) *acl.Table { + m := new(acl.Table) + + if !empty { + m.SetRecords(GenerateRecords(false)) + m.SetContainerID(accountingtest.GenerateContainerID(false)) + } + + m.SetVersion(accountingtest.GenerateVersion(empty)) + + return m +} + +func GenerateRecords(empty bool) []acl.Record { + var rs []acl.Record + + if !empty { + rs = append(rs, + *GenerateRecord(false), + *GenerateRecord(false), + ) + } + + return rs +} + +func GenerateRecord(empty bool) *acl.Record { + m := new(acl.Record) + + if !empty { + m.SetAction(acl.ActionAllow) + m.SetOperation(acl.OperationGet) + m.SetFilters(GenerateFilters(false)) + m.SetTargets(GenerateTargets(false)) + } + + return m +} + +func GenerateFilters(empty bool) []acl.HeaderFilter { + var fs []acl.HeaderFilter + + if !empty { + fs = append(fs, + *GenerateFilter(false), + *GenerateFilter(false), + ) + } + + return fs +} + +func GenerateFilter(empty bool) *acl.HeaderFilter { + m := new(acl.HeaderFilter) + + if !empty { + m.SetKey("key") + m.SetValue("val") + m.SetHeaderType(acl.HeaderTypeRequest) + m.SetMatchType(acl.MatchTypeStringEqual) + } + + return m +} + +func GenerateTargets(empty bool) []acl.Target { + var ts []acl.Target + + if !empty { + ts = append(ts, + *GenerateTarget(false), + *GenerateTarget(false), + ) + } + + return ts +} + +func GenerateTarget(empty bool) *acl.Target { + m := new(acl.Target) + + if !empty { + m.SetRole(acl.RoleSystem) + m.SetKeys([][]byte{{1}, {2}}) + } + + return m +} + +func GenerateTokenLifetime(empty bool) *acl.TokenLifetime { + m := new(acl.TokenLifetime) + + if !empty { + m.SetExp(1) + m.SetIat(2) + m.SetExp(3) + } + + return m +} diff --git a/apiv2/acl/types.go b/apiv2/acl/types.go new file mode 100644 index 0000000..06eb2f0 --- /dev/null +++ b/apiv2/acl/types.go @@ -0,0 +1,426 @@ +package acl + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" +) + +// HeaderFilter is a unified structure of FilterInfo +// message from proto definition. +type HeaderFilter struct { + hdrType HeaderType + + matchType MatchType + + key, value string +} + +// Target is a unified structure of Target +// message from proto definition. +type Target struct { + role Role + + keys [][]byte +} + +// Record is a unified structure of EACLRecord +// message from proto definition. +type Record struct { + op Operation + + action Action + + filters []HeaderFilter + + targets []Target +} + +// Table is a unified structure of EACLTable +// message from proto definition. +type Table struct { + version *refs.Version + + cid *refs.ContainerID + + records []Record +} + +type TokenLifetime struct { + exp, nbf, iat uint64 +} + +type APEOverride struct { + target *ape.ChainTarget + + chains []*ape.Chain +} + +type BearerTokenBody struct { + eacl *Table + + ownerID *refs.OwnerID + + lifetime *TokenLifetime + + apeOverride *APEOverride + + impersonate bool +} + +type BearerToken struct { + body *BearerTokenBody + + sig *refs.Signature +} + +// Target is a unified enum of MatchType enum from proto definition. +type MatchType uint32 + +// HeaderType is a unified enum of HeaderType enum from proto definition. +type HeaderType uint32 + +// Action is a unified enum of Action enum from proto definition. +type Action uint32 + +// Operation is a unified enum of Operation enum from proto definition. +type Operation uint32 + +// Role is a unified enum of Role enum from proto definition. +type Role uint32 + +const ( + MatchTypeUnknown MatchType = iota + MatchTypeStringEqual + MatchTypeStringNotEqual +) + +const ( + HeaderTypeUnknown HeaderType = iota + HeaderTypeRequest + HeaderTypeObject + HeaderTypeService +) + +const ( + ActionUnknown Action = iota + ActionAllow + ActionDeny +) + +const ( + OperationUnknown Operation = iota + OperationGet + OperationHead + OperationPut + OperationDelete + OperationSearch + OperationRange + OperationRangeHash +) + +const ( + RoleUnknown Role = iota + RoleUser + RoleSystem + RoleOthers +) + +func (f *HeaderFilter) GetHeaderType() HeaderType { + if f != nil { + return f.hdrType + } + + return HeaderTypeUnknown +} + +func (f *HeaderFilter) SetHeaderType(v HeaderType) { + f.hdrType = v +} + +func (f *HeaderFilter) GetMatchType() MatchType { + if f != nil { + return f.matchType + } + + return MatchTypeUnknown +} + +func (f *HeaderFilter) SetMatchType(v MatchType) { + f.matchType = v +} + +func (f *HeaderFilter) GetKey() string { + if f != nil { + return f.key + } + + return "" +} + +func (f *HeaderFilter) SetKey(v string) { + f.key = v +} + +func (f *HeaderFilter) GetValue() string { + if f != nil { + return f.value + } + + return "" +} + +func (f *HeaderFilter) SetValue(v string) { + f.value = v +} + +func (t *Target) GetRole() Role { + if t != nil { + return t.role + } + + return RoleUnknown +} + +func (t *Target) SetRole(v Role) { + t.role = v +} + +func (t *Target) GetKeys() [][]byte { + if t != nil { + return t.keys + } + + return nil +} + +func (t *Target) SetKeys(v [][]byte) { + t.keys = v +} + +func (r *Record) GetOperation() Operation { + if r != nil { + return r.op + } + + return OperationUnknown +} + +func (r *Record) SetOperation(v Operation) { + r.op = v +} + +func (r *Record) GetAction() Action { + if r != nil { + return r.action + } + + return ActionUnknown +} + +func (r *Record) SetAction(v Action) { + r.action = v +} + +func (r *Record) GetFilters() []HeaderFilter { + if r != nil { + return r.filters + } + + return nil +} + +func (r *Record) SetFilters(v []HeaderFilter) { + r.filters = v +} + +func (r *Record) GetTargets() []Target { + if r != nil { + return r.targets + } + + return nil +} + +func (r *Record) SetTargets(v []Target) { + r.targets = v +} + +func (t *Table) GetVersion() *refs.Version { + if t != nil { + return t.version + } + + return nil +} + +func (t *Table) SetVersion(v *refs.Version) { + t.version = v +} + +func (t *Table) GetContainerID() *refs.ContainerID { + if t != nil { + return t.cid + } + + return nil +} + +func (t *Table) SetContainerID(v *refs.ContainerID) { + t.cid = v +} + +func (t *Table) GetRecords() []Record { + if t != nil { + return t.records + } + + return nil +} + +func (t *Table) SetRecords(v []Record) { + t.records = v +} + +func (l *TokenLifetime) GetExp() uint64 { + if l != nil { + return l.exp + } + + return 0 +} + +func (l *TokenLifetime) SetExp(v uint64) { + l.exp = v +} + +func (l *TokenLifetime) GetNbf() uint64 { + if l != nil { + return l.nbf + } + + return 0 +} + +func (l *TokenLifetime) SetNbf(v uint64) { + l.nbf = v +} + +func (l *TokenLifetime) GetIat() uint64 { + if l != nil { + return l.iat + } + + return 0 +} + +func (l *TokenLifetime) SetIat(v uint64) { + l.iat = v +} + +func (bt *BearerTokenBody) GetEACL() *Table { + if bt != nil { + return bt.eacl + } + + return nil +} + +func (bt *BearerTokenBody) SetEACL(v *Table) { + bt.eacl = v +} + +func (t *APEOverride) GetTarget() *ape.ChainTarget { + if t == nil { + return nil + } + + return t.target +} + +func (t *APEOverride) GetChains() []*ape.Chain { + if t == nil { + return nil + } + + return t.chains +} + +func (t *APEOverride) SetTarget(v *ape.ChainTarget) { + t.target = v +} + +func (t *APEOverride) SetChains(v []*ape.Chain) { + t.chains = v +} + +func (bt *BearerTokenBody) GetAPEOverride() *APEOverride { + if bt != nil { + return bt.apeOverride + } + + return nil +} + +func (bt *BearerTokenBody) SetAPEOverride(v *APEOverride) { + bt.apeOverride = v +} + +func (bt *BearerTokenBody) GetOwnerID() *refs.OwnerID { + if bt != nil { + return bt.ownerID + } + + return nil +} + +func (bt *BearerTokenBody) SetOwnerID(v *refs.OwnerID) { + bt.ownerID = v +} + +func (bt *BearerTokenBody) GetLifetime() *TokenLifetime { + if bt != nil { + return bt.lifetime + } + + return nil +} + +func (bt *BearerTokenBody) SetLifetime(v *TokenLifetime) { + bt.lifetime = v +} + +func (bt *BearerTokenBody) GetImpersonate() bool { + if bt != nil { + return bt.impersonate + } + + return false +} + +func (bt *BearerTokenBody) SetImpersonate(v bool) { + bt.impersonate = v +} + +func (bt *BearerToken) GetBody() *BearerTokenBody { + if bt != nil { + return bt.body + } + + return nil +} + +func (bt *BearerToken) SetBody(v *BearerTokenBody) { + bt.body = v +} + +func (bt *BearerToken) GetSignature() *refs.Signature { + if bt != nil { + return bt.sig + } + + return nil +} + +func (bt *BearerToken) SetSignature(v *refs.Signature) { + bt.sig = v +} diff --git a/apiv2/ape/convert.go b/apiv2/ape/convert.go new file mode 100644 index 0000000..f70acf3 --- /dev/null +++ b/apiv2/ape/convert.go @@ -0,0 +1,132 @@ +package ape + +import ( + "fmt" + + ape "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +func TargetTypeToGRPCField(typ TargetType) ape.TargetType { + switch typ { + case TargetTypeNamespace: + return ape.TargetType_NAMESPACE + case TargetTypeContainer: + return ape.TargetType_CONTAINER + case TargetTypeUser: + return ape.TargetType_USER + case TargetTypeGroup: + return ape.TargetType_GROUP + default: + return ape.TargetType_UNDEFINED + } +} + +func TargetTypeFromGRPCField(typ ape.TargetType) TargetType { + switch typ { + case ape.TargetType_NAMESPACE: + return TargetTypeNamespace + case ape.TargetType_CONTAINER: + return TargetTypeContainer + case ape.TargetType_USER: + return TargetTypeUser + case ape.TargetType_GROUP: + return TargetTypeGroup + default: + return TargetTypeUndefined + } +} + +func TargetTypeToGRPC(typ TargetType) ape.TargetType { + return ape.TargetType(typ) +} + +func TargetTypeFromGRPC(typ ape.TargetType) TargetType { + return TargetType(typ) +} + +func (v2 *ChainTarget) ToGRPCMessage() grpc.Message { + var mgrpc *ape.ChainTarget + + if v2 != nil { + mgrpc = new(ape.ChainTarget) + + mgrpc.SetType(TargetTypeToGRPC(v2.GetTargetType())) + mgrpc.SetName(v2.GetName()) + } + + return mgrpc +} + +func (v2 *ChainTarget) FromGRPCMessage(m grpc.Message) error { + mgrpc, ok := m.(*ape.ChainTarget) + if !ok { + return message.NewUnexpectedMessageType(m, mgrpc) + } + + v2.SetTargetType(TargetTypeFromGRPC(mgrpc.GetType())) + v2.SetName(mgrpc.GetName()) + + return nil +} + +func (v2 *ChainRaw) ToGRPCMessage() grpc.Message { + var mgrpc *ape.Chain_Raw + + if v2 != nil { + mgrpc = new(ape.Chain_Raw) + + mgrpc.SetRaw(v2.GetRaw()) + } + + return mgrpc +} + +func (v2 *ChainRaw) FromGRPCMessage(m grpc.Message) error { + mgrpc, ok := m.(*ape.Chain_Raw) + if !ok { + return message.NewUnexpectedMessageType(m, mgrpc) + } + + v2.SetRaw(mgrpc.GetRaw()) + + return nil +} + +func (v2 *Chain) ToGRPCMessage() grpc.Message { + var mgrpc *ape.Chain + + if v2 != nil { + mgrpc = new(ape.Chain) + + switch chainKind := v2.GetKind().(type) { + default: + panic(fmt.Sprintf("unsupported chain kind: %T", chainKind)) + case *ChainRaw: + mgrpc.SetKind(chainKind.ToGRPCMessage().(*ape.Chain_Raw)) + } + } + + return mgrpc +} + +func (v2 *Chain) FromGRPCMessage(m grpc.Message) error { + mgrpc, ok := m.(*ape.Chain) + if !ok { + return message.NewUnexpectedMessageType(m, mgrpc) + } + + switch chainKind := mgrpc.GetKind().(type) { + default: + return fmt.Errorf("unsupported chain kind: %T", chainKind) + case *ape.Chain_Raw: + chainRaw := new(ChainRaw) + if err := chainRaw.FromGRPCMessage(chainKind); err != nil { + return err + } + v2.SetKind(chainRaw) + } + + return nil +} diff --git a/apiv2/ape/grpc/types_frostfs.pb.go b/apiv2/ape/grpc/types_frostfs.pb.go new file mode 100644 index 0000000..fe31b5a Binary files /dev/null and b/apiv2/ape/grpc/types_frostfs.pb.go differ diff --git a/apiv2/ape/grpc/types_frostfs_fuzz.go b/apiv2/ape/grpc/types_frostfs_fuzz.go new file mode 100644 index 0000000..b7bf367 --- /dev/null +++ b/apiv2/ape/grpc/types_frostfs_fuzz.go @@ -0,0 +1,45 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package ape + +func DoFuzzProtoChainTarget(data []byte) int { + msg := new(ChainTarget) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONChainTarget(data []byte) int { + msg := new(ChainTarget) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoChain(data []byte) int { + msg := new(Chain) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONChain(data []byte) int { + msg := new(Chain) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/ape/grpc/types_frostfs_test.go b/apiv2/ape/grpc/types_frostfs_test.go new file mode 100644 index 0000000..93d7eea --- /dev/null +++ b/apiv2/ape/grpc/types_frostfs_test.go @@ -0,0 +1,31 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package ape + +import ( + testing "testing" +) + +func FuzzProtoChainTarget(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoChainTarget(data) + }) +} +func FuzzJSONChainTarget(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONChainTarget(data) + }) +} +func FuzzProtoChain(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoChain(data) + }) +} +func FuzzJSONChain(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONChain(data) + }) +} diff --git a/apiv2/ape/json.go b/apiv2/ape/json.go new file mode 100644 index 0000000..498cb1e --- /dev/null +++ b/apiv2/ape/json.go @@ -0,0 +1,14 @@ +package ape + +import ( + ape "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +func (t *ChainTarget) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(t) +} + +func (t *ChainTarget) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(t, data, new(ape.ChainTarget)) +} diff --git a/apiv2/ape/marshal.go b/apiv2/ape/marshal.go new file mode 100644 index 0000000..0263b49 --- /dev/null +++ b/apiv2/ape/marshal.go @@ -0,0 +1,92 @@ +package ape + +import ( + "fmt" + + ape "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto" +) + +const ( + chainTargetTargetTypeField = 1 + chainTargetNameField = 2 + + chainRawField = 1 +) + +func (t *ChainTarget) StableSize() (size int) { + if t == nil { + return 0 + } + + size += proto.EnumSize(chainTargetTargetTypeField, int32(t.targeType)) + size += proto.StringSize(chainTargetNameField, t.name) + + return size +} + +func (t *ChainTarget) StableMarshal(buf []byte) []byte { + if t == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, t.StableSize()) + } + + var offset int + + offset += proto.EnumMarshal(chainTargetTargetTypeField, buf[offset:], int32(t.targeType)) + proto.StringMarshal(chainTargetNameField, buf[offset:], t.name) + + return buf +} + +func (t *ChainTarget) Unmarshal(data []byte) error { + return message.Unmarshal(t, data, new(ape.ChainTarget)) +} + +func (c *Chain) StableSize() (size int) { + if c == nil { + return 0 + } + + switch v := c.GetKind().(type) { + case *ChainRaw: + if v != nil { + size += proto.BytesSize(chainRawField, v.GetRaw()) + } + default: + panic(fmt.Sprintf("unsupported chain kind: %T", v)) + } + + return size +} + +func (c *Chain) StableMarshal(buf []byte) []byte { + if c == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, c.StableSize()) + } + + var offset int + + switch v := c.GetKind().(type) { + case *ChainRaw: + if v != nil { + proto.BytesMarshal(chainRawField, buf[offset:], v.GetRaw()) + } + default: + panic(fmt.Sprintf("unsupported chain kind: %T", v)) + } + + return buf +} + +func (c *Chain) Unmarshal(data []byte) error { + return message.Unmarshal(c, data, new(ape.Chain)) +} diff --git a/apiv2/ape/message_test.go b/apiv2/ape/message_test.go new file mode 100644 index 0000000..31e38a7 --- /dev/null +++ b/apiv2/ape/message_test.go @@ -0,0 +1,15 @@ +package ape_test + +import ( + "testing" + + apetest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape/test" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + messagetest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message/test" +) + +func TestMessageConvert(t *testing.T) { + messagetest.TestRPCMessage(t, + func(empty bool) message.Message { return apetest.GenerateChainTarget(empty) }, + ) +} diff --git a/apiv2/ape/string.go b/apiv2/ape/string.go new file mode 100644 index 0000000..b823764 --- /dev/null +++ b/apiv2/ape/string.go @@ -0,0 +1,18 @@ +package ape + +import ( + apegrpc "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape/grpc" +) + +func (tt TargetType) String() string { + return TargetTypeToGRPCField(tt).String() +} + +func (tt *TargetType) FromString(s string) bool { + i, ok := apegrpc.TargetType_value[s] + if ok { + *tt = TargetType(i) + } + + return ok +} diff --git a/apiv2/ape/test/generate.go b/apiv2/ape/test/generate.go new file mode 100644 index 0000000..806d211 --- /dev/null +++ b/apiv2/ape/test/generate.go @@ -0,0 +1,71 @@ +package test + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape" +) + +func GenerateRawChains(empty bool, n int) []*ape.Chain { + if empty { + return []*ape.Chain{} + } + + res := make([]*ape.Chain, n) + for i := range res { + res[i] = GenerateRawChain(empty) + } + return res +} + +func GenerateRawChain(empty bool) *ape.Chain { + chRaw := new(ape.ChainRaw) + + if empty { + chRaw.SetRaw([]byte("{}")) + } else { + chRaw.SetRaw([]byte(`{ + "ID": "", + "Rules": [ + { + "Status": "Allow", + "Actions": { + "Inverted": false, + "Names": [ + "GetObject" + ] + }, + "Resources": { + "Inverted": false, + "Names": [ + "native:object/*" + ] + }, + "Any": false, + "Condition": [ + { + "Op": "StringEquals", + "Object": "Resource", + "Key": "Department", + "Value": "HR" + } + ] + } + ], + "MatchType": "DenyPriority" + }`)) + } + + ch := new(ape.Chain) + ch.SetKind(chRaw) + return ch +} + +func GenerateChainTarget(empty bool) *ape.ChainTarget { + m := new(ape.ChainTarget) + + if !empty { + m.SetTargetType(ape.TargetTypeContainer) + m.SetName("BzQw5HH3feoxFDD5tCT87Y1726qzgLfxEE7wgtoRzB3R") + } + + return m +} diff --git a/apiv2/ape/types.go b/apiv2/ape/types.go new file mode 100644 index 0000000..467a441 --- /dev/null +++ b/apiv2/ape/types.go @@ -0,0 +1,79 @@ +package ape + +type TargetType uint32 + +const ( + TargetTypeUndefined TargetType = iota + TargetTypeNamespace + TargetTypeContainer + TargetTypeUser + TargetTypeGroup +) + +type ChainTarget struct { + targeType TargetType + + name string +} + +func (ct *ChainTarget) SetTargetType(targeType TargetType) { + ct.targeType = targeType +} + +func (ct *ChainTarget) SetName(name string) { + ct.name = name +} + +func (ct *ChainTarget) GetTargetType() TargetType { + if ct != nil { + return ct.targeType + } + + return 0 +} + +func (ct *ChainTarget) GetName() string { + if ct != nil { + return ct.name + } + + return "" +} + +type chainKind interface { + isChainKind() +} + +type Chain struct { + kind chainKind +} + +func (c *Chain) SetKind(kind chainKind) { + c.kind = kind +} + +func (c *Chain) GetKind() chainKind { + if c == nil { + return nil + } + + return c.kind +} + +type ChainRaw struct { + Raw []byte +} + +func (*ChainRaw) isChainKind() {} + +func (c *ChainRaw) SetRaw(raw []byte) { + c.Raw = raw +} + +func (c *ChainRaw) GetRaw() []byte { + if c == nil { + return nil + } + + return c.Raw +} diff --git a/apiv2/apemanager/convert.go b/apiv2/apemanager/convert.go new file mode 100644 index 0000000..0ce02d1 --- /dev/null +++ b/apiv2/apemanager/convert.go @@ -0,0 +1,358 @@ +package apemanager + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape" + apeGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape/grpc" + apemanager "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/apemanager/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +func (reqBody *AddChainRequestBody) ToGRPCMessage() grpc.Message { + var reqBodygrpc *apemanager.AddChainRequest_Body + + if reqBody != nil { + reqBodygrpc = new(apemanager.AddChainRequest_Body) + + reqBodygrpc.SetTarget(reqBody.GetTarget().ToGRPCMessage().(*apeGRPC.ChainTarget)) + reqBodygrpc.SetChain(reqBody.GetChain().ToGRPCMessage().(*apeGRPC.Chain)) + } + + return reqBodygrpc +} + +func (reqBody *AddChainRequestBody) FromGRPCMessage(m grpc.Message) error { + reqBodygrpc, ok := m.(*apemanager.AddChainRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, reqBodygrpc) + } + + if targetgrpc := reqBodygrpc.GetTarget(); targetgrpc != nil { + reqBody.target = new(ape.ChainTarget) + if err := reqBody.target.FromGRPCMessage(targetgrpc); err != nil { + return err + } + } + + if chaingrpc := reqBodygrpc.GetChain(); chaingrpc != nil { + reqBody.chain = new(ape.Chain) + if err := reqBody.GetChain().FromGRPCMessage(chaingrpc); err != nil { + return err + } + } + + return nil +} + +func (req *AddChainRequest) ToGRPCMessage() grpc.Message { + var reqgrpc *apemanager.AddChainRequest + + if req != nil { + reqgrpc = new(apemanager.AddChainRequest) + + reqgrpc.SetBody(req.GetBody().ToGRPCMessage().(*apemanager.AddChainRequest_Body)) + req.RequestHeaders.ToMessage(reqgrpc) + } + + return reqgrpc +} + +func (req *AddChainRequest) FromGRPCMessage(m grpc.Message) error { + reqgrpc, ok := m.(*apemanager.AddChainRequest) + if !ok { + return message.NewUnexpectedMessageType(m, reqgrpc) + } + + if reqBodygrpc := reqgrpc.GetBody(); reqBodygrpc != nil { + req.body = new(AddChainRequestBody) + if err := req.body.FromGRPCMessage(reqBodygrpc); err != nil { + return err + } + } + + return req.RequestHeaders.FromMessage(reqgrpc) +} + +func (respBody *AddChainResponseBody) ToGRPCMessage() grpc.Message { + var respBodygrpc *apemanager.AddChainResponse_Body + + if respBody != nil { + respBodygrpc = new(apemanager.AddChainResponse_Body) + + respBodygrpc.SetChainId(respBody.GetChainID()) + } + + return respBodygrpc +} + +func (respBody *AddChainResponseBody) FromGRPCMessage(m grpc.Message) error { + respBodygrpc, ok := m.(*apemanager.AddChainResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, respBodygrpc) + } + + respBody.SetChainID(respBodygrpc.GetChainId()) + + return nil +} + +func (resp *AddChainResponse) ToGRPCMessage() grpc.Message { + var respgrpc *apemanager.AddChainResponse + + if resp != nil { + respgrpc = new(apemanager.AddChainResponse) + + respgrpc.SetBody(resp.body.ToGRPCMessage().(*apemanager.AddChainResponse_Body)) + resp.ResponseHeaders.ToMessage(respgrpc) + } + + return respgrpc +} + +func (resp *AddChainResponse) FromGRPCMessage(m grpc.Message) error { + respgrpc, ok := m.(*apemanager.AddChainResponse) + if !ok { + return message.NewUnexpectedMessageType(m, respgrpc) + } + + if respBodygrpc := respgrpc.GetBody(); respBodygrpc != nil { + resp.body = new(AddChainResponseBody) + if err := resp.body.FromGRPCMessage(respBodygrpc); err != nil { + return err + } + } + + return resp.ResponseHeaders.FromMessage(respgrpc) +} + +func (reqBody *RemoveChainRequestBody) ToGRPCMessage() grpc.Message { + var reqBodygrpc *apemanager.RemoveChainRequest_Body + + if reqBody != nil { + reqBodygrpc = new(apemanager.RemoveChainRequest_Body) + + reqBodygrpc.SetTarget(reqBody.target.ToGRPCMessage().(*apeGRPC.ChainTarget)) + reqBodygrpc.SetChainId(reqBody.GetChainID()) + } + + return reqBodygrpc +} + +func (reqBody *RemoveChainRequestBody) FromGRPCMessage(m grpc.Message) error { + reqBodygrpc, ok := m.(*apemanager.RemoveChainRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, reqBodygrpc) + } + + if targetgrpc := reqBodygrpc.GetTarget(); targetgrpc != nil { + reqBody.target = new(ape.ChainTarget) + if err := reqBody.target.FromGRPCMessage(targetgrpc); err != nil { + return err + } + } + + reqBody.SetChainID(reqBodygrpc.GetChainId()) + + return nil +} + +func (req *RemoveChainRequest) ToGRPCMessage() grpc.Message { + var reqgrpc *apemanager.RemoveChainRequest + + if req != nil { + reqgrpc = new(apemanager.RemoveChainRequest) + + reqgrpc.SetBody(req.body.ToGRPCMessage().(*apemanager.RemoveChainRequest_Body)) + req.RequestHeaders.ToMessage(reqgrpc) + } + + return reqgrpc +} + +func (req *RemoveChainRequest) FromGRPCMessage(m grpc.Message) error { + reqgrpc, ok := m.(*apemanager.RemoveChainRequest) + if !ok { + return message.NewUnexpectedMessageType(m, reqgrpc) + } + + if reqBodygrpc := reqgrpc.GetBody(); reqBodygrpc != nil { + req.body = new(RemoveChainRequestBody) + if err := req.body.FromGRPCMessage(reqBodygrpc); err != nil { + return err + } + } + + return req.RequestHeaders.FromMessage(reqgrpc) +} + +func (respBody *RemoveChainResponseBody) ToGRPCMessage() grpc.Message { + var respBodygrpc *apemanager.RemoveChainResponse_Body + + if respBody != nil { + respBodygrpc = new(apemanager.RemoveChainResponse_Body) + } + + return respBodygrpc +} + +func (respBody *RemoveChainResponseBody) FromGRPCMessage(m grpc.Message) error { + respBodygrpc, ok := m.(*apemanager.RemoveChainResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, respBodygrpc) + } + + return nil +} + +func (resp *RemoveChainResponse) ToGRPCMessage() grpc.Message { + var respgrpc *apemanager.RemoveChainResponse + + if resp != nil { + respgrpc = new(apemanager.RemoveChainResponse) + + respgrpc.SetBody(resp.body.ToGRPCMessage().(*apemanager.RemoveChainResponse_Body)) + resp.ResponseHeaders.ToMessage(respgrpc) + } + + return respgrpc +} + +func (resp *RemoveChainResponse) FromGRPCMessage(m grpc.Message) error { + respgrpc, ok := m.(*apemanager.RemoveChainResponse) + if !ok { + return message.NewUnexpectedMessageType(m, respgrpc) + } + + if respBodygrpc := respgrpc.GetBody(); respBodygrpc != nil { + resp.body = new(RemoveChainResponseBody) + if err := resp.body.FromGRPCMessage(respBodygrpc); err != nil { + return err + } + } + + return resp.ResponseHeaders.FromMessage(respgrpc) +} + +func (reqBody *ListChainsRequestBody) ToGRPCMessage() grpc.Message { + var reqBodygrpc *apemanager.ListChainsRequest_Body + + if reqBody != nil { + reqBodygrpc = new(apemanager.ListChainsRequest_Body) + + reqBodygrpc.SetTarget(reqBody.target.ToGRPCMessage().(*apeGRPC.ChainTarget)) + } + + return reqBodygrpc +} + +func (reqBody *ListChainsRequestBody) FromGRPCMessage(m grpc.Message) error { + reqBodygrpc, ok := m.(*apemanager.ListChainsRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, reqBodygrpc) + } + + if targetgrpc := reqBodygrpc.GetTarget(); targetgrpc != nil { + reqBody.target = new(ape.ChainTarget) + if err := reqBody.target.FromGRPCMessage(targetgrpc); err != nil { + return err + } + } + + return nil +} + +func (req *ListChainsRequest) ToGRPCMessage() grpc.Message { + var reqgrpc *apemanager.ListChainsRequest + + if req != nil { + reqgrpc = new(apemanager.ListChainsRequest) + + reqgrpc.SetBody(req.body.ToGRPCMessage().(*apemanager.ListChainsRequest_Body)) + req.RequestHeaders.ToMessage(reqgrpc) + } + + return reqgrpc +} + +func (req *ListChainsRequest) FromGRPCMessage(m grpc.Message) error { + reqgrpc, ok := m.(*apemanager.ListChainsRequest) + if !ok { + return message.NewUnexpectedMessageType(m, reqgrpc) + } + + if reqBodygrpc := reqgrpc.GetBody(); reqBodygrpc != nil { + req.body = new(ListChainsRequestBody) + if err := req.body.FromGRPCMessage(reqBodygrpc); err != nil { + return err + } + } + + return req.RequestHeaders.FromMessage(reqgrpc) +} + +func (respBody *ListChainsResponseBody) ToGRPCMessage() grpc.Message { + var respBodygrpc *apemanager.ListChainsResponse_Body + + if respBody != nil { + respBodygrpc = new(apemanager.ListChainsResponse_Body) + + chainsgrpc := make([]apeGRPC.Chain, 0, len(respBody.GetChains())) + for _, chain := range respBody.GetChains() { + chainsgrpc = append(chainsgrpc, *chain.ToGRPCMessage().(*apeGRPC.Chain)) + } + + respBodygrpc.SetChains(chainsgrpc) + } + + return respBodygrpc +} + +func (respBody *ListChainsResponseBody) FromGRPCMessage(m grpc.Message) error { + respBodygrpc, ok := m.(*apemanager.ListChainsResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, respBodygrpc) + } + + chains := make([]*ape.Chain, 0, len(respBodygrpc.GetChains())) + + for _, chaingrpc := range respBodygrpc.GetChains() { + chain := new(ape.Chain) + if err := chain.FromGRPCMessage(&chaingrpc); err != nil { + return err + } + chains = append(chains, chain) + } + + respBody.SetChains(chains) + + return nil +} + +func (resp *ListChainsResponse) ToGRPCMessage() grpc.Message { + var respgrpc *apemanager.ListChainsResponse + + if resp != nil { + respgrpc = new(apemanager.ListChainsResponse) + + respgrpc.SetBody(resp.body.ToGRPCMessage().(*apemanager.ListChainsResponse_Body)) + resp.ResponseHeaders.ToMessage(respgrpc) + } + + return respgrpc +} + +func (resp *ListChainsResponse) FromGRPCMessage(m grpc.Message) error { + respgrpc, ok := m.(*apemanager.ListChainsResponse) + if !ok { + return message.NewUnexpectedMessageType(m, respgrpc) + } + + if respBodygrpc := respgrpc.GetBody(); respBodygrpc != nil { + resp.body = new(ListChainsResponseBody) + if err := resp.body.FromGRPCMessage(respBodygrpc); err != nil { + return err + } + } + + return resp.ResponseHeaders.FromMessage(respgrpc) +} diff --git a/apiv2/apemanager/grpc/service_frostfs.pb.go b/apiv2/apemanager/grpc/service_frostfs.pb.go new file mode 100644 index 0000000..8141975 Binary files /dev/null and b/apiv2/apemanager/grpc/service_frostfs.pb.go differ diff --git a/apiv2/apemanager/grpc/service_frostfs_fuzz.go b/apiv2/apemanager/grpc/service_frostfs_fuzz.go new file mode 100644 index 0000000..08af63e --- /dev/null +++ b/apiv2/apemanager/grpc/service_frostfs_fuzz.go @@ -0,0 +1,121 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package apemanager + +func DoFuzzProtoAddChainRequest(data []byte) int { + msg := new(AddChainRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONAddChainRequest(data []byte) int { + msg := new(AddChainRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoAddChainResponse(data []byte) int { + msg := new(AddChainResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONAddChainResponse(data []byte) int { + msg := new(AddChainResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoRemoveChainRequest(data []byte) int { + msg := new(RemoveChainRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONRemoveChainRequest(data []byte) int { + msg := new(RemoveChainRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoRemoveChainResponse(data []byte) int { + msg := new(RemoveChainResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONRemoveChainResponse(data []byte) int { + msg := new(RemoveChainResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoListChainsRequest(data []byte) int { + msg := new(ListChainsRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONListChainsRequest(data []byte) int { + msg := new(ListChainsRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoListChainsResponse(data []byte) int { + msg := new(ListChainsResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONListChainsResponse(data []byte) int { + msg := new(ListChainsResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/apemanager/grpc/service_frostfs_test.go b/apiv2/apemanager/grpc/service_frostfs_test.go new file mode 100644 index 0000000..5c4653c --- /dev/null +++ b/apiv2/apemanager/grpc/service_frostfs_test.go @@ -0,0 +1,71 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package apemanager + +import ( + testing "testing" +) + +func FuzzProtoAddChainRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoAddChainRequest(data) + }) +} +func FuzzJSONAddChainRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONAddChainRequest(data) + }) +} +func FuzzProtoAddChainResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoAddChainResponse(data) + }) +} +func FuzzJSONAddChainResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONAddChainResponse(data) + }) +} +func FuzzProtoRemoveChainRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoRemoveChainRequest(data) + }) +} +func FuzzJSONRemoveChainRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONRemoveChainRequest(data) + }) +} +func FuzzProtoRemoveChainResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoRemoveChainResponse(data) + }) +} +func FuzzJSONRemoveChainResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONRemoveChainResponse(data) + }) +} +func FuzzProtoListChainsRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoListChainsRequest(data) + }) +} +func FuzzJSONListChainsRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONListChainsRequest(data) + }) +} +func FuzzProtoListChainsResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoListChainsResponse(data) + }) +} +func FuzzJSONListChainsResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONListChainsResponse(data) + }) +} diff --git a/apiv2/apemanager/grpc/service_grpc.pb.go b/apiv2/apemanager/grpc/service_grpc.pb.go new file mode 100644 index 0000000..4781427 Binary files /dev/null and b/apiv2/apemanager/grpc/service_grpc.pb.go differ diff --git a/apiv2/apemanager/marshal.go b/apiv2/apemanager/marshal.go new file mode 100644 index 0000000..b742248 --- /dev/null +++ b/apiv2/apemanager/marshal.go @@ -0,0 +1,205 @@ +package apemanager + +import ( + apemanager "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/apemanager/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto" +) + +const ( + addChainReqBodyTargetField = 1 + addChainReqBodyChainField = 2 + + addChainRespBodyChainIDField = 1 + + removeChainReqBodyTargetField = 1 + removeChainReqBodyChainField = 2 + + /* + Fields for RemoveResponseBody are missed since RemoveResponseBody is empty. + */ + + listChainsReqBodyTargetField = 1 + + listChainsRespBodyChainsField = 1 +) + +func (rb *AddChainRequestBody) StableSize() (size int) { + if rb == nil { + return 0 + } + + size += proto.NestedStructureSize(addChainReqBodyTargetField, rb.target) + size += proto.NestedStructureSize(addChainReqBodyChainField, rb.chain) + + return size +} + +func (rb *AddChainRequestBody) StableMarshal(buf []byte) []byte { + if rb == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, rb.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(addChainReqBodyTargetField, buf[offset:], rb.target) + proto.NestedStructureMarshal(addChainReqBodyChainField, buf[offset:], rb.chain) + + return buf +} + +func (rb *AddChainRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(rb, data, new(apemanager.AddChainRequest_Body)) +} + +func (rb *AddChainResponseBody) StableSize() (size int) { + if rb == nil { + return 0 + } + + size += proto.BytesSize(addChainRespBodyChainIDField, rb.chainID) + + return size +} + +func (rb *AddChainResponseBody) StableMarshal(buf []byte) []byte { + if rb == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, rb.StableSize()) + } + + var offset int + + proto.BytesMarshal(addChainRespBodyChainIDField, buf[offset:], rb.chainID) + + return buf +} + +func (rb *AddChainResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(rb, data, new(apemanager.AddChainResponse_Body)) +} + +func (rb *RemoveChainRequestBody) StableSize() (size int) { + if rb == nil { + return 0 + } + + size += proto.NestedStructureSize(addChainReqBodyTargetField, rb.target) + size += proto.BytesSize(addChainReqBodyChainField, rb.chainID) + + return size +} + +func (rb *RemoveChainRequestBody) StableMarshal(buf []byte) []byte { + if rb == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, rb.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(removeChainReqBodyTargetField, buf[offset:], rb.target) + proto.BytesMarshal(removeChainReqBodyChainField, buf[offset:], rb.chainID) + + return buf +} + +func (rb *RemoveChainRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(rb, data, new(apemanager.RemoveChainRequest_Body)) +} + +func (rb *RemoveChainResponseBody) StableSize() (size int) { + if rb == nil { + return 0 + } + + return size +} + +func (rb *RemoveChainResponseBody) StableMarshal(buf []byte) []byte { + if rb == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, rb.StableSize()) + } + + return buf +} + +func (rb *RemoveChainResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(rb, data, new(apemanager.RemoveChainResponse_Body)) +} + +func (rb *ListChainsRequestBody) StableSize() (size int) { + if rb == nil { + return 0 + } + + size += proto.NestedStructureSize(listChainsReqBodyTargetField, rb.target) + + return size +} + +func (rb *ListChainsRequestBody) StableMarshal(buf []byte) []byte { + if rb == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, rb.StableSize()) + } + + var offset int + proto.NestedStructureMarshal(addChainReqBodyTargetField, buf[offset:], rb.target) + + return buf +} + +func (rb *ListChainsRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(rb, data, new(apemanager.ListChainsRequest_Body)) +} + +func (rb *ListChainsResponseBody) StableSize() (size int) { + if rb == nil { + return 0 + } + + for _, chain := range rb.GetChains() { + size += proto.NestedStructureSize(listChainsRespBodyChainsField, chain) + } + + return size +} + +func (rb *ListChainsResponseBody) StableMarshal(buf []byte) []byte { + if rb == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, rb.StableSize()) + } + + var offset int + for _, chain := range rb.GetChains() { + offset += proto.NestedStructureMarshal(listChainsRespBodyChainsField, buf[offset:], chain) + } + + return buf +} + +func (rb *ListChainsResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(rb, data, new(apemanager.ListChainsResponse_Body)) +} diff --git a/apiv2/apemanager/message_test.go b/apiv2/apemanager/message_test.go new file mode 100644 index 0000000..caac5ee --- /dev/null +++ b/apiv2/apemanager/message_test.go @@ -0,0 +1,26 @@ +package apemanager_test + +import ( + "testing" + + apemanagertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/apemanager/test" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + messagetest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message/test" +) + +func TestMessageConvert(t *testing.T) { + messagetest.TestRPCMessage(t, + func(empty bool) message.Message { return apemanagertest.GenerateAddChainRequestBody(empty) }, + func(empty bool) message.Message { return apemanagertest.GenerateAddChainRequest(empty) }, + func(empty bool) message.Message { return apemanagertest.GenerateAddChainResponseBody(empty) }, + func(empty bool) message.Message { return apemanagertest.GenerateAddChainResponse(empty) }, + func(empty bool) message.Message { return apemanagertest.GenerateRemoveChainRequestBody(empty) }, + func(empty bool) message.Message { return apemanagertest.GenerateRemoveChainRequest(empty) }, + func(empty bool) message.Message { return apemanagertest.GenerateRemoveChainResponseBody(empty) }, + func(empty bool) message.Message { return apemanagertest.GenerateRemoveChainResponse(empty) }, + func(empty bool) message.Message { return apemanagertest.GenerateListChainsRequestBody(empty) }, + func(empty bool) message.Message { return apemanagertest.GenerateListChainsRequest(empty) }, + func(empty bool) message.Message { return apemanagertest.GenerateListChainsResponseBody(empty) }, + func(empty bool) message.Message { return apemanagertest.GenerateListChainsResponse(empty) }, + ) +} diff --git a/apiv2/apemanager/status.go b/apiv2/apemanager/status.go new file mode 100644 index 0000000..bf1ffa0 --- /dev/null +++ b/apiv2/apemanager/status.go @@ -0,0 +1,76 @@ +package apemanager + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" + statusgrpc "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status/grpc" +) + +// LocalizeFailStatus checks if passed global status.Code is related to ape manager failure and: +// +// then localizes the code and returns true, +// else leaves the code unchanged and returns false. +// +// Arg must be non-nil. +func LocalizeFailStatus(c *status.Code) bool { + return status.LocalizeIfInSection(c, uint32(statusgrpc.Section_SECTION_APE_MANAGER)) +} + +// GlobalizeFail globalizes local code of ape manager failure. +// +// Arg must be non-nil. +func GlobalizeFail(c *status.Code) { + c.GlobalizeSection(uint32(statusgrpc.Section_SECTION_APE_MANAGER)) +} + +const ( + // StatusAPEManagerAccessDenied is a local status.Code value for + // ACCESS_DENIED ape manager failure. + StatusAPEManagerAccessDenied status.Code = iota +) + +const ( + // detailAccessDeniedDesc is a StatusAccessDenied detail ID for + // human-readable description. + detailAccessDeniedDesc = iota +) + +// WriteAccessDeniedDesc writes human-readable description of StatusAccessDenied +// into status.Status as a detail. The status must not be nil. +// +// Existing details are expected to be ID-unique, otherwise undefined behavior. +func WriteAccessDeniedDesc(st *status.Status, desc string) { + var found bool + + st.IterateDetails(func(d *status.Detail) bool { + if d.ID() == detailAccessDeniedDesc { + found = true + d.SetValue([]byte(desc)) + } + + return found + }) + + if !found { + var d status.Detail + + d.SetID(detailAccessDeniedDesc) + d.SetValue([]byte(desc)) + + st.AppendDetails(d) + } +} + +// ReadAccessDeniedDesc looks up for status detail with human-readable description +// of StatusAccessDenied. Returns empty string if detail is missing. +func ReadAccessDeniedDesc(st status.Status) (desc string) { + st.IterateDetails(func(d *status.Detail) bool { + if d.ID() == detailAccessDeniedDesc { + desc = string(d.Value()) + return true + } + + return false + }) + + return +} diff --git a/apiv2/apemanager/status_test.go b/apiv2/apemanager/status_test.go new file mode 100644 index 0000000..3cccf16 --- /dev/null +++ b/apiv2/apemanager/status_test.go @@ -0,0 +1,30 @@ +package apemanager_test + +import ( + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/apemanager" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" + statustest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status/test" + "github.com/stretchr/testify/require" +) + +func TestStatusCodes(t *testing.T) { + statustest.TestCodes(t, apemanager.LocalizeFailStatus, apemanager.GlobalizeFail, + apemanager.StatusAPEManagerAccessDenied, 5120, + ) +} + +func TestAccessDeniedDesc(t *testing.T) { + var st status.Status + + require.Empty(t, apemanager.ReadAccessDeniedDesc(st)) + + const desc = "some description" + + apemanager.WriteAccessDeniedDesc(&st, desc) + require.Equal(t, desc, apemanager.ReadAccessDeniedDesc(st)) + + apemanager.WriteAccessDeniedDesc(&st, desc+"1") + require.Equal(t, desc+"1", apemanager.ReadAccessDeniedDesc(st)) +} diff --git a/apiv2/apemanager/test/generate.go b/apiv2/apemanager/test/generate.go new file mode 100644 index 0000000..c9bc41b --- /dev/null +++ b/apiv2/apemanager/test/generate.go @@ -0,0 +1,143 @@ +package apemanagertest + +import ( + apetest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape/test" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/apemanager" + sessiontest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session/test" +) + +func generateChainID(empty bool) []byte { + if empty { + return []byte{} + } + + return []byte("616c6c6f774f626a476574436e72") +} + +func GenerateAddChainRequestBody(empty bool) *apemanager.AddChainRequestBody { + m := new(apemanager.AddChainRequestBody) + + if !empty { + m.SetTarget(apetest.GenerateChainTarget(empty)) + m.SetChain(apetest.GenerateRawChain(empty)) + } + + return m +} + +func GenerateAddChainRequest(empty bool) *apemanager.AddChainRequest { + m := new(apemanager.AddChainRequest) + + if !empty { + m.SetBody(GenerateAddChainRequestBody(empty)) + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + } + + return m +} + +func GenerateAddChainResponseBody(empty bool) *apemanager.AddChainResponseBody { + m := new(apemanager.AddChainResponseBody) + + if !empty { + m.SetChainID(generateChainID(empty)) + } + + return m +} + +func GenerateAddChainResponse(empty bool) *apemanager.AddChainResponse { + m := new(apemanager.AddChainResponse) + + if !empty { + m.SetBody(GenerateAddChainResponseBody(empty)) + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + } + + return m +} + +func GenerateRemoveChainRequestBody(empty bool) *apemanager.RemoveChainRequestBody { + m := new(apemanager.RemoveChainRequestBody) + + if !empty { + m.SetChainID(generateChainID(empty)) + m.SetTarget(apetest.GenerateChainTarget(empty)) + } + + return m +} + +func GenerateRemoveChainRequest(empty bool) *apemanager.RemoveChainRequest { + m := new(apemanager.RemoveChainRequest) + + if !empty { + m.SetBody(GenerateRemoveChainRequestBody(empty)) + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + } + + return m +} + +func GenerateRemoveChainResponseBody(_ bool) *apemanager.RemoveChainResponseBody { + return new(apemanager.RemoveChainResponseBody) +} + +func GenerateRemoveChainResponse(empty bool) *apemanager.RemoveChainResponse { + m := new(apemanager.RemoveChainResponse) + + if !empty { + m.SetBody(GenerateRemoveChainResponseBody(empty)) + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + } + + return m +} + +func GenerateListChainsRequestBody(empty bool) *apemanager.ListChainsRequestBody { + m := new(apemanager.ListChainsRequestBody) + + if !empty { + m.SetTarget(apetest.GenerateChainTarget(empty)) + } + + return m +} + +func GenerateListChainsRequest(empty bool) *apemanager.ListChainsRequest { + m := new(apemanager.ListChainsRequest) + + if !empty { + m.SetBody(GenerateListChainsRequestBody(empty)) + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + } + + return m +} + +func GenerateListChainsResponseBody(empty bool) *apemanager.ListChainsResponseBody { + m := new(apemanager.ListChainsResponseBody) + + if !empty { + m.SetChains(apetest.GenerateRawChains(empty, 10)) + } + + return m +} + +func GenerateListChainsResponse(empty bool) *apemanager.ListChainsResponse { + m := new(apemanager.ListChainsResponse) + + if !empty { + m.SetBody(GenerateListChainsResponseBody(empty)) + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + } + + return m +} diff --git a/apiv2/apemanager/types.go b/apiv2/apemanager/types.go new file mode 100644 index 0000000..74554aa --- /dev/null +++ b/apiv2/apemanager/types.go @@ -0,0 +1,226 @@ +package apemanager + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/ape" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" +) + +type AddChainRequest struct { + body *AddChainRequestBody + + session.RequestHeaders +} + +func (r *AddChainRequest) SetBody(body *AddChainRequestBody) { + r.body = body +} + +func (r *AddChainRequest) GetBody() *AddChainRequestBody { + if r == nil { + return nil + } + + return r.body +} + +type AddChainRequestBody struct { + target *ape.ChainTarget + + chain *ape.Chain +} + +func (rb *AddChainRequestBody) SetTarget(target *ape.ChainTarget) { + rb.target = target +} + +func (rb *AddChainRequestBody) GetTarget() *ape.ChainTarget { + if rb == nil { + return nil + } + + return rb.target +} + +func (rb *AddChainRequestBody) SetChain(chain *ape.Chain) { + rb.chain = chain +} + +func (rb *AddChainRequestBody) GetChain() *ape.Chain { + if rb == nil { + return nil + } + + return rb.chain +} + +type AddChainResponse struct { + body *AddChainResponseBody + + session.ResponseHeaders +} + +func (r *AddChainResponse) SetBody(body *AddChainResponseBody) { + r.body = body +} + +func (r *AddChainResponse) GetBody() *AddChainResponseBody { + if r == nil { + return nil + } + + return r.body +} + +type AddChainResponseBody struct { + chainID []byte +} + +func (rb *AddChainResponseBody) SetChainID(chainID []byte) { + rb.chainID = chainID +} + +func (rb *AddChainResponseBody) GetChainID() []byte { + if rb == nil { + return nil + } + + return rb.chainID +} + +type RemoveChainRequest struct { + body *RemoveChainRequestBody + + session.RequestHeaders +} + +func (r *RemoveChainRequest) SetBody(body *RemoveChainRequestBody) { + r.body = body +} + +func (r *RemoveChainRequest) GetBody() *RemoveChainRequestBody { + if r == nil { + return nil + } + + return r.body +} + +type RemoveChainRequestBody struct { + target *ape.ChainTarget + + chainID []byte +} + +func (rb *RemoveChainRequestBody) SetTarget(target *ape.ChainTarget) { + rb.target = target +} + +func (rb *RemoveChainRequestBody) GetTarget() *ape.ChainTarget { + if rb == nil { + return nil + } + + return rb.target +} + +func (rb *RemoveChainRequestBody) SetChainID(chainID []byte) { + rb.chainID = chainID +} + +func (rb *RemoveChainRequestBody) GetChainID() []byte { + if rb == nil { + return nil + } + + return rb.chainID +} + +type RemoveChainResponse struct { + body *RemoveChainResponseBody + + session.ResponseHeaders +} + +type RemoveChainResponseBody struct{} + +func (r *RemoveChainResponse) SetBody(body *RemoveChainResponseBody) { + r.body = body +} + +func (r *RemoveChainResponse) GetBody() *RemoveChainResponseBody { + if r == nil { + return nil + } + + return r.body +} + +type ListChainsRequest struct { + body *ListChainsRequestBody + + session.RequestHeaders +} + +func (r *ListChainsRequest) SetBody(body *ListChainsRequestBody) { + r.body = body +} + +func (r *ListChainsRequest) GetBody() *ListChainsRequestBody { + if r == nil { + return nil + } + + return r.body +} + +type ListChainsRequestBody struct { + target *ape.ChainTarget +} + +func (rb *ListChainsRequestBody) SetTarget(target *ape.ChainTarget) { + rb.target = target +} + +func (rb *ListChainsRequestBody) GetTarget() *ape.ChainTarget { + if rb == nil { + return nil + } + + return rb.target +} + +type ListChainsResponse struct { + body *ListChainsResponseBody + + session.ResponseHeaders +} + +func (r *ListChainsResponse) SetBody(body *ListChainsResponseBody) { + r.body = body +} + +func (r *ListChainsResponse) GetBody() *ListChainsResponseBody { + if r == nil { + return nil + } + + return r.body +} + +type ListChainsResponseBody struct { + chains []*ape.Chain + + session.RequestHeaders +} + +func (r *ListChainsResponseBody) SetChains(chains []*ape.Chain) { + r.chains = chains +} + +func (r *ListChainsResponseBody) GetChains() []*ape.Chain { + if r == nil { + return nil + } + + return r.chains +} diff --git a/apiv2/container/attributes.go b/apiv2/container/attributes.go new file mode 100644 index 0000000..288d048 --- /dev/null +++ b/apiv2/container/attributes.go @@ -0,0 +1,90 @@ +package container + +// SysAttributePrefix is a prefix of key to system attribute. +const SysAttributePrefix = "__SYSTEM__" + +const ( + // SysAttributeName is a string of human-friendly container name registered as the domain in NNS contract. + SysAttributeName = SysAttributePrefix + "NAME" + + // SysAttributeZone is a string of zone for container name. + SysAttributeZone = SysAttributePrefix + "ZONE" + + // SysAttributeHomomorphicHashing is a container's homomorphic hashing state. + SysAttributeHomomorphicHashing = SysAttributePrefix + "DISABLE_HOMOMORPHIC_HASHING" +) + +// SysAttributePrefixNeoFS is a prefix of key to system attribute. +// Deprecated: use SysAttributePrefix. +const SysAttributePrefixNeoFS = "__NEOFS__" + +const ( + // SysAttributeNameNeoFS is a string of human-friendly container name registered as the domain in NNS contract. + // Deprecated: use SysAttributeName. + SysAttributeNameNeoFS = SysAttributePrefixNeoFS + "NAME" + + // SysAttributeZoneNeoFS is a string of zone for container name. + // Deprecated: use SysAttributeZone. + SysAttributeZoneNeoFS = SysAttributePrefixNeoFS + "ZONE" + + // SysAttributeHomomorphicHashingNeoFS is a container's homomorphic hashing state. + // Deprecated: use SysAttributeHomomorphicHashing. + SysAttributeHomomorphicHashingNeoFS = SysAttributePrefixNeoFS + "DISABLE_HOMOMORPHIC_HASHING" +) + +// SysAttributeZoneDefault is a default value for SysAttributeZone attribute. +const SysAttributeZoneDefault = "container" + +const disabledHomomorphicHashingValue = "true" + +// HomomorphicHashingState returns container's homomorphic +// hashing state: +// - true if hashing is enabled; +// - false if hashing is disabled. +// +// All container's attributes must be unique, otherwise behavior +// is undefined. +// +// See also SetHomomorphicHashingState. +func (c Container) HomomorphicHashingState() bool { + for i := range c.attr { + if c.attr[i].GetKey() == SysAttributeHomomorphicHashing || c.attr[i].GetKey() == SysAttributeHomomorphicHashingNeoFS { + return c.attr[i].GetValue() != disabledHomomorphicHashingValue + } + } + + return true +} + +// SetHomomorphicHashingState sets homomorphic hashing state for +// container. +// +// All container's attributes must be unique, otherwise behavior +// is undefined. +// +// See also HomomorphicHashingState. +func (c *Container) SetHomomorphicHashingState(enable bool) { + for i := range c.attr { + if c.attr[i].GetKey() == SysAttributeHomomorphicHashing || c.attr[i].GetKey() == SysAttributeHomomorphicHashingNeoFS { + if enable { + // approach without allocation/waste + // coping works since the attributes + // order is not important + c.attr[i] = c.attr[len(c.attr)-1] + c.attr = c.attr[:len(c.attr)-1] + } else { + c.attr[i].SetValue(disabledHomomorphicHashingValue) + } + + return + } + } + + if !enable { + attr := Attribute{} + attr.SetKey(SysAttributeHomomorphicHashing) + attr.SetValue(disabledHomomorphicHashingValue) + + c.attr = append(c.attr, attr) + } +} diff --git a/apiv2/container/attributes_test.go b/apiv2/container/attributes_test.go new file mode 100644 index 0000000..3599b0f --- /dev/null +++ b/apiv2/container/attributes_test.go @@ -0,0 +1,59 @@ +package container_test + +import ( + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container" + containertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container/test" + "github.com/stretchr/testify/require" +) + +func TestContainer_HomomorphicHashingDisabled(t *testing.T) { + cnr := containertest.GenerateContainer(false) + + t.Run("defaults", func(t *testing.T) { + require.True(t, cnr.HomomorphicHashingState()) + }) + + t.Run("disabled", func(t *testing.T) { + attr := container.Attribute{} + attr.SetKey(container.SysAttributeHomomorphicHashing) + attr.SetValue("NOT_true") + + cnr.SetAttributes(append(cnr.GetAttributes(), attr)) + require.True(t, cnr.HomomorphicHashingState()) + + attr.SetValue("true") + + cnr.SetAttributes([]container.Attribute{attr}) + require.False(t, cnr.HomomorphicHashingState()) + }) +} + +func TestContainer_SetHomomorphicHashingState(t *testing.T) { + cnr := containertest.GenerateContainer(false) + attrs := cnr.GetAttributes() + attrLen := len(attrs) + + cnr.SetHomomorphicHashingState(true) + + // enabling hashing should not add any new attributes + require.Equal(t, attrLen, len(cnr.GetAttributes())) + require.True(t, cnr.HomomorphicHashingState()) + + cnr.SetHomomorphicHashingState(false) + + // disabling hashing should add exactly one attribute + require.Equal(t, attrLen+1, len(cnr.GetAttributes())) + require.False(t, cnr.HomomorphicHashingState()) + + cnr.SetHomomorphicHashingState(true) + + // enabling hashing should remove 1 attribute if + // hashing was disabled before + require.Equal(t, attrLen, len(cnr.GetAttributes())) + require.True(t, cnr.HomomorphicHashingState()) + + // hashing operations should not change any other attributes + require.ElementsMatch(t, attrs, cnr.GetAttributes()) +} diff --git a/apiv2/container/convert.go b/apiv2/container/convert.go new file mode 100644 index 0000000..6f450fb --- /dev/null +++ b/apiv2/container/convert.go @@ -0,0 +1,764 @@ +package container + +import ( + container "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" + netmapGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + refsGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + sessionGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session/grpc" +) + +func (a *Attribute) ToGRPCMessage() grpc.Message { + var m *container.Container_Attribute + + if a != nil { + m = new(container.Container_Attribute) + + m.SetKey(a.key) + m.SetValue(a.val) + } + + return m +} + +func (a *Attribute) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.Container_Attribute) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + a.key = v.GetKey() + a.val = v.GetValue() + + return nil +} + +func AttributesToGRPC(xs []Attribute) (res []container.Container_Attribute) { + if xs != nil { + res = make([]container.Container_Attribute, 0, len(xs)) + + for i := range xs { + res = append(res, *xs[i].ToGRPCMessage().(*container.Container_Attribute)) + } + } + + return +} + +func AttributesFromGRPC(xs []container.Container_Attribute) (res []Attribute, err error) { + if xs != nil { + res = make([]Attribute, len(xs)) + + for i := range xs { + err = res[i].FromGRPCMessage(&xs[i]) + if err != nil { + return + } + } + } + + return +} + +func (c *Container) ToGRPCMessage() grpc.Message { + var m *container.Container + + if c != nil { + m = new(container.Container) + + m.SetVersion(c.version.ToGRPCMessage().(*refsGRPC.Version)) + m.SetOwnerId(c.ownerID.ToGRPCMessage().(*refsGRPC.OwnerID)) + m.SetPlacementPolicy(c.policy.ToGRPCMessage().(*netmapGRPC.PlacementPolicy)) + m.SetAttributes(AttributesToGRPC(c.attr)) + m.SetBasicAcl(c.basicACL) + m.SetNonce(c.nonce) + } + + return m +} + +func (c *Container) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.Container) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + version := v.GetVersion() + if version == nil { + c.version = nil + } else { + if c.version == nil { + c.version = new(refs.Version) + } + + err = c.version.FromGRPCMessage(version) + if err != nil { + return err + } + } + + ownerID := v.GetOwnerId() + if ownerID == nil { + c.ownerID = nil + } else { + if c.ownerID == nil { + c.ownerID = new(refs.OwnerID) + } + + err = c.ownerID.FromGRPCMessage(ownerID) + if err != nil { + return err + } + } + + policy := v.GetPlacementPolicy() + if policy == nil { + c.policy = nil + } else { + if c.policy == nil { + c.policy = new(netmap.PlacementPolicy) + } + + err = c.policy.FromGRPCMessage(policy) + if err != nil { + return err + } + } + + c.attr, err = AttributesFromGRPC(v.GetAttributes()) + if err != nil { + return err + } + + c.basicACL = v.GetBasicAcl() + c.nonce = v.GetNonce() + + return nil +} + +func toSignatureRFC6979(s *refs.Signature) *refsGRPC.SignatureRFC6979 { + var res *refsGRPC.SignatureRFC6979 + + if s != nil { + res = new(refsGRPC.SignatureRFC6979) + res.SetKey(s.GetKey()) + res.SetSign(s.GetSign()) + } + + return res +} + +func (r *PutRequestBody) ToGRPCMessage() grpc.Message { + var m *container.PutRequest_Body + + if r != nil { + m = new(container.PutRequest_Body) + + m.SetContainer(r.cnr.ToGRPCMessage().(*container.Container)) + m.SetSignature(toSignatureRFC6979(r.sig)) + } + + return m +} + +func (r *PutRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.PutRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + cnr := v.GetContainer() + if cnr == nil { + r.cnr = nil + } else { + if r.cnr == nil { + r.cnr = new(Container) + } + + err = r.cnr.FromGRPCMessage(cnr) + if err != nil { + return err + } + } + + sig := v.GetSignature() + if sig == nil { + r.sig = nil + } else { + if r.sig == nil { + r.sig = new(refs.Signature) + } + + r.sig.SetKey(sig.GetKey()) + r.sig.SetSign(sig.GetSign()) + } + + return err +} + +func (r *PutRequest) ToGRPCMessage() grpc.Message { + var m *container.PutRequest + + if r != nil { + m = new(container.PutRequest) + + m.SetBody(r.body.ToGRPCMessage().(*container.PutRequest_Body)) + r.RequestHeaders.ToMessage(m) + } + + return m +} + +func (r *PutRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.PutRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(PutRequestBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.RequestHeaders.FromMessage(v) +} + +func (r *PutResponseBody) ToGRPCMessage() grpc.Message { + var m *container.PutResponse_Body + + if r != nil { + m = new(container.PutResponse_Body) + + m.SetContainerId(r.cid.ToGRPCMessage().(*refsGRPC.ContainerID)) + } + + return m +} + +func (r *PutResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.PutResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + cid := v.GetContainerId() + if cid == nil { + r.cid = nil + } else { + if r.cid == nil { + r.cid = new(refs.ContainerID) + } + + err = r.cid.FromGRPCMessage(cid) + } + + return err +} + +func (r *PutResponse) ToGRPCMessage() grpc.Message { + var m *container.PutResponse + + if r != nil { + m = new(container.PutResponse) + + m.SetBody(r.body.ToGRPCMessage().(*container.PutResponse_Body)) + r.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (r *PutResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.PutResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(PutResponseBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.ResponseHeaders.FromMessage(v) +} + +func (r *GetRequestBody) ToGRPCMessage() grpc.Message { + var m *container.GetRequest_Body + + if r != nil { + m = new(container.GetRequest_Body) + + m.SetContainerId(r.cid.ToGRPCMessage().(*refsGRPC.ContainerID)) + } + + return m +} + +func (r *GetRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.GetRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + cid := v.GetContainerId() + if cid == nil { + r.cid = nil + } else { + if r.cid == nil { + r.cid = new(refs.ContainerID) + } + + err = r.cid.FromGRPCMessage(cid) + } + + return err +} + +func (r *GetRequest) ToGRPCMessage() grpc.Message { + var m *container.GetRequest + + if r != nil { + m = new(container.GetRequest) + + m.SetBody(r.body.ToGRPCMessage().(*container.GetRequest_Body)) + r.RequestHeaders.ToMessage(m) + } + + return m +} + +func (r *GetRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.GetRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(GetRequestBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.RequestHeaders.FromMessage(v) +} + +func (r *GetResponseBody) ToGRPCMessage() grpc.Message { + var m *container.GetResponse_Body + + if r != nil { + m = new(container.GetResponse_Body) + + m.SetContainer(r.cnr.ToGRPCMessage().(*container.Container)) + m.SetSessionToken(r.token.ToGRPCMessage().(*sessionGRPC.SessionToken)) + m.SetSignature(toSignatureRFC6979(r.sig)) + } + + return m +} + +func (r *GetResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.GetResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + cnr := v.GetContainer() + if cnr == nil { + r.cnr = nil + } else { + if r.cnr == nil { + r.cnr = new(Container) + } + + err = r.cnr.FromGRPCMessage(cnr) + } + + sig := v.GetSignature() + if sig == nil { + r.sig = nil + } else { + if r.sig == nil { + r.sig = new(refs.Signature) + } + + r.sig.SetKey(sig.GetKey()) + r.sig.SetSign(sig.GetSign()) + } + + token := v.GetSessionToken() + if token == nil { + r.token = nil + } else { + if r.token == nil { + r.token = new(session.Token) + } + + err = r.token.FromGRPCMessage(token) + } + + return err +} + +func (r *GetResponse) ToGRPCMessage() grpc.Message { + var m *container.GetResponse + + if r != nil { + m = new(container.GetResponse) + + m.SetBody(r.body.ToGRPCMessage().(*container.GetResponse_Body)) + r.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (r *GetResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.GetResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(GetResponseBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.ResponseHeaders.FromMessage(v) +} + +func (r *DeleteRequestBody) ToGRPCMessage() grpc.Message { + var m *container.DeleteRequest_Body + + if r != nil { + m = new(container.DeleteRequest_Body) + + m.SetContainerId(r.cid.ToGRPCMessage().(*refsGRPC.ContainerID)) + m.SetSignature(toSignatureRFC6979(r.sig)) + } + + return m +} + +func (r *DeleteRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.DeleteRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + cid := v.GetContainerId() + if cid == nil { + r.cid = nil + } else { + if r.cid == nil { + r.cid = new(refs.ContainerID) + } + + err = r.cid.FromGRPCMessage(cid) + if err != nil { + return err + } + } + + sig := v.GetSignature() + if sig == nil { + r.sig = nil + } else { + if r.sig == nil { + r.sig = new(refs.Signature) + } + + r.sig.SetKey(sig.GetKey()) + r.sig.SetSign(sig.GetSign()) + } + + return err +} + +func (r *DeleteRequest) ToGRPCMessage() grpc.Message { + var m *container.DeleteRequest + + if r != nil { + m = new(container.DeleteRequest) + + m.SetBody(r.body.ToGRPCMessage().(*container.DeleteRequest_Body)) + r.RequestHeaders.ToMessage(m) + } + + return m +} + +func (r *DeleteRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.DeleteRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(DeleteRequestBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.RequestHeaders.FromMessage(v) +} + +func (r *DeleteResponseBody) ToGRPCMessage() grpc.Message { + var m *container.DeleteResponse_Body + + if r != nil { + m = new(container.DeleteResponse_Body) + } + + return m +} + +func (r *DeleteResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.DeleteResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + return nil +} + +func (r *DeleteResponse) ToGRPCMessage() grpc.Message { + var m *container.DeleteResponse + + if r != nil { + m = new(container.DeleteResponse) + + m.SetBody(r.body.ToGRPCMessage().(*container.DeleteResponse_Body)) + r.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (r *DeleteResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.DeleteResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(DeleteResponseBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.ResponseHeaders.FromMessage(v) +} + +func (r *ListRequestBody) ToGRPCMessage() grpc.Message { + var m *container.ListRequest_Body + + if r != nil { + m = new(container.ListRequest_Body) + + m.SetOwnerId(r.ownerID.ToGRPCMessage().(*refsGRPC.OwnerID)) + } + + return m +} + +func (r *ListRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.ListRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + ownerID := v.GetOwnerId() + if ownerID == nil { + r.ownerID = nil + } else { + if r.ownerID == nil { + r.ownerID = new(refs.OwnerID) + } + + err = r.ownerID.FromGRPCMessage(ownerID) + } + + return err +} + +func (r *ListRequest) ToGRPCMessage() grpc.Message { + var m *container.ListRequest + + if r != nil { + m = new(container.ListRequest) + + m.SetBody(r.body.ToGRPCMessage().(*container.ListRequest_Body)) + r.RequestHeaders.ToMessage(m) + } + + return m +} + +func (r *ListRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.ListRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(ListRequestBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.RequestHeaders.FromMessage(v) +} + +func (r *ListResponseBody) ToGRPCMessage() grpc.Message { + var m *container.ListResponse_Body + + if r != nil { + m = new(container.ListResponse_Body) + + m.SetContainerIds(refs.ContainerIDsToGRPCMessage(r.cidList)) + } + + return m +} + +func (r *ListResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.ListResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + r.cidList, err = refs.ContainerIDsFromGRPCMessage(v.GetContainerIds()) + + return err +} + +func (r *ListResponse) ToGRPCMessage() grpc.Message { + var m *container.ListResponse + + if r != nil { + m = new(container.ListResponse) + + m.SetBody(r.body.ToGRPCMessage().(*container.ListResponse_Body)) + r.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (r *ListResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*container.ListResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(ListResponseBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.ResponseHeaders.FromMessage(v) +} diff --git a/apiv2/container/grpc/service_frostfs.pb.go b/apiv2/container/grpc/service_frostfs.pb.go new file mode 100644 index 0000000..1fa1b11 Binary files /dev/null and b/apiv2/container/grpc/service_frostfs.pb.go differ diff --git a/apiv2/container/grpc/service_frostfs_fuzz.go b/apiv2/container/grpc/service_frostfs_fuzz.go new file mode 100644 index 0000000..7e6d6e6 --- /dev/null +++ b/apiv2/container/grpc/service_frostfs_fuzz.go @@ -0,0 +1,159 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package container + +func DoFuzzProtoPutRequest(data []byte) int { + msg := new(PutRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONPutRequest(data []byte) int { + msg := new(PutRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoPutResponse(data []byte) int { + msg := new(PutResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONPutResponse(data []byte) int { + msg := new(PutResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoDeleteRequest(data []byte) int { + msg := new(DeleteRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONDeleteRequest(data []byte) int { + msg := new(DeleteRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoDeleteResponse(data []byte) int { + msg := new(DeleteResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONDeleteResponse(data []byte) int { + msg := new(DeleteResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoGetRequest(data []byte) int { + msg := new(GetRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONGetRequest(data []byte) int { + msg := new(GetRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoGetResponse(data []byte) int { + msg := new(GetResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONGetResponse(data []byte) int { + msg := new(GetResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoListRequest(data []byte) int { + msg := new(ListRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONListRequest(data []byte) int { + msg := new(ListRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoListResponse(data []byte) int { + msg := new(ListResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONListResponse(data []byte) int { + msg := new(ListResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/container/grpc/service_frostfs_test.go b/apiv2/container/grpc/service_frostfs_test.go new file mode 100644 index 0000000..804b89c --- /dev/null +++ b/apiv2/container/grpc/service_frostfs_test.go @@ -0,0 +1,91 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package container + +import ( + testing "testing" +) + +func FuzzProtoPutRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoPutRequest(data) + }) +} +func FuzzJSONPutRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONPutRequest(data) + }) +} +func FuzzProtoPutResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoPutResponse(data) + }) +} +func FuzzJSONPutResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONPutResponse(data) + }) +} +func FuzzProtoDeleteRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoDeleteRequest(data) + }) +} +func FuzzJSONDeleteRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONDeleteRequest(data) + }) +} +func FuzzProtoDeleteResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoDeleteResponse(data) + }) +} +func FuzzJSONDeleteResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONDeleteResponse(data) + }) +} +func FuzzProtoGetRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoGetRequest(data) + }) +} +func FuzzJSONGetRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONGetRequest(data) + }) +} +func FuzzProtoGetResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoGetResponse(data) + }) +} +func FuzzJSONGetResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONGetResponse(data) + }) +} +func FuzzProtoListRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoListRequest(data) + }) +} +func FuzzJSONListRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONListRequest(data) + }) +} +func FuzzProtoListResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoListResponse(data) + }) +} +func FuzzJSONListResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONListResponse(data) + }) +} diff --git a/apiv2/container/grpc/service_grpc.pb.go b/apiv2/container/grpc/service_grpc.pb.go new file mode 100644 index 0000000..abb0fef Binary files /dev/null and b/apiv2/container/grpc/service_grpc.pb.go differ diff --git a/apiv2/container/grpc/types_frostfs.pb.go b/apiv2/container/grpc/types_frostfs.pb.go new file mode 100644 index 0000000..f562e15 Binary files /dev/null and b/apiv2/container/grpc/types_frostfs.pb.go differ diff --git a/apiv2/container/grpc/types_frostfs_fuzz.go b/apiv2/container/grpc/types_frostfs_fuzz.go new file mode 100644 index 0000000..5551978 --- /dev/null +++ b/apiv2/container/grpc/types_frostfs_fuzz.go @@ -0,0 +1,26 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package container + +func DoFuzzProtoContainer(data []byte) int { + msg := new(Container) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONContainer(data []byte) int { + msg := new(Container) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/container/grpc/types_frostfs_test.go b/apiv2/container/grpc/types_frostfs_test.go new file mode 100644 index 0000000..64d840e --- /dev/null +++ b/apiv2/container/grpc/types_frostfs_test.go @@ -0,0 +1,21 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package container + +import ( + testing "testing" +) + +func FuzzProtoContainer(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoContainer(data) + }) +} +func FuzzJSONContainer(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONContainer(data) + }) +} diff --git a/apiv2/container/json.go b/apiv2/container/json.go new file mode 100644 index 0000000..91c4c08 --- /dev/null +++ b/apiv2/container/json.go @@ -0,0 +1,22 @@ +package container + +import ( + container "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +func (a *Attribute) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(a) +} + +func (a *Attribute) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(a, data, new(container.Container_Attribute)) +} + +func (c *Container) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(c) +} + +func (c *Container) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(c, data, new(container.Container)) +} diff --git a/apiv2/container/marshal.go b/apiv2/container/marshal.go new file mode 100644 index 0000000..ae38bc5 --- /dev/null +++ b/apiv2/container/marshal.go @@ -0,0 +1,345 @@ +package container + +import ( + container "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + protoutil "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto" +) + +const ( + attributeKeyField = 1 + attributeValueField = 2 + + containerVersionField = 1 + containerOwnerField = 2 + containerNonceField = 3 + containerBasicACLField = 4 + containerAttributesField = 5 + containerPlacementField = 6 + + putReqBodyContainerField = 1 + putReqBodySignatureField = 2 + + putRespBodyIDField = 1 + + deleteReqBodyIDField = 1 + deleteReqBodySignatureField = 2 + + getReqBodyIDField = 1 + + getRespBodyContainerField = 1 + getRespBodySignatureField = 2 + getRespBodyTokenField = 3 + + listReqBodyOwnerField = 1 + + listRespBodyIDsField = 1 +) + +func (a *Attribute) StableMarshal(buf []byte) []byte { + if a == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, a.StableSize()) + } + + var offset int + + offset += protoutil.StringMarshal(attributeKeyField, buf[offset:], a.key) + protoutil.StringMarshal(attributeValueField, buf[offset:], a.val) + + return buf +} + +func (a *Attribute) StableSize() (size int) { + if a == nil { + return 0 + } + + size += protoutil.StringSize(attributeKeyField, a.key) + size += protoutil.StringSize(attributeValueField, a.val) + + return size +} + +func (a *Attribute) Unmarshal(data []byte) error { + return message.Unmarshal(a, data, new(container.Container_Attribute)) +} + +func (c *Container) StableMarshal(buf []byte) []byte { + if c == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, c.StableSize()) + } + + var offset int + + offset += protoutil.NestedStructureMarshal(containerVersionField, buf[offset:], c.version) + offset += protoutil.NestedStructureMarshal(containerOwnerField, buf[offset:], c.ownerID) + offset += protoutil.BytesMarshal(containerNonceField, buf[offset:], c.nonce) + offset += protoutil.UInt32Marshal(containerBasicACLField, buf[offset:], c.basicACL) + + for i := range c.attr { + offset += protoutil.NestedStructureMarshal(containerAttributesField, buf[offset:], &c.attr[i]) + } + + protoutil.NestedStructureMarshal(containerPlacementField, buf[offset:], c.policy) + + return buf +} + +func (c *Container) StableSize() (size int) { + if c == nil { + return 0 + } + + size += protoutil.NestedStructureSize(containerVersionField, c.version) + size += protoutil.NestedStructureSize(containerOwnerField, c.ownerID) + size += protoutil.BytesSize(containerNonceField, c.nonce) + size += protoutil.UInt32Size(containerBasicACLField, c.basicACL) + + for i := range c.attr { + size += protoutil.NestedStructureSize(containerAttributesField, &c.attr[i]) + } + + size += protoutil.NestedStructureSize(containerPlacementField, c.policy) + + return size +} + +func (c *Container) Unmarshal(data []byte) error { + return message.Unmarshal(c, data, new(container.Container)) +} + +func (r *PutRequestBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += protoutil.NestedStructureMarshal(putReqBodyContainerField, buf[offset:], r.cnr) + protoutil.NestedStructureMarshal(putReqBodySignatureField, buf[offset:], r.sig) + + return buf +} + +func (r *PutRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += protoutil.NestedStructureSize(putReqBodyContainerField, r.cnr) + size += protoutil.NestedStructureSize(putReqBodySignatureField, r.sig) + + return size +} + +func (r *PutRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(container.PutRequest_Body)) +} + +func (r *PutResponseBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + protoutil.NestedStructureMarshal(putRespBodyIDField, buf, r.cid) + + return buf +} + +func (r *PutResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += protoutil.NestedStructureSize(putRespBodyIDField, r.cid) + + return size +} + +func (r *PutResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(container.PutResponse_Body)) +} + +func (r *DeleteRequestBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += protoutil.NestedStructureMarshal(deleteReqBodyIDField, buf[offset:], r.cid) + protoutil.NestedStructureMarshal(deleteReqBodySignatureField, buf[offset:], r.sig) + + return buf +} + +func (r *DeleteRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += protoutil.NestedStructureSize(deleteReqBodyIDField, r.cid) + size += protoutil.NestedStructureSize(deleteReqBodySignatureField, r.sig) + + return size +} + +func (r *DeleteRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(container.DeleteRequest_Body)) +} + +func (r *DeleteResponseBody) StableMarshal(_ []byte) []byte { + return nil +} + +func (r *DeleteResponseBody) StableSize() (size int) { + return 0 +} + +func (r *DeleteResponseBody) Unmarshal([]byte) error { + return nil +} + +func (r *GetRequestBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + protoutil.NestedStructureMarshal(getReqBodyIDField, buf, r.cid) + + return buf +} + +func (r *GetRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += protoutil.NestedStructureSize(getReqBodyIDField, r.cid) + + return size +} + +func (r *GetRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(container.GetRequest_Body)) +} + +func (r *GetResponseBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += protoutil.NestedStructureMarshal(getRespBodyContainerField, buf, r.cnr) + offset += protoutil.NestedStructureMarshal(getRespBodySignatureField, buf[offset:], r.sig) + protoutil.NestedStructureMarshal(getRespBodyTokenField, buf[offset:], r.token) + + return buf +} + +func (r *GetResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += protoutil.NestedStructureSize(getRespBodyContainerField, r.cnr) + size += protoutil.NestedStructureSize(getRespBodySignatureField, r.sig) + size += protoutil.NestedStructureSize(getRespBodyTokenField, r.token) + + return size +} + +func (r *GetResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(container.GetResponse_Body)) +} + +func (r *ListRequestBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + protoutil.NestedStructureMarshal(listReqBodyOwnerField, buf, r.ownerID) + + return buf +} + +func (r *ListRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += protoutil.NestedStructureSize(listReqBodyOwnerField, r.ownerID) + + return size +} + +func (r *ListRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(container.ListRequest_Body)) +} + +func (r *ListResponseBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + for i := range r.cidList { + offset += protoutil.NestedStructureMarshal(listRespBodyIDsField, buf[offset:], &r.cidList[i]) + } + + return buf +} + +func (r *ListResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + for i := range r.cidList { + size += protoutil.NestedStructureSize(listRespBodyIDsField, &r.cidList[i]) + } + + return size +} + +func (r *ListResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(container.ListResponse_Body)) +} diff --git a/apiv2/container/message_test.go b/apiv2/container/message_test.go new file mode 100644 index 0000000..2df5bdb --- /dev/null +++ b/apiv2/container/message_test.go @@ -0,0 +1,36 @@ +package container_test + +import ( + "testing" + + containertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container/test" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + messagetest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message/test" +) + +func TestMessageConvert(t *testing.T) { + messagetest.TestRPCMessage(t, + func(empty bool) message.Message { return containertest.GenerateAttribute(empty) }, + func(empty bool) message.Message { return containertest.GenerateContainer(empty) }, + func(empty bool) message.Message { return containertest.GeneratePutRequestBody(empty) }, + func(empty bool) message.Message { return containertest.GeneratePutRequest(empty) }, + func(empty bool) message.Message { return containertest.GeneratePutResponseBody(empty) }, + func(empty bool) message.Message { return containertest.GeneratePutResponse(empty) }, + func(empty bool) message.Message { return containertest.GenerateGetRequestBody(empty) }, + func(empty bool) message.Message { return containertest.GenerateGetRequest(empty) }, + func(empty bool) message.Message { return containertest.GenerateGetResponseBody(empty) }, + func(empty bool) message.Message { return containertest.GenerateGetResponse(empty) }, + func(empty bool) message.Message { return containertest.GenerateDeleteRequestBody(empty) }, + func(empty bool) message.Message { return containertest.GenerateDeleteRequest(empty) }, + func(empty bool) message.Message { return containertest.GenerateDeleteResponseBody(empty) }, + func(empty bool) message.Message { return containertest.GenerateDeleteResponse(empty) }, + func(empty bool) message.Message { return containertest.GenerateListRequestBody(empty) }, + func(empty bool) message.Message { return containertest.GenerateListRequest(empty) }, + func(empty bool) message.Message { return containertest.GenerateListResponseBody(empty) }, + func(empty bool) message.Message { return containertest.GenerateListResponse(empty) }, + func(empty bool) message.Message { return containertest.GenerateGetRequestBody(empty) }, + func(empty bool) message.Message { return containertest.GenerateGetRequest(empty) }, + func(empty bool) message.Message { return containertest.GenerateGetResponseBody(empty) }, + func(empty bool) message.Message { return containertest.GenerateGetResponse(empty) }, + ) +} diff --git a/apiv2/container/status.go b/apiv2/container/status.go new file mode 100644 index 0000000..94046f7 --- /dev/null +++ b/apiv2/container/status.go @@ -0,0 +1,33 @@ +package container + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" + statusgrpc "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status/grpc" +) + +// LocalizeFailStatus checks if passed global status.Code is related to container failure and: +// +// then localizes the code and returns true, +// else leaves the code unchanged and returns false. +// +// Arg must not be nil. +func LocalizeFailStatus(c *status.Code) bool { + return status.LocalizeIfInSection(c, uint32(statusgrpc.Section_SECTION_CONTAINER)) +} + +// GlobalizeFail globalizes local code of container failure. +// +// Arg must not be nil. +func GlobalizeFail(c *status.Code) { + c.GlobalizeSection(uint32(statusgrpc.Section_SECTION_CONTAINER)) +} + +const ( + // StatusNotFound is a local status.Code value for + // CONTAINER_NOT_FOUND container failure. + StatusNotFound status.Code = iota + + // StatusEACLNotFound is a local status.Code value for + // EACL_NOT_FOUND failure. + StatusEACLNotFound +) diff --git a/apiv2/container/status_test.go b/apiv2/container/status_test.go new file mode 100644 index 0000000..f3f2483 --- /dev/null +++ b/apiv2/container/status_test.go @@ -0,0 +1,15 @@ +package container_test + +import ( + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container" + statustest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status/test" +) + +func TestStatusCodes(t *testing.T) { + statustest.TestCodes(t, container.LocalizeFailStatus, container.GlobalizeFail, + container.StatusNotFound, 3072, + container.StatusEACLNotFound, 3073, + ) +} diff --git a/apiv2/container/test/generate.go b/apiv2/container/test/generate.go new file mode 100644 index 0000000..143aca3 --- /dev/null +++ b/apiv2/container/test/generate.go @@ -0,0 +1,240 @@ +package containertest + +import ( + "crypto/rand" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container" + netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap/test" + refstest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/test" + sessiontest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session/test" +) + +func GenerateAttribute(empty bool) *container.Attribute { + m := new(container.Attribute) + + if !empty { + m.SetKey("key") + m.SetValue("val") + } + + return m +} + +func GenerateAttributes(empty bool) []container.Attribute { + var res []container.Attribute + + if !empty { + res = append(res, + *GenerateAttribute(false), + *GenerateAttribute(false), + ) + } + + return res +} + +func GenerateContainer(empty bool) *container.Container { + m := new(container.Container) + + if !empty { + nonce := make([]byte, 16) + _, _ = rand.Read(nonce) + + m.SetBasicACL(12) + m.SetNonce(nonce) + m.SetOwnerID(refstest.GenerateOwnerID(false)) + m.SetAttributes(GenerateAttributes(false)) + m.SetPlacementPolicy(netmaptest.GeneratePlacementPolicy(false)) + } + + m.SetVersion(refstest.GenerateVersion(empty)) + + return m +} + +func GeneratePutRequestBody(empty bool) *container.PutRequestBody { + m := new(container.PutRequestBody) + + if !empty { + m.SetContainer(GenerateContainer(false)) + } + + m.SetSignature(refstest.GenerateSignature(empty)) + + return m +} + +func GeneratePutRequest(empty bool) *container.PutRequest { + m := new(container.PutRequest) + + if !empty { + m.SetBody(GeneratePutRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GeneratePutResponseBody(empty bool) *container.PutResponseBody { + m := new(container.PutResponseBody) + + if !empty { + m.SetContainerID(refstest.GenerateContainerID(false)) + } + + return m +} + +func GeneratePutResponse(empty bool) *container.PutResponse { + m := new(container.PutResponse) + + if !empty { + m.SetBody(GeneratePutResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} + +func GenerateGetRequestBody(empty bool) *container.GetRequestBody { + m := new(container.GetRequestBody) + + if !empty { + m.SetContainerID(refstest.GenerateContainerID(false)) + } + + return m +} + +func GenerateGetRequest(empty bool) *container.GetRequest { + m := new(container.GetRequest) + + if !empty { + m.SetBody(GenerateGetRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateGetResponseBody(empty bool) *container.GetResponseBody { + m := new(container.GetResponseBody) + + if !empty { + m.SetContainer(GenerateContainer(false)) + } + + m.SetSignature(refstest.GenerateSignature(empty)) + m.SetSessionToken(sessiontest.GenerateSessionToken(empty)) + + return m +} + +func GenerateGetResponse(empty bool) *container.GetResponse { + m := new(container.GetResponse) + + if !empty { + m.SetBody(GenerateGetResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} + +func GenerateDeleteRequestBody(empty bool) *container.DeleteRequestBody { + m := new(container.DeleteRequestBody) + + if !empty { + m.SetContainerID(refstest.GenerateContainerID(false)) + } + + m.SetSignature(refstest.GenerateSignature(empty)) + + return m +} + +func GenerateDeleteRequest(empty bool) *container.DeleteRequest { + m := new(container.DeleteRequest) + + if !empty { + m.SetBody(GenerateDeleteRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateDeleteResponseBody(_ bool) *container.DeleteResponseBody { + m := new(container.DeleteResponseBody) + + return m +} + +func GenerateDeleteResponse(empty bool) *container.DeleteResponse { + m := new(container.DeleteResponse) + + if !empty { + m.SetBody(GenerateDeleteResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} + +func GenerateListRequestBody(empty bool) *container.ListRequestBody { + m := new(container.ListRequestBody) + + if !empty { + m.SetOwnerID(refstest.GenerateOwnerID(false)) + } + + return m +} + +func GenerateListRequest(empty bool) *container.ListRequest { + m := new(container.ListRequest) + + if !empty { + m.SetBody(GenerateListRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateListResponseBody(empty bool) *container.ListResponseBody { + m := new(container.ListResponseBody) + + if !empty { + m.SetContainerIDs(refstest.GenerateContainerIDs(false)) + } + + return m +} + +func GenerateListResponse(empty bool) *container.ListResponse { + m := new(container.ListResponse) + + if !empty { + m.SetBody(GenerateListResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} diff --git a/apiv2/container/types.go b/apiv2/container/types.go new file mode 100644 index 0000000..453a1cc --- /dev/null +++ b/apiv2/container/types.go @@ -0,0 +1,446 @@ +package container + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" +) + +type Attribute struct { + key, val string +} + +type Container struct { + version *refs.Version + + ownerID *refs.OwnerID + + nonce []byte + + basicACL uint32 + + attr []Attribute + + policy *netmap.PlacementPolicy +} + +type PutRequestBody struct { + cnr *Container + + sig *refs.Signature +} +type PutRequest struct { + body *PutRequestBody + + session.RequestHeaders +} + +type PutResponseBody struct { + cid *refs.ContainerID +} + +type PutResponse struct { + body *PutResponseBody + + session.ResponseHeaders +} + +type GetRequestBody struct { + cid *refs.ContainerID +} + +type GetRequest struct { + body *GetRequestBody + + session.RequestHeaders +} + +type GetResponseBody struct { + cnr *Container + + token *session.Token + + sig *refs.Signature +} + +type GetResponse struct { + body *GetResponseBody + + session.ResponseHeaders +} + +type DeleteRequestBody struct { + cid *refs.ContainerID + + sig *refs.Signature +} + +type DeleteRequest struct { + body *DeleteRequestBody + + session.RequestHeaders +} + +type DeleteResponseBody struct{} + +type DeleteResponse struct { + body *DeleteResponseBody + + session.ResponseHeaders +} + +type ListRequestBody struct { + ownerID *refs.OwnerID +} + +type ListRequest struct { + body *ListRequestBody + + session.RequestHeaders +} + +type ListResponseBody struct { + cidList []refs.ContainerID +} + +type ListResponse struct { + body *ListResponseBody + + session.ResponseHeaders +} + +func (a *Attribute) GetKey() string { + if a != nil { + return a.key + } + + return "" +} + +func (a *Attribute) SetKey(v string) { + a.key = v +} + +func (a *Attribute) GetValue() string { + if a != nil { + return a.val + } + + return "" +} + +func (a *Attribute) SetValue(v string) { + a.val = v +} + +func (c *Container) GetVersion() *refs.Version { + if c != nil { + return c.version + } + + return nil +} + +func (c *Container) SetVersion(v *refs.Version) { + c.version = v +} + +func (c *Container) GetOwnerID() *refs.OwnerID { + if c != nil { + return c.ownerID + } + + return nil +} + +func (c *Container) SetOwnerID(v *refs.OwnerID) { + c.ownerID = v +} + +func (c *Container) GetNonce() []byte { + if c != nil { + return c.nonce + } + + return nil +} + +func (c *Container) SetNonce(v []byte) { + c.nonce = v +} + +func (c *Container) GetBasicACL() uint32 { + if c != nil { + return c.basicACL + } + + return 0 +} + +func (c *Container) SetBasicACL(v uint32) { + c.basicACL = v +} + +func (c *Container) GetAttributes() []Attribute { + if c != nil { + return c.attr + } + + return nil +} + +func (c *Container) SetAttributes(v []Attribute) { + c.attr = v +} + +func (c *Container) GetPlacementPolicy() *netmap.PlacementPolicy { + if c != nil { + return c.policy + } + + return nil +} + +func (c *Container) SetPlacementPolicy(v *netmap.PlacementPolicy) { + c.policy = v +} + +func (r *PutRequestBody) GetContainer() *Container { + if r != nil { + return r.cnr + } + + return nil +} + +func (r *PutRequestBody) SetContainer(v *Container) { + r.cnr = v +} + +func (r *PutRequestBody) GetSignature() *refs.Signature { + if r != nil { + return r.sig + } + + return nil +} + +func (r *PutRequestBody) SetSignature(v *refs.Signature) { + // TODO: (neofs-api-go#381) avoid this hack (e.g. create refs.SignatureRFC6979 type) + v.SetScheme(0) + r.sig = v +} + +func (r *PutRequest) GetBody() *PutRequestBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *PutRequest) SetBody(v *PutRequestBody) { + r.body = v +} + +func (r *PutResponseBody) GetContainerID() *refs.ContainerID { + if r != nil { + return r.cid + } + + return nil +} + +func (r *PutResponseBody) SetContainerID(v *refs.ContainerID) { + r.cid = v +} + +func (r *PutResponse) GetBody() *PutResponseBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *PutResponse) SetBody(v *PutResponseBody) { + r.body = v +} + +func (r *GetRequestBody) GetContainerID() *refs.ContainerID { + if r != nil { + return r.cid + } + + return nil +} + +func (r *GetRequestBody) SetContainerID(v *refs.ContainerID) { + r.cid = v +} + +func (r *GetRequest) GetBody() *GetRequestBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *GetRequest) SetBody(v *GetRequestBody) { + r.body = v +} + +func (r *GetResponseBody) GetContainer() *Container { + if r != nil { + return r.cnr + } + + return nil +} + +func (r *GetResponseBody) SetContainer(v *Container) { + r.cnr = v +} + +// GetSessionToken returns token of the session within which requested +// container was created. +func (r *GetResponseBody) GetSessionToken() *session.Token { + if r != nil { + return r.token + } + + return nil +} + +// SetSessionToken sets token of the session within which requested +// container was created. +func (r *GetResponseBody) SetSessionToken(v *session.Token) { + r.token = v +} + +// GetSignature returns signature of the requested container. +func (r *GetResponseBody) GetSignature() *refs.Signature { + if r != nil { + return r.sig + } + + return nil +} + +// SetSignature sets signature of the requested container. +func (r *GetResponseBody) SetSignature(v *refs.Signature) { + // TODO: (neofs-api-go#381) avoid this hack (e.g. create refs.SignatureRFC6979 type) + v.SetScheme(0) + r.sig = v +} + +func (r *GetResponse) GetBody() *GetResponseBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *GetResponse) SetBody(v *GetResponseBody) { + r.body = v +} + +func (r *DeleteRequestBody) GetContainerID() *refs.ContainerID { + if r != nil { + return r.cid + } + + return nil +} + +func (r *DeleteRequestBody) SetContainerID(v *refs.ContainerID) { + r.cid = v +} + +func (r *DeleteRequestBody) GetSignature() *refs.Signature { + if r != nil { + return r.sig + } + + return nil +} + +func (r *DeleteRequestBody) SetSignature(v *refs.Signature) { + // TODO: (neofs-api-go#381) avoid this hack (e.g. create refs.SignatureRFC6979 type) + v.SetScheme(0) + r.sig = v +} + +func (r *DeleteRequest) GetBody() *DeleteRequestBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *DeleteRequest) SetBody(v *DeleteRequestBody) { + r.body = v +} + +func (r *DeleteResponse) GetBody() *DeleteResponseBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *DeleteResponse) SetBody(v *DeleteResponseBody) { + r.body = v +} + +func (r *ListRequestBody) GetOwnerID() *refs.OwnerID { + if r != nil { + return r.ownerID + } + + return nil +} + +func (r *ListRequestBody) SetOwnerID(v *refs.OwnerID) { + r.ownerID = v +} + +func (r *ListRequest) GetBody() *ListRequestBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *ListRequest) SetBody(v *ListRequestBody) { + r.body = v +} + +func (r *ListResponseBody) GetContainerIDs() []refs.ContainerID { + if r != nil { + return r.cidList + } + + return nil +} + +func (r *ListResponseBody) SetContainerIDs(v []refs.ContainerID) { + r.cidList = v +} + +func (r *ListResponse) GetBody() *ListResponseBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *ListResponse) SetBody(v *ListResponseBody) { + r.body = v +} diff --git a/apiv2/lock/grpc/types_frostfs.pb.go b/apiv2/lock/grpc/types_frostfs.pb.go new file mode 100644 index 0000000..31343da Binary files /dev/null and b/apiv2/lock/grpc/types_frostfs.pb.go differ diff --git a/apiv2/lock/grpc/types_frostfs_fuzz.go b/apiv2/lock/grpc/types_frostfs_fuzz.go new file mode 100644 index 0000000..cb55151 --- /dev/null +++ b/apiv2/lock/grpc/types_frostfs_fuzz.go @@ -0,0 +1,26 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package lock + +func DoFuzzProtoLock(data []byte) int { + msg := new(Lock) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONLock(data []byte) int { + msg := new(Lock) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/lock/grpc/types_frostfs_test.go b/apiv2/lock/grpc/types_frostfs_test.go new file mode 100644 index 0000000..7c69064 --- /dev/null +++ b/apiv2/lock/grpc/types_frostfs_test.go @@ -0,0 +1,21 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package lock + +import ( + testing "testing" +) + +func FuzzProtoLock(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoLock(data) + }) +} +func FuzzJSONLock(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONLock(data) + }) +} diff --git a/apiv2/netmap/convert.go b/apiv2/netmap/convert.go new file mode 100644 index 0000000..d301343 --- /dev/null +++ b/apiv2/netmap/convert.go @@ -0,0 +1,916 @@ +package netmap + +import ( + netmap "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + refsGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +func (f *Filter) ToGRPCMessage() grpc.Message { + var m *netmap.Filter + + if f != nil { + m = new(netmap.Filter) + + m.SetKey(f.key) + m.SetValue(f.value) + m.SetName(f.name) + m.SetOp(OperationToGRPCMessage(f.op)) + m.SetFilters(FiltersToGRPC(f.filters)) + } + + return m +} + +func (f *Filter) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.Filter) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + f.filters, err = FiltersFromGRPC(v.GetFilters()) + if err != nil { + return err + } + + f.key = v.GetKey() + f.value = v.GetValue() + f.name = v.GetName() + f.op = OperationFromGRPCMessage(v.GetOp()) + + return nil +} + +func FiltersToGRPC(fs []Filter) (res []netmap.Filter) { + if fs != nil { + res = make([]netmap.Filter, 0, len(fs)) + + for i := range fs { + res = append(res, *fs[i].ToGRPCMessage().(*netmap.Filter)) + } + } + + return +} + +func FiltersFromGRPC(fs []netmap.Filter) (res []Filter, err error) { + if fs != nil { + res = make([]Filter, len(fs)) + + for i := range fs { + err = res[i].FromGRPCMessage(&fs[i]) + if err != nil { + return + } + } + } + + return +} + +func (s *Selector) ToGRPCMessage() grpc.Message { + var m *netmap.Selector + + if s != nil { + m = new(netmap.Selector) + + m.SetName(s.name) + m.SetAttribute(s.attribute) + m.SetFilter(s.filter) + m.SetCount(s.count) + m.SetClause(ClauseToGRPCMessage(s.clause)) + } + + return m +} + +func (s *Selector) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.Selector) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + s.name = v.GetName() + s.attribute = v.GetAttribute() + s.filter = v.GetFilter() + s.count = v.GetCount() + s.clause = ClauseFromGRPCMessage(v.GetClause()) + + return nil +} + +func SelectorsToGRPC(ss []Selector) (res []netmap.Selector) { + if ss != nil { + res = make([]netmap.Selector, 0, len(ss)) + + for i := range ss { + res = append(res, *ss[i].ToGRPCMessage().(*netmap.Selector)) + } + } + + return +} + +func SelectorsFromGRPC(ss []netmap.Selector) (res []Selector, err error) { + if ss != nil { + res = make([]Selector, len(ss)) + + for i := range ss { + err = res[i].FromGRPCMessage(&ss[i]) + if err != nil { + return + } + } + } + + return +} + +func (r *Replica) ToGRPCMessage() grpc.Message { + var m *netmap.Replica + + if r != nil { + m = new(netmap.Replica) + + m.SetSelector(r.selector) + m.SetCount(r.count) + m.EcDataCount = r.ecDataCount + m.EcParityCount = r.ecParityCount + } + + return m +} + +func (r *Replica) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.Replica) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + r.selector = v.GetSelector() + r.count = v.GetCount() + r.ecDataCount = v.GetEcDataCount() + r.ecParityCount = v.GetEcParityCount() + + return nil +} + +func ReplicasToGRPC(rs []Replica) (res []netmap.Replica) { + if rs != nil { + res = make([]netmap.Replica, 0, len(rs)) + + for i := range rs { + res = append(res, *rs[i].ToGRPCMessage().(*netmap.Replica)) + } + } + + return +} + +func ReplicasFromGRPC(rs []netmap.Replica) (res []Replica, err error) { + if rs != nil { + res = make([]Replica, len(rs)) + + for i := range rs { + err = res[i].FromGRPCMessage(&rs[i]) + if err != nil { + return + } + } + } + + return +} + +func (p *PlacementPolicy) ToGRPCMessage() grpc.Message { + var m *netmap.PlacementPolicy + + if p != nil { + m = new(netmap.PlacementPolicy) + + m.SetFilters(FiltersToGRPC(p.filters)) + m.SetSelectors(SelectorsToGRPC(p.selectors)) + m.SetReplicas(ReplicasToGRPC(p.replicas)) + m.SetContainerBackupFactor(p.backupFactor) + m.SetUnique(p.unique) + } + + return m +} + +func (p *PlacementPolicy) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.PlacementPolicy) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + p.filters, err = FiltersFromGRPC(v.GetFilters()) + if err != nil { + return err + } + + p.selectors, err = SelectorsFromGRPC(v.GetSelectors()) + if err != nil { + return err + } + + p.replicas, err = ReplicasFromGRPC(v.GetReplicas()) + if err != nil { + return err + } + + p.backupFactor = v.GetContainerBackupFactor() + + p.unique = v.GetUnique() + + return nil +} + +func ClauseToGRPCMessage(n Clause) netmap.Clause { + return netmap.Clause(n) +} + +func ClauseFromGRPCMessage(n netmap.Clause) Clause { + return Clause(n) +} + +func OperationToGRPCMessage(n Operation) netmap.Operation { + return netmap.Operation(n) +} + +func OperationFromGRPCMessage(n netmap.Operation) Operation { + return Operation(n) +} + +func NodeStateToGRPCMessage(n NodeState) netmap.NodeInfo_State { + return netmap.NodeInfo_State(n) +} + +func NodeStateFromRPCMessage(n netmap.NodeInfo_State) NodeState { + return NodeState(n) +} + +func (a *Attribute) ToGRPCMessage() grpc.Message { + var m *netmap.NodeInfo_Attribute + + if a != nil { + m = new(netmap.NodeInfo_Attribute) + + m.SetKey(a.key) + m.SetValue(a.value) + m.SetParents(a.parents) + } + + return m +} + +func (a *Attribute) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.NodeInfo_Attribute) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + a.key = v.GetKey() + a.value = v.GetValue() + a.parents = v.GetParents() + + return nil +} + +func AttributesToGRPC(as []Attribute) (res []netmap.NodeInfo_Attribute) { + if as != nil { + res = make([]netmap.NodeInfo_Attribute, 0, len(as)) + + for i := range as { + res = append(res, *as[i].ToGRPCMessage().(*netmap.NodeInfo_Attribute)) + } + } + + return +} + +func AttributesFromGRPC(as []netmap.NodeInfo_Attribute) (res []Attribute, err error) { + if as != nil { + res = make([]Attribute, len(as)) + + for i := range as { + err = res[i].FromGRPCMessage(&as[i]) + if err != nil { + return + } + } + } + + return +} + +func (ni *NodeInfo) ToGRPCMessage() grpc.Message { + var m *netmap.NodeInfo + + if ni != nil { + m = new(netmap.NodeInfo) + + m.SetPublicKey(ni.publicKey) + m.SetAddresses(ni.addresses) + m.SetState(NodeStateToGRPCMessage(ni.state)) + m.SetAttributes(AttributesToGRPC(ni.attributes)) + } + + return m +} + +func (ni *NodeInfo) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.NodeInfo) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + ni.attributes, err = AttributesFromGRPC(v.GetAttributes()) + if err != nil { + return err + } + + ni.publicKey = v.GetPublicKey() + ni.addresses = v.GetAddresses() + ni.state = NodeStateFromRPCMessage(v.GetState()) + + return nil +} + +func (l *LocalNodeInfoRequestBody) ToGRPCMessage() grpc.Message { + var m *netmap.LocalNodeInfoRequest_Body + + if l != nil { + m = new(netmap.LocalNodeInfoRequest_Body) + } + + return m +} + +func (l *LocalNodeInfoRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.LocalNodeInfoRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + return nil +} + +func (l *LocalNodeInfoRequest) ToGRPCMessage() grpc.Message { + var m *netmap.LocalNodeInfoRequest + + if l != nil { + m = new(netmap.LocalNodeInfoRequest) + + m.SetBody(l.body.ToGRPCMessage().(*netmap.LocalNodeInfoRequest_Body)) + l.RequestHeaders.ToMessage(m) + } + + return m +} + +func (l *LocalNodeInfoRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.LocalNodeInfoRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + l.body = nil + } else { + if l.body == nil { + l.body = new(LocalNodeInfoRequestBody) + } + + err = l.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return l.RequestHeaders.FromMessage(v) +} + +func (l *LocalNodeInfoResponseBody) ToGRPCMessage() grpc.Message { + var m *netmap.LocalNodeInfoResponse_Body + + if l != nil { + m = new(netmap.LocalNodeInfoResponse_Body) + + m.SetVersion(l.version.ToGRPCMessage().(*refsGRPC.Version)) + m.SetNodeInfo(l.nodeInfo.ToGRPCMessage().(*netmap.NodeInfo)) + } + + return m +} + +func (l *LocalNodeInfoResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.LocalNodeInfoResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + version := v.GetVersion() + if version == nil { + l.version = nil + } else { + if l.version == nil { + l.version = new(refs.Version) + } + + err = l.version.FromGRPCMessage(version) + if err != nil { + return err + } + } + + nodeInfo := v.GetNodeInfo() + if nodeInfo == nil { + l.nodeInfo = nil + } else { + if l.nodeInfo == nil { + l.nodeInfo = new(NodeInfo) + } + + err = l.nodeInfo.FromGRPCMessage(nodeInfo) + } + + return err +} + +func (l *LocalNodeInfoResponse) ToGRPCMessage() grpc.Message { + var m *netmap.LocalNodeInfoResponse + + if l != nil { + m = new(netmap.LocalNodeInfoResponse) + + m.SetBody(l.body.ToGRPCMessage().(*netmap.LocalNodeInfoResponse_Body)) + l.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (l *LocalNodeInfoResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.LocalNodeInfoResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + l.body = nil + } else { + if l.body == nil { + l.body = new(LocalNodeInfoResponseBody) + } + + err = l.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return l.ResponseHeaders.FromMessage(v) +} + +func (x *NetworkParameter) ToGRPCMessage() grpc.Message { + var m *netmap.NetworkConfig_Parameter + + if x != nil { + m = new(netmap.NetworkConfig_Parameter) + + m.SetKey(x.k) + m.SetValue(x.v) + } + + return m +} + +func (x *NetworkParameter) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.NetworkConfig_Parameter) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + x.k = v.GetKey() + x.v = v.GetValue() + + return nil +} + +func (x *NetworkConfig) ToGRPCMessage() grpc.Message { + var m *netmap.NetworkConfig + + if x != nil { + m = new(netmap.NetworkConfig) + + var ps []netmap.NetworkConfig_Parameter + + if ln := len(x.ps); ln > 0 { + ps = make([]netmap.NetworkConfig_Parameter, 0, ln) + + for i := range ln { + ps = append(ps, *x.ps[i].ToGRPCMessage().(*netmap.NetworkConfig_Parameter)) + } + } + + m.SetParameters(ps) + } + + return m +} + +func (x *NetworkConfig) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.NetworkConfig) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var ( + ps []NetworkParameter + psV2 = v.GetParameters() + ) + + if psV2 != nil { + ln := len(psV2) + + ps = make([]NetworkParameter, ln) + + for i := range ln { + if err := ps[i].FromGRPCMessage(&psV2[i]); err != nil { + return err + } + } + } + + x.ps = ps + + return nil +} + +func (i *NetworkInfo) ToGRPCMessage() grpc.Message { + var m *netmap.NetworkInfo + + if i != nil { + m = new(netmap.NetworkInfo) + + m.SetMagicNumber(i.magicNum) + m.SetCurrentEpoch(i.curEpoch) + m.SetMsPerBlock(i.msPerBlock) + m.SetNetworkConfig(i.netCfg.ToGRPCMessage().(*netmap.NetworkConfig)) + } + + return m +} + +func (i *NetworkInfo) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.NetworkInfo) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + netCfg := v.GetNetworkConfig() + if netCfg == nil { + i.netCfg = nil + } else { + if i.netCfg == nil { + i.netCfg = new(NetworkConfig) + } + + err = i.netCfg.FromGRPCMessage(netCfg) + if err != nil { + return err + } + } + + i.magicNum = v.GetMagicNumber() + i.curEpoch = v.GetCurrentEpoch() + i.msPerBlock = v.GetMsPerBlock() + + return nil +} + +func (l *NetworkInfoRequestBody) ToGRPCMessage() grpc.Message { + var m *netmap.NetworkInfoRequest_Body + + if l != nil { + m = new(netmap.NetworkInfoRequest_Body) + } + + return m +} + +func (l *NetworkInfoRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.NetworkInfoRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + return nil +} + +func (l *NetworkInfoRequest) ToGRPCMessage() grpc.Message { + var m *netmap.NetworkInfoRequest + + if l != nil { + m = new(netmap.NetworkInfoRequest) + + m.SetBody(l.body.ToGRPCMessage().(*netmap.NetworkInfoRequest_Body)) + l.RequestHeaders.ToMessage(m) + } + + return m +} + +func (l *NetworkInfoRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.NetworkInfoRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + l.body = nil + } else { + if l.body == nil { + l.body = new(NetworkInfoRequestBody) + } + + err = l.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return l.RequestHeaders.FromMessage(v) +} + +func (i *NetworkInfoResponseBody) ToGRPCMessage() grpc.Message { + var m *netmap.NetworkInfoResponse_Body + + if i != nil { + m = new(netmap.NetworkInfoResponse_Body) + + m.SetNetworkInfo(i.netInfo.ToGRPCMessage().(*netmap.NetworkInfo)) + } + + return m +} + +func (i *NetworkInfoResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.NetworkInfoResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + netInfo := v.GetNetworkInfo() + if netInfo == nil { + i.netInfo = nil + } else { + if i.netInfo == nil { + i.netInfo = new(NetworkInfo) + } + + err = i.netInfo.FromGRPCMessage(netInfo) + } + + return err +} + +func (l *NetworkInfoResponse) ToGRPCMessage() grpc.Message { + var m *netmap.NetworkInfoResponse + + if l != nil { + m = new(netmap.NetworkInfoResponse) + + m.SetBody(l.body.ToGRPCMessage().(*netmap.NetworkInfoResponse_Body)) + l.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (l *NetworkInfoResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.NetworkInfoResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + l.body = nil + } else { + if l.body == nil { + l.body = new(NetworkInfoResponseBody) + } + + err = l.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return l.ResponseHeaders.FromMessage(v) +} + +func (x *NetMap) ToGRPCMessage() grpc.Message { + var m *netmap.Netmap + + if x != nil { + m = new(netmap.Netmap) + + m.SetEpoch(x.epoch) + + if x.nodes != nil { + nodes := make([]netmap.NodeInfo, len(x.nodes)) + + for i := range x.nodes { + nodes[i] = *x.nodes[i].ToGRPCMessage().(*netmap.NodeInfo) + } + + m.SetNodes(nodes) + } + } + + return m +} + +func (x *NetMap) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.Netmap) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + nodes := v.GetNodes() + if nodes == nil { + x.nodes = nil + } else { + x.nodes = make([]NodeInfo, len(nodes)) + + for i := range nodes { + err = x.nodes[i].FromGRPCMessage(&nodes[i]) + if err != nil { + return err + } + } + } + + x.epoch = v.GetEpoch() + + return nil +} + +func (x *SnapshotRequestBody) ToGRPCMessage() grpc.Message { + var m *netmap.NetmapSnapshotRequest_Body + + if x != nil { + m = new(netmap.NetmapSnapshotRequest_Body) + } + + return m +} + +func (x *SnapshotRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.NetmapSnapshotRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + return nil +} + +func (x *SnapshotRequest) ToGRPCMessage() grpc.Message { + var m *netmap.NetmapSnapshotRequest + + if x != nil { + m = new(netmap.NetmapSnapshotRequest) + + m.SetBody(x.body.ToGRPCMessage().(*netmap.NetmapSnapshotRequest_Body)) + x.RequestHeaders.ToMessage(m) + } + + return m +} + +func (x *SnapshotRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.NetmapSnapshotRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + x.body = nil + } else { + if x.body == nil { + x.body = new(SnapshotRequestBody) + } + + err = x.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return x.RequestHeaders.FromMessage(v) +} + +func (x *SnapshotResponseBody) ToGRPCMessage() grpc.Message { + var m *netmap.NetmapSnapshotResponse_Body + + if x != nil { + m = new(netmap.NetmapSnapshotResponse_Body) + + m.SetNetmap(x.netMap.ToGRPCMessage().(*netmap.Netmap)) + } + + return m +} + +func (x *SnapshotResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.NetmapSnapshotResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + netMap := v.GetNetmap() + if netMap == nil { + x.netMap = nil + } else { + if x.netMap == nil { + x.netMap = new(NetMap) + } + + err = x.netMap.FromGRPCMessage(netMap) + } + + return err +} + +func (x *SnapshotResponse) ToGRPCMessage() grpc.Message { + var m *netmap.NetmapSnapshotResponse + + if x != nil { + m = new(netmap.NetmapSnapshotResponse) + + m.SetBody(x.body.ToGRPCMessage().(*netmap.NetmapSnapshotResponse_Body)) + x.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (x *SnapshotResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*netmap.NetmapSnapshotResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + x.body = nil + } else { + if x.body == nil { + x.body = new(SnapshotResponseBody) + } + + err = x.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return x.ResponseHeaders.FromMessage(v) +} diff --git a/apiv2/netmap/grpc/service_frostfs.pb.go b/apiv2/netmap/grpc/service_frostfs.pb.go new file mode 100644 index 0000000..b137fb9 Binary files /dev/null and b/apiv2/netmap/grpc/service_frostfs.pb.go differ diff --git a/apiv2/netmap/grpc/service_frostfs_fuzz.go b/apiv2/netmap/grpc/service_frostfs_fuzz.go new file mode 100644 index 0000000..ebb59bc --- /dev/null +++ b/apiv2/netmap/grpc/service_frostfs_fuzz.go @@ -0,0 +1,121 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package netmap + +func DoFuzzProtoLocalNodeInfoRequest(data []byte) int { + msg := new(LocalNodeInfoRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONLocalNodeInfoRequest(data []byte) int { + msg := new(LocalNodeInfoRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoLocalNodeInfoResponse(data []byte) int { + msg := new(LocalNodeInfoResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONLocalNodeInfoResponse(data []byte) int { + msg := new(LocalNodeInfoResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoNetworkInfoRequest(data []byte) int { + msg := new(NetworkInfoRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONNetworkInfoRequest(data []byte) int { + msg := new(NetworkInfoRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoNetworkInfoResponse(data []byte) int { + msg := new(NetworkInfoResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONNetworkInfoResponse(data []byte) int { + msg := new(NetworkInfoResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoNetmapSnapshotRequest(data []byte) int { + msg := new(NetmapSnapshotRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONNetmapSnapshotRequest(data []byte) int { + msg := new(NetmapSnapshotRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoNetmapSnapshotResponse(data []byte) int { + msg := new(NetmapSnapshotResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONNetmapSnapshotResponse(data []byte) int { + msg := new(NetmapSnapshotResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/netmap/grpc/service_frostfs_test.go b/apiv2/netmap/grpc/service_frostfs_test.go new file mode 100644 index 0000000..5c9035f --- /dev/null +++ b/apiv2/netmap/grpc/service_frostfs_test.go @@ -0,0 +1,71 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package netmap + +import ( + testing "testing" +) + +func FuzzProtoLocalNodeInfoRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoLocalNodeInfoRequest(data) + }) +} +func FuzzJSONLocalNodeInfoRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONLocalNodeInfoRequest(data) + }) +} +func FuzzProtoLocalNodeInfoResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoLocalNodeInfoResponse(data) + }) +} +func FuzzJSONLocalNodeInfoResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONLocalNodeInfoResponse(data) + }) +} +func FuzzProtoNetworkInfoRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoNetworkInfoRequest(data) + }) +} +func FuzzJSONNetworkInfoRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONNetworkInfoRequest(data) + }) +} +func FuzzProtoNetworkInfoResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoNetworkInfoResponse(data) + }) +} +func FuzzJSONNetworkInfoResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONNetworkInfoResponse(data) + }) +} +func FuzzProtoNetmapSnapshotRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoNetmapSnapshotRequest(data) + }) +} +func FuzzJSONNetmapSnapshotRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONNetmapSnapshotRequest(data) + }) +} +func FuzzProtoNetmapSnapshotResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoNetmapSnapshotResponse(data) + }) +} +func FuzzJSONNetmapSnapshotResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONNetmapSnapshotResponse(data) + }) +} diff --git a/apiv2/netmap/grpc/service_grpc.pb.go b/apiv2/netmap/grpc/service_grpc.pb.go new file mode 100644 index 0000000..7881cd6 Binary files /dev/null and b/apiv2/netmap/grpc/service_grpc.pb.go differ diff --git a/apiv2/netmap/grpc/types_frostfs.pb.go b/apiv2/netmap/grpc/types_frostfs.pb.go new file mode 100644 index 0000000..1ee614f Binary files /dev/null and b/apiv2/netmap/grpc/types_frostfs.pb.go differ diff --git a/apiv2/netmap/grpc/types_frostfs_fuzz.go b/apiv2/netmap/grpc/types_frostfs_fuzz.go new file mode 100644 index 0000000..89ccd74 --- /dev/null +++ b/apiv2/netmap/grpc/types_frostfs_fuzz.go @@ -0,0 +1,159 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package netmap + +func DoFuzzProtoFilter(data []byte) int { + msg := new(Filter) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONFilter(data []byte) int { + msg := new(Filter) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoSelector(data []byte) int { + msg := new(Selector) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONSelector(data []byte) int { + msg := new(Selector) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoReplica(data []byte) int { + msg := new(Replica) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONReplica(data []byte) int { + msg := new(Replica) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoPlacementPolicy(data []byte) int { + msg := new(PlacementPolicy) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONPlacementPolicy(data []byte) int { + msg := new(PlacementPolicy) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoNodeInfo(data []byte) int { + msg := new(NodeInfo) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONNodeInfo(data []byte) int { + msg := new(NodeInfo) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoNetmap(data []byte) int { + msg := new(Netmap) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONNetmap(data []byte) int { + msg := new(Netmap) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoNetworkConfig(data []byte) int { + msg := new(NetworkConfig) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONNetworkConfig(data []byte) int { + msg := new(NetworkConfig) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoNetworkInfo(data []byte) int { + msg := new(NetworkInfo) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONNetworkInfo(data []byte) int { + msg := new(NetworkInfo) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/netmap/grpc/types_frostfs_test.go b/apiv2/netmap/grpc/types_frostfs_test.go new file mode 100644 index 0000000..9996dc9 --- /dev/null +++ b/apiv2/netmap/grpc/types_frostfs_test.go @@ -0,0 +1,91 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package netmap + +import ( + testing "testing" +) + +func FuzzProtoFilter(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoFilter(data) + }) +} +func FuzzJSONFilter(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONFilter(data) + }) +} +func FuzzProtoSelector(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoSelector(data) + }) +} +func FuzzJSONSelector(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONSelector(data) + }) +} +func FuzzProtoReplica(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoReplica(data) + }) +} +func FuzzJSONReplica(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONReplica(data) + }) +} +func FuzzProtoPlacementPolicy(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoPlacementPolicy(data) + }) +} +func FuzzJSONPlacementPolicy(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONPlacementPolicy(data) + }) +} +func FuzzProtoNodeInfo(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoNodeInfo(data) + }) +} +func FuzzJSONNodeInfo(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONNodeInfo(data) + }) +} +func FuzzProtoNetmap(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoNetmap(data) + }) +} +func FuzzJSONNetmap(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONNetmap(data) + }) +} +func FuzzProtoNetworkConfig(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoNetworkConfig(data) + }) +} +func FuzzJSONNetworkConfig(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONNetworkConfig(data) + }) +} +func FuzzProtoNetworkInfo(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoNetworkInfo(data) + }) +} +func FuzzJSONNetworkInfo(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONNetworkInfo(data) + }) +} diff --git a/apiv2/netmap/json.go b/apiv2/netmap/json.go new file mode 100644 index 0000000..5839dda --- /dev/null +++ b/apiv2/netmap/json.go @@ -0,0 +1,62 @@ +package netmap + +import ( + netmap "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +func (p *PlacementPolicy) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(p) +} + +func (p *PlacementPolicy) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(p, data, new(netmap.PlacementPolicy)) +} + +func (f *Filter) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(f) +} + +func (f *Filter) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(f, data, new(netmap.Filter)) +} + +func (s *Selector) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(s) +} + +func (s *Selector) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(s, data, new(netmap.Selector)) +} + +func (r *Replica) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(r) +} + +func (r *Replica) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(r, data, new(netmap.Replica)) +} + +func (a *Attribute) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(a) +} + +func (a *Attribute) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(a, data, new(netmap.NodeInfo_Attribute)) +} + +func (ni *NodeInfo) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(ni) +} + +func (ni *NodeInfo) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(ni, data, new(netmap.NodeInfo)) +} + +func (i *NetworkInfo) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(i) +} + +func (i *NetworkInfo) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(i, data, new(netmap.NetworkInfo)) +} diff --git a/apiv2/netmap/marshal.go b/apiv2/netmap/marshal.go new file mode 100644 index 0000000..a44389c --- /dev/null +++ b/apiv2/netmap/marshal.go @@ -0,0 +1,576 @@ +package netmap + +import ( + netmap "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + protoutil "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto" +) + +const ( + nameFilterField = 1 + keyFilterField = 2 + opFilterField = 3 + valueFilterField = 4 + filtersFilterField = 5 + + nameSelectorField = 1 + countSelectorField = 2 + clauseSelectorField = 3 + attributeSelectorField = 4 + filterSelectorField = 5 + + countReplicaField = 1 + selectorReplicaField = 2 + ecDataCountReplicaField = 3 + ecParityCountReplicaField = 4 + + replicasPolicyField = 1 + backupPolicyField = 2 + selectorsPolicyField = 3 + filtersPolicyField = 4 + uniquePolicyField = 5 + + keyAttributeField = 1 + valueAttributeField = 2 + parentsAttributeField = 3 + + keyNodeInfoField = 1 + addressNodeInfoField = 2 + attributesNodeInfoField = 3 + stateNodeInfoField = 4 + + versionInfoResponseBodyField = 1 + nodeInfoResponseBodyField = 2 +) + +func (f *Filter) StableMarshal(buf []byte) []byte { + if f == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, f.StableSize()) + } + + var offset int + + offset += protoutil.StringMarshal(nameFilterField, buf[offset:], f.name) + offset += protoutil.StringMarshal(keyFilterField, buf[offset:], f.key) + offset += protoutil.EnumMarshal(opFilterField, buf[offset:], int32(f.op)) + offset += protoutil.StringMarshal(valueFilterField, buf[offset:], f.value) + + for i := range f.filters { + offset += protoutil.NestedStructureMarshal(filtersFilterField, buf[offset:], &f.filters[i]) + } + + return buf +} + +func (f *Filter) StableSize() (size int) { + if f == nil { + return 0 + } + + size += protoutil.StringSize(nameFilterField, f.name) + size += protoutil.StringSize(keyFilterField, f.key) + size += protoutil.EnumSize(opFilterField, int32(f.op)) + size += protoutil.StringSize(valueFilterField, f.value) + for i := range f.filters { + size += protoutil.NestedStructureSize(filtersFilterField, &f.filters[i]) + } + + return size +} + +func (f *Filter) Unmarshal(data []byte) error { + return message.Unmarshal(f, data, new(netmap.Filter)) +} + +func (s *Selector) StableMarshal(buf []byte) []byte { + if s == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, s.StableSize()) + } + + var offset int + + offset += protoutil.StringMarshal(nameSelectorField, buf[offset:], s.name) + offset += protoutil.UInt32Marshal(countSelectorField, buf[offset:], s.count) + offset += protoutil.EnumMarshal(clauseSelectorField, buf[offset:], int32(s.clause)) + offset += protoutil.StringMarshal(attributeSelectorField, buf[offset:], s.attribute) + protoutil.StringMarshal(filterSelectorField, buf[offset:], s.filter) + + return buf +} + +func (s *Selector) StableSize() (size int) { + if s == nil { + return 0 + } + + size += protoutil.StringSize(nameSelectorField, s.name) + size += protoutil.UInt32Size(countSelectorField, s.count) + size += protoutil.EnumSize(countSelectorField, int32(s.clause)) + size += protoutil.StringSize(attributeSelectorField, s.attribute) + size += protoutil.StringSize(filterSelectorField, s.filter) + + return size +} + +func (s *Selector) Unmarshal(data []byte) error { + return message.Unmarshal(s, data, new(netmap.Selector)) +} + +func (r *Replica) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += protoutil.UInt32Marshal(countReplicaField, buf[offset:], r.count) + offset += protoutil.StringMarshal(selectorReplicaField, buf[offset:], r.selector) + offset += protoutil.UInt32Marshal(ecDataCountReplicaField, buf[offset:], r.ecDataCount) + protoutil.UInt32Marshal(ecParityCountReplicaField, buf[offset:], r.ecParityCount) + + return buf +} + +func (r *Replica) StableSize() (size int) { + if r == nil { + return 0 + } + + size += protoutil.UInt32Size(countReplicaField, r.count) + size += protoutil.StringSize(selectorReplicaField, r.selector) + size += protoutil.UInt32Size(ecDataCountReplicaField, r.ecDataCount) + size += protoutil.UInt32Size(ecParityCountReplicaField, r.ecParityCount) + + return size +} + +func (r *Replica) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(netmap.Replica)) +} + +func (p *PlacementPolicy) StableMarshal(buf []byte) []byte { + if p == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, p.StableSize()) + } + + var offset int + + for i := range p.replicas { + offset += protoutil.NestedStructureMarshal(replicasPolicyField, buf[offset:], &p.replicas[i]) + } + + offset += protoutil.UInt32Marshal(backupPolicyField, buf[offset:], p.backupFactor) + + for i := range p.selectors { + offset += protoutil.NestedStructureMarshal(selectorsPolicyField, buf[offset:], &p.selectors[i]) + } + + for i := range p.filters { + offset += protoutil.NestedStructureMarshal(filtersPolicyField, buf[offset:], &p.filters[i]) + } + + protoutil.BoolMarshal(uniquePolicyField, buf[offset:], p.unique) + + return buf +} + +func (p *PlacementPolicy) StableSize() (size int) { + if p == nil { + return 0 + } + + for i := range p.replicas { + size += protoutil.NestedStructureSize(replicasPolicyField, &p.replicas[i]) + } + + size += protoutil.UInt32Size(backupPolicyField, p.backupFactor) + + for i := range p.selectors { + size += protoutil.NestedStructureSize(selectorsPolicyField, &p.selectors[i]) + } + + for i := range p.filters { + size += protoutil.NestedStructureSize(filtersPolicyField, &p.filters[i]) + } + + size += protoutil.BoolSize(uniquePolicyField, p.unique) + + return size +} + +func (p *PlacementPolicy) Unmarshal(data []byte) error { + return message.Unmarshal(p, data, new(netmap.PlacementPolicy)) +} + +func (a *Attribute) StableMarshal(buf []byte) []byte { + if a == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, a.StableSize()) + } + + var offset int + + offset += protoutil.StringMarshal(keyAttributeField, buf[offset:], a.key) + offset += protoutil.StringMarshal(valueAttributeField, buf[offset:], a.value) + + for i := range a.parents { + offset += protoutil.StringMarshal(parentsAttributeField, buf[offset:], a.parents[i]) + } + + return buf +} + +func (a *Attribute) StableSize() (size int) { + if a == nil { + return 0 + } + + size += protoutil.StringSize(keyAttributeField, a.key) + size += protoutil.StringSize(valueAttributeField, a.value) + + for i := range a.parents { + size += protoutil.StringSize(parentsAttributeField, a.parents[i]) + } + + return size +} + +func (a *Attribute) Unmarshal(data []byte) error { + return message.Unmarshal(a, data, new(netmap.NodeInfo_Attribute)) +} + +func (ni *NodeInfo) StableMarshal(buf []byte) []byte { + if ni == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, ni.StableSize()) + } + + var offset int + + offset += protoutil.BytesMarshal(keyNodeInfoField, buf[offset:], ni.publicKey) + offset += protoutil.RepeatedStringMarshal(addressNodeInfoField, buf[offset:], ni.addresses) + + for i := range ni.attributes { + offset += protoutil.NestedStructureMarshal(attributesNodeInfoField, buf[offset:], &ni.attributes[i]) + } + + protoutil.EnumMarshal(stateNodeInfoField, buf[offset:], int32(ni.state)) + + return buf +} + +func (ni *NodeInfo) StableSize() (size int) { + if ni == nil { + return 0 + } + + size += protoutil.BytesSize(keyNodeInfoField, ni.publicKey) + size += protoutil.RepeatedStringSize(addressNodeInfoField, ni.addresses) + + for i := range ni.attributes { + size += protoutil.NestedStructureSize(attributesNodeInfoField, &ni.attributes[i]) + } + + size += protoutil.EnumSize(stateNodeInfoField, int32(ni.state)) + + return size +} + +func (ni *NodeInfo) Unmarshal(data []byte) error { + return message.Unmarshal(ni, data, new(netmap.NodeInfo)) +} + +func (l *LocalNodeInfoRequestBody) StableMarshal(_ []byte) []byte { + return nil +} + +func (l *LocalNodeInfoRequestBody) StableSize() (size int) { + return 0 +} + +func (l *LocalNodeInfoRequestBody) Unmarshal([]byte) error { + return nil +} + +func (l *LocalNodeInfoResponseBody) StableMarshal(buf []byte) []byte { + if l == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, l.StableSize()) + } + + var offset int + + offset += protoutil.NestedStructureMarshal(versionInfoResponseBodyField, buf[offset:], l.version) + protoutil.NestedStructureMarshal(nodeInfoResponseBodyField, buf[offset:], l.nodeInfo) + + return buf +} + +func (l *LocalNodeInfoResponseBody) StableSize() (size int) { + if l == nil { + return 0 + } + + size += protoutil.NestedStructureSize(versionInfoResponseBodyField, l.version) + size += protoutil.NestedStructureSize(nodeInfoResponseBodyField, l.nodeInfo) + + return size +} + +func (l *LocalNodeInfoResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(l, data, new(netmap.LocalNodeInfoResponse_Body)) +} + +const ( + _ = iota + netPrmKeyFNum + netPrmValFNum +) + +func (x *NetworkParameter) StableMarshal(buf []byte) []byte { + if x == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, x.StableSize()) + } + + var offset int + + offset += protoutil.BytesMarshal(netPrmKeyFNum, buf[offset:], x.k) + protoutil.BytesMarshal(netPrmValFNum, buf[offset:], x.v) + + return buf +} + +func (x *NetworkParameter) StableSize() (size int) { + if x == nil { + return 0 + } + + size += protoutil.BytesSize(netPrmKeyFNum, x.k) + size += protoutil.BytesSize(netPrmValFNum, x.v) + + return size +} + +const ( + _ = iota + netCfgPrmsFNum +) + +func (x *NetworkConfig) StableMarshal(buf []byte) []byte { + if x == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, x.StableSize()) + } + + var offset int + + for i := range x.ps { + offset += protoutil.NestedStructureMarshal(netCfgPrmsFNum, buf[offset:], &x.ps[i]) + } + + return buf +} + +func (x *NetworkConfig) StableSize() (size int) { + if x == nil { + return 0 + } + + for i := range x.ps { + size += protoutil.NestedStructureSize(netCfgPrmsFNum, &x.ps[i]) + } + + return size +} + +const ( + _ = iota + netInfoCurEpochFNum + netInfoMagicNumFNum + netInfoMSPerBlockFNum + netInfoCfgFNum +) + +func (i *NetworkInfo) StableMarshal(buf []byte) []byte { + if i == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, i.StableSize()) + } + + var offset int + + offset += protoutil.UInt64Marshal(netInfoCurEpochFNum, buf[offset:], i.curEpoch) + offset += protoutil.UInt64Marshal(netInfoMagicNumFNum, buf[offset:], i.magicNum) + offset += protoutil.Int64Marshal(netInfoMSPerBlockFNum, buf[offset:], i.msPerBlock) + protoutil.NestedStructureMarshal(netInfoCfgFNum, buf[offset:], i.netCfg) + + return buf +} + +func (i *NetworkInfo) StableSize() (size int) { + if i == nil { + return 0 + } + + size += protoutil.UInt64Size(netInfoCurEpochFNum, i.curEpoch) + size += protoutil.UInt64Size(netInfoMagicNumFNum, i.magicNum) + size += protoutil.Int64Size(netInfoMSPerBlockFNum, i.msPerBlock) + size += protoutil.NestedStructureSize(netInfoCfgFNum, i.netCfg) + + return size +} + +func (i *NetworkInfo) Unmarshal(data []byte) error { + return message.Unmarshal(i, data, new(netmap.NetworkInfo)) +} + +func (l *NetworkInfoRequestBody) StableMarshal(_ []byte) []byte { + return nil +} + +func (l *NetworkInfoRequestBody) StableSize() (size int) { + return 0 +} + +func (l *NetworkInfoRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(l, data, new(netmap.NetworkInfoRequest_Body)) +} + +const ( + _ = iota + netInfoRespBodyNetInfoFNum +) + +func (i *NetworkInfoResponseBody) StableMarshal(buf []byte) []byte { + if i == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, i.StableSize()) + } + + protoutil.NestedStructureMarshal(netInfoRespBodyNetInfoFNum, buf, i.netInfo) + + return buf +} + +func (i *NetworkInfoResponseBody) StableSize() (size int) { + if i == nil { + return 0 + } + + size += protoutil.NestedStructureSize(netInfoRespBodyNetInfoFNum, i.netInfo) + + return size +} + +func (i *NetworkInfoResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(i, data, new(netmap.NetworkInfoResponse_Body)) +} + +const ( + _ = iota + fNumNetMapEpoch + fNumNetMapNodes +) + +func (x *NetMap) StableMarshal(buf []byte) []byte { + if x == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, x.StableSize()) + } + + offset := protoutil.UInt64Marshal(fNumNetMapEpoch, buf, x.epoch) + + for i := range x.nodes { + offset += protoutil.NestedStructureMarshal(fNumNetMapNodes, buf[offset:], &x.nodes[i]) + } + + return buf +} + +func (x *NetMap) StableSize() (size int) { + if x != nil { + size = protoutil.UInt64Size(fNumNetMapEpoch, x.epoch) + + for i := range x.nodes { + size += protoutil.NestedStructureSize(fNumNetMapNodes, &x.nodes[i]) + } + } + + return +} + +func (x *SnapshotRequestBody) StableMarshal([]byte) []byte { + return nil +} + +func (x *SnapshotRequestBody) StableSize() int { + return 0 +} + +const ( + _ = iota + fNumSnapshotResponseBodyNetMap +) + +func (x *SnapshotResponseBody) StableMarshal(buf []byte) []byte { + if x == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, x.StableSize()) + } + + protoutil.NestedStructureMarshal(fNumSnapshotResponseBodyNetMap, buf, x.netMap) + + return buf +} + +func (x *SnapshotResponseBody) StableSize() (size int) { + if x != nil { + size = protoutil.NestedStructureSize(fNumSnapshotResponseBodyNetMap, x.netMap) + } + + return +} diff --git a/apiv2/netmap/message_test.go b/apiv2/netmap/message_test.go new file mode 100644 index 0000000..60f4561 --- /dev/null +++ b/apiv2/netmap/message_test.go @@ -0,0 +1,32 @@ +package netmap_test + +import ( + "testing" + + netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap/test" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + messagetest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message/test" +) + +func TestMessageConvert(t *testing.T) { + messagetest.TestRPCMessage(t, + func(empty bool) message.Message { return netmaptest.GenerateFilter(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateSelector(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateReplica(empty) }, + func(empty bool) message.Message { return netmaptest.GeneratePlacementPolicy(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateAttribute(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateNodeInfo(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateLocalNodeInfoRequest(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateLocalNodeInfoResponseBody(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateNetworkParameter(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateNetworkConfig(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateNetworkInfo(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateNetworkInfoRequest(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateNetworkInfoResponseBody(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateNetMap(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateSnapshotRequestBody(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateSnapshotRequest(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateSnapshotResponseBody(empty) }, + func(empty bool) message.Message { return netmaptest.GenerateSnapshotResponse(empty) }, + ) +} diff --git a/apiv2/netmap/string.go b/apiv2/netmap/string.go new file mode 100644 index 0000000..7f93a7f --- /dev/null +++ b/apiv2/netmap/string.go @@ -0,0 +1,68 @@ +package netmap + +import ( + netmap "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap/grpc" +) + +// String returns string representation of Clause. +func (x Clause) String() string { + return ClauseToGRPCMessage(x).String() +} + +// FromString parses Clause from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (x *Clause) FromString(s string) bool { + var g netmap.Clause + + ok := g.FromString(s) + + if ok { + *x = ClauseFromGRPCMessage(g) + } + + return ok +} + +// String returns string representation of Operation. +func (x Operation) String() string { + return OperationToGRPCMessage(x).String() +} + +// FromString parses Operation from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (x *Operation) FromString(s string) bool { + var g netmap.Operation + + ok := g.FromString(s) + + if ok { + *x = OperationFromGRPCMessage(g) + } + + return ok +} + +// String returns string representation of NodeState. +func (x NodeState) String() string { + return NodeStateToGRPCMessage(x).String() +} + +// FromString parses NodeState from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (x *NodeState) FromString(s string) bool { + var g netmap.NodeInfo_State + + ok := g.FromString(s) + + if ok { + *x = NodeStateFromRPCMessage(g) + } + + return ok +} diff --git a/apiv2/netmap/test/generate.go b/apiv2/netmap/test/generate.go new file mode 100644 index 0000000..6b35cc3 --- /dev/null +++ b/apiv2/netmap/test/generate.go @@ -0,0 +1,335 @@ +package netmaptest + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" + refstest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/test" + sessiontest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session/test" +) + +func GenerateFilter(empty bool) *netmap.Filter { + return generateFilter(empty, true) +} + +func generateFilter(empty, withSub bool) *netmap.Filter { + m := new(netmap.Filter) + + if !empty { + m.SetKey("filter key") + m.SetValue("filter value") + m.SetName("filter name") + m.SetOp(1) + + if withSub { + m.SetFilters([]netmap.Filter{ + *generateFilter(empty, false), + *generateFilter(empty, false), + }) + } + } + + return m +} + +func GenerateFilters(empty bool) []netmap.Filter { + var res []netmap.Filter + + if !empty { + res = append(res, + *GenerateFilter(false), + *GenerateFilter(false), + ) + } + + return res +} + +func GenerateSelector(empty bool) *netmap.Selector { + m := new(netmap.Selector) + + if !empty { + m.SetCount(66) + m.SetAttribute("selector attribute") + m.SetFilter("select filter") + m.SetName("select name") + m.SetClause(1) + } + + return m +} + +func GenerateSelectors(empty bool) []netmap.Selector { + var res []netmap.Selector + + if !empty { + res = append(res, + *GenerateSelector(false), + *GenerateSelector(false), + ) + } + + return res +} + +func GenerateReplica(empty bool) *netmap.Replica { + m := new(netmap.Replica) + + if !empty { + m.SetCount(42) + m.SetSelector("replica selector") + } + + return m +} + +func GenerateEC(empty bool) *netmap.Replica { + m := new(netmap.Replica) + + if !empty { + m.SetECDataCount(4) + m.SetECParityCount(2) + } + + return m +} + +func GenerateReplicas(empty bool) []netmap.Replica { + var res []netmap.Replica + + if !empty { + res = append(res, + *GenerateReplica(false), + *GenerateReplica(false), + *GenerateEC(false), + ) + } + + return res +} + +func GeneratePlacementPolicy(empty bool) *netmap.PlacementPolicy { + m := new(netmap.PlacementPolicy) + + if !empty { + m.SetContainerBackupFactor(322) + m.SetFilters(GenerateFilters(false)) + m.SetSelectors(GenerateSelectors(false)) + m.SetReplicas(GenerateReplicas(false)) + m.SetUnique(true) + } + + return m +} + +func GenerateAttribute(empty bool) *netmap.Attribute { + m := new(netmap.Attribute) + + if !empty { + m.SetKey("attribute key") + m.SetValue("attribute val") + } + + return m +} + +func GenerateAttributes(empty bool) []netmap.Attribute { + var res []netmap.Attribute + + if !empty { + res = append(res, + *GenerateAttribute(false), + *GenerateAttribute(false), + ) + } + + return res +} + +func GenerateNodeInfo(empty bool) *netmap.NodeInfo { + m := new(netmap.NodeInfo) + + if !empty { + m.SetAddresses("node address", "node address 2") + m.SetPublicKey([]byte{1, 2, 3}) + m.SetState(33) + m.SetAttributes(GenerateAttributes(empty)) + } + + return m +} + +func GenerateLocalNodeInfoRequestBody(_ bool) *netmap.LocalNodeInfoRequestBody { + m := new(netmap.LocalNodeInfoRequestBody) + + return m +} + +func GenerateLocalNodeInfoRequest(empty bool) *netmap.LocalNodeInfoRequest { + m := new(netmap.LocalNodeInfoRequest) + + if !empty { + m.SetBody(GenerateLocalNodeInfoRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateLocalNodeInfoResponseBody(empty bool) *netmap.LocalNodeInfoResponseBody { + m := new(netmap.LocalNodeInfoResponseBody) + + if !empty { + m.SetNodeInfo(GenerateNodeInfo(false)) + } + + m.SetVersion(refstest.GenerateVersion(empty)) + + return m +} + +func GenerateLocalNodeInfoResponse(empty bool) *netmap.LocalNodeInfoResponse { + m := new(netmap.LocalNodeInfoResponse) + + if !empty { + m.SetBody(GenerateLocalNodeInfoResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} + +func GenerateNetworkParameter(empty bool) *netmap.NetworkParameter { + m := new(netmap.NetworkParameter) + + if !empty { + m.SetKey([]byte("key")) + m.SetValue([]byte("value")) + } + + return m +} + +func GenerateNetworkConfig(empty bool) *netmap.NetworkConfig { + m := new(netmap.NetworkConfig) + + if !empty { + m.SetParameters( + *GenerateNetworkParameter(empty), + *GenerateNetworkParameter(empty), + ) + } + + return m +} + +func GenerateNetworkInfo(empty bool) *netmap.NetworkInfo { + m := new(netmap.NetworkInfo) + + if !empty { + m.SetMagicNumber(228) + m.SetCurrentEpoch(666) + m.SetMsPerBlock(5678) + m.SetNetworkConfig(GenerateNetworkConfig(empty)) + } + + return m +} + +func GenerateNetworkInfoRequestBody(_ bool) *netmap.NetworkInfoRequestBody { + m := new(netmap.NetworkInfoRequestBody) + + return m +} + +func GenerateNetworkInfoRequest(empty bool) *netmap.NetworkInfoRequest { + m := new(netmap.NetworkInfoRequest) + + if !empty { + m.SetBody(GenerateNetworkInfoRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateNetworkInfoResponseBody(empty bool) *netmap.NetworkInfoResponseBody { + m := new(netmap.NetworkInfoResponseBody) + + if !empty { + m.SetNetworkInfo(GenerateNetworkInfo(false)) + } + + return m +} + +func GenerateNetworkInfoResponse(empty bool) *netmap.NetworkInfoResponse { + m := new(netmap.NetworkInfoResponse) + + if !empty { + m.SetBody(GenerateNetworkInfoResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} + +func GenerateNetMap(empty bool) *netmap.NetMap { + m := new(netmap.NetMap) + + if !empty { + m.SetEpoch(987) + m.SetNodes([]netmap.NodeInfo{ + *GenerateNodeInfo(false), + *GenerateNodeInfo(false), + }) + } + + return m +} + +func GenerateSnapshotRequestBody(_ bool) *netmap.SnapshotRequestBody { + return new(netmap.SnapshotRequestBody) +} + +func GenerateSnapshotRequest(empty bool) *netmap.SnapshotRequest { + m := new(netmap.SnapshotRequest) + + if !empty { + m.SetBody(GenerateSnapshotRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateSnapshotResponseBody(empty bool) *netmap.SnapshotResponseBody { + m := new(netmap.SnapshotResponseBody) + + if !empty { + m.SetNetMap(GenerateNetMap(false)) + } + + return m +} + +func GenerateSnapshotResponse(empty bool) *netmap.SnapshotResponse { + m := new(netmap.SnapshotResponse) + + if !empty { + m.SetBody(GenerateSnapshotResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} diff --git a/apiv2/netmap/types.go b/apiv2/netmap/types.go new file mode 100644 index 0000000..e855293 --- /dev/null +++ b/apiv2/netmap/types.go @@ -0,0 +1,783 @@ +package netmap + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" +) + +type LocalNodeInfoRequest struct { + body *LocalNodeInfoRequestBody + + session.RequestHeaders +} + +type LocalNodeInfoResponse struct { + body *LocalNodeInfoResponseBody + + session.ResponseHeaders +} + +// NetworkInfoRequest is a structure of NetworkInfo request. +type NetworkInfoRequest struct { + body *NetworkInfoRequestBody + + session.RequestHeaders +} + +// NetworkInfoResponse is a structure of NetworkInfo response. +type NetworkInfoResponse struct { + body *NetworkInfoResponseBody + + session.ResponseHeaders +} + +type Filter struct { + name string + key string + op Operation + value string + filters []Filter +} + +type Selector struct { + name string + count uint32 + clause Clause + attribute string + filter string +} + +type Replica struct { + count uint32 + selector string + + ecDataCount uint32 + ecParityCount uint32 +} + +type Operation uint32 + +type PlacementPolicy struct { + replicas []Replica + backupFactor uint32 + selectors []Selector + filters []Filter + unique bool +} + +// Attribute of storage node. +type Attribute struct { + key string + value string + parents []string +} + +// NodeInfo of storage node. +type NodeInfo struct { + publicKey []byte + addresses []string + attributes []Attribute + state NodeState +} + +// NodeState of storage node. +type NodeState uint32 + +// Clause of placement selector. +type Clause uint32 + +type LocalNodeInfoRequestBody struct{} + +type LocalNodeInfoResponseBody struct { + version *refs.Version + nodeInfo *NodeInfo +} + +const ( + UnspecifiedState NodeState = iota + Online + Offline + Maintenance +) + +const ( + UnspecifiedOperation Operation = iota + EQ + NE + GT + GE + LT + LE + OR + AND + NOT + LIKE +) + +const ( + UnspecifiedClause Clause = iota + Same + Distinct +) + +func (f *Filter) GetFilters() []Filter { + if f != nil { + return f.filters + } + + return nil +} + +func (f *Filter) SetFilters(filters []Filter) { + f.filters = filters +} + +func (f *Filter) GetValue() string { + if f != nil { + return f.value + } + + return "" +} + +func (f *Filter) SetValue(value string) { + f.value = value +} + +func (f *Filter) GetOp() Operation { + if f != nil { + return f.op + } + return UnspecifiedOperation +} + +func (f *Filter) SetOp(op Operation) { + f.op = op +} + +func (f *Filter) GetKey() string { + if f != nil { + return f.key + } + + return "" +} + +func (f *Filter) SetKey(key string) { + f.key = key +} + +func (f *Filter) GetName() string { + if f != nil { + return f.name + } + + return "" +} + +func (f *Filter) SetName(name string) { + f.name = name +} + +func (s *Selector) GetFilter() string { + if s != nil { + return s.filter + } + + return "" +} + +func (s *Selector) SetFilter(filter string) { + s.filter = filter +} + +func (s *Selector) GetAttribute() string { + if s != nil { + return s.attribute + } + + return "" +} + +func (s *Selector) SetAttribute(attribute string) { + s.attribute = attribute +} + +func (s *Selector) GetClause() Clause { + if s != nil { + return s.clause + } + + return UnspecifiedClause +} + +func (s *Selector) SetClause(clause Clause) { + s.clause = clause +} + +func (s *Selector) GetCount() uint32 { + if s != nil { + return s.count + } + + return 0 +} + +func (s *Selector) SetCount(count uint32) { + s.count = count +} + +func (s *Selector) GetName() string { + if s != nil { + return s.name + } + + return "" +} + +func (s *Selector) SetName(name string) { + s.name = name +} + +func (r *Replica) GetSelector() string { + if r != nil { + return r.selector + } + + return "" +} + +func (r *Replica) SetSelector(selector string) { + r.selector = selector +} + +func (r *Replica) GetCount() uint32 { + if r != nil { + return r.count + } + + return 0 +} + +func (r *Replica) SetCount(count uint32) { + r.count = count +} + +func (r *Replica) GetECDataCount() uint32 { + if r != nil { + return r.ecDataCount + } + + return 0 +} + +func (r *Replica) SetECDataCount(count uint32) { + r.ecDataCount = count +} + +func (r *Replica) GetECParityCount() uint32 { + if r != nil { + return r.ecParityCount + } + + return 0 +} + +func (r *Replica) SetECParityCount(count uint32) { + r.ecParityCount = count +} + +func (p *PlacementPolicy) GetUnique() bool { + if p != nil { + return p.unique + } + return false +} + +func (p *PlacementPolicy) SetUnique(unique bool) { + p.unique = unique +} + +func (p *PlacementPolicy) GetFilters() []Filter { + if p != nil { + return p.filters + } + + return nil +} + +func (p *PlacementPolicy) SetFilters(filters []Filter) { + p.filters = filters +} + +func (p *PlacementPolicy) GetSelectors() []Selector { + if p != nil { + return p.selectors + } + + return nil +} + +func (p *PlacementPolicy) SetSelectors(selectors []Selector) { + p.selectors = selectors +} + +func (p *PlacementPolicy) GetContainerBackupFactor() uint32 { + if p != nil { + return p.backupFactor + } + + return 0 +} + +func (p *PlacementPolicy) SetContainerBackupFactor(backupFactor uint32) { + p.backupFactor = backupFactor +} + +func (p *PlacementPolicy) GetReplicas() []Replica { + if p == nil { + return nil + } + + return p.replicas +} + +func (p *PlacementPolicy) SetReplicas(replicas []Replica) { + p.replicas = replicas +} + +func (a *Attribute) GetKey() string { + if a != nil { + return a.key + } + + return "" +} + +func (a *Attribute) SetKey(v string) { + a.key = v +} + +func (a *Attribute) GetValue() string { + if a != nil { + return a.value + } + + return "" +} + +func (a *Attribute) SetValue(v string) { + a.value = v +} + +func (a *Attribute) GetParents() []string { + if a != nil { + return a.parents + } + + return nil +} + +func (a *Attribute) SetParents(parent []string) { + a.parents = parent +} + +func (ni *NodeInfo) GetPublicKey() []byte { + if ni != nil { + return ni.publicKey + } + + return nil +} + +func (ni *NodeInfo) SetPublicKey(v []byte) { + ni.publicKey = v +} + +// GetAddress returns node's network address. +// +// Deprecated: use IterateAddresses. +func (ni *NodeInfo) GetAddress() (addr string) { + ni.IterateAddresses(func(s string) bool { + addr = s + return true + }) + + return +} + +// SetAddress sets node's network address. +// +// Deprecated: use SetAddresses. +func (ni *NodeInfo) SetAddress(v string) { + ni.SetAddresses(v) +} + +// SetAddresses sets list of network addresses of the node. +func (ni *NodeInfo) SetAddresses(v ...string) { + ni.addresses = v +} + +// NumberOfAddresses returns number of network addresses of the node. +func (ni *NodeInfo) NumberOfAddresses() int { + if ni != nil { + return len(ni.addresses) + } + + return 0 +} + +// IterateAddresses iterates over network addresses of the node. +// Breaks iteration on f's true return. +// +// Handler should not be nil. +func (ni *NodeInfo) IterateAddresses(f func(string) bool) { + if ni != nil { + for i := range ni.addresses { + if f(ni.addresses[i]) { + break + } + } + } +} + +func (ni *NodeInfo) GetAttributes() []Attribute { + if ni != nil { + return ni.attributes + } + + return nil +} + +func (ni *NodeInfo) SetAttributes(v []Attribute) { + ni.attributes = v +} + +func (ni *NodeInfo) GetState() NodeState { + if ni != nil { + return ni.state + } + + return UnspecifiedState +} + +func (ni *NodeInfo) SetState(state NodeState) { + ni.state = state +} + +func (l *LocalNodeInfoResponseBody) GetVersion() *refs.Version { + if l != nil { + return l.version + } + + return nil +} + +func (l *LocalNodeInfoResponseBody) SetVersion(version *refs.Version) { + l.version = version +} + +func (l *LocalNodeInfoResponseBody) GetNodeInfo() *NodeInfo { + if l != nil { + return l.nodeInfo + } + + return nil +} + +func (l *LocalNodeInfoResponseBody) SetNodeInfo(nodeInfo *NodeInfo) { + l.nodeInfo = nodeInfo +} + +func (l *LocalNodeInfoRequest) GetBody() *LocalNodeInfoRequestBody { + if l != nil { + return l.body + } + return nil +} + +func (l *LocalNodeInfoRequest) SetBody(body *LocalNodeInfoRequestBody) { + l.body = body +} + +func (l *LocalNodeInfoResponse) GetBody() *LocalNodeInfoResponseBody { + if l != nil { + return l.body + } + return nil +} + +func (l *LocalNodeInfoResponse) SetBody(body *LocalNodeInfoResponseBody) { + l.body = body +} + +// NetworkParameter represents NeoFS network parameter. +type NetworkParameter struct { + k, v []byte +} + +// GetKey returns parameter key. +func (x *NetworkParameter) GetKey() []byte { + if x != nil { + return x.k + } + + return nil +} + +// SetKey sets parameter key. +func (x *NetworkParameter) SetKey(k []byte) { + x.k = k +} + +// GetValue returns parameter value. +func (x *NetworkParameter) GetValue() []byte { + if x != nil { + return x.v + } + + return nil +} + +// SetValue sets parameter value. +func (x *NetworkParameter) SetValue(v []byte) { + x.v = v +} + +// NetworkConfig represents NeoFS network configuration. +type NetworkConfig struct { + ps []NetworkParameter +} + +// NumberOfParameters returns number of network parameters. +func (x *NetworkConfig) NumberOfParameters() int { + if x != nil { + return len(x.ps) + } + + return 0 +} + +// IterateParameters iterates over network parameters. +// Breaks iteration on f's true return. +// +// Handler must not be nil. +func (x *NetworkConfig) IterateParameters(f func(*NetworkParameter) bool) { + if x != nil { + for i := range x.ps { + if f(&x.ps[i]) { + break + } + } + } +} + +// SetParameters sets list of network parameters. +func (x *NetworkConfig) SetParameters(v ...NetworkParameter) { + x.ps = v +} + +// NetworkInfo groups information about +// NeoFS network. +type NetworkInfo struct { + curEpoch, magicNum uint64 + + msPerBlock int64 + + netCfg *NetworkConfig +} + +// GetCurrentEpoch returns number of the current epoch. +func (i *NetworkInfo) GetCurrentEpoch() uint64 { + if i != nil { + return i.curEpoch + } + + return 0 +} + +// SetCurrentEpoch sets number of the current epoch. +func (i *NetworkInfo) SetCurrentEpoch(epoch uint64) { + i.curEpoch = epoch +} + +// GetMagicNumber returns magic number of the sidechain. +func (i *NetworkInfo) GetMagicNumber() uint64 { + if i != nil { + return i.magicNum + } + + return 0 +} + +// SetMagicNumber sets magic number of the sidechain. +func (i *NetworkInfo) SetMagicNumber(magic uint64) { + i.magicNum = magic +} + +// GetMsPerBlock returns MillisecondsPerBlock network parameter. +func (i *NetworkInfo) GetMsPerBlock() int64 { + if i != nil { + return i.msPerBlock + } + + return 0 +} + +// SetMsPerBlock sets MillisecondsPerBlock network parameter. +func (i *NetworkInfo) SetMsPerBlock(v int64) { + i.msPerBlock = v +} + +// GetNetworkConfig returns NeoFS network configuration. +func (i *NetworkInfo) GetNetworkConfig() *NetworkConfig { + if i != nil { + return i.netCfg + } + + return nil +} + +// SetNetworkConfig sets NeoFS network configuration. +func (i *NetworkInfo) SetNetworkConfig(v *NetworkConfig) { + i.netCfg = v +} + +// NetworkInfoRequestBody is a structure of NetworkInfo request body. +type NetworkInfoRequestBody struct{} + +// NetworkInfoResponseBody is a structure of NetworkInfo response body. +type NetworkInfoResponseBody struct { + netInfo *NetworkInfo +} + +// GetNetworkInfo returns information about the NeoFS network. +func (i *NetworkInfoResponseBody) GetNetworkInfo() *NetworkInfo { + if i != nil { + return i.netInfo + } + + return nil +} + +// SetNetworkInfo sets information about the NeoFS network. +func (i *NetworkInfoResponseBody) SetNetworkInfo(netInfo *NetworkInfo) { + i.netInfo = netInfo +} + +func (l *NetworkInfoRequest) GetBody() *NetworkInfoRequestBody { + if l != nil { + return l.body + } + return nil +} + +func (l *NetworkInfoRequest) SetBody(body *NetworkInfoRequestBody) { + l.body = body +} + +func (l *NetworkInfoResponse) GetBody() *NetworkInfoResponseBody { + if l != nil { + return l.body + } + return nil +} + +func (l *NetworkInfoResponse) SetBody(body *NetworkInfoResponseBody) { + l.body = body +} + +// NetMap represents structure of NeoFS network map. +type NetMap struct { + epoch uint64 + + nodes []NodeInfo +} + +// Epoch returns revision number of the NetMap. +func (x *NetMap) Epoch() uint64 { + if x != nil { + return x.epoch + } + + return 0 +} + +// SetEpoch sets revision number of the NetMap. +func (x *NetMap) SetEpoch(v uint64) { + x.epoch = v +} + +// Nodes returns nodes presented in the NetMap. +func (x *NetMap) Nodes() []NodeInfo { + if x != nil { + return x.nodes + } + + return nil +} + +// SetNodes sets nodes presented in the NetMap. +func (x *NetMap) SetNodes(v []NodeInfo) { + x.nodes = v +} + +// SnapshotRequestBody represents structure of Snapshot request body. +type SnapshotRequestBody struct{} + +// SnapshotRequest represents structure of Snapshot request. +type SnapshotRequest struct { + body *SnapshotRequestBody + + session.RequestHeaders +} + +func (x *SnapshotRequest) GetBody() *SnapshotRequestBody { + if x != nil { + return x.body + } + + return nil +} + +func (x *SnapshotRequest) SetBody(body *SnapshotRequestBody) { + x.body = body +} + +// SnapshotResponseBody represents structure of Snapshot response body. +type SnapshotResponseBody struct { + netMap *NetMap +} + +// NetMap returns current NetMap. +func (x *SnapshotResponseBody) NetMap() *NetMap { + if x != nil { + return x.netMap + } + + return nil +} + +// SetNetMap sets current NetMap. +func (x *SnapshotResponseBody) SetNetMap(netMap *NetMap) { + x.netMap = netMap +} + +// SnapshotResponse represents structure of Snapshot response. +type SnapshotResponse struct { + body *SnapshotResponseBody + + session.ResponseHeaders +} + +func (x *SnapshotResponse) GetBody() *SnapshotResponseBody { + if x != nil { + return x.body + } + + return nil +} + +func (x *SnapshotResponse) SetBody(body *SnapshotResponseBody) { + x.body = body +} diff --git a/apiv2/object/attributes.go b/apiv2/object/attributes.go new file mode 100644 index 0000000..7f4fca0 --- /dev/null +++ b/apiv2/object/attributes.go @@ -0,0 +1,187 @@ +package object + +import ( + "errors" + "fmt" + "strconv" +) + +// SysAttributePrefix is a prefix of key to system attribute. +const SysAttributePrefix = "__SYSTEM__" + +const ( + // SysAttributeUploadID marks smaller parts of a split bigger object. + SysAttributeUploadID = SysAttributePrefix + "UPLOAD_ID" + + // SysAttributeExpEpoch tells GC to delete object after that epoch. + SysAttributeExpEpoch = SysAttributePrefix + "EXPIRATION_EPOCH" + + // SysAttributeTickEpoch defines what epoch must produce object + // notification. + SysAttributeTickEpoch = SysAttributePrefix + "TICK_EPOCH" + + // SysAttributeTickTopic defines what topic object notification + // must be sent to. + SysAttributeTickTopic = SysAttributePrefix + "TICK_TOPIC" +) + +// SysAttributePrefixNeoFS is a prefix of key to system attribute. +// Deprecated: use SysAttributePrefix. +const SysAttributePrefixNeoFS = "__NEOFS__" + +const ( + // SysAttributeUploadIDNeoFS marks smaller parts of a split bigger object. + // Deprecated: use SysAttributeUploadID. + SysAttributeUploadIDNeoFS = SysAttributePrefixNeoFS + "UPLOAD_ID" + + // SysAttributeExpEpochNeoFS tells GC to delete object after that epoch. + // Deprecated: use SysAttributeExpEpoch. + SysAttributeExpEpochNeoFS = SysAttributePrefixNeoFS + "EXPIRATION_EPOCH" + + // SysAttributeTickEpochNeoFS defines what epoch must produce object + // notification. + // Deprecated: use SysAttributeTickEpoch. + SysAttributeTickEpochNeoFS = SysAttributePrefixNeoFS + "TICK_EPOCH" + + // SysAttributeTickTopicNeoFS defines what topic object notification + // must be sent to. + // Deprecated: use SysAttributeTickTopic. + SysAttributeTickTopicNeoFS = SysAttributePrefixNeoFS + "TICK_TOPIC" +) + +// NotificationInfo groups information about object notification +// that can be written to object. +// +// Topic is an optional field. +type NotificationInfo struct { + epoch uint64 + topic string +} + +// Epoch returns object notification tick +// epoch. +func (n NotificationInfo) Epoch() uint64 { + return n.epoch +} + +// SetEpoch sets object notification tick +// epoch. +func (n *NotificationInfo) SetEpoch(epoch uint64) { + n.epoch = epoch +} + +// Topic return optional object notification +// topic. +func (n NotificationInfo) Topic() string { + return n.topic +} + +// SetTopic sets optional object notification +// topic. +func (n *NotificationInfo) SetTopic(topic string) { + n.topic = topic +} + +// WriteNotificationInfo writes NotificationInfo to the Object via attributes. Object must not be nil. +// +// Existing notification attributes are expected to be key-unique, otherwise undefined behavior. +func WriteNotificationInfo(o *Object, ni NotificationInfo) { + h := o.GetHeader() + if h == nil { + h = new(Header) + o.SetHeader(h) + } + + var ( + attrs = h.GetAttributes() + + epoch = strconv.FormatUint(ni.Epoch(), 10) + topic = ni.Topic() + + changedEpoch bool + changedTopic bool + deleteIndex = -1 + ) + + for i := range attrs { + switch attrs[i].GetKey() { + case SysAttributeTickEpoch, SysAttributeTickEpochNeoFS: + attrs[i].SetValue(epoch) + changedEpoch = true + case SysAttributeTickTopic, SysAttributeTickTopicNeoFS: + changedTopic = true + + if topic == "" { + deleteIndex = i + break + } + + attrs[i].SetValue(topic) + } + + if changedEpoch && changedTopic { + break + } + } + + if deleteIndex != -1 { + // approach without allocation/waste + // coping works since the attributes + // order is not important + attrs[deleteIndex] = attrs[len(attrs)-1] + attrs = attrs[:len(attrs)-1] + } + + if !changedEpoch { + index := len(attrs) + attrs = append(attrs, Attribute{}) + attrs[index].SetKey(SysAttributeTickEpoch) + attrs[index].SetValue(epoch) + } + + if !changedTopic && topic != "" { + index := len(attrs) + attrs = append(attrs, Attribute{}) + attrs[index].SetKey(SysAttributeTickTopic) + attrs[index].SetValue(topic) + } + + h.SetAttributes(attrs) +} + +// ErrNotificationNotSet means that object does not have notification. +var ErrNotificationNotSet = errors.New("notification for object is not set") + +// GetNotificationInfo looks for object notification attributes. Object must not be nil. +// Returns ErrNotificationNotSet if no corresponding attributes +// were found. +// +// Existing notification attributes are expected to be key-unique, otherwise undefined behavior. +func GetNotificationInfo(o *Object) (*NotificationInfo, error) { + var ( + foundEpoch bool + ni = new(NotificationInfo) + ) + + for _, attr := range o.GetHeader().GetAttributes() { + switch key := attr.GetKey(); key { + case SysAttributeTickEpoch, SysAttributeTickEpochNeoFS: + epoch, err := strconv.ParseUint(attr.GetValue(), 10, 64) + if err != nil { + return nil, fmt.Errorf("could not parse epoch: %w", err) + } + + ni.SetEpoch(epoch) + + foundEpoch = true + case SysAttributeTickTopic, SysAttributeTickTopicNeoFS: + ni.SetTopic(attr.GetValue()) + } + } + + if !foundEpoch { + return nil, ErrNotificationNotSet + } + + return ni, nil +} diff --git a/apiv2/object/attributes_test.go b/apiv2/object/attributes_test.go new file mode 100644 index 0000000..85635da --- /dev/null +++ b/apiv2/object/attributes_test.go @@ -0,0 +1,89 @@ +package object + +import ( + "strconv" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSetNotification(t *testing.T) { + o := new(Object) + + ni := NotificationInfo{ + epoch: 10, + topic: "test", + } + + WriteNotificationInfo(o, ni) + + var foundEpoch, foundTopic bool + + for _, attr := range o.GetHeader().GetAttributes() { + switch key := attr.GetKey(); key { + case SysAttributeTickEpoch: + require.Equal(t, false, foundEpoch) + + uEpoch, err := strconv.ParseUint(attr.GetValue(), 10, 64) + require.NoError(t, err) + + require.Equal(t, ni.Epoch(), uEpoch) + foundEpoch = true + case SysAttributeTickTopic: + require.Equal(t, false, foundTopic) + require.Equal(t, ni.Topic(), attr.GetValue()) + foundTopic = true + } + } + + require.Equal(t, true, foundEpoch && foundTopic) +} + +func TestGetNotification(t *testing.T) { + o := new(Object) + + attr := []Attribute{ + {SysAttributeTickEpoch, "10"}, + {SysAttributeTickTopic, "test"}, + } + + h := new(Header) + h.SetAttributes(attr) + + o.SetHeader(h) + + t.Run("No error", func(t *testing.T) { + ni, err := GetNotificationInfo(o) + require.NoError(t, err) + + require.Equal(t, uint64(10), ni.Epoch()) + require.Equal(t, "test", ni.Topic()) + }) +} + +func TestIntegration(t *testing.T) { + o := new(Object) + + var ( + ni1 = NotificationInfo{ + epoch: 10, + topic: "", + } + ni2 = NotificationInfo{ + epoch: 11, + topic: "test", + } + ) + + WriteNotificationInfo(o, ni1) + WriteNotificationInfo(o, ni2) + + t.Run("double set", func(t *testing.T) { + ni, err := GetNotificationInfo(o) + require.NoError(t, err) + + require.Equal(t, ni2.epoch, ni.Epoch()) + require.Equal(t, ni2.topic, ni.Topic()) + require.Equal(t, 2, len(o.GetHeader().GetAttributes())) + }) +} diff --git a/apiv2/object/bench_test.go b/apiv2/object/bench_test.go new file mode 100644 index 0000000..b29b1d1 --- /dev/null +++ b/apiv2/object/bench_test.go @@ -0,0 +1,45 @@ +package object + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" +) + +func randString(n int) string { + x := make([]byte, n) + for i := range x { + x[i] = byte('a' + rand.Intn('z'-'a')) + } + return string(x) +} + +func BenchmarkAttributesMarshal(b *testing.B) { + attrs := make([]Attribute, 50) + for i := range attrs { + attrs[i].key = SysAttributePrefix + randString(10) + attrs[i].val = randString(10) + } + raw := AttributesToGRPC(attrs) + require.Equal(b, len(raw), len(attrs)) + + b.Run("marshal", func(b *testing.B) { + b.ReportAllocs() + for range b.N { + res := AttributesToGRPC(attrs) + if len(res) != len(raw) { + b.FailNow() + } + } + }) + b.Run("unmarshal", func(b *testing.B) { + b.ReportAllocs() + for range b.N { + res, err := AttributesFromGRPC(raw) + if err != nil || len(res) != len(raw) { + b.FailNow() + } + } + }) +} diff --git a/apiv2/object/convert.go b/apiv2/object/convert.go new file mode 100644 index 0000000..8a2d82b --- /dev/null +++ b/apiv2/object/convert.go @@ -0,0 +1,2555 @@ +package object + +import ( + "fmt" + + object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + refsGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + sessionGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session/grpc" +) + +func TypeToGRPCField(t Type) object.ObjectType { + return object.ObjectType(t) +} + +func TypeFromGRPCField(t object.ObjectType) Type { + return Type(t) +} + +func MatchTypeToGRPCField(t MatchType) object.MatchType { + return object.MatchType(t) +} + +func MatchTypeFromGRPCField(t object.MatchType) MatchType { + return MatchType(t) +} + +func (h *ShortHeader) ToGRPCMessage() grpc.Message { + var m *object.ShortHeader + + if h != nil { + m = new(object.ShortHeader) + + m.SetVersion(h.version.ToGRPCMessage().(*refsGRPC.Version)) + m.SetOwnerId(h.ownerID.ToGRPCMessage().(*refsGRPC.OwnerID)) + m.SetHomomorphicHash(h.homoHash.ToGRPCMessage().(*refsGRPC.Checksum)) + m.SetPayloadHash(h.payloadHash.ToGRPCMessage().(*refsGRPC.Checksum)) + m.SetObjectType(TypeToGRPCField(h.typ)) + m.SetCreationEpoch(h.creatEpoch) + m.SetPayloadLength(h.payloadLen) + } + + return m +} + +func (h *ShortHeader) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.ShortHeader) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + version := v.GetVersion() + if version == nil { + h.version = nil + } else { + if h.version == nil { + h.version = new(refs.Version) + } + + err = h.version.FromGRPCMessage(version) + if err != nil { + return err + } + } + + ownerID := v.GetOwnerId() + if ownerID == nil { + h.ownerID = nil + } else { + if h.ownerID == nil { + h.ownerID = new(refs.OwnerID) + } + + err = h.ownerID.FromGRPCMessage(ownerID) + if err != nil { + return err + } + } + + homoHash := v.GetHomomorphicHash() + if homoHash == nil { + h.homoHash = nil + } else { + if h.homoHash == nil { + h.homoHash = new(refs.Checksum) + } + + err = h.homoHash.FromGRPCMessage(homoHash) + if err != nil { + return err + } + } + + payloadHash := v.GetPayloadHash() + if payloadHash == nil { + h.payloadHash = nil + } else { + if h.payloadHash == nil { + h.payloadHash = new(refs.Checksum) + } + + err = h.payloadHash.FromGRPCMessage(payloadHash) + if err != nil { + return err + } + } + + h.typ = TypeFromGRPCField(v.GetObjectType()) + h.creatEpoch = v.GetCreationEpoch() + h.payloadLen = v.GetPayloadLength() + + return nil +} + +func (a *Attribute) ToGRPCMessage() grpc.Message { + var m *object.Header_Attribute + + if a != nil { + m = new(object.Header_Attribute) + + m.SetKey(a.key) + m.SetValue(a.val) + } + + return m +} + +func (a *Attribute) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.Header_Attribute) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + a.key = v.GetKey() + a.val = v.GetValue() + + return nil +} + +func AttributesToGRPC(xs []Attribute) (res []object.Header_Attribute) { + if xs != nil { + res = make([]object.Header_Attribute, 0, len(xs)) + + for i := range xs { + res = append(res, *xs[i].ToGRPCMessage().(*object.Header_Attribute)) + } + } + + return +} + +func AttributesFromGRPC(xs []object.Header_Attribute) (res []Attribute, err error) { + if xs != nil { + res = make([]Attribute, len(xs)) + + for i := range xs { + err = res[i].FromGRPCMessage(&xs[i]) + if err != nil { + return + } + } + } + + return +} + +func (h *SplitHeader) ToGRPCMessage() grpc.Message { + var m *object.Header_Split + + if h != nil { + m = new(object.Header_Split) + + m.SetParent(h.par.ToGRPCMessage().(*refsGRPC.ObjectID)) + m.SetPrevious(h.prev.ToGRPCMessage().(*refsGRPC.ObjectID)) + m.SetParentHeader(h.parHdr.ToGRPCMessage().(*object.Header)) + m.SetParentSignature(h.parSig.ToGRPCMessage().(*refsGRPC.Signature)) + m.SetChildren(refs.ObjectIDListToGRPCMessage(h.children)) + m.SetSplitId(h.splitID) + } + + return m +} + +func (h *SplitHeader) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.Header_Split) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + par := v.GetParent() + if par == nil { + h.par = nil + } else { + if h.par == nil { + h.par = new(refs.ObjectID) + } + + err = h.par.FromGRPCMessage(par) + if err != nil { + return err + } + } + + prev := v.GetPrevious() + if prev == nil { + h.prev = nil + } else { + if h.prev == nil { + h.prev = new(refs.ObjectID) + } + + err = h.prev.FromGRPCMessage(prev) + if err != nil { + return err + } + } + + parHdr := v.GetParentHeader() + if parHdr == nil { + h.parHdr = nil + } else { + if h.parHdr == nil { + h.parHdr = new(Header) + } + + err = h.parHdr.FromGRPCMessage(parHdr) + if err != nil { + return err + } + } + + parSig := v.GetParentSignature() + if parSig == nil { + h.parSig = nil + } else { + if h.parSig == nil { + h.parSig = new(refs.Signature) + } + + err = h.parSig.FromGRPCMessage(parSig) + if err != nil { + return err + } + } + + h.children, err = refs.ObjectIDListFromGRPCMessage(v.GetChildren()) + if err != nil { + return err + } + + h.splitID = v.GetSplitId() + + return nil +} + +func (h *ECHeader) ToGRPCMessage() grpc.Message { + var m *object.Header_EC + + if h != nil { + m = new(object.Header_EC) + + m.Parent = h.Parent.ToGRPCMessage().(*refsGRPC.ObjectID) + m.ParentSplitId = h.ParentSplitID + m.ParentSplitParentId = h.ParentSplitParentID.ToGRPCMessage().(*refsGRPC.ObjectID) + m.ParentAttributes = AttributesToGRPC(h.ParentAttributes) + m.Index = h.Index + m.Total = h.Total + m.Header = h.Header + m.HeaderLength = h.HeaderLength + } + + return m +} + +func (h *ECHeader) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.Header_EC) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + par := v.GetParent() + if par == nil { + h.Parent = nil + } else { + if h.Parent == nil { + h.Parent = new(refs.ObjectID) + } + + err = h.Parent.FromGRPCMessage(par) + if err != nil { + return err + } + } + + h.ParentSplitID = v.GetParentSplitId() + + parSplitParentID := v.GetParentSplitParentId() + if parSplitParentID == nil { + h.ParentSplitParentID = nil + } else { + if h.ParentSplitParentID == nil { + h.ParentSplitParentID = new(refs.ObjectID) + } + + err = h.ParentSplitParentID.FromGRPCMessage(parSplitParentID) + if err != nil { + return err + } + } + + if h.ParentAttributes, err = AttributesFromGRPC(v.GetParentAttributes()); err != nil { + return err + } + + h.Index = v.GetIndex() + h.Total = v.GetTotal() + h.Header = v.GetHeader() + h.HeaderLength = v.GetHeaderLength() + return nil +} + +func (h *Header) ToGRPCMessage() grpc.Message { + var m *object.Header + + if h != nil { + m = new(object.Header) + + m.SetVersion(h.version.ToGRPCMessage().(*refsGRPC.Version)) + m.SetPayloadHash(h.payloadHash.ToGRPCMessage().(*refsGRPC.Checksum)) + m.SetOwnerId(h.ownerID.ToGRPCMessage().(*refsGRPC.OwnerID)) + m.SetHomomorphicHash(h.homoHash.ToGRPCMessage().(*refsGRPC.Checksum)) + m.SetContainerId(h.cid.ToGRPCMessage().(*refsGRPC.ContainerID)) + m.SetSessionToken(h.sessionToken.ToGRPCMessage().(*sessionGRPC.SessionToken)) + m.SetSplit(h.split.ToGRPCMessage().(*object.Header_Split)) + m.Ec = h.ec.ToGRPCMessage().(*object.Header_EC) + m.SetAttributes(AttributesToGRPC(h.attr)) + m.SetPayloadLength(h.payloadLen) + m.SetCreationEpoch(h.creatEpoch) + m.SetObjectType(TypeToGRPCField(h.typ)) + } + + return m +} + +func (h *Header) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.Header) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + if err := h.fillVersion(v); err != nil { + return err + } + if err := h.fillPayloadHash(v); err != nil { + return err + } + if err := h.fillOwnerID(v); err != nil { + return err + } + if err := h.fillHomomorphicHash(v); err != nil { + return err + } + if err := h.fillContainerID(v); err != nil { + return err + } + if err := h.fillSessionToken(v); err != nil { + return err + } + if err := h.fillSplitHeader(v); err != nil { + return err + } + if err := h.fillECHeader(v); err != nil { + return err + } + + h.attr, err = AttributesFromGRPC(v.GetAttributes()) + if err != nil { + return err + } + + h.payloadLen = v.GetPayloadLength() + h.creatEpoch = v.GetCreationEpoch() + h.typ = TypeFromGRPCField(v.GetObjectType()) + + return nil +} + +func (h *Header) fillVersion(v *object.Header) error { + version := v.GetVersion() + if version == nil { + h.version = nil + return nil + } + + if h.version == nil { + h.version = new(refs.Version) + } + return h.version.FromGRPCMessage(version) +} + +func (h *Header) fillPayloadHash(v *object.Header) error { + payloadHash := v.GetPayloadHash() + if payloadHash == nil { + h.payloadHash = nil + return nil + } + + if h.payloadHash == nil { + h.payloadHash = new(refs.Checksum) + } + return h.payloadHash.FromGRPCMessage(payloadHash) +} + +func (h *Header) fillOwnerID(v *object.Header) error { + ownerID := v.GetOwnerId() + if ownerID == nil { + h.ownerID = nil + return nil + } + + if h.ownerID == nil { + h.ownerID = new(refs.OwnerID) + } + return h.ownerID.FromGRPCMessage(ownerID) +} + +func (h *Header) fillHomomorphicHash(v *object.Header) error { + homoHash := v.GetHomomorphicHash() + if homoHash == nil { + h.homoHash = nil + return nil + } + + if h.homoHash == nil { + h.homoHash = new(refs.Checksum) + } + return h.homoHash.FromGRPCMessage(homoHash) +} + +func (h *Header) fillContainerID(v *object.Header) error { + cid := v.GetContainerId() + if cid == nil { + h.cid = nil + return nil + } + + if h.cid == nil { + h.cid = new(refs.ContainerID) + } + return h.cid.FromGRPCMessage(cid) +} + +func (h *Header) fillSessionToken(v *object.Header) error { + sessionToken := v.GetSessionToken() + if sessionToken == nil { + h.sessionToken = nil + return nil + } + + if h.sessionToken == nil { + h.sessionToken = new(session.Token) + } + return h.sessionToken.FromGRPCMessage(sessionToken) +} + +func (h *Header) fillSplitHeader(v *object.Header) error { + split := v.GetSplit() + if split == nil { + h.split = nil + return nil + } + + if h.split == nil { + h.split = new(SplitHeader) + } + return h.split.FromGRPCMessage(split) +} + +func (h *Header) fillECHeader(v *object.Header) error { + ec := v.GetEc() + if ec == nil { + h.ec = nil + return nil + } + + if h.ec == nil { + h.ec = new(ECHeader) + } + return h.ec.FromGRPCMessage(ec) +} + +func (h *HeaderWithSignature) ToGRPCMessage() grpc.Message { + var m *object.HeaderWithSignature + + if h != nil { + m = new(object.HeaderWithSignature) + + m.SetSignature(h.signature.ToGRPCMessage().(*refsGRPC.Signature)) + m.SetHeader(h.header.ToGRPCMessage().(*object.Header)) + } + + return m +} + +func (h *HeaderWithSignature) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.HeaderWithSignature) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + signature := v.GetSignature() + if signature == nil { + h.signature = nil + } else { + if h.signature == nil { + h.signature = new(refs.Signature) + } + + err = h.signature.FromGRPCMessage(signature) + if err != nil { + return err + } + } + + header := v.GetHeader() + if header == nil { + h.header = nil + } else { + if h.header == nil { + h.header = new(Header) + } + + err = h.header.FromGRPCMessage(header) + } + + return err +} + +func (o *Object) ToGRPCMessage() grpc.Message { + var m *object.Object + + if o != nil { + m = new(object.Object) + + m.SetObjectId(o.objectID.ToGRPCMessage().(*refsGRPC.ObjectID)) + m.SetSignature(o.idSig.ToGRPCMessage().(*refsGRPC.Signature)) + m.SetHeader(o.header.ToGRPCMessage().(*object.Header)) + m.SetPayload(o.payload) + } + + return m +} + +func (o *Object) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.Object) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + objectID := v.GetObjectId() + if objectID == nil { + o.objectID = nil + } else { + if o.objectID == nil { + o.objectID = new(refs.ObjectID) + } + + err = o.objectID.FromGRPCMessage(objectID) + if err != nil { + return err + } + } + + idSig := v.GetSignature() + if idSig == nil { + o.idSig = nil + } else { + if o.idSig == nil { + o.idSig = new(refs.Signature) + } + + err = o.idSig.FromGRPCMessage(idSig) + if err != nil { + return err + } + } + + header := v.GetHeader() + if header == nil { + o.header = nil + } else { + if o.header == nil { + o.header = new(Header) + } + + err = o.header.FromGRPCMessage(header) + if err != nil { + return err + } + } + + o.payload = v.GetPayload() + + return nil +} + +func (s *SplitInfo) ToGRPCMessage() grpc.Message { + var m *object.SplitInfo + + if s != nil { + m = new(object.SplitInfo) + + m.SetLastPart(s.lastPart.ToGRPCMessage().(*refsGRPC.ObjectID)) + m.SetLink(s.link.ToGRPCMessage().(*refsGRPC.ObjectID)) + m.SetSplitId(s.splitID) + } + + return m +} + +func (s *SplitInfo) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.SplitInfo) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + lastPart := v.GetLastPart() + if lastPart == nil { + s.lastPart = nil + } else { + if s.lastPart == nil { + s.lastPart = new(refs.ObjectID) + } + + err = s.lastPart.FromGRPCMessage(lastPart) + if err != nil { + return err + } + } + + link := v.GetLink() + if link == nil { + s.link = nil + } else { + if s.link == nil { + s.link = new(refs.ObjectID) + } + + err = s.link.FromGRPCMessage(link) + if err != nil { + return err + } + } + + s.splitID = v.GetSplitId() + + return nil +} + +func (s *ECInfo) ToGRPCMessage() grpc.Message { + var m *object.ECInfo + + if s != nil { + m = new(object.ECInfo) + + if s.Chunks != nil { + chunks := make([]object.ECInfo_Chunk, len(s.Chunks)) + for i := range chunks { + chunks[i] = *s.Chunks[i].ToGRPCMessage().(*object.ECInfo_Chunk) + } + m.Chunks = chunks + } + } + + return m +} + +func (s *ECInfo) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.ECInfo) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + chunks := v.GetChunks() + if chunks == nil { + s.Chunks = nil + } else { + s.Chunks = make([]ECChunk, len(chunks)) + for i := range chunks { + if err := s.Chunks[i].FromGRPCMessage(&chunks[i]); err != nil { + return err + } + } + } + return nil +} + +func (c *ECChunk) ToGRPCMessage() grpc.Message { + var m *object.ECInfo_Chunk + + if c != nil { + m = new(object.ECInfo_Chunk) + + m.Total = c.Total + m.Index = c.Index + m.Id = c.ID.ToGRPCMessage().(*refsGRPC.ObjectID) + } + + return m +} + +func (c *ECChunk) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.ECInfo_Chunk) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + if err := c.ID.FromGRPCMessage(v.GetId()); err != nil { + return err + } + c.Index = v.Index + c.Total = v.Total + + return nil +} + +func (r *GetRequestBody) ToGRPCMessage() grpc.Message { + var m *object.GetRequest_Body + + if r != nil { + m = new(object.GetRequest_Body) + + m.SetAddress(r.addr.ToGRPCMessage().(*refsGRPC.Address)) + m.SetRaw(r.raw) + } + + return m +} + +func (r *GetRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + addr := v.GetAddress() + if addr == nil { + r.addr = nil + } else { + if r.addr == nil { + r.addr = new(refs.Address) + } + + err = r.addr.FromGRPCMessage(addr) + if err != nil { + return err + } + } + + r.raw = v.GetRaw() + + return nil +} + +func (r *GetRequest) ToGRPCMessage() grpc.Message { + var m *object.GetRequest + + if r != nil { + m = new(object.GetRequest) + + m.SetBody(r.body.ToGRPCMessage().(*object.GetRequest_Body)) + r.RequestHeaders.ToMessage(m) + } + + return m +} + +func (r *GetRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(GetRequestBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.RequestHeaders.FromMessage(v) +} + +func (r *GetObjectPartInit) ToGRPCMessage() grpc.Message { + var m *object.GetResponse_Body_Init + + if r != nil { + m = new(object.GetResponse_Body_Init) + + m.SetObjectId(r.id.ToGRPCMessage().(*refsGRPC.ObjectID)) + m.SetSignature(r.sig.ToGRPCMessage().(*refsGRPC.Signature)) + m.SetHeader(r.hdr.ToGRPCMessage().(*object.Header)) + } + + return m +} + +func (r *GetObjectPartInit) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetResponse_Body_Init) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + id := v.GetObjectId() + if id == nil { + r.id = nil + } else { + if r.id == nil { + r.id = new(refs.ObjectID) + } + + err = r.id.FromGRPCMessage(id) + if err != nil { + return err + } + } + + sig := v.GetSignature() + if sig == nil { + r.sig = nil + } else { + if r.sig == nil { + r.sig = new(refs.Signature) + } + + err = r.sig.FromGRPCMessage(sig) + if err != nil { + return err + } + } + + hdr := v.GetHeader() + if hdr == nil { + r.hdr = nil + } else { + if r.hdr == nil { + r.hdr = new(Header) + } + + err = r.hdr.FromGRPCMessage(hdr) + } + + return err +} + +func (r *GetObjectPartChunk) ToGRPCMessage() grpc.Message { + var m *object.GetResponse_Body_Chunk + + if r != nil { + m = new(object.GetResponse_Body_Chunk) + + m.SetChunk(r.chunk) + } + + return m +} + +func (r *GetObjectPartChunk) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetResponse_Body_Chunk) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + r.chunk = v.GetChunk() + + return nil +} + +func (r *GetResponseBody) ToGRPCMessage() grpc.Message { + var m *object.GetResponse_Body + + if r != nil { + m = new(object.GetResponse_Body) + + switch v := r.GetObjectPart(); t := v.(type) { + case nil: + m.ObjectPart = nil + case *GetObjectPartInit: + m.SetInit(t.ToGRPCMessage().(*object.GetResponse_Body_Init)) + case *GetObjectPartChunk: + m.SetChunk(t.ToGRPCMessage().(*object.GetResponse_Body_Chunk)) + case *SplitInfo: + m.SetSplitInfo(t.ToGRPCMessage().(*object.SplitInfo)) + case *ECInfo: + m.SetEcInfo(t.ToGRPCMessage().(*object.ECInfo)) + default: + panic(fmt.Sprintf("unknown get object part %T", t)) + } + } + + return m +} + +func (r *GetResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + r.objPart = nil + + switch pt := v.GetObjectPart().(type) { + case nil: + case *object.GetResponse_Body_Init_: + if pt != nil { + partInit := new(GetObjectPartInit) + r.objPart = partInit + err = partInit.FromGRPCMessage(pt.Init) + } + case *object.GetResponse_Body_Chunk: + if pt != nil { + partChunk := new(GetObjectPartChunk) + r.objPart = partChunk + err = partChunk.FromGRPCMessage(pt) + } + case *object.GetResponse_Body_SplitInfo: + if pt != nil { + partSplit := new(SplitInfo) + r.objPart = partSplit + err = partSplit.FromGRPCMessage(pt.SplitInfo) + } + case *object.GetResponse_Body_EcInfo: + if pt != nil { + partEC := new(ECInfo) + r.objPart = partEC + err = partEC.FromGRPCMessage(pt.EcInfo) + } + default: + err = fmt.Errorf("unknown get object part %T", pt) + } + + return err +} + +func (r *GetResponse) ToGRPCMessage() grpc.Message { + var m *object.GetResponse + + if r != nil { + m = new(object.GetResponse) + + m.SetBody(r.body.ToGRPCMessage().(*object.GetResponse_Body)) + r.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (r *GetResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(GetResponseBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.ResponseHeaders.FromMessage(v) +} + +func (r *PutObjectPartInit) ToGRPCMessage() grpc.Message { + var m *object.PutRequest_Body_Init + + if r != nil { + m = new(object.PutRequest_Body_Init) + + m.SetObjectId(r.id.ToGRPCMessage().(*refsGRPC.ObjectID)) + m.SetSignature(r.sig.ToGRPCMessage().(*refsGRPC.Signature)) + m.SetHeader(r.hdr.ToGRPCMessage().(*object.Header)) + m.SetCopiesNumber(r.copyNum) + } + + return m +} + +func (r *PutObjectPartInit) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PutRequest_Body_Init) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + id := v.GetObjectId() + if id == nil { + r.id = nil + } else { + if r.id == nil { + r.id = new(refs.ObjectID) + } + + err = r.id.FromGRPCMessage(id) + if err != nil { + return err + } + } + + sig := v.GetSignature() + if sig == nil { + r.sig = nil + } else { + if r.sig == nil { + r.sig = new(refs.Signature) + } + + err = r.sig.FromGRPCMessage(sig) + if err != nil { + return err + } + } + + hdr := v.GetHeader() + if hdr == nil { + r.hdr = nil + } else { + if r.hdr == nil { + r.hdr = new(Header) + } + + err = r.hdr.FromGRPCMessage(hdr) + if err != nil { + return err + } + } + + r.copyNum = v.GetCopiesNumber() + + return nil +} + +func (r *PutObjectPartChunk) ToGRPCMessage() grpc.Message { + var m *object.PutRequest_Body_Chunk + + if r != nil { + m = new(object.PutRequest_Body_Chunk) + + m.SetChunk(r.chunk) + } + + return m +} + +func (r *PutObjectPartChunk) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PutRequest_Body_Chunk) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + r.chunk = v.GetChunk() + + return nil +} + +func (r *PutRequestBody) ToGRPCMessage() grpc.Message { + var m *object.PutRequest_Body + + if r != nil { + m = new(object.PutRequest_Body) + + switch v := r.GetObjectPart(); t := v.(type) { + case nil: + m.ObjectPart = nil + case *PutObjectPartInit: + m.SetInit(t.ToGRPCMessage().(*object.PutRequest_Body_Init)) + case *PutObjectPartChunk: + m.SetChunk(t.ToGRPCMessage().(*object.PutRequest_Body_Chunk)) + default: + panic(fmt.Sprintf("unknown put object part %T", t)) + } + } + + return m +} + +func (r *PutRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PutRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + r.objPart = nil + + switch pt := v.GetObjectPart().(type) { + case nil: + case *object.PutRequest_Body_Init_: + if pt != nil { + partInit := new(PutObjectPartInit) + r.objPart = partInit + err = partInit.FromGRPCMessage(pt.Init) + } + case *object.PutRequest_Body_Chunk: + if pt != nil { + partChunk := new(PutObjectPartChunk) + r.objPart = partChunk + err = partChunk.FromGRPCMessage(pt) + } + default: + err = fmt.Errorf("unknown put object part %T", pt) + } + + return err +} + +func (r *PutRequest) ToGRPCMessage() grpc.Message { + var m *object.PutRequest + + if r != nil { + m = new(object.PutRequest) + + m.SetBody(r.body.ToGRPCMessage().(*object.PutRequest_Body)) + r.RequestHeaders.ToMessage(m) + } + + return m +} + +func (r *PutRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PutRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(PutRequestBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.RequestHeaders.FromMessage(v) +} + +func (r *PutResponseBody) ToGRPCMessage() grpc.Message { + var m *object.PutResponse_Body + + if r != nil { + m = new(object.PutResponse_Body) + + m.SetObjectId(r.id.ToGRPCMessage().(*refsGRPC.ObjectID)) + } + + return m +} + +func (r *PutResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PutResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + id := v.GetObjectId() + if id == nil { + r.id = nil + } else { + if r.id == nil { + r.id = new(refs.ObjectID) + } + + err = r.id.FromGRPCMessage(id) + } + + return err +} + +func (r *PutResponse) ToGRPCMessage() grpc.Message { + var m *object.PutResponse + + if r != nil { + m = new(object.PutResponse) + + m.SetBody(r.body.ToGRPCMessage().(*object.PutResponse_Body)) + r.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (r *PutResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PutResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(PutResponseBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.ResponseHeaders.FromMessage(v) +} + +func (r *DeleteRequestBody) ToGRPCMessage() grpc.Message { + var m *object.DeleteRequest_Body + + if r != nil { + m = new(object.DeleteRequest_Body) + + m.SetAddress(r.addr.ToGRPCMessage().(*refsGRPC.Address)) + } + + return m +} + +func (r *DeleteRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.DeleteRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + addr := v.GetAddress() + if addr == nil { + r.addr = nil + } else { + if r.addr == nil { + r.addr = new(refs.Address) + } + + err = r.addr.FromGRPCMessage(addr) + } + + return err +} + +func (r *DeleteRequest) ToGRPCMessage() grpc.Message { + var m *object.DeleteRequest + + if r != nil { + m = new(object.DeleteRequest) + + m.SetBody(r.body.ToGRPCMessage().(*object.DeleteRequest_Body)) + r.RequestHeaders.ToMessage(m) + } + + return m +} + +func (r *DeleteRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.DeleteRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(DeleteRequestBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.RequestHeaders.FromMessage(v) +} + +func (r *DeleteResponseBody) ToGRPCMessage() grpc.Message { + var m *object.DeleteResponse_Body + + if r != nil { + m = new(object.DeleteResponse_Body) + + m.SetTombstone(r.tombstone.ToGRPCMessage().(*refsGRPC.Address)) + } + + return m +} + +func (r *DeleteResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.DeleteResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + tombstone := v.GetTombstone() + if tombstone == nil { + r.tombstone = nil + } else { + if r.tombstone == nil { + r.tombstone = new(refs.Address) + } + + err = r.tombstone.FromGRPCMessage(tombstone) + } + + return err +} + +func (r *DeleteResponse) ToGRPCMessage() grpc.Message { + var m *object.DeleteResponse + + if r != nil { + m = new(object.DeleteResponse) + + m.SetBody(r.body.ToGRPCMessage().(*object.DeleteResponse_Body)) + r.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (r *DeleteResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.DeleteResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(DeleteResponseBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.ResponseHeaders.FromMessage(v) +} + +func (r *HeadRequestBody) ToGRPCMessage() grpc.Message { + var m *object.HeadRequest_Body + + if r != nil { + m = new(object.HeadRequest_Body) + + m.SetAddress(r.addr.ToGRPCMessage().(*refsGRPC.Address)) + m.SetRaw(r.raw) + m.SetMainOnly(r.mainOnly) + } + + return m +} + +func (r *HeadRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.HeadRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + addr := v.GetAddress() + if addr == nil { + r.addr = nil + } else { + if r.addr == nil { + r.addr = new(refs.Address) + } + + err = r.addr.FromGRPCMessage(addr) + if err != nil { + return err + } + } + + r.raw = v.GetRaw() + r.mainOnly = v.GetMainOnly() + + return nil +} + +func (r *HeadRequest) ToGRPCMessage() grpc.Message { + var m *object.HeadRequest + + if r != nil { + m = new(object.HeadRequest) + + m.SetBody(r.body.ToGRPCMessage().(*object.HeadRequest_Body)) + r.RequestHeaders.ToMessage(m) + } + + return m +} + +func (r *HeadRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.HeadRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(HeadRequestBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.RequestHeaders.FromMessage(v) +} + +func (r *HeadResponseBody) ToGRPCMessage() grpc.Message { + var m *object.HeadResponse_Body + + if r != nil { + m = new(object.HeadResponse_Body) + + switch v := r.hdrPart.(type) { + case nil: + m.Head = nil + case *HeaderWithSignature: + m.SetHeader(v.ToGRPCMessage().(*object.HeaderWithSignature)) + case *ShortHeader: + m.SetShortHeader(v.ToGRPCMessage().(*object.ShortHeader)) + case *SplitInfo: + m.SetSplitInfo(v.ToGRPCMessage().(*object.SplitInfo)) + case *ECInfo: + m.SetEcInfo(v.ToGRPCMessage().(*object.ECInfo)) + default: + panic(fmt.Sprintf("unknown head part %T", v)) + } + } + + return m +} + +func (r *HeadResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.HeadResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + r.hdrPart = nil + + switch pt := v.GetHead().(type) { + case nil: + case *object.HeadResponse_Body_Header: + if pt != nil { + partHdr := new(HeaderWithSignature) + r.hdrPart = partHdr + err = partHdr.FromGRPCMessage(pt.Header) + } + case *object.HeadResponse_Body_ShortHeader: + if pt != nil { + partShort := new(ShortHeader) + r.hdrPart = partShort + err = partShort.FromGRPCMessage(pt.ShortHeader) + } + case *object.HeadResponse_Body_SplitInfo: + if pt != nil { + partSplit := new(SplitInfo) + r.hdrPart = partSplit + err = partSplit.FromGRPCMessage(pt.SplitInfo) + } + case *object.HeadResponse_Body_EcInfo: + if pt != nil { + partEC := new(ECInfo) + r.hdrPart = partEC + err = partEC.FromGRPCMessage(pt.EcInfo) + } + default: + err = fmt.Errorf("unknown head part %T", pt) + } + + return err +} + +func (r *HeadResponse) ToGRPCMessage() grpc.Message { + var m *object.HeadResponse + + if r != nil { + m = new(object.HeadResponse) + + m.SetBody(r.body.ToGRPCMessage().(*object.HeadResponse_Body)) + r.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (r *HeadResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.HeadResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(HeadResponseBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.ResponseHeaders.FromMessage(v) +} + +func (f *SearchFilter) ToGRPCMessage() grpc.Message { + var m *object.SearchRequest_Body_Filter + + if f != nil { + m = new(object.SearchRequest_Body_Filter) + + m.SetKey(f.key) + m.SetValue(f.val) + m.SetMatchType(MatchTypeToGRPCField(f.matchType)) + } + + return m +} + +func (f *SearchFilter) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.SearchRequest_Body_Filter) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + f.key = v.GetKey() + f.val = v.GetValue() + f.matchType = MatchTypeFromGRPCField(v.GetMatchType()) + + return nil +} + +func SearchFiltersToGRPC(fs []SearchFilter) (res []object.SearchRequest_Body_Filter) { + if fs != nil { + res = make([]object.SearchRequest_Body_Filter, 0, len(fs)) + + for i := range fs { + res = append(res, *fs[i].ToGRPCMessage().(*object.SearchRequest_Body_Filter)) + } + } + + return +} + +func SearchFiltersFromGRPC(fs []object.SearchRequest_Body_Filter) (res []SearchFilter, err error) { + if fs != nil { + res = make([]SearchFilter, len(fs)) + + for i := range fs { + err = res[i].FromGRPCMessage(&fs[i]) + if err != nil { + return + } + } + } + + return +} + +func (r *SearchRequestBody) ToGRPCMessage() grpc.Message { + var m *object.SearchRequest_Body + + if r != nil { + m = new(object.SearchRequest_Body) + + m.SetContainerId(r.cid.ToGRPCMessage().(*refsGRPC.ContainerID)) + m.SetFilters(SearchFiltersToGRPC(r.filters)) + m.SetVersion(r.version) + } + + return m +} + +func (r *SearchRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.SearchRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + cid := v.GetContainerId() + if cid == nil { + r.cid = nil + } else { + if r.cid == nil { + r.cid = new(refs.ContainerID) + } + + err = r.cid.FromGRPCMessage(cid) + if err != nil { + return err + } + } + + r.filters, err = SearchFiltersFromGRPC(v.GetFilters()) + if err != nil { + return err + } + + r.version = v.GetVersion() + + return nil +} + +func (r *SearchRequest) ToGRPCMessage() grpc.Message { + var m *object.SearchRequest + + if r != nil { + m = new(object.SearchRequest) + + m.SetBody(r.body.ToGRPCMessage().(*object.SearchRequest_Body)) + r.RequestHeaders.ToMessage(m) + } + + return m +} + +func (r *SearchRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.SearchRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(SearchRequestBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.RequestHeaders.FromMessage(v) +} + +func (r *SearchResponseBody) ToGRPCMessage() grpc.Message { + var m *object.SearchResponse_Body + + if r != nil { + m = new(object.SearchResponse_Body) + + m.SetIdList(refs.ObjectIDListToGRPCMessage(r.idList)) + } + + return m +} + +func (r *SearchResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.SearchResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + r.idList, err = refs.ObjectIDListFromGRPCMessage(v.GetIdList()) + + return err +} + +func (r *SearchResponse) ToGRPCMessage() grpc.Message { + var m *object.SearchResponse + + if r != nil { + m = new(object.SearchResponse) + + m.SetBody(r.body.ToGRPCMessage().(*object.SearchResponse_Body)) + r.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (r *SearchResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.SearchResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(SearchResponseBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.ResponseHeaders.FromMessage(v) +} + +func (r *Range) ToGRPCMessage() grpc.Message { + var m *object.Range + + if r != nil { + m = new(object.Range) + + m.SetLength(r.len) + m.SetOffset(r.off) + } + + return m +} + +func (r *Range) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.Range) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + r.len = v.GetLength() + r.off = v.GetOffset() + + return nil +} + +func RangesToGRPC(rs []Range) (res []object.Range) { + if rs != nil { + res = make([]object.Range, 0, len(rs)) + + for i := range rs { + res = append(res, *rs[i].ToGRPCMessage().(*object.Range)) + } + } + + return +} + +func RangesFromGRPC(rs []object.Range) (res []Range, err error) { + if rs != nil { + res = make([]Range, len(rs)) + + for i := range rs { + err = res[i].FromGRPCMessage(&rs[i]) + if err != nil { + return + } + } + } + + return +} + +func (r *GetRangeRequestBody) ToGRPCMessage() grpc.Message { + var m *object.GetRangeRequest_Body + + if r != nil { + m = new(object.GetRangeRequest_Body) + + m.SetAddress(r.addr.ToGRPCMessage().(*refsGRPC.Address)) + m.SetRange(r.rng.ToGRPCMessage().(*object.Range)) + m.SetRaw(r.raw) + } + + return m +} + +func (r *GetRangeRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetRangeRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + addr := v.GetAddress() + if addr == nil { + r.addr = nil + } else { + if r.addr == nil { + r.addr = new(refs.Address) + } + + err = r.addr.FromGRPCMessage(addr) + if err != nil { + return err + } + } + + rng := v.GetRange() + if rng == nil { + r.rng = nil + } else { + if r.rng == nil { + r.rng = new(Range) + } + + err = r.rng.FromGRPCMessage(rng) + if err != nil { + return err + } + } + + r.raw = v.GetRaw() + + return nil +} + +func (r *GetRangeRequest) ToGRPCMessage() grpc.Message { + var m *object.GetRangeRequest + + if r != nil { + m = new(object.GetRangeRequest) + + m.SetBody(r.body.ToGRPCMessage().(*object.GetRangeRequest_Body)) + r.RequestHeaders.ToMessage(m) + } + + return m +} + +func (r *GetRangeRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetRangeRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(GetRangeRequestBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.RequestHeaders.FromMessage(v) +} + +func (r *GetRangePartChunk) ToGRPCMessage() grpc.Message { + var m *object.GetRangeResponse_Body_Chunk + + if r != nil { + m = new(object.GetRangeResponse_Body_Chunk) + + m.SetChunk(r.chunk) + } + + return m +} + +func (r *GetRangePartChunk) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetRangeResponse_Body_Chunk) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + r.chunk = v.GetChunk() + + return nil +} + +func (r *GetRangeResponseBody) ToGRPCMessage() grpc.Message { + var m *object.GetRangeResponse_Body + + if r != nil { + m = new(object.GetRangeResponse_Body) + + switch v := r.rngPart.(type) { + case nil: + m.RangePart = nil + case *GetRangePartChunk: + m.SetChunk(v.ToGRPCMessage().(*object.GetRangeResponse_Body_Chunk)) + case *SplitInfo: + m.SetSplitInfo(v.ToGRPCMessage().(*object.SplitInfo)) + case *ECInfo: + m.SetEcInfo(v.ToGRPCMessage().(*object.ECInfo)) + default: + panic(fmt.Sprintf("unknown get range part %T", v)) + } + } + + return m +} + +func (r *GetRangeResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetRangeResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + r.rngPart = nil + + switch pt := v.GetRangePart().(type) { + case nil: + case *object.GetRangeResponse_Body_Chunk: + if pt != nil { + partChunk := new(GetRangePartChunk) + r.rngPart = partChunk + err = partChunk.FromGRPCMessage(pt) + } + case *object.GetRangeResponse_Body_SplitInfo: + if pt != nil { + partSplit := new(SplitInfo) + r.rngPart = partSplit + err = partSplit.FromGRPCMessage(pt.SplitInfo) + } + case *object.GetRangeResponse_Body_EcInfo: + if pt != nil { + partEC := new(ECInfo) + r.rngPart = partEC + err = partEC.FromGRPCMessage(pt.EcInfo) + } + default: + err = fmt.Errorf("unknown get range part %T", pt) + } + + return err +} + +func (r *GetRangeResponse) ToGRPCMessage() grpc.Message { + var m *object.GetRangeResponse + + if r != nil { + m = new(object.GetRangeResponse) + + m.SetBody(r.body.ToGRPCMessage().(*object.GetRangeResponse_Body)) + r.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (r *GetRangeResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetRangeResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(GetRangeResponseBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.ResponseHeaders.FromMessage(v) +} + +func (r *GetRangeHashRequestBody) ToGRPCMessage() grpc.Message { + var m *object.GetRangeHashRequest_Body + + if r != nil { + m = new(object.GetRangeHashRequest_Body) + + m.SetAddress(r.addr.ToGRPCMessage().(*refsGRPC.Address)) + m.SetRanges(RangesToGRPC(r.rngs)) + m.SetType(refs.ChecksumTypeToGRPC(r.typ)) + m.SetSalt(r.salt) + } + + return m +} + +func (r *GetRangeHashRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetRangeHashRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + addr := v.GetAddress() + if addr == nil { + r.addr = nil + } else { + if r.addr == nil { + r.addr = new(refs.Address) + } + + err = r.addr.FromGRPCMessage(addr) + if err != nil { + return err + } + } + + r.rngs, err = RangesFromGRPC(v.GetRanges()) + if err != nil { + return err + } + + r.typ = refs.ChecksumTypeFromGRPC(v.GetType()) + r.salt = v.GetSalt() + + return nil +} + +func (r *GetRangeHashRequest) ToGRPCMessage() grpc.Message { + var m *object.GetRangeHashRequest + + if r != nil { + m = new(object.GetRangeHashRequest) + + m.SetBody(r.body.ToGRPCMessage().(*object.GetRangeHashRequest_Body)) + r.RequestHeaders.ToMessage(m) + } + + return m +} + +func (r *GetRangeHashRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetRangeHashRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(GetRangeHashRequestBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.RequestHeaders.FromMessage(v) +} + +func (r *GetRangeHashResponseBody) ToGRPCMessage() grpc.Message { + var m *object.GetRangeHashResponse_Body + + if r != nil { + m = new(object.GetRangeHashResponse_Body) + + m.SetType(refs.ChecksumTypeToGRPC(r.typ)) + m.SetHashList(r.hashList) + } + + return m +} + +func (r *GetRangeHashResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetRangeHashResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + r.typ = refs.ChecksumTypeFromGRPC(v.GetType()) + r.hashList = v.GetHashList() + + return nil +} + +func (r *GetRangeHashResponse) ToGRPCMessage() grpc.Message { + var m *object.GetRangeHashResponse + + if r != nil { + m = new(object.GetRangeHashResponse) + + m.SetBody(r.body.ToGRPCMessage().(*object.GetRangeHashResponse_Body)) + r.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (r *GetRangeHashResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.GetRangeHashResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(GetRangeHashResponseBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.ResponseHeaders.FromMessage(v) +} + +func (r *PutSingleRequestBody) ToGRPCMessage() grpc.Message { + var m *object.PutSingleRequest_Body + + if r != nil { + m = new(object.PutSingleRequest_Body) + m.SetObject(r.GetObject().ToGRPCMessage().(*object.Object)) + m.SetCopiesNumber(r.GetCopiesNumber()) + } + + return m +} + +func (r *PutSingleRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PutSingleRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + if v.GetObject() == nil { + r.object = nil + } else { + if r.object == nil { + r.object = new(Object) + } + + err := r.object.FromGRPCMessage(v.GetObject()) + if err != nil { + return err + } + } + + r.copyNum = v.GetCopiesNumber() + + return nil +} + +func (r *PutSingleRequest) ToGRPCMessage() grpc.Message { + var m *object.PutSingleRequest + + if r != nil { + m = new(object.PutSingleRequest) + + m.SetBody(r.body.ToGRPCMessage().(*object.PutSingleRequest_Body)) + r.RequestHeaders.ToMessage(m) + } + + return m +} + +func (r *PutSingleRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PutSingleRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(PutSingleRequestBody) + } + + err := r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.RequestHeaders.FromMessage(v) +} + +func (r *PutSingleResponseBody) ToGRPCMessage() grpc.Message { + var b *object.PutSingleResponse_Body + if r != nil { + b = new(object.PutSingleResponse_Body) + } + return b +} + +func (r *PutSingleResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PutSingleResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + return nil +} + +func (r *PutSingleResponse) ToGRPCMessage() grpc.Message { + var m *object.PutSingleResponse + + if r != nil { + m = new(object.PutSingleResponse) + + m.SetBody(r.body.ToGRPCMessage().(*object.PutSingleResponse_Body)) + r.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (r *PutSingleResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PutSingleResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(PutSingleResponseBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.ResponseHeaders.FromMessage(v) +} + +func (r *PatchRequestBodyPatch) ToGRPCMessage() grpc.Message { + var m *object.PatchRequest_Body_Patch + + if r != nil { + m = new(object.PatchRequest_Body_Patch) + + m.SetSourceRange(r.GetRange().ToGRPCMessage().(*object.Range)) + m.SetChunk(r.GetChunk()) + } + + return m +} + +func (r *PatchRequestBodyPatch) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PatchRequest_Body_Patch) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + srcRange := v.GetSourceRange() + if srcRange == nil { + r.Range = nil + } else { + if r.Range == nil { + r.Range = new(Range) + } + + err = r.Range.FromGRPCMessage(srcRange) + if err != nil { + return err + } + } + + r.Chunk = v.GetChunk() + + return nil +} + +func (r *PatchRequestBody) ToGRPCMessage() grpc.Message { + var m *object.PatchRequest_Body + + if r != nil { + m = new(object.PatchRequest_Body) + + m.SetAddress(r.address.ToGRPCMessage().(*refsGRPC.Address)) + m.SetNewAttributes(AttributesToGRPC(r.newAttributes)) + m.SetReplaceAttributes(r.replaceAttributes) + m.SetPatch(r.patch.ToGRPCMessage().(*object.PatchRequest_Body_Patch)) + } + + return m +} + +func (r *PatchRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PatchRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + addr := v.GetAddress() + if addr == nil { + r.address = nil + } else { + if r.address == nil { + r.address = new(refs.Address) + } + + err = r.address.FromGRPCMessage(addr) + if err != nil { + return err + } + } + + r.newAttributes, err = AttributesFromGRPC(v.GetNewAttributes()) + if err != nil { + return err + } + + r.replaceAttributes = v.GetReplaceAttributes() + + patch := v.GetPatch() + if patch == nil { + r.patch = nil + } else { + if r.patch == nil { + r.patch = new(PatchRequestBodyPatch) + } + + err = r.patch.FromGRPCMessage(patch) + if err != nil { + return err + } + } + + return nil +} + +func (r *PatchRequest) ToGRPCMessage() grpc.Message { + var m *object.PatchRequest + + if r != nil { + m = new(object.PatchRequest) + + m.SetBody(r.body.ToGRPCMessage().(*object.PatchRequest_Body)) + r.RequestHeaders.ToMessage(m) + } + + return m +} + +func (r *PatchRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PatchRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.body = nil + } else { + if r.body == nil { + r.body = new(PatchRequestBody) + } + + err = r.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.RequestHeaders.FromMessage(v) +} + +func (r *PatchResponseBody) ToGRPCMessage() grpc.Message { + var m *object.PatchResponse_Body + + if r != nil { + m = new(object.PatchResponse_Body) + + m.SetObjectId(r.ObjectID.ToGRPCMessage().(*refsGRPC.ObjectID)) + } + + return m +} + +func (r *PatchResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PatchResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + objID := v.GetObjectId() + if objID == nil { + r.ObjectID = nil + } else { + if r.ObjectID == nil { + r.ObjectID = new(refs.ObjectID) + } + + err = r.ObjectID.FromGRPCMessage(objID) + if err != nil { + return err + } + } + + return nil +} + +func (r *PatchResponse) ToGRPCMessage() grpc.Message { + var m *object.PatchResponse + + if r != nil { + m = new(object.PatchResponse) + + m.SetBody(r.Body.ToGRPCMessage().(*object.PatchResponse_Body)) + r.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (r *PatchResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*object.PatchResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + r.Body = nil + } else { + if r.Body == nil { + r.Body = new(PatchResponseBody) + } + + err = r.Body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return r.ResponseHeaders.FromMessage(v) +} diff --git a/apiv2/object/filters.go b/apiv2/object/filters.go new file mode 100644 index 0000000..9fe024d --- /dev/null +++ b/apiv2/object/filters.go @@ -0,0 +1,58 @@ +package object + +// ReservedFilterPrefix is a prefix of key to object header value or property. +const ReservedFilterPrefix = "$Object:" + +const ( + // FilterHeaderVersion is a filter key to "version" field of the object header. + FilterHeaderVersion = ReservedFilterPrefix + "version" + + // FilterHeaderObjectID is a filter key to "object_id" field of the object. + FilterHeaderObjectID = ReservedFilterPrefix + "objectID" + + // FilterHeaderContainerID is a filter key to "container_id" field of the object header. + FilterHeaderContainerID = ReservedFilterPrefix + "containerID" + + // FilterHeaderOwnerID is a filter key to "owner_id" field of the object header. + FilterHeaderOwnerID = ReservedFilterPrefix + "ownerID" + + // FilterHeaderCreationEpoch is a filter key to "creation_epoch" field of the object header. + FilterHeaderCreationEpoch = ReservedFilterPrefix + "creationEpoch" + + // FilterHeaderPayloadLength is a filter key to "payload_length" field of the object header. + FilterHeaderPayloadLength = ReservedFilterPrefix + "payloadLength" + + // FilterHeaderPayloadHash is a filter key to "payload_hash" field of the object header. + FilterHeaderPayloadHash = ReservedFilterPrefix + "payloadHash" + + // FilterHeaderObjectType is a filter key to "object_type" field of the object header. + FilterHeaderObjectType = ReservedFilterPrefix + "objectType" + + // FilterHeaderHomomorphicHash is a filter key to "homomorphic_hash" field of the object header. + FilterHeaderHomomorphicHash = ReservedFilterPrefix + "homomorphicHash" + + // FilterHeaderParent is a filter key to "split.parent" field of the object header. + FilterHeaderParent = ReservedFilterPrefix + "split.parent" + + // FilterHeaderSplitID is a filter key to "split.splitID" field of the object header. + FilterHeaderSplitID = ReservedFilterPrefix + "split.splitID" + + // FilterHeaderECParent is a filter key to "ec.parent" field of the object header. + FilterHeaderECParent = ReservedFilterPrefix + "ec.parent" +) + +const ( + // FilterPropertyRoot is a filter key to check if regular object is on top of split hierarchy. + FilterPropertyRoot = ReservedFilterPrefix + "ROOT" + + // FilterPropertyPhy is a filter key to check if an object physically stored on a node. + FilterPropertyPhy = ReservedFilterPrefix + "PHY" +) + +const ( + // BooleanPropertyValueTrue is a true value for boolean property filters. + BooleanPropertyValueTrue = "true" + + // BooleanPropertyValueFalse is a false value for boolean property filters. + BooleanPropertyValueFalse = "" +) diff --git a/apiv2/object/grpc/client.go b/apiv2/object/grpc/client.go new file mode 100644 index 0000000..35028cf --- /dev/null +++ b/apiv2/object/grpc/client.go @@ -0,0 +1,86 @@ +package object + +import ( + "context" + "errors" + + "google.golang.org/grpc" +) + +// Client wraps ObjectServiceClient +// with pre-defined configurations. +type Client struct { + *cfg + + client ObjectServiceClient +} + +// Option represents Client option. +type Option func(*cfg) + +type cfg struct { + callOpts []grpc.CallOption +} + +// ErrNilObjectServiceClient is returned by functions that expect +// a non-nil ObjectServiceClient, but received nil. +var ErrNilObjectServiceClient = errors.New("object gRPC client is nil") + +func defaultCfg() *cfg { + return new(cfg) +} + +// NewClient creates, initializes and returns a new Client instance. +// +// Options are applied one by one in order. +func NewClient(c ObjectServiceClient, opts ...Option) (*Client, error) { + if c == nil { + return nil, ErrNilObjectServiceClient + } + + cfg := defaultCfg() + for i := range opts { + opts[i](cfg) + } + + return &Client{ + cfg: cfg, + client: c, + }, nil +} + +func (c *Client) Get(ctx context.Context, req *GetRequest) (ObjectService_GetClient, error) { + return c.client.Get(ctx, req, c.callOpts...) +} + +func (c *Client) Put(ctx context.Context) (ObjectService_PutClient, error) { + return c.client.Put(ctx, c.callOpts...) +} + +func (c *Client) Head(ctx context.Context, req *HeadRequest) (*HeadResponse, error) { + return c.client.Head(ctx, req, c.callOpts...) +} + +func (c *Client) Search(ctx context.Context, req *SearchRequest) (ObjectService_SearchClient, error) { + return c.client.Search(ctx, req, c.callOpts...) +} + +func (c *Client) Delete(ctx context.Context, req *DeleteRequest) (*DeleteResponse, error) { + return c.client.Delete(ctx, req, c.callOpts...) +} + +func (c *Client) GetRange(ctx context.Context, req *GetRangeRequest) (ObjectService_GetRangeClient, error) { + return c.client.GetRange(ctx, req, c.callOpts...) +} + +func (c *Client) GetRangeHash(ctx context.Context, req *GetRangeHashRequest) (*GetRangeHashResponse, error) { + return c.client.GetRangeHash(ctx, req, c.callOpts...) +} + +// WithCallOptions returns Option that configures +// Client to attach call options to each rpc call. +func WithCallOptions(opts []grpc.CallOption) Option { + return func(c *cfg) { + c.callOpts = opts + } +} diff --git a/apiv2/object/grpc/service_frostfs.pb.go b/apiv2/object/grpc/service_frostfs.pb.go new file mode 100644 index 0000000..82ad76f Binary files /dev/null and b/apiv2/object/grpc/service_frostfs.pb.go differ diff --git a/apiv2/object/grpc/service_frostfs_fuzz.go b/apiv2/object/grpc/service_frostfs_fuzz.go new file mode 100644 index 0000000..f58ee01 --- /dev/null +++ b/apiv2/object/grpc/service_frostfs_fuzz.go @@ -0,0 +1,387 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package object + +func DoFuzzProtoGetRequest(data []byte) int { + msg := new(GetRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONGetRequest(data []byte) int { + msg := new(GetRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoGetResponse(data []byte) int { + msg := new(GetResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONGetResponse(data []byte) int { + msg := new(GetResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoPutRequest(data []byte) int { + msg := new(PutRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONPutRequest(data []byte) int { + msg := new(PutRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoPutResponse(data []byte) int { + msg := new(PutResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONPutResponse(data []byte) int { + msg := new(PutResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoDeleteRequest(data []byte) int { + msg := new(DeleteRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONDeleteRequest(data []byte) int { + msg := new(DeleteRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoDeleteResponse(data []byte) int { + msg := new(DeleteResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONDeleteResponse(data []byte) int { + msg := new(DeleteResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoHeadRequest(data []byte) int { + msg := new(HeadRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONHeadRequest(data []byte) int { + msg := new(HeadRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoHeaderWithSignature(data []byte) int { + msg := new(HeaderWithSignature) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONHeaderWithSignature(data []byte) int { + msg := new(HeaderWithSignature) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoHeadResponse(data []byte) int { + msg := new(HeadResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONHeadResponse(data []byte) int { + msg := new(HeadResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoSearchRequest(data []byte) int { + msg := new(SearchRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONSearchRequest(data []byte) int { + msg := new(SearchRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoSearchResponse(data []byte) int { + msg := new(SearchResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONSearchResponse(data []byte) int { + msg := new(SearchResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoRange(data []byte) int { + msg := new(Range) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONRange(data []byte) int { + msg := new(Range) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoGetRangeRequest(data []byte) int { + msg := new(GetRangeRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONGetRangeRequest(data []byte) int { + msg := new(GetRangeRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoGetRangeResponse(data []byte) int { + msg := new(GetRangeResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONGetRangeResponse(data []byte) int { + msg := new(GetRangeResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoGetRangeHashRequest(data []byte) int { + msg := new(GetRangeHashRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONGetRangeHashRequest(data []byte) int { + msg := new(GetRangeHashRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoGetRangeHashResponse(data []byte) int { + msg := new(GetRangeHashResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONGetRangeHashResponse(data []byte) int { + msg := new(GetRangeHashResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoPutSingleRequest(data []byte) int { + msg := new(PutSingleRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONPutSingleRequest(data []byte) int { + msg := new(PutSingleRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoPutSingleResponse(data []byte) int { + msg := new(PutSingleResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONPutSingleResponse(data []byte) int { + msg := new(PutSingleResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoPatchRequest(data []byte) int { + msg := new(PatchRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONPatchRequest(data []byte) int { + msg := new(PatchRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoPatchResponse(data []byte) int { + msg := new(PatchResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONPatchResponse(data []byte) int { + msg := new(PatchResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/object/grpc/service_frostfs_test.go b/apiv2/object/grpc/service_frostfs_test.go new file mode 100644 index 0000000..cb4baeb --- /dev/null +++ b/apiv2/object/grpc/service_frostfs_test.go @@ -0,0 +1,211 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package object + +import ( + testing "testing" +) + +func FuzzProtoGetRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoGetRequest(data) + }) +} +func FuzzJSONGetRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONGetRequest(data) + }) +} +func FuzzProtoGetResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoGetResponse(data) + }) +} +func FuzzJSONGetResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONGetResponse(data) + }) +} +func FuzzProtoPutRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoPutRequest(data) + }) +} +func FuzzJSONPutRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONPutRequest(data) + }) +} +func FuzzProtoPutResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoPutResponse(data) + }) +} +func FuzzJSONPutResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONPutResponse(data) + }) +} +func FuzzProtoDeleteRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoDeleteRequest(data) + }) +} +func FuzzJSONDeleteRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONDeleteRequest(data) + }) +} +func FuzzProtoDeleteResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoDeleteResponse(data) + }) +} +func FuzzJSONDeleteResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONDeleteResponse(data) + }) +} +func FuzzProtoHeadRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoHeadRequest(data) + }) +} +func FuzzJSONHeadRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONHeadRequest(data) + }) +} +func FuzzProtoHeaderWithSignature(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoHeaderWithSignature(data) + }) +} +func FuzzJSONHeaderWithSignature(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONHeaderWithSignature(data) + }) +} +func FuzzProtoHeadResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoHeadResponse(data) + }) +} +func FuzzJSONHeadResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONHeadResponse(data) + }) +} +func FuzzProtoSearchRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoSearchRequest(data) + }) +} +func FuzzJSONSearchRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONSearchRequest(data) + }) +} +func FuzzProtoSearchResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoSearchResponse(data) + }) +} +func FuzzJSONSearchResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONSearchResponse(data) + }) +} +func FuzzProtoRange(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoRange(data) + }) +} +func FuzzJSONRange(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONRange(data) + }) +} +func FuzzProtoGetRangeRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoGetRangeRequest(data) + }) +} +func FuzzJSONGetRangeRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONGetRangeRequest(data) + }) +} +func FuzzProtoGetRangeResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoGetRangeResponse(data) + }) +} +func FuzzJSONGetRangeResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONGetRangeResponse(data) + }) +} +func FuzzProtoGetRangeHashRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoGetRangeHashRequest(data) + }) +} +func FuzzJSONGetRangeHashRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONGetRangeHashRequest(data) + }) +} +func FuzzProtoGetRangeHashResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoGetRangeHashResponse(data) + }) +} +func FuzzJSONGetRangeHashResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONGetRangeHashResponse(data) + }) +} +func FuzzProtoPutSingleRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoPutSingleRequest(data) + }) +} +func FuzzJSONPutSingleRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONPutSingleRequest(data) + }) +} +func FuzzProtoPutSingleResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoPutSingleResponse(data) + }) +} +func FuzzJSONPutSingleResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONPutSingleResponse(data) + }) +} +func FuzzProtoPatchRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoPatchRequest(data) + }) +} +func FuzzJSONPatchRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONPatchRequest(data) + }) +} +func FuzzProtoPatchResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoPatchResponse(data) + }) +} +func FuzzJSONPatchResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONPatchResponse(data) + }) +} diff --git a/apiv2/object/grpc/service_grpc.pb.go b/apiv2/object/grpc/service_grpc.pb.go new file mode 100644 index 0000000..687df99 Binary files /dev/null and b/apiv2/object/grpc/service_grpc.pb.go differ diff --git a/apiv2/object/grpc/types_frostfs.pb.go b/apiv2/object/grpc/types_frostfs.pb.go new file mode 100644 index 0000000..5d60ff7 Binary files /dev/null and b/apiv2/object/grpc/types_frostfs.pb.go differ diff --git a/apiv2/object/grpc/types_frostfs_fuzz.go b/apiv2/object/grpc/types_frostfs_fuzz.go new file mode 100644 index 0000000..8491638 --- /dev/null +++ b/apiv2/object/grpc/types_frostfs_fuzz.go @@ -0,0 +1,102 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package object + +func DoFuzzProtoShortHeader(data []byte) int { + msg := new(ShortHeader) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONShortHeader(data []byte) int { + msg := new(ShortHeader) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoHeader(data []byte) int { + msg := new(Header) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONHeader(data []byte) int { + msg := new(Header) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoObject(data []byte) int { + msg := new(Object) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONObject(data []byte) int { + msg := new(Object) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoSplitInfo(data []byte) int { + msg := new(SplitInfo) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONSplitInfo(data []byte) int { + msg := new(SplitInfo) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoECInfo(data []byte) int { + msg := new(ECInfo) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONECInfo(data []byte) int { + msg := new(ECInfo) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/object/grpc/types_frostfs_test.go b/apiv2/object/grpc/types_frostfs_test.go new file mode 100644 index 0000000..11825be --- /dev/null +++ b/apiv2/object/grpc/types_frostfs_test.go @@ -0,0 +1,61 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package object + +import ( + testing "testing" +) + +func FuzzProtoShortHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoShortHeader(data) + }) +} +func FuzzJSONShortHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONShortHeader(data) + }) +} +func FuzzProtoHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoHeader(data) + }) +} +func FuzzJSONHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONHeader(data) + }) +} +func FuzzProtoObject(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoObject(data) + }) +} +func FuzzJSONObject(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONObject(data) + }) +} +func FuzzProtoSplitInfo(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoSplitInfo(data) + }) +} +func FuzzJSONSplitInfo(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONSplitInfo(data) + }) +} +func FuzzProtoECInfo(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoECInfo(data) + }) +} +func FuzzJSONECInfo(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONECInfo(data) + }) +} diff --git a/apiv2/object/json.go b/apiv2/object/json.go new file mode 100644 index 0000000..dbd3af0 --- /dev/null +++ b/apiv2/object/json.go @@ -0,0 +1,94 @@ +package object + +import ( + object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +func (h *ShortHeader) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(h) +} + +func (h *ShortHeader) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(h, data, new(object.ShortHeader)) +} + +func (a *Attribute) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(a) +} + +func (a *Attribute) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(a, data, new(object.Header_Attribute)) +} + +func (h *SplitHeader) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(h) +} + +func (h *SplitHeader) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(h, data, new(object.Header_Split)) +} + +func (h *Header) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(h) +} + +func (h *Header) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(h, data, new(object.Header)) +} + +func (h *HeaderWithSignature) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(h) +} + +func (h *HeaderWithSignature) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(h, data, new(object.HeaderWithSignature)) +} + +func (o *Object) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(o) +} + +func (o *Object) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(o, data, new(object.Object)) +} + +func (s *SplitInfo) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(s) +} + +func (s *SplitInfo) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(s, data, new(object.SplitInfo)) +} + +func (e *ECInfo) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(e) +} + +func (e *ECInfo) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(e, data, new(object.ECInfo)) +} + +func (e *ECChunk) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(e) +} + +func (e *ECChunk) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(e, data, new(object.ECInfo_Chunk)) +} + +func (f *SearchFilter) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(f) +} + +func (f *SearchFilter) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(f, data, new(object.SearchRequest_Body_Filter)) +} + +func (r *Range) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(r) +} + +func (r *Range) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(r, data, new(object.Range)) +} diff --git a/apiv2/object/lock.go b/apiv2/object/lock.go new file mode 100644 index 0000000..7e1667a --- /dev/null +++ b/apiv2/object/lock.go @@ -0,0 +1,160 @@ +package object + +import ( + "errors" + "fmt" + + lock "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/lock/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + refsGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto" +) + +// Lock represents object Lock message from NeoFS API V2 protocol. +type Lock struct { + members []refs.ObjectID +} + +// NumberOfMembers returns length of lock list. +func (x *Lock) NumberOfMembers() int { + if x != nil { + return len(x.members) + } + + return 0 +} + +// IterateMembers passes members of the lock list to f. +func (x *Lock) IterateMembers(f func(refs.ObjectID)) { + if x != nil { + for i := range x.members { + f(x.members[i]) + } + } +} + +// SetMembers sets list of locked members. +// Arg must not be mutated for the duration of the Lock. +func (x *Lock) SetMembers(ids []refs.ObjectID) { + x.members = ids +} + +const ( + _ = iota + fNumLockMembers +) + +// StableMarshal encodes the Lock into Protocol Buffers binary format +// with direct field order. +func (x *Lock) StableMarshal(buf []byte) []byte { + if x == nil || len(x.members) == 0 { + return []byte{} + } + + if buf == nil { + buf = make([]byte, x.StableSize()) + } + + var offset int + + for i := range x.members { + offset += proto.NestedStructureMarshal(fNumLockMembers, buf[offset:], &x.members[i]) + } + + return buf +} + +// StableSize size of the buffer required to write the Lock in Protocol Buffers +// binary format. +func (x *Lock) StableSize() (sz int) { + if x != nil { + for i := range x.members { + sz += proto.NestedStructureSize(fNumLockMembers, &x.members[i]) + } + } + + return +} + +// Unmarshal decodes the Lock from its Protocol Buffers binary format. +func (x *Lock) Unmarshal(data []byte) error { + return message.Unmarshal(x, data, new(lock.Lock)) +} + +func (x *Lock) ToGRPCMessage() grpc.Message { + var m *lock.Lock + + if x != nil { + m = new(lock.Lock) + + var members []refsGRPC.ObjectID + + if x.members != nil { + members = make([]refsGRPC.ObjectID, len(x.members)) + + for i := range x.members { + members[i] = *x.members[i].ToGRPCMessage().(*refsGRPC.ObjectID) + } + } + + m.SetMembers(members) + } + + return m +} + +func (x *Lock) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*lock.Lock) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + members := v.GetMembers() + if members == nil { + x.members = nil + } else { + x.members = make([]refs.ObjectID, len(members)) + var err error + + for i := range x.members { + err = x.members[i].FromGRPCMessage(&members[i]) + if err != nil { + return err + } + } + } + + return nil +} + +// WriteLock writes Lock to the Object as a payload content. +// The object must not be nil. +func WriteLock(obj *Object, lock Lock) { + hdr := obj.GetHeader() + if hdr == nil { + hdr = new(Header) + obj.SetHeader(hdr) + } + + hdr.SetObjectType(TypeLock) + + payload := lock.StableMarshal(nil) + obj.SetPayload(payload) +} + +// ReadLock reads Lock from the Object payload content. +func ReadLock(lock *Lock, obj Object) error { + payload := obj.GetPayload() + if len(payload) == 0 { + return errors.New("empty payload") + } + + err := lock.Unmarshal(payload) + if err != nil { + return fmt.Errorf("decode lock content from payload: %w", err) + } + + return nil +} diff --git a/apiv2/object/lock_test.go b/apiv2/object/lock_test.go new file mode 100644 index 0000000..30a9c72 --- /dev/null +++ b/apiv2/object/lock_test.go @@ -0,0 +1,26 @@ +package object_test + +import ( + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" + objecttest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object/test" + "github.com/stretchr/testify/require" +) + +func TestLockRW(t *testing.T) { + var l object.Lock + var obj object.Object + + require.Error(t, object.ReadLock(&l, obj)) + + l = *objecttest.GenerateLock(false) + + object.WriteLock(&obj, l) + + var l2 object.Lock + + require.NoError(t, object.ReadLock(&l2, obj)) + + require.Equal(t, l, l2) +} diff --git a/apiv2/object/marshal.go b/apiv2/object/marshal.go new file mode 100644 index 0000000..89ac640 --- /dev/null +++ b/apiv2/object/marshal.go @@ -0,0 +1,1428 @@ +package object + +import ( + object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto" +) + +const ( + shortHdrVersionField = 1 + shortHdrEpochField = 2 + shortHdrOwnerField = 3 + shortHdrObjectTypeField = 4 + shortHdrPayloadLength = 5 + shortHdrHashField = 6 + shortHdrHomoHashField = 7 + + attributeKeyField = 1 + attributeValueField = 2 + + splitHdrParentField = 1 + splitHdrPreviousField = 2 + splitHdrParentSignatureField = 3 + splitHdrParentHeaderField = 4 + splitHdrChildrenField = 5 + splitHdrSplitIDField = 6 + + ecHdrParentField = 1 + ecHdrIndexField = 2 + ecHdrTotalField = 3 + ecHdrHeaderLengthField = 4 + ecHdrHeaderField = 5 + ecHdrParentSplitID = 6 + ecHdrParentSplitParentID = 7 + ecHdrParentAttributes = 8 + + hdrVersionField = 1 + hdrContainerIDField = 2 + hdrOwnerIDField = 3 + hdrEpochField = 4 + hdrPayloadLengthField = 5 + hdrPayloadHashField = 6 + hdrObjectTypeField = 7 + hdrHomomorphicHashField = 8 + hdrSessionTokenField = 9 + hdrAttributesField = 10 + hdrSplitField = 11 + hdrECField = 12 + + hdrWithSigHeaderField = 1 + hdrWithSigSignatureField = 2 + + objIDField = 1 + objSignatureField = 2 + objHeaderField = 3 + objPayloadField = 4 + + splitInfoSplitIDField = 1 + splitInfoLastPartField = 2 + splitInfoLinkField = 3 + + ecInfoChunksField = 1 + + ecChunkIDField = 1 + ecChunkIndexField = 2 + ecChunkTotalField = 3 + + getReqBodyAddressField = 1 + getReqBodyRawFlagField = 2 + + getRespInitObjectIDField = 1 + getRespInitSignatureField = 2 + getRespInitHeaderField = 3 + + getRespBodyInitField = 1 + getRespBodyChunkField = 2 + getRespBodySplitInfoField = 3 + getRespBodyECInfoField = 4 + + putReqInitObjectIDField = 1 + putReqInitSignatureField = 2 + putReqInitHeaderField = 3 + putReqInitCopiesNumField = 4 + + putReqBodyInitField = 1 + putReqBodyChunkField = 2 + + putRespBodyObjectIDField = 1 + + deleteReqBodyAddressField = 1 + + deleteRespBodyTombstoneFNum = 1 + + headReqBodyAddressField = 1 + headReqBodyMainFlagField = 2 + headReqBodyRawFlagField = 3 + + headRespBodyHeaderField = 1 + headRespBodyShortHeaderField = 2 + headRespBodySplitInfoField = 3 + headRespBodyECInfoField = 4 + + searchFilterMatchField = 1 + searchFilterNameField = 2 + searchFilterValueField = 3 + + searchReqBodyContainerIDField = 1 + searchReqBodyVersionField = 2 + searchReqBodyFiltersField = 3 + + searchRespBodyObjectIDsField = 1 + + rangeOffsetField = 1 + rangeLengthField = 2 + + getRangeReqBodyAddressField = 1 + getRangeReqBodyRangeField = 2 + getRangeReqBodyRawField = 3 + + getRangeRespChunkField = 1 + getRangeRespSplitInfoField = 2 + getRangeRespECInfoField = 3 + + getRangeHashReqBodyAddressField = 1 + getRangeHashReqBodyRangesField = 2 + getRangeHashReqBodySaltField = 3 + getRangeHashReqBodyTypeField = 4 + + getRangeHashRespBodyTypeField = 1 + getRangeHashRespBodyHashListField = 2 + + putSingleReqObjectField = 1 + putSingleReqCopiesNumberField = 2 + + patchRequestBodyPatchRangeField = 1 + patchRequestBodyPatchChunkField = 2 + + patchRequestBodyAddrField = 1 + patchRequestBodyNewAttrsField = 2 + patchRequestBodyReplaceAttrField = 3 + patchRequestBodyPatchField = 4 + + patchResponseBodyObjectIDField = 1 +) + +func (h *ShortHeader) StableMarshal(buf []byte) []byte { + if h == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, h.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(shortHdrVersionField, buf[offset:], h.version) + offset += proto.UInt64Marshal(shortHdrEpochField, buf[offset:], h.creatEpoch) + offset += proto.NestedStructureMarshal(shortHdrOwnerField, buf[offset:], h.ownerID) + offset += proto.EnumMarshal(shortHdrObjectTypeField, buf[offset:], int32(h.typ)) + offset += proto.UInt64Marshal(shortHdrPayloadLength, buf[offset:], h.payloadLen) + offset += proto.NestedStructureMarshal(shortHdrHashField, buf[offset:], h.payloadHash) + proto.NestedStructureMarshal(shortHdrHomoHashField, buf[offset:], h.homoHash) + + return buf +} + +func (h *ShortHeader) StableSize() (size int) { + if h == nil { + return 0 + } + + size += proto.NestedStructureSize(shortHdrVersionField, h.version) + size += proto.UInt64Size(shortHdrEpochField, h.creatEpoch) + size += proto.NestedStructureSize(shortHdrOwnerField, h.ownerID) + size += proto.EnumSize(shortHdrObjectTypeField, int32(h.typ)) + size += proto.UInt64Size(shortHdrPayloadLength, h.payloadLen) + size += proto.NestedStructureSize(shortHdrHashField, h.payloadHash) + size += proto.NestedStructureSize(shortHdrHomoHashField, h.homoHash) + + return size +} + +func (h *ShortHeader) Unmarshal(data []byte) error { + return message.Unmarshal(h, data, new(object.ShortHeader)) +} + +func (a *Attribute) StableMarshal(buf []byte) []byte { + if a == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, a.StableSize()) + } + + var offset int + + offset += proto.StringMarshal(attributeKeyField, buf[offset:], a.key) + proto.StringMarshal(attributeValueField, buf[offset:], a.val) + + return buf +} + +func (a *Attribute) StableSize() (size int) { + if a == nil { + return 0 + } + + size += proto.StringSize(shortHdrVersionField, a.key) + size += proto.StringSize(shortHdrEpochField, a.val) + + return size +} + +func (a *Attribute) Unmarshal(data []byte) error { + return message.Unmarshal(a, data, new(object.Header_Attribute)) +} + +func (h *SplitHeader) StableMarshal(buf []byte) []byte { + if h == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, h.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(splitHdrParentField, buf[offset:], h.par) + offset += proto.NestedStructureMarshal(splitHdrPreviousField, buf[offset:], h.prev) + offset += proto.NestedStructureMarshal(splitHdrParentSignatureField, buf[offset:], h.parSig) + offset += proto.NestedStructureMarshal(splitHdrParentHeaderField, buf[offset:], h.parHdr) + offset += refs.ObjectIDNestedListMarshal(splitHdrChildrenField, buf[offset:], h.children) + proto.BytesMarshal(splitHdrSplitIDField, buf[offset:], h.splitID) + + return buf +} + +func (h *SplitHeader) StableSize() (size int) { + if h == nil { + return 0 + } + + size += proto.NestedStructureSize(splitHdrParentField, h.par) + size += proto.NestedStructureSize(splitHdrPreviousField, h.prev) + size += proto.NestedStructureSize(splitHdrParentSignatureField, h.parSig) + size += proto.NestedStructureSize(splitHdrParentHeaderField, h.parHdr) + size += refs.ObjectIDNestedListSize(splitHdrChildrenField, h.children) + size += proto.BytesSize(splitHdrSplitIDField, h.splitID) + + return size +} + +func (h *SplitHeader) Unmarshal(data []byte) error { + return message.Unmarshal(h, data, new(object.Header_Split)) +} + +func (h *ECHeader) StableMarshal(buf []byte) []byte { + if h == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, h.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(ecHdrParentField, buf[offset:], h.Parent) + offset += proto.UInt32Marshal(ecHdrIndexField, buf[offset:], h.Index) + offset += proto.UInt32Marshal(ecHdrTotalField, buf[offset:], h.Total) + offset += proto.UInt32Marshal(ecHdrHeaderLengthField, buf[offset:], h.HeaderLength) + offset += proto.BytesMarshal(ecHdrHeaderField, buf[offset:], h.Header) + offset += proto.BytesMarshal(ecHdrParentSplitID, buf[offset:], h.ParentSplitID) + offset += proto.NestedStructureMarshal(ecHdrParentSplitParentID, buf[offset:], h.ParentSplitParentID) + for i := range h.ParentAttributes { + offset += proto.NestedStructureMarshal(ecHdrParentAttributes, buf[offset:], &h.ParentAttributes[i]) + } + return buf +} + +func (h *ECHeader) StableSize() (size int) { + if h == nil { + return 0 + } + + size += proto.NestedStructureSize(ecHdrParentField, h.Parent) + size += proto.UInt32Size(ecHdrIndexField, h.Index) + size += proto.UInt32Size(ecHdrTotalField, h.Total) + size += proto.UInt32Size(ecHdrHeaderLengthField, h.HeaderLength) + size += proto.BytesSize(ecHdrHeaderField, h.Header) + size += proto.BytesSize(ecHdrParentSplitID, h.ParentSplitID) + size += proto.NestedStructureSize(ecHdrParentSplitParentID, h.ParentSplitParentID) + for i := range h.ParentAttributes { + size += proto.NestedStructureSize(ecHdrParentAttributes, &h.ParentAttributes[i]) + } + + return size +} + +func (h *ECHeader) Unmarshal(data []byte) error { + return message.Unmarshal(h, data, new(object.Header_EC)) +} + +func (h *Header) StableMarshal(buf []byte) []byte { + if h == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, h.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(hdrVersionField, buf[offset:], h.version) + offset += proto.NestedStructureMarshal(hdrContainerIDField, buf[offset:], h.cid) + offset += proto.NestedStructureMarshal(hdrOwnerIDField, buf[offset:], h.ownerID) + offset += proto.UInt64Marshal(hdrEpochField, buf[offset:], h.creatEpoch) + offset += proto.UInt64Marshal(hdrPayloadLengthField, buf[offset:], h.payloadLen) + offset += proto.NestedStructureMarshal(hdrPayloadHashField, buf[offset:], h.payloadHash) + offset += proto.EnumMarshal(hdrObjectTypeField, buf[offset:], int32(h.typ)) + offset += proto.NestedStructureMarshal(hdrHomomorphicHashField, buf[offset:], h.homoHash) + offset += proto.NestedStructureMarshal(hdrSessionTokenField, buf[offset:], h.sessionToken) + + for i := range h.attr { + offset += proto.NestedStructureMarshal(hdrAttributesField, buf[offset:], &h.attr[i]) + } + + proto.NestedStructureMarshal(hdrSplitField, buf[offset:], h.split) + proto.NestedStructureMarshal(hdrECField, buf[offset:], h.ec) + + return buf +} + +func (h *Header) StableSize() (size int) { + if h == nil { + return 0 + } + + size += proto.NestedStructureSize(hdrVersionField, h.version) + size += proto.NestedStructureSize(hdrContainerIDField, h.cid) + size += proto.NestedStructureSize(hdrOwnerIDField, h.ownerID) + size += proto.UInt64Size(hdrEpochField, h.creatEpoch) + size += proto.UInt64Size(hdrPayloadLengthField, h.payloadLen) + size += proto.NestedStructureSize(hdrPayloadHashField, h.payloadHash) + size += proto.EnumSize(hdrObjectTypeField, int32(h.typ)) + size += proto.NestedStructureSize(hdrHomomorphicHashField, h.homoHash) + size += proto.NestedStructureSize(hdrSessionTokenField, h.sessionToken) + for i := range h.attr { + size += proto.NestedStructureSize(hdrAttributesField, &h.attr[i]) + } + size += proto.NestedStructureSize(hdrSplitField, h.split) + size += proto.NestedStructureSize(hdrECField, h.ec) + + return size +} + +func (h *Header) Unmarshal(data []byte) error { + return message.Unmarshal(h, data, new(object.Header)) +} + +func (h *HeaderWithSignature) StableMarshal(buf []byte) []byte { + if h == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, h.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(hdrWithSigHeaderField, buf[offset:], h.header) + proto.NestedStructureMarshal(hdrWithSigSignatureField, buf[offset:], h.signature) + + return buf +} + +func (h *HeaderWithSignature) StableSize() (size int) { + if h == nil { + return 0 + } + + size += proto.NestedStructureSize(hdrVersionField, h.header) + size += proto.NestedStructureSize(hdrContainerIDField, h.signature) + + return size +} + +func (h *HeaderWithSignature) Unmarshal(data []byte) error { + return message.Unmarshal(h, data, new(object.HeaderWithSignature)) +} + +func (o *Object) StableMarshal(buf []byte) []byte { + if o == nil { + return []byte{} + } + + if o.marshalData != nil { + if buf == nil { + return o.marshalData + } + copy(buf, o.marshalData) + return buf + } + + if buf == nil { + buf = make([]byte, o.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(objIDField, buf[offset:], o.objectID) + offset += proto.NestedStructureMarshal(objSignatureField, buf[offset:], o.idSig) + offset += proto.NestedStructureMarshal(objHeaderField, buf[offset:], o.header) + proto.BytesMarshal(objPayloadField, buf[offset:], o.payload) + + return buf +} + +// SetMarshalData sets marshal data to reduce memory allocations. +// +// It is unsafe to modify/copy object data after setting marshal data. +func (o *Object) SetMarshalData(data []byte) { + if o == nil { + return + } + o.marshalData = data +} + +func (o *Object) StableSize() (size int) { + if o == nil { + return 0 + } + + size += proto.NestedStructureSize(objIDField, o.objectID) + size += proto.NestedStructureSize(objSignatureField, o.idSig) + size += proto.NestedStructureSize(objHeaderField, o.header) + size += proto.BytesSize(objPayloadField, o.payload) + + return size +} + +func (o *Object) Unmarshal(data []byte) error { + return message.Unmarshal(o, data, new(object.Object)) +} + +func (s *SplitInfo) StableMarshal(buf []byte) []byte { + if s == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, s.StableSize()) + } + + var offset int + + offset += proto.BytesMarshal(splitInfoSplitIDField, buf[offset:], s.splitID) + offset += proto.NestedStructureMarshal(splitInfoLastPartField, buf[offset:], s.lastPart) + proto.NestedStructureMarshal(splitInfoLinkField, buf[offset:], s.link) + + return buf +} + +func (s *SplitInfo) StableSize() (size int) { + if s == nil { + return 0 + } + + size += proto.BytesSize(splitInfoSplitIDField, s.splitID) + size += proto.NestedStructureSize(splitInfoLastPartField, s.lastPart) + size += proto.NestedStructureSize(splitInfoLinkField, s.link) + + return size +} + +func (s *SplitInfo) Unmarshal(data []byte) error { + return message.Unmarshal(s, data, new(object.SplitInfo)) +} + +func (e *ECInfo) StableMarshal(buf []byte) []byte { + if e == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, e.StableSize()) + } + + var offset int + + for i := range e.Chunks { + offset += proto.NestedStructureMarshal(ecInfoChunksField, buf[offset:], &e.Chunks[i]) + } + + return buf +} + +func (e *ECInfo) StableSize() (size int) { + if e == nil { + return 0 + } + + for i := range e.Chunks { + size += proto.NestedStructureSize(ecInfoChunksField, &e.Chunks[i]) + } + + return size +} + +func (e *ECInfo) Unmarshal(data []byte) error { + return message.Unmarshal(e, data, new(object.ECInfo)) +} + +func (c *ECChunk) StableMarshal(buf []byte) []byte { + if c == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, c.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(ecChunkIDField, buf[offset:], &c.ID) + offset += proto.UInt32Marshal(ecChunkIndexField, buf[offset:], c.Index) + proto.UInt32Marshal(ecChunkTotalField, buf[offset:], c.Total) + + return buf +} + +func (c *ECChunk) StableSize() (size int) { + if c == nil { + return 0 + } + + size += proto.NestedStructureSize(ecChunkIDField, &c.ID) + size += proto.UInt32Size(ecChunkIndexField, c.Index) + size += proto.UInt32Size(ecChunkTotalField, c.Total) + + return size +} + +func (c *ECChunk) Unmarshal(data []byte) error { + return message.Unmarshal(c, data, new(object.ECInfo_Chunk)) +} + +func (r *GetRequestBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(getReqBodyAddressField, buf[offset:], r.addr) + proto.BoolMarshal(getReqBodyRawFlagField, buf[offset:], r.raw) + + return buf +} + +func (r *GetRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(getReqBodyAddressField, r.addr) + size += proto.BoolSize(getReqBodyRawFlagField, r.raw) + + return size +} + +func (r *GetRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.GetRequest_Body)) +} + +func (r *GetObjectPartInit) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(getRespInitObjectIDField, buf[offset:], r.id) + offset += proto.NestedStructureMarshal(getRespInitSignatureField, buf[offset:], r.sig) + proto.NestedStructureMarshal(getRespInitHeaderField, buf[offset:], r.hdr) + + return buf +} + +func (r *GetObjectPartInit) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(getRespInitObjectIDField, r.id) + size += proto.NestedStructureSize(getRespInitSignatureField, r.sig) + size += proto.NestedStructureSize(getRespInitHeaderField, r.hdr) + + return size +} + +func (r *GetObjectPartInit) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.GetResponse_Body_Init)) +} + +func (r *GetResponseBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + switch v := r.objPart.(type) { + case nil: + case *GetObjectPartInit: + proto.NestedStructureMarshal(getRespBodyInitField, buf, v) + case *GetObjectPartChunk: + if v != nil { + proto.BytesMarshal(getRespBodyChunkField, buf, v.chunk) + } + case *SplitInfo: + proto.NestedStructureMarshal(getRespBodySplitInfoField, buf, v) + case *ECInfo: + proto.NestedStructureMarshal(getRespBodyECInfoField, buf, v) + default: + panic("unknown one of object get response body type") + } + + return buf +} + +func (r *GetResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + switch v := r.objPart.(type) { + case nil: + case *GetObjectPartInit: + size += proto.NestedStructureSize(getRespBodyInitField, v) + case *GetObjectPartChunk: + if v != nil { + size += proto.BytesSize(getRespBodyChunkField, v.chunk) + } + case *SplitInfo: + size += proto.NestedStructureSize(getRespBodySplitInfoField, v) + case *ECInfo: + size += proto.NestedStructureSize(getRespBodyECInfoField, v) + default: + panic("unknown one of object get response body type") + } + + return +} + +func (r *GetResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.GetResponse_Body)) +} + +func (r *PutObjectPartInit) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(putReqInitObjectIDField, buf[offset:], r.id) + offset += proto.NestedStructureMarshal(putReqInitSignatureField, buf[offset:], r.sig) + offset += proto.NestedStructureMarshal(putReqInitHeaderField, buf[offset:], r.hdr) + proto.RepeatedUInt32Marshal(putReqInitCopiesNumField, buf[offset:], r.copyNum) + + return buf +} + +func (r *PutObjectPartInit) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(putReqInitObjectIDField, r.id) + size += proto.NestedStructureSize(putReqInitSignatureField, r.sig) + size += proto.NestedStructureSize(putReqInitHeaderField, r.hdr) + + arrSize, _ := proto.RepeatedUInt32Size(putReqInitCopiesNumField, r.copyNum) + size += arrSize + + return size +} + +func (r *PutObjectPartInit) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.PutRequest_Body_Init)) +} + +func (r *PutRequestBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + switch v := r.objPart.(type) { + case nil: + case *PutObjectPartInit: + proto.NestedStructureMarshal(putReqBodyInitField, buf, v) + case *PutObjectPartChunk: + if v != nil { + proto.BytesMarshal(putReqBodyChunkField, buf, v.chunk) + } + default: + panic("unknown one of object put request body type") + } + + return buf +} + +func (r *PutRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + switch v := r.objPart.(type) { + case nil: + case *PutObjectPartInit: + size += proto.NestedStructureSize(putReqBodyInitField, v) + case *PutObjectPartChunk: + if v != nil { + size += proto.BytesSize(putReqBodyChunkField, v.chunk) + } + default: + panic("unknown one of object get response body type") + } + + return size +} + +func (r *PutRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.PutRequest_Body)) +} + +func (r *PutResponseBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + proto.NestedStructureMarshal(putRespBodyObjectIDField, buf, r.id) + + return buf +} + +func (r *PutResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(putRespBodyObjectIDField, r.id) + + return size +} + +func (r *PutResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.PutResponse_Body)) +} + +func (r *DeleteRequestBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + proto.NestedStructureMarshal(deleteReqBodyAddressField, buf, r.addr) + + return buf +} + +func (r *DeleteRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(deleteReqBodyAddressField, r.addr) + + return size +} + +func (r *DeleteRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.DeleteRequest_Body)) +} + +func (r *DeleteResponseBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + proto.NestedStructureMarshal(deleteRespBodyTombstoneFNum, buf, r.tombstone) + + return buf +} + +func (r *DeleteResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(deleteRespBodyTombstoneFNum, r.tombstone) + + return size +} + +func (r *DeleteResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.DeleteResponse_Body)) +} + +func (r *HeadRequestBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(headReqBodyAddressField, buf[offset:], r.addr) + offset += proto.BoolMarshal(headReqBodyMainFlagField, buf[offset:], r.mainOnly) + proto.BoolMarshal(headReqBodyRawFlagField, buf[offset:], r.raw) + + return buf +} + +func (r *HeadRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(headReqBodyAddressField, r.addr) + size += proto.BoolSize(headReqBodyMainFlagField, r.mainOnly) + size += proto.BoolSize(headReqBodyRawFlagField, r.raw) + + return size +} + +func (r *HeadRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.HeadRequest_Body)) +} + +func (r *HeadResponseBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + switch v := r.hdrPart.(type) { + case nil: + case *HeaderWithSignature: + if v != nil { + proto.NestedStructureMarshal(headRespBodyHeaderField, buf, v) + } + case *ShortHeader: + if v != nil { + proto.NestedStructureMarshal(headRespBodyShortHeaderField, buf, v) + } + case *SplitInfo: + if v != nil { + proto.NestedStructureMarshal(headRespBodySplitInfoField, buf, v) + } + case *ECInfo: + if v != nil { + proto.NestedStructureMarshal(headRespBodyECInfoField, buf, v) + } + default: + panic("unknown one of object put request body type") + } + + return buf +} + +func (r *HeadResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + switch v := r.hdrPart.(type) { + case nil: + case *HeaderWithSignature: + if v != nil { + size += proto.NestedStructureSize(headRespBodyHeaderField, v) + } + case *ShortHeader: + if v != nil { + size += proto.NestedStructureSize(headRespBodyShortHeaderField, v) + } + case *SplitInfo: + if v != nil { + size += proto.NestedStructureSize(headRespBodySplitInfoField, v) + } + case *ECInfo: + if v != nil { + size += proto.NestedStructureSize(headRespBodyECInfoField, v) + } + default: + panic("unknown one of object put request body type") + } + + return +} + +func (r *HeadResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.HeadResponse_Body)) +} + +func (f *SearchFilter) StableMarshal(buf []byte) []byte { + if f == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, f.StableSize()) + } + + var offset int + + offset += proto.EnumMarshal(searchFilterMatchField, buf[offset:], int32(f.matchType)) + offset += proto.StringMarshal(searchFilterNameField, buf[offset:], f.key) + proto.StringMarshal(searchFilterValueField, buf[offset:], f.val) + + return buf +} + +func (f *SearchFilter) StableSize() (size int) { + if f == nil { + return 0 + } + + size += proto.EnumSize(searchFilterMatchField, int32(f.matchType)) + size += proto.StringSize(searchFilterNameField, f.key) + size += proto.StringSize(searchFilterValueField, f.val) + + return size +} + +func (f *SearchFilter) Unmarshal(data []byte) error { + return message.Unmarshal(f, data, new(object.SearchRequest_Body_Filter)) +} + +func (r *SearchRequestBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(searchReqBodyContainerIDField, buf[offset:], r.cid) + offset += proto.UInt32Marshal(searchReqBodyVersionField, buf[offset:], r.version) + + for i := range r.filters { + offset += proto.NestedStructureMarshal(searchReqBodyFiltersField, buf[offset:], &r.filters[i]) + } + + return buf +} + +func (r *SearchRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(searchReqBodyContainerIDField, r.cid) + size += proto.UInt32Size(searchReqBodyVersionField, r.version) + + for i := range r.filters { + size += proto.NestedStructureSize(searchReqBodyFiltersField, &r.filters[i]) + } + + return size +} + +func (r *SearchRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.SearchRequest_Body)) +} + +func (r *SearchResponseBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + refs.ObjectIDNestedListMarshal(searchRespBodyObjectIDsField, buf[offset:], r.idList) + + return buf +} + +func (r *SearchResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += refs.ObjectIDNestedListSize(searchRespBodyObjectIDsField, r.idList) + + return size +} + +func (r *SearchResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.SearchResponse_Body)) +} + +func (r *Range) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += proto.UInt64Marshal(rangeOffsetField, buf[offset:], r.off) + proto.UInt64Marshal(rangeLengthField, buf[offset:], r.len) + + return buf +} + +func (r *Range) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.UInt64Size(rangeOffsetField, r.off) + size += proto.UInt64Size(rangeLengthField, r.len) + + return size +} + +func (r *Range) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.Range)) +} + +func (r *GetRangeRequestBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(getRangeReqBodyAddressField, buf[offset:], r.addr) + offset += proto.NestedStructureMarshal(getRangeReqBodyRangeField, buf[offset:], r.rng) + proto.BoolMarshal(getRangeReqBodyRawField, buf[offset:], r.raw) + + return buf +} + +func (r *GetRangeRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(getRangeReqBodyAddressField, r.addr) + size += proto.NestedStructureSize(getRangeReqBodyRangeField, r.rng) + size += proto.BoolSize(getRangeReqBodyRawField, r.raw) + + return size +} + +func (r *GetRangeRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.GetRangeRequest_Body)) +} + +func (r *GetRangeResponseBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + switch v := r.rngPart.(type) { + case nil: + case *GetRangePartChunk: + if v != nil { + proto.BytesMarshal(getRangeRespChunkField, buf, v.chunk) + } + case *SplitInfo: + if v != nil { + proto.NestedStructureMarshal(getRangeRespSplitInfoField, buf, v) + } + case *ECInfo: + if v != nil { + proto.NestedStructureMarshal(getRangeRespECInfoField, buf, v) + } + default: + panic("unknown one of object get range request body type") + } + + return buf +} + +func (r *GetRangeResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + switch v := r.rngPart.(type) { + case nil: + case *GetRangePartChunk: + if v != nil { + size += proto.BytesSize(getRangeRespChunkField, v.chunk) + } + case *SplitInfo: + if v != nil { + size = proto.NestedStructureSize(getRangeRespSplitInfoField, v) + } + case *ECInfo: + if v != nil { + size = proto.NestedStructureSize(getRangeRespECInfoField, v) + } + default: + panic("unknown one of object get range request body type") + } + + return +} + +func (r *GetRangeResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.GetRangeResponse_Body)) +} + +func (r *GetRangeHashRequestBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(getRangeHashReqBodyAddressField, buf[offset:], r.addr) + + for i := range r.rngs { + offset += proto.NestedStructureMarshal(getRangeHashReqBodyRangesField, buf[offset:], &r.rngs[i]) + } + + offset += proto.BytesMarshal(getRangeHashReqBodySaltField, buf[offset:], r.salt) + proto.EnumMarshal(getRangeHashReqBodyTypeField, buf[offset:], int32(r.typ)) + + return buf +} + +func (r *GetRangeHashRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(getRangeHashReqBodyAddressField, r.addr) + + for i := range r.rngs { + size += proto.NestedStructureSize(getRangeHashReqBodyRangesField, &r.rngs[i]) + } + + size += proto.BytesSize(getRangeHashReqBodySaltField, r.salt) + size += proto.EnumSize(getRangeHashReqBodyTypeField, int32(r.typ)) + + return size +} + +func (r *GetRangeHashRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.GetRangeHashRequest_Body)) +} + +func (r *GetRangeHashResponseBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += proto.EnumMarshal(getRangeHashRespBodyTypeField, buf, int32(r.typ)) + proto.RepeatedBytesMarshal(getRangeHashRespBodyHashListField, buf[offset:], r.hashList) + + return buf +} + +func (r *GetRangeHashResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.EnumSize(getRangeHashRespBodyTypeField, int32(r.typ)) + size += proto.RepeatedBytesSize(getRangeHashRespBodyHashListField, r.hashList) + + return size +} + +func (r *GetRangeHashResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.GetRangeHashResponse_Body)) +} + +func (r *PutSingleRequestBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if r.marshalData != nil { + if buf == nil { + return r.marshalData + } + copy(buf, r.marshalData) + return buf + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + offset += proto.NestedStructureMarshal(putSingleReqObjectField, buf[offset:], r.object) + proto.RepeatedUInt32Marshal(putSingleReqCopiesNumberField, buf[offset:], r.copyNum) + + return buf +} + +// SetMarshalData sets marshal data to reduce memory allocations. +// +// It is unsafe to modify/copy request data after setting marshal data. +func (r *PutSingleRequestBody) SetMarshalData(data []byte) { + if r == nil { + return + } + + r.marshalData = data + + proto.NestedStructureSetMarshalData(putSingleReqObjectField, r.marshalData, r.object) +} + +func (r *PutSingleRequestBody) StableSize() int { + if r == nil { + return 0 + } + + var size int + size += proto.NestedStructureSize(putSingleReqObjectField, r.object) + arrSize, _ := proto.RepeatedUInt32Size(putSingleReqCopiesNumberField, r.copyNum) + size += arrSize + + return size +} + +func (r *PutSingleRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.PutSingleRequest_Body)) +} + +func (r *PutSingleResponseBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + return buf +} + +func (r *PutSingleResponseBody) StableSize() int { + return 0 +} + +func (r *PutSingleResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.PutSingleResponse_Body)) +} + +func (r *PatchRequestBodyPatch) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + offset += proto.NestedStructureMarshal(patchRequestBodyPatchRangeField, buf[offset:], r.GetRange()) + proto.BytesMarshal(patchRequestBodyPatchChunkField, buf[offset:], r.GetChunk()) + + return buf +} + +func (r *PatchRequestBodyPatch) StableSize() int { + if r == nil { + return 0 + } + + var size int + size += proto.NestedStructureSize(patchRequestBodyPatchRangeField, r.GetRange()) + size += proto.BytesSize(patchRequestBodyPatchChunkField, r.GetChunk()) + + return size +} + +func (r *PatchRequestBodyPatch) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.PatchRequest_Body_Patch)) +} + +func (r *PatchRequestBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + offset += proto.NestedStructureMarshal(patchRequestBodyAddrField, buf[offset:], r.address) + for i := range r.newAttributes { + offset += proto.NestedStructureMarshal(patchRequestBodyNewAttrsField, buf[offset:], &r.newAttributes[i]) + } + offset += proto.BoolMarshal(patchRequestBodyReplaceAttrField, buf[offset:], r.replaceAttributes) + proto.NestedStructureMarshal(patchRequestBodyPatchField, buf[offset:], r.patch) + + return buf +} + +func (r *PatchRequestBody) StableSize() int { + if r == nil { + return 0 + } + + var size int + size += proto.NestedStructureSize(patchRequestBodyAddrField, r.address) + for i := range r.newAttributes { + size += proto.NestedStructureSize(patchRequestBodyNewAttrsField, &r.newAttributes[i]) + } + size += proto.BoolSize(patchRequestBodyReplaceAttrField, r.replaceAttributes) + size += proto.NestedStructureSize(patchRequestBodyPatchField, r.patch) + + return size +} + +func (r *PatchRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.PatchRequest_Body)) +} + +func (r *PatchResponseBody) StableSize() int { + if r == nil { + return 0 + } + + var size int + size += proto.NestedStructureSize(patchResponseBodyObjectIDField, r.ObjectID) + + return size +} + +func (r *PatchResponseBody) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + proto.NestedStructureMarshal(patchResponseBodyObjectIDField, buf[offset:], r.ObjectID) + + return buf +} + +func (r *PatchResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(object.PatchResponse_Body)) +} diff --git a/apiv2/object/message_test.go b/apiv2/object/message_test.go new file mode 100644 index 0000000..24f40d8 --- /dev/null +++ b/apiv2/object/message_test.go @@ -0,0 +1,65 @@ +package object_test + +import ( + "testing" + + objecttest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object/test" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + messagetest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message/test" +) + +func TestMessageConvert(t *testing.T) { + messagetest.TestRPCMessage(t, + func(empty bool) message.Message { return objecttest.GenerateShortHeader(empty) }, + func(empty bool) message.Message { return objecttest.GenerateAttribute(empty) }, + func(empty bool) message.Message { return objecttest.GenerateSplitHeader(empty) }, + func(empty bool) message.Message { return objecttest.GenerateHeaderWithSplitHeader(empty) }, + func(empty bool) message.Message { return objecttest.GenerateHeaderWithECHeader(empty) }, + func(empty bool) message.Message { return objecttest.GenerateECHeader(empty) }, + func(empty bool) message.Message { return objecttest.GenerateObject(empty) }, + func(empty bool) message.Message { return objecttest.GenerateSplitInfo(empty) }, + func(empty bool) message.Message { return objecttest.GenerateECInfo(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetRequestBody(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetRequest(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetObjectPartInit(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetObjectPartChunk(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetResponseBody(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetResponse(empty) }, + func(empty bool) message.Message { return objecttest.GeneratePutObjectPartInit(empty) }, + func(empty bool) message.Message { return objecttest.GeneratePutObjectPartChunk(empty) }, + func(empty bool) message.Message { return objecttest.GeneratePutRequestBody(empty) }, + func(empty bool) message.Message { return objecttest.GeneratePutRequest(empty) }, + func(empty bool) message.Message { return objecttest.GeneratePutResponseBody(empty) }, + func(empty bool) message.Message { return objecttest.GeneratePutResponse(empty) }, + func(empty bool) message.Message { return objecttest.GenerateDeleteRequestBody(empty) }, + func(empty bool) message.Message { return objecttest.GenerateDeleteRequest(empty) }, + func(empty bool) message.Message { return objecttest.GenerateDeleteResponseBody(empty) }, + func(empty bool) message.Message { return objecttest.GenerateDeleteResponse(empty) }, + func(empty bool) message.Message { return objecttest.GenerateHeadRequestBody(empty) }, + func(empty bool) message.Message { return objecttest.GenerateHeadRequest(empty) }, + func(empty bool) message.Message { return objecttest.GenerateHeadResponseBody(empty) }, + func(empty bool) message.Message { return objecttest.GenerateHeadResponse(empty) }, + func(empty bool) message.Message { return objecttest.GenerateSearchFilter(empty) }, + func(empty bool) message.Message { return objecttest.GenerateSearchRequestBody(empty) }, + func(empty bool) message.Message { return objecttest.GenerateSearchRequest(empty) }, + func(empty bool) message.Message { return objecttest.GenerateSearchResponseBody(empty) }, + func(empty bool) message.Message { return objecttest.GenerateSearchResponse(empty) }, + func(empty bool) message.Message { return objecttest.GenerateRange(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetRangeRequestBody(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetRangeRequest(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetRangeResponseBody(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetRangeResponse(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetRangeHashRequestBody(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetRangeHashRequest(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetRangeHashResponseBody(empty) }, + func(empty bool) message.Message { return objecttest.GenerateGetRangeHashResponse(empty) }, + func(empty bool) message.Message { return objecttest.GenerateLock(empty) }, + func(empty bool) message.Message { return objecttest.GeneratePutSingleRequest(empty) }, + func(empty bool) message.Message { return objecttest.GeneratePutSingleResponse(empty) }, + func(empty bool) message.Message { return objecttest.GeneratePatchRequestBodyPatch(empty) }, + func(empty bool) message.Message { return objecttest.GeneratePatchRequestBody(empty) }, + func(empty bool) message.Message { return objecttest.GeneratePatchRequest(empty) }, + func(empty bool) message.Message { return objecttest.GeneratePatchResponseBody(empty) }, + func(empty bool) message.Message { return objecttest.GeneratePatchResponse(empty) }, + ) +} diff --git a/apiv2/object/status.go b/apiv2/object/status.go new file mode 100644 index 0000000..6bebace --- /dev/null +++ b/apiv2/object/status.go @@ -0,0 +1,91 @@ +package object + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" + statusgrpc "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status/grpc" +) + +// LocalizeFailStatus checks if passed global status.Code is related to object failure and: +// +// then localizes the code and returns true, +// else leaves the code unchanged and returns false. +// +// Arg must not be nil. +func LocalizeFailStatus(c *status.Code) bool { + return status.LocalizeIfInSection(c, uint32(statusgrpc.Section_SECTION_OBJECT)) +} + +// GlobalizeFail globalizes local code of object failure. +// +// Arg must not be nil. +func GlobalizeFail(c *status.Code) { + c.GlobalizeSection(uint32(statusgrpc.Section_SECTION_OBJECT)) +} + +const ( + // StatusAccessDenied is a local status.Code value for + // ACCESS_DENIED object failure. + StatusAccessDenied status.Code = iota + // StatusNotFound is a local status.Code value for + // OBJECT_NOT_FOUND object failure. + StatusNotFound + // StatusLocked is a local status.Code value for + // LOCKED object failure. + StatusLocked + // StatusLockNonRegularObject is a local status.Code value for + // LOCK_NON_REGULAR_OBJECT object failure. + StatusLockNonRegularObject + // StatusAlreadyRemoved is a local status.Code value for + // OBJECT_ALREADY_REMOVED object failure. + StatusAlreadyRemoved + // StatusOutOfRange is a local status.Code value for + // OUT_OF_RANGE object failure. + StatusOutOfRange +) + +const ( + // detailAccessDeniedDesc is a StatusAccessDenied detail ID for + // human-readable description. + detailAccessDeniedDesc = iota +) + +// WriteAccessDeniedDesc writes human-readable description of StatusAccessDenied +// into status.Status as a detail. The status must not be nil. +// +// Existing details are expected to be ID-unique, otherwise undefined behavior. +func WriteAccessDeniedDesc(st *status.Status, desc string) { + var found bool + + st.IterateDetails(func(d *status.Detail) bool { + if d.ID() == detailAccessDeniedDesc { + found = true + d.SetValue([]byte(desc)) + } + + return found + }) + + if !found { + var d status.Detail + + d.SetID(detailAccessDeniedDesc) + d.SetValue([]byte(desc)) + + st.AppendDetails(d) + } +} + +// ReadAccessDeniedDesc looks up for status detail with human-readable description +// of StatusAccessDenied. Returns empty string if detail is missing. +func ReadAccessDeniedDesc(st status.Status) (desc string) { + st.IterateDetails(func(d *status.Detail) bool { + if d.ID() == detailAccessDeniedDesc { + desc = string(d.Value()) + return true + } + + return false + }) + + return +} diff --git a/apiv2/object/status_test.go b/apiv2/object/status_test.go new file mode 100644 index 0000000..689891e --- /dev/null +++ b/apiv2/object/status_test.go @@ -0,0 +1,35 @@ +package object_test + +import ( + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" + statustest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status/test" + "github.com/stretchr/testify/require" +) + +func TestStatusCodes(t *testing.T) { + statustest.TestCodes(t, object.LocalizeFailStatus, object.GlobalizeFail, + object.StatusAccessDenied, 2048, + object.StatusNotFound, 2049, + object.StatusLocked, 2050, + object.StatusLockNonRegularObject, 2051, + object.StatusAlreadyRemoved, 2052, + object.StatusOutOfRange, 2053, + ) +} + +func TestAccessDeniedDesc(t *testing.T) { + var st status.Status + + require.Empty(t, object.ReadAccessDeniedDesc(st)) + + const desc = "some description" + + object.WriteAccessDeniedDesc(&st, desc) + require.Equal(t, desc, object.ReadAccessDeniedDesc(st)) + + object.WriteAccessDeniedDesc(&st, desc+"1") + require.Equal(t, desc+"1", object.ReadAccessDeniedDesc(st)) +} diff --git a/apiv2/object/string.go b/apiv2/object/string.go new file mode 100644 index 0000000..875cf9c --- /dev/null +++ b/apiv2/object/string.go @@ -0,0 +1,55 @@ +package object + +import ( + object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object/grpc" +) + +// String returns string representation of Type. +func (t Type) String() string { + return TypeToGRPCField(t).String() +} + +// FromString parses Type from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (t *Type) FromString(s string) bool { + var g object.ObjectType + + ok := g.FromString(s) + + if ok { + *t = TypeFromGRPCField(g) + } + + return ok +} + +// TypeFromString converts string to Type. +// +// Deprecated: use FromString method. +func TypeFromString(s string) (t Type) { + t.FromString(s) + return +} + +// String returns string representation of MatchType. +func (t MatchType) String() string { + return MatchTypeToGRPCField(t).String() +} + +// FromString parses MatchType from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (t *MatchType) FromString(s string) bool { + var g object.MatchType + + ok := g.FromString(s) + + if ok { + *t = MatchTypeFromGRPCField(g) + } + + return ok +} diff --git a/apiv2/object/test/generate.go b/apiv2/object/test/generate.go new file mode 100644 index 0000000..db59c6a --- /dev/null +++ b/apiv2/object/test/generate.go @@ -0,0 +1,766 @@ +package objecttest + +import ( + crand "crypto/rand" + "math/rand" + "time" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + refstest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/test" + sessiontest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session/test" +) + +func GenerateShortHeader(empty bool) *object.ShortHeader { + m := new(object.ShortHeader) + + if !empty { + m.SetObjectType(13) + m.SetCreationEpoch(100) + m.SetPayloadLength(12321) + m.SetOwnerID(refstest.GenerateOwnerID(false)) + } + + m.SetVersion(refstest.GenerateVersion(empty)) + m.SetHomomorphicHash(refstest.GenerateChecksum(empty)) + m.SetPayloadHash(refstest.GenerateChecksum(empty)) + + return m +} + +func GenerateAttribute(empty bool) *object.Attribute { + m := new(object.Attribute) + + if !empty { + m.SetKey("object key") + m.SetValue("object value") + } + + return m +} + +func GenerateAttributes(empty bool) []object.Attribute { + var res []object.Attribute + + if !empty { + res = append(res, + *GenerateAttribute(false), + *GenerateAttribute(false), + ) + } + + return res +} + +func GenerateSplitHeader(empty bool) *object.SplitHeader { + return generateSplitHeader(empty, true) +} + +func generateSplitHeader(empty, withPar bool) *object.SplitHeader { + m := new(object.SplitHeader) + + if !empty { + id := make([]byte, 16) + _, _ = crand.Read(id) + + m.SetSplitID(id) + m.SetParent(refstest.GenerateObjectID(false)) + m.SetPrevious(refstest.GenerateObjectID(false)) + m.SetChildren(refstest.GenerateObjectIDs(false)) + } + + m.SetParentSignature(refstest.GenerateSignature(empty)) + + if withPar { + m.SetParentHeader(GenerateHeaderWithSplitHeader(empty)) + } + + return m +} + +func GenerateHeaderWithSplitHeader(empty bool) *object.Header { + m := generateHeader(empty) + m.SetSplit(generateSplitHeader(empty, false)) + return m +} + +func GenerateHeaderWithECHeader(empty bool) *object.Header { + m := generateHeader(empty) + m.SetEC(GenerateECHeader(empty)) + return m +} + +func GenerateECHeader(empty bool) *object.ECHeader { + ech := new(object.ECHeader) + + if !empty { + ech.Parent = refstest.GenerateObjectID(empty) + + ech.ParentSplitID = make([]byte, 16) + _, _ = crand.Read(ech.ParentSplitID) + + ech.ParentSplitParentID = refstest.GenerateObjectID(empty) + ech.ParentAttributes = GenerateAttributes(empty) + ech.Index = 0 + ech.Total = 2 + ech.Header = []byte("chunk of ec-encoded parent header") + ech.HeaderLength = uint32(2 * len(ech.Header)) + } + + return ech +} + +func generateHeader(empty bool) *object.Header { + m := new(object.Header) + + if !empty { + m.SetPayloadLength(777) + m.SetCreationEpoch(432) + m.SetObjectType(111) + m.SetOwnerID(refstest.GenerateOwnerID(false)) + m.SetContainerID(refstest.GenerateContainerID(false)) + m.SetAttributes(GenerateAttributes(false)) + } + + m.SetVersion(refstest.GenerateVersion(empty)) + m.SetPayloadHash(refstest.GenerateChecksum(empty)) + m.SetHomomorphicHash(refstest.GenerateChecksum(empty)) + m.SetSessionToken(sessiontest.GenerateSessionToken(empty)) + + return m +} + +func GenerateHeaderWithSignature(empty bool) *object.HeaderWithSignature { + m := new(object.HeaderWithSignature) + + m.SetSignature(refstest.GenerateSignature(empty)) + m.SetHeader(GenerateHeaderWithSplitHeader(empty)) + + return m +} + +func GenerateObject(empty bool) *object.Object { + m := new(object.Object) + + if !empty { + m.SetPayload([]byte{7, 8, 9}) + m.SetObjectID(refstest.GenerateObjectID(false)) + } + + m.SetSignature(refstest.GenerateSignature(empty)) + m.SetHeader(GenerateHeaderWithSplitHeader(empty)) + + return m +} + +func GenerateSplitInfo(empty bool) *object.SplitInfo { + m := new(object.SplitInfo) + + if !empty { + id := make([]byte, 16) + _, _ = crand.Read(id) + + m.SetSplitID(id) + m.SetLastPart(refstest.GenerateObjectID(false)) + m.SetLink(refstest.GenerateObjectID(false)) + } + + return m +} + +func GenerateECInfo(empty bool) *object.ECInfo { + m := new(object.ECInfo) + + if !empty { + m.Chunks = make([]object.ECChunk, 2) + for i := range m.Chunks { + m.Chunks[i] = *GenerateECChunk(false) + } + } + + return m +} + +func GenerateECChunk(empty bool) *object.ECChunk { + m := new(object.ECChunk) + + if !empty { + m.ID = *refstest.GenerateObjectID(false) + m.Index = 4 + m.Total = 7 + } + + return m +} + +func GenerateGetRequestBody(empty bool) *object.GetRequestBody { + m := new(object.GetRequestBody) + + if !empty { + m.SetRaw(true) + m.SetAddress(refstest.GenerateAddress(false)) + } + + return m +} + +func GenerateGetRequest(empty bool) *object.GetRequest { + m := new(object.GetRequest) + + if !empty { + m.SetBody(GenerateGetRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateGetObjectPartInit(empty bool) *object.GetObjectPartInit { + m := new(object.GetObjectPartInit) + + if !empty { + m.SetObjectID(refstest.GenerateObjectID(false)) + } + + m.SetSignature(refstest.GenerateSignature(empty)) + m.SetHeader(GenerateHeaderWithSplitHeader(empty)) + + return m +} + +func GenerateGetObjectPartChunk(empty bool) *object.GetObjectPartChunk { + m := new(object.GetObjectPartChunk) + + if !empty { + m.SetChunk([]byte("get chunk")) + } + + return m +} + +func GenerateGetResponseBody(empty bool) *object.GetResponseBody { + m := new(object.GetResponseBody) + + if !empty { + switch randomInt(3) { + case 0: + m.SetObjectPart(GenerateGetObjectPartInit(false)) + case 1: + m.SetObjectPart(GenerateGetObjectPartChunk(false)) + case 2: + m.SetObjectPart(GenerateSplitInfo(false)) + } + } + + return m +} + +func GenerateGetResponse(empty bool) *object.GetResponse { + m := new(object.GetResponse) + + if !empty { + m.SetBody(GenerateGetResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} + +func GeneratePutObjectPartInit(empty bool) *object.PutObjectPartInit { + m := new(object.PutObjectPartInit) + + if !empty { + m.SetCopiesNumber([]uint32{234}) + m.SetObjectID(refstest.GenerateObjectID(false)) + } + + m.SetSignature(refstest.GenerateSignature(empty)) + m.SetHeader(GenerateHeaderWithSplitHeader(empty)) + + return m +} + +func GeneratePutObjectPartChunk(empty bool) *object.PutObjectPartChunk { + m := new(object.PutObjectPartChunk) + + if !empty { + m.SetChunk([]byte("put chunk")) + } + + return m +} + +func GeneratePutRequestBody(empty bool) *object.PutRequestBody { + m := new(object.PutRequestBody) + + if !empty { + switch randomInt(2) { + case 0: + m.SetObjectPart(GeneratePutObjectPartInit(false)) + case 1: + m.SetObjectPart(GeneratePutObjectPartChunk(false)) + } + } + + return m +} + +func GeneratePutRequest(empty bool) *object.PutRequest { + m := new(object.PutRequest) + + if !empty { + m.SetBody(GeneratePutRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GeneratePutResponseBody(empty bool) *object.PutResponseBody { + m := new(object.PutResponseBody) + + if !empty { + m.SetObjectID(refstest.GenerateObjectID(false)) + } + + return m +} + +func GeneratePutResponse(empty bool) *object.PutResponse { + m := new(object.PutResponse) + + if !empty { + m.SetBody(GeneratePutResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} + +func GenerateDeleteRequestBody(empty bool) *object.DeleteRequestBody { + m := new(object.DeleteRequestBody) + + if !empty { + m.SetAddress(refstest.GenerateAddress(false)) + } + + return m +} + +func GenerateDeleteRequest(empty bool) *object.DeleteRequest { + m := new(object.DeleteRequest) + + if !empty { + m.SetBody(GenerateDeleteRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateDeleteResponseBody(empty bool) *object.DeleteResponseBody { + m := new(object.DeleteResponseBody) + + if !empty { + m.SetTombstone(refstest.GenerateAddress(false)) + } + + return m +} + +func GenerateDeleteResponse(empty bool) *object.DeleteResponse { + m := new(object.DeleteResponse) + + if !empty { + m.SetBody(GenerateDeleteResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} + +func GenerateHeadRequestBody(empty bool) *object.HeadRequestBody { + m := new(object.HeadRequestBody) + + if !empty { + m.SetRaw(true) + m.SetMainOnly(true) + m.SetAddress(refstest.GenerateAddress(false)) + } + + return m +} + +func GenerateHeadRequest(empty bool) *object.HeadRequest { + m := new(object.HeadRequest) + + if !empty { + m.SetBody(GenerateHeadRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateHeadResponseBody(empty bool) *object.HeadResponseBody { + m := new(object.HeadResponseBody) + + if !empty { + switch randomInt(3) { + case 0: + m.SetHeaderPart(GenerateHeaderWithSignature(false)) + case 1: + m.SetHeaderPart(GenerateShortHeader(false)) + case 2: + m.SetHeaderPart(GenerateSplitInfo(false)) + } + } + + return m +} + +func GenerateHeadResponse(empty bool) *object.HeadResponse { + m := new(object.HeadResponse) + + if !empty { + m.SetBody(GenerateHeadResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} + +func GenerateSearchFilter(empty bool) *object.SearchFilter { + m := new(object.SearchFilter) + + if !empty { + m.SetKey("search filter key") + m.SetValue("search filter val") + m.SetMatchType(987) + } + + return m +} + +func GenerateSearchFilters(empty bool) []object.SearchFilter { + var res []object.SearchFilter + + if !empty { + res = append(res, + *GenerateSearchFilter(false), + *GenerateSearchFilter(false), + ) + } + + return res +} + +func GenerateSearchRequestBody(empty bool) *object.SearchRequestBody { + m := new(object.SearchRequestBody) + + if !empty { + m.SetVersion(555) + m.SetContainerID(refstest.GenerateContainerID(false)) + m.SetFilters(GenerateSearchFilters(false)) + } + + return m +} + +func GenerateSearchRequest(empty bool) *object.SearchRequest { + m := new(object.SearchRequest) + + if !empty { + m.SetBody(GenerateSearchRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateSearchResponseBody(empty bool) *object.SearchResponseBody { + m := new(object.SearchResponseBody) + + if !empty { + m.SetIDList(refstest.GenerateObjectIDs(false)) + } + + return m +} + +func GenerateSearchResponse(empty bool) *object.SearchResponse { + m := new(object.SearchResponse) + + if !empty { + m.SetBody(GenerateSearchResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} + +func GenerateRange(empty bool) *object.Range { + m := new(object.Range) + + if !empty { + m.SetLength(11) + m.SetOffset(22) + } + + return m +} + +func GenerateRanges(empty bool) []object.Range { + var res []object.Range + + if !empty { + res = append(res, + *GenerateRange(false), + *GenerateRange(false), + ) + } + + return res +} + +func GenerateGetRangeRequestBody(empty bool) *object.GetRangeRequestBody { + m := new(object.GetRangeRequestBody) + + if !empty { + m.SetRaw(true) + m.SetAddress(refstest.GenerateAddress(empty)) + m.SetRange(GenerateRange(empty)) + } + + return m +} + +func GenerateGetRangeRequest(empty bool) *object.GetRangeRequest { + m := new(object.GetRangeRequest) + + if !empty { + m.SetBody(GenerateGetRangeRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateGetRangePartChunk(empty bool) *object.GetRangePartChunk { + m := new(object.GetRangePartChunk) + + if !empty { + m.SetChunk([]byte("get range chunk")) + } + + return m +} + +func GenerateGetRangeResponseBody(empty bool) *object.GetRangeResponseBody { + m := new(object.GetRangeResponseBody) + + if !empty { + switch randomInt(2) { + case 0: + m.SetRangePart(GenerateGetRangePartChunk(false)) + case 1: + m.SetRangePart(GenerateSplitInfo(false)) + } + } + + return m +} + +func GenerateGetRangeResponse(empty bool) *object.GetRangeResponse { + m := new(object.GetRangeResponse) + + if !empty { + m.SetBody(GenerateGetRangeResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} + +func GenerateGetRangeHashRequestBody(empty bool) *object.GetRangeHashRequestBody { + m := new(object.GetRangeHashRequestBody) + + if !empty { + m.SetSalt([]byte("range hash salt")) + m.SetType(455) + m.SetAddress(refstest.GenerateAddress(false)) + m.SetRanges(GenerateRanges(false)) + } + + return m +} + +func GenerateGetRangeHashRequest(empty bool) *object.GetRangeHashRequest { + m := new(object.GetRangeHashRequest) + + if !empty { + m.SetBody(GenerateGetRangeHashRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateGetRangeHashResponseBody(empty bool) *object.GetRangeHashResponseBody { + m := new(object.GetRangeHashResponseBody) + + if !empty { + m.SetType(678) + m.SetHashList([][]byte{ + refstest.GenerateChecksum(false).GetSum(), + refstest.GenerateChecksum(false).GetSum(), + }) + } + + return m +} + +func GenerateGetRangeHashResponse(empty bool) *object.GetRangeHashResponse { + m := new(object.GetRangeHashResponse) + + if !empty { + m.SetBody(GenerateGetRangeHashResponseBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + + return m +} + +func GenerateLock(empty bool) *object.Lock { + m := new(object.Lock) + + if !empty { + m.SetMembers([]refs.ObjectID{ + *refstest.GenerateObjectID(false), + *refstest.GenerateObjectID(false), + }) + } + + return m +} + +func GeneratePutSingleRequest(empty bool) *object.PutSingleRequest { + m := new(object.PutSingleRequest) + + if !empty { + m.SetBody(GeneratePutSingleRequestBody(false)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GeneratePutSingleRequestBody(empty bool) *object.PutSingleRequestBody { + b := new(object.PutSingleRequestBody) + if !empty { + b.SetObject(GenerateObject(empty)) + b.SetCopiesNumber([]uint32{12345}) + } + return b +} + +func GeneratePutSingleResponse(empty bool) *object.PutSingleResponse { + m := new(object.PutSingleResponse) + if !empty { + m.SetBody(new(object.PutSingleResponseBody)) + } + m.SetMetaHeader(sessiontest.GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateResponseVerificationHeader(empty)) + return m +} + +func GeneratePatchRequestBodyPatch(empty bool) *object.PatchRequestBodyPatch { + m := new(object.PatchRequestBodyPatch) + + if !empty { + m.Range = GenerateRange(false) + m.Chunk = []byte("GeneratePatchRequestBodyPatch") + } + + return m +} + +func GeneratePatchRequestBody(empty bool) *object.PatchRequestBody { + m := new(object.PatchRequestBody) + + if !empty { + m.SetAddress(refstest.GenerateAddress(empty)) + m.SetNewAttributes(GenerateAttributes(empty)) + m.SetReplaceAttributes(false) + m.SetPatch(GeneratePatchRequestBodyPatch(empty)) + } + + return m +} + +func GeneratePatchRequest(empty bool) *object.PatchRequest { + m := new(object.PatchRequest) + + if !empty { + m.SetBody(GeneratePatchRequestBody(empty)) + } + + m.SetMetaHeader(sessiontest.GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(sessiontest.GenerateRequestVerificationHeader(empty)) + + return m +} + +func GeneratePatchResponseBody(empty bool) *object.PatchResponseBody { + m := new(object.PatchResponseBody) + + if !empty { + m.ObjectID = refstest.GenerateObjectID(empty) + } + + return m +} + +func GeneratePatchResponse(empty bool) *object.PatchResponse { + m := new(object.PatchResponse) + + if !empty { + m.Body = GeneratePatchResponseBody(empty) + } + + return m +} + +func randomInt(n int) int { + return rand.New(rand.NewSource(time.Now().UnixNano())).Intn(n) +} diff --git a/apiv2/object/types.go b/apiv2/object/types.go new file mode 100644 index 0000000..4a0220b --- /dev/null +++ b/apiv2/object/types.go @@ -0,0 +1,1650 @@ +package object + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" +) + +type Type uint32 + +type MatchType uint32 + +type ShortHeader struct { + version *refs.Version + + creatEpoch uint64 + + ownerID *refs.OwnerID + + typ Type + + payloadLen uint64 + + payloadHash, homoHash *refs.Checksum +} + +type Attribute struct { + key, val string +} + +type SplitHeader struct { + par, prev *refs.ObjectID + + parSig *refs.Signature + + parHdr *Header + + children []refs.ObjectID + + splitID []byte +} + +type ECHeader struct { + Parent *refs.ObjectID + ParentSplitID []byte + ParentSplitParentID *refs.ObjectID + ParentAttributes []Attribute + Index uint32 + Total uint32 + Header []byte + HeaderLength uint32 +} + +type Header struct { + version *refs.Version + + cid *refs.ContainerID + + ownerID *refs.OwnerID + + creatEpoch uint64 + + payloadLen uint64 + + payloadHash, homoHash *refs.Checksum + + typ Type + + sessionToken *session.Token + + attr []Attribute + + split *SplitHeader + + ec *ECHeader +} + +type HeaderWithSignature struct { + header *Header + + signature *refs.Signature +} + +type Object struct { + objectID *refs.ObjectID + + idSig *refs.Signature + + header *Header + + payload []byte + + // marshalData holds marshaled data, must not be marshaled by StableMarshal + marshalData []byte +} + +type SplitInfo struct { + splitID []byte + + lastPart *refs.ObjectID + + link *refs.ObjectID +} + +type ECChunk struct { + ID refs.ObjectID + Index uint32 + Total uint32 +} + +type ECInfo struct { + Chunks []ECChunk +} + +type GetRequestBody struct { + addr *refs.Address + + raw bool +} + +type GetObjectPart interface { + getObjectPart() +} + +type GetObjectPartInit struct { + id *refs.ObjectID + + sig *refs.Signature + + hdr *Header +} + +type GetObjectPartChunk struct { + chunk []byte +} + +type GetRequest struct { + body *GetRequestBody + + session.RequestHeaders +} + +type GetResponseBody struct { + objPart GetObjectPart +} + +type PutObjectPart interface { + putObjectPart() +} + +type PutObjectPartInit struct { + id *refs.ObjectID + + sig *refs.Signature + + hdr *Header + + copyNum []uint32 +} + +type PutObjectPartChunk struct { + chunk []byte +} + +type GetResponse struct { + body *GetResponseBody + + session.ResponseHeaders +} + +type PutRequestBody struct { + objPart PutObjectPart +} + +type PutRequest struct { + body *PutRequestBody + + session.RequestHeaders +} + +type PutResponseBody struct { + id *refs.ObjectID +} + +type PutResponse struct { + body *PutResponseBody + + session.ResponseHeaders +} + +type DeleteRequestBody struct { + addr *refs.Address +} + +type DeleteRequest struct { + body *DeleteRequestBody + + session.RequestHeaders +} + +type DeleteResponseBody struct { + tombstone *refs.Address +} + +type DeleteResponse struct { + body *DeleteResponseBody + + session.ResponseHeaders +} + +type HeadRequestBody struct { + addr *refs.Address + + mainOnly, raw bool +} + +type GetHeaderPart interface { + getHeaderPart() +} + +type HeadRequest struct { + body *HeadRequestBody + + session.RequestHeaders +} + +type HeadResponseBody struct { + hdrPart GetHeaderPart +} + +type HeadResponse struct { + body *HeadResponseBody + + session.ResponseHeaders +} + +type SearchFilter struct { + matchType MatchType + + key, val string +} + +type SearchRequestBody struct { + cid *refs.ContainerID + + version uint32 + + filters []SearchFilter +} + +type SearchRequest struct { + body *SearchRequestBody + + session.RequestHeaders +} + +type SearchResponseBody struct { + idList []refs.ObjectID +} + +type SearchResponse struct { + body *SearchResponseBody + + session.ResponseHeaders +} + +type Range struct { + off, len uint64 +} + +type GetRangeRequestBody struct { + addr *refs.Address + + rng *Range + + raw bool +} + +type GetRangeRequest struct { + body *GetRangeRequestBody + + session.RequestHeaders +} + +type GetRangePart interface { + getRangePart() +} + +type GetRangePartChunk struct { + chunk []byte +} + +type GetRangeResponseBody struct { + rngPart GetRangePart +} + +type GetRangeResponse struct { + body *GetRangeResponseBody + + session.ResponseHeaders +} + +type GetRangeHashRequestBody struct { + addr *refs.Address + + rngs []Range + + salt []byte + + typ refs.ChecksumType +} + +type GetRangeHashRequest struct { + body *GetRangeHashRequestBody + + session.RequestHeaders +} + +type GetRangeHashResponseBody struct { + typ refs.ChecksumType + + hashList [][]byte +} + +type GetRangeHashResponse struct { + body *GetRangeHashResponseBody + + session.ResponseHeaders +} + +type PutSingleRequestBody struct { + object *Object + copyNum []uint32 + + // marshalData holds marshaled data, must not be marshaled by StableMarshal + marshalData []byte +} + +type PutSingleRequest struct { + body *PutSingleRequestBody + + session.RequestHeaders +} + +type PutSingleResponseBody struct{} + +type PutSingleResponse struct { + body *PutSingleResponseBody + + session.ResponseHeaders +} + +type PatchRequestBodyPatch struct { + Range *Range + + Chunk []byte +} + +type PatchRequestBody struct { + address *refs.Address + + newAttributes []Attribute + + replaceAttributes bool + + patch *PatchRequestBodyPatch +} + +type PatchRequest struct { + body *PatchRequestBody + + session.RequestHeaders +} + +type PatchResponseBody struct { + ObjectID *refs.ObjectID +} + +type PatchResponse struct { + Body *PatchResponseBody + + session.ResponseHeaders +} + +const ( + TypeRegular Type = iota + TypeTombstone + _ + TypeLock +) + +const ( + MatchUnknown MatchType = iota + MatchStringEqual + MatchStringNotEqual + MatchNotPresent + MatchCommonPrefix +) + +func (h *ShortHeader) GetVersion() *refs.Version { + if h != nil { + return h.version + } + + return nil +} + +func (h *ShortHeader) SetVersion(v *refs.Version) { + h.version = v +} + +func (h *ShortHeader) GetCreationEpoch() uint64 { + if h != nil { + return h.creatEpoch + } + + return 0 +} + +func (h *ShortHeader) SetCreationEpoch(v uint64) { + h.creatEpoch = v +} + +func (h *ShortHeader) GetOwnerID() *refs.OwnerID { + if h != nil { + return h.ownerID + } + + return nil +} + +func (h *ShortHeader) SetOwnerID(v *refs.OwnerID) { + h.ownerID = v +} + +func (h *ShortHeader) GetObjectType() Type { + if h != nil { + return h.typ + } + + return TypeRegular +} + +func (h *ShortHeader) SetObjectType(v Type) { + h.typ = v +} + +func (h *ShortHeader) GetPayloadLength() uint64 { + if h != nil { + return h.payloadLen + } + + return 0 +} + +func (h *ShortHeader) SetPayloadLength(v uint64) { + h.payloadLen = v +} + +func (h *ShortHeader) GetPayloadHash() *refs.Checksum { + if h != nil { + return h.payloadHash + } + + return nil +} + +func (h *ShortHeader) SetPayloadHash(v *refs.Checksum) { + h.payloadHash = v +} + +func (h *ShortHeader) GetHomomorphicHash() *refs.Checksum { + if h != nil { + return h.homoHash + } + + return nil +} + +func (h *ShortHeader) SetHomomorphicHash(v *refs.Checksum) { + h.homoHash = v +} + +func (h *ShortHeader) getHeaderPart() {} + +func (a *Attribute) GetKey() string { + if a != nil { + return a.key + } + + return "" +} + +func (a *Attribute) SetKey(v string) { + a.key = v +} + +func (a *Attribute) GetValue() string { + if a != nil { + return a.val + } + + return "" +} + +func (a *Attribute) SetValue(v string) { + a.val = v +} + +func (h *SplitHeader) GetParent() *refs.ObjectID { + if h != nil { + return h.par + } + + return nil +} + +func (h *SplitHeader) SetParent(v *refs.ObjectID) { + h.par = v +} + +func (h *SplitHeader) GetPrevious() *refs.ObjectID { + if h != nil { + return h.prev + } + + return nil +} + +func (h *SplitHeader) SetPrevious(v *refs.ObjectID) { + h.prev = v +} + +func (h *SplitHeader) GetParentSignature() *refs.Signature { + if h != nil { + return h.parSig + } + + return nil +} + +func (h *SplitHeader) SetParentSignature(v *refs.Signature) { + h.parSig = v +} + +func (h *SplitHeader) GetParentHeader() *Header { + if h != nil { + return h.parHdr + } + + return nil +} + +func (h *SplitHeader) SetParentHeader(v *Header) { + h.parHdr = v +} + +func (h *SplitHeader) GetChildren() []refs.ObjectID { + if h != nil { + return h.children + } + + return nil +} + +func (h *SplitHeader) SetChildren(v []refs.ObjectID) { + h.children = v +} + +func (h *SplitHeader) GetSplitID() []byte { + if h != nil { + return h.splitID + } + + return nil +} + +func (h *SplitHeader) SetSplitID(v []byte) { + h.splitID = v +} + +func (h *Header) GetVersion() *refs.Version { + if h != nil { + return h.version + } + + return nil +} + +func (h *Header) SetVersion(v *refs.Version) { + h.version = v +} + +func (h *Header) GetContainerID() *refs.ContainerID { + if h != nil { + return h.cid + } + + return nil +} + +func (h *Header) SetContainerID(v *refs.ContainerID) { + h.cid = v +} + +func (h *Header) GetOwnerID() *refs.OwnerID { + if h != nil { + return h.ownerID + } + + return nil +} + +func (h *Header) SetOwnerID(v *refs.OwnerID) { + h.ownerID = v +} + +func (h *Header) GetCreationEpoch() uint64 { + if h != nil { + return h.creatEpoch + } + + return 0 +} + +func (h *Header) SetCreationEpoch(v uint64) { + h.creatEpoch = v +} + +func (h *Header) GetPayloadLength() uint64 { + if h != nil { + return h.payloadLen + } + + return 0 +} + +func (h *Header) SetPayloadLength(v uint64) { + h.payloadLen = v +} + +func (h *Header) GetPayloadHash() *refs.Checksum { + if h != nil { + return h.payloadHash + } + + return nil +} + +func (h *Header) SetPayloadHash(v *refs.Checksum) { + h.payloadHash = v +} + +func (h *Header) GetObjectType() Type { + if h != nil { + return h.typ + } + + return TypeRegular +} + +func (h *Header) SetObjectType(v Type) { + h.typ = v +} + +func (h *Header) GetHomomorphicHash() *refs.Checksum { + if h != nil { + return h.homoHash + } + + return nil +} + +func (h *Header) SetHomomorphicHash(v *refs.Checksum) { + h.homoHash = v +} + +func (h *Header) GetSessionToken() *session.Token { + if h != nil { + return h.sessionToken + } + + return nil +} + +func (h *Header) SetSessionToken(v *session.Token) { + h.sessionToken = v +} + +func (h *Header) GetAttributes() []Attribute { + if h != nil { + return h.attr + } + + return nil +} + +func (h *Header) SetAttributes(v []Attribute) { + h.attr = v +} + +func (h *Header) GetSplit() *SplitHeader { + if h != nil { + return h.split + } + + return nil +} + +func (h *Header) SetSplit(v *SplitHeader) { + h.split = v +} + +func (h *Header) GetEC() *ECHeader { + if h != nil { + return h.ec + } + return nil +} + +func (h *Header) SetEC(v *ECHeader) { + h.ec = v +} + +func (h *HeaderWithSignature) GetHeader() *Header { + if h != nil { + return h.header + } + + return nil +} + +func (h *HeaderWithSignature) SetHeader(v *Header) { + h.header = v +} + +func (h *HeaderWithSignature) GetSignature() *refs.Signature { + if h != nil { + return h.signature + } + + return nil +} + +func (h *HeaderWithSignature) SetSignature(v *refs.Signature) { + h.signature = v +} + +func (h *HeaderWithSignature) getHeaderPart() {} + +func (o *Object) GetObjectID() *refs.ObjectID { + if o != nil { + return o.objectID + } + + return nil +} + +func (o *Object) SetObjectID(v *refs.ObjectID) { + o.objectID = v +} + +func (o *Object) GetSignature() *refs.Signature { + if o != nil { + return o.idSig + } + + return nil +} + +func (o *Object) SetSignature(v *refs.Signature) { + o.idSig = v +} + +func (o *Object) GetHeader() *Header { + if o != nil { + return o.header + } + + return nil +} + +func (o *Object) SetHeader(v *Header) { + o.header = v +} + +func (o *Object) GetPayload() []byte { + if o != nil { + return o.payload + } + + return nil +} + +func (o *Object) SetPayload(v []byte) { + o.payload = v +} + +func (s *SplitInfo) GetSplitID() []byte { + if s != nil { + return s.splitID + } + + return nil +} + +func (s *SplitInfo) SetSplitID(v []byte) { + s.splitID = v +} + +func (s *SplitInfo) GetLastPart() *refs.ObjectID { + if s != nil { + return s.lastPart + } + + return nil +} + +func (s *SplitInfo) SetLastPart(v *refs.ObjectID) { + s.lastPart = v +} + +func (s *SplitInfo) GetLink() *refs.ObjectID { + if s != nil { + return s.link + } + + return nil +} + +func (s *SplitInfo) SetLink(v *refs.ObjectID) { + s.link = v +} + +func (s *SplitInfo) getObjectPart() {} + +func (s *SplitInfo) getHeaderPart() {} + +func (s *SplitInfo) getRangePart() {} + +func (r *GetRequestBody) GetAddress() *refs.Address { + if r != nil { + return r.addr + } + + return nil +} + +func (r *GetRequestBody) SetAddress(v *refs.Address) { + r.addr = v +} + +func (r *GetRequestBody) GetRaw() bool { + if r != nil { + return r.raw + } + + return false +} + +func (r *GetRequestBody) SetRaw(v bool) { + r.raw = v +} + +func (r *GetRequest) GetBody() *GetRequestBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *GetRequest) SetBody(v *GetRequestBody) { + r.body = v +} + +func (r *GetObjectPartInit) GetObjectID() *refs.ObjectID { + if r != nil { + return r.id + } + + return nil +} + +func (r *GetObjectPartInit) SetObjectID(v *refs.ObjectID) { + r.id = v +} + +func (r *GetObjectPartInit) GetSignature() *refs.Signature { + if r != nil { + return r.sig + } + + return nil +} + +func (r *GetObjectPartInit) SetSignature(v *refs.Signature) { + r.sig = v +} + +func (r *GetObjectPartInit) GetHeader() *Header { + if r != nil { + return r.hdr + } + + return nil +} + +func (r *GetObjectPartInit) SetHeader(v *Header) { + r.hdr = v +} + +func (r *GetObjectPartInit) getObjectPart() {} + +func (r *GetObjectPartChunk) GetChunk() []byte { + if r != nil { + return r.chunk + } + + return nil +} + +func (r *GetObjectPartChunk) SetChunk(v []byte) { + r.chunk = v +} + +func (r *GetObjectPartChunk) getObjectPart() {} + +func (r *GetResponseBody) GetObjectPart() GetObjectPart { + if r != nil { + return r.objPart + } + + return nil +} + +func (r *GetResponseBody) SetObjectPart(v GetObjectPart) { + r.objPart = v +} + +func (r *GetResponse) GetBody() *GetResponseBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *GetResponse) SetBody(v *GetResponseBody) { + r.body = v +} + +func (r *PutObjectPartInit) GetObjectID() *refs.ObjectID { + if r != nil { + return r.id + } + + return nil +} + +func (r *PutObjectPartInit) SetObjectID(v *refs.ObjectID) { + r.id = v +} + +func (r *PutObjectPartInit) GetSignature() *refs.Signature { + if r != nil { + return r.sig + } + + return nil +} + +func (r *PutObjectPartInit) SetSignature(v *refs.Signature) { + r.sig = v +} + +func (r *PutObjectPartInit) GetHeader() *Header { + if r != nil { + return r.hdr + } + + return nil +} + +func (r *PutObjectPartInit) SetHeader(v *Header) { + r.hdr = v +} + +func (r *PutObjectPartInit) GetCopiesNumber() []uint32 { + if r != nil { + return r.copyNum + } + + return nil +} + +func (r *PutObjectPartInit) SetCopiesNumber(v []uint32) { + r.copyNum = v +} + +func (r *PutObjectPartInit) putObjectPart() {} + +func (r *PutObjectPartChunk) GetChunk() []byte { + if r != nil { + return r.chunk + } + + return nil +} + +func (r *PutObjectPartChunk) SetChunk(v []byte) { + r.chunk = v +} + +func (r *PutObjectPartChunk) putObjectPart() {} + +func (r *PutRequestBody) GetObjectPart() PutObjectPart { + if r != nil { + return r.objPart + } + + return nil +} + +func (r *PutRequestBody) SetObjectPart(v PutObjectPart) { + r.objPart = v +} + +func (r *PutRequest) GetBody() *PutRequestBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *PutRequest) SetBody(v *PutRequestBody) { + r.body = v +} + +func (r *PutResponseBody) GetObjectID() *refs.ObjectID { + if r != nil { + return r.id + } + + return nil +} + +func (r *PutResponseBody) SetObjectID(v *refs.ObjectID) { + r.id = v +} + +func (r *PutResponse) GetBody() *PutResponseBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *PutResponse) SetBody(v *PutResponseBody) { + r.body = v +} + +func (r *DeleteRequestBody) GetAddress() *refs.Address { + if r != nil { + return r.addr + } + + return nil +} + +func (r *DeleteRequestBody) SetAddress(v *refs.Address) { + r.addr = v +} + +func (r *DeleteRequest) GetBody() *DeleteRequestBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *DeleteRequest) SetBody(v *DeleteRequestBody) { + r.body = v +} + +// GetTombstone returns tombstone address. +func (r *DeleteResponseBody) GetTombstone() *refs.Address { + if r != nil { + return r.tombstone + } + + return nil +} + +// SetTombstone sets tombstone address. +func (r *DeleteResponseBody) SetTombstone(v *refs.Address) { + r.tombstone = v +} + +func (r *DeleteResponse) GetBody() *DeleteResponseBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *DeleteResponse) SetBody(v *DeleteResponseBody) { + r.body = v +} + +func (r *HeadRequestBody) GetAddress() *refs.Address { + if r != nil { + return r.addr + } + + return nil +} + +func (r *HeadRequestBody) SetAddress(v *refs.Address) { + r.addr = v +} + +func (r *HeadRequestBody) GetMainOnly() bool { + if r != nil { + return r.mainOnly + } + + return false +} + +func (r *HeadRequestBody) SetMainOnly(v bool) { + r.mainOnly = v +} + +func (r *HeadRequestBody) GetRaw() bool { + if r != nil { + return r.raw + } + + return false +} + +func (r *HeadRequestBody) SetRaw(v bool) { + r.raw = v +} + +func (r *HeadRequest) GetBody() *HeadRequestBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *HeadRequest) SetBody(v *HeadRequestBody) { + r.body = v +} + +func (r *HeadResponseBody) GetHeaderPart() GetHeaderPart { + if r != nil { + return r.hdrPart + } + + return nil +} + +func (r *HeadResponseBody) SetHeaderPart(v GetHeaderPart) { + r.hdrPart = v +} + +func (r *HeadResponse) GetBody() *HeadResponseBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *HeadResponse) SetBody(v *HeadResponseBody) { + r.body = v +} + +func (f *SearchFilter) GetMatchType() MatchType { + if f != nil { + return f.matchType + } + + return MatchUnknown +} + +func (f *SearchFilter) SetMatchType(v MatchType) { + f.matchType = v +} + +func (f *SearchFilter) GetKey() string { + if f != nil { + return f.key + } + + return "" +} + +func (f *SearchFilter) SetKey(v string) { + f.key = v +} + +func (f *SearchFilter) GetValue() string { + if f != nil { + return f.val + } + + return "" +} + +func (f *SearchFilter) SetValue(v string) { + f.val = v +} + +func (r *SearchRequestBody) GetContainerID() *refs.ContainerID { + if r != nil { + return r.cid + } + + return nil +} + +func (r *SearchRequestBody) SetContainerID(v *refs.ContainerID) { + r.cid = v +} + +func (r *SearchRequestBody) GetVersion() uint32 { + if r != nil { + return r.version + } + + return 0 +} + +func (r *SearchRequestBody) SetVersion(v uint32) { + r.version = v +} + +func (r *SearchRequestBody) GetFilters() []SearchFilter { + if r != nil { + return r.filters + } + + return nil +} + +func (r *SearchRequestBody) SetFilters(v []SearchFilter) { + r.filters = v +} + +func (r *SearchRequest) GetBody() *SearchRequestBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *SearchRequest) SetBody(v *SearchRequestBody) { + r.body = v +} + +func (r *SearchResponseBody) GetIDList() []refs.ObjectID { + if r != nil { + return r.idList + } + + return nil +} + +func (r *SearchResponseBody) SetIDList(v []refs.ObjectID) { + r.idList = v +} + +func (r *SearchResponse) GetBody() *SearchResponseBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *SearchResponse) SetBody(v *SearchResponseBody) { + r.body = v +} + +func (r *Range) GetOffset() uint64 { + if r != nil { + return r.off + } + + return 0 +} + +func (r *Range) SetOffset(v uint64) { + r.off = v +} + +func (r *Range) GetLength() uint64 { + if r != nil { + return r.len + } + + return 0 +} + +func (r *Range) SetLength(v uint64) { + r.len = v +} + +func (r *GetRangeRequestBody) GetAddress() *refs.Address { + if r != nil { + return r.addr + } + + return nil +} + +func (r *GetRangeRequestBody) SetAddress(v *refs.Address) { + r.addr = v +} + +func (r *GetRangeRequestBody) GetRange() *Range { + if r != nil { + return r.rng + } + + return nil +} + +func (r *GetRangeRequestBody) SetRange(v *Range) { + r.rng = v +} + +func (r *GetRangeRequestBody) GetRaw() bool { + if r != nil { + return r.raw + } + + return false +} + +func (r *GetRangeRequestBody) SetRaw(v bool) { + r.raw = v +} + +func (r *GetRangeRequest) GetBody() *GetRangeRequestBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *GetRangeRequest) SetBody(v *GetRangeRequestBody) { + r.body = v +} + +func (r *GetRangePartChunk) GetChunk() []byte { + if r != nil { + return r.chunk + } + + return nil +} + +func (r *GetRangePartChunk) SetChunk(v []byte) { + r.chunk = v +} + +func (r *GetRangePartChunk) getRangePart() {} + +func (r *GetRangeResponseBody) GetRangePart() GetRangePart { + if r != nil { + return r.rngPart + } + + return nil +} + +func (r *GetRangeResponseBody) SetRangePart(v GetRangePart) { + r.rngPart = v +} + +func (r *GetRangeResponse) GetBody() *GetRangeResponseBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *GetRangeResponse) SetBody(v *GetRangeResponseBody) { + r.body = v +} + +func (r *GetRangeHashRequestBody) GetAddress() *refs.Address { + if r != nil { + return r.addr + } + + return nil +} + +func (r *GetRangeHashRequestBody) SetAddress(v *refs.Address) { + r.addr = v +} + +func (r *GetRangeHashRequestBody) GetRanges() []Range { + if r != nil { + return r.rngs + } + + return nil +} + +func (r *GetRangeHashRequestBody) SetRanges(v []Range) { + r.rngs = v +} + +func (r *GetRangeHashRequestBody) GetSalt() []byte { + if r != nil { + return r.salt + } + + return nil +} + +func (r *GetRangeHashRequestBody) SetSalt(v []byte) { + r.salt = v +} + +func (r *GetRangeHashRequestBody) GetType() refs.ChecksumType { + if r != nil { + return r.typ + } + + return refs.UnknownChecksum +} + +func (r *GetRangeHashRequestBody) SetType(v refs.ChecksumType) { + r.typ = v +} + +func (r *GetRangeHashRequest) GetBody() *GetRangeHashRequestBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *GetRangeHashRequest) SetBody(v *GetRangeHashRequestBody) { + r.body = v +} + +func (r *GetRangeHashResponseBody) GetType() refs.ChecksumType { + if r != nil { + return r.typ + } + + return refs.UnknownChecksum +} + +func (r *GetRangeHashResponseBody) SetType(v refs.ChecksumType) { + r.typ = v +} + +func (r *GetRangeHashResponseBody) GetHashList() [][]byte { + if r != nil { + return r.hashList + } + + return nil +} + +func (r *GetRangeHashResponseBody) SetHashList(v [][]byte) { + r.hashList = v +} + +func (r *GetRangeHashResponse) GetBody() *GetRangeHashResponseBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *GetRangeHashResponse) SetBody(v *GetRangeHashResponseBody) { + r.body = v +} + +func (r *PutSingleRequest) GetBody() *PutSingleRequestBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *PutSingleRequest) SetBody(v *PutSingleRequestBody) { + r.body = v +} + +func (b *PutSingleRequestBody) GetObject() *Object { + if b == nil { + return nil + } + return b.object +} + +func (b *PutSingleRequestBody) SetObject(o *Object) { + b.object = o +} + +func (b *PutSingleRequestBody) GetCopiesNumber() []uint32 { + if b == nil { + return nil + } + return b.copyNum +} + +func (b *PutSingleRequestBody) SetCopiesNumber(v []uint32) { + b.copyNum = v +} + +func (r *PutSingleResponse) GetBody() *PutSingleResponseBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *PutSingleResponse) SetBody(v *PutSingleResponseBody) { + r.body = v +} + +func (r *PatchRequest) GetBody() *PatchRequestBody { + if r != nil { + return r.body + } + + return nil +} + +func (r *PatchRequest) SetBody(v *PatchRequestBody) { + r.body = v +} + +func (r *PatchRequestBody) GetAddress() *refs.Address { + if r != nil { + return r.address + } + + return nil +} + +func (r *PatchRequestBody) SetAddress(addr *refs.Address) { + r.address = addr +} + +func (r *PatchRequestBody) GetNewAttributes() []Attribute { + if r != nil { + return r.newAttributes + } + + return nil +} + +func (r *PatchRequestBody) SetNewAttributes(attrs []Attribute) { + r.newAttributes = attrs +} + +func (r *PatchRequestBody) GetReplaceAttributes() bool { + if r != nil { + return r.replaceAttributes + } + + return false +} + +func (r *PatchRequestBody) SetReplaceAttributes(replace bool) { + r.replaceAttributes = replace +} + +func (r *PatchRequestBody) GetPatch() *PatchRequestBodyPatch { + if r != nil { + return r.patch + } + + return nil +} + +func (r *PatchRequestBody) SetPatch(patch *PatchRequestBodyPatch) { + r.patch = patch +} + +func (r *PatchResponse) GetBody() *PatchResponseBody { + if r != nil { + return r.Body + } + + return nil +} + +func (r *PatchResponse) SetBody(v *PatchResponseBody) { + r.Body = v +} + +func (r *PatchResponseBody) GetObjectID() *refs.ObjectID { + if r != nil { + return r.ObjectID + } + + return nil +} + +func (r *PatchResponseBody) SetObjectID(objectID *refs.ObjectID) { + r.ObjectID = objectID +} + +func (r *PatchRequestBodyPatch) GetChunk() []byte { + if r != nil { + return r.Chunk + } + + return nil +} + +func (r *PatchRequestBodyPatch) GetRange() *Range { + if r != nil { + return r.Range + } + + return nil +} + +func (s *ECInfo) getObjectPart() {} + +func (s *ECInfo) getHeaderPart() {} + +func (s *ECInfo) getRangePart() {} diff --git a/apiv2/refs/bench_test.go b/apiv2/refs/bench_test.go new file mode 100644 index 0000000..40784c6 --- /dev/null +++ b/apiv2/refs/bench_test.go @@ -0,0 +1,53 @@ +package refs + +import ( + "math/rand" + "strconv" + "testing" +) + +func BenchmarkObjectIDSlice(b *testing.B) { + for _, size := range []int{0, 1, 50} { + b.Run(strconv.Itoa(size)+" elements", func(b *testing.B) { + benchmarkObjectIDSlice(b, size) + }) + } +} + +func benchmarkObjectIDSlice(b *testing.B, size int) { + ids := make([]ObjectID, size) + for i := range ids { + ids[i].val = make([]byte, 32) + rand.Read(ids[i].val) + } + raw := ObjectIDListToGRPCMessage(ids) + + b.Run("to grpc message", func(b *testing.B) { + b.ReportAllocs() + for range b.N { + raw := ObjectIDListToGRPCMessage(ids) + if len(raw) != len(ids) { + b.FailNow() + } + } + }) + b.Run("from grpc message", func(b *testing.B) { + b.ReportAllocs() + for range b.N { + ids, err := ObjectIDListFromGRPCMessage(raw) + if err != nil || len(raw) != len(ids) { + b.FailNow() + } + } + }) + b.Run("marshal", func(b *testing.B) { + b.ReportAllocs() + for range b.N { + buf := make([]byte, ObjectIDNestedListSize(1, ids)) + n := ObjectIDNestedListMarshal(1, buf, ids) + if n != len(buf) { + b.FailNow() + } + } + }) +} diff --git a/apiv2/refs/convert.go b/apiv2/refs/convert.go new file mode 100644 index 0000000..0ae66a8 --- /dev/null +++ b/apiv2/refs/convert.go @@ -0,0 +1,264 @@ +package refs + +import ( + refs "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +func (o *OwnerID) ToGRPCMessage() grpc.Message { + var m *refs.OwnerID + + if o != nil { + m = new(refs.OwnerID) + + m.SetValue(o.val) + } + + return m +} + +func (o *OwnerID) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*refs.OwnerID) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + o.val = v.GetValue() + + return nil +} + +func (c *ContainerID) ToGRPCMessage() grpc.Message { + var m *refs.ContainerID + + if c != nil { + m = new(refs.ContainerID) + + m.SetValue(c.val) + } + + return m +} + +func (c *ContainerID) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*refs.ContainerID) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + c.val = v.GetValue() + + return nil +} + +func ContainerIDsToGRPCMessage(ids []ContainerID) (res []refs.ContainerID) { + if ids != nil { + res = make([]refs.ContainerID, 0, len(ids)) + + for i := range ids { + res = append(res, *ids[i].ToGRPCMessage().(*refs.ContainerID)) + } + } + + return +} + +func ContainerIDsFromGRPCMessage(idsV2 []refs.ContainerID) (res []ContainerID, err error) { + if idsV2 != nil { + res = make([]ContainerID, len(idsV2)) + + for i := range idsV2 { + err = res[i].FromGRPCMessage(&idsV2[i]) + if err != nil { + return + } + } + } + + return +} + +func (o *ObjectID) ToGRPCMessage() grpc.Message { + var m *refs.ObjectID + + if o != nil { + m = new(refs.ObjectID) + + m.SetValue(o.val) + } + + return m +} + +func (o *ObjectID) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*refs.ObjectID) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + o.val = v.GetValue() + + return nil +} + +func ObjectIDListToGRPCMessage(ids []ObjectID) (res []refs.ObjectID) { + if ids != nil { + res = make([]refs.ObjectID, 0, len(ids)) + + for i := range ids { + res = append(res, *ids[i].ToGRPCMessage().(*refs.ObjectID)) + } + } + + return +} + +func ObjectIDListFromGRPCMessage(idsV2 []refs.ObjectID) (res []ObjectID, err error) { + if idsV2 != nil { + res = make([]ObjectID, len(idsV2)) + + for i := range idsV2 { + err = res[i].FromGRPCMessage(&idsV2[i]) + if err != nil { + return + } + } + } + + return +} + +func (a *Address) ToGRPCMessage() grpc.Message { + var m *refs.Address + + if a != nil { + m = new(refs.Address) + + m.SetContainerId(a.cid.ToGRPCMessage().(*refs.ContainerID)) + m.SetObjectId(a.oid.ToGRPCMessage().(*refs.ObjectID)) + } + + return m +} + +func (a *Address) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*refs.Address) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + cid := v.GetContainerId() + if cid == nil { + a.cid = nil + } else { + if a.cid == nil { + a.cid = new(ContainerID) + } + + err = a.cid.FromGRPCMessage(cid) + if err != nil { + return err + } + } + + oid := v.GetObjectId() + if oid == nil { + a.oid = nil + } else { + if a.oid == nil { + a.oid = new(ObjectID) + } + + err = a.oid.FromGRPCMessage(oid) + } + + return err +} + +func ChecksumTypeToGRPC(t ChecksumType) refs.ChecksumType { + return refs.ChecksumType(t) +} + +func ChecksumTypeFromGRPC(t refs.ChecksumType) ChecksumType { + return ChecksumType(t) +} + +func (c *Checksum) ToGRPCMessage() grpc.Message { + var m *refs.Checksum + + if c != nil { + m = new(refs.Checksum) + + m.SetType(ChecksumTypeToGRPC(c.typ)) + m.SetSum(c.sum) + } + + return m +} + +func (c *Checksum) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*refs.Checksum) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + c.typ = ChecksumTypeFromGRPC(v.GetType()) + c.sum = v.GetSum() + + return nil +} + +func (v *Version) ToGRPCMessage() grpc.Message { + var m *refs.Version + + if v != nil { + m = new(refs.Version) + + m.SetMajor(v.major) + m.SetMinor(v.minor) + } + + return m +} + +func (v *Version) FromGRPCMessage(m grpc.Message) error { + ver, ok := m.(*refs.Version) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + v.major = ver.GetMajor() + v.minor = ver.GetMinor() + + return nil +} + +func (s *Signature) ToGRPCMessage() grpc.Message { + var m *refs.Signature + + if s != nil { + m = new(refs.Signature) + + m.SetKey(s.key) + m.SetSign(s.sign) + m.SetScheme(refs.SignatureScheme(s.scheme)) + } + + return m +} + +func (s *Signature) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*refs.Signature) + if !ok { + return message.NewUnexpectedMessageType(m, s) + } + + s.key = v.GetKey() + s.sign = v.GetSign() + s.scheme = SignatureScheme(v.GetScheme()) + + return nil +} diff --git a/apiv2/refs/grpc/types_frostfs.pb.go b/apiv2/refs/grpc/types_frostfs.pb.go new file mode 100644 index 0000000..94349af Binary files /dev/null and b/apiv2/refs/grpc/types_frostfs.pb.go differ diff --git a/apiv2/refs/grpc/types_frostfs_fuzz.go b/apiv2/refs/grpc/types_frostfs_fuzz.go new file mode 100644 index 0000000..a64a9bf --- /dev/null +++ b/apiv2/refs/grpc/types_frostfs_fuzz.go @@ -0,0 +1,159 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package refs + +func DoFuzzProtoAddress(data []byte) int { + msg := new(Address) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONAddress(data []byte) int { + msg := new(Address) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoObjectID(data []byte) int { + msg := new(ObjectID) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONObjectID(data []byte) int { + msg := new(ObjectID) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoContainerID(data []byte) int { + msg := new(ContainerID) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONContainerID(data []byte) int { + msg := new(ContainerID) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoOwnerID(data []byte) int { + msg := new(OwnerID) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONOwnerID(data []byte) int { + msg := new(OwnerID) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoVersion(data []byte) int { + msg := new(Version) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONVersion(data []byte) int { + msg := new(Version) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoSignature(data []byte) int { + msg := new(Signature) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONSignature(data []byte) int { + msg := new(Signature) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoSignatureRFC6979(data []byte) int { + msg := new(SignatureRFC6979) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONSignatureRFC6979(data []byte) int { + msg := new(SignatureRFC6979) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoChecksum(data []byte) int { + msg := new(Checksum) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONChecksum(data []byte) int { + msg := new(Checksum) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/refs/grpc/types_frostfs_test.go b/apiv2/refs/grpc/types_frostfs_test.go new file mode 100644 index 0000000..9b19892 --- /dev/null +++ b/apiv2/refs/grpc/types_frostfs_test.go @@ -0,0 +1,91 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package refs + +import ( + testing "testing" +) + +func FuzzProtoAddress(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoAddress(data) + }) +} +func FuzzJSONAddress(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONAddress(data) + }) +} +func FuzzProtoObjectID(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoObjectID(data) + }) +} +func FuzzJSONObjectID(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONObjectID(data) + }) +} +func FuzzProtoContainerID(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoContainerID(data) + }) +} +func FuzzJSONContainerID(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONContainerID(data) + }) +} +func FuzzProtoOwnerID(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoOwnerID(data) + }) +} +func FuzzJSONOwnerID(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONOwnerID(data) + }) +} +func FuzzProtoVersion(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoVersion(data) + }) +} +func FuzzJSONVersion(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONVersion(data) + }) +} +func FuzzProtoSignature(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoSignature(data) + }) +} +func FuzzJSONSignature(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONSignature(data) + }) +} +func FuzzProtoSignatureRFC6979(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoSignatureRFC6979(data) + }) +} +func FuzzJSONSignatureRFC6979(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONSignatureRFC6979(data) + }) +} +func FuzzProtoChecksum(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoChecksum(data) + }) +} +func FuzzJSONChecksum(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONChecksum(data) + }) +} diff --git a/apiv2/refs/json.go b/apiv2/refs/json.go new file mode 100644 index 0000000..15c1f63 --- /dev/null +++ b/apiv2/refs/json.go @@ -0,0 +1,62 @@ +package refs + +import ( + refs "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +func (a *Address) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(a) +} + +func (a *Address) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(a, data, new(refs.Address)) +} + +func (o *ObjectID) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(o) +} + +func (o *ObjectID) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(o, data, new(refs.ObjectID)) +} + +func (c *ContainerID) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(c) +} + +func (c *ContainerID) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(c, data, new(refs.ContainerID)) +} + +func (o *OwnerID) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(o) +} + +func (o *OwnerID) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(o, data, new(refs.OwnerID)) +} + +func (v *Version) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(v) +} + +func (v *Version) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(v, data, new(refs.Version)) +} + +func (s *Signature) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(s) +} + +func (s *Signature) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(s, data, new(refs.Signature)) +} + +func (c *Checksum) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(c) +} + +func (c *Checksum) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(c, data, new(refs.Checksum)) +} diff --git a/apiv2/refs/marshal.go b/apiv2/refs/marshal.go new file mode 100644 index 0000000..70af7fd --- /dev/null +++ b/apiv2/refs/marshal.go @@ -0,0 +1,264 @@ +package refs + +import ( + "encoding/binary" + + refs "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto" + "google.golang.org/protobuf/encoding/protowire" +) + +const ( + ownerIDValField = 1 + + containerIDValField = 1 + + objectIDValField = 1 + + addressContainerField = 1 + addressObjectField = 2 + + checksumTypeField = 1 + checksumValueField = 2 + + signatureKeyField = 1 + signatureValueField = 2 + signatureSchemeField = 3 + + versionMajorField = 1 + versionMinorField = 2 +) + +func (o *OwnerID) StableMarshal(buf []byte) []byte { + if o == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, o.StableSize()) + } + + proto.BytesMarshal(ownerIDValField, buf, o.val) + + return buf +} + +func (o *OwnerID) StableSize() int { + if o == nil { + return 0 + } + + return proto.BytesSize(ownerIDValField, o.val) +} + +func (o *OwnerID) Unmarshal(data []byte) error { + return message.Unmarshal(o, data, new(refs.OwnerID)) +} + +func (c *ContainerID) StableMarshal(buf []byte) []byte { + if c == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, c.StableSize()) + } + + proto.BytesMarshal(containerIDValField, buf, c.val) + + return buf +} + +func (c *ContainerID) StableSize() int { + if c == nil { + return 0 + } + + return proto.BytesSize(containerIDValField, c.val) +} + +func (c *ContainerID) Unmarshal(data []byte) error { + return message.Unmarshal(c, data, new(refs.ContainerID)) +} + +func (o *ObjectID) StableMarshal(buf []byte) []byte { + if o == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, o.StableSize()) + } + + proto.BytesMarshal(objectIDValField, buf, o.val) + + return buf +} + +// ObjectIDNestedListSize returns byte length of nested +// repeated ObjectID field with fNum number. +func ObjectIDNestedListSize(fNum int64, ids []ObjectID) (sz int) { + for i := range ids { + sz += proto.NestedStructureSize(fNum, &ids[i]) + } + + return +} + +func (o *ObjectID) StableSize() int { + if o == nil { + return 0 + } + + return proto.BytesSize(objectIDValField, o.val) +} + +// ObjectIDNestedListMarshal writes protobuf repeated ObjectID field +// with fNum number to buf. +func ObjectIDNestedListMarshal(fNum int64, buf []byte, ids []ObjectID) (off int) { + prefix := protowire.EncodeTag(protowire.Number(fNum), protowire.BytesType) + for i := range ids { + off += binary.PutUvarint(buf[off:], prefix) + + n := ids[i].StableSize() + off += binary.PutUvarint(buf[off:], uint64(n)) + off += proto.BytesMarshal(objectIDValField, buf[off:], ids[i].val) + } + + return +} + +func (o *ObjectID) Unmarshal(data []byte) error { + return message.Unmarshal(o, data, new(refs.ObjectID)) +} + +func (a *Address) StableMarshal(buf []byte) []byte { + if a == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, a.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(addressContainerField, buf[offset:], a.cid) + proto.NestedStructureMarshal(addressObjectField, buf[offset:], a.oid) + + return buf +} + +func (a *Address) StableSize() (size int) { + if a == nil { + return 0 + } + + size += proto.NestedStructureSize(addressContainerField, a.cid) + size += proto.NestedStructureSize(addressObjectField, a.oid) + + return size +} + +func (a *Address) Unmarshal(data []byte) error { + return message.Unmarshal(a, data, new(refs.Address)) +} + +func (c *Checksum) StableMarshal(buf []byte) []byte { + if c == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, c.StableSize()) + } + + var offset int + + offset += proto.EnumMarshal(checksumTypeField, buf[offset:], int32(c.typ)) + proto.BytesMarshal(checksumValueField, buf[offset:], c.sum) + + return buf +} + +func (c *Checksum) StableSize() (size int) { + if c == nil { + return 0 + } + + size += proto.EnumSize(checksumTypeField, int32(c.typ)) + size += proto.BytesSize(checksumValueField, c.sum) + + return size +} + +func (c *Checksum) Unmarshal(data []byte) error { + return message.Unmarshal(c, data, new(refs.Checksum)) +} + +func (s *Signature) StableMarshal(buf []byte) []byte { + if s == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, s.StableSize()) + } + + var offset int + + offset += proto.BytesMarshal(signatureKeyField, buf[offset:], s.key) + offset += proto.BytesMarshal(signatureValueField, buf[offset:], s.sign) + proto.EnumMarshal(signatureSchemeField, buf[offset:], int32(s.scheme)) + + return buf +} + +func (s *Signature) StableSize() (size int) { + if s == nil { + return 0 + } + + size += proto.BytesSize(signatureKeyField, s.key) + size += proto.BytesSize(signatureValueField, s.sign) + size += proto.EnumSize(signatureSchemeField, int32(s.scheme)) + + return size +} + +func (s *Signature) Unmarshal(data []byte) error { + return message.Unmarshal(s, data, new(refs.Signature)) +} + +func (v *Version) StableMarshal(buf []byte) []byte { + if v == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, v.StableSize()) + } + + var offset int + + offset += proto.UInt32Marshal(versionMajorField, buf[offset:], v.major) + proto.UInt32Marshal(versionMinorField, buf[offset:], v.minor) + + return buf +} + +func (v *Version) StableSize() (size int) { + if v == nil { + return 0 + } + + size += proto.UInt32Size(versionMajorField, v.major) + size += proto.UInt32Size(versionMinorField, v.minor) + + return size +} + +func (v *Version) Unmarshal(data []byte) error { + return message.Unmarshal(v, data, new(refs.Version)) +} diff --git a/apiv2/refs/message_test.go b/apiv2/refs/message_test.go new file mode 100644 index 0000000..4d70cd8 --- /dev/null +++ b/apiv2/refs/message_test.go @@ -0,0 +1,21 @@ +package refs_test + +import ( + "testing" + + refstest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/test" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + messagetest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message/test" +) + +func TestMessageConvert(t *testing.T) { + messagetest.TestRPCMessage(t, + func(empty bool) message.Message { return refstest.GenerateOwnerID(empty) }, + func(empty bool) message.Message { return refstest.GenerateObjectID(empty) }, + func(empty bool) message.Message { return refstest.GenerateContainerID(empty) }, + func(empty bool) message.Message { return refstest.GenerateAddress(empty) }, + func(empty bool) message.Message { return refstest.GenerateChecksum(empty) }, + func(empty bool) message.Message { return refstest.GenerateSignature(empty) }, + func(empty bool) message.Message { return refstest.GenerateVersion(empty) }, + ) +} diff --git a/apiv2/refs/string.go b/apiv2/refs/string.go new file mode 100644 index 0000000..1675f3b --- /dev/null +++ b/apiv2/refs/string.go @@ -0,0 +1,47 @@ +package refs + +import ( + refs "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/grpc" +) + +// String returns string representation of ChecksumType. +func (t ChecksumType) String() string { + return ChecksumTypeToGRPC(t).String() +} + +// FromString parses ChecksumType from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (t *ChecksumType) FromString(s string) bool { + var g refs.ChecksumType + + ok := g.FromString(s) + + if ok { + *t = ChecksumTypeFromGRPC(g) + } + + return ok +} + +// String returns string representation of SignatureScheme. +func (t SignatureScheme) String() string { + return refs.SignatureScheme(t).String() +} + +// FromString parses SignatureScheme from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (t *SignatureScheme) FromString(s string) bool { + var g refs.SignatureScheme + + ok := g.FromString(s) + + if ok { + *t = SignatureScheme(g) + } + + return ok +} diff --git a/apiv2/refs/test/generate.go b/apiv2/refs/test/generate.go new file mode 100644 index 0000000..5e22e73 --- /dev/null +++ b/apiv2/refs/test/generate.go @@ -0,0 +1,127 @@ +package refstest + +import ( + crand "crypto/rand" + "crypto/sha256" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" +) + +func GenerateVersion(empty bool) *refs.Version { + m := new(refs.Version) + + if !empty { + m.SetMajor(2) + m.SetMinor(1) + } + + return m +} + +func GenerateOwnerID(empty bool) *refs.OwnerID { + m := new(refs.OwnerID) + + if !empty { + id := make([]byte, 25) + _, _ = crand.Read(id) + + m.SetValue(id) + } + + return m +} + +func GenerateAddress(empty bool) *refs.Address { + m := new(refs.Address) + + if !empty { + m.SetObjectID(GenerateObjectID(false)) + m.SetContainerID(GenerateContainerID(false)) + } + + return m +} + +func GenerateObjectID(empty bool) *refs.ObjectID { + m := new(refs.ObjectID) + + if !empty { + id := make([]byte, sha256.Size) + _, _ = crand.Read(id) + + m.SetValue(id) + } + + return m +} + +func GenerateObjectIDs(empty bool) []refs.ObjectID { + var ids []refs.ObjectID + + if !empty { + ids = append(ids, + *GenerateObjectID(false), + *GenerateObjectID(false), + ) + } + + return ids +} + +func GenerateContainerID(empty bool) *refs.ContainerID { + m := new(refs.ContainerID) + + if !empty { + id := make([]byte, sha256.Size) + _, _ = crand.Read(id) + + m.SetValue(id) + } + + return m +} + +func GenerateContainerIDs(empty bool) []refs.ContainerID { + var res []refs.ContainerID + + if !empty { + res = append(res, + *GenerateContainerID(false), + *GenerateContainerID(false), + ) + } + + return res +} + +func GenerateSignature(empty bool) *refs.Signature { + m := new(refs.Signature) + + if !empty { + key := make([]byte, 33) + _, _ = crand.Read(key) + + sign := make([]byte, 65) + _, _ = crand.Read(sign) + + m.SetScheme(refs.ECDSA_SHA512) + m.SetKey(key) + m.SetSign(sign) + } + + return m +} + +func GenerateChecksum(empty bool) *refs.Checksum { + m := new(refs.Checksum) + + if !empty { + cs := make([]byte, sha256.Size) + _, _ = crand.Read(cs) + + m.SetType(refs.SHA256) + m.SetSum(cs) + } + + return m +} diff --git a/apiv2/refs/types.go b/apiv2/refs/types.go new file mode 100644 index 0000000..d8f0d9b --- /dev/null +++ b/apiv2/refs/types.go @@ -0,0 +1,194 @@ +package refs + +type OwnerID struct { + val []byte +} + +type ContainerID struct { + val []byte +} + +type ObjectID struct { + val []byte +} + +type Address struct { + cid *ContainerID + + oid *ObjectID +} + +type Checksum struct { + typ ChecksumType + + sum []byte +} + +type ChecksumType uint32 + +type SignatureScheme uint32 + +//nolint:revive +const ( + ECDSA_SHA512 SignatureScheme = iota + ECDSA_RFC6979_SHA256 + ECDSA_RFC6979_SHA256_WALLET_CONNECT +) + +type Signature struct { + key, sign []byte + scheme SignatureScheme +} + +type Version struct { + major, minor uint32 +} + +const ( + UnknownChecksum ChecksumType = iota + TillichZemor + SHA256 +) + +func (o *OwnerID) GetValue() []byte { + if o != nil { + return o.val + } + + return nil +} + +func (o *OwnerID) SetValue(v []byte) { + o.val = v +} + +func (c *ContainerID) GetValue() []byte { + if c != nil { + return c.val + } + + return nil +} + +func (c *ContainerID) SetValue(v []byte) { + c.val = v +} + +func (o *ObjectID) GetValue() []byte { + if o != nil { + return o.val + } + + return nil +} + +func (o *ObjectID) SetValue(v []byte) { + o.val = v +} + +func (a *Address) GetContainerID() *ContainerID { + if a != nil { + return a.cid + } + + return nil +} + +func (a *Address) SetContainerID(v *ContainerID) { + a.cid = v +} + +func (a *Address) GetObjectID() *ObjectID { + if a != nil { + return a.oid + } + + return nil +} + +func (a *Address) SetObjectID(v *ObjectID) { + a.oid = v +} + +func (c *Checksum) GetType() ChecksumType { + if c != nil { + return c.typ + } + + return UnknownChecksum +} + +func (c *Checksum) SetType(v ChecksumType) { + c.typ = v +} + +func (c *Checksum) GetSum() []byte { + if c != nil { + return c.sum + } + + return nil +} + +func (c *Checksum) SetSum(v []byte) { + c.sum = v +} + +func (s *Signature) GetKey() []byte { + if s != nil { + return s.key + } + + return nil +} + +func (s *Signature) SetKey(v []byte) { + s.key = v +} + +func (s *Signature) GetSign() []byte { + if s != nil { + return s.sign + } + + return nil +} + +func (s *Signature) SetSign(v []byte) { + s.sign = v +} + +func (s *Signature) GetScheme() SignatureScheme { + if s != nil { + return s.scheme + } + return 0 +} + +func (s *Signature) SetScheme(scheme SignatureScheme) { + s.scheme = scheme +} + +func (v *Version) GetMajor() uint32 { + if v != nil { + return v.major + } + + return 0 +} + +func (v *Version) SetMajor(val uint32) { + v.major = val +} + +func (v *Version) GetMinor() uint32 { + if v != nil { + return v.minor + } + + return 0 +} + +func (v *Version) SetMinor(val uint32) { + v.minor = val +} diff --git a/apiv2/rpc/accounting.go b/apiv2/rpc/accounting.go new file mode 100644 index 0000000..7c7145c --- /dev/null +++ b/apiv2/rpc/accounting.go @@ -0,0 +1,29 @@ +package rpc + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/common" +) + +const serviceAccounting = serviceNamePrefix + "accounting.AccountingService" + +const ( + rpcAccountingBalance = "Balance" +) + +// Balance executes AccountingService.Balance RPC. +func Balance( + cli *client.Client, + req *accounting.BalanceRequest, + opts ...client.CallOption, +) (*accounting.BalanceResponse, error) { + resp := new(accounting.BalanceResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceAccounting, rpcAccountingBalance), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} diff --git a/apiv2/rpc/apemanager.go b/apiv2/rpc/apemanager.go new file mode 100644 index 0000000..8732cac --- /dev/null +++ b/apiv2/rpc/apemanager.go @@ -0,0 +1,60 @@ +package rpc + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/apemanager" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/common" +) + +const serviceAPEManager = frostfsServiceNamePrefix + "apemanager.APEManagerService" + +const ( + rpcAPEManagerAddChain = "AddChain" + rpcAPEManagerRemoveChain = "RemoveChain" + rpcAPEManagerListChains = "ListChains" +) + +func AddChain( + cli *client.Client, + req *apemanager.AddChainRequest, + opts ...client.CallOption, +) (*apemanager.AddChainResponse, error) { + resp := new(apemanager.AddChainResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceAPEManager, rpcAPEManagerAddChain), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} + +func RemoveChain( + cli *client.Client, + req *apemanager.RemoveChainRequest, + opts ...client.CallOption, +) (*apemanager.RemoveChainResponse, error) { + resp := new(apemanager.RemoveChainResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceAPEManager, rpcAPEManagerRemoveChain), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} + +func ListChains( + cli *client.Client, + req *apemanager.ListChainsRequest, + opts ...client.CallOption, +) (*apemanager.ListChainsResponse, error) { + resp := new(apemanager.ListChainsResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceAPEManager, rpcAPEManagerListChains), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} diff --git a/apiv2/rpc/client/call_options.go b/apiv2/rpc/client/call_options.go new file mode 100644 index 0000000..4fe8791 --- /dev/null +++ b/apiv2/rpc/client/call_options.go @@ -0,0 +1,40 @@ +package client + +import ( + "context" + + "google.golang.org/grpc" +) + +// CallOption is a messaging session option within Protobuf RPC. +type CallOption func(*callParameters) + +type callParameters struct { + ctx context.Context // nolint:containedctx + dialer func(context.Context, grpc.ClientConnInterface) error +} + +func defaultCallParameters() *callParameters { + return &callParameters{ + ctx: context.Background(), + } +} + +// WithContext returns option to specify call context. If provided, all network +// communications will be based on this context. Otherwise, context.Background() +// is used. +// +// Context SHOULD NOT be nil. +func WithContext(ctx context.Context) CallOption { + return func(prm *callParameters) { + prm.ctx = ctx + } +} + +// WithDialer returns option to specify grpc dialer. If passed, it will be +// called after the connection is successfully created. +func WithDialer(dialer func(context.Context, grpc.ClientConnInterface) error) CallOption { + return func(prm *callParameters) { + prm.dialer = dialer + } +} diff --git a/apiv2/rpc/client/client.go b/apiv2/rpc/client/client.go new file mode 100644 index 0000000..7ec56a1 --- /dev/null +++ b/apiv2/rpc/client/client.go @@ -0,0 +1,30 @@ +package client + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto/encoding" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +// Client represents client for exchanging messages +// with a remote server using Protobuf RPC. +type Client struct { + cfg +} + +// New creates, configures via options and returns new Client instance. +func New(opts ...Option) *Client { + var c Client + c.initDefault() + + for _, opt := range opts { + opt(&c.cfg) + } + + c.grpcDialOpts = append(c.grpcDialOpts, grpc.WithDefaultCallOptions(grpc.ForceCodec(encoding.ProtoCodec{}))) + if c.tlsCfg != nil { + c.grpcDialOpts = append(c.grpcDialOpts, grpc.WithTransportCredentials(credentials.NewTLS(c.tlsCfg))) + } + + return &c +} diff --git a/apiv2/rpc/client/conn.go b/apiv2/rpc/client/conn.go new file mode 100644 index 0000000..f208413 --- /dev/null +++ b/apiv2/rpc/client/conn.go @@ -0,0 +1,24 @@ +package client + +import ( + "io" + + "google.golang.org/grpc" +) + +// Conn is an interface for grpc client connection. +type Conn interface { + grpc.ClientConnInterface + io.Closer +} + +// Conn returns underlying connection. +// +// Returns non-nil result after the first Init() call +// completed without a connection error. +// +// Client should not be used after Close() call +// on the connection: behavior is undefined. +func (c *Client) Conn() io.Closer { + return c.conn +} diff --git a/apiv2/rpc/client/connect.go b/apiv2/rpc/client/connect.go new file mode 100644 index 0000000..e22e0a6 --- /dev/null +++ b/apiv2/rpc/client/connect.go @@ -0,0 +1,72 @@ +package client + +import ( + "context" + "errors" + "fmt" + "net" + "net/url" + + grpcstd "google.golang.org/grpc" +) + +var errInvalidEndpoint = errors.New("invalid endpoint options") + +func (c *Client) openGRPCConn(ctx context.Context, dialer func(ctx context.Context, cc grpcstd.ClientConnInterface) error) error { + if c.conn != nil { + return nil + } + + if c.addr == "" { + return errInvalidEndpoint + } + + var err error + + c.conn, err = grpcstd.NewClient(c.addr, c.grpcDialOpts...) + if err != nil { + return fmt.Errorf("gRPC new client: %w", err) + } + + if dialer != nil { + ctx, cancel := context.WithTimeout(ctx, c.dialTimeout) + defer cancel() + + if err := dialer(ctx, c.conn); err != nil { + _ = c.conn.Close() + return fmt.Errorf("gRPC dial: %w", err) + } + } + + return nil +} + +// ParseURI parses s as address and returns a host and a flag +// indicating that TLS is enabled. If multi-address is provided +// the argument is returned unchanged. +func ParseURI(s string) (string, bool, error) { + uri, err := url.ParseRequestURI(s) + if err != nil { + return s, false, nil + } + + // check if passed string was parsed correctly + // URIs that do not start with a slash after the scheme are interpreted as: + // `scheme:opaque` => if `opaque` is not empty, then it is supposed that URI + // is in `host:port` format + if uri.Host == "" { + uri.Host = uri.Scheme + uri.Scheme = grpcScheme // assume GRPC by default + if uri.Opaque != "" { + uri.Host = net.JoinHostPort(uri.Host, uri.Opaque) + } + } + + switch uri.Scheme { + case grpcTLSScheme, grpcScheme: + default: + return "", false, fmt.Errorf("unsupported scheme: %s", uri.Scheme) + } + + return uri.Host, uri.Scheme == grpcTLSScheme, nil +} diff --git a/apiv2/rpc/client/flows.go b/apiv2/rpc/client/flows.go new file mode 100644 index 0000000..1648ee0 --- /dev/null +++ b/apiv2/rpc/client/flows.go @@ -0,0 +1,124 @@ +package client + +import ( + "errors" + "io" + "sync" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/common" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +// SendUnary initializes communication session by RPC info, performs unary RPC +// and closes the session. +func SendUnary(cli *Client, info common.CallMethodInfo, req, resp message.Message, opts ...CallOption) error { + rw, err := cli.Init(info, opts...) + if err != nil { + return err + } + + err = rw.WriteMessage(req) + if err != nil { + return err + } + + err = rw.ReadMessage(resp) + if err != nil { + return err + } + + return rw.Close() +} + +// MessageWriterCloser wraps MessageWriter +// and io.Closer interfaces. +type MessageWriterCloser interface { + MessageWriter + io.Closer +} + +type clientStreamWriterCloser struct { + MessageReadWriter + + resp message.Message +} + +func (c *clientStreamWriterCloser) Close() error { + err := c.MessageReadWriter.Close() + if err != nil { + return err + } + + return c.ReadMessage(c.resp) +} + +// OpenClientStream initializes communication session by RPC info, opens client-side stream +// and returns its interface. +// +// All stream writes must be performed before the closing. Close must be called once. +func OpenClientStream(cli *Client, info common.CallMethodInfo, resp message.Message, opts ...CallOption) (MessageWriterCloser, error) { + rw, err := cli.Init(info, opts...) + if err != nil { + return nil, err + } + + return &clientStreamWriterCloser{ + MessageReadWriter: rw, + resp: resp, + }, nil +} + +// MessageReaderCloser wraps MessageReader +// and io.Closer interface. +type MessageReaderCloser interface { + MessageReader + io.Closer +} + +type serverStreamReaderCloser struct { + rw MessageReadWriter + + once sync.Once + + req message.Message +} + +func (s *serverStreamReaderCloser) ReadMessage(msg message.Message) error { + var err error + + s.once.Do(func() { + err = s.rw.WriteMessage(s.req) + }) + + if err != nil { + return err + } + + err = s.rw.ReadMessage(msg) + if !errors.Is(err, io.EOF) { + return err + } + + err = s.rw.Close() + if err != nil { + return err + } + + return io.EOF +} + +// OpenServerStream initializes communication session by RPC info, opens server-side stream +// and returns its interface. +// +// All stream reads must be performed before the closing. Close must be called once. +func OpenServerStream(cli *Client, info common.CallMethodInfo, req message.Message, opts ...CallOption) (MessageReader, error) { + rw, err := cli.Init(info, opts...) + if err != nil { + return nil, err + } + + return &serverStreamReaderCloser{ + rw: rw, + req: req, + }, nil +} diff --git a/apiv2/rpc/client/init.go b/apiv2/rpc/client/init.go new file mode 100644 index 0000000..b53b118 --- /dev/null +++ b/apiv2/rpc/client/init.go @@ -0,0 +1,69 @@ +package client + +import ( + "context" + "io" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/common" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + "google.golang.org/grpc" +) + +// MessageReader is an interface of the Message reader. +type MessageReader interface { + // ReadMessage reads the next Message. + // + // Returns io.EOF if there are no more messages to read. + // ReadMessage should not be called after io.EOF occasion. + ReadMessage(message.Message) error +} + +// MessageWriter is an interface of the Message writer. +type MessageWriter interface { + // WriteMessage writers the next Message. + // + // WriteMessage should not be called after any error. + WriteMessage(message.Message) error +} + +// MessageReadWriter is a component interface +// for transmitting raw Protobuf messages. +type MessageReadWriter interface { + MessageReader + MessageWriter + + // Closes the communication session. + // + // All calls to send/receive messages must be done before closing. + io.Closer +} + +// Init initiates a messaging session and returns the interface for message transmitting. +func (c *Client) Init(info common.CallMethodInfo, opts ...CallOption) (MessageReadWriter, error) { + prm := defaultCallParameters() + + for _, opt := range opts { + opt(prm) + } + + if err := c.openGRPCConn(prm.ctx, prm.dialer); err != nil { + return nil, err + } + + ctx, cancel := context.WithCancel(prm.ctx) + stream, err := c.conn.NewStream(ctx, &grpc.StreamDesc{ + StreamName: info.Name, + ServerStreams: info.ServerStream(), + ClientStreams: info.ClientStream(), + }, toMethodName(info)) + if err != nil { + cancel() + return nil, err + } + + return &streamWrapper{ + ClientStream: stream, + cancel: cancel, + timeout: c.rwTimeout, + }, nil +} diff --git a/apiv2/rpc/client/options.go b/apiv2/rpc/client/options.go new file mode 100644 index 0000000..5711cd4 --- /dev/null +++ b/apiv2/rpc/client/options.go @@ -0,0 +1,129 @@ +package client + +import ( + "crypto/tls" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +const ( + grpcScheme = "grpc" + grpcTLSScheme = "grpcs" +) + +// Option is a Client's option. +type Option func(*cfg) + +type cfg struct { + addr string + + dialTimeout time.Duration + rwTimeout time.Duration + + tlsCfg *tls.Config + grpcDialOpts []grpc.DialOption + + conn Conn +} + +const ( + defaultDialTimeout = 5 * time.Second + defaultRWTimeout = 1 * time.Minute +) + +func (c *cfg) initDefault() { + c.dialTimeout = defaultDialTimeout + c.rwTimeout = defaultRWTimeout + c.grpcDialOpts = []grpc.DialOption{ + grpc.WithTransportCredentials(insecure.NewCredentials()), + } +} + +// WithNetworkAddress returns option to specify +// network address of the remote server. +// +// Ignored if WithGRPCConn is provided. +func WithNetworkAddress(v string) Option { + return func(c *cfg) { + if v != "" { + c.addr = v + } + } +} + +// WithNetworkURIAddress combines WithNetworkAddress and WithTLSCfg options +// based on arguments. +// +// Do not use along with WithNetworkAddress and WithTLSCfg. +// +// Ignored if WithGRPCConn is provided. +func WithNetworkURIAddress(addr string, tlsCfg *tls.Config) []Option { + host, isTLS, err := ParseURI(addr) + if err != nil { + return nil + } + + opts := make([]Option, 2) + opts[0] = WithNetworkAddress(host) + if isTLS { + if tlsCfg == nil { + tlsCfg = &tls.Config{} + } + opts[1] = WithTLSCfg(tlsCfg) + } else { + opts[1] = WithTLSCfg(nil) + } + + return opts +} + +// WithDialTimeout returns option to specify +// dial timeout of the remote server connection. +// +// Ignored if WithGRPCConn is provided. +func WithDialTimeout(v time.Duration) Option { + return func(c *cfg) { + if v > 0 { + c.dialTimeout = v + } + } +} + +// WithRWTimeout returns option to specify timeout +// for reading and writing single gRPC message. +func WithRWTimeout(v time.Duration) Option { + return func(c *cfg) { + if v > 0 { + c.rwTimeout = v + } + } +} + +// WithTLSCfg returns option to specify +// TLS configuration. +// +// Ignored if WithGRPCConn is provided. +func WithTLSCfg(v *tls.Config) Option { + return func(c *cfg) { + c.tlsCfg = v + } +} + +// WithGRPCConn returns option to specify +// gRPC virtual connection. +func WithGRPCConn(v Conn) Option { + return func(c *cfg) { + if v != nil { + c.conn = v + } + } +} + +// WithGRPCDialOptions returns an option to specify grpc.DialOption. +func WithGRPCDialOptions(opts []grpc.DialOption) Option { + return func(c *cfg) { + c.grpcDialOpts = append(c.grpcDialOpts, opts...) + } +} diff --git a/apiv2/rpc/client/options_test.go b/apiv2/rpc/client/options_test.go new file mode 100644 index 0000000..56704b6 --- /dev/null +++ b/apiv2/rpc/client/options_test.go @@ -0,0 +1,197 @@ +package client + +import ( + "crypto/tls" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestWithNetworkURIAddress(t *testing.T) { + hostPort := "frostfs.example.com:8080" + apiPort := "127.0.0.1:8080" + serverName := "testServer" + + testCases := []struct { + uri string + tlsConfig *tls.Config + + wantHost string + wantTLS bool + }{ + { + uri: grpcScheme + "://" + hostPort, + tlsConfig: nil, + wantHost: "frostfs.example.com:8080", + wantTLS: false, + }, + { + uri: grpcScheme + "://" + hostPort, + tlsConfig: &tls.Config{}, + wantHost: "frostfs.example.com:8080", + wantTLS: false, + }, + { + uri: grpcTLSScheme + "://" + hostPort, + tlsConfig: nil, + wantHost: "frostfs.example.com:8080", + wantTLS: true, + }, + { + uri: grpcTLSScheme + "://" + hostPort, + tlsConfig: &tls.Config{ServerName: serverName}, + wantHost: "frostfs.example.com:8080", + wantTLS: true, + }, + { + uri: "wrongScheme://" + hostPort, + tlsConfig: nil, + wantHost: "", + wantTLS: false, + }, + { + uri: "impossibleToParseIt", + tlsConfig: nil, + wantHost: "impossibleToParseIt", + wantTLS: false, + }, + { + uri: hostPort, + tlsConfig: nil, + wantHost: hostPort, + wantTLS: false, + }, + { + uri: apiPort, + tlsConfig: nil, + wantHost: apiPort, + wantTLS: false, + }, + } + + for _, test := range testCases { + cfg := &cfg{} + opts := WithNetworkURIAddress(test.uri, test.tlsConfig) + + for _, opt := range opts { + opt(cfg) + } + + require.Equal(t, test.wantHost, cfg.addr, test.uri) + require.Equal(t, test.wantTLS, cfg.tlsCfg != nil, test.uri) + // check if custom tlsConfig was applied + if test.tlsConfig != nil && test.wantTLS { + require.Equal(t, test.tlsConfig.ServerName, cfg.tlsCfg.ServerName, test.uri) + } + } +} + +func Test_WithNetworkAddress_WithTLS_WithNetworkURIAddress(t *testing.T) { + addr1, addr2 := "example1.com:8080", "example2.com:8080" + + testCases := []struct { + addr string + withTLS bool + + uri string + + wantHost string + wantTLS bool + }{ + { + addr: addr1, + withTLS: true, + + uri: grpcScheme + "://" + addr2, + + wantHost: addr2, + wantTLS: false, + }, + { + addr: addr1, + withTLS: false, + + uri: grpcTLSScheme + "://" + addr2, + + wantHost: addr2, + wantTLS: true, + }, + } + + for _, test := range testCases { + // order: + // 1. WithNetworkAddress + // 2. WithTLSCfg(if test.withTLS == true) + // 3. WithNetworkURIAddress + config := &cfg{} + opts := []Option{WithNetworkAddress(test.addr)} + + if test.withTLS { + opts = append(opts, WithTLSCfg(&tls.Config{})) + } + + opts = append(opts, WithNetworkURIAddress(test.uri, nil)...) + + for _, opt := range opts { + opt(config) + } + + require.Equal(t, test.wantHost, config.addr, test.addr) + require.Equal(t, test.wantTLS, config.tlsCfg != nil, test.addr) + } +} + +func Test_WithNetworkURIAddress_WithTLS_WithNetworkAddress(t *testing.T) { + addr1, addr2 := "example1.com:8080", "example2.com:8080" + + testCases := []struct { + addr string + withTLS bool + + uri string + + wantHost string + wantTLS bool + }{ + { + uri: grpcScheme + "://" + addr1, + + addr: addr2, + withTLS: true, + + wantHost: addr2, + wantTLS: true, + }, + { + uri: grpcTLSScheme + "://" + addr1, + + addr: addr2, + withTLS: false, + + wantHost: addr2, + wantTLS: true, + }, + } + + for _, test := range testCases { + // order: + // 1. WithNetworkURIAddress + // 2. WithNetworkAddress + // 3. WithTLSCfg(if test.withTLS == true) + config := &cfg{} + opts := WithNetworkURIAddress(test.uri, nil) + + opts = append(opts, WithNetworkAddress(test.addr)) + + if test.withTLS { + opts = append(opts, WithTLSCfg(&tls.Config{})) + } + + for _, opt := range opts { + opt(config) + } + + require.Equal(t, test.wantHost, config.addr, test.uri) + require.Equal(t, test.wantTLS, config.tlsCfg != nil, test.uri) + } +} diff --git a/apiv2/rpc/client/stream_wrapper.go b/apiv2/rpc/client/stream_wrapper.go new file mode 100644 index 0000000..092fbb9 --- /dev/null +++ b/apiv2/rpc/client/stream_wrapper.go @@ -0,0 +1,58 @@ +package client + +import ( + "context" + "time" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + "google.golang.org/grpc" +) + +type streamWrapper struct { + grpc.ClientStream + timeout time.Duration + cancel context.CancelFunc +} + +func (w streamWrapper) ReadMessage(m message.Message) error { + // Can be optimized: we can create blank message here. + gm := m.ToGRPCMessage() + + err := w.withTimeout(func() error { + return w.ClientStream.RecvMsg(gm) + }) + if err != nil { + return err + } + + return m.FromGRPCMessage(gm) +} + +func (w streamWrapper) WriteMessage(m message.Message) error { + return w.withTimeout(func() error { + return w.ClientStream.SendMsg(m.ToGRPCMessage()) + }) +} + +func (w *streamWrapper) Close() error { + return w.withTimeout(w.ClientStream.CloseSend) +} + +func (w *streamWrapper) withTimeout(closure func() error) error { + ch := make(chan error, 1) + go func() { + ch <- closure() + close(ch) + }() + + tt := time.NewTimer(w.timeout) + + select { + case err := <-ch: + tt.Stop() + return err + case <-tt.C: + w.cancel() + return context.DeadlineExceeded + } +} diff --git a/apiv2/rpc/client/util.go b/apiv2/rpc/client/util.go new file mode 100644 index 0000000..eb9e84a --- /dev/null +++ b/apiv2/rpc/client/util.go @@ -0,0 +1,13 @@ +package client + +import ( + "fmt" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/common" +) + +const methodNameFmt = "/%s/%s" + +func toMethodName(p common.CallMethodInfo) string { + return fmt.Sprintf(methodNameFmt, p.Service, p.Name) +} diff --git a/apiv2/rpc/common.go b/apiv2/rpc/common.go new file mode 100644 index 0000000..8177694 --- /dev/null +++ b/apiv2/rpc/common.go @@ -0,0 +1,10 @@ +package rpc + +const ( + // serviceNamePrefix is still used in "old" services but should be + // considered as deprecated. Since new services use "frostfs" root, + // `frostfsServiceNamePrefix` must be used for their rpc interface. + serviceNamePrefix = "neo.fs.v2." + + frostfsServiceNamePrefix = "frostfs.v2." +) diff --git a/apiv2/rpc/common/call.go b/apiv2/rpc/common/call.go new file mode 100644 index 0000000..bc3410a --- /dev/null +++ b/apiv2/rpc/common/call.go @@ -0,0 +1,75 @@ +package common + +type callType uint8 + +const ( + _ callType = iota + callUnary + callClientStream + callServerStream + callBidirStream +) + +// CallMethodInfo is an information about the RPC. +type CallMethodInfo struct { + // Name of the service. + Service string + + // Name of the RPC. + Name string + + t callType +} + +// ServerStream checks if CallMethodInfo contains +// information about the server-side streaming RPC. +func (c CallMethodInfo) ServerStream() bool { + return c.t == callServerStream || c.t == callBidirStream +} + +// ClientStream checks if CallMethodInfo contains +// information about the client-side streaming RPC. +func (c CallMethodInfo) ClientStream() bool { + return c.t == callClientStream || c.t == callBidirStream +} + +func (c *CallMethodInfo) setCommon(service, name string) { + c.Service = service + c.Name = name +} + +// CallMethodInfoUnary returns CallMethodInfo structure +// initialized for the unary RPC. +func CallMethodInfoUnary(service, name string) (info CallMethodInfo) { + info.setCommon(service, name) + info.t = callUnary + + return +} + +// CallMethodInfoClientStream returns CallMethodInfo structure +// initialized for the client-side streaming RPC. +func CallMethodInfoClientStream(service, name string) (info CallMethodInfo) { + info.setCommon(service, name) + info.t = callClientStream + + return +} + +// CallMethodInfoServerStream returns CallMethodInfo structure +// initialized for the server-side streaming RPC. +func CallMethodInfoServerStream(service, name string) (info CallMethodInfo) { + info.setCommon(service, name) + info.t = callServerStream + + return +} + +// CallMethodInfoBidirectionalStream returns CallMethodInfo structure +// initialized for the bidirectional streaming RPC. +func CallMethodInfoBidirectionalStream(service, name string) (info CallMethodInfo) { + info.setCommon(service, name) + info.t = callBidirStream + + return +} diff --git a/apiv2/rpc/common/call_test.go b/apiv2/rpc/common/call_test.go new file mode 100644 index 0000000..49d4da2 --- /dev/null +++ b/apiv2/rpc/common/call_test.go @@ -0,0 +1,49 @@ +package common_test + +import ( + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/common" + "github.com/stretchr/testify/require" +) + +const ( + testServiceName = "test service" + testRPCName = "test RPC" +) + +func TestCallMethodInfoUnary(t *testing.T) { + i := common.CallMethodInfoUnary(testServiceName, testRPCName) + + require.Equal(t, testServiceName, i.Service) + require.Equal(t, testRPCName, i.Name) + require.False(t, i.ClientStream()) + require.False(t, i.ServerStream()) +} + +func TestCallMethodInfoServerStream(t *testing.T) { + i := common.CallMethodInfoServerStream(testServiceName, testRPCName) + + require.Equal(t, testServiceName, i.Service) + require.Equal(t, testRPCName, i.Name) + require.False(t, i.ClientStream()) + require.True(t, i.ServerStream()) +} + +func TestCallMethodInfoClientStream(t *testing.T) { + i := common.CallMethodInfoClientStream(testServiceName, testRPCName) + + require.Equal(t, testServiceName, i.Service) + require.Equal(t, testRPCName, i.Name) + require.True(t, i.ClientStream()) + require.False(t, i.ServerStream()) +} + +func TestCallMethodInfoBidirectionalStream(t *testing.T) { + i := common.CallMethodInfoBidirectionalStream(testServiceName, testRPCName) + + require.Equal(t, testServiceName, i.Service) + require.Equal(t, testRPCName, i.Name) + require.True(t, i.ClientStream()) + require.True(t, i.ServerStream()) +} diff --git a/apiv2/rpc/container.go b/apiv2/rpc/container.go new file mode 100644 index 0000000..ef584a2 --- /dev/null +++ b/apiv2/rpc/container.go @@ -0,0 +1,82 @@ +package rpc + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/common" +) + +const serviceContainer = serviceNamePrefix + "container.ContainerService" + +const ( + rpcContainerPut = "Put" + rpcContainerGet = "Get" + rpcContainerDel = "Delete" + rpcContainerList = "List" + rpcContainerGetEACL = "GetExtendedACL" + rpcContainerUsedSpace = "AnnounceUsedSpace" +) + +// PutContainer executes ContainerService.Put RPC. +func PutContainer( + cli *client.Client, + req *container.PutRequest, + opts ...client.CallOption, +) (*container.PutResponse, error) { + resp := new(container.PutResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceContainer, rpcContainerPut), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} + +// GetContainer executes ContainerService.Get RPC. +func GetContainer( + cli *client.Client, + req *container.GetRequest, + opts ...client.CallOption, +) (*container.GetResponse, error) { + resp := new(container.GetResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceContainer, rpcContainerGet), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} + +// DeleteContainer executes ContainerService.Delete RPC. +func DeleteContainer( + cli *client.Client, + req *container.DeleteRequest, + opts ...client.CallOption, +) (*container.PutResponse, error) { + resp := new(container.PutResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceContainer, rpcContainerDel), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} + +// ListContainers executes ContainerService.List RPC. +func ListContainers( + cli *client.Client, + req *container.ListRequest, + opts ...client.CallOption, +) (*container.ListResponse, error) { + resp := new(container.ListResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceContainer, rpcContainerList), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} diff --git a/apiv2/rpc/grpc/init.go b/apiv2/rpc/grpc/init.go new file mode 100644 index 0000000..0092d39 --- /dev/null +++ b/apiv2/rpc/grpc/init.go @@ -0,0 +1,4 @@ +package grpc + +// Message represents raw gRPC message. +type Message any diff --git a/apiv2/rpc/message/encoding.go b/apiv2/rpc/message/encoding.go new file mode 100644 index 0000000..d656acd --- /dev/null +++ b/apiv2/rpc/message/encoding.go @@ -0,0 +1,40 @@ +package message + +import ( + "encoding/json" +) + +// GRPCConvertedMessage is an interface +// of the gRPC message that is used +// for Message encoding/decoding. +type GRPCConvertedMessage interface { + UnmarshalProtobuf([]byte) error +} + +// Unmarshal decodes m from its Protobuf binary representation +// via related gRPC message. +// +// gm should be tof the same type as the m.ToGRPCMessage() return. +func Unmarshal(m Message, data []byte, gm GRPCConvertedMessage) error { + if err := gm.UnmarshalProtobuf(data); err != nil { + return err + } + + return m.FromGRPCMessage(gm) +} + +// MarshalJSON encodes m to Protobuf JSON representation. +func MarshalJSON(m Message) ([]byte, error) { + return json.Marshal(m.ToGRPCMessage()) +} + +// UnmarshalJSON decodes m from its Protobuf JSON representation +// via related gRPC message. +// +// gm should be tof the same type as the m.ToGRPCMessage() return. +func UnmarshalJSON(m Message, data []byte, gm any) error { + if err := json.Unmarshal(data, gm); err != nil { + return err + } + return m.FromGRPCMessage(gm) +} diff --git a/apiv2/rpc/message/message.go b/apiv2/rpc/message/message.go new file mode 100644 index 0000000..6f6af1a --- /dev/null +++ b/apiv2/rpc/message/message.go @@ -0,0 +1,43 @@ +package message + +import ( + "fmt" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/grpc" +) + +// Message represents raw Protobuf message +// that can be transmitted via several +// transport protocols. +type Message interface { + // Must return gRPC message that can + // be used for gRPC protocol transmission. + ToGRPCMessage() grpc.Message + + // Must restore the message from related + // gRPC message. + // + // If gRPC message is not a related one, + // ErrUnexpectedMessageType can be returned + // to indicate this. + FromGRPCMessage(grpc.Message) error +} + +// ErrUnexpectedMessageType is an error that +// is used to indicate message mismatch. +type ErrUnexpectedMessageType struct { + exp, act any +} + +// NewUnexpectedMessageType initializes an error about message mismatch +// between act and exp. +func NewUnexpectedMessageType(act, exp any) ErrUnexpectedMessageType { + return ErrUnexpectedMessageType{ + exp: exp, + act: act, + } +} + +func (e ErrUnexpectedMessageType) Error() string { + return fmt.Sprintf("unexpected message type %T: expected %T", e.act, e.exp) +} diff --git a/apiv2/rpc/message/test/message.go b/apiv2/rpc/message/test/message.go new file mode 100644 index 0000000..44df8e9 --- /dev/null +++ b/apiv2/rpc/message/test/message.go @@ -0,0 +1,126 @@ +package messagetest + +import ( + "encoding/json" + "errors" + "fmt" + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto/encoding" + "github.com/stretchr/testify/require" +) + +type jsonMessage interface { + json.Marshaler + json.Unmarshaler +} + +type binaryMessage interface { + StableMarshal([]byte) []byte + StableSize() int + Unmarshal([]byte) error +} + +func TestRPCMessage(t *testing.T, msgGens ...func(empty bool) message.Message) { + for _, msgGen := range msgGens { + msg := msgGen(false) + + t.Run(fmt.Sprintf("convert_%T", msg), func(t *testing.T) { + msg := msgGen(false) + + err := msg.FromGRPCMessage(100) + + require.True(t, errors.As(err, new(message.ErrUnexpectedMessageType))) + + msg2 := msgGen(true) + + err = msg2.FromGRPCMessage(msg.ToGRPCMessage()) + require.NoError(t, err) + + require.Equal(t, msg, msg2) + }) + + t.Run("encoding", func(t *testing.T) { + if jm, ok := msg.(jsonMessage); ok { + t.Run(fmt.Sprintf("JSON_%T", msg), func(t *testing.T) { + data, err := jm.MarshalJSON() + require.NoError(t, err) + + jm2 := msgGen(true).(jsonMessage) + require.NoError(t, jm2.UnmarshalJSON(data)) + + require.Equal(t, jm, jm2) + }) + } + + if bm, ok := msg.(binaryMessage); ok { + t.Run(fmt.Sprintf("%T.StableSize() does no allocations", bm), func(t *testing.T) { + require.Zero(t, testing.AllocsPerRun(1000, func() { + _ = bm.StableSize() + })) + }) + t.Run(fmt.Sprintf("Binary_%T", msg), func(t *testing.T) { + data := bm.StableMarshal(nil) + + bm2 := msgGen(true).(binaryMessage) + require.NoError(t, bm2.Unmarshal(data)) + + require.Equal(t, bm, bm2) + }) + } + t.Run("compatibility", func(t *testing.T) { + testCompatibility(t, msgGen) + }) + }) + } +} + +func testCompatibility(t *testing.T, msgGen func(empty bool) message.Message) { + compareBinary := func(t *testing.T, msg message.Message) { + am, ok := msg.(binaryMessage) + if !ok { + t.Skip() + } + + a := am.StableMarshal(nil) + b := msg.ToGRPCMessage().(encoding.ProtoMarshaler).MarshalProtobuf(nil) + if len(a) == 0 { + require.Empty(t, b) + } else { + require.Equal(t, a, b) + } + } + compareJSON := func(t *testing.T, msg message.Message) { + am, ok := msg.(jsonMessage) + if !ok { + t.Skip() + } + + a, err := am.MarshalJSON() + require.NoError(t, err) + + b, err := json.Marshal(msg.ToGRPCMessage()) + require.NoError(t, err) + + require.JSONEq(t, string(a), string(b)) + } + t.Run("empty", func(t *testing.T) { + msg := msgGen(true) + t.Run(fmt.Sprintf("Binary_%T", msg), func(t *testing.T) { + compareBinary(t, msg) + }) + t.Run(fmt.Sprintf("JSON_%T", msg), func(t *testing.T) { + compareJSON(t, msg) + }) + }) + t.Run("not empty", func(t *testing.T) { + msg := msgGen(false) + t.Run(fmt.Sprintf("Binary_%T", msg), func(t *testing.T) { + compareBinary(t, msg) + }) + t.Run(fmt.Sprintf("JSON_%T", msg), func(t *testing.T) { + compareJSON(t, msg) + }) + }) +} diff --git a/apiv2/rpc/netmap.go b/apiv2/rpc/netmap.go new file mode 100644 index 0000000..3b297e7 --- /dev/null +++ b/apiv2/rpc/netmap.go @@ -0,0 +1,62 @@ +package rpc + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/common" +) + +const serviceNetmap = serviceNamePrefix + "netmap.NetmapService" + +const ( + rpcNetmapNodeInfo = "LocalNodeInfo" + rpcNetmapNetInfo = "NetworkInfo" + rpcNetmapSnapshot = "NetmapSnapshot" +) + +// LocalNodeInfo executes NetmapService.LocalNodeInfo RPC. +func LocalNodeInfo( + cli *client.Client, + req *netmap.LocalNodeInfoRequest, + opts ...client.CallOption, +) (*netmap.LocalNodeInfoResponse, error) { + resp := new(netmap.LocalNodeInfoResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceNetmap, rpcNetmapNodeInfo), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} + +// NetworkInfo executes NetmapService.NetworkInfo RPC. +func NetworkInfo( + cli *client.Client, + req *netmap.NetworkInfoRequest, + opts ...client.CallOption, +) (*netmap.NetworkInfoResponse, error) { + resp := new(netmap.NetworkInfoResponse) + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceNetmap, rpcNetmapNetInfo), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} + +// NetMapSnapshot executes NetmapService.NetmapSnapshot RPC. +func NetMapSnapshot( + cli *client.Client, + req *netmap.SnapshotRequest, + opts ...client.CallOption, +) (*netmap.SnapshotResponse, error) { + resp := new(netmap.SnapshotResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceNetmap, rpcNetmapSnapshot), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} diff --git a/apiv2/rpc/object.go b/apiv2/rpc/object.go new file mode 100644 index 0000000..6217b11 --- /dev/null +++ b/apiv2/rpc/object.go @@ -0,0 +1,243 @@ +package rpc + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/common" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" +) + +const serviceObject = serviceNamePrefix + "object.ObjectService" + +const ( + rpcObjectPut = "Put" + rpcObjectGet = "Get" + rpcObjectSearch = "Search" + rpcObjectRange = "GetRange" + rpcObjectHash = "GetRangeHash" + rpcObjectHead = "Head" + rpcObjectDelete = "Delete" + rpcObjectPutSingle = "PutSingle" + rpcObjectPatch = "Patch" +) + +// PutRequestWriter is an object.PutRequest +// message streaming component. +type PutRequestWriter struct { + wc client.MessageWriterCloser + + resp message.Message +} + +// Write writes req to the stream. +func (w *PutRequestWriter) Write(req *object.PutRequest) error { + return w.wc.WriteMessage(req) +} + +// Close closes the stream. +func (w *PutRequestWriter) Close() error { + return w.wc.Close() +} + +// PutObject executes ObjectService.Put RPC. +func PutObject( + cli *client.Client, + resp *object.PutResponse, + opts ...client.CallOption, +) (*PutRequestWriter, error) { + wc, err := client.OpenClientStream(cli, common.CallMethodInfoClientStream(serviceObject, rpcObjectPut), resp, opts...) + if err != nil { + return nil, err + } + + return &PutRequestWriter{ + wc: wc, + resp: resp, + }, nil +} + +// GetResponseReader is an object.GetResponse +// stream reader. +type GetResponseReader struct { + r client.MessageReader +} + +// Read reads response from the stream. +// +// Returns io.EOF of streaming is finished. +func (r *GetResponseReader) Read(resp *object.GetResponse) error { + return r.r.ReadMessage(resp) +} + +// GetObject executes ObjectService.Get RPC. +func GetObject( + cli *client.Client, + req *object.GetRequest, + opts ...client.CallOption, +) (*GetResponseReader, error) { + wc, err := client.OpenServerStream(cli, common.CallMethodInfoServerStream(serviceObject, rpcObjectGet), req, opts...) + if err != nil { + return nil, err + } + + return &GetResponseReader{ + r: wc, + }, nil +} + +// GetResponseReader is an object.SearchResponse +// stream reader. +type SearchResponseReader struct { + r client.MessageReader +} + +// Read reads response from the stream. +// +// Returns io.EOF of streaming is finished. +func (r *SearchResponseReader) Read(resp *object.SearchResponse) error { + return r.r.ReadMessage(resp) +} + +// SearchObjects executes ObjectService.Search RPC. +func SearchObjects( + cli *client.Client, + req *object.SearchRequest, + opts ...client.CallOption, +) (*SearchResponseReader, error) { + wc, err := client.OpenServerStream(cli, common.CallMethodInfoServerStream(serviceObject, rpcObjectSearch), req, opts...) + if err != nil { + return nil, err + } + + return &SearchResponseReader{ + r: wc, + }, nil +} + +// GetResponseReader is an object.GetRangeResponse +// stream reader. +type ObjectRangeResponseReader struct { + r client.MessageReader +} + +// Read reads response from the stream. +// +// Returns io.EOF of streaming is finished. +func (r *ObjectRangeResponseReader) Read(resp *object.GetRangeResponse) error { + return r.r.ReadMessage(resp) +} + +// GetObjectRange executes ObjectService.GetRange RPC. +func GetObjectRange( + cli *client.Client, + req *object.GetRangeRequest, + opts ...client.CallOption, +) (*ObjectRangeResponseReader, error) { + wc, err := client.OpenServerStream(cli, common.CallMethodInfoServerStream(serviceObject, rpcObjectRange), req, opts...) + if err != nil { + return nil, err + } + + return &ObjectRangeResponseReader{ + r: wc, + }, nil +} + +// HeadObject executes ObjectService.Head RPC. +func HeadObject( + cli *client.Client, + req *object.HeadRequest, + opts ...client.CallOption, +) (*object.HeadResponse, error) { + resp := new(object.HeadResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceObject, rpcObjectHead), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} + +// DeleteObject executes ObjectService.Delete RPC. +func DeleteObject( + cli *client.Client, + req *object.DeleteRequest, + opts ...client.CallOption, +) (*object.DeleteResponse, error) { + resp := new(object.DeleteResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceObject, rpcObjectDelete), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} + +// HashObjectRange executes ObjectService.GetRangeHash RPC. +func HashObjectRange( + cli *client.Client, + req *object.GetRangeHashRequest, + opts ...client.CallOption, +) (*object.GetRangeHashResponse, error) { + resp := new(object.GetRangeHashResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceObject, rpcObjectHash), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} + +// PutSingleObject executes ObjectService.PutSingle RPC. +func PutSingleObject( + cli *client.Client, + req *object.PutSingleRequest, + opts ...client.CallOption, +) (*object.PutSingleResponse, error) { + resp := new(object.PutSingleResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceObject, rpcObjectPutSingle), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} + +// PatchRequestWriter is an object.PatchRequest +// message streaming component. +type PatchRequestWriter struct { + wc client.MessageWriterCloser + + resp message.Message +} + +// Write writes req to the stream. +func (w *PatchRequestWriter) Write(req *object.PatchRequest) error { + return w.wc.WriteMessage(req) +} + +// Close closes the stream. +func (w *PatchRequestWriter) Close() error { + return w.wc.Close() +} + +// Patch executes ObjectService.Patch RPC. +func Patch( + cli *client.Client, + resp *object.PatchResponse, + opts ...client.CallOption, +) (*PatchRequestWriter, error) { + wc, err := client.OpenClientStream(cli, common.CallMethodInfoClientStream(serviceObject, rpcObjectPatch), resp, opts...) + if err != nil { + return nil, err + } + + return &PatchRequestWriter{ + wc: wc, + resp: resp, + }, nil +} diff --git a/apiv2/rpc/session.go b/apiv2/rpc/session.go new file mode 100644 index 0000000..5458afe --- /dev/null +++ b/apiv2/rpc/session.go @@ -0,0 +1,28 @@ +package rpc + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/common" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" +) + +const serviceSession = serviceNamePrefix + "session.SessionService" + +const ( + rpcSessionCreate = "Create" +) + +func CreateSession( + cli *client.Client, + req *session.CreateRequest, + opts ...client.CallOption, +) (*session.CreateResponse, error) { + resp := new(session.CreateResponse) + + err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceSession, rpcSessionCreate), req, resp, opts...) + if err != nil { + return nil, err + } + + return resp, nil +} diff --git a/apiv2/session/convert.go b/apiv2/session/convert.go new file mode 100644 index 0000000..20a60da --- /dev/null +++ b/apiv2/session/convert.go @@ -0,0 +1,898 @@ +package session + +import ( + "fmt" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" + aclGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + refsGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" + statusGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status/grpc" +) + +func (c *CreateRequestBody) ToGRPCMessage() grpc.Message { + var m *session.CreateRequest_Body + + if c != nil { + m = new(session.CreateRequest_Body) + + m.SetOwnerId(c.ownerID.ToGRPCMessage().(*refsGRPC.OwnerID)) + m.SetExpiration(c.expiration) + } + + return m +} + +func (c *CreateRequestBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.CreateRequest_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + ownerID := v.GetOwnerId() + if ownerID == nil { + c.ownerID = nil + } else { + if c.ownerID == nil { + c.ownerID = new(refs.OwnerID) + } + + err = c.ownerID.FromGRPCMessage(ownerID) + if err != nil { + return err + } + } + + c.expiration = v.GetExpiration() + + return nil +} + +func (c *CreateRequest) ToGRPCMessage() grpc.Message { + var m *session.CreateRequest + + if c != nil { + m = new(session.CreateRequest) + + m.SetBody(c.body.ToGRPCMessage().(*session.CreateRequest_Body)) + c.RequestHeaders.ToMessage(m) + } + + return m +} + +func (c *CreateRequest) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.CreateRequest) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + c.body = nil + } else { + if c.body == nil { + c.body = new(CreateRequestBody) + } + + err = c.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return c.RequestHeaders.FromMessage(v) +} + +func (c *CreateResponseBody) ToGRPCMessage() grpc.Message { + var m *session.CreateResponse_Body + + if c != nil { + m = new(session.CreateResponse_Body) + + m.SetSessionKey(c.sessionKey) + m.SetId(c.id) + } + + return m +} + +func (c *CreateResponseBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.CreateResponse_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + c.sessionKey = v.GetSessionKey() + c.id = v.GetId() + + return nil +} + +func (c *CreateResponse) ToGRPCMessage() grpc.Message { + var m *session.CreateResponse + + if c != nil { + m = new(session.CreateResponse) + + m.SetBody(c.body.ToGRPCMessage().(*session.CreateResponse_Body)) + c.ResponseHeaders.ToMessage(m) + } + + return m +} + +func (c *CreateResponse) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.CreateResponse) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + c.body = nil + } else { + if c.body == nil { + c.body = new(CreateResponseBody) + } + + err = c.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + return c.ResponseHeaders.FromMessage(v) +} + +func (l *TokenLifetime) ToGRPCMessage() grpc.Message { + var m *session.SessionToken_Body_TokenLifetime + + if l != nil { + m = new(session.SessionToken_Body_TokenLifetime) + + m.SetExp(l.exp) + m.SetIat(l.iat) + m.SetNbf(l.nbf) + } + + return m +} + +func (l *TokenLifetime) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.SessionToken_Body_TokenLifetime) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + l.exp = v.GetExp() + l.iat = v.GetIat() + l.nbf = v.GetNbf() + + return nil +} + +func (x *XHeader) ToGRPCMessage() grpc.Message { + var m *session.XHeader + + if x != nil { + m = new(session.XHeader) + + m.SetKey(x.key) + m.SetValue(x.val) + } + + return m +} + +func (x *XHeader) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.XHeader) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + x.key = v.GetKey() + x.val = v.GetValue() + + return nil +} + +func XHeadersToGRPC(xs []XHeader) (res []session.XHeader) { + if xs != nil { + res = make([]session.XHeader, 0, len(xs)) + + for i := range xs { + res = append(res, *xs[i].ToGRPCMessage().(*session.XHeader)) + } + } + + return +} + +func XHeadersFromGRPC(xs []session.XHeader) (res []XHeader, err error) { + if xs != nil { + res = make([]XHeader, len(xs)) + + for i := range xs { + err = res[i].FromGRPCMessage(&xs[i]) + if err != nil { + return + } + } + } + + return +} + +func (t *Token) ToGRPCMessage() grpc.Message { + var m *session.SessionToken + + if t != nil { + m = new(session.SessionToken) + + m.SetBody(t.body.ToGRPCMessage().(*session.SessionToken_Body)) + m.SetSignature(t.sig.ToGRPCMessage().(*refsGRPC.Signature)) + } + + return m +} + +func (t *Token) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.SessionToken) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + body := v.GetBody() + if body == nil { + t.body = nil + } else { + if t.body == nil { + t.body = new(TokenBody) + } + + err = t.body.FromGRPCMessage(body) + if err != nil { + return err + } + } + + sig := v.GetSignature() + if sig == nil { + t.sig = nil + } else { + if t.sig == nil { + t.sig = new(refs.Signature) + } + + err = t.sig.FromGRPCMessage(sig) + if err != nil { + return err + } + } + + return nil +} + +func (r *RequestVerificationHeader) ToGRPCMessage() grpc.Message { + var m *session.RequestVerificationHeader + + if r != nil { + m = new(session.RequestVerificationHeader) + + m.SetBodySignature(r.bodySig.ToGRPCMessage().(*refsGRPC.Signature)) + m.SetMetaSignature(r.metaSig.ToGRPCMessage().(*refsGRPC.Signature)) + m.SetOriginSignature(r.originSig.ToGRPCMessage().(*refsGRPC.Signature)) + m.SetOrigin(r.origin.ToGRPCMessage().(*session.RequestVerificationHeader)) + } + + return m +} + +func (r *RequestVerificationHeader) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.RequestVerificationHeader) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + originSig := v.GetOriginSignature() + if originSig == nil { + r.originSig = nil + } else { + if r.originSig == nil { + r.originSig = new(refs.Signature) + } + + err = r.originSig.FromGRPCMessage(originSig) + if err != nil { + return err + } + } + + metaSig := v.GetMetaSignature() + if metaSig == nil { + r.metaSig = nil + } else { + if r.metaSig == nil { + r.metaSig = new(refs.Signature) + } + + err = r.metaSig.FromGRPCMessage(metaSig) + if err != nil { + return err + } + } + + bodySig := v.GetBodySignature() + if bodySig == nil { + r.bodySig = nil + } else { + if r.bodySig == nil { + r.bodySig = new(refs.Signature) + } + + err = r.bodySig.FromGRPCMessage(bodySig) + if err != nil { + return err + } + } + + origin := v.GetOrigin() + if origin == nil { + r.origin = nil + } else { + if r.origin == nil { + r.origin = new(RequestVerificationHeader) + } + + err = r.origin.FromGRPCMessage(origin) + if err != nil { + return err + } + } + + return nil +} + +func (r *RequestMetaHeader) ToGRPCMessage() grpc.Message { + var m *session.RequestMetaHeader + + if r != nil { + m = new(session.RequestMetaHeader) + + m.SetVersion(r.version.ToGRPCMessage().(*refsGRPC.Version)) + m.SetSessionToken(r.sessionToken.ToGRPCMessage().(*session.SessionToken)) + m.SetBearerToken(r.bearerToken.ToGRPCMessage().(*aclGRPC.BearerToken)) + m.SetXHeaders(XHeadersToGRPC(r.xHeaders)) + m.SetEpoch(r.epoch) + m.SetTtl(r.ttl) + m.SetOrigin(r.origin.ToGRPCMessage().(*session.RequestMetaHeader)) + m.SetMagicNumber(r.netMagic) + } + + return m +} + +func (r *RequestMetaHeader) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.RequestMetaHeader) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + version := v.GetVersion() + if version == nil { + r.version = nil + } else { + if r.version == nil { + r.version = new(refs.Version) + } + + err = r.version.FromGRPCMessage(version) + if err != nil { + return err + } + } + + sessionToken := v.GetSessionToken() + if sessionToken == nil { + r.sessionToken = nil + } else { + if r.sessionToken == nil { + r.sessionToken = new(Token) + } + + err = r.sessionToken.FromGRPCMessage(sessionToken) + if err != nil { + return err + } + } + + bearerToken := v.GetBearerToken() + if bearerToken == nil { + r.bearerToken = nil + } else { + if r.bearerToken == nil { + r.bearerToken = new(acl.BearerToken) + } + + err = r.bearerToken.FromGRPCMessage(bearerToken) + if err != nil { + return err + } + } + + origin := v.GetOrigin() + if origin == nil { + r.origin = nil + } else { + if r.origin == nil { + r.origin = new(RequestMetaHeader) + } + + err = r.origin.FromGRPCMessage(origin) + if err != nil { + return err + } + } + + r.xHeaders, err = XHeadersFromGRPC(v.GetXHeaders()) + if err != nil { + return err + } + + r.epoch = v.GetEpoch() + r.ttl = v.GetTtl() + r.netMagic = v.GetMagicNumber() + + return nil +} + +func (r *ResponseVerificationHeader) ToGRPCMessage() grpc.Message { + var m *session.ResponseVerificationHeader + + if r != nil { + m = new(session.ResponseVerificationHeader) + + m.SetBodySignature(r.bodySig.ToGRPCMessage().(*refsGRPC.Signature)) + m.SetMetaSignature(r.metaSig.ToGRPCMessage().(*refsGRPC.Signature)) + m.SetOriginSignature(r.originSig.ToGRPCMessage().(*refsGRPC.Signature)) + m.SetOrigin(r.origin.ToGRPCMessage().(*session.ResponseVerificationHeader)) + } + + return m +} + +func (r *ResponseVerificationHeader) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.ResponseVerificationHeader) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + originSig := v.GetOriginSignature() + if originSig == nil { + r.originSig = nil + } else { + if r.originSig == nil { + r.originSig = new(refs.Signature) + } + + err = r.originSig.FromGRPCMessage(originSig) + if err != nil { + return err + } + } + + metaSig := v.GetMetaSignature() + if metaSig == nil { + r.metaSig = nil + } else { + if r.metaSig == nil { + r.metaSig = new(refs.Signature) + } + + err = r.metaSig.FromGRPCMessage(metaSig) + if err != nil { + return err + } + } + + bodySig := v.GetBodySignature() + if bodySig == nil { + r.bodySig = nil + } else { + if r.bodySig == nil { + r.bodySig = new(refs.Signature) + } + + err = r.bodySig.FromGRPCMessage(bodySig) + if err != nil { + return err + } + } + + origin := v.GetOrigin() + if origin == nil { + r.origin = nil + } else { + if r.origin == nil { + r.origin = new(ResponseVerificationHeader) + } + + err = r.origin.FromGRPCMessage(origin) + if err != nil { + return err + } + } + + return nil +} + +func (r *ResponseMetaHeader) ToGRPCMessage() grpc.Message { + var m *session.ResponseMetaHeader + + if r != nil { + m = new(session.ResponseMetaHeader) + + m.SetVersion(r.version.ToGRPCMessage().(*refsGRPC.Version)) + m.SetXHeaders(XHeadersToGRPC(r.xHeaders)) + m.SetEpoch(r.epoch) + m.SetTtl(r.ttl) + m.SetOrigin(r.origin.ToGRPCMessage().(*session.ResponseMetaHeader)) + m.SetStatus(r.status.ToGRPCMessage().(*statusGRPC.Status)) + } + + return m +} + +func (r *ResponseMetaHeader) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.ResponseMetaHeader) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + version := v.GetVersion() + if version == nil { + r.version = nil + } else { + if r.version == nil { + r.version = new(refs.Version) + } + + err = r.version.FromGRPCMessage(version) + if err != nil { + return err + } + } + + origin := v.GetOrigin() + if origin == nil { + r.origin = nil + } else { + if r.origin == nil { + r.origin = new(ResponseMetaHeader) + } + + err = r.origin.FromGRPCMessage(origin) + if err != nil { + return err + } + } + + st := v.GetStatus() + if st == nil { + r.status = nil + } else { + if r.status == nil { + r.status = new(status.Status) + } + + err = r.status.FromGRPCMessage(st) + if err != nil { + return err + } + } + + r.xHeaders, err = XHeadersFromGRPC(v.GetXHeaders()) + if err != nil { + return err + } + + r.epoch = v.GetEpoch() + r.ttl = v.GetTtl() + + return nil +} + +func ObjectSessionVerbToGRPCField(v ObjectSessionVerb) session.ObjectSessionContext_Verb { + switch v { + case ObjectVerbPut: + return session.ObjectSessionContext_PUT + case ObjectVerbGet: + return session.ObjectSessionContext_GET + case ObjectVerbHead: + return session.ObjectSessionContext_HEAD + case ObjectVerbSearch: + return session.ObjectSessionContext_SEARCH + case ObjectVerbDelete: + return session.ObjectSessionContext_DELETE + case ObjectVerbRange: + return session.ObjectSessionContext_RANGE + case ObjectVerbRangeHash: + return session.ObjectSessionContext_RANGEHASH + case ObjectVerbPatch: + return session.ObjectSessionContext_PATCH + default: + return session.ObjectSessionContext_VERB_UNSPECIFIED + } +} + +func ObjectSessionVerbFromGRPCField(v session.ObjectSessionContext_Verb) ObjectSessionVerb { + switch v { + case session.ObjectSessionContext_PUT: + return ObjectVerbPut + case session.ObjectSessionContext_GET: + return ObjectVerbGet + case session.ObjectSessionContext_HEAD: + return ObjectVerbHead + case session.ObjectSessionContext_SEARCH: + return ObjectVerbSearch + case session.ObjectSessionContext_DELETE: + return ObjectVerbDelete + case session.ObjectSessionContext_RANGE: + return ObjectVerbRange + case session.ObjectSessionContext_RANGEHASH: + return ObjectVerbRangeHash + case session.ObjectSessionContext_PATCH: + return ObjectVerbPatch + default: + return ObjectVerbUnknown + } +} + +func (c *ObjectSessionContext) ToGRPCMessage() grpc.Message { + var m *session.ObjectSessionContext + + if c != nil { + m = new(session.ObjectSessionContext) + + m.SetVerb(ObjectSessionVerbToGRPCField(c.verb)) + m.SetTarget(&session.ObjectSessionContext_Target{ + Container: c.cnr.ToGRPCMessage().(*refsGRPC.ContainerID), + Objects: refs.ObjectIDListToGRPCMessage(c.objs), + }) + } + + return m +} + +func (c *ObjectSessionContext) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.ObjectSessionContext) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + cnr := v.GetTarget().GetContainer() + if cnr == nil { + c.cnr = nil + } else { + if c.cnr == nil { + c.cnr = new(refs.ContainerID) + } + + err = c.cnr.FromGRPCMessage(cnr) + if err != nil { + return err + } + } + + c.objs, err = refs.ObjectIDListFromGRPCMessage(v.GetTarget().GetObjects()) + if err != nil { + return err + } + + c.verb = ObjectSessionVerbFromGRPCField(v.GetVerb()) + + return nil +} + +func (t *TokenBody) ToGRPCMessage() grpc.Message { + var m *session.SessionToken_Body + + if t != nil { + m = new(session.SessionToken_Body) + + switch typ := t.ctx.(type) { + default: + panic(fmt.Sprintf("unknown session context %T", typ)) + case nil: + m.Context = nil + case *ObjectSessionContext: + m.SetObject(typ.ToGRPCMessage().(*session.ObjectSessionContext)) + case *ContainerSessionContext: + m.SetContainer(typ.ToGRPCMessage().(*session.ContainerSessionContext)) + } + + m.SetOwnerId(t.ownerID.ToGRPCMessage().(*refsGRPC.OwnerID)) + m.SetId(t.id) + m.SetSessionKey(t.sessionKey) + m.SetLifetime(t.lifetime.ToGRPCMessage().(*session.SessionToken_Body_TokenLifetime)) + } + + return m +} + +func (t *TokenBody) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.SessionToken_Body) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + t.ctx = nil + + switch val := v.GetContext().(type) { + default: + err = fmt.Errorf("unknown session context %T", val) + case nil: + case *session.SessionToken_Body_Object: + ctx, ok := t.ctx.(*ObjectSessionContext) + if !ok { + ctx = new(ObjectSessionContext) + t.ctx = ctx + } + + err = ctx.FromGRPCMessage(val.Object) + case *session.SessionToken_Body_Container: + ctx, ok := t.ctx.(*ContainerSessionContext) + if !ok { + ctx = new(ContainerSessionContext) + t.ctx = ctx + } + + err = ctx.FromGRPCMessage(val.Container) + } + + if err != nil { + return err + } + + ownerID := v.GetOwnerId() + if ownerID == nil { + t.ownerID = nil + } else { + if t.ownerID == nil { + t.ownerID = new(refs.OwnerID) + } + + err = t.ownerID.FromGRPCMessage(ownerID) + if err != nil { + return err + } + } + + lifetime := v.GetLifetime() + if lifetime == nil { + t.lifetime = nil + } else { + if t.lifetime == nil { + t.lifetime = new(TokenLifetime) + } + + err = t.lifetime.FromGRPCMessage(lifetime) + if err != nil { + return err + } + } + + t.id = v.GetId() + t.sessionKey = v.GetSessionKey() + + return nil +} + +// ContainerSessionVerbToGRPCField converts ContainerSessionVerb +// to gRPC-generated session.ContainerSessionContext_Verb. +// +// If v is outside of the ContainerSessionVerb enum, +// session.ContainerSessionContext_VERB_UNSPECIFIED is returned. +func ContainerSessionVerbToGRPCField(v ContainerSessionVerb) session.ContainerSessionContext_Verb { + switch v { + default: + return session.ContainerSessionContext_VERB_UNSPECIFIED + case ContainerVerbPut: + return session.ContainerSessionContext_PUT + case ContainerVerbDelete: + return session.ContainerSessionContext_DELETE + case ContainerVerbSetEACL: + return session.ContainerSessionContext_SETEACL + } +} + +// ContainerSessionVerbFromGRPCField converts gRPC-generated +// session.ContainerSessionContext_Verb to ContainerSessionVerb. +// +// If v is outside of the session.ContainerSessionContext_Verb enum, +// ContainerVerbUnknown is returned. +func ContainerSessionVerbFromGRPCField(v session.ContainerSessionContext_Verb) ContainerSessionVerb { + switch v { + default: + return ContainerVerbUnknown + case session.ContainerSessionContext_PUT: + return ContainerVerbPut + case session.ContainerSessionContext_DELETE: + return ContainerVerbDelete + case session.ContainerSessionContext_SETEACL: + return ContainerVerbSetEACL + } +} + +// ToGRPCMessage converts ContainerSessionContext to gRPC-generated +// session.ContainerSessionContext message. +func (x *ContainerSessionContext) ToGRPCMessage() grpc.Message { + var m *session.ContainerSessionContext + + if x != nil { + m = new(session.ContainerSessionContext) + + m.SetVerb(ContainerSessionVerbToGRPCField(x.verb)) + m.SetWildcard(x.wildcard) + m.SetContainerId(x.cid.ToGRPCMessage().(*refsGRPC.ContainerID)) + } + + return m +} + +// FromGRPCMessage tries to restore ContainerSessionContext from grpc.Message. +// +// Returns message.ErrUnexpectedMessageType if m is not +// a gRPC-generated session.ContainerSessionContext message. +func (x *ContainerSessionContext) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*session.ContainerSessionContext) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + cid := v.GetContainerId() + if cid == nil { + x.cid = nil + } else { + if x.cid == nil { + x.cid = new(refs.ContainerID) + } + + err = x.cid.FromGRPCMessage(cid) + if err != nil { + return err + } + } + + x.verb = ContainerSessionVerbFromGRPCField(v.GetVerb()) + x.wildcard = v.GetWildcard() + + return nil +} diff --git a/apiv2/session/grpc/client.go b/apiv2/session/grpc/client.go new file mode 100644 index 0000000..2dfe77e --- /dev/null +++ b/apiv2/session/grpc/client.go @@ -0,0 +1,62 @@ +package session + +import ( + "context" + "errors" + + "google.golang.org/grpc" +) + +// Client wraps SessionServiceClient +// with pre-defined configurations. +type Client struct { + *cfg + + client SessionServiceClient +} + +// Option represents Client option. +type Option func(*cfg) + +type cfg struct { + callOpts []grpc.CallOption +} + +// ErrNilSessionServiceClient is returned by functions that expect +// a non-nil SessionServiceClient, but received nil. +var ErrNilSessionServiceClient = errors.New("session gRPC client is nil") + +func defaultCfg() *cfg { + return new(cfg) +} + +// NewClient creates, initializes and returns a new Client instance. +// +// Options are applied one by one in order. +func NewClient(c SessionServiceClient, opts ...Option) (*Client, error) { + if c == nil { + return nil, ErrNilSessionServiceClient + } + + cfg := defaultCfg() + for i := range opts { + opts[i](cfg) + } + + return &Client{ + cfg: cfg, + client: c, + }, nil +} + +func (c *Client) Create(ctx context.Context, req *CreateRequest) (*CreateResponse, error) { + return c.client.Create(ctx, req, c.callOpts...) +} + +// WithCallOptions returns Option that configures +// Client to attach call options to each rpc call. +func WithCallOptions(opts []grpc.CallOption) Option { + return func(c *cfg) { + c.callOpts = opts + } +} diff --git a/apiv2/session/grpc/service_frostfs.pb.go b/apiv2/session/grpc/service_frostfs.pb.go new file mode 100644 index 0000000..387bc29 Binary files /dev/null and b/apiv2/session/grpc/service_frostfs.pb.go differ diff --git a/apiv2/session/grpc/service_frostfs_fuzz.go b/apiv2/session/grpc/service_frostfs_fuzz.go new file mode 100644 index 0000000..759361c --- /dev/null +++ b/apiv2/session/grpc/service_frostfs_fuzz.go @@ -0,0 +1,45 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package session + +func DoFuzzProtoCreateRequest(data []byte) int { + msg := new(CreateRequest) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONCreateRequest(data []byte) int { + msg := new(CreateRequest) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoCreateResponse(data []byte) int { + msg := new(CreateResponse) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONCreateResponse(data []byte) int { + msg := new(CreateResponse) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/session/grpc/service_frostfs_test.go b/apiv2/session/grpc/service_frostfs_test.go new file mode 100644 index 0000000..fc8664e --- /dev/null +++ b/apiv2/session/grpc/service_frostfs_test.go @@ -0,0 +1,31 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package session + +import ( + testing "testing" +) + +func FuzzProtoCreateRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoCreateRequest(data) + }) +} +func FuzzJSONCreateRequest(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONCreateRequest(data) + }) +} +func FuzzProtoCreateResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoCreateResponse(data) + }) +} +func FuzzJSONCreateResponse(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONCreateResponse(data) + }) +} diff --git a/apiv2/session/grpc/service_grpc.pb.go b/apiv2/session/grpc/service_grpc.pb.go new file mode 100644 index 0000000..adf6fc1 Binary files /dev/null and b/apiv2/session/grpc/service_grpc.pb.go differ diff --git a/apiv2/session/grpc/types_frostfs.pb.go b/apiv2/session/grpc/types_frostfs.pb.go new file mode 100644 index 0000000..3342a89 Binary files /dev/null and b/apiv2/session/grpc/types_frostfs.pb.go differ diff --git a/apiv2/session/grpc/types_frostfs_fuzz.go b/apiv2/session/grpc/types_frostfs_fuzz.go new file mode 100644 index 0000000..fae4a05 --- /dev/null +++ b/apiv2/session/grpc/types_frostfs_fuzz.go @@ -0,0 +1,159 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package session + +func DoFuzzProtoObjectSessionContext(data []byte) int { + msg := new(ObjectSessionContext) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONObjectSessionContext(data []byte) int { + msg := new(ObjectSessionContext) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoContainerSessionContext(data []byte) int { + msg := new(ContainerSessionContext) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONContainerSessionContext(data []byte) int { + msg := new(ContainerSessionContext) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoSessionToken(data []byte) int { + msg := new(SessionToken) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONSessionToken(data []byte) int { + msg := new(SessionToken) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoXHeader(data []byte) int { + msg := new(XHeader) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONXHeader(data []byte) int { + msg := new(XHeader) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoRequestMetaHeader(data []byte) int { + msg := new(RequestMetaHeader) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONRequestMetaHeader(data []byte) int { + msg := new(RequestMetaHeader) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoResponseMetaHeader(data []byte) int { + msg := new(ResponseMetaHeader) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONResponseMetaHeader(data []byte) int { + msg := new(ResponseMetaHeader) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoRequestVerificationHeader(data []byte) int { + msg := new(RequestVerificationHeader) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONRequestVerificationHeader(data []byte) int { + msg := new(RequestVerificationHeader) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} +func DoFuzzProtoResponseVerificationHeader(data []byte) int { + msg := new(ResponseVerificationHeader) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONResponseVerificationHeader(data []byte) int { + msg := new(ResponseVerificationHeader) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/session/grpc/types_frostfs_test.go b/apiv2/session/grpc/types_frostfs_test.go new file mode 100644 index 0000000..5c9b6c2 --- /dev/null +++ b/apiv2/session/grpc/types_frostfs_test.go @@ -0,0 +1,91 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package session + +import ( + testing "testing" +) + +func FuzzProtoObjectSessionContext(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoObjectSessionContext(data) + }) +} +func FuzzJSONObjectSessionContext(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONObjectSessionContext(data) + }) +} +func FuzzProtoContainerSessionContext(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoContainerSessionContext(data) + }) +} +func FuzzJSONContainerSessionContext(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONContainerSessionContext(data) + }) +} +func FuzzProtoSessionToken(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoSessionToken(data) + }) +} +func FuzzJSONSessionToken(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONSessionToken(data) + }) +} +func FuzzProtoXHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoXHeader(data) + }) +} +func FuzzJSONXHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONXHeader(data) + }) +} +func FuzzProtoRequestMetaHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoRequestMetaHeader(data) + }) +} +func FuzzJSONRequestMetaHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONRequestMetaHeader(data) + }) +} +func FuzzProtoResponseMetaHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoResponseMetaHeader(data) + }) +} +func FuzzJSONResponseMetaHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONResponseMetaHeader(data) + }) +} +func FuzzProtoRequestVerificationHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoRequestVerificationHeader(data) + }) +} +func FuzzJSONRequestVerificationHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONRequestVerificationHeader(data) + }) +} +func FuzzProtoResponseVerificationHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoResponseVerificationHeader(data) + }) +} +func FuzzJSONResponseVerificationHeader(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONResponseVerificationHeader(data) + }) +} diff --git a/apiv2/session/json.go b/apiv2/session/json.go new file mode 100644 index 0000000..3ef09c5 --- /dev/null +++ b/apiv2/session/json.go @@ -0,0 +1,86 @@ +package session + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session/grpc" +) + +func (c *ObjectSessionContext) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(c) +} + +func (c *ObjectSessionContext) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(c, data, new(session.ObjectSessionContext)) +} + +func (l *TokenLifetime) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(l) +} + +func (l *TokenLifetime) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(l, data, new(session.SessionToken_Body_TokenLifetime)) +} + +func (t *TokenBody) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(t) +} + +func (t *TokenBody) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(t, data, new(session.SessionToken_Body)) +} + +func (t *Token) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(t) +} + +func (t *Token) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(t, data, new(session.SessionToken)) +} + +func (x *XHeader) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(x) +} + +func (x *XHeader) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(x, data, new(session.XHeader)) +} + +func (r *RequestMetaHeader) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(r) +} + +func (r *RequestMetaHeader) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(r, data, new(session.RequestMetaHeader)) +} + +func (r *RequestVerificationHeader) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(r) +} + +func (r *RequestVerificationHeader) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(r, data, new(session.RequestVerificationHeader)) +} + +func (r *ResponseMetaHeader) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(r) +} + +func (r *ResponseMetaHeader) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(r, data, new(session.ResponseMetaHeader)) +} + +func (r *ResponseVerificationHeader) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(r) +} + +func (r *ResponseVerificationHeader) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(r, data, new(session.ResponseVerificationHeader)) +} + +func (x *ContainerSessionContext) MarshalJSON() ([]byte, error) { + return message.MarshalJSON(x) +} + +func (x *ContainerSessionContext) UnmarshalJSON(data []byte) error { + return message.UnmarshalJSON(x, data, new(session.ContainerSessionContext)) +} diff --git a/apiv2/session/marshal.go b/apiv2/session/marshal.go new file mode 100644 index 0000000..9bfb8f2 --- /dev/null +++ b/apiv2/session/marshal.go @@ -0,0 +1,536 @@ +package session + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto" +) + +const ( + createReqBodyOwnerField = 1 + createReqBodyExpirationField = 2 + + createRespBodyIDField = 1 + createRespBodyKeyField = 2 + + xheaderKeyField = 1 + xheaderValueField = 2 + + lifetimeExpirationField = 1 + lifetimeNotValidBeforeField = 2 + lifetimeIssuedAtField = 3 + + objectCtxVerbField = 1 + objectCtxTargetField = 2 + + sessionTokenBodyIDField = 1 + sessionTokenBodyOwnerField = 2 + sessionTokenBodyLifetimeField = 3 + sessionTokenBodyKeyField = 4 + sessionTokenBodyObjectCtxField = 5 + sessionTokenBodyCnrCtxField = 6 + + sessionTokenBodyField = 1 + sessionTokenSignatureField = 2 + + reqMetaHeaderVersionField = 1 + reqMetaHeaderEpochField = 2 + reqMetaHeaderTTLField = 3 + reqMetaHeaderXHeadersField = 4 + reqMetaHeaderSessionTokenField = 5 + reqMetaHeaderBearerTokenField = 6 + reqMetaHeaderOriginField = 7 + reqMetaHeaderNetMagicField = 8 + + reqVerifHeaderBodySignatureField = 1 + reqVerifHeaderMetaSignatureField = 2 + reqVerifHeaderOriginSignatureField = 3 + reqVerifHeaderOriginField = 4 + + respMetaHeaderVersionField = 1 + respMetaHeaderEpochField = 2 + respMetaHeaderTTLField = 3 + respMetaHeaderXHeadersField = 4 + respMetaHeaderOriginField = 5 + respMetaHeaderStatusField = 6 + + respVerifHeaderBodySignatureField = 1 + respVerifHeaderMetaSignatureField = 2 + respVerifHeaderOriginSignatureField = 3 + respVerifHeaderOriginField = 4 +) + +func (c *CreateRequestBody) StableMarshal(buf []byte) []byte { + if c == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, c.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(createReqBodyOwnerField, buf[offset:], c.ownerID) + proto.UInt64Marshal(createReqBodyExpirationField, buf[offset:], c.expiration) + + return buf +} + +func (c *CreateRequestBody) StableSize() (size int) { + if c == nil { + return 0 + } + + size += proto.NestedStructureSize(createReqBodyOwnerField, c.ownerID) + size += proto.UInt64Size(createReqBodyExpirationField, c.expiration) + + return size +} + +func (c *CreateRequestBody) Unmarshal(data []byte) error { + return message.Unmarshal(c, data, new(session.CreateRequest_Body)) +} + +func (c *CreateResponseBody) StableMarshal(buf []byte) []byte { + if c == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, c.StableSize()) + } + + var offset int + + offset += proto.BytesMarshal(createRespBodyIDField, buf[offset:], c.id) + proto.BytesMarshal(createRespBodyKeyField, buf[offset:], c.sessionKey) + + return buf +} + +func (c *CreateResponseBody) StableSize() (size int) { + if c == nil { + return 0 + } + + size += proto.BytesSize(createRespBodyIDField, c.id) + size += proto.BytesSize(createRespBodyKeyField, c.sessionKey) + + return size +} + +func (c *CreateResponseBody) Unmarshal(data []byte) error { + return message.Unmarshal(c, data, new(session.CreateResponse_Body)) +} + +func (x *XHeader) StableMarshal(buf []byte) []byte { + if x == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, x.StableSize()) + } + + var offset int + + offset += proto.StringMarshal(xheaderKeyField, buf[offset:], x.key) + proto.StringMarshal(xheaderValueField, buf[offset:], x.val) + + return buf +} + +func (x *XHeader) StableSize() (size int) { + if x == nil { + return 0 + } + + size += proto.StringSize(xheaderKeyField, x.key) + size += proto.StringSize(xheaderValueField, x.val) + + return size +} + +func (x *XHeader) Unmarshal(data []byte) error { + return message.Unmarshal(x, data, new(session.XHeader)) +} + +func (l *TokenLifetime) StableMarshal(buf []byte) []byte { + if l == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, l.StableSize()) + } + + var offset int + + offset += proto.UInt64Marshal(lifetimeExpirationField, buf[offset:], l.exp) + offset += proto.UInt64Marshal(lifetimeNotValidBeforeField, buf[offset:], l.nbf) + proto.UInt64Marshal(lifetimeIssuedAtField, buf[offset:], l.iat) + + return buf +} + +func (l *TokenLifetime) StableSize() (size int) { + if l == nil { + return 0 + } + + size += proto.UInt64Size(lifetimeExpirationField, l.exp) + size += proto.UInt64Size(lifetimeNotValidBeforeField, l.nbf) + size += proto.UInt64Size(lifetimeIssuedAtField, l.iat) + + return size +} + +func (l *TokenLifetime) Unmarshal(data []byte) error { + return message.Unmarshal(l, data, new(session.SessionToken_Body_TokenLifetime)) +} + +func (c *ObjectSessionContext) StableMarshal(buf []byte) []byte { + if c == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, c.StableSize()) + } + + offset := proto.EnumMarshal(objectCtxVerbField, buf, int32(c.verb)) + proto.NestedStructureMarshalUnchecked(objectCtxTargetField, buf[offset:], objectSessionContextTarget{ + cnr: c.cnr, + objs: c.objs, + }) + + return buf +} + +func (c *ObjectSessionContext) StableSize() (size int) { + if c == nil { + return 0 + } + + size += proto.EnumSize(objectCtxVerbField, int32(c.verb)) + size += proto.NestedStructureSizeUnchecked(objectCtxTargetField, objectSessionContextTarget{ + cnr: c.cnr, + objs: c.objs, + }) + + return size +} + +func (c *ObjectSessionContext) Unmarshal(data []byte) error { + return message.Unmarshal(c, data, new(session.ObjectSessionContext)) +} + +const ( + _ = iota + cnrCtxVerbFNum + cnrCtxWildcardFNum + cnrCtxCidFNum +) + +func (x *ContainerSessionContext) StableMarshal(buf []byte) []byte { + if x == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, x.StableSize()) + } + + var offset int + + offset += proto.EnumMarshal(cnrCtxVerbFNum, buf[offset:], int32(ContainerSessionVerbToGRPCField(x.verb))) + offset += proto.BoolMarshal(cnrCtxWildcardFNum, buf[offset:], x.wildcard) + proto.NestedStructureMarshal(cnrCtxCidFNum, buf[offset:], x.cid) + + return buf +} + +func (x *ContainerSessionContext) StableSize() (size int) { + if x == nil { + return 0 + } + + size += proto.EnumSize(cnrCtxVerbFNum, int32(ContainerSessionVerbToGRPCField(x.verb))) + size += proto.BoolSize(cnrCtxWildcardFNum, x.wildcard) + size += proto.NestedStructureSize(cnrCtxCidFNum, x.cid) + + return size +} + +func (x *ContainerSessionContext) Unmarshal(data []byte) error { + return message.Unmarshal(x, data, new(session.ContainerSessionContext)) +} + +func (t *TokenBody) StableMarshal(buf []byte) []byte { + if t == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, t.StableSize()) + } + + var offset int + + offset += proto.BytesMarshal(sessionTokenBodyIDField, buf[offset:], t.id) + offset += proto.NestedStructureMarshal(sessionTokenBodyOwnerField, buf[offset:], t.ownerID) + offset += proto.NestedStructureMarshal(sessionTokenBodyLifetimeField, buf[offset:], t.lifetime) + offset += proto.BytesMarshal(sessionTokenBodyKeyField, buf[offset:], t.sessionKey) + + if t.ctx != nil { + switch v := t.ctx.(type) { + case *ObjectSessionContext: + proto.NestedStructureMarshal(sessionTokenBodyObjectCtxField, buf[offset:], v) + case *ContainerSessionContext: + proto.NestedStructureMarshal(sessionTokenBodyCnrCtxField, buf[offset:], v) + default: + panic("cannot marshal unknown session token context") + } + } + + return buf +} + +func (t *TokenBody) StableSize() (size int) { + if t == nil { + return 0 + } + + size += proto.BytesSize(sessionTokenBodyIDField, t.id) + size += proto.NestedStructureSize(sessionTokenBodyOwnerField, t.ownerID) + size += proto.NestedStructureSize(sessionTokenBodyLifetimeField, t.lifetime) + size += proto.BytesSize(sessionTokenBodyKeyField, t.sessionKey) + + if t.ctx != nil { + switch v := t.ctx.(type) { + case *ObjectSessionContext: + size += proto.NestedStructureSize(sessionTokenBodyObjectCtxField, v) + case *ContainerSessionContext: + size += proto.NestedStructureSize(sessionTokenBodyCnrCtxField, v) + default: + panic("cannot marshal unknown session token context") + } + } + + return size +} + +func (t *TokenBody) Unmarshal(data []byte) error { + return message.Unmarshal(t, data, new(session.SessionToken_Body)) +} + +func (t *Token) StableMarshal(buf []byte) []byte { + if t == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, t.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(sessionTokenBodyField, buf[offset:], t.body) + proto.NestedStructureMarshal(sessionTokenSignatureField, buf[offset:], t.sig) + + return buf +} + +func (t *Token) StableSize() (size int) { + if t == nil { + return 0 + } + + size += proto.NestedStructureSize(sessionTokenBodyField, t.body) + size += proto.NestedStructureSize(sessionTokenSignatureField, t.sig) + + return size +} + +func (t *Token) Unmarshal(data []byte) error { + return message.Unmarshal(t, data, new(session.SessionToken)) +} + +func (r *RequestMetaHeader) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(reqMetaHeaderVersionField, buf[offset:], r.version) + offset += proto.UInt64Marshal(reqMetaHeaderEpochField, buf[offset:], r.epoch) + offset += proto.UInt32Marshal(reqMetaHeaderTTLField, buf[offset:], r.ttl) + + for i := range r.xHeaders { + offset += proto.NestedStructureMarshal(reqMetaHeaderXHeadersField, buf[offset:], &r.xHeaders[i]) + } + + offset += proto.NestedStructureMarshal(reqMetaHeaderSessionTokenField, buf[offset:], r.sessionToken) + offset += proto.NestedStructureMarshal(reqMetaHeaderBearerTokenField, buf[offset:], r.bearerToken) + offset += proto.NestedStructureMarshal(reqMetaHeaderOriginField, buf[offset:], r.origin) + proto.UInt64Marshal(reqMetaHeaderNetMagicField, buf[offset:], r.netMagic) + + return buf +} + +func (r *RequestMetaHeader) StableSize() (size int) { + if r == nil { + return 0 + } + + if r.version != nil { + size += proto.NestedStructureSize(reqMetaHeaderVersionField, r.version) + } + + size += proto.UInt64Size(reqMetaHeaderEpochField, r.epoch) + size += proto.UInt32Size(reqMetaHeaderTTLField, r.ttl) + + for i := range r.xHeaders { + size += proto.NestedStructureSize(reqMetaHeaderXHeadersField, &r.xHeaders[i]) + } + + size += proto.NestedStructureSize(reqMetaHeaderSessionTokenField, r.sessionToken) + size += proto.NestedStructureSize(reqMetaHeaderBearerTokenField, r.bearerToken) + size += proto.NestedStructureSize(reqMetaHeaderOriginField, r.origin) + size += proto.UInt64Size(reqMetaHeaderNetMagicField, r.netMagic) + + return size +} + +func (r *RequestMetaHeader) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(session.RequestMetaHeader)) +} + +func (r *RequestVerificationHeader) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(reqVerifHeaderBodySignatureField, buf[offset:], r.bodySig) + offset += proto.NestedStructureMarshal(reqVerifHeaderMetaSignatureField, buf[offset:], r.metaSig) + offset += proto.NestedStructureMarshal(reqVerifHeaderOriginSignatureField, buf[offset:], r.originSig) + proto.NestedStructureMarshal(reqVerifHeaderOriginField, buf[offset:], r.origin) + + return buf +} + +func (r *RequestVerificationHeader) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(reqVerifHeaderBodySignatureField, r.bodySig) + size += proto.NestedStructureSize(reqVerifHeaderMetaSignatureField, r.metaSig) + size += proto.NestedStructureSize(reqVerifHeaderOriginSignatureField, r.originSig) + size += proto.NestedStructureSize(reqVerifHeaderOriginField, r.origin) + + return size +} + +func (r *RequestVerificationHeader) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(session.RequestVerificationHeader)) +} + +func (r *ResponseMetaHeader) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(respMetaHeaderVersionField, buf[offset:], r.version) + offset += proto.UInt64Marshal(respMetaHeaderEpochField, buf[offset:], r.epoch) + offset += proto.UInt32Marshal(respMetaHeaderTTLField, buf[offset:], r.ttl) + + for i := range r.xHeaders { + offset += proto.NestedStructureMarshal(respMetaHeaderXHeadersField, buf[offset:], &r.xHeaders[i]) + } + + offset += proto.NestedStructureMarshal(respMetaHeaderOriginField, buf[offset:], r.origin) + proto.NestedStructureMarshal(respMetaHeaderStatusField, buf[offset:], r.status) + + return buf +} + +func (r *ResponseMetaHeader) StableSize() (size int) { + if r == nil { + return 0 + } + + if r.version != nil { + size += proto.NestedStructureSize(respMetaHeaderVersionField, r.version) + } + + size += proto.UInt64Size(respMetaHeaderEpochField, r.epoch) + size += proto.UInt32Size(respMetaHeaderTTLField, r.ttl) + + for i := range r.xHeaders { + size += proto.NestedStructureSize(respMetaHeaderXHeadersField, &r.xHeaders[i]) + } + + size += proto.NestedStructureSize(respMetaHeaderOriginField, r.origin) + size += proto.NestedStructureSize(respMetaHeaderStatusField, r.status) + + return size +} + +func (r *ResponseMetaHeader) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(session.ResponseMetaHeader)) +} + +func (r *ResponseVerificationHeader) StableMarshal(buf []byte) []byte { + if r == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var offset int + + offset += proto.NestedStructureMarshal(respVerifHeaderBodySignatureField, buf[offset:], r.bodySig) + offset += proto.NestedStructureMarshal(respVerifHeaderMetaSignatureField, buf[offset:], r.metaSig) + offset += proto.NestedStructureMarshal(respVerifHeaderOriginSignatureField, buf[offset:], r.originSig) + proto.NestedStructureMarshal(respVerifHeaderOriginField, buf[offset:], r.origin) + + return buf +} + +func (r *ResponseVerificationHeader) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(respVerifHeaderBodySignatureField, r.bodySig) + size += proto.NestedStructureSize(respVerifHeaderMetaSignatureField, r.metaSig) + size += proto.NestedStructureSize(respVerifHeaderOriginSignatureField, r.originSig) + size += proto.NestedStructureSize(respVerifHeaderOriginField, r.origin) + + return size +} + +func (r *ResponseVerificationHeader) Unmarshal(data []byte) error { + return message.Unmarshal(r, data, new(session.ResponseVerificationHeader)) +} diff --git a/apiv2/session/message_test.go b/apiv2/session/message_test.go new file mode 100644 index 0000000..202701d --- /dev/null +++ b/apiv2/session/message_test.go @@ -0,0 +1,27 @@ +package session_test + +import ( + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + rpctest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message/test" + sessiontest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session/test" +) + +func TestMessageConvert(t *testing.T) { + rpctest.TestRPCMessage(t, + func(empty bool) message.Message { return sessiontest.GenerateCreateRequestBody(empty) }, + func(empty bool) message.Message { return sessiontest.GenerateCreateRequest(empty) }, + func(empty bool) message.Message { return sessiontest.GenerateCreateResponseBody(empty) }, + func(empty bool) message.Message { return sessiontest.GenerateCreateResponse(empty) }, + func(empty bool) message.Message { return sessiontest.GenerateTokenLifetime(empty) }, + func(empty bool) message.Message { return sessiontest.GenerateXHeader(empty) }, + func(empty bool) message.Message { return sessiontest.GenerateSessionTokenBody(empty) }, + func(empty bool) message.Message { return sessiontest.GenerateSessionToken(empty) }, + func(empty bool) message.Message { return sessiontest.GenerateRequestMetaHeader(empty) }, + func(empty bool) message.Message { return sessiontest.GenerateRequestVerificationHeader(empty) }, + func(empty bool) message.Message { return sessiontest.GenerateResponseMetaHeader(empty) }, + func(empty bool) message.Message { return sessiontest.GenerateResponseVerificationHeader(empty) }, + func(empty bool) message.Message { return sessiontest.GenerateContainerSessionContext(empty) }, + ) +} diff --git a/apiv2/session/status.go b/apiv2/session/status.go new file mode 100644 index 0000000..0f38c3e --- /dev/null +++ b/apiv2/session/status.go @@ -0,0 +1,32 @@ +package session + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" + statusgrpc "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status/grpc" +) + +// LocalizeFailStatus checks if passed global status.Code is related to session failure and: +// +// then localizes the code and returns true, +// else leaves the code unchanged and returns false. +// +// Arg must not be nil. +func LocalizeFailStatus(c *status.Code) bool { + return status.LocalizeIfInSection(c, uint32(statusgrpc.Section_SECTION_SESSION)) +} + +// GlobalizeFail globalizes local code of session failure. +// +// Arg must not be nil. +func GlobalizeFail(c *status.Code) { + c.GlobalizeSection(uint32(statusgrpc.Section_SECTION_SESSION)) +} + +const ( + // StatusTokenNotFound is a local status.Code value for + // TOKEN_NOT_FOUND session failure. + StatusTokenNotFound status.Code = iota + // StatusTokenExpired is a local status.Code value for + // TOKEN_EXPIRED session failure. + StatusTokenExpired +) diff --git a/apiv2/session/status_test.go b/apiv2/session/status_test.go new file mode 100644 index 0000000..ba739d6 --- /dev/null +++ b/apiv2/session/status_test.go @@ -0,0 +1,15 @@ +package session_test + +import ( + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + statustest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status/test" +) + +func TestStatusCodes(t *testing.T) { + statustest.TestCodes(t, session.LocalizeFailStatus, session.GlobalizeFail, + session.StatusTokenNotFound, 4096, + session.StatusTokenExpired, 4097, + ) +} diff --git a/apiv2/session/string.go b/apiv2/session/string.go new file mode 100644 index 0000000..c01d9e4 --- /dev/null +++ b/apiv2/session/string.go @@ -0,0 +1,47 @@ +package session + +import ( + session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session/grpc" +) + +// String returns string representation of ObjectSessionVerb. +func (x ObjectSessionVerb) String() string { + return ObjectSessionVerbToGRPCField(x).String() +} + +// FromString parses ObjectSessionVerb from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (x *ObjectSessionVerb) FromString(s string) bool { + var g session.ObjectSessionContext_Verb + + ok := g.FromString(s) + + if ok { + *x = ObjectSessionVerbFromGRPCField(g) + } + + return ok +} + +// String returns string representation of ContainerSessionVerb. +func (x ContainerSessionVerb) String() string { + return ContainerSessionVerbToGRPCField(x).String() +} + +// FromString parses ContainerSessionVerb from a string representation. +// It is a reverse action to String(). +// +// Returns true if s was parsed successfully. +func (x *ContainerSessionVerb) FromString(s string) bool { + var g session.ContainerSessionContext_Verb + + ok := g.FromString(s) + + if ok { + *x = ContainerSessionVerbFromGRPCField(g) + } + + return ok +} diff --git a/apiv2/session/test/generate.go b/apiv2/session/test/generate.go new file mode 100644 index 0000000..59d7f3f --- /dev/null +++ b/apiv2/session/test/generate.go @@ -0,0 +1,251 @@ +package sessiontest + +import ( + crand "crypto/rand" + "math/rand" + "time" + + acltest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl/test" + refstest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs/test" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + statustest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status/test" +) + +func GenerateCreateRequestBody(empty bool) *session.CreateRequestBody { + m := new(session.CreateRequestBody) + + if !empty { + m.SetExpiration(555) + m.SetOwnerID(refstest.GenerateOwnerID(false)) + } + + return m +} + +func GenerateCreateRequest(empty bool) *session.CreateRequest { + m := new(session.CreateRequest) + + if !empty { + m.SetBody(GenerateCreateRequestBody(false)) + } + + m.SetMetaHeader(GenerateRequestMetaHeader(empty)) + m.SetVerificationHeader(GenerateRequestVerificationHeader(empty)) + + return m +} + +func GenerateCreateResponseBody(empty bool) *session.CreateResponseBody { + m := new(session.CreateResponseBody) + + if !empty { + id := make([]byte, 16) + _, _ = crand.Read(id) + + m.SetID(id) + m.SetSessionKey([]byte{4, 5, 6}) + } + + return m +} + +func GenerateCreateResponse(empty bool) *session.CreateResponse { + m := new(session.CreateResponse) + + if !empty { + m.SetBody(GenerateCreateResponseBody(false)) + } + + m.SetMetaHeader(GenerateResponseMetaHeader(empty)) + m.SetVerificationHeader(GenerateResponseVerificationHeader(empty)) + + return m +} + +func GenerateResponseVerificationHeader(empty bool) *session.ResponseVerificationHeader { + return generateResponseVerificationHeader(empty, true) +} + +func generateResponseVerificationHeader(empty, withOrigin bool) *session.ResponseVerificationHeader { + m := new(session.ResponseVerificationHeader) + + if !empty { + m.SetBodySignature(refstest.GenerateSignature(false)) + } + + m.SetMetaSignature(refstest.GenerateSignature(empty)) + m.SetOriginSignature(refstest.GenerateSignature(empty)) + + if withOrigin { + m.SetOrigin(generateResponseVerificationHeader(empty, false)) + } + + return m +} + +func GenerateResponseMetaHeader(empty bool) *session.ResponseMetaHeader { + return generateResponseMetaHeader(empty, true) +} + +func generateResponseMetaHeader(empty, withOrigin bool) *session.ResponseMetaHeader { + m := new(session.ResponseMetaHeader) + + if !empty { + m.SetEpoch(13) + m.SetTTL(100) + } + + m.SetXHeaders(GenerateXHeaders(empty)) + m.SetVersion(refstest.GenerateVersion(empty)) + m.SetStatus(statustest.Status(empty)) + + if withOrigin { + m.SetOrigin(generateResponseMetaHeader(empty, false)) + } + + return m +} + +func GenerateRequestVerificationHeader(empty bool) *session.RequestVerificationHeader { + return generateRequestVerificationHeader(empty, true) +} + +func generateRequestVerificationHeader(empty, withOrigin bool) *session.RequestVerificationHeader { + m := new(session.RequestVerificationHeader) + + if !empty { + m.SetBodySignature(refstest.GenerateSignature(false)) + } + + m.SetMetaSignature(refstest.GenerateSignature(empty)) + m.SetOriginSignature(refstest.GenerateSignature(empty)) + + if withOrigin { + m.SetOrigin(generateRequestVerificationHeader(empty, false)) + } + + return m +} + +func GenerateRequestMetaHeader(empty bool) *session.RequestMetaHeader { + return generateRequestMetaHeader(empty, true) +} + +func generateRequestMetaHeader(empty, withOrigin bool) *session.RequestMetaHeader { + m := new(session.RequestMetaHeader) + + if !empty { + m.SetEpoch(13) + m.SetTTL(100) + m.SetNetworkMagic(1337) + } + + m.SetXHeaders(GenerateXHeaders(empty)) + m.SetVersion(refstest.GenerateVersion(empty)) + m.SetSessionToken(GenerateSessionToken(empty)) + m.SetBearerToken(acltest.GenerateBearerToken(empty)) + + if withOrigin { + m.SetOrigin(generateRequestMetaHeader(empty, false)) + } + + return m +} + +func GenerateSessionToken(empty bool) *session.Token { + m := new(session.Token) + + if !empty { + m.SetBody(GenerateSessionTokenBody(false)) + } + + m.SetSignature(refstest.GenerateSignature(empty)) + + return m +} + +func GenerateSessionTokenBody(empty bool) *session.TokenBody { + m := new(session.TokenBody) + + if !empty { + id := make([]byte, 16) + _, _ = crand.Read(id) + + m.SetID(id) + m.SetSessionKey([]byte{2}) + m.SetOwnerID(refstest.GenerateOwnerID(false)) + m.SetLifetime(GenerateTokenLifetime(false)) + + switch randomInt(2) { + case 0: + m.SetContext(GenerateObjectSessionContext(false)) + case 1: + m.SetContext(GenerateContainerSessionContext(false)) + } + } + + return m +} + +func GenerateTokenLifetime(empty bool) *session.TokenLifetime { + m := new(session.TokenLifetime) + + if !empty { + m.SetExp(1) + m.SetIat(2) + m.SetExp(3) + } + + return m +} + +func GenerateObjectSessionContext(empty bool) *session.ObjectSessionContext { + m := new(session.ObjectSessionContext) + + if !empty { + m.SetVerb(session.ObjectVerbHead) + m.SetTarget(refstest.GenerateContainerID(false), refstest.GenerateObjectIDs(false)...) + } + + return m +} + +func GenerateContainerSessionContext(empty bool) *session.ContainerSessionContext { + m := new(session.ContainerSessionContext) + + if !empty { + m.SetVerb(session.ContainerVerbDelete) + m.SetWildcard(true) + m.SetContainerID(refstest.GenerateContainerID(false)) + } + + return m +} + +func GenerateXHeader(empty bool) *session.XHeader { + m := new(session.XHeader) + + if !empty { + m.SetKey("key") + m.SetValue("val") + } + + return m +} + +func GenerateXHeaders(empty bool) []session.XHeader { + var xs []session.XHeader + + if !empty { + xs = append(xs, + *GenerateXHeader(false), + *GenerateXHeader(false), + ) + } + + return xs +} + +func randomInt(n int) int { + return rand.New(rand.NewSource(time.Now().UnixNano())).Intn(n) +} diff --git a/apiv2/session/types.go b/apiv2/session/types.go new file mode 100644 index 0000000..9a57947 --- /dev/null +++ b/apiv2/session/types.go @@ -0,0 +1,836 @@ +package session + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto" +) + +type CreateRequestBody struct { + ownerID *refs.OwnerID + + expiration uint64 +} + +type CreateRequest struct { + body *CreateRequestBody + + RequestHeaders +} + +type CreateResponseBody struct { + id []byte + + sessionKey []byte +} + +type CreateResponse struct { + body *CreateResponseBody + + ResponseHeaders +} + +type XHeader struct { + key, val string +} + +type TokenLifetime struct { + exp, nbf, iat uint64 +} + +type ObjectSessionVerb uint32 + +type objectSessionContextTarget struct { + cnr *refs.ContainerID + + objs []refs.ObjectID +} + +const ( + _ = iota + fNumObjectTargetContainer + fNumObjectTargetObjects +) + +func (x objectSessionContextTarget) StableMarshal(buf []byte) []byte { + if buf == nil { + buf = make([]byte, x.StableSize()) + } + + offset := proto.NestedStructureMarshal(fNumObjectTargetContainer, buf, x.cnr) + + for i := range x.objs { + offset += proto.NestedStructureMarshal(fNumObjectTargetObjects, buf[offset:], &x.objs[i]) + } + + return buf +} + +func (x objectSessionContextTarget) StableSize() (size int) { + size += proto.NestedStructureSize(fNumObjectTargetContainer, x.cnr) + + for i := range x.objs { + size += proto.NestedStructureSize(fNumObjectTargetObjects, &x.objs[i]) + } + + return size +} + +type ObjectSessionContext struct { + verb ObjectSessionVerb + + cnr *refs.ContainerID + + objs []refs.ObjectID +} + +type TokenContext interface { + sessionTokenContext() +} + +// Deprecated: use TokenContext instead. +// +//nolint:revive +type SessionTokenContext = TokenContext + +type TokenBody struct { + id []byte + + ownerID *refs.OwnerID + + lifetime *TokenLifetime + + sessionKey []byte + + ctx TokenContext +} + +// Deprecated: use TokenBody instead. +// +//nolint:revive +type SessionTokenBody = TokenBody + +type Token struct { + body *TokenBody + + sig *refs.Signature +} + +// Deprecated: use Token instead. +// +//nolint:revive +type SessionToken = Token + +type RequestVerificationHeader struct { + bodySig, metaSig, originSig *refs.Signature + + origin *RequestVerificationHeader +} + +type RequestMetaHeader struct { + version *refs.Version + + ttl uint32 + + epoch uint64 + + xHeaders []XHeader + + sessionToken *Token + + bearerToken *acl.BearerToken + + origin *RequestMetaHeader + + netMagic uint64 +} + +type ResponseVerificationHeader struct { + bodySig, metaSig, originSig *refs.Signature + + origin *ResponseVerificationHeader +} + +type ResponseMetaHeader struct { + version *refs.Version + + ttl uint32 + + epoch uint64 + + xHeaders []XHeader + + origin *ResponseMetaHeader + + status *status.Status +} + +const ( + ObjectVerbUnknown ObjectSessionVerb = iota + ObjectVerbPut + ObjectVerbGet + ObjectVerbHead + ObjectVerbSearch + ObjectVerbDelete + ObjectVerbRange + ObjectVerbRangeHash + ObjectVerbPatch +) + +func (c *CreateRequestBody) GetOwnerID() *refs.OwnerID { + if c != nil { + return c.ownerID + } + + return nil +} + +func (c *CreateRequestBody) SetOwnerID(v *refs.OwnerID) { + c.ownerID = v +} + +func (c *CreateRequestBody) GetExpiration() uint64 { + if c != nil { + return c.expiration + } + + return 0 +} + +func (c *CreateRequestBody) SetExpiration(v uint64) { + c.expiration = v +} + +func (c *CreateRequest) GetBody() *CreateRequestBody { + if c != nil { + return c.body + } + + return nil +} + +func (c *CreateRequest) SetBody(v *CreateRequestBody) { + c.body = v +} + +func (c *CreateRequest) GetMetaHeader() *RequestMetaHeader { + if c != nil { + return c.metaHeader + } + + return nil +} + +func (c *CreateRequest) SetMetaHeader(v *RequestMetaHeader) { + c.metaHeader = v +} + +func (c *CreateRequest) GetVerificationHeader() *RequestVerificationHeader { + if c != nil { + return c.verifyHeader + } + + return nil +} + +func (c *CreateRequest) SetVerificationHeader(v *RequestVerificationHeader) { + c.verifyHeader = v +} + +func (c *CreateResponseBody) GetID() []byte { + if c != nil { + return c.id + } + + return nil +} + +func (c *CreateResponseBody) SetID(v []byte) { + c.id = v +} + +func (c *CreateResponseBody) GetSessionKey() []byte { + if c != nil { + return c.sessionKey + } + + return nil +} + +func (c *CreateResponseBody) SetSessionKey(v []byte) { + c.sessionKey = v +} + +func (c *CreateResponse) GetBody() *CreateResponseBody { + if c != nil { + return c.body + } + + return nil +} + +func (c *CreateResponse) SetBody(v *CreateResponseBody) { + c.body = v +} + +func (c *CreateResponse) GetMetaHeader() *ResponseMetaHeader { + if c != nil { + return c.metaHeader + } + + return nil +} + +func (c *CreateResponse) SetMetaHeader(v *ResponseMetaHeader) { + c.metaHeader = v +} + +func (c *CreateResponse) GetVerificationHeader() *ResponseVerificationHeader { + if c != nil { + return c.verifyHeader + } + + return nil +} + +func (c *CreateResponse) SetVerificationHeader(v *ResponseVerificationHeader) { + c.verifyHeader = v +} + +func (x *XHeader) GetKey() string { + if x != nil { + return x.key + } + + return "" +} + +func (x *XHeader) SetKey(v string) { + x.key = v +} + +func (x *XHeader) GetValue() string { + if x != nil { + return x.val + } + + return "" +} + +func (x *XHeader) SetValue(v string) { + x.val = v +} + +func (r *RequestVerificationHeader) GetBodySignature() *refs.Signature { + if r != nil { + return r.bodySig + } + + return nil +} + +func (r *RequestVerificationHeader) SetBodySignature(v *refs.Signature) { + r.bodySig = v +} + +func (r *RequestVerificationHeader) GetMetaSignature() *refs.Signature { + if r != nil { + return r.metaSig + } + + return nil +} + +func (r *RequestVerificationHeader) SetMetaSignature(v *refs.Signature) { + r.metaSig = v +} + +func (r *RequestVerificationHeader) GetOriginSignature() *refs.Signature { + if r != nil { + return r.originSig + } + + return nil +} + +func (r *RequestVerificationHeader) SetOriginSignature(v *refs.Signature) { + r.originSig = v +} + +func (r *RequestVerificationHeader) GetOrigin() *RequestVerificationHeader { + if r != nil { + return r.origin + } + + return nil +} + +func (r *RequestVerificationHeader) SetOrigin(v *RequestVerificationHeader) { + r.origin = v +} + +func (r *RequestMetaHeader) GetVersion() *refs.Version { + if r != nil { + return r.version + } + + return nil +} + +func (r *RequestMetaHeader) SetVersion(v *refs.Version) { + r.version = v +} + +func (r *RequestMetaHeader) GetTTL() uint32 { + if r != nil { + return r.ttl + } + + return 0 +} + +func (r *RequestMetaHeader) SetTTL(v uint32) { + r.ttl = v +} + +func (r *RequestMetaHeader) GetEpoch() uint64 { + if r != nil { + return r.epoch + } + + return 0 +} + +func (r *RequestMetaHeader) SetEpoch(v uint64) { + r.epoch = v +} + +func (r *RequestMetaHeader) GetXHeaders() []XHeader { + if r != nil { + return r.xHeaders + } + + return nil +} + +func (r *RequestMetaHeader) SetXHeaders(v []XHeader) { + r.xHeaders = v +} + +func (r *RequestMetaHeader) GetSessionToken() *Token { + if r != nil { + return r.sessionToken + } + + return nil +} + +func (r *RequestMetaHeader) SetSessionToken(v *Token) { + r.sessionToken = v +} + +func (r *RequestMetaHeader) GetBearerToken() *acl.BearerToken { + if r != nil { + return r.bearerToken + } + + return nil +} + +func (r *RequestMetaHeader) SetBearerToken(v *acl.BearerToken) { + r.bearerToken = v +} + +func (r *RequestMetaHeader) GetOrigin() *RequestMetaHeader { + if r != nil { + return r.origin + } + + return nil +} + +func (r *RequestMetaHeader) SetOrigin(v *RequestMetaHeader) { + r.origin = v +} + +// GetNetworkMagic returns NeoFS network magic. +func (r *RequestMetaHeader) GetNetworkMagic() uint64 { + if r != nil { + return r.netMagic + } + + return 0 +} + +// SetNetworkMagic sets NeoFS network magic. +func (r *RequestMetaHeader) SetNetworkMagic(v uint64) { + r.netMagic = v +} + +func (l *TokenLifetime) GetExp() uint64 { + if l != nil { + return l.exp + } + + return 0 +} + +func (l *TokenLifetime) SetExp(v uint64) { + l.exp = v +} + +func (l *TokenLifetime) GetNbf() uint64 { + if l != nil { + return l.nbf + } + + return 0 +} + +func (l *TokenLifetime) SetNbf(v uint64) { + l.nbf = v +} + +func (l *TokenLifetime) GetIat() uint64 { + if l != nil { + return l.iat + } + + return 0 +} + +func (l *TokenLifetime) SetIat(v uint64) { + l.iat = v +} + +func (r *ResponseVerificationHeader) GetBodySignature() *refs.Signature { + if r != nil { + return r.bodySig + } + + return nil +} + +func (r *ResponseVerificationHeader) SetBodySignature(v *refs.Signature) { + r.bodySig = v +} + +func (r *ResponseVerificationHeader) GetMetaSignature() *refs.Signature { + if r != nil { + return r.metaSig + } + + return nil +} + +func (r *ResponseVerificationHeader) SetMetaSignature(v *refs.Signature) { + r.metaSig = v +} + +func (r *ResponseVerificationHeader) GetOriginSignature() *refs.Signature { + if r != nil { + return r.originSig + } + + return nil +} + +func (r *ResponseVerificationHeader) SetOriginSignature(v *refs.Signature) { + r.originSig = v +} + +func (r *ResponseVerificationHeader) GetOrigin() *ResponseVerificationHeader { + if r != nil { + return r.origin + } + + return nil +} + +func (r *ResponseVerificationHeader) SetOrigin(v *ResponseVerificationHeader) { + r.origin = v +} + +func (r *ResponseMetaHeader) GetVersion() *refs.Version { + if r != nil { + return r.version + } + + return nil +} + +func (r *ResponseMetaHeader) SetVersion(v *refs.Version) { + r.version = v +} + +func (r *ResponseMetaHeader) GetTTL() uint32 { + if r != nil { + return r.ttl + } + + return 0 +} + +func (r *ResponseMetaHeader) SetTTL(v uint32) { + r.ttl = v +} + +func (r *ResponseMetaHeader) GetEpoch() uint64 { + if r != nil { + return r.epoch + } + + return 0 +} + +func (r *ResponseMetaHeader) SetEpoch(v uint64) { + r.epoch = v +} + +func (r *ResponseMetaHeader) GetXHeaders() []XHeader { + if r != nil { + return r.xHeaders + } + + return nil +} + +func (r *ResponseMetaHeader) SetXHeaders(v []XHeader) { + r.xHeaders = v +} + +func (r *ResponseMetaHeader) GetOrigin() *ResponseMetaHeader { + if r != nil { + return r.origin + } + + return nil +} + +func (r *ResponseMetaHeader) SetOrigin(v *ResponseMetaHeader) { + r.origin = v +} + +// GetStatus returns response status. +func (r *ResponseMetaHeader) GetStatus() *status.Status { + if r != nil { + return r.status + } + + return nil +} + +// SetStatus sets response status. +func (r *ResponseMetaHeader) SetStatus(v *status.Status) { + r.status = v +} + +// SetStatus sets status of the message which can carry ResponseMetaHeader. +// +// Sets status field on the "highest" level of meta headers. +// If meta header is missing in message, it is allocated. +func SetStatus(msg interface { + GetMetaHeader() *ResponseMetaHeader + SetMetaHeader(*ResponseMetaHeader) +}, st *status.Status, +) { + meta := msg.GetMetaHeader() + if meta == nil { + meta = new(ResponseMetaHeader) + msg.SetMetaHeader(meta) + } + + meta.SetStatus(st) +} + +func (c *ObjectSessionContext) sessionTokenContext() {} + +func (c *ObjectSessionContext) GetVerb() ObjectSessionVerb { + if c != nil { + return c.verb + } + + return ObjectVerbUnknown +} + +func (c *ObjectSessionContext) SetVerb(v ObjectSessionVerb) { + c.verb = v +} + +func (c *ObjectSessionContext) GetContainer() *refs.ContainerID { + if c != nil { + return c.cnr + } + + return nil +} + +func (c *ObjectSessionContext) GetObjects() []refs.ObjectID { + if c != nil { + return c.objs + } + + return nil +} + +func (c *ObjectSessionContext) SetTarget(cnr *refs.ContainerID, objs ...refs.ObjectID) { + c.cnr = cnr + c.objs = objs +} + +func (t *TokenBody) GetID() []byte { + if t != nil { + return t.id + } + + return nil +} + +func (t *TokenBody) SetID(v []byte) { + t.id = v +} + +func (t *TokenBody) GetOwnerID() *refs.OwnerID { + if t != nil { + return t.ownerID + } + + return nil +} + +func (t *TokenBody) SetOwnerID(v *refs.OwnerID) { + t.ownerID = v +} + +func (t *TokenBody) GetLifetime() *TokenLifetime { + if t != nil { + return t.lifetime + } + + return nil +} + +func (t *TokenBody) SetLifetime(v *TokenLifetime) { + t.lifetime = v +} + +func (t *TokenBody) GetSessionKey() []byte { + if t != nil { + return t.sessionKey + } + + return nil +} + +func (t *TokenBody) SetSessionKey(v []byte) { + t.sessionKey = v +} + +func (t *TokenBody) GetContext() TokenContext { + if t != nil { + return t.ctx + } + + return nil +} + +func (t *TokenBody) SetContext(v TokenContext) { + t.ctx = v +} + +func (t *Token) GetBody() *TokenBody { + if t != nil { + return t.body + } + + return nil +} + +func (t *Token) SetBody(v *TokenBody) { + t.body = v +} + +func (t *Token) GetSignature() *refs.Signature { + if t != nil { + return t.sig + } + + return nil +} + +func (t *Token) SetSignature(v *refs.Signature) { + t.sig = v +} + +// ContainerSessionVerb represents NeoFS API v2 +// session.ContainerSessionContext.Verb enumeration. +type ContainerSessionVerb uint32 + +const ( + // ContainerVerbUnknown corresponds to VERB_UNSPECIFIED enum value. + ContainerVerbUnknown ContainerSessionVerb = iota + + // ContainerVerbPut corresponds to PUT enum value. + ContainerVerbPut + + // ContainerVerbDelete corresponds to DELETE enum value. + ContainerVerbDelete + + // ContainerVerbSetEACL corresponds to SETEACL enum value. + ContainerVerbSetEACL +) + +// ContainerSessionContext represents structure of the +// NeoFS API v2 session.ContainerSessionContext message. +type ContainerSessionContext struct { + verb ContainerSessionVerb + + wildcard bool + + cid *refs.ContainerID +} + +func (x *ContainerSessionContext) sessionTokenContext() {} + +// Verb returns type of request for which the token is issued. +func (x *ContainerSessionContext) Verb() ContainerSessionVerb { + if x != nil { + return x.verb + } + + return ContainerVerbUnknown +} + +// SetVerb sets type of request for which the token is issued. +func (x *ContainerSessionContext) SetVerb(v ContainerSessionVerb) { + x.verb = v +} + +// Wildcard returns wildcard flag of the container session. +func (x *ContainerSessionContext) Wildcard() bool { + if x != nil { + return x.wildcard + } + + return false +} + +// SetWildcard sets wildcard flag of the container session. +func (x *ContainerSessionContext) SetWildcard(v bool) { + x.wildcard = v +} + +// ContainerID returns identifier of the container related to the session. +func (x *ContainerSessionContext) ContainerID() *refs.ContainerID { + if x != nil { + return x.cid + } + + return nil +} + +// SetContainerID sets identifier of the container related to the session. +func (x *ContainerSessionContext) SetContainerID(v *refs.ContainerID) { + x.cid = v +} diff --git a/apiv2/session/util.go b/apiv2/session/util.go new file mode 100644 index 0000000..0e94066 --- /dev/null +++ b/apiv2/session/util.go @@ -0,0 +1,167 @@ +package session + +import ( + session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session/grpc" +) + +// RequestHeaders represents common part of +// all NeoFS requests including headers. +type RequestHeaders struct { + metaHeader *RequestMetaHeader + + verifyHeader *RequestVerificationHeader +} + +// GetMetaHeader returns meta header of the request. +func (c *RequestHeaders) GetMetaHeader() *RequestMetaHeader { + if c != nil { + return c.metaHeader + } + + return nil +} + +// SetMetaHeader sets meta header of the request. +func (c *RequestHeaders) SetMetaHeader(v *RequestMetaHeader) { + c.metaHeader = v +} + +// GetVerificationHeader returns verification header of the request. +func (c *RequestHeaders) GetVerificationHeader() *RequestVerificationHeader { + if c != nil { + return c.verifyHeader + } + + return nil +} + +// SetVerificationHeader sets verification header of the request. +func (c *RequestHeaders) SetVerificationHeader(v *RequestVerificationHeader) { + c.verifyHeader = v +} + +func (c *RequestHeaders) ToMessage(m interface { + SetMetaHeader(*session.RequestMetaHeader) + SetVerifyHeader(*session.RequestVerificationHeader) +}, +) { + m.SetMetaHeader(c.metaHeader.ToGRPCMessage().(*session.RequestMetaHeader)) + m.SetVerifyHeader(c.verifyHeader.ToGRPCMessage().(*session.RequestVerificationHeader)) +} + +func (c *RequestHeaders) FromMessage(m interface { + GetMetaHeader() *session.RequestMetaHeader + GetVerifyHeader() *session.RequestVerificationHeader +}, +) error { + metaHdr := m.GetMetaHeader() + if metaHdr == nil { + c.metaHeader = nil + } else { + if c.metaHeader == nil { + c.metaHeader = new(RequestMetaHeader) + } + + err := c.metaHeader.FromGRPCMessage(metaHdr) + if err != nil { + return err + } + } + + verifyHdr := m.GetVerifyHeader() + if verifyHdr == nil { + c.verifyHeader = nil + } else { + if c.verifyHeader == nil { + c.verifyHeader = new(RequestVerificationHeader) + } + + err := c.verifyHeader.FromGRPCMessage(verifyHdr) + if err != nil { + return err + } + } + + return nil +} + +// ResponseHeaders represents common part of +// all NeoFS responses including headers. +type ResponseHeaders struct { + metaHeader *ResponseMetaHeader + + verifyHeader *ResponseVerificationHeader +} + +// GetMetaHeader returns meta header of the response. +func (c *ResponseHeaders) GetMetaHeader() *ResponseMetaHeader { + if c != nil { + return c.metaHeader + } + + return nil +} + +// SetMetaHeader sets meta header of the response. +func (c *ResponseHeaders) SetMetaHeader(v *ResponseMetaHeader) { + c.metaHeader = v +} + +// GetVerificationHeader returns verification header of the response. +func (c *ResponseHeaders) GetVerificationHeader() *ResponseVerificationHeader { + if c != nil { + return c.verifyHeader + } + + return nil +} + +// SetVerificationHeader sets verification header of the response. +func (c *ResponseHeaders) SetVerificationHeader(v *ResponseVerificationHeader) { + c.verifyHeader = v +} + +func (c *ResponseHeaders) ToMessage(m interface { + SetMetaHeader(*session.ResponseMetaHeader) + SetVerifyHeader(*session.ResponseVerificationHeader) +}, +) { + m.SetMetaHeader(c.metaHeader.ToGRPCMessage().(*session.ResponseMetaHeader)) + m.SetVerifyHeader(c.verifyHeader.ToGRPCMessage().(*session.ResponseVerificationHeader)) +} + +func (c *ResponseHeaders) FromMessage(m interface { + GetMetaHeader() *session.ResponseMetaHeader + GetVerifyHeader() *session.ResponseVerificationHeader +}, +) error { + metaHdr := m.GetMetaHeader() + if metaHdr == nil { + c.metaHeader = nil + } else { + if c.metaHeader == nil { + c.metaHeader = new(ResponseMetaHeader) + } + + err := c.metaHeader.FromGRPCMessage(metaHdr) + if err != nil { + return err + } + } + + verifyHdr := m.GetVerifyHeader() + if verifyHdr == nil { + c.verifyHeader = nil + } else { + if c.verifyHeader == nil { + c.verifyHeader = new(ResponseVerificationHeader) + } + + err := c.verifyHeader.FromGRPCMessage(verifyHdr) + if err != nil { + return err + } + } + + return nil +} diff --git a/apiv2/session/xheaders.go b/apiv2/session/xheaders.go new file mode 100644 index 0000000..c575d5f --- /dev/null +++ b/apiv2/session/xheaders.go @@ -0,0 +1,34 @@ +package session + +// ReservedXHeaderPrefix is a prefix of keys to "well-known" X-headers. +const ReservedXHeaderPrefix = "__SYSTEM__" + +const ( + // XHeaderNetmapEpoch is a key to the reserved X-header that specifies netmap epoch + // to use for object placement calculation. If set to '0' or not set, the current + // epoch only will be used. + XHeaderNetmapEpoch = ReservedXHeaderPrefix + "NETMAP_EPOCH" + + // XHeaderNetmapLookupDepth is a key to the reserved X-header that limits + // how many past epochs back the node will can lookup. If set to '0' or not + // set, the current epoch only will be used. + XHeaderNetmapLookupDepth = ReservedXHeaderPrefix + "NETMAP_LOOKUP_DEPTH" +) + +// ReservedXHeaderPrefixNeoFS is a prefix of keys to "well-known" X-headers. +// Deprecated: use ReservedXHeaderPrefix. +const ReservedXHeaderPrefixNeoFS = "__NEOFS__" + +const ( + // XHeaderNetmapEpochNeoFS is a key to the reserved X-header that specifies netmap epoch + // to use for object placement calculation. If set to '0' or not set, the current + // epoch only will be used. + // Deprecated: use XHeaderNetmapEpoch. + XHeaderNetmapEpochNeoFS = ReservedXHeaderPrefixNeoFS + "NETMAP_EPOCH" + + // XHeaderNetmapLookupDepthNeoFS is a key to the reserved X-header that limits + // how many past epochs back the node will can lookup. If set to '0' or not + // set, the current epoch only will be used. + // Deprecated: use XHeaderNetmapLookupDepth. + XHeaderNetmapLookupDepthNeoFS = ReservedXHeaderPrefixNeoFS + "NETMAP_LOOKUP_DEPTH" +) diff --git a/apiv2/signature/body.go b/apiv2/signature/body.go new file mode 100644 index 0000000..b6c04c2 --- /dev/null +++ b/apiv2/signature/body.go @@ -0,0 +1,116 @@ +package signature + +import ( + "fmt" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/apemanager" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" +) + +// nolint:funlen +func serviceMessageBody(req any) stableMarshaler { + switch v := req.(type) { + default: + panic(fmt.Sprintf("unsupported session message %T", req)) + + /* Accounting */ + case *accounting.BalanceRequest: + return v.GetBody() + case *accounting.BalanceResponse: + return v.GetBody() + + /* Session */ + case *session.CreateRequest: + return v.GetBody() + case *session.CreateResponse: + return v.GetBody() + + /* Container */ + case *container.PutRequest: + return v.GetBody() + case *container.PutResponse: + return v.GetBody() + case *container.DeleteRequest: + return v.GetBody() + case *container.DeleteResponse: + return v.GetBody() + case *container.GetRequest: + return v.GetBody() + case *container.GetResponse: + return v.GetBody() + case *container.ListRequest: + return v.GetBody() + case *container.ListResponse: + return v.GetBody() + + /* Object */ + case *object.PutRequest: + return v.GetBody() + case *object.PutResponse: + return v.GetBody() + case *object.GetRequest: + return v.GetBody() + case *object.GetResponse: + return v.GetBody() + case *object.HeadRequest: + return v.GetBody() + case *object.HeadResponse: + return v.GetBody() + case *object.SearchRequest: + return v.GetBody() + case *object.SearchResponse: + return v.GetBody() + case *object.DeleteRequest: + return v.GetBody() + case *object.DeleteResponse: + return v.GetBody() + case *object.GetRangeRequest: + return v.GetBody() + case *object.GetRangeResponse: + return v.GetBody() + case *object.GetRangeHashRequest: + return v.GetBody() + case *object.GetRangeHashResponse: + return v.GetBody() + case *object.PutSingleRequest: + return v.GetBody() + case *object.PutSingleResponse: + return v.GetBody() + case *object.PatchRequest: + return v.GetBody() + case *object.PatchResponse: + return v.GetBody() + + /* Netmap */ + case *netmap.LocalNodeInfoRequest: + return v.GetBody() + case *netmap.LocalNodeInfoResponse: + return v.GetBody() + case *netmap.NetworkInfoRequest: + return v.GetBody() + case *netmap.NetworkInfoResponse: + return v.GetBody() + case *netmap.SnapshotRequest: + return v.GetBody() + case *netmap.SnapshotResponse: + return v.GetBody() + + /* APEManager */ + case *apemanager.AddChainRequest: + return v.GetBody() + case *apemanager.AddChainResponse: + return v.GetBody() + case *apemanager.RemoveChainRequest: + return v.GetBody() + case *apemanager.RemoveChainResponse: + return v.GetBody() + case *apemanager.ListChainsRequest: + return v.GetBody() + case *apemanager.ListChainsResponse: + return v.GetBody() + } +} diff --git a/apiv2/signature/marshaller.go b/apiv2/signature/marshaller.go new file mode 100644 index 0000000..ff9beb3 --- /dev/null +++ b/apiv2/signature/marshaller.go @@ -0,0 +1,26 @@ +package signature + +type stableMarshaler interface { + StableMarshal([]byte) []byte + StableSize() int +} + +type StableMarshalerWrapper struct { + SM stableMarshaler +} + +func (s StableMarshalerWrapper) ReadSignedData(buf []byte) ([]byte, error) { + if s.SM != nil { + return s.SM.StableMarshal(buf), nil + } + + return nil, nil +} + +func (s StableMarshalerWrapper) SignedDataSize() int { + if s.SM != nil { + return s.SM.StableSize() + } + + return 0 +} diff --git a/apiv2/signature/sign.go b/apiv2/signature/sign.go new file mode 100644 index 0000000..1dca5a2 --- /dev/null +++ b/apiv2/signature/sign.go @@ -0,0 +1,122 @@ +package signature + +import ( + "crypto/ecdsa" + "fmt" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/signature" + "golang.org/x/sync/errgroup" +) + +type serviceRequest interface { + GetMetaHeader() *session.RequestMetaHeader + GetVerificationHeader() *session.RequestVerificationHeader + SetVerificationHeader(*session.RequestVerificationHeader) +} + +type serviceResponse interface { + GetMetaHeader() *session.ResponseMetaHeader + GetVerificationHeader() *session.ResponseVerificationHeader + SetVerificationHeader(*session.ResponseVerificationHeader) +} + +type signatureReceiver interface { + SetBodySignature(*refs.Signature) + SetMetaSignature(*refs.Signature) + SetOriginSignature(*refs.Signature) +} + +// SignServiceMessage signes service message with key. +func SignServiceMessage(key *ecdsa.PrivateKey, msg any) error { + switch v := msg.(type) { + case nil: + return nil + case serviceRequest: + return signServiceRequest(key, v) + case serviceResponse: + return signServiceResponse(key, v) + default: + panic(fmt.Sprintf("unsupported session message %T", v)) + } +} + +func signServiceRequest(key *ecdsa.PrivateKey, v serviceRequest) error { + result := &session.RequestVerificationHeader{} + body := serviceMessageBody(v) + meta := v.GetMetaHeader() + header := v.GetVerificationHeader() + if err := signMessageParts(key, body, meta, header, header != nil, result); err != nil { + return err + } + result.SetOrigin(header) + v.SetVerificationHeader(result) + return nil +} + +func signServiceResponse(key *ecdsa.PrivateKey, v serviceResponse) error { + result := &session.ResponseVerificationHeader{} + body := serviceMessageBody(v) + meta := v.GetMetaHeader() + header := v.GetVerificationHeader() + if err := signMessageParts(key, body, meta, header, header != nil, result); err != nil { + return err + } + result.SetOrigin(header) + v.SetVerificationHeader(result) + return nil +} + +func signMessageParts(key *ecdsa.PrivateKey, body, meta, header stableMarshaler, hasHeader bool, result signatureReceiver) error { + eg := &errgroup.Group{} + if !hasHeader { + // sign session message body + eg.Go(func() error { + if err := signServiceMessagePart(key, body, result.SetBodySignature); err != nil { + return fmt.Errorf("could not sign body: %w", err) + } + return nil + }) + } + + // sign meta header + eg.Go(func() error { + if err := signServiceMessagePart(key, meta, result.SetMetaSignature); err != nil { + return fmt.Errorf("could not sign meta header: %w", err) + } + return nil + }) + + // sign verification header origin + eg.Go(func() error { + if err := signServiceMessagePart(key, header, result.SetOriginSignature); err != nil { + return fmt.Errorf("could not sign origin of verification header: %w", err) + } + return nil + }) + return eg.Wait() +} + +func signServiceMessagePart(key *ecdsa.PrivateKey, part stableMarshaler, sigWrite func(*refs.Signature)) error { + var sig *refs.Signature + + wrapper := StableMarshalerWrapper{ + SM: part, + } + // sign part + if err := signature.SignDataWithHandler( + key, + wrapper, + func(s *refs.Signature) { + sig = s + }, + ); err != nil { + return err + } + + // write part signature + sigWrite(sig) + + return nil +} diff --git a/apiv2/signature/sign_test.go b/apiv2/signature/sign_test.go new file mode 100644 index 0000000..1e5b57b --- /dev/null +++ b/apiv2/signature/sign_test.go @@ -0,0 +1,125 @@ +package signature + +import ( + "testing" + + crypto "git.frostfs.info/TrueCloudLab/frostfs-crypto" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "github.com/stretchr/testify/require" +) + +func TestBalanceResponse(t *testing.T) { + dec := new(accounting.Decimal) + dec.SetValue(100) + + body := new(accounting.BalanceResponseBody) + body.SetBalance(dec) + + meta := new(session.ResponseMetaHeader) + meta.SetTTL(1) + + req := new(accounting.BalanceResponse) + req.SetBody(body) + req.SetMetaHeader(meta) + + // verify unsigned request + require.Error(t, VerifyServiceMessage(req)) + + key, err := crypto.LoadPrivateKey("Kwk6k2eC3L3QuPvD8aiaNyoSXgQ2YL1bwS5CP1oKoA9waeAze97s") + require.NoError(t, err) + + // sign request + require.NoError(t, SignServiceMessage(key, req)) + + // verification must pass + require.NoError(t, VerifyServiceMessage(req)) + + // add level to meta header matryoshka + meta = new(session.ResponseMetaHeader) + meta.SetOrigin(req.GetMetaHeader()) + req.SetMetaHeader(meta) + + // sign request + require.NoError(t, SignServiceMessage(key, req)) + + // verification must pass + require.NoError(t, VerifyServiceMessage(req)) + + // corrupt body + dec.SetValue(dec.GetValue() + 1) + + // verification must fail + require.Error(t, VerifyServiceMessage(req)) + + // restore body + dec.SetValue(dec.GetValue() - 1) + + // corrupt meta header + meta.SetTTL(meta.GetTTL() + 1) + + // verification must fail + require.Error(t, VerifyServiceMessage(req)) + + // restore meta header + meta.SetTTL(meta.GetTTL() - 1) + + // corrupt origin verification header + req.GetVerificationHeader().SetOrigin(nil) + + // verification must fail + require.Error(t, VerifyServiceMessage(req)) +} + +func BenchmarkSignRequest(b *testing.B) { + key, _ := crypto.LoadPrivateKey("Kwk6k2eC3L3QuPvD8aiaNyoSXgQ2YL1bwS5CP1oKoA9waeAze97s") + + b.ResetTimer() + b.ReportAllocs() + + for range b.N { + b.StopTimer() + dec := new(accounting.Decimal) + dec.SetValue(100) + + body := new(accounting.BalanceResponseBody) + body.SetBalance(dec) + + meta := new(session.ResponseMetaHeader) + meta.SetTTL(1) + + resp := new(accounting.BalanceResponse) + resp.SetBody(body) + resp.SetMetaHeader(meta) + + b.StartTimer() + SignServiceMessage(key, resp) + } +} + +func BenchmarkVerifyRequest(b *testing.B) { + key, _ := crypto.LoadPrivateKey("Kwk6k2eC3L3QuPvD8aiaNyoSXgQ2YL1bwS5CP1oKoA9waeAze97s") + + b.ResetTimer() + b.ReportAllocs() + + for range b.N { + b.StopTimer() + dec := new(accounting.Decimal) + dec.SetValue(100) + + body := new(accounting.BalanceResponseBody) + body.SetBalance(dec) + + meta := new(session.ResponseMetaHeader) + meta.SetTTL(1) + + resp := new(accounting.BalanceResponse) + resp.SetBody(body) + resp.SetMetaHeader(meta) + SignServiceMessage(key, resp) + b.StartTimer() + + VerifyServiceMessage(resp) + } +} diff --git a/apiv2/signature/verify.go b/apiv2/signature/verify.go new file mode 100644 index 0000000..941974b --- /dev/null +++ b/apiv2/signature/verify.go @@ -0,0 +1,127 @@ +package signature + +import ( + "errors" + "fmt" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/signature" + "golang.org/x/sync/errgroup" +) + +type signatureProvider interface { + GetBodySignature() *refs.Signature + GetMetaSignature() *refs.Signature + GetOriginSignature() *refs.Signature +} + +// VerifyServiceMessage verifies service message. +func VerifyServiceMessage(msg any) error { + switch v := msg.(type) { + case nil: + return nil + case serviceRequest: + return verifyServiceRequest(v) + case serviceResponse: + return verifyServiceResponse(v) + default: + panic(fmt.Sprintf("unsupported session message %T", v)) + } +} + +func verifyServiceRequest(v serviceRequest) error { + meta := v.GetMetaHeader() + verificationHeader := v.GetVerificationHeader() + body := serviceMessageBody(v) + return verifyServiceRequestRecursive(body, meta, verificationHeader) +} + +func verifyServiceRequestRecursive(body stableMarshaler, meta *session.RequestMetaHeader, verify *session.RequestVerificationHeader) error { + verificationHeaderOrigin := verify.GetOrigin() + metaOrigin := meta.GetOrigin() + + stop, err := verifyMessageParts(body, meta, verificationHeaderOrigin, verificationHeaderOrigin != nil, verify) + if err != nil { + return err + } + if stop { + return nil + } + + return verifyServiceRequestRecursive(body, metaOrigin, verificationHeaderOrigin) +} + +func verifyMessageParts(body, meta, originHeader stableMarshaler, hasOriginHeader bool, sigProvider signatureProvider) (stop bool, err error) { + eg := &errgroup.Group{} + + eg.Go(func() error { + if err := verifyServiceMessagePart(meta, sigProvider.GetMetaSignature); err != nil { + return fmt.Errorf("could not verify meta header: %w", err) + } + return nil + }) + + eg.Go(func() error { + if err := verifyServiceMessagePart(originHeader, sigProvider.GetOriginSignature); err != nil { + return fmt.Errorf("could not verify origin of verification header: %w", err) + } + return nil + }) + + if !hasOriginHeader { + eg.Go(func() error { + if err := verifyServiceMessagePart(body, sigProvider.GetBodySignature); err != nil { + return fmt.Errorf("could not verify body: %w", err) + } + return nil + }) + } + + if err := eg.Wait(); err != nil { + return false, err + } + + if !hasOriginHeader { + return true, nil + } + + if sigProvider.GetBodySignature() != nil { + return false, errors.New("body signature misses at the matryoshka upper level") + } + + return false, nil +} + +func verifyServiceResponse(v serviceResponse) error { + meta := v.GetMetaHeader() + verificationHeader := v.GetVerificationHeader() + body := serviceMessageBody(v) + return verifyServiceResponseRecursive(body, meta, verificationHeader) +} + +func verifyServiceResponseRecursive(body stableMarshaler, meta *session.ResponseMetaHeader, verify *session.ResponseVerificationHeader) error { + verificationHeaderOrigin := verify.GetOrigin() + metaOrigin := meta.GetOrigin() + + stop, err := verifyMessageParts(body, meta, verificationHeaderOrigin, verificationHeaderOrigin != nil, verify) + if err != nil { + return err + } + if stop { + return nil + } + + return verifyServiceResponseRecursive(body, metaOrigin, verificationHeaderOrigin) +} + +func verifyServiceMessagePart(part stableMarshaler, sigRdr func() *refs.Signature) error { + wrapper := StableMarshalerWrapper{ + SM: part, + } + + return signature.VerifyDataWithSource( + wrapper, + sigRdr, + ) +} diff --git a/apiv2/status/convert.go b/apiv2/status/convert.go new file mode 100644 index 0000000..903a409 --- /dev/null +++ b/apiv2/status/convert.go @@ -0,0 +1,95 @@ +package status + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/grpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + status "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status/grpc" +) + +func (x *Detail) ToGRPCMessage() grpc.Message { + var m *status.Status_Detail + + if x != nil { + m = new(status.Status_Detail) + + m.SetId(x.id) + m.SetValue(x.val) + } + + return m +} + +func (x *Detail) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*status.Status_Detail) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + x.id = v.GetId() + x.val = v.GetValue() + + return nil +} + +func CodeFromGRPC(v uint32) Code { + return Code(v) +} + +func CodeToGRPC(v Code) uint32 { + return uint32(v) +} + +func (x *Status) ToGRPCMessage() grpc.Message { + var m *status.Status + + if x != nil { + m = new(status.Status) + + m.SetCode(CodeToGRPC(x.code)) + m.SetMessage(x.msg) + + var ds []status.Status_Detail + + if ln := len(x.details); ln > 0 { + ds = make([]status.Status_Detail, 0, ln) + + for i := range ln { + ds = append(ds, *x.details[i].ToGRPCMessage().(*status.Status_Detail)) + } + } + + m.SetDetails(ds) + } + + return m +} + +func (x *Status) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*status.Status) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var ( + ds []Detail + dsV2 = v.GetDetails() + ) + + if dsV2 != nil { + ln := len(dsV2) + + ds = make([]Detail, ln) + + for i := range ln { + if err := ds[i].FromGRPCMessage(&dsV2[i]); err != nil { + return err + } + } + } + + x.details = ds + x.msg = v.GetMessage() + x.code = CodeFromGRPC(v.GetCode()) + + return nil +} diff --git a/apiv2/status/details.go b/apiv2/status/details.go new file mode 100644 index 0000000..5b8f460 --- /dev/null +++ b/apiv2/status/details.go @@ -0,0 +1,8 @@ +package status + +// details for WrongMagicNumber code. +const ( + // DetailIDCorrectMagic is an identifier of details with correct network magic + // which can be attached to WrongMagicNumber code. + DetailIDCorrectMagic = iota +) diff --git a/apiv2/status/grpc/types_frostfs.pb.go b/apiv2/status/grpc/types_frostfs.pb.go new file mode 100644 index 0000000..8e58166 Binary files /dev/null and b/apiv2/status/grpc/types_frostfs.pb.go differ diff --git a/apiv2/status/grpc/types_frostfs_fuzz.go b/apiv2/status/grpc/types_frostfs_fuzz.go new file mode 100644 index 0000000..ce9d84e --- /dev/null +++ b/apiv2/status/grpc/types_frostfs_fuzz.go @@ -0,0 +1,26 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package status + +func DoFuzzProtoStatus(data []byte) int { + msg := new(Status) + if err := msg.UnmarshalProtobuf(data); err != nil { + return 0 + } + _ = msg.MarshalProtobuf(nil) + return 1 +} +func DoFuzzJSONStatus(data []byte) int { + msg := new(Status) + if err := msg.UnmarshalJSON(data); err != nil { + return 0 + } + _, err := msg.MarshalJSON() + if err != nil { + panic(err) + } + return 1 +} diff --git a/apiv2/status/grpc/types_frostfs_test.go b/apiv2/status/grpc/types_frostfs_test.go new file mode 100644 index 0000000..dfc5631 --- /dev/null +++ b/apiv2/status/grpc/types_frostfs_test.go @@ -0,0 +1,21 @@ +//go:build gofuzz +// +build gofuzz + +// Code generated by protoc-gen-go-frostfs. DO NOT EDIT. + +package status + +import ( + testing "testing" +) + +func FuzzProtoStatus(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzProtoStatus(data) + }) +} +func FuzzJSONStatus(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + DoFuzzJSONStatus(data) + }) +} diff --git a/apiv2/status/marshal.go b/apiv2/status/marshal.go new file mode 100644 index 0000000..bfb5870 --- /dev/null +++ b/apiv2/status/marshal.go @@ -0,0 +1,92 @@ +package status + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + status "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status/grpc" + protoutil "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto" +) + +const ( + _ = iota + detailIDFNum + detailValueFNum +) + +func (x *Detail) StableMarshal(buf []byte) []byte { + if x == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, x.StableSize()) + } + + var offset int + + offset += protoutil.UInt32Marshal(detailIDFNum, buf[offset:], x.id) + protoutil.BytesMarshal(detailValueFNum, buf[offset:], x.val) + + return buf +} + +func (x *Detail) StableSize() (size int) { + if x == nil { + return 0 + } + + size += protoutil.UInt32Size(detailIDFNum, x.id) + size += protoutil.BytesSize(detailValueFNum, x.val) + + return size +} + +func (x *Detail) Unmarshal(data []byte) error { + return message.Unmarshal(x, data, new(status.Status_Detail)) +} + +const ( + _ = iota + statusCodeFNum + statusMsgFNum + statusDetailsFNum +) + +func (x *Status) StableMarshal(buf []byte) []byte { + if x == nil { + return []byte{} + } + + if buf == nil { + buf = make([]byte, x.StableSize()) + } + + var offset int + + offset += protoutil.UInt32Marshal(statusCodeFNum, buf[offset:], CodeToGRPC(x.code)) + offset += protoutil.StringMarshal(statusMsgFNum, buf[offset:], x.msg) + + for i := range x.details { + offset += protoutil.NestedStructureMarshal(statusDetailsFNum, buf[offset:], &x.details[i]) + } + + return buf +} + +func (x *Status) StableSize() (size int) { + if x == nil { + return 0 + } + + size += protoutil.UInt32Size(statusCodeFNum, CodeToGRPC(x.code)) + size += protoutil.StringSize(statusMsgFNum, x.msg) + + for i := range x.details { + size += protoutil.NestedStructureSize(statusDetailsFNum, &x.details[i]) + } + + return size +} + +func (x *Status) Unmarshal(data []byte) error { + return message.Unmarshal(x, data, new(status.Status)) +} diff --git a/apiv2/status/message_test.go b/apiv2/status/message_test.go new file mode 100644 index 0000000..53e0168 --- /dev/null +++ b/apiv2/status/message_test.go @@ -0,0 +1,16 @@ +package status_test + +import ( + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message" + messagetest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/message/test" + statustest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status/test" +) + +func TestMessageConvert(t *testing.T) { + messagetest.TestRPCMessage(t, + func(empty bool) message.Message { return statustest.Detail(empty) }, + func(empty bool) message.Message { return statustest.Status(empty) }, + ) +} diff --git a/apiv2/status/status.go b/apiv2/status/status.go new file mode 100644 index 0000000..53d361e --- /dev/null +++ b/apiv2/status/status.go @@ -0,0 +1,103 @@ +package status + +const sectionBitSize = 10 + +// InSections checks if the Code is in [i,j] section list. +func (x Code) InSections(i, j uint32) bool { + return uint32(x) >= i< int(pool.poolSliceSize) { + return + } + + buf.Data = buf.Data[:0] + pool.buffersPool.Put(buf) +} + +// PoolSliceSize returns the size for buffer slices in the pool. +func (pool BufferPool) PoolSliceSize() uint32 { + return uint32(pool.poolSliceSize) +} diff --git a/apiv2/util/pool/marshal.go b/apiv2/util/pool/marshal.go new file mode 100644 index 0000000..107df28 --- /dev/null +++ b/apiv2/util/pool/marshal.go @@ -0,0 +1,7 @@ +package pool + +import ( + "github.com/VictoriaMetrics/easyproto" +) + +var MarshalerPool easyproto.MarshalerPool diff --git a/apiv2/util/proto/encoding/compat.go b/apiv2/util/proto/encoding/compat.go new file mode 100644 index 0000000..09d45e6 --- /dev/null +++ b/apiv2/util/proto/encoding/compat.go @@ -0,0 +1,22 @@ +package encoding + +import ( + _ "google.golang.org/grpc/encoding/proto" // Ensure default codec is registered before our one. + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/protoadapt" +) + +// messageV2Of converts v to a proto.Message. +// This is needed for this library to continue working in presence of external gRPC packages, +// such as opentelemetry gRPC exporter. +// Copied from https://github.com/grpc/grpc-go/blob/e524655becd8d4c7ba9e8687faef456e495e341e/encoding/proto/proto.go#L59. +func messageV2Of(v any) proto.Message { + switch v := v.(type) { + case protoadapt.MessageV1: + return protoadapt.MessageV2Of(v) + case protoadapt.MessageV2: + return v + } + + return nil +} diff --git a/apiv2/util/proto/encoding/json.go b/apiv2/util/proto/encoding/json.go new file mode 100644 index 0000000..3456a40 --- /dev/null +++ b/apiv2/util/proto/encoding/json.go @@ -0,0 +1,48 @@ +package encoding + +import ( + "encoding/json" + "fmt" + + "google.golang.org/grpc/encoding" + "google.golang.org/protobuf/encoding/protojson" +) + +// JSONCodec is easyjson codec used for code generated by protogen. +// It is binary-level compatible with the standard protojson format, thus uses the same name. +type JSONCodec struct{} + +var _ encoding.Codec = JSONCodec{} + +func init() { + encoding.RegisterCodec(JSONCodec{}) +} + +// Name implements the encoding.Codec interface. +func (JSONCodec) Name() string { return "json" } + +// Marshal implements the encoding.Codec interface. +func (JSONCodec) Marshal(v any) ([]byte, error) { + switch v := v.(type) { + case json.Marshaler: + return json.Marshal(v) + default: + if v := messageV2Of(v); v != nil { + return protojson.Marshal(v) + } + return nil, fmt.Errorf("failed to marshal, message is %T, want proto.Message", v) + } +} + +// Unmarshal implements the encoding.Codec interface. +func (JSONCodec) Unmarshal(data []byte, v any) error { + switch v := v.(type) { + case json.Unmarshaler: + return json.Unmarshal(data, v) + default: + if v := messageV2Of(v); v != nil { + return protojson.Unmarshal(data, v) + } + return fmt.Errorf("failed to unmarshal, message is %T, want proto.Message", v) + } +} diff --git a/apiv2/util/proto/encoding/proto.go b/apiv2/util/proto/encoding/proto.go new file mode 100644 index 0000000..5f3c556 --- /dev/null +++ b/apiv2/util/proto/encoding/proto.go @@ -0,0 +1,57 @@ +package encoding + +import ( + "fmt" + + "google.golang.org/grpc/encoding" + "google.golang.org/protobuf/proto" +) + +// ProtoCodec is easyproto codec used for code generated by protogen. +// It is binary-level compatible with the standard proto codec, thus uses the same name. +type ProtoCodec struct{} + +// ProtoMarshaler is an interface accepted by ProtoCodec.Marshal. +type ProtoMarshaler interface { + MarshalProtobuf([]byte) []byte +} + +// ProtoUnmarshaler is an interface accepted by ProtoCodec.Unmarshal. +type ProtoUnmarshaler interface { + UnmarshalProtobuf([]byte) error +} + +var _ encoding.Codec = ProtoCodec{} + +func init() { + encoding.RegisterCodec(ProtoCodec{}) +} + +// Name implements the encoding.Codec interface. +func (ProtoCodec) Name() string { return "proto" } + +// Marshal implements the encoding.Codec interface. +func (ProtoCodec) Marshal(v any) ([]byte, error) { + switch v := v.(type) { + case ProtoMarshaler: + return v.MarshalProtobuf(nil), nil + default: + if v := messageV2Of(v); v != nil { + return proto.Marshal(v) + } + return nil, fmt.Errorf("failed to marshal, message is %T, want proto.Message", v) + } +} + +// Unmarshal implements the encoding.Codec interface. +func (ProtoCodec) Unmarshal(data []byte, v any) error { + switch v := v.(type) { + case ProtoUnmarshaler: + return v.UnmarshalProtobuf(data) + default: + if v := messageV2Of(v); v != nil { + return proto.Unmarshal(data, v) + } + return fmt.Errorf("failed to unmarshal, message is %T, want proto.Message", v) + } +} diff --git a/apiv2/util/proto/marshal.go b/apiv2/util/proto/marshal.go new file mode 100644 index 0000000..5016255 --- /dev/null +++ b/apiv2/util/proto/marshal.go @@ -0,0 +1,413 @@ +/* +This package contains help functions for stable marshaller. Their usage is +totally optional. One can implement fast stable marshaller without these +runtime function calls. +*/ + +package proto + +import ( + "encoding/binary" + "math" + "math/bits" + + "google.golang.org/protobuf/encoding/protowire" +) + +type ( + stableMarshaler interface { + StableMarshal([]byte) []byte + stableSizer + } + + stableSizer interface { + StableSize() int + } + + setMarshalData[T any] interface { + SetMarshalData([]byte) + StableSize() int + ~*T + } +) + +func BytesMarshal(field int, buf, v []byte) int { + return bytesMarshal(field, buf, v) +} + +func BytesSize(field int, v []byte) int { + return bytesSize(field, v) +} + +func bytesMarshal[T ~[]byte | ~string](field int, buf []byte, v T) int { + if len(v) == 0 { + return 0 + } + return bytesMarshalNoCheck(field, buf, v) +} + +func bytesMarshalNoCheck[T ~[]byte | ~string](field int, buf []byte, v T) int { + prefix := protowire.EncodeTag(protowire.Number(field), protowire.BytesType) + + // buf length check can prevent panic at PutUvarint, but it will make + // marshaller a bit slower. + i := binary.PutUvarint(buf, uint64(prefix)) + i += binary.PutUvarint(buf[i:], uint64(len(v))) + i += copy(buf[i:], v) + + return i +} + +func bytesSize[T ~[]byte | ~string](field int, v T) int { + if len(v) == 0 { + return 0 + } + return bytesSizeNoCheck(field, v) +} + +func bytesSizeNoCheck[T ~[]byte | ~string](field int, v T) int { + return protowire.SizeGroup(protowire.Number(field), protowire.SizeBytes(len(v))) +} + +func StringMarshal(field int, buf []byte, v string) int { + return bytesMarshal(field, buf, v) +} + +func StringSize(field int, v string) int { + return bytesSize(field, v) +} + +func BoolMarshal(field int, buf []byte, v bool) int { + if !v { + return 0 + } + + prefix := protowire.EncodeTag(protowire.Number(field), protowire.VarintType) + + // buf length check can prevent panic at PutUvarint, but it will make + // marshaller a bit slower. + i := binary.PutUvarint(buf, uint64(prefix)) + const boolTrueValue = 0x1 + buf[i] = boolTrueValue + + return i + 1 +} + +func BoolSize(field int, v bool) int { + if !v { + return 0 + } + const boolLength = 1 + return protowire.SizeGroup(protowire.Number(field), boolLength) +} + +func UInt64Marshal(field int, buf []byte, v uint64) int { + if v == 0 { + return 0 + } + + prefix := protowire.EncodeTag(protowire.Number(field), protowire.VarintType) + + // buf length check can prevent panic at PutUvarint, but it will make + // marshaller a bit slower. + i := binary.PutUvarint(buf, uint64(prefix)) + i += binary.PutUvarint(buf[i:], v) + + return i +} + +func UInt64Size(field int, v uint64) int { + if v == 0 { + return 0 + } + return protowire.SizeGroup(protowire.Number(field), protowire.SizeVarint(v)) +} + +func Int64Marshal(field int, buf []byte, v int64) int { + return UInt64Marshal(field, buf, uint64(v)) +} + +func Int64Size(field int, v int64) int { + return UInt64Size(field, uint64(v)) +} + +func UInt32Marshal(field int, buf []byte, v uint32) int { + return UInt64Marshal(field, buf, uint64(v)) +} + +func UInt32Size(field int, v uint32) int { + return UInt64Size(field, uint64(v)) +} + +func Int32Marshal(field int, buf []byte, v int32) int { + return UInt64Marshal(field, buf, uint64(uint32(v))) +} + +func Int32Size(field int, v int32) int { + return UInt64Size(field, uint64(uint32(v))) +} + +func EnumMarshal(field int, buf []byte, v int32) int { + return UInt64Marshal(field, buf, uint64(uint32(v))) +} + +func EnumSize(field int, v int32) int { + return UInt64Size(field, uint64(uint32(v))) +} + +func RepeatedBytesMarshal(field int, buf []byte, v [][]byte) int { + var offset int + + for i := range v { + offset += bytesMarshalNoCheck(field, buf[offset:], v[i]) + } + + return offset +} + +func RepeatedBytesSize(field int, v [][]byte) (size int) { + for i := range v { + size += bytesSizeNoCheck(field, v[i]) + } + + return size +} + +func RepeatedStringMarshal(field int, buf []byte, v []string) int { + var offset int + + for i := range v { + offset += bytesMarshalNoCheck(field, buf[offset:], v[i]) + } + + return offset +} + +func RepeatedStringSize(field int, v []string) (size int) { + for i := range v { + size += bytesSizeNoCheck(field, v[i]) + } + + return size +} + +func repeatedUIntSize[T ~uint64 | ~int64 | ~uint32 | ~int32](field int, v []T) (size, arraySize int) { + if len(v) == 0 { + return 0, 0 + } + + for i := range v { + arraySize += protowire.SizeVarint(uint64(v[i])) + } + + size = protowire.SizeGroup(protowire.Number(field), protowire.SizeBytes(arraySize)) + + return +} + +func repeatedUIntMarshal[T ~uint64 | ~int64 | ~uint32 | ~int32](field int, buf []byte, v []T) int { + if len(v) == 0 { + return 0 + } + + prefix := protowire.EncodeTag(protowire.Number(field), protowire.BytesType) + offset := binary.PutUvarint(buf, uint64(prefix)) + + _, arrSize := repeatedUIntSize(field, v) + offset += binary.PutUvarint(buf[offset:], uint64(arrSize)) + for i := range v { + offset += binary.PutUvarint(buf[offset:], uint64(v[i])) + } + + return offset +} + +func RepeatedUInt64Marshal(field int, buf []byte, v []uint64) int { + return repeatedUIntMarshal(field, buf, v) +} + +func RepeatedUInt64Size(field int, v []uint64) (size, arraySize int) { + return repeatedUIntSize(field, v) +} + +func RepeatedInt64Marshal(field int, buf []byte, v []int64) int { + return repeatedUIntMarshal(field, buf, v) +} + +func RepeatedInt64Size(field int, v []int64) (size, arraySize int) { + return repeatedUIntSize(field, v) +} + +func RepeatedUInt32Marshal(field int, buf []byte, v []uint32) int { + return repeatedUIntMarshal(field, buf, v) +} + +func RepeatedUInt32Size(field int, v []uint32) (size, arraySize int) { + return repeatedUIntSize(field, v) +} + +func RepeatedInt32Marshal(field int, buf []byte, v []int32) int { + if len(v) == 0 { + return 0 + } + + prefix := protowire.EncodeTag(protowire.Number(field), protowire.BytesType) + offset := binary.PutUvarint(buf, uint64(prefix)) + _, arrSize := RepeatedInt32Size(field, v) + offset += binary.PutUvarint(buf[offset:], uint64(arrSize)) + for i := range v { + offset += binary.PutUvarint(buf[offset:], uint64(uint32(v[i]))) + } + return offset +} + +func RepeatedInt32Size(field int, v []int32) (size, arraySize int) { + if len(v) == 0 { + return 0, 0 + } + for i := range v { + arraySize += protowire.SizeVarint(uint64(uint32(v[i]))) + } + return protowire.SizeGroup(protowire.Number(field), protowire.SizeBytes(arraySize)), arraySize +} + +// VarUIntSize returns length of varint byte sequence for uint64 value 'x'. +func VarUIntSize(x uint64) int { + return (bits.Len64(x|1) + 6) / 7 +} + +type ptrStableMarshaler[T any] interface { + stableMarshaler + ~*T +} + +type ptrStableSizer[T any] interface { + stableSizer + ~*T +} + +func NestedStructureMarshal[T any, M ptrStableMarshaler[T]](field int64, buf []byte, v M) int { + if v == nil { + return 0 + } + + return NestedStructureMarshalUnchecked(field, buf, v) +} + +func NestedStructureMarshalUnchecked[T stableMarshaler](field int64, buf []byte, v T) int { + n := v.StableSize() + prefix := protowire.EncodeTag(protowire.Number(field), protowire.BytesType) + offset := binary.PutUvarint(buf, prefix) + offset += binary.PutUvarint(buf[offset:], uint64(n)) + v.StableMarshal(buf[offset:]) + + return offset + n +} + +// NestedStructureSetMarshalData calculates offset for field in parentData +// and calls SetMarshalData for nested structure. +// +// Returns marshalled data length of nested structure. +func NestedStructureSetMarshalData[T any, M setMarshalData[T]](field int64, parentData []byte, v M) int { + if v == nil { + return 0 + } + + if parentData == nil { + v.SetMarshalData(nil) + return 0 + } + + n := v.StableSize() + buf := make([]byte, binary.MaxVarintLen64) + prefix := protowire.EncodeTag(protowire.Number(field), protowire.BytesType) + offset := binary.PutUvarint(buf, prefix) + offset += binary.PutUvarint(buf, uint64(n)) + + v.SetMarshalData(parentData[offset : offset+n]) + + return offset + n +} + +func NestedStructureSize[T any, M ptrStableSizer[T]](field int64, v M) (size int) { + if v == nil { + return 0 + } + + return NestedStructureSizeUnchecked(field, v) +} + +func NestedStructureSizeUnchecked[T stableSizer](field int64, v T) int { + n := v.StableSize() + return protowire.SizeGroup(protowire.Number(field), protowire.SizeBytes(n)) +} + +func Fixed64Marshal(field int, buf []byte, v uint64) int { + if v == 0 { + return 0 + } + + prefix := protowire.EncodeTag(protowire.Number(field), protowire.Fixed64Type) + + // buf length check can prevent panic at PutUvarint, but it will make + // marshaller a bit slower. + i := binary.PutUvarint(buf, uint64(prefix)) + binary.LittleEndian.PutUint64(buf[i:], v) + + return i + 8 +} + +func Fixed64Size(fNum int, v uint64) int { + if v == 0 { + return 0 + } + return protowire.SizeGroup(protowire.Number(fNum), protowire.SizeFixed64()) +} + +func Float64Marshal(field int, buf []byte, v float64) int { + if v == 0 { + return 0 + } + + prefix := protowire.EncodeTag(protowire.Number(field), protowire.Fixed64Type) + + i := binary.PutUvarint(buf, uint64(prefix)) + binary.LittleEndian.PutUint64(buf[i:], math.Float64bits(v)) + + return i + 8 +} + +func Float64Size(fNum int, v float64) int { + if v == 0 { + return 0 + } + return protowire.SizeGroup(protowire.Number(fNum), protowire.SizeFixed64()) +} + +// Fixed32Marshal encodes uint32 value to Protocol Buffers fixed32 field with specified number, +// and writes it to specified buffer. Returns number of bytes written. +// +// Panics if the buffer is undersized. +func Fixed32Marshal(field int, buf []byte, v uint32) int { + if v == 0 { + return 0 + } + + prefix := protowire.EncodeTag(protowire.Number(field), protowire.Fixed32Type) + + // buf length check can prevent panic at PutUvarint, but it will make + // marshaller a bit slower. + i := binary.PutUvarint(buf, uint64(prefix)) + binary.LittleEndian.PutUint32(buf[i:], v) + + return i + 4 +} + +// Fixed32Size returns number of bytes required to encode uint32 value to Protocol Buffers fixed32 field +// with specified number. +func Fixed32Size(fNum int, v uint32) int { + if v == 0 { + return 0 + } + return protowire.SizeGroup(protowire.Number(fNum), protowire.SizeFixed32()) +} diff --git a/apiv2/util/proto/marshal_test.go b/apiv2/util/proto/marshal_test.go new file mode 100644 index 0000000..7d7ab91 --- /dev/null +++ b/apiv2/util/proto/marshal_test.go @@ -0,0 +1,217 @@ +package proto_test + +import ( + "math" + "math/rand" + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto/test" + generated "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto/test/custom" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/encoding/protojson" + goproto "google.golang.org/protobuf/proto" +) + +type protoInt interface { + ~int32 | ~uint32 | ~int64 | ~uint64 +} + +func nonZero[T protoInt]() T { + var r T + for r == 0 { + r = T(rand.Uint64()) + } + return r +} + +func TestStableMarshalSingle(t *testing.T) { + t.Run("empty", func(t *testing.T) { + input := &generated.Primitives{} + require.Zero(t, input.StableSize()) + + r := input.MarshalProtobuf(nil) + require.Empty(t, r) + }) + + marshalCases := []struct { + name string + input *generated.Primitives + }{ + {name: "bytes", input: &generated.Primitives{FieldA: []byte{1, 2, 3}}}, + {name: "string", input: &generated.Primitives{FieldB: "123"}}, + {name: "bool", input: &generated.Primitives{FieldC: true}}, + {name: "int32", input: &generated.Primitives{FieldD: -10}}, + {name: "uint32", input: &generated.Primitives{FieldE: nonZero[uint32]()}}, + {name: "int64", input: &generated.Primitives{FieldF: nonZero[int64]()}}, + {name: "uint64", input: &generated.Primitives{FieldG: nonZero[uint64]()}}, + {name: "uint64", input: &generated.Primitives{FieldI: nonZero[uint64]()}}, + {name: "float64", input: &generated.Primitives{FieldJ: math.Float64frombits(12345677890)}}, + {name: "fixed32", input: &generated.Primitives{FieldK: nonZero[uint32]()}}, + {name: "enum, positive", input: &generated.Primitives{FieldH: generated.Primitives_POSITIVE}}, + {name: "enum, negative", input: &generated.Primitives{FieldH: generated.Primitives_NEGATIVE}}, + {name: "oneof, first", input: &generated.Primitives{FieldM: &generated.Primitives_FieldMa{FieldMa: []byte{4, 2}}}}, + {name: "oneof, second", input: &generated.Primitives{FieldM: &generated.Primitives_FieldMe{FieldMe: nonZero[uint32]()}}}, + } + for _, tc := range marshalCases { + t.Run(tc.name, func(t *testing.T) { + t.Run("proto", func(t *testing.T) { + r := tc.input.MarshalProtobuf(nil) + require.Equal(t, len(r), tc.input.StableSize()) + require.NotEmpty(t, r) + + var actual test.Primitives + require.NoError(t, goproto.Unmarshal(r, &actual)) + + var actualFrostfs generated.Primitives + require.NoError(t, actualFrostfs.UnmarshalProtobuf(r)) + require.Equal(t, tc.input, &actualFrostfs) + + primitivesEqual(t, tc.input, &actual) + }) + t.Run("json", func(t *testing.T) { + r, err := tc.input.MarshalJSON() + require.NoError(t, err) + require.NotEmpty(t, r) + + var actual test.Primitives + require.NoError(t, protojson.Unmarshal(r, &actual)) + + var actualFrostfs generated.Primitives + require.NoError(t, actualFrostfs.UnmarshalJSON(r)) + require.Equal(t, tc.input, &actualFrostfs) + + primitivesEqual(t, tc.input, &actual) + }) + }) + } +} + +func primitivesEqual(t *testing.T, a *generated.Primitives, b *test.Primitives) { + // Compare each field directly, because proto-generated code has private fields. + require.Equal(t, a.FieldA, b.FieldA) + require.Equal(t, a.FieldB, b.FieldB) + require.Equal(t, a.FieldC, b.FieldC) + require.Equal(t, a.FieldD, b.FieldD) + require.Equal(t, a.FieldE, b.FieldE) + require.Equal(t, a.FieldF, b.FieldF) + require.Equal(t, a.FieldG, b.FieldG) + require.Equal(t, a.FieldI, b.FieldI) + require.Equal(t, a.FieldJ, b.FieldJ) + require.Equal(t, a.FieldK, b.FieldK) + require.EqualValues(t, a.FieldH, b.FieldH) + require.Equal(t, a.GetFieldMa(), b.GetFieldMa()) + require.Equal(t, a.GetFieldMe(), b.GetFieldMe()) + require.Equal(t, a.GetFieldAux().GetInnerField(), b.GetFieldAux().GetInnerField()) +} + +func repPrimitivesEqual(t *testing.T, a *generated.RepPrimitives, b *test.RepPrimitives) { + // Compare each field directly, because proto-generated code has private fields. + require.Equal(t, a.FieldA, b.FieldA) + require.Equal(t, a.FieldB, b.FieldB) + require.Equal(t, a.FieldC, b.FieldC) + require.Equal(t, a.FieldD, b.FieldD) + require.Equal(t, a.FieldE, b.FieldE) + require.Equal(t, a.FieldF, b.FieldF) + require.Equal(t, a.FieldFu, b.FieldFu) + require.Equal(t, len(a.GetFieldAux()), len(b.GetFieldAux())) + for i := range a.FieldAux { + require.Equal(t, a.GetFieldAux()[i].GetInnerField(), b.GetFieldAux()[i].GetInnerField()) + } +} + +func randIntSlice[T protoInt](n int, includeZero bool) []T { + r := make([]T, n) + if n == 0 { + return r + } + for i := range r { + r[i] = T(rand.Uint64()) + } + if includeZero { + r[0] = 0 + } + return r +} + +func uint32SliceToAux(s []uint32) []generated.RepPrimitives_Aux { + r := make([]generated.RepPrimitives_Aux, len(s)) + for i := range s { + r[i] = generated.RepPrimitives_Aux{InnerField: s[i]} + } + return r +} + +func TestStableMarshalRep(t *testing.T) { + t.Run("empty", func(t *testing.T) { + marshalCases := []struct { + name string + input *generated.RepPrimitives + }{ + {name: "default", input: &generated.RepPrimitives{}}, + {name: "bytes", input: &generated.RepPrimitives{FieldA: [][]byte{}}}, + {name: "string", input: &generated.RepPrimitives{FieldB: []string{}}}, + {name: "int32", input: &generated.RepPrimitives{FieldC: []int32{}}}, + {name: "uint32", input: &generated.RepPrimitives{FieldD: []uint32{}}}, + {name: "int64", input: &generated.RepPrimitives{FieldE: []int64{}}}, + {name: "uint64", input: &generated.RepPrimitives{FieldF: []uint64{}}}, + {name: "uint64", input: &generated.RepPrimitives{FieldFu: []uint64{}}}, + } + + for _, tc := range marshalCases { + t.Run(tc.name, func(t *testing.T) { + require.Zero(t, tc.input.StableSize()) + + r := tc.input.MarshalProtobuf(nil) + require.Empty(t, r) + }) + } + }) + + marshalCases := []struct { + name string + input *generated.RepPrimitives + }{ + {name: "bytes", input: &generated.RepPrimitives{FieldA: [][]byte{{1, 2, 3}}}}, + {name: "string", input: &generated.RepPrimitives{FieldB: []string{"123"}}}, + {name: "int32", input: &generated.RepPrimitives{FieldC: randIntSlice[int32](1, true)}}, + {name: "int32", input: &generated.RepPrimitives{FieldC: randIntSlice[int32](2, true)}}, + {name: "int32", input: &generated.RepPrimitives{FieldC: randIntSlice[int32](2, false)}}, + {name: "uint32", input: &generated.RepPrimitives{FieldD: randIntSlice[uint32](1, true)}}, + {name: "uint32", input: &generated.RepPrimitives{FieldD: randIntSlice[uint32](2, true)}}, + {name: "uint32", input: &generated.RepPrimitives{FieldD: randIntSlice[uint32](2, false)}}, + {name: "int64", input: &generated.RepPrimitives{FieldE: randIntSlice[int64](1, true)}}, + {name: "int64", input: &generated.RepPrimitives{FieldE: randIntSlice[int64](2, true)}}, + {name: "int64", input: &generated.RepPrimitives{FieldE: randIntSlice[int64](2, false)}}, + {name: "uint64", input: &generated.RepPrimitives{FieldF: randIntSlice[uint64](1, true)}}, + {name: "uint64", input: &generated.RepPrimitives{FieldF: randIntSlice[uint64](2, true)}}, + {name: "uint64", input: &generated.RepPrimitives{FieldF: randIntSlice[uint64](2, false)}}, + {name: "uint64", input: &generated.RepPrimitives{FieldFu: randIntSlice[uint64](1, true)}}, + {name: "uint64", input: &generated.RepPrimitives{FieldFu: randIntSlice[uint64](2, true)}}, + {name: "uint64", input: &generated.RepPrimitives{FieldFu: randIntSlice[uint64](2, false)}}, + {name: "message", input: &generated.RepPrimitives{FieldAux: uint32SliceToAux(randIntSlice[uint32](1, true))}}, + {name: "message", input: &generated.RepPrimitives{FieldAux: uint32SliceToAux(randIntSlice[uint32](2, true))}}, + {name: "message", input: &generated.RepPrimitives{FieldAux: uint32SliceToAux(randIntSlice[uint32](2, false))}}, + } + for _, tc := range marshalCases { + t.Run(tc.name, func(t *testing.T) { + t.Run("proto", func(t *testing.T) { + r := tc.input.MarshalProtobuf(nil) + require.Equal(t, len(r), tc.input.StableSize()) + require.NotEmpty(t, r) + + var actual test.RepPrimitives + require.NoError(t, goproto.Unmarshal(r, &actual)) + repPrimitivesEqual(t, tc.input, &actual) + }) + t.Run("json", func(t *testing.T) { + r, err := tc.input.MarshalJSON() + require.NoError(t, err) + require.NotEmpty(t, r) + + var actual test.RepPrimitives + require.NoError(t, protojson.Unmarshal(r, &actual)) + repPrimitivesEqual(t, tc.input, &actual) + }) + }) + } +} diff --git a/apiv2/util/proto/test/custom/test_frostfs.pb.go b/apiv2/util/proto/test/custom/test_frostfs.pb.go new file mode 100644 index 0000000..74b3cbb Binary files /dev/null and b/apiv2/util/proto/test/custom/test_frostfs.pb.go differ diff --git a/apiv2/util/proto/test/test.pb.go b/apiv2/util/proto/test/test.pb.go new file mode 100644 index 0000000..e7d5699 Binary files /dev/null and b/apiv2/util/proto/test/test.pb.go differ diff --git a/apiv2/util/proto/test/test.proto b/apiv2/util/proto/test/test.proto new file mode 100644 index 0000000..58cee2f --- /dev/null +++ b/apiv2/util/proto/test/test.proto @@ -0,0 +1,46 @@ +syntax = "proto3"; + +package test; + +option go_package = "util/proto/test"; + +message Primitives { + bytes field_a = 1; + string field_b = 2; + bool field_c = 200; + int32 field_d = 201; + uint32 field_e = 202; + int64 field_f = 203; + uint64 field_g = 204; + fixed64 field_i = 205; + double field_j = 206; + fixed32 field_k = 207; + + enum SomeEnum { + UNKNOWN = 0; + POSITIVE = 1; + NEGATIVE = -1; + } + SomeEnum field_h = 300; + + message Aux { uint32 inner_field = 1; } + + oneof field_m { + bytes field_ma = 401; + uint32 field_me = 402; + Aux field_aux = 403; + } +} + +message RepPrimitives { + repeated bytes field_a = 1; + repeated string field_b = 2; + repeated int32 field_c = 3; + repeated uint32 field_d = 4; + repeated int64 field_e = 5; + repeated uint64 field_f = 6; + repeated uint64 field_fu = 7 [ packed = false ]; + + message Aux { uint32 inner_field = 1; } + repeated Aux field_aux = 8; +} diff --git a/apiv2/util/protogen/internalgengo/file.go b/apiv2/util/protogen/internalgengo/file.go new file mode 100644 index 0000000..f02f061 --- /dev/null +++ b/apiv2/util/protogen/internalgengo/file.go @@ -0,0 +1,245 @@ +package internalgengo + +import ( + "fmt" + "sort" + "strconv" + "strings" + + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +var ( + strconvPackage = protogen.GoImportPath("strconv") + fmtPackage = protogen.GoImportPath("fmt") + jsonPackage = protogen.GoImportPath("encoding/json") + easyprotoPackage = protogen.GoImportPath("github.com/VictoriaMetrics/easyproto") + mpPackage = protogen.GoImportPath("git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/pool") + protoPackage = protogen.GoImportPath("git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto") + encodingPackage = protogen.GoImportPath("git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/proto/encoding") + + mp = mpPackage.Ident("MarshalerPool") +) + +// GenerateFile generates a *.pb.go file enforcing field-order serialization. +func GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile { + filename := file.GeneratedFilenamePrefix + "_frostfs.pb.go" + g := gen.NewGeneratedFile(filename, file.GoImportPath) + + g.P("// Code generated by protoc-gen-go-frostfs. DO NOT EDIT.") + g.P() + g.P("package ", file.GoPackageName) + g.P() + g.Import(encodingPackage) + + // Doesn't work for multiple files in a single package, use external pool. + // g.P("var mp ", easyprotoPackage.Ident("MarshalerPool")) + + for _, e := range file.Enums { + emitEnum(g, e) + } + for _, msg := range file.Messages { + emitEasyProto(g, msg) + } + return g +} + +func emitEnum(g *protogen.GeneratedFile, e *protogen.Enum) { + g.P("type " + e.GoIdent.GoName + " int32") + g.P("const (") + for _, ev := range e.Values { + g.P(ev.GoIdent.GoName, " ", e.GoIdent.GoName, " = ", ev.Desc.Number()) + } + g.P(")") + + g.P("var (") + g.P(e.GoIdent.GoName+"_name", " = map[int32]string{") + for _, value := range e.Values { + g.P(value.Desc.Number(), ": ", strconv.Quote(string(value.Desc.Name())), ",") + } + g.P("}") + g.P(e.GoIdent.GoName+"_value", " = map[string]int32{") + for _, value := range e.Values { + g.P(strconv.Quote(string(value.Desc.Name())), ": ", value.Desc.Number(), ",") + } + g.P("}") + g.P(")") + g.P() + + g.P("func (x ", e.GoIdent.GoName, ") String() string {") + g.P("if v, ok := ", e.GoIdent.GoName+"_name[int32(x)]; ok {") + g.P("return v") + g.P("}") + g.P("return ", strconvPackage.Ident("FormatInt"), "(int64(x), 10)") + g.P("}") + + g.P("func (x *", e.GoIdent.GoName, ") FromString(s string) bool {") + g.P("if v, ok := ", e.GoIdent.GoName+"_value[s]; ok {") + g.P("*x = ", e.GoIdent.GoName, "(v)") + g.P("return true") + g.P("}") + g.P("return false") + g.P("}") +} + +func emitEasyProto(g *protogen.GeneratedFile, msg *protogen.Message) { + for _, e := range msg.Enums { + emitEnum(g, e) + } + for _, m := range msg.Messages { + emitEasyProto(g, m) + } + + g.P("type " + msg.GoIdent.GoName + " struct {") + emitMessageFields(g, msg) + g.P("}") + + g.P("var (") + g.P("_ ", encodingPackage.Ident("ProtoMarshaler"), " = (*", msg.GoIdent.GoName, ")(nil)") + g.P("_ ", encodingPackage.Ident("ProtoUnmarshaler"), " = (*", msg.GoIdent.GoName, ")(nil)") + g.P("_ ", jsonPackage.Ident("Marshaler"), " = (*", msg.GoIdent.GoName, ")(nil)") + g.P("_ ", jsonPackage.Ident("Unmarshaler"), " = (*", msg.GoIdent.GoName, ")(nil)") + g.P(")") + + emitStableSize(g, msg) + if strings.HasSuffix(msg.GoIdent.GoName, "Request") || strings.HasSuffix(msg.GoIdent.GoName, "Response") { + emitSignatureMethods(g, msg) + } + + emitProtoMethods(g, msg) + emitGettersSetters(g, msg) + emitJSONMethods(g, msg) + + for _, f := range msg.Fields { + if isFirstOneof(f) { + genOneof(g, f) + } + } +} + +func isFirstOneof(f *protogen.Field) bool { + return f.Oneof != nil && f == f.Oneof.Fields[0] +} + +func emitOneofGettersSetters(g *protogen.GeneratedFile, msg *protogen.Message, ff *protogen.Field) { + // For some reason protoc generates different code for oneof message/non-message fields: + // 1. For message we have 2 level struct wrapping and setters use inner type. + // 2. For other types we also have 2 level wrapping, but setters use outer type. + ft := fieldType(g, ff) + + g.P("func (x *", msg.GoIdent.GoName, ") Get", ff.GoName, "() ", ft, " {") + g.P("if xx, ok := x.Get", ff.Oneof.GoName, "().(*", ff.GoIdent, "); ok { return xx.", ff.GoName, " }") + g.P("return ", fieldDefaultValue(ff)) + g.P("}") + + if ff.Desc.Kind() == protoreflect.MessageKind { + g.P("func (x *", msg.GoIdent.GoName, ") Set", ff.GoName, "(v ", ft, ") {") + g.P("x.", ff.Oneof.GoName, " = &", ff.GoIdent, "{", ff.GoName, ": v}") + g.P("}") + } else { + g.P("func (x *", msg.GoIdent.GoName, ") Set", ff.GoName, "(v *", ff.GoIdent, ") {") + g.P("x.", ff.Oneof.GoName, " = v") + g.P("}") + + ft := fieldType(g, ff) + emitGetterSetter(g, ff.GoIdent.GoName, ff.GoName, ft.String(), fieldDefaultValue(ff)) + } +} + +func emitGettersSetters(g *protogen.GeneratedFile, msg *protogen.Message) { + for _, f := range msg.Fields { + if f.Oneof != nil { + if f.Oneof.Fields[0] == f { + emitGetterSetter(g, msg.GoIdent.GoName, f.Oneof.GoName, oneOfDescriptor(f.Oneof), "nil") + for _, ff := range f.Oneof.Fields { + emitOneofGettersSetters(g, msg, ff) + } + } + continue + } + + ft := fieldType(g, f) + emitGetterSetter(g, msg.GoIdent.GoName, f.GoName, ft.String(), fieldDefaultValue(f)) + } +} + +func emitMessageFields(g *protogen.GeneratedFile, msg *protogen.Message) { + for _, field := range msg.Fields { + genMessageField(g, field) + } +} + +func genMessageField(g *protogen.GeneratedFile, field *protogen.Field) { + if field.Oneof != nil { + if field.Oneof.Fields[0] == field { + g.P(field.Oneof.GoName, " ", oneOfDescriptor(field.Oneof)) + } + return + } + + typ := fieldType(g, field) + g.P(field.GoName, " ", typ, fmt.Sprintf(" `json:%q`", fieldJSONName(field))) +} + +func oneOfDescriptor(oneof *protogen.Oneof) string { + return "is" + oneof.GoIdent.GoName +} + +func genOneof(g *protogen.GeneratedFile, field *protogen.Field) { + ifName := oneOfDescriptor(field.Oneof) + g.P("type ", ifName, " interface {") + g.P(ifName, "()") + g.P("}") + g.P() + for _, field := range field.Oneof.Fields { + g.P("type ", field.GoIdent, " struct {") + + ft := fieldType(g, field) + g.P(field.GoName, " ", ft) + g.P("}") + g.P() + } + for _, field := range field.Oneof.Fields { + g.P("func (*", field.GoIdent, ") ", ifName, "() {}") + g.P() + } +} + +func fieldDefaultValue(field *protogen.Field) string { + if field.Desc.Cardinality() == protoreflect.Repeated { + return "nil" + } + + switch field.Desc.Kind() { + case protoreflect.MessageKind, protoreflect.BytesKind: + return "nil" + case protoreflect.BoolKind: + return "false" + case protoreflect.StringKind: + return `""` + default: + return "0" + } +} + +func castFieldName(f *protogen.Field) string { + if f.Oneof != nil { + return "x." + f.Oneof.GoName + } + + name := "x." + f.GoName + if f.Desc.Kind() != protoreflect.EnumKind { + return name + } + return "int32(" + name + ")" +} + +func sortFields(fs []*protogen.Field) []*protogen.Field { + res := make([]*protogen.Field, len(fs)) + copy(res, fs) + sort.Slice(res, func(i, j int) bool { + return res[i].Desc.Number() < res[j].Desc.Number() + }) + return res +} diff --git a/apiv2/util/protogen/internalgengo/fuzz.go b/apiv2/util/protogen/internalgengo/fuzz.go new file mode 100644 index 0000000..ec99692 --- /dev/null +++ b/apiv2/util/protogen/internalgengo/fuzz.go @@ -0,0 +1,69 @@ +package internalgengo + +import ( + "google.golang.org/protobuf/compiler/protogen" +) + +var testingPackage = protogen.GoImportPath("testing") + +func GenerateFuzzTests(gen *protogen.Plugin, file *protogen.File) { + { + filename := file.GeneratedFilenamePrefix + "_frostfs_fuzz.go" + g := gen.NewGeneratedFile(filename, file.GoImportPath) + + g.P("//go:build gofuzz") + g.P("// +build gofuzz") + g.P("// Code generated by protoc-gen-go-frostfs. DO NOT EDIT.") + g.P() + g.P("package ", file.GoPackageName) + g.P() + + for _, msg := range file.Messages { + emitFuzzWrappers(g, msg) + } + } + { + filename := file.GeneratedFilenamePrefix + "_frostfs_test.go" + g := gen.NewGeneratedFile(filename, file.GoImportPath) + + g.P("//go:build gofuzz") + g.P("// +build gofuzz") + g.P("// Code generated by protoc-gen-go-frostfs. DO NOT EDIT.") + g.P() + g.P("package ", file.GoPackageName) + g.P() + + for _, msg := range file.Messages { + emitFuzzTests(g, msg) + } + } +} + +func emitFuzzWrappers(g *protogen.GeneratedFile, msg *protogen.Message) { + g.P("func DoFuzzProto", msg.GoIdent.GoName, "(data []byte) int {") + g.P("msg := new(", msg.GoIdent.GoName, ")") + g.P("if err := msg.UnmarshalProtobuf(data); err != nil { return 0 }") + g.P("_ = msg.MarshalProtobuf(nil)") + g.P("return 1") + g.P("}") + + g.P("func DoFuzzJSON", msg.GoIdent.GoName, "(data []byte) int {") + g.P("msg := new(", msg.GoIdent.GoName, ")") + g.P("if err := msg.UnmarshalJSON(data); err != nil { return 0 }") + g.P("_, err := msg.MarshalJSON()") + g.P("if err != nil { panic(err) }") + g.P("return 1") + g.P("}") +} + +func emitFuzzTests(g *protogen.GeneratedFile, msg *protogen.Message) { + g.P("func FuzzProto", msg.GoIdent.GoName, "(f *", testingPackage.Ident("F"), ") {") + g.P("f.Fuzz(func(t *", testingPackage.Ident("T"), ", data []byte) {") + g.P("DoFuzzProto", msg.GoIdent.GoName, "(data)") + g.P("})}") + + g.P("func FuzzJSON", msg.GoIdent.GoName, "(f *", testingPackage.Ident("F"), ") {") + g.P("f.Fuzz(func(t *", testingPackage.Ident("T"), ", data []byte) {") + g.P("DoFuzzJSON", msg.GoIdent.GoName, "(data)") + g.P("})}") +} diff --git a/apiv2/util/protogen/internalgengo/getter.go b/apiv2/util/protogen/internalgengo/getter.go new file mode 100644 index 0000000..78deef7 --- /dev/null +++ b/apiv2/util/protogen/internalgengo/getter.go @@ -0,0 +1,14 @@ +package internalgengo + +import "google.golang.org/protobuf/compiler/protogen" + +func emitGetterSetter(g *protogen.GeneratedFile, typeName string, fieldName string, fieldType string, defaultValue string) { + g.P("func (x *", typeName, ") Get", fieldName, "() ", fieldType, " {") + g.P("if x != nil { return x.", fieldName, "}") + g.P("return ", defaultValue) + g.P("}") + + g.P("func (x *", typeName, ") Set", fieldName, "(v ", fieldType, ") {") + g.P("x.", fieldName, " = v") + g.P("}") +} diff --git a/apiv2/util/protogen/internalgengo/json.go b/apiv2/util/protogen/internalgengo/json.go new file mode 100644 index 0000000..f7cd284 --- /dev/null +++ b/apiv2/util/protogen/internalgengo/json.go @@ -0,0 +1,228 @@ +package internalgengo + +import ( + "fmt" + + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +var ( + jwriterPackage = protogen.GoImportPath("github.com/mailru/easyjson/jwriter") + jlexerPackage = protogen.GoImportPath("github.com/mailru/easyjson/jlexer") +) + +func emitJSONMethods(g *protogen.GeneratedFile, msg *protogen.Message) { + emitJSONMarshal(g, msg) + emitJSONUnmarshal(g, msg) +} + +func emitJSONUnmarshal(g *protogen.GeneratedFile, msg *protogen.Message) { + g.P("// UnmarshalJSON implements the json.Unmarshaler interface.") + g.P("func (x *", msg.GoIdent.GoName, ") UnmarshalJSON(data []byte) error {") + g.P("r := ", jlexerPackage.Ident("Lexer"), "{Data: data}") + g.P("x.UnmarshalEasyJSON(&r)") + g.P("return r.Error()") + g.P("}") + + g.P("func (x *", msg.GoIdent.GoName, ") UnmarshalEasyJSON(in *", jlexerPackage.Ident("Lexer"), ") {") + + g.P("isTopLevel := in.IsStart()") + g.P("if in.IsNull() {") + g.P("if isTopLevel { in.Consumed() }") + g.P("in.Skip()") + g.P("return") + g.P("}") + + g.P("in.Delim('{')") + g.P("for !in.IsDelim('}') {") + + g.P("key := in.UnsafeFieldName(false)") + g.P("in.WantColon()") + g.P("if in.IsNull() { in.Skip(); in.WantComma(); continue }") + g.P("switch key {") + for _, f := range msg.Fields { + g.P(`case "`, fieldJSONName(f), `":`) + if f.Oneof != nil { + g.P("xx := new(", f.GoIdent, ")") + g.P("x." + f.Oneof.GoName + " = xx") + emitJSONFieldRead(g, f, "xx") + continue + } + emitJSONFieldRead(g, f, "x") + } + g.P("}") + g.P("in.WantComma()") + g.P("}") + g.P("in.Delim('}')") + g.P("if isTopLevel { in.Consumed() }") + g.P("}") +} + +func emitJSONFieldRead(g *protogen.GeneratedFile, f *protogen.Field, name string) { + g.P("{") + defer g.P("}") + + if f.Desc.IsList() { + g.P("var f ", fieldType(g, f)[2:]) + g.P("var list ", fieldType(g, f)) + g.P("in.Delim('[')") + defer g.P("in.Delim(']')") + + g.P("for !in.IsDelim(']') {") + } else { + g.P("var f ", fieldType(g, f)) + } + + var template string + switch f.Desc.Kind() { + case protoreflect.BoolKind: + template = "%s = in.Bool()" + case protoreflect.EnumKind: + g.Import(strconvPackage) + + enumType := fieldType(g, f).String() + g.P("var parsedValue " + enumType) + g.P(`switch v := in.Interface().(type) { + case string: + if vv, ok := `+enumType+`_value[v]; ok { + parsedValue = `+enumType+`(vv) + break + } + vv, err := `, strconvPackage.Ident("ParseInt"), `(v, 10, 32) + if err != nil { + in.AddError(err) + return + } + parsedValue = `+enumType+`(vv) + case float64: + parsedValue = `+enumType+`(v) + }`) + template = "%s = parsedValue" + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: + template = "%s = in.Int32()" + case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: + template = "%s = in.Uint32()" + case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: + template = "%s = in.Int64()" + case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: + template = "%s = in.Uint64()" + case protoreflect.FloatKind: + template = "%s = in.Float32()" + case protoreflect.DoubleKind: + template = "%s = in.Float64()" + case protoreflect.StringKind: + template = "%s = in.String()" + case protoreflect.BytesKind: + template = "%s = in.Bytes()" + case protoreflect.MessageKind: + if f.Desc.IsList() { + g.P("f = ", fieldType(g, f)[2:], "{}") + } else { + g.P("f = new(", fieldType(g, f)[1:], ")") + } + template = "%s.UnmarshalEasyJSON(in)" + case protoreflect.GroupKind: + panic("unimplemented") + } + g.P(fmt.Sprintf(template, "f")) + if f.Desc.IsList() { + g.P("list = append(list, f)") + g.P("in.WantComma()") + g.P("}") + g.P(name, ".", f.GoName, " = list") + } else { + g.P(name, ".", f.GoName, " = f") + } +} + +func emitJSONMarshal(g *protogen.GeneratedFile, msg *protogen.Message) { + g.P("// MarshalJSON implements the json.Marshaler interface.") + g.P("func (x *", msg.GoIdent.GoName, ") MarshalJSON() ([]byte, error) {") + g.P("w := ", jwriterPackage.Ident("Writer"), "{}") + g.P("x.MarshalEasyJSON(&w)") + g.P("return w.Buffer.BuildBytes(), w.Error") + g.P("}") + + g.P("func (x *", msg.GoIdent.GoName, ") MarshalEasyJSON(out *", jwriterPackage.Ident("Writer"), ") {") + g.P(`if x == nil { out.RawString("null"); return }`) + + g.P("out.RawByte('{')") + for i, f := range msg.Fields { + if f.Oneof != nil { + if f.Oneof.Fields[0] != f { + continue + } + + g.P("switch xx := x.", f.Oneof.GoName, ".(type) {") + for _, ff := range f.Oneof.Fields { + g.P("case *", ff.GoIdent, ":") + emitJSONFieldWrite(g, ff, "xx", i == 0) + } + g.P("}") + continue + } + emitJSONFieldWrite(g, f, "x", i == 0) + } + g.P("out.RawByte('}')") + g.P("}") +} + +func emitJSONFieldWrite(g *protogen.GeneratedFile, f *protogen.Field, name string, first bool) { + g.P("{") + defer g.P("}") + + g.P("const prefix string = ", `",\"`, fieldJSONName(f), `\":"`) + if first { + g.P("out.RawString(prefix[1:])") + } else { + g.P("out.RawString(prefix)") + } + + selector := name + "." + f.GoName + if f.Desc.IsList() { + selector += "[i]" + g.P("out.RawByte('[')") + defer g.P("out.RawByte(']')") + + g.P("for i := range ", name, ".", f.GoName, " {") + g.P("if i != 0 { out.RawByte(',') }") + defer g.P("}") + } + + var template string + switch f.Desc.Kind() { + case protoreflect.BoolKind: + template = "out.Bool(%s)" + case protoreflect.EnumKind: + template = "out.Int32(int32(%s))" + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: + template = "out.Int32(%s)" + case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: + template = "out.Uint32(%s)" + case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: + template = "out.Int64(%s)" + case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: + template = "out.Uint64(%s)" + case protoreflect.FloatKind: + template = "out.Float32(%s)" + case protoreflect.DoubleKind: + template = "out.Float64(%s)" + case protoreflect.StringKind: + template = "out.String(%s)" + case protoreflect.BytesKind: + template = "out.Base64Bytes(%s)" + case protoreflect.MessageKind: + template = "%s.MarshalEasyJSON(out)" + case protoreflect.GroupKind: + panic("unimplemented") + } + g.P(fmt.Sprintf(template, selector)) +} + +func fieldJSONName(f *protogen.Field) string { + if f.Desc.HasJSONName() { + return f.Desc.JSONName() + } + return string(f.Desc.Name()) +} diff --git a/apiv2/util/protogen/internalgengo/options.go b/apiv2/util/protogen/internalgengo/options.go new file mode 100644 index 0000000..8aab8f0 --- /dev/null +++ b/apiv2/util/protogen/internalgengo/options.go @@ -0,0 +1,7 @@ +package internalgengo + +type Options struct { + Fuzz bool `yaml:"fuzz"` + JSON bool `yaml:"json"` + MessageData []string `yaml:"message_data"` +} diff --git a/apiv2/util/protogen/internalgengo/proto.go b/apiv2/util/protogen/internalgengo/proto.go new file mode 100644 index 0000000..1467541 --- /dev/null +++ b/apiv2/util/protogen/internalgengo/proto.go @@ -0,0 +1,202 @@ +package internalgengo + +import ( + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +func emitProtoMethods(g *protogen.GeneratedFile, msg *protogen.Message) { + emitMarshalProtobuf(g, msg) + emitUnmarshalProtobuf(g, msg) +} + +func emitUnmarshalProtobuf(g *protogen.GeneratedFile, msg *protogen.Message) { + g.P("// UnmarshalProtobuf implements the encoding.ProtoUnmarshaler interface.") + g.P("func (x *", msg.GoIdent.GoName, ") UnmarshalProtobuf(src []byte) (err error) {") + g.P("var fc ", easyprotoPackage.Ident("FieldContext")) + g.P("for len(src) > 0 {") + { + g.P("src, err = fc.NextField(src)") + g.P("if err != nil { return ", fmtPackage.Ident("Errorf"), `("cannot read next field in %s", "`, msg.GoIdent.GoName, `")}`) + g.P("switch fc.FieldNum {") + { + for _, f := range msg.Fields { + g.P("case ", f.Desc.Number(), ":", " // ", f.GoName) + emitFieldUnmarshal(g, f) + } + } + g.P("}") + } + g.P("}") + + g.P("return nil") + g.P("}") +} + +func emitFieldUnmarshal(g *protogen.GeneratedFile, f *protogen.Field) { + name := castFieldName(f) + if f.Desc.Kind() == protoreflect.MessageKind { + g.P("data, ok := fc.MessageData()") + g.P(`if !ok { return fmt.Errorf("cannot unmarshal field %s", "`, f.GoName, `") }`) + if f.Desc.IsList() { + g.P(name, " = append(", name, ", ", fieldType(g, f)[2:], "{})") + g.P("ff := &", name, "[len(", name, ")-1]") + name = "ff" + } else if f.Oneof != nil { + const tmp = "oneofField" + g.P(tmp, " := &", f.GoIdent, "{", f.GoName, ": ", "new(", fieldType(g, f)[1:], ")}") + defer g.P(name, " = ", tmp) + + name = tmp + "." + f.GoName + } else { + g.P(name, " = new(", fieldType(g, f)[1:], ")") + } + + g.P(`if err := `, name, `.UnmarshalProtobuf(data); err != nil { return fmt.Errorf("unmarshal: %w", err)}`) + return + } + + getter, _ := easyprotoKindInfo(f.Desc.Kind()) + + if f.Desc.IsList() && (f.Desc.Kind() == protoreflect.BytesKind || f.Desc.Kind() == protoreflect.StringKind || f.Desc.Kind() == protoreflect.Uint64Kind && !f.Desc.IsPacked()) { + g.P("data, ok := fc.", getter, "()") + g.P(`if !ok { return fmt.Errorf("cannot unmarshal field %s", "`, f.GoName, `") }`) + g.P(name, " = append(", name, ", data)") + return + } + + if f.Desc.IsList() { + g.P("data, ok := fc.Unpack", getter, "s(nil)") + } else { + g.P("data, ok := fc.", getter, "()") + } + + g.P(`if !ok { return fmt.Errorf("cannot unmarshal field %s", "`, f.GoName, `") }`) + value := "data" + if f.Desc.Kind() == protoreflect.EnumKind { + value = fieldType(g, f).String() + "(data)" + } + + if f.Oneof == nil { + g.P("x.", f.GoName, " = ", value) + } else { + g.P("x.", f.Oneof.GoName, " = &", f.GoIdent, "{", f.GoName, ": data}") + } +} + +func emitMarshalProtobuf(g *protogen.GeneratedFile, msg *protogen.Message) { + g.P("// MarshalProtobuf implements the encoding.ProtoMarshaler interface.") + g.P("func (x *", msg.GoIdent.GoName, ") MarshalProtobuf(dst []byte) []byte {") + g.P("m := ", mp, ".Get()") + g.P("defer ", mp, ".Put(m)") + g.P("x.EmitProtobuf(m.MessageMarshaler())") + g.P("dst = m.Marshal(dst)") + g.P("return dst") + g.P("}\n") + + g.P("func (x *", msg.GoIdent.GoName, ") EmitProtobuf(mm *", easyprotoPackage.Ident("MessageMarshaler"), ") {") + if len(msg.Fields) != 0 { + fs := sortFields(msg.Fields) + + g.P("if x == nil { return }") + for _, f := range fs { + emitFieldMarshal(g, f) + } + } + g.P("}") +} + +func emitMarshalOneof(g *protogen.GeneratedFile, f *protogen.Field) { + name := "x." + f.Oneof.GoName + g.P("if inner, ok := ", name, ".(*", f.GoIdent.GoName, "); ok {") + defer g.P("}") + emitMarshalRaw(g, f, "inner."+f.GoName) +} + +// easyprotoKindInfo returns string name for kind, used in easyproto methods. +// The second return value is a condition to test for the default value of kind. +func easyprotoKindInfo(kind protoreflect.Kind) (string, func(string) string) { + switch kind { + case protoreflect.BoolKind: + return "Bool", identity + case protoreflect.EnumKind: + return "Int32", notZero + case protoreflect.Int32Kind: + return "Int32", notZero + case protoreflect.Sint32Kind: + return "Sint32", notZero + case protoreflect.Uint32Kind: + return "Uint32", notZero + case protoreflect.Int64Kind: + return "Int64", notZero + case protoreflect.Sint64Kind: + return "Sint64", notZero + case protoreflect.Uint64Kind: + return "Uint64", notZero + case protoreflect.Sfixed32Kind: + return "Sfixed32", notZero + case protoreflect.Fixed32Kind: + return "Fixed32", notZero + case protoreflect.FloatKind: + return "Float", notZero + case protoreflect.Sfixed64Kind: + return "Sfixed64", notZero + case protoreflect.Fixed64Kind: + return "Fixed64", notZero + case protoreflect.DoubleKind: + return "Double", notZero + case protoreflect.StringKind: + return "String", notEmpty + case protoreflect.BytesKind: + return "Bytes", notEmpty + case protoreflect.GroupKind: + panic("unimplemented") + default: + panic("unreachable") + } +} + +func emitFieldMarshal(g *protogen.GeneratedFile, f *protogen.Field) { + if f.Oneof != nil { + emitMarshalOneof(g, f) + return + } + + emitMarshalRaw(g, f, castFieldName(f)) +} + +func emitMarshalRaw(g *protogen.GeneratedFile, f *protogen.Field, name string) { + if f.Desc.Kind() == protoreflect.MessageKind { + if f.Desc.IsList() { + g.P("for i := range ", name, " {") + defer g.P("}") + + name += "[i]" + } else { + g.P("if ", notNil(name), " {") + defer g.P("}") + } + + g.P(name, ".EmitProtobuf(mm.AppendMessage(", f.Desc.Number(), "))") + return + } + + method, cond := easyprotoKindInfo(f.Desc.Kind()) + method = "Append" + method + if f.Desc.IsList() && !f.Desc.IsPacked() { + g.P("for j := range ", name, " {") + g.P("mm.", method, "(", f.Desc.Number(), ", ", name, "[j])") + g.P("}") + return + } + + if f.Desc.IsList() { + method += "s" + g.P("if ", notEmpty(name), "{") + } else { + g.P("if ", cond(name), " {") + } + + g.P("mm.", method, "(", f.Desc.Number(), ", ", name, ")") + g.P("}") +} diff --git a/apiv2/util/protogen/internalgengo/proto_field_type.go b/apiv2/util/protogen/internalgengo/proto_field_type.go new file mode 100644 index 0000000..0096751 --- /dev/null +++ b/apiv2/util/protogen/internalgengo/proto_field_type.go @@ -0,0 +1,59 @@ +package internalgengo + +import ( + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type structField string + +func (f structField) String() string { + return string(f) +} + +func (f structField) PointerTo() structField { + return "*" + f +} + +func (f structField) SliceOf() structField { + return "[]" + f +} + +func fieldType(g *protogen.GeneratedFile, field *protogen.Field) structField { + var typ structField + switch field.Desc.Kind() { + case protoreflect.BoolKind: + typ = "bool" + case protoreflect.EnumKind: + typ = structField(g.QualifiedGoIdent(field.Enum.GoIdent)) + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: + typ = "int32" + case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: + typ = "uint32" + case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: + typ = "int64" + case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: + typ = "uint64" + case protoreflect.FloatKind: + typ = "float32" + case protoreflect.DoubleKind: + typ = "float64" + case protoreflect.StringKind: + typ = "string" + case protoreflect.BytesKind: + typ = "[]byte" + case protoreflect.MessageKind: + typ = structField(g.QualifiedGoIdent(field.Message.GoIdent)) + if !field.Desc.IsList() { + typ = typ.PointerTo() + } + case protoreflect.GroupKind: + panic("unimplemented") + } + + if field.Desc.IsList() { + typ = "[]" + typ + } + + return typ +} diff --git a/apiv2/util/protogen/internalgengo/proto_stable_compat.go b/apiv2/util/protogen/internalgengo/proto_stable_compat.go new file mode 100644 index 0000000..3c4670c --- /dev/null +++ b/apiv2/util/protogen/internalgengo/proto_stable_compat.go @@ -0,0 +1,124 @@ +package internalgengo + +import ( + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +var protowirePackage = protogen.GoImportPath("google.golang.org/protobuf/encoding/protowire") + +func emitStableSize(g *protogen.GeneratedFile, msg *protogen.Message) { + fs := sortFields(msg.Fields) + + g.P("// StableSize returns the size of x in protobuf format.") + g.P("//") + g.P("// Structures with the same field values have the same binary size.") + g.P("func (x *", msg.GoIdent.GoName, ") StableSize() (size int) {") + g.P("if x == nil { return 0 }") + if len(fs) != 0 { + for _, f := range fs { + if f.Desc.IsList() && marshalers[f.Desc.Kind()].RepeatedDouble && !(f.Desc.Kind() == protoreflect.Uint64Kind && !f.Desc.IsPacked()) { + g.P("var n int") + break + } + } + for _, f := range fs { + emitFieldSize(g, f) + } + } + g.P("return size") + g.P("}\n") +} + +func emitSignatureMethods(g *protogen.GeneratedFile, msg *protogen.Message) { + // SignedDataSize implementation (only for requests and responses). + g.P("// ReadSignedData fills buf with signed data of x.") + g.P("// If buffer length is less than x.SignedDataSize(), new buffer is allocated.") + g.P("//") + g.P("// Returns any error encountered which did not allow writing the data completely.") + g.P("// Otherwise, returns the buffer in which the data is written.") + g.P("//") + g.P("// Structures with the same field values have the same signed data.") + g.P("func (x *", msg.GoIdent.GoName, ") SignedDataSize() int {") + g.P("return x.GetBody().StableSize()") + g.P("}\n") + + // ReadSignedData implementation (only for requests and responses). + g.P("// SignedDataSize returns size of the request signed data in bytes.") + g.P("//") + g.P("// Structures with the same field values have the same signed data size.") + g.P("func (x *", msg.GoIdent.GoName, ") ReadSignedData(buf []byte) ([]byte, error) {") + g.P("return x.GetBody().MarshalProtobuf(buf), nil") + g.P("}\n") +} + +func emitFieldSize(g *protogen.GeneratedFile, f *protogen.Field) { + m := marshalers[f.Desc.Kind()] + if m.Prefix == "" { + g.P("// FIXME missing field marshaler: ", f.GoName, " of type ", f.Desc.Kind().String()) + g.P(`panic("unimplemented")`) + return + } + + name := castFieldName(f) + if f.Oneof != nil { + name = "x." + f.Oneof.GoName + g.P("if inner, ok := ", name, ".(*", f.GoIdent.GoName, "); ok {") + defer g.P("}") + name = "inner." + f.GoName + } + + switch { + case f.Desc.IsList() && (f.Desc.Kind() == protoreflect.MessageKind || f.Desc.Kind() == protoreflect.Uint64Kind && !f.Desc.IsPacked()): + g.P("for i := range ", name, "{") + if f.Desc.Kind() == protoreflect.MessageKind { + g.P("size += ", protoPackage.Ident("NestedStructureSizeUnchecked"), "(", f.Desc.Number(), ", &", name, "[i])") + } else { + if f.Desc.Kind() != protoreflect.Uint64Kind { + panic("only uint64 unpacked primitive is supported") + } + + g.P("size += ", protowirePackage.Ident("SizeGroup"), "(", + protowirePackage.Ident("Number"), "(", f.Desc.Number(), "), ", + protowirePackage.Ident("SizeVarint"), "(", name, "[i]))") + } + g.P("}") + + case f.Desc.IsList(): + if m.RepeatedDouble { + g.P("n, _ = ", protoPackage.Ident("Repeated"+m.Prefix+"Size"), "(", f.Desc.Number(), ", ", name, ")") + g.P("size += n") + } else { + g.P("size += ", protoPackage.Ident("Repeated"+m.Prefix+"Size"), "(", f.Desc.Number(), ", ", name, ")") + } + default: + g.P("size += ", protoPackage.Ident(m.Prefix+"Size"), "(", f.Desc.Number(), ", ", name, ")") + } +} + +type marshalerDesc struct { + Prefix string + RepeatedDouble bool +} + +// Unused kinds are commented. +var marshalers = map[protoreflect.Kind]marshalerDesc{ + protoreflect.BoolKind: {Prefix: "Bool"}, + protoreflect.EnumKind: {Prefix: "Enum"}, + protoreflect.Int32Kind: {Prefix: "Int32", RepeatedDouble: true}, + // protoreflect.Sint32Kind: "", + protoreflect.Uint32Kind: {Prefix: "UInt32", RepeatedDouble: true}, + protoreflect.Int64Kind: {Prefix: "Int64", RepeatedDouble: true}, + // protoreflect.Sint64Kind: "", + protoreflect.Uint64Kind: {Prefix: "UInt64", RepeatedDouble: true}, + // protoreflect.Sfixed32Kind: "", + protoreflect.Fixed32Kind: {Prefix: "Fixed32", RepeatedDouble: true}, + // protoreflect.FloatKind: "", + // protoreflect.Sfixed64Kind: "", + protoreflect.Fixed64Kind: {Prefix: "Fixed64", RepeatedDouble: true}, + protoreflect.DoubleKind: {Prefix: "Float64"}, + protoreflect.StringKind: {Prefix: "String"}, + protoreflect.BytesKind: {Prefix: "Bytes"}, + protoreflect.MessageKind: {Prefix: "NestedStructure"}, + // protoreflect.GroupKind: "", +} diff --git a/apiv2/util/protogen/internalgengo/writer.go b/apiv2/util/protogen/internalgengo/writer.go new file mode 100644 index 0000000..7b0d4f1 --- /dev/null +++ b/apiv2/util/protogen/internalgengo/writer.go @@ -0,0 +1,30 @@ +package internalgengo + +import ( + "fmt" +) + +type condition = func(string) string + +var ( + _ condition = notZero + _ condition = notEmpty + _ condition = identity + _ condition = notNil +) + +func notZero(name string) string { + return fmt.Sprintf("%s != 0", name) +} + +func notEmpty(name string) string { + return fmt.Sprintf("len(%s) != 0", name) +} + +func identity(name string) string { + return name +} + +func notNil(name string) string { + return fmt.Sprintf("%s != nil", name) +} diff --git a/apiv2/util/protogen/main.go b/apiv2/util/protogen/main.go new file mode 100644 index 0000000..6e28c69 --- /dev/null +++ b/apiv2/util/protogen/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "flag" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/protogen/internalgengo" + "google.golang.org/protobuf/compiler/protogen" +) + +func main() { + var flags flag.FlagSet + genFuzz := flags.Bool("fuzz", false, "generate fuzz tests") + + protogen.Options{ + ParamFunc: flags.Set, + }.Run(func(gen *protogen.Plugin) error { + for _, f := range gen.Files { + if f.Generate { + internalgengo.GenerateFile(gen, f) + if *genFuzz { + internalgengo.GenerateFuzzTests(gen, f) + } + } + } + return nil + }) +} diff --git a/apiv2/util/signature/data.go b/apiv2/util/signature/data.go new file mode 100644 index 0000000..fc291db --- /dev/null +++ b/apiv2/util/signature/data.go @@ -0,0 +1,93 @@ +package signature + +import ( + "crypto/ecdsa" + + crypto "git.frostfs.info/TrueCloudLab/frostfs-crypto" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/pool" +) + +const poolSliceMaxSize = 128 * 1024 + +var buffersPool = pool.NewBufferPool(poolSliceMaxSize) + +type DataSource interface { + ReadSignedData([]byte) ([]byte, error) + SignedDataSize() int +} + +type DataWithSignature interface { + DataSource + GetSignature() *refs.Signature + SetSignature(*refs.Signature) +} + +type SignOption func(*cfg) + +type KeySignatureHandler func(*refs.Signature) + +type KeySignatureSource func() *refs.Signature + +func SignDataWithHandler(key *ecdsa.PrivateKey, src DataSource, handler KeySignatureHandler, opts ...SignOption) error { + if key == nil { + return crypto.ErrEmptyPrivateKey + } + + cfg := defaultCfg() + + for i := range opts { + opts[i](cfg) + } + + buffer := buffersPool.Get(uint32(src.SignedDataSize())) + defer buffersPool.Put(buffer) + + data, err := src.ReadSignedData(buffer.Data) + if err != nil { + return err + } + + sigData, err := sign(cfg, key, data) + if err != nil { + return err + } + + sig := new(refs.Signature) + sig.SetScheme(cfg.scheme) + sig.SetKey(crypto.MarshalPublicKey(&key.PublicKey)) + sig.SetSign(sigData) + handler(sig) + + return nil +} + +func VerifyDataWithSource(dataSrc DataSource, sigSrc KeySignatureSource, opts ...SignOption) error { + buffer := buffersPool.Get(uint32(dataSrc.SignedDataSize())) + defer buffersPool.Put(buffer) + + data, err := dataSrc.ReadSignedData(buffer.Data) + if err != nil { + return err + } + + return VerifyDataSlice(data, sigSrc, opts...) +} + +func SignData(key *ecdsa.PrivateKey, v DataWithSignature, opts ...SignOption) error { + return SignDataWithHandler(key, v, v.SetSignature, opts...) +} + +func VerifyData(src DataWithSignature, opts ...SignOption) error { + return VerifyDataWithSource(src, src.GetSignature, opts...) +} + +func VerifyDataSlice(data []byte, sigSrc KeySignatureSource, opts ...SignOption) error { + cfg := defaultCfg() + + for i := range opts { + opts[i](cfg) + } + + return verify(cfg, data, sigSrc()) +} diff --git a/apiv2/util/signature/options.go b/apiv2/util/signature/options.go new file mode 100644 index 0000000..a755c2d --- /dev/null +++ b/apiv2/util/signature/options.go @@ -0,0 +1,77 @@ +package signature + +import ( + "crypto/ecdsa" + "encoding/base64" + "fmt" + + crypto "git.frostfs.info/TrueCloudLab/frostfs-crypto" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/signature/walletconnect" +) + +type cfg struct { + schemeFixed bool + scheme refs.SignatureScheme +} + +func defaultCfg() *cfg { + return new(cfg) +} + +func verify(cfg *cfg, data []byte, sig *refs.Signature) error { + if !cfg.schemeFixed { + cfg.scheme = sig.GetScheme() + } + + pub := crypto.UnmarshalPublicKey(sig.GetKey()) + if pub == nil { + return crypto.ErrEmptyPublicKey + } + + switch cfg.scheme { + case refs.ECDSA_SHA512: + return crypto.Verify(pub, data, sig.GetSign()) + case refs.ECDSA_RFC6979_SHA256: + return crypto.VerifyRFC6979(pub, data, sig.GetSign()) + case refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT: + buffer := buffersPool.Get(uint32(base64.StdEncoding.EncodedLen(len(data)))) + defer buffersPool.Put(buffer) + base64.StdEncoding.Encode(buffer.Data, data) + if !walletconnect.Verify(pub, buffer.Data, sig.GetSign()) { + return crypto.ErrInvalidSignature + } + return nil + default: + return fmt.Errorf("unsupported signature scheme %s", cfg.scheme) + } +} + +func sign(cfg *cfg, key *ecdsa.PrivateKey, data []byte) ([]byte, error) { + switch cfg.scheme { + case refs.ECDSA_SHA512: + return crypto.Sign(key, data) + case refs.ECDSA_RFC6979_SHA256: + return crypto.SignRFC6979(key, data) + case refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT: + buffer := buffersPool.Get(uint32(base64.StdEncoding.EncodedLen(len(data)))) + defer buffersPool.Put(buffer) + base64.StdEncoding.Encode(buffer.Data, data) + return walletconnect.Sign(key, buffer.Data) + default: + panic(fmt.Sprintf("unsupported scheme %s", cfg.scheme)) + } +} + +func SignWithRFC6979() SignOption { + return func(c *cfg) { + c.schemeFixed = true + c.scheme = refs.ECDSA_RFC6979_SHA256 + } +} + +func SignWithWalletConnect() SignOption { + return func(c *cfg) { + c.scheme = refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT + } +} diff --git a/apiv2/util/signature/sign_test.go b/apiv2/util/signature/sign_test.go new file mode 100644 index 0000000..1c6e897 --- /dev/null +++ b/apiv2/util/signature/sign_test.go @@ -0,0 +1,44 @@ +package signature + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "github.com/stretchr/testify/require" +) + +type testData struct { + data []byte + sig *refs.Signature +} + +func (t testData) SignedDataSize() int { return len(t.data) } +func (t testData) ReadSignedData(data []byte) ([]byte, error) { + n := copy(data, t.data) + return data[:n], nil +} +func (t testData) GetSignature() *refs.Signature { return t.sig } +func (t *testData) SetSignature(s *refs.Signature) { t.sig = s } + +func TestWalletConnect(t *testing.T) { + testCases := [...][]byte{ + {}, + {0}, + {1, 2}, + {3, 4, 5}, + {6, 7, 8, 9, 10, 11, 12}, + } + + pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + + for _, tc := range testCases { + td := &testData{data: tc} + require.NoError(t, SignData(pk, td, SignWithWalletConnect())) + require.Equal(t, refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT, td.sig.GetScheme()) + require.NoError(t, VerifyData(td)) + } +} diff --git a/apiv2/util/signature/walletconnect/sign.go b/apiv2/util/signature/walletconnect/sign.go new file mode 100644 index 0000000..b96a842 --- /dev/null +++ b/apiv2/util/signature/walletconnect/sign.go @@ -0,0 +1,142 @@ +package walletconnect + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/binary" + "encoding/hex" + + crypto "git.frostfs.info/TrueCloudLab/frostfs-crypto" +) + +const ( + // saltSize is the salt size added to signed message. + saltSize = 16 + // signatureLen is the length of RFC6979 signature. + signatureLen = 64 +) + +// SignedMessage contains mirrors `SignedMessage` struct from the WalletConnect API. +// https://neon.coz.io/wksdk/core/modules.html#SignedMessage +type SignedMessage struct { + Data []byte + Message []byte + PublicKey []byte + Salt []byte +} + +// Sign signs message using WalletConnect API. The returned signature +// contains RFC6979 signature and 16-byte salt. +func Sign(p *ecdsa.PrivateKey, msg []byte) ([]byte, error) { + sm, err := SignMessage(p, msg) + if err != nil { + return nil, err + } + return append(sm.Data, sm.Salt...), nil +} + +// Verify verifies message using WalletConnect API. +func Verify(p *ecdsa.PublicKey, data, sign []byte) bool { + if len(sign) != signatureLen+saltSize { + return false + } + + salt := sign[signatureLen:] + return VerifyMessage(p, SignedMessage{ + Data: sign[:signatureLen], + Message: createMessageWithSalt(data, salt), + Salt: salt, + }) +} + +// SignMessage signs message with a private key and returns structure similar to +// `signMessage` of the WalletConnect API. +// https://github.com/CityOfZion/wallet-connect-sdk/blob/89c236b/packages/wallet-connect-sdk-core/src/index.ts#L496 +// https://github.com/CityOfZion/neon-wallet/blob/1174a9388480e6bbc4f79eb13183c2a573f67ca8/app/context/WalletConnect/helpers.js#L133 +func SignMessage(p *ecdsa.PrivateKey, msg []byte) (SignedMessage, error) { + var salt [saltSize]byte + _, _ = rand.Read(salt[:]) + + msg = createMessageWithSalt(msg, salt[:]) + sign, err := crypto.SignRFC6979(p, msg) + if err != nil { + return SignedMessage{}, err + } + + return SignedMessage{ + Data: sign, + Message: msg, + PublicKey: elliptic.MarshalCompressed(p.Curve, p.X, p.Y), + Salt: salt[:], + }, nil +} + +// VerifyMessage verifies message with a private key and returns structure similar to +// `verifyMessage` of WalletConnect API. +// https://github.com/CityOfZion/wallet-connect-sdk/blob/89c236b/packages/wallet-connect-sdk-core/src/index.ts#L515 +// https://github.com/CityOfZion/neon-wallet/blob/1174a9388480e6bbc4f79eb13183c2a573f67ca8/app/context/WalletConnect/helpers.js#L147 +func VerifyMessage(p *ecdsa.PublicKey, m SignedMessage) bool { + if p == nil { + x, y := elliptic.UnmarshalCompressed(elliptic.P256(), m.PublicKey) + if x == nil || y == nil { + return false + } + p = &ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: x, + Y: y, + } + } + return crypto.VerifyRFC6979(p, m.Message, m.Data) == nil +} + +func createMessageWithSalt(msg, salt []byte) []byte { + // 4 byte prefix + length of the message with salt in bytes + + // + salt + message + 2 byte postfix. + saltedLen := hex.EncodedLen(len(salt)) + len(msg) + data := make([]byte, 4+getVarIntSize(saltedLen)+saltedLen+2) + + n := copy(data, []byte{0x01, 0x00, 0x01, 0xf0}) // fixed prefix + n += putVarUint(data[n:], uint64(saltedLen)) // salt is hex encoded, double its size + n += hex.Encode(data[n:], salt[:]) // for some reason we encode salt in hex + n += copy(data[n:], msg) + copy(data[n:], []byte{0x00, 0x00}) + + return data +} + +// Following functions are copied from github.com/nspcc-dev/neo-go/pkg/io package +// to avoid having another dependency. + +// getVarIntSize returns the size in number of bytes of a variable integer. +// Reference: https://github.com/neo-project/neo/blob/26d04a642ac5a1dd1827dabf5602767e0acba25c/src/neo/IO/Helper.cs#L131 +func getVarIntSize(value int) int { + var size uintptr + + if value < 0xFD { + size = 1 // unit8 + } else if value <= 0xFFFF { + size = 3 // byte + uint16 + } else { + size = 5 // byte + uint32 + } + return int(size) +} + +// putVarUint puts val in varint form to the pre-allocated buffer. +func putVarUint(data []byte, val uint64) int { + if val < 0xfd { + data[0] = byte(val) + return 1 + } + if val <= 0xFFFF { + data[0] = byte(0xfd) + binary.LittleEndian.PutUint16(data[1:], uint16(val)) + return 3 + } + + data[0] = byte(0xfe) + binary.LittleEndian.PutUint32(data[1:], uint32(val)) + return 5 +} diff --git a/apiv2/util/signature/walletconnect/sign_test.go b/apiv2/util/signature/walletconnect/sign_test.go new file mode 100644 index 0000000..1b4fe18 --- /dev/null +++ b/apiv2/util/signature/walletconnect/sign_test.go @@ -0,0 +1,112 @@ +package walletconnect + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/hex" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSignMessage(t *testing.T) { + p1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + + msg := []byte("NEO") + result, err := SignMessage(p1, msg) + require.NoError(t, err) + require.Equal(t, elliptic.MarshalCompressed(elliptic.P256(), p1.PublicKey.X, p1.PublicKey.Y), result.PublicKey) + require.Equal(t, saltSize, len(result.Salt)) + require.Equal(t, 64, len(result.Data)) + require.Equal(t, 4+1+16*2+3+2, len(result.Message)) + + require.True(t, VerifyMessage(&p1.PublicKey, result)) + + t.Run("invalid public key", func(t *testing.T) { + p2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + require.False(t, VerifyMessage(&p2.PublicKey, result)) + }) + t.Run("invalid signature", func(t *testing.T) { + result := result + result.Data[0] ^= 0xFF + require.False(t, VerifyMessage(&p1.PublicKey, result)) + }) +} + +func TestSign(t *testing.T) { + p1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + + msg := []byte("NEO") + sign, err := Sign(p1, msg) + require.NoError(t, err) + require.True(t, Verify(&p1.PublicKey, msg, sign)) + + t.Run("invalid public key", func(t *testing.T) { + p2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + require.False(t, Verify(&p2.PublicKey, msg, sign)) + }) + t.Run("invalid signature", func(t *testing.T) { + sign[0] ^= 0xFF + require.False(t, Verify(&p1.PublicKey, msg, sign)) + }) +} + +func TestVerifyNeonWallet(t *testing.T) { + testCases := [...]struct { + publicKey string + data string + salt string + messageHex string + messageOriginal string + }{ + { // Test values from this GIF https://github.com/CityOfZion/neon-wallet/pull/2390 . + publicKey: "02ce6228ba2cb2fc235be93aff9cd5fc0851702eb9791552f60db062f01e3d83f6", + data: "90ab1886ca0bece59b982d9ade8f5598065d651362fb9ce45ad66d0474b89c0b80913c8f0118a282acbdf200a429ba2d81bc52534a53ab41a2c6dfe2f0b4fb1b", + salt: "d41e348afccc2f3ee45cd9f5128b16dc", + messageHex: "010001f05c6434316533343861666363633266336565343563643966353132386231366463436172616c686f2c206d756c65712c206f2062616775697520656820697373756d65726d6f2074616978206c696761646f206e61206d697373e36f3f0000", + messageOriginal: "436172616c686f2c206d756c65712c206f2062616775697520656820697373756d65726d6f2074616978206c696761646f206e61206d697373e36f3f", + }, + { // Test value from wallet connect integration test + publicKey: "03bd9108c0b49f657e9eee50d1399022bd1e436118e5b7529a1b7cd606652f578f", + data: "510caa8cb6db5dedf04d215a064208d64be7496916d890df59aee132db8f2b07532e06f7ea664c4a99e3bcb74b43a35eb9653891b5f8701d2aef9e7526703eaa", + salt: "2c5b189569e92cce12e1c640f23e83ba", + messageHex: "010001f02632633562313839353639653932636365313265316336343066323365383362613132333435360000", + messageOriginal: "313233343536", // ascii string "123456" + }, + { // Test value from wallet connect integration test + publicKey: "03bd9108c0b49f657e9eee50d1399022bd1e436118e5b7529a1b7cd606652f578f", + data: "1e13f248962d8b3b60708b55ddf448d6d6a28c6b43887212a38b00bf6bab695e61261e54451c6e3d5f1f000e5534d166c7ca30f662a296d3a9aafa6d8c173c01", + salt: "58c86b2e74215b4f36b47d731236be3b", + messageHex: "010001f02035386338366232653734323135623466333662343764373331323336626533620000", + messageOriginal: "", // empty string + }, + } + + for _, testCase := range testCases { + rawPub, err := hex.DecodeString(testCase.publicKey) + require.NoError(t, err) + data, err := hex.DecodeString(testCase.data) + require.NoError(t, err) + salt, err := hex.DecodeString(testCase.salt) + require.NoError(t, err) + msg, err := hex.DecodeString(testCase.messageHex) + require.NoError(t, err) + orig, err := hex.DecodeString(testCase.messageOriginal) + require.NoError(t, err) + + require.Equal(t, msg, createMessageWithSalt(orig, salt)) + + sm := SignedMessage{ + Data: data, + Message: msg, + PublicKey: rawPub, + Salt: salt, + } + require.True(t, VerifyMessage(nil, sm)) + } +} diff --git a/bearer/bearer_test.go b/bearer/bearer_test.go index 341c4f2..35a7749 100644 --- a/bearer/bearer_test.go +++ b/bearer/bearer_test.go @@ -6,8 +6,8 @@ import ( "reflect" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" bearertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer/test" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" diff --git a/checksum/checksum.go b/checksum/checksum.go index 9d227ad..cbcf550 100644 --- a/checksum/checksum.go +++ b/checksum/checksum.go @@ -6,13 +6,13 @@ import ( "errors" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" "git.frostfs.info/TrueCloudLab/tzhash/tz" ) // Checksum represents checksum of some digital data. // -// Checksum is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.Checksum +// Checksum is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs.Checksum // message. See ReadFromV2 / WriteToV2 methods. // // Instances can be created using built-in var declaration. diff --git a/checksum/checksum_test.go b/checksum/checksum_test.go index 9da857d..c65f8a5 100644 --- a/checksum/checksum_test.go +++ b/checksum/checksum_test.go @@ -5,7 +5,7 @@ import ( "crypto/sha256" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" "git.frostfs.info/TrueCloudLab/tzhash/tz" "github.com/stretchr/testify/require" ) diff --git a/checksum/example_test.go b/checksum/example_test.go index 337767a..e381367 100644 --- a/checksum/example_test.go +++ b/checksum/example_test.go @@ -6,7 +6,7 @@ import ( "crypto/sha256" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" ) func ExampleCalculate() { diff --git a/client/accounting.go b/client/accounting.go index faf7ddc..e4dd3a2 100644 --- a/client/accounting.go +++ b/client/accounting.go @@ -11,6 +11,10 @@ import ( v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting" + v2accounting "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + rpcapi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" ) diff --git a/client/api.go b/client/api.go index dd18c42..05c5785 100644 --- a/client/api.go +++ b/client/api.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" - rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" + v2netmap "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" + rpcapi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" ) // interface of FrostFS API server. Exists for test purposes only. diff --git a/client/client.go b/client/client.go index 4dd5059..9adcb70 100644 --- a/client/client.go +++ b/client/client.go @@ -7,9 +7,9 @@ import ( "errors" "time" - v2accounting "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" + v2accounting "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/accounting" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -124,7 +124,7 @@ func (c *Client) Dial(ctx context.Context, prm PrmDial) error { // sets underlying provider of frostFSAPIServer. The method is used for testing as an approach // to skip Dial stage and override FrostFS API server. MUST NOT be used outside test code. -// In real applications wrapper over git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client +// In real applications wrapper over git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client // is statically used. func (c *Client) setFrostFSAPIServer(server frostFSAPIServer) { c.server = server diff --git a/client/common.go b/client/common.go index 65abae5..936a948 100644 --- a/client/common.go +++ b/client/common.go @@ -4,10 +4,10 @@ import ( "errors" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" - v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/signature" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version" ) @@ -115,7 +115,7 @@ func (c *Client) processResponse(resp responseV2) (apistatus.Status, error) { // before closing the connection. // // See also Dial and Close. -// See also git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client package docs. +// See also git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client package docs. func (c *Client) ExecRaw(f func(client *client.Client) error) error { return f(&c.c) } diff --git a/client/container_delete.go b/client/container_delete.go index 6d4b345..0a70f8a 100644 --- a/client/container_delete.go +++ b/client/container_delete.go @@ -4,12 +4,12 @@ import ( "context" "fmt" - v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" - v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" + v2container "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + rpcapi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/signature" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa" diff --git a/client/container_eacl.go b/client/container_eacl.go new file mode 100644 index 0000000..e69de29 diff --git a/client/container_get.go b/client/container_get.go index da8166f..f54dbf6 100644 --- a/client/container_get.go +++ b/client/container_get.go @@ -5,12 +5,12 @@ import ( "errors" "fmt" - v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" - v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" + v2container "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + rpcapi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/signature" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" diff --git a/client/container_list.go b/client/container_list.go index 6d2efb6..e5273fa 100644 --- a/client/container_list.go +++ b/client/container_list.go @@ -4,12 +4,12 @@ import ( "context" "fmt" - v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" - v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" + v2container "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + rpcapi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/signature" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session" diff --git a/client/container_put.go b/client/container_put.go index e5b8f18..61e46e9 100644 --- a/client/container_put.go +++ b/client/container_put.go @@ -4,12 +4,12 @@ import ( "context" "fmt" - v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" - v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" + v2container "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + rpcapi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/signature" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" diff --git a/client/container_set_eacl.go b/client/container_set_eacl.go new file mode 100644 index 0000000..e69de29 diff --git a/client/container_space.go b/client/container_space.go new file mode 100644 index 0000000..e69de29 diff --git a/client/doc.go b/client/doc.go index 7f6ec42..e69d923 100644 --- a/client/doc.go +++ b/client/doc.go @@ -47,8 +47,8 @@ Consume custom service of the server: rpc CustomRPC(CustomRPCRequest) returns (CustomRPCResponse); } - import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" - import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/common" + import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/common" req := new(CustomRPCRequest) // ... diff --git a/client/netmap.go b/client/netmap.go index a870ab2..bbe3326 100644 --- a/client/netmap.go +++ b/client/netmap.go @@ -4,11 +4,11 @@ import ( "context" "fmt" - v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" - rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" - v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" + v2netmap "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" + rpcapi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/signature" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version" diff --git a/client/netmap_test.go b/client/netmap_test.go index bce6b3a..43b0580 100644 --- a/client/netmap_test.go +++ b/client/netmap_test.go @@ -6,9 +6,9 @@ import ( "fmt" "testing" - v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" + v2netmap "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/signature" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap" "github.com/stretchr/testify/require" diff --git a/client/object_delete.go b/client/object_delete.go index 3214cff..eb75e68 100644 --- a/client/object_delete.go +++ b/client/object_delete.go @@ -5,13 +5,13 @@ import ( "crypto/ecdsa" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" - v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" - v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" - v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" + v2object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" + v2refs "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + rpcapi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/signature" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" diff --git a/client/object_get.go b/client/object_get.go index ba4f72d..b7d686e 100644 --- a/client/object_get.go +++ b/client/object_get.go @@ -7,13 +7,13 @@ import ( "fmt" "io" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" - v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" - v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" - v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" + v2object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" + v2refs "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + rpcapi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/signature" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" diff --git a/client/object_hash.go b/client/object_hash.go index b1b9ffc..7d23806 100644 --- a/client/object_hash.go +++ b/client/object_hash.go @@ -5,13 +5,13 @@ import ( "crypto/ecdsa" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" - v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" - v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" - v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" + v2object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" + v2refs "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + rpcapi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/signature" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" diff --git a/client/object_search.go b/client/object_search.go index aa02542..fa063a6 100644 --- a/client/object_search.go +++ b/client/object_search.go @@ -7,13 +7,13 @@ import ( "fmt" "io" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" - v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" - v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" - v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" + v2object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" + v2refs "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + rpcapi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/signature" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" diff --git a/client/object_search_test.go b/client/object_search_test.go index f449d61..e048e86 100644 --- a/client/object_search_test.go +++ b/client/object_search_test.go @@ -7,9 +7,9 @@ import ( "io" "testing" - v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - signatureV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" + v2object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + signatureV2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/signature" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" diff --git a/client/response.go b/client/response.go index e1a702e..0de646d 100644 --- a/client/response.go +++ b/client/response.go @@ -1,6 +1,6 @@ package client -import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" +import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" // ResponseMetaInfo groups meta information about any FrostFS API response. type ResponseMetaInfo struct { diff --git a/client/session.go b/client/session.go index b49c67b..391a973 100644 --- a/client/session.go +++ b/client/session.go @@ -5,11 +5,11 @@ import ( "crypto/ecdsa" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" - v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + rpcapi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/rpc/client" + v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/signature" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" ) diff --git a/client/status/common.go b/client/status/common.go index 598631b..e1f52f4 100644 --- a/client/status/common.go +++ b/client/status/common.go @@ -3,7 +3,7 @@ package apistatus import ( "encoding/binary" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" ) // ServerInternal describes failure statuses related to internal server errors. diff --git a/client/status/common_test.go b/client/status/common_test.go index 5a7a2b7..76648b1 100644 --- a/client/status/common_test.go +++ b/client/status/common_test.go @@ -3,7 +3,7 @@ package apistatus_test import ( "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" "github.com/stretchr/testify/require" ) diff --git a/client/status/container.go b/client/status/container.go index 9f06b27..a5d817c 100644 --- a/client/status/container.go +++ b/client/status/container.go @@ -1,8 +1,8 @@ package apistatus import ( - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" ) // ContainerNotFound describes status of the failure because of the missing container. diff --git a/client/status/object.go b/client/status/object.go index 27ea86c..78ae81e 100644 --- a/client/status/object.go +++ b/client/status/object.go @@ -1,8 +1,8 @@ package apistatus import ( - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" ) // ObjectLocked describes status of the failure because of the locked object. diff --git a/client/status/session.go b/client/status/session.go index 6f60758..a5a78fd 100644 --- a/client/status/session.go +++ b/client/status/session.go @@ -1,8 +1,8 @@ package apistatus import ( - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" ) // SessionTokenNotFound describes status of the failure because of the missing session token. diff --git a/client/status/success.go b/client/status/success.go index a68983d..7206890 100644 --- a/client/status/success.go +++ b/client/status/success.go @@ -1,7 +1,7 @@ package apistatus import ( - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" ) // SuccessDefaultV2 represents Status instance of default success. Implements StatusV2. diff --git a/client/status/unrecognized.go b/client/status/unrecognized.go index 19e481e..feb1097 100644 --- a/client/status/unrecognized.go +++ b/client/status/unrecognized.go @@ -1,7 +1,7 @@ package apistatus import ( - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/status" ) type unrecognizedStatusV2 struct { diff --git a/container/container.go b/container/container.go index 39c6259..67e1708 100644 --- a/container/container.go +++ b/container/container.go @@ -9,9 +9,9 @@ import ( "strings" "time" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" - v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container" + v2netmap "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" @@ -37,7 +37,7 @@ import ( // Instances for existing containers can be initialized using decoding methods // (e.g Unmarshal). // -// Container is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container.Container +// Container is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container.Container // message. See ReadFromV2 / WriteToV2 methods. type Container struct { v2 container.Container diff --git a/container/container_test.go b/container/container_test.go index c5f1b7e..a92346c 100644 --- a/container/container_test.go +++ b/container/container_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" - v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + v2container "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container" + v2netmap "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" diff --git a/container/doc.go b/container/doc.go index 34b044d..d27dc75 100644 --- a/container/doc.go +++ b/container/doc.go @@ -27,7 +27,7 @@ Instances can be also used to process FrostFS API V2 protocol messages On client side: - import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" + import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/container" var msg container.Container cnr.WriteToV2(&msg) diff --git a/container/id/id.go b/container/id/id.go index bde739c..aeb44c0 100644 --- a/container/id/id.go +++ b/container/id/id.go @@ -4,13 +4,13 @@ import ( "crypto/sha256" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" "github.com/mr-tron/base58" ) // ID represents FrostFS container identifier. // -// ID is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.ContainerID +// ID is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs.ContainerID // message. See ReadFromV2 / WriteToV2 methods. // // Instances can be created using built-in var declaration. diff --git a/container/id/id_test.go b/container/id/id_test.go index ded7457..7efd692 100644 --- a/container/id/id_test.go +++ b/container/id/id_test.go @@ -5,7 +5,7 @@ import ( "crypto/sha256" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" "github.com/mr-tron/base58" diff --git a/container/size.go b/container/size.go new file mode 100644 index 0000000..e69de29 diff --git a/container/size_test.go b/container/size_test.go new file mode 100644 index 0000000..e69de29 diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 7d1ad52..94e7c8c 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -4,7 +4,7 @@ import ( "crypto/rand" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" diff --git a/crypto/doc.go b/crypto/doc.go index 8b568f5..0bc980e 100644 --- a/crypto/doc.go +++ b/crypto/doc.go @@ -29,7 +29,7 @@ Signature can be also used to process FrostFS API V2 protocol messages On client side: - import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" var msg refs.Signature sig.WriteToV2(&msg) diff --git a/crypto/ecdsa/wallet_connect.go b/crypto/ecdsa/wallet_connect.go index 34cbcae..f7bf31c 100644 --- a/crypto/ecdsa/wallet_connect.go +++ b/crypto/ecdsa/wallet_connect.go @@ -6,7 +6,7 @@ import ( "encoding/base64" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/signature/walletconnect" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/util/signature/walletconnect" frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" ) diff --git a/crypto/signature.go b/crypto/signature.go index 33c6132..09cacf4 100644 --- a/crypto/signature.go +++ b/crypto/signature.go @@ -4,13 +4,13 @@ import ( "errors" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" ) // Signature represents a confirmation of data integrity received by the // digital signature mechanism. // -// Signature is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.Signature +// Signature is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs.Signature // message. See ReadFromV2 / WriteToV2 methods. // // Note that direct typecast is not safe and may result in loss of compatibility: diff --git a/crypto/signer.go b/crypto/signer.go index 9f99e3d..86b3be3 100644 --- a/crypto/signer.go +++ b/crypto/signer.go @@ -3,7 +3,7 @@ package frostfscrypto import ( "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" ) // Scheme represents digital signature algorithm with fixed cryptographic hash function. diff --git a/eacl/enums.go b/eacl/enums.go index b2b5353..2fac0cd 100644 --- a/eacl/enums.go +++ b/eacl/enums.go @@ -1,7 +1,7 @@ package eacl import ( - v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" + v2acl "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" ) // Action taken if ContainerEACL record matched request. diff --git a/eacl/enums_test.go b/eacl/enums_test.go index 29f2518..d8e35aa 100644 --- a/eacl/enums_test.go +++ b/eacl/enums_test.go @@ -3,7 +3,7 @@ package eacl_test import ( "testing" - v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" + v2acl "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl" "github.com/stretchr/testify/require" ) diff --git a/eacl/filter.go b/eacl/filter.go index 4403e5a..f4e1f30 100644 --- a/eacl/filter.go +++ b/eacl/filter.go @@ -3,7 +3,7 @@ package eacl import ( "strconv" - v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" + v2acl "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" ) // Filter defines check conditions if request header is matched or not. Matched diff --git a/eacl/filter_test.go b/eacl/filter_test.go index 74b9a10..2e431aa 100644 --- a/eacl/filter_test.go +++ b/eacl/filter_test.go @@ -4,7 +4,7 @@ import ( "strconv" "testing" - v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" + v2acl "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" "github.com/stretchr/testify/require" ) diff --git a/eacl/record.go b/eacl/record.go index 3c0d44a..91d075b 100644 --- a/eacl/record.go +++ b/eacl/record.go @@ -3,7 +3,7 @@ package eacl import ( "crypto/ecdsa" - v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" + v2acl "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" diff --git a/eacl/record_test.go b/eacl/record_test.go index a1738fc..2827295 100644 --- a/eacl/record_test.go +++ b/eacl/record_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" + v2acl "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" checksumtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum/test" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" diff --git a/eacl/table.go b/eacl/table.go index 6982b85..140e57b 100644 --- a/eacl/table.go +++ b/eacl/table.go @@ -4,8 +4,8 @@ import ( "crypto/sha256" "fmt" - v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + v2acl "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version" ) diff --git a/eacl/table_test.go b/eacl/table_test.go index 4ec110f..4960253 100644 --- a/eacl/table_test.go +++ b/eacl/table_test.go @@ -4,7 +4,7 @@ import ( "crypto/sha256" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl" eacltest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl/test" diff --git a/eacl/target.go b/eacl/target.go index 2b8b709..8cba6bf 100644 --- a/eacl/target.go +++ b/eacl/target.go @@ -4,7 +4,7 @@ import ( "bytes" "crypto/ecdsa" - v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" + v2acl "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" ) diff --git a/eacl/target_test.go b/eacl/target_test.go index e226194..bc3a0b4 100644 --- a/eacl/target_test.go +++ b/eacl/target_test.go @@ -4,7 +4,7 @@ import ( "crypto/ecdsa" "testing" - v2acl "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" + v2acl "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/acl" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/stretchr/testify/require" ) diff --git a/netmap/context.go b/netmap/context.go index ca791e8..66030a1 100644 --- a/netmap/context.go +++ b/netmap/context.go @@ -3,7 +3,7 @@ package netmap import ( "errors" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" "git.frostfs.info/TrueCloudLab/hrw" ) diff --git a/netmap/doc.go b/netmap/doc.go index cea3f48..af53177 100644 --- a/netmap/doc.go +++ b/netmap/doc.go @@ -19,7 +19,7 @@ Instances can be also used to process FrostFS API V2 protocol messages On client side: - import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" var msg netmap.NodeInfo info.WriteToV2(&msg) diff --git a/netmap/filter.go b/netmap/filter.go index 76d8545..a3bfa7d 100644 --- a/netmap/filter.go +++ b/netmap/filter.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" ) // mainFilterName is a name of the filter diff --git a/netmap/filter_test.go b/netmap/filter_test.go index 2b88b3c..660b677 100644 --- a/netmap/filter_test.go +++ b/netmap/filter_test.go @@ -4,7 +4,7 @@ import ( "errors" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" "github.com/stretchr/testify/require" ) diff --git a/netmap/helper_test.go b/netmap/helper_test.go index 3775f18..b026b47 100644 --- a/netmap/helper_test.go +++ b/netmap/helper_test.go @@ -1,7 +1,7 @@ package netmap import ( - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" ) func newFilter(name string, k, v string, op netmap.Operation, fs ...Filter) (f Filter) { diff --git a/netmap/netmap.go b/netmap/netmap.go index 85e2a84..78f141f 100644 --- a/netmap/netmap.go +++ b/netmap/netmap.go @@ -3,14 +3,14 @@ package netmap import ( "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" "git.frostfs.info/TrueCloudLab/hrw" ) // NetMap represents FrostFS network map. It includes information about all // storage nodes registered in FrostFS the network. // -// NetMap is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap.NetMap +// NetMap is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap.NetMap // message. See ReadFromV2 / WriteToV2 methods. // // Instances can be created using built-in var declaration. diff --git a/netmap/netmap_test.go b/netmap/netmap_test.go index 7c286a4..ee8adbd 100644 --- a/netmap/netmap_test.go +++ b/netmap/netmap_test.go @@ -3,7 +3,7 @@ package netmap_test import ( "testing" - v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + v2netmap "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap" netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test" "github.com/stretchr/testify/require" diff --git a/netmap/network_info.go b/netmap/network_info.go index 1e553f4..459461f 100644 --- a/netmap/network_info.go +++ b/netmap/network_info.go @@ -6,14 +6,14 @@ import ( "errors" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) // NetworkInfo groups information about the FrostFS network state. Mainly used to // describe the current state of the network. // -// NetworkInfo is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap.NetworkInfo +// NetworkInfo is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap.NetworkInfo // message. See ReadFromV2 / WriteToV2 methods. // // Instances can be created using built-in var declaration. diff --git a/netmap/network_info_test.go b/netmap/network_info_test.go index 161d152..dfb36c6 100644 --- a/netmap/network_info_test.go +++ b/netmap/network_info_test.go @@ -4,7 +4,7 @@ import ( "encoding/binary" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" . "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap" "github.com/stretchr/testify/require" ) diff --git a/netmap/node_info.go b/netmap/node_info.go index 4be4b70..b2b42ae 100644 --- a/netmap/node_info.go +++ b/netmap/node_info.go @@ -7,7 +7,7 @@ import ( "strconv" "strings" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" "git.frostfs.info/TrueCloudLab/hrw" ) @@ -18,7 +18,7 @@ import ( // about the nodes is available to all network participants to work with the network // map (mainly to comply with container storage policies). // -// NodeInfo is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap.NodeInfo +// NodeInfo is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap.NodeInfo // message. See ReadFromV2 / WriteToV2 methods. // // Instances can be created using built-in var declaration. diff --git a/netmap/policy.go b/netmap/policy.go index 48ca364..c6132e0 100644 --- a/netmap/policy.go +++ b/netmap/policy.go @@ -7,7 +7,7 @@ import ( "strconv" "strings" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/parser" "github.com/antlr4-go/antlr/v4" ) @@ -16,7 +16,7 @@ import ( // Within itself, PlacementPolicy represents a set of rules to select a subset // of nodes from FrostFS network map - node-candidates for object storage. // -// PlacementPolicy is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap.PlacementPolicy +// PlacementPolicy is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap.PlacementPolicy // message. See ReadFromV2 / WriteToV2 methods. // // Instances can be created using built-in var declaration. diff --git a/netmap/selector.go b/netmap/selector.go index ab73cec..dbbefc4 100644 --- a/netmap/selector.go +++ b/netmap/selector.go @@ -5,7 +5,7 @@ import ( "fmt" "slices" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" "git.frostfs.info/TrueCloudLab/hrw" ) diff --git a/netmap/selector_test.go b/netmap/selector_test.go index 6ef5883..226fa0e 100644 --- a/netmap/selector_test.go +++ b/netmap/selector_test.go @@ -12,7 +12,7 @@ import ( "strings" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/netmap" "git.frostfs.info/TrueCloudLab/hrw" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/object/attribute.go b/object/attribute.go index e4c7f60..e44305f 100644 --- a/object/attribute.go +++ b/object/attribute.go @@ -1,7 +1,7 @@ package object import ( - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" ) // Attribute represents v2-compatible object attribute. diff --git a/object/attribute_test.go b/object/attribute_test.go index eafc312..9e82602 100644 --- a/object/attribute_test.go +++ b/object/attribute_test.go @@ -3,7 +3,7 @@ package object import ( "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" "github.com/stretchr/testify/require" ) diff --git a/object/fmt.go b/object/fmt.go index 5e6386d..72ebf12 100644 --- a/object/fmt.go +++ b/object/fmt.go @@ -7,7 +7,7 @@ import ( "errors" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum" frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" diff --git a/object/id/address.go b/object/id/address.go index 6d5f12a..d868b5f 100644 --- a/object/id/address.go +++ b/object/id/address.go @@ -5,14 +5,14 @@ import ( "fmt" "strings" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" ) // Address represents global object identifier in FrostFS network. Each object // belongs to exactly one container and is uniquely addressed within the container. // -// Address is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.Address +// Address is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs.Address // message. See ReadFromV2 / WriteToV2 methods. // // Instances can be created using built-in var declaration. diff --git a/object/id/address_test.go b/object/id/address_test.go index 5ae91ca..e33dd00 100644 --- a/object/id/address_test.go +++ b/object/id/address_test.go @@ -3,7 +3,7 @@ package oid_test import ( "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" diff --git a/object/id/id.go b/object/id/id.go index 5ba0c6d..c305615 100644 --- a/object/id/id.go +++ b/object/id/id.go @@ -5,7 +5,7 @@ import ( "crypto/sha256" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa" "github.com/mr-tron/base58" @@ -13,7 +13,7 @@ import ( // ID represents FrostFS object identifier in a container. // -// ID is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.ObjectID +// ID is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs.ObjectID // message. See ReadFromV2 / WriteToV2 methods. // // Instances can be created using built-in var declaration. diff --git a/object/id/id_test.go b/object/id/id_test.go index 3f35a3b..856a449 100644 --- a/object/id/id_test.go +++ b/object/id/id_test.go @@ -6,7 +6,7 @@ import ( "strconv" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" "github.com/mr-tron/base58" "github.com/stretchr/testify/require" ) diff --git a/object/lock.go b/object/lock.go index dd08eba..a4d202b 100644 --- a/object/lock.go +++ b/object/lock.go @@ -1,8 +1,8 @@ package object import ( - v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + v2object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" ) diff --git a/object/range.go b/object/range.go index 87ad3b7..d2248e9 100644 --- a/object/range.go +++ b/object/range.go @@ -1,7 +1,7 @@ package object import ( - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" ) // Range represents v2-compatible object payload range. diff --git a/object/range_test.go b/object/range_test.go index c8208d8..2b2e1e4 100644 --- a/object/range_test.go +++ b/object/range_test.go @@ -3,7 +3,7 @@ package object import ( "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" "github.com/stretchr/testify/require" ) diff --git a/object/raw.go b/object/raw.go index 5c91f1d..3b87a19 100644 --- a/object/raw.go +++ b/object/raw.go @@ -1,7 +1,7 @@ package object import ( - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" ) // RawObject represents v2-compatible FrostFS object that provides diff --git a/object/raw_test.go b/object/raw_test.go index 52ac9de..8cfe92d 100644 --- a/object/raw_test.go +++ b/object/raw_test.go @@ -5,7 +5,7 @@ import ( "crypto/sha256" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" diff --git a/object/search.go b/object/search.go index 818099d..e78a29a 100644 --- a/object/search.go +++ b/object/search.go @@ -4,7 +4,7 @@ import ( "encoding/json" "strconv" - v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + v2object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" diff --git a/object/search_test.go b/object/search_test.go index 4241f7d..e286bad 100644 --- a/object/search_test.go +++ b/object/search_test.go @@ -5,7 +5,7 @@ import ( "crypto/sha256" "testing" - v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + v2object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "github.com/stretchr/testify/require" diff --git a/object/splitinfo.go b/object/splitinfo.go index c38b2a4..3ad36b4 100644 --- a/object/splitinfo.go +++ b/object/splitinfo.go @@ -4,8 +4,8 @@ import ( "errors" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" ) diff --git a/object/splitinfo_test.go b/object/splitinfo_test.go index ba3a54f..1c1fd92 100644 --- a/object/splitinfo_test.go +++ b/object/splitinfo_test.go @@ -5,7 +5,7 @@ import ( "encoding/json" "testing" - objv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + objv2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "github.com/stretchr/testify/require" diff --git a/object/tombstone.go b/object/tombstone.go index e332630..0d76a1b 100644 --- a/object/tombstone.go +++ b/object/tombstone.go @@ -1,8 +1,8 @@ package object import ( - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/tombstone" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/tombstone" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" ) diff --git a/object/tombstone_test.go b/object/tombstone_test.go index e819b22..3c55a24 100644 --- a/object/tombstone_test.go +++ b/object/tombstone_test.go @@ -5,7 +5,7 @@ import ( "crypto/sha256" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/tombstone" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/tombstone" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "github.com/stretchr/testify/require" ) diff --git a/object/type.go b/object/type.go index 29328d3..51aafb4 100644 --- a/object/type.go +++ b/object/type.go @@ -1,7 +1,7 @@ package object import ( - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" ) type Type object.Type diff --git a/object/type_test.go b/object/type_test.go index 5faa936..0980248 100644 --- a/object/type_test.go +++ b/object/type_test.go @@ -3,7 +3,7 @@ package object_test import ( "testing" - v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + v2object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" "github.com/stretchr/testify/require" ) diff --git a/session/common.go b/session/common.go index b103b59..9bd074c 100644 --- a/session/common.go +++ b/session/common.go @@ -6,8 +6,8 @@ import ( "errors" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" diff --git a/session/container.go b/session/container.go index 45c5ff8..202fbc6 100644 --- a/session/container.go +++ b/session/container.go @@ -5,8 +5,8 @@ import ( "errors" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" @@ -18,7 +18,7 @@ import ( // limited validity period, and applies to a strictly defined set of operations. // See methods for details. // -// Container is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session.Token +// Container is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session.Token // message. See ReadFromV2 / WriteToV2 methods. // // Instances can be created using built-in var declaration. diff --git a/session/container_test.go b/session/container_test.go index 73fd815..c8193dd 100644 --- a/session/container_test.go +++ b/session/container_test.go @@ -8,8 +8,8 @@ import ( mrand "math/rand" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa" diff --git a/session/doc.go b/session/doc.go index 12ec0c4..19d5f10 100644 --- a/session/doc.go +++ b/session/doc.go @@ -28,7 +28,7 @@ Instances can be also used to process FrostFS API V2 protocol messages On client side: - import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" + import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" var msg session.Token tok.WriteToV2(&msg) diff --git a/session/object.go b/session/object.go index 3641bd1..db98188 100644 --- a/session/object.go +++ b/session/object.go @@ -5,8 +5,8 @@ import ( "errors" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" ) @@ -17,7 +17,7 @@ import ( // limited validity period, and applies to a strictly defined set of operations. // See methods for details. // -// Object is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session.Token +// Object is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session.Token // message. See ReadFromV2 / WriteToV2 methods. // // Instances can be created using built-in var declaration. diff --git a/session/object_test.go b/session/object_test.go index 59ff8d6..8b14030 100644 --- a/session/object_test.go +++ b/session/object_test.go @@ -8,8 +8,8 @@ import ( "math/rand" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" + v2session "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/session" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa" diff --git a/user/doc.go b/user/doc.go index 4809e4a..16f3a7c 100644 --- a/user/doc.go +++ b/user/doc.go @@ -39,7 +39,7 @@ Instances can be also used to process FrostFS API protocol messages On client side: - import "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" var msg refs.OwnerID id.WriteToV2(&msg) diff --git a/user/id.go b/user/id.go index 31ff9b6..54846db 100644 --- a/user/id.go +++ b/user/id.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" "github.com/mr-tron/base58" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/encoding/address" @@ -18,7 +18,7 @@ var zeroSlice = bytes.Repeat([]byte{0}, idSize) // ID identifies users of the FrostFS system. // -// ID is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.OwnerID +// ID is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs.OwnerID // message. See ReadFromV2 / WriteToV2 methods. // // Instances can be created using built-in var declaration. Zero ID is not valid, diff --git a/user/id_test.go b/user/id_test.go index 9867472..c770c48 100644 --- a/user/id_test.go +++ b/user/id_test.go @@ -5,7 +5,7 @@ import ( "crypto/rand" "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" . "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test" "github.com/mr-tron/base58" diff --git a/version/version.go b/version/version.go index 47e61c8..ecc27db 100644 --- a/version/version.go +++ b/version/version.go @@ -3,12 +3,12 @@ package version import ( "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" ) // Version represents revision number in SemVer scheme. // -// Version is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.Version +// Version is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs.Version // message. See ReadFromV2 / WriteToV2 methods. // // Instances can be created using built-in var declaration. diff --git a/version/version_test.go b/version/version_test.go index fdf2b6a..a1fd5ca 100644 --- a/version/version_test.go +++ b/version/version_test.go @@ -3,7 +3,7 @@ package version import ( "testing" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apiv2/refs" "github.com/stretchr/testify/require" )