github.com/cs3org/reva/v2@v2.27.7/pkg/store/store.go (about)

     1  // Copyright 2018-2023 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package store
    20  
    21  import (
    22  	"context"
    23  	"strings"
    24  	"time"
    25  
    26  	"github.com/cs3org/reva/v2/pkg/store/etcd"
    27  	"github.com/cs3org/reva/v2/pkg/store/memory"
    28  	natsjs "github.com/go-micro/plugins/v4/store/nats-js"
    29  	natsjskv "github.com/go-micro/plugins/v4/store/nats-js-kv"
    30  	"github.com/go-micro/plugins/v4/store/redis"
    31  	redisopts "github.com/go-redis/redis/v8"
    32  	"github.com/nats-io/nats.go"
    33  	"go-micro.dev/v4/logger"
    34  	microstore "go-micro.dev/v4/store"
    35  )
    36  
    37  var ocMemStore *microstore.Store
    38  
    39  const (
    40  	// TypeMemory represents memory stores
    41  	TypeMemory = "memory"
    42  	// TypeNoop represents noop stores
    43  	TypeNoop = "noop"
    44  	// TypeEtcd represents etcd stores
    45  	TypeEtcd = "etcd"
    46  	// TypeRedis represents redis stores
    47  	TypeRedis = "redis"
    48  	// TypeRedisSentinel represents redis-sentinel stores
    49  	TypeRedisSentinel = "redis-sentinel"
    50  	// TypeOCMem represents ocmem stores
    51  	TypeOCMem = "ocmem"
    52  	// TypeNatsJS represents nats-js stores
    53  	TypeNatsJS = "nats-js"
    54  	// TypeNatsJSKV represents nats-js-kv stores
    55  	TypeNatsJSKV = "nats-js-kv"
    56  )
    57  
    58  // Create initializes a new store
    59  func Create(opts ...microstore.Option) microstore.Store {
    60  	options := &microstore.Options{
    61  		Context: context.Background(),
    62  	}
    63  	for _, o := range opts {
    64  		o(options)
    65  	}
    66  
    67  	storeType, _ := options.Context.Value(typeContextKey{}).(string)
    68  
    69  	switch storeType {
    70  	case TypeNoop:
    71  		return microstore.NewNoopStore(opts...)
    72  	case TypeEtcd:
    73  		return etcd.NewStore(opts...)
    74  	case TypeRedis:
    75  		// FIXME redis plugin does not support redis cluster or ring -> needs upstream patch or our implementation
    76  		return redis.NewStore(opts...)
    77  	case TypeRedisSentinel:
    78  		redisMaster := ""
    79  		redisNodes := []string{}
    80  		for _, node := range options.Nodes {
    81  			parts := strings.SplitN(node, "/", 2)
    82  			if len(parts) != 2 {
    83  				return nil
    84  			}
    85  			// the first node is used to retrieve the redis master
    86  			redisNodes = append(redisNodes, parts[0])
    87  			if redisMaster == "" {
    88  				redisMaster = parts[1]
    89  			}
    90  		}
    91  		return redis.NewStore(
    92  			microstore.Database(options.Database),
    93  			microstore.Table(options.Table),
    94  			microstore.Nodes(redisNodes...),
    95  			redis.WithRedisOptions(redisopts.UniversalOptions{
    96  				MasterName: redisMaster,
    97  			}),
    98  		)
    99  	case TypeOCMem:
   100  		if ocMemStore == nil {
   101  			var memStore microstore.Store
   102  
   103  			sizeNum, _ := options.Context.Value(sizeContextKey{}).(int)
   104  			if sizeNum <= 0 {
   105  				memStore = memory.NewMultiMemStore()
   106  			} else {
   107  				memStore = memory.NewMultiMemStore(
   108  					microstore.WithContext(
   109  						memory.NewContext(
   110  							context.Background(),
   111  							map[string]interface{}{
   112  								"maxCap": sizeNum,
   113  							},
   114  						)),
   115  				)
   116  			}
   117  			ocMemStore = &memStore
   118  		}
   119  		return *ocMemStore
   120  	case TypeNatsJS:
   121  		ttl, _ := options.Context.Value(ttlContextKey{}).(time.Duration)
   122  		if mem, _ := options.Context.Value(disablePersistanceContextKey{}).(bool); mem {
   123  			opts = append(opts, natsjs.DefaultMemory())
   124  		}
   125  		// TODO nats needs a DefaultTTL option as it does not support per Write TTL ...
   126  		// FIXME nats has restrictions on the key, we cannot use slashes AFAICT
   127  		// host, port, clusterid
   128  		natsOptions := nats.GetDefaultOptions()
   129  		natsOptions.Name = "TODO" // we can pass in the service name to allow identifying the client, but that requires adding a custom context option
   130  		if auth, ok := options.Context.Value(authenticationContextKey{}).([]string); ok && len(auth) == 2 {
   131  			natsOptions.User = auth[0]
   132  			natsOptions.Password = auth[1]
   133  		}
   134  		return natsjs.NewStore(
   135  			append(opts,
   136  				natsjs.NatsOptions(natsOptions), // always pass in properly initialized default nats options
   137  				natsjs.DefaultTTL(ttl))...,
   138  		) // TODO test with ocis nats
   139  	case TypeNatsJSKV:
   140  		// NOTE: nats needs a DefaultTTL option as it does not support per Write TTL ...
   141  		ttl, _ := options.Context.Value(ttlContextKey{}).(time.Duration)
   142  		if mem, _ := options.Context.Value(disablePersistanceContextKey{}).(bool); mem {
   143  			opts = append(opts, natsjskv.DefaultMemory())
   144  		}
   145  
   146  		natsOptions := nats.GetDefaultOptions()
   147  		natsOptions.Name = "TODO" // we can pass in the service name to allow identifying the client, but that requires adding a custom context option
   148  		if auth, ok := options.Context.Value(authenticationContextKey{}).([]string); ok && len(auth) == 2 {
   149  			natsOptions.User = auth[0]
   150  			natsOptions.Password = auth[1]
   151  		}
   152  		return natsjskv.NewStore(
   153  			append(opts,
   154  				natsjskv.NatsOptions(natsOptions), // always pass in properly initialized default nats options
   155  				natsjskv.EncodeKeys(),
   156  				natsjskv.DefaultTTL(ttl))...,
   157  		)
   158  	case TypeMemory, "mem", "": // allow existing short form and use as default
   159  		return microstore.NewMemoryStore(opts...)
   160  	default:
   161  		// try to log an error
   162  		if options.Logger == nil {
   163  			options.Logger = logger.DefaultLogger
   164  		}
   165  		options.Logger.Logf(logger.ErrorLevel, "unknown store type: '%s', falling back to memory", storeType)
   166  		return microstore.NewMemoryStore(opts...)
   167  	}
   168  }