forked from TrueCloudLab/frostfs-node
[#1324] services/tree: Implement Object Tree Service
Object Tree Service allows changing trees assotiated with the container in runtime. Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>support/v0.30
parent
46f4ce2773
commit
62154da17c
|
@ -0,0 +1,39 @@
|
|||
package treeconfig
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neofs-node/cmd/neofs-node/config"
|
||||
)
|
||||
|
||||
// GRPCConfig is a wrapper over "grpc" config section which provides access
|
||||
// to gRPC configuration of control service.
|
||||
type GRPCConfig struct {
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
const (
|
||||
subsection = "tree"
|
||||
grpcSubsection = "grpc"
|
||||
|
||||
// GRPCEndpointDefault is a default endpoint of gRPC Control service.
|
||||
GRPCEndpointDefault = ""
|
||||
)
|
||||
|
||||
// GRPC returns structure that provides access to "grpc" subsection of
|
||||
// "tree" section.
|
||||
func GRPC(c *config.Config) GRPCConfig {
|
||||
return GRPCConfig{
|
||||
c.Sub(subsection).Sub(grpcSubsection),
|
||||
}
|
||||
}
|
||||
|
||||
// Endpoint returns value of "endpoint" config parameter.
|
||||
//
|
||||
// Returns GRPCEndpointDefault if value is not a non-empty string.
|
||||
func (g GRPCConfig) Endpoint() string {
|
||||
v := config.String(g.cfg, "endpoint")
|
||||
if v != "" {
|
||||
return v
|
||||
}
|
||||
|
||||
return GRPCEndpointDefault
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package treeconfig_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/cmd/neofs-node/config"
|
||||
configtest "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/test"
|
||||
treeconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/tree"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestControlSection(t *testing.T) {
|
||||
t.Run("defaults", func(t *testing.T) {
|
||||
empty := configtest.EmptyConfig()
|
||||
|
||||
require.Equal(t, treeconfig.GRPCEndpointDefault, treeconfig.GRPC(empty).Endpoint())
|
||||
})
|
||||
|
||||
const path = "../../../../config/example/node"
|
||||
|
||||
var fileConfigTest = func(c *config.Config) {
|
||||
require.Equal(t, "127.0.0.1:8091", treeconfig.GRPC(c).Endpoint())
|
||||
}
|
||||
|
||||
configtest.ForEachFileType(path, fileConfigTest)
|
||||
|
||||
t.Run("ENV", func(t *testing.T) {
|
||||
configtest.ForEnvFileType(path, fileConfigTest)
|
||||
})
|
||||
}
|
|
@ -81,6 +81,7 @@ func initApp(c *cfg) {
|
|||
initAndLog(c, "pprof", initProfiler)
|
||||
initAndLog(c, "prometheus", initMetrics)
|
||||
initAndLog(c, "control", initControlService)
|
||||
initAndLog(c, "tree", initTreeService)
|
||||
|
||||
initAndLog(c, "storage engine", func(c *cfg) {
|
||||
fatalOnErr(c.cfgObject.cfgLocalStorage.localStorage.Open())
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
treeconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/tree"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/services/tree"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func initTreeService(c *cfg) {
|
||||
endpoint := treeconfig.GRPC(c.appCfg).Endpoint()
|
||||
if endpoint == treeconfig.GRPCEndpointDefault {
|
||||
return
|
||||
}
|
||||
|
||||
treeSvc := tree.New(
|
||||
tree.WithContainerSource(c.cfgObject.cnrSource),
|
||||
tree.WithNetmapSource(c.netMapSource),
|
||||
tree.WithPrivateKey(&c.key.PrivateKey),
|
||||
tree.WithLogger(c.log),
|
||||
tree.WithStorage(c.cfgObject.cfgLocalStorage.localStorage))
|
||||
|
||||
treeServer := grpc.NewServer()
|
||||
|
||||
c.onShutdown(func() {
|
||||
stopGRPC("NeoFS Tree Service API", treeServer, c.log)
|
||||
treeSvc.Shutdown()
|
||||
})
|
||||
|
||||
lis, err := net.Listen("tcp", endpoint)
|
||||
fatalOnErr(err)
|
||||
|
||||
tree.RegisterTreeServiceServer(treeServer, treeSvc)
|
||||
|
||||
c.workers = append(c.workers, newWorkerFromFunc(func(ctx context.Context) {
|
||||
treeSvc.Start(ctx)
|
||||
fatalOnErr(treeServer.Serve(lis))
|
||||
}))
|
||||
}
|
|
@ -46,6 +46,9 @@ NEOFS_GRPC_1_TLS_ENABLED=false
|
|||
NEOFS_CONTROL_AUTHORIZED_KEYS="035839e45d472a3b7769a2a1bd7d54c4ccd4943c3b40f547870e83a8fcbfb3ce11 028f42cfcb74499d7b15b35d9bff260a1c8d27de4f446a627406a382d8961486d6"
|
||||
NEOFS_CONTROL_GRPC_ENDPOINT=localhost:8090
|
||||
|
||||
# Tree service section
|
||||
NEOFS_TREE_GRPC_ENDPOINT=127.0.0.1:8091
|
||||
|
||||
# Contracts section
|
||||
NEOFS_CONTRACTS_BALANCE=5263abba1abedbf79bb57f3e40b50b4425d2d6cd
|
||||
NEOFS_CONTRACTS_CONTAINER=5d084790d7aa36cea7b53fe897380dab11d2cd3c
|
||||
|
|
|
@ -84,6 +84,11 @@
|
|||
"endpoint": "localhost:8090"
|
||||
}
|
||||
},
|
||||
"tree": {
|
||||
"grpc": {
|
||||
"endpoint": "127.0.0.1:8091"
|
||||
}
|
||||
},
|
||||
"contracts": {
|
||||
"balance": "5263abba1abedbf79bb57f3e40b50b4425d2d6cd",
|
||||
"container": "5d084790d7aa36cea7b53fe897380dab11d2cd3c",
|
||||
|
|
|
@ -66,6 +66,10 @@ control:
|
|||
grpc:
|
||||
endpoint: localhost:8090 # endpoint that is listened by the Control Service
|
||||
|
||||
tree:
|
||||
grpc:
|
||||
endpoint: 127.0.0.1:8091 # endpoint that is listened by the Tree Service
|
||||
|
||||
contracts: # side chain NEOFS contract script hashes; optional, override values retrieved from NNS contract
|
||||
balance: 5263abba1abedbf79bb57f3e40b50b4425d2d6cd
|
||||
container: 5d084790d7aa36cea7b53fe897380dab11d2cd3c
|
||||
|
|
8
go.mod
8
go.mod
|
@ -38,7 +38,10 @@ require (
|
|||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require google.golang.org/api v0.44.0
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.81.0 // indirect
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521073959-f0d4d129b7f1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/btcsuite/btcd v0.22.0-beta // indirect
|
||||
|
@ -47,8 +50,10 @@ require (
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.3 // indirect
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
|
@ -87,12 +92,15 @@ require (
|
|||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 // indirect
|
||||
github.com/twmb/murmur3 v1.1.5 // indirect
|
||||
github.com/urfave/cli v1.22.5 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
|
6
go.sum
6
go.sum
|
@ -17,6 +17,7 @@ cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKP
|
|||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||
cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
|
||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
|
@ -161,6 +162,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
|
|||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
|
@ -559,6 +561,7 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
|
@ -687,6 +690,7 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ
|
|||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 h1:0Ja1LBD+yisY6RWM/BH7TJVXWsSjs2VwBSmvSX4HdBc=
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -871,6 +875,7 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q
|
|||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/api v0.44.0 h1:URs6qR1lAxDsqWITsQXI4ZkGiYJ5dHtRNiCpfs2OeKA=
|
||||
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
@ -878,6 +883,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
|||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package tree
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/netmap"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/pilorama"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type cfg struct {
|
||||
log *zap.Logger
|
||||
key *ecdsa.PrivateKey
|
||||
nmSource netmap.Source
|
||||
cnrSource container.Source
|
||||
forest pilorama.Forest
|
||||
}
|
||||
|
||||
// Option represents configuration option for a tree service.
|
||||
type Option func(*cfg)
|
||||
|
||||
// WithContainerSource sets a container source for a tree service.
|
||||
// This option is required.
|
||||
func WithContainerSource(src container.Source) Option {
|
||||
return func(c *cfg) {
|
||||
c.cnrSource = src
|
||||
}
|
||||
}
|
||||
|
||||
// WithNetmapSource sets a netmap source for a tree service.
|
||||
// This option is required.
|
||||
func WithNetmapSource(src netmap.Source) Option {
|
||||
return func(c *cfg) {
|
||||
c.nmSource = src
|
||||
}
|
||||
}
|
||||
|
||||
// WithPrivateKey sets a netmap source for a tree service.
|
||||
// This option is required.
|
||||
func WithPrivateKey(key *ecdsa.PrivateKey) Option {
|
||||
return func(c *cfg) {
|
||||
c.key = key
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger sets logger for a tree service.
|
||||
func WithLogger(log *zap.Logger) Option {
|
||||
return func(c *cfg) {
|
||||
c.log = log
|
||||
}
|
||||
}
|
||||
|
||||
// WithStorage sets tree storage for a service.
|
||||
func WithStorage(s pilorama.Forest) Option {
|
||||
return func(c *cfg) {
|
||||
c.forest = s
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
package tree
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
clientcore "github.com/nspcc-dev/neofs-node/pkg/core/client"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/pilorama"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/network"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement"
|
||||
cidSDK "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
netmapSDK "github.com/nspcc-dev/neofs-sdk-go/netmap"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/api/transport/grpc"
|
||||
)
|
||||
|
||||
type movePair struct {
|
||||
cid cidSDK.ID
|
||||
treeID string
|
||||
op *pilorama.LogMove
|
||||
}
|
||||
|
||||
const (
|
||||
defaultReplicatorCapacity = 64
|
||||
defaultReplicatorTimeout = time.Second * 2
|
||||
)
|
||||
|
||||
func (s *Service) replicateLoop(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-s.closeCh:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case op := <-s.replicateCh:
|
||||
ctx, cancel := context.WithTimeout(ctx, defaultReplicatorTimeout)
|
||||
err := s.replicate(ctx, op)
|
||||
cancel()
|
||||
|
||||
if err != nil {
|
||||
s.log.Error("error during replication",
|
||||
zap.String("err", err.Error()),
|
||||
zap.Stringer("cid", op.cid),
|
||||
zap.String("treeID", op.treeID))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) replicate(ctx context.Context, op movePair) error {
|
||||
req := newApplyRequest(&op)
|
||||
// TODO(@fyrchik): #1328 access control
|
||||
//err := signature.SignDataWithHandler(s.key, req, func(key, sign []byte) {
|
||||
// req.Signature = &Signature{
|
||||
// Key: key,
|
||||
// Sign: sign,
|
||||
// }
|
||||
//})
|
||||
//if err != nil {
|
||||
// return fmt.Errorf("can't sign data: %w", err)
|
||||
//}
|
||||
|
||||
nodes, err := s.getContainerNodes(op.cid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't get container nodes: %w", err)
|
||||
}
|
||||
|
||||
var node clientcore.NodeInfo
|
||||
for _, n := range nodes {
|
||||
var lastErr error
|
||||
|
||||
n.IterateNetworkEndpoints(func(addr string) bool {
|
||||
cc, err := grpc.Dial(ctx, option.WithEndpoint(addr))
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO cache clients
|
||||
c := NewTreeServiceClient(cc)
|
||||
|
||||
_, lastErr = c.Apply(ctx, req)
|
||||
return lastErr == nil
|
||||
})
|
||||
|
||||
if lastErr != nil {
|
||||
s.log.Warn("failed to sent update to the node",
|
||||
zap.String("last_error", lastErr.Error()),
|
||||
zap.String("address", network.StringifyGroup(node.AddressGroup())),
|
||||
zap.String("key", base64.StdEncoding.EncodeToString(node.PublicKey())))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) pushToQueue(cid cidSDK.ID, treeID string, op *pilorama.LogMove) {
|
||||
s.replicateCh <- movePair{
|
||||
cid: cid,
|
||||
treeID: treeID,
|
||||
op: op,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) getContainerNodes(cid cidSDK.ID) ([]netmapSDK.NodeInfo, error) {
|
||||
nm, err := s.nmSource.GetNetMap(0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get netmap: %w", err)
|
||||
}
|
||||
|
||||
cnr, err := s.cnrSource.Get(cid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get container: %w", err)
|
||||
}
|
||||
|
||||
policy := cnr.Value.PlacementPolicy()
|
||||
rawCID := make([]byte, sha256.Size)
|
||||
cid.Encode(rawCID)
|
||||
|
||||
nodes, err := nm.ContainerNodes(policy, rawCID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return placement.FlattenNodes(nodes), nil
|
||||
}
|
||||
|
||||
func newApplyRequest(op *movePair) *ApplyRequest {
|
||||
rawCID := make([]byte, sha256.Size)
|
||||
op.cid.Encode(rawCID)
|
||||
|
||||
return &ApplyRequest{
|
||||
Body: &ApplyRequest_Body{
|
||||
ContainerId: rawCID,
|
||||
TreeId: op.treeID,
|
||||
Operation: &LogMove{
|
||||
ParentId: op.op.Parent,
|
||||
Meta: op.op.Meta.Bytes(),
|
||||
ChildId: op.op.Child,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,294 @@
|
|||
package tree
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/signature"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/pilorama"
|
||||
cidSDK "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Service represents tree-service capable of working with multiple
|
||||
// instances of CRDT trees.
|
||||
type Service struct {
|
||||
cfg
|
||||
|
||||
replicateCh chan movePair
|
||||
closeCh chan struct{}
|
||||
}
|
||||
|
||||
var _ TreeServiceServer = (*Service)(nil)
|
||||
|
||||
// New creates new tree service instance.
|
||||
func New(opts ...Option) *Service {
|
||||
var s Service
|
||||
for i := range opts {
|
||||
opts[i](&s.cfg)
|
||||
}
|
||||
|
||||
if s.log == nil {
|
||||
s.log = zap.NewNop()
|
||||
}
|
||||
|
||||
s.closeCh = make(chan struct{})
|
||||
s.replicateCh = make(chan movePair, defaultReplicatorCapacity)
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
// Start starts the service.
|
||||
func (s *Service) Start(ctx context.Context) {
|
||||
go s.replicateLoop(ctx)
|
||||
}
|
||||
|
||||
// Shutdown shutdowns the service.
|
||||
func (s *Service) Shutdown() {
|
||||
close(s.closeCh)
|
||||
}
|
||||
|
||||
func (s *Service) Add(_ context.Context, req *AddRequest) (*AddResponse, error) {
|
||||
b := req.GetBody()
|
||||
|
||||
var cid cidSDK.ID
|
||||
if err := cid.Decode(b.GetContainerId()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := s.verifyClient(req, cid, req.GetSignature().GetKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log, err := s.forest.TreeMove(cid, b.GetTreeId(), &pilorama.Move{
|
||||
Parent: b.GetParentId(),
|
||||
Child: pilorama.RootID,
|
||||
Meta: pilorama.Meta{Items: constructMeta(b.GetMeta())},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.pushToQueue(cid, b.GetTreeId(), log)
|
||||
return &AddResponse{
|
||||
Body: &AddResponse_Body{
|
||||
NodeId: log.Child,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) AddByPath(_ context.Context, req *AddByPathRequest) (*AddByPathResponse, error) {
|
||||
b := req.GetBody()
|
||||
|
||||
var cid cidSDK.ID
|
||||
if err := cid.Decode(b.GetContainerId()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := s.verifyClient(req, cid, req.GetSignature().GetKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
meta := constructMeta(b.GetMeta())
|
||||
|
||||
attr := b.GetPathAttribute()
|
||||
if len(attr) == 0 {
|
||||
attr = pilorama.AttributeFilename
|
||||
}
|
||||
|
||||
logs, err := s.forest.TreeAddByPath(cid, b.GetTreeId(), attr, b.GetPath(), meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range logs {
|
||||
s.pushToQueue(cid, b.GetTreeId(), &logs[i])
|
||||
}
|
||||
|
||||
nodes := make([]uint64, len(logs))
|
||||
nodes[0] = logs[len(logs)-1].Child
|
||||
for i, l := range logs[:len(logs)-1] {
|
||||
nodes[i+1] = l.Child
|
||||
}
|
||||
|
||||
return &AddByPathResponse{
|
||||
Body: &AddByPathResponse_Body{
|
||||
Nodes: nodes,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) Remove(_ context.Context, req *RemoveRequest) (*RemoveResponse, error) {
|
||||
b := req.GetBody()
|
||||
|
||||
var cid cidSDK.ID
|
||||
if err := cid.Decode(b.GetContainerId()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := s.verifyClient(req, cid, req.GetSignature().GetKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b.GetNodeId() == pilorama.RootID {
|
||||
return nil, fmt.Errorf("node with ID %d is root and can't be removed", b.GetNodeId())
|
||||
}
|
||||
|
||||
log, err := s.forest.TreeMove(cid, b.GetTreeId(), &pilorama.Move{
|
||||
Parent: pilorama.TrashID,
|
||||
Child: b.GetNodeId(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.pushToQueue(cid, b.GetTreeId(), log)
|
||||
return new(RemoveResponse), nil
|
||||
}
|
||||
|
||||
// Move applies client operation to the specified tree and pushes in queue
|
||||
// for replication on other nodes.
|
||||
func (s *Service) Move(_ context.Context, req *MoveRequest) (*MoveResponse, error) {
|
||||
b := req.GetBody()
|
||||
|
||||
var cid cidSDK.ID
|
||||
if err := cid.Decode(b.GetContainerId()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := s.verifyClient(req, cid, req.GetSignature().GetKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b.GetNodeId() == pilorama.RootID {
|
||||
return nil, fmt.Errorf("node with ID %d is root and can't be moved", b.GetNodeId())
|
||||
}
|
||||
|
||||
log, err := s.forest.TreeMove(cid, b.GetTreeId(), &pilorama.Move{
|
||||
Parent: b.GetParentId(),
|
||||
Child: b.GetNodeId(),
|
||||
Meta: pilorama.Meta{Items: constructMeta(b.GetMeta())},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.pushToQueue(cid, b.GetTreeId(), log)
|
||||
return new(MoveResponse), nil
|
||||
}
|
||||
|
||||
func (s *Service) GetNodeByPath(_ context.Context, req *GetNodeByPathRequest) (*GetNodeByPathResponse, error) {
|
||||
b := req.GetBody()
|
||||
|
||||
var cid cidSDK.ID
|
||||
if err := cid.Decode(b.GetContainerId()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attr := b.GetPathAttribute()
|
||||
if len(attr) == 0 {
|
||||
attr = pilorama.AttributeFilename
|
||||
}
|
||||
|
||||
nodes, err := s.forest.TreeGetByPath(cid, b.GetTreeId(), attr, b.GetPath(), b.GetLatestOnly())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := make([]*GetNodeByPathResponse_Info, 0, len(nodes))
|
||||
for _, node := range nodes {
|
||||
m, err := s.forest.TreeGetMeta(cid, b.GetTreeId(), node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var x GetNodeByPathResponse_Info
|
||||
x.NodeId = node
|
||||
x.Timestamp = m.Time
|
||||
for _, kv := range m.Items {
|
||||
needAttr := b.AllAttributes
|
||||
if !needAttr {
|
||||
for _, attr := range b.GetAttributes() {
|
||||
if kv.Key == attr {
|
||||
needAttr = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if needAttr {
|
||||
x.Meta = append(x.Meta, &KeyValue{
|
||||
Key: kv.Key,
|
||||
Value: kv.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
info = append(info, &x)
|
||||
}
|
||||
|
||||
return &GetNodeByPathResponse{
|
||||
Body: &GetNodeByPathResponse_Body{
|
||||
Nodes: info,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetSubTree(_ context.Context, req *GetSubTreeRequest) (*GetSubTreeResponse, error) {
|
||||
return nil, errors.New("GetSubTree is unimplemented")
|
||||
}
|
||||
|
||||
// Apply locally applies operation from the remote node to the tree.
|
||||
func (s *Service) Apply(_ context.Context, req *ApplyRequest) (*ApplyResponse, error) {
|
||||
err := signature.VerifyServiceMessage(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cid cidSDK.ID
|
||||
if err := cid.Decode(req.GetBody().GetContainerId()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
found := false
|
||||
key := req.GetSignature().GetKey()
|
||||
nodes, _ := s.getContainerNodes(cid)
|
||||
|
||||
loop:
|
||||
for _, n := range nodes {
|
||||
if bytes.Equal(key, n.PublicKey()) {
|
||||
found = true
|
||||
break loop
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, errors.New("`Apply` request must be signed by a container node")
|
||||
}
|
||||
|
||||
op := req.GetBody().GetOperation()
|
||||
|
||||
var meta pilorama.Meta
|
||||
if err := meta.FromBytes(op.GetMeta()); err != nil {
|
||||
return nil, fmt.Errorf("can't parse meta-information: %w", err)
|
||||
}
|
||||
|
||||
return nil, s.forest.TreeApply(cid, req.GetBody().GetTreeId(), &pilorama.Move{
|
||||
Parent: op.GetParentId(),
|
||||
Child: op.GetChildId(),
|
||||
Meta: meta,
|
||||
})
|
||||
}
|
||||
|
||||
func constructMeta(arr []*KeyValue) []pilorama.KeyValue {
|
||||
meta := make([]pilorama.KeyValue, len(arr))
|
||||
for i, kv := range arr {
|
||||
meta[i].Key = kv.Key
|
||||
meta[i].Value = kv.Value
|
||||
}
|
||||
return meta
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,194 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package tree;
|
||||
|
||||
import "pkg/services/tree/types.proto";
|
||||
|
||||
option go_package = "github.com/nspcc-dev/neofs-node/pkg/services/tree";
|
||||
|
||||
// `TreeService` provides an interface for working with distributed tree.
|
||||
service TreeService {
|
||||
/* Client API */
|
||||
|
||||
// Add adds new node to the tree. Invoked by a client.
|
||||
rpc Add (AddRequest) returns (AddResponse);
|
||||
// AddByPath adds new node to the tree by path. Invoked by a client.
|
||||
rpc AddByPath (AddByPathRequest) returns (AddByPathResponse);
|
||||
// Remove removes node from the tree. Invoked by a client.
|
||||
rpc Remove (RemoveRequest) returns (RemoveResponse);
|
||||
// Move moves node from one parent to another. Invoked by a client.
|
||||
rpc Move (MoveRequest) returns (MoveResponse);
|
||||
// GetNodeByPath returns list of IDs corresponding to a specific filepath.
|
||||
rpc GetNodeByPath (GetNodeByPathRequest) returns (GetNodeByPathResponse);
|
||||
// GetSubTree returns tree corresponding to a specific node.
|
||||
rpc GetSubTree (GetSubTreeRequest) returns (GetSubTreeResponse);
|
||||
|
||||
/* Synchronization API */
|
||||
|
||||
// Apply pushes log operation from another node to the current.
|
||||
// The request must be signed by a container node.
|
||||
rpc Apply (ApplyRequest) returns (ApplyResponse);
|
||||
}
|
||||
|
||||
message AddRequest {
|
||||
message Body {
|
||||
bytes container_id = 1;
|
||||
string tree_id = 2;
|
||||
uint64 parent_id = 3;
|
||||
repeated KeyValue meta = 4;
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
}
|
||||
|
||||
message AddResponse {
|
||||
message Body {
|
||||
uint64 node_id = 1;
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
};
|
||||
|
||||
|
||||
message AddByPathRequest {
|
||||
message Body {
|
||||
bytes container_id = 1;
|
||||
string tree_id = 2;
|
||||
string path_attribute = 3;
|
||||
repeated string path = 4;
|
||||
repeated KeyValue meta = 5;
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
}
|
||||
|
||||
message AddByPathResponse {
|
||||
message Body {
|
||||
repeated uint64 nodes = 1;
|
||||
uint64 parent_id = 2;
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
};
|
||||
|
||||
|
||||
message RemoveRequest {
|
||||
message Body {
|
||||
bytes container_id = 1;
|
||||
string tree_id = 2;
|
||||
uint64 node_id = 3;
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
}
|
||||
|
||||
message RemoveResponse {
|
||||
message Body {
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
};
|
||||
|
||||
|
||||
message MoveRequest {
|
||||
message Body {
|
||||
// TODO import neo.fs.v2.refs.ContainerID directly.
|
||||
bytes container_id = 1;
|
||||
string tree_id = 2;
|
||||
uint64 parent_id = 3;
|
||||
uint64 node_id = 4;
|
||||
repeated KeyValue meta = 5;
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
}
|
||||
|
||||
message MoveResponse {
|
||||
message Body {
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
};
|
||||
|
||||
|
||||
message GetNodeByPathRequest {
|
||||
message Body {
|
||||
bytes container_id = 1;
|
||||
string tree_id = 2;
|
||||
string path_attribute = 3;
|
||||
repeated string path = 4;
|
||||
repeated string attributes = 5;
|
||||
bool latest_only = 6;
|
||||
bool all_attributes = 7;
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
}
|
||||
|
||||
message GetNodeByPathResponse {
|
||||
message Info {
|
||||
uint64 node_id = 1;
|
||||
uint64 timestamp = 2;
|
||||
repeated KeyValue meta = 3;
|
||||
}
|
||||
message Body {
|
||||
repeated Info nodes = 1;
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
};
|
||||
|
||||
|
||||
message GetSubTreeRequest {
|
||||
message Body {
|
||||
bytes container_id = 1;
|
||||
string tree_id = 2;
|
||||
repeated uint64 nodes = 3;
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
}
|
||||
|
||||
message GetSubTreeResponse {
|
||||
message Info {
|
||||
uint64 node_id = 1;
|
||||
repeated KeyValue meta = 2;
|
||||
}
|
||||
message Body {
|
||||
repeated Info info = 1;
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
};
|
||||
|
||||
|
||||
message ApplyRequest {
|
||||
message Body {
|
||||
bytes container_id = 1;
|
||||
string tree_id = 2;
|
||||
LogMove operation = 3;
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
}
|
||||
|
||||
message ApplyResponse {
|
||||
message Body {
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
};
|
|
@ -0,0 +1,335 @@
|
|||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.19.4
|
||||
// source: pkg/services/tree/service.proto
|
||||
|
||||
package tree
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// TreeServiceClient is the client API for TreeService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type TreeServiceClient interface {
|
||||
// Add adds new node to the tree. Invoked by a client.
|
||||
Add(ctx context.Context, in *AddRequest, opts ...grpc.CallOption) (*AddResponse, error)
|
||||
// AddByPath adds new node to the tree by path. Invoked by a client.
|
||||
AddByPath(ctx context.Context, in *AddByPathRequest, opts ...grpc.CallOption) (*AddByPathResponse, error)
|
||||
// Remove removes node from the tree. Invoked by a client.
|
||||
Remove(ctx context.Context, in *RemoveRequest, opts ...grpc.CallOption) (*RemoveResponse, error)
|
||||
// Move moves node from one parent to another. Invoked by a client.
|
||||
Move(ctx context.Context, in *MoveRequest, opts ...grpc.CallOption) (*MoveResponse, error)
|
||||
// GetNodeByPath returns list of IDs corresponding to a specific filepath.
|
||||
GetNodeByPath(ctx context.Context, in *GetNodeByPathRequest, opts ...grpc.CallOption) (*GetNodeByPathResponse, error)
|
||||
// GetSubTree returns tree corresponding to a specific node.
|
||||
GetSubTree(ctx context.Context, in *GetSubTreeRequest, opts ...grpc.CallOption) (*GetSubTreeResponse, error)
|
||||
// Apply pushes log operation from another node to the current.
|
||||
// The request must be signed by a container node.
|
||||
Apply(ctx context.Context, in *ApplyRequest, opts ...grpc.CallOption) (*ApplyResponse, error)
|
||||
}
|
||||
|
||||
type treeServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewTreeServiceClient(cc grpc.ClientConnInterface) TreeServiceClient {
|
||||
return &treeServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *treeServiceClient) Add(ctx context.Context, in *AddRequest, opts ...grpc.CallOption) (*AddResponse, error) {
|
||||
out := new(AddResponse)
|
||||
err := c.cc.Invoke(ctx, "/tree.TreeService/Add", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *treeServiceClient) AddByPath(ctx context.Context, in *AddByPathRequest, opts ...grpc.CallOption) (*AddByPathResponse, error) {
|
||||
out := new(AddByPathResponse)
|
||||
err := c.cc.Invoke(ctx, "/tree.TreeService/AddByPath", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *treeServiceClient) Remove(ctx context.Context, in *RemoveRequest, opts ...grpc.CallOption) (*RemoveResponse, error) {
|
||||
out := new(RemoveResponse)
|
||||
err := c.cc.Invoke(ctx, "/tree.TreeService/Remove", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *treeServiceClient) Move(ctx context.Context, in *MoveRequest, opts ...grpc.CallOption) (*MoveResponse, error) {
|
||||
out := new(MoveResponse)
|
||||
err := c.cc.Invoke(ctx, "/tree.TreeService/Move", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *treeServiceClient) GetNodeByPath(ctx context.Context, in *GetNodeByPathRequest, opts ...grpc.CallOption) (*GetNodeByPathResponse, error) {
|
||||
out := new(GetNodeByPathResponse)
|
||||
err := c.cc.Invoke(ctx, "/tree.TreeService/GetNodeByPath", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *treeServiceClient) GetSubTree(ctx context.Context, in *GetSubTreeRequest, opts ...grpc.CallOption) (*GetSubTreeResponse, error) {
|
||||
out := new(GetSubTreeResponse)
|
||||
err := c.cc.Invoke(ctx, "/tree.TreeService/GetSubTree", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *treeServiceClient) Apply(ctx context.Context, in *ApplyRequest, opts ...grpc.CallOption) (*ApplyResponse, error) {
|
||||
out := new(ApplyResponse)
|
||||
err := c.cc.Invoke(ctx, "/tree.TreeService/Apply", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// TreeServiceServer is the server API for TreeService service.
|
||||
// All implementations should embed UnimplementedTreeServiceServer
|
||||
// for forward compatibility
|
||||
type TreeServiceServer interface {
|
||||
// Add adds new node to the tree. Invoked by a client.
|
||||
Add(context.Context, *AddRequest) (*AddResponse, error)
|
||||
// AddByPath adds new node to the tree by path. Invoked by a client.
|
||||
AddByPath(context.Context, *AddByPathRequest) (*AddByPathResponse, error)
|
||||
// Remove removes node from the tree. Invoked by a client.
|
||||
Remove(context.Context, *RemoveRequest) (*RemoveResponse, error)
|
||||
// Move moves node from one parent to another. Invoked by a client.
|
||||
Move(context.Context, *MoveRequest) (*MoveResponse, error)
|
||||
// GetNodeByPath returns list of IDs corresponding to a specific filepath.
|
||||
GetNodeByPath(context.Context, *GetNodeByPathRequest) (*GetNodeByPathResponse, error)
|
||||
// GetSubTree returns tree corresponding to a specific node.
|
||||
GetSubTree(context.Context, *GetSubTreeRequest) (*GetSubTreeResponse, error)
|
||||
// Apply pushes log operation from another node to the current.
|
||||
// The request must be signed by a container node.
|
||||
Apply(context.Context, *ApplyRequest) (*ApplyResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedTreeServiceServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedTreeServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedTreeServiceServer) Add(context.Context, *AddRequest) (*AddResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Add not implemented")
|
||||
}
|
||||
func (UnimplementedTreeServiceServer) AddByPath(context.Context, *AddByPathRequest) (*AddByPathResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method AddByPath not implemented")
|
||||
}
|
||||
func (UnimplementedTreeServiceServer) Remove(context.Context, *RemoveRequest) (*RemoveResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Remove not implemented")
|
||||
}
|
||||
func (UnimplementedTreeServiceServer) Move(context.Context, *MoveRequest) (*MoveResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Move not implemented")
|
||||
}
|
||||
func (UnimplementedTreeServiceServer) GetNodeByPath(context.Context, *GetNodeByPathRequest) (*GetNodeByPathResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetNodeByPath not implemented")
|
||||
}
|
||||
func (UnimplementedTreeServiceServer) GetSubTree(context.Context, *GetSubTreeRequest) (*GetSubTreeResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetSubTree not implemented")
|
||||
}
|
||||
func (UnimplementedTreeServiceServer) Apply(context.Context, *ApplyRequest) (*ApplyResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Apply not implemented")
|
||||
}
|
||||
|
||||
// UnsafeTreeServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to TreeServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeTreeServiceServer interface {
|
||||
mustEmbedUnimplementedTreeServiceServer()
|
||||
}
|
||||
|
||||
func RegisterTreeServiceServer(s grpc.ServiceRegistrar, srv TreeServiceServer) {
|
||||
s.RegisterService(&TreeService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _TreeService_Add_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AddRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TreeServiceServer).Add(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/tree.TreeService/Add",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TreeServiceServer).Add(ctx, req.(*AddRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TreeService_AddByPath_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AddByPathRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TreeServiceServer).AddByPath(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/tree.TreeService/AddByPath",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TreeServiceServer).AddByPath(ctx, req.(*AddByPathRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TreeService_Remove_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RemoveRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TreeServiceServer).Remove(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/tree.TreeService/Remove",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TreeServiceServer).Remove(ctx, req.(*RemoveRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TreeService_Move_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(MoveRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TreeServiceServer).Move(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/tree.TreeService/Move",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TreeServiceServer).Move(ctx, req.(*MoveRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TreeService_GetNodeByPath_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetNodeByPathRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TreeServiceServer).GetNodeByPath(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/tree.TreeService/GetNodeByPath",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TreeServiceServer).GetNodeByPath(ctx, req.(*GetNodeByPathRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TreeService_GetSubTree_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetSubTreeRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TreeServiceServer).GetSubTree(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/tree.TreeService/GetSubTree",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TreeServiceServer).GetSubTree(ctx, req.(*GetSubTreeRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TreeService_Apply_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ApplyRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TreeServiceServer).Apply(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/tree.TreeService/Apply",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TreeServiceServer).Apply(ctx, req.(*ApplyRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// TreeService_ServiceDesc is the grpc.ServiceDesc for TreeService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var TreeService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "tree.TreeService",
|
||||
HandlerType: (*TreeServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Add",
|
||||
Handler: _TreeService_Add_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AddByPath",
|
||||
Handler: _TreeService_AddByPath_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Remove",
|
||||
Handler: _TreeService_Remove_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Move",
|
||||
Handler: _TreeService_Move_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetNodeByPath",
|
||||
Handler: _TreeService_GetNodeByPath_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetSubTree",
|
||||
Handler: _TreeService_GetSubTree_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Apply",
|
||||
Handler: _TreeService_Apply_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "pkg/services/tree/service.proto",
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package tree
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/signature"
|
||||
cidSDK "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||
)
|
||||
|
||||
func (s *Service) verifyClient(req interface{}, cid cidSDK.ID, rawKey []byte) error {
|
||||
// TODO(@fyrchik): #1328 access control
|
||||
return nil
|
||||
//nolint:govet
|
||||
err := signature.VerifyServiceMessage(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cnr, err := s.cnrSource.Get(cid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't get container %s: %w", cid, err)
|
||||
}
|
||||
|
||||
ownerID := cnr.Value.Owner()
|
||||
|
||||
pub, err := keys.NewPublicKeyFromBytes(rawKey, elliptic.P256())
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid public key: %w", err)
|
||||
}
|
||||
|
||||
var actualID user.ID
|
||||
user.IDFromKey(&actualID, (ecdsa.PublicKey)(*pub))
|
||||
|
||||
if !actualID.Equals(ownerID) {
|
||||
return errors.New("`Move` request must be signed by a container owner")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,313 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.19.4
|
||||
// source: pkg/services/tree/types.proto
|
||||
|
||||
package tree
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// KeyValue represents key-value pair attached to an object.
|
||||
type KeyValue struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (x *KeyValue) Reset() {
|
||||
*x = KeyValue{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_pkg_services_tree_types_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *KeyValue) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*KeyValue) ProtoMessage() {}
|
||||
|
||||
func (x *KeyValue) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_pkg_services_tree_types_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use KeyValue.ProtoReflect.Descriptor instead.
|
||||
func (*KeyValue) Descriptor() ([]byte, []int) {
|
||||
return file_pkg_services_tree_types_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *KeyValue) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *KeyValue) GetValue() []byte {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogMove represents log-entry for a single move operation.
|
||||
type LogMove struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// ID of the parent node.
|
||||
ParentId uint64 `protobuf:"varint,2,opt,name=parent_id,json=parentID,proto3" json:"parent_id,omitempty"`
|
||||
// Node meta information, including operation timestamp.
|
||||
Meta []byte `protobuf:"bytes,3,opt,name=meta,proto3" json:"meta,omitempty"`
|
||||
// ID of the node to move.
|
||||
ChildId uint64 `protobuf:"varint,4,opt,name=child_id,json=childID,proto3" json:"child_id,omitempty"`
|
||||
}
|
||||
|
||||
func (x *LogMove) Reset() {
|
||||
*x = LogMove{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_pkg_services_tree_types_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *LogMove) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*LogMove) ProtoMessage() {}
|
||||
|
||||
func (x *LogMove) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_pkg_services_tree_types_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use LogMove.ProtoReflect.Descriptor instead.
|
||||
func (*LogMove) Descriptor() ([]byte, []int) {
|
||||
return file_pkg_services_tree_types_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *LogMove) GetParentId() uint64 {
|
||||
if x != nil {
|
||||
return x.ParentId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *LogMove) GetMeta() []byte {
|
||||
if x != nil {
|
||||
return x.Meta
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *LogMove) GetChildId() uint64 {
|
||||
if x != nil {
|
||||
return x.ChildId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Signature of a message.
|
||||
type Signature struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Sign []byte `protobuf:"bytes,2,opt,name=sign,json=signature,proto3" json:"sign,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Signature) Reset() {
|
||||
*x = Signature{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_pkg_services_tree_types_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Signature) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Signature) ProtoMessage() {}
|
||||
|
||||
func (x *Signature) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_pkg_services_tree_types_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Signature.ProtoReflect.Descriptor instead.
|
||||
func (*Signature) Descriptor() ([]byte, []int) {
|
||||
return file_pkg_services_tree_types_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *Signature) GetKey() []byte {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Signature) GetSign() []byte {
|
||||
if x != nil {
|
||||
return x.Sign
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_pkg_services_tree_types_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_pkg_services_tree_types_proto_rawDesc = []byte{
|
||||
0x0a, 0x1d, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x74,
|
||||
0x72, 0x65, 0x65, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
|
||||
0x04, 0x74, 0x72, 0x65, 0x65, 0x22, 0x32, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
|
||||
0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x55, 0x0a, 0x07, 0x4c, 0x6f, 0x67,
|
||||
0x4d, 0x6f, 0x76, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69,
|
||||
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49,
|
||||
0x44, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69,
|
||||
0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x44,
|
||||
0x22, 0x36, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
|
||||
0x17, 0x0a, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73,
|
||||
0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x73, 0x70, 0x63, 0x63, 0x2d, 0x64, 0x65, 0x76,
|
||||
0x2f, 0x6e, 0x65, 0x6f, 0x66, 0x73, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f,
|
||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_pkg_services_tree_types_proto_rawDescOnce sync.Once
|
||||
file_pkg_services_tree_types_proto_rawDescData = file_pkg_services_tree_types_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_pkg_services_tree_types_proto_rawDescGZIP() []byte {
|
||||
file_pkg_services_tree_types_proto_rawDescOnce.Do(func() {
|
||||
file_pkg_services_tree_types_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_services_tree_types_proto_rawDescData)
|
||||
})
|
||||
return file_pkg_services_tree_types_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_pkg_services_tree_types_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_pkg_services_tree_types_proto_goTypes = []interface{}{
|
||||
(*KeyValue)(nil), // 0: tree.KeyValue
|
||||
(*LogMove)(nil), // 1: tree.LogMove
|
||||
(*Signature)(nil), // 2: tree.Signature
|
||||
}
|
||||
var file_pkg_services_tree_types_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_pkg_services_tree_types_proto_init() }
|
||||
func file_pkg_services_tree_types_proto_init() {
|
||||
if File_pkg_services_tree_types_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_pkg_services_tree_types_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*KeyValue); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_pkg_services_tree_types_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*LogMove); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_pkg_services_tree_types_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Signature); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_pkg_services_tree_types_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_pkg_services_tree_types_proto_goTypes,
|
||||
DependencyIndexes: file_pkg_services_tree_types_proto_depIdxs,
|
||||
MessageInfos: file_pkg_services_tree_types_proto_msgTypes,
|
||||
}.Build()
|
||||
File_pkg_services_tree_types_proto = out.File
|
||||
file_pkg_services_tree_types_proto_rawDesc = nil
|
||||
file_pkg_services_tree_types_proto_goTypes = nil
|
||||
file_pkg_services_tree_types_proto_depIdxs = nil
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package tree;
|
||||
|
||||
option go_package = "github.com/nspcc-dev/neofs-node/pkg/services/tree";
|
||||
|
||||
// KeyValue represents key-value pair attached to an object.
|
||||
message KeyValue {
|
||||
string key = 1 [json_name = "key"];
|
||||
bytes value = 2 [json_name = "value"];
|
||||
}
|
||||
|
||||
// LogMove represents log-entry for a single move operation.
|
||||
message LogMove {
|
||||
// ID of the parent node.
|
||||
uint64 parent_id = 2 [json_name = "parentID"];
|
||||
// Node meta information, including operation timestamp.
|
||||
bytes meta = 3 [json_name = "meta"];
|
||||
// ID of the node to move.
|
||||
uint64 child_id = 4 [json_name = "childID"];
|
||||
}
|
||||
|
||||
// Signature of a message.
|
||||
message Signature {
|
||||
bytes key = 1 [json_name = "key"];
|
||||
bytes sign = 2 [json_name = "signature"];
|
||||
}
|
Loading…
Reference in New Issue