Add suport for query api on root level 23/123023/2
authorLukasz Rajewski <lukasz.rajewski@orange.com>
Sun, 1 Aug 2021 19:01:44 +0000 (21:01 +0200)
committerLukasz Rajewski <lukasz.rajewski@orange.com>
Mon, 2 Aug 2021 20:48:48 +0000 (22:48 +0200)
Add suport for query api on root level. Instance
query handler and root query handler merged.

Issue-ID: MULTICLOUD-1379
Signed-off-by: Lukasz Rajewski <lukasz.rajewski@orange.com>
Change-Id: I1dc95c7aca0e734edf5794e7c36143dc12070f2b

13 files changed:
src/k8splugin/api/api.go
src/k8splugin/api/brokerhandler_test.go
src/k8splugin/api/confighandler.go
src/k8splugin/api/defhandler_test.go
src/k8splugin/api/healthcheckhandler_test.go
src/k8splugin/api/instancehandler.go
src/k8splugin/api/instancehandler_test.go
src/k8splugin/api/profilehandler_test.go
src/k8splugin/api/queryhandler.go [new file with mode: 0644]
src/k8splugin/cmd/main.go
src/k8splugin/internal/app/client.go
src/k8splugin/internal/app/instance.go
src/k8splugin/internal/app/query.go [new file with mode: 0644]

index 4a196ae..56e6db6 100644 (file)
@@ -1,6 +1,8 @@
 /*
 Copyright 2018 Intel Corporation.
 Copyright © 2021 Samsung Electronics
+Copyright © 2021 Orange
+
 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
@@ -27,6 +29,7 @@ import (
 func NewRouter(defClient rb.DefinitionManager,
        profileClient rb.ProfileManager,
        instClient app.InstanceManager,
+       queryClient app.QueryManager,
        configClient app.ConfigManager,
        connectionClient connection.ConnectionManager,
        templateClient rb.ConfigTemplateManager,
@@ -56,8 +59,21 @@ func NewRouter(defClient rb.DefinitionManager,
                        "Name", "{Name}",
                        "Labels", "{Labels}").Methods("GET")
        instRouter.HandleFunc("/instance/{instID}", instHandler.deleteHandler).Methods("DELETE")
-       // (TODO): Fix update method
-       // instRouter.HandleFunc("/{vnfInstanceId}", UpdateHandler).Methods("PUT")
+
+       // Query handler routes
+       if queryClient == nil {
+               queryClient = app.NewQueryClient()
+       }
+       queryHandler := queryHandler{client: queryClient}
+       queryRouter := router.PathPrefix("/v1").Subrouter()
+       queryRouter.HandleFunc("/query", queryHandler.queryHandler).Methods("GET")
+       queryRouter.HandleFunc("/query", queryHandler.queryHandler).
+               Queries("Namespace", "{Namespace}",
+                       "CloudRegion", "{CloudRegion}",
+                       "ApiVersion", "{ApiVersion}",
+                       "Kind", "{Kind}",
+                       "Name", "{Name}",
+                       "Labels", "{Labels}").Methods("GET")
 
        //Setup the broker handler here
        //Use the base router without any path prefixes
index 97c8a39..767cae1 100644 (file)
@@ -313,7 +313,7 @@ func TestBrokerCreateHandler(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
 
                        request := httptest.NewRequest("POST", "/cloudowner/cloudregion/infra_workload", testCase.input)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
                        defer resp.Body.Close()
 
                        if testCase.expectedCode != resp.StatusCode {
@@ -409,7 +409,7 @@ func TestBrokerGetHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/cloudowner/cloudregion/infra_workload/"+testCase.input, nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                t.Fatalf("Request method returned: %v and it was expected: %v",
@@ -489,7 +489,7 @@ func TestBrokerFindHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/cloudowner/cloudregion/infra_workload?name="+testCase.input, nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                t.Fatalf("Request method returned: %v and it was expected: %v",
@@ -551,7 +551,7 @@ func TestBrokerDeleteHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("DELETE", "/cloudowner/cloudregion/infra_workload/"+testCase.input, nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                t.Fatalf("Request method returned: %v and it was expected: %v", resp.StatusCode, testCase.expectedCode)
index f4bb086..7369833 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright 2018 Intel Corporation, Inc
  * Copyright © 2021 Samsung Electronics
+ * Copyright © 2021 Orange
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index bb2f9dc..b626b6f 100644 (file)
@@ -139,7 +139,7 @@ func TestRBDefCreateHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("POST", "/v1/rb/definition", testCase.reader)
-                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -208,7 +208,7 @@ func TestRBDefListVersionsHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v1/rb/definition/testresourcebundle", nil)
-                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -288,7 +288,7 @@ func TestRBDefListAllHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v1/rb/definition", nil)
-                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -368,7 +368,7 @@ func TestRBDefGetHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v1/rb/definition/"+testCase.name+"/"+testCase.version, nil)
-                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -419,7 +419,7 @@ func TestRBDefDeleteHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("DELETE", "/v1/rb/definition/"+testCase.name+"/"+testCase.version, nil)
-                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -476,7 +476,7 @@ func TestRBDefUploadHandler(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("POST",
                                "/v1/rb/definition/"+testCase.name+"/"+testCase.version+"/content", testCase.body)
-                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
index 293ddf9..3a03d90 100644 (file)
@@ -35,7 +35,7 @@ func TestHealthCheckHandler(t *testing.T) {
                        Err: nil,
                }
                request := httptest.NewRequest("GET", "/v1/healthcheck", nil)
-               resp := executeRequest(request, NewRouter(nil, nil, nil, nil, nil, nil, nil))
+               resp := executeRequest(request, NewRouter(nil, nil, nil, nil, nil, nil, nil, nil))
 
                //Check returned code
                if resp.StatusCode != http.StatusOK {
@@ -48,7 +48,7 @@ func TestHealthCheckHandler(t *testing.T) {
                        Err: pkgerrors.New("Runtime Error in DB"),
                }
                request := httptest.NewRequest("GET", "/v1/healthcheck", nil)
-               resp := executeRequest(request, NewRouter(nil, nil, nil, nil, nil, nil, nil))
+               resp := executeRequest(request, NewRouter(nil, nil, nil, nil, nil, nil, nil, nil))
 
                //Check returned code
                if resp.StatusCode != http.StatusInternalServerError {
index b56a8e1..3baa806 100644 (file)
@@ -1,6 +1,8 @@
 /*
 Copyright 2018 Intel Corporation.
 Copyright © 2021 Samsung Electronics
+Copyright © 2021 Orange
+
 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
@@ -181,15 +183,11 @@ func (i instanceHandler) queryHandler(w http.ResponseWriter, r *http.Request) {
        name := r.FormValue("Name")
        labels := r.FormValue("Labels")
        if apiVersion == "" {
-               http.Error(w, "Missing apiVersion mandatory parameter", http.StatusBadRequest)
+               http.Error(w, "Missing ApiVersion mandatory parameter", http.StatusBadRequest)
                return
        }
        if kind == "" {
-               http.Error(w, "Missing kind mandatory parameter", http.StatusBadRequest)
-               return
-       }
-       if name == "" && labels == "" {
-               http.Error(w, "Name or Labels parameter must be provided", http.StatusBadRequest)
+               http.Error(w, "Missing Kind mandatory parameter", http.StatusBadRequest)
                return
        }
        resp, err := i.client.Query(id, apiVersion, kind, name, labels)
index e05bd2d..faec132 100644 (file)
@@ -205,7 +205,7 @@ func TestInstanceCreateHandler(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
 
                        request := httptest.NewRequest("POST", "/v1/instance", testCase.input)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                body, _ := ioutil.ReadAll(resp.Body)
@@ -306,7 +306,7 @@ func TestInstanceGetHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v1/instance/"+testCase.input, nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                t.Fatalf("Request method returned: %v and it was expected: %v",
@@ -441,7 +441,7 @@ func TestInstanceListHandler(t *testing.T) {
                                }
                                request.URL.RawQuery = q.Encode()
                        }
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                t.Fatalf("Request method returned: %v and it was expected: %v",
@@ -500,7 +500,7 @@ func TestDeleteHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("DELETE", "/v1/instance/"+testCase.input, nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                t.Fatalf("Request method returned: %v and it was expected: %v", resp.StatusCode, testCase.expectedCode)
@@ -538,18 +538,6 @@ func TestInstanceQueryHandler(t *testing.T) {
                                err: pkgerrors.New("Missing kind mandatory parameter"),
                        },
                },
-               {
-                       label: "Missing name or label mandatory parameters",
-                       id:    "HaKpys8e",
-                       input: map[string]string{
-                               "ApiVersion": "v1",
-                               "Kind":       "Pod",
-                       },
-                       expectedCode: http.StatusBadRequest,
-                       instClient: &mockInstanceClient{
-                               err: pkgerrors.New("Name or Labels parameter must be provided"),
-                       },
-               },
                {
                        label: "Query instance by name",
                        id:    "HaKpys8e",
@@ -746,7 +734,7 @@ func TestInstanceQueryHandler(t *testing.T) {
                        }
                        url := "/v1/instance/" + testCase.id + "/query?" + params.Encode()
                        request := httptest.NewRequest("GET", url, nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                body, _ := ioutil.ReadAll(resp.Body)
index 6897e01..32d0061 100644 (file)
@@ -127,7 +127,7 @@ func TestRBProfileCreateHandler(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("POST", "/v1/rb/definition/test-rbdef/v1/profile",
                                testCase.reader)
-                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -207,7 +207,7 @@ func TestRBProfileGetHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v1/rb/definition/test-rbdef/v1/profile/"+testCase.prname, nil)
-                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -288,7 +288,7 @@ func TestRBProfileListHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v1/rb/definition/"+testCase.def+"/"+testCase.version+"/profile", nil)
-                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -347,7 +347,7 @@ func TestRBProfileDeleteHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("DELETE", "/v1/rb/definition/test-rbdef/v1/profile/"+testCase.prname, nil)
-                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -400,7 +400,7 @@ func TestRBProfileUploadHandler(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("POST",
                                "/v1/rb/definition/test-rbdef/v1/profile/"+testCase.prname+"/content", testCase.body)
-                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
diff --git a/src/k8splugin/api/queryhandler.go b/src/k8splugin/api/queryhandler.go
new file mode 100644 (file)
index 0000000..9c11954
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+Copyright 2018 Intel Corporation.
+Copyright © 2021 Samsung Electronics
+Copyright © 2021 Orange
+
+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 api
+
+import (
+       "encoding/json"
+       "net/http"
+
+       "github.com/onap/multicloud-k8s/src/k8splugin/internal/app"
+       log "github.com/onap/multicloud-k8s/src/k8splugin/internal/logutils"
+)
+
+// Used to store the backend implementation objects
+// Also simplifies the mocking needed for unit testing
+type queryHandler struct {
+       // Interface that implements the Instance operations
+       client app.QueryManager
+}
+
+// queryHandler retrieves information about specified resources for instance
+func (i queryHandler) queryHandler(w http.ResponseWriter, r *http.Request) {
+       namespace := r.FormValue("Namespace")
+       cloudRegion := r.FormValue("CloudRegion")
+       apiVersion := r.FormValue("ApiVersion")
+       kind := r.FormValue("Kind")
+       name := r.FormValue("Name")
+       labels := r.FormValue("Labels")
+       if cloudRegion == "" {
+               http.Error(w, "Missing CloudRegion mandatory parameter", http.StatusBadRequest)
+               return
+       }
+       if apiVersion == "" {
+               http.Error(w, "Missing ApiVersion mandatory parameter", http.StatusBadRequest)
+               return
+       }
+       if kind == "" {
+               http.Error(w, "Missing Kind mandatory parameter", http.StatusBadRequest)
+               return
+       }
+       // instance id is irrelevant here
+       resp, err := i.client.Query(namespace, cloudRegion, apiVersion, kind, name, labels, "query")
+       if err != nil {
+               log.Error("Error getting Query results", log.Fields{
+                       "error":       err,
+                       "cloudRegion": cloudRegion,
+                       "namespace":   namespace,
+                       "apiVersion":  apiVersion,
+                       "kind":        kind,
+                       "name":        name,
+                       "labels":      labels,
+               })
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+       }
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusOK)
+       err = json.NewEncoder(w).Encode(resp)
+       if err != nil {
+               log.Error("Error Marshaling Response", log.Fields{
+                       "error":    err,
+                       "response": resp,
+               })
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
index 2b7346b..c37354e 100644 (file)
@@ -40,7 +40,7 @@ func main() {
 
        rand.Seed(time.Now().UnixNano())
 
-       httpRouter := api.NewRouter(nil, nil, nil, nil, nil, nil, nil)
+       httpRouter := api.NewRouter(nil, nil, nil, nil, nil, nil, nil, nil)
        loggedRouter := handlers.LoggingHandler(os.Stdout, httpRouter)
        log.Println("Starting Kubernetes Multicloud API")
 
index 00fd8e9..4c5f7e1 100644 (file)
@@ -1,6 +1,7 @@
 /*
 Copyright 2018 Intel Corporation.
 Copyright © 2021 Samsung Electronics
+Copyright © 2021 Orange
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -121,9 +122,11 @@ func (k *KubernetesClient) queryResources(apiVersion, kind, labelSelector, names
                return nil, pkgerrors.Wrap(err, "Querying for resources")
        }
 
-       resp := make([]ResourceStatus, len(unstrList.Items))
+       resp := make([]ResourceStatus, 0)
        for _, unstr := range unstrList.Items {
-               resp = append(resp, ResourceStatus{unstr.GetName(), gvk, unstr})
+               if unstr.GetName() != "" {
+                       resp = append(resp, ResourceStatus{unstr.GetName(), gvk, unstr})
+               }
        }
        return resp, nil
 }
index c1ec35b..01d924f 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright 2018 Intel Corporation, Inc
  * Copyright © 2021 Samsung Electronics
+ * Copyright © 2021 Orange
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,13 +20,13 @@ package app
 
 import (
        "encoding/json"
+       "log"
+       "strings"
+
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/db"
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/helm"
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/namegenerator"
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/rb"
-       "k8s.io/apimachinery/pkg/runtime/schema"
-       "log"
-       "strings"
 
        pkgerrors "github.com/pkg/errors"
 )
@@ -214,6 +215,7 @@ func (v *InstanceClient) Get(id string) (InstanceResponse, error) {
 // Query returns state of instance's filtered resources
 func (v *InstanceClient) Query(id, apiVersion, kind, name, labels string) (InstanceStatus, error) {
 
+       queryClient := NewQueryClient()
        //Read the status from the DB
        key := InstanceKey{
                ID: id,
@@ -231,47 +233,15 @@ func (v *InstanceClient) Query(id, apiVersion, kind, name, labels string) (Insta
                return InstanceStatus{}, pkgerrors.Wrap(err, "Unmarshaling Instance Value")
        }
 
-       k8sClient := KubernetesClient{}
-       err = k8sClient.Init(resResp.Request.CloudRegion, id)
+       resources, err := queryClient.Query(resResp.Namespace, resResp.Request.CloudRegion, apiVersion, kind, name, labels, id)
        if err != nil {
-               return InstanceStatus{}, pkgerrors.Wrap(err, "Getting CloudRegion Information")
-       }
-
-       var resourcesStatus []ResourceStatus
-       if labels != "" {
-               resList, err := k8sClient.queryResources(apiVersion, kind, labels, resResp.Namespace)
-               if err != nil {
-                       return InstanceStatus{}, pkgerrors.Wrap(err, "Querying Resources")
-               }
-               // If user specifies both label and name, we want to pick up only single resource from these matching label
-               if name != "" {
-                       //Assigning 0-length, because we may actually not find matching name
-                       resourcesStatus = make([]ResourceStatus, 0)
-                       for _, res := range resList {
-                               if res.Name == name {
-                                       resourcesStatus = append(resourcesStatus, res)
-                                       break
-                               }
-                       }
-               } else {
-                       resourcesStatus = resList
-               }
-       } else if name != "" {
-               resIdentifier := helm.KubernetesResource{
-                       Name: name,
-                       GVK:  schema.FromAPIVersionAndKind(apiVersion, kind),
-               }
-               res, err := k8sClient.GetResourceStatus(resIdentifier, resResp.Namespace)
-               if err != nil {
-                       return InstanceStatus{}, pkgerrors.Wrap(err, "Querying Resource")
-               }
-               resourcesStatus = []ResourceStatus{res}
+               return InstanceStatus{}, pkgerrors.Wrap(err, "Querying Resources")
        }
 
        resp := InstanceStatus{
                Request:         resResp.Request,
-               ResourceCount:   int32(len(resourcesStatus)),
-               ResourcesStatus: resourcesStatus,
+               ResourceCount:   resources.ResourceCount,
+               ResourcesStatus: resources.ResourcesStatus,
        }
        return resp, nil
 }
diff --git a/src/k8splugin/internal/app/query.go b/src/k8splugin/internal/app/query.go
new file mode 100644 (file)
index 0000000..cb645af
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ * Copyright © 2021 Samsung Electronics
+ * Copyright © 2021 Orange
+ *
+ * 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 app
+
+import (
+       "github.com/onap/multicloud-k8s/src/k8splugin/internal/helm"
+       "k8s.io/apimachinery/pkg/runtime/schema"
+
+       pkgerrors "github.com/pkg/errors"
+)
+
+// QueryStatus is what is returned when status is queried for an instance
+type QueryStatus struct {
+       ResourceCount   int32            `json:"resourceCount"`
+       ResourcesStatus []ResourceStatus `json:"resourcesStatus"`
+}
+
+// QueryManager is an interface exposes the instantiation functionality
+type QueryManager interface {
+       Query(namespace, cloudRegion, apiVersion, kind, name, labels, id string) (QueryStatus, error)
+}
+
+// QueryClient implements the InstanceManager interface
+// It will also be used to maintain some localized state
+type QueryClient struct {
+       storeName string
+       tagInst   string
+}
+
+// NewQueryClient returns an instance of the QueryClient
+// which implements the InstanceManager
+func NewQueryClient() *QueryClient {
+       return &QueryClient{
+               storeName: "rbdef",
+               tagInst:   "instance",
+       }
+}
+
+// Query returns state of instance's filtered resources
+func (v *QueryClient) Query(namespace, cloudRegion, apiVersion, kind, name, labels, id string) (QueryStatus, error) {
+
+       //Read the status from the DD
+
+       k8sClient := KubernetesClient{}
+       err := k8sClient.Init(cloudRegion, id)
+       if err != nil {
+               return QueryStatus{}, pkgerrors.Wrap(err, "Getting CloudRegion Information")
+       }
+
+       var resourcesStatus []ResourceStatus
+       if labels != "" {
+               resList, err := k8sClient.queryResources(apiVersion, kind, labels, namespace)
+               if err != nil {
+                       return QueryStatus{}, pkgerrors.Wrap(err, "Querying Resources")
+               }
+               // If user specifies both label and name, we want to pick up only single resource from these matching label
+               if name != "" {
+                       //Assigning 0-length, because we may actually not find matching name
+                       resourcesStatus = make([]ResourceStatus, 0)
+                       for _, res := range resList {
+                               if res.Name == name {
+                                       resourcesStatus = append(resourcesStatus, res)
+                                       break
+                               }
+                       }
+               } else {
+                       resourcesStatus = resList
+               }
+       } else if name != "" {
+               resIdentifier := helm.KubernetesResource{
+                       Name: name,
+                       GVK:  schema.FromAPIVersionAndKind(apiVersion, kind),
+               }
+               res, err := k8sClient.GetResourceStatus(resIdentifier, namespace)
+               if err != nil {
+                       return QueryStatus{}, pkgerrors.Wrap(err, "Querying Resource")
+               }
+               resourcesStatus = []ResourceStatus{res}
+       } else {
+               resList, err := k8sClient.queryResources(apiVersion, kind, labels, namespace)
+               if err != nil {
+                       return QueryStatus{}, pkgerrors.Wrap(err, "Querying Resources")
+               }
+               resourcesStatus = resList
+       }
+
+       resp := QueryStatus{
+               ResourceCount:   int32(len(resourcesStatus)),
+               ResourcesStatus: resourcesStatus,
+       }
+       return resp, nil
+}