Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
+24
View File
@@ -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
View File
@@ -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
}
@@ -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
}
@@ -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()
}
@@ -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
}
@@ -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
}
}
@@ -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)
}
}
@@ -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
}