Repository: owncloud/ocis
Pull request #8011 (2023-12-19 10:58:35)

Switch default registry to `nats-js-kv`
- Switches the default registry to `nats-js-kv`
- Deprecates `consul`, `nats`, `etcd` and `mdns` registries
- Documents registry usage

update-docs[bot]: Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a [changelog](https://github.com/owncloud/ocis/blob/master/changelog/README.md) item based on your changes.

sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=8011) **Quality Gate passed**  
Kudos, no new issues were introduced!

[0 New issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=8011&resolved=false&inNewCodePeriod=true)  
[0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=8011&resolved=false&inNewCodePeriod=true)  
[0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=8011&metric=new_coverage&view=list)  
[0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=8011&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=8011)



Patch:
diff --git a/changelog/unreleased/default-registry.md b/changelog/unreleased/default-registry.md
new file mode 100644
index 00000000000..407af748107
--- /dev/null
+++ b/changelog/unreleased/default-registry.md
@@ -0,0 +1,5 @@
+Enhancement: Make nats-js-kv the default registry
+
+The previously used default `mdns` is faulty. Deprecated it together with `consul`, `nats` and `etcd` implementations.
+
+https://github.com/owncloud/ocis/pull/8011
diff --git a/docs/services/general-info/registry.md b/docs/services/general-info/registry.md
new file mode 100644
index 00000000000..5db99e82b31
--- /dev/null
+++ b/docs/services/general-info/registry.md
@@ -0,0 +1,39 @@
+---
+title: Registry
+date: 2023-12-19T00:00:00+00:00
+weight: 20
+geekdocRepo: https://github.com/owncloud/ocis
+geekdocEditPath: edit/master/docs/services/general-info
+geekdocFilePath: registry.md
+geekdocCollapseSection: true
+---
+
+To be able to communicate with each other, services need to register in a common registry.
+
+## Configuration
+
+The type of registry to use can be configured with the `MICRO_REGISTRY` environment variable. Supported values are:
+
+### `memory`
+
+Setting the environment variable to `memory` starts an inmemory registry. This only works with the single binary deployment.
+
+### `nats-js-kv` (Default)
+
+Set the environment variable to `nats-js-kv` or leave it empty to use a nats-js key value store as registry.
+- Note: If not running build-in nats, `MICRO_REGISTRY_ADDRESS` needs to be set to the address of the nats-js cluster, which is the same value as `OCIS_EVENTS_ENDPOINT`.
+- Optional: Use `MICRO_REGISTRY_AUTH_USERNAME` and `MICRO_REGISTRY_AUTH_PASSWORD` to authenticate with the nats cluster.
+### `kubernetes`
+
+When deploying in a kubernetes cluster, the kubernetes registry can be used. Additionally the `MICRO_REGISTRY_ADDRESS` environment
+variable needs to be set to the url of the kubernetes registry.
+
+### Deprecated Registries
+
+These registries are currently working but will be removed in a later version. It is recommended to switch to a supported one.
+
+- `nats`. Uses a registry based on nats streams. Requires `MICRO_REGISTRY_ADDRESS` to bet set.
+- `etcd`. Uses an etcd cluster as registry. Requires `MICRO_REGISTRY_ADDRESS` to bet set.
+- `consul`. Uses `HashiCorp Consul` as registry. Requires `MICRO_REGISTRY_ADDRESS` to bet set.
+- `mdns`.  Uses multicast dns for registration. This type can have unwanted side effects when other devices in the local network use mdns too.
+
diff --git a/ocis-pkg/registry/registry.go b/ocis-pkg/registry/registry.go
index cee918bf81c..3d1b2b80e3f 100644
--- a/ocis-pkg/registry/registry.go
+++ b/ocis-pkg/registry/registry.go
@@ -1,6 +1,7 @@
 package registry
 
 import (
+	"fmt"
 	"os"
 	"strings"
 	"sync"
@@ -21,73 +22,110 @@ import (
 const (
 	registryEnv         = "MICRO_REGISTRY"
 	registryAddressEnv  = "MICRO_REGISTRY_ADDRESS"
-	regisryUsernameEnv  = "MICRO_REGISTRY_AUTH_USERNAME"
+	registryUsernameEnv = "MICRO_REGISTRY_AUTH_USERNAME"
 	registryPasswordEnv = "MICRO_REGISTRY_AUTH_PASSWORD"
 )
 
 var (
-	once      sync.Once
-	regPlugin string
-	reg       mRegistry.Registry
+	_once sync.Once
+	_reg  mRegistry.Registry
 )
 
-// Configure configures the registry
-func Configure(plugin string) {
-	if reg == nil {
-		regPlugin = plugin
+// Config is the config for a registry
+type Config struct {
+	Type         string   `mapstructure:"type"`
+	Addresses    []string `mapstructure:"addresses"`
+	Username     string   `mapstructure:"username"`
+	Password     string   `mapstructure:"password"`
+	DisableCache bool     `mapstructure:"disable_cache"`
+}
+
+// Option allows configuring the registry
+type Option func(*Config)
+
+// Inmemory overrides env values to use an in-memory registry
+func Inmemory() Option {
+	return func(c *Config) {
+		c.Type = "memory"
 	}
 }
 
 // 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() mRegistry.Registry {
-	once.Do(func() {
-		addresses := strings.Split(os.Getenv(registryAddressEnv), ",")
-		// prefer env of setting from Configure()
-		plugin := os.Getenv(registryEnv)
-		if plugin == "" {
-			plugin = regPlugin
-		}
+func GetRegistry(opts ...Option) mRegistry.Registry {
+	_once.Do(func() {
+		cfg := getEnvs(opts...)
 
-		switch plugin {
-		case "nats":
-			reg = natsr.NewRegistry(
-				mRegistry.Addrs(addresses...),
-				natsr.RegisterAction("put"),
+		switch cfg.Type {
+		default:
+			fmt.Println("Attention: unknown registry type, using default nats-js-kv")
+			fallthrough
+		case "natsjs", "nats-js", "nats-js-kv": // for backwards compatibility - we will stick with one of those
+			_reg = natsjsregistry.NewRegistry(
+				mRegistry.Addrs(cfg.Addresses...),
+				natsjsregistry.Authenticate(cfg.Username, cfg.Password),
 			)
 		case "kubernetes":
-			reg = kubernetesr.NewRegistry(
-				mRegistry.Addrs(addresses...),
+			_reg = kubernetesr.NewRegistry(
+				mRegistry.Addrs(cfg.Addresses...),
+			)
+		case "memory":
+			_reg = memr.NewRegistry()
+			cfg.DisableCache = true // no cache needed for in-memory registry
+		case "nats":
+			fmt.Println("Attention: nats registry is deprecated, use nats-js-kv instead")
+			_reg = natsr.NewRegistry(
+				mRegistry.Addrs(cfg.Addresses...),
+				natsr.RegisterAction("put"),
 			)
 		case "etcd":
-			reg = etcdr.NewRegistry(
-				mRegistry.Addrs(addresses...),
+			fmt.Println("Attention: etcd registry is deprecated, use nats-js-kv instead")
+			_reg = etcdr.NewRegistry(
+				mRegistry.Addrs(cfg.Addresses...),
 			)
 		case "consul":
-			reg = consulr.NewRegistry(
-				mRegistry.Addrs(addresses...),
-			)
-		case "memory":
-			reg = memr.NewRegistry()
-		case "natsjs", "nats-js", "nats-js-kv": // for backwards compatibility - we will stick with one of those
-			reg = natsjsregistry.NewRegistry(
-				mRegistry.Addrs(addresses...),
-				natsjsregistry.Authenticate(os.Getenv(regisryUsernameEnv), os.Getenv(registryPasswordEnv)),
+			fmt.Println("Attention: consul registry is deprecated, use nats-js-kv instead")
+			_reg = consulr.NewRegistry(
+				mRegistry.Addrs(cfg.Addresses...),
 			)
-		default:
-			reg = mdnsr.NewRegistry()
+		case "mdns":
+			fmt.Println("Attention: mdns registry is deprecated, use nats-js-kv instead")
+			_reg = mdnsr.NewRegistry()
 		}
 
-		// No cache needed for in-memory registry
-		if plugin != "memory" {
-			reg = cache.New(reg, cache.WithTTL(30*time.Second))
+		// Disable cache if wanted
+		if !cfg.DisableCache {
+			_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)
+		_ = rRegistry.Init(_reg)
 	})
 	// always use cached registry to prevent registry
 	// lookup for every request
-	return reg
+	return _reg
+}
+
+func getEnvs(opts ...Option) *Config {
+	cfg := &Config{
+		Type:      "nats-js-kv",
+		Addresses: []string{"127.0.0.1:9233"},
+		Username:  os.Getenv(registryUsernameEnv),
+		Password:  os.Getenv(registryPasswordEnv),
+	}
+
+	if s := os.Getenv(registryEnv); s != "" {
+		cfg.Type = s
+	}
+
+	if s := strings.Split(os.Getenv(registryAddressEnv), ","); len(s) > 0 {
+		cfg.Addresses = s
+	}
+
+	for _, o := range opts {
+		o(cfg)
+	}
+
+	return cfg
 }
diff --git a/ocis/pkg/command/server.go b/ocis/pkg/command/server.go
index 60d450cf9e2..108038ce72d 100644
--- a/ocis/pkg/command/server.go
+++ b/ocis/pkg/command/server.go
@@ -4,7 +4,6 @@ import (
 	"github.com/owncloud/ocis/v2/ocis-pkg/config"
 	"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
 	"github.com/owncloud/ocis/v2/ocis-pkg/config/parser"
-	"github.com/owncloud/ocis/v2/ocis-pkg/registry"
 	"github.com/owncloud/ocis/v2/ocis/pkg/register"
 	"github.com/owncloud/ocis/v2/ocis/pkg/runtime"
 	"github.com/urfave/cli/v2"
@@ -21,7 +20,6 @@ func Server(cfg *config.Config) *cli.Command {
 		},
 		Action: func(c *cli.Context) error {
 			// Prefer the in-memory registry as the default when running in single-binary mode
-			registry.Configure("memory")
 			r := runtime.New(cfg)
 			return r.Start()
 		},


Test patch:
diff --git a/services/graph/pkg/service/v0/graph_suite_test.go b/services/graph/pkg/service/v0/graph_suite_test.go
index 7b6ed5b234f..70dfcce1450 100644
--- a/services/graph/pkg/service/v0/graph_suite_test.go
+++ b/services/graph/pkg/service/v0/graph_suite_test.go
@@ -11,8 +11,7 @@ import (
 )
 
 func init() {
-	registry.Configure("memory")
-	r := registry.GetRegistry()
+	r := registry.GetRegistry(registry.Inmemory())
 	service := registry.BuildGRPCService("com.owncloud.api.gateway", "", "", "")
 	service.Nodes = []*mRegistry.Node{{
 		Address: "any",
diff --git a/services/notifications/pkg/service/notification_suite_test.go b/services/notifications/pkg/service/notification_suite_test.go
index dc3c1f42511..7f282054fb6 100644
--- a/services/notifications/pkg/service/notification_suite_test.go
+++ b/services/notifications/pkg/service/notification_suite_test.go
@@ -11,8 +11,7 @@ import (
 )
 
 func init() {
-	registry.Configure("memory")
-	r := registry.GetRegistry()
+	r := registry.GetRegistry(registry.Inmemory())
 	service := registry.BuildGRPCService("com.owncloud.api.gateway", "", "", "")
 	service.Nodes = []*mRegistry.Node{{
 		Address: "any",
diff --git a/services/search/pkg/search/search_suite_test.go b/services/search/pkg/search/search_suite_test.go
index 75873d6daaa..9422179fc53 100644
--- a/services/search/pkg/search/search_suite_test.go
+++ b/services/search/pkg/search/search_suite_test.go
@@ -11,8 +11,7 @@ import (
 )
 
 func init() {
-	registry.Configure("memory")
-	r := registry.GetRegistry()
+	r := registry.GetRegistry(registry.Inmemory())
 	service := registry.BuildGRPCService("com.owncloud.api.gateway", "", "", "")
 	service.Nodes = []*mRegistry.Node{{
 		Address: "any",
diff --git a/services/storage-users/pkg/task/task_suite_test.go b/services/storage-users/pkg/task/task_suite_test.go
index 5b904e7e302..ac0c8a44b9d 100644
--- a/services/storage-users/pkg/task/task_suite_test.go
+++ b/services/storage-users/pkg/task/task_suite_test.go
@@ -11,8 +11,7 @@ import (
 )
 
 func init() {
-	registry.Configure("memory")
-	r := registry.GetRegistry()
+	r := registry.GetRegistry(registry.Inmemory())
 	service := registry.BuildGRPCService("com.owncloud.api.gateway", "", "", "")
 	service.Nodes = []*mRegistry.Node{{
 		Address: "any",
diff --git a/services/userlog/pkg/service/service_suit_test.go b/services/userlog/pkg/service/service_suit_test.go
index ab7fb7b1989..9da9654d4e9 100644
--- a/services/userlog/pkg/service/service_suit_test.go
+++ b/services/userlog/pkg/service/service_suit_test.go
@@ -11,8 +11,7 @@ import (
 )
 
 func init() {
-	registry.Configure("memory")
-	r := registry.GetRegistry()
+	r := registry.GetRegistry(registry.Inmemory())
 	service := registry.BuildGRPCService("com.owncloud.api.gateway", "", "", "")
 	service.Nodes = []*mRegistry.Node{{
 		Address: "any",