Add query parameters support to instance API 82/93482/1
authorKiran Kamineni <kiran.k.kamineni@intel.com>
Wed, 7 Aug 2019 22:45:54 +0000 (15:45 -0700)
committerKiran Kamineni <kiran.k.kamineni@intel.com>
Tue, 13 Aug 2019 19:48:48 +0000 (12:48 -0700)
Add support for query parameters to find instances
with rb-name, rb-version, profile-name as query parameters
eg: curl IP:PORT/v1/instance?rb-name=test&rb-version=v1

Issue-ID: MULTICLOUD-662
Change-Id: If68c87885c8b8dd097cbb3860e8c7320310a6ef7
Signed-off-by: Kiran Kamineni <kiran.k.kamineni@intel.com>
src/k8splugin/api/api.go
src/k8splugin/api/brokerhandler_test.go
src/k8splugin/api/instancehandler.go
src/k8splugin/api/instancehandler_test.go
src/k8splugin/internal/app/instance.go
src/k8splugin/internal/app/instance_test.go

index 4308db4..726bd11 100644 (file)
@@ -39,6 +39,12 @@ func NewRouter(defClient rb.DefinitionManager,
        instRouter := router.PathPrefix("/v1").Subrouter()
        instRouter.HandleFunc("/instance", instHandler.createHandler).Methods("POST")
        instRouter.HandleFunc("/instance", instHandler.listHandler).Methods("GET")
+       // Match rb-names, versions or profiles
+       instRouter.HandleFunc("/instance", instHandler.listHandler).
+               Queries("rb-name", "{rb-name}",
+                       "rb-version", "{rb-version}",
+                       "profile-name", "{profile-name}").Methods("GET")
+
        instRouter.HandleFunc("/instance/{instID}", instHandler.getHandler).Methods("GET")
        instRouter.HandleFunc("/instance/{instID}", instHandler.deleteHandler).Methods("DELETE")
        // (TODO): Fix update method
index 319c64e..8ef5e18 100644 (file)
@@ -285,7 +285,7 @@ func TestBrokerFindHandler(t *testing.T) {
                                },
                        },
                        instClient: &mockInstanceClient{
-                               items: []app.InstanceResponse{
+                               miniitems: []app.InstanceMiniResponse{
                                        {
                                                ID: "HaKpys8e",
                                                Request: app.InstanceRequest{
@@ -295,22 +295,6 @@ func TestBrokerFindHandler(t *testing.T) {
                                                        CloudRegion: "region1",
                                                },
                                                Namespace: "testnamespace",
-                                               Resources: []helm.KubernetesResource{
-                                                       {
-                                                               GVK: schema.GroupVersionKind{
-                                                                       Group:   "apps",
-                                                                       Version: "v1",
-                                                                       Kind:    "Deployment"},
-                                                               Name: "test-deployment",
-                                                       },
-                                                       {
-                                                               GVK: schema.GroupVersionKind{
-                                                                       Group:   "",
-                                                                       Version: "v1",
-                                                                       Kind:    "Service"},
-                                                               Name: "test-service",
-                                                       },
-                                               },
                                        },
                                },
                        },
index 3ec055b..be8e64f 100644 (file)
@@ -106,10 +106,16 @@ func (i instanceHandler) getHandler(w http.ResponseWriter, r *http.Request) {
        }
 }
 
-// getHandler retrieves information about an instance via the ID
+// listHandler retrieves information about an instance via the ID
 func (i instanceHandler) listHandler(w http.ResponseWriter, r *http.Request) {
 
-       resp, err := i.client.List()
+       //If parameters are not provided, they are sent as empty strings
+       //Which will list all instances
+       rbName := r.FormValue("rb-name")
+       rbVersion := r.FormValue("rb-version")
+       ProfileName := r.FormValue("profile-name")
+
+       resp, err := i.client.List(rbName, rbVersion, ProfileName)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
index 83fa3d2..418054e 100644 (file)
@@ -60,7 +60,7 @@ func (m *mockInstanceClient) Get(id string) (app.InstanceResponse, error) {
        return m.items[0], nil
 }
 
-func (m *mockInstanceClient) List() ([]app.InstanceMiniResponse, error) {
+func (m *mockInstanceClient) List(rbname, rbversion, profilename string) ([]app.InstanceMiniResponse, error) {
        if m.err != nil {
                return []app.InstanceMiniResponse{}, m.err
        }
@@ -68,12 +68,12 @@ func (m *mockInstanceClient) List() ([]app.InstanceMiniResponse, error) {
        return m.miniitems, nil
 }
 
-func (m *mockInstanceClient) Find(rbName string, ver string, profile string, labelKeys map[string]string) ([]app.InstanceResponse, error) {
+func (m *mockInstanceClient) Find(rbName string, ver string, profile string, labelKeys map[string]string) ([]app.InstanceMiniResponse, error) {
        if m.err != nil {
                return nil, m.err
        }
 
-       return m.items, nil
+       return m.miniitems, nil
 }
 
 func (m *mockInstanceClient) Delete(id string) error {
@@ -312,6 +312,8 @@ func TestInstanceListHandler(t *testing.T) {
                label            string
                input            string
                expectedCode     int
+               queryParams      bool
+               queryParamsMap   map[string]string
                expectedResponse []app.InstanceMiniResponse
                instClient       *mockInstanceClient
        }{
@@ -373,11 +375,52 @@ func TestInstanceListHandler(t *testing.T) {
                                },
                        },
                },
+               {
+                       label:       "List Instances Based on Query Parameters",
+                       queryParams: true,
+                       queryParamsMap: map[string]string{
+                               "rb-name": "test-rbdef1",
+                       },
+                       expectedCode: http.StatusOK,
+                       expectedResponse: []app.InstanceMiniResponse{
+                               {
+                                       ID: "HaKpys8e",
+                                       Request: app.InstanceRequest{
+                                               RBName:      "test-rbdef",
+                                               RBVersion:   "v1",
+                                               ProfileName: "profile1",
+                                               CloudRegion: "region1",
+                                       },
+                                       Namespace: "testnamespace",
+                               },
+                       },
+                       instClient: &mockInstanceClient{
+                               miniitems: []app.InstanceMiniResponse{
+                                       {
+                                               ID: "HaKpys8e",
+                                               Request: app.InstanceRequest{
+                                                       RBName:      "test-rbdef",
+                                                       RBVersion:   "v1",
+                                                       ProfileName: "profile1",
+                                                       CloudRegion: "region1",
+                                               },
+                                               Namespace: "testnamespace",
+                                       },
+                               },
+                       },
+               },
        }
 
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v1/instance", nil)
+                       if testCase.queryParams {
+                               q := request.URL.Query()
+                               for k, v := range testCase.queryParamsMap {
+                                       q.Add(k, v)
+                               }
+                               request.URL.RawQuery = q.Encode()
+                       }
                        resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
index 5cfdaea..cf96d50 100644 (file)
@@ -60,8 +60,8 @@ type InstanceMiniResponse struct {
 type InstanceManager interface {
        Create(i InstanceRequest) (InstanceResponse, error)
        Get(id string) (InstanceResponse, error)
-       List() ([]InstanceMiniResponse, error)
-       Find(rbName string, ver string, profile string, labelKeys map[string]string) ([]InstanceResponse, error)
+       List(rbname, rbversion, profilename string) ([]InstanceMiniResponse, error)
+       Find(rbName string, ver string, profile string, labelKeys map[string]string) ([]InstanceMiniResponse, error)
        Delete(id string) error
 }
 
@@ -184,7 +184,7 @@ func (v *InstanceClient) Get(id string) (InstanceResponse, error) {
 
 // List returns the instance for corresponding ID
 // Empty string returns all
-func (v *InstanceClient) List() ([]InstanceMiniResponse, error) {
+func (v *InstanceClient) List(rbname, rbversion, profilename string) ([]InstanceMiniResponse, error) {
 
        dbres, err := db.DBconn.ReadAll(v.storeName, v.tagInst)
        if err != nil || len(dbres) == 0 {
@@ -192,6 +192,7 @@ func (v *InstanceClient) List() ([]InstanceMiniResponse, error) {
        }
 
        var results []InstanceMiniResponse
+
        for key, value := range dbres {
                //value is a byte array
                if value != nil {
@@ -206,6 +207,21 @@ func (v *InstanceClient) List() ([]InstanceMiniResponse, error) {
                                Request:   resp.Request,
                                Namespace: resp.Namespace,
                        }
+
+                       //Filter based on the accepted keys
+                       if len(rbname) != 0 &&
+                               miniresp.Request.RBName != rbname {
+                               continue
+                       }
+                       if len(rbversion) != 0 &&
+                               miniresp.Request.RBVersion != rbversion {
+                               continue
+                       }
+                       if len(profilename) != 0 &&
+                               miniresp.Request.ProfileName != profilename {
+                               continue
+                       }
+
                        results = append(results, miniresp)
                }
        }
@@ -218,70 +234,36 @@ func (v *InstanceClient) List() ([]InstanceMiniResponse, error) {
 // If profile is empty, it will return all instances for a given rbName+version
 // If labelKeys are provided, the results are filtered based on that.
 // It is an AND operation for labelkeys.
-func (v *InstanceClient) Find(rbName string, version string, profile string, labelKeys map[string]string) ([]InstanceResponse, error) {
+func (v *InstanceClient) Find(rbName string, version string, profile string, labelKeys map[string]string) ([]InstanceMiniResponse, error) {
        if rbName == "" && len(labelKeys) == 0 {
-               return []InstanceResponse{}, pkgerrors.New("rbName or labelkeys is required and cannot be empty")
+               return []InstanceMiniResponse{}, pkgerrors.New("rbName or labelkeys is required and cannot be empty")
        }
 
-       values, err := db.DBconn.ReadAll(v.storeName, v.tagInst)
-       if err != nil || len(values) == 0 {
-               return []InstanceResponse{}, pkgerrors.Wrap(err, "Find Instance")
+       responses, err := v.List(rbName, version, profile)
+       if err != nil {
+               return []InstanceMiniResponse{}, pkgerrors.Wrap(err, "Listing Instances")
        }
 
-       response := []InstanceResponse{}
-       //values is a map[string][]byte
-InstanceResponseLoop:
-       for _, value := range values {
-               resp := InstanceResponse{}
-               db.DBconn.Unmarshal(value, &resp)
-               if err != nil {
-                       return []InstanceResponse{}, pkgerrors.Wrap(err, "Unmarshaling Instance Value")
-               }
+       ret := []InstanceMiniResponse{}
 
-               // Filter by labels provided
-               if len(labelKeys) != 0 {
-                       for lkey, lvalue := range labelKeys {
-                               //Check if label key exists and get its value
-                               if val, ok := resp.Request.Labels[lkey]; ok {
-                                       if lvalue != val {
-                                               continue InstanceResponseLoop
-                                       }
-                               } else {
-                                       continue InstanceResponseLoop
-                               }
-                       }
-               }
+       //filter the list by labelKeys now
+       for _, resp := range responses {
 
-               if rbName != "" {
-                       if resp.Request.RBName == rbName {
-
-                               //Check if a version is provided and if it matches
-                               if version != "" {
-                                       if resp.Request.RBVersion == version {
-                                               //Check if a profilename matches or if it is not provided
-                                               if profile == "" || resp.Request.ProfileName == profile {
-                                                       response = append(response, resp)
-                                               }
-                                       }
-                               } else {
-                                       //Append all versions as version is not provided
-                                       response = append(response, resp)
-                               }
+               add := true
+               for k, v := range labelKeys {
+                       if resp.Request.Labels[k] != v {
+                               add = false
+                               break
                        }
-               } else {
-                       response = append(response, resp)
                }
-       }
-
-       //filter the list by labelKeys now
-       for _, value := range response {
-               for _, label := range labelKeys {
-                       if _, ok := value.Request.Labels[label]; ok {
-                       }
+               // If label was not found in the response, don't add it
+               if add {
+                       ret = append(ret, resp)
                }
+
        }
 
-       return response, nil
+       return ret, nil
 }
 
 // Delete the Instance from database
index 3cb62ee..b79cf38 100644 (file)
@@ -434,7 +434,7 @@ func TestInstanceFind(t *testing.T) {
                        Items: items,
                }
 
-               expected := []InstanceResponse{
+               expected := []InstanceMiniResponse{
                        {
                                ID: "HaKpys8e",
                                Request: InstanceRequest{
@@ -447,22 +447,6 @@ func TestInstanceFind(t *testing.T) {
                                        },
                                },
                                Namespace: "testnamespace",
-                               Resources: []helm.KubernetesResource{
-                                       {
-                                               GVK: schema.GroupVersionKind{
-                                                       Group:   "apps",
-                                                       Version: "v1",
-                                                       Kind:    "Deployment"},
-                                               Name: "deployment-1",
-                                       },
-                                       {
-                                               GVK: schema.GroupVersionKind{
-                                                       Group:   "",
-                                                       Version: "v1",
-                                                       Kind:    "Service"},
-                                               Name: "service-1",
-                                       },
-                               },
                        },
                        {
                                ID: "HaKpys8f",
@@ -473,22 +457,6 @@ func TestInstanceFind(t *testing.T) {
                                        CloudRegion: "region1",
                                },
                                Namespace: "testnamespace",
-                               Resources: []helm.KubernetesResource{
-                                       {
-                                               GVK: schema.GroupVersionKind{
-                                                       Group:   "apps",
-                                                       Version: "v1",
-                                                       Kind:    "Deployment"},
-                                               Name: "deployment-1",
-                                       },
-                                       {
-                                               GVK: schema.GroupVersionKind{
-                                                       Group:   "",
-                                                       Version: "v1",
-                                                       Kind:    "Service"},
-                                               Name: "service-1",
-                                       },
-                               },
                        },
                        {
                                ID: "HaKpys8g",
@@ -499,22 +467,6 @@ func TestInstanceFind(t *testing.T) {
                                        CloudRegion: "region1",
                                },
                                Namespace: "testnamespace",
-                               Resources: []helm.KubernetesResource{
-                                       {
-                                               GVK: schema.GroupVersionKind{
-                                                       Group:   "apps",
-                                                       Version: "v1",
-                                                       Kind:    "Deployment"},
-                                               Name: "deployment-1",
-                                       },
-                                       {
-                                               GVK: schema.GroupVersionKind{
-                                                       Group:   "",
-                                                       Version: "v1",
-                                                       Kind:    "Service"},
-                                               Name: "service-1",
-                                       },
-                               },
                        },
                }
                ic := NewInstanceClient()
@@ -541,12 +493,12 @@ func TestInstanceFind(t *testing.T) {
                }
        })
 
-       t.Run("Successfully Find Instance By Name Version", func(t *testing.T) {
+       t.Run("Successfully Find Instance By Name and Label", func(t *testing.T) {
                db.DBconn = &db.MockDB{
                        Items: items,
                }
 
-               expected := []InstanceResponse{
+               expected := []InstanceMiniResponse{
                        {
                                ID: "HaKpys8e",
                                Request: InstanceRequest{
@@ -559,23 +511,53 @@ func TestInstanceFind(t *testing.T) {
                                        },
                                },
                                Namespace: "testnamespace",
+                       },
+               }
+               ic := NewInstanceClient()
+               name := "test-rbdef"
+               labels := map[string]string{
+                       "vf_module_id": "test-vf-module-id",
+               }
+               data, err := ic.Find(name, "", "", labels)
+               if err != nil {
+                       t.Fatalf("TestInstanceFind returned an error (%s)", err)
+               }
 
-                               Resources: []helm.KubernetesResource{
-                                       {
-                                               GVK: schema.GroupVersionKind{
-                                                       Group:   "apps",
-                                                       Version: "v1",
-                                                       Kind:    "Deployment"},
-                                               Name: "deployment-1",
-                                       },
-                                       {
-                                               GVK: schema.GroupVersionKind{
-                                                       Group:   "",
-                                                       Version: "v1",
-                                                       Kind:    "Service"},
-                                               Name: "service-1",
+               // Since the order of returned slice is not guaranteed
+               // Check both and return error if both don't match
+               sort.Slice(data, func(i, j int) bool {
+                       return data[i].ID < data[j].ID
+               })
+               // Sort both as it is not expected that testCase.expected
+               // is sorted
+               sort.Slice(expected, func(i, j int) bool {
+                       return expected[i].ID < expected[j].ID
+               })
+
+               if !reflect.DeepEqual(expected, data) {
+                       t.Fatalf("TestInstanceFind returned:\n result=%v\n expected=%v",
+                               data, expected)
+               }
+       })
+
+       t.Run("Successfully Find Instance By Name Version", func(t *testing.T) {
+               db.DBconn = &db.MockDB{
+                       Items: items,
+               }
+
+               expected := []InstanceMiniResponse{
+                       {
+                               ID: "HaKpys8e",
+                               Request: InstanceRequest{
+                                       RBName:      "test-rbdef",
+                                       RBVersion:   "v1",
+                                       ProfileName: "profile1",
+                                       CloudRegion: "region1",
+                                       Labels: map[string]string{
+                                               "vf_module_id": "test-vf-module-id",
                                        },
                                },
+                               Namespace: "testnamespace",
                        },
                        {
                                ID: "HaKpys8f",
@@ -586,23 +568,6 @@ func TestInstanceFind(t *testing.T) {
                                        CloudRegion: "region1",
                                },
                                Namespace: "testnamespace",
-
-                               Resources: []helm.KubernetesResource{
-                                       {
-                                               GVK: schema.GroupVersionKind{
-                                                       Group:   "apps",
-                                                       Version: "v1",
-                                                       Kind:    "Deployment"},
-                                               Name: "deployment-1",
-                                       },
-                                       {
-                                               GVK: schema.GroupVersionKind{
-                                                       Group:   "",
-                                                       Version: "v1",
-                                                       Kind:    "Service"},
-                                               Name: "service-1",
-                                       },
-                               },
                        },
                }
                ic := NewInstanceClient()
@@ -634,7 +599,7 @@ func TestInstanceFind(t *testing.T) {
                        Items: items,
                }
 
-               expected := []InstanceResponse{
+               expected := []InstanceMiniResponse{
                        {
                                ID: "HaKpys8e",
                                Request: InstanceRequest{
@@ -647,23 +612,6 @@ func TestInstanceFind(t *testing.T) {
                                        },
                                },
                                Namespace: "testnamespace",
-
-                               Resources: []helm.KubernetesResource{
-                                       {
-                                               GVK: schema.GroupVersionKind{
-                                                       Group:   "apps",
-                                                       Version: "v1",
-                                                       Kind:    "Deployment"},
-                                               Name: "deployment-1",
-                                       },
-                                       {
-                                               GVK: schema.GroupVersionKind{
-                                                       Group:   "",
-                                                       Version: "v1",
-                                                       Kind:    "Service"},
-                                               Name: "service-1",
-                                       },
-                               },
                        },
                }
                ic := NewInstanceClient()