sdlcli get -command is used to display one or many resources.
Usage:
sdlcli get [command]
sdlcli get keys -subcommand is used to list keys in the given
namespace matching key search pattern.
Usage:
sdlcli get keys <namespace> [pattern|default '*'] [flags]
Issue-Id: RIC-113
Change-Id: I86b81ff8d8d2cdc0e959e285b19f93696ebe377e
Signed-off-by: Petri Ovaska <petri.ovaska@nokia.com>
package main
import (
- "fmt"
"gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/cli"
- "os"
)
func main() {
- root := cli.NewRootCmd()
- if err := root.Execute(); err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
+ cli.Execute()
}
package cli
-import (
- "github.com/spf13/cobra"
+var (
+ NewRootCmd = newRootCmd
+ NewHealthCheckCmd = newHealthCheckCmd
+ NewKeysCmdForTest = newKeysCmd
+ NewGetCmdForTest = newGetCmd
)
-
-// NewHealthCheckCmdForTest is used only in unit tests to mock database.
-func NewHealthCheckCmdForTest(dbCreateCb DbCreateCb) *cobra.Command {
- return newHealthCheckCmd(dbCreateCb)
-}
--- /dev/null
+/*
+ Copyright (c) 2021 AT&T Intellectual Property.
+ Copyright (c) 2018-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 cli
+
+import (
+ "github.com/spf13/cobra"
+)
+
+var getCmd = newGetCmd()
+
+func init() {
+ rootCmd.AddCommand(getCmd)
+}
+
+var (
+ getLong = `Display one or many resources.
+
+Prints important information about the specified resources.`
+
+ getExample = ` # List keys in the given namespace.
+ sdlcli get keys sdlns`
+)
+
+func newGetCmd() *cobra.Command {
+ return &cobra.Command{
+ Use: "get",
+ Short: "Display one or many resources",
+ Long: getLong,
+ Example: getExample,
+ }
+}
--- /dev/null
+/*
+ Copyright (c) 2021 AT&T Intellectual Property.
+ Copyright (c) 2018-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 cli_test
+
+import (
+ "bytes"
+ "errors"
+ "gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/cli"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestGetCmdShowHelp(t *testing.T) {
+ var expOkErr error
+ expHelp := "Display one or many resources.\n\nPrints important information about the specified resources."
+ expExamples := "Examples:\n # List keys in the given namespace."
+ expNokErr := errors.New("unknown flag: --ff")
+ tests := []struct {
+ args []string
+ expErr error
+ expOut string
+ }{
+ {args: []string{"-h"}, expErr: expOkErr, expOut: expHelp},
+ {args: []string{"--help"}, expErr: expOkErr, expOut: expHelp},
+ {args: []string{}, expErr: expOkErr, expOut: expHelp},
+ {args: []string{"--ff"}, expErr: expNokErr, expOut: expExamples},
+ }
+
+ for _, test := range tests {
+ buf := new(bytes.Buffer)
+ cmd := cli.NewGetCmdForTest()
+ cmd.SetOut(buf)
+ cmd.SetErr(buf)
+ cmd.SetArgs(test.args)
+ err := cmd.Execute()
+ result := buf.String()
+
+ assert.Equal(t, test.expErr, err)
+ assert.Contains(t, result, test.expOut)
+ }
+}
package cli
import (
- "bytes"
"fmt"
"gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/sdlgoredis"
"github.com/spf13/cobra"
"os"
)
-func NewHealthCheckCmd() *cobra.Command {
- return newHealthCheckCmd(newDatabase)
+func init() {
+ rootCmd.AddCommand(newHealthCheckCmd(newDatabase))
}
func newHealthCheckCmd(dbCreateCb DbCreateCb) *cobra.Command {
Short: "healthcheck - validates database healthiness",
Long: `healthcheck - validates database healthiness`,
RunE: func(cmd *cobra.Command, args []string) error {
- var buf bytes.Buffer
- sdlgoredis.SetDbLogger(&buf)
out, err := runHealthCheck(dbCreateCb)
cmd.Println(out)
if err != nil {
func runHcCli() (string, error) {
buf := new(bytes.Buffer)
- cmd := cli.NewHealthCheckCmdForTest(newMockDatabase)
+ cmd := cli.NewHealthCheckCmd(newMockDatabase)
cmd.SetOut(buf)
err := cmd.Execute()
for _, test := range tests {
buf := new(bytes.Buffer)
- cmd := cli.NewHealthCheckCmd()
+ cmd := cli.NewHealthCheckCmd(newMockDatabase)
cmd.SetOut(buf)
cmd.SetArgs([]string{test.args})
expCliErr := errors.New("SDL CLI error: Some error")
buf := new(bytes.Buffer)
- cmd := cli.NewHealthCheckCmdForTest(newMockDatabase)
+ cmd := cli.NewHealthCheckCmd(newMockDatabase)
cmd.SetErr(buf)
err := cmd.Execute()
--- /dev/null
+/*
+ Copyright (c) 2021 AT&T Intellectual Property.
+ Copyright (c) 2018-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 cli
+
+import (
+ "fmt"
+ "os"
+ "sort"
+
+ "gerrit.o-ran-sc.org/r/ric-plt/sdlgo"
+ "gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/sdlgoredis"
+ "github.com/spf13/cobra"
+)
+
+func init() {
+ getCmd.AddCommand(newKeysCmd(func() ISyncStorage {
+ return sdlgo.NewSyncStorage()
+ }))
+}
+
+var (
+ keysLong = `List keys in the given namespace matching key search pattern.`
+
+ keysExample = ` # List all keys in the given namespace.
+ sdlcli get keys sdlns
+ # List keys in the given namespace matching given key search pattern.
+ sdlcli get keys sdlns 'he*'
+
+ 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*`
+)
+
+func newKeysCmd(sdlCb SyncStorageCreateCb) *cobra.Command {
+ return &cobra.Command{
+ Use: "keys <namespace> [pattern|default '*']",
+ Short: "List keys in the given namespace matching key search pattern",
+ Long: keysLong,
+ Example: keysExample,
+ Args: cobra.RangeArgs(1, 2),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ sdlgoredis.SetDbLogger(&buf)
+ keysArgs := newKeysArgs(args[0], "*")
+ if len(args) > 1 {
+ keysArgs.pattern = args[1]
+ }
+ keys, err := runListKeys(sdlCb, keysArgs)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s", buf.String())
+ return err
+ }
+ printKeys(cmd, keys)
+ return nil
+ },
+ }
+}
+
+func runListKeys(sdlCb SyncStorageCreateCb, args keysArgs) ([]string, error) {
+ keys, err := sdlCb().ListKeys(args.ns, args.pattern)
+ if err != nil {
+ return nil, err
+ }
+ sort.Strings(keys)
+ return keys, err
+}
+
+func printKeys(cmd *cobra.Command, keys []string) {
+ for _, k := range keys {
+ cmd.Println(k)
+ }
+}
--- /dev/null
+/*
+ Copyright (c) 2021 AT&T Intellectual Property.
+ Copyright (c) 2018-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 cli_test
+
+import (
+ "bytes"
+ "errors"
+ "gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/cli"
+ "gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/mocks"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+var mKeys *keysMock
+
+type keysMock struct {
+ sdlIface *mocks.MockSdlApi
+ ns string
+ pattern string
+ keys []string
+ err error
+}
+
+func setupKeysCliMock(ns, pattern string, keys []string, err error) {
+ mKeys = new(keysMock)
+ mKeys.ns = ns
+ mKeys.pattern = pattern
+ mKeys.keys = keys
+ mKeys.err = err
+}
+
+func newMockSdlApi() cli.ISyncStorage {
+ mKeys.sdlIface = new(mocks.MockSdlApi)
+ mKeys.sdlIface.On("ListKeys", mKeys.ns, mKeys.pattern).Return(mKeys.keys, mKeys.err)
+ return mKeys.sdlIface
+}
+
+func TestKeysCmdShowHelp(t *testing.T) {
+ var expOkErr error
+ expNokErrZeroArgs := errors.New("accepts between 1 and 2 arg(s), received 0")
+ expNokErrThreeArgs := errors.New("accepts between 1 and 2 arg(s), received 3")
+ expHelp := "Usage:\n keys <namespace> [pattern|default '*'] [flags]"
+ tests := []struct {
+ args []string
+ expOut string
+ expErr error
+ }{
+ {args: []string{"-h"}, expErr: expOkErr, expOut: expHelp},
+ {args: []string{"--help"}, expErr: expOkErr, expOut: expHelp},
+ {args: []string{}, expErr: expNokErrZeroArgs, expOut: expHelp},
+ {args: []string{"ns", "key", "extra-arg"}, expErr: expNokErrThreeArgs, expOut: expHelp},
+ }
+
+ for _, test := range tests {
+ buf := new(bytes.Buffer)
+ cmd := cli.NewKeysCmdForTest(newMockSdlApi)
+ cmd.SetOut(buf)
+ cmd.SetErr(buf)
+ cmd.SetArgs(test.args)
+ err := cmd.Execute()
+ result := buf.String()
+
+ assert.Equal(t, test.expErr, err)
+ assert.Contains(t, result, test.expOut)
+ }
+}
+
+func TestKeysCmdSuccess(t *testing.T) {
+ var expOkErr error
+ argsNsPattern := []string{"testns", "*"}
+ argsNs := []string{"testns"}
+ tests := []struct {
+ args []string
+ outListKeys []string
+ expOut string
+ expErr error
+ }{
+ {args: argsNs, outListKeys: []string{"key13"}, expOut: "key13", expErr: expOkErr},
+ {args: argsNsPattern, outListKeys: []string{"key1"}, expOut: "key1", expErr: expOkErr},
+ {args: argsNsPattern, outListKeys: []string{"key1", "key2"}, expOut: "key1\nkey2", expErr: expOkErr},
+ {args: argsNsPattern, outListKeys: []string{"key2", "key3", "key1"}, expOut: "key1\nkey2\nkey3", expErr: expOkErr},
+ {args: argsNsPattern, outListKeys: []string{}, expOut: "", expErr: expOkErr},
+ }
+
+ for _, test := range tests {
+ buf := new(bytes.Buffer)
+ if len(test.args) > 1 {
+ setupKeysCliMock(test.args[0], test.args[1], test.outListKeys, test.expErr)
+ } else {
+ setupKeysCliMock(test.args[0], "*", test.outListKeys, test.expErr)
+ }
+ cmd := cli.NewKeysCmdForTest(newMockSdlApi)
+ cmd.SetOut(buf)
+ cmd.SetErr(buf)
+ cmd.SetArgs(test.args)
+ err := cmd.Execute()
+ result := buf.String()
+
+ assert.Equal(t, test.expErr, err)
+ assert.Contains(t, result, test.expOut)
+ }
+}
+
+func TestKeysCmdFails(t *testing.T) {
+ argsNs := []string{"testns"}
+ expNokErr := errors.New("Boom!")
+ expNokOut := "Usage:\n keys <namespace> [pattern|default '*'] [flags]"
+
+ buf := new(bytes.Buffer)
+ setupKeysCliMock("testns", "*", []string{"very wrong"}, expNokErr)
+ cmd := cli.NewKeysCmdForTest(newMockSdlApi)
+ cmd.SetOut(buf)
+ cmd.SetErr(buf)
+ cmd.SetArgs(argsNs)
+ err := cmd.Execute()
+ result := buf.String()
+
+ assert.Equal(t, expNokErr, err)
+ assert.Contains(t, result, expNokOut)
+}
package cli
import (
+ "fmt"
"github.com/spf13/cobra"
+ "os"
)
-func NewRootCmd() *cobra.Command {
- cmd := &cobra.Command{
+var rootCmd = newRootCmd()
+
+func newRootCmd() *cobra.Command {
+ return &cobra.Command{
Use: SdlCliApp,
Short: "Shared Data Layer (SDL) troubleshooting command line tool",
Long: `Shared Data Layer (SDL) troubleshooting command line tool`,
Run: func(cmd *cobra.Command, args []string) {
},
}
- cmd.AddCommand(NewHealthCheckCmd())
- return cmd
+}
+
+func Execute() {
+ if err := rootCmd.Execute(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
}
//DbCreateCb callback function type to create a new database
type DbCreateCb func() *Database
+//iSyncStorage is an interface towards SDL SyncStorage API
+type ISyncStorage interface {
+ ListKeys(ns string, pattern string) ([]string, error)
+}
+
+//SyncStorageCreateCb callback function type to create a new SyncStorageInterface
+type SyncStorageCreateCb func() ISyncStorage
+
+//keysArgs struct is used for keys command arguments.
+type keysArgs struct {
+ ns string
+ pattern string
+}
+
+//newKeysArgs constructs a new keysArgs struct.
+func newKeysArgs(ns string, pattern string) keysArgs {
+ return keysArgs{
+ ns: ns,
+ pattern: pattern,
+ }
+}
+
//SdlCliApp constant defines the name of the SDL CLI application
const SdlCliApp = "sdlcli"
package cli
import (
+ "bytes"
"gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/sdlgoredis"
)
+var (
+ buf bytes.Buffer
+)
+
func newDatabase() *Database {
db := &Database{}
+ sdlgoredis.SetDbLogger(&buf)
for _, v := range sdlgoredis.Create() {
db.Instances = append(db.Instances, v)
}
a := m.Called()
return a.Get(0).(*sdlgoredis.DbState), a.Error(1)
}
+
+type MockSdlApi struct {
+ mock.Mock
+}
+
+func (m *MockSdlApi) ListKeys(ns string, pattern string) ([]string, error) {
+ a := m.Called(ns, pattern)
+ return a.Get(0).([]string), a.Error(1)
+}