From 037cfda2181e4995e4e2a47db6f1121b532b686b Mon Sep 17 00:00:00 2001 From: Kiran Kamineni Date: Fri, 15 Mar 2019 15:03:01 -0700 Subject: Add support for composite keys Composite keys help us store objects which are unique for a given set of pre-existing objects. Eg: Many profiles can exist for a definition and its key will have a definition name as a part of the composite key. P2: Use a predefined interface for keys instead of generic interfaceP{} P3: Add check for empty strings in stringer interface P5: Add appropriate keys in other packages. Issue-ID: MULTICLOUD-531 Change-Id: I314b1fbd718489ae8a45f0f38915c08ca32f9f43 Signed-off-by: Kiran Kamineni --- src/k8splugin/api/handler.go | 16 ++- src/k8splugin/internal/db/consul.go | 37 ++++-- src/k8splugin/internal/db/consul_test.go | 42 ++++--- src/k8splugin/internal/db/mongo.go | 27 +++-- src/k8splugin/internal/db/mongo_test.go | 191 ++++++++++++++++--------------- src/k8splugin/internal/db/store.go | 15 ++- src/k8splugin/internal/db/testing.go | 18 ++- src/k8splugin/internal/rb/definition.go | 24 +++- src/k8splugin/internal/rb/profile.go | 24 +++- 9 files changed, 236 insertions(+), 158 deletions(-) diff --git a/src/k8splugin/api/handler.go b/src/k8splugin/api/handler.go index 4d6abe27..b1cc6709 100644 --- a/src/k8splugin/api/handler.go +++ b/src/k8splugin/api/handler.go @@ -35,6 +35,14 @@ import ( var storeName = "rbinst" var tagData = "data" +type instanceKey struct { + Key string +} + +func (dk instanceKey) String() string { + return dk.Key +} + // GetVNFClient retrieves the client used to communicate with a Kubernetes Cluster var GetVNFClient = func(kubeConfigPath string) (kubernetes.Clientset, error) { client, err := helper.GetKubeClient(kubeConfigPath) @@ -130,7 +138,7 @@ func CreateHandler(w http.ResponseWriter, r *http.Request) { // key: cloud1-default-uuid // value: "{"deployment":<>,"service":<>}" - err = db.DBconn.Create(storeName, internalVNFID, tagData, resourceNameMap) + err = db.DBconn.Create(storeName, instanceKey{Key: internalVNFID}, tagData, resourceNameMap) if err != nil { werr := pkgerrors.Wrap(err, "Create VNF deployment DB error") http.Error(w, werr.Error(), http.StatusInternalServerError) @@ -202,7 +210,7 @@ func DeleteHandler(w http.ResponseWriter, r *http.Request) { // key: cloud1-default-uuid // value: "{"deployment":<>,"service":<>}" - res, err := db.DBconn.Read(storeName, internalVNFID, tagData) + res, err := db.DBconn.Read(storeName, instanceKey{Key: internalVNFID}, tagData) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -237,7 +245,7 @@ func DeleteHandler(w http.ResponseWriter, r *http.Request) { return } - err = db.DBconn.Delete(storeName, internalVNFID, tagData) + err = db.DBconn.Delete(storeName, instanceKey{Key: internalVNFID}, tagData) if err != nil { werr := pkgerrors.Wrap(err, "Delete VNF db record error") http.Error(w, werr.Error(), http.StatusInternalServerError) @@ -330,7 +338,7 @@ func GetHandler(w http.ResponseWriter, r *http.Request) { // key: cloud1-default-uuid // value: "{"deployment":<>,"service":<>}" - res, err := db.DBconn.Read(storeName, internalVNFID, tagData) + res, err := db.DBconn.Read(storeName, instanceKey{Key: internalVNFID}, tagData) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return diff --git a/src/k8splugin/internal/db/consul.go b/src/k8splugin/internal/db/consul.go index a61a4c10..23d2ae88 100644 --- a/src/k8splugin/internal/db/consul.go +++ b/src/k8splugin/internal/db/consul.go @@ -54,7 +54,7 @@ func NewConsulStore(store ConsulKVStore) (Store, error) { // HealthCheck verifies if the database is up and running func (c *ConsulStore) HealthCheck() error { - _, err := c.Read("test", "test", "test") + _, _, err := c.client.Get("test", nil) if err != nil { return pkgerrors.New("[ERROR] Cannot talk to Datastore. Check if it is running/reachable.") } @@ -67,7 +67,13 @@ func (c *ConsulStore) Unmarshal(inp []byte, out interface{}) error { } // Create is used to create a DB entry -func (c *ConsulStore) Create(root, key, tag string, data interface{}) error { +func (c *ConsulStore) Create(root string, key Key, tag string, data interface{}) error { + + //Convert to string as Consul only supports string based keys + k := key.String() + if k == "" { + return pkgerrors.New("Key.String() returned an empty string") + } value, err := Serialize(data) if err != nil { @@ -75,7 +81,7 @@ func (c *ConsulStore) Create(root, key, tag string, data interface{}) error { } p := &api.KVPair{ - Key: key, + Key: k, Value: []byte(value), } _, err = c.client.Put(p, nil) @@ -83,9 +89,16 @@ func (c *ConsulStore) Create(root, key, tag string, data interface{}) error { } // Read method returns the internalID for a particular externalID -func (c *ConsulStore) Read(root, key, tag string) ([]byte, error) { - key = root + "/" + key + "/" + tag - pair, _, err := c.client.Get(key, nil) +func (c *ConsulStore) Read(root string, key Key, tag string) ([]byte, error) { + + //Convert to string as Consul only supports string based keys + k := key.String() + if k == "" { + return nil, pkgerrors.New("Key.String() returned an empty string") + } + + k = root + "/" + k + "/" + tag + pair, _, err := c.client.Get(k, nil) if err != nil { return nil, err } @@ -96,13 +109,19 @@ func (c *ConsulStore) Read(root, key, tag string) ([]byte, error) { } // Delete method removes an internalID from the Database -func (c *ConsulStore) Delete(root, key, tag string) error { - _, err := c.client.Delete(key, nil) +func (c *ConsulStore) Delete(root string, key Key, tag string) error { + + //Convert to string as Consul only supports string based keys + k := key.String() + if k == "" { + return pkgerrors.New("Key.String() returned an empty string") + } + _, err := c.client.Delete(k, nil) return err } // ReadAll is used to get all ExternalIDs in a namespace -func (c *ConsulStore) ReadAll(root, tag string) (map[string][]byte, error) { +func (c *ConsulStore) ReadAll(root string, tag string) (map[string][]byte, error) { pairs, _, err := c.client.List(root, nil) if err != nil { return nil, err diff --git a/src/k8splugin/internal/db/consul_test.go b/src/k8splugin/internal/db/consul_test.go index 754112ad..6d127841 100644 --- a/src/k8splugin/internal/db/consul_test.go +++ b/src/k8splugin/internal/db/consul_test.go @@ -102,19 +102,20 @@ func TestConsulCreate(t *testing.T) { testCases := []struct { label string input map[string]string + key Key mock *mockConsulKVStore expectedError string }{ { label: "Sucessful register a record to Consul Database", - input: map[string]string{"root": "rbinst", "key": "test-key", - "tag": "data", "value": "test-value"}, - mock: &mockConsulKVStore{}, + key: mockKey{Key: "test-key"}, + input: map[string]string{"root": "rbinst", "tag": "data", "value": "test-value"}, + mock: &mockConsulKVStore{}, }, { label: "Fail to create a new record in Consul Database", - input: map[string]string{"root": "rbinst", "key": "test-key", - "tag": "data", "value": "test-value"}, + key: mockKey{Key: "test-key"}, + input: map[string]string{"root": "rbinst", "tag": "data", "value": "test-value"}, mock: &mockConsulKVStore{ Err: pkgerrors.New("DB error"), }, @@ -125,7 +126,7 @@ func TestConsulCreate(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.label, func(t *testing.T) { client, _ := NewConsulStore(testCase.mock) - err := client.Create(testCase.input["root"], testCase.input["key"], + err := client.Create(testCase.input["root"], testCase.key, testCase.input["tag"], testCase.input["value"]) if err != nil { if testCase.expectedError == "" { @@ -143,14 +144,15 @@ func TestConsulRead(t *testing.T) { testCases := []struct { label string input map[string]string + key Key mock *mockConsulKVStore expectedError string expectedResult string }{ { label: "Sucessful retrieve a record from Consul Database", - input: map[string]string{"root": "rbinst", "key": "test", - "tag": "data"}, + key: mockKey{Key: "test"}, + input: map[string]string{"root": "rbinst", "tag": "data"}, mock: &mockConsulKVStore{ Items: api.KVPairs{ &api.KVPair{ @@ -163,14 +165,14 @@ func TestConsulRead(t *testing.T) { }, { label: "Fail retrieve a non-existing record from Consul Database", - input: map[string]string{"root": "rbinst", "key": "test-key", - "tag": "data"}, - mock: &mockConsulKVStore{}, + key: mockKey{Key: "test-key"}, + input: map[string]string{"root": "rbinst", "tag": "data"}, + mock: &mockConsulKVStore{}, }, { label: "Fail retrieve a record from Consul Database", - input: map[string]string{"root": "rbinst", "key": "test-key", - "tag": "data"}, + key: mockKey{Key: "test-key"}, + input: map[string]string{"root": "rbinst", "tag": "data"}, mock: &mockConsulKVStore{ Err: pkgerrors.New("DB error"), }, @@ -181,7 +183,7 @@ func TestConsulRead(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.label, func(t *testing.T) { client, _ := NewConsulStore(testCase.mock) - result, err := client.Read(testCase.input["root"], testCase.input["key"], + result, err := client.Read(testCase.input["root"], testCase.key, testCase.input["tag"]) if err != nil { if testCase.expectedError == "" { @@ -196,7 +198,7 @@ func TestConsulRead(t *testing.T) { } if !reflect.DeepEqual(testCase.expectedResult, string(result)) { - t.Fatalf("Read method returned: \n%v\n and it was expected: \n%v", result, testCase.expectedResult) + t.Fatalf("Read method returned: \n%v\n while expected value was: \n%v", result, testCase.expectedResult) } } }) @@ -207,17 +209,19 @@ func TestConsulDelete(t *testing.T) { testCases := []struct { label string input map[string]string + key Key mock *mockConsulKVStore expectedError string }{ { label: "Sucessful delete a record to Consul Database", - input: map[string]string{"root": "rbinst", "key": "test-key", - "tag": "data"}, - mock: &mockConsulKVStore{}, + key: mockKey{Key: "test-key"}, + input: map[string]string{"root": "rbinst", "tag": "data"}, + mock: &mockConsulKVStore{}, }, { label: "Fail to delete a record in Consul Database", + key: mockKey{Key: "test-key"}, mock: &mockConsulKVStore{ Err: pkgerrors.New("DB error"), }, @@ -228,7 +232,7 @@ func TestConsulDelete(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.label, func(t *testing.T) { client, _ := NewConsulStore(testCase.mock) - err := client.Delete(testCase.input["root"], testCase.input["key"], + err := client.Delete(testCase.input["root"], testCase.key, testCase.input["tag"]) if err != nil { if testCase.expectedError == "" { diff --git a/src/k8splugin/internal/db/mongo.go b/src/k8splugin/internal/db/mongo.go index d414f543..8c422380 100644 --- a/src/k8splugin/internal/db/mongo.go +++ b/src/k8splugin/internal/db/mongo.go @@ -108,10 +108,17 @@ func (m *MongoStore) HealthCheck() error { } // validateParams checks to see if any parameters are empty -func (m *MongoStore) validateParams(args ...string) bool { +func (m *MongoStore) validateParams(args ...interface{}) bool { for _, v := range args { - if v == "" { - return false + val, ok := v.(string) + if ok { + if val == "" { + return false + } + } else { + if v == nil { + return false + } } } @@ -119,7 +126,7 @@ func (m *MongoStore) validateParams(args ...string) bool { } // Create is used to create a DB entry -func (m *MongoStore) Create(coll, key, tag string, data interface{}) error { +func (m *MongoStore) Create(coll string, key Key, tag string, data interface{}) error { if data == nil || !m.validateParams(coll, key, tag) { return pkgerrors.New("No Data to store") } @@ -168,7 +175,7 @@ func (m *MongoStore) Unmarshal(inp []byte, out interface{}) error { } // Read method returns the data stored for this key and for this particular tag -func (m *MongoStore) Read(coll, key, tag string) ([]byte, error) { +func (m *MongoStore) Read(coll string, key Key, tag string) ([]byte, error) { if !m.validateParams(coll, key, tag) { return nil, pkgerrors.New("Mandatory fields are missing") } @@ -223,7 +230,7 @@ func (m *MongoStore) deleteObjectByID(coll string, objID primitive.ObjectID) err // Delete method removes a document from the Database that matches key // TODO: delete all referenced docs if tag is empty string -func (m *MongoStore) Delete(coll, key, tag string) error { +func (m *MongoStore) Delete(coll string, key Key, tag string) error { if !m.validateParams(coll, key, tag) { return pkgerrors.New("Mandatory fields are missing") } @@ -314,10 +321,10 @@ func (m *MongoStore) ReadAll(coll, tag string) (map[string][]byte, error) { d := cursor.Current //Read key of each master table - key, ok := d.Lookup("key").StringValueOK() + key, ok := d.Lookup("key").DocumentOK() if !ok { - log.Printf("Unable to read key string from mastertable %s", err.Error()) - continue + //Throw error if key is not found + pkgerrors.New("Unable to read key from mastertable") } //Get objectID of tag document @@ -333,7 +340,7 @@ func (m *MongoStore) ReadAll(coll, tag string) (map[string][]byte, error) { log.Printf("Unable to decode tag data %s", err.Error()) continue } - result[key] = tagData.Lookup(tag).Value + result[key.String()] = tagData.Lookup(tag).Value } if len(result) == 0 { diff --git a/src/k8splugin/internal/db/mongo_test.go b/src/k8splugin/internal/db/mongo_test.go index 973921c3..deb51044 100644 --- a/src/k8splugin/internal/db/mongo_test.go +++ b/src/k8splugin/internal/db/mongo_test.go @@ -84,7 +84,7 @@ func TestCreate(t *testing.T) { label: "Successfull creation of entry", input: map[string]interface{}{ "coll": "collname", - "key": "keyvalue", + "key": mockKey{Key: "keyvalue"}, "tag": "tagName", "data": "Data In String Format", }, @@ -95,7 +95,7 @@ func TestCreate(t *testing.T) { label: "UnSuccessfull creation of entry", input: map[string]interface{}{ "coll": "collname", - "key": "keyvalue", + "key": mockKey{Key: "keyvalue"}, "tag": "tagName", "data": "Data In String Format", }, @@ -108,7 +108,7 @@ func TestCreate(t *testing.T) { label: "Missing input fields", input: map[string]interface{}{ "coll": "", - "key": "", + "key": mockKey{Key: ""}, "tag": "", "data": "", }, @@ -129,7 +129,7 @@ func TestCreate(t *testing.T) { return testCase.bson, testCase.mockColl.Err } - err := m.Create(testCase.input["coll"].(string), testCase.input["key"].(string), + err := m.Create(testCase.input["coll"].(string), testCase.input["key"].(Key), testCase.input["tag"].(string), testCase.input["data"]) if err != nil { if testCase.expectedError == "" { @@ -156,59 +156,57 @@ func TestRead(t *testing.T) { label: "Successfull Read of entry", input: map[string]interface{}{ "coll": "collname", - "key": "keyvalue", + "key": mockKey{Key: "keyvalue"}, "tag": "metadata", }, // Binary form of // { // "_id" : ObjectId("5c115156777ff85654248ae1"), - // "key" : "b82c4bb1-09ff-6093-4d58-8327b94e1e20", + // "key" : bson.D{{"name","testdef"},{"version","v1"}}, // "metadata" : ObjectId("5c115156c9755047e318bbfd") // } bson: bson.Raw{ - '\x5a', '\x00', '\x00', '\x00', '\x07', '\x5f', '\x69', '\x64', - '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', '\xf8', - '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x02', '\x6b', '\x65', - '\x79', '\x00', '\x25', '\x00', '\x00', '\x00', '\x62', '\x38', - '\x32', '\x63', '\x34', '\x62', '\x62', '\x31', '\x2d', '\x30', - '\x39', '\x66', '\x66', '\x2d', '\x36', '\x30', '\x39', '\x33', - '\x2d', '\x34', '\x64', '\x35', '\x38', '\x2d', '\x38', '\x33', - '\x32', '\x37', '\x62', '\x39', '\x34', '\x65', '\x31', '\x65', - '\x32', '\x30', '\x00', '\x07', '\x6d', '\x65', '\x74', '\x61', - '\x64', '\x61', '\x74', '\x61', '\x00', '\x5c', '\x11', '\x51', - '\x56', '\xc9', '\x75', '\x50', '\x47', '\xe3', '\x18', '\xbb', - '\xfd', '\x00', + '\x58', '\x00', '\x00', '\x00', '\x03', '\x6b', '\x65', '\x79', + '\x00', '\x27', '\x00', '\x00', '\x00', '\x02', '\x6e', '\x61', + '\x6d', '\x65', '\x00', '\x08', '\x00', '\x00', '\x00', '\x74', + '\x65', '\x73', '\x74', '\x64', '\x65', '\x66', '\x00', '\x02', + '\x76', '\x65', '\x72', '\x73', '\x69', '\x6f', '\x6e', '\x00', + '\x03', '\x00', '\x00', '\x00', '\x76', '\x31', '\x00', '\x00', + '\x07', '\x6d', '\x65', '\x74', '\x61', '\x64', '\x61', '\x74', + '\x61', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', + '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x07', '\x5f', + '\x69', '\x64', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', + '\x7f', '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x00', }, mockColl: &mockCollection{}, // This is not the document because we are mocking decodeBytes - expected: []byte{92, 17, 81, 86, 201, 117, 80, 71, 227, 24, 187, 253}, + expected: []byte{92, 17, 81, 86, 119, 127, 248, 86, 84, 36, 138, 225}, }, { label: "UnSuccessfull Read of entry: object not found", input: map[string]interface{}{ "coll": "collname", - "key": "keyvalue", + "key": mockKey{Key: "keyvalue"}, "tag": "badtag", }, // Binary form of // { // "_id" : ObjectId("5c115156777ff85654248ae1"), - // "key" : "b82c4bb1-09ff-6093-4d58-8327b94e1e20", + // "key" : bson.D{{"name","testdef"},{"version","v1"}}, // "metadata" : ObjectId("5c115156c9755047e318bbfd") // } bson: bson.Raw{ - '\x5a', '\x00', '\x00', '\x00', '\x07', '\x5f', '\x69', '\x64', - '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', '\xf8', - '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x02', '\x6b', '\x65', - '\x79', '\x00', '\x25', '\x00', '\x00', '\x00', '\x62', '\x38', - '\x32', '\x63', '\x34', '\x62', '\x62', '\x31', '\x2d', '\x30', - '\x39', '\x66', '\x66', '\x2d', '\x36', '\x30', '\x39', '\x33', - '\x2d', '\x34', '\x64', '\x35', '\x38', '\x2d', '\x38', '\x33', - '\x32', '\x37', '\x62', '\x39', '\x34', '\x65', '\x31', '\x65', - '\x32', '\x30', '\x00', '\x07', '\x6d', '\x65', '\x74', '\x61', - '\x64', '\x61', '\x74', '\x61', '\x00', '\x5c', '\x11', '\x51', - '\x56', '\xc9', '\x75', '\x50', '\x47', '\xe3', '\x18', '\xbb', - '\xfd', '\x00', + '\x58', '\x00', '\x00', '\x00', '\x03', '\x6b', '\x65', '\x79', + '\x00', '\x27', '\x00', '\x00', '\x00', '\x02', '\x6e', '\x61', + '\x6d', '\x65', '\x00', '\x08', '\x00', '\x00', '\x00', '\x74', + '\x65', '\x73', '\x74', '\x64', '\x65', '\x66', '\x00', '\x02', + '\x76', '\x65', '\x72', '\x73', '\x69', '\x6f', '\x6e', '\x00', + '\x03', '\x00', '\x00', '\x00', '\x76', '\x31', '\x00', '\x00', + '\x07', '\x6d', '\x65', '\x74', '\x61', '\x64', '\x61', '\x74', + '\x61', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', + '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x07', '\x5f', + '\x69', '\x64', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', + '\x7f', '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x00', }, mockColl: &mockCollection{}, expectedError: "Error finding objectID", @@ -217,7 +215,7 @@ func TestRead(t *testing.T) { label: "UnSuccessfull Read of entry", input: map[string]interface{}{ "coll": "collname", - "key": "keyvalue", + "key": mockKey{Key: "keyvalue"}, "tag": "tagName", }, mockColl: &mockCollection{ @@ -229,7 +227,7 @@ func TestRead(t *testing.T) { label: "Missing input fields", input: map[string]interface{}{ "coll": "", - "key": "", + "key": mockKey{Key: ""}, "tag": "", }, expectedError: "Mandatory fields are missing", @@ -248,7 +246,7 @@ func TestRead(t *testing.T) { decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) { return testCase.bson, testCase.mockColl.Err } - got, err := m.Read(testCase.input["coll"].(string), testCase.input["key"].(string), + got, err := m.Read(testCase.input["coll"].(string), testCase.input["key"].(Key), testCase.input["tag"].(string)) if err != nil { if testCase.expectedError == "" { @@ -259,7 +257,7 @@ func TestRead(t *testing.T) { } } else { if bytes.Compare(got, testCase.expected) != 0 { - t.Fatalf("Read returned unexpected data: %s, expected: %s", + t.Fatalf("Read returned unexpected data: %v, expected: %v", string(got), testCase.expected) } } @@ -279,28 +277,27 @@ func TestDelete(t *testing.T) { label: "Successfull Delete of entry", input: map[string]interface{}{ "coll": "collname", - "key": "keyvalue", + "key": mockKey{Key: "keyvalue"}, "tag": "metadata", }, // Binary form of // { // "_id" : ObjectId("5c115156777ff85654248ae1"), - // "key" : "b82c4bb1-09ff-6093-4d58-8327b94e1e20", + // "key" : bson.D{{"name","testdef"},{"version","v1"}}, // "metadata" : ObjectId("5c115156c9755047e318bbfd") // } bson: bson.Raw{ - '\x5a', '\x00', '\x00', '\x00', '\x07', '\x5f', '\x69', '\x64', - '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', '\xf8', - '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x02', '\x6b', '\x65', - '\x79', '\x00', '\x25', '\x00', '\x00', '\x00', '\x62', '\x38', - '\x32', '\x63', '\x34', '\x62', '\x62', '\x31', '\x2d', '\x30', - '\x39', '\x66', '\x66', '\x2d', '\x36', '\x30', '\x39', '\x33', - '\x2d', '\x34', '\x64', '\x35', '\x38', '\x2d', '\x38', '\x33', - '\x32', '\x37', '\x62', '\x39', '\x34', '\x65', '\x31', '\x65', - '\x32', '\x30', '\x00', '\x07', '\x6d', '\x65', '\x74', '\x61', - '\x64', '\x61', '\x74', '\x61', '\x00', '\x5c', '\x11', '\x51', - '\x56', '\xc9', '\x75', '\x50', '\x47', '\xe3', '\x18', '\xbb', - '\xfd', '\x00', + '\x58', '\x00', '\x00', '\x00', '\x03', '\x6b', '\x65', '\x79', + '\x00', '\x27', '\x00', '\x00', '\x00', '\x02', '\x6e', '\x61', + '\x6d', '\x65', '\x00', '\x08', '\x00', '\x00', '\x00', '\x74', + '\x65', '\x73', '\x74', '\x64', '\x65', '\x66', '\x00', '\x02', + '\x76', '\x65', '\x72', '\x73', '\x69', '\x6f', '\x6e', '\x00', + '\x03', '\x00', '\x00', '\x00', '\x76', '\x31', '\x00', '\x00', + '\x07', '\x6d', '\x65', '\x74', '\x61', '\x64', '\x61', '\x74', + '\x61', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', + '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x07', '\x5f', + '\x69', '\x64', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', + '\x7f', '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x00', }, mockColl: &mockCollection{}, }, @@ -308,7 +305,7 @@ func TestDelete(t *testing.T) { label: "UnSuccessfull Delete of entry", input: map[string]interface{}{ "coll": "collname", - "key": "keyvalue", + "key": mockKey{Key: "keyvalue"}, "tag": "tagName", }, mockColl: &mockCollection{ @@ -320,22 +317,27 @@ func TestDelete(t *testing.T) { label: "UnSuccessfull Delete, key not found", input: map[string]interface{}{ "coll": "collname", - "key": "keyvalue", + "key": mockKey{Key: "keyvalue"}, "tag": "tagName", }, + // Binary form of + // { + // "_id" : ObjectId("5c115156777ff85654248ae1"), + // "key" : bson.D{{"name","testdef"},{"version","v1"}}, + // "metadata" : ObjectId("5c115156c9755047e318bbfd") + // } bson: bson.Raw{ - '\x5a', '\x00', '\x00', '\x00', '\x07', '\x5f', '\x69', '\x64', - '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', '\xf8', - '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x02', '\x6b', '\x65', - '\x79', '\x00', '\x25', '\x00', '\x00', '\x00', '\x62', '\x38', - '\x32', '\x63', '\x34', '\x62', '\x62', '\x31', '\x2d', '\x30', - '\x39', '\x66', '\x66', '\x2d', '\x36', '\x30', '\x39', '\x33', - '\x2d', '\x34', '\x64', '\x35', '\x38', '\x2d', '\x38', '\x33', - '\x32', '\x37', '\x62', '\x39', '\x34', '\x65', '\x31', '\x65', - '\x32', '\x30', '\x00', '\x07', '\x6d', '\x65', '\x74', '\x61', - '\x64', '\x61', '\x74', '\x61', '\x00', '\x5c', '\x11', '\x51', - '\x56', '\xc9', '\x75', '\x50', '\x47', '\xe3', '\x18', '\xbb', - '\xfd', '\x00', + '\x58', '\x00', '\x00', '\x00', '\x03', '\x6b', '\x65', '\x79', + '\x00', '\x27', '\x00', '\x00', '\x00', '\x02', '\x6e', '\x61', + '\x6d', '\x65', '\x00', '\x08', '\x00', '\x00', '\x00', '\x74', + '\x65', '\x73', '\x74', '\x64', '\x65', '\x66', '\x00', '\x02', + '\x76', '\x65', '\x72', '\x73', '\x69', '\x6f', '\x6e', '\x00', + '\x03', '\x00', '\x00', '\x00', '\x76', '\x31', '\x00', '\x00', + '\x07', '\x6d', '\x65', '\x74', '\x61', '\x64', '\x61', '\x74', + '\x61', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', + '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x07', '\x5f', + '\x69', '\x64', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', + '\x7f', '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x00', }, mockColl: &mockCollection{}, expectedError: "Error finding objectID", @@ -344,7 +346,7 @@ func TestDelete(t *testing.T) { label: "Missing input fields", input: map[string]interface{}{ "coll": "", - "key": "", + "key": mockKey{Key: ""}, "tag": "", }, expectedError: "Mandatory fields are missing", @@ -363,7 +365,7 @@ func TestDelete(t *testing.T) { decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) { return testCase.bson, testCase.mockColl.Err } - err := m.Delete(testCase.input["coll"].(string), testCase.input["key"].(string), + err := m.Delete(testCase.input["coll"].(string), testCase.input["key"].(Key), testCase.input["tag"].(string)) if err != nil { if testCase.expectedError == "" { @@ -397,29 +399,29 @@ func TestReadAll(t *testing.T) { // Binary form of // { // "_id" : ObjectId("5c115156777ff85654248ae1"), - // "key" : "b82c4bb1-09ff-6093-4d58-8327b94e1e20", + // "key" : bson.D{{"name","testdef"},{"version","v1"}}, // "metadata" : ObjectId("5c115156c9755047e318bbfd") // } + Current: bson.Raw{ - '\x5a', '\x00', '\x00', '\x00', '\x07', '\x5f', '\x69', '\x64', - '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', '\xf8', - '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x02', '\x6b', '\x65', - '\x79', '\x00', '\x25', '\x00', '\x00', '\x00', '\x62', '\x38', - '\x32', '\x63', '\x34', '\x62', '\x62', '\x31', '\x2d', '\x30', - '\x39', '\x66', '\x66', '\x2d', '\x36', '\x30', '\x39', '\x33', - '\x2d', '\x34', '\x64', '\x35', '\x38', '\x2d', '\x38', '\x33', - '\x32', '\x37', '\x62', '\x39', '\x34', '\x65', '\x31', '\x65', - '\x32', '\x30', '\x00', '\x07', '\x6d', '\x65', '\x74', '\x61', - '\x64', '\x61', '\x74', '\x61', '\x00', '\x5c', '\x11', '\x51', - '\x56', '\xc9', '\x75', '\x50', '\x47', '\xe3', '\x18', '\xbb', - '\xfd', '\x00', + '\x58', '\x00', '\x00', '\x00', '\x03', '\x6b', '\x65', '\x79', + '\x00', '\x27', '\x00', '\x00', '\x00', '\x02', '\x6e', '\x61', + '\x6d', '\x65', '\x00', '\x08', '\x00', '\x00', '\x00', '\x74', + '\x65', '\x73', '\x74', '\x64', '\x65', '\x66', '\x00', '\x02', + '\x76', '\x65', '\x72', '\x73', '\x69', '\x6f', '\x6e', '\x00', + '\x03', '\x00', '\x00', '\x00', '\x76', '\x31', '\x00', '\x00', + '\x07', '\x6d', '\x65', '\x74', '\x61', '\x64', '\x61', '\x74', + '\x61', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', + '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x07', '\x5f', + '\x69', '\x64', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', + '\x7f', '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x00', }, }, mCursorCount: 1, }, expected: map[string][]byte{ - "b82c4bb1-09ff-6093-4d58-8327b94e1e20": []byte{ - 92, 17, 81, 86, 201, 117, 80, 71, 227, 24, 187, 253}, + `{"name": "testdef","version": "v1"}`: []byte{ + 92, 17, 81, 86, 119, 127, 248, 86, 84, 36, 138, 225}, }, }, { @@ -444,22 +446,21 @@ func TestReadAll(t *testing.T) { // Binary form of // { // "_id" : ObjectId("5c115156777ff85654248ae1"), - // "key" : "b82c4bb1-09ff-6093-4d58-8327b94e1e20", + // "key" : bson.D{{"name","testdef"},{"version","v1"}}, // "metadata" : ObjectId("5c115156c9755047e318bbfd") // } Current: bson.Raw{ - '\x5a', '\x00', '\x00', '\x00', '\x07', '\x5f', '\x69', '\x64', - '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', '\xf8', - '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x02', '\x6b', '\x65', - '\x79', '\x00', '\x25', '\x00', '\x00', '\x00', '\x62', '\x38', - '\x32', '\x63', '\x34', '\x62', '\x62', '\x31', '\x2d', '\x30', - '\x39', '\x66', '\x66', '\x2d', '\x36', '\x30', '\x39', '\x33', - '\x2d', '\x34', '\x64', '\x35', '\x38', '\x2d', '\x38', '\x33', - '\x32', '\x37', '\x62', '\x39', '\x34', '\x65', '\x31', '\x65', - '\x32', '\x30', '\x00', '\x07', '\x6d', '\x65', '\x74', '\x61', - '\x64', '\x61', '\x74', '\x61', '\x00', '\x5c', '\x11', '\x51', - '\x56', '\xc9', '\x75', '\x50', '\x47', '\xe3', '\x18', '\xbb', - '\xfd', '\x00', + '\x58', '\x00', '\x00', '\x00', '\x03', '\x6b', '\x65', '\x79', + '\x00', '\x27', '\x00', '\x00', '\x00', '\x02', '\x6e', '\x61', + '\x6d', '\x65', '\x00', '\x08', '\x00', '\x00', '\x00', '\x74', + '\x65', '\x73', '\x74', '\x64', '\x65', '\x66', '\x00', '\x02', + '\x76', '\x65', '\x72', '\x73', '\x69', '\x6f', '\x6e', '\x00', + '\x03', '\x00', '\x00', '\x00', '\x76', '\x31', '\x00', '\x00', + '\x07', '\x6d', '\x65', '\x74', '\x61', '\x64', '\x61', '\x74', + '\x61', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', + '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x07', '\x5f', + '\x69', '\x64', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', + '\x7f', '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x00', }, }, mCursorCount: 1, diff --git a/src/k8splugin/internal/db/store.go b/src/k8splugin/internal/db/store.go index a235597a..148e078e 100644 --- a/src/k8splugin/internal/db/store.go +++ b/src/k8splugin/internal/db/store.go @@ -23,6 +23,13 @@ import ( // DBconn interface used to talk a concrete Database connection var DBconn Store +// Key is an interface that will be implemented by anypackage +// that wants to use the Store interface. This allows various +// db backends and key types. +type Key interface { + String() string +} + // Store is an interface for accessing a database type Store interface { // Returns nil if db health is good @@ -33,19 +40,19 @@ type Store interface { // Creates a new master table with key and links data with tag and // creates a pointer to the newly added data in the master table - Create(table, key, tag string, data interface{}) error + Create(table string, key Key, tag string, data interface{}) error // Reads data for a particular key with specific tag. - Read(table, key, tag string) ([]byte, error) + Read(table string, key Key, tag string) ([]byte, error) //TODO: Update(context.Context, string, interface{}) error // Deletes a specific tag data for key. // TODO: If tag is empty, it will delete all tags under key. - Delete(table, key, tag string) error + Delete(table string, key Key, tag string) error // Reads all master tables and data from the specified tag in table - ReadAll(table, tag string) (map[string][]byte, error) + ReadAll(table string, tag string) (map[string][]byte, error) } // CreateDBClient creates the DB client diff --git a/src/k8splugin/internal/db/testing.go b/src/k8splugin/internal/db/testing.go index a6c940ef..a411790e 100644 --- a/src/k8splugin/internal/db/testing.go +++ b/src/k8splugin/internal/db/testing.go @@ -20,6 +20,14 @@ import ( pkgerrors "github.com/pkg/errors" ) +type mockKey struct { + Key string +} + +func (m mockKey) String() string { + return m.Key +} + //Creating an embedded interface via anonymous variable //This allows us to make mockDB satisfy the DatabaseConnection //interface even if we are not implementing all the methods in it @@ -29,7 +37,7 @@ type MockDB struct { Err error } -func (m *MockDB) Create(table, key, tag string, data interface{}) error { +func (m *MockDB) Create(table string, key Key, tag string, data interface{}) error { return m.Err } @@ -42,13 +50,13 @@ func (m *MockDB) Unmarshal(inp []byte, out interface{}) error { return nil } -func (m *MockDB) Read(table, key, tag string) ([]byte, error) { +func (m *MockDB) Read(table string, key Key, tag string) ([]byte, error) { if m.Err != nil { return nil, m.Err } for k, v := range m.Items { - if k == key { + if k == key.String() { return v[tag], nil } } @@ -56,11 +64,11 @@ func (m *MockDB) Read(table, key, tag string) ([]byte, error) { return nil, m.Err } -func (m *MockDB) Delete(table, key, tag string) error { +func (m *MockDB) Delete(table string, key Key, tag string) error { return m.Err } -func (m *MockDB) ReadAll(table, tag string) (map[string][]byte, error) { +func (m *MockDB) ReadAll(table string, tag string) (map[string][]byte, error) { if m.Err != nil { return nil, m.Err } diff --git a/src/k8splugin/internal/rb/definition.go b/src/k8splugin/internal/rb/definition.go index 4eaa9578..2ebbb08a 100644 --- a/src/k8splugin/internal/rb/definition.go +++ b/src/k8splugin/internal/rb/definition.go @@ -49,6 +49,14 @@ type DefinitionManager interface { Upload(resID string, inp []byte) error } +type definitionKey struct { + Key string +} + +func (dk definitionKey) String() string { + return dk.Key +} + // DefinitionClient implements the DefinitionManager // It will also be used to maintain some localized state type DefinitionClient struct { @@ -73,7 +81,7 @@ func (v *DefinitionClient) Create(def Definition) (Definition, error) { if def.UUID == "" { def.UUID, _ = uuid.GenerateUUID() } - key := def.UUID + key := definitionKey{Key: def.UUID} err := db.DBconn.Create(v.storeName, key, v.tagMeta, def) if err != nil { @@ -109,7 +117,8 @@ func (v *DefinitionClient) List() ([]Definition, error) { // Get returns the Resource Bundle Definition for corresponding ID func (v *DefinitionClient) Get(id string) (Definition, error) { - value, err := db.DBconn.Read(v.storeName, id, v.tagMeta) + key := definitionKey{Key: id} + value, err := db.DBconn.Read(v.storeName, key, v.tagMeta) if err != nil { return Definition{}, pkgerrors.Wrap(err, "Get Resource Bundle definition") } @@ -129,13 +138,14 @@ func (v *DefinitionClient) Get(id string) (Definition, error) { // Delete the Resource Bundle definition from database func (v *DefinitionClient) Delete(id string) error { - err := db.DBconn.Delete(v.storeName, id, v.tagMeta) + key := definitionKey{Key: id} + err := db.DBconn.Delete(v.storeName, key, v.tagMeta) if err != nil { return pkgerrors.Wrap(err, "Delete Resource Bundle Definition") } //Delete the content when the delete operation happens - err = db.DBconn.Delete(v.storeName, id, v.tagContent) + err = db.DBconn.Delete(v.storeName, key, v.tagContent) if err != nil { return pkgerrors.Wrap(err, "Delete Resource Bundle Definition Content") } @@ -146,6 +156,7 @@ func (v *DefinitionClient) Delete(id string) error { // Upload the contents of resource bundle into database func (v *DefinitionClient) Upload(id string, inp []byte) error { + key := definitionKey{Key: id} //Check if definition metadata exists def, err := v.Get(id) if err != nil { @@ -192,7 +203,7 @@ func (v *DefinitionClient) Upload(id string, inp []byte) error { //Encode given byte stream to text for storage encodedStr := base64.StdEncoding.EncodeToString(inp) - err = db.DBconn.Create(v.storeName, id, v.tagContent, encodedStr) + err = db.DBconn.Create(v.storeName, key, v.tagContent, encodedStr) if err != nil { return pkgerrors.Errorf("Error uploading data to db: %s", err.Error()) } @@ -205,6 +216,7 @@ func (v *DefinitionClient) Upload(id string, inp []byte) error { // ExtractTarBall code to create the folder structure on disk func (v *DefinitionClient) Download(id string) ([]byte, error) { + key := definitionKey{Key: id} //ignore the returned data here //Check if id is valid _, err := v.Get(id) @@ -212,7 +224,7 @@ func (v *DefinitionClient) Download(id string) ([]byte, error) { return nil, pkgerrors.Errorf("Invalid Definition ID provided: %s", err.Error()) } - value, err := db.DBconn.Read(v.storeName, id, v.tagContent) + value, err := db.DBconn.Read(v.storeName, key, v.tagContent) if err != nil { return nil, pkgerrors.Wrap(err, "Get Resource Bundle definition content") } diff --git a/src/k8splugin/internal/rb/profile.go b/src/k8splugin/internal/rb/profile.go index 086c3486..006fa913 100644 --- a/src/k8splugin/internal/rb/profile.go +++ b/src/k8splugin/internal/rb/profile.go @@ -49,6 +49,14 @@ type ProfileManager interface { Upload(resID string, inp []byte) error } +type profileKey struct { + Key string +} + +func (dk profileKey) String() string { + return dk.Key +} + // ProfileClient implements the ProfileManager // It will also be used to maintain some localized state type ProfileClient struct { @@ -95,7 +103,7 @@ func (v *ProfileClient) Create(p Profile) (Profile, error) { if p.UUID == "" { p.UUID, _ = uuid.GenerateUUID() } - key := p.UUID + key := profileKey{Key: p.UUID} err = db.DBconn.Create(v.storeName, key, v.tagMeta, p) if err != nil { @@ -132,7 +140,8 @@ func (v *ProfileClient) List() ([]Profile, error) { // Get returns the Resource Bundle Profile for corresponding ID func (v *ProfileClient) Get(id string) (Profile, error) { - value, err := db.DBconn.Read(v.storeName, id, v.tagMeta) + key := profileKey{Key: id} + value, err := db.DBconn.Read(v.storeName, key, v.tagMeta) if err != nil { return Profile{}, pkgerrors.Wrap(err, "Get Resource Bundle Profile") } @@ -152,12 +161,13 @@ func (v *ProfileClient) Get(id string) (Profile, error) { // Delete the Resource Bundle Profile from database func (v *ProfileClient) Delete(id string) error { - err := db.DBconn.Delete(v.storeName, id, v.tagMeta) + key := profileKey{Key: id} + err := db.DBconn.Delete(v.storeName, key, v.tagMeta) if err != nil { return pkgerrors.Wrap(err, "Delete Resource Bundle Profile") } - err = db.DBconn.Delete(v.storeName, id, v.tagContent) + err = db.DBconn.Delete(v.storeName, key, v.tagContent) if err != nil { return pkgerrors.Wrap(err, "Delete Resource Bundle Profile Content") } @@ -168,6 +178,7 @@ func (v *ProfileClient) Delete(id string) error { // Upload the contents of resource bundle into database func (v *ProfileClient) Upload(id string, inp []byte) error { + key := profileKey{Key: id} //ignore the returned data here. _, err := v.Get(id) if err != nil { @@ -181,7 +192,7 @@ func (v *ProfileClient) Upload(id string, inp []byte) error { //Encode given byte stream to text for storage encodedStr := base64.StdEncoding.EncodeToString(inp) - err = db.DBconn.Create(v.storeName, id, v.tagContent, encodedStr) + err = db.DBconn.Create(v.storeName, key, v.tagContent, encodedStr) if err != nil { return pkgerrors.Errorf("Error uploading data to db %s", err.Error()) } @@ -194,6 +205,7 @@ func (v *ProfileClient) Upload(id string, inp []byte) error { // ExtractTarBall code to create the folder structure on disk func (v *ProfileClient) Download(id string) ([]byte, error) { + key := profileKey{Key: id} //ignore the returned data here //Check if id is valid _, err := v.Get(id) @@ -201,7 +213,7 @@ func (v *ProfileClient) Download(id string) ([]byte, error) { return nil, pkgerrors.Errorf("Invalid Profile ID provided: %s", err.Error()) } - value, err := db.DBconn.Read(v.storeName, id, v.tagContent) + value, err := db.DBconn.Read(v.storeName, key, v.tagContent) if err != nil { return nil, pkgerrors.Wrap(err, "Get Resource Bundle Profile content") } -- cgit 1.2.3-korg