From 84cfa53b496155eaad4a2749f7799d6a523554a4 Mon Sep 17 00:00:00 2001 From: Manjunath Ranganathaiah Date: Fri, 28 Feb 2020 15:19:24 -0800 Subject: Run time context interface library Issue-ID: MULTICLOUD-1005 Signed-off-by: Manjunath Ranganathaiah Change-Id: I12c55441b5701387ead851b8294088457f4401b7 --- src/orchestrator/pkg/rtcontext/rtcontext.go | 222 +++++++++ src/orchestrator/pkg/rtcontext/rtcontext_test.go | 549 +++++++++++++++++++++++ 2 files changed, 771 insertions(+) create mode 100644 src/orchestrator/pkg/rtcontext/rtcontext.go create mode 100644 src/orchestrator/pkg/rtcontext/rtcontext_test.go (limited to 'src/orchestrator') diff --git a/src/orchestrator/pkg/rtcontext/rtcontext.go b/src/orchestrator/pkg/rtcontext/rtcontext.go new file mode 100644 index 00000000..5c5bee13 --- /dev/null +++ b/src/orchestrator/pkg/rtcontext/rtcontext.go @@ -0,0 +1,222 @@ +/* + * Copyright 2020 Intel Corporation, Inc + * + * 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. + */ + +package rtcontext + +import ( + "fmt" + "math/rand" + "time" + "strings" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/contextdb" + pkgerrors "github.com/pkg/errors" +) + +const maxrand = 0x7fffffffffffffff +const prefix string = "/context/" + +type RunTimeContext struct { + cid interface{} +} + +type Rtcontext interface { + RtcCreate() (interface{}, error) + RtcGet() (interface{}, error) + RtcAddLevel(handle interface{}, level string, value string) (interface{}, error) + RtcAddResource(handle interface{}, resname string, value interface{}) (interface{}, error) + RtcAddInstruction(handle interface{}, level string, insttype string, value interface{}) (interface{}, error) + RtcDeletePair(handle interface{}) (error) + RtcDeletePrefix(handle interface{}) (error) + RtcGetHandles(handle interface{}) ([]interface{}, error) + RtcGetValue(handle interface{}, value interface{}) (error) +} + +//Create context by assiging a new id +func (rtc *RunTimeContext) RtcCreate() (interface{}, error) { + + ra := rand.New(rand.NewSource(time.Now().UnixNano())) + rn := ra.Int63n(maxrand) + id := fmt.Sprintf("%v", rn) + cid := (prefix + id + "/") + rtc.cid = interface{}(cid) + + err := contextdb.Db.Put(cid, id) + if err != nil { + return nil, pkgerrors.Errorf("Error creating run time context: %s", err.Error()) + } + + return rtc.cid, nil +} + +//Get the root handle +func (rtc *RunTimeContext) RtcGet() (interface{}, error) { + str := fmt.Sprintf("%v", rtc.cid) + if !strings.HasPrefix(str, prefix) { + return nil, pkgerrors.Errorf("Not a valid run time context") + } + + var value string + err := contextdb.Db.Get(str, &value) + if err != nil { + return nil, pkgerrors.Errorf("Error getting run time context metadata: %s", err.Error()) + } + if !strings.Contains(str, value) { + return nil, pkgerrors.Errorf("Error matching run time context metadata") + } + + return rtc.cid, nil +} + +//Add a new level at a given handle and return the new handle +func (rtc *RunTimeContext) RtcAddLevel(handle interface{}, level string, value string) (interface{}, error) { + str := fmt.Sprintf("%v", handle) + sid := fmt.Sprintf("%v", rtc.cid) + if !strings.HasPrefix(str, sid) { + return nil, pkgerrors.Errorf("Not a valid run time context handle") + } + + if level == "" { + return nil, pkgerrors.Errorf("Not a valid run time context level") + } + if value == "" { + return nil, pkgerrors.Errorf("Not a valid run time context level value") + } + + key := str + level + "/" + value + "/" + err := contextdb.Db.Put(key, value) + if err != nil { + return nil, pkgerrors.Errorf("Error adding run time context level: %s", err.Error()) + } + + return (interface{})(key), nil +} + +// Add a resource under the given level and return new handle +func (rtc *RunTimeContext) RtcAddResource(handle interface{}, resname string, value interface{}) (interface{}, error) { + + str := fmt.Sprintf("%v", handle) + sid := fmt.Sprintf("%v", rtc.cid) + if !strings.HasPrefix(str, sid) { + return nil, pkgerrors.Errorf("Not a valid run time context handle") + } + if resname == "" { + return nil, pkgerrors.Errorf("Not a valid run time context resource name") + } + if value == nil { + return nil, pkgerrors.Errorf("Not a valid run time context resource value") + } + + k := str + "resource" + "/" + resname + "/" + err := contextdb.Db.Put(k, value) + if err != nil { + return nil, pkgerrors.Errorf("Error adding run time context resource: %s", err.Error()) + } + return (interface{})(k), nil +} + +// Add instruction at a given level and type, return the new handle +func (rtc *RunTimeContext) RtcAddInstruction(handle interface{}, level string, insttype string, value interface{}) (interface{}, error) { + str := fmt.Sprintf("%v", handle) + sid := fmt.Sprintf("%v", rtc.cid) + if !strings.HasPrefix(str, sid) { + return nil, pkgerrors.Errorf("Not a valid run time context handle") + } + + if level == "" { + return nil, pkgerrors.Errorf("Not a valid run time context level") + } + if insttype == "" { + return nil, pkgerrors.Errorf("Not a valid run time context instruction type") + } + if value == nil { + return nil, pkgerrors.Errorf("Not a valid run time context instruction value") + } + + k := str + level + "/" + "instruction" + "/" + insttype +"/" + err := contextdb.Db.Put(k, fmt.Sprintf("%v", value)) + if err != nil { + return nil, pkgerrors.Errorf("Error adding run time context instruction: %s", err.Error()) + } + + return (interface{})(k), nil +} + +//Delete the key value pair using given handle +func (rtc *RunTimeContext) RtcDeletePair(handle interface{}) (error) { + str := fmt.Sprintf("%v", handle) + sid := fmt.Sprintf("%v", rtc.cid) + if !strings.HasPrefix(str, sid) { + return pkgerrors.Errorf("Not a valid run time context handle") + } + + err := contextdb.Db.Delete(str) + if err != nil { + return pkgerrors.Errorf("Error deleting run time context pair: %s", err.Error()) + } + + return nil +} + +// Delete all handles underneath the given handle +func (rtc *RunTimeContext) RtcDeletePrefix(handle interface{}) (error) { + str := fmt.Sprintf("%v", handle) + sid := fmt.Sprintf("%v", rtc.cid) + if !strings.HasPrefix(str, sid) { + return pkgerrors.Errorf("Not a valid run time context handle") + } + + err := contextdb.Db.DeleteAll(str) + if err != nil { + return pkgerrors.Errorf("Error deleting run time context with prefix: %s", err.Error()) + } + + return nil +} + +// Return the list of handles under the given handle +func (rtc *RunTimeContext) RtcGetHandles(handle interface{}) ([]interface{}, error) { + str := fmt.Sprintf("%v", handle) + sid := fmt.Sprintf("%v", rtc.cid) + if !strings.HasPrefix(str, sid) { + return nil, pkgerrors.Errorf("Not a valid run time context handle") + } + + s, err := contextdb.Db.GetAllKeys(str) + if err != nil { + return nil, pkgerrors.Errorf("Error getting run time context handles: %s", err.Error()) + } + r := make([]interface{}, len(s)) + for i, v := range s { + r[i] = v + } + return r, nil +} + +// Get the value for a given handle +func (rtc *RunTimeContext) RtcGetValue(handle interface{}, value interface{}) (error) { + str := fmt.Sprintf("%v", handle) + sid := fmt.Sprintf("%v", rtc.cid) + if !strings.HasPrefix(str, sid) { + return pkgerrors.Errorf("Not a valid run time context handle") + } + + err := contextdb.Db.Get(str, value) + if err != nil { + return pkgerrors.Errorf("Error getting run time context value: %s", err.Error()) + } + + return nil +} diff --git a/src/orchestrator/pkg/rtcontext/rtcontext_test.go b/src/orchestrator/pkg/rtcontext/rtcontext_test.go new file mode 100644 index 00000000..670b76bb --- /dev/null +++ b/src/orchestrator/pkg/rtcontext/rtcontext_test.go @@ -0,0 +1,549 @@ +/* + * Copyright 2020 Intel Corporation, Inc + * + * 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. + */ + +package rtcontext + +import ( + "testing" + "strings" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/contextdb" + pkgerrors "github.com/pkg/errors" +) + +// MockContextDb for mocking contextdb +type MockContextDb struct { + + Items map[string]interface{} + Err error +} + +// Put function +func (c *MockContextDb) Put(key string, val interface{}) (error) { + if c.Items == nil { + c.Items = make(map[string]interface{}) + } + c.Items[key] = val + return c.Err +} + +// Get function +func (c *MockContextDb) Get(key string, val interface{}) (error) { + var s *string + s = val.(*string) + for kvKey, kvValue := range c.Items { + if kvKey == key { + *s = kvValue.(string) + return c.Err + } + } + return c.Err +} + +// Delete function +func (c *MockContextDb) Delete(key string) (error) { + delete(c.Items, key) + return c.Err +} + +// Delete all function +func (c *MockContextDb) DeleteAll(key string) (error) { + for kvKey, _ := range c.Items { + delete(c.Items, kvKey) + } + return c.Err +} + +// GetAllKeys function +func (c *MockContextDb) GetAllKeys(path string) ([]string, error) { + var keys []string + + for k, _ := range c.Items { + keys = append(keys, string(k)) + } + return keys, c.Err +} + +func (c *MockContextDb) HealthCheck() error { + return nil +} + +func TestRtcCreate(t *testing.T) { + var rtc = RunTimeContext{} + testCases := []struct { + label string + mockContextDb *MockContextDb + expectedError string + }{ + { + label: "Success case", + mockContextDb: &MockContextDb{}, + }, + { + label: "Create returns error case", + mockContextDb: &MockContextDb{Err: pkgerrors.Errorf("Client not intialized")}, + expectedError: "Error creating run time context:", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + contextdb.Db = testCase.mockContextDb + _, err := rtc.RtcCreate() + if err != nil { + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + + }) + } +} + +func TestRtcGet(t *testing.T) { + var rtc = RunTimeContext{} + var rtc1 = RunTimeContext{"/context/5345674458787728/"} + testCases := []struct { + label string + mockContextDb *MockContextDb + expectedError string + }{ + { + label: "Success case", + mockContextDb: &MockContextDb{}, + }, + { + label: "Get returns error case", + mockContextDb: &MockContextDb{Err: pkgerrors.Errorf("Client not intialized")}, + expectedError: "Error getting run time context metadata:", + }, + { + label: "Context handle does not match", + mockContextDb: &MockContextDb{Err: nil}, + expectedError: "Error matching run time context metadata", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + switch testCase.label { + case "Success case": + contextdb.Db = testCase.mockContextDb + chandle, err := rtc.RtcCreate() + if err != nil { + t.Fatalf("Create returned an error (%s)", err) + } + ghandle, err := rtc.RtcGet() + if err != nil { + t.Fatalf("Get returned an error (%s)", err) + } + if ( chandle != ghandle ) { + t.Fatalf("Create and Get does not match") + } + case "Get returns error case": + contextdb.Db = testCase.mockContextDb + _, err := rtc.RtcGet() + if err != nil { + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + case "Context handle does not match": + contextdb.Db = testCase.mockContextDb + contextdb.Db.Put("/context/5345674458787728/", "6345674458787728") + _, err := rtc1.RtcGet() + if err != nil { + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + } + }) + } +} + +func TestRtcAddLevel(t *testing.T) { + var rtc = RunTimeContext{"/context/3528435435454354/"} + testCases := []struct { + label string + mockContextDb *MockContextDb + handle interface{} + level string + value string + expectedError string + }{ + { + label: "Success case", + mockContextDb: &MockContextDb{}, + handle: "/context/3528435435454354/", + level: "app", + value: "testapp1", + }, + { + label: "Not a valid rtc handle", + mockContextDb: &MockContextDb{}, + handle: "/context/9528435435454354/", + level: "app", + value: "testapp1", + expectedError: "Not a valid run time context handle", + }, + { + label: "Not a valid rtc level", + mockContextDb: &MockContextDb{}, + handle: "/context/3528435435454354/", + level: "", + value: "testapp1", + expectedError: "Not a valid run time context level", + }, + { + label: "Not a valid rtc value", + mockContextDb: &MockContextDb{}, + handle: "/context/3528435435454354/", + level: "app", + value: "", + expectedError: "Not a valid run time context level value", + }, + { + label: "Put returns error", + mockContextDb: &MockContextDb{Err: pkgerrors.Errorf("Client not intialized")}, + handle: "/context/3528435435454354/", + level: "app", + value: "testapp1", + expectedError: "Error adding run time context level:", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + contextdb.Db = testCase.mockContextDb + _, err := rtc.RtcAddLevel(testCase.handle, testCase.level, testCase.value) + if err != nil { + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + }) + } +} + +func TestRtcAddResource(t *testing.T) { + var rtc = RunTimeContext{"/context/3528435435454354/"} + testCases := []struct { + label string + mockContextDb *MockContextDb + handle interface{} + resname string + value interface{} + expectedError string + }{ + { + label: "Success case", + mockContextDb: &MockContextDb{}, + handle: "/context/3528435435454354/app/apptest1/cluster/cluster1/", + resname: "R1", + value: "res1", + }, + { + label: "Not a valid rtc handle", + mockContextDb: &MockContextDb{}, + handle: "/context/9528435435454354/app/apptest1/cluster/cluster1/", + resname: "R1", + value: "res1", + expectedError: "Not a valid run time context handle", + }, + { + label: "Not a valid rtc resource name", + mockContextDb: &MockContextDb{}, + handle: "/context/3528435435454354/app/apptest1/cluster/cluster1/", + resname: "", + value: "res1", + expectedError: "Not a valid run time context resource name", + }, + { + label: "Not a valid rtc value", + mockContextDb: &MockContextDb{}, + handle: "/context/3528435435454354/app/apptest1/cluster/cluster1/", + resname: "R1", + value: nil, + expectedError: "Not a valid run time context resource value", + }, + { + label: "Put returns error", + mockContextDb: &MockContextDb{Err: pkgerrors.Errorf("Client not intialized")}, + handle: "/context/3528435435454354/app/apptest1/cluster/cluster1/", + resname: "R1", + value: "res1", + expectedError: "Error adding run time context resource:", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + contextdb.Db = testCase.mockContextDb + _, err := rtc.RtcAddResource(testCase.handle, testCase.resname, testCase.value) + if err != nil { + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + }) + } +} + +func TestRtcAddInstruction(t *testing.T) { + var rtc = RunTimeContext{"/context/3528435435454354/"} + testCases := []struct { + label string + mockContextDb *MockContextDb + handle interface{} + level string + insttype string + value interface{} + expectedError string + }{ + { + label: "Success case", + mockContextDb: &MockContextDb{}, + handle: "/context/3528435435454354/app/apptest1/cluster/cluster1/", + level: "resource", + insttype: "order", + value: "{resorder: [R3, R1, R2]}", + }, + { + label: "Not a valid rtc handle", + mockContextDb: &MockContextDb{}, + handle: "/context/9528435435454354/app/apptest1/cluster/cluster1/", + level: "resource", + insttype: "order", + value: "{resorder: [R3, R1, R2]}", + expectedError: "Not a valid run time context handle", + }, + { + label: "Not a valid rtc level name", + mockContextDb: &MockContextDb{}, + handle: "/context/3528435435454354/app/apptest1/cluster/cluster1/", + level: "", + insttype: "order", + value: "{resorder: [R3, R1, R2]}", + expectedError: "Not a valid run time context level", + }, + { + label: "Not a valid rtc instruction type", + mockContextDb: &MockContextDb{}, + handle: "/context/3528435435454354/app/apptest1/cluster/cluster1/", + level: "resource", + insttype: "", + value: "{resorder: [R3, R1, R2]}", + expectedError: "Not a valid run time context instruction type", + }, + { + label: "Not a valid rtc value", + mockContextDb: &MockContextDb{}, + handle: "/context/3528435435454354/app/apptest1/cluster/cluster1/", + level: "resource", + insttype: "order", + value: nil, + expectedError: "Not a valid run time context instruction value", + }, + { + label: "Put returns error", + mockContextDb: &MockContextDb{Err: pkgerrors.Errorf("Client not intialized")}, + handle: "/context/3528435435454354/app/apptest1/cluster/cluster1/", + level: "resource", + insttype: "order", + value: "{resorder: [R3, R1, R2]}", + expectedError: "Error adding run time context instruction:", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + contextdb.Db = testCase.mockContextDb + _, err := rtc.RtcAddInstruction(testCase.handle, testCase.level, testCase.insttype, testCase.value) + if err != nil { + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + }) + } +} + +func TestRtcGetHandles(t *testing.T) { + var rtc = RunTimeContext{"/context/5345674458787728/"} + testCases := []struct { + label string + mockContextDb *MockContextDb + key interface{} + expectedError string + }{ + { + label: "Not valid input handle case", + mockContextDb: &MockContextDb{}, + key: "/context/3528435435454354/", + expectedError: "Not a valid run time context handle", + }, + { + label: "Contextdb call returns error case", + mockContextDb: &MockContextDb{Err: pkgerrors.Errorf("Key does not exist")}, + key: "/context/5345674458787728/", + expectedError: "Error getting run time context handles:", + }, + { + label: "Success case", + mockContextDb: &MockContextDb{}, + key: "/context/5345674458787728/", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + contextdb.Db = testCase.mockContextDb + if testCase.label == "Success case" { + contextdb.Db.Put("/context/5345674458787728/", 5345674458787728) + } + _, err := rtc.RtcGetHandles(testCase.key) + if err != nil { + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + }) + } +} + +func TestRtcGetValue(t *testing.T) { + var rtc = RunTimeContext{"/context/5345674458787728/"} + testCases := []struct { + label string + mockContextDb *MockContextDb + key interface{} + expectedError string + }{ + { + label: "Not valid input handle case", + mockContextDb: &MockContextDb{}, + key: "/context/3528435435454354/", + expectedError: "Not a valid run time context handle", + }, + { + label: "Contextdb call returns error case", + mockContextDb: &MockContextDb{Err: pkgerrors.Errorf("Key does not exist")}, + key: "/context/5345674458787728/", + expectedError: "Error getting run time context value:", + }, + { + label: "Success case", + mockContextDb: &MockContextDb{}, + key: "/context/5345674458787728/", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + contextdb.Db = testCase.mockContextDb + if testCase.label == "Success case" { + contextdb.Db.Put("/context/5345674458787728/", "5345674458787728") + } + var val string + err := rtc.RtcGetValue(testCase.key, &val) + if err != nil { + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + }) + } +} + +func TestRtcDeletePair(t *testing.T) { + var rtc = RunTimeContext{"/context/5345674458787728/"} + testCases := []struct { + label string + mockContextDb *MockContextDb + key interface{} + expectedError string + }{ + { + label: "Not valid input handle case", + mockContextDb: &MockContextDb{}, + key: "/context/3528435435454354/", + expectedError: "Not a valid run time context handle", + }, + { + label: "Contextdb call returns error case", + mockContextDb: &MockContextDb{Err: pkgerrors.Errorf("Key does not exist")}, + key: "/context/5345674458787728/", + expectedError: "Error deleting run time context pair:", + }, + { + label: "Success case", + mockContextDb: &MockContextDb{}, + key: "/context/5345674458787728/", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + contextdb.Db = testCase.mockContextDb + err := rtc.RtcDeletePair(testCase.key) + if err != nil { + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + }) + } +} + +func TestRtcDeletePrefix(t *testing.T) { + var rtc = RunTimeContext{"/context/5345674458787728/"} + testCases := []struct { + label string + mockContextDb *MockContextDb + key interface{} + expectedError string + }{ + { + label: "Not valid input handle case", + mockContextDb: &MockContextDb{}, + key: "/context/3528435435454354/", + expectedError: "Not a valid run time context handle", + }, + { + label: "Contextdb call returns error case", + mockContextDb: &MockContextDb{Err: pkgerrors.Errorf("Key does not exist")}, + key: "/context/5345674458787728/", + expectedError: "Error deleting run time context with prefix:", + }, + { + label: "Success case", + mockContextDb: &MockContextDb{}, + key: "/context/5345674458787728/", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + contextdb.Db = testCase.mockContextDb + err := rtc.RtcDeletePrefix(testCase.key) + if err != nil { + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + }) + } +} -- cgit 1.2.3-korg