From 4f26424db666d923f2201b28ed197621faa44b71 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Thu, 8 Jun 2023 12:41:04 +0200 Subject: [PATCH] [full-ci] enhancement: use reva client pool selectors (#6452) * enhancement: use reva client pool selectors register mock service to registry and pass tests * enhancement: bump reva * Fix a couple of linter issues --------- Co-authored-by: Ralf Haferkamp --- .drone.star | 2 +- changelog/unreleased/client-selector-pool.md | 8 + go.mod | 2 +- go.sum | 4 +- ocis-pkg/registry/register.go | 47 ++ .../register_test.go} | 2 +- ocis-pkg/registry/registry.go | 22 +- ocis-pkg/registry/service.go | 83 +++ ocis-pkg/service/external/external.go | 120 ---- ocis-pkg/shared/reva.go | 6 +- services/app-provider/pkg/command/server.go | 26 +- services/app-registry/pkg/command/server.go | 26 +- .../app-registry/pkg/revaconfig/config.go | 3 +- services/auth-basic/pkg/command/server.go | 28 +- services/auth-basic/pkg/revaconfig/config.go | 4 +- services/auth-bearer/pkg/command/server.go | 26 +- services/auth-bearer/pkg/revaconfig/config.go | 5 +- services/auth-machine/pkg/command/server.go | 26 +- services/frontend/pkg/command/server.go | 27 +- services/gateway/pkg/command/server.go | 26 +- .../pkg/config/defaults/defaultconfig.go | 20 +- services/gateway/pkg/revaconfig/config.go | 3 +- services/graph/pkg/identity/cs3.go | 31 +- services/graph/pkg/server/http/server.go | 9 +- .../graph/pkg/service/v0/application_test.go | 20 +- .../pkg/service/v0/approleassignments_test.go | 18 +- services/graph/pkg/service/v0/driveitems.go | 33 +- .../graph/pkg/service/v0/driveitems_test.go | 20 +- services/graph/pkg/service/v0/drives.go | 38 +- .../pkg/service/v0/educationclasses_test.go | 22 +- .../pkg/service/v0/educationschools_test.go | 17 +- .../graph/pkg/service/v0/educationuser.go | 14 +- .../pkg/service/v0/educationuser_test.go | 20 +- services/graph/pkg/service/v0/graph.go | 8 +- .../graph/pkg/service/v0/graph_suite_test.go | 14 + services/graph/pkg/service/v0/graph_test.go | 15 +- services/graph/pkg/service/v0/groups_test.go | 22 +- services/graph/pkg/service/v0/option.go | 9 +- services/graph/pkg/service/v0/password.go | 7 +- .../graph/pkg/service/v0/password_test.go | 14 +- services/graph/pkg/service/v0/personaldata.go | 34 +- services/graph/pkg/service/v0/service.go | 17 +- services/graph/pkg/service/v0/tags.go | 20 +- services/graph/pkg/service/v0/users.go | 24 +- services/graph/pkg/service/v0/users_test.go | 24 +- services/groups/pkg/command/server.go | 28 +- services/notifications/pkg/command/server.go | 8 +- .../pkg/service/notification_suite_test.go | 14 + services/notifications/pkg/service/service.go | 27 +- .../notifications/pkg/service/service_test.go | 58 +- services/notifications/pkg/service/shares.go | 16 +- services/notifications/pkg/service/spaces.go | 24 +- services/proxy/pkg/command/server.go | 14 +- services/proxy/pkg/middleware/create_home.go | 35 +- services/proxy/pkg/middleware/options.go | 11 +- .../proxy/pkg/middleware/public_share_auth.go | 18 +- .../pkg/middleware/public_share_auth_test.go | 38 +- services/proxy/pkg/user/backend/backend.go | 7 - services/proxy/pkg/user/backend/cs3.go | 23 +- services/proxy/pkg/userroles/defaultrole.go | 4 +- services/proxy/pkg/userroles/oidcroles.go | 2 +- services/search/pkg/content/cs3.go | 21 +- services/search/pkg/content/tika.go | 5 +- services/search/pkg/search/search.go | 29 +- .../search/pkg/search/search_suite_test.go | 13 + services/search/pkg/search/service.go | 48 +- services/search/pkg/search/service_test.go | 48 +- .../search/pkg/service/grpc/v0/service.go | 9 +- .../pkg/config/defaults/defaultconfig.go | 4 +- services/sharing/pkg/command/server.go | 26 +- .../pkg/config/defaults/defaultconfig.go | 8 +- .../storage-publiclink/pkg/command/server.go | 26 +- services/storage-shares/pkg/command/server.go | 26 +- .../pkg/config/defaults/defaultconfig.go | 2 +- services/storage-system/pkg/command/server.go | 38 +- .../storage-system/pkg/revaconfig/config.go | 18 +- services/storage-users/pkg/command/server.go | 30 +- .../pkg/config/defaults/defaultconfig.go | 6 +- services/storage-users/pkg/event/service.go | 21 +- .../storage-users/pkg/task/task_suite_test.go | 13 + services/storage-users/pkg/task/trash_bin.go | 18 +- .../storage-users/pkg/task/trash_bin_test.go | 68 ++- services/thumbnails/pkg/server/grpc/server.go | 11 +- .../thumbnails/pkg/service/grpc/v0/option.go | 10 +- .../thumbnails/pkg/service/grpc/v0/service.go | 21 +- .../thumbnails/pkg/thumbnail/imgsource/cs3.go | 20 +- services/userlog/pkg/command/server.go | 8 +- services/userlog/pkg/server/http/option.go | 9 +- services/userlog/pkg/server/http/server.go | 2 +- services/userlog/pkg/service/conversion.go | 15 +- services/userlog/pkg/service/http.go | 2 +- services/userlog/pkg/service/options.go | 9 +- services/userlog/pkg/service/service.go | 58 +- .../userlog/pkg/service/service_suit_test.go | 13 + services/userlog/pkg/service/service_test.go | 25 +- services/users/pkg/command/server.go | 28 +- .../web/pkg/config/defaults/defaultconfig.go | 2 +- services/web/pkg/server/http/server.go | 5 +- services/web/pkg/service/v0/branding.go | 16 +- services/web/pkg/service/v0/option.go | 15 +- services/web/pkg/service/v0/service.go | 21 +- services/webdav/pkg/service/v0/service.go | 25 +- .../reva/v2/cmd/revad/runtime/option.go | 2 +- .../reva/v2/cmd/revad/runtime/runtime.go | 18 +- .../grpc/services/gateway/authprovider.go | 1 + .../publicstorageprovider.go | 94 ++- .../sharesstorageprovider.go | 145 ++++- .../http/services/archiver/handler.go | 30 +- .../http/services/owncloud/ocdav/copy.go | 44 +- .../http/services/owncloud/ocdav/dav.go | 26 +- .../http/services/owncloud/ocdav/delete.go | 11 +- .../http/services/owncloud/ocdav/get.go | 12 +- .../http/services/owncloud/ocdav/head.go | 11 +- .../http/services/owncloud/ocdav/locks.go | 28 +- .../http/services/owncloud/ocdav/meta.go | 9 +- .../http/services/owncloud/ocdav/mkcol.go | 17 +- .../http/services/owncloud/ocdav/move.go | 25 +- .../http/services/owncloud/ocdav/ocdav.go | 38 +- .../owncloud/ocdav/propfind/propfind.go | 20 +- .../http/services/owncloud/ocdav/proppatch.go | 27 +- .../http/services/owncloud/ocdav/put.go | 16 +- .../http/services/owncloud/ocdav/report.go | 8 +- .../owncloud/ocdav/spacelookup/spacelookup.go | 12 +- .../http/services/owncloud/ocdav/spaces.go | 5 +- .../http/services/owncloud/ocdav/tpc.go | 37 +- .../http/services/owncloud/ocdav/trashbin.go | 38 +- .../http/services/owncloud/ocdav/tus.go | 13 +- .../http/services/owncloud/ocdav/versions.go | 18 +- .../http/services/owncloud/ocdav/webdav.go | 5 +- .../services/owncloud/ocs/conversions/main.go | 2 +- .../reva/v2/pkg/eosclient/eosgrpc/eoshttp.go | 6 + .../cs3org/reva/v2/pkg/micro/ocdav/option.go | 9 +- .../cs3org/reva/v2/pkg/micro/ocdav/service.go | 9 +- .../ocm/share/manager/nextcloud/nextcloud.go | 1 - .../cs3org/reva/v2/pkg/registry/config.go | 53 -- .../reva/v2/pkg/registry/memory/memory.go | 81 --- .../reva/v2/pkg/registry/memory/node.go | 44 -- .../reva/v2/pkg/registry/memory/service.go | 70 --- .../cs3org/reva/v2/pkg/registry/registry.go | 52 +- .../reva/v2/pkg/rgrpc/todo/pool/client.go | 155 +++++ .../reva/v2/pkg/rgrpc/todo/pool/connection.go | 102 ++++ .../reva/v2/pkg/rgrpc/todo/pool/option.go | 78 +++ .../reva/v2/pkg/rgrpc/todo/pool/pool.go | 560 ------------------ .../reva/v2/pkg/rgrpc/todo/pool/selector.go | 315 ++++++++++ .../v2/pkg/share/manager/jsoncs3/jsoncs3.go | 167 ++++-- .../jsoncs3/providercache/providercache.go | 39 +- .../receivedsharecache/receivedsharecache.go | 33 +- .../manager/jsoncs3/sharecache/sharecache.go | 35 +- .../reva/v2/pkg/storage/cache/filemetadata.go | 2 +- .../utils/decomposedfs/decomposedfs.go | 5 +- .../storage/utils/decomposedfs/node/node.go | 1 - .../utils/decomposedfs/spacepermissions.go | 16 +- .../storage/utils/downloader/downloader.go | 17 +- .../reva/v2/pkg/storage/utils/metadata/cs3.go | 48 +- .../v2/pkg/storage/utils/walker/walker.go | 19 +- .../cs3org/reva/v2/pkg/utils/utils.go | 5 - vendor/modules.txt | 5 +- 157 files changed, 2834 insertions(+), 1890 deletions(-) create mode 100644 changelog/unreleased/client-selector-pool.md create mode 100644 ocis-pkg/registry/register.go rename ocis-pkg/{service/external/external_test.go => registry/register_test.go} (99%) create mode 100644 ocis-pkg/registry/service.go delete mode 100644 ocis-pkg/service/external/external.go delete mode 100644 vendor/github.com/cs3org/reva/v2/pkg/registry/config.go delete mode 100644 vendor/github.com/cs3org/reva/v2/pkg/registry/memory/memory.go delete mode 100644 vendor/github.com/cs3org/reva/v2/pkg/registry/memory/node.go delete mode 100644 vendor/github.com/cs3org/reva/v2/pkg/registry/memory/service.go create mode 100644 vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/client.go create mode 100644 vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/connection.go create mode 100644 vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/option.go create mode 100644 vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/selector.go diff --git a/.drone.star b/.drone.star index c9b608b4ab0..cefd1492da6 100644 --- a/.drone.star +++ b/.drone.star @@ -2144,7 +2144,7 @@ def ocisServer(storage, accounts_hash_difficulty = 4, volumes = [], depends_on = "IDM_ADMIN_PASSWORD": "admin", # override the random admin password from `ocis init` "FRONTEND_SEARCH_MIN_LENGTH": "2", "GATEWAY_GRPC_ADDR": "0.0.0.0:9142", # make gateway available to wopi server - "APP_PROVIDER_EXTERNAL_ADDR": "127.0.0.1:9164", + "APP_PROVIDER_EXTERNAL_ADDR": "com.owncloud.api.app-provider", "APP_PROVIDER_DRIVER": "wopi", "APP_PROVIDER_WOPI_APP_NAME": "FakeOffice", "APP_PROVIDER_WOPI_APP_URL": "http://fakeoffice:8080", diff --git a/changelog/unreleased/client-selector-pool.md b/changelog/unreleased/client-selector-pool.md new file mode 100644 index 00000000000..975c9928eef --- /dev/null +++ b/changelog/unreleased/client-selector-pool.md @@ -0,0 +1,8 @@ +Enhancement: Use reva client selectors + +Use reva client selectors instead of the static clients, this introduces the ocis service registry in reva. +The service discovery now resolves reva services by name and the client selectors pick a random registered service node. + +https://github.com/owncloud/ocis/pull/6452 +https://github.com/cs3org/reva/pull/3939 +https://github.com/cs3org/reva/pull/3953 diff --git a/go.mod b/go.mod index 4c8076bc333..7ec2524a42e 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/coreos/go-oidc v2.2.1+incompatible github.com/coreos/go-oidc/v3 v3.6.0 github.com/cs3org/go-cs3apis v0.0.0-20230516150832-730ac860c71d - github.com/cs3org/reva/v2 v2.14.0 + github.com/cs3org/reva/v2 v2.14.1-0.20230607220921-238a03c2f795 github.com/disintegration/imaging v1.6.2 github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e github.com/egirna/icap-client v0.1.1 diff --git a/go.sum b/go.sum index e0cfe0ea594..f7e567be78a 100644 --- a/go.sum +++ b/go.sum @@ -629,8 +629,8 @@ github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4= github.com/crewjam/saml v0.4.13 h1:TYHggH/hwP7eArqiXSJUvtOPNzQDyQ7vwmwEqlFWhMc= github.com/crewjam/saml v0.4.13/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA= -github.com/cs3org/reva/v2 v2.14.0 h1:X5da4chnEPzqUb76y/DDDawFloRAG7Gy/BMpeYh7vu8= -github.com/cs3org/reva/v2 v2.14.0/go.mod h1:vMQqSn30fEPHO/GKC2WmGimlOPqvfSy4gdhRSpbvrWc= +github.com/cs3org/reva/v2 v2.14.1-0.20230607220921-238a03c2f795 h1:uyzA03PcmG7mjd+3KJrkws0IXuXQCvHEn25xXBmO2QI= +github.com/cs3org/reva/v2 v2.14.1-0.20230607220921-238a03c2f795/go.mod h1:E32krZG159YflDSjDWfx/QGIC2529PS5LiPnGNHu3d0= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= diff --git a/ocis-pkg/registry/register.go b/ocis-pkg/registry/register.go new file mode 100644 index 00000000000..e4e55837f78 --- /dev/null +++ b/ocis-pkg/registry/register.go @@ -0,0 +1,47 @@ +package registry + +import ( + "context" + "time" + + "github.com/owncloud/ocis/v2/ocis-pkg/log" + mRegistry "go-micro.dev/v4/registry" +) + +// RegisterService publishes an arbitrary endpoint to the service-registry. This allows querying nodes of +// non-micro services like reva. No health-checks are done, thus the caller is responsible for canceling. +func RegisterService(ctx context.Context, service *mRegistry.Service, logger log.Logger) error { + registry := GetRegistry() + node := service.Nodes[0] + + logger.Info().Msgf("registering external service %v@%v", node.Id, node.Address) + + rOpts := []mRegistry.RegisterOption{mRegistry.RegisterTTL(time.Minute)} + if err := registry.Register(service, rOpts...); err != nil { + logger.Fatal().Err(err).Msgf("Registration error for external service %v", service.Name) + } + + t := time.NewTicker(time.Second * 30) + + go func() { + for { + select { + case <-t.C: + logger.Debug().Interface("service", service).Msg("refreshing external service-registration") + err := registry.Register(service, rOpts...) + if err != nil { + logger.Error().Err(err).Msgf("registration error for external service %v", service.Name) + } + case <-ctx.Done(): + logger.Debug().Interface("service", service).Msg("unregistering") + t.Stop() + err := registry.Deregister(service) + if err != nil { + logger.Err(err).Msgf("Error unregistering external service %v", service.Name) + } + } + } + }() + + return nil +} diff --git a/ocis-pkg/service/external/external_test.go b/ocis-pkg/registry/register_test.go similarity index 99% rename from ocis-pkg/service/external/external_test.go rename to ocis-pkg/registry/register_test.go index 637d8601cc3..29167004253 100644 --- a/ocis-pkg/service/external/external_test.go +++ b/ocis-pkg/registry/register_test.go @@ -1,4 +1,4 @@ -package external +package registry // //import ( diff --git a/ocis-pkg/registry/registry.go b/ocis-pkg/registry/registry.go index 6f048e44889..6b4ccda0159 100644 --- a/ocis-pkg/registry/registry.go +++ b/ocis-pkg/registry/registry.go @@ -6,14 +6,14 @@ import ( "sync" "time" + rRegistry "github.com/cs3org/reva/v2/pkg/registry" consulr "github.com/go-micro/plugins/v4/registry/consul" etcdr "github.com/go-micro/plugins/v4/registry/etcd" kubernetesr "github.com/go-micro/plugins/v4/registry/kubernetes" mdnsr "github.com/go-micro/plugins/v4/registry/mdns" memr "github.com/go-micro/plugins/v4/registry/memory" natsr "github.com/go-micro/plugins/v4/registry/nats" - - "go-micro.dev/v4/registry" + mRegistry "go-micro.dev/v4/registry" "go-micro.dev/v4/registry/cache" ) @@ -25,7 +25,7 @@ const ( var ( once sync.Once regPlugin string - reg registry.Registry + reg mRegistry.Registry ) func Configure(plugin string) { @@ -37,7 +37,7 @@ func Configure(plugin string) { // GetRegistry returns a configured micro registry based on Micro env vars. // It defaults to mDNS, so mind that systems with mDNS disabled by default (i.e SUSE) will have a hard time // and it needs to explicitly use etcd. Os awareness for providing a working registry out of the box should be done. -func GetRegistry() registry.Registry { +func GetRegistry() mRegistry.Registry { once.Do(func() { addresses := strings.Split(os.Getenv(registryAddressEnv), ",") // prefer env of setting from Configure() @@ -49,29 +49,33 @@ func GetRegistry() registry.Registry { switch plugin { case "nats": reg = natsr.NewRegistry( - registry.Addrs(addresses...), + mRegistry.Addrs(addresses...), ) case "kubernetes": reg = kubernetesr.NewRegistry( - registry.Addrs(addresses...), + mRegistry.Addrs(addresses...), ) case "etcd": reg = etcdr.NewRegistry( - registry.Addrs(addresses...), + mRegistry.Addrs(addresses...), ) case "consul": reg = consulr.NewRegistry( - registry.Addrs(addresses...), + mRegistry.Addrs(addresses...), ) case "memory": reg = memr.NewRegistry() default: reg = mdnsr.NewRegistry() } + // No cache needed for in-memory registry if plugin != "memory" { - reg = cache.New(reg, cache.WithTTL(20*time.Second)) + reg = cache.New(reg, cache.WithTTL(30*time.Second)) } + + // fixme: lazy initialization of reva registry, needs refactor to a explicit call per service + _ = rRegistry.Init(reg) }) // always use cached registry to prevent registry // lookup for every request diff --git a/ocis-pkg/registry/service.go b/ocis-pkg/registry/service.go new file mode 100644 index 00000000000..da80a891223 --- /dev/null +++ b/ocis-pkg/registry/service.go @@ -0,0 +1,83 @@ +package registry + +import ( + "fmt" + "net" + "strconv" + "strings" + + mRegistry "go-micro.dev/v4/registry" + "go-micro.dev/v4/util/addr" +) + +func BuildGRPCService(serviceID, uuid, address string, version string) *mRegistry.Service { + var host string + var port int + + parts := strings.Split(address, ":") + if len(parts) > 1 { + host = strings.Join(parts[:len(parts)-1], ":") + port, _ = strconv.Atoi(parts[len(parts)-1]) + } else { + host = parts[0] + } + + addr, err := addr.Extract(host) + if err != nil { + addr = host + } + + node := &mRegistry.Node{ + Id: serviceID + "-" + uuid, + Address: net.JoinHostPort(addr, fmt.Sprint(port)), + Metadata: make(map[string]string), + } + + node.Metadata["registry"] = GetRegistry().String() + node.Metadata["server"] = "grpc" + node.Metadata["transport"] = "grpc" + node.Metadata["protocol"] = "grpc" + + return &mRegistry.Service{ + Name: serviceID, + Version: version, + Nodes: []*mRegistry.Node{node}, + Endpoints: make([]*mRegistry.Endpoint, 0), + } +} + +func BuildHTTPService(serviceID, uuid, address string, version string) *mRegistry.Service { + var host string + var port int + + parts := strings.Split(address, ":") + if len(parts) > 1 { + host = strings.Join(parts[:len(parts)-1], ":") + port, _ = strconv.Atoi(parts[len(parts)-1]) + } else { + host = parts[0] + } + + addr, err := addr.Extract(host) + if err != nil { + addr = host + } + + node := &mRegistry.Node{ + Id: serviceID + "-" + uuid, + Address: net.JoinHostPort(addr, fmt.Sprint(port)), + Metadata: make(map[string]string), + } + + node.Metadata["registry"] = GetRegistry().String() + node.Metadata["server"] = "http" + node.Metadata["transport"] = "http" + node.Metadata["protocol"] = "http" + + return &mRegistry.Service{ + Name: serviceID, + Version: version, + Nodes: []*mRegistry.Node{node}, + Endpoints: make([]*mRegistry.Endpoint, 0), + } +} diff --git a/ocis-pkg/service/external/external.go b/ocis-pkg/service/external/external.go deleted file mode 100644 index 7b90fe7fb6e..00000000000 --- a/ocis-pkg/service/external/external.go +++ /dev/null @@ -1,120 +0,0 @@ -package external - -import ( - "context" - "time" - - "github.com/owncloud/ocis/v2/ocis-pkg/log" - oregistry "github.com/owncloud/ocis/v2/ocis-pkg/registry" - "go-micro.dev/v4/registry" -) - -// RegisterGRPCEndpoint publishes an arbitrary endpoint to the service-registry. This allows querying nodes of -// non-micro GRPC-services like reva. No health-checks are done, thus the caller is responsible for canceling. -func RegisterGRPCEndpoint(ctx context.Context, serviceID, uuid, addr string, version string, logger log.Logger) error { - node := ®istry.Node{ - Id: serviceID + "-" + uuid, - Address: addr, - Metadata: make(map[string]string), - } - ocisRegistry := oregistry.GetRegistry() - - node.Metadata["registry"] = ocisRegistry.String() - node.Metadata["server"] = "grpc" - node.Metadata["transport"] = "grpc" - node.Metadata["protocol"] = "grpc" - - service := ®istry.Service{ - Name: serviceID, - Version: version, - Nodes: []*registry.Node{node}, - Endpoints: make([]*registry.Endpoint, 0), - } - - logger.Info().Msgf("registering external service %v@%v", node.Id, node.Address) - - rOpts := []registry.RegisterOption{registry.RegisterTTL(time.Minute)} - if err := ocisRegistry.Register(service, rOpts...); err != nil { - logger.Fatal().Err(err).Msgf("Registration error for external service %v", serviceID) - } - - t := time.NewTicker(time.Second * 30) - - go func() { - for { - select { - case <-t.C: - logger.Debug().Interface("service", service).Msg("refreshing external service-registration") - err := ocisRegistry.Register(service, rOpts...) - if err != nil { - logger.Error().Err(err).Msgf("registration error for external service %v", serviceID) - } - case <-ctx.Done(): - logger.Debug().Interface("service", service).Msg("unregistering") - t.Stop() - err := ocisRegistry.Deregister(service) - if err != nil { - logger.Err(err).Msgf("Error unregistering external service %v", serviceID) - } - - } - } - }() - - return nil -} - -// RegisterHTTPEndpoint publishes an arbitrary endpoint to the service-registry. This allows querying nodes of -// non-micro HTTP-services like reva. No health-checks are done, thus the caller is responsible for canceling. -func RegisterHTTPEndpoint(ctx context.Context, serviceID, uuid, addr string, version string, logger log.Logger) error { - node := ®istry.Node{ - Id: serviceID + "-" + uuid, - Address: addr, - Metadata: make(map[string]string), - } - ocisRegistry := oregistry.GetRegistry() - - node.Metadata["registry"] = ocisRegistry.String() - node.Metadata["server"] = "http" - node.Metadata["transport"] = "http" - node.Metadata["protocol"] = "http" - - service := ®istry.Service{ - Name: serviceID, - Version: version, - Nodes: []*registry.Node{node}, - Endpoints: make([]*registry.Endpoint, 0), - } - - logger.Info().Msgf("registering external service %v@%v", node.Id, node.Address) - - rOpts := []registry.RegisterOption{registry.RegisterTTL(time.Minute)} - if err := ocisRegistry.Register(service, rOpts...); err != nil { - logger.Fatal().Err(err).Msgf("Registration error for external service %v", serviceID) - } - - t := time.NewTicker(time.Second * 30) - - go func() { - for { - select { - case <-t.C: - logger.Debug().Interface("service", service).Msg("refreshing external service-registration") - err := ocisRegistry.Register(service, rOpts...) - if err != nil { - logger.Error().Err(err).Msgf("registration error for external service %v", serviceID) - } - case <-ctx.Done(): - logger.Debug().Interface("service", service).Msg("unregistering") - t.Stop() - err := ocisRegistry.Deregister(service) - if err != nil { - logger.Err(err).Msgf("Error unregistering external service %v", serviceID) - } - - } - } - }() - - return nil -} diff --git a/ocis-pkg/shared/reva.go b/ocis-pkg/shared/reva.go index cc38370903e..a4a9f55553c 100644 --- a/ocis-pkg/shared/reva.go +++ b/ocis-pkg/shared/reva.go @@ -1,9 +1,11 @@ package shared -import "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" +import ( + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" +) var defaultRevaConfig = Reva{ - Address: "127.0.0.1:9142", + Address: "com.owncloud.api.gateway", } func DefaultRevaConfig() *Reva { diff --git a/services/app-provider/pkg/command/server.go b/services/app-provider/pkg/command/server.go index a7fc216d07e..902c293865b 100644 --- a/services/app-provider/pkg/command/server.go +++ b/services/app-provider/pkg/command/server.go @@ -10,7 +10,7 @@ import ( "github.com/gofrs/uuid" "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/sync" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/app-provider/pkg/config" @@ -42,12 +42,16 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + gr.Add(func() error { + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.AppProviderConfigFromStruct(cfg) + reg := registry.GetRegistry() - rcfg := revaconfig.AppProviderConfigFromStruct(cfg) + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) - gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) return nil }, func(err error) { logger.Error(). @@ -77,15 +81,9 @@ func Server(cfg *config.Config) *cli.Command { sync.Trap(&gr, cancel) } - if err := external.RegisterGRPCEndpoint( - ctx, - cfg.GRPC.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.GRPC.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the grpc endpoint") + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") } return gr.Run() diff --git a/services/app-registry/pkg/command/server.go b/services/app-registry/pkg/command/server.go index aa230570fbe..3fae646d8d8 100644 --- a/services/app-registry/pkg/command/server.go +++ b/services/app-registry/pkg/command/server.go @@ -10,7 +10,7 @@ import ( "github.com/gofrs/uuid" "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/app-registry/pkg/config" "github.com/owncloud/ocis/v2/services/app-registry/pkg/config/parser" @@ -41,12 +41,16 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + gr.Add(func() error { + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.AppRegistryConfigFromStruct(cfg, logger) + reg := registry.GetRegistry() - rcfg := revaconfig.AppRegistryConfigFromStruct(cfg, logger) + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) - gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) return nil }, func(err error) { logger.Error(). @@ -72,15 +76,9 @@ func Server(cfg *config.Config) *cli.Command { cancel() }) - if err := external.RegisterGRPCEndpoint( - ctx, - cfg.GRPC.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.GRPC.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the grpc endpoint") + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") } return gr.Run() diff --git a/services/app-registry/pkg/revaconfig/config.go b/services/app-registry/pkg/revaconfig/config.go index 8698f2fb796..68215a32801 100644 --- a/services/app-registry/pkg/revaconfig/config.go +++ b/services/app-registry/pkg/revaconfig/config.go @@ -1,9 +1,8 @@ package revaconfig import ( - "github.com/owncloud/ocis/v2/ocis-pkg/log" - "github.com/mitchellh/mapstructure" + "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/app-registry/pkg/config" ) diff --git a/services/auth-basic/pkg/command/server.go b/services/auth-basic/pkg/command/server.go index 4dc4ffba2a0..f137ae5b51b 100644 --- a/services/auth-basic/pkg/command/server.go +++ b/services/auth-basic/pkg/command/server.go @@ -11,7 +11,7 @@ import ( "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" "github.com/owncloud/ocis/v2/ocis-pkg/ldap" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/sync" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/auth-basic/pkg/config" @@ -43,10 +43,6 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") - - rcfg := revaconfig.AuthBasicConfigFromStruct(cfg) - // the reva runtime calls os.Exit in the case of a failure and there is no way for the oCIS // runtime to catch it and restart a reva service. Therefore we need to ensure the service has // everything it needs, before starting the service. @@ -60,7 +56,15 @@ func Server(cfg *config.Config) *cli.Command { } gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.AuthBasicConfigFromStruct(cfg) + reg := registry.GetRegistry() + + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) + return nil }, func(err error) { logger.Error(). @@ -90,15 +94,9 @@ func Server(cfg *config.Config) *cli.Command { sync.Trap(&gr, cancel) } - if err := external.RegisterGRPCEndpoint( - ctx, - cfg.GRPC.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.GRPC.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the grpc endpoint") + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") } return gr.Run() diff --git a/services/auth-basic/pkg/revaconfig/config.go b/services/auth-basic/pkg/revaconfig/config.go index 7fe28fd675a..ad393269496 100644 --- a/services/auth-basic/pkg/revaconfig/config.go +++ b/services/auth-basic/pkg/revaconfig/config.go @@ -1,6 +1,8 @@ package revaconfig -import "github.com/owncloud/ocis/v2/services/auth-basic/pkg/config" +import ( + "github.com/owncloud/ocis/v2/services/auth-basic/pkg/config" +) // AuthBasicConfigFromStruct will adapt an oCIS config struct into a reva mapstructure to start a reva service. func AuthBasicConfigFromStruct(cfg *config.Config) map[string]interface{} { diff --git a/services/auth-bearer/pkg/command/server.go b/services/auth-bearer/pkg/command/server.go index 4c181bfab9c..50d97b7857c 100644 --- a/services/auth-bearer/pkg/command/server.go +++ b/services/auth-bearer/pkg/command/server.go @@ -10,7 +10,7 @@ import ( "github.com/gofrs/uuid" "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/sync" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/auth-bearer/pkg/config" @@ -42,12 +42,16 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + gr.Add(func() error { + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.AuthBearerConfigFromStruct(cfg) + reg := registry.GetRegistry() - rcfg := revaconfig.AuthBearerConfigFromStruct(cfg) + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) - gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) return nil }, func(err error) { logger.Error(). @@ -77,15 +81,9 @@ func Server(cfg *config.Config) *cli.Command { sync.Trap(&gr, cancel) } - if err := external.RegisterGRPCEndpoint( - ctx, - cfg.GRPC.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.GRPC.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the grpc endpoint") + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") } return gr.Run() diff --git a/services/auth-bearer/pkg/revaconfig/config.go b/services/auth-bearer/pkg/revaconfig/config.go index 6c19622e6a0..82cd79f1b1f 100644 --- a/services/auth-bearer/pkg/revaconfig/config.go +++ b/services/auth-bearer/pkg/revaconfig/config.go @@ -1,6 +1,9 @@ +// Package revaconfig transfers the config struct to reva config map package revaconfig -import "github.com/owncloud/ocis/v2/services/auth-bearer/pkg/config" +import ( + "github.com/owncloud/ocis/v2/services/auth-bearer/pkg/config" +) // AuthBearerConfigFromStruct will adapt an oCIS config struct into a reva mapstructure to start a reva service. func AuthBearerConfigFromStruct(cfg *config.Config) map[string]interface{} { diff --git a/services/auth-machine/pkg/command/server.go b/services/auth-machine/pkg/command/server.go index bf2ad19cca0..16894766491 100644 --- a/services/auth-machine/pkg/command/server.go +++ b/services/auth-machine/pkg/command/server.go @@ -10,7 +10,7 @@ import ( "github.com/gofrs/uuid" "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/sync" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/auth-machine/pkg/config" @@ -42,12 +42,16 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + gr.Add(func() error { + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.AuthMachineConfigFromStruct(cfg) + reg := registry.GetRegistry() - rcfg := revaconfig.AuthMachineConfigFromStruct(cfg) + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) - gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) return nil }, func(err error) { logger.Error(). @@ -77,15 +81,9 @@ func Server(cfg *config.Config) *cli.Command { sync.Trap(&gr, cancel) } - if err := external.RegisterGRPCEndpoint( - ctx, - cfg.GRPC.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.GRPC.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the grpc endpoint") + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") } return gr.Run() diff --git a/services/frontend/pkg/command/server.go b/services/frontend/pkg/command/server.go index 6508799699a..d899fa02756 100644 --- a/services/frontend/pkg/command/server.go +++ b/services/frontend/pkg/command/server.go @@ -10,7 +10,7 @@ import ( "github.com/gofrs/uuid" "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/sync" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/frontend/pkg/config" @@ -42,15 +42,20 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") - - rcfg, err := revaconfig.FrontendConfigFromStruct(cfg) + rCfg, err := revaconfig.FrontendConfigFromStruct(cfg) if err != nil { return err } gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + reg := registry.GetRegistry() + + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) + return nil }, func(err error) { logger.Error(). @@ -80,15 +85,9 @@ func Server(cfg *config.Config) *cli.Command { sync.Trap(&gr, cancel) } - if err := external.RegisterHTTPEndpoint( - ctx, - cfg.HTTP.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.HTTP.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the http endpoint") + httpSvc := registry.BuildHTTPService(cfg.HTTP.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.HTTP.Addr, version.GetString()) + if err := registry.RegisterService(ctx, httpSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the http service") } return gr.Run() diff --git a/services/gateway/pkg/command/server.go b/services/gateway/pkg/command/server.go index f2ddf03afab..37c34c976d8 100644 --- a/services/gateway/pkg/command/server.go +++ b/services/gateway/pkg/command/server.go @@ -10,7 +10,7 @@ import ( "github.com/gofrs/uuid" "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/gateway/pkg/config" "github.com/owncloud/ocis/v2/services/gateway/pkg/config/parser" @@ -41,12 +41,16 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + gr.Add(func() error { + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.GatewayConfigFromStruct(cfg, logger) + reg := registry.GetRegistry() - rcfg := revaconfig.GatewayConfigFromStruct(cfg, logger) + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) - gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) return nil }, func(err error) { logger.Error(). @@ -72,15 +76,9 @@ func Server(cfg *config.Config) *cli.Command { cancel() }) - if err := external.RegisterGRPCEndpoint( - ctx, - cfg.GRPC.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.GRPC.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the grpc endpoint") + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") } return gr.Run() diff --git a/services/gateway/pkg/config/defaults/defaultconfig.go b/services/gateway/pkg/config/defaults/defaultconfig.go index f776c55a218..3e0fd026e53 100644 --- a/services/gateway/pkg/config/defaults/defaultconfig.go +++ b/services/gateway/pkg/config/defaults/defaultconfig.go @@ -52,16 +52,16 @@ func DefaultConfig() *config.Config { FrontendPublicURL: "https://localhost:9200", - AppRegistryEndpoint: "localhost:9242", - AuthBasicEndpoint: "localhost:9146", - AuthMachineEndpoint: "localhost:9166", - GroupsEndpoint: "localhost:9160", - PermissionsEndpoint: "localhost:9191", - SharingEndpoint: "localhost:9150", - StoragePublicLinkEndpoint: "localhost:9178", - StorageSharesEndpoint: "localhost:9154", - StorageUsersEndpoint: "localhost:9157", - UsersEndpoint: "localhost:9144", + AppRegistryEndpoint: "com.owncloud.api.app-registry", + AuthBasicEndpoint: "com.owncloud.api.auth-basic", + AuthMachineEndpoint: "com.owncloud.api.auth-machine", + GroupsEndpoint: "com.owncloud.api.groups", + PermissionsEndpoint: "com.owncloud.api.settings", + SharingEndpoint: "com.owncloud.api.sharing", + StoragePublicLinkEndpoint: "com.owncloud.api.storage-publiclink", + StorageSharesEndpoint: "com.owncloud.api.storage-shares", + StorageUsersEndpoint: "com.owncloud.api.storage-users", + UsersEndpoint: "com.owncloud.api.users", StorageRegistry: config.StorageRegistry{ Driver: "spaces", diff --git a/services/gateway/pkg/revaconfig/config.go b/services/gateway/pkg/revaconfig/config.go index 7654d3ea9db..d9c5df0d9eb 100644 --- a/services/gateway/pkg/revaconfig/config.go +++ b/services/gateway/pkg/revaconfig/config.go @@ -6,9 +6,8 @@ import ( "strings" "time" - "github.com/owncloud/ocis/v2/ocis-pkg/log" - "github.com/cs3org/reva/v2/pkg/utils" + "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/gateway/pkg/config" ) diff --git a/services/graph/pkg/identity/cs3.go b/services/graph/pkg/identity/cs3.go index 1f03170d127..a21ef4bbc9a 100644 --- a/services/graph/pkg/identity/cs3.go +++ b/services/graph/pkg/identity/cs3.go @@ -5,12 +5,12 @@ import ( "net/url" "github.com/CiscoM31/godata" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" cs3group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" cs3user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" libregraph "github.com/owncloud/libre-graph-api-go" - "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/shared" "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode" @@ -21,8 +21,9 @@ var ( ) type CS3 struct { - Config *shared.Reva - Logger *log.Logger + Config *shared.Reva + Logger *log.Logger + GatewaySelector pool.Selectable[gateway.GatewayAPIClient] } // CreateUser implements the Backend Interface. It's currently not supported for the CS3 backend @@ -44,13 +45,13 @@ func (i *CS3) UpdateUser(ctx context.Context, nameOrID string, user libregraph.U func (i *CS3) GetUser(ctx context.Context, userID string, _ *godata.GoDataRequest) (*libregraph.User, error) { logger := i.Logger.SubloggerWithRequestID(ctx) logger.Debug().Str("backend", "cs3").Msg("GetUser") - client, err := pool.GetGatewayServiceClient(i.Config.Address, i.Config.GetRevaOptions()...) + gatewayClient, err := i.GatewaySelector.Next() if err != nil { - logger.Error().Str("backend", "cs3").Err(err).Msg("could not get client") + logger.Error().Str("backend", "cs3").Err(err).Msg("could not get gatewayClient") return nil, errorcode.New(errorcode.ServiceNotAvailable, err.Error()) } - res, err := client.GetUserByClaim(ctx, &cs3user.GetUserByClaimRequest{ + res, err := gatewayClient.GetUserByClaim(ctx, &cs3user.GetUserByClaimRequest{ Claim: "userid", // FIXME add consts to reva Value: userID, }) @@ -73,9 +74,9 @@ func (i *CS3) GetUser(ctx context.Context, userID string, _ *godata.GoDataReques func (i *CS3) GetUsers(ctx context.Context, oreq *godata.GoDataRequest) ([]*libregraph.User, error) { logger := i.Logger.SubloggerWithRequestID(ctx) logger.Debug().Str("backend", "cs3").Msg("GetUsers") - client, err := pool.GetGatewayServiceClient(i.Config.Address, i.Config.GetRevaOptions()...) + gatewayClient, err := i.GatewaySelector.Next() if err != nil { - logger.Error().Str("backend", "cs3").Err(err).Msg("could not get client") + logger.Error().Str("backend", "cs3").Err(err).Msg("could not get gatewayClient") return nil, errorcode.New(errorcode.ServiceNotAvailable, err.Error()) } @@ -84,7 +85,7 @@ func (i *CS3) GetUsers(ctx context.Context, oreq *godata.GoDataRequest) ([]*libr return nil, err } - res, err := client.FindUsers(ctx, &cs3user.FindUsersRequest{ + res, err := gatewayClient.FindUsers(ctx, &cs3user.FindUsersRequest{ // FIXME presence match is currently not implemented, an empty search currently leads to // Unwilling To Perform": Search Error: error parsing filter: (&(objectclass=posixAccount)(|(cn=*)(displayname=*)(mail=*))), error: Present filter match for cn not implemented Filter: search, @@ -114,9 +115,9 @@ func (i *CS3) GetUsers(ctx context.Context, oreq *godata.GoDataRequest) ([]*libr func (i *CS3) GetGroups(ctx context.Context, queryParam url.Values) ([]*libregraph.Group, error) { logger := i.Logger.SubloggerWithRequestID(ctx) logger.Debug().Str("backend", "cs3").Msg("GetGroups") - client, err := pool.GetGatewayServiceClient(i.Config.Address, i.Config.GetRevaOptions()...) + gatewayClient, err := i.GatewaySelector.Next() if err != nil { - logger.Error().Str("backend", "cs3").Err(err).Msg("could not get client") + logger.Error().Str("backend", "cs3").Err(err).Msg("could not get gatewayClient") return nil, errorcode.New(errorcode.ServiceNotAvailable, err.Error()) } @@ -125,7 +126,7 @@ func (i *CS3) GetGroups(ctx context.Context, queryParam url.Values) ([]*libregra search = queryParam.Get("$search") } - res, err := client.FindGroups(ctx, &cs3group.FindGroupsRequest{ + res, err := gatewayClient.FindGroups(ctx, &cs3group.FindGroupsRequest{ // FIXME presence match is currently not implemented, an empty search currently leads to // Unwilling To Perform": Search Error: error parsing filter: (&(objectclass=posixAccount)(|(cn=*)(displayname=*)(mail=*))), error: Present filter match for cn not implemented Filter: search, @@ -161,13 +162,13 @@ func (i *CS3) CreateGroup(ctx context.Context, group libregraph.Group) (*libregr func (i *CS3) GetGroup(ctx context.Context, groupID string, queryParam url.Values) (*libregraph.Group, error) { logger := i.Logger.SubloggerWithRequestID(ctx) logger.Debug().Str("backend", "cs3").Msg("GetGroup") - client, err := pool.GetGatewayServiceClient(i.Config.Address, i.Config.GetRevaOptions()...) + gatewayClient, err := i.GatewaySelector.Next() if err != nil { - logger.Error().Str("backend", "cs3").Err(err).Msg("could not get client") + logger.Error().Str("backend", "cs3").Err(err).Msg("could not get gatewayClient") return nil, errorcode.New(errorcode.ServiceNotAvailable, err.Error()) } - res, err := client.GetGroupByClaim(ctx, &cs3group.GetGroupByClaimRequest{ + res, err := gatewayClient.GetGroupByClaim(ctx, &cs3group.GetGroupByClaimRequest{ Claim: "groupid", // FIXME add consts to reva Value: groupID, }) diff --git a/services/graph/pkg/server/http/server.go b/services/graph/pkg/server/http/server.go index 213cc7577f2..203ac1ee707 100644 --- a/services/graph/pkg/server/http/server.go +++ b/services/graph/pkg/server/http/server.go @@ -17,6 +17,7 @@ import ( ociscrypto "github.com/owncloud/ocis/v2/ocis-pkg/crypto" "github.com/owncloud/ocis/v2/ocis-pkg/keycloak" "github.com/owncloud/ocis/v2/ocis-pkg/middleware" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/service/http" "github.com/owncloud/ocis/v2/ocis-pkg/version" @@ -114,7 +115,7 @@ func Server(opts ...Option) (http.Service, error) { // how do we secure the api? var requireAdminMiddleware func(stdhttp.Handler) stdhttp.Handler var roleService svc.RoleService - var gatewayClient gateway.GatewayAPIClient + var gatewaySelector pool.Selectable[gateway.GatewayAPIClient] grpcClient, err := grpc.NewClient(append(grpc.GetClientOptions(options.Config.GRPCClientTLS), grpc.WithTraceProvider(tracing.TraceProvider))...) if err != nil { return http.Service{}, err @@ -126,9 +127,9 @@ func Server(opts ...Option) (http.Service, error) { account.JWTSecret(options.Config.TokenManager.JWTSecret), )) roleService = settingssvc.NewRoleService("com.owncloud.api.settings", grpcClient) - gatewayClient, err = pool.GetGatewayServiceClient(options.Config.Reva.Address, options.Config.Reva.GetRevaOptions()...) + gatewaySelector, err = pool.GatewaySelector(options.Config.Reva.Address, append(options.Config.Reva.GetRevaOptions(), pool.WithRegistry(registry.GetRegistry()))...) if err != nil { - return http.Service{}, errors.Wrap(err, "could not initialize gateway client") + return http.Service{}, errors.Wrap(err, "could not initialize gateway selector") } } else { middlewares = append(middlewares, graphMiddleware.Token(options.Config.HTTP.APIToken)) @@ -159,7 +160,7 @@ func Server(opts ...Option) (http.Service, error) { svc.EventsPublisher(publisher), svc.WithRoleService(roleService), svc.WithRequireAdminMiddleware(requireAdminMiddleware), - svc.WithGatewayClient(gatewayClient), + svc.WithGatewaySelector(gatewaySelector), svc.WithSearchService(searchsvc.NewSearchProviderService("com.owncloud.api.search", grpcClient)), svc.KeycloakClient(keyCloakClient), svc.EventHistoryClient(hClient), diff --git a/services/graph/pkg/service/v0/application_test.go b/services/graph/pkg/service/v0/application_test.go index 851e2ebed3b..dc136709c77 100644 --- a/services/graph/pkg/service/v0/application_test.go +++ b/services/graph/pkg/service/v0/application_test.go @@ -7,13 +7,13 @@ import ( "net/http" "net/http/httptest" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" + cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" "github.com/go-chi/chi/v5" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" libregraph "github.com/owncloud/libre-graph-api-go" - "github.com/stretchr/testify/mock" - - cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/shared" settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0" @@ -23,6 +23,8 @@ import ( "github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults" identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks" service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0" + "github.com/stretchr/testify/mock" + "google.golang.org/grpc" ) type applicationList struct { @@ -35,6 +37,7 @@ var _ = Describe("Applications", func() { ctx context.Context cfg *config.Config gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] eventsPublisher mocks.Publisher roleService *mocks.RoleService identityBackend *identitymocks.Backend @@ -47,7 +50,16 @@ var _ = Describe("Applications", func() { identityBackend = &identitymocks.Backend{} roleService = &mocks.RoleService{} + + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) rr = httptest.NewRecorder() ctx = context.Background() @@ -62,7 +74,7 @@ var _ = Describe("Applications", func() { _ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...) svc, _ = service.NewService( service.Config(cfg), - service.WithGatewayClient(gatewayClient), + service.WithGatewaySelector(gatewaySelector), service.EventsPublisher(&eventsPublisher), service.WithIdentityBackend(identityBackend), service.WithRoleService(roleService), diff --git a/services/graph/pkg/service/v0/approleassignments_test.go b/services/graph/pkg/service/v0/approleassignments_test.go index 7f37975a54d..b67165c5260 100644 --- a/services/graph/pkg/service/v0/approleassignments_test.go +++ b/services/graph/pkg/service/v0/approleassignments_test.go @@ -8,15 +8,15 @@ import ( "net/http" "net/http/httptest" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" "github.com/go-chi/chi/v5" "github.com/golang/protobuf/ptypes/empty" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/stretchr/testify/mock" - libregraph "github.com/owncloud/libre-graph-api-go" ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/shared" @@ -27,6 +27,8 @@ import ( "github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults" identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks" service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0" + "github.com/stretchr/testify/mock" + "google.golang.org/grpc" ) type assignmentList struct { @@ -39,6 +41,7 @@ var _ = Describe("AppRoleAssignments", func() { ctx context.Context cfg *config.Config gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] eventsPublisher mocks.Publisher roleService *mocks.RoleService identityBackend *identitymocks.Backend @@ -57,7 +60,16 @@ var _ = Describe("AppRoleAssignments", func() { identityBackend = &identitymocks.Backend{} roleService = &mocks.RoleService{} + + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) rr = httptest.NewRecorder() ctx = context.Background() @@ -72,7 +84,7 @@ var _ = Describe("AppRoleAssignments", func() { _ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...) svc, _ = service.NewService( service.Config(cfg), - service.WithGatewayClient(gatewayClient), + service.WithGatewaySelector(gatewaySelector), service.EventsPublisher(&eventsPublisher), service.WithIdentityBackend(identityBackend), service.WithRoleService(roleService), diff --git a/services/graph/pkg/service/v0/driveitems.go b/services/graph/pkg/service/v0/driveitems.go index 3c9d412b62b..5a09f2876ff 100644 --- a/services/graph/pkg/service/v0/driveitems.go +++ b/services/graph/pkg/service/v0/driveitems.go @@ -25,9 +25,14 @@ func (g Graph) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) { g.logger.Info().Msg("Calling GetRootDriveChildren") ctx := r.Context() - client := g.GetGatewayClient() + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + g.logger.Error().Err(err).Msg("could not select next gateway client") + errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, "could not select next gateway client, aborting") + return + } - res, err := client.GetHome(ctx, &storageprovider.GetHomeRequest{}) + res, err := gatewayClient.GetHome(ctx, &storageprovider.GetHomeRequest{}) switch { case err != nil: g.logger.Error().Err(err).Msg("error sending get home grpc request") @@ -43,7 +48,7 @@ func (g Graph) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) { return } - lRes, err := client.ListContainer(ctx, &storageprovider.ListContainerRequest{ + lRes, err := gatewayClient.ListContainer(ctx, &storageprovider.ListContainerRequest{ Ref: &storageprovider.Reference{ Path: res.Path, }, @@ -80,9 +85,12 @@ func (g Graph) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) { } func (g Graph) getDriveItem(ctx context.Context, ref storageprovider.Reference) (*libregraph.DriveItem, error) { - client := g.GetGatewayClient() + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + return nil, err + } - res, err := client.Stat(ctx, &storageprovider.StatRequest{Ref: &ref}) + res, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: &ref}) if err != nil { return nil, err } @@ -94,12 +102,15 @@ func (g Graph) getDriveItem(ctx context.Context, ref storageprovider.Reference) } func (g Graph) getRemoteItem(ctx context.Context, root *storageprovider.ResourceId, baseURL *url.URL) (*libregraph.RemoteItem, error) { - client := g.GetGatewayClient() + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + return nil, err + } ref := &storageprovider.Reference{ ResourceId: root, } - res, err := client.Stat(ctx, &storageprovider.StatRequest{Ref: ref}) + res, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: ref}) if err != nil { return nil, err } @@ -204,8 +215,12 @@ func cs3ResourceToRemoteItem(res *storageprovider.ResourceInfo) (*libregraph.Rem } func (g Graph) getPathForResource(ctx context.Context, id storageprovider.ResourceId) (string, error) { - client := g.GetGatewayClient() - res, err := client.GetPath(ctx, &storageprovider.GetPathRequest{ResourceId: &id}) + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + return "", err + } + + res, err := gatewayClient.GetPath(ctx, &storageprovider.GetPathRequest{ResourceId: &id}) if err != nil { return "", err } diff --git a/services/graph/pkg/service/v0/driveitems_test.go b/services/graph/pkg/service/v0/driveitems_test.go index b830cb14a9c..c907ec67046 100644 --- a/services/graph/pkg/service/v0/driveitems_test.go +++ b/services/graph/pkg/service/v0/driveitems_test.go @@ -8,14 +8,14 @@ import ( "net/http/httptest" "time" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/utils" cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/stretchr/testify/mock" - libregraph "github.com/owncloud/libre-graph-api-go" ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/shared" @@ -24,6 +24,8 @@ import ( "github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults" identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks" service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0" + "github.com/stretchr/testify/mock" + "google.golang.org/grpc" ) type itemsList struct { @@ -36,6 +38,7 @@ var _ = Describe("Driveitems", func() { ctx context.Context cfg *config.Config gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] eventsPublisher mocks.Publisher identityBackend *identitymocks.Backend @@ -49,8 +52,17 @@ var _ = Describe("Driveitems", func() { rr = httptest.NewRecorder() - identityBackend = &identitymocks.Backend{} + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + + identityBackend = &identitymocks.Backend{} newGroup = libregraph.NewGroup() newGroup.SetMembersodataBind([]string{"/users/user1"}) newGroup.SetId("group1") @@ -67,7 +79,7 @@ var _ = Describe("Driveitems", func() { _ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...) svc, _ = service.NewService( service.Config(cfg), - service.WithGatewayClient(gatewayClient), + service.WithGatewaySelector(gatewaySelector), service.EventsPublisher(&eventsPublisher), service.WithIdentityBackend(identityBackend), ) diff --git a/services/graph/pkg/service/v0/drives.go b/services/graph/pkg/service/v0/drives.go index 5cb2a466220..306da720f7b 100644 --- a/services/graph/pkg/service/v0/drives.go +++ b/services/graph/pkg/service/v0/drives.go @@ -261,7 +261,13 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) { return } - client := g.GetGatewayClient() + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + logger.Error().Err(err).Msg("could not select next gateway client") + errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, "could not select next gateway client, aborting") + return + } + drive := libregraph.Drive{} if err := StrictJSONUnmarshal(r.Body, &drive); err != nil { logger.Debug().Err(err).Interface("body", r.Body).Msg("could not create drive: invalid body schema definition") @@ -306,7 +312,7 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) { csr.Owner = us } - resp, err := client.CreateStorageSpace(r.Context(), &csr) + resp, err := gatewayClient.CreateStorageSpace(r.Context(), &csr) if err != nil { logger.Error().Err(err).Msg("could not create drive: transport error") errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) @@ -374,7 +380,12 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) { } root := &rid - client := g.GetGatewayClient() + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + g.logger.Error().Err(err).Msg("could not select next gateway client") + errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, "could not select next gateway client, aborting") + return + } updateSpaceRequest := &storageprovider.UpdateStorageSpaceRequest{ // Prepare the object to apply the diff from. The properties on StorageSpace will overwrite @@ -459,7 +470,7 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) { } logger.Debug().Interface("payload", updateSpaceRequest).Msg("calling update space on backend") - resp, err := client.UpdateStorageSpace(r.Context(), updateSpaceRequest) + resp, err := gatewayClient.UpdateStorageSpace(r.Context(), updateSpaceRequest) if err != nil { logger.Error().Err(err).Msg("could not update drive: transport error") errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "transport error") @@ -581,7 +592,10 @@ func (g Graph) formatDrives(ctx context.Context, baseURL *url.URL, storageSpaces // ListStorageSpacesWithFilters List Storage Spaces using filters func (g Graph) ListStorageSpacesWithFilters(ctx context.Context, filters []*storageprovider.ListStorageSpacesRequest_Filter, unrestricted bool) (*storageprovider.ListStorageSpacesResponse, error) { - client := g.GetGatewayClient() + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + return nil, err + } grpcClient, err := grpc.NewClient(append(grpc.GetClientOptions(g.config.GRPCClientTLS), grpc.WithTraceProvider(gtracing.TraceProvider))...) if err != nil { @@ -615,7 +629,7 @@ func (g Graph) ListStorageSpacesWithFilters(ctx context.Context, filters []*stor }}, Filters: filters, } - res, err := client.ListStorageSpaces(ctx, lReq) + res, err := gatewayClient.ListStorageSpaces(ctx, lReq) return res, err } @@ -858,7 +872,10 @@ func (g Graph) getDriveQuota(ctx context.Context, space *storageprovider.Storage if noQuotaInOpaque { // we have to make a trip to the storage // TODO only if quota property was requested - client := g.GetGatewayClient() + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + return libregraph.Quota{}, err + } req := &gateway.GetQuotaRequest{ Ref: &storageprovider.Reference{ @@ -866,7 +883,8 @@ func (g Graph) getDriveQuota(ctx context.Context, space *storageprovider.Storage Path: ".", }, } - res, err := client.GetQuota(ctx, req) + + res, err := gatewayClient.GetQuota(ctx, req) switch { case err != nil: logger.Error().Err(err).Interface("ref", req.Ref).Msg("could not call GetQuota: transport error") @@ -1041,8 +1059,8 @@ func (g Graph) DeleteDrive(w http.ResponseWriter, r *http.Request) { }, } } - - dRes, err := g.gatewayClient.DeleteStorageSpace(r.Context(), &storageprovider.DeleteStorageSpaceRequest{ + gatewayClient, _ := g.gatewaySelector.Next() + dRes, err := gatewayClient.DeleteStorageSpace(r.Context(), &storageprovider.DeleteStorageSpaceRequest{ Opaque: opaque, Id: &storageprovider.StorageSpaceId{ OpaqueId: storagespace.FormatResourceID(rid), diff --git a/services/graph/pkg/service/v0/educationclasses_test.go b/services/graph/pkg/service/v0/educationclasses_test.go index 50564cf8e99..009d14d456a 100644 --- a/services/graph/pkg/service/v0/educationclasses_test.go +++ b/services/graph/pkg/service/v0/educationclasses_test.go @@ -9,14 +9,14 @@ import ( "net/http" "net/http/httptest" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" "github.com/go-chi/chi/v5" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/test-go/testify/mock" - libregraph "github.com/owncloud/libre-graph-api-go" "github.com/owncloud/ocis/v2/ocis-pkg/log" ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" @@ -27,6 +27,8 @@ import ( identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks" service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0" "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode" + "github.com/test-go/testify/mock" + "google.golang.org/grpc" ) var _ = Describe("EducationClass", func() { @@ -35,6 +37,7 @@ var _ = Describe("EducationClass", func() { ctx context.Context cfg *config.Config gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] eventsPublisher mocks.Publisher identityBackend *identitymocks.Backend identityEducationBackend *identitymocks.EducationBackend @@ -52,9 +55,18 @@ var _ = Describe("EducationClass", func() { BeforeEach(func() { eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil) + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") + gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + identityEducationBackend = &identitymocks.EducationBackend{} identityBackend = &identitymocks.Backend{} - gatewayClient = &cs3mocks.GatewayAPIClient{} newClass = libregraph.NewEducationClass("math", "course") newClass.SetMembersodataBind([]string{"/users/user1"}) newClass.SetId("math") @@ -71,7 +83,7 @@ var _ = Describe("EducationClass", func() { _ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...) svc, _ = service.NewService( service.Config(cfg), - service.WithGatewayClient(gatewayClient), + service.WithGatewaySelector(gatewaySelector), service.EventsPublisher(&eventsPublisher), service.WithIdentityBackend(identityBackend), service.WithIdentityEducationBackend(identityEducationBackend), @@ -320,7 +332,7 @@ var _ = Describe("EducationClass", func() { cfg.API.GroupMembersPatchLimit = 21 svc, _ = service.NewService( service.Config(cfg), - service.WithGatewayClient(gatewayClient), + service.WithGatewaySelector(gatewaySelector), service.EventsPublisher(&eventsPublisher), service.WithIdentityBackend(identityBackend), service.WithIdentityEducationBackend(identityEducationBackend), diff --git a/services/graph/pkg/service/v0/educationschools_test.go b/services/graph/pkg/service/v0/educationschools_test.go index 3bf5157faff..83358243fe4 100644 --- a/services/graph/pkg/service/v0/educationschools_test.go +++ b/services/graph/pkg/service/v0/educationschools_test.go @@ -9,13 +9,16 @@ import ( "net/http" "net/http/httptest" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" "github.com/go-chi/chi/v5" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/test-go/testify/mock" + "google.golang.org/grpc" libregraph "github.com/owncloud/libre-graph-api-go" ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" @@ -37,6 +40,7 @@ var _ = Describe("Schools", func() { ctx context.Context cfg *config.Config gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] identityEducationBackend *identitymocks.EducationBackend rr *httptest.ResponseRecorder @@ -50,8 +54,17 @@ var _ = Describe("Schools", func() { ) BeforeEach(func() { - identityEducationBackend = &identitymocks.EducationBackend{} + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + + identityEducationBackend = &identitymocks.EducationBackend{} newSchool = libregraph.NewEducationSchool() newSchool.SetId("school1") @@ -67,7 +80,7 @@ var _ = Describe("Schools", func() { _ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...) svc, _ = service.NewService( service.Config(cfg), - service.WithGatewayClient(gatewayClient), + service.WithGatewaySelector(gatewaySelector), service.WithIdentityEducationBackend(identityEducationBackend), ) }) diff --git a/services/graph/pkg/service/v0/educationuser.go b/services/graph/pkg/service/v0/educationuser.go index 3ddaf2374e5..ace6307952d 100644 --- a/services/graph/pkg/service/v0/educationuser.go +++ b/services/graph/pkg/service/v0/educationuser.go @@ -239,13 +239,19 @@ func (g Graph) DeleteEducationUser(w http.ResponseWriter, r *http.Request) { e.Executant = currentUser.GetId() } - if g.gatewayClient != nil { + if g.gatewaySelector != nil { logger.Debug(). Str("user", user.GetId()). Msg("calling list spaces with user filter to fetch the personal space for deletion") opaque := utils.AppendPlainToOpaque(nil, "unrestricted", "T") f := listStorageSpacesUserFilter(user.GetId()) - lspr, err := g.gatewayClient.ListStorageSpaces(r.Context(), &storageprovider.ListStorageSpacesRequest{ + client, err := g.gatewaySelector.Next() + if err != nil { + logger.Error().Err(err).Msg("could not select next gateway client") + errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, "could not select next gateway client, aborting") + return + } + lspr, err := client.ListStorageSpaces(r.Context(), &storageprovider.ListStorageSpacesRequest{ Opaque: opaque, Filters: []*storageprovider.ListStorageSpacesRequest_Filter{f}, }) @@ -266,7 +272,7 @@ func (g Graph) DeleteEducationUser(w http.ResponseWriter, r *http.Request) { // Deleting a space a two step process (1. disabling/trashing, 2. purging) // Do the "disable/trash" step only if the space is not marked as trashed yet: if _, ok := sp.Opaque.Map["trashed"]; !ok { - _, err := g.gatewayClient.DeleteStorageSpace(r.Context(), &storageprovider.DeleteStorageSpaceRequest{ + _, err := client.DeleteStorageSpace(r.Context(), &storageprovider.DeleteStorageSpaceRequest{ Id: &storageprovider.StorageSpaceId{ OpaqueId: sp.Id.OpaqueId, }, @@ -278,7 +284,7 @@ func (g Graph) DeleteEducationUser(w http.ResponseWriter, r *http.Request) { } } purgeFlag := utils.AppendPlainToOpaque(nil, "purge", "") - _, err := g.gatewayClient.DeleteStorageSpace(r.Context(), &storageprovider.DeleteStorageSpaceRequest{ + _, err := client.DeleteStorageSpace(r.Context(), &storageprovider.DeleteStorageSpaceRequest{ Opaque: purgeFlag, Id: &storageprovider.StorageSpaceId{ OpaqueId: sp.Id.OpaqueId, diff --git a/services/graph/pkg/service/v0/educationuser_test.go b/services/graph/pkg/service/v0/educationuser_test.go index 1d4a10407de..80e5ed41dd9 100644 --- a/services/graph/pkg/service/v0/educationuser_test.go +++ b/services/graph/pkg/service/v0/educationuser_test.go @@ -8,17 +8,17 @@ import ( "net/http" "net/http/httptest" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" "github.com/go-chi/chi/v5" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/stretchr/testify/mock" - libregraph "github.com/owncloud/libre-graph-api-go" ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/shared" @@ -28,6 +28,8 @@ import ( "github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults" identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks" service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0" + "github.com/stretchr/testify/mock" + "google.golang.org/grpc" ) type educationUserList struct { @@ -40,6 +42,7 @@ var _ = Describe("EducationUsers", func() { ctx context.Context cfg *config.Config gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] eventsPublisher mocks.Publisher roleService *mocks.RoleService identityEducationBackend *identitymocks.EducationBackend @@ -56,9 +59,18 @@ var _ = Describe("EducationUsers", func() { BeforeEach(func() { eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil) + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") + gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + identityEducationBackend = &identitymocks.EducationBackend{} roleService = &mocks.RoleService{} - gatewayClient = &cs3mocks.GatewayAPIClient{} rr = httptest.NewRecorder() ctx = context.Background() @@ -72,7 +84,7 @@ var _ = Describe("EducationUsers", func() { _ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...) svc, _ = service.NewService( service.Config(cfg), - service.WithGatewayClient(gatewayClient), + service.WithGatewaySelector(gatewaySelector), service.EventsPublisher(&eventsPublisher), service.WithIdentityEducationBackend(identityEducationBackend), service.WithRoleService(roleService), diff --git a/services/graph/pkg/service/v0/graph.go b/services/graph/pkg/service/v0/graph.go index eab4903d402..86045e00ab9 100644 --- a/services/graph/pkg/service/v0/graph.go +++ b/services/graph/pkg/service/v0/graph.go @@ -10,6 +10,7 @@ import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/go-chi/chi/v5" "github.com/jellydator/ttlcache/v3" libregraph "github.com/owncloud/libre-graph-api-go" @@ -62,7 +63,7 @@ type Graph struct { logger *log.Logger identityBackend identity.Backend identityEducationBackend identity.EducationBackend - gatewayClient gateway.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] roleService RoleService permissionsService Permissions specialDriveItemsCache *ttlcache.Cache[string, interface{}] @@ -86,11 +87,6 @@ func (g Graph) ServeHTTP(w http.ResponseWriter, r *http.Request) { g.mux.ServeHTTP(w, r) } -// GetGatewayClient returns a gateway client to talk to reva -func (g Graph) GetGatewayClient() gateway.GatewayAPIClient { - return g.gatewayClient -} - func (g Graph) publishEvent(ev interface{}) { if g.eventsPublisher != nil { if err := events.Publish(g.eventsPublisher, ev); err != nil { diff --git a/services/graph/pkg/service/v0/graph_suite_test.go b/services/graph/pkg/service/v0/graph_suite_test.go index 6b34ae0631b..7b6ed5b234f 100644 --- a/services/graph/pkg/service/v0/graph_suite_test.go +++ b/services/graph/pkg/service/v0/graph_suite_test.go @@ -3,10 +3,24 @@ package svc_test import ( "testing" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" + mRegistry "go-micro.dev/v4/registry" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) +func init() { + registry.Configure("memory") + r := registry.GetRegistry() + service := registry.BuildGRPCService("com.owncloud.api.gateway", "", "", "") + service.Nodes = []*mRegistry.Node{{ + Address: "any", + }} + + _ = r.Register(service) +} + func TestGraph(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Graph Suite") diff --git a/services/graph/pkg/service/v0/graph_test.go b/services/graph/pkg/service/v0/graph_test.go index 0b244d50571..61a08d27bdf 100644 --- a/services/graph/pkg/service/v0/graph_test.go +++ b/services/graph/pkg/service/v0/graph_test.go @@ -16,13 +16,13 @@ import ( typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/utils" cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" "github.com/go-chi/chi/v5" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" libregraph "github.com/owncloud/libre-graph-api-go" - ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/shared" v0 "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" @@ -40,6 +40,7 @@ var _ = Describe("Graph", func() { var ( svc service.Service gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] eventsPublisher mocks.Publisher permissionService mocks.Permissions ctx context.Context @@ -63,13 +64,21 @@ var _ = Describe("Graph", func() { cfg.Commons = &shared.Commons{} cfg.GRPCClientTLS = &shared.GRPCClientTLS{} - _ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...) + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + eventsPublisher = mocks.Publisher{} permissionService = mocks.Permissions{} svc, _ = service.NewService( service.Config(cfg), - service.WithGatewayClient(gatewayClient), + service.WithGatewaySelector(gatewaySelector), service.EventsPublisher(&eventsPublisher), service.PermissionService(&permissionService), ) diff --git a/services/graph/pkg/service/v0/groups_test.go b/services/graph/pkg/service/v0/groups_test.go index 2712641b624..598cbabef05 100644 --- a/services/graph/pkg/service/v0/groups_test.go +++ b/services/graph/pkg/service/v0/groups_test.go @@ -9,14 +9,14 @@ import ( "net/http" "net/http/httptest" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" "github.com/go-chi/chi/v5" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/test-go/testify/mock" - libregraph "github.com/owncloud/libre-graph-api-go" ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/shared" @@ -26,6 +26,8 @@ import ( identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks" service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0" "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode" + "github.com/test-go/testify/mock" + "google.golang.org/grpc" ) type groupList struct { @@ -38,6 +40,7 @@ var _ = Describe("Groups", func() { ctx context.Context cfg *config.Config gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] eventsPublisher mocks.Publisher identityBackend *identitymocks.Backend @@ -54,8 +57,17 @@ var _ = Describe("Groups", func() { BeforeEach(func() { eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil) - identityBackend = &identitymocks.Backend{} + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + + identityBackend = &identitymocks.Backend{} newGroup = libregraph.NewGroup() newGroup.SetMembersodataBind([]string{"/users/user1"}) newGroup.SetId("group1") @@ -72,7 +84,7 @@ var _ = Describe("Groups", func() { _ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...) svc, _ = service.NewService( service.Config(cfg), - service.WithGatewayClient(gatewayClient), + service.WithGatewaySelector(gatewaySelector), service.EventsPublisher(&eventsPublisher), service.WithIdentityBackend(identityBackend), ) @@ -316,7 +328,7 @@ var _ = Describe("Groups", func() { cfg.API.GroupMembersPatchLimit = 21 svc, _ = service.NewService( service.Config(cfg), - service.WithGatewayClient(gatewayClient), + service.WithGatewaySelector(gatewaySelector), service.EventsPublisher(&eventsPublisher), service.WithIdentityBackend(identityBackend), ) diff --git a/services/graph/pkg/service/v0/option.go b/services/graph/pkg/service/v0/option.go index 51581ade76a..a13714f54d3 100644 --- a/services/graph/pkg/service/v0/option.go +++ b/services/graph/pkg/service/v0/option.go @@ -5,6 +5,7 @@ import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/v2/ocis-pkg/keycloak" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/roles" @@ -24,7 +25,7 @@ type Options struct { Config *config.Config Middleware []func(http.Handler) http.Handler RequireAdminMiddleware func(http.Handler) http.Handler - GatewayClient gateway.GatewayAPIClient + GatewaySelector pool.Selectable[gateway.GatewayAPIClient] IdentityBackend identity.Backend IdentityEducationBackend identity.EducationBackend RoleService RoleService @@ -75,10 +76,10 @@ func WithRequireAdminMiddleware(val func(http.Handler) http.Handler) Option { } } -// WithGatewayClient provides a function to set the gateway client option. -func WithGatewayClient(val gateway.GatewayAPIClient) Option { +// WithGatewaySelector provides a function to set the gateway client option. +func WithGatewaySelector(val pool.Selectable[gateway.GatewayAPIClient]) Option { return func(o *Options) { - o.GatewayClient = val + o.GatewaySelector = val } } diff --git a/services/graph/pkg/service/v0/password.go b/services/graph/pkg/service/v0/password.go index 551c0e29ff5..2312040888c 100644 --- a/services/graph/pkg/service/v0/password.go +++ b/services/graph/pkg/service/v0/password.go @@ -61,7 +61,12 @@ func (g Graph) ChangeOwnPassword(w http.ResponseWriter, r *http.Request) { ClientId: u.Username, ClientSecret: currentPw, } - authRes, err := g.gatewayClient.Authenticate(r.Context(), authReq) + client, err := g.gatewaySelector.Next() + if err != nil { + errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, "could not select next gateway client, aborting") + return + } + authRes, err := client.Authenticate(r.Context(), authReq) if err != nil { errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, err.Error()) return diff --git a/services/graph/pkg/service/v0/password_test.go b/services/graph/pkg/service/v0/password_test.go index 72bf4a07fcd..cb54ab6e5b0 100644 --- a/services/graph/pkg/service/v0/password_test.go +++ b/services/graph/pkg/service/v0/password_test.go @@ -12,6 +12,7 @@ import ( userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" "github.com/go-ldap/ldap/v3" . "github.com/onsi/ginkgo/v2" @@ -25,12 +26,14 @@ import ( "github.com/owncloud/ocis/v2/services/graph/pkg/identity" service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0" "github.com/stretchr/testify/mock" + "google.golang.org/grpc" ) var _ = Describe("Users changing their own password", func() { var ( svc service.Service gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] ldapClient *mocks.Client ldapConfig config.LDAP identityBackend identity.Backend @@ -47,7 +50,16 @@ var _ = Describe("Users changing their own password", func() { cfg.TokenManager.JWTSecret = "loremipsum" cfg.GRPCClientTLS = &shared.GRPCClientTLS{} + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + ldapClient = mockedLDAPClient() ldapConfig = config.LDAP{ @@ -68,7 +80,7 @@ var _ = Describe("Users changing their own password", func() { eventsPublisher = mocks.Publisher{} svc, _ = service.NewService( service.Config(cfg), - service.WithGatewayClient(gatewayClient), + service.WithGatewaySelector(gatewaySelector), service.WithIdentityBackend(identityBackend), service.EventsPublisher(&eventsPublisher), ) diff --git a/services/graph/pkg/service/v0/personaldata.go b/services/graph/pkg/service/v0/personaldata.go index de263ea86b3..77bba3ab4a3 100644 --- a/services/graph/pkg/service/v0/personaldata.go +++ b/services/graph/pkg/service/v0/personaldata.go @@ -10,6 +10,8 @@ import ( "path/filepath" "strconv" + "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" @@ -68,8 +70,15 @@ func (g Graph) ExportPersonalData(w http.ResponseWriter, r *http.Request) { Path: loc, } + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + g.logger.Error().Err(err).Msg("could not select next gateway client") + errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, "could not select next gateway client, aborting") + return + } + // touch file - if err := mustTouchFile(ctx, ref, g.GetGatewayClient()); err != nil { + if err := mustTouchFile(ctx, ref, gatewayClient); err != nil { g.logger.Error().Err(err).Msg("error touching file") w.WriteHeader(http.StatusInternalServerError) return @@ -83,8 +92,14 @@ func (g Graph) ExportPersonalData(w http.ResponseWriter, r *http.Request) { // GatherPersonalData will all gather all personal data of the user and save it to a file in the users personal space func (g Graph) GatherPersonalData(usr *user.User, ref *provider.Reference, token string, marsh Marshaller) { + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + g.logger.Error().Err(err).Msg("could not select next gateway client") + return + } + // the context might already be cancelled. We need to impersonate the acting user again - ctx, err := utils.ImpersonateUser(usr, g.gatewayClient, g.config.MachineAuthAPIKey) + ctx, err := utils.ImpersonateUser(usr, gatewayClient, g.config.MachineAuthAPIKey) if err != nil { g.logger.Error().Err(err).Str("userID", usr.GetId().GetOpaqueId()).Msg("cannot impersonate user") } @@ -141,13 +156,22 @@ func (g Graph) upload(u *user.User, data []byte, ref *provider.Reference, th str Opaque: utils.AppendPlainToOpaque(nil, "Upload-Length", strconv.FormatUint(uint64(len(data)), 10)), } - gwc := g.GetGatewayClient() - ctx, err := utils.ImpersonateUser(u, gwc, g.config.MachineAuthAPIKey) + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + g.logger.Error().Err(err).Msg("could not select next gateway client") + return err + } + + ctx, err := utils.ImpersonateUser(u, gatewayClient, g.config.MachineAuthAPIKey) + if err != nil { + return err + } + client, err := g.gatewaySelector.Next() if err != nil { return err } ctx = revactx.ContextSetToken(ctx, th) - uRes, err := gwc.InitiateFileUpload(ctx, uReq) + uRes, err := client.InitiateFileUpload(ctx, uReq) if err != nil { return err } diff --git a/services/graph/pkg/service/v0/service.go b/services/graph/pkg/service/v0/service.go index 86fe823dd99..404e10a4849 100644 --- a/services/graph/pkg/service/v0/service.go +++ b/services/graph/pkg/service/v0/service.go @@ -10,6 +10,7 @@ import ( "strconv" "time" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/store" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" @@ -17,6 +18,7 @@ import ( "github.com/jellydator/ttlcache/v3" libregraph "github.com/owncloud/libre-graph-api-go" ocisldap "github.com/owncloud/ocis/v2/ocis-pkg/ldap" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/roles" "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" @@ -144,7 +146,7 @@ func NewService(opts ...Option) (Graph, error) { usersCache: usersCache, groupsCache: groupsCache, eventsPublisher: options.EventsPublisher, - gatewayClient: options.GatewayClient, + gatewaySelector: options.GatewaySelector, searchService: options.SearchService, identityEducationBackend: options.IdentityEducationBackend, keycloakClient: options.KeycloakClient, @@ -316,9 +318,18 @@ func setIdentityBackends(options Options, svc *Graph) error { if options.IdentityBackend == nil { switch options.Config.Identity.Backend { case "cs3": + gatewaySelector, err := pool.GatewaySelector( + options.Config.Reva.Address, + append(options.Config.Reva.GetRevaOptions(), pool.WithRegistry(registry.GetRegistry()))..., + ) + if err != nil { + return err + } + svc.identityBackend = &identity.CS3{ - Config: options.Config.Reva, - Logger: &options.Logger, + Config: options.Config.Reva, + Logger: &options.Logger, + GatewaySelector: gatewaySelector, } case "ldap": var err error diff --git a/services/graph/pkg/service/v0/tags.go b/services/graph/pkg/service/v0/tags.go index b3e95ef5e73..786eb0e49f9 100644 --- a/services/graph/pkg/service/v0/tags.go +++ b/services/graph/pkg/service/v0/tags.go @@ -66,7 +66,13 @@ func (g Graph) AssignTags(w http.ResponseWriter, r *http.Request) { return } - sres, err := g.gatewayClient.Stat(ctx, &provider.StatRequest{ + client, err := g.gatewaySelector.Next() + if err != nil { + g.logger.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return + } + sres, err := client.Stat(ctx, &provider.StatRequest{ Ref: &provider.Reference{ResourceId: &rid}, }) if err != nil { @@ -105,7 +111,7 @@ func (g Graph) AssignTags(w http.ResponseWriter, r *http.Request) { return } - resp, err := g.gatewayClient.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{ + resp, err := client.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{ Ref: &provider.Reference{ResourceId: &rid}, ArbitraryMetadata: &provider.ArbitraryMetadata{ Metadata: map[string]string{ @@ -155,7 +161,13 @@ func (g Graph) UnassignTags(w http.ResponseWriter, r *http.Request) { return } - sres, err := g.gatewayClient.Stat(ctx, &provider.StatRequest{ + client, err := g.gatewaySelector.Next() + if err != nil { + g.logger.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return + } + sres, err := client.Stat(ctx, &provider.StatRequest{ Ref: &provider.Reference{ResourceId: &rid}, }) if err != nil { @@ -194,7 +206,7 @@ func (g Graph) UnassignTags(w http.ResponseWriter, r *http.Request) { return } - resp, err := g.gatewayClient.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{ + resp, err := client.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{ Ref: &provider.Reference{ResourceId: &rid}, ArbitraryMetadata: &provider.ArbitraryMetadata{ Metadata: map[string]string{ diff --git a/services/graph/pkg/service/v0/users.go b/services/graph/pkg/service/v0/users.go index be24c2ccc15..7a8c0df04f2 100644 --- a/services/graph/pkg/service/v0/users.go +++ b/services/graph/pkg/service/v0/users.go @@ -427,10 +427,18 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) { return } logger.Debug().Str("id", user.GetId()).Msg("calling list storage spaces with filter") + // use the unrestricted flag to get all possible spaces // users with the canListAllSpaces permission should see all spaces + + client, err := g.gatewaySelector.Next() + if err != nil { + logger.Error().Err(err).Msg("error selecting next gateway client") + render.Status(r, http.StatusInternalServerError) + return + } opaque := utils.AppendPlainToOpaque(nil, "unrestricted", "T") - lspr, err := g.gatewayClient.ListStorageSpaces(r.Context(), &storageprovider.ListStorageSpacesRequest{ + lspr, err := client.ListStorageSpaces(r.Context(), &storageprovider.ListStorageSpacesRequest{ Opaque: opaque, Filters: filters, }) @@ -545,13 +553,19 @@ func (g Graph) DeleteUser(w http.ResponseWriter, r *http.Request) { e.Executant = currentUser.GetId() } - if g.gatewayClient != nil { + if g.gatewaySelector != nil { logger.Debug(). Str("user", user.GetId()). Msg("calling list spaces with user filter to fetch the personal space for deletion") opaque := utils.AppendPlainToOpaque(nil, "unrestricted", "T") f := listStorageSpacesUserFilter(user.GetId()) - lspr, err := g.gatewayClient.ListStorageSpaces(r.Context(), &storageprovider.ListStorageSpacesRequest{ + client, err := g.gatewaySelector.Next() + if err != nil { + logger.Error().Err(err).Msg("error selecting next gateway client") + errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, "error selecting next gateway client, aborting") + return + } + lspr, err := client.ListStorageSpaces(r.Context(), &storageprovider.ListStorageSpacesRequest{ Opaque: opaque, Filters: []*storageprovider.ListStorageSpacesRequest_Filter{f}, }) @@ -572,7 +586,7 @@ func (g Graph) DeleteUser(w http.ResponseWriter, r *http.Request) { // Deleting a space a two step process (1. disabling/trashing, 2. purging) // Do the "disable/trash" step only if the space is not marked as trashed yet: if _, ok := sp.Opaque.Map["trashed"]; !ok { - _, err := g.gatewayClient.DeleteStorageSpace(r.Context(), &storageprovider.DeleteStorageSpaceRequest{ + _, err := client.DeleteStorageSpace(r.Context(), &storageprovider.DeleteStorageSpaceRequest{ Id: &storageprovider.StorageSpaceId{ OpaqueId: sp.Id.OpaqueId, }, @@ -584,7 +598,7 @@ func (g Graph) DeleteUser(w http.ResponseWriter, r *http.Request) { } } purgeFlag := utils.AppendPlainToOpaque(nil, "purge", "") - _, err := g.gatewayClient.DeleteStorageSpace(r.Context(), &storageprovider.DeleteStorageSpaceRequest{ + _, err := client.DeleteStorageSpace(r.Context(), &storageprovider.DeleteStorageSpaceRequest{ Opaque: purgeFlag, Id: &storageprovider.StorageSpaceId{ OpaqueId: sp.Id.OpaqueId, diff --git a/services/graph/pkg/service/v0/users_test.go b/services/graph/pkg/service/v0/users_test.go index 4719f366e6f..5dbe88f5f79 100644 --- a/services/graph/pkg/service/v0/users_test.go +++ b/services/graph/pkg/service/v0/users_test.go @@ -9,18 +9,17 @@ import ( "net/http/httptest" "net/url" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" "github.com/go-chi/chi/v5" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/stretchr/testify/mock" - "go-micro.dev/v4/client" - libregraph "github.com/owncloud/libre-graph-api-go" ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/shared" @@ -31,6 +30,9 @@ import ( "github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults" identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks" service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0" + "github.com/stretchr/testify/mock" + "go-micro.dev/v4/client" + "google.golang.org/grpc" ) type userList struct { @@ -43,6 +45,7 @@ var _ = Describe("Users", func() { ctx context.Context cfg *config.Config gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] eventsPublisher mocks.Publisher roleService *mocks.RoleService identityBackend *identitymocks.Backend @@ -59,9 +62,18 @@ var _ = Describe("Users", func() { BeforeEach(func() { eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil) + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") + gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + identityBackend = &identitymocks.Backend{} roleService = &mocks.RoleService{} - gatewayClient = &cs3mocks.GatewayAPIClient{} rr = httptest.NewRecorder() ctx = context.Background() @@ -76,7 +88,7 @@ var _ = Describe("Users", func() { _ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...) svc, _ = service.NewService( service.Config(cfg), - service.WithGatewayClient(gatewayClient), + service.WithGatewaySelector(gatewaySelector), service.EventsPublisher(&eventsPublisher), service.WithIdentityBackend(identityBackend), service.WithRoleService(roleService), @@ -681,7 +693,7 @@ var _ = Describe("Users", func() { _ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...) localSvc, _ := service.NewService( service.Config(localCfg), - service.WithGatewayClient(gatewayClient), + service.WithGatewaySelector(gatewaySelector), service.EventsPublisher(&eventsPublisher), service.WithIdentityBackend(identityBackend), service.WithRoleService(roleService), diff --git a/services/groups/pkg/command/server.go b/services/groups/pkg/command/server.go index 6605c3dfd3a..55a69ddfa93 100644 --- a/services/groups/pkg/command/server.go +++ b/services/groups/pkg/command/server.go @@ -11,7 +11,7 @@ import ( "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" "github.com/owncloud/ocis/v2/ocis-pkg/ldap" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/sync" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/groups/pkg/config" @@ -43,10 +43,6 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") - - rcfg := revaconfig.GroupsConfigFromStruct(cfg) - // the reva runtime calls os.Exit in the case of a failure and there is no way for the oCIS // runtime to catch it and restart a reva service. Therefore we need to ensure the service has // everything it needs, before starting the service. @@ -60,7 +56,15 @@ func Server(cfg *config.Config) *cli.Command { } gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.GroupsConfigFromStruct(cfg) + reg := registry.GetRegistry() + + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) + return nil }, func(err error) { logger.Error(). @@ -90,15 +94,9 @@ func Server(cfg *config.Config) *cli.Command { sync.Trap(&gr, cancel) } - if err := external.RegisterGRPCEndpoint( - ctx, - cfg.GRPC.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.GRPC.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the grpc endpoint") + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") } return gr.Run() diff --git a/services/notifications/pkg/command/server.go b/services/notifications/pkg/command/server.go index f1b623c2b9c..cfdd7a57a14 100644 --- a/services/notifications/pkg/command/server.go +++ b/services/notifications/pkg/command/server.go @@ -15,6 +15,7 @@ import ( "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" "github.com/owncloud/ocis/v2/ocis-pkg/crypto" "github.com/owncloud/ocis/v2/ocis-pkg/handlers" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/service/debug" "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/version" @@ -122,16 +123,17 @@ func Server(cfg *config.Config) *cli.Command { if err != nil { return err } - gwclient, err := pool.GetGatewayServiceClient( + gatewaySelector, err := pool.GatewaySelector( cfg.Notifications.RevaGateway, pool.WithTLSCACert(cfg.Notifications.GRPCClientTLS.CACert), pool.WithTLSMode(tm), + pool.WithRegistry(registry.GetRegistry()), ) if err != nil { - logger.Fatal().Err(err).Str("addr", cfg.Notifications.RevaGateway).Msg("could not get reva client") + logger.Fatal().Err(err).Str("addr", cfg.Notifications.RevaGateway).Msg("could not get reva gateway selector") } valueService := settingssvc.NewValueService("com.owncloud.api.settings", grpc.DefaultClient()) - svc := service.NewEventsNotifier(evts, channel, logger, gwclient, valueService, cfg.Notifications.MachineAuthAPIKey, cfg.Notifications.EmailTemplatePath, cfg.WebUIURL) + svc := service.NewEventsNotifier(evts, channel, logger, gatewaySelector, valueService, cfg.Notifications.MachineAuthAPIKey, cfg.Notifications.EmailTemplatePath, cfg.WebUIURL) gr.Add(svc.Run, func(error) { cancel() diff --git a/services/notifications/pkg/service/notification_suite_test.go b/services/notifications/pkg/service/notification_suite_test.go index 5735c73ff4c..dc3c1f42511 100644 --- a/services/notifications/pkg/service/notification_suite_test.go +++ b/services/notifications/pkg/service/notification_suite_test.go @@ -3,10 +3,24 @@ package service_test import ( "testing" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" + mRegistry "go-micro.dev/v4/registry" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) +func init() { + registry.Configure("memory") + r := registry.GetRegistry() + service := registry.BuildGRPCService("com.owncloud.api.gateway", "", "", "") + service.Nodes = []*mRegistry.Node{{ + Address: "any", + }} + + _ = r.Register(service) +} + func TestNotifications(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Notification Suite") diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index b187f81ddbd..c55eef6afcc 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -16,6 +16,7 @@ import ( rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/middleware" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" @@ -38,7 +39,7 @@ func NewEventsNotifier( events <-chan events.Event, channel channels.Channel, logger log.Logger, - gwClient gateway.GatewayAPIClient, + gatewaySelector pool.Selectable[gateway.GatewayAPIClient], valueService settingssvc.ValueService, machineAuthAPIKey, emailTemplatePath, ocisURL string) Service { @@ -47,7 +48,7 @@ func NewEventsNotifier( channel: channel, events: events, signals: make(chan os.Signal, 1), - gwClient: gwClient, + gatewaySelector: gatewaySelector, valueService: valueService, machineAuthAPIKey: machineAuthAPIKey, emailTemplatePath: emailTemplatePath, @@ -60,7 +61,7 @@ type eventsNotifier struct { channel channels.Channel events <-chan events.Event signals chan os.Signal - gwClient gateway.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] valueService settingssvc.ValueService machineAuthAPIKey string emailTemplatePath string @@ -137,7 +138,12 @@ func (s eventsNotifier) getGranteeList(ctx context.Context, executant, u *user.U } return []*user.User{usr}, nil case g != nil: - res, err := s.gwClient.GetGroup(ctx, &group.GetGroupRequest{GroupId: g}) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + res, err := gatewayClient.GetGroup(ctx, &group.GetGroupRequest{GroupId: g}) if err != nil { return nil, err } @@ -171,7 +177,11 @@ func (s eventsNotifier) getUser(ctx context.Context, u *user.UserId) (*user.User if u == nil { return nil, errors.New("need at least one non-nil grantee") } - r, err := s.gwClient.GetUser(ctx, &user.GetUserRequest{UserId: u}) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + r, err := gatewayClient.GetUser(ctx, &user.GetUserRequest{UserId: u}) if err != nil { return nil, err } @@ -213,7 +223,12 @@ func (s eventsNotifier) disableEmails(ctx context.Context, u *user.UserId) bool func (s eventsNotifier) getResourceInfo(ctx context.Context, resourceID *provider.ResourceId, fieldmask *fieldmaskpb.FieldMask) (*provider.ResourceInfo, error) { // TODO: maybe cache this stat to reduce storage iops - md, err := s.gwClient.Stat(ctx, &provider.StatRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + md, err := gatewayClient.Stat(ctx, &provider.StatRequest{ Ref: &provider.Reference{ ResourceId: resourceID, }, diff --git a/services/notifications/pkg/service/service_test.go b/services/notifications/pkg/service/service_test.go index 925fcc4bbf7..8497d75d511 100644 --- a/services/notifications/pkg/service/service_test.go +++ b/services/notifications/pkg/service/service_test.go @@ -9,6 +9,7 @@ import ( rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/utils" cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" . "github.com/onsi/ginkgo/v2" @@ -22,13 +23,15 @@ import ( "github.com/owncloud/ocis/v2/services/notifications/pkg/service" "github.com/test-go/testify/mock" "go-micro.dev/v4/client" + "google.golang.org/grpc" ) var _ = Describe("Notifications", func() { var ( - gwc *cs3mocks.GatewayAPIClient - vs *settingssvc.MockValueService - sharer = &user.User{ + gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + vs *settingssvc.MockValueService + sharer = &user.User{ Id: &user.UserId{ OpaqueId: "sharer", }, @@ -50,11 +53,20 @@ var _ = Describe("Notifications", func() { ) BeforeEach(func() { - gwc = &cs3mocks.GatewayAPIClient{} - gwc.On("GetUser", mock.Anything, mock.Anything).Return(&user.GetUserResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharer}, nil).Once() - gwc.On("GetUser", mock.Anything, mock.Anything).Return(&user.GetUserResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharee}, nil).Once() - gwc.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharer}, nil) - gwc.On("Stat", mock.Anything, mock.Anything).Return(&provider.StatResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, Info: &provider.ResourceInfo{Name: "secrets of the board", Space: &provider.StorageSpace{Name: "secret space"}}}, nil) + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") + gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(&user.GetUserResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharer}, nil).Once() + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(&user.GetUserResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharee}, nil).Once() + gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharer}, nil) + gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(&provider.StatResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, Info: &provider.ResourceInfo{Name: "secrets of the board", Space: &provider.StorageSpace{Name: "secret space"}}}, nil) vs = &settingssvc.MockValueService{} vs.GetValueByUniqueIdentifiersFunc = func(ctx context.Context, req *settingssvc.GetValueByUniqueIdentifiersRequest, opts ...client.CallOption) (*settingssvc.GetValueResponse, error) { return nil, nil @@ -67,7 +79,7 @@ var _ = Describe("Notifications", func() { cfg.GRPCClientTLS = &shared.GRPCClientTLS{} _ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...) ch := make(chan events.Event) - evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gwc, vs, "", "", "") + evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gatewaySelector, vs, "", "", "") go evts.Run() ch <- ev @@ -210,9 +222,10 @@ https://owncloud.com var _ = Describe("Notifications X-Site Scripting", func() { var ( - gwc *cs3mocks.GatewayAPIClient - vs *settingssvc.MockValueService - sharer = &user.User{ + gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + vs *settingssvc.MockValueService + sharer = &user.User{ Id: &user.UserId{ OpaqueId: "sharer", }, @@ -234,11 +247,20 @@ var _ = Describe("Notifications X-Site Scripting", func() { ) BeforeEach(func() { - gwc = &cs3mocks.GatewayAPIClient{} - gwc.On("GetUser", mock.Anything, mock.Anything).Return(&user.GetUserResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharer}, nil).Once() - gwc.On("GetUser", mock.Anything, mock.Anything).Return(&user.GetUserResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharee}, nil).Once() - gwc.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharer}, nil) - gwc.On("Stat", mock.Anything, mock.Anything).Return(&provider.StatResponse{ + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") + gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(&user.GetUserResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharer}, nil).Once() + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(&user.GetUserResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharee}, nil).Once() + gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharer}, nil) + gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(&provider.StatResponse{ Status: &rpc.Status{Code: rpc.Code_CODE_OK}, Info: &provider.ResourceInfo{ Name: "", @@ -256,7 +278,7 @@ var _ = Describe("Notifications X-Site Scripting", func() { cfg.GRPCClientTLS = &shared.GRPCClientTLS{} _ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...) ch := make(chan events.Event) - evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gwc, vs, "", "", "") + evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gatewaySelector, vs, "", "", "") go evts.Run() ch <- ev diff --git a/services/notifications/pkg/service/shares.go b/services/notifications/pkg/service/shares.go index c4349436ecd..9328c7f881b 100644 --- a/services/notifications/pkg/service/shares.go +++ b/services/notifications/pkg/service/shares.go @@ -13,7 +13,13 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) { Str("itemid", e.ItemID.OpaqueId). Logger() - ownerCtx, owner, err := utils.Impersonate(e.Sharer, s.gwClient, s.machineAuthAPIKey) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + logger.Error().Err(err).Msg("could not select next gateway client") + return + } + + ownerCtx, owner, err := utils.Impersonate(e.Sharer, gatewayClient, s.machineAuthAPIKey) if err != nil { logger.Error().Err(err).Msg("Could not impersonate sharer") return @@ -62,7 +68,13 @@ func (s eventsNotifier) handleShareExpired(e events.ShareExpired) { Str("itemid", e.ItemID.GetOpaqueId()). Logger() - ownerCtx, owner, err := utils.Impersonate(e.ShareOwner, s.gwClient, s.machineAuthAPIKey) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + logger.Error().Err(err).Msg("could not select next gateway client") + return + } + + ownerCtx, owner, err := utils.Impersonate(e.ShareOwner, gatewayClient, s.machineAuthAPIKey) if err != nil { logger.Error().Err(err).Msg("Could not impersonate sharer") return diff --git a/services/notifications/pkg/service/spaces.go b/services/notifications/pkg/service/spaces.go index d764e9f5a86..cd8f072783c 100644 --- a/services/notifications/pkg/service/spaces.go +++ b/services/notifications/pkg/service/spaces.go @@ -13,7 +13,13 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { Str("itemid", e.ID.OpaqueId). Logger() - executantCtx, executant, err := utils.Impersonate(e.Executant, s.gwClient, s.machineAuthAPIKey) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + logger.Error().Err(err).Msg("could not select next gateway client") + return + } + + executantCtx, executant, err := utils.Impersonate(e.Executant, gatewayClient, s.machineAuthAPIKey) if err != nil { logger.Error(). Err(err). @@ -75,7 +81,13 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) { Str("itemid", e.ID.OpaqueId). Logger() - executantCtx, executant, err := utils.Impersonate(e.Executant, s.gwClient, s.machineAuthAPIKey) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + logger.Error().Err(err).Msg("could not select next gateway client") + return + } + + executantCtx, executant, err := utils.Impersonate(e.Executant, gatewayClient, s.machineAuthAPIKey) if err != nil { logger.Error().Err(err).Msg("could not handle space unshared event") return @@ -135,7 +147,13 @@ func (s eventsNotifier) handleSpaceMembershipExpired(e events.SpaceMembershipExp Str("itemid", e.SpaceID.GetOpaqueId()). Logger() - ownerCtx, owner, err := utils.Impersonate(e.SpaceOwner, s.gwClient, s.machineAuthAPIKey) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + logger.Error().Err(err).Msg("could not select next gateway client") + return + } + + ownerCtx, owner, err := utils.Impersonate(e.SpaceOwner, gatewayClient, s.machineAuthAPIKey) if err != nil { logger.Error().Err(err).Msg("Could not impersonate sharer") return diff --git a/services/proxy/pkg/command/server.go b/services/proxy/pkg/command/server.go index 11b18850d1e..5c89ffa516e 100644 --- a/services/proxy/pkg/command/server.go +++ b/services/proxy/pkg/command/server.go @@ -20,6 +20,7 @@ import ( "github.com/owncloud/ocis/v2/ocis-pkg/log" pkgmiddleware "github.com/owncloud/ocis/v2/ocis-pkg/middleware" "github.com/owncloud/ocis/v2/ocis-pkg/oidc" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/version" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" @@ -276,9 +277,9 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config, logger.Fatal().Err(err).Msg("Failed to get gateway client") } rolesClient := settingssvc.NewRoleService("com.owncloud.api.settings", grpcClient) - revaClient, err := pool.GetGatewayServiceClient(cfg.Reva.Address, cfg.Reva.GetRevaOptions()...) + gatewaySelector, err := pool.GatewaySelector(cfg.Reva.Address, append(cfg.Reva.GetRevaOptions(), pool.WithRegistry(registry.GetRegistry()))...) if err != nil { - logger.Fatal().Err(err).Msg("Failed to get gateway client") + logger.Fatal().Err(err).Msg("Failed to get gateway selector") } tokenManager, err := jwt.New(map[string]interface{}{ "secret": cfg.TokenManager.JWTSecret, @@ -291,10 +292,9 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config, var userProvider backend.UserBackend switch cfg.AccountBackend { case "cs3": - userProvider = backend.NewCS3UserBackend( backend.WithLogger(logger), - backend.WithRevaAuthenticator(revaClient), + backend.WithRevaGatewaySelector(gatewaySelector), backend.WithMachineAuthAPIKey(cfg.MachineAuthAPIKey), backend.WithOIDCissuer(cfg.OIDC.Issuer), backend.WithAutoProvisonCreator(autoProvsionCreator), @@ -364,8 +364,8 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config, )), )) authenticators = append(authenticators, middleware.PublicShareAuthenticator{ - Logger: logger, - RevaGatewayClient: revaClient, + Logger: logger, + RevaGatewaySelector: gatewaySelector, }) authenticators = append(authenticators, middleware.SignedURLAuthenticator{ Logger: logger, @@ -412,7 +412,7 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config, // finally, trigger home creation when a user logs in middleware.CreateHome( middleware.Logger(logger), - middleware.RevaGatewayClient(revaClient), + middleware.WithRevaGatewaySelector(gatewaySelector), middleware.RoleQuotas(cfg.RoleQuotas), ), ) diff --git a/services/proxy/pkg/middleware/create_home.go b/services/proxy/pkg/middleware/create_home.go index cc4abf77871..f7da59a0e9b 100644 --- a/services/proxy/pkg/middleware/create_home.go +++ b/services/proxy/pkg/middleware/create_home.go @@ -10,6 +10,7 @@ import ( provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/utils" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode" @@ -23,19 +24,19 @@ func CreateHome(optionSetters ...Option) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return &createHome{ - next: next, - logger: logger, - revaGatewayClient: options.RevaGatewayClient, - roleQuotas: options.RoleQuotas, + next: next, + logger: logger, + revaGatewaySelector: options.RevaGatewaySelector, + roleQuotas: options.RoleQuotas, } } } type createHome struct { - next http.Handler - logger log.Logger - revaGatewayClient gateway.GatewayAPIClient - roleQuotas map[string]uint64 + next http.Handler + logger log.Logger + revaGatewaySelector pool.Selectable[gateway.GatewayAPIClient] + roleQuotas map[string]uint64 } func (m createHome) ServeHTTP(w http.ResponseWriter, req *http.Request) { @@ -64,14 +65,18 @@ func (m createHome) ServeHTTP(w http.ResponseWriter, req *http.Request) { } } - createHomeRes, err := m.revaGatewayClient.CreateHome(ctx, createHomeReq) - + client, err := m.revaGatewaySelector.Next() if err != nil { - m.logger.Err(err).Msg("error calling CreateHome") - } else if createHomeRes.Status.Code != rpc.Code_CODE_OK { - err := status.NewErrorFromCode(createHomeRes.Status.Code, "gateway") - if createHomeRes.Status.Code != rpc.Code_CODE_ALREADY_EXISTS { - m.logger.Err(err).Msg("error when calling Createhome") + m.logger.Err(err).Msg("error selecting next gateway client") + } else { + createHomeRes, err := client.CreateHome(ctx, createHomeReq) + if err != nil { + m.logger.Err(err).Msg("error calling CreateHome") + } else if createHomeRes.Status.Code != rpc.Code_CODE_OK { + err := status.NewErrorFromCode(createHomeRes.Status.Code, "gateway") + if createHomeRes.Status.Code != rpc.Code_CODE_ALREADY_EXISTS { + m.logger.Err(err).Msg("error when calling Createhome") + } } } diff --git a/services/proxy/pkg/middleware/options.go b/services/proxy/pkg/middleware/options.go index d4759079f49..edfb39fc8c0 100644 --- a/services/proxy/pkg/middleware/options.go +++ b/services/proxy/pkg/middleware/options.go @@ -5,6 +5,7 @@ import ( "time" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/oidc" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" @@ -38,8 +39,8 @@ type Options struct { OIDCClient oidc.OIDCClient // OIDCIss is the oidcAuth-issuer OIDCIss string - // RevaGatewayClient to send requests to the reva gateway - RevaGatewayClient gateway.GatewayAPIClient + // RevaGatewaySelector to send requests to the reva gateway + RevaGatewaySelector pool.Selectable[gateway.GatewayAPIClient] // Store for persisting data Store storesvc.StoreService // PreSignedURLConfig to configure the middleware @@ -135,10 +136,10 @@ func CredentialsByUserAgent(v map[string]string) Option { } } -// RevaGatewayClient provides a function to set the the reva gateway service client option. -func RevaGatewayClient(gc gateway.GatewayAPIClient) Option { +// WithRevaGatewaySelector provides a function to set the the reva gateway service selector option. +func WithRevaGatewaySelector(val pool.Selectable[gateway.GatewayAPIClient]) Option { return func(o *Options) { - o.RevaGatewayClient = gc + o.RevaGatewaySelector = val } } diff --git a/services/proxy/pkg/middleware/public_share_auth.go b/services/proxy/pkg/middleware/public_share_auth.go index 0f7d8004c7e..f66e030a006 100644 --- a/services/proxy/pkg/middleware/public_share_auth.go +++ b/services/proxy/pkg/middleware/public_share_auth.go @@ -5,6 +5,7 @@ import ( "strings" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/v2/ocis-pkg/log" ) @@ -21,8 +22,8 @@ const ( // PublicShareAuthenticator is the authenticator which can authenticate public share requests. // It will add the share owner into the request context. type PublicShareAuthenticator struct { - Logger log.Logger - RevaGatewayClient gateway.GatewayAPIClient + Logger log.Logger + RevaGatewaySelector pool.Selectable[gateway.GatewayAPIClient] } // The archiver is able to create archives from public shares in which case it needs to use the @@ -83,7 +84,18 @@ func (a PublicShareAuthenticator) Authenticate(r *http.Request) (*http.Request, } } - authResp, err := a.RevaGatewayClient.Authenticate(r.Context(), &gateway.AuthenticateRequest{ + client, err := a.RevaGatewaySelector.Next() + if err != nil { + a.Logger.Error(). + Err(err). + Str("authenticator", "public_share"). + Str("public_share_token", shareToken). + Str("path", r.URL.Path). + Msg("could not select next gateway client") + return nil, false + } + + authResp, err := client.Authenticate(r.Context(), &gateway.AuthenticateRequest{ Type: authenticationType, ClientId: shareToken, ClientSecret: sharePassword, diff --git a/services/proxy/pkg/middleware/public_share_auth_test.go b/services/proxy/pkg/middleware/public_share_auth_test.go index 4df9a47ca9e..5c3aad445ab 100644 --- a/services/proxy/pkg/middleware/public_share_auth_test.go +++ b/services/proxy/pkg/middleware/public_share_auth_test.go @@ -5,8 +5,10 @@ import ( "net/http" "net/http/httptest" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/owncloud/ocis/v2/ocis-pkg/log" @@ -18,23 +20,29 @@ var _ = Describe("Authenticating requests", Label("PublicShareAuthenticator"), f BeforeEach(func() { authenticator = PublicShareAuthenticator{ Logger: log.NewLogger(), - RevaGatewayClient: mockGatewayClient{ - AuthenticateFunc: func(authType, clientID, clientSecret string) (string, rpcv1beta1.Code) { - if authType != "publicshares" { - return "", rpcv1beta1.Code_CODE_NOT_FOUND + RevaGatewaySelector: pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return mockGatewayClient{ + AuthenticateFunc: func(authType, clientID, clientSecret string) (string, rpcv1beta1.Code) { + if authType != "publicshares" { + return "", rpcv1beta1.Code_CODE_NOT_FOUND + } + + if clientID == "sharetoken" && (clientSecret == "password|examples3cr3t" || clientSecret == "signature|examplesignature|exampleexpiration") { + return "exampletoken", rpcv1beta1.Code_CODE_OK + } + + if clientID == "sharetoken" && clientSecret == "password|" { + return "otherexampletoken", rpcv1beta1.Code_CODE_OK + } + + return "", rpcv1beta1.Code_CODE_NOT_FOUND + }, } - - if clientID == "sharetoken" && (clientSecret == "password|examples3cr3t" || clientSecret == "signature|examplesignature|exampleexpiration") { - return "exampletoken", rpcv1beta1.Code_CODE_OK - } - - if clientID == "sharetoken" && clientSecret == "password|" { - return "otherexampletoken", rpcv1beta1.Code_CODE_OK - } - - return "", rpcv1beta1.Code_CODE_NOT_FOUND }, - }, + ), } }) When("the request contains correct data", func() { diff --git a/services/proxy/pkg/user/backend/backend.go b/services/proxy/pkg/user/backend/backend.go index efce21d55dc..8ff2c254a00 100644 --- a/services/proxy/pkg/user/backend/backend.go +++ b/services/proxy/pkg/user/backend/backend.go @@ -4,9 +4,7 @@ import ( "context" "errors" - gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" - "google.golang.org/grpc" ) var ( @@ -26,8 +24,3 @@ type UserBackend interface { Authenticate(ctx context.Context, username string, password string) (*cs3.User, string, error) CreateUserFromClaims(ctx context.Context, claims map[string]interface{}) (*cs3.User, error) } - -// RevaAuthenticator helper interface to mock auth-method from reva gateway-client. -type RevaAuthenticator interface { - Authenticate(ctx context.Context, in *gateway.AuthenticateRequest, opts ...grpc.CallOption) (*gateway.AuthenticateResponse, error) -} diff --git a/services/proxy/pkg/user/backend/cs3.go b/services/proxy/pkg/user/backend/cs3.go index 0dcbc8fd170..6a006fb9cda 100644 --- a/services/proxy/pkg/user/backend/cs3.go +++ b/services/proxy/pkg/user/backend/cs3.go @@ -11,6 +11,7 @@ import ( cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" libregraph "github.com/owncloud/libre-graph-api-go" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/oidc" @@ -31,7 +32,7 @@ type Option func(o *Options) // Options defines the available options for this package. type Options struct { logger log.Logger - authProvider RevaAuthenticator + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] machineAuthAPIKey string oidcISS string autoProvsionCreator autoprovision.Creator @@ -44,10 +45,10 @@ func WithLogger(l log.Logger) Option { } } -// WithRevaAuthenticator set the RevaAuthenticator option -func WithRevaAuthenticator(ra RevaAuthenticator) Option { +// WithRevaGatewaySelector set the gatewaySelector option +func WithRevaGatewaySelector(selectable pool.Selectable[gateway.GatewayAPIClient]) Option { return func(o *Options) { - o.authProvider = ra + o.gatewaySelector = selectable } } @@ -91,7 +92,12 @@ func NewCS3UserBackend(opts ...Option) UserBackend { } func (c *cs3backend) GetUserByClaims(ctx context.Context, claim, value string) (*cs3.User, string, error) { - res, err := c.authProvider.Authenticate(ctx, &gateway.AuthenticateRequest{ + gatewayClient, err := c.gatewaySelector.Next() + if err != nil { + return nil, "", fmt.Errorf("could not obtain gatewayClient: %s", err) + } + + res, err := gatewayClient.Authenticate(ctx, &gateway.AuthenticateRequest{ Type: "machine", ClientId: claim + ":" + value, ClientSecret: c.machineAuthAPIKey, @@ -113,7 +119,12 @@ func (c *cs3backend) GetUserByClaims(ctx context.Context, claim, value string) ( } func (c *cs3backend) Authenticate(ctx context.Context, username string, password string) (*cs3.User, string, error) { - res, err := c.authProvider.Authenticate(ctx, &gateway.AuthenticateRequest{ + gatewayClient, err := c.gatewaySelector.Next() + if err != nil { + return nil, "", fmt.Errorf("could not obtain gatewayClient: %s", err) + } + + res, err := gatewayClient.Authenticate(ctx, &gateway.AuthenticateRequest{ Type: "basic", ClientId: username, ClientSecret: password, diff --git a/services/proxy/pkg/userroles/defaultrole.go b/services/proxy/pkg/userroles/defaultrole.go index d1a59eb2d8a..29125177f0d 100644 --- a/services/proxy/pkg/userroles/defaultrole.go +++ b/services/proxy/pkg/userroles/defaultrole.go @@ -35,7 +35,7 @@ func (d defaultRoleAssigner) UpdateUserRoleAssignment(ctx context.Context, user var err error roleIDs, err = loadRolesIDs(ctx, user.Id.OpaqueId, d.roleService) if err != nil { - d.logger.Error().Err(err).Msgf("Could not load roles") + d.logger.Error().Err(err).Msg("Could not load roles") return nil, err } @@ -68,7 +68,7 @@ func (d defaultRoleAssigner) UpdateUserRoleAssignment(ctx context.Context, user func (d defaultRoleAssigner) ApplyUserRole(ctx context.Context, user *cs3.User) (*cs3.User, error) { roleIDs, err := loadRolesIDs(ctx, user.Id.OpaqueId, d.roleService) if err != nil { - d.logger.Error().Err(err).Msgf("Could not load roles") + d.logger.Error().Err(err).Msg("Could not load roles") return nil, err } diff --git a/services/proxy/pkg/userroles/oidcroles.go b/services/proxy/pkg/userroles/oidcroles.go index 0d36c922ffd..65a5a2aa016 100644 --- a/services/proxy/pkg/userroles/oidcroles.go +++ b/services/proxy/pkg/userroles/oidcroles.go @@ -120,7 +120,7 @@ func (ra oidcRoleAssigner) UpdateUserRoleAssignment(ctx context.Context, user *c func (ra oidcRoleAssigner) ApplyUserRole(ctx context.Context, user *cs3.User) (*cs3.User, error) { roleIDs, err := loadRolesIDs(ctx, user.Id.OpaqueId, ra.roleService) if err != nil { - ra.logger.Error().Err(err).Msgf("Could not load roles") + ra.logger.Error().Err(err).Msg("Could not load roles") return nil, err } diff --git a/services/search/pkg/content/cs3.go b/services/search/pkg/content/cs3.go index 9d2332fdc82..4a90e85a964 100644 --- a/services/search/pkg/content/cs3.go +++ b/services/search/pkg/content/cs3.go @@ -11,24 +11,25 @@ import ( rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/v2/ocis-pkg/log" ) type cs3 struct { - httpClient http.Client - gwClient gateway.GatewayAPIClient - logger log.Logger + httpClient http.Client + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + logger log.Logger } -func newCS3Retriever(client gateway.GatewayAPIClient, logger log.Logger, insecure bool) cs3 { +func newCS3Retriever(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], logger log.Logger, insecure bool) cs3 { return cs3{ httpClient: http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure}, //nolint:gosec }, }, - gwClient: client, - logger: logger, + gatewaySelector: gatewaySelector, + logger: logger, } } @@ -40,7 +41,13 @@ func (s cs3) Retrieve(ctx context.Context, rID *provider.ResourceId) (io.ReadClo return nil, fmt.Errorf("context without %s", revactx.TokenHeader) } - res, err := s.gwClient.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{Ref: &provider.Reference{ResourceId: rID, Path: "."}}) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + s.logger.Error().Err(err).Msg("could not get reva gatewayClient") + return nil, err + } + + res, err := gatewayClient.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{Ref: &provider.Reference{ResourceId: rID, Path: "."}}) if err != nil { return nil, err } diff --git a/services/search/pkg/content/tika.go b/services/search/pkg/content/tika.go index 46283a4c13a..1979cdbcdb3 100644 --- a/services/search/pkg/content/tika.go +++ b/services/search/pkg/content/tika.go @@ -8,6 +8,7 @@ import ( "github.com/bbalet/stopwords" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/google/go-tika/tika" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/search/pkg/config" @@ -23,7 +24,7 @@ type Tika struct { } // NewTikaExtractor creates a new Tika instance. -func NewTikaExtractor(gw gateway.GatewayAPIClient, logger log.Logger, cfg *config.Config) (*Tika, error) { +func NewTikaExtractor(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], logger log.Logger, cfg *config.Config) (*Tika, error) { basic, err := NewBasicExtractor(logger) if err != nil { return nil, err @@ -38,7 +39,7 @@ func NewTikaExtractor(gw gateway.GatewayAPIClient, logger log.Logger, cfg *confi return &Tika{ Basic: basic, - Retriever: newCS3Retriever(gw, logger, cfg.Extractor.CS3AllowInsecure), + Retriever: newCS3Retriever(gatewaySelector, logger, cfg.Extractor.CS3AllowInsecure), tika: tika.NewClient(nil, cfg.Extractor.Tika.TikaURL), contentExtractionSizeLimit: cfg.ContentExtractionSizeLimit, }, nil diff --git a/services/search/pkg/search/search.go b/services/search/pkg/search/search.go index 4dbd892414b..b4a1ad749c4 100644 --- a/services/search/pkg/search/search.go +++ b/services/search/pkg/search/search.go @@ -12,6 +12,7 @@ import ( provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/errtypes" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/utils" "github.com/owncloud/ocis/v2/ocis-pkg/log" searchmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0" @@ -20,12 +21,17 @@ import ( ) // ResolveReference makes sure the path is relative to the space root -func ResolveReference(ctx context.Context, ref *provider.Reference, ri *provider.ResourceInfo, gw gateway.GatewayAPIClient) (*provider.Reference, error) { +func ResolveReference(ctx context.Context, ref *provider.Reference, ri *provider.ResourceInfo, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) (*provider.Reference, error) { if ref.GetResourceId().GetOpaqueId() == ref.GetResourceId().GetSpaceId() { return ref, nil } - gpRes, err := gw.GetPath(ctx, &provider.GetPathRequest{ + gatewayClient, err := gatewaySelector.Next() + if err != nil { + return nil, err + } + + gpRes, err := gatewayClient.GetPath(ctx, &provider.GetPathRequest{ ResourceId: ri.Id, }) if err != nil || gpRes.Status.Code != rpc.Code_CODE_OK { @@ -61,9 +67,15 @@ func logDocCount(engine engine.Engine, logger log.Logger) { logger.Debug().Interface("count", c).Msg("new document count") } -func getAuthContext(owner *user.User, gw gateway.GatewayAPIClient, secret string, logger log.Logger) (context.Context, error) { +func getAuthContext(owner *user.User, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], secret string, logger log.Logger) (context.Context, error) { + gatewayClient, err := gatewaySelector.Next() + if err != nil { + logger.Error().Err(err).Msg("could not get reva gatewayClient") + return nil, err + } + ownerCtx := ctxpkg.ContextSetUser(context.Background(), owner) - authRes, err := gw.Authenticate(ownerCtx, &gateway.AuthenticateRequest{ + authRes, err := gatewayClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{ Type: "machine", ClientId: "userid:" + owner.GetId().GetOpaqueId(), ClientSecret: secret, @@ -81,8 +93,13 @@ func getAuthContext(owner *user.User, gw gateway.GatewayAPIClient, secret string return metadata.AppendToOutgoingContext(ownerCtx, ctxpkg.TokenHeader, authRes.Token), nil } -func statResource(ctx context.Context, ref *provider.Reference, gw gateway.GatewayAPIClient, logger log.Logger) (*provider.StatResponse, error) { - res, err := gw.Stat(ctx, &provider.StatRequest{Ref: ref}) +func statResource(ctx context.Context, ref *provider.Reference, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], logger log.Logger) (*provider.StatResponse, error) { + gatewayClient, err := gatewaySelector.Next() + if err != nil { + return nil, err + } + + res, err := gatewayClient.Stat(ctx, &provider.StatRequest{Ref: ref}) if err != nil { logger.Error().Err(err).Msg("failed to stat the moved resource") return nil, err diff --git a/services/search/pkg/search/search_suite_test.go b/services/search/pkg/search/search_suite_test.go index 9b2060ec0da..75873d6daaa 100644 --- a/services/search/pkg/search/search_suite_test.go +++ b/services/search/pkg/search/search_suite_test.go @@ -3,10 +3,23 @@ package search_test import ( "testing" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" + mRegistry "go-micro.dev/v4/registry" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) +func init() { + registry.Configure("memory") + r := registry.GetRegistry() + service := registry.BuildGRPCService("com.owncloud.api.gateway", "", "", "") + service.Nodes = []*mRegistry.Node{{ + Address: "any", + }} + + _ = r.Register(service) +} func TestSearch(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Search Suite") diff --git a/services/search/pkg/search/service.go b/services/search/pkg/search/service.go index 8407ae67f12..9367d7e49d0 100644 --- a/services/search/pkg/search/service.go +++ b/services/search/pkg/search/service.go @@ -13,6 +13,7 @@ import ( rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/errtypes" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" sdk "github.com/cs3org/reva/v2/pkg/sdk/common" "github.com/cs3org/reva/v2/pkg/storage/utils/walker" "github.com/cs3org/reva/v2/pkg/storagespace" @@ -46,23 +47,23 @@ type Searcher interface { // Service is responsible for indexing spaces and pass on a search // to it's underlying engine. type Service struct { - logger log.Logger - gateway gateway.GatewayAPIClient - engine engine.Engine - extractor content.Extractor - secret string + logger log.Logger + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + engine engine.Engine + extractor content.Extractor + secret string } var errSkipSpace error // NewService creates a new Provider instance. -func NewService(gw gateway.GatewayAPIClient, eng engine.Engine, extractor content.Extractor, logger log.Logger, cfg *config.Config) *Service { +func NewService(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], eng engine.Engine, extractor content.Extractor, logger log.Logger, cfg *config.Config) *Service { var s = &Service{ - gateway: gw, - engine: eng, - secret: cfg.MachineAuthAPIKey, - logger: logger, - extractor: extractor, + gatewaySelector: gatewaySelector, + engine: eng, + secret: cfg.MachineAuthAPIKey, + logger: logger, + extractor: extractor, } return s @@ -75,7 +76,12 @@ func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*se } s.logger.Debug().Str("query", req.Query).Msg("performing a search") - listSpacesRes, err := s.gateway.ListStorageSpaces(ctx, &provider.ListStorageSpacesRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + listSpacesRes, err := gatewayClient.ListStorageSpaces(ctx, &provider.ListStorageSpacesRequest{ Filters: []*provider.ListStorageSpacesRequest_Filter{ { Type: provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE, @@ -227,7 +233,13 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest, s.logger.Warn().Interface("space", space).Msg("could not find mountpoint space for grant space") return nil, errSkipSpace } - gpRes, err := s.gateway.GetPath(ctx, &provider.GetPathRequest{ + + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + gpRes, err := gatewayClient.GetPath(ctx, &provider.GetPathRequest{ ResourceId: space.Root, }) if err != nil { @@ -296,7 +308,7 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest, // IndexSpace (re)indexes all resources of a given space. func (s *Service) IndexSpace(spaceID *provider.StorageSpaceId, uID *user.UserId) error { - ownerCtx, err := getAuthContext(&user.User{Id: uID}, s.gateway, s.secret, s.logger) + ownerCtx, err := getAuthContext(&user.User{Id: uID}, s.gatewaySelector, s.secret, s.logger) if err != nil { return err } @@ -312,7 +324,7 @@ func (s *Service) IndexSpace(spaceID *provider.StorageSpaceId, uID *user.UserId) } rootID.OpaqueId = rootID.SpaceId - w := walker.NewWalker(s.gateway) + w := walker.NewWalker(s.gatewaySelector) err = w.Walk(ownerCtx, &rootID, func(wd string, info *provider.ResourceInfo, err error) error { if err != nil { s.logger.Error().Err(err).Msg("error walking the tree") @@ -426,17 +438,17 @@ func (s *Service) MoveItem(ref *provider.Reference, uID *user.UserId) { } func (s *Service) resInfo(uID *user.UserId, ref *provider.Reference) (context.Context, *provider.StatResponse, string) { - ownerCtx, err := getAuthContext(&user.User{Id: uID}, s.gateway, s.secret, s.logger) + ownerCtx, err := getAuthContext(&user.User{Id: uID}, s.gatewaySelector, s.secret, s.logger) if err != nil { return nil, nil, "" } - statRes, err := statResource(ownerCtx, ref, s.gateway, s.logger) + statRes, err := statResource(ownerCtx, ref, s.gatewaySelector, s.logger) if err != nil { return nil, nil, "" } - r, err := ResolveReference(ownerCtx, ref, statRes.GetInfo(), s.gateway) + r, err := ResolveReference(ownerCtx, ref, statRes.GetInfo(), s.gatewaySelector) if err != nil { return nil, nil, "" } diff --git a/services/search/pkg/search/service_test.go b/services/search/pkg/search/service_test.go index fc374c7e16f..b6b0eb2c24f 100644 --- a/services/search/pkg/search/service_test.go +++ b/services/search/pkg/search/service_test.go @@ -8,6 +8,7 @@ import ( sprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -20,17 +21,19 @@ import ( engineMocks "github.com/owncloud/ocis/v2/services/search/pkg/engine/mocks" "github.com/owncloud/ocis/v2/services/search/pkg/search" "github.com/stretchr/testify/mock" + "google.golang.org/grpc" ) var _ = Describe("Searchprovider", func() { var ( - s search.Searcher - extractor *contentMocks.Extractor - gw *cs3mocks.GatewayAPIClient - indexClient *engineMocks.Engine - ctx context.Context - logger = log.NewLogger() - user = &userv1beta1.User{ + s search.Searcher + extractor *contentMocks.Extractor + gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + indexClient *engineMocks.Engine + ctx context.Context + logger = log.NewLogger() + user = &userv1beta1.User{ Id: &userv1beta1.UserId{ OpaqueId: "user", }, @@ -70,22 +73,31 @@ var _ = Describe("Searchprovider", func() { ) BeforeEach(func() { + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") + gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + ctx = context.Background() - gw = &cs3mocks.GatewayAPIClient{} indexClient = &engineMocks.Engine{} extractor = &contentMocks.Extractor{} - s = search.NewService(gw, indexClient, extractor, logger, &config.Config{}) + s = search.NewService(gatewaySelector, indexClient, extractor, logger, &config.Config{}) - gw.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{ + gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{ Status: status.NewOK(ctx), Token: "authtoken", }, nil) - gw.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{ + gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{ Status: status.NewOK(context.Background()), Info: ri, }, nil) - gw.On("GetPath", mock.Anything, mock.MatchedBy(func(req *sprovider.GetPathRequest) bool { + gatewayClient.On("GetPath", mock.Anything, mock.MatchedBy(func(req *sprovider.GetPathRequest) bool { return req.ResourceId.OpaqueId == ri.Id.OpaqueId })).Return(&sprovider.GetPathResponse{ Status: status.NewOK(context.Background()), @@ -96,14 +108,14 @@ var _ = Describe("Searchprovider", func() { Describe("New", func() { It("returns a new instance", func() { - s := search.NewService(gw, indexClient, extractor, logger, &config.Config{}) + s := search.NewService(gatewaySelector, indexClient, extractor, logger, &config.Config{}) Expect(s).ToNot(BeNil()) }) }) Describe("IndexSpace", func() { It("walks the space and indexes all files", func() { - gw.On("GetUserByClaim", mock.Anything, mock.Anything).Return(&userv1beta1.GetUserByClaimResponse{ + gatewayClient.On("GetUserByClaim", mock.Anything, mock.Anything).Return(&userv1beta1.GetUserByClaimResponse{ Status: status.NewOK(context.Background()), User: user, }, nil) @@ -127,7 +139,7 @@ var _ = Describe("Searchprovider", func() { Context("with a personal space", func() { BeforeEach(func() { - gw.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{ + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{ Status: status.NewOK(ctx), StorageSpaces: []*sprovider.StorageSpace{personalSpace}, }, nil) @@ -210,14 +222,14 @@ var _ = Describe("Searchprovider", func() { }, }, } - gw.On("GetPath", mock.Anything, mock.Anything).Return(&sprovider.GetPathResponse{ + gatewayClient.On("GetPath", mock.Anything, mock.Anything).Return(&sprovider.GetPathResponse{ Status: status.NewOK(ctx), Path: "/grant/path", }, nil) }) It("searches the received spaces", func() { - gw.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{ + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{ Status: status.NewOK(ctx), StorageSpaces: []*sprovider.StorageSpace{grantSpace, mountpointSpace}, }, nil) @@ -259,7 +271,7 @@ var _ = Describe("Searchprovider", func() { Context("when searching both spaces", func() { BeforeEach(func() { - gw.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{ + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{ Status: status.NewOK(ctx), StorageSpaces: []*sprovider.StorageSpace{personalSpace, grantSpace, mountpointSpace}, }, nil) diff --git a/services/search/pkg/service/grpc/v0/service.go b/services/search/pkg/service/grpc/v0/service.go index 38e55d133c6..1cb9e89c9d7 100644 --- a/services/search/pkg/service/grpc/v0/service.go +++ b/services/search/pkg/service/grpc/v0/service.go @@ -19,6 +19,7 @@ import ( "github.com/jellydator/ttlcache/v2" ociscrypto "github.com/owncloud/ocis/v2/ocis-pkg/crypto" "github.com/owncloud/ocis/v2/ocis-pkg/log" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" v0 "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0" searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0" "github.com/owncloud/ocis/v2/services/search/pkg/content" @@ -55,9 +56,9 @@ func NewHandler(opts ...Option) (searchsvc.SearchProviderHandler, func(), error) } // initialize gateway - gw, err := pool.GetGatewayServiceClient(cfg.Reva.Address) + selector, err := pool.GatewaySelector(cfg.Reva.Address, pool.WithRegistry(registry.GetRegistry())) if err != nil { - logger.Fatal().Err(err).Str("addr", cfg.Reva.Address).Msg("could not get reva client") + logger.Fatal().Err(err).Msg("could not get reva gateway selector") return nil, teardown, err } // initialize search content extractor @@ -68,7 +69,7 @@ func NewHandler(opts ...Option) (searchsvc.SearchProviderHandler, func(), error) return nil, teardown, err } case "tika": - if extractor, err = content.NewTikaExtractor(gw, logger, cfg); err != nil { + if extractor, err = content.NewTikaExtractor(selector, logger, cfg); err != nil { return nil, teardown, err } default: @@ -106,7 +107,7 @@ func NewHandler(opts ...Option) (searchsvc.SearchProviderHandler, func(), error) return nil, teardown, err } - ss := search.NewService(gw, eng, extractor, logger, cfg) + ss := search.NewService(selector, eng, extractor, logger, cfg) // setup event handling if err := search.HandleEvents(ss, bus, logger, cfg); err != nil { diff --git a/services/settings/pkg/config/defaults/defaultconfig.go b/services/settings/pkg/config/defaults/defaultconfig.go index d5fd3ab5ed1..64866ae3f8e 100644 --- a/services/settings/pkg/config/defaults/defaultconfig.go +++ b/services/settings/pkg/config/defaults/defaultconfig.go @@ -53,8 +53,8 @@ func DefaultConfig() *config.Config { DataPath: path.Join(defaults.BaseDataPath(), "settings"), SetupDefaultAssignments: false, Metadata: config.Metadata{ - GatewayAddress: "127.0.0.1:9215", // system storage - StorageAddress: "127.0.0.1:9215", + GatewayAddress: "com.owncloud.api.storage-system", + StorageAddress: "com.owncloud.api.storage-system", SystemUserIDP: "internal", Cache: &config.Cache{ Store: "memory", diff --git a/services/sharing/pkg/command/server.go b/services/sharing/pkg/command/server.go index 6154ff20b96..11a521b0cce 100644 --- a/services/sharing/pkg/command/server.go +++ b/services/sharing/pkg/command/server.go @@ -11,7 +11,7 @@ import ( "github.com/gofrs/uuid" "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/sync" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/sharing/pkg/config" @@ -55,12 +55,16 @@ func Server(cfg *config.Config) *cli.Command { } } - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + gr.Add(func() error { + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.SharingConfigFromStruct(cfg) + reg := registry.GetRegistry() - rcfg := revaconfig.SharingConfigFromStruct(cfg) + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) - gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) return nil }, func(err error) { logger.Error(). @@ -90,15 +94,9 @@ func Server(cfg *config.Config) *cli.Command { sync.Trap(&gr, cancel) } - if err := external.RegisterGRPCEndpoint( - ctx, - cfg.GRPC.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.GRPC.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the grpc endpoint") + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") } return gr.Run() diff --git a/services/sharing/pkg/config/defaults/defaultconfig.go b/services/sharing/pkg/config/defaults/defaultconfig.go index 044e4694a36..3a2d450d2bf 100644 --- a/services/sharing/pkg/config/defaults/defaultconfig.go +++ b/services/sharing/pkg/config/defaults/defaultconfig.go @@ -41,11 +41,11 @@ func DefaultConfig() *config.Config { File: filepath.Join(defaults.BaseDataPath(), "storage", "shares.json"), }, CS3: config.UserSharingCS3Driver{ - ProviderAddr: "127.0.0.1:9215", // system storage + ProviderAddr: "com.owncloud.api.storage-system", SystemUserIDP: "internal", }, JSONCS3: config.UserSharingJSONCS3Driver{ - ProviderAddr: "127.0.0.1:9215", // system storage + ProviderAddr: "com.owncloud.api.storage-system", SystemUserIDP: "internal", }, OwnCloudSQL: config.UserSharingOwnCloudSQLDriver{ @@ -61,11 +61,11 @@ func DefaultConfig() *config.Config { File: filepath.Join(defaults.BaseDataPath(), "storage", "publicshares.json"), }, CS3: config.PublicSharingCS3Driver{ - ProviderAddr: "127.0.0.1:9215", // system storage + ProviderAddr: "com.owncloud.api.storage-system", // system storage SystemUserIDP: "internal", }, JSONCS3: config.PublicSharingJSONCS3Driver{ - ProviderAddr: "127.0.0.1:9215", // system storage + ProviderAddr: "com.owncloud.api.storage-system", // system storage SystemUserIDP: "internal", }, // TODO implement and add owncloudsql publicshare driver diff --git a/services/storage-publiclink/pkg/command/server.go b/services/storage-publiclink/pkg/command/server.go index 1c731ee52d7..9eb75d21d6d 100644 --- a/services/storage-publiclink/pkg/command/server.go +++ b/services/storage-publiclink/pkg/command/server.go @@ -10,7 +10,7 @@ import ( "github.com/gofrs/uuid" "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/sync" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/storage-publiclink/pkg/config" @@ -42,12 +42,16 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + gr.Add(func() error { + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.StoragePublicLinkConfigFromStruct(cfg) + reg := registry.GetRegistry() - rcfg := revaconfig.StoragePublicLinkConfigFromStruct(cfg) + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) - gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) return nil }, func(err error) { logger.Error(). @@ -77,15 +81,9 @@ func Server(cfg *config.Config) *cli.Command { sync.Trap(&gr, cancel) } - if err := external.RegisterGRPCEndpoint( - ctx, - cfg.GRPC.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.GRPC.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the grpc endpoint") + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") } return gr.Run() diff --git a/services/storage-shares/pkg/command/server.go b/services/storage-shares/pkg/command/server.go index d3e507e2bb5..78e546f0e88 100644 --- a/services/storage-shares/pkg/command/server.go +++ b/services/storage-shares/pkg/command/server.go @@ -10,7 +10,7 @@ import ( "github.com/gofrs/uuid" "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/sync" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/storage-shares/pkg/config" @@ -42,12 +42,16 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + gr.Add(func() error { + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.StorageSharesConfigFromStruct(cfg) + reg := registry.GetRegistry() - rcfg := revaconfig.StorageSharesConfigFromStruct(cfg) + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) - gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) return nil }, func(err error) { logger.Error(). @@ -77,15 +81,9 @@ func Server(cfg *config.Config) *cli.Command { sync.Trap(&gr, cancel) } - if err := external.RegisterGRPCEndpoint( - ctx, - cfg.GRPC.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.GRPC.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the grpc endpoint") + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") } return gr.Run() diff --git a/services/storage-shares/pkg/config/defaults/defaultconfig.go b/services/storage-shares/pkg/config/defaults/defaultconfig.go index 44680f424ad..202cc0241eb 100644 --- a/services/storage-shares/pkg/config/defaults/defaultconfig.go +++ b/services/storage-shares/pkg/config/defaults/defaultconfig.go @@ -34,7 +34,7 @@ func DefaultConfig() *config.Config { Reva: shared.DefaultRevaConfig(), MountID: "7639e57c-4433-4a12-8201-722fd0009154", ReadOnly: false, - SharesProviderEndpoint: "localhost:9150", + SharesProviderEndpoint: "com.owncloud.api.sharing", } } diff --git a/services/storage-system/pkg/command/server.go b/services/storage-system/pkg/command/server.go index de6095a66da..03e5ab82574 100644 --- a/services/storage-system/pkg/command/server.go +++ b/services/storage-system/pkg/command/server.go @@ -10,7 +10,7 @@ import ( "github.com/gofrs/uuid" "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/sync" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/storage-system/pkg/config" @@ -42,12 +42,16 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + gr.Add(func() error { + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.StorageSystemFromStruct(cfg) + reg := registry.GetRegistry() - rcfg := revaconfig.StorageSystemFromStruct(cfg) + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) - gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) return nil }, func(err error) { logger.Error(). @@ -77,26 +81,14 @@ func Server(cfg *config.Config) *cli.Command { sync.Trap(&gr, cancel) } - if err := external.RegisterGRPCEndpoint( - ctx, - cfg.GRPC.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.GRPC.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the grpc endpoint") + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") } - if err := external.RegisterHTTPEndpoint( - ctx, - cfg.HTTP.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.HTTP.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the http endpoint") + httpScv := registry.BuildHTTPService(cfg.HTTP.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.HTTP.Addr, version.GetString()) + if err := registry.RegisterService(ctx, httpScv, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the http service") } return gr.Run() diff --git a/services/storage-system/pkg/revaconfig/config.go b/services/storage-system/pkg/revaconfig/config.go index 3f9ebd1b2cb..7f079e768a9 100644 --- a/services/storage-system/pkg/revaconfig/config.go +++ b/services/storage-system/pkg/revaconfig/config.go @@ -34,12 +34,12 @@ func StorageSystemFromStruct(cfg *config.Config) map[string]interface{} { "services": map[string]interface{}{ "gateway": map[string]interface{}{ // registries are located on the gateway - "authregistrysvc": cfg.GRPC.Addr, - "storageregistrysvc": cfg.GRPC.Addr, + "authregistrysvc": "com.owncloud.api.storage-system", + "storageregistrysvc": "com.owncloud.api.storage-system", // user metadata is located on the users services - "userprovidersvc": cfg.GRPC.Addr, - "groupprovidersvc": cfg.GRPC.Addr, - "permissionssvc": cfg.GRPC.Addr, + "userprovidersvc": "com.owncloud.api.storage-system", + "groupprovidersvc": "com.owncloud.api.storage-system", + "permissionssvc": "com.owncloud.api.storage-system", // other "disable_home_creation_on_login": true, // metadata manually creates a space // metadata always uses the simple upload, so no transfer secret or datagateway needed @@ -69,7 +69,7 @@ func StorageSystemFromStruct(cfg *config.Config) map[string]interface{} { "drivers": map[string]interface{}{ "static": map[string]interface{}{ "rules": map[string]interface{}{ - "machine": cfg.GRPC.Addr, + "machine": "com.owncloud.api.storage-system", }, }, }, @@ -79,7 +79,7 @@ func StorageSystemFromStruct(cfg *config.Config) map[string]interface{} { "auth_managers": map[string]interface{}{ "machine": map[string]interface{}{ "api_key": cfg.SystemUserAPIKey, - "gateway_addr": cfg.GRPC.Addr, + "gateway_addr": "com.owncloud.api.storage-system", }, }, }, @@ -95,7 +95,7 @@ func StorageSystemFromStruct(cfg *config.Config) map[string]interface{} { "static": map[string]interface{}{ "rules": map[string]interface{}{ "/": map[string]interface{}{ - "address": cfg.GRPC.Addr, + "address": "com.owncloud.api.storage-system", }, }, }, @@ -161,7 +161,7 @@ func metadataDrivers(cfg *config.Config) map[string]interface{} { "user_layout": "{{.Id.OpaqueId}}", "treetime_accounting": false, "treesize_accounting": false, - "permissionssvc": cfg.GRPC.Addr, + "permissionssvc": "com.owncloud.api.storage-system", "max_acquire_lock_cycles": cfg.Drivers.OCIS.MaxAcquireLockCycles, "lock_cycle_duration_factor": cfg.Drivers.OCIS.LockCycleDurationFactor, "statcache": map[string]interface{}{ diff --git a/services/storage-users/pkg/command/server.go b/services/storage-users/pkg/command/server.go index ffff3afe21b..d7fa6b98631 100644 --- a/services/storage-users/pkg/command/server.go +++ b/services/storage-users/pkg/command/server.go @@ -11,7 +11,7 @@ import ( "github.com/gofrs/uuid" "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/sync" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/storage-users/pkg/config" @@ -44,12 +44,16 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + gr.Add(func() error { + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.StorageUsersConfigFromStruct(cfg) + reg := registry.GetRegistry() - rcfg := revaconfig.StorageUsersConfigFromStruct(cfg) + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) - gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) return nil }, func(err error) { logger.Error(). @@ -79,15 +83,9 @@ func Server(cfg *config.Config) *cli.Command { sync.Trap(&gr, cancel) } - if err := external.RegisterGRPCEndpoint( - ctx, - cfg.GRPC.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.GRPC.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the grpc endpoint") + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") } { @@ -96,12 +94,12 @@ func Server(cfg *config.Config) *cli.Command { logger.Fatal().Err(err).Msg("can't connect to nats") } - gw, err := pool.GetGatewayServiceClient(cfg.Reva.Address) + selector, err := pool.GatewaySelector(cfg.Reva.Address, pool.WithRegistry(registry.GetRegistry())) if err != nil { return err } - eventSVC, err := event.NewService(gw, stream, logger, *cfg) + eventSVC, err := event.NewService(selector, stream, logger, *cfg) if err != nil { logger.Fatal().Err(err).Msg("can't create event service") } diff --git a/services/storage-users/pkg/config/defaults/defaultconfig.go b/services/storage-users/pkg/config/defaults/defaultconfig.go index 30a174f3397..f8673c23e11 100644 --- a/services/storage-users/pkg/config/defaults/defaultconfig.go +++ b/services/storage-users/pkg/config/defaults/defaultconfig.go @@ -58,7 +58,7 @@ func DefaultConfig() *config.Config { DBHost: "", DBPort: 3306, DBName: "owncloud", - UsersProviderEndpoint: "localhost:9144", + UsersProviderEndpoint: "com.owncloud.api.users", }, S3NG: config.S3NGDriver{ MetadataBackend: "messagepack", @@ -68,7 +68,7 @@ func DefaultConfig() *config.Config { Region: "default", PersonalSpaceAliasTemplate: "{{.SpaceType}}/{{.User.Username | lower}}", GeneralSpaceAliasTemplate: "{{.SpaceType}}/{{.SpaceName | replace \" \" \"-\" | lower}}", - PermissionsEndpoint: "127.0.0.1:9191", + PermissionsEndpoint: "com.owncloud.api.settings", MaxAcquireLockCycles: 20, LockCycleDurationFactor: 30, }, @@ -79,7 +79,7 @@ func DefaultConfig() *config.Config { UserLayout: "{{.Id.OpaqueId}}", PersonalSpaceAliasTemplate: "{{.SpaceType}}/{{.User.Username | lower}}", GeneralSpaceAliasTemplate: "{{.SpaceType}}/{{.SpaceName | replace \" \" \"-\" | lower}}", - PermissionsEndpoint: "127.0.0.1:9191", + PermissionsEndpoint: "com.owncloud.api.settings", MaxAcquireLockCycles: 20, LockCycleDurationFactor: 30, }, diff --git a/services/storage-users/pkg/event/service.go b/services/storage-users/pkg/event/service.go index b546a13b1db..91384f75d59 100644 --- a/services/storage-users/pkg/event/service.go +++ b/services/storage-users/pkg/event/service.go @@ -6,6 +6,7 @@ import ( apiGateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" apiUser "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/storage-users/pkg/config" "github.com/owncloud/ocis/v2/services/storage-users/pkg/task" @@ -17,19 +18,19 @@ const ( // Service wraps all common logic that is needed to react to incoming events. type Service struct { - gatewayClient apiGateway.GatewayAPIClient - eventStream events.Stream - logger log.Logger - config config.Config + gatewaySelector pool.Selectable[apiGateway.GatewayAPIClient] + eventStream events.Stream + logger log.Logger + config config.Config } // NewService prepares and returns a Service implementation. -func NewService(gatewayClient apiGateway.GatewayAPIClient, eventStream events.Stream, logger log.Logger, conf config.Config) (Service, error) { +func NewService(gatewaySelector pool.Selectable[apiGateway.GatewayAPIClient], eventStream events.Stream, logger log.Logger, conf config.Config) (Service, error) { svc := Service{ - gatewayClient: gatewayClient, - eventStream: eventStream, - logger: logger, - config: conf, + gatewaySelector: gatewaySelector, + eventStream: eventStream, + logger: logger, + config: conf, } return svc, nil @@ -69,7 +70,7 @@ func (s Service) Run() error { continue } - if err = task.PurgeTrashBin(executantID, deleteBefore, spaceType, s.gatewayClient, s.config.Commons.MachineAuthAPIKey); err != nil { + if err = task.PurgeTrashBin(executantID, deleteBefore, spaceType, s.gatewaySelector, s.config.Commons.MachineAuthAPIKey); err != nil { errs = append(errs, err) } } diff --git a/services/storage-users/pkg/task/task_suite_test.go b/services/storage-users/pkg/task/task_suite_test.go index dbcb7996a78..5b904e7e302 100644 --- a/services/storage-users/pkg/task/task_suite_test.go +++ b/services/storage-users/pkg/task/task_suite_test.go @@ -3,10 +3,23 @@ package task_test import ( "testing" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" + mRegistry "go-micro.dev/v4/registry" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) +func init() { + registry.Configure("memory") + r := registry.GetRegistry() + service := registry.BuildGRPCService("com.owncloud.api.gateway", "", "", "") + service.Nodes = []*mRegistry.Node{{ + Address: "any", + }} + + _ = r.Register(service) +} func TestTask(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Task Suite") diff --git a/services/storage-users/pkg/task/trash_bin.go b/services/storage-users/pkg/task/trash_bin.go index 354f9cf09d1..daf49fad1ed 100644 --- a/services/storage-users/pkg/task/trash_bin.go +++ b/services/storage-users/pkg/task/trash_bin.go @@ -9,6 +9,7 @@ import ( apiRpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" apiProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/errtypes" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/utils" ) @@ -16,13 +17,18 @@ import ( // the provided executantID must have space access. // removeBefore specifies how long an item must be in the trash-bin to be deleted, // items that stay there for a shorter time are ignored and kept in place. -func PurgeTrashBin(executantID *apiUser.UserId, deleteBefore time.Time, spaceType SpaceType, gwc apiGateway.GatewayAPIClient, machineAuthAPIKey string) error { - executantCtx, _, err := utils.Impersonate(executantID, gwc, machineAuthAPIKey) +func PurgeTrashBin(executantID *apiUser.UserId, deleteBefore time.Time, spaceType SpaceType, gatewaySelector pool.Selectable[apiGateway.GatewayAPIClient], machineAuthAPIKey string) error { + gatewayClient, err := gatewaySelector.Next() if err != nil { return err } - listStorageSpacesResponse, err := gwc.ListStorageSpaces(executantCtx, &apiProvider.ListStorageSpacesRequest{ + executantCtx, _, err := utils.Impersonate(executantID, gatewayClient, machineAuthAPIKey) + if err != nil { + return err + } + + listStorageSpacesResponse, err := gatewayClient.ListStorageSpaces(executantCtx, &apiProvider.ListStorageSpacesRequest{ Filters: []*apiProvider.ListStorageSpacesRequest_Filter{ { Type: apiProvider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE, @@ -77,12 +83,12 @@ func PurgeTrashBin(executantID *apiUser.UserId, deleteBefore time.Time, spaceTyp return fmt.Errorf("can't impersonate space user for space: %s", storageSpace.GetId().GetOpaqueId()) } - impersonatedCtx, _, err := utils.Impersonate(impersonationID, gwc, machineAuthAPIKey) + impersonatedCtx, _, err := utils.Impersonate(impersonationID, gatewayClient, machineAuthAPIKey) if err != nil { return err } - listRecycleResponse, err := gwc.ListRecycle(impersonatedCtx, &apiProvider.ListRecycleRequest{Ref: storageSpaceReference}) + listRecycleResponse, err := gatewayClient.ListRecycle(impersonatedCtx, &apiProvider.ListRecycleRequest{Ref: storageSpaceReference}) if err != nil { return err } @@ -93,7 +99,7 @@ func PurgeTrashBin(executantID *apiUser.UserId, deleteBefore time.Time, spaceTyp continue } - purgeRecycleResponse, err := gwc.PurgeRecycle(impersonatedCtx, &apiProvider.PurgeRecycleRequest{ + purgeRecycleResponse, err := gatewayClient.PurgeRecycle(impersonatedCtx, &apiProvider.PurgeRecycleRequest{ Ref: storageSpaceReference, Key: recycleItem.Key, }) diff --git a/services/storage-users/pkg/task/trash_bin_test.go b/services/storage-users/pkg/task/trash_bin_test.go index 1da8c231d17..1066565c958 100644 --- a/services/storage-users/pkg/task/trash_bin_test.go +++ b/services/storage-users/pkg/task/trash_bin_test.go @@ -4,21 +4,23 @@ import ( "context" "encoding/json" "errors" - "google.golang.org/grpc" "time" apiGateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" apiUser "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" apiRpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" apiProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" apiTypes "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/utils" cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/owncloud/ocis/v2/services/storage-users/pkg/task" "github.com/stretchr/testify/mock" + "google.golang.org/grpc" ) func MustMarshal(v any) []byte { @@ -31,7 +33,8 @@ func MustMarshal(v any) []byte { var _ = Describe("trash", func() { var ( - gwc *cs3mocks.GatewayAPIClient + gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] ctx context.Context now time.Time genericError error @@ -45,7 +48,16 @@ var _ = Describe("trash", func() { ) BeforeEach(func() { - gwc = &cs3mocks.GatewayAPIClient{} + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") + gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + ctx = context.Background() now = time.Now() genericError = errors.New("any") @@ -100,36 +112,36 @@ var _ = Describe("trash", func() { Describe("PurgeTrashBin", func() { It("throws an error if the user cannot authenticate", func() { - gwc.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) - gwc.On("Authenticate", mock.Anything, mock.Anything).Return(nil, genericError) + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) + gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(nil, genericError) - err := task.PurgeTrashBin(user.Id, now, task.Project, gwc, "") + err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "") Expect(err).To(HaveOccurred()) }) It("throws an error if space listing fails", func() { - gwc.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) - gwc.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) - gwc.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(nil, genericError) + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) + gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(nil, genericError) - err := task.PurgeTrashBin(user.Id, now, task.Project, gwc, "") + err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "") Expect(err).To(HaveOccurred()) }) It("throws an error if a personal space user can't be impersonated", func() { listStorageSpacesResponse.StorageSpaces = []*apiProvider.StorageSpace{personalSpace} - gwc.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) - gwc.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) - gwc.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil) + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) + gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil) - err := task.PurgeTrashBin(user.Id, now, task.Project, gwc, "") + err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "") Expect(err).To(MatchError(errors.New("can't impersonate space user for space: personal"))) }) It("throws an error if a project space user can't be impersonated", func() { listStorageSpacesResponse.StorageSpaces = []*apiProvider.StorageSpace{projectSpace} - gwc.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) - gwc.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) - gwc.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil) + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) + gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil) - err := task.PurgeTrashBin(user.Id, now, task.Project, gwc, "") + err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "") Expect(err).To(MatchError(errors.New("can't impersonate space user for space: project"))) }) It("throws an error if a project space has no user with delete permissions", func() { @@ -143,11 +155,11 @@ var _ = Describe("trash", func() { }), }, } - gwc.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) - gwc.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) - gwc.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil) + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) + gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil) - err := task.PurgeTrashBin(user.Id, now, task.Project, gwc, "") + err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "") Expect(err).To(MatchError(errors.New("can't impersonate space user for space: project"))) }) It("only deletes items older than the specified period", func() { @@ -188,17 +200,17 @@ var _ = Describe("trash", func() { }, } - gwc.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) - gwc.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) - gwc.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil) - gwc.On("ListRecycle", mock.Anything, mock.Anything).Return( + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) + gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil) + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil) + gatewayClient.On("ListRecycle", mock.Anything, mock.Anything).Return( func(_ context.Context, req *apiProvider.ListRecycleRequest, _ ...grpc.CallOption) *apiProvider.ListRecycleResponse { return &apiProvider.ListRecycleResponse{ RecycleItems: recycleItems[req.Ref.ResourceId.OpaqueId], } }, nil, ) - gwc.On("PurgeRecycle", mock.Anything, mock.Anything).Return( + gatewayClient.On("PurgeRecycle", mock.Anything, mock.Anything).Return( func(_ context.Context, req *apiProvider.PurgeRecycleRequest, _ ...grpc.CallOption) *apiProvider.PurgeRecycleResponse { var items []*apiProvider.RecycleItem for _, item := range recycleItems[req.Ref.ResourceId.OpaqueId] { @@ -219,7 +231,7 @@ var _ = Describe("trash", func() { }, nil, ) - err := task.PurgeTrashBin(user.Id, now, task.Project, gwc, "") + err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "") Expect(err).To(BeNil()) Expect(recycleItems["personal"]).To(HaveLen(2)) Expect(recycleItems["project"]).To(HaveLen(2)) diff --git a/services/thumbnails/pkg/server/grpc/server.go b/services/thumbnails/pkg/server/grpc/server.go index a783d18fa67..26d10bc71f6 100644 --- a/services/thumbnails/pkg/server/grpc/server.go +++ b/services/thumbnails/pkg/server/grpc/server.go @@ -2,6 +2,7 @@ package grpc import ( "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/version" thumbnailssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/thumbnails/v0" @@ -41,12 +42,14 @@ func NewService(opts ...Option) grpc.Service { options.Logger.Error().Err(err).Msg("could not get gateway client tls mode") return grpc.Service{} } - gc, err := pool.GetGatewayServiceClient(tconf.RevaGateway, + + gatewaySelector, err := pool.GatewaySelector(tconf.RevaGateway, pool.WithTLSCACert(options.Config.GRPCClientTLS.CACert), pool.WithTLSMode(tm), + pool.WithRegistry(registry.GetRegistry()), ) if err != nil { - options.Logger.Error().Err(err).Msg("could not get gateway client") + options.Logger.Error().Err(err).Msg("could not get gateway selector") return grpc.Service{} } var thumbnail decorators.DecoratedService @@ -61,8 +64,8 @@ func NewService(opts ...Option) grpc.Service { options.Logger, ), ), - svc.CS3Source(imgsource.NewCS3Source(tconf, gc)), - svc.CS3Client(gc), + svc.CS3Source(imgsource.NewCS3Source(tconf, gatewaySelector)), + svc.GatewaySelector(gatewaySelector), ) thumbnail = decorators.NewInstrument(thumbnail, options.Metrics) thumbnail = decorators.NewLogging(thumbnail, options.Logger) diff --git a/services/thumbnails/pkg/service/grpc/v0/option.go b/services/thumbnails/pkg/service/grpc/v0/option.go index 19b341c5804..00cf0eeab39 100644 --- a/services/thumbnails/pkg/service/grpc/v0/option.go +++ b/services/thumbnails/pkg/service/grpc/v0/option.go @@ -4,7 +4,7 @@ import ( "net/http" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" - + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/config" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/thumbnail/imgsource" @@ -22,7 +22,7 @@ type Options struct { ThumbnailStorage storage.Storage ImageSource imgsource.Source CS3Source imgsource.Source - CS3Client gateway.GatewayAPIClient + GatewaySelector pool.Selectable[gateway.GatewayAPIClient] } // newOptions initializes the available default options. @@ -71,14 +71,16 @@ func ThumbnailSource(val imgsource.Source) Option { } } +// CS3Source provides a function to set the CS3Source option func CS3Source(val imgsource.Source) Option { return func(o *Options) { o.CS3Source = val } } -func CS3Client(c gateway.GatewayAPIClient) Option { +// GatewaySelector adds a grpc client selector for the gateway service +func GatewaySelector(val pool.Selectable[gateway.GatewayAPIClient]) Option { return func(o *Options) { - o.CS3Client = c + o.GatewaySelector = val } } diff --git a/services/thumbnails/pkg/service/grpc/v0/service.go b/services/thumbnails/pkg/service/grpc/v0/service.go index 541e56bca78..070613b0c03 100644 --- a/services/thumbnails/pkg/service/grpc/v0/service.go +++ b/services/thumbnails/pkg/service/grpc/v0/service.go @@ -12,6 +12,7 @@ import ( rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" "github.com/golang-jwt/jwt/v4" @@ -45,7 +46,7 @@ func NewService(opts ...Option) decorators.DecoratedService { webdavSource: options.ImageSource, cs3Source: options.CS3Source, logger: logger, - cs3Client: options.CS3Client, + selector: options.GatewaySelector, preprocessorOpts: PreprocessorOpts{ TxtFontFileMap: options.Config.Thumbnail.FontMapFile, }, @@ -65,10 +66,11 @@ type Thumbnail struct { webdavSource imgsource.Source cs3Source imgsource.Source logger log.Logger - cs3Client gateway.GatewayAPIClient + selector pool.Selectable[gateway.GatewayAPIClient] preprocessorOpts PreprocessorOpts } +// PreprocessorOpts holds the options for the preprocessor type PreprocessorOpts struct { TxtFontFileMap string } @@ -161,21 +163,24 @@ func (g Thumbnail) handleWebdavSource(ctx context.Context, req *thumbnailssvc.Ge } var auth, statPath string - if src.IsPublicLink { q := imgURL.Query() var rsp *gateway.AuthenticateResponse + client, err := g.selector.Next() + if err != nil { + return "", merrors.InternalServerError(g.serviceID, "could not select next gateway client: %s", err.Error()) + } if q.Get("signature") != "" && q.Get("expiration") != "" { // Handle pre-signed public links sig := q.Get("signature") exp := q.Get("expiration") - rsp, err = g.cs3Client.Authenticate(ctx, &gateway.AuthenticateRequest{ + rsp, err = client.Authenticate(ctx, &gateway.AuthenticateRequest{ Type: "publicshares", ClientId: src.PublicLinkToken, ClientSecret: strings.Join([]string{"signature", sig, exp}, "|"), }) } else { - rsp, err = g.cs3Client.Authenticate(ctx, &gateway.AuthenticateRequest{ + rsp, err = client.Authenticate(ctx, &gateway.AuthenticateRequest{ Type: "publicshares", ClientId: src.PublicLinkToken, // We pass an empty password because we expect non pre-signed public links @@ -248,8 +253,12 @@ func (g Thumbnail) stat(path, auth string) (*provider.StatResponse, error) { } } + client, err := g.selector.Next() + if err != nil { + return nil, merrors.InternalServerError(g.serviceID, "could not select next gateway client: %s", err.Error()) + } req := &provider.StatRequest{Ref: &ref} - rsp, err := g.cs3Client.Stat(ctx, req) + rsp, err := client.Stat(ctx, req) if err != nil { g.logger.Error().Err(err).Str("path", path).Msg("could not stat file") return nil, merrors.InternalServerError(g.serviceID, "could not stat file: %s", err.Error()) diff --git a/services/thumbnails/pkg/thumbnail/imgsource/cs3.go b/services/thumbnails/pkg/thumbnail/imgsource/cs3.go index be17365c01f..d17648e5f3d 100644 --- a/services/thumbnails/pkg/thumbnail/imgsource/cs3.go +++ b/services/thumbnails/pkg/thumbnail/imgsource/cs3.go @@ -11,6 +11,7 @@ import ( rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/rhttp" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/config" @@ -24,15 +25,17 @@ const ( TokenTransportHeader = "X-Reva-Transfer" ) +// CS3 implements a CS3 image source type CS3 struct { - client gateway.GatewayAPIClient - insecure bool + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + insecure bool } -func NewCS3Source(cfg config.Thumbnail, c gateway.GatewayAPIClient) CS3 { +// NewCS3Source configures a new CS3 image source +func NewCS3Source(cfg config.Thumbnail, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) CS3 { return CS3{ - client: c, - insecure: cfg.CS3AllowInsecure, + gatewaySelector: gatewaySelector, + insecure: cfg.CS3AllowInsecure, } } @@ -51,8 +54,13 @@ func (s CS3) Get(ctx context.Context, path string) (io.ReadCloser, error) { Path: path, } } + ctx = metadata.AppendToOutgoingContext(context.Background(), revactx.TokenHeader, auth) - rsp, err := s.client.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{Ref: &ref}) + gwc, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + rsp, err := gwc.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{Ref: &ref}) if err != nil { return nil, err diff --git a/services/userlog/pkg/command/server.go b/services/userlog/pkg/command/server.go index c814b322c19..58a6f209d4a 100644 --- a/services/userlog/pkg/command/server.go +++ b/services/userlog/pkg/command/server.go @@ -11,6 +11,7 @@ import ( "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" "github.com/owncloud/ocis/v2/ocis-pkg/handlers" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/service/debug" ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/version" @@ -90,13 +91,14 @@ func Server(cfg *config.Config) *cli.Command { if err != nil { return err } - gwclient, err := pool.GetGatewayServiceClient( + gatewaySelector, err := pool.GatewaySelector( cfg.RevaGateway, pool.WithTLSCACert(cfg.GRPCClientTLS.CACert), pool.WithTLSMode(tm), + pool.WithRegistry(registry.GetRegistry()), ) if err != nil { - return fmt.Errorf("could not get reva client: %s", err) + return fmt.Errorf("could not get reva client selector: %s", err) } hClient := ehsvc.NewEventHistoryService("com.owncloud.api.eventhistory", ogrpc.DefaultClient()) @@ -109,7 +111,7 @@ func Server(cfg *config.Config) *cli.Command { http.Metrics(mtrcs), http.Store(st), http.Consumer(consumer), - http.Gateway(gwclient), + http.GatewaySelector(gatewaySelector), http.History(hClient), http.RegisteredEvents(_registeredEvents), ) diff --git a/services/userlog/pkg/server/http/option.go b/services/userlog/pkg/server/http/option.go index 11700b5c145..430c854dbca 100644 --- a/services/userlog/pkg/server/http/option.go +++ b/services/userlog/pkg/server/http/option.go @@ -5,6 +5,7 @@ import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/v2/ocis-pkg/log" ehsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/eventhistory/v0" "github.com/owncloud/ocis/v2/services/userlog/pkg/config" @@ -26,7 +27,7 @@ type Options struct { Namespace string Store store.Store Consumer events.Consumer - GatewayClient gateway.GatewayAPIClient + GatewaySelector pool.Selectable[gateway.GatewayAPIClient] HistoryClient ehsvc.EventHistoryService RegisteredEvents []events.Unmarshaller } @@ -98,10 +99,10 @@ func Consumer(consumer events.Consumer) Option { } } -// Gateway provides a function to configure the gateway client -func Gateway(gw gateway.GatewayAPIClient) Option { +// GatewaySelector provides a function to configure the gateway client selector +func GatewaySelector(gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) Option { return func(o *Options) { - o.GatewayClient = gw + o.GatewaySelector = gatewaySelector } } diff --git a/services/userlog/pkg/server/http/server.go b/services/userlog/pkg/server/http/server.go index 280c5b775a8..724248439c1 100644 --- a/services/userlog/pkg/server/http/server.go +++ b/services/userlog/pkg/server/http/server.go @@ -75,7 +75,7 @@ func Server(opts ...Option) (http.Service, error) { svc.Store(options.Store), svc.Config(options.Config), svc.HistoryClient(options.HistoryClient), - svc.GatewayClient(options.GatewayClient), + svc.GatewaySelector(options.GatewaySelector), svc.RegisteredEvents(options.RegisteredEvents), ) if err != nil { diff --git a/services/userlog/pkg/service/conversion.go b/services/userlog/pkg/service/conversion.go index e1bce3525c6..93fa14d2bdd 100644 --- a/services/userlog/pkg/service/conversion.go +++ b/services/userlog/pkg/service/conversion.go @@ -16,6 +16,7 @@ import ( collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" "github.com/leonelquinteros/gotext" @@ -52,7 +53,7 @@ type OC10Notification struct { // Converter is responsible for converting eventhistory events to OC10Notifications type Converter struct { locale string - gwClient gateway.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] machineAuthAPIKey string serviceName string registeredEvents map[string]events.Unmarshaller @@ -66,10 +67,10 @@ type Converter struct { } // NewConverter returns a new Converter -func NewConverter(loc string, gwc gateway.GatewayAPIClient, machineAuthAPIKey string, name string, translationPath string, registeredEvents map[string]events.Unmarshaller) *Converter { +func NewConverter(loc string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], machineAuthAPIKey string, name string, translationPath string, registeredEvents map[string]events.Unmarshaller) *Converter { return &Converter{ locale: loc, - gwClient: gwc, + gatewaySelector: gatewaySelector, machineAuthAPIKey: machineAuthAPIKey, serviceName: name, registeredEvents: registeredEvents, @@ -306,7 +307,7 @@ func (c *Converter) authenticate(usr *user.User) (context.Context, error) { if ctx, ok := c.contexts[usr.GetId().GetOpaqueId()]; ok { return ctx, nil } - ctx, err := authenticate(usr, c.gwClient, c.machineAuthAPIKey) + ctx, err := authenticate(usr, c.gatewaySelector, c.machineAuthAPIKey) if err == nil { c.contexts[usr.GetId().GetOpaqueId()] = ctx } @@ -317,7 +318,7 @@ func (c *Converter) getSpace(ctx context.Context, spaceID string) (*storageprovi if space, ok := c.spaces[spaceID]; ok { return space, nil } - space, err := getSpace(ctx, spaceID, c.gwClient) + space, err := getSpace(ctx, spaceID, c.gatewaySelector) if err == nil { c.spaces[spaceID] = space } @@ -328,7 +329,7 @@ func (c *Converter) getResource(ctx context.Context, resourceID *storageprovider if r, ok := c.resources[resourceID.GetOpaqueId()]; ok { return r, nil } - resource, err := getResource(ctx, resourceID, c.gwClient) + resource, err := getResource(ctx, resourceID, c.gatewaySelector) if err == nil { c.resources[resourceID.GetOpaqueId()] = resource } @@ -339,7 +340,7 @@ func (c *Converter) getUser(ctx context.Context, userID *user.UserId) (*user.Use if u, ok := c.users[userID.GetOpaqueId()]; ok { return u, nil } - usr, err := getUser(ctx, userID, c.gwClient) + usr, err := getUser(ctx, userID, c.gatewaySelector) if err == nil { c.users[userID.GetOpaqueId()] = usr } diff --git a/services/userlog/pkg/service/http.go b/services/userlog/pkg/service/http.go index 31d7c9b640d..f49d383e643 100644 --- a/services/userlog/pkg/service/http.go +++ b/services/userlog/pkg/service/http.go @@ -31,7 +31,7 @@ func (ul *UserlogService) HandleGetEvents(w http.ResponseWriter, r *http.Request return } - conv := NewConverter(r.Header.Get(HeaderAcceptLanguage), ul.gwClient, ul.cfg.MachineAuthAPIKey, ul.cfg.Service.Name, ul.cfg.TranslationPath, ul.registeredEvents) + conv := NewConverter(r.Header.Get(HeaderAcceptLanguage), ul.gatewaySelector, ul.cfg.MachineAuthAPIKey, ul.cfg.Service.Name, ul.cfg.TranslationPath, ul.registeredEvents) resp := GetEventResponseOC10{} for _, e := range evs { diff --git a/services/userlog/pkg/service/options.go b/services/userlog/pkg/service/options.go index b84c67725e1..acea1a4b796 100644 --- a/services/userlog/pkg/service/options.go +++ b/services/userlog/pkg/service/options.go @@ -3,6 +3,7 @@ package service import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/go-chi/chi/v5" "github.com/owncloud/ocis/v2/ocis-pkg/log" ehsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/eventhistory/v0" @@ -21,7 +22,7 @@ type Options struct { Store store.Store Config *config.Config HistoryClient ehsvc.EventHistoryService - GatewayClient gateway.GatewayAPIClient + GatewaySelector pool.Selectable[gateway.GatewayAPIClient] RegisteredEvents []events.Unmarshaller } @@ -67,10 +68,10 @@ func HistoryClient(hc ehsvc.EventHistoryService) Option { } } -// GatewayClient adds a grpc client for the gateway service -func GatewayClient(gwc gateway.GatewayAPIClient) Option { +// GatewaySelector adds a grpc client selector for the gateway service +func GatewaySelector(gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) Option { return func(o *Options) { - o.GatewayClient = gwc + o.GatewaySelector = gatewaySelector } } diff --git a/services/userlog/pkg/service/service.go b/services/userlog/pkg/service/service.go index d9a663ffb84..37e445dbf5c 100644 --- a/services/userlog/pkg/service/service.go +++ b/services/userlog/pkg/service/service.go @@ -14,6 +14,7 @@ import ( storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/utils" "github.com/go-chi/chi/v5" "github.com/owncloud/ocis/v2/ocis-pkg/log" @@ -31,7 +32,7 @@ type UserlogService struct { store store.Store cfg *config.Config historyClient ehsvc.EventHistoryService - gwClient gateway.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] registeredEvents map[string]events.Unmarshaller translationPath string } @@ -58,7 +59,7 @@ func NewUserlogService(opts ...Option) (*UserlogService, error) { store: o.Store, cfg: o.Config, historyClient: o.HistoryClient, - gwClient: o.GatewayClient, + gatewaySelector: o.GatewaySelector, registeredEvents: make(map[string]events.Unmarshaller), } @@ -283,7 +284,7 @@ func (ul *UserlogService) findSpaceMembers(ctx context.Context, spaceID string, return nil, errors.New("need authenticated context to find space members") } - space, err := getSpace(ctx, spaceID, ul.gwClient) + space, err := getSpace(ctx, spaceID, ul.gatewaySelector) if err != nil { return nil, err } @@ -361,7 +362,7 @@ func (ul *UserlogService) resolveID(ctx context.Context, userid *user.UserId, gr // resolves the users of a group func (ul *UserlogService) resolveGroup(ctx context.Context, groupID string) ([]string, error) { - grp, err := getGroup(ctx, groupID, ul.gwClient) + grp, err := getGroup(ctx, groupID, ul.gatewaySelector) if err != nil { return nil, err } @@ -380,13 +381,13 @@ func (ul *UserlogService) impersonate(uid *user.UserId) context.Context { return nil } - u, err := getUser(context.Background(), uid, ul.gwClient) + u, err := getUser(context.Background(), uid, ul.gatewaySelector) if err != nil { ul.log.Error().Err(err).Msg("cannot get user") return nil } - ctx, err := authenticate(u, ul.gwClient, ul.cfg.MachineAuthAPIKey) + ctx, err := authenticate(u, ul.gatewaySelector, ul.cfg.MachineAuthAPIKey) if err != nil { ul.log.Error().Err(err).Str("userid", u.GetId().GetOpaqueId()).Msg("failed to impersonate user") return nil @@ -394,9 +395,14 @@ func (ul *UserlogService) impersonate(uid *user.UserId) context.Context { return ctx } -func authenticate(usr *user.User, gwc gateway.GatewayAPIClient, machineAuthAPIKey string) (context.Context, error) { +func authenticate(usr *user.User, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], machineAuthAPIKey string) (context.Context, error) { + gatewayClient, err := gatewaySelector.Next() + if err != nil { + return nil, err + } + ctx := revactx.ContextSetUser(context.Background(), usr) - authRes, err := gwc.Authenticate(ctx, &gateway.AuthenticateRequest{ + authRes, err := gatewayClient.Authenticate(ctx, &gateway.AuthenticateRequest{ Type: "machine", ClientId: "userid:" + usr.GetId().GetOpaqueId(), ClientSecret: machineAuthAPIKey, @@ -411,8 +417,13 @@ func authenticate(usr *user.User, gwc gateway.GatewayAPIClient, machineAuthAPIKe return metadata.AppendToOutgoingContext(ctx, revactx.TokenHeader, authRes.Token), nil } -func getSpace(ctx context.Context, spaceID string, gwc gateway.GatewayAPIClient) (*storageprovider.StorageSpace, error) { - res, err := gwc.ListStorageSpaces(ctx, listStorageSpaceRequest(spaceID)) +func getSpace(ctx context.Context, spaceID string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) (*storageprovider.StorageSpace, error) { + gatewayClient, err := gatewaySelector.Next() + if err != nil { + return nil, err + } + + res, err := gatewayClient.ListStorageSpaces(ctx, listStorageSpaceRequest(spaceID)) if err != nil { return nil, err } @@ -428,8 +439,13 @@ func getSpace(ctx context.Context, spaceID string, gwc gateway.GatewayAPIClient) return res.StorageSpaces[0], nil } -func getUser(ctx context.Context, userid *user.UserId, gwc gateway.GatewayAPIClient) (*user.User, error) { - getUserResponse, err := gwc.GetUser(context.Background(), &user.GetUserRequest{ +func getUser(ctx context.Context, userid *user.UserId, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) (*user.User, error) { + gatewayClient, err := gatewaySelector.Next() + if err != nil { + return nil, err + } + + getUserResponse, err := gatewayClient.GetUser(context.Background(), &user.GetUserRequest{ UserId: userid, }) if err != nil { @@ -443,8 +459,13 @@ func getUser(ctx context.Context, userid *user.UserId, gwc gateway.GatewayAPICli return getUserResponse.GetUser(), nil } -func getGroup(ctx context.Context, groupid string, gwc gateway.GatewayAPIClient) (*group.Group, error) { - r, err := gwc.GetGroup(ctx, &group.GetGroupRequest{GroupId: &group.GroupId{OpaqueId: groupid}}) +func getGroup(ctx context.Context, groupid string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) (*group.Group, error) { + gatewayClient, err := gatewaySelector.Next() + if err != nil { + return nil, err + } + + r, err := gatewayClient.GetGroup(ctx, &group.GetGroupRequest{GroupId: &group.GroupId{OpaqueId: groupid}}) if err != nil { return nil, err } @@ -456,8 +477,13 @@ func getGroup(ctx context.Context, groupid string, gwc gateway.GatewayAPIClient) return r.GetGroup(), nil } -func getResource(ctx context.Context, resourceid *storageprovider.ResourceId, gwc gateway.GatewayAPIClient) (*storageprovider.ResourceInfo, error) { - res, err := gwc.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: resourceid}}) +func getResource(ctx context.Context, resourceid *storageprovider.ResourceId, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) (*storageprovider.ResourceInfo, error) { + gatewayClient, err := gatewaySelector.Next() + if err != nil { + return nil, err + } + + res, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: resourceid}}) if err != nil { return nil, err } diff --git a/services/userlog/pkg/service/service_suit_test.go b/services/userlog/pkg/service/service_suit_test.go index 77d1081a82d..ab7fb7b1989 100644 --- a/services/userlog/pkg/service/service_suit_test.go +++ b/services/userlog/pkg/service/service_suit_test.go @@ -3,10 +3,23 @@ package service_test import ( "testing" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" + mRegistry "go-micro.dev/v4/registry" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) +func init() { + registry.Configure("memory") + r := registry.GetRegistry() + service := registry.BuildGRPCService("com.owncloud.api.gateway", "", "", "") + service.Nodes = []*mRegistry.Node{{ + Address: "any", + }} + + _ = r.Register(service) +} func TestSearch(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Userlog service Suite") diff --git a/services/userlog/pkg/service/service_test.go b/services/userlog/pkg/service/service_test.go index acecb66a8fd..2a7e4983069 100644 --- a/services/userlog/pkg/service/service_test.go +++ b/services/userlog/pkg/service/service_test.go @@ -11,6 +11,7 @@ import ( rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/store" "github.com/cs3org/reva/v2/pkg/utils" cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" @@ -27,6 +28,7 @@ import ( "github.com/test-go/testify/mock" microevents "go-micro.dev/v4/events" microstore "go-micro.dev/v4/store" + "google.golang.org/grpc" ) var _ = Describe("UserlogService", func() { @@ -37,7 +39,9 @@ var _ = Describe("UserlogService", func() { bus testBus sto microstore.Store - gwc cs3mocks.GatewayAPIClient + gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + ehc mocks.EventHistoryService ) @@ -45,15 +49,26 @@ var _ = Describe("UserlogService", func() { var err error sto = store.Create() bus = testBus(make(chan events.Event)) + + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") + gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + o := utils.AppendJSONToOpaque(nil, "grants", map[string]*provider.ResourcePermissions{"userid": {Stat: true}}) - gwc.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{StorageSpaces: []*provider.StorageSpace{ + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{StorageSpaces: []*provider.StorageSpace{ { Opaque: o, SpaceType: "project", }, }, Status: &rpc.Status{Code: rpc.Code_CODE_OK}}, nil) - gwc.On("GetUser", mock.Anything, mock.Anything).Return(&user.GetUserResponse{User: &user.User{Id: &user.UserId{OpaqueId: "userid"}}, Status: &rpc.Status{Code: rpc.Code_CODE_OK}}, nil) - gwc.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}}, nil) + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(&user.GetUserResponse{User: &user.User{Id: &user.UserId{OpaqueId: "userid"}}, Status: &rpc.Status{Code: rpc.Code_CODE_OK}}, nil) + gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}}, nil) ul, err = service.NewUserlogService( service.Config(cfg), @@ -61,7 +76,7 @@ var _ = Describe("UserlogService", func() { service.Store(sto), service.Logger(log.NewLogger()), service.Mux(chi.NewMux()), - service.GatewayClient(&gwc), + service.GatewaySelector(gatewaySelector), service.HistoryClient(&ehc), service.RegisteredEvents([]events.Unmarshaller{ events.SpaceDisabled{}, diff --git a/services/users/pkg/command/server.go b/services/users/pkg/command/server.go index b0419b305a0..e661eee747a 100644 --- a/services/users/pkg/command/server.go +++ b/services/users/pkg/command/server.go @@ -11,7 +11,7 @@ import ( "github.com/oklog/run" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" "github.com/owncloud/ocis/v2/ocis-pkg/ldap" - "github.com/owncloud/ocis/v2/ocis-pkg/service/external" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/sync" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/services/users/pkg/config" @@ -43,10 +43,6 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") - - rcfg := revaconfig.UsersConfigFromStruct(cfg) - // the reva runtime calls os.Exit in the case of a failure and there is no way for the oCIS // runtime to catch it and restart a reva service. Therefore we need to ensure the service has // everything it needs, before starting the service. @@ -60,7 +56,15 @@ func Server(cfg *config.Config) *cli.Command { } gr.Add(func() error { - runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) + pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") + rCfg := revaconfig.UsersConfigFromStruct(cfg) + reg := registry.GetRegistry() + + runtime.RunWithOptions(rCfg, pidFile, + runtime.WithLogger(&logger.Logger), + runtime.WithRegistry(reg), + ) + return nil }, func(err error) { logger.Error(). @@ -90,15 +94,9 @@ func Server(cfg *config.Config) *cli.Command { sync.Trap(&gr, cancel) } - if err := external.RegisterGRPCEndpoint( - ctx, - cfg.GRPC.Namespace+"."+cfg.Service.Name, - uuid.Must(uuid.NewV4()).String(), - cfg.GRPC.Addr, - version.GetString(), - logger, - ); err != nil { - logger.Fatal().Err(err).Msg("failed to register the grpc endpoint") + grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString()) + if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil { + logger.Fatal().Err(err).Msg("failed to register the grpc service") } return gr.Run() diff --git a/services/web/pkg/config/defaults/defaultconfig.go b/services/web/pkg/config/defaults/defaultconfig.go index b24440867a4..110a12d8dd4 100644 --- a/services/web/pkg/config/defaults/defaultconfig.go +++ b/services/web/pkg/config/defaults/defaultconfig.go @@ -82,7 +82,7 @@ func DefaultConfig() *config.Config { Asset: config.Asset{ Path: filepath.Join(defaults.BaseDataPath(), "web/assets"), }, - GatewayAddress: "127.0.0.1:9142", + GatewayAddress: "com.owncloud.api.gateway", Web: config.Web{ Path: "", ThemeServer: "https://localhost:9200", diff --git a/services/web/pkg/server/http/server.go b/services/web/pkg/server/http/server.go index 564afabbd33..8312f54b22e 100644 --- a/services/web/pkg/server/http/server.go +++ b/services/web/pkg/server/http/server.go @@ -7,6 +7,7 @@ import ( chimiddleware "github.com/go-chi/chi/v5/middleware" "github.com/owncloud/ocis/v2/ocis-pkg/cors" "github.com/owncloud/ocis/v2/ocis-pkg/middleware" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/service/http" "github.com/owncloud/ocis/v2/ocis-pkg/version" webmid "github.com/owncloud/ocis/v2/services/web/pkg/middleware" @@ -35,7 +36,7 @@ func Server(opts ...Option) (http.Service, error) { return http.Service{}, fmt.Errorf("could not initialize http service: %w", err) } - client, err := pool.GetGatewayServiceClient(options.Config.GatewayAddress) + gatewaySelector, err := pool.GatewaySelector(options.Config.GatewayAddress, pool.WithRegistry(registry.GetRegistry())) if err != nil { return http.Service{}, err } @@ -43,7 +44,7 @@ func Server(opts ...Option) (http.Service, error) { handle := svc.NewService( svc.Logger(options.Logger), svc.Config(options.Config), - svc.GatewayClient(client), + svc.GatewaySelector(gatewaySelector), svc.Middleware( chimiddleware.RealIP, chimiddleware.RequestID, diff --git a/services/web/pkg/service/v0/branding.go b/services/web/pkg/service/v0/branding.go index 7d7cec63a90..4eae627320f 100644 --- a/services/web/pkg/service/v0/branding.go +++ b/services/web/pkg/service/v0/branding.go @@ -26,8 +26,14 @@ var ( // UploadLogo implements the endpoint to upload a custom logo for the oCIS instance. func (p Web) UploadLogo(w http.ResponseWriter, r *http.Request) { + gatewayClient, err := p.gatewaySelector.Next() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + user := revactx.ContextMustGetUser(r.Context()) - rsp, err := p.gatewayClient.CheckPermission(r.Context(), &permissionsapi.CheckPermissionRequest{ + rsp, err := gatewayClient.CheckPermission(r.Context(), &permissionsapi.CheckPermissionRequest{ Permission: "Logo.Write", SubjectRef: &permissionsapi.SubjectReference{ Spec: &permissionsapi.SubjectReference_UserId{ @@ -79,8 +85,14 @@ func (p Web) UploadLogo(w http.ResponseWriter, r *http.Request) { // ResetLogo implements the endpoint to reset the instance logo. // The config will be changed back to use the embedded logo asset. func (p Web) ResetLogo(w http.ResponseWriter, r *http.Request) { + gatewayClient, err := p.gatewaySelector.Next() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + user := revactx.ContextMustGetUser(r.Context()) - rsp, err := p.gatewayClient.CheckPermission(r.Context(), &permissionsapi.CheckPermissionRequest{ + rsp, err := gatewayClient.CheckPermission(r.Context(), &permissionsapi.CheckPermissionRequest{ Permission: "Logo.Write", SubjectRef: &permissionsapi.SubjectReference{ Spec: &permissionsapi.SubjectReference_UserId{ diff --git a/services/web/pkg/service/v0/option.go b/services/web/pkg/service/v0/option.go index 8a0ed024bef..8f6f261a4da 100644 --- a/services/web/pkg/service/v0/option.go +++ b/services/web/pkg/service/v0/option.go @@ -4,6 +4,7 @@ import ( "net/http" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/web/pkg/config" ) @@ -13,10 +14,10 @@ type Option func(o *Options) // Options defines the available options for this package. type Options struct { - Logger log.Logger - Config *config.Config - Middleware []func(http.Handler) http.Handler - GatewayClient gateway.GatewayAPIClient + Logger log.Logger + Config *config.Config + Middleware []func(http.Handler) http.Handler + GatewaySelector pool.Selectable[gateway.GatewayAPIClient] } // newOptions initializes the available default options. @@ -51,9 +52,9 @@ func Middleware(val ...func(http.Handler) http.Handler) Option { } } -// GatewayClient provides a function to set the GatewayClient option. -func GatewayClient(client gateway.GatewayAPIClient) Option { +// GatewaySelector provides a function to set the gatewaySelector option. +func GatewaySelector(gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) Option { return func(o *Options) { - o.GatewayClient = client + o.GatewaySelector = gatewaySelector } } diff --git a/services/web/pkg/service/v0/service.go b/services/web/pkg/service/v0/service.go index 2699f975f2e..7e557b3574f 100644 --- a/services/web/pkg/service/v0/service.go +++ b/services/web/pkg/service/v0/service.go @@ -11,6 +11,7 @@ import ( "time" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/go-chi/chi/v5" "github.com/owncloud/ocis/v2/ocis-pkg/account" "github.com/owncloud/ocis/v2/ocis-pkg/assetsfs" @@ -42,11 +43,11 @@ func NewService(opts ...Option) Service { m.Use(options.Middleware...) svc := Web{ - logger: options.Logger, - config: options.Config, - mux: m, - fs: assetsfs.New(web.Assets, options.Config.Asset.Path, options.Logger), - gatewayClient: options.GatewayClient, + logger: options.Logger, + config: options.Config, + mux: m, + fs: assetsfs.New(web.Assets, options.Config.Asset.Path, options.Logger), + gatewaySelector: options.GatewaySelector, } m.Route(options.Config.HTTP.Root, func(r chi.Router) { @@ -72,11 +73,11 @@ func NewService(opts ...Option) Service { // Web defines implements the business logic for Service. type Web struct { - logger log.Logger - config *config.Config - mux *chi.Mux - fs *assetsfs.FileSystem - gatewayClient gateway.GatewayAPIClient + logger log.Logger + config *config.Config + mux *chi.Mux + fs *assetsfs.FileSystem + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] } // ServeHTTP implements the Service interface. diff --git a/services/webdav/pkg/service/v0/service.go b/services/webdav/pkg/service/v0/service.go index fe63df7cb84..a411a2968b0 100644 --- a/services/webdav/pkg/service/v0/service.go +++ b/services/webdav/pkg/service/v0/service.go @@ -17,10 +17,8 @@ import ( "github.com/cs3org/reva/v2/pkg/storage/utils/templates" "github.com/go-chi/chi/v5" "github.com/go-chi/render" - merrors "go-micro.dev/v4/errors" - "google.golang.org/grpc/metadata" - "github.com/owncloud/ocis/v2/ocis-pkg/log" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" thumbnailsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/thumbnails/v0" searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0" @@ -28,6 +26,8 @@ import ( "github.com/owncloud/ocis/v2/services/webdav/pkg/config" "github.com/owncloud/ocis/v2/services/webdav/pkg/constants" "github.com/owncloud/ocis/v2/services/webdav/pkg/dav/requests" + merrors "go-micro.dev/v4/errors" + "google.golang.org/grpc/metadata" ) const ( @@ -64,9 +64,10 @@ func NewService(opts ...Option) (Service, error) { if err != nil { return nil, err } - gwc, err := pool.GetGatewayServiceClient(conf.RevaGateway, + gatewaySelector, err := pool.GatewaySelector(conf.RevaGateway, pool.WithTLSCACert(conf.GRPCClientTLS.CACert), pool.WithTLSMode(tm), + pool.WithRegistry(registry.GetRegistry()), ) if err != nil { return nil, err @@ -78,7 +79,7 @@ func NewService(opts ...Option) (Service, error) { mux: m, searchClient: searchsvc.NewSearchProviderService("com.owncloud.api.search", grpc.DefaultClient()), thumbnailsClient: thumbnailssvc.NewThumbnailService("com.owncloud.api.thumbnails", grpc.DefaultClient()), - revaClient: gwc, + gatewaySelector: gatewaySelector, } m.Route(options.Config.HTTP.Root, func(r chi.Router) { @@ -149,7 +150,7 @@ type Webdav struct { mux *chi.Mux searchClient searchsvc.SearchProviderService thumbnailsClient thumbnailssvc.ThumbnailService - revaClient gatewayv1beta1.GatewayAPIClient + gatewaySelector pool.Selectable[gatewayv1beta1.GatewayAPIClient] } // ServeHTTP implements the Service interface. @@ -278,11 +279,17 @@ func (g Webdav) Thumbnail(w http.ResponseWriter, r *http.Request) { t := r.Header.Get(TokenHeader) - var user *userv1beta1.User + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + logger.Error().Err(err).Msg("could not get reva gatewayClient") + renderError(w, r, errInternalError("could not get reva gatewayClient")) + return + } + var user *userv1beta1.User if tr.Identifier == "" { // look up user from token via WhoAmI - userRes, err := g.revaClient.WhoAmI(r.Context(), &gatewayv1beta1.WhoAmIRequest{ + userRes, err := gatewayClient.WhoAmI(r.Context(), &gatewayv1beta1.WhoAmIRequest{ Token: t, }) if err != nil { @@ -299,7 +306,7 @@ func (g Webdav) Thumbnail(w http.ResponseWriter, r *http.Request) { } else { // look up user from URL via GetUserByClaim ctx := metadata.AppendToOutgoingContext(r.Context(), TokenHeader, t) - userRes, err := g.revaClient.GetUserByClaim(ctx, &userv1beta1.GetUserByClaimRequest{ + userRes, err := gatewayClient.GetUserByClaim(ctx, &userv1beta1.GetUserByClaimRequest{ Claim: "username", Value: tr.Identifier, }) diff --git a/vendor/github.com/cs3org/reva/v2/cmd/revad/runtime/option.go b/vendor/github.com/cs3org/reva/v2/cmd/revad/runtime/option.go index 44be8f9591f..daa1654e5f8 100644 --- a/vendor/github.com/cs3org/reva/v2/cmd/revad/runtime/option.go +++ b/vendor/github.com/cs3org/reva/v2/cmd/revad/runtime/option.go @@ -19,8 +19,8 @@ package runtime import ( - "github.com/cs3org/reva/v2/pkg/registry" "github.com/rs/zerolog" + "go-micro.dev/v4/registry" ) // Option defines a single option function. diff --git a/vendor/github.com/cs3org/reva/v2/cmd/revad/runtime/runtime.go b/vendor/github.com/cs3org/reva/v2/cmd/revad/runtime/runtime.go index d161a23b4ba..ef99449fbe7 100644 --- a/vendor/github.com/cs3org/reva/v2/cmd/revad/runtime/runtime.go +++ b/vendor/github.com/cs3org/reva/v2/cmd/revad/runtime/runtime.go @@ -30,12 +30,11 @@ import ( "github.com/cs3org/reva/v2/cmd/revad/internal/grace" "github.com/cs3org/reva/v2/pkg/logger" - "github.com/cs3org/reva/v2/pkg/registry/memory" + "github.com/cs3org/reva/v2/pkg/registry" "github.com/cs3org/reva/v2/pkg/rgrpc" "github.com/cs3org/reva/v2/pkg/rhttp" "github.com/cs3org/reva/v2/pkg/sharedconf" rtrace "github.com/cs3org/reva/v2/pkg/trace" - "github.com/cs3org/reva/v2/pkg/utils" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "github.com/rs/zerolog" @@ -55,19 +54,8 @@ func RunWithOptions(mainConf map[string]interface{}, pidFile string, opts ...Opt parseSharedConfOrDie(mainConf["shared"]) coreConf := parseCoreConfOrDie(mainConf["core"]) - // TODO: one can pass the options from the config file to registry.New() and initialize a registry based upon config files. - if options.Registry != nil { - utils.GlobalRegistry = options.Registry - } else if _, ok := mainConf["registry"]; ok { - for _, services := range mainConf["registry"].(map[string]interface{}) { - for sName, nodes := range services.(map[string]interface{}) { - for _, instance := range nodes.([]interface{}) { - if err := utils.GlobalRegistry.Add(memory.NewService(sName, instance.(map[string]interface{})["nodes"].([]interface{}))); err != nil { - panic(err) - } - } - } - } + if err := registry.Init(options.Registry); err != nil { + panic(err) } run(mainConf, coreConf, options.Logger, pidFile) diff --git a/vendor/github.com/cs3org/reva/v2/internal/grpc/services/gateway/authprovider.go b/vendor/github.com/cs3org/reva/v2/internal/grpc/services/gateway/authprovider.go index 52875480e39..59214afffea 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/grpc/services/gateway/authprovider.go +++ b/vendor/github.com/cs3org/reva/v2/internal/grpc/services/gateway/authprovider.go @@ -44,6 +44,7 @@ func (s *svc) Authenticate(ctx context.Context, req *gateway.AuthenticateRequest // find auth provider c, err := s.findAuthProvider(ctx, req.Type) if err != nil { + log.Err(err).Str("type", req.Type).Msg("error getting auth provider client") return &gateway.AuthenticateResponse{ Status: status.NewInternal(ctx, "error getting auth provider client"), }, nil diff --git a/vendor/github.com/cs3org/reva/v2/internal/grpc/services/publicstorageprovider/publicstorageprovider.go b/vendor/github.com/cs3org/reva/v2/internal/grpc/services/publicstorageprovider/publicstorageprovider.go index afff2e72941..48fb7779190 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/grpc/services/publicstorageprovider/publicstorageprovider.go +++ b/vendor/github.com/cs3org/reva/v2/internal/grpc/services/publicstorageprovider/publicstorageprovider.go @@ -59,8 +59,8 @@ type config struct { } type service struct { - conf *config - gateway gateway.GatewayAPIClient + conf *config + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] } func (s *service) Close() error { @@ -91,14 +91,14 @@ func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { return nil, err } - gateway, err := pool.GetGatewayServiceClient(c.GatewayAddr) + gatewaySelector, err := pool.GatewaySelector(c.GatewayAddr) if err != nil { return nil, err } service := &service{ - conf: c, - gateway: gateway, + conf: c, + gatewaySelector: gatewaySelector, } return service, nil @@ -114,7 +114,11 @@ func (s *service) SetArbitraryMetadata(ctx context.Context, req *provider.SetArb Status: st, }, nil } - return s.gateway.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{Opaque: req.Opaque, Ref: ref, ArbitraryMetadata: req.ArbitraryMetadata}) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + return gatewayClient.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{Opaque: req.Opaque, Ref: ref, ArbitraryMetadata: req.ArbitraryMetadata}) } func (s *service) UnsetArbitraryMetadata(ctx context.Context, req *provider.UnsetArbitraryMetadataRequest) (*provider.UnsetArbitraryMetadataResponse, error) { @@ -132,7 +136,11 @@ func (s *service) SetLock(ctx context.Context, req *provider.SetLockRequest) (*p Status: st, }, nil } - return s.gateway.SetLock(ctx, &provider.SetLockRequest{Opaque: req.Opaque, Ref: ref, Lock: req.Lock}) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + return gatewayClient.SetLock(ctx, &provider.SetLockRequest{Opaque: req.Opaque, Ref: ref, Lock: req.Lock}) } // GetLock returns an existing lock on the given reference @@ -146,7 +154,11 @@ func (s *service) GetLock(ctx context.Context, req *provider.GetLockRequest) (*p Status: st, }, nil } - return s.gateway.GetLock(ctx, &provider.GetLockRequest{Opaque: req.Opaque, Ref: ref}) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + return gatewayClient.GetLock(ctx, &provider.GetLockRequest{Opaque: req.Opaque, Ref: ref}) } // RefreshLock refreshes an existing lock on the given reference @@ -160,7 +172,11 @@ func (s *service) RefreshLock(ctx context.Context, req *provider.RefreshLockRequ Status: st, }, nil } - return s.gateway.RefreshLock(ctx, &provider.RefreshLockRequest{Opaque: req.Opaque, Ref: ref, Lock: req.Lock}) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + return gatewayClient.RefreshLock(ctx, &provider.RefreshLockRequest{Opaque: req.Opaque, Ref: ref, Lock: req.Lock}) } // Unlock removes an existing lock from the given reference @@ -174,7 +190,11 @@ func (s *service) Unlock(ctx context.Context, req *provider.UnlockRequest) (*pro Status: st, }, nil } - return s.gateway.Unlock(ctx, &provider.UnlockRequest{Opaque: req.Opaque, Ref: ref, Lock: req.Lock}) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + return gatewayClient.Unlock(ctx, &provider.UnlockRequest{Opaque: req.Opaque, Ref: ref, Lock: req.Lock}) } func (s *service) InitiateFileDownload(ctx context.Context, req *provider.InitiateFileDownloadRequest) (*provider.InitiateFileDownloadResponse, error) { @@ -265,7 +285,11 @@ func (s *service) initiateFileDownload(ctx context.Context, req *provider.Initia Ref: cs3Ref, } - dRes, err := s.gateway.InitiateFileDownload(ctx, dReq) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + dRes, err := gatewayClient.InitiateFileDownload(ctx, dReq) if err != nil { return &provider.InitiateFileDownloadResponse{ Status: status.NewInternal(ctx, "initiateFileDownload: error calling InitiateFileDownload"), @@ -319,7 +343,11 @@ func (s *service) InitiateFileUpload(ctx context.Context, req *provider.Initiate Opaque: req.Opaque, } - uRes, err := s.gateway.InitiateFileUpload(ctx, uReq) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + uRes, err := gatewayClient.InitiateFileUpload(ctx, uReq) if err != nil { return &provider.InitiateFileUploadResponse{ Status: status.NewInternal(ctx, "InitiateFileUpload: error calling InitiateFileUpload"), @@ -543,7 +571,11 @@ func (s *service) CreateContainer(ctx context.Context, req *provider.CreateConta var res *provider.CreateContainerResponse // the call has to be made to the gateway instead of the storage. - res, err = s.gateway.CreateContainer(ctx, &provider.CreateContainerRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + res, err = gatewayClient.CreateContainer(ctx, &provider.CreateContainerRequest{ Ref: cs3Ref, }) if err != nil { @@ -568,7 +600,11 @@ func (s *service) TouchFile(ctx context.Context, req *provider.TouchFileRequest) Status: st, }, nil } - return s.gateway.TouchFile(ctx, &provider.TouchFileRequest{Opaque: req.Opaque, Ref: ref}) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + return gatewayClient.TouchFile(ctx, &provider.TouchFileRequest{Opaque: req.Opaque, Ref: ref}) } func (s *service) Delete(ctx context.Context, req *provider.DeleteRequest) (*provider.DeleteResponse, error) { @@ -596,7 +632,11 @@ func (s *service) Delete(ctx context.Context, req *provider.DeleteRequest) (*pro var res *provider.DeleteResponse // the call has to be made to the gateway instead of the storage. - res, err = s.gateway.Delete(ctx, &provider.DeleteRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + res, err = gatewayClient.Delete(ctx, &provider.DeleteRequest{ Ref: cs3Ref, }) if err != nil { @@ -658,7 +698,11 @@ func (s *service) Move(ctx context.Context, req *provider.MoveRequest) (*provide var res *provider.MoveResponse // the call has to be made to the gateway instead of the storage. - res, err = s.gateway.Move(ctx, &provider.MoveRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + res, err = gatewayClient.Move(ctx, &provider.MoveRequest{ Source: cs3RefSource, Destination: cs3RefDestination, }) @@ -721,7 +765,11 @@ func (s *service) Stat(ctx context.Context, req *provider.StatRequest) (*provide Path: utils.MakeRelativePath(req.Ref.Path), } - statResponse, err := s.gateway.Stat(ctx, &provider.StatRequest{Ref: ref}) + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + statResponse, err := gatewayClient.Stat(ctx, &provider.StatRequest{Ref: ref}) if err != nil { return &provider.StatResponse{ Status: status.NewInternal(ctx, "Stat: error calling Stat for ref:"+req.Ref.String()), @@ -796,7 +844,11 @@ func (s *service) ListContainer(ctx context.Context, req *provider.ListContainer }, nil } - listContainerR, err := s.gateway.ListContainer( + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + listContainerR, err := gatewayClient.ListContainer( ctx, &provider.ListContainerRequest{ Ref: &provider.Reference{ @@ -926,7 +978,11 @@ func (s *service) resolveToken(ctx context.Context, token string) (*link.PublicS return nil, nil, publicShareResponse.Status, nil } - sRes, err := s.gateway.Stat(ctx, &provider.StatRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, nil, nil, err + } + sRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{ Ref: &provider.Reference{ ResourceId: publicShareResponse.GetShare().GetResourceId(), }, diff --git a/vendor/github.com/cs3org/reva/v2/internal/grpc/services/sharesstorageprovider/sharesstorageprovider.go b/vendor/github.com/cs3org/reva/v2/internal/grpc/services/sharesstorageprovider/sharesstorageprovider.go index 709631a0424..4b1a12f8baf 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/grpc/services/sharesstorageprovider/sharesstorageprovider.go +++ b/vendor/github.com/cs3org/reva/v2/internal/grpc/services/sharesstorageprovider/sharesstorageprovider.go @@ -62,8 +62,8 @@ type config struct { } type service struct { - gateway gateway.GatewayAPIClient - sharesProviderClient collaboration.CollaborationAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + sharingCollaborationSelector pool.Selectable[collaboration.CollaborationAPIClient] } func (s *service) Close() error { @@ -86,24 +86,24 @@ func NewDefault(m map[string]interface{}, _ *grpc.Server) (rgrpc.Service, error) return nil, err } - gateway, err := pool.GetGatewayServiceClient(sharedconf.GetGatewaySVC(c.GatewayAddr)) + gatewaySelector, err := pool.GatewaySelector(sharedconf.GetGatewaySVC(c.GatewayAddr)) if err != nil { return nil, err } - client, err := pool.GetUserShareProviderClient(sharedconf.GetGatewaySVC(c.UserShareProviderEndpoint)) + sharingCollaborationSelector, err := pool.SharingCollaborationSelector(sharedconf.GetGatewaySVC(c.UserShareProviderEndpoint)) if err != nil { return nil, errors.Wrap(err, "sharesstorageprovider: error getting UserShareProvider client") } - return New(gateway, client) + return New(gatewaySelector, sharingCollaborationSelector) } // New returns a new instance of the SharesStorageProvider service -func New(gateway gateway.GatewayAPIClient, c collaboration.CollaborationAPIClient) (rgrpc.Service, error) { +func New(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], sharingCollaborationSelector pool.Selectable[collaboration.CollaborationAPIClient]) (rgrpc.Service, error) { s := &service{ - gateway: gateway, - sharesProviderClient: c, + gatewaySelector: gatewaySelector, + sharingCollaborationSelector: sharingCollaborationSelector, } return s, nil } @@ -123,7 +123,12 @@ func (s *service) SetArbitraryMetadata(ctx context.Context, req *provider.SetArb }, nil } - return s.gateway.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + return gatewayClient.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{ Opaque: req.Opaque, Ref: buildReferenceInShare(req.Ref, receivedShare), ArbitraryMetadata: req.ArbitraryMetadata, @@ -145,7 +150,12 @@ func (s *service) UnsetArbitraryMetadata(ctx context.Context, req *provider.Unse }, nil } - return s.gateway.UnsetArbitraryMetadata(ctx, &provider.UnsetArbitraryMetadataRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + return gatewayClient.UnsetArbitraryMetadata(ctx, &provider.UnsetArbitraryMetadataRequest{ Opaque: req.Opaque, Ref: buildReferenceInShare(req.Ref, receivedShare), ArbitraryMetadataKeys: req.ArbitraryMetadataKeys, @@ -167,7 +177,12 @@ func (s *service) InitiateFileDownload(ctx context.Context, req *provider.Initia }, nil } - gwres, err := s.gateway.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + gwres, err := gatewayClient.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{ Opaque: req.Opaque, Ref: buildReferenceInShare(req.Ref, receivedShare), LockId: req.LockId, @@ -229,7 +244,13 @@ func (s *service) InitiateFileUpload(ctx context.Context, req *provider.Initiate Status: status.NewPermissionDenied(ctx, nil, "share does not grant InitiateFileDownload permission"), }, nil } - gwres, err := s.gateway.InitiateFileUpload(ctx, &provider.InitiateFileUploadRequest{ + + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + gwres, err := gatewayClient.InitiateFileUpload(ctx, &provider.InitiateFileUploadRequest{ Opaque: req.Opaque, Ref: buildReferenceInShare(req.Ref, receivedShare), LockId: req.LockId, @@ -513,7 +534,12 @@ func (s *service) CreateContainer(ctx context.Context, req *provider.CreateConta }, nil } - return s.gateway.CreateContainer(ctx, &provider.CreateContainerRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + return gatewayClient.CreateContainer(ctx, &provider.CreateContainerRequest{ Opaque: req.Opaque, Ref: buildReferenceInShare(req.Ref, receivedShare), }) @@ -548,7 +574,12 @@ func (s *service) Delete(ctx context.Context, req *provider.DeleteRequest) (*pro }, nil } - return s.gateway.Delete(ctx, &provider.DeleteRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + return gatewayClient.Delete(ctx, &provider.DeleteRequest{ Opaque: req.Opaque, Ref: buildReferenceInShare(req.Ref, receivedShare), }) @@ -584,7 +615,12 @@ func (s *service) Move(ctx context.Context, req *provider.MoveRequest) (*provide Path: filepath.Base(req.Destination.Path), } - _, err = s.sharesProviderClient.UpdateReceivedShare(ctx, &collaboration.UpdateReceivedShareRequest{ + sharingCollaborationClient, err := s.sharingCollaborationSelector.Next() + if err != nil { + return nil, err + } + + _, err = sharingCollaborationClient.UpdateReceivedShare(ctx, &collaboration.UpdateReceivedShareRequest{ Share: srcReceivedShare, UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"state", "mount_point"}}, }) @@ -613,7 +649,12 @@ func (s *service) Move(ctx context.Context, req *provider.MoveRequest) (*provide }, nil } - return s.gateway.Move(ctx, &provider.MoveRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + return gatewayClient.Move(ctx, &provider.MoveRequest{ Opaque: req.Opaque, Source: buildReferenceInShare(req.Source, srcReceivedShare), Destination: buildReferenceInShare(req.Destination, dstReceivedShare), @@ -712,8 +753,13 @@ func (s *service) Stat(ctx context.Context, req *provider.StatRequest) (*provide }, nil } + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + // TODO return reference? - return s.gateway.Stat(ctx, &provider.StatRequest{ + return gatewayClient.Stat(ctx, &provider.StatRequest{ Opaque: req.Opaque, Ref: buildReferenceInShare(req.Ref, receivedShare), ArbitraryMetadataKeys: req.ArbitraryMetadataKeys, @@ -742,13 +788,18 @@ func (s *service) ListContainer(ctx context.Context, req *provider.ListContainer return nil, errors.Wrap(err, "sharesstorageprovider: error calling ListReceivedSharesRequest") } + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + infos := []*provider.ResourceInfo{} for _, share := range receivedShares { if share.GetState() != collaboration.ShareState_SHARE_STATE_ACCEPTED { continue } - statRes, err := s.gateway.Stat(ctx, &provider.StatRequest{ + statRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{ Opaque: req.Opaque, Ref: &provider.Reference{ ResourceId: share.Share.ResourceId, @@ -802,7 +853,12 @@ func (s *service) ListContainer(ctx context.Context, req *provider.ListContainer }, nil } - return s.gateway.ListContainer(ctx, &provider.ListContainerRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + return gatewayClient.ListContainer(ctx, &provider.ListContainerRequest{ Opaque: req.Opaque, Ref: buildReferenceInShare(req.Ref, receivedShare), ArbitraryMetadataKeys: req.ArbitraryMetadataKeys, @@ -824,7 +880,12 @@ func (s *service) ListFileVersions(ctx context.Context, req *provider.ListFileVe }, nil } - return s.gateway.ListFileVersions(ctx, &provider.ListFileVersionsRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + return gatewayClient.ListFileVersions(ctx, &provider.ListFileVersionsRequest{ Opaque: req.Opaque, Ref: buildReferenceInShare(req.Ref, receivedShare), }) @@ -846,7 +907,12 @@ func (s *service) RestoreFileVersion(ctx context.Context, req *provider.RestoreF }, nil } - return s.gateway.RestoreFileVersion(ctx, &provider.RestoreFileVersionRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + return gatewayClient.RestoreFileVersion(ctx, &provider.RestoreFileVersionRequest{ Opaque: req.Opaque, Ref: buildReferenceInShare(req.Ref, receivedShare), }) @@ -911,7 +977,12 @@ func (s *service) TouchFile(ctx context.Context, req *provider.TouchFileRequest) }, nil } - return s.gateway.TouchFile(ctx, &provider.TouchFileRequest{ + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + + return gatewayClient.TouchFile(ctx, &provider.TouchFileRequest{ Opaque: req.Opaque, Ref: buildReferenceInShare(req.Ref, receivedShare), }) @@ -938,10 +1009,15 @@ func (s *service) resolveAcceptedShare(ctx context.Context, ref *provider.Refere return nil, status.NewNotFound(ctx, "sharesstorageprovider: not found "+ref.String()), nil } + sharingCollaborationClient, err := s.sharingCollaborationSelector.Next() + if err != nil { + return nil, nil, err + } + // we can get the share if the reference carries a share id if ref.ResourceId.OpaqueId != utils.ShareStorageProviderID { // look up share for this resourceid - lsRes, err := s.sharesProviderClient.GetReceivedShare(ctx, &collaboration.GetReceivedShareRequest{ + lsRes, err := sharingCollaborationClient.GetReceivedShare(ctx, &collaboration.GetReceivedShareRequest{ Ref: &collaboration.ShareReference{ Spec: &collaboration.ShareReference_Id{ Id: &collaboration.ShareId{ @@ -968,7 +1044,7 @@ func (s *service) resolveAcceptedShare(ctx context.Context, ref *provider.Refere // we need to list accepted shares and match the path // look up share for this resourceid - lsRes, err := s.sharesProviderClient.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{ + lsRes, err := sharingCollaborationClient.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{ Filters: []*collaboration.Filter{ // FIXME filter by accepted ... and by mountpoint? }, @@ -997,7 +1073,12 @@ func (s *service) rejectReceivedShare(ctx context.Context, receivedShare *collab receivedShare.State = collaboration.ShareState_SHARE_STATE_REJECTED receivedShare.MountPoint = nil - res, err := s.sharesProviderClient.UpdateReceivedShare(ctx, &collaboration.UpdateReceivedShareRequest{ + sharingCollaborationClient, err := s.sharingCollaborationSelector.Next() + if err != nil { + return err + } + + res, err := sharingCollaborationClient.UpdateReceivedShare(ctx, &collaboration.UpdateReceivedShareRequest{ Share: receivedShare, UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"state", "mount_point"}}, }) @@ -1009,7 +1090,12 @@ func (s *service) rejectReceivedShare(ctx context.Context, receivedShare *collab } func (s *service) fetchShares(ctx context.Context) ([]*collaboration.ReceivedShare, map[string]*provider.ResourceInfo, error) { - lsRes, err := s.sharesProviderClient.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{ + sharingCollaborationClient, err := s.sharingCollaborationSelector.Next() + if err != nil { + return nil, nil, err + } + + lsRes, err := sharingCollaborationClient.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{ // FIXME filter by received shares for resource id - listing all shares is tooo expensive! }) if err != nil { @@ -1019,6 +1105,11 @@ func (s *service) fetchShares(ctx context.Context) ([]*collaboration.ReceivedSha return nil, nil, fmt.Errorf("sharesstorageprovider: error calling ListReceivedSharesRequest") } + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, nil, err + } + shareMetaData := make(map[string]*provider.ResourceInfo, len(lsRes.Shares)) for _, rs := range lsRes.Shares { // only stat accepted shares @@ -1029,7 +1120,7 @@ func (s *service) fetchShares(ctx context.Context) ([]*collaboration.ReceivedSha // convert backwards compatible share id rs.Share.ResourceId.StorageId, rs.Share.ResourceId.SpaceId = storagespace.SplitStorageID(rs.Share.ResourceId.StorageId) } - sRes, err := s.gateway.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: rs.Share.ResourceId}}) + sRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: rs.Share.ResourceId}}) if err != nil { appctx.GetLogger(ctx).Error(). Err(err). diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/archiver/handler.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/archiver/handler.go index 831fb5718b3..d668ad625e8 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/archiver/handler.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/archiver/handler.go @@ -47,11 +47,11 @@ import ( ) type svc struct { - config *Config - gtwClient gateway.GatewayAPIClient - log *zerolog.Logger - walker walker.Walker - downloader downloader.Downloader + config *Config + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + log *zerolog.Logger + walker walker.Walker + downloader downloader.Downloader allowedFolders []*regexp.Regexp } @@ -82,7 +82,7 @@ func New(conf map[string]interface{}, log *zerolog.Logger) (global.Service, erro c.init() - gtw, err := pool.GetGatewayServiceClient(c.GatewaySvc) + gatewaySelector, err := pool.GatewaySelector(c.GatewaySvc) if err != nil { return nil, err } @@ -98,12 +98,12 @@ func New(conf map[string]interface{}, log *zerolog.Logger) (global.Service, erro } return &svc{ - config: c, - gtwClient: gtw, - downloader: downloader.NewDownloader(gtw, rhttp.Insecure(c.Insecure), rhttp.Timeout(time.Duration(c.Timeout*int64(time.Second)))), - walker: walker.NewWalker(gtw), - log: log, - allowedFolders: allowedFolderRegex, + config: c, + gatewaySelector: gatewaySelector, + downloader: downloader.NewDownloader(gatewaySelector, rhttp.Insecure(c.Insecure), rhttp.Timeout(time.Duration(c.Timeout*int64(time.Second)))), + walker: walker.NewWalker(gatewaySelector), + log: log, + allowedFolders: allowedFolderRegex, }, nil } @@ -138,10 +138,14 @@ func (s *svc) getResources(ctx context.Context, paths, ids []string) ([]*provide } + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } for _, p := range paths { // id is base64 encoded and after decoding has the form : - resp, err := s.gtwClient.Stat(ctx, &provider.StatRequest{ + resp, err := gatewayClient.Stat(ctx, &provider.StatRequest{ Ref: &provider.Reference{ Path: p, }, diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/copy.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/copy.go index 1aa89ac6e45..7b4bce24ee0 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/copy.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/copy.go @@ -36,6 +36,7 @@ import ( "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spacelookup" "github.com/cs3org/reva/v2/pkg/appctx" "github.com/cs3org/reva/v2/pkg/errtypes" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/rhttp" "github.com/cs3org/reva/v2/pkg/rhttp/router" "github.com/cs3org/reva/v2/pkg/storagespace" @@ -105,7 +106,7 @@ func (s *svc) handlePathCopy(w http.ResponseWriter, r *http.Request, ns string) sublog := appctx.GetLogger(ctx).With().Str("src", src).Str("dst", dst).Logger() - srcSpace, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gwClient, src) + srcSpace, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gatewaySelector, src) if err != nil { sublog.Error().Err(err).Str("path", src).Msg("failed to look up storage space") w.WriteHeader(http.StatusInternalServerError) @@ -115,7 +116,7 @@ func (s *svc) handlePathCopy(w http.ResponseWriter, r *http.Request, ns string) errors.HandleErrorStatus(&sublog, w, status) return } - dstSpace, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gwClient, dst) + dstSpace, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gatewaySelector, dst) if err != nil { sublog.Error().Err(err).Str("path", dst).Msg("failed to look up storage space") w.WriteHeader(http.StatusInternalServerError) @@ -131,17 +132,22 @@ func (s *svc) handlePathCopy(w http.ResponseWriter, r *http.Request, ns string) return } - if err := s.executePathCopy(ctx, s.gwClient, w, r, cp); err != nil { + if err := s.executePathCopy(ctx, s.gatewaySelector, w, r, cp); err != nil { sublog.Error().Err(err).Str("depth", cp.depth.String()).Msg("error executing path copy") w.WriteHeader(http.StatusInternalServerError) } w.WriteHeader(cp.successCode) } -func (s *svc) executePathCopy(ctx context.Context, client gateway.GatewayAPIClient, w http.ResponseWriter, r *http.Request, cp *copy) error { +func (s *svc) executePathCopy(ctx context.Context, selector pool.Selectable[gateway.GatewayAPIClient], w http.ResponseWriter, r *http.Request, cp *copy) error { log := appctx.GetLogger(ctx) log.Debug().Str("src", cp.sourceInfo.Path).Str("dst", cp.destination.Path).Msg("descending") + client, err := selector.Next() + if err != nil { + return err + } + var fileid string if cp.sourceInfo.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { // create dir @@ -192,7 +198,7 @@ func (s *svc) executePathCopy(ctx context.Context, client gateway.GatewayAPIClie ResourceId: cp.destination.ResourceId, Path: utils.MakeRelativePath(filepath.Join(cp.destination.Path, child)), } - err := s.executePathCopy(ctx, client, w, r, ©{source: src, sourceInfo: res.Infos[i], destination: childDst, depth: cp.depth, successCode: cp.successCode}) + err := s.executePathCopy(ctx, selector, w, r, ©{source: src, sourceInfo: res.Infos[i], destination: childDst, depth: cp.depth, successCode: cp.successCode}) if err != nil { return err } @@ -354,7 +360,7 @@ func (s *svc) handleSpacesCopy(w http.ResponseWriter, r *http.Request, spaceID s return } - err = s.executeSpacesCopy(ctx, w, s.gwClient, cp) + err = s.executeSpacesCopy(ctx, w, s.gatewaySelector, cp) if err != nil { sublog.Error().Err(err).Str("depth", cp.depth.String()).Msg("error descending directory") w.WriteHeader(http.StatusInternalServerError) @@ -362,10 +368,15 @@ func (s *svc) handleSpacesCopy(w http.ResponseWriter, r *http.Request, spaceID s w.WriteHeader(cp.successCode) } -func (s *svc) executeSpacesCopy(ctx context.Context, w http.ResponseWriter, client gateway.GatewayAPIClient, cp *copy) error { +func (s *svc) executeSpacesCopy(ctx context.Context, w http.ResponseWriter, selector pool.Selectable[gateway.GatewayAPIClient], cp *copy) error { log := appctx.GetLogger(ctx) log.Debug().Interface("src", cp.sourceInfo).Interface("dst", cp.destination).Msg("descending") + client, err := selector.Next() + if err != nil { + return err + } + var fileid string if cp.sourceInfo.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { // create dir @@ -410,7 +421,7 @@ func (s *svc) executeSpacesCopy(ctx context.Context, w http.ResponseWriter, clie ResourceId: cp.destination.ResourceId, Path: utils.MakeRelativePath(path.Join(cp.destination.Path, res.Infos[i].Path)), } - err := s.executeSpacesCopy(ctx, w, client, ©{sourceInfo: res.Infos[i], destination: childRef, depth: cp.depth, successCode: cp.successCode}) + err := s.executeSpacesCopy(ctx, w, selector, ©{sourceInfo: res.Infos[i], destination: childRef, depth: cp.depth, successCode: cp.successCode}) if err != nil { return err } @@ -528,7 +539,7 @@ func (s *svc) executeSpacesCopy(ctx context.Context, w http.ResponseWriter, clie } func (s *svc) prepareCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, srcRef, dstRef *provider.Reference, log *zerolog.Logger) *copy { - isChild, err := s.referenceIsChildOf(ctx, s.gwClient, dstRef, srcRef) + isChild, err := s.referenceIsChildOf(ctx, s.gatewaySelector, dstRef, srcRef) if err != nil { switch err.(type) { case errtypes.IsNotSupported: @@ -573,8 +584,15 @@ func (s *svc) prepareCopy(ctx context.Context, w http.ResponseWriter, r *http.Re log.Debug().Bool("overwrite", overwrite).Str("depth", depth.String()).Msg("copy") + client, err := s.gatewaySelector.Next() + if err != nil { + log.Error().Err(err).Msg("error selecting next client") + w.WriteHeader(http.StatusInternalServerError) + return nil + } + srcStatReq := &provider.StatRequest{Ref: srcRef} - srcStatRes, err := s.gwClient.Stat(ctx, srcStatReq) + srcStatRes, err := client.Stat(ctx, srcStatReq) switch { case err != nil: log.Error().Err(err).Msg("error sending grpc stat request") @@ -592,7 +610,7 @@ func (s *svc) prepareCopy(ctx context.Context, w http.ResponseWriter, r *http.Re } dstStatReq := &provider.StatRequest{Ref: dstRef} - dstStatRes, err := s.gwClient.Stat(ctx, dstStatReq) + dstStatRes, err := client.Stat(ctx, dstStatReq) switch { case err != nil: log.Error().Err(err).Msg("error sending grpc stat request") @@ -621,7 +639,7 @@ func (s *svc) prepareCopy(ctx context.Context, w http.ResponseWriter, r *http.Re (dstStatRes.Info.Type == provider.ResourceType_RESOURCE_TYPE_FILE && srcStatRes.Info.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER) { delReq := &provider.DeleteRequest{Ref: dstRef} - delRes, err := s.gwClient.Delete(ctx, delReq) + delRes, err := client.Delete(ctx, delReq) if err != nil { log.Error().Err(err).Msg("error sending grpc delete request") w.WriteHeader(http.StatusInternalServerError) @@ -640,7 +658,7 @@ func (s *svc) prepareCopy(ctx context.Context, w http.ResponseWriter, r *http.Re Path: utils.MakeRelativePath(p), } intStatReq := &provider.StatRequest{Ref: pRef} - intStatRes, err := s.gwClient.Stat(ctx, intStatReq) + intStatRes, err := client.Stat(ctx, intStatReq) if err != nil { log.Error().Err(err).Msg("error sending grpc stat request") w.WriteHeader(http.StatusInternalServerError) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/dav.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/dav.go index 2795346529a..b3a133b2415 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/dav.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/dav.go @@ -32,6 +32,7 @@ import ( "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net" "github.com/cs3org/reva/v2/pkg/appctx" ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/rhttp/router" "github.com/cs3org/reva/v2/pkg/utils" "google.golang.org/grpc/metadata" @@ -188,7 +189,7 @@ func (h *DavHandler) Handler(s *svc) http.Handler { var pass string var err error if _, pass, hasValidBasicAuthHeader = r.BasicAuth(); hasValidBasicAuthHeader { - res, err = handleBasicAuth(r.Context(), s.gwClient, token, pass) + res, err = handleBasicAuth(r.Context(), s.gatewaySelector, token, pass) } else { q := r.URL.Query() sig := q.Get("signature") @@ -198,7 +199,7 @@ func (h *DavHandler) Handler(s *svc) http.Handler { w.WriteHeader(http.StatusUnauthorized) return } - res, err = handleSignatureAuth(r.Context(), s.gwClient, token, sig, expiration) + res, err = handleSignatureAuth(r.Context(), s.gatewaySelector, token, sig, expiration) } switch { @@ -232,7 +233,7 @@ func (h *DavHandler) Handler(s *svc) http.Handler { r = r.WithContext(ctx) // the public share manager knew the token, but does the referenced target still exist? - sRes, err := getTokenStatInfo(ctx, s.gwClient, token) + sRes, err := getTokenStatInfo(ctx, s.gatewaySelector, token) switch { case err != nil: log.Error().Err(err).Msg("error sending grpc stat request") @@ -271,7 +272,12 @@ func (h *DavHandler) Handler(s *svc) http.Handler { }) } -func getTokenStatInfo(ctx context.Context, client gatewayv1beta1.GatewayAPIClient, token string) (*provider.StatResponse, error) { +func getTokenStatInfo(ctx context.Context, selector pool.Selectable[gatewayv1beta1.GatewayAPIClient], token string) (*provider.StatResponse, error) { + client, err := selector.Next() + if err != nil { + return nil, err + } + return client.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ ResourceId: &provider.ResourceId{ StorageId: utils.PublicStorageProviderID, @@ -281,7 +287,11 @@ func getTokenStatInfo(ctx context.Context, client gatewayv1beta1.GatewayAPIClien }}) } -func handleBasicAuth(ctx context.Context, c gatewayv1beta1.GatewayAPIClient, token, pw string) (*gatewayv1beta1.AuthenticateResponse, error) { +func handleBasicAuth(ctx context.Context, selector pool.Selectable[gatewayv1beta1.GatewayAPIClient], token, pw string) (*gatewayv1beta1.AuthenticateResponse, error) { + c, err := selector.Next() + if err != nil { + return nil, err + } authenticateRequest := gatewayv1beta1.AuthenticateRequest{ Type: "publicshares", ClientId: token, @@ -291,7 +301,11 @@ func handleBasicAuth(ctx context.Context, c gatewayv1beta1.GatewayAPIClient, tok return c.Authenticate(ctx, &authenticateRequest) } -func handleSignatureAuth(ctx context.Context, c gatewayv1beta1.GatewayAPIClient, token, sig, expiration string) (*gatewayv1beta1.AuthenticateResponse, error) { +func handleSignatureAuth(ctx context.Context, selector pool.Selectable[gatewayv1beta1.GatewayAPIClient], token, sig, expiration string) (*gatewayv1beta1.AuthenticateResponse, error) { + c, err := selector.Next() + if err != nil { + return nil, err + } authenticateRequest := gatewayv1beta1.AuthenticateRequest{ Type: "publicshares", ClientId: token, diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/delete.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/delete.go index c091000376a..24ed1a7ada9 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/delete.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/delete.go @@ -45,7 +45,7 @@ func (s *svc) handlePathDelete(w http.ResponseWriter, r *http.Request, ns string fn := path.Join(ns, r.URL.Path) - space, rpcStatus, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gwClient, fn) + space, rpcStatus, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gatewaySelector, fn) switch { case err != nil: span.RecordError(err) @@ -73,7 +73,12 @@ func (s *svc) handleDelete(ctx context.Context, w http.ResponseWriter, r *http.R return http.StatusBadRequest, errtypes.BadRequest("invalid if header") } - res, err := s.gwClient.Delete(ctx, req) + client, err := s.gatewaySelector.Next() + if err != nil { + return http.StatusInternalServerError, errtypes.InternalError(err.Error()) + } + + res, err := client.Delete(ctx, req) switch { case err != nil: span.RecordError(err) @@ -92,7 +97,7 @@ func (s *svc) handleDelete(ctx context.Context, w http.ResponseWriter, r *http.R status = http.StatusLocked } // check if user has access to resource - sRes, err := s.gwClient.Stat(ctx, &provider.StatRequest{Ref: ref}) + sRes, err := client.Stat(ctx, &provider.StatRequest{Ref: ref}) if err != nil { span.RecordError(err) return http.StatusInternalServerError, err diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/get.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/get.go index 21b10ff7a7f..191d9cc043c 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/get.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/get.go @@ -45,7 +45,7 @@ func (s *svc) handlePathGet(w http.ResponseWriter, r *http.Request, ns string) { sublog := appctx.GetLogger(ctx).With().Str("path", fn).Str("svc", "ocdav").Str("handler", "get").Logger() - space, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gwClient, fn) + space, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gatewaySelector, fn) if err != nil { sublog.Error().Err(err).Str("path", fn).Msg("failed to look up storage space") w.WriteHeader(http.StatusInternalServerError) @@ -60,10 +60,16 @@ func (s *svc) handlePathGet(w http.ResponseWriter, r *http.Request, ns string) { } func (s *svc) handleGet(ctx context.Context, w http.ResponseWriter, r *http.Request, ref *provider.Reference, dlProtocol string, log zerolog.Logger) { + client, err := s.gatewaySelector.Next() + if err != nil { + log.Error().Err(err).Msg("error selecting next client") + w.WriteHeader(http.StatusInternalServerError) + return + } sReq := &provider.StatRequest{ Ref: ref, } - sRes, err := s.gwClient.Stat(ctx, sReq) + sRes, err := client.Stat(ctx, sReq) if err != nil { log.Error().Err(err).Msg("error stat resource") w.WriteHeader(http.StatusInternalServerError) @@ -85,7 +91,7 @@ func (s *svc) handleGet(ctx context.Context, w http.ResponseWriter, r *http.Requ } dReq := &provider.InitiateFileDownloadRequest{Ref: ref} - dRes, err := s.gwClient.InitiateFileDownload(ctx, dReq) + dRes, err := client.InitiateFileDownload(ctx, dReq) switch { case err != nil: log.Error().Err(err).Msg("error initiating file download") diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/head.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/head.go index 702193b3926..f055d18f491 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/head.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/head.go @@ -48,7 +48,7 @@ func (s *svc) handlePathHead(w http.ResponseWriter, r *http.Request, ns string) sublog := appctx.GetLogger(ctx).With().Str("path", fn).Logger() - space, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gwClient, fn) + space, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gatewaySelector, fn) if err != nil { sublog.Error().Err(err).Str("path", fn).Msg("failed to look up storage space") w.WriteHeader(http.StatusInternalServerError) @@ -63,9 +63,14 @@ func (s *svc) handlePathHead(w http.ResponseWriter, r *http.Request, ns string) } func (s *svc) handleHead(ctx context.Context, w http.ResponseWriter, r *http.Request, ref *provider.Reference, log zerolog.Logger) { - + client, err := s.gatewaySelector.Next() + if err != nil { + log.Error().Err(err).Msg("error selecting next client") + w.WriteHeader(http.StatusInternalServerError) + return + } req := &provider.StatRequest{Ref: ref} - res, err := s.gwClient.Stat(ctx, req) + res, err := client.Stat(ctx, req) if err != nil { log.Error().Err(err).Msg("error sending grpc stat request") w.WriteHeader(http.StatusInternalServerError) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/locks.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/locks.go index d822b10556d..54d69a1e21e 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/locks.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/locks.go @@ -41,6 +41,7 @@ import ( "github.com/cs3org/reva/v2/pkg/appctx" ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/errtypes" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/google/uuid" "go.opentelemetry.io/otel/attribute" ) @@ -159,14 +160,14 @@ type LockSystem interface { } // NewCS3LS returns a new CS3 based LockSystem. -func NewCS3LS(c gateway.GatewayAPIClient) LockSystem { +func NewCS3LS(s pool.Selectable[gateway.GatewayAPIClient]) LockSystem { return &cs3LS{ - client: c, + selector: s, } } type cs3LS struct { - client gateway.GatewayAPIClient + selector pool.Selectable[gateway.GatewayAPIClient] } func (cls *cs3LS) Confirm(ctx context.Context, now time.Time, name0, name1 string, conditions ...Condition) (func(), error) { @@ -205,7 +206,13 @@ func (cls *cs3LS) Create(ctx context.Context, now time.Time, details LockDetails Nanos: uint32(expiration.Nanosecond()), } } - res, err := cls.client.SetLock(ctx, r) + + client, err := cls.selector.Next() + if err != nil { + return "", err + } + + res, err := client.SetLock(ctx, r) if err != nil { return "", err } @@ -233,10 +240,17 @@ func (cls *cs3LS) Unlock(ctx context.Context, now time.Time, ref *provider.Refer User: u.Id, }, } - res, err := cls.client.Unlock(ctx, r) + + client, err := cls.selector.Next() if err != nil { return err } + + res, err := client.Unlock(ctx, r) + if err != nil { + return err + } + switch res.Status.Code { case rpc.Code_CODE_OK: return nil @@ -388,7 +402,7 @@ func (s *svc) handleLock(w http.ResponseWriter, r *http.Request, ns string) (ret fn := path.Join(ns, r.URL.Path) // TODO do we still need to jail if we query the registry about the spaces? // TODO instead of using a string namespace ns pass in the space with the request? - ref, cs3Status, err := spacelookup.LookupReferenceForPath(ctx, s.gwClient, fn) + ref, cs3Status, err := spacelookup.LookupReferenceForPath(ctx, s.gatewaySelector, fn) if err != nil { return http.StatusInternalServerError, err } @@ -566,7 +580,7 @@ func (s *svc) handleUnlock(w http.ResponseWriter, r *http.Request, ns string) (s fn := path.Join(ns, r.URL.Path) // TODO do we still need to jail if we query the registry about the spaces? // TODO instead of using a string namespace ns pass in the space with the request? - ref, cs3Status, err := spacelookup.LookupReferenceForPath(ctx, s.gwClient, fn) + ref, cs3Status, err := spacelookup.LookupReferenceForPath(ctx, s.gatewaySelector, fn) if err != nil { return http.StatusInternalServerError, err } diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/meta.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/meta.go index 69d7da344d3..169f9fd3fe4 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/meta.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/meta.go @@ -119,9 +119,14 @@ func (h *MetaHandler) handlePathForUser(w http.ResponseWriter, r *http.Request, w.WriteHeader(http.StatusBadRequest) return } - + client, err := s.gatewaySelector.Next() + if err != nil { + sublog.Error().Err(err).Msg("error selecting next client") + w.WriteHeader(http.StatusInternalServerError) + return + } pathReq := &provider.GetPathRequest{ResourceId: rid} - pathRes, err := s.gwClient.GetPath(ctx, pathReq) + pathRes, err := client.GetPath(ctx, pathReq) if err != nil { sublog.Error().Err(err).Msg("could not send GetPath grpc request: transport error") w.WriteHeader(http.StatusInternalServerError) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/mkcol.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/mkcol.go index 0636052b0eb..9b445a14392 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/mkcol.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/mkcol.go @@ -45,9 +45,14 @@ func (s *svc) handlePathMkcol(w http.ResponseWriter, r *http.Request, ns string) } sublog := appctx.GetLogger(ctx).With().Str("path", fn).Logger() + client, err := s.gatewaySelector.Next() + if err != nil { + return http.StatusInternalServerError, errtypes.InternalError(err.Error()) + } + // stat requested path to make sure it isn't existing yet // NOTE: It could be on another storage provider than the 'parent' of it - sr, err := s.gwClient.Stat(ctx, &provider.StatRequest{ + sr, err := client.Stat(ctx, &provider.StatRequest{ Ref: &provider.Reference{ Path: fn, }, @@ -67,7 +72,7 @@ func (s *svc) handlePathMkcol(w http.ResponseWriter, r *http.Request, ns string) parentPath := path.Dir(fn) - space, rpcStatus, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gwClient, parentPath) + space, rpcStatus, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gatewaySelector, parentPath) switch { case err != nil: return http.StatusInternalServerError, err @@ -108,8 +113,12 @@ func (s *svc) handleMkcol(ctx context.Context, w http.ResponseWriter, r *http.Re return http.StatusUnsupportedMediaType, fmt.Errorf("extended-mkcol not supported") } + client, err := s.gatewaySelector.Next() + if err != nil { + return http.StatusInternalServerError, errtypes.InternalError(err.Error()) + } req := &provider.CreateContainerRequest{Ref: childRef} - res, err := s.gwClient.CreateContainer(ctx, req) + res, err := client.CreateContainer(ctx, req) switch { case err != nil: return http.StatusInternalServerError, err @@ -123,7 +132,7 @@ func (s *svc) handleMkcol(ctx context.Context, w http.ResponseWriter, r *http.Re return http.StatusNotFound, errors.New("Resource not found") case res.Status.Code == rpc.Code_CODE_PERMISSION_DENIED: // check if user has access to parent - sRes, err := s.gwClient.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ + sRes, err := client.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ ResourceId: childRef.GetResourceId(), Path: utils.MakeRelativePath(path.Dir(childRef.Path)), }}) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/move.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/move.go index 793eaa9d8db..10ccb32ac56 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/move.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/move.go @@ -78,7 +78,7 @@ func (s *svc) handlePathMove(w http.ResponseWriter, r *http.Request, ns string) sublog := appctx.GetLogger(ctx).With().Str("src", srcPath).Str("dst", dstPath).Logger() - srcSpace, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gwClient, srcPath) + srcSpace, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gatewaySelector, srcPath) if err != nil { sublog.Error().Err(err).Str("path", srcPath).Msg("failed to look up source storage space") w.WriteHeader(http.StatusInternalServerError) @@ -88,7 +88,7 @@ func (s *svc) handlePathMove(w http.ResponseWriter, r *http.Request, ns string) errors.HandleErrorStatus(&sublog, w, status) return } - dstSpace, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gwClient, dstPath) + dstSpace, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gatewaySelector, dstPath) if err != nil { sublog.Error().Err(err).Str("path", dstPath).Msg("failed to look up destination storage space") w.WriteHeader(http.StatusInternalServerError) @@ -141,7 +141,7 @@ func (s *svc) handleSpacesMove(w http.ResponseWriter, r *http.Request, srcSpaceI } func (s *svc) handleMove(ctx context.Context, w http.ResponseWriter, r *http.Request, src, dst *provider.Reference, log zerolog.Logger) { - isChild, err := s.referenceIsChildOf(ctx, s.gwClient, dst, src) + isChild, err := s.referenceIsChildOf(ctx, s.gatewaySelector, dst, src) if err != nil { switch err.(type) { case errtypes.IsNotSupported: @@ -169,9 +169,16 @@ func (s *svc) handleMove(ctx context.Context, w http.ResponseWriter, r *http.Req return } + client, err := s.gatewaySelector.Next() + if err != nil { + log.Error().Err(err).Msg("error selecting next client") + w.WriteHeader(http.StatusInternalServerError) + return + } + // check src exists srcStatReq := &provider.StatRequest{Ref: src} - srcStatRes, err := s.gwClient.Stat(ctx, srcStatReq) + srcStatRes, err := client.Stat(ctx, srcStatReq) if err != nil { log.Error().Err(err).Msg("error sending grpc stat request") w.WriteHeader(http.StatusInternalServerError) @@ -190,7 +197,7 @@ func (s *svc) handleMove(ctx context.Context, w http.ResponseWriter, r *http.Req // check dst exists dstStatReq := &provider.StatRequest{Ref: dst} - dstStatRes, err := s.gwClient.Stat(ctx, dstStatReq) + dstStatRes, err := client.Stat(ctx, dstStatReq) if err != nil { log.Error().Err(err).Msg("error sending grpc stat request") w.WriteHeader(http.StatusInternalServerError) @@ -213,7 +220,7 @@ func (s *svc) handleMove(ctx context.Context, w http.ResponseWriter, r *http.Req // delete existing tree delReq := &provider.DeleteRequest{Ref: dst} - delRes, err := s.gwClient.Delete(ctx, delReq) + delRes, err := client.Delete(ctx, delReq) if err != nil { log.Error().Err(err).Msg("error sending grpc delete request") w.WriteHeader(http.StatusInternalServerError) @@ -230,7 +237,7 @@ func (s *svc) handleMove(ctx context.Context, w http.ResponseWriter, r *http.Req ResourceId: dst.ResourceId, Path: utils.MakeRelativePath(path.Dir(dst.Path)), }} - intStatRes, err := s.gwClient.Stat(ctx, intStatReq) + intStatRes, err := client.Stat(ctx, intStatReq) if err != nil { log.Error().Err(err).Msg("error sending grpc stat request") w.WriteHeader(http.StatusInternalServerError) @@ -250,7 +257,7 @@ func (s *svc) handleMove(ctx context.Context, w http.ResponseWriter, r *http.Req } mReq := &provider.MoveRequest{Source: src, Destination: dst} - mRes, err := s.gwClient.Move(ctx, mReq) + mRes, err := client.Move(ctx, mReq) if err != nil { log.Error().Err(err).Msg("error sending move grpc request") w.WriteHeader(http.StatusInternalServerError) @@ -279,7 +286,7 @@ func (s *svc) handleMove(ctx context.Context, w http.ResponseWriter, r *http.Req return } - dstStatRes, err = s.gwClient.Stat(ctx, dstStatReq) + dstStatRes, err = client.Stat(ctx, dstStatReq) if err != nil { log.Error().Err(err).Msg("error sending grpc stat request") w.WriteHeader(http.StatusInternalServerError) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/ocdav.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/ocdav.go index db25c5d7ae8..e0291a31946 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/ocdav.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/ocdav.go @@ -144,7 +144,7 @@ type svc struct { davHandler *DavHandler favoritesManager favorite.Manager client *http.Client - gwClient gateway.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] // LockSystem is the lock management system. LockSystem LockSystem userIdentifierCache *ttlcache.Cache @@ -163,11 +163,11 @@ func getFavoritesManager(c *Config) (favorite.Manager, error) { } func getLockSystem(c *Config) (LockSystem, error) { // TODO in memory implementation - client, err := pool.GetGatewayServiceClient(c.GatewaySvc) + selector, err := pool.GatewaySelector(c.GatewaySvc) if err != nil { return nil, err } - return NewCS3LS(client), nil + return NewCS3LS(selector), nil } // New returns a new ocdav service @@ -192,7 +192,7 @@ func New(m map[string]interface{}, log *zerolog.Logger) (global.Service, error) } // NewWith returns a new ocdav service -func NewWith(conf *Config, fm favorite.Manager, ls LockSystem, _ *zerolog.Logger, gwc gateway.GatewayAPIClient) (global.Service, error) { +func NewWith(conf *Config, fm favorite.Manager, ls LockSystem, _ *zerolog.Logger, selector pool.Selectable[gateway.GatewayAPIClient]) (global.Service, error) { // be safe - init the conf again conf.init() @@ -204,7 +204,7 @@ func NewWith(conf *Config, fm favorite.Manager, ls LockSystem, _ *zerolog.Logger rhttp.Timeout(time.Duration(conf.Timeout*int64(time.Second))), rhttp.Insecure(conf.Insecure), ), - gwClient: gwc, + gatewaySelector: selector, favoritesManager: fm, LockSystem: ls, userIdentifierCache: ttlcache.NewCache(), @@ -219,9 +219,9 @@ func NewWith(conf *Config, fm favorite.Manager, ls LockSystem, _ *zerolog.Logger if err := s.davHandler.init(conf); err != nil { return nil, err } - if gwc == nil { + if selector == nil { var err error - s.gwClient, err = pool.GetGatewayServiceClient(s.c.GatewaySvc) + s.gatewaySelector, err = pool.GatewaySelector(s.c.GatewaySvc) if err != nil { return nil, err } @@ -326,7 +326,12 @@ func (s *svc) ApplyLayout(ctx context.Context, ns string, useLoggedInUserNS bool requestUsernameOrID, requestPath = router.ShiftPath(requestPath) // Check if this is a Userid - userRes, err := s.gwClient.GetUser(ctx, &userpb.GetUserRequest{ + client, err := s.gatewaySelector.Next() + if err != nil { + return "", "", err + } + + userRes, err := client.GetUser(ctx, &userpb.GetUserRequest{ UserId: &userpb.UserId{OpaqueId: requestUsernameOrID}, }) if err != nil { @@ -335,7 +340,7 @@ func (s *svc) ApplyLayout(ctx context.Context, ns string, useLoggedInUserNS bool // If it's not a userid try if it is a user name if userRes.Status.Code != rpc.Code_CODE_OK { - res, err := s.gwClient.GetUserByClaim(ctx, &userpb.GetUserByClaimRequest{ + res, err := client.GetUserByClaim(ctx, &userpb.GetUserByClaimRequest{ Claim: "username", Value: requestUsernameOrID, }) @@ -406,7 +411,11 @@ func authContextForUser(client gateway.GatewayAPIClient, userID *userpb.UserId, return granteeCtx, nil } -func (s *svc) sspReferenceIsChildOf(ctx context.Context, client gateway.GatewayAPIClient, child, parent *provider.Reference) (bool, error) { +func (s *svc) sspReferenceIsChildOf(ctx context.Context, selector pool.Selectable[gateway.GatewayAPIClient], child, parent *provider.Reference) (bool, error) { + client, err := selector.Next() + if err != nil { + return false, err + } parentStatRes, err := client.Stat(ctx, &provider.StatRequest{Ref: parent}) if err != nil { return false, err @@ -448,7 +457,7 @@ func (s *svc) sspReferenceIsChildOf(ctx context.Context, client gateway.GatewayA return strings.HasPrefix(cp, pp), nil } -func (s *svc) referenceIsChildOf(ctx context.Context, client gateway.GatewayAPIClient, child, parent *provider.Reference) (bool, error) { +func (s *svc) referenceIsChildOf(ctx context.Context, selector pool.Selectable[gateway.GatewayAPIClient], child, parent *provider.Reference) (bool, error) { if child.ResourceId.SpaceId != parent.ResourceId.SpaceId { return false, nil // Not on the same storage -> not a child } @@ -459,7 +468,12 @@ func (s *svc) referenceIsChildOf(ctx context.Context, client gateway.GatewayAPIC if child.ResourceId.SpaceId == utils.ShareStorageSpaceID || parent.ResourceId.SpaceId == utils.ShareStorageSpaceID { // the sharesstorageprovider needs some special handling - return s.sspReferenceIsChildOf(ctx, client, child, parent) + return s.sspReferenceIsChildOf(ctx, selector, child, parent) + } + + client, err := selector.Next() + if err != nil { + return false, err } // the references are on the same storage but relative to different resources diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind/propfind.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind/propfind.go index ad4c671a293..6e0de1022e7 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind/propfind.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind/propfind.go @@ -47,6 +47,7 @@ import ( ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/publicshare" rstatus "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/rhttp/router" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" @@ -163,20 +164,17 @@ func NewMultiStatusResponseXML() *MultiStatusResponseXML { } } -// GetGatewayServiceClientFunc is a callback used to pass in a StorageProviderClient during testing -type GetGatewayServiceClientFunc func() (gateway.GatewayAPIClient, error) - // Handler handles propfind requests type Handler struct { PublicURL string - getClient GetGatewayServiceClientFunc + selector pool.Selectable[gateway.GatewayAPIClient] } // NewHandler returns a new PropfindHandler instance -func NewHandler(publicURL string, getClientFunc GetGatewayServiceClientFunc) *Handler { +func NewHandler(publicURL string, selector pool.Selectable[gateway.GatewayAPIClient]) *Handler { return &Handler{ PublicURL: publicURL, - getClient: getClientFunc, + selector: selector, } } @@ -197,7 +195,7 @@ func (p *Handler) HandlePathPropfind(w http.ResponseWriter, r *http.Request, ns } // retrieve a specific storage space - client, err := p.getClient() + client, err := p.selector.Next() if err != nil { sublog.Error().Err(err).Msg("error retrieving a gateway service client") w.WriteHeader(http.StatusInternalServerError) @@ -263,7 +261,7 @@ func (p *Handler) HandleSpacesPropfind(w http.ResponseWriter, r *http.Request, s return } - client, err := p.getClient() + client, err := p.selector.Next() if err != nil { sublog.Error().Err(err).Msg("error getting grpc client") w.WriteHeader(http.StatusInternalServerError) @@ -386,7 +384,7 @@ func (p *Handler) propfindResponse(ctx context.Context, w http.ResponseWriter, r // same as user / group shares for share indicators filters = append(filters, publicshare.ResourceIDFilter(resourceInfos[i].Id)) } - client, err := p.getClient() + client, err := p.selector.Next() if err != nil { log.Error().Err(err).Msg("error getting grpc client") w.WriteHeader(http.StatusInternalServerError) @@ -465,7 +463,7 @@ func (p *Handler) getResourceInfos(ctx context.Context, w http.ResponseWriter, r } span.SetAttributes(attribute.KeyValue{Key: "depth", Value: attribute.StringValue(depth.String())}) - client, err := p.getClient() + client, err := p.selector.Next() if err != nil { log.Error().Err(err).Msg("error getting grpc client") w.WriteHeader(http.StatusInternalServerError) @@ -675,7 +673,7 @@ func (p *Handler) getSpaceResourceInfos(ctx context.Context, w http.ResponseWrit span.SetAttributes(attribute.KeyValue{Key: "depth", Value: attribute.StringValue(depth.String())}) defer span.End() - client, err := p.getClient() + client, err := p.selector.Next() if err != nil { log.Error().Err(err).Msg("error getting grpc client") w.WriteHeader(http.StatusInternalServerError) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/proppatch.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/proppatch.go index 9358e6b8cdc..492348d4072 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/proppatch.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/proppatch.go @@ -55,7 +55,7 @@ func (s *svc) handlePathProppatch(w http.ResponseWriter, r *http.Request, ns str return status, err } - space, rpcStatus, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gwClient, fn) + space, rpcStatus, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gatewaySelector, fn) switch { case err != nil: return http.StatusInternalServerError, err @@ -64,9 +64,14 @@ func (s *svc) handlePathProppatch(w http.ResponseWriter, r *http.Request, ns str case rpcStatus.Code != rpc.Code_CODE_OK: return rstatus.HTTPStatusFromCode(rpcStatus.Code), errtypes.NewErrtypeFromStatus(rpcStatus) } + + client, err := s.gatewaySelector.Next() + if err != nil { + return http.StatusInternalServerError, errtypes.InternalError(err.Error()) + } // check if resource exists statReq := &provider.StatRequest{Ref: spacelookup.MakeRelativeReference(space, fn, false)} - statRes, err := s.gwClient.Stat(ctx, statReq) + statRes, err := client.Stat(ctx, statReq) switch { case err != nil: return http.StatusInternalServerError, err @@ -137,6 +142,12 @@ func (s *svc) handleProppatch(ctx context.Context, w http.ResponseWriter, r *htt acceptedProps := []xml.Name{} removedProps := []xml.Name{} + client, err := s.gatewaySelector.Next() + if err != nil { + log.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return nil, nil, false + } for i := range patches { if len(patches[i].Props) < 1 { continue @@ -161,7 +172,7 @@ func (s *svc) handleProppatch(ctx context.Context, w http.ResponseWriter, r *htt // FIXME: batch this somehow if remove { rreq.ArbitraryMetadataKeys[0] = key - res, err := s.gwClient.UnsetArbitraryMetadata(ctx, rreq) + res, err := client.UnsetArbitraryMetadata(ctx, rreq) if err != nil { log.Error().Err(err).Msg("error sending a grpc UnsetArbitraryMetadata request") w.WriteHeader(http.StatusInternalServerError) @@ -178,7 +189,7 @@ func (s *svc) handleProppatch(ctx context.Context, w http.ResponseWriter, r *htt m := res.Status.Message if res.Status.Code == rpc.Code_CODE_PERMISSION_DENIED { // check if user has access to resource - sRes, err := s.gwClient.Stat(ctx, &provider.StatRequest{Ref: ref}) + sRes, err := client.Stat(ctx, &provider.StatRequest{Ref: ref}) if err != nil { log.Error().Err(err).Msg("error performing stat grpc request") w.WriteHeader(http.StatusInternalServerError) @@ -200,7 +211,7 @@ func (s *svc) handleProppatch(ctx context.Context, w http.ResponseWriter, r *htt return nil, nil, false } if key == "http://owncloud.org/ns/favorite" { - statRes, err := s.gwClient.Stat(ctx, &provider.StatRequest{Ref: ref}) + statRes, err := client.Stat(ctx, &provider.StatRequest{Ref: ref}) if err != nil { w.WriteHeader(http.StatusInternalServerError) return nil, nil, false @@ -215,7 +226,7 @@ func (s *svc) handleProppatch(ctx context.Context, w http.ResponseWriter, r *htt removedProps = append(removedProps, propNameXML) } else { sreq.ArbitraryMetadata.Metadata[key] = value - res, err := s.gwClient.SetArbitraryMetadata(ctx, sreq) + res, err := client.SetArbitraryMetadata(ctx, sreq) if err != nil { log.Error().Err(err).Str("key", key).Str("value", value).Msg("error sending a grpc SetArbitraryMetadata request") w.WriteHeader(http.StatusInternalServerError) @@ -232,7 +243,7 @@ func (s *svc) handleProppatch(ctx context.Context, w http.ResponseWriter, r *htt m := res.Status.Message if res.Status.Code == rpc.Code_CODE_PERMISSION_DENIED { // check if user has access to resource - sRes, err := s.gwClient.Stat(ctx, &provider.StatRequest{Ref: ref}) + sRes, err := client.Stat(ctx, &provider.StatRequest{Ref: ref}) if err != nil { log.Error().Err(err).Msg("error performing stat grpc request") w.WriteHeader(http.StatusInternalServerError) @@ -258,7 +269,7 @@ func (s *svc) handleProppatch(ctx context.Context, w http.ResponseWriter, r *htt delete(sreq.ArbitraryMetadata.Metadata, key) if key == "http://owncloud.org/ns/favorite" { - statRes, err := s.gwClient.Stat(ctx, &provider.StatRequest{Ref: ref}) + statRes, err := client.Stat(ctx, &provider.StatRequest{Ref: ref}) if err != nil || statRes.Info == nil { w.WriteHeader(http.StatusInternalServerError) return nil, nil, false diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/put.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/put.go index f90b0d7b3ee..c76aafeb652 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/put.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/put.go @@ -114,7 +114,7 @@ func (s *svc) handlePathPut(w http.ResponseWriter, r *http.Request, ns string) { fn := path.Join(ns, r.URL.Path) sublog := appctx.GetLogger(ctx).With().Str("path", fn).Logger() - space, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gwClient, fn) + space, status, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gatewaySelector, fn) if err != nil { sublog.Error().Err(err).Str("path", fn).Msg("failed to look up storage space") w.WriteHeader(http.StatusInternalServerError) @@ -148,8 +148,14 @@ func (s *svc) handlePut(ctx context.Context, w http.ResponseWriter, r *http.Requ return } + client, err := s.gatewaySelector.Next() + if err != nil { + log.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return + } if length == 0 { - tfRes, err := s.gwClient.TouchFile(ctx, &provider.TouchFileRequest{ + tfRes, err := client.TouchFile(ctx, &provider.TouchFileRequest{ Ref: ref, }) if err != nil { @@ -162,7 +168,7 @@ func (s *svc) handlePut(ctx context.Context, w http.ResponseWriter, r *http.Requ w.WriteHeader(http.StatusInternalServerError) return } - sRes, err := s.gwClient.Stat(ctx, &provider.StatRequest{ + sRes, err := client.Stat(ctx, &provider.StatRequest{ Ref: ref, }) if err != nil { @@ -241,7 +247,7 @@ func (s *svc) handlePut(ctx context.Context, w http.ResponseWriter, r *http.Requ } // where to upload the file? - uRes, err := s.gwClient.InitiateFileUpload(ctx, uReq) + uRes, err := client.InitiateFileUpload(ctx, uReq) if err != nil { log.Error().Err(err).Msg("error initiating file upload") w.WriteHeader(http.StatusInternalServerError) @@ -258,7 +264,7 @@ func (s *svc) handlePut(ctx context.Context, w http.ResponseWriter, r *http.Requ status := http.StatusForbidden m := uRes.Status.Message // check if user has access to parent - sRes, err := s.gwClient.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ + sRes, err := client.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ ResourceId: ref.ResourceId, Path: utils.MakeRelativePath(path.Dir(ref.Path)), }}) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/report.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/report.go index 3cea9b1f7af..02f68867677 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/report.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/report.go @@ -81,9 +81,15 @@ func (s *svc) doFilterFiles(w http.ResponseWriter, r *http.Request, ff *reportFi return } + client, err := s.gatewaySelector.Next() + if err != nil { + log.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return + } infos := make([]*provider.ResourceInfo, 0, len(favorites)) for i := range favorites { - statRes, err := s.gwClient.Stat(ctx, &providerv1beta1.StatRequest{Ref: &providerv1beta1.Reference{ResourceId: favorites[i]}}) + statRes, err := client.Stat(ctx, &providerv1beta1.StatRequest{Ref: &providerv1beta1.Reference{ResourceId: favorites[i]}}) if err != nil { log.Error().Err(err).Msg("error getting resource info") continue diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spacelookup/spacelookup.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spacelookup/spacelookup.go index b9f68ed22e4..db6b0cc2a8b 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spacelookup/spacelookup.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spacelookup/spacelookup.go @@ -29,6 +29,7 @@ import ( storageProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" "google.golang.org/protobuf/types/known/fieldmaskpb" @@ -37,8 +38,8 @@ import ( // LookupReferenceForPath returns: // a reference with root and relative path // the status and error for the lookup -func LookupReferenceForPath(ctx context.Context, client gateway.GatewayAPIClient, path string) (*storageProvider.Reference, *rpc.Status, error) { - space, cs3Status, err := LookUpStorageSpaceForPath(ctx, client, path) +func LookupReferenceForPath(ctx context.Context, selector pool.Selectable[gateway.GatewayAPIClient], path string) (*storageProvider.Reference, *rpc.Status, error) { + space, cs3Status, err := LookUpStorageSpaceForPath(ctx, selector, path) if err != nil || cs3Status.Code != rpc.Code_CODE_OK { return nil, cs3Status, err } @@ -52,7 +53,7 @@ func LookupReferenceForPath(ctx context.Context, client gateway.GatewayAPIClient // LookUpStorageSpaceForPath returns: // the storage spaces responsible for a path // the status and error for the lookup -func LookUpStorageSpaceForPath(ctx context.Context, client gateway.GatewayAPIClient, path string) (*storageProvider.StorageSpace, *rpc.Status, error) { +func LookUpStorageSpaceForPath(ctx context.Context, selector pool.Selectable[gateway.GatewayAPIClient], path string) (*storageProvider.StorageSpace, *rpc.Status, error) { // TODO add filter to only fetch spaces changed in the last 30 sec? // TODO cache space information, invalidate after ... 5min? so we do not need to fetch all spaces? // TODO use ListContainerStream to listen for changes @@ -72,6 +73,11 @@ func LookUpStorageSpaceForPath(ctx context.Context, client gateway.GatewayAPICli }, } + client, err := selector.Next() + if err != nil { + return nil, status.NewInternal(ctx, "could not select next client"), err + } + lSSRes, err := client.ListStorageSpaces(ctx, lSSReq) if err != nil || lSSRes.Status.Code != rpc.Code_CODE_OK { status := status.NewStatusFromErrType(ctx, "failed to lookup storage spaces", err) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spaces.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spaces.go index 40c22728708..b915ce63b09 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spaces.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spaces.go @@ -22,7 +22,6 @@ import ( "net/http" "path" - gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind" @@ -79,9 +78,7 @@ func (h *SpacesHandler) Handler(s *svc, trashbinHandler *TrashbinHandler) http.H var err error switch r.Method { case MethodPropfind: - p := propfind.NewHandler(config.PublicURL, func() (gateway.GatewayAPIClient, error) { - return s.gwClient, nil - }) + p := propfind.NewHandler(config.PublicURL, s.gatewaySelector) p.HandleSpacesPropfind(w, r, spaceID) case MethodProppatch: status, err = s.handleSpacesProppatch(w, r, spaceID) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tpc.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tpc.go index 1b138c1a8e1..0c18979f803 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tpc.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tpc.go @@ -37,6 +37,7 @@ import ( "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net" "github.com/cs3org/reva/v2/pkg/appctx" "github.com/cs3org/reva/v2/pkg/errtypes" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/rhttp" ) @@ -134,10 +135,16 @@ func (s *svc) handleTPCPull(ctx context.Context, w http.ResponseWriter, r *http. } sublog.Debug().Bool("overwrite", overwrite).Msg("TPC Pull") + client, err := s.gatewaySelector.Next() + if err != nil { + sublog.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return + } // check if destination exists ref := &provider.Reference{Path: dst} dstStatReq := &provider.StatRequest{Ref: ref} - dstStatRes, err := s.gwClient.Stat(ctx, dstStatReq) + dstStatRes, err := client.Stat(ctx, dstStatReq) if err != nil { sublog.Error().Err(err).Msg("error sending grpc stat request") w.WriteHeader(http.StatusInternalServerError) @@ -153,7 +160,7 @@ func (s *svc) handleTPCPull(ctx context.Context, w http.ResponseWriter, r *http. return } - err = s.performHTTPPull(ctx, s.gwClient, r, w, ns) + err = s.performHTTPPull(ctx, s.gatewaySelector, r, w, ns) if err != nil { sublog.Error().Err(err).Msg("error performing TPC Pull") return @@ -161,7 +168,7 @@ func (s *svc) handleTPCPull(ctx context.Context, w http.ResponseWriter, r *http. fmt.Fprintf(w, "success: Created") } -func (s *svc) performHTTPPull(ctx context.Context, client gateway.GatewayAPIClient, r *http.Request, w http.ResponseWriter, ns string) error { +func (s *svc) performHTTPPull(ctx context.Context, selector pool.Selectable[gateway.GatewayAPIClient], r *http.Request, w http.ResponseWriter, ns string) error { src := r.Header.Get("Source") dst := path.Join(ns, r.URL.Path) sublog := appctx.GetLogger(ctx) @@ -197,6 +204,12 @@ func (s *svc) performHTTPPull(ctx context.Context, client gateway.GatewayAPIClie return errtypes.InternalError(fmt.Sprintf("Remote GET returned status code %d", httpDownloadRes.StatusCode)) } + client, err := s.gatewaySelector.Next() + if err != nil { + sublog.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return errtypes.InternalError(err.Error()) + } // get upload url uReq := &provider.InitiateFileUploadRequest{ Ref: &provider.Reference{Path: dst}, @@ -287,9 +300,15 @@ func (s *svc) handleTPCPush(ctx context.Context, w http.ResponseWriter, r *http. sublog.Debug().Bool("overwrite", overwrite).Msg("TPC Push") + client, err := s.gatewaySelector.Next() + if err != nil { + sublog.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return + } ref := &provider.Reference{Path: src} srcStatReq := &provider.StatRequest{Ref: ref} - srcStatRes, err := s.gwClient.Stat(ctx, srcStatReq) + srcStatRes, err := client.Stat(ctx, srcStatReq) if err != nil { sublog.Error().Err(err).Msg("error sending grpc stat request") w.WriteHeader(http.StatusInternalServerError) @@ -305,7 +324,7 @@ func (s *svc) handleTPCPush(ctx context.Context, w http.ResponseWriter, r *http. return } - err = s.performHTTPPush(ctx, s.gwClient, r, w, srcStatRes.Info, ns) + err = s.performHTTPPush(ctx, r, w, srcStatRes.Info, ns) if err != nil { sublog.Error().Err(err).Msg("error performing TPC Push") return @@ -313,7 +332,7 @@ func (s *svc) handleTPCPush(ctx context.Context, w http.ResponseWriter, r *http. fmt.Fprintf(w, "success: Created") } -func (s *svc) performHTTPPush(ctx context.Context, client gateway.GatewayAPIClient, r *http.Request, w http.ResponseWriter, srcInfo *provider.ResourceInfo, ns string) error { +func (s *svc) performHTTPPush(ctx context.Context, r *http.Request, w http.ResponseWriter, srcInfo *provider.ResourceInfo, ns string) error { src := path.Join(ns, r.URL.Path) dst := r.Header.Get("Destination") @@ -325,6 +344,12 @@ func (s *svc) performHTTPPush(ctx context.Context, client gateway.GatewayAPIClie Ref: &provider.Reference{Path: src}, } + client, err := s.gatewaySelector.Next() + if err != nil { + sublog.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return err + } dRes, err := client.InitiateFileDownload(ctx, dReq) if err != nil { w.WriteHeader(http.StatusInternalServerError) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/trashbin.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/trashbin.go index 3944546a036..78de68e4af0 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/trashbin.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/trashbin.go @@ -111,7 +111,7 @@ func (h *TrashbinHandler) Handler(s *svc) http.Handler { r.URL.Path = newPath basePath := path.Join(ns, newPath) - space, rpcstatus, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gwClient, basePath) + space, rpcstatus, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gatewaySelector, basePath) switch { case err != nil: log.Error().Err(err).Str("path", basePath).Msg("failed to look up storage space") @@ -151,7 +151,7 @@ func (h *TrashbinHandler) Handler(s *svc) http.Handler { p := path.Join(ns, dst) // The destination can be in another space. E.g. the 'Shares Jail'. - space, rpcstatus, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gwClient, p) + space, rpcstatus, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gatewaySelector, p) if err != nil { log.Error().Err(err).Str("path", p).Msg("failed to look up destination storage space") w.WriteHeader(http.StatusInternalServerError) @@ -216,8 +216,14 @@ func (h *TrashbinHandler) listTrashbin(w http.ResponseWriter, r *http.Request, s return } + client, err := s.gatewaySelector.Next() + if err != nil { + sublog.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return + } // ask gateway for recycle items - getRecycleRes, err := s.gwClient.ListRecycle(ctx, &provider.ListRecycleRequest{Ref: ref, Key: path.Join(key, itemPath)}) + getRecycleRes, err := client.ListRecycle(ctx, &provider.ListRecycleRequest{Ref: ref, Key: path.Join(key, itemPath)}) if err != nil { sublog.Error().Err(err).Msg("error calling ListRecycle") w.WriteHeader(http.StatusInternalServerError) @@ -247,7 +253,7 @@ func (h *TrashbinHandler) listTrashbin(w http.ResponseWriter, r *http.Request, s for len(stack) > 0 { key := stack[len(stack)-1] - getRecycleRes, err := s.gwClient.ListRecycle(ctx, &provider.ListRecycleRequest{Ref: ref, Key: key}) + getRecycleRes, err := client.ListRecycle(ctx, &provider.ListRecycleRequest{Ref: ref, Key: key}) if err != nil { sublog.Error().Err(err).Msg("error calling ListRecycle") w.WriteHeader(http.StatusInternalServerError) @@ -465,8 +471,14 @@ func (h *TrashbinHandler) restore(w http.ResponseWriter, r *http.Request, s *svc return } + client, err := s.gatewaySelector.Next() + if err != nil { + sublog.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return + } dstStatReq := &provider.StatRequest{Ref: dst} - dstStatRes, err := s.gwClient.Stat(ctx, dstStatReq) + dstStatRes, err := client.Stat(ctx, dstStatReq) if err != nil { sublog.Error().Err(err).Msg("error sending grpc stat request") w.WriteHeader(http.StatusInternalServerError) @@ -484,7 +496,7 @@ func (h *TrashbinHandler) restore(w http.ResponseWriter, r *http.Request, s *svc parentRef := &provider.Reference{ResourceId: dst.ResourceId, Path: utils.MakeRelativePath(path.Dir(dst.Path))} parentStatReq := &provider.StatRequest{Ref: parentRef} - parentStatResponse, err := s.gwClient.Stat(ctx, parentStatReq) + parentStatResponse, err := client.Stat(ctx, parentStatReq) if err != nil { sublog.Error().Err(err).Msg("error sending grpc stat request") w.WriteHeader(http.StatusInternalServerError) @@ -515,7 +527,7 @@ func (h *TrashbinHandler) restore(w http.ResponseWriter, r *http.Request, s *svc } // delete existing tree delReq := &provider.DeleteRequest{Ref: dst} - delRes, err := s.gwClient.Delete(ctx, delReq) + delRes, err := client.Delete(ctx, delReq) if err != nil { sublog.Error().Err(err).Msg("error sending grpc delete request") w.WriteHeader(http.StatusInternalServerError) @@ -534,7 +546,7 @@ func (h *TrashbinHandler) restore(w http.ResponseWriter, r *http.Request, s *svc RestoreRef: dst, } - res, err := s.gwClient.RestoreRecycleItem(ctx, req) + res, err := client.RestoreRecycleItem(ctx, req) if err != nil { sublog.Error().Err(err).Msg("error sending a grpc restore recycle item request") w.WriteHeader(http.StatusInternalServerError) @@ -551,7 +563,7 @@ func (h *TrashbinHandler) restore(w http.ResponseWriter, r *http.Request, s *svc return } - dstStatRes, err = s.gwClient.Stat(ctx, dstStatReq) + dstStatRes, err = client.Stat(ctx, dstStatReq) if err != nil { sublog.Error().Err(err).Msg("error sending grpc stat request") w.WriteHeader(http.StatusInternalServerError) @@ -584,7 +596,13 @@ func (h *TrashbinHandler) delete(w http.ResponseWriter, r *http.Request, s *svc, Key: trashPath, } - res, err := s.gwClient.PurgeRecycle(ctx, req) + client, err := s.gatewaySelector.Next() + if err != nil { + sublog.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return + } + res, err := client.PurgeRecycle(ctx, req) if err != nil { sublog.Error().Err(err).Msg("error sending a grpc restore recycle item request") w.WriteHeader(http.StatusInternalServerError) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tus.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tus.go index 7e9148d068a..8a6e6a66cc4 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tus.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tus.go @@ -114,10 +114,15 @@ func (s *svc) handleTusPost(ctx context.Context, w http.ResponseWriter, r *http. // TODO check Expect: 100-continue + client, err := s.gatewaySelector.Next() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } sReq := &provider.StatRequest{ Ref: ref, } - sRes, err := s.gwClient.Stat(ctx, sReq) + sRes, err := client.Stat(ctx, sReq) if err != nil { log.Error().Err(err).Msg("error sending grpc stat request") w.WriteHeader(http.StatusInternalServerError) @@ -155,7 +160,7 @@ func (s *svc) handleTusPost(ctx context.Context, w http.ResponseWriter, r *http. return } if uploadLength == 0 { - tfRes, err := s.gwClient.TouchFile(ctx, &provider.TouchFileRequest{ + tfRes, err := client.TouchFile(ctx, &provider.TouchFileRequest{ Ref: ref, }) if err != nil { @@ -193,7 +198,7 @@ func (s *svc) handleTusPost(ctx context.Context, w http.ResponseWriter, r *http. }, } - uRes, err := s.gwClient.InitiateFileUpload(ctx, uReq) + uRes, err := client.InitiateFileUpload(ctx, uReq) if err != nil { log.Error().Err(err).Msg("error initiating file upload") w.WriteHeader(http.StatusInternalServerError) @@ -284,7 +289,7 @@ func (s *svc) handleTusPost(ctx context.Context, w http.ResponseWriter, r *http. } } - sRes, err := s.gwClient.Stat(ctx, sReq) + sRes, err := client.Stat(ctx, sReq) if err != nil { log.Error().Err(err).Msg("error sending grpc stat request") w.WriteHeader(http.StatusInternalServerError) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/versions.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/versions.go index 36831f0cf2c..ae249c1a6f7 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/versions.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/versions.go @@ -122,8 +122,14 @@ func (h *VersionsHandler) doListVersions(w http.ResponseWriter, r *http.Request, return } + client, err := s.gatewaySelector.Next() + if err != nil { + sublog.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return + } ref := &provider.Reference{ResourceId: rid} - res, err := s.gwClient.Stat(ctx, &provider.StatRequest{Ref: ref}) + res, err := client.Stat(ctx, &provider.StatRequest{Ref: ref}) if err != nil { sublog.Error().Err(err).Msg("error sending a grpc stat request") w.WriteHeader(http.StatusInternalServerError) @@ -142,7 +148,7 @@ func (h *VersionsHandler) doListVersions(w http.ResponseWriter, r *http.Request, info := res.Info - lvRes, err := s.gwClient.ListFileVersions(ctx, &provider.ListFileVersionsRequest{Ref: ref}) + lvRes, err := client.ListFileVersions(ctx, &provider.ListFileVersionsRequest{Ref: ref}) if err != nil { sublog.Error().Err(err).Msg("error sending list container grpc request") w.WriteHeader(http.StatusInternalServerError) @@ -225,7 +231,13 @@ func (h *VersionsHandler) doRestore(w http.ResponseWriter, r *http.Request, s *s Key: key, } - res, err := s.gwClient.RestoreFileVersion(ctx, req) + client, err := s.gatewaySelector.Next() + if err != nil { + sublog.Error().Err(err).Msg("error selecting next gateway client") + w.WriteHeader(http.StatusInternalServerError) + return + } + res, err := client.RestoreFileVersion(ctx, req) if err != nil { sublog.Error().Err(err).Msg("error sending a grpc restore version request") w.WriteHeader(http.StatusInternalServerError) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/webdav.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/webdav.go index 6a169edb66d..e471bfdf41d 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/webdav.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/webdav.go @@ -23,7 +23,6 @@ import ( "net/http" "path" - gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind" "github.com/cs3org/reva/v2/pkg/appctx" @@ -73,9 +72,7 @@ func (h *WebDavHandler) Handler(s *svc) http.Handler { var status int // status 0 means the handler already sent the response switch r.Method { case MethodPropfind: - p := propfind.NewHandler(config.PublicURL, func() (gateway.GatewayAPIClient, error) { - return s.gwClient, nil - }) + p := propfind.NewHandler(config.PublicURL, s.gatewaySelector) p.HandlePathPropfind(w, r, ns) case MethodLock: status, err = s.handleLock(w, r, ns) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/conversions/main.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/conversions/main.go index 307ed0f3998..c475b2be16a 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/conversions/main.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/conversions/main.go @@ -345,7 +345,7 @@ func ParseTimestamp(timestampString string) (*types.Timestamp, error) { parsedTime, err = time.Parse("2006-01-02", timestampString) if err == nil { // the link needs to be valid for the whole day - parsedTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second) + parsedTime = parsedTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second) } } if err != nil { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/eosclient/eosgrpc/eoshttp.go b/vendor/github.com/cs3org/reva/v2/pkg/eosclient/eosgrpc/eoshttp.go index 591f4ae0029..bf7872a141d 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/eosclient/eosgrpc/eoshttp.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/eosclient/eosgrpc/eoshttp.go @@ -379,6 +379,9 @@ func (c *EOSHTTPClient) PUTFile(ctx context.Context, remoteuser string, auth eos // Execute the request. I don't like that there is no explicit timeout or buffer control on the input stream log.Debug().Str("func", "PUTFile").Msg("sending req") resp, err := c.cl.Do(req) + if resp != nil { + resp.Body.Close() + } // Let's support redirections... and if we retry we retry at the same FST if resp != nil && resp.StatusCode == 307 { @@ -471,6 +474,9 @@ func (c *EOSHTTPClient) Head(ctx context.Context, remoteuser string, auth eoscli } // Execute the request. I don't like that there is no explicit timeout or buffer control on the input stream resp, err := c.cl.Do(req) + if resp != nil { + resp.Body.Close() + } // And get an error code (if error) that is worth propagating e := c.getRespError(resp, err) diff --git a/vendor/github.com/cs3org/reva/v2/pkg/micro/ocdav/option.go b/vendor/github.com/cs3org/reva/v2/pkg/micro/ocdav/option.go index 762cbb8e911..5ff8644ac9b 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/micro/ocdav/option.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/micro/ocdav/option.go @@ -24,6 +24,7 @@ import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/storage/favorite" "github.com/rs/zerolog" "go-micro.dev/v4/broker" @@ -45,7 +46,7 @@ type Options struct { JWTSecret string FavoriteManager favorite.Manager - GatewayClient gateway.GatewayAPIClient + GatewaySelector pool.Selectable[gateway.GatewayAPIClient] TracingEnabled bool TracingInsecure bool @@ -196,10 +197,10 @@ func FavoriteManager(val favorite.Manager) Option { } } -// GatewayClient provides a function to set the GatewayClient option. -func GatewayClient(val gateway.GatewayAPIClient) Option { +// GatewaySelector provides a function to set the GatewaySelector option. +func GatewaySelector(val pool.Selectable[gateway.GatewayAPIClient]) Option { return func(o *Options) { - o.GatewayClient = val + o.GatewaySelector = val } } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/micro/ocdav/service.go b/vendor/github.com/cs3org/reva/v2/pkg/micro/ocdav/service.go index 5e384e3a6fc..a54f5280a6e 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/micro/ocdav/service.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/micro/ocdav/service.go @@ -35,6 +35,7 @@ import ( "github.com/go-chi/chi/v5/middleware" httpServer "github.com/go-micro/plugins/v4/server/http" "github.com/owncloud/ocis/v2/ocis-pkg/registry" + "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "go-micro.dev/v4" @@ -68,7 +69,7 @@ func Service(opts ...Option) (micro.Service, error) { server.Version(sopts.config.VersionString), ) - revaService, err := ocdav.NewWith(&sopts.config, sopts.FavoriteManager, sopts.lockSystem, &sopts.Logger, sopts.GatewayClient) + revaService, err := ocdav.NewWith(&sopts.config, sopts.FavoriteManager, sopts.lockSystem, &sopts.Logger, sopts.GatewaySelector) if err != nil { return nil, err } @@ -137,11 +138,11 @@ func setDefaults(sopts *Options) error { sopts.Name = ServerName } if sopts.lockSystem == nil { - client, err := pool.GetGatewayServiceClient(sopts.config.GatewaySvc) + selector, err := pool.GatewaySelector(sopts.config.GatewaySvc) if err != nil { - return err + return errors.Wrap(err, "error getting gateway selector") } - sopts.lockSystem = ocdav.NewCS3LS(client) + sopts.lockSystem = ocdav.NewCS3LS(selector) } if sopts.FavoriteManager == nil { sopts.FavoriteManager, _ = memory.New(map[string]interface{}{}) diff --git a/vendor/github.com/cs3org/reva/v2/pkg/ocm/share/manager/nextcloud/nextcloud.go b/vendor/github.com/cs3org/reva/v2/pkg/ocm/share/manager/nextcloud/nextcloud.go index d186b2e0223..449387c0e17 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/ocm/share/manager/nextcloud/nextcloud.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/ocm/share/manager/nextcloud/nextcloud.go @@ -58,7 +58,6 @@ func randSeq(n int) string { } func init() { - rand.Seed(time.Now().UnixNano()) registry.Register("nextcloud", New) } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/registry/config.go b/vendor/github.com/cs3org/reva/v2/pkg/registry/config.go deleted file mode 100644 index f798b65b9c9..00000000000 --- a/vendor/github.com/cs3org/reva/v2/pkg/registry/config.go +++ /dev/null @@ -1,53 +0,0 @@ -// 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 registry - -import ( - "github.com/mitchellh/mapstructure" -) - -// Config configures a registry -type Config struct { - Services map[string]map[string]*service `mapstructure:"services"` -} - -// service implements the Service interface. Attributes are exported so that mapstructure can unmarshal values onto them. -type service struct { - Name string `mapstructure:"name"` - Nodes []node `mapstructure:"nodes"` -} - -type node struct { - Address string `mapstructure:"address"` - Metadata map[string]string `mapstructure:"metadata"` -} - -// ParseConfig translates Config file values into a Config struct for consumers. -func ParseConfig(m map[string]interface{}) (*Config, error) { - c := &Config{} - if err := mapstructure.Decode(m, c); err != nil { - return nil, err - } - - if len(c.Services) == 0 { - c.Services = make(map[string]map[string]*service) - } - - return c, nil -} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/registry/memory/memory.go b/vendor/github.com/cs3org/reva/v2/pkg/registry/memory/memory.go deleted file mode 100644 index aee78c9ad57..00000000000 --- a/vendor/github.com/cs3org/reva/v2/pkg/registry/memory/memory.go +++ /dev/null @@ -1,81 +0,0 @@ -// 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 memory - -import ( - "fmt" - "sync" - - "github.com/cs3org/reva/v2/pkg/registry" -) - -// Registry implements the Registry interface. -type Registry struct { - // m protects async access to the services map. - sync.Mutex - // services map a service name with a set of nodes. - services map[string]registry.Service -} - -// Add implements the Registry interface. If the service is already known in this registry it will only update the nodes. -func (r *Registry) Add(svc registry.Service) error { - r.Lock() - defer r.Unlock() - - // append the nodes if the service is already registered. - if _, ok := r.services[svc.Name()]; ok { - s := service{ - name: svc.Name(), - nodes: make([]node, 0), - } - - s.mergeNodes(svc.Nodes(), r.services[svc.Name()].Nodes()) - - r.services[svc.Name()] = s - return nil - } - - r.services[svc.Name()] = svc - return nil -} - -// GetService implements the Registry interface. There is currently no load balance being done, but it should not be -// hard to add. -func (r *Registry) GetService(name string) (registry.Service, error) { - r.Lock() - defer r.Unlock() - - if service, ok := r.services[name]; ok { - return service, nil - } - - return nil, fmt.Errorf("service %v not found", name) -} - -// New returns an implementation of the Registry interface. -func New(m map[string]interface{}) registry.Registry { - // c, err := registry.ParseConfig(m) - // if err != nil { - // return nil - // } - - return &Registry{ - services: map[string]registry.Service{}, - } -} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/registry/memory/node.go b/vendor/github.com/cs3org/reva/v2/pkg/registry/memory/node.go deleted file mode 100644 index 22042306dd3..00000000000 --- a/vendor/github.com/cs3org/reva/v2/pkg/registry/memory/node.go +++ /dev/null @@ -1,44 +0,0 @@ -// 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 memory - -import "fmt" - -// node implements the registry.Node interface. -type node struct { - id string - address string - metadata map[string]string -} - -func (n node) Address() string { - return n.address -} - -func (n node) Metadata() map[string]string { - return n.metadata -} - -func (n node) String() string { - return fmt.Sprintf("%v-%v", n.id, n.address) -} - -func (n node) ID() string { - return n.id -} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/registry/memory/service.go b/vendor/github.com/cs3org/reva/v2/pkg/registry/memory/service.go deleted file mode 100644 index d34e92cc378..00000000000 --- a/vendor/github.com/cs3org/reva/v2/pkg/registry/memory/service.go +++ /dev/null @@ -1,70 +0,0 @@ -// 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 memory - -import "github.com/cs3org/reva/v2/pkg/registry" - -// NewService creates a new memory registry.Service. -func NewService(name string, nodes []interface{}) registry.Service { - n := make([]node, 0) - for i := 0; i < len(nodes); i++ { - n = append(n, node{ - // explicit type conversions because types are not exported to prevent from circular dependencies until released. - id: nodes[i].(map[string]interface{})["id"].(string), - address: nodes[i].(map[string]interface{})["address"].(string), - //metadata: nodes[i].(map[string]interface{})["metadata"].(map[string]string), - }) - } - - return service{ - name: name, - nodes: n, - } -} - -// service implements the Service interface -type service struct { - name string - nodes []node -} - -// Name implements the service interface. -func (s service) Name() string { - return s.name -} - -// Nodes implements the service interface. -func (s service) Nodes() []registry.Node { - ret := make([]registry.Node, 0) - for i := range s.nodes { - ret = append(ret, s.nodes[i]) - } - return ret -} - -func (s *service) mergeNodes(n1, n2 []registry.Node) { - n1 = append(n1, n2...) - for _, n := range n1 { - s.nodes = append(s.nodes, node{ - id: n.ID(), - address: n.Address(), - metadata: n.Metadata(), - }) - } -} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/registry/registry.go b/vendor/github.com/cs3org/reva/v2/pkg/registry/registry.go index 8b705a0937d..1595726f094 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/registry/registry.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/registry/registry.go @@ -1,4 +1,4 @@ -// Copyright 2018-2021 CERN +// Copyright 2018-2023 CERN // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,32 +18,38 @@ package registry -// Registry provides with means for dynamically registering services. -type Registry interface { - // Add registers a Service on the memoryRegistry. Repeated names is allowed, services are distinguished by their metadata. - Add(Service) error +import ( + mRegistry "go-micro.dev/v4/registry" + "go-micro.dev/v4/selector" +) - // GetService retrieves a Service and all of its nodes by Service name. It returns []*Service because we can have - // multiple versions of the same Service running alongside each others. - GetService(string) (Service, error) -} +var ( + // fixme: get rid of global registry + gRegistry mRegistry.Registry +) + +// Init prepares the service registry +func Init(nRegistry mRegistry.Registry) error { + // first come first serves, the first service defines the registry type. + if gRegistry == nil && nRegistry != nil { + gRegistry = nRegistry + } -// Service defines a service. -type Service interface { - Name() string - Nodes() []Node + return nil } -// Node defines nodes on a service. -type Node interface { - // Address where the given node is running. - Address() string +// GetRegistry exposes the registry +func GetRegistry() mRegistry.Registry { + return gRegistry +} - // metadata is used in order to differentiate services implementations. For instance an AuthProvider Service could - // have multiple implementations, basic, bearer ..., metadata would be used to select the Service type depending on - // its implementation. - Metadata() map[string]string +// GetNodeAddress returns a random address from the service nodes +func GetNodeAddress(services []*mRegistry.Service) (string, error) { + next := selector.Random(services) + node, err := next() + if err != nil { + return "", err + } - // ID returns the node ID. - ID() string + return node.Address, nil } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/client.go b/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/client.go new file mode 100644 index 00000000000..dc22dd417fa --- /dev/null +++ b/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/client.go @@ -0,0 +1,155 @@ +// 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" + 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() +} + +// 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() +} + +// 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() +} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/connection.go b/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/connection.go new file mode 100644 index 00000000000..253ee7f8f7a --- /dev/null +++ b/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/connection.go @@ -0,0 +1,102 @@ +// 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" + + rtrace "github.com/cs3org/reva/v2/pkg/trace" + "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 ( + maxCallRecvMsgSize = 10240000 +) + +// NewConn creates a new connection to a grpc server +// with open census tracing support. +// TODO(labkode): make grpc tls configurable. +// TODO make maxCallRecvMsgSize configurable, raised from the default 4MB to be able to list 10k files +func NewConn(address 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{}) + } + } + + conn, err := grpc.Dial( + address, + grpc.WithTransportCredentials(cred), + grpc.WithDefaultCallOptions( + grpc.MaxCallRecvMsgSize(maxCallRecvMsgSize), + ), + grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor( + otelgrpc.WithTracerProvider( + options.tracerProvider, + ), + otelgrpc.WithPropagators( + rtrace.Propagator, + ), + )), + grpc.WithUnaryInterceptor( + otelgrpc.UnaryClientInterceptor( + otelgrpc.WithTracerProvider( + options.tracerProvider, + ), + otelgrpc.WithPropagators( + rtrace.Propagator, + ), + ), + ), + ) + if err != nil { + return nil, err + } + + return conn, nil +} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/option.go b/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/option.go new file mode 100644 index 00000000000..2dc598b7e06 --- /dev/null +++ b/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/option.go @@ -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/cs3org/reva/v2/pkg/sharedconf" + rtrace "github.com/cs3org/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 + } +} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/pool.go b/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/pool.go index cd506bf9d66..087ae5c632a 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/pool.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/pool.go @@ -19,50 +19,9 @@ package pool import ( - "crypto/tls" "fmt" - "sync" - - 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" - 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" - "github.com/cs3org/reva/v2/pkg/sharedconf" - rtrace "github.com/cs3org/reva/v2/pkg/trace" - "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/credentials/insecure" ) -type provider struct { - m sync.Mutex - conn map[string]interface{} -} - -func newProvider() provider { - return provider{ - sync.Mutex{}, - make(map[string]interface{}), - } -} - // TLSMode represents TLS mode for the clients type TLSMode int @@ -76,41 +35,6 @@ const ( TLSInsecure ) -// ClientOptions represent additional options (e.g. tls settings) for the grpc clients -type ClientOptions struct { - tlsMode TLSMode - caCert string - tracerProvider trace.TracerProvider -} - -// Option is used to pass client options -type Option func(opts *ClientOptions) - -// TODO(labkode): is concurrent access to the maps safe? -// var storageProviders = map[string]storageprovider.ProviderAPIClient{} -var ( - storageProviders = newProvider() - authProviders = newProvider() - appAuthProviders = newProvider() - authRegistries = newProvider() - userShareProviders = newProvider() - ocmShareProviders = newProvider() - ocmInviteManagers = newProvider() - ocmProviderAuthorizers = newProvider() - ocmCores = newProvider() - publicShareProviders = newProvider() - preferencesProviders = newProvider() - permissionsProviders = newProvider() - appRegistries = newProvider() - appProviders = newProvider() - storageRegistries = newProvider() - gatewayProviders = newProvider() - userProviders = newProvider() - groupProviders = newProvider() - dataTxs = newProvider() - maxCallRecvMsgSize = 10240000 -) - // StringToTLSMode converts the supply string into the equivalent TLSMode constant func StringToTLSMode(m string) (TLSMode, error) { switch m { @@ -124,487 +48,3 @@ func StringToTLSMode(m string) (TLSMode, error) { return TLSOff, fmt.Errorf("unknown TLS mode: '%s'. Valid values are 'on', 'off' and 'insecure'", m) } } - -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 - } -} - -// NewConn creates a new connection to a grpc server -// with open census tracing support. -// TODO(labkode): make grpc tls configurable. -// TODO make maxCallRecvMsgSize configurable, raised from the default 4MB to be able to list 10k files -func NewConn(endpoint 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{}) - } - } - - conn, err := grpc.Dial( - endpoint, - grpc.WithTransportCredentials(cred), - grpc.WithDefaultCallOptions( - grpc.MaxCallRecvMsgSize(maxCallRecvMsgSize), - ), - grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor( - otelgrpc.WithTracerProvider( - options.tracerProvider, - ), - otelgrpc.WithPropagators( - rtrace.Propagator, - ), - )), - grpc.WithUnaryInterceptor( - otelgrpc.UnaryClientInterceptor( - otelgrpc.WithTracerProvider( - options.tracerProvider, - ), - otelgrpc.WithPropagators( - rtrace.Propagator, - ), - ), - ), - ) - if err != nil { - return nil, err - } - - return conn, nil -} - -// GetGatewayServiceClient returns a GatewayServiceClient. -func GetGatewayServiceClient(endpoint string, opts ...Option) (gateway.GatewayAPIClient, error) { - gatewayProviders.m.Lock() - defer gatewayProviders.m.Unlock() - - if val, ok := gatewayProviders.conn[endpoint]; ok { - return val.(gateway.GatewayAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := gateway.NewGatewayAPIClient(conn) - gatewayProviders.conn[endpoint] = v - - return v, nil -} - -// GetUserProviderServiceClient returns a UserProviderServiceClient. -func GetUserProviderServiceClient(endpoint string, opts ...Option) (user.UserAPIClient, error) { - userProviders.m.Lock() - defer userProviders.m.Unlock() - - if val, ok := userProviders.conn[endpoint]; ok { - return val.(user.UserAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := user.NewUserAPIClient(conn) - userProviders.conn[endpoint] = v - return v, nil -} - -// GetGroupProviderServiceClient returns a GroupProviderServiceClient. -func GetGroupProviderServiceClient(endpoint string, opts ...Option) (group.GroupAPIClient, error) { - groupProviders.m.Lock() - defer groupProviders.m.Unlock() - - if val, ok := groupProviders.conn[endpoint]; ok { - return val.(group.GroupAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := group.NewGroupAPIClient(conn) - groupProviders.conn[endpoint] = v - return v, nil -} - -// GetStorageProviderServiceClient returns a StorageProviderServiceClient. -func GetStorageProviderServiceClient(endpoint string, opts ...Option) (storageprovider.ProviderAPIClient, error) { - storageProviders.m.Lock() - defer storageProviders.m.Unlock() - - if c, ok := storageProviders.conn[endpoint]; ok { - return c.(storageprovider.ProviderAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := storageprovider.NewProviderAPIClient(conn) - storageProviders.conn[endpoint] = v - return v, nil -} - -// GetAuthRegistryServiceClient returns a new AuthRegistryServiceClient. -func GetAuthRegistryServiceClient(endpoint string, opts ...Option) (authregistry.RegistryAPIClient, error) { - authRegistries.m.Lock() - defer authRegistries.m.Unlock() - - // if there is already a connection to this node, use it. - if c, ok := authRegistries.conn[endpoint]; ok { - return c.(authregistry.RegistryAPIClient), nil - } - - // if not, create a new connection - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - // and memoize it - v := authregistry.NewRegistryAPIClient(conn) - authRegistries.conn[endpoint] = v - return v, nil -} - -// GetAuthProviderServiceClient returns a new AuthProviderServiceClient. -func GetAuthProviderServiceClient(endpoint string, opts ...Option) (authprovider.ProviderAPIClient, error) { - authProviders.m.Lock() - defer authProviders.m.Unlock() - - if c, ok := authProviders.conn[endpoint]; ok { - return c.(authprovider.ProviderAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := authprovider.NewProviderAPIClient(conn) - authProviders.conn[endpoint] = v - return v, nil -} - -// GetAppAuthProviderServiceClient returns a new AppAuthProviderServiceClient. -func GetAppAuthProviderServiceClient(endpoint string, opts ...Option) (applicationauth.ApplicationsAPIClient, error) { - appAuthProviders.m.Lock() - defer appAuthProviders.m.Unlock() - - if c, ok := appAuthProviders.conn[endpoint]; ok { - return c.(applicationauth.ApplicationsAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := applicationauth.NewApplicationsAPIClient(conn) - appAuthProviders.conn[endpoint] = v - return v, nil -} - -// GetUserShareProviderClient returns a new UserShareProviderClient. -func GetUserShareProviderClient(endpoint string, opts ...Option) (collaboration.CollaborationAPIClient, error) { - userShareProviders.m.Lock() - defer userShareProviders.m.Unlock() - - if c, ok := userShareProviders.conn[endpoint]; ok { - return c.(collaboration.CollaborationAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := collaboration.NewCollaborationAPIClient(conn) - userShareProviders.conn[endpoint] = v - return v, nil -} - -// GetOCMShareProviderClient returns a new OCMShareProviderClient. -func GetOCMShareProviderClient(endpoint string, opts ...Option) (ocm.OcmAPIClient, error) { - ocmShareProviders.m.Lock() - defer ocmShareProviders.m.Unlock() - - if c, ok := ocmShareProviders.conn[endpoint]; ok { - return c.(ocm.OcmAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := ocm.NewOcmAPIClient(conn) - ocmShareProviders.conn[endpoint] = v - return v, nil -} - -// GetOCMInviteManagerClient returns a new OCMInviteManagerClient. -func GetOCMInviteManagerClient(endpoint string, opts ...Option) (invitepb.InviteAPIClient, error) { - ocmInviteManagers.m.Lock() - defer ocmInviteManagers.m.Unlock() - - if c, ok := ocmInviteManagers.conn[endpoint]; ok { - return c.(invitepb.InviteAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := invitepb.NewInviteAPIClient(conn) - ocmInviteManagers.conn[endpoint] = v - return v, nil -} - -// GetPublicShareProviderClient returns a new PublicShareProviderClient. -func GetPublicShareProviderClient(endpoint string, opts ...Option) (link.LinkAPIClient, error) { - publicShareProviders.m.Lock() - defer publicShareProviders.m.Unlock() - - if c, ok := publicShareProviders.conn[endpoint]; ok { - return c.(link.LinkAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := link.NewLinkAPIClient(conn) - publicShareProviders.conn[endpoint] = v - return v, nil -} - -// GetPreferencesClient returns a new PreferencesClient. -func GetPreferencesClient(endpoint string, opts ...Option) (preferences.PreferencesAPIClient, error) { - preferencesProviders.m.Lock() - defer preferencesProviders.m.Unlock() - - if c, ok := preferencesProviders.conn[endpoint]; ok { - return c.(preferences.PreferencesAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := preferences.NewPreferencesAPIClient(conn) - preferencesProviders.conn[endpoint] = v - return v, nil -} - -// GetPermissionsClient returns a new PermissionsClient. -func GetPermissionsClient(endpoint string, opts ...Option) (permissions.PermissionsAPIClient, error) { - permissionsProviders.m.Lock() - defer permissionsProviders.m.Unlock() - - if c, ok := permissionsProviders.conn[endpoint]; ok { - return c.(permissions.PermissionsAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := permissions.NewPermissionsAPIClient(conn) - permissionsProviders.conn[endpoint] = v - return v, nil -} - -// GetAppRegistryClient returns a new AppRegistryClient. -func GetAppRegistryClient(endpoint string, opts ...Option) (appregistry.RegistryAPIClient, error) { - appRegistries.m.Lock() - defer appRegistries.m.Unlock() - - if c, ok := appRegistries.conn[endpoint]; ok { - return c.(appregistry.RegistryAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := appregistry.NewRegistryAPIClient(conn) - appRegistries.conn[endpoint] = v - return v, nil -} - -// GetAppProviderClient returns a new AppRegistryClient. -func GetAppProviderClient(endpoint string, opts ...Option) (appprovider.ProviderAPIClient, error) { - appProviders.m.Lock() - defer appProviders.m.Unlock() - - if c, ok := appProviders.conn[endpoint]; ok { - return c.(appprovider.ProviderAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := appprovider.NewProviderAPIClient(conn) - appProviders.conn[endpoint] = v - return v, nil -} - -// GetStorageRegistryClient returns a new StorageRegistryClient. -func GetStorageRegistryClient(endpoint string, opts ...Option) (storageregistry.RegistryAPIClient, error) { - storageRegistries.m.Lock() - defer storageRegistries.m.Unlock() - - if c, ok := storageRegistries.conn[endpoint]; ok { - return c.(storageregistry.RegistryAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := storageregistry.NewRegistryAPIClient(conn) - storageRegistries.conn[endpoint] = v - return v, nil -} - -// GetOCMProviderAuthorizerClient returns a new OCMProviderAuthorizerClient. -func GetOCMProviderAuthorizerClient(endpoint string, opts ...Option) (ocmprovider.ProviderAPIClient, error) { - ocmProviderAuthorizers.m.Lock() - defer ocmProviderAuthorizers.m.Unlock() - - if c, ok := ocmProviderAuthorizers.conn[endpoint]; ok { - return c.(ocmprovider.ProviderAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := ocmprovider.NewProviderAPIClient(conn) - ocmProviderAuthorizers.conn[endpoint] = v - return v, nil -} - -// GetOCMCoreClient returns a new OCMCoreClient. -func GetOCMCoreClient(endpoint string, opts ...Option) (ocmcore.OcmCoreAPIClient, error) { - ocmCores.m.Lock() - defer ocmCores.m.Unlock() - - if c, ok := ocmCores.conn[endpoint]; ok { - return c.(ocmcore.OcmCoreAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := ocmcore.NewOcmCoreAPIClient(conn) - ocmCores.conn[endpoint] = v - return v, nil -} - -// GetDataTxClient returns a new DataTxClient. -func GetDataTxClient(endpoint string, opts ...Option) (datatx.TxAPIClient, error) { - dataTxs.m.Lock() - defer dataTxs.m.Unlock() - - if c, ok := dataTxs.conn[endpoint]; ok { - return c.(datatx.TxAPIClient), nil - } - - conn, err := NewConn(endpoint, opts...) - if err != nil { - return nil, err - } - - v := datatx.NewTxAPIClient(conn) - dataTxs.conn[endpoint] = v - return v, nil -} - -// getEndpointByName resolve service names to ip addresses present on the registry. -// func getEndpointByName(name string) (string, error) { -// if services, err := utils.GlobalRegistry.GetService(name); err == nil { -// if len(services) > 0 { -// for i := range services { -// for j := range services[i].Nodes() { -// // return the first one. This MUST be improved upon with selectors. -// return services[i].Nodes()[j].Address(), nil -// } -// } -// } -// } -// -// return "", fmt.Errorf("could not get service by name: %v", name) -// } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/selector.go b/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/selector.go new file mode 100644 index 00000000000..57f9c114e9f --- /dev/null +++ b/vendor/github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool/selector.go @@ -0,0 +1,315 @@ +// 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" + "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" + 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/cs3org/reva/v2/pkg/registry" + "github.com/pkg/errors" + "google.golang.org/grpc" +) + +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.ClientConn) 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.ClientConn) 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) + } + + address := s.id + if options.registry != nil { + 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) + } + + address = nodeAddress + } + + existingClient, ok := s.clientMap.Load(address) + if ok { + return existingClient.(T), nil + } + + conn, err := NewConn(address, allOpts...) + if err != nil { + return *new(T), errors.Wrap(err, fmt.Sprintf("could not create connection for %s to %s", s.id, address)) + } + + newClient := s.clientFactory(conn) + s.clientMap.Store(address, 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 +} + +// 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 +} + +// 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 +} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go index 2d301ba830c..6943605f089 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go @@ -33,6 +33,7 @@ import ( "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "github.com/rs/zerolog/log" + "golang.org/x/sync/errgroup" "google.golang.org/genproto/protobuf/field_mask" gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" @@ -109,12 +110,16 @@ import ( - if the mtime changed we download the file to update the local cache */ +// name is the Tracer name used to identify this instrumentation library. +const tracerName = "jsoncs3" + func init() { registry.Register("jsoncs3", NewDefault) } type config struct { GatewayAddr string `mapstructure:"gateway_addr"` + MaxConcurrency int `mapstructure:"max_concurrency"` ProviderAddr string `mapstructure:"provider_addr"` ServiceUserID string `mapstructure:"service_user_id"` ServiceUserIdp string `mapstructure:"service_user_idp"` @@ -145,6 +150,8 @@ type Manager struct { initialized bool + MaxConcurrency int + gateway gatewayv1beta1.GatewayAPIClient eventStream events.Stream } @@ -205,11 +212,11 @@ func NewDefault(m map[string]interface{}) (share.Manager, error) { } } - return New(s, gc, c.CacheTTL, es) + return New(s, gc, c.CacheTTL, es, c.MaxConcurrency) } // New returns a new manager instance. -func New(s metadata.Storage, gc gatewayv1beta1.GatewayAPIClient, ttlSeconds int, es events.Stream) (*Manager, error) { +func New(s metadata.Storage, gc gatewayv1beta1.GatewayAPIClient, ttlSeconds int, es events.Stream, maxconcurrency int) (*Manager, error) { ttl := time.Duration(ttlSeconds) * time.Second return &Manager{ Cache: providercache.New(s, ttl), @@ -219,6 +226,7 @@ func New(s metadata.Storage, gc gatewayv1beta1.GatewayAPIClient, ttlSeconds int, storage: s, gateway: gc, eventStream: es, + MaxConcurrency: maxconcurrency, }, nil } @@ -259,6 +267,8 @@ func (m *Manager) initialize() error { // Share creates a new share func (m *Manager) Share(ctx context.Context, md *provider.ResourceInfo, g *collaboration.ShareGrant) (*collaboration.Share, error) { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Share") + defer span.End() if err := m.initialize(); err != nil { return nil, err } @@ -413,6 +423,8 @@ func (m *Manager) get(ctx context.Context, ref *collaboration.ShareReference) (s // GetShare gets the information for a share by the given ref. func (m *Manager) GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error) { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetShare") + defer span.End() if err := m.initialize(); err != nil { return nil, err } @@ -463,6 +475,9 @@ func (m *Manager) GetShare(ctx context.Context, ref *collaboration.ShareReferenc // Unshare deletes a share func (m *Manager) Unshare(ctx context.Context, ref *collaboration.ShareReference) error { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Unshare") + defer span.End() + if err := m.initialize(); err != nil { return err } @@ -486,6 +501,9 @@ func (m *Manager) Unshare(ctx context.Context, ref *collaboration.ShareReference // UpdateShare updates the mode of the given share. func (m *Manager) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions, updated *collaboration.Share, fieldMask *field_mask.FieldMask) (*collaboration.Share, error) { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "UpdateShare") + defer span.End() + if err := m.initialize(); err != nil { return nil, err } @@ -565,6 +583,9 @@ func (m *Manager) UpdateShare(ctx context.Context, ref *collaboration.ShareRefer // ListShares returns the shares created by the user func (m *Manager) ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error) { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "ListShares") + defer span.End() + if err := m.initialize(); err != nil { return nil, err } @@ -582,6 +603,9 @@ func (m *Manager) ListShares(ctx context.Context, filters []*collaboration.Filte } func (m *Manager) listSharesByIDs(ctx context.Context, user *userv1beta1.User, filters []*collaboration.Filter) ([]*collaboration.Share, error) { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "listSharesByIDs") + defer span.End() + providerSpaces := make(map[string]map[string]struct{}) for _, f := range share.FilterFiltersByType(filters, collaboration.Filter_TYPE_RESOURCE_ID) { storageID := f.GetResourceId().GetStorageId() @@ -649,6 +673,9 @@ func (m *Manager) listSharesByIDs(ctx context.Context, user *userv1beta1.User, f } func (m *Manager) listCreatedShares(ctx context.Context, user *userv1beta1.User, filters []*collaboration.Filter) ([]*collaboration.Share, error) { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "listCreatedShares") + defer span.End() + var ss []*collaboration.Share if err := m.CreatedCache.Sync(ctx, user.Id.OpaqueId); err != nil { @@ -696,6 +723,9 @@ func (m *Manager) listCreatedShares(ctx context.Context, user *userv1beta1.User, // ListReceivedShares returns the list of shares the user has access to. func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.ReceivedShare, error) { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "ListReceivedShares") + defer span.End() + if err := m.initialize(); err != nil { return nil, err } @@ -703,7 +733,6 @@ func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaborati m.Lock() defer m.Unlock() - var rss []*collaboration.ReceivedShare user := ctxpkg.ContextMustGetUser(ctx) ssids := map[string]*receivedsharecache.Space{} @@ -750,46 +779,98 @@ func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaborati } } - for ssid, rspace := range ssids { - storageID, spaceID, _ := shareid.Decode(ssid) - err := m.Cache.Sync(ctx, storageID, spaceID) - if err != nil { - continue - } - for shareID, state := range rspace.States { - s := m.Cache.Get(storageID, spaceID, shareID) - if s == nil { - continue + numWorkers := m.MaxConcurrency + if numWorkers == 0 || len(ssids) < numWorkers { + numWorkers = len(ssids) + } + + type w struct { + ssid string + rspace *receivedsharecache.Space + } + work := make(chan w) + results := make(chan *collaboration.ReceivedShare) + + g, ctx := errgroup.WithContext(ctx) + + // Distribute work + g.Go(func() error { + defer close(work) + for ssid, rspace := range ssids { + select { + case work <- w{ssid, rspace}: + case <-ctx.Done(): + return ctx.Err() } - if share.IsExpired(s) { - if err := m.removeShare(ctx, s); err != nil { - log.Error().Err(err). - Msg("failed to unshare expired share") - } - if err := events.Publish(m.eventStream, events.ShareExpired{ - ShareOwner: s.GetOwner(), - ItemID: s.GetResourceId(), - ExpiredAt: time.Unix(int64(s.GetExpiration().GetSeconds()), int64(s.GetExpiration().GetNanos())), - GranteeUserID: s.GetGrantee().GetUserId(), - GranteeGroupID: s.GetGrantee().GetGroupId(), - }); err != nil { - log.Error().Err(err). - Msg("failed to publish share expired event") + } + return nil + }) + + // Spawn workers that'll concurrently work the queue + for i := 0; i < numWorkers; i++ { + g.Go(func() error { + for w := range work { + storageID, spaceID, _ := shareid.Decode(w.ssid) + err := m.Cache.Sync(ctx, storageID, spaceID) + if err != nil { + continue } - continue - } + for shareID, state := range w.rspace.States { + s := m.Cache.Get(storageID, spaceID, shareID) + if s == nil { + continue + } + if share.IsExpired(s) { + if err := m.removeShare(ctx, s); err != nil { + log.Error().Err(err). + Msg("failed to unshare expired share") + } + if err := events.Publish(m.eventStream, events.ShareExpired{ + ShareOwner: s.GetOwner(), + ItemID: s.GetResourceId(), + ExpiredAt: time.Unix(int64(s.GetExpiration().GetSeconds()), int64(s.GetExpiration().GetNanos())), + GranteeUserID: s.GetGrantee().GetUserId(), + GranteeGroupID: s.GetGrantee().GetGroupId(), + }); err != nil { + log.Error().Err(err). + Msg("failed to publish share expired event") + } + continue + } - if share.IsGrantedToUser(s, user) { - if share.MatchesFiltersWithState(s, state.State, filters) { - rs := &collaboration.ReceivedShare{ - Share: s, - State: state.State, - MountPoint: state.MountPoint, + if share.IsGrantedToUser(s, user) { + if share.MatchesFiltersWithState(s, state.State, filters) { + rs := &collaboration.ReceivedShare{ + Share: s, + State: state.State, + MountPoint: state.MountPoint, + } + select { + case results <- rs: + case <-ctx.Done(): + return ctx.Err() + } + } } - rss = append(rss, rs) } } - } + return nil + }) + } + + // Wait for things to settle down, then close results chan + go func() { + _ = g.Wait() // error is checked later + close(results) + }() + + rss := []*collaboration.ReceivedShare{} + for n := range results { + rss = append(rss, n) + } + + if err := g.Wait(); err != nil { + return nil, err } return rss, nil @@ -797,6 +878,9 @@ func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaborati // convert must be called in a lock-controlled block. func (m *Manager) convert(ctx context.Context, userID string, s *collaboration.Share) *collaboration.ReceivedShare { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "convert") + defer span.End() + rs := &collaboration.ReceivedShare{ Share: s, State: collaboration.ShareState_SHARE_STATE_PENDING, @@ -823,6 +907,9 @@ func (m *Manager) GetReceivedShare(ctx context.Context, ref *collaboration.Share } func (m *Manager) getReceived(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "getReceived") + defer span.End() + m.Lock() defer m.Unlock() s, err := m.get(ctx, ref) @@ -854,6 +941,9 @@ func (m *Manager) getReceived(ctx context.Context, ref *collaboration.ShareRefer // UpdateReceivedShare updates the received share with share state. func (m *Manager) UpdateReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask) (*collaboration.ReceivedShare, error) { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "UpdateReceivedShare") + defer span.End() + if err := m.initialize(); err != nil { return nil, err } @@ -964,6 +1054,9 @@ func (m *Manager) Load(ctx context.Context, shareChan <-chan *collaboration.Shar } func (m *Manager) removeShare(ctx context.Context, s *collaboration.Share) error { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "removeShare") + defer span.End() + storageID, spaceID, _ := shareid.Decode(s.Id.OpaqueId) err := m.Cache.Remove(ctx, storageID, spaceID, s.Id.OpaqueId) if _, ok := err.(errtypes.IsPreconditionFailed); ok { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/providercache/providercache.go b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/providercache/providercache.go index 528922ac491..2fc4ed18262 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/providercache/providercache.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/providercache/providercache.go @@ -33,8 +33,13 @@ import ( "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/storage/utils/metadata" "github.com/cs3org/reva/v2/pkg/utils" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" ) +// name is the Tracer name used to identify this instrumentation library. +const tracerName = "providercache" + // Cache holds share information structured by provider and space type Cache struct { Providers map[string]*Spaces @@ -106,6 +111,10 @@ func New(s metadata.Storage, ttl time.Duration) Cache { // Add adds a share to the cache func (c *Cache) Add(ctx context.Context, storageID, spaceID, shareID string, share *collaboration.Share) error { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Add") + defer span.End() + span.SetAttributes(attribute.String("cs3.storageid", storageID), attribute.String("cs3.spaceid", spaceID), attribute.String("cs3.shareid", shareID)) + switch { case storageID == "": return fmt.Errorf("missing storage id") @@ -122,6 +131,10 @@ func (c *Cache) Add(ctx context.Context, storageID, spaceID, shareID string, sha // Remove removes a share from the cache func (c *Cache) Remove(ctx context.Context, storageID, spaceID, shareID string) error { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Remove") + defer span.End() + span.SetAttributes(attribute.String("cs3.storageid", storageID), attribute.String("cs3.spaceid", spaceID), attribute.String("cs3.shareid", shareID)) + if c.Providers[storageID] == nil || c.Providers[storageID].Spaces[spaceID] == nil { return nil @@ -150,6 +163,10 @@ func (c *Cache) ListSpace(storageID, spaceID string) *Shares { // PersistWithTime persists the data of one space if it has not been modified since the given mtime func (c *Cache) PersistWithTime(ctx context.Context, storageID, spaceID string, mtime time.Time) error { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "PersistWithTime") + defer span.End() + span.SetAttributes(attribute.String("cs3.storageid", storageID), attribute.String("cs3.spaceid", spaceID)) + if c.Providers[storageID] == nil || c.Providers[storageID].Spaces[spaceID] == nil { return nil } @@ -187,15 +204,20 @@ func (c *Cache) Persist(ctx context.Context, storageID, spaceID string) error { // Sync updates the in-memory data with the data from the storage if it is outdated func (c *Cache) Sync(ctx context.Context, storageID, spaceID string) error { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Sync") + defer span.End() + + span.SetAttributes(attribute.String("cs3.storageid", storageID), attribute.String("cs3.spaceid", spaceID)) + log := appctx.GetLogger(ctx).With().Str("storageID", storageID).Str("spaceID", spaceID).Logger() - log.Debug().Msg("Syncing provider cache...") var mtime time.Time if c.Providers[storageID] != nil && c.Providers[storageID].Spaces[spaceID] != nil { mtime = c.Providers[storageID].Spaces[spaceID].Mtime if time.Now().Before(c.Providers[storageID].Spaces[spaceID].nextSync) { - log.Debug().Msg("Skipping provider cache sync, it was just recently synced...") + span.AddEvent("skip sync") + span.SetStatus(codes.Ok, "") return nil } c.Providers[storageID].Spaces[spaceID].nextSync = time.Now().Add(c.ttl) @@ -207,28 +229,33 @@ func (c *Cache) Sync(ctx context.Context, storageID, spaceID string) error { info, err := c.storage.Stat(ctx, jsonPath) if err != nil { if _, ok := err.(errtypes.NotFound); ok { - log.Debug().Msg("no json file, nothing to sync") + span.AddEvent("no file") + span.SetStatus(codes.Ok, "") return nil // Nothing to sync against } if _, ok := err.(*os.PathError); ok { - log.Debug().Msg("no storage dir, nothing to sync") + span.AddEvent("no dir") + span.SetStatus(codes.Ok, "") return nil // Nothing to sync against } + span.SetStatus(codes.Error, fmt.Sprintf("Failed to stat the provider cache: %s", err.Error())) log.Error().Err(err).Msg("Failed to stat the provider cache") return err } // check mtime of /users/{userid}/created.json if utils.TSToTime(info.Mtime).After(mtime) { - log.Debug().Msg("Updating provider cache...") + span.AddEvent("updating cache") // - update cached list of created shares for the user in memory if changed createdBlob, err := c.storage.SimpleDownload(ctx, jsonPath) if err != nil { + span.SetStatus(codes.Error, fmt.Sprintf("Failed to download the provider cache: %s", err.Error())) log.Error().Err(err).Msg("Failed to download the provider cache") return err } newShares := &Shares{} err = json.Unmarshal(createdBlob, newShares) if err != nil { + span.SetStatus(codes.Error, fmt.Sprintf("Failed to unmarshal the provider cache: %s", err.Error())) log.Error().Err(err).Msg("Failed to unmarshal the provider cache") return err } @@ -236,7 +263,7 @@ func (c *Cache) Sync(ctx context.Context, storageID, spaceID string) error { c.initializeIfNeeded(storageID, spaceID) c.Providers[storageID].Spaces[spaceID] = newShares } - log.Debug().Msg("Provider cache is up to date") + span.SetStatus(codes.Ok, "") return nil } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/receivedsharecache/receivedsharecache.go b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/receivedsharecache/receivedsharecache.go index 0693b802ce2..c58e761d86f 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/receivedsharecache/receivedsharecache.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/receivedsharecache/receivedsharecache.go @@ -21,6 +21,7 @@ package receivedsharecache import ( "context" "encoding/json" + "fmt" "path" "path/filepath" "time" @@ -31,8 +32,13 @@ import ( "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/storage/utils/metadata" "github.com/cs3org/reva/v2/pkg/utils" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" ) +// name is the Tracer name used to identify this instrumentation library. +const tracerName = "receivedsharecache" + // Cache stores the list of received shares and their states // It functions as an in-memory cache with a persistence layer // The storage is sharded by user @@ -74,6 +80,10 @@ func New(s metadata.Storage, ttl time.Duration) Cache { // Add adds a new entry to the cache func (c *Cache) Add(ctx context.Context, userID, spaceID string, rs *collaboration.ReceivedShare) error { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Add") + defer span.End() + span.SetAttributes(attribute.String("cs3.userid", userID), attribute.String("cs3.spaceid", spaceID)) + if c.ReceivedSpaces[userID] == nil { c.ReceivedSpaces[userID] = &Spaces{ Spaces: map[string]*Space{}, @@ -106,13 +116,17 @@ func (c *Cache) Get(userID, spaceID, shareID string) *State { // Sync updates the in-memory data with the data from the storage if it is outdated func (c *Cache) Sync(ctx context.Context, userID string) error { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Sync") + defer span.End() + span.SetAttributes(attribute.String("cs3.userid", userID)) + log := appctx.GetLogger(ctx).With().Str("userID", userID).Logger() - log.Debug().Msg("Syncing received share cache...") var mtime time.Time if c.ReceivedSpaces[userID] != nil { if time.Now().Before(c.ReceivedSpaces[userID].nextSync) { - log.Debug().Msg("Skipping received share cache sync, it was just recently synced...") + span.AddEvent("skip sync") + span.SetStatus(codes.Ok, "") return nil } c.ReceivedSpaces[userID].nextSync = time.Now().Add(c.ttl) @@ -123,38 +137,47 @@ func (c *Cache) Sync(ctx context.Context, userID string) error { } jsonPath := userJSONPath(userID) - info, err := c.storage.Stat(ctx, jsonPath) + info, err := c.storage.Stat(ctx, jsonPath) // TODO we only need the mtime ... use fieldmask to make the request cheaper if err != nil { if _, ok := err.(errtypes.NotFound); ok { + span.AddEvent("no file") + span.SetStatus(codes.Ok, "") return nil // Nothing to sync against } + span.SetStatus(codes.Error, fmt.Sprintf("Failed to stat the received share: %s", err.Error())) log.Error().Err(err).Msg("Failed to stat the received share") return err } // check mtime of /users/{userid}/created.json if utils.TSToTime(info.Mtime).After(mtime) { - log.Debug().Msg("Updating received share cache...") + span.AddEvent("updating cache") // - update cached list of created shares for the user in memory if changed createdBlob, err := c.storage.SimpleDownload(ctx, jsonPath) if err != nil { + span.SetStatus(codes.Error, fmt.Sprintf("Failed to download the received share: %s", err.Error())) log.Error().Err(err).Msg("Failed to download the received share") return err } newSpaces := &Spaces{} err = json.Unmarshal(createdBlob, newSpaces) if err != nil { + span.SetStatus(codes.Error, fmt.Sprintf("Failed to unmarshal the received share: %s", err.Error())) log.Error().Err(err).Msg("Failed to unmarshal the received share") return err } newSpaces.Mtime = utils.TSToTime(info.Mtime) c.ReceivedSpaces[userID] = newSpaces } - log.Debug().Msg("Received share cache is up to date") + span.SetStatus(codes.Ok, "") return nil } // Persist persists the data for one user to the storage func (c *Cache) Persist(ctx context.Context, userID string) error { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Persist") + defer span.End() + span.SetAttributes(attribute.String("cs3.userid", userID)) + if c.ReceivedSpaces[userID] == nil { return nil } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/sharecache/sharecache.go b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/sharecache/sharecache.go index 7fdd7e392de..78ed5aacca0 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/sharecache/sharecache.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/sharecache/sharecache.go @@ -21,6 +21,7 @@ package sharecache import ( "context" "encoding/json" + "fmt" "path" "path/filepath" "time" @@ -30,8 +31,13 @@ import ( "github.com/cs3org/reva/v2/pkg/share/manager/jsoncs3/shareid" "github.com/cs3org/reva/v2/pkg/storage/utils/metadata" "github.com/cs3org/reva/v2/pkg/utils" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" ) +// name is the Tracer name used to identify this instrumentation library. +const tracerName = "sharecache" + // Cache caches the list of share ids for users/groups // It functions as an in-memory cache with a persistence layer // The storage is sharded by user/group @@ -71,6 +77,10 @@ func New(s metadata.Storage, namespace, filename string, ttl time.Duration) Cach // Add adds a share to the cache func (c *Cache) Add(ctx context.Context, userid, shareID string) error { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Add") + defer span.End() + span.SetAttributes(attribute.String("cs3.userid", userid), attribute.String("cs3.shareid", shareID)) + storageid, spaceid, _ := shareid.Decode(shareID) ssid := storageid + shareid.IDDelimiter + spaceid @@ -94,6 +104,10 @@ func (c *Cache) Add(ctx context.Context, userid, shareID string) error { // Remove removes a share for the given user func (c *Cache) Remove(ctx context.Context, userid, shareID string) error { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Remove") + defer span.End() + span.SetAttributes(attribute.String("cs3.userid", userid), attribute.String("cs3.shareid", shareID)) + storageid, spaceid, _ := shareid.Decode(shareID) ssid := storageid + shareid.IDDelimiter + spaceid @@ -133,14 +147,18 @@ func (c *Cache) List(userid string) map[string]SpaceShareIDs { // Sync updates the in-memory data with the data from the storage if it is outdated func (c *Cache) Sync(ctx context.Context, userID string) error { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Sync") + defer span.End() + span.SetAttributes(attribute.String("cs3.userid", userID)) + log := appctx.GetLogger(ctx).With().Str("userID", userID).Logger() - log.Debug().Msg("Syncing share cache...") var mtime time.Time // - do we have a cached list of created shares for the user in memory? if usc := c.UserShares[userID]; usc != nil { if time.Now().Before(c.UserShares[userID].nextSync) { - log.Debug().Msg("Skipping share cache sync, it was just recently synced...") + span.AddEvent("skip sync") + span.SetStatus(codes.Ok, "") return nil } c.UserShares[userID].nextSync = time.Now().Add(c.ttl) @@ -155,35 +173,44 @@ func (c *Cache) Sync(ctx context.Context, userID string) error { info, err := c.storage.Stat(ctx, userCreatedPath) if err != nil { if _, ok := err.(errtypes.NotFound); ok { + span.AddEvent("no file") + span.SetStatus(codes.Ok, "") return nil // Nothing to sync against } + span.SetStatus(codes.Error, fmt.Sprintf("Failed to stat the share cache: %s", err.Error())) log.Error().Err(err).Msg("Failed to stat the share cache") return err } // check mtime of /users/{userid}/created.json if utils.TSToTime(info.Mtime).After(mtime) { - log.Debug().Msg("Updating share cache...") + span.AddEvent("updating cache") // - update cached list of created shares for the user in memory if changed createdBlob, err := c.storage.SimpleDownload(ctx, userCreatedPath) if err != nil { + span.SetStatus(codes.Error, fmt.Sprintf("Failed to download the share cache: %s", err.Error())) log.Error().Err(err).Msg("Failed to download the share cache") return err } newShareCache := &UserShareCache{} err = json.Unmarshal(createdBlob, newShareCache) if err != nil { + span.SetStatus(codes.Error, fmt.Sprintf("Failed to unmarshal the share cache: %s", err.Error())) log.Error().Err(err).Msg("Failed to unmarshal the share cache") return err } newShareCache.Mtime = utils.TSToTime(info.Mtime) c.UserShares[userID] = newShareCache } - log.Debug().Msg("Share cache is up to date") + span.SetStatus(codes.Ok, "") return nil } // Persist persists the data for one user/group to the storage func (c *Cache) Persist(ctx context.Context, userid string) error { + ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Persist") + defer span.End() + span.SetAttributes(attribute.String("cs3.userid", userid)) + oldMtime := c.UserShares[userid].Mtime c.UserShares[userid].Mtime = time.Now() diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/cache/filemetadata.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/cache/filemetadata.go index 9f8828ccccf..41053b4f28c 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/cache/filemetadata.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/cache/filemetadata.go @@ -39,5 +39,5 @@ func NewFileMetadataCache(store string, nodes []string, database, table string, // RemoveMetadata removes a reference from the metadata cache func (c *fileMetadataCache) RemoveMetadata(path string) error { - return c.s.Delete(path) + return c.Delete(path) } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go index 80278f1835d..3065a168aad 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go @@ -127,12 +127,13 @@ func NewDefault(m map[string]interface{}, bs tree.Blobstore, es events.Stream) ( microstore.Database(o.IDCache.Database), microstore.Table(o.IDCache.Table), )) - permissionsClient, err := pool.GetPermissionsClient(o.PermissionsSVC, pool.WithTLSMode(o.PermTLSMode)) + + permissionsSelector, err := pool.PermissionsSelector(o.PermissionsSVC, pool.WithTLSMode(o.PermTLSMode)) if err != nil { return nil, err } - permissions := NewPermissions(node.NewPermissions(lu), permissionsClient) + permissions := NewPermissions(node.NewPermissions(lu), permissionsSelector) return New(o, lu, permissions, tp, es) } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/node.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/node.go index 25a8a8e4ab7..a2332444a57 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/node.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/node.go @@ -1039,7 +1039,6 @@ func (n *Node) ReadUserPermissions(ctx context.Context, u *userpb.User) (ap prov } AddPermissions(&ap, g.GetPermissions()) case metadata.IsAttrUnset(err): - err = nil appctx.GetLogger(ctx).Error().Interface("node", n).Str("grant", grantees[i]).Interface("grantees", grantees).Msg("grant vanished from node after listing") // continue with next segment default: diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spacepermissions.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spacepermissions.go index 31043239adb..010484a0667 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spacepermissions.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spacepermissions.go @@ -8,6 +8,7 @@ import ( v1beta11 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" "github.com/cs3org/reva/v2/pkg/utils" "google.golang.org/grpc" @@ -25,13 +26,13 @@ type CS3PermissionsClient interface { // Permissions manages permissions type Permissions struct { - item PermissionsChecker // handles item permissions - space CS3PermissionsClient // handlers space permissions + item PermissionsChecker // handles item permissions + permissionsSelector pool.Selectable[cs3permissions.PermissionsAPIClient] // handlers space permissions } // NewPermissions returns a new Permissions instance -func NewPermissions(item PermissionsChecker, space CS3PermissionsClient) Permissions { - return Permissions{item: item, space: space} +func NewPermissions(item PermissionsChecker, permissionsSelector pool.Selectable[cs3permissions.PermissionsAPIClient]) Permissions { + return Permissions{item: item, permissionsSelector: permissionsSelector} } // AssemblePermissions is used to assemble file permissions @@ -96,8 +97,13 @@ func (p Permissions) DeleteAllHomeSpaces(ctx context.Context) bool { // checkPermission is used to check a users space permissions func (p Permissions) checkPermission(ctx context.Context, perm string, ref *provider.Reference) bool { + permissionsClient, err := p.permissionsSelector.Next() + if err != nil { + return false + } + user := ctxpkg.ContextMustGetUser(ctx) - checkRes, err := p.space.CheckPermission(ctx, &cs3permissions.CheckPermissionRequest{ + checkRes, err := permissionsClient.CheckPermission(ctx, &cs3permissions.CheckPermissionRequest{ Permission: perm, SubjectRef: &cs3permissions.SubjectReference{ Spec: &cs3permissions.SubjectReference_UserId{ diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/downloader/downloader.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/downloader/downloader.go index a3795082eba..b38ad80b3a3 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/downloader/downloader.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/downloader/downloader.go @@ -29,6 +29,7 @@ import ( provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/internal/http/services/datagateway" "github.com/cs3org/reva/v2/pkg/errtypes" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/rhttp" ) @@ -39,15 +40,15 @@ type Downloader interface { } type revaDownloader struct { - gtw gateway.GatewayAPIClient - httpClient *http.Client + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + httpClient *http.Client } // NewDownloader creates a Downloader from the reva gateway -func NewDownloader(gtw gateway.GatewayAPIClient, options ...rhttp.Option) Downloader { +func NewDownloader(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], options ...rhttp.Option) Downloader { return &revaDownloader{ - gtw: gtw, - httpClient: rhttp.GetHTTPClient(options...), + gatewaySelector: gatewaySelector, + httpClient: rhttp.GetHTTPClient(options...), } } @@ -62,7 +63,11 @@ func getDownloadProtocol(protocols []*gateway.FileDownloadProtocol, prot string) // Download downloads a resource given the path to the dst Writer func (r *revaDownloader) Download(ctx context.Context, id *provider.ResourceId, dst io.Writer) error { - downResp, err := r.gtw.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{ + gatewayClient, err := r.gatewaySelector.Next() + if err != nil { + return err + } + downResp, err := gatewayClient.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{ Ref: &provider.Reference{ ResourceId: id, Path: ".", diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/metadata/cs3.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/metadata/cs3.go index 76cff96d122..a536f9284e3 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/metadata/cs3.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/metadata/cs3.go @@ -37,9 +37,17 @@ import ( "github.com/cs3org/reva/v2/pkg/rgrpc/status" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/utils" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" "google.golang.org/grpc/metadata" ) +var tracer trace.Tracer + +func init() { + tracer = otel.Tracer("github.com/cs3org/reva/pkg/storage/utils/metadata") +} + // CS3 represents a metadata storage with a cs3 storage backend type CS3 struct { providerAddr string @@ -75,6 +83,9 @@ func (cs3 *CS3) Backend() string { // Init creates the metadata space func (cs3 *CS3) Init(ctx context.Context, spaceid string) (err error) { + ctx, span := tracer.Start(ctx, "Init") + defer span.End() + client, err := cs3.providerClient() if err != nil { return err @@ -114,6 +125,9 @@ func (cs3 *CS3) Init(ctx context.Context, spaceid string) (err error) { // SimpleUpload uploads a file to the metadata storage func (cs3 *CS3) SimpleUpload(ctx context.Context, uploadpath string, content []byte) error { + ctx, span := tracer.Start(ctx, "SimpleUpload") + defer span.End() + return cs3.Upload(ctx, UploadRequest{ Path: uploadpath, Content: content, @@ -122,6 +136,9 @@ func (cs3 *CS3) SimpleUpload(ctx context.Context, uploadpath string, content []b // Upload uploads a file to the metadata storage func (cs3 *CS3) Upload(ctx context.Context, req UploadRequest) error { + ctx, span := tracer.Start(ctx, "Upload") + defer span.End() + client, err := cs3.providerClient() if err != nil { return err @@ -185,6 +202,9 @@ func (cs3 *CS3) Upload(ctx context.Context, req UploadRequest) error { // Stat returns the metadata for the given path func (cs3 *CS3) Stat(ctx context.Context, path string) (*provider.ResourceInfo, error) { + ctx, span := tracer.Start(ctx, "Stat") + defer span.End() + client, err := cs3.providerClient() if err != nil { return nil, err @@ -214,6 +234,9 @@ func (cs3 *CS3) Stat(ctx context.Context, path string) (*provider.ResourceInfo, // SimpleDownload reads a file from the metadata storage func (cs3 *CS3) SimpleDownload(ctx context.Context, downloadpath string) (content []byte, err error) { + ctx, span := tracer.Start(ctx, "SimpleDownload") + defer span.End() + client, err := cs3.providerClient() if err != nil { return nil, err @@ -277,6 +300,9 @@ func (cs3 *CS3) SimpleDownload(ctx context.Context, downloadpath string) (conten // Delete deletes a path func (cs3 *CS3) Delete(ctx context.Context, path string) error { + ctx, span := tracer.Start(ctx, "Delete") + defer span.End() + client, err := cs3.providerClient() if err != nil { return err @@ -304,6 +330,9 @@ func (cs3 *CS3) Delete(ctx context.Context, path string) error { // ReadDir returns the entries in a given directory func (cs3 *CS3) ReadDir(ctx context.Context, path string) ([]string, error) { + ctx, span := tracer.Start(ctx, "ReadDir") + defer span.End() + infos, err := cs3.ListDir(ctx, path) if err != nil { return nil, err @@ -318,6 +347,9 @@ func (cs3 *CS3) ReadDir(ctx context.Context, path string) ([]string, error) { // ListDir returns a list of ResourceInfos for the entries in a given directory func (cs3 *CS3) ListDir(ctx context.Context, path string) ([]*provider.ResourceInfo, error) { + ctx, span := tracer.Start(ctx, "ListDir") + defer span.End() + client, err := cs3.providerClient() if err != nil { return nil, err @@ -347,6 +379,9 @@ func (cs3 *CS3) ListDir(ctx context.Context, path string) ([]*provider.ResourceI // MakeDirIfNotExist will create a root node in the metadata storage. Requires an authenticated context. func (cs3 *CS3) MakeDirIfNotExist(ctx context.Context, folder string) error { + ctx, span := tracer.Start(ctx, "MakeDirIfNotExist") + defer span.End() + client, err := cs3.providerClient() if err != nil { return err @@ -395,6 +430,9 @@ func (cs3 *CS3) MakeDirIfNotExist(ctx context.Context, folder string) error { // CreateSymlink creates a symlink func (cs3 *CS3) CreateSymlink(ctx context.Context, oldname, newname string) error { + ctx, span := tracer.Start(ctx, "CreateSymlink") + defer span.End() + if _, err := cs3.ResolveSymlink(ctx, newname); err == nil { return os.ErrExist } @@ -404,6 +442,9 @@ func (cs3 *CS3) CreateSymlink(ctx context.Context, oldname, newname string) erro // ResolveSymlink resolves a symlink func (cs3 *CS3) ResolveSymlink(ctx context.Context, name string) (string, error) { + ctx, span := tracer.Start(ctx, "ResolveSymlink") + defer span.End() + b, err := cs3.SimpleDownload(ctx, name) if err != nil { if errors.Is(err, errtypes.NotFound("")) { @@ -420,12 +461,17 @@ func (cs3 *CS3) providerClient() (provider.ProviderAPIClient, error) { } func (cs3 *CS3) getAuthContext(ctx context.Context) (context.Context, error) { + // we need to start a new context to get rid of an existing x-access-token in the outgoing context + authCtx := context.Background() + authCtx, span := tracer.Start(authCtx, "getAuthContext", trace.WithLinks(trace.LinkFromContext(ctx))) + defer span.End() + client, err := pool.GetGatewayServiceClient(cs3.gatewayAddr) if err != nil { return nil, err } - authCtx := ctxpkg.ContextSetUser(context.Background(), cs3.serviceUser) + authCtx = ctxpkg.ContextSetUser(authCtx, cs3.serviceUser) authRes, err := client.Authenticate(authCtx, &gateway.AuthenticateRequest{ Type: "machine", ClientId: "userid:" + cs3.serviceUser.Id.OpaqueId, diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/walker/walker.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/walker/walker.go index ab3c4ff591e..acca4869e70 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/walker/walker.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/walker/walker.go @@ -27,6 +27,7 @@ import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" "github.com/cs3org/reva/v2/pkg/errtypes" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" ) // WalkFunc is the type of function called by Walk to visit each file or directory @@ -46,12 +47,12 @@ type Walker interface { } type revaWalker struct { - gtw gateway.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] } // NewWalker creates a Walker object that uses the reva gateway -func NewWalker(gtw gateway.GatewayAPIClient) Walker { - return &revaWalker{gtw: gtw} +func NewWalker(gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) Walker { + return &revaWalker{gatewaySelector: gatewaySelector} } // Walk walks the file tree rooted at root, calling fn for each file or folder in the tree, including the root. @@ -95,7 +96,11 @@ func (r *revaWalker) walkRecursively(ctx context.Context, wd string, info *provi } func (r *revaWalker) readDir(ctx context.Context, id *provider.ResourceId) ([]*provider.ResourceInfo, error) { - resp, err := r.gtw.ListContainer(ctx, &provider.ListContainerRequest{Ref: &provider.Reference{ResourceId: id, Path: "."}}) + gatewayClient, err := r.gatewaySelector.Next() + if err != nil { + return nil, err + } + resp, err := gatewayClient.ListContainer(ctx, &provider.ListContainerRequest{Ref: &provider.Reference{ResourceId: id, Path: "."}}) switch { case err != nil: @@ -108,7 +113,11 @@ func (r *revaWalker) readDir(ctx context.Context, id *provider.ResourceId) ([]*p } func (r *revaWalker) stat(ctx context.Context, id *provider.ResourceId) (*provider.ResourceInfo, error) { - resp, err := r.gtw.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: id, Path: "."}}) + gatewayClient, err := r.gatewaySelector.Next() + if err != nil { + return nil, err + } + resp, err := gatewayClient.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: id, Path: "."}}) switch { case err != nil: diff --git a/vendor/github.com/cs3org/reva/v2/pkg/utils/utils.go b/vendor/github.com/cs3org/reva/v2/pkg/utils/utils.go index 8a4fb54be37..c67c50dbf91 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/utils/utils.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/utils/utils.go @@ -39,8 +39,6 @@ import ( userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" - "github.com/cs3org/reva/v2/pkg/registry" - "github.com/cs3org/reva/v2/pkg/registry/memory" "github.com/golang/protobuf/proto" "google.golang.org/protobuf/encoding/protojson" ) @@ -49,9 +47,6 @@ var ( matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") matchEmail = regexp.MustCompile(`^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$`) - // GlobalRegistry configures a service registry globally accessible. It defaults to a memory registry. The usage of - // globals is not encouraged, and this is a workaround until the PR is out of a draft state. - GlobalRegistry registry.Registry = memory.New(map[string]interface{}{}) // ShareStorageProviderID is the provider id used by the sharestorageprovider ShareStorageProviderID = "a0ca6a90-a365-4782-871e-d44447bbc668" diff --git a/vendor/modules.txt b/vendor/modules.txt index 2102e482de2..19438ac0074 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -352,8 +352,8 @@ github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1 github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1 github.com/cs3org/go-cs3apis/cs3/tx/v1beta1 github.com/cs3org/go-cs3apis/cs3/types/v1beta1 -# github.com/cs3org/reva/v2 v2.14.0 -## explicit; go 1.19 +# github.com/cs3org/reva/v2 v2.14.1-0.20230607220921-238a03c2f795 +## explicit; go 1.20 github.com/cs3org/reva/v2/cmd/revad/internal/grace github.com/cs3org/reva/v2/cmd/revad/runtime github.com/cs3org/reva/v2/internal/grpc/interceptors/appctx @@ -568,7 +568,6 @@ github.com/cs3org/reva/v2/pkg/publicshare/manager/memory github.com/cs3org/reva/v2/pkg/publicshare/manager/owncloudsql github.com/cs3org/reva/v2/pkg/publicshare/manager/registry github.com/cs3org/reva/v2/pkg/registry -github.com/cs3org/reva/v2/pkg/registry/memory github.com/cs3org/reva/v2/pkg/rgrpc github.com/cs3org/reva/v2/pkg/rgrpc/status github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool