New ListKeys API in syncstorage 65/6765/3 v0.8.0
authorPetri Ovaska <petri.ovaska@nokia.com>
Wed, 22 Sep 2021 05:09:53 +0000 (08:09 +0300)
committerPetri Ovaska <petri.ovaska@nokia.com>
Mon, 4 Oct 2021 10:26:09 +0000 (13:26 +0300)
The ListKeys API returns all keys in the given namespace matching
key search pattern.

Also update sdltester -tool to test ListKeys api:
  listkeys
    - List keys in the given namespace matching key search pattern.

Upgraded ci/Dockerfile golang image version tag to 1.16. This is
due to expired Root CA certificates in golang:1.12 image which
result an error when building docker image:
  fatal: unable to access 'https://gopkg.in/tomb.v1/': server
  certificate verification failed. CAfile: none CRLfile: none

Version: 0.8.0

Issue-Id: RIC-110
Signed-off-by: Petri Ovaska <petri.ovaska@nokia.com>
Change-Id: Ic02a3f127934a1e02e01a36c573d035db34dbd6f

ci/Dockerfile
cmd/sdltester/sdltester.go
docs/release-notes.rst
sdl_private_fn_test.go
syncstorage.go
syncstorage_test.go [new file with mode: 0644]

index a5b8141..f9dd918 100644 (file)
@@ -23,7 +23,7 @@
 # This is a Dockerfile for code verification only.
 # Not to be pushed or used for anything else.
 #
-FROM golang:1.12
+FROM golang:1.16
 
 RUN mkdir -p $GOPATH/src/sdlgo
 COPY . $GOPATH/src/sdlgo
index f2eb379..9f6e493 100644 (file)
@@ -79,6 +79,8 @@ func main() {
                        noexist(sdl)
                case "getall":
                        getall(sdl)
+               case "listkeys":
+                       listkeys(sdl)
                case "removeall":
                        removeall(sdl)
                case "emptymap":
@@ -112,6 +114,7 @@ func printHelp() {
        fmt.Println("             found")
        fmt.Println("getall       Read all keys within a namespace. One can manually add keys under the used namespace and all")
        fmt.Println("             those keys should be returned here. Performance is measured")
+       fmt.Println("listkeys     List keys in the given namespace matching key search pattern.")
        fmt.Println("removeall    Remove all keys within a namespace. Performance is measured")
        fmt.Println("emptymap     Write an empty container. Performance is measured")
        fmt.Println("multiple     Make two writes. Performance is measured")
@@ -193,6 +196,22 @@ func getall(sdl *sdlgo.SyncStorage) {
        }
 }
 
+func listkeys(sdl *sdlgo.SyncStorage) {
+       start := time.Now()
+       ns := "tag1"
+       pattern := "*"
+       keys, err := sdl.ListKeys(ns, pattern)
+       elapsed := time.Since(start)
+       if err == nil {
+               fmt.Printf("ListKeys(%s, %s): %s\n", ns, pattern, elapsed)
+               for _, i := range keys {
+                       fmt.Println(i)
+               }
+       } else {
+               fmt.Println(err)
+       }
+}
+
 func removeall(sdl *sdlgo.SyncStorage) {
        start := time.Now()
        err := sdl.RemoveAll("tag1")
index b0a7dd6..319c497 100644 (file)
@@ -30,6 +30,11 @@ This document provides the release notes of the sdlgo.
 Version history
 ---------------
 
+[0.8.0] - 2021-09-22
+
+* New ListKeys API in syncstorage
+* Add deprecation warnings for SdlInstance APIs
+
 [0.7.0] - 2021-05-11
 
 * Add DB backend instance selection based on namespace value to balance DB load.
index feef2dc..fae2a02 100644 (file)
@@ -35,3 +35,11 @@ func NewSdlInstanceForTest(NameSpace string, instance iDatabase) *SdlInstance {
                storage:   newSyncStorage(db),
        }
 }
+
+// NewSyncStorageForTest is used only in unit tests to mock database.
+func NewSyncStorageForTest(dbMock iDatabase) *SyncStorage {
+       db := &Database{}
+       db.instances = append(db.instances, dbMock)
+
+       return newSyncStorage(db)
+}
index f2f708e..850213a 100644 (file)
@@ -435,6 +435,34 @@ func (s *SyncStorage) GetAll(ns string) ([]string, error) {
        return retVal, err
 }
 
+// ListKeys returns all keys in the given namespace matching key search pattern.
+//
+//  Supported search glob-style patterns:
+//    h?llo matches hello, hallo and hxllo
+//    h*llo matches hllo and heeeello
+//    h[ae]llo matches hello and hallo, but not hillo
+//    h[^e]llo matches hallo, hbllo, ... but not hello
+//    h[a-b]llo matches hallo and hbllo
+//
+//  The \ escapes character in key search pattern and those will be treated as a normal
+//  character:
+//    h\[?llo\* matches h[ello* and h[allo*
+//
+// No prior knowledge about the keys in the given namespace exists,
+// thus operation is not guaranteed to be atomic or isolated.
+func (s *SyncStorage) ListKeys(ns string, pattern string) ([]string, error) {
+       nsPrefix := getNsPrefix(ns)
+       nsKeys, err := s.getDbBackend(ns).Keys(nsPrefix + pattern)
+       var keys []string
+       if err != nil {
+               return keys, err
+       }
+       for _, key := range nsKeys {
+               keys = append(keys, strings.Split(key, nsPrefix)[1])
+       }
+       return keys, err
+}
+
 //RemoveAll removes all keys under the namespace. Remove operation is not atomic, thus
 //it is not guaranteed that all keys are removed.
 func (s *SyncStorage) RemoveAll(ns string) error {
diff --git a/syncstorage_test.go b/syncstorage_test.go
new file mode 100644 (file)
index 0000000..467275b
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+   Copyright (c) 2021 Nokia.
+
+   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.
+
+   This source code is part of the near-RT RIC (RAN Intelligent Controller)
+   platform project (RICP).
+*/
+package sdlgo_test
+
+import (
+       "errors"
+       "testing"
+
+       "gerrit.o-ran-sc.org/r/ric-plt/sdlgo"
+       "github.com/stretchr/testify/assert"
+)
+
+func setupSDL() (*mockDB, *sdlgo.SyncStorage) {
+       dbMock := new(mockDB)
+       sdl := sdlgo.NewSyncStorageForTest(dbMock)
+       return dbMock, sdl
+}
+
+func TestListKeys(t *testing.T) {
+       dbMock, sdl := setupSDL()
+
+       tests := []struct {
+               keysPatternMockDB     string
+               keysReturnValueMockDB []string
+               keysPattern           string
+               expected              []string
+       }{
+               {"{ns1},*", []string{"{ns1},key1", "{ns1},key2"}, "*", []string{"key1", "key2"}},
+               {"{ns1},ke*", []string{"{ns1},key1", "{ns1},key2"}, "ke*", []string{"key1", "key2"}},
+               {"{ns1},ke?2", []string{"{ns1},key2"}, "ke?2", []string{"key2"}},
+       }
+
+       for _, test := range tests {
+               dbMock.On("Keys", test.keysPatternMockDB).Return(test.keysReturnValueMockDB, nil)
+
+               keys, err := sdl.ListKeys("ns1", test.keysPattern)
+               assert.Nil(t, err)
+               assert.Equal(t, test.expected, keys)
+               dbMock.AssertExpectations(t)
+       }
+}
+
+func TestListKeysEmpty(t *testing.T) {
+       dbMock, sdl := setupSDL()
+
+       dbMock.On("Keys", "{ns1},").Return([]string{}, nil)
+
+       keys, err := sdl.ListKeys("ns1", "")
+       assert.Nil(t, err)
+       assert.Nil(t, keys)
+       dbMock.AssertExpectations(t)
+}
+
+func TestListKeysError(t *testing.T) {
+       dbMock, sdl := setupSDL()
+
+       errorStringMockDB := string("(empty list or set)")
+       dbMock.On("Keys", "{ns1},").Return([]string{}, errors.New(errorStringMockDB))
+
+       keys, err := sdl.ListKeys("ns1", "")
+       assert.NotNil(t, err)
+       assert.EqualError(t, err, errorStringMockDB)
+       assert.Nil(t, keys)
+       dbMock.AssertExpectations(t)
+}