aboutsummaryrefslogtreecommitdiffstats
path: root/src/k8splugin
diff options
context:
space:
mode:
authorVictor Morales <victor.morales@intel.com>2018-10-23 11:54:58 -0700
committerVictor Morales <victor.morales@intel.com>2018-11-13 16:20:57 -0800
commitcc05d4af8f082d8174bde5c43fc45b1acc61339f (patch)
treee5614b80fd1f4762c195eb26f639f7963eb2bb5f /src/k8splugin
parent7d2d48d3d0b35de0acd03c6e8a1261efd736edc3 (diff)
Create UTs to cover DB calls
This change pretends to increase the code coverage creating Unit Tests for the interactions with the Databases. Change-Id: I3b78ebe8ddb131e3c06bcee0065ad5eabeed5677 Signed-off-by: Victor Morales <victor.morales@intel.com> Issue-ID: MULTICLOUD-301
Diffstat (limited to 'src/k8splugin')
-rw-r--r--src/k8splugin/api/api.go7
-rw-r--r--src/k8splugin/api/handler.go39
-rw-r--r--src/k8splugin/api/handler_test.go577
-rw-r--r--src/k8splugin/db/consul.go118
-rw-r--r--src/k8splugin/db/consul_test.go298
-rw-r--r--src/k8splugin/db/db_test.go99
-rw-r--r--src/k8splugin/db/store.go (renamed from src/k8splugin/db/DB.go)27
-rw-r--r--src/k8splugin/db/store_test.go123
-rw-r--r--src/k8splugin/db/testing.go65
-rw-r--r--src/k8splugin/vnfd/vnfd.go12
-rw-r--r--src/k8splugin/vnfd/vnfd_test.go71
11 files changed, 980 insertions, 456 deletions
diff --git a/src/k8splugin/api/api.go b/src/k8splugin/api/api.go
index e90c994e..f05fbb0b 100644
--- a/src/k8splugin/api/api.go
+++ b/src/k8splugin/api/api.go
@@ -47,12 +47,7 @@ func CheckDatabaseConnection() error {
return pkgerrors.Cause(err)
}
- err = db.DBconn.InitializeDatabase()
- if err != nil {
- return pkgerrors.Cause(err)
- }
-
- err = db.DBconn.CheckDatabase()
+ err = db.DBconn.HealthCheck()
if err != nil {
return pkgerrors.Cause(err)
}
diff --git a/src/k8splugin/api/handler.go b/src/k8splugin/api/handler.go
index b4828b76..53fa2317 100644
--- a/src/k8splugin/api/handler.go
+++ b/src/k8splugin/api/handler.go
@@ -127,7 +127,7 @@ func CreateHandler(w http.ResponseWriter, r *http.Request) {
// key: cloud1-default-uuid
// value: "{"deployment":<>,"service":<>}"
- err = db.DBconn.CreateEntry(internalVNFID, serializedResourceNameMap)
+ err = db.DBconn.Create(internalVNFID, serializedResourceNameMap)
if err != nil {
werr := pkgerrors.Wrap(err, "Create VNF deployment DB error")
http.Error(w, werr.Error(), http.StatusInternalServerError)
@@ -156,8 +156,8 @@ func ListHandler(w http.ResponseWriter, r *http.Request) {
internalVNFIDs, err := db.DBconn.ReadAll(prefix)
if err != nil {
- werr := pkgerrors.Wrap(err, "Get VNF list error")
- http.Error(w, werr.Error(), http.StatusInternalServerError)
+ http.Error(w, pkgerrors.Wrap(err, "Get VNF list error").Error(),
+ http.StatusInternalServerError)
return
}
@@ -202,24 +202,15 @@ func DeleteHandler(w http.ResponseWriter, r *http.Request) {
// cloud1-default-uuid
internalVNFID := cloudRegionID + "-" + namespace + "-" + externalVNFID
- // (TODO): Read kubeconfig for specific Cloud Region from local file system
- // if present or download it from AAI
- // err := DownloadKubeConfigFromAAI(resource.CloudRegionID, os.Getenv("KUBE_CONFIG_DIR")
- kubeclient, err := GetVNFClient(os.Getenv("KUBE_CONFIG_DIR") + "/" + cloudRegionID)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
// key: cloud1-default-uuid
// value: "{"deployment":<>,"service":<>}"
- serializedResourceNameMap, found, err := db.DBconn.ReadEntry(internalVNFID)
+ serializedResourceNameMap, err := db.DBconn.Read(internalVNFID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- if found == false {
+ if serializedResourceNameMap == "" {
w.WriteHeader(http.StatusNotFound)
return
}
@@ -233,11 +224,19 @@ func DeleteHandler(w http.ResponseWriter, r *http.Request) {
deserializedResourceNameMap := make(map[string][]string)
err = db.DeSerialize(serializedResourceNameMap, &deserializedResourceNameMap)
if err != nil {
- werr := pkgerrors.Wrap(err, "Delete VNF error")
+ werr := pkgerrors.Wrap(err, "Unmarshal VNF error")
http.Error(w, werr.Error(), http.StatusInternalServerError)
return
}
+ // (TODO): Read kubeconfig for specific Cloud Region from local file system
+ // if present or download it from AAI
+ // err := DownloadKubeConfigFromAAI(resource.CloudRegionID, os.Getenv("KUBE_CONFIG_DIR")
+ kubeclient, err := GetVNFClient(os.Getenv("KUBE_CONFIG_DIR") + "/" + cloudRegionID)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
err = csar.DestroyVNF(deserializedResourceNameMap, namespace, &kubeclient)
if err != nil {
werr := pkgerrors.Wrap(err, "Delete VNF error")
@@ -245,9 +244,9 @@ func DeleteHandler(w http.ResponseWriter, r *http.Request) {
return
}
- err = db.DBconn.DeleteEntry(internalVNFID)
+ err = db.DBconn.Delete(internalVNFID)
if err != nil {
- werr := pkgerrors.Wrap(err, "Delete VNF error")
+ werr := pkgerrors.Wrap(err, "Delete VNF db record error")
http.Error(w, werr.Error(), http.StatusInternalServerError)
return
}
@@ -338,13 +337,13 @@ func GetHandler(w http.ResponseWriter, r *http.Request) {
// key: cloud1-default-uuid
// value: "{"deployment":<>,"service":<>}"
- serializedResourceNameMap, found, err := db.DBconn.ReadEntry(internalVNFID)
+ serializedResourceNameMap, err := db.DBconn.Read(internalVNFID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- if found == false {
+ if serializedResourceNameMap == "" {
w.WriteHeader(http.StatusNotFound)
return
}
@@ -358,7 +357,7 @@ func GetHandler(w http.ResponseWriter, r *http.Request) {
deserializedResourceNameMap := make(map[string][]string)
err = db.DeSerialize(serializedResourceNameMap, &deserializedResourceNameMap)
if err != nil {
- werr := pkgerrors.Wrap(err, "Get VNF error")
+ werr := pkgerrors.Wrap(err, "Unmarshal VNF error")
http.Error(w, werr.Error(), http.StatusInternalServerError)
return
}
diff --git a/src/k8splugin/api/handler_test.go b/src/k8splugin/api/handler_test.go
index ac97d011..3336bbc2 100644
--- a/src/k8splugin/api/handler_test.go
+++ b/src/k8splugin/api/handler_test.go
@@ -18,48 +18,34 @@ package api
import (
"bytes"
"encoding/json"
+ "io"
"net/http"
"net/http/httptest"
"reflect"
"testing"
+ "github.com/hashicorp/consul/api"
+ pkgerrors "github.com/pkg/errors"
"k8s.io/client-go/kubernetes"
"k8splugin/csar"
"k8splugin/db"
)
-//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
-type mockDB struct {
- db.DatabaseConnection
+type mockCSAR struct {
+ externalVNFID string
+ resourceYAMLNameMap map[string][]string
+ err error
}
-func (c *mockDB) InitializeDatabase() error {
- return nil
+func (c *mockCSAR) CreateVNF(id, r, n string,
+ kubeclient *kubernetes.Clientset) (string, map[string][]string, error) {
+ return c.externalVNFID, c.resourceYAMLNameMap, c.err
}
-func (c *mockDB) CheckDatabase() error {
- return nil
-}
-
-func (c *mockDB) CreateEntry(key string, value string) error {
- return nil
-}
-
-func (c *mockDB) ReadEntry(key string) (string, bool, error) {
- str := "{\"deployment\":[\"cloud1-default-uuid-sisedeploy\"],\"service\":[\"cloud1-default-uuid-sisesvc\"]}"
- return str, true, nil
-}
-
-func (c *mockDB) DeleteEntry(key string) error {
- return nil
-}
-
-func (c *mockDB) ReadAll(key string) ([]string, error) {
- returnVal := []string{"cloud1-default-uuid1", "cloud1-default-uuid2"}
- return returnVal, nil
+func (c *mockCSAR) DestroyVNF(data map[string][]string, namespace string,
+ kubeclient *kubernetes.Clientset) error {
+ return c.err
}
func executeRequest(req *http.Request) *httptest.ResponseRecorder {
@@ -76,146 +62,308 @@ func checkResponseCode(t *testing.T, expected, actual int) {
}
}
-func TestVNFInstanceCreation(t *testing.T) {
- t.Run("Succesful create a VNF", func(t *testing.T) {
- payload := []byte(`{
- "cloud_region_id": "region1",
- "namespace": "test",
- "csar_id": "UUID-1",
- "oof_parameters": [{
- "key1": "value1",
- "key2": "value2",
- "key3": {}
- }],
- "network_parameters": {
- "oam_ip_address": {
- "connection_point": "string",
- "ip_address": "string",
- "workload_name": "string"
- }
- }
- }`)
-
- data := map[string][]string{
- "deployment": []string{"cloud1-default-uuid-sisedeploy"},
- "service": []string{"cloud1-default-uuid-sisesvc"},
- }
-
- expected := &CreateVnfResponse{
- VNFID: "test_UUID",
- CloudRegionID: "region1",
- Namespace: "test",
- VNFComponents: data,
- }
-
- var result CreateVnfResponse
-
- req, _ := http.NewRequest("POST", "/v1/vnf_instances/", bytes.NewBuffer(payload))
-
- GetVNFClient = func(configPath string) (kubernetes.Clientset, error) {
- return kubernetes.Clientset{}, nil
- }
-
- csar.CreateVNF = func(id string, r string, n string, kubeclient *kubernetes.Clientset) (string, map[string][]string, error) {
- return "externaluuid", data, nil
- }
-
- db.DBconn = &mockDB{}
+func TestCreateHandler(t *testing.T) {
+ testCases := []struct {
+ label string
+ input io.Reader
+ expectedCode int
+ mockGetVNFClientErr error
+ mockCreateVNF *mockCSAR
+ mockStore *db.MockDB
+ }{
+ {
+ label: "Missing body failure",
+ expectedCode: http.StatusBadRequest,
+ },
+ {
+ label: "Invalid JSON request format",
+ input: bytes.NewBuffer([]byte("invalid")),
+ expectedCode: http.StatusUnprocessableEntity,
+ },
+ {
+ label: "Missing parameter failure",
+ input: bytes.NewBuffer([]byte(`{
+ "csar_id": "testID",
+ "oof_parameters": {
+ "key_values": {
+ "key1": "value1",
+ "key2": "value2"
+ }
+ },
+ "vnf_instance_name": "test",
+ "vnf_instance_description": "vRouter_test_description"
+ }`)),
+ expectedCode: http.StatusUnprocessableEntity,
+ },
+ {
+ label: "Fail to get the VNF client",
+ input: bytes.NewBuffer([]byte(`{
+ "cloud_region_id": "region1",
+ "namespace": "test",
+ "csar_id": "UUID-1"
+ }`)),
+ expectedCode: http.StatusInternalServerError,
+ mockGetVNFClientErr: pkgerrors.New("Get VNF client error"),
+ },
+ {
+ label: "Fail to create the VNF instance",
+ input: bytes.NewBuffer([]byte(`{
+ "cloud_region_id": "region1",
+ "namespace": "test",
+ "csar_id": "UUID-1"
+ }`)),
+ expectedCode: http.StatusInternalServerError,
+ mockCreateVNF: &mockCSAR{
+ err: pkgerrors.New("Internal error"),
+ },
+ },
+ {
+ label: "Fail to create a VNF DB record",
+ input: bytes.NewBuffer([]byte(`{
+ "cloud_region_id": "region1",
+ "namespace": "test",
+ "csar_id": "UUID-1"
+ }`)),
+ expectedCode: http.StatusInternalServerError,
+ mockCreateVNF: &mockCSAR{
+ resourceYAMLNameMap: map[string][]string{},
+ },
+ mockStore: &db.MockDB{
+ Err: pkgerrors.New("Internal error"),
+ },
+ },
+ {
+ label: "Succesful create a VNF",
+ input: bytes.NewBuffer([]byte(`{
+ "cloud_region_id": "region1",
+ "namespace": "test",
+ "csar_id": "UUID-1"
+ }`)),
+ expectedCode: http.StatusCreated,
+ mockCreateVNF: &mockCSAR{
+ resourceYAMLNameMap: map[string][]string{
+ "deployment": []string{"cloud1-default-uuid-sisedeploy"},
+ "service": []string{"cloud1-default-uuid-sisesvc"},
+ },
+ },
+ mockStore: &db.MockDB{},
+ },
+ }
- response := executeRequest(req)
- checkResponseCode(t, http.StatusCreated, response.Code)
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ GetVNFClient = func(configPath string) (kubernetes.Clientset, error) {
+ return kubernetes.Clientset{}, testCase.mockGetVNFClientErr
+ }
+ if testCase.mockCreateVNF != nil {
+ csar.CreateVNF = testCase.mockCreateVNF.CreateVNF
+ }
+ if testCase.mockStore != nil {
+ db.DBconn = testCase.mockStore
+ }
- err := json.NewDecoder(response.Body).Decode(&result)
- if err != nil {
- t.Fatalf("TestVNFInstanceCreation returned:\n result=%v\n expected=%v", err, expected.VNFComponents)
- }
- })
- t.Run("Missing body failure", func(t *testing.T) {
- req, _ := http.NewRequest("POST", "/v1/vnf_instances/", nil)
- response := executeRequest(req)
+ request, _ := http.NewRequest("POST", "/v1/vnf_instances/", testCase.input)
+ result := executeRequest(request)
- checkResponseCode(t, http.StatusBadRequest, response.Code)
- })
- t.Run("Invalid JSON request format", func(t *testing.T) {
- payload := []byte("invalid")
- req, _ := http.NewRequest("POST", "/v1/vnf_instances/", bytes.NewBuffer(payload))
- response := executeRequest(req)
- checkResponseCode(t, http.StatusUnprocessableEntity, response.Code)
- })
- t.Run("Missing parameter failure", func(t *testing.T) {
- payload := []byte(`{
- "csar_id": "testID",
- "oof_parameters": {
- "key_values": {
- "key1": "value1",
- "key2": "value2"
+ if testCase.expectedCode != result.Code {
+ t.Fatalf("Request method returned: \n%v\n and it was expected: \n%v", result.Code, testCase.expectedCode)
+ }
+ if result.Code == http.StatusCreated {
+ var response CreateVnfResponse
+ err := json.NewDecoder(result.Body).Decode(&response)
+ if err != nil {
+ t.Fatalf("Parsing the returned response got an error (%s)", err)
}
- },
- "vnf_instance_name": "test",
- "vnf_instance_description": "vRouter_test_description"
- }`)
- req, _ := http.NewRequest("POST", "/v1/vnf_instances/", bytes.NewBuffer(payload))
- response := executeRequest(req)
- checkResponseCode(t, http.StatusUnprocessableEntity, response.Code)
- })
+ }
+ })
+ }
}
-func TestVNFInstancesRetrieval(t *testing.T) {
- t.Run("Succesful get a list of VNF", func(t *testing.T) {
- expected := &ListVnfsResponse{
- VNFs: []string{"uuid1", "uuid2"},
- }
- var result ListVnfsResponse
-
- req, _ := http.NewRequest("GET", "/v1/vnf_instances/cloud1/default", nil)
+func TestListHandler(t *testing.T) {
+ testCases := []struct {
+ label string
+ expectedCode int
+ expectedResponse []string
+ mockStore *db.MockDB
+ }{
+ {
+ label: "Fail to retrieve DB records",
+ expectedCode: http.StatusInternalServerError,
+ mockStore: &db.MockDB{
+ Err: pkgerrors.New("Internal error"),
+ },
+ },
+ {
+ label: "Get result from DB non-records",
+ expectedCode: http.StatusNotFound,
+ mockStore: &db.MockDB{},
+ },
+ {
+ label: "Get empty list",
+ expectedCode: http.StatusOK,
+ expectedResponse: []string{""},
+ mockStore: &db.MockDB{
+ Items: api.KVPairs{
+ &api.KVPair{
+ Key: "",
+ Value: []byte("{}"),
+ },
+ },
+ },
+ },
+ {
+ label: "Succesful get a list of VNF",
+ expectedCode: http.StatusOK,
+ expectedResponse: []string{"uid1", "uid2"},
+ mockStore: &db.MockDB{
+ Items: api.KVPairs{
+ &api.KVPair{
+ Key: "uuid1",
+ Value: []byte("{}"),
+ },
+ &api.KVPair{
+ Key: "uuid2",
+ Value: []byte("{}"),
+ },
+ },
+ },
+ },
+ }
- db.DBconn = &mockDB{}
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ if testCase.mockStore != nil {
+ db.DBconn = testCase.mockStore
+ }
- response := executeRequest(req)
- checkResponseCode(t, http.StatusOK, response.Code)
+ request, _ := http.NewRequest("GET", "/v1/vnf_instances/cloud1/default", nil)
+ result := executeRequest(request)
- err := json.NewDecoder(response.Body).Decode(&result)
- if err != nil {
- t.Fatalf("TestVNFInstancesRetrieval returned:\n result=%v\n expected=list", err)
- }
- if !reflect.DeepEqual(*expected, result) {
- t.Fatalf("TestVNFInstancesRetrieval returned:\n result=%v\n expected=%v", result, *expected)
- }
- })
- t.Run("Get empty list", func(t *testing.T) {
- req, _ := http.NewRequest("GET", "/v1/vnf_instances/cloudregion1/testnamespace", nil)
- db.DBconn = &mockDB{}
- response := executeRequest(req)
- checkResponseCode(t, http.StatusOK, response.Code)
- })
+ if testCase.expectedCode != result.Code {
+ t.Fatalf("Request method returned: \n%v\n and it was expected: \n%v",
+ result.Code, testCase.expectedCode)
+ }
+ if result.Code == http.StatusOK {
+ var response ListVnfsResponse
+ err := json.NewDecoder(result.Body).Decode(&response)
+ if err != nil {
+ t.Fatalf("Parsing the returned response got an error (%s)", err)
+ }
+ if !reflect.DeepEqual(testCase.expectedResponse, response.VNFs) {
+ t.Fatalf("TestListHandler returned:\n result=%v\n expected=%v",
+ response.VNFs, testCase.expectedResponse)
+ }
+ }
+ })
+ }
}
-func TestVNFInstanceDeletion(t *testing.T) {
- t.Run("Succesful delete a VNF", func(t *testing.T) {
- req, _ := http.NewRequest("DELETE", "/v1/vnf_instances/cloudregion1/testnamespace/1", nil)
-
- GetVNFClient = func(configPath string) (kubernetes.Clientset, error) {
- return kubernetes.Clientset{}, nil
- }
-
- csar.DestroyVNF = func(d map[string][]string, n string, kubeclient *kubernetes.Clientset) error {
- return nil
- }
+func TestDeleteHandler(t *testing.T) {
+ testCases := []struct {
+ label string
+ expectedCode int
+ mockGetVNFClientErr error
+ mockDeleteVNF *mockCSAR
+ mockStore *db.MockDB
+ }{
+ {
+ label: "Fail to read a VNF DB record",
+ expectedCode: http.StatusInternalServerError,
+ mockStore: &db.MockDB{
+ Err: pkgerrors.New("Internal error"),
+ },
+ },
+ {
+ label: "Fail to find VNF record be deleted",
+ expectedCode: http.StatusNotFound,
+ mockStore: &db.MockDB{
+ Items: api.KVPairs{},
+ },
+ },
+ {
+ label: "Fail to unmarshal the DB record",
+ expectedCode: http.StatusInternalServerError,
+ mockStore: &db.MockDB{
+ Items: api.KVPairs{
+ &api.KVPair{
+ Key: "cloudregion1-testnamespace-uuid1",
+ Value: []byte("{invalid format}"),
+ },
+ },
+ },
+ },
+ {
+ label: "Fail to get the VNF client",
+ expectedCode: http.StatusInternalServerError,
+ mockGetVNFClientErr: pkgerrors.New("Get VNF client error"),
+ mockStore: &db.MockDB{
+ Items: api.KVPairs{
+ &api.KVPair{
+ Key: "cloudregion1-testnamespace-uuid1",
+ Value: []byte("{" +
+ "\"deployment\": [\"deploy1\", \"deploy2\"]," +
+ "\"service\": [\"svc1\", \"svc2\"]" +
+ "}"),
+ },
+ },
+ },
+ },
+ {
+ label: "Fail to destroy VNF",
+ expectedCode: http.StatusInternalServerError,
+ mockStore: &db.MockDB{
+ Items: api.KVPairs{
+ &api.KVPair{
+ Key: "cloudregion1-testnamespace-uuid1",
+ Value: []byte("{" +
+ "\"deployment\": [\"deploy1\", \"deploy2\"]," +
+ "\"service\": [\"svc1\", \"svc2\"]" +
+ "}"),
+ },
+ },
+ },
+ mockDeleteVNF: &mockCSAR{
+ err: pkgerrors.New("Internal error"),
+ },
+ },
+ {
+ label: "Succesful delete a VNF",
+ expectedCode: http.StatusAccepted,
+ mockStore: &db.MockDB{
+ Items: api.KVPairs{
+ &api.KVPair{
+ Key: "cloudregion1-testnamespace-uuid1",
+ Value: []byte("{" +
+ "\"deployment\": [\"deploy1\", \"deploy2\"]," +
+ "\"service\": [\"svc1\", \"svc2\"]" +
+ "}"),
+ },
+ },
+ },
+ mockDeleteVNF: &mockCSAR{},
+ },
+ }
- db.DBconn = &mockDB{}
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ GetVNFClient = func(configPath string) (kubernetes.Clientset, error) {
+ return kubernetes.Clientset{}, testCase.mockGetVNFClientErr
+ }
+ if testCase.mockStore != nil {
+ db.DBconn = testCase.mockStore
+ }
+ if testCase.mockDeleteVNF != nil {
+ csar.DestroyVNF = testCase.mockDeleteVNF.DestroyVNF
+ }
- response := executeRequest(req)
- checkResponseCode(t, http.StatusAccepted, response.Code)
+ request, _ := http.NewRequest("DELETE", "/v1/vnf_instances/cloudregion1/testnamespace/uuid1", nil)
+ result := executeRequest(request)
- if result := response.Body.String(); result != "" {
- t.Fatalf("TestVNFInstanceDeletion returned:\n result=%v\n expected=%v", result, "")
- }
- })
- // t.Run("Malformed delete request", func(t *testing.T) {
- // req, _ := http.NewRequest("DELETE", "/v1/vnf_instances/foo", nil)
- // response := executeRqequest(req)
- // checkResponseCode(t, http.StatusBadRequest, response.Code)
- // })
+ if testCase.expectedCode != result.Code {
+ t.Fatalf("Request method returned: %v and it was expected: %v", result.Code, testCase.expectedCode)
+ }
+ })
+ }
}
// TODO: Update this test when the UpdateVNF endpoint is fixed.
@@ -276,47 +424,88 @@ func TestVNFInstanceUpdate(t *testing.T) {
}
*/
-func TestVNFInstanceRetrieval(t *testing.T) {
- t.Run("Succesful get a VNF", func(t *testing.T) {
-
- data := map[string][]string{
- "deployment": []string{"cloud1-default-uuid-sisedeploy"},
- "service": []string{"cloud1-default-uuid-sisesvc"},
- }
-
- expected := GetVnfResponse{
- VNFID: "1",
- CloudRegionID: "cloud1",
- Namespace: "default",
- VNFComponents: data,
- }
-
- req, _ := http.NewRequest("GET", "/v1/vnf_instances/cloud1/default/1", nil)
-
- GetVNFClient = func(configPath string) (kubernetes.Clientset, error) {
- return kubernetes.Clientset{}, nil
- }
-
- db.DBconn = &mockDB{}
-
- response := executeRequest(req)
- checkResponseCode(t, http.StatusOK, response.Code)
-
- var result GetVnfResponse
-
- err := json.NewDecoder(response.Body).Decode(&result)
- if err != nil {
- t.Fatalf("TestVNFInstanceRetrieval returned:\n result=%v\n expected=%v", err, expected)
- }
+func TestGetHandler(t *testing.T) {
+ testCases := []struct {
+ label string
+ expectedCode int
+ expectedResponse *GetVnfResponse
+ mockStore *db.MockDB
+ }{
+ {
+ label: "Fail to retrieve DB record",
+ expectedCode: http.StatusInternalServerError,
+ mockStore: &db.MockDB{
+ Err: pkgerrors.New("Internal error"),
+ },
+ },
+ {
+ label: "Not found DB record",
+ expectedCode: http.StatusNotFound,
+ mockStore: &db.MockDB{},
+ },
+ {
+ label: "Fail to unmarshal the DB record",
+ expectedCode: http.StatusInternalServerError,
+ mockStore: &db.MockDB{
+ Items: api.KVPairs{
+ &api.KVPair{
+ Key: "cloud1-default-1",
+ Value: []byte("{invalid-format}"),
+ },
+ },
+ },
+ },
+ {
+ label: "Succesful get a list of VNF",
+ expectedCode: http.StatusOK,
+ expectedResponse: &GetVnfResponse{
+ VNFID: "1",
+ CloudRegionID: "cloud1",
+ Namespace: "default",
+ VNFComponents: map[string][]string{
+ "deployment": []string{"deploy1", "deploy2"},
+ "service": []string{"svc1", "svc2"},
+ },
+ },
+ mockStore: &db.MockDB{
+ Items: api.KVPairs{
+ &api.KVPair{
+ Key: "cloud1-default-1",
+ Value: []byte("{" +
+ "\"deployment\": [\"deploy1\", \"deploy2\"]," +
+ "\"service\": [\"svc1\", \"svc2\"]" +
+ "}"),
+ },
+ &api.KVPair{
+ Key: "cloud1-default-2",
+ Value: []byte("{}"),
+ },
+ },
+ },
+ },
+ }
- if !reflect.DeepEqual(expected, result) {
- t.Fatalf("TestVNFInstanceRetrieval returned:\n result=%v\n expected=%v", result, expected)
- }
- })
- t.Run("VNF not found", func(t *testing.T) {
- req, _ := http.NewRequest("GET", "/v1/vnf_instances/cloudregion1/testnamespace/1", nil)
- response := executeRequest(req)
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ db.DBconn = testCase.mockStore
+ request, _ := http.NewRequest("GET", "/v1/vnf_instances/cloud1/default/1", nil)
+ result := executeRequest(request)
- checkResponseCode(t, http.StatusOK, response.Code)
- })
+ if testCase.expectedCode != result.Code {
+ t.Fatalf("Request method returned: %v and it was expected: %v",
+ result.Code, testCase.expectedCode)
+ }
+ if result.Code == http.StatusOK {
+ var response GetVnfResponse
+ err := json.NewDecoder(result.Body).Decode(&response)
+ if err != nil {
+ t.Fatalf("Parsing the returned response got an error (%s)", err)
+ }
+ if !reflect.DeepEqual(testCase.expectedResponse, &response) {
+ t.Fatalf("TestGetHandler returned:\n result=%v\n expected=%v",
+ &response, testCase.expectedResponse)
+ }
+ }
+ })
+ }
}
diff --git a/src/k8splugin/db/consul.go b/src/k8splugin/db/consul.go
index 950eea34..d7507242 100644
--- a/src/k8splugin/db/consul.go
+++ b/src/k8splugin/db/consul.go
@@ -16,95 +16,89 @@ package db
import (
"os"
- consulapi "github.com/hashicorp/consul/api"
+ "github.com/hashicorp/consul/api"
pkgerrors "github.com/pkg/errors"
)
-// ConsulDB is an implementation of the DatabaseConnection interface
-type ConsulDB struct {
- consulClient *consulapi.Client
+// ConsulKVStore defines the a subset of Consul DB operations
+// Note: This interface is defined mainly for allowing mock testing
+type ConsulKVStore interface {
+ List(prefix string, q *api.QueryOptions) (api.KVPairs, *api.QueryMeta, error)
+ Get(key string, q *api.QueryOptions) (*api.KVPair, *api.QueryMeta, error)
+ Put(p *api.KVPair, q *api.WriteOptions) (*api.WriteMeta, error)
+ Delete(key string, w *api.WriteOptions) (*api.WriteMeta, error)
}
-// InitializeDatabase initialized the initial steps
-func (c *ConsulDB) InitializeDatabase() error {
- config := consulapi.DefaultConfig()
- config.Address = os.Getenv("DATABASE_IP") + ":8500"
+// ConsulStore is an implementation of the ConsulKVStore interface
+type ConsulStore struct {
+ client ConsulKVStore
+}
- client, err := consulapi.NewClient(config)
- if err != nil {
- return err
+// NewConsulStore initializes a Consul Store instance using the default values
+func NewConsulStore(store ConsulKVStore) (Store, error) {
+ if store == nil {
+ config := api.DefaultConfig()
+ config.Address = os.Getenv("DATABASE_IP") + ":8500"
+
+ consulClient, err := api.NewClient(config)
+ if err != nil {
+ return nil, err
+ }
+ store = consulClient.KV()
}
- c.consulClient = client
- return nil
+
+ return &ConsulStore{
+ client: store,
+ }, nil
}
-// CheckDatabase checks if the database is running
-func (c *ConsulDB) CheckDatabase() error {
- kv := c.consulClient.KV()
- _, _, err := kv.Get("test", nil)
+// HealthCheck verifies if the database is up and running
+func (c *ConsulStore) HealthCheck() error {
+ _, err := c.Read("test")
if err != nil {
return pkgerrors.New("[ERROR] Cannot talk to Datastore. Check if it is running/reachable.")
}
return nil
}
-// CreateEntry is used to create a DB entry
-func (c *ConsulDB) CreateEntry(key string, value string) error {
- kv := c.consulClient.KV()
-
- p := &consulapi.KVPair{Key: key, Value: []byte(value)}
-
- _, err := kv.Put(p, nil)
-
- if err != nil {
- return err
+// Create is used to create a DB entry
+func (c *ConsulStore) Create(key, value string) error {
+ p := &api.KVPair{
+ Key: key,
+ Value: []byte(value),
}
-
- return nil
+ _, err := c.client.Put(p, nil)
+ return err
}
-// ReadEntry returns the internalID for a particular externalID is present in a namespace
-func (c *ConsulDB) ReadEntry(key string) (string, bool, error) {
-
- kv := c.consulClient.KV()
-
- pair, _, err := kv.Get(key, nil)
-
+// Read method returns the internalID for a particular externalID
+func (c *ConsulStore) Read(key string) (string, error) {
+ pair, _, err := c.client.Get(key, nil)
+ if err != nil {
+ return "", err
+ }
if pair == nil {
- return string("No value found for ID: " + key), false, err
+ return "", nil
}
- return string(pair.Value), true, err
+ return string(pair.Value), nil
}
-// DeleteEntry is used to delete an ID
-func (c *ConsulDB) DeleteEntry(key string) error {
-
- kv := c.consulClient.KV()
-
- _, err := kv.Delete(key, nil)
-
- if err != nil {
- return err
- }
-
- return nil
+// Delete method removes an internalID from the Database
+func (c *ConsulStore) Delete(key string) error {
+ _, err := c.client.Delete(key, nil)
+ return err
}
// ReadAll is used to get all ExternalIDs in a namespace
-func (c *ConsulDB) ReadAll(prefix string) ([]string, error) {
- kv := c.consulClient.KV()
-
- pairs, _, err := kv.List(prefix, nil)
-
- if len(pairs) == 0 {
- return []string{""}, err
+func (c *ConsulStore) ReadAll(prefix string) ([]string, error) {
+ pairs, _, err := c.client.List(prefix, nil)
+ if err != nil {
+ return nil, err
}
-
- var res []string
-
+ var result []string
for _, keypair := range pairs {
- res = append(res, keypair.Key)
+ result = append(result, keypair.Key)
}
- return res, err
+ return result, nil
}
diff --git a/src/k8splugin/db/consul_test.go b/src/k8splugin/db/consul_test.go
new file mode 100644
index 00000000..ede1a5e9
--- /dev/null
+++ b/src/k8splugin/db/consul_test.go
@@ -0,0 +1,298 @@
+// +build unit
+
+/*
+Copyright 2018 Intel Corporation.
+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 db
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/hashicorp/consul/api"
+ pkgerrors "github.com/pkg/errors"
+)
+
+type mockConsulKVStore struct {
+ Items api.KVPairs
+ Err error
+}
+
+func (c *mockConsulKVStore) Put(p *api.KVPair, q *api.WriteOptions) (*api.WriteMeta, error) {
+ return nil, c.Err
+}
+
+func (c *mockConsulKVStore) Get(key string, q *api.QueryOptions) (*api.KVPair, *api.QueryMeta, error) {
+ if c.Err != nil {
+ return nil, nil, c.Err
+ }
+ for _, kvpair := range c.Items {
+ if kvpair.Key == key {
+ return kvpair, nil, nil
+ }
+ }
+ return nil, nil, nil
+}
+
+func (c *mockConsulKVStore) Delete(key string, w *api.WriteOptions) (*api.WriteMeta, error) {
+ return nil, c.Err
+}
+
+func (c *mockConsulKVStore) List(prefix string, q *api.QueryOptions) (api.KVPairs, *api.QueryMeta, error) {
+ if c.Err != nil {
+ return nil, nil, c.Err
+ }
+ return c.Items, nil, nil
+}
+
+func TestConsulHealthCheck(t *testing.T) {
+ testCases := []struct {
+ label string
+ mock *mockConsulKVStore
+ expectedError string
+ }{
+ {
+ label: "Sucessful health check Consul Database",
+ mock: &mockConsulKVStore{
+ Items: api.KVPairs{
+ &api.KVPair{
+ Key: "test-key",
+ Value: nil,
+ },
+ },
+ },
+ },
+ {
+ label: "Fail connectivity to Consul Database",
+ mock: &mockConsulKVStore{
+ Err: pkgerrors.New("Timeout"),
+ },
+ expectedError: "Cannot talk to Datastore. Check if it is running/reachable.",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ client, _ := NewConsulStore(testCase.mock)
+ err := client.HealthCheck()
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("HealthCheck method return an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("HealthCheck method returned an error (%s)", err)
+ }
+ }
+ })
+ }
+}
+
+func TestConsulCreate(t *testing.T) {
+ testCases := []struct {
+ label string
+ input map[string]string
+ mock *mockConsulKVStore
+ expectedError string
+ }{
+ {
+ label: "Sucessful register a record to Consul Database",
+ input: map[string]string{"key": "test-key", "value": "test-value"},
+ mock: &mockConsulKVStore{},
+ },
+ {
+ label: "Fail to create a new record in Consul Database",
+ input: map[string]string{"key": "test-key", "value": "test-value"},
+ mock: &mockConsulKVStore{
+ Err: pkgerrors.New("DB error"),
+ },
+ expectedError: "DB error",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ client, _ := NewConsulStore(testCase.mock)
+ err := client.Create(testCase.input["key"], testCase.input["value"])
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Create method return an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Create method returned an error (%s)", err)
+ }
+ }
+ })
+ }
+}
+
+func TestConsulRead(t *testing.T) {
+ testCases := []struct {
+ label string
+ input string
+ mock *mockConsulKVStore
+ expectedError string
+ expectedResult string
+ }{
+ {
+ label: "Sucessful retrieve a record from Consul Database",
+ input: "test",
+ mock: &mockConsulKVStore{
+ Items: api.KVPairs{
+ &api.KVPair{
+ Key: "test",
+ Value: []byte("test-value"),
+ },
+ },
+ },
+ expectedResult: "test-value",
+ },
+ {
+ label: "Fail retrieve a non-existing record from Consul Database",
+ input: "test",
+ mock: &mockConsulKVStore{},
+ },
+ {
+ label: "Fail retrieve a record from Consul Database",
+ input: "test",
+ mock: &mockConsulKVStore{
+ Err: pkgerrors.New("DB error"),
+ },
+ expectedError: "DB error",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ client, _ := NewConsulStore(testCase.mock)
+ result, err := client.Read(testCase.input)
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Read method return an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Read method returned an error (%s)", err)
+ }
+ } else {
+ if testCase.expectedError != "" && testCase.expectedResult == "" {
+ t.Fatalf("Read method was expecting \"%s\" error message", testCase.expectedError)
+ }
+ if !reflect.DeepEqual(testCase.expectedResult, result) {
+
+ t.Fatalf("Read method returned: \n%v\n and it was expected: \n%v", result, testCase.expectedResult)
+ }
+ }
+ })
+ }
+}
+
+func TestConsulDelete(t *testing.T) {
+ testCases := []struct {
+ label string
+ input string
+ mock *mockConsulKVStore
+ expectedError string
+ }{
+ {
+ label: "Sucessful delete a record to Consul Database",
+ input: "test",
+ mock: &mockConsulKVStore{},
+ },
+ {
+ label: "Fail to delete a record in Consul Database",
+ mock: &mockConsulKVStore{
+ Err: pkgerrors.New("DB error"),
+ },
+ expectedError: "DB error",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ client, _ := NewConsulStore(testCase.mock)
+ err := client.Delete(testCase.input)
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Delete method return an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Delete method returned an error (%s)", err)
+ }
+ }
+ })
+ }
+}
+
+func TestConsulReadAll(t *testing.T) {
+ testCases := []struct {
+ label string
+ input string
+ mock *mockConsulKVStore
+ expectedError string
+ expectedResult []string
+ }{
+ {
+ label: "Sucessful retrieve a list from Consul Database",
+ input: "test",
+ mock: &mockConsulKVStore{
+ Items: api.KVPairs{
+ &api.KVPair{
+ Key: "test",
+ Value: []byte("test-value"),
+ },
+ &api.KVPair{
+ Key: "test2",
+ Value: []byte("test-value2"),
+ },
+ },
+ },
+ expectedResult: []string{"test", "test2"},
+ },
+ {
+ label: "Sucessful retrieve an empty list from Consul Database",
+ input: "test",
+ mock: &mockConsulKVStore{},
+ },
+ {
+ label: "Fail retrieve a record from Consul Database",
+ input: "test",
+ mock: &mockConsulKVStore{
+ Err: pkgerrors.New("DB error"),
+ },
+ expectedError: "DB error",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ client, _ := NewConsulStore(testCase.mock)
+ result, err := client.ReadAll(testCase.input)
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("ReadAll method return an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("ReadAll method returned an error (%s)", err)
+ }
+ } else {
+ if testCase.expectedError != "" && testCase.expectedResult == nil {
+ t.Fatalf("ReadAll method was expecting \"%s\" error message", testCase.expectedError)
+ }
+ if !reflect.DeepEqual(testCase.expectedResult, result) {
+
+ t.Fatalf("ReadAll method returned: \n%v\n and it was expected: \n%v", result, testCase.expectedResult)
+ }
+ }
+ })
+ }
+}
diff --git a/src/k8splugin/db/db_test.go b/src/k8splugin/db/db_test.go
deleted file mode 100644
index d37cd7ae..00000000
--- a/src/k8splugin/db/db_test.go
+++ /dev/null
@@ -1,99 +0,0 @@
-// +build unit
-
-/*
-Copyright 2018 Intel Corporation.
-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 db
-
-import (
- "reflect"
- "testing"
-)
-
-func TestCreateDBClient(t *testing.T) {
- oldDBconn := DBconn
-
- defer func() {
- DBconn = oldDBconn
- }()
-
- t.Run("Successfully create DB client", func(t *testing.T) {
- expectedDB := ConsulDB{}
-
- err := CreateDBClient("consul")
- if err != nil {
- t.Fatalf("TestCreateDBClient returned an error (%s)", err)
- }
-
- if !reflect.DeepEqual(DBconn, &expectedDB) {
- t.Fatalf("TestCreateDBClient set DBconn as:\n result=%v\n expected=%v", DBconn, expectedDB)
- }
- })
-}
-
-func TestSerialize(t *testing.T) {
-
- inp := map[string]interface{}{
- "UUID": "123e4567-e89b-12d3-a456-426655440000",
- "Data": "sdaijsdiodalkfjsdlagf",
- "Number": 23,
- "Float": 34.4,
- "Map": map[string]interface{}{
- "m1": "m1",
- "m2": 2,
- "m3": 3.0,
- },
- }
-
- got, err := Serialize(inp)
- if err != nil {
- t.Fatal(err)
- }
-
- expected := "{\"Data\":\"sdaijsdiodalkfjsdlagf\"," +
- "\"Float\":34.4,\"Map\":{\"m1\":\"m1\",\"m2\":2,\"m3\":3}," +
- "\"Number\":23,\"UUID\":\"123e4567-e89b-12d3-a456-426655440000\"}"
-
- if expected != got {
- t.Errorf("Serialize returned unexpected string: %s;"+
- " expected %sv", got, expected)
- }
-}
-
-func TestDeSerialize(t *testing.T) {
-
- inp := "{\"Data\":\"sdaijsdiodalkfjsdlagf\"," +
- "\"Float\":34.4,\"Map\":{\"m1\":\"m1\",\"m3\":3}," +
- "\"UUID\":\"123e4567-e89b-12d3-a456-426655440000\"}"
-
- got := make(map[string]interface{})
- err := DeSerialize(inp, &got)
- if err != nil {
- t.Fatal(err)
- }
-
- expected := map[string]interface{}{
- "UUID": "123e4567-e89b-12d3-a456-426655440000",
- "Data": "sdaijsdiodalkfjsdlagf",
- "Float": 34.4,
- "Map": map[string]interface{}{
- "m1": "m1",
- "m3": 3.0,
- },
- }
-
- if reflect.DeepEqual(expected, got) == false {
- t.Errorf("Serialize returned unexpected : %s;"+
- " expected %s", got, expected)
- }
-}
diff --git a/src/k8splugin/db/DB.go b/src/k8splugin/db/store.go
index d92b5953..c1a8b31f 100644
--- a/src/k8splugin/db/DB.go
+++ b/src/k8splugin/db/store.go
@@ -21,27 +21,30 @@ import (
)
// DBconn interface used to talk a concrete Database connection
-var DBconn DatabaseConnection
-
-// DatabaseConnection is an interface for accessing a database
-type DatabaseConnection interface {
- InitializeDatabase() error
- CheckDatabase() error
- CreateEntry(string, string) error
- ReadEntry(string) (string, bool, error)
- DeleteEntry(string) error
+var DBconn Store
+
+// Store is an interface for accessing a database
+type Store interface {
+ HealthCheck() error
+
+ Create(string, string) error
+ Read(string) (string, error)
+ // Update(string) (string, error)
+ Delete(string) error
+
ReadAll(string) ([]string, error)
}
// CreateDBClient creates the DB client
-var CreateDBClient = func(dbType string) error {
+func CreateDBClient(dbType string) error {
+ var err error
switch dbType {
case "consul":
- DBconn = &ConsulDB{}
- return nil
+ DBconn, err = NewConsulStore(nil)
default:
return pkgerrors.New(dbType + "DB not supported")
}
+ return err
}
// Serialize converts given data into a JSON string
diff --git a/src/k8splugin/db/store_test.go b/src/k8splugin/db/store_test.go
new file mode 100644
index 00000000..9bbe4a92
--- /dev/null
+++ b/src/k8splugin/db/store_test.go
@@ -0,0 +1,123 @@
+// +build unit
+
+/*
+Copyright 2018 Intel Corporation.
+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 db
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestCreateDBClient(t *testing.T) {
+ t.Run("Successfully create DB client", func(t *testing.T) {
+ expected := &ConsulStore{}
+
+ err := CreateDBClient("consul")
+ if err != nil {
+ t.Fatalf("CreateDBClient returned an error (%s)", err)
+ }
+ if reflect.TypeOf(DBconn) != reflect.TypeOf(expected) {
+ t.Fatalf("CreateDBClient set DBconn as:\n result=%T\n expected=%T", DBconn, expected)
+ }
+ })
+ t.Run("Fail to create client for unsupported DB", func(t *testing.T) {
+ err := CreateDBClient("fakeDB")
+ if err == nil {
+ t.Fatal("CreateDBClient didn't return an error")
+ }
+ if !strings.Contains(string(err.Error()), "DB not supported") {
+ t.Fatalf("CreateDBClient method returned an error (%s)", err)
+ }
+ })
+}
+
+func TestSerialize(t *testing.T) {
+
+ inp := map[string]interface{}{
+ "UUID": "123e4567-e89b-12d3-a456-426655440000",
+ "Data": "sdaijsdiodalkfjsdlagf",
+ "Number": 23,
+ "Float": 34.4,
+ "Map": map[string]interface{}{
+ "m1": "m1",
+ "m2": 2,
+ "m3": 3.0,
+ },
+ }
+
+ got, err := Serialize(inp)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expected := "{\"Data\":\"sdaijsdiodalkfjsdlagf\"," +
+ "\"Float\":34.4,\"Map\":{\"m1\":\"m1\",\"m2\":2,\"m3\":3}," +
+ "\"Number\":23,\"UUID\":\"123e4567-e89b-12d3-a456-426655440000\"}"
+
+ if expected != got {
+ t.Errorf("Serialize returned unexpected string: %s;"+
+ " expected %sv", got, expected)
+ }
+}
+
+func TestDeSerialize(t *testing.T) {
+ testCases := []struct {
+ label string
+ input string
+ expected map[string]interface{}
+ errMsg string
+ }{
+ {
+ label: "Sucessful deserialize entry",
+ input: "{\"Data\":\"sdaijsdiodalkfjsdlagf\"," +
+ "\"Float\":34.4,\"Map\":{\"m1\":\"m1\",\"m3\":3}," +
+ "\"UUID\":\"123e4567-e89b-12d3-a456-426655440000\"}",
+ expected: map[string]interface{}{
+ "UUID": "123e4567-e89b-12d3-a456-426655440000",
+ "Data": "sdaijsdiodalkfjsdlagf",
+ "Float": 34.4,
+ "Map": map[string]interface{}{
+ "m1": "m1",
+ "m3": 3.0,
+ },
+ },
+ },
+ {
+ label: "Fail to deserialize invalid entry",
+ input: "{invalid}",
+ errMsg: "Error deSerializing {invalid}: invalid character 'i' looking for beginning of object key string",
+ },
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ got := make(map[string]interface{})
+ err := DeSerialize(testCase.input, &got)
+ if err != nil {
+ if testCase.errMsg == "" {
+ t.Fatalf("DeSerialize method return an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.errMsg) {
+ t.Fatalf("DeSerialize method returned an error (%s)", err)
+ }
+ } else {
+ if !reflect.DeepEqual(testCase.expected, got) {
+ t.Errorf("Serialize returned unexpected : %v;"+
+ " expected %v", got, testCase.expected)
+ }
+ }
+ })
+ }
+}
diff --git a/src/k8splugin/db/testing.go b/src/k8splugin/db/testing.go
new file mode 100644
index 00000000..672fcbfb
--- /dev/null
+++ b/src/k8splugin/db/testing.go
@@ -0,0 +1,65 @@
+// +build unit
+
+/*
+Copyright 2018 Intel Corporation.
+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 db
+
+import (
+ "github.com/hashicorp/consul/api"
+)
+
+//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
+type MockDB struct {
+ Store
+ Items api.KVPairs
+ Err error
+}
+
+func (m *MockDB) Create(key string, value string) error {
+ return m.Err
+}
+
+func (m *MockDB) Read(key string) (string, error) {
+ if m.Err != nil {
+ return "", m.Err
+ }
+
+ for _, kvpair := range m.Items {
+ if kvpair.Key == key {
+ return string(kvpair.Value), nil
+ }
+ }
+
+ return "", nil
+}
+
+func (m *MockDB) Delete(key string) error {
+ return m.Err
+}
+
+func (m *MockDB) ReadAll(prefix string) ([]string, error) {
+ if m.Err != nil {
+ return []string{}, m.Err
+ }
+
+ var res []string
+
+ for _, keypair := range m.Items {
+ res = append(res, keypair.Key)
+ }
+
+ return res, nil
+}
diff --git a/src/k8splugin/vnfd/vnfd.go b/src/k8splugin/vnfd/vnfd.go
index 322b2d78..0fb81dbd 100644
--- a/src/k8splugin/vnfd/vnfd.go
+++ b/src/k8splugin/vnfd/vnfd.go
@@ -67,7 +67,7 @@ func (v *VNFDefinitionClient) Create(vnfd VNFDefinition) (VNFDefinition, error)
return VNFDefinition{}, pkgerrors.Wrap(err, "Serialize VNF Definition")
}
- err = db.DBconn.CreateEntry(key, serData)
+ err = db.DBconn.Create(key, serData)
if err != nil {
return VNFDefinition{}, pkgerrors.Wrap(err, "Creating DB Entry")
}
@@ -85,12 +85,12 @@ func (v *VNFDefinitionClient) List() ([]VNFDefinition, error) {
var retData []VNFDefinition
for _, key := range strArray {
- value, ok, err := db.DBconn.ReadEntry(key)
+ value, err := db.DBconn.Read(key)
if err != nil {
log.Printf("Error Reading Key: %s", key)
continue
}
- if ok {
+ if value != "" {
vnfd := VNFDefinition{}
err = db.DeSerialize(value, &vnfd)
if err != nil {
@@ -106,12 +106,12 @@ func (v *VNFDefinitionClient) List() ([]VNFDefinition, error) {
// Get returns the VNF Definition for corresponding ID
func (v *VNFDefinitionClient) Get(vnfID string) (VNFDefinition, error) {
- value, ok, err := db.DBconn.ReadEntry(v.keyPrefix + vnfID)
+ value, err := db.DBconn.Read(v.keyPrefix + vnfID)
if err != nil {
return VNFDefinition{}, pkgerrors.Wrap(err, "Get VNF Definitions")
}
- if ok {
+ if value != "" {
vnfd := VNFDefinition{}
err = db.DeSerialize(value, &vnfd)
if err != nil {
@@ -125,7 +125,7 @@ func (v *VNFDefinitionClient) Get(vnfID string) (VNFDefinition, error) {
// Delete deletes the VNF Definition from database
func (v *VNFDefinitionClient) Delete(vnfID string) error {
- err := db.DBconn.DeleteEntry(v.keyPrefix + vnfID)
+ err := db.DBconn.Delete(v.keyPrefix + vnfID)
if err != nil {
return pkgerrors.Wrap(err, "Delete VNF Definitions")
}
diff --git a/src/k8splugin/vnfd/vnfd_test.go b/src/k8splugin/vnfd/vnfd_test.go
index 54ab5f49..3230d3ef 100644
--- a/src/k8splugin/vnfd/vnfd_test.go
+++ b/src/k8splugin/vnfd/vnfd_test.go
@@ -1,3 +1,5 @@
+// +build unit
+
/*
* Copyright 2018 Intel Corporation, Inc
*
@@ -26,57 +28,12 @@ import (
pkgerrors "github.com/pkg/errors"
)
-//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
-type mockDB struct {
- db.DatabaseConnection
- Items api.KVPairs
- Err error
-}
-
-func (m *mockDB) CreateEntry(key string, value string) error {
- return m.Err
-}
-
-func (m *mockDB) ReadEntry(key string) (string, bool, error) {
- if m.Err != nil {
- return "", false, m.Err
- }
-
- for _, kvpair := range m.Items {
- if kvpair.Key == key {
- return string(kvpair.Value), true, nil
- }
- }
-
- return "", false, nil
-}
-
-func (m *mockDB) DeleteEntry(key string) error {
- return m.Err
-}
-
-func (m *mockDB) ReadAll(prefix string) ([]string, error) {
- if m.Err != nil {
- return []string{}, m.Err
- }
-
- var res []string
-
- for _, keypair := range m.Items {
- res = append(res, keypair.Key)
- }
-
- return res, nil
-}
-
func TestCreate(t *testing.T) {
testCases := []struct {
label string
inp VNFDefinition
expectedError string
- mockdb *mockDB
+ mockdb *db.MockDB
expected VNFDefinition
}{
{
@@ -94,12 +51,12 @@ func TestCreate(t *testing.T) {
ServiceType: "firewall",
},
expectedError: "",
- mockdb: &mockDB{},
+ mockdb: &db.MockDB{},
},
{
label: "Failed Create VNF Definition",
expectedError: "Error Creating Definition",
- mockdb: &mockDB{
+ mockdb: &db.MockDB{
Err: pkgerrors.New("Error Creating Definition"),
},
},
@@ -132,7 +89,7 @@ func TestList(t *testing.T) {
testCases := []struct {
label string
expectedError string
- mockdb *mockDB
+ mockdb *db.MockDB
expected []VNFDefinition
}{
{
@@ -152,7 +109,7 @@ func TestList(t *testing.T) {
},
},
expectedError: "",
- mockdb: &mockDB{
+ mockdb: &db.MockDB{
Items: api.KVPairs{
&api.KVPair{
Key: "vnfd/123e4567-e89b-12d3-a456-426655440000",
@@ -174,7 +131,7 @@ func TestList(t *testing.T) {
{
label: "List Error",
expectedError: "DB Error",
- mockdb: &mockDB{
+ mockdb: &db.MockDB{
Err: pkgerrors.New("DB Error"),
},
},
@@ -207,7 +164,7 @@ func TestGet(t *testing.T) {
testCases := []struct {
label string
expectedError string
- mockdb *mockDB
+ mockdb *db.MockDB
inp string
expected VNFDefinition
}{
@@ -221,7 +178,7 @@ func TestGet(t *testing.T) {
ServiceType: "firewall",
},
expectedError: "",
- mockdb: &mockDB{
+ mockdb: &db.MockDB{
Items: api.KVPairs{
&api.KVPair{
Key: "vnfd/123e4567-e89b-12d3-a456-426655440000",
@@ -236,7 +193,7 @@ func TestGet(t *testing.T) {
{
label: "Get Error",
expectedError: "DB Error",
- mockdb: &mockDB{
+ mockdb: &db.MockDB{
Err: pkgerrors.New("DB Error"),
},
},
@@ -270,18 +227,18 @@ func TestDelete(t *testing.T) {
label string
inp string
expectedError string
- mockdb *mockDB
+ mockdb *db.MockDB
expected []VNFDefinition
}{
{
label: "Delete VNF Definition",
inp: "123e4567-e89b-12d3-a456-426655440000",
- mockdb: &mockDB{},
+ mockdb: &db.MockDB{},
},
{
label: "Delete Error",
expectedError: "DB Error",
- mockdb: &mockDB{
+ mockdb: &db.MockDB{
Err: pkgerrors.New("DB Error"),
},
},