Add support for composite keys 09/82509/9
authorKiran Kamineni <kiran.k.kamineni@intel.com>
Fri, 15 Mar 2019 22:03:01 +0000 (15:03 -0700)
committerKiran Kamineni <kiran.k.kamineni@intel.com>
Mon, 25 Mar 2019 21:41:34 +0000 (14:41 -0700)
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 <kiran.k.kamineni@intel.com>
src/k8splugin/api/handler.go
src/k8splugin/internal/db/consul.go
src/k8splugin/internal/db/consul_test.go
src/k8splugin/internal/db/mongo.go
src/k8splugin/internal/db/mongo_test.go
src/k8splugin/internal/db/store.go
src/k8splugin/internal/db/testing.go
src/k8splugin/internal/rb/definition.go
src/k8splugin/internal/rb/profile.go

index 4d6abe2..b1cc670 100644 (file)
@@ -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
index a61a4c1..23d2ae8 100644 (file)
@@ -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
index 754112a..6d12784 100644 (file)
@@ -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 == "" {
index d414f54..8c42238 100644 (file)
@@ -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 {
index 973921c..deb5104 100644 (file)
@@ -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,
index a235597..148e078 100644 (file)
@@ -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
index a6c940e..a411790 100644 (file)
@@ -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
        }
index 4eaa957..2ebbb08 100644 (file)
@@ -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")
        }
index 086c348..006fa91 100644 (file)
@@ -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")
        }