Initial QSfera import
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
||||
package rgrpc
|
||||
|
||||
import (
|
||||
"math"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
_serverMaxConnectionAgeEnv = "GRPC_MAX_CONNECTION_AGE"
|
||||
|
||||
// same default as grpc
|
||||
infinity = time.Duration(math.MaxInt64)
|
||||
_defaultMaxConnectionAge = infinity
|
||||
)
|
||||
|
||||
// GetMaxConnectionAge returns the maximum grpc connection age.
|
||||
func GetMaxConnectionAge() time.Duration {
|
||||
d, err := time.ParseDuration(os.Getenv(_serverMaxConnectionAgeEnv))
|
||||
if err != nil {
|
||||
return _defaultMaxConnectionAge
|
||||
}
|
||||
return d
|
||||
}
|
||||
+398
@@ -0,0 +1,398 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package rgrpc
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sort"
|
||||
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/opencloud-eu/reva/v2/internal/grpc/interceptors/appctx"
|
||||
"github.com/opencloud-eu/reva/v2/internal/grpc/interceptors/auth"
|
||||
"github.com/opencloud-eu/reva/v2/internal/grpc/interceptors/log"
|
||||
"github.com/opencloud-eu/reva/v2/internal/grpc/interceptors/recovery"
|
||||
"github.com/opencloud-eu/reva/v2/internal/grpc/interceptors/token"
|
||||
"github.com/opencloud-eu/reva/v2/internal/grpc/interceptors/useragent"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/sharedconf"
|
||||
rtrace "github.com/opencloud-eu/reva/v2/pkg/trace"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
mtls "go-micro.dev/v4/util/tls"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/reflection"
|
||||
)
|
||||
|
||||
// UnaryInterceptors is a map of registered unary grpc interceptors.
|
||||
var UnaryInterceptors = map[string]NewUnaryInterceptor{}
|
||||
|
||||
// StreamInterceptors is a map of registered streaming grpc interceptor
|
||||
var StreamInterceptors = map[string]NewStreamInterceptor{}
|
||||
|
||||
// NewUnaryInterceptor is the type that unary interceptors need to register.
|
||||
type NewUnaryInterceptor func(m map[string]interface{}) (grpc.UnaryServerInterceptor, int, error)
|
||||
|
||||
// NewStreamInterceptor is the type that stream interceptors need to register.
|
||||
type NewStreamInterceptor func(m map[string]interface{}) (grpc.StreamServerInterceptor, int, error)
|
||||
|
||||
// RegisterUnaryInterceptor registers a new unary interceptor.
|
||||
func RegisterUnaryInterceptor(name string, newFunc NewUnaryInterceptor) {
|
||||
UnaryInterceptors[name] = newFunc
|
||||
}
|
||||
|
||||
// RegisterStreamInterceptor registers a new stream interceptor.
|
||||
func RegisterStreamInterceptor(name string, newFunc NewStreamInterceptor) {
|
||||
StreamInterceptors[name] = newFunc
|
||||
}
|
||||
|
||||
// Services is a map of service name and its new function.
|
||||
var Services = map[string]NewService{}
|
||||
|
||||
// Register registers a new gRPC service with name and new function.
|
||||
func Register(name string, newFunc NewService) {
|
||||
Services[name] = newFunc
|
||||
}
|
||||
|
||||
// NewService is the function that gRPC services need to register at init time.
|
||||
// It returns an io.Closer to close the service and a list of service endpoints that need to be unprotected.
|
||||
type NewService func(conf map[string]interface{}, ss *grpc.Server, log *zerolog.Logger) (Service, error)
|
||||
|
||||
// Service represents a grpc service.
|
||||
type Service interface {
|
||||
Register(ss *grpc.Server)
|
||||
io.Closer
|
||||
UnprotectedEndpoints() []string
|
||||
}
|
||||
|
||||
type unaryInterceptorTriple struct {
|
||||
Name string
|
||||
Priority int
|
||||
Interceptor grpc.UnaryServerInterceptor
|
||||
}
|
||||
|
||||
type streamInterceptorTriple struct {
|
||||
Name string
|
||||
Priority int
|
||||
Interceptor grpc.StreamServerInterceptor
|
||||
}
|
||||
|
||||
type tlsSettings struct {
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
CertificateFile string `mapstructure:"certificate"`
|
||||
KeyFile string `mapstructure:"key"`
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
type config struct {
|
||||
Network string `mapstructure:"network"`
|
||||
Address string `mapstructure:"address"`
|
||||
TLSSettings tlsSettings `mapstructure:"tls_settings"`
|
||||
ShutdownDeadline int `mapstructure:"shutdown_deadline"`
|
||||
Services map[string]map[string]interface{} `mapstructure:"services"`
|
||||
Interceptors map[string]map[string]interface{} `mapstructure:"interceptors"`
|
||||
EnableReflection bool `mapstructure:"enable_reflection"`
|
||||
}
|
||||
|
||||
func (c *config) init() {
|
||||
if c.Network == "" {
|
||||
c.Network = "tcp"
|
||||
}
|
||||
|
||||
if c.Address == "" {
|
||||
c.Address = sharedconf.GetGatewaySVC("0.0.0.0:19000")
|
||||
}
|
||||
}
|
||||
|
||||
// Server is a gRPC server.
|
||||
type Server struct {
|
||||
s *grpc.Server
|
||||
conf *config
|
||||
listener net.Listener
|
||||
log zerolog.Logger
|
||||
tracerProvider trace.TracerProvider
|
||||
services map[string]Service
|
||||
}
|
||||
|
||||
// NewServer returns a new Server.
|
||||
func NewServer(m interface{}, log zerolog.Logger, tp trace.TracerProvider) (*Server, error) {
|
||||
var err error
|
||||
conf := &config{}
|
||||
if err := mapstructure.Decode(m, conf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf.init()
|
||||
|
||||
if conf.TLSSettings.Enabled {
|
||||
var cert tls.Certificate
|
||||
switch {
|
||||
case conf.TLSSettings.CertificateFile == "" && conf.TLSSettings.KeyFile == "":
|
||||
// Generate a self-signed server certificate on the fly. This requires the clients
|
||||
// to connect with InsecureSkipVerify.
|
||||
subj := []string{conf.Address}
|
||||
if host, _, err := net.SplitHostPort(conf.Address); err == nil && host != "" {
|
||||
subj = []string{host}
|
||||
}
|
||||
|
||||
log.Warn().Str("address", conf.Address).Str("network", conf.Network).
|
||||
Msg("No server certificate configured. Generating a temporary self-signed certificate")
|
||||
|
||||
cert, err = mtls.Certificate(subj...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
cert, err = tls.LoadX509KeyPair(
|
||||
conf.TLSSettings.CertificateFile,
|
||||
conf.TLSSettings.KeyFile,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
conf.TLSSettings.tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
}
|
||||
|
||||
server := &Server{conf: conf, log: log, tracerProvider: tp, services: map[string]Service{}}
|
||||
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// Start starts the server.
|
||||
func (s *Server) Start(ln net.Listener) error {
|
||||
if err := s.registerServices(); err != nil {
|
||||
err = errors.Wrap(err, "unable to register services")
|
||||
return err
|
||||
}
|
||||
|
||||
s.listener = ln
|
||||
s.log.Info().Msgf("grpc server listening at %s:%s", s.Network(), s.Address())
|
||||
err := s.s.Serve(s.listener)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "serve failed")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) isInterceptorEnabled(name string) bool {
|
||||
for k := range s.conf.Interceptors {
|
||||
if k == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Server) isServiceEnabled(svcName string) bool {
|
||||
for key := range Services {
|
||||
if key == svcName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Server) registerServices() error {
|
||||
for svcName := range s.conf.Services {
|
||||
if s.isServiceEnabled(svcName) {
|
||||
newFunc := Services[svcName]
|
||||
svc, err := newFunc(s.conf.Services[svcName], s.s, &s.log)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "rgrpc: grpc service %s could not be started,", svcName)
|
||||
}
|
||||
s.services[svcName] = svc
|
||||
s.log.Info().Msgf("rgrpc: grpc service enabled: %s", svcName)
|
||||
} else {
|
||||
message := fmt.Sprintf("rgrpc: grpc service %s does not exist", svcName)
|
||||
return errors.New(message)
|
||||
}
|
||||
}
|
||||
|
||||
// obtain list of unprotected endpoints
|
||||
unprotected := []string{}
|
||||
for _, svc := range s.services {
|
||||
unprotected = append(unprotected, svc.UnprotectedEndpoints()...)
|
||||
}
|
||||
|
||||
opts, err := s.getInterceptors(unprotected)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.conf.TLSSettings.tlsConfig != nil {
|
||||
opts = append(opts, grpc.Creds(credentials.NewTLS(s.conf.TLSSettings.tlsConfig)))
|
||||
}
|
||||
opts = append(opts, grpc.KeepaliveParams(keepalive.ServerParameters{
|
||||
MaxConnectionAge: GetMaxConnectionAge(), // this forces clients to reconnect after 30 seconds, triggering a new DNS lookup to pick up new IPs
|
||||
}))
|
||||
|
||||
grpcServer := grpc.NewServer(opts...)
|
||||
|
||||
for _, svc := range s.services {
|
||||
svc.Register(grpcServer)
|
||||
}
|
||||
|
||||
if s.conf.EnableReflection {
|
||||
s.log.Info().Msg("rgrpc: grpc server reflection enabled")
|
||||
reflection.Register(grpcServer)
|
||||
}
|
||||
|
||||
s.s = grpcServer
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(labkode): make closing with deadline.
|
||||
func (s *Server) cleanupServices() {
|
||||
for name, svc := range s.services {
|
||||
if err := svc.Close(); err != nil {
|
||||
s.log.Error().Err(err).Msgf("error closing service %q", name)
|
||||
} else {
|
||||
s.log.Info().Msgf("service %q correctly closed", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops the server.
|
||||
func (s *Server) Stop() error {
|
||||
s.s.Stop()
|
||||
s.cleanupServices()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GracefulStop gracefully stops the server.
|
||||
func (s *Server) GracefulStop() error {
|
||||
s.s.GracefulStop()
|
||||
s.cleanupServices()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Network returns the network type.
|
||||
func (s *Server) Network() string {
|
||||
return s.conf.Network
|
||||
}
|
||||
|
||||
// Address returns the network address.
|
||||
func (s *Server) Address() string {
|
||||
return s.conf.Address
|
||||
}
|
||||
|
||||
func (s *Server) getInterceptors(unprotected []string) ([]grpc.ServerOption, error) {
|
||||
unaryTriples := []*unaryInterceptorTriple{}
|
||||
for name, newFunc := range UnaryInterceptors {
|
||||
if s.isInterceptorEnabled(name) {
|
||||
inter, prio, err := newFunc(s.conf.Interceptors[name])
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "rgrpc: error creating unary interceptor: %s,", name)
|
||||
return nil, err
|
||||
}
|
||||
triple := &unaryInterceptorTriple{
|
||||
Name: name,
|
||||
Priority: prio,
|
||||
Interceptor: inter,
|
||||
}
|
||||
unaryTriples = append(unaryTriples, triple)
|
||||
}
|
||||
}
|
||||
|
||||
// sort unary triples
|
||||
sort.SliceStable(unaryTriples, func(i, j int) bool {
|
||||
return unaryTriples[i].Priority < unaryTriples[j].Priority
|
||||
})
|
||||
|
||||
authUnary, err := auth.NewUnary(s.conf.Interceptors["auth"], unprotected, s.tracerProvider)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "rgrpc: error creating unary auth interceptor")
|
||||
}
|
||||
|
||||
unaryInterceptors := []grpc.UnaryServerInterceptor{
|
||||
appctx.NewUnary(s.log, s.tracerProvider),
|
||||
token.NewUnary(),
|
||||
useragent.NewUnary(),
|
||||
log.NewUnary(),
|
||||
recovery.NewUnary(),
|
||||
authUnary,
|
||||
}
|
||||
|
||||
for _, t := range unaryTriples {
|
||||
unaryInterceptors = append(unaryInterceptors, t.Interceptor)
|
||||
s.log.Info().Msgf("rgrpc: chaining grpc unary interceptor %s with priority %d", t.Name, t.Priority)
|
||||
}
|
||||
|
||||
unaryChain := grpc_middleware.ChainUnaryServer(unaryInterceptors...)
|
||||
|
||||
streamTriples := []*streamInterceptorTriple{}
|
||||
for name, newFunc := range StreamInterceptors {
|
||||
if s.isInterceptorEnabled(name) {
|
||||
inter, prio, err := newFunc(s.conf.Interceptors[name])
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "rgrpc: error creating streaming interceptor: %s,", name)
|
||||
return nil, err
|
||||
}
|
||||
triple := &streamInterceptorTriple{
|
||||
Name: name,
|
||||
Priority: prio,
|
||||
Interceptor: inter,
|
||||
}
|
||||
streamTriples = append(streamTriples, triple)
|
||||
}
|
||||
}
|
||||
// sort stream triples
|
||||
sort.SliceStable(streamTriples, func(i, j int) bool {
|
||||
return streamTriples[i].Priority < streamTriples[j].Priority
|
||||
})
|
||||
|
||||
authStream, err := auth.NewStream(s.conf.Interceptors["auth"], unprotected, s.tracerProvider)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "rgrpc: error creating stream auth interceptor")
|
||||
}
|
||||
|
||||
streamInterceptors := []grpc.StreamServerInterceptor{
|
||||
appctx.NewStream(s.log, s.tracerProvider),
|
||||
token.NewStream(),
|
||||
useragent.NewStream(),
|
||||
log.NewStream(),
|
||||
recovery.NewStream(),
|
||||
authStream,
|
||||
}
|
||||
|
||||
for _, t := range streamTriples {
|
||||
streamInterceptors = append(streamInterceptors, t.Interceptor)
|
||||
s.log.Info().Msgf("rgrpc: chaining grpc streaming interceptor %s with priority %d", t.Name, t.Priority)
|
||||
}
|
||||
streamChain := grpc_middleware.ChainStreamServer(streamInterceptors...)
|
||||
|
||||
opts := []grpc.ServerOption{
|
||||
grpc.StatsHandler(otelgrpc.NewServerHandler(
|
||||
otelgrpc.WithTracerProvider(s.tracerProvider),
|
||||
otelgrpc.WithPropagators(rtrace.Propagator))),
|
||||
grpc.UnaryInterceptor(unaryChain),
|
||||
grpc.StreamInterceptor(streamChain),
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
// Package status contains helpers functions
|
||||
// to create grpc Status with contextual information,
|
||||
// like traces.
|
||||
package status
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// NewOK returns a Status with CODE_OK.
|
||||
func NewOK(ctx context.Context) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_OK,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewNotFound returns a Status with CODE_NOT_FOUND.
|
||||
func NewNotFound(ctx context.Context, msg string) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_NOT_FOUND,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewInvalid returns a Status with CODE_INVALID_ARGUMENT.
|
||||
func NewInvalid(ctx context.Context, msg string) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_INVALID_ARGUMENT,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewInternal returns a Status with CODE_INTERNAL.
|
||||
func NewInternal(ctx context.Context, msg string) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_INTERNAL,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewUnavailable returns a Status with CODE_UNAVAILABLE.
|
||||
func NewUnavailable(ctx context.Context, msg string) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_UNAVAILABLE,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewUnauthenticated returns a Status with CODE_UNAUTHENTICATED.
|
||||
func NewUnauthenticated(ctx context.Context, err error, msg string) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_UNAUTHENTICATED,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewPermissionDenied returns a Status with PERMISSION_DENIED.
|
||||
func NewPermissionDenied(ctx context.Context, err error, msg string) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_PERMISSION_DENIED,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewAborted returns a Status with ABORTED.
|
||||
func NewAborted(ctx context.Context, err error, msg string) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_ABORTED,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFailedPrecondition returns a Status with FAILED_PRECONDITION.
|
||||
func NewFailedPrecondition(ctx context.Context, err error, msg string) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_FAILED_PRECONDITION,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewInsufficientStorage returns a Status with INSUFFICIENT_STORAGE.
|
||||
func NewInsufficientStorage(ctx context.Context, err error, msg string) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_INSUFFICIENT_STORAGE,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewUnimplemented returns a Status with CODE_UNIMPLEMENTED.
|
||||
func NewUnimplemented(ctx context.Context, err error, msg string) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_UNIMPLEMENTED,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewAlreadyExists returns a Status with CODE_ALREADY_EXISTS.
|
||||
func NewAlreadyExists(ctx context.Context, err error, msg string) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_ALREADY_EXISTS,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewInvalidArg returns a Status with CODE_INVALID_ARGUMENT.
|
||||
func NewInvalidArg(ctx context.Context, msg string) *rpc.Status {
|
||||
return &rpc.Status{Code: rpc.Code_CODE_INVALID_ARGUMENT,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewConflict returns a Status with Code_CODE_ABORTED.
|
||||
//
|
||||
// Deprecated: NewConflict exists for historical compatibility
|
||||
// and should not be used. To create a Status with code ABORTED,
|
||||
// use NewAborted.
|
||||
func NewConflict(ctx context.Context, err error, msg string) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_ABORTED,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewLocked returns a status Code_CODE_LOCKED
|
||||
func NewLocked(ctx context.Context, msg string) *rpc.Status {
|
||||
return &rpc.Status{
|
||||
Code: rpc.Code_CODE_LOCKED,
|
||||
Message: msg,
|
||||
Trace: getTrace(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStatusFromErrType returns a status that corresponds to the given errtype
|
||||
func NewStatusFromErrType(ctx context.Context, msg string, err error) *rpc.Status {
|
||||
switch e := err.(type) {
|
||||
case nil:
|
||||
return NewOK(ctx)
|
||||
case errtypes.NotFound:
|
||||
return NewNotFound(ctx, msg+": "+err.Error())
|
||||
case errtypes.IsNotFound:
|
||||
return NewNotFound(ctx, msg+": "+err.Error())
|
||||
case errtypes.AlreadyExists:
|
||||
return NewAlreadyExists(ctx, err, msg+": "+err.Error())
|
||||
case errtypes.InvalidCredentials:
|
||||
return NewPermissionDenied(ctx, e, msg+": "+err.Error())
|
||||
case errtypes.IsInvalidCredentials:
|
||||
// TODO this maps badly
|
||||
return NewUnauthenticated(ctx, err, msg+": "+err.Error())
|
||||
case errtypes.PermissionDenied:
|
||||
return NewPermissionDenied(ctx, e, msg+": "+err.Error())
|
||||
case errtypes.Locked:
|
||||
// FIXME a locked error returns the current lockid
|
||||
// FIXME use NewAborted as per the rpc code docs
|
||||
return NewLocked(ctx, msg+": "+err.Error())
|
||||
case errtypes.Aborted:
|
||||
return NewAborted(ctx, e, msg+": "+err.Error())
|
||||
case errtypes.PreconditionFailed:
|
||||
return NewFailedPrecondition(ctx, e, msg+": "+err.Error())
|
||||
case errtypes.IsNotSupported:
|
||||
return NewUnimplemented(ctx, err, msg+":"+err.Error())
|
||||
case errtypes.BadRequest:
|
||||
return NewInvalid(ctx, msg+":"+err.Error())
|
||||
case errtypes.Unavailable:
|
||||
return NewUnavailable(ctx, msg+": "+err.Error())
|
||||
case errtypes.IsUnavailable:
|
||||
return NewUnavailable(ctx, msg+": "+err.Error())
|
||||
}
|
||||
|
||||
// map GRPC status codes coming from the auth middleware
|
||||
grpcErr := err
|
||||
for {
|
||||
st, ok := status.FromError(grpcErr)
|
||||
if ok {
|
||||
switch st.Code() {
|
||||
case codes.NotFound:
|
||||
return NewNotFound(ctx, msg+": "+err.Error())
|
||||
case codes.Unauthenticated:
|
||||
return NewUnauthenticated(ctx, err, msg+": "+err.Error())
|
||||
case codes.PermissionDenied:
|
||||
return NewPermissionDenied(ctx, err, msg+": "+err.Error())
|
||||
case codes.Unimplemented:
|
||||
return NewUnimplemented(ctx, err, msg+": "+err.Error())
|
||||
}
|
||||
}
|
||||
// the actual error can be wrapped multiple times
|
||||
grpcErr = errors.Unwrap(grpcErr)
|
||||
if grpcErr == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return NewInternal(ctx, msg+":"+err.Error())
|
||||
}
|
||||
|
||||
// NewErrorFromCode returns a standardized Error for a given RPC code.
|
||||
func NewErrorFromCode(code rpc.Code, pkgname string) error {
|
||||
return errors.New(pkgname + ": grpc failed with code " + code.String())
|
||||
}
|
||||
|
||||
// internal function to attach the trace to a context
|
||||
func getTrace(ctx context.Context) string {
|
||||
span := trace.SpanFromContext(ctx)
|
||||
return span.SpanContext().TraceID().String()
|
||||
}
|
||||
|
||||
// a mapping from the CS3 status codes to http codes
|
||||
var httpStatusCode = map[rpc.Code]int{
|
||||
rpc.Code_CODE_ABORTED: http.StatusConflict, // webdav uses 412 PreconditionFailed for locks and etags
|
||||
rpc.Code_CODE_ALREADY_EXISTS: http.StatusConflict,
|
||||
rpc.Code_CODE_CANCELLED: 499, // Client Closed Request
|
||||
rpc.Code_CODE_DATA_LOSS: http.StatusInternalServerError,
|
||||
rpc.Code_CODE_DEADLINE_EXCEEDED: http.StatusGatewayTimeout,
|
||||
rpc.Code_CODE_FAILED_PRECONDITION: http.StatusPreconditionFailed,
|
||||
rpc.Code_CODE_INSUFFICIENT_STORAGE: http.StatusInsufficientStorage,
|
||||
rpc.Code_CODE_INTERNAL: http.StatusInternalServerError,
|
||||
rpc.Code_CODE_INVALID: http.StatusInternalServerError,
|
||||
rpc.Code_CODE_INVALID_ARGUMENT: http.StatusBadRequest,
|
||||
rpc.Code_CODE_NOT_FOUND: http.StatusNotFound,
|
||||
rpc.Code_CODE_OK: http.StatusOK,
|
||||
rpc.Code_CODE_OUT_OF_RANGE: http.StatusBadRequest,
|
||||
rpc.Code_CODE_PERMISSION_DENIED: http.StatusForbidden,
|
||||
rpc.Code_CODE_REDIRECTION: http.StatusTemporaryRedirect, // or permanent?
|
||||
rpc.Code_CODE_RESOURCE_EXHAUSTED: http.StatusTooManyRequests,
|
||||
rpc.Code_CODE_UNAUTHENTICATED: http.StatusUnauthorized,
|
||||
rpc.Code_CODE_UNAVAILABLE: http.StatusServiceUnavailable,
|
||||
rpc.Code_CODE_UNIMPLEMENTED: http.StatusNotImplemented,
|
||||
rpc.Code_CODE_UNKNOWN: http.StatusInternalServerError,
|
||||
rpc.Code_CODE_LOCKED: http.StatusLocked,
|
||||
rpc.Code_CODE_TOO_EARLY: http.StatusTooEarly,
|
||||
}
|
||||
|
||||
// HTTPStatusFromCode returns an HTTP status code for the rpc code. It returns
|
||||
// an internal server error (500) if the code is unknown
|
||||
func HTTPStatusFromCode(code rpc.Code) int {
|
||||
if s, ok := httpStatusCode[code]; ok {
|
||||
return s
|
||||
}
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package pool
|
||||
|
||||
import (
|
||||
appprovider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
|
||||
appregistry "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1"
|
||||
applicationauth "github.com/cs3org/go-cs3apis/cs3/auth/applications/v1beta1"
|
||||
authprovider "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
|
||||
authregistry "github.com/cs3org/go-cs3apis/cs3/auth/registry/v1beta1"
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
|
||||
tenant "github.com/cs3org/go-cs3apis/cs3/identity/tenant/v1beta1"
|
||||
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
ocmcore "github.com/cs3org/go-cs3apis/cs3/ocm/core/v1beta1"
|
||||
invitepb "github.com/cs3org/go-cs3apis/cs3/ocm/invite/v1beta1"
|
||||
ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1"
|
||||
permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
|
||||
preferences "github.com/cs3org/go-cs3apis/cs3/preferences/v1beta1"
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
|
||||
ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
|
||||
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
storageregistry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1"
|
||||
datatx "github.com/cs3org/go-cs3apis/cs3/tx/v1beta1"
|
||||
)
|
||||
|
||||
// GetGatewayServiceClient returns a GatewayServiceClient.
|
||||
func GetGatewayServiceClient(id string, opts ...Option) (gateway.GatewayAPIClient, error) {
|
||||
selector, _ := GatewaySelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetUserProviderServiceClient returns a UserProviderServiceClient.
|
||||
func GetUserProviderServiceClient(id string, opts ...Option) (user.UserAPIClient, error) {
|
||||
selector, _ := IdentityUserSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetTenantProviderServiceClient returns a TenantProviderServiceClient.
|
||||
func GetTenantProviderServiceClient(id string, opts ...Option) (tenant.TenantAPIClient, error) {
|
||||
selector, _ := IdentityTenantSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetGroupProviderServiceClient returns a GroupProviderServiceClient.
|
||||
func GetGroupProviderServiceClient(id string, opts ...Option) (group.GroupAPIClient, error) {
|
||||
selector, _ := IdentityGroupSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetStorageProviderServiceClient returns a StorageProviderServiceClient.
|
||||
func GetStorageProviderServiceClient(id string, opts ...Option) (storageprovider.ProviderAPIClient, error) {
|
||||
selector, _ := StorageProviderSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetSpacesProviderServiceClient returns a SpacesProviderServiceClient.
|
||||
func GetSpacesProviderServiceClient(id string, opts ...Option) (storageprovider.SpacesAPIClient, error) {
|
||||
selector, _ := SpacesProviderSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetAuthRegistryServiceClient returns a new AuthRegistryServiceClient.
|
||||
func GetAuthRegistryServiceClient(id string, opts ...Option) (authregistry.RegistryAPIClient, error) {
|
||||
selector, _ := AuthRegistrySelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetAuthProviderServiceClient returns a new AuthProviderServiceClient.
|
||||
func GetAuthProviderServiceClient(id string, opts ...Option) (authprovider.ProviderAPIClient, error) {
|
||||
selector, _ := AuthProviderSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetAppAuthProviderServiceClient returns a new AppAuthProviderServiceClient.
|
||||
func GetAppAuthProviderServiceClient(id string, opts ...Option) (applicationauth.ApplicationsAPIClient, error) {
|
||||
selector, _ := AuthApplicationSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetUserShareProviderClient returns a new UserShareProviderClient.
|
||||
func GetUserShareProviderClient(id string, opts ...Option) (collaboration.CollaborationAPIClient, error) {
|
||||
selector, _ := SharingCollaborationSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetOCMShareProviderClient returns a new OCMShareProviderClient.
|
||||
func GetOCMShareProviderClient(id string, opts ...Option) (ocm.OcmAPIClient, error) {
|
||||
selector, _ := SharingOCMSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetOCMInviteManagerClient returns a new OCMInviteManagerClient.
|
||||
func GetOCMInviteManagerClient(id string, opts ...Option) (invitepb.InviteAPIClient, error) {
|
||||
selector, _ := OCMInviteSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetPublicShareProviderClient returns a new PublicShareProviderClient.
|
||||
func GetPublicShareProviderClient(id string, opts ...Option) (link.LinkAPIClient, error) {
|
||||
selector, _ := SharingLinkSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetPreferencesClient returns a new PreferencesClient.
|
||||
func GetPreferencesClient(id string, opts ...Option) (preferences.PreferencesAPIClient, error) {
|
||||
selector, _ := PreferencesSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetPermissionsClient returns a new PermissionsClient.
|
||||
func GetPermissionsClient(id string, opts ...Option) (permissions.PermissionsAPIClient, error) {
|
||||
selector, _ := PermissionsSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetAppRegistryClient returns a new AppRegistryClient.
|
||||
func GetAppRegistryClient(id string, opts ...Option) (appregistry.RegistryAPIClient, error) {
|
||||
selector, _ := AppRegistrySelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetAppProviderClient returns a new AppRegistryClient.
|
||||
func GetAppProviderClient(id string, opts ...Option) (appprovider.ProviderAPIClient, error) {
|
||||
selector, _ := AppProviderSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetStorageRegistryClient returns a new StorageRegistryClient.
|
||||
func GetStorageRegistryClient(id string, opts ...Option) (storageregistry.RegistryAPIClient, error) {
|
||||
selector, _ := StorageRegistrySelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetOCMProviderAuthorizerClient returns a new OCMProviderAuthorizerClient.
|
||||
func GetOCMProviderAuthorizerClient(id string, opts ...Option) (ocmprovider.ProviderAPIClient, error) {
|
||||
selector, _ := OCMProviderSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetOCMCoreClient returns a new OCMCoreClient.
|
||||
func GetOCMCoreClient(id string, opts ...Option) (ocmcore.OcmCoreAPIClient, error) {
|
||||
selector, _ := OCMCoreSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
|
||||
// GetDataTxClient returns a new DataTxClient.
|
||||
func GetDataTxClient(id string, opts ...Option) (datatx.TxAPIClient, error) {
|
||||
selector, _ := TXSelector(id, opts...)
|
||||
return selector.Next()
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package pool
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
rtrace "github.com/opencloud-eu/reva/v2/pkg/trace"
|
||||
"github.com/pkg/errors"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
var (
|
||||
_defaultMaxCallRecvMsgSize = 10240000
|
||||
)
|
||||
|
||||
// NewConn creates a new connection to a grpc server
|
||||
// with open census tracing support.
|
||||
// TODO(labkode): make grpc tls configurable.
|
||||
func NewConn(target string, opts ...Option) (*grpc.ClientConn, error) {
|
||||
|
||||
options := ClientOptions{}
|
||||
if err := options.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// then overwrite with supplied options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
var cred credentials.TransportCredentials
|
||||
switch options.tlsMode {
|
||||
case TLSOff:
|
||||
cred = insecure.NewCredentials()
|
||||
case TLSInsecure:
|
||||
tlsConfig := tls.Config{
|
||||
InsecureSkipVerify: true, //nolint:gosec
|
||||
}
|
||||
cred = credentials.NewTLS(&tlsConfig)
|
||||
case TLSOn:
|
||||
if options.caCert != "" {
|
||||
var err error
|
||||
if cred, err = credentials.NewClientTLSFromFile(options.caCert, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Use system's cert pool
|
||||
cred = credentials.NewTLS(&tls.Config{})
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: We need to configure some grpc options in a central place.
|
||||
// If many services configure the (e.g.) gateway client differently, one will be pick randomly. This leads to inconsistencies when using the single binary.
|
||||
// To avoid inconsistencies and race conditions we get the configuration here.
|
||||
// Please do NOT follow the pattern of calling `os.Getenv` in the wild without consulting docu team first.
|
||||
maxRcvMsgSize := _defaultMaxCallRecvMsgSize
|
||||
if e := os.Getenv("OC_GRPC_MAX_RECEIVED_MESSAGE_SIZE"); e != "" {
|
||||
s, err := strconv.Atoi(e)
|
||||
if err != nil || s <= 0 {
|
||||
return nil, errors.Wrap(err, "grpc max message size is not a valid int")
|
||||
}
|
||||
maxRcvMsgSize = s
|
||||
}
|
||||
|
||||
conn, err := grpc.NewClient(
|
||||
target,
|
||||
grpc.WithTransportCredentials(cred),
|
||||
grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallRecvMsgSize(maxRcvMsgSize),
|
||||
),
|
||||
grpc.WithDefaultServiceConfig(`{
|
||||
"loadBalancingPolicy":"round_robin"
|
||||
}`),
|
||||
/* we may want to retry more often than the default transparent retry, see https://grpc.io/docs/guides/retry/#retry-configuration
|
||||
grpc.WithDefaultServiceConfig(`{
|
||||
"loadBalancingPolicy":"round_robin"
|
||||
"methodConfig": [
|
||||
{
|
||||
"name": [
|
||||
{ "service": "grpc.examples.echo.Echo" }
|
||||
],
|
||||
"retryPolicy": {
|
||||
"maxAttempts": 3,
|
||||
"initialBackoff": "0.1s",
|
||||
"maxBackoff": "1s",
|
||||
"backoffMultiplier": 2,
|
||||
"retryableStatusCodes": ["UNAVAILABLE", "CANCELLED", "RESOURCE_EXHAUSTED", "DEADLINE_EXCEEDED"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}`),
|
||||
*/
|
||||
grpc.WithStatsHandler(otelgrpc.NewClientHandler(
|
||||
otelgrpc.WithTracerProvider(
|
||||
options.tracerProvider,
|
||||
),
|
||||
otelgrpc.WithPropagators(
|
||||
rtrace.Propagator,
|
||||
),
|
||||
)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package pool
|
||||
|
||||
import (
|
||||
"github.com/opencloud-eu/reva/v2/pkg/sharedconf"
|
||||
rtrace "github.com/opencloud-eu/reva/v2/pkg/trace"
|
||||
"go-micro.dev/v4/registry"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// Option is used to pass client options
|
||||
type Option func(opts *ClientOptions)
|
||||
|
||||
// ClientOptions represent additional options (e.g. tls settings) for the grpc clients
|
||||
type ClientOptions struct {
|
||||
tlsMode TLSMode
|
||||
caCert string
|
||||
tracerProvider trace.TracerProvider
|
||||
registry registry.Registry
|
||||
}
|
||||
|
||||
func (o *ClientOptions) init() error {
|
||||
// default to shared settings
|
||||
sharedOpt := sharedconf.GRPCClientOptions()
|
||||
var err error
|
||||
|
||||
if o.tlsMode, err = StringToTLSMode(sharedOpt.TLSMode); err != nil {
|
||||
return err
|
||||
}
|
||||
o.caCert = sharedOpt.CACertFile
|
||||
o.tracerProvider = rtrace.DefaultProvider()
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithTLSMode allows to set the TLSMode option for grpc clients
|
||||
func WithTLSMode(v TLSMode) Option {
|
||||
return func(o *ClientOptions) {
|
||||
o.tlsMode = v
|
||||
}
|
||||
}
|
||||
|
||||
// WithTLSCACert allows to set the CA Certificate for grpc clients
|
||||
func WithTLSCACert(v string) Option {
|
||||
return func(o *ClientOptions) {
|
||||
o.caCert = v
|
||||
}
|
||||
}
|
||||
|
||||
// WithTracerProvider allows to set the opentelemetry tracer provider for grpc clients
|
||||
func WithTracerProvider(v trace.TracerProvider) Option {
|
||||
return func(o *ClientOptions) {
|
||||
o.tracerProvider = v
|
||||
}
|
||||
}
|
||||
|
||||
// WithRegistry allows to set the registry for service lookup
|
||||
func WithRegistry(v registry.Registry) Option {
|
||||
return func(o *ClientOptions) {
|
||||
o.registry = v
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package pool
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// TLSMode represents TLS mode for the clients
|
||||
type TLSMode int
|
||||
|
||||
const (
|
||||
// TLSOff completely disables transport security
|
||||
TLSOff TLSMode = iota
|
||||
// TLSOn enables transport security
|
||||
TLSOn
|
||||
// TLSInsecure enables transport security, but disables the verification of the
|
||||
// server certificate
|
||||
TLSInsecure
|
||||
)
|
||||
|
||||
// StringToTLSMode converts the supply string into the equivalent TLSMode constant
|
||||
func StringToTLSMode(m string) (TLSMode, error) {
|
||||
switch m {
|
||||
case "off", "":
|
||||
return TLSOff, nil
|
||||
case "insecure":
|
||||
return TLSInsecure, nil
|
||||
case "on":
|
||||
return TLSOn, nil
|
||||
default:
|
||||
return TLSOff, fmt.Errorf("unknown TLS mode: '%s'. Valid values are 'on', 'off' and 'insecure'", m)
|
||||
}
|
||||
}
|
||||
+356
@@ -0,0 +1,356 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package pool
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
appProvider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
|
||||
appRegistry "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1"
|
||||
authApplication "github.com/cs3org/go-cs3apis/cs3/auth/applications/v1beta1"
|
||||
authProvider "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
|
||||
authRegistry "github.com/cs3org/go-cs3apis/cs3/auth/registry/v1beta1"
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
identityGroup "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
|
||||
identityTenant "github.com/cs3org/go-cs3apis/cs3/identity/tenant/v1beta1"
|
||||
identityUser "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
ocmCore "github.com/cs3org/go-cs3apis/cs3/ocm/core/v1beta1"
|
||||
ocmInvite "github.com/cs3org/go-cs3apis/cs3/ocm/invite/v1beta1"
|
||||
ocmProvider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1"
|
||||
permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
|
||||
preferences "github.com/cs3org/go-cs3apis/cs3/preferences/v1beta1"
|
||||
sharingCollaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
sharingLink "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
|
||||
sharingOCM "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
|
||||
storageProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
storageRegistry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1"
|
||||
tx "github.com/cs3org/go-cs3apis/cs3/tx/v1beta1"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/registry"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sercand/kuberesolver/v5"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// grpc go resolver.Register must only be called during initialization time (i.e. in
|
||||
// an init() function), and is not thread-safe.
|
||||
kuberesolver.RegisterInCluster()
|
||||
}
|
||||
|
||||
type Selectable[T any] interface {
|
||||
Next(opts ...Option) (T, error)
|
||||
}
|
||||
|
||||
var selectors sync.Map
|
||||
|
||||
// RemoveSelector removes given id from the selectors map.
|
||||
func RemoveSelector(id string) {
|
||||
selectors.Delete(id)
|
||||
}
|
||||
|
||||
func GetSelector[T any](k string, id string, f func(cc grpc.ClientConnInterface) T, options ...Option) *Selector[T] {
|
||||
existingSelector, ok := selectors.Load(k + id)
|
||||
if ok {
|
||||
return existingSelector.(*Selector[T])
|
||||
}
|
||||
|
||||
newSelector := &Selector[T]{
|
||||
id: id,
|
||||
clientFactory: f,
|
||||
options: options,
|
||||
}
|
||||
|
||||
selectors.Store(k+id, newSelector)
|
||||
|
||||
return newSelector
|
||||
}
|
||||
|
||||
type Selector[T any] struct {
|
||||
id string
|
||||
clientFactory func(cc grpc.ClientConnInterface) T
|
||||
clientMap sync.Map
|
||||
options []Option
|
||||
}
|
||||
|
||||
func (s *Selector[T]) Next(opts ...Option) (T, error) {
|
||||
options := ClientOptions{
|
||||
registry: registry.GetRegistry(),
|
||||
}
|
||||
|
||||
allOpts := append([]Option{}, s.options...)
|
||||
allOpts = append(allOpts, opts...)
|
||||
|
||||
for _, opt := range allOpts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
target := s.id
|
||||
// if the target is given as a recognized gRPC URI, skip registry lookup
|
||||
// see https://github.com/grpc/grpc/blob/master/doc/naming.md#name-syntax
|
||||
prefix := strings.SplitN(s.id, ":", 2)[0]
|
||||
switch {
|
||||
case prefix == "dns":
|
||||
fallthrough
|
||||
case prefix == "unix":
|
||||
fallthrough
|
||||
case prefix == "kubernetes":
|
||||
// use target as is and skip registry lookup
|
||||
case options.registry != nil:
|
||||
// use service registry to look up address
|
||||
services, err := options.registry.GetService(s.id)
|
||||
if err != nil {
|
||||
return *new(T), fmt.Errorf("%s: %w", s.id, err)
|
||||
}
|
||||
|
||||
nodeAddress, err := registry.GetNodeAddress(services)
|
||||
if err != nil {
|
||||
return *new(T), fmt.Errorf("%s: %w", s.id, err)
|
||||
}
|
||||
target = nodeAddress
|
||||
default:
|
||||
// if no registry is available, use the target as is
|
||||
}
|
||||
|
||||
existingClient, ok := s.clientMap.Load(target)
|
||||
if ok {
|
||||
return existingClient.(T), nil
|
||||
}
|
||||
|
||||
conn, err := NewConn(target, allOpts...)
|
||||
if err != nil {
|
||||
return *new(T), errors.Wrap(err, fmt.Sprintf("could not create connection for %s to %s", s.id, target))
|
||||
}
|
||||
|
||||
newClient := s.clientFactory(conn)
|
||||
s.clientMap.Store(target, newClient)
|
||||
|
||||
return newClient, nil
|
||||
}
|
||||
|
||||
// GatewaySelector returns a Selector[gateway.GatewayAPIClient].
|
||||
func GatewaySelector(id string, options ...Option) (*Selector[gateway.GatewayAPIClient], error) {
|
||||
return GetSelector[gateway.GatewayAPIClient](
|
||||
"GatewaySelector",
|
||||
id,
|
||||
gateway.NewGatewayAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// IdentityUserSelector returns a Selector[identityUser.UserAPIClient].
|
||||
func IdentityUserSelector(id string, options ...Option) (*Selector[identityUser.UserAPIClient], error) {
|
||||
return GetSelector[identityUser.UserAPIClient](
|
||||
"IdentityUserSelector",
|
||||
id,
|
||||
identityUser.NewUserAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// IdentityGroupSelector returns a Selector[identityGroup.GroupAPIClient].
|
||||
func IdentityGroupSelector(id string, options ...Option) (*Selector[identityGroup.GroupAPIClient], error) {
|
||||
return GetSelector[identityGroup.GroupAPIClient](
|
||||
"IdentityGroupSelector",
|
||||
id,
|
||||
identityGroup.NewGroupAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// IdentityTentantSelector returns a Selector[identityTenant.TenantAPIClient].
|
||||
func IdentityTenantSelector(id string, options ...Option) (*Selector[identityTenant.TenantAPIClient], error) {
|
||||
return GetSelector[identityTenant.TenantAPIClient](
|
||||
"IdentityTenantSelector",
|
||||
id,
|
||||
identityTenant.NewTenantAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// StorageProviderSelector returns a Selector[storageProvider.ProviderAPIClient].
|
||||
func StorageProviderSelector(id string, options ...Option) (*Selector[storageProvider.ProviderAPIClient], error) {
|
||||
return GetSelector[storageProvider.ProviderAPIClient](
|
||||
"StorageProviderSelector",
|
||||
id,
|
||||
storageProvider.NewProviderAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// SpacesProviderSelector returns a Selector[storageProvider.SpacesAPIClient].
|
||||
func SpacesProviderSelector(id string, options ...Option) (*Selector[storageProvider.SpacesAPIClient], error) {
|
||||
return GetSelector[storageProvider.SpacesAPIClient](
|
||||
"SpacesProviderSelector",
|
||||
id,
|
||||
storageProvider.NewSpacesAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// AuthRegistrySelector returns a Selector[authRegistry.RegistryAPIClient].
|
||||
func AuthRegistrySelector(id string, options ...Option) (*Selector[authRegistry.RegistryAPIClient], error) {
|
||||
return GetSelector[authRegistry.RegistryAPIClient](
|
||||
"AuthRegistrySelector",
|
||||
id,
|
||||
authRegistry.NewRegistryAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// AuthProviderSelector returns a Selector[authProvider.RegistryAPIClient].
|
||||
func AuthProviderSelector(id string, options ...Option) (*Selector[authProvider.ProviderAPIClient], error) {
|
||||
return GetSelector[authProvider.ProviderAPIClient](
|
||||
"AuthProviderSelector",
|
||||
id,
|
||||
authProvider.NewProviderAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// AuthApplicationSelector returns a Selector[authApplication.ApplicationsAPIClient].
|
||||
func AuthApplicationSelector(id string, options ...Option) (*Selector[authApplication.ApplicationsAPIClient], error) {
|
||||
return GetSelector[authApplication.ApplicationsAPIClient](
|
||||
"AuthApplicationSelector",
|
||||
id,
|
||||
authApplication.NewApplicationsAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// SharingCollaborationSelector returns a Selector[sharingCollaboration.ApplicationsAPIClient].
|
||||
func SharingCollaborationSelector(id string, options ...Option) (*Selector[sharingCollaboration.CollaborationAPIClient], error) {
|
||||
return GetSelector[sharingCollaboration.CollaborationAPIClient](
|
||||
"SharingCollaborationSelector",
|
||||
id,
|
||||
sharingCollaboration.NewCollaborationAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// SharingOCMSelector returns a Selector[sharingOCM.OcmAPIClient].
|
||||
func SharingOCMSelector(id string, options ...Option) (*Selector[sharingOCM.OcmAPIClient], error) {
|
||||
return GetSelector[sharingOCM.OcmAPIClient](
|
||||
"SharingOCMSelector",
|
||||
id,
|
||||
sharingOCM.NewOcmAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// SharingLinkSelector returns a Selector[sharingLink.LinkAPIClient].
|
||||
func SharingLinkSelector(id string, options ...Option) (*Selector[sharingLink.LinkAPIClient], error) {
|
||||
return GetSelector[sharingLink.LinkAPIClient](
|
||||
"SharingLinkSelector",
|
||||
id,
|
||||
sharingLink.NewLinkAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// PreferencesSelector returns a Selector[preferences.PreferencesAPIClient].
|
||||
func PreferencesSelector(id string, options ...Option) (*Selector[preferences.PreferencesAPIClient], error) {
|
||||
return GetSelector[preferences.PreferencesAPIClient](
|
||||
"PreferencesSelector",
|
||||
id,
|
||||
preferences.NewPreferencesAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// PermissionsSelector returns a Selector[permissions.PermissionsAPIClient].
|
||||
func PermissionsSelector(id string, options ...Option) (*Selector[permissions.PermissionsAPIClient], error) {
|
||||
return GetSelector[permissions.PermissionsAPIClient](
|
||||
"PermissionsSelector",
|
||||
id,
|
||||
permissions.NewPermissionsAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// AppRegistrySelector returns a Selector[appRegistry.RegistryAPIClient].
|
||||
func AppRegistrySelector(id string, options ...Option) (*Selector[appRegistry.RegistryAPIClient], error) {
|
||||
return GetSelector[appRegistry.RegistryAPIClient](
|
||||
"AppRegistrySelector",
|
||||
id,
|
||||
appRegistry.NewRegistryAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// AppProviderSelector returns a Selector[appProvider.ProviderAPIClient].
|
||||
func AppProviderSelector(id string, options ...Option) (*Selector[appProvider.ProviderAPIClient], error) {
|
||||
return GetSelector[appProvider.ProviderAPIClient](
|
||||
"AppProviderSelector",
|
||||
id,
|
||||
appProvider.NewProviderAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// StorageRegistrySelector returns a Selector[storageRegistry.RegistryAPIClient].
|
||||
func StorageRegistrySelector(id string, options ...Option) (*Selector[storageRegistry.RegistryAPIClient], error) {
|
||||
return GetSelector[storageRegistry.RegistryAPIClient](
|
||||
"StorageRegistrySelector",
|
||||
id,
|
||||
storageRegistry.NewRegistryAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// OCMProviderSelector returns a Selector[storageRegistry.RegistryAPIClient].
|
||||
func OCMProviderSelector(id string, options ...Option) (*Selector[ocmProvider.ProviderAPIClient], error) {
|
||||
return GetSelector[ocmProvider.ProviderAPIClient](
|
||||
"OCMProviderSelector",
|
||||
id,
|
||||
ocmProvider.NewProviderAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// OCMCoreSelector returns a Selector[ocmCore.OcmCoreAPIClient].
|
||||
func OCMCoreSelector(id string, options ...Option) (*Selector[ocmCore.OcmCoreAPIClient], error) {
|
||||
return GetSelector[ocmCore.OcmCoreAPIClient](
|
||||
"OCMCoreSelector",
|
||||
id,
|
||||
ocmCore.NewOcmCoreAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// OCMInviteSelector returns a Selector[ocmInvite.InviteAPIClient].
|
||||
func OCMInviteSelector(id string, options ...Option) (*Selector[ocmInvite.InviteAPIClient], error) {
|
||||
return GetSelector[ocmInvite.InviteAPIClient](
|
||||
"OCMInviteSelector",
|
||||
id,
|
||||
ocmInvite.NewInviteAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
|
||||
// TXSelector returns a Selector[tx.TxAPIClient].
|
||||
func TXSelector(id string, options ...Option) (*Selector[tx.TxAPIClient], error) {
|
||||
return GetSelector[tx.TxAPIClient](
|
||||
"TXSelector",
|
||||
id,
|
||||
tx.NewTxAPIClient,
|
||||
options...,
|
||||
), nil
|
||||
}
|
||||
Reference in New Issue
Block a user