Add v2 cluster registration api to k8s orchestrator 41/102441/4
authorEric Multanen <eric.w.multanen@intel.com>
Fri, 21 Feb 2020 06:41:34 +0000 (22:41 -0800)
committerEric Multanen <eric.w.multanen@intel.com>
Fri, 28 Feb 2020 17:50:10 +0000 (09:50 -0800)
Adds orchestrator API support for creating
cluster-providers, registering clusters and their
kubeconfig, adding labels to cluster and key value
paisr to clusters.
See: https://wiki.onap.org/display/DW/V2+API+Specification#V2APISpecification-ClusterRegistrationAPI

Issue-ID: MULTICLOUD-922
Signed-off-by: Eric Multanen <eric.w.multanen@intel.com>
Change-Id: I775a308e9ffb19acf65cb0e5752705998bc5b659

src/orchestrator/api/api.go
src/orchestrator/api/clusterhandler.go [new file with mode: 0644]
src/orchestrator/api/clusterhandler_test.go [new file with mode: 0644]
src/orchestrator/api/controllerhandler_test.go
src/orchestrator/api/projecthandler_test.go
src/orchestrator/cmd/main.go
src/orchestrator/pkg/module/cluster.go [new file with mode: 0644]
src/orchestrator/pkg/module/module.go

index f034243..5e05b31 100644 (file)
@@ -22,8 +22,7 @@ import (
 var moduleClient *moduleLib.Client
 
 // NewRouter creates a router that registers the various urls that are supported
-
-func NewRouter(projectClient moduleLib.ProjectManager, compositeAppClient moduleLib.CompositeAppManager, ControllerClient moduleLib.ControllerManager) *mux.Router {
+func NewRouter(projectClient moduleLib.ProjectManager, compositeAppClient moduleLib.CompositeAppManager, ControllerClient moduleLib.ControllerManager, clusterClient moduleLib.ClusterManager) *mux.Router {
 
        router := mux.NewRouter().PathPrefix("/v2").Subrouter()
        moduleClient = moduleLib.NewClient()
@@ -39,6 +38,12 @@ func NewRouter(projectClient moduleLib.ProjectManager, compositeAppClient module
        controlHandler := controllerHandler{
                client: ControllerClient,
        }
+       if clusterClient == nil {
+               clusterClient = moduleClient.Cluster
+       }
+       clusterHandler := clusterHandler{
+               client: clusterClient,
+       }
        router.HandleFunc("/projects", projHandler.createHandler).Methods("POST")
        router.HandleFunc("/projects/{project-name}", projHandler.getHandler).Methods("GET")
        router.HandleFunc("/projects/{project-name}", projHandler.deleteHandler).Methods("DELETE")
@@ -58,5 +63,22 @@ func NewRouter(projectClient moduleLib.ProjectManager, compositeAppClient module
        router.HandleFunc("/controllers", controlHandler.createHandler).Methods("PUT")
        router.HandleFunc("/controllers/{controller-name}", controlHandler.getHandler).Methods("GET")
        router.HandleFunc("/controllers/{controller-name}", controlHandler.deleteHandler).Methods("DELETE")
+       router.HandleFunc("/cluster-providers", clusterHandler.createClusterProviderHandler).Methods("POST")
+       router.HandleFunc("/cluster-providers", clusterHandler.getClusterProviderHandler).Methods("GET")
+       router.HandleFunc("/cluster-providers/{name}", clusterHandler.getClusterProviderHandler).Methods("GET")
+       router.HandleFunc("/cluster-providers/{name}", clusterHandler.deleteClusterProviderHandler).Methods("DELETE")
+       router.HandleFunc("/cluster-providers/{provider-name}/clusters", clusterHandler.createClusterHandler).Methods("POST")
+       router.HandleFunc("/cluster-providers/{provider-name}/clusters", clusterHandler.getClusterHandler).Methods("GET")
+       router.HandleFunc("/cluster-providers/{provider-name}/clusters/{name}", clusterHandler.getClusterHandler).Methods("GET")
+       router.HandleFunc("/cluster-providers/{provider-name}/clusters/{name}", clusterHandler.deleteClusterHandler).Methods("DELETE")
+       router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/labels", clusterHandler.createClusterLabelHandler).Methods("POST")
+       router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/labels", clusterHandler.getClusterLabelHandler).Methods("GET")
+       router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/labels/{label}", clusterHandler.getClusterLabelHandler).Methods("GET")
+       router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/labels/{label}", clusterHandler.deleteClusterLabelHandler).Methods("DELETE")
+       router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/kv-pairs", clusterHandler.createClusterKvPairsHandler).Methods("POST")
+       router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/kv-pairs", clusterHandler.getClusterKvPairsHandler).Methods("GET")
+       router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/kv-pairs/{kvpair}", clusterHandler.getClusterKvPairsHandler).Methods("GET")
+       router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/kv-pairs/{kvpair}", clusterHandler.deleteClusterKvPairsHandler).Methods("DELETE")
+
        return router
 }
diff --git a/src/orchestrator/api/clusterhandler.go b/src/orchestrator/api/clusterhandler.go
new file mode 100644 (file)
index 0000000..7e769f3
--- /dev/null
@@ -0,0 +1,468 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * 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 (
+       "bytes"
+       "encoding/base64"
+       "encoding/json"
+       "io"
+       "io/ioutil"
+       "mime"
+       "mime/multipart"
+       "net/http"
+       "net/textproto"
+
+       moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
+
+       "github.com/gorilla/mux"
+)
+
+// Used to store backend implementations objects
+// Also simplifies mocking for unit testing purposes
+type clusterHandler struct {
+       // Interface that implements Cluster operations
+       // We will set this variable with a mock interface for testing
+       client moduleLib.ClusterManager
+}
+
+// Create handles creation of the ClusterProvider entry in the database
+func (h clusterHandler) createClusterProviderHandler(w http.ResponseWriter, r *http.Request) {
+       var p moduleLib.ClusterProvider
+
+       err := json.NewDecoder(r.Body).Decode(&p)
+
+       switch {
+       case err == io.EOF:
+               http.Error(w, "Empty body", http.StatusBadRequest)
+               return
+       case err != nil:
+               http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+               return
+       }
+
+       // Name is required.
+       if p.Metadata.Name == "" {
+               http.Error(w, "Missing name in POST request", http.StatusBadRequest)
+               return
+       }
+
+       ret, err := h.client.CreateClusterProvider(p)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusCreated)
+       err = json.NewEncoder(w).Encode(ret)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
+
+// Get handles GET operations on a particular ClusterProvider Name
+// Returns a ClusterProvider
+func (h clusterHandler) getClusterProviderHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       name := vars["name"]
+       var ret interface{}
+       var err error
+
+       if len(name) == 0 {
+               ret, err = h.client.GetClusterProviders()
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+       } else {
+               ret, err = h.client.GetClusterProvider(name)
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+       }
+
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusOK)
+       err = json.NewEncoder(w).Encode(ret)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
+
+// Delete handles DELETE operations on a particular ClusterProvider  Name
+func (h clusterHandler) deleteClusterProviderHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       name := vars["name"]
+
+       err := h.client.DeleteClusterProvider(name)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       w.WriteHeader(http.StatusNoContent)
+}
+
+// Create handles creation of the Cluster entry in the database
+func (h clusterHandler) createClusterHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       provider := vars["provider-name"]
+       var p moduleLib.Cluster
+       var q moduleLib.ClusterContent
+
+       // Implemenation using multipart form
+       // Review and enable/remove at a later date
+       // Set Max size to 16mb here
+       err := r.ParseMultipartForm(16777216)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+               return
+       }
+
+       jsn := bytes.NewBuffer([]byte(r.FormValue("metadata")))
+       err = json.NewDecoder(jsn).Decode(&p)
+       switch {
+       case err == io.EOF:
+               http.Error(w, "Empty body", http.StatusBadRequest)
+               return
+       case err != nil:
+               http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+               return
+       }
+
+       //Read the file section and ignore the header
+       file, _, err := r.FormFile("file")
+       if err != nil {
+               http.Error(w, "Unable to process file", http.StatusUnprocessableEntity)
+               return
+       }
+
+       defer file.Close()
+
+       //Convert the file content to base64 for storage
+       content, err := ioutil.ReadAll(file)
+       if err != nil {
+               http.Error(w, "Unable to read file", http.StatusUnprocessableEntity)
+               return
+       }
+
+       q.Kubeconfig = base64.StdEncoding.EncodeToString(content)
+
+       // Name is required.
+       if p.Metadata.Name == "" {
+               http.Error(w, "Missing name in POST request", http.StatusBadRequest)
+               return
+       }
+
+       ret, err := h.client.CreateCluster(provider, p, q)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       //      w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusCreated)
+       err = json.NewEncoder(w).Encode(ret)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
+
+// Get handles GET operations on a particular Cluster Name
+// Returns a Cluster
+func (h clusterHandler) getClusterHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       provider := vars["provider-name"]
+       name := vars["name"]
+
+       // handle the get all clusters case - return a list of only the json parts
+       if len(name) == 0 {
+               var retList []moduleLib.Cluster
+
+               ret, err := h.client.GetClusters(provider)
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+
+               for _, cl := range ret {
+                       retList = append(retList, moduleLib.Cluster{Metadata: cl.Metadata})
+               }
+
+               w.Header().Set("Content-Type", "application/json")
+               w.WriteHeader(http.StatusOK)
+               err = json.NewEncoder(w).Encode(retList)
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+               return
+       }
+
+       accepted, _, err := mime.ParseMediaType(r.Header.Get("Accept"))
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusNotAcceptable)
+               return
+       }
+
+       retCluster, err := h.client.GetCluster(provider, name)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       retKubeconfig, err := h.client.GetClusterContent(provider, name)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       switch accepted {
+       case "multipart/form-data":
+               mpw := multipart.NewWriter(w)
+               w.Header().Set("Content-Type", mpw.FormDataContentType())
+               w.WriteHeader(http.StatusOK)
+               pw, err := mpw.CreatePart(textproto.MIMEHeader{"Content-Type": {"application/json"}, "Content-Disposition": {"form-data; name=metadata"}})
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+               if err := json.NewEncoder(pw).Encode(retCluster); err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+               pw, err = mpw.CreatePart(textproto.MIMEHeader{"Content-Type": {"application/octet-stream"}, "Content-Disposition": {"form-data; name=file; filename=kubeconfig"}})
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+               kc_bytes, err := base64.StdEncoding.DecodeString(retKubeconfig.Kubeconfig)
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+               _, err = pw.Write(kc_bytes)
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+       case "application/json":
+               w.Header().Set("Content-Type", "application/json")
+               w.WriteHeader(http.StatusOK)
+               err = json.NewEncoder(w).Encode(retCluster)
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+       case "application/octet-stream":
+               w.Header().Set("Content-Type", "application/octet-stream")
+               w.WriteHeader(http.StatusOK)
+               kc_bytes, err := base64.StdEncoding.DecodeString(retKubeconfig.Kubeconfig)
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+               _, err = w.Write(kc_bytes)
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+       default:
+               http.Error(w, "set Accept: multipart/form-data, application/json or application/octet-stream", http.StatusMultipleChoices)
+               return
+       }
+}
+
+// Delete handles DELETE operations on a particular Cluster Name
+func (h clusterHandler) deleteClusterHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       provider := vars["provider-name"]
+       name := vars["name"]
+
+       err := h.client.DeleteCluster(provider, name)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       w.WriteHeader(http.StatusNoContent)
+}
+
+// Create handles creation of the ClusterLabel entry in the database
+func (h clusterHandler) createClusterLabelHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       provider := vars["provider-name"]
+       cluster := vars["cluster-name"]
+       var p moduleLib.ClusterLabel
+
+       err := json.NewDecoder(r.Body).Decode(&p)
+
+       // LabelName is required.
+       if p.LabelName == "" {
+               http.Error(w, "Missing label name in POST request", http.StatusBadRequest)
+               return
+       }
+
+       ret, err := h.client.CreateClusterLabel(provider, cluster, p)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusCreated)
+       err = json.NewEncoder(w).Encode(ret)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
+
+// Get handles GET operations on a particular Cluster Label
+// Returns a ClusterLabel
+func (h clusterHandler) getClusterLabelHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       provider := vars["provider-name"]
+       cluster := vars["cluster-name"]
+       label := vars["label"]
+
+       var ret interface{}
+       var err error
+
+       if len(label) == 0 {
+               ret, err = h.client.GetClusterLabels(provider, cluster)
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+       } else {
+               ret, err = h.client.GetClusterLabel(provider, cluster, label)
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+       }
+
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusOK)
+       err = json.NewEncoder(w).Encode(ret)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
+
+// Delete handles DELETE operations on a particular ClusterLabel Name
+func (h clusterHandler) deleteClusterLabelHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       provider := vars["provider-name"]
+       cluster := vars["cluster-name"]
+       label := vars["label"]
+
+       err := h.client.DeleteClusterLabel(provider, cluster, label)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       w.WriteHeader(http.StatusNoContent)
+}
+
+// Create handles creation of the ClusterKvPairs entry in the database
+func (h clusterHandler) createClusterKvPairsHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       provider := vars["provider-name"]
+       cluster := vars["cluster-name"]
+       var p moduleLib.ClusterKvPairs
+
+       err := json.NewDecoder(r.Body).Decode(&p)
+
+       // KvPairsName is required.
+       if p.Metadata.Name == "" {
+               http.Error(w, "Missing Key Value pair name in POST request", http.StatusBadRequest)
+               return
+       }
+
+       ret, err := h.client.CreateClusterKvPairs(provider, cluster, p)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusCreated)
+       err = json.NewEncoder(w).Encode(ret)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
+
+// Get handles GET operations on a particular Cluster Key Value Pair
+// Returns a ClusterKvPairs
+func (h clusterHandler) getClusterKvPairsHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       provider := vars["provider-name"]
+       cluster := vars["cluster-name"]
+       kvpair := vars["kvpair"]
+
+       var ret interface{}
+       var err error
+
+       if len(kvpair) == 0 {
+               ret, err = h.client.GetAllClusterKvPairs(provider, cluster)
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+       } else {
+               ret, err = h.client.GetClusterKvPairs(provider, cluster, kvpair)
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+       }
+
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusOK)
+       err = json.NewEncoder(w).Encode(ret)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
+
+// Delete handles DELETE operations on a particular Cluster Name
+func (h clusterHandler) deleteClusterKvPairsHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       provider := vars["provider-name"]
+       cluster := vars["cluster-name"]
+       kvpair := vars["kvpair"]
+
+       err := h.client.DeleteClusterKvPairs(provider, cluster, kvpair)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       w.WriteHeader(http.StatusNoContent)
+}
diff --git a/src/orchestrator/api/clusterhandler_test.go b/src/orchestrator/api/clusterhandler_test.go
new file mode 100644 (file)
index 0000000..db1b6f9
--- /dev/null
@@ -0,0 +1,1412 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * 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 (
+       "bytes"
+       "encoding/json"
+       "io"
+       "mime/multipart"
+       "net/http"
+       "net/http/httptest"
+       "net/textproto"
+       "reflect"
+       "testing"
+
+       moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
+
+       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 mockClusterManager struct {
+       // Items and err will be used to customize each test
+       // via a localized instantiation of mockClusterManager
+       ClusterProviderItems []moduleLib.ClusterProvider
+       ClusterItems         []moduleLib.Cluster
+       ClusterContentItems  []moduleLib.ClusterContent
+       ClusterLabelItems    []moduleLib.ClusterLabel
+       ClusterKvPairsItems  []moduleLib.ClusterKvPairs
+       Err                  error
+}
+
+func (m *mockClusterManager) CreateClusterProvider(inp moduleLib.ClusterProvider) (moduleLib.ClusterProvider, error) {
+       if m.Err != nil {
+               return moduleLib.ClusterProvider{}, m.Err
+       }
+
+       return m.ClusterProviderItems[0], nil
+}
+
+func (m *mockClusterManager) GetClusterProvider(name string) (moduleLib.ClusterProvider, error) {
+       if m.Err != nil {
+               return moduleLib.ClusterProvider{}, m.Err
+       }
+
+       return m.ClusterProviderItems[0], nil
+}
+
+func (m *mockClusterManager) GetClusterProviders() ([]moduleLib.ClusterProvider, error) {
+       if m.Err != nil {
+               return []moduleLib.ClusterProvider{}, m.Err
+       }
+
+       return m.ClusterProviderItems, nil
+}
+
+func (m *mockClusterManager) DeleteClusterProvider(name string) error {
+       return m.Err
+}
+
+func (m *mockClusterManager) CreateCluster(provider string, inp moduleLib.Cluster, inq moduleLib.ClusterContent) (moduleLib.Cluster, error) {
+       if m.Err != nil {
+               return moduleLib.Cluster{}, m.Err
+       }
+
+       return m.ClusterItems[0], nil
+}
+
+func (m *mockClusterManager) GetCluster(provider, name string) (moduleLib.Cluster, error) {
+       if m.Err != nil {
+               return moduleLib.Cluster{}, m.Err
+       }
+
+       return m.ClusterItems[0], nil
+}
+
+func (m *mockClusterManager) GetClusterContent(provider, name string) (moduleLib.ClusterContent, error) {
+       if m.Err != nil {
+               return moduleLib.ClusterContent{}, m.Err
+       }
+
+       return m.ClusterContentItems[0], nil
+}
+
+func (m *mockClusterManager) GetClusters(provider string) ([]moduleLib.Cluster, error) {
+       if m.Err != nil {
+               return []moduleLib.Cluster{}, m.Err
+       }
+
+       return m.ClusterItems, nil
+}
+
+func (m *mockClusterManager) DeleteCluster(provider, name string) error {
+       return m.Err
+}
+
+func (m *mockClusterManager) CreateClusterLabel(provider, cluster string, inp moduleLib.ClusterLabel) (moduleLib.ClusterLabel, error) {
+       if m.Err != nil {
+               return moduleLib.ClusterLabel{}, m.Err
+       }
+
+       return m.ClusterLabelItems[0], nil
+}
+
+func (m *mockClusterManager) GetClusterLabel(provider, cluster, label string) (moduleLib.ClusterLabel, error) {
+       if m.Err != nil {
+               return moduleLib.ClusterLabel{}, m.Err
+       }
+
+       return m.ClusterLabelItems[0], nil
+}
+
+func (m *mockClusterManager) GetClusterLabels(provider, cluster string) ([]moduleLib.ClusterLabel, error) {
+       if m.Err != nil {
+               return []moduleLib.ClusterLabel{}, m.Err
+       }
+
+       return m.ClusterLabelItems, nil
+}
+
+func (m *mockClusterManager) DeleteClusterLabel(provider, cluster, label string) error {
+       return m.Err
+}
+
+func (m *mockClusterManager) CreateClusterKvPairs(provider, cluster string, inp moduleLib.ClusterKvPairs) (moduleLib.ClusterKvPairs, error) {
+       if m.Err != nil {
+               return moduleLib.ClusterKvPairs{}, m.Err
+       }
+
+       return m.ClusterKvPairsItems[0], nil
+}
+
+func (m *mockClusterManager) GetClusterKvPairs(provider, cluster, kvpair string) (moduleLib.ClusterKvPairs, error) {
+       if m.Err != nil {
+               return moduleLib.ClusterKvPairs{}, m.Err
+       }
+
+       return m.ClusterKvPairsItems[0], nil
+}
+
+func (m *mockClusterManager) GetAllClusterKvPairs(provider, cluster string) ([]moduleLib.ClusterKvPairs, error) {
+       if m.Err != nil {
+               return []moduleLib.ClusterKvPairs{}, m.Err
+       }
+
+       return m.ClusterKvPairsItems, nil
+}
+
+func (m *mockClusterManager) DeleteClusterKvPairs(provider, cluster, kvpair string) error {
+       return m.Err
+}
+
+func TestClusterProviderCreateHandler(t *testing.T) {
+       testCases := []struct {
+               label         string
+               reader        io.Reader
+               expected      moduleLib.ClusterProvider
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:         "Missing Cluster Provider Body Failure",
+                       expectedCode:  http.StatusBadRequest,
+                       clusterClient: &mockClusterManager{},
+               },
+               {
+                       label:        "Create Cluster Provider",
+                       expectedCode: http.StatusCreated,
+                       reader: bytes.NewBuffer([]byte(`{
+                                       "metadata": {
+                                               "name": "clusterProviderTest",
+                                               "description": "testClusterProvider",
+                                               "userData1": "some user data 1",
+                                               "userData2": "some user data 2"
+                                       }
+                               }`)),
+                       expected: moduleLib.ClusterProvider{
+                               Metadata: moduleLib.Metadata{
+                                       Name:        "clusterProviderTest",
+                                       Description: "testClusterProvider",
+                                       UserData1:   "some user data 1",
+                                       UserData2:   "some user data 2",
+                               },
+                       },
+                       clusterClient: &mockClusterManager{
+                               //Items that will be returned by the mocked Client
+                               ClusterProviderItems: []moduleLib.ClusterProvider{
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "clusterProviderTest",
+                                                       Description: "testClusterProvider",
+                                                       UserData1:   "some user data 1",
+                                                       UserData2:   "some user data 2",
+                                               },
+                                       },
+                               },
+                       },
+               },
+               {
+                       label: "Missing ClusterProvider Name in Request Body",
+                       reader: bytes.NewBuffer([]byte(`{
+                                       "metadata": {
+                                               "description": "this is a test cluster provider",
+                                               "userData1": "some user data 1",
+                                               "userData2": "some user data 2"
+                                       }
+                               }`)),
+                       expectedCode:  http.StatusBadRequest,
+                       clusterClient: &mockClusterManager{},
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("POST", "/v2/cluster-providers", testCase.reader)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusCreated
+                       if resp.StatusCode == http.StatusCreated {
+                               got := moduleLib.ClusterProvider{}
+                               json.NewDecoder(resp.Body).Decode(&got)
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("createHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
+func TestClusterProviderGetAllHandler(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               expected      []moduleLib.ClusterProvider
+               name, version string
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:        "Get Cluster Provider",
+                       expectedCode: http.StatusOK,
+                       expected: []moduleLib.ClusterProvider{
+                               {
+                                       Metadata: moduleLib.Metadata{
+                                               Name:        "testClusterProvider1",
+                                               Description: "testClusterProvider 1 description",
+                                               UserData1:   "some user data 1",
+                                               UserData2:   "some user data 2",
+                                       },
+                               },
+                               {
+                                       Metadata: moduleLib.Metadata{
+                                               Name:        "testClusterProvider2",
+                                               Description: "testClusterProvider 2 description",
+                                               UserData1:   "some user data A",
+                                               UserData2:   "some user data B",
+                                       },
+                               },
+                       },
+                       clusterClient: &mockClusterManager{
+                               //Items that will be returned by the mocked Client
+                               ClusterProviderItems: []moduleLib.ClusterProvider{
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "testClusterProvider1",
+                                                       Description: "testClusterProvider 1 description",
+                                                       UserData1:   "some user data 1",
+                                                       UserData2:   "some user data 2",
+                                               },
+                                       },
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "testClusterProvider2",
+                                                       Description: "testClusterProvider 2 description",
+                                                       UserData1:   "some user data A",
+                                                       UserData2:   "some user data B",
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("GET", "/v2/cluster-providers", nil)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusOK
+                       if resp.StatusCode == http.StatusOK {
+                               got := []moduleLib.ClusterProvider{}
+                               json.NewDecoder(resp.Body).Decode(&got)
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("listHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
+func TestClusterProviderGetHandler(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               expected      moduleLib.ClusterProvider
+               name, version string
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:        "Get Cluster Provider",
+                       expectedCode: http.StatusOK,
+                       expected: moduleLib.ClusterProvider{
+                               Metadata: moduleLib.Metadata{
+                                       Name:        "testClusterProvider",
+                                       Description: "testClusterProvider description",
+                                       UserData1:   "some user data 1",
+                                       UserData2:   "some user data 2",
+                               },
+                       },
+                       name: "testClusterProvider",
+                       clusterClient: &mockClusterManager{
+                               //Items that will be returned by the mocked Client
+                               ClusterProviderItems: []moduleLib.ClusterProvider{
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "testClusterProvider",
+                                                       Description: "testClusterProvider description",
+                                                       UserData1:   "some user data 1",
+                                                       UserData2:   "some user data 2",
+                                               },
+                                       },
+                               },
+                       },
+               },
+               {
+                       label:        "Get Non-Existing Cluster Provider",
+                       expectedCode: http.StatusInternalServerError,
+                       name:         "nonexistingclusterprovider",
+                       clusterClient: &mockClusterManager{
+                               ClusterProviderItems: []moduleLib.ClusterProvider{},
+                               Err:                  pkgerrors.New("Internal Error"),
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("GET", "/v2/cluster-providers/"+testCase.name, nil)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusOK
+                       if resp.StatusCode == http.StatusOK {
+                               got := moduleLib.ClusterProvider{}
+                               json.NewDecoder(resp.Body).Decode(&got)
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("listHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
+func TestClusterProviderDeleteHandler(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               name          string
+               version       string
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:         "Delete Cluster Provider",
+                       expectedCode:  http.StatusNoContent,
+                       name:          "testClusterProvider",
+                       clusterClient: &mockClusterManager{},
+               },
+               {
+                       label:        "Delete Non-Existing Cluster Provider",
+                       expectedCode: http.StatusInternalServerError,
+                       name:         "testClusterProvider",
+                       clusterClient: &mockClusterManager{
+                               Err: pkgerrors.New("Internal Error"),
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("DELETE", "/v2/cluster-providers/"+testCase.name, nil)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+               })
+       }
+}
+
+func TestClusterCreateHandler(t *testing.T) {
+       testCases := []struct {
+               label         string
+               metadata      string
+               kubeconfig    string
+               expected      moduleLib.Cluster
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:         "Missing Cluster Body Failure",
+                       expectedCode:  http.StatusBadRequest,
+                       clusterClient: &mockClusterManager{},
+               },
+               {
+                       label:        "Create Cluster",
+                       expectedCode: http.StatusCreated,
+                       metadata: `
+{
+ "metadata": {
+  "name": "clusterTest",
+  "description": "this is test cluster",
+  "userData1": "some cluster data abc",
+  "userData2": "some cluster data def"
+ }
+}`,
+                       kubeconfig: `test contents
+of a file attached
+to the creation
+of clusterTest
+`,
+                       expected: moduleLib.Cluster{
+                               Metadata: moduleLib.Metadata{
+                                       Name:        "clusterTest",
+                                       Description: "testCluster",
+                                       UserData1:   "some user data 1",
+                                       UserData2:   "some user data 2",
+                               },
+                       },
+                       clusterClient: &mockClusterManager{
+                               //Items that will be returned by the mocked Client
+                               ClusterProviderItems: []moduleLib.ClusterProvider{
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "clusterProvider1",
+                                                       Description: "ClusterProvider 1 description",
+                                                       UserData1:   "some user data 1",
+                                                       UserData2:   "some user data 2",
+                                               },
+                                       },
+                               },
+                               ClusterItems: []moduleLib.Cluster{
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "clusterTest",
+                                                       Description: "testCluster",
+                                                       UserData1:   "some user data 1",
+                                                       UserData2:   "some user data 2",
+                                               },
+                                       },
+                               },
+                               ClusterContentItems: []moduleLib.ClusterContent{
+                                       {
+                                               Kubeconfig: "dGVzdCBjb250ZW50cwpvZiBhIGZpbGUgYXR0YWNoZWQKdG8gdGhlIGNyZWF0aW9uCm9mIGNsdXN0ZXJUZXN0Cg==",
+                                       },
+                               },
+                       },
+               },
+               {
+                       label:        "Missing Cluster Name in Request Body",
+                       expectedCode: http.StatusBadRequest,
+                       metadata: `
+{
+ "metadata": {
+  "description": "this is test cluster",
+  "userData1": "some cluster data abc",
+  "userData2": "some cluster data def"
+ }
+}`,
+                       kubeconfig: `test contents
+of a file attached
+to the creation
+of clusterTest
+`,
+                       clusterClient: &mockClusterManager{},
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       // Create the multipart test Request body
+                       body := new(bytes.Buffer)
+                       multiwr := multipart.NewWriter(body)
+                       multiwr.SetBoundary("------------------------f77f80a7092eb312")
+                       pw, _ := multiwr.CreatePart(textproto.MIMEHeader{"Content-Type": {"application/json"}, "Content-Disposition": {"form-data; name=metadata"}})
+                       pw.Write([]byte(testCase.metadata))
+                       pw, _ = multiwr.CreateFormFile("file", "kubeconfig")
+                       pw.Write([]byte(testCase.kubeconfig))
+                       multiwr.Close()
+
+                       request := httptest.NewRequest("POST", "/v2/cluster-providers/clusterProvider1/clusters", bytes.NewBuffer(body.Bytes()))
+                       request.Header.Set("Content-Type", multiwr.FormDataContentType())
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusCreated
+                       if resp.StatusCode == http.StatusCreated {
+                               got := moduleLib.Cluster{}
+                               json.NewDecoder(resp.Body).Decode(&got)
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("createHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
+func TestClusterGetAllHandler(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               expected      []moduleLib.Cluster
+               name, version string
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:        "Get Clusters",
+                       expectedCode: http.StatusOK,
+                       expected: []moduleLib.Cluster{
+                               {
+                                       Metadata: moduleLib.Metadata{
+                                               Name:        "testCluster1",
+                                               Description: "testCluster 1 description",
+                                               UserData1:   "some user data 1",
+                                               UserData2:   "some user data 2",
+                                       },
+                               },
+                               {
+                                       Metadata: moduleLib.Metadata{
+                                               Name:        "testCluster2",
+                                               Description: "testCluster 2 description",
+                                               UserData1:   "some user data A",
+                                               UserData2:   "some user data B",
+                                       },
+                               },
+                       },
+                       clusterClient: &mockClusterManager{
+                               //Items that will be returned by the mocked Client
+                               ClusterItems: []moduleLib.Cluster{
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "testCluster1",
+                                                       Description: "testCluster 1 description",
+                                                       UserData1:   "some user data 1",
+                                                       UserData2:   "some user data 2",
+                                               },
+                                       },
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "testCluster2",
+                                                       Description: "testCluster 2 description",
+                                                       UserData1:   "some user data A",
+                                                       UserData2:   "some user data B",
+                                               },
+                                       },
+                               },
+                               ClusterContentItems: []moduleLib.ClusterContent{
+                                       // content here doesn't matter - just needs to be present
+                                       {
+                                               Kubeconfig: "dGVzdCBjb250ZW50cwpvZiBhIGZpbGUgYXR0YWNoZWQKdG8gdGhlIGNyZWF0aW9uCm9mIGNsdXN0ZXJUZXN0Cg==",
+                                       },
+                                       {
+                                               Kubeconfig: "dGVzdCBjb250ZW50cwpvZiBhIGZpbGUgYXR0YWNoZWQKdG8gdGhlIGNyZWF0aW9uCm9mIGNsdXN0ZXJUZXN0Cg==",
+                                       },
+                               },
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("GET", "/v2/cluster-providers/clusterProvder1/clusters", nil)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusOK
+                       if resp.StatusCode == http.StatusOK {
+                               got := []moduleLib.Cluster{}
+                               json.NewDecoder(resp.Body).Decode(&got)
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("listHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
+func TestClusterGetHandler(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               expected      moduleLib.Cluster
+               name, version string
+               accept        string
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:        "Get Cluster with Accept: application/json",
+                       accept:       "application/json",
+                       expectedCode: http.StatusOK,
+                       expected: moduleLib.Cluster{
+                               Metadata: moduleLib.Metadata{
+                                       Name:        "testCluster",
+                                       Description: "testCluster description",
+                                       UserData1:   "some user data 1",
+                                       UserData2:   "some user data 2",
+                               },
+                       },
+                       name: "testCluster",
+                       clusterClient: &mockClusterManager{
+                               //Items that will be returned by the mocked Client
+                               ClusterItems: []moduleLib.Cluster{
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "testCluster",
+                                                       Description: "testCluster description",
+                                                       UserData1:   "some user data 1",
+                                                       UserData2:   "some user data 2",
+                                               },
+                                       },
+                               },
+                               ClusterContentItems: []moduleLib.ClusterContent{
+                                       {
+                                               Kubeconfig: "dGVzdCBjb250ZW50cwpvZiBhIGZpbGUgYXR0YWNoZWQKdG8gdGhlIGNyZWF0aW9uCm9mIGNsdXN0ZXJUZXN0Cg==",
+                                       },
+                               },
+                       },
+               },
+               {
+                       label:        "Get Non-Existing Cluster",
+                       accept:       "application/json",
+                       expectedCode: http.StatusInternalServerError,
+                       name:         "nonexistingcluster",
+                       clusterClient: &mockClusterManager{
+                               ClusterItems: []moduleLib.Cluster{},
+                               Err:          pkgerrors.New("Internal Error"),
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("GET", "/v2/cluster-providers/clusterProvider1/clusters/"+testCase.name, nil)
+                       if len(testCase.accept) > 0 {
+                               request.Header.Set("Accept", testCase.accept)
+                       }
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusOK
+                       if resp.StatusCode == http.StatusOK {
+                               got := moduleLib.Cluster{}
+                               json.NewDecoder(resp.Body).Decode(&got)
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("listHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
+func TestClusterGetContentHandler(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               expected      string
+               name, version string
+               accept        string
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:        "Get Cluster Content with Accept: application/octet-stream",
+                       accept:       "application/octet-stream",
+                       expectedCode: http.StatusOK,
+                       expected: `test contents
+of a file attached
+to the creation
+of clusterTest
+`,
+                       name: "testCluster",
+                       clusterClient: &mockClusterManager{
+                               //Items that will be returned by the mocked Client
+                               ClusterItems: []moduleLib.Cluster{
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "testCluster",
+                                                       Description: "testCluster description",
+                                                       UserData1:   "some user data 1",
+                                                       UserData2:   "some user data 2",
+                                               },
+                                       },
+                               },
+                               ClusterContentItems: []moduleLib.ClusterContent{
+                                       {
+                                               Kubeconfig: "dGVzdCBjb250ZW50cwpvZiBhIGZpbGUgYXR0YWNoZWQKdG8gdGhlIGNyZWF0aW9uCm9mIGNsdXN0ZXJUZXN0Cg==",
+                                       },
+                               },
+                       },
+               },
+               {
+                       label:        "Get Non-Existing Cluster",
+                       accept:       "application/octet-stream",
+                       expectedCode: http.StatusInternalServerError,
+                       name:         "nonexistingcluster",
+                       clusterClient: &mockClusterManager{
+                               ClusterItems: []moduleLib.Cluster{},
+                               Err:          pkgerrors.New("Internal Error"),
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("GET", "/v2/cluster-providers/clusterProvider1/clusters/"+testCase.name, nil)
+                       if len(testCase.accept) > 0 {
+                               request.Header.Set("Accept", testCase.accept)
+                       }
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusOK
+                       if resp.StatusCode == http.StatusOK {
+                               body := new(bytes.Buffer)
+                               body.ReadFrom(resp.Body)
+                               got := body.String()
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("listHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
+func TestClusterDeleteHandler(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               name          string
+               version       string
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:         "Delete Cluster",
+                       expectedCode:  http.StatusNoContent,
+                       name:          "testCluster",
+                       clusterClient: &mockClusterManager{},
+               },
+               {
+                       label:        "Delete Non-Existing Cluster",
+                       expectedCode: http.StatusInternalServerError,
+                       name:         "testCluster",
+                       clusterClient: &mockClusterManager{
+                               Err: pkgerrors.New("Internal Error"),
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("DELETE", "/v2/cluster-providers/clusterProvider1/clusters/"+testCase.name, nil)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+               })
+       }
+}
+
+func TestClusterLabelCreateHandler(t *testing.T) {
+       testCases := []struct {
+               label         string
+               reader        io.Reader
+               expected      moduleLib.ClusterLabel
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:         "Missing Cluster Label Body Failure",
+                       expectedCode:  http.StatusBadRequest,
+                       clusterClient: &mockClusterManager{},
+               },
+               {
+                       label:        "Create Cluster Label",
+                       expectedCode: http.StatusCreated,
+                       reader: bytes.NewBuffer([]byte(`{
+                                       "label-name": "test-label"
+                               }`)),
+                       expected: moduleLib.ClusterLabel{
+                               LabelName: "test-label",
+                       },
+                       clusterClient: &mockClusterManager{
+                               //Items that will be returned by the mocked Client
+                               ClusterLabelItems: []moduleLib.ClusterLabel{
+                                       {
+                                               LabelName: "test-label",
+                                       },
+                               },
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("POST", "/v2/cluster-providers/cp1/clusters/cl1/labels", testCase.reader)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusCreated
+                       if resp.StatusCode == http.StatusCreated {
+                               got := moduleLib.ClusterLabel{}
+                               json.NewDecoder(resp.Body).Decode(&got)
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("createHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
+func TestClusterLabelsGetHandler(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               expected      []moduleLib.ClusterLabel
+               name, version string
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:        "Get Cluster Labels",
+                       expectedCode: http.StatusOK,
+                       expected: []moduleLib.ClusterLabel{
+                               {
+                                       LabelName: "test-label1",
+                               },
+                               {
+                                       LabelName: "test-label-two",
+                               },
+                               {
+                                       LabelName: "test-label-3",
+                               },
+                       },
+                       clusterClient: &mockClusterManager{
+                               //Items that will be returned by the mocked Client
+                               ClusterLabelItems: []moduleLib.ClusterLabel{
+                                       {
+                                               LabelName: "test-label1",
+                                       },
+                                       {
+                                               LabelName: "test-label-two",
+                                       },
+                                       {
+                                               LabelName: "test-label-3",
+                                       },
+                               },
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("GET", "/v2/cluster-providers/cp1/clusters/cl1/labels", nil)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusOK
+                       if resp.StatusCode == http.StatusOK {
+                               got := []moduleLib.ClusterLabel{}
+                               json.NewDecoder(resp.Body).Decode(&got)
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("listHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
+func TestClusterLabelGetHandler(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               expected      moduleLib.ClusterLabel
+               name, version string
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:        "Get Cluster Label",
+                       expectedCode: http.StatusOK,
+                       expected: moduleLib.ClusterLabel{
+                               LabelName: "testlabel",
+                       },
+                       name: "testlabel",
+                       clusterClient: &mockClusterManager{
+                               //Items that will be returned by the mocked Client
+                               ClusterLabelItems: []moduleLib.ClusterLabel{
+                                       {
+                                               LabelName: "testlabel",
+                                       },
+                               },
+                       },
+               },
+               {
+                       label:        "Get Non-Existing Cluster Label",
+                       expectedCode: http.StatusInternalServerError,
+                       name:         "nonexistingclusterlabel",
+                       clusterClient: &mockClusterManager{
+                               ClusterLabelItems: []moduleLib.ClusterLabel{},
+                               Err:               pkgerrors.New("Internal Error"),
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("GET", "/v2/cluster-providers/clusterProvider1/clusters/cl1/labels/"+testCase.name, nil)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusOK
+                       if resp.StatusCode == http.StatusOK {
+                               got := moduleLib.ClusterLabel{}
+                               json.NewDecoder(resp.Body).Decode(&got)
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("listHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
+func TestClusterLabelDeleteHandler(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               name          string
+               version       string
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:         "Delete Cluster Label",
+                       expectedCode:  http.StatusNoContent,
+                       name:          "testClusterLabel",
+                       clusterClient: &mockClusterManager{},
+               },
+               {
+                       label:        "Delete Non-Existing Cluster Label",
+                       expectedCode: http.StatusInternalServerError,
+                       name:         "testClusterLabel",
+                       clusterClient: &mockClusterManager{
+                               Err: pkgerrors.New("Internal Error"),
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("DELETE", "/v2/cluster-providers/cp1/clusters/cl1/labels/"+testCase.name, nil)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+               })
+       }
+}
+
+func TestClusterKvPairsCreateHandler(t *testing.T) {
+       testCases := []struct {
+               label         string
+               reader        io.Reader
+               expected      moduleLib.ClusterKvPairs
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:         "Missing Cluster KvPairs Body Failure",
+                       expectedCode:  http.StatusBadRequest,
+                       clusterClient: &mockClusterManager{},
+               },
+               {
+                       label:        "Create Cluster KvPairs",
+                       expectedCode: http.StatusCreated,
+                       reader: bytes.NewBuffer([]byte(`{
+                                       "metadata": {
+                                               "name": "ClusterKvPair1",
+                                               "description": "test cluster kv pairs",
+                                               "userData1": "some user data 1",
+                                               "userData2": "some user data 2"
+                                       },
+                                       "spec": {
+                                               "kv": [
+                                                       {
+                                                               "key1": "value1"
+                                                       },
+                                                       {
+                                                               "key2": "value2"
+                                                       }
+                                               ]
+                                       }
+                               }`)),
+                       expected: moduleLib.ClusterKvPairs{
+                               Metadata: moduleLib.Metadata{
+                                       Name:        "ClusterKvPair1",
+                                       Description: "test cluster kv pairs",
+                                       UserData1:   "some user data 1",
+                                       UserData2:   "some user data 2",
+                               },
+                               Spec: moduleLib.ClusterKvSpec{
+                                       Kv: []map[string]interface{}{
+                                               {
+                                                       "key1": "value1",
+                                               },
+                                               {
+                                                       "key2": "value2",
+                                               },
+                                       },
+                               },
+                       },
+                       clusterClient: &mockClusterManager{
+                               //Items that will be returned by the mocked Client
+                               ClusterKvPairsItems: []moduleLib.ClusterKvPairs{
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "ClusterKvPair1",
+                                                       Description: "test cluster kv pairs",
+                                                       UserData1:   "some user data 1",
+                                                       UserData2:   "some user data 2",
+                                               },
+                                               Spec: moduleLib.ClusterKvSpec{
+                                                       Kv: []map[string]interface{}{
+                                                               {
+                                                                       "key1": "value1",
+                                                               },
+                                                               {
+                                                                       "key2": "value2",
+                                                               },
+                                                       },
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("POST", "/v2/cluster-providers/cp1/clusters/cl1/kv-pairs", testCase.reader)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusCreated
+                       if resp.StatusCode == http.StatusCreated {
+                               got := moduleLib.ClusterKvPairs{}
+                               json.NewDecoder(resp.Body).Decode(&got)
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("createHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
+func TestClusterKvPairsGetAllHandler(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               expected      []moduleLib.ClusterKvPairs
+               name, version string
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:        "Get Cluster KvPairs",
+                       expectedCode: http.StatusOK,
+                       expected: []moduleLib.ClusterKvPairs{
+                               {
+                                       Metadata: moduleLib.Metadata{
+                                               Name:        "ClusterKvPair1",
+                                               Description: "test cluster kv pairs",
+                                               UserData1:   "some user data 1",
+                                               UserData2:   "some user data 2",
+                                       },
+                                       Spec: moduleLib.ClusterKvSpec{
+                                               Kv: []map[string]interface{}{
+                                                       {
+                                                               "key1": "value1",
+                                                       },
+                                                       {
+                                                               "key2": "value2",
+                                                       },
+                                               },
+                                       },
+                               },
+                               {
+                                       Metadata: moduleLib.Metadata{
+                                               Name:        "ClusterKvPair2",
+                                               Description: "test cluster kv pairs",
+                                               UserData1:   "some user data A",
+                                               UserData2:   "some user data B",
+                                       },
+                                       Spec: moduleLib.ClusterKvSpec{
+                                               Kv: []map[string]interface{}{
+                                                       {
+                                                               "keyA": "valueA",
+                                                       },
+                                                       {
+                                                               "keyB": "valueB",
+                                                       },
+                                               },
+                                       },
+                               },
+                       },
+                       clusterClient: &mockClusterManager{
+                               //Items that will be returned by the mocked Client
+                               ClusterKvPairsItems: []moduleLib.ClusterKvPairs{
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "ClusterKvPair1",
+                                                       Description: "test cluster kv pairs",
+                                                       UserData1:   "some user data 1",
+                                                       UserData2:   "some user data 2",
+                                               },
+                                               Spec: moduleLib.ClusterKvSpec{
+                                                       Kv: []map[string]interface{}{
+                                                               {
+                                                                       "key1": "value1",
+                                                               },
+                                                               {
+                                                                       "key2": "value2",
+                                                               },
+                                                       },
+                                               },
+                                       },
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "ClusterKvPair2",
+                                                       Description: "test cluster kv pairs",
+                                                       UserData1:   "some user data A",
+                                                       UserData2:   "some user data B",
+                                               },
+                                               Spec: moduleLib.ClusterKvSpec{
+                                                       Kv: []map[string]interface{}{
+                                                               {
+                                                                       "keyA": "valueA",
+                                                               },
+                                                               {
+                                                                       "keyB": "valueB",
+                                                               },
+                                                       },
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("GET", "/v2/cluster-providers/cp1/clusters/cl1/kv-pairs", nil)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusOK
+                       if resp.StatusCode == http.StatusOK {
+                               got := []moduleLib.ClusterKvPairs{}
+                               json.NewDecoder(resp.Body).Decode(&got)
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("listHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
+func TestClusterKvPairsGetHandler(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               expected      moduleLib.ClusterKvPairs
+               name, version string
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:        "Get Cluster KV Pairs",
+                       expectedCode: http.StatusOK,
+                       expected: moduleLib.ClusterKvPairs{
+                               Metadata: moduleLib.Metadata{
+                                       Name:        "ClusterKvPair2",
+                                       Description: "test cluster kv pairs",
+                                       UserData1:   "some user data A",
+                                       UserData2:   "some user data B",
+                               },
+                               Spec: moduleLib.ClusterKvSpec{
+                                       Kv: []map[string]interface{}{
+                                               {
+                                                       "keyA": "valueA",
+                                               },
+                                               {
+                                                       "keyB": "valueB",
+                                               },
+                                       },
+                               },
+                       },
+                       name: "ClusterKvPair2",
+                       clusterClient: &mockClusterManager{
+                               //Items that will be returned by the mocked Client
+                               ClusterKvPairsItems: []moduleLib.ClusterKvPairs{
+                                       {
+                                               Metadata: moduleLib.Metadata{
+                                                       Name:        "ClusterKvPair2",
+                                                       Description: "test cluster kv pairs",
+                                                       UserData1:   "some user data A",
+                                                       UserData2:   "some user data B",
+                                               },
+                                               Spec: moduleLib.ClusterKvSpec{
+                                                       Kv: []map[string]interface{}{
+                                                               {
+                                                                       "keyA": "valueA",
+                                                               },
+                                                               {
+                                                                       "keyB": "valueB",
+                                                               },
+                                                       },
+                                               },
+                                       },
+                               },
+                       },
+               },
+               {
+                       label:        "Get Non-Existing Cluster KV Pairs",
+                       expectedCode: http.StatusInternalServerError,
+                       name:         "nonexistingclusterkvpairs",
+                       clusterClient: &mockClusterManager{
+                               ClusterKvPairsItems: []moduleLib.ClusterKvPairs{},
+                               Err:                 pkgerrors.New("Internal Error"),
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("GET", "/v2/cluster-providers/clusterProvider1/clusters/cl1/kv-pairs/"+testCase.name, nil)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusOK
+                       if resp.StatusCode == http.StatusOK {
+                               got := moduleLib.ClusterKvPairs{}
+                               json.NewDecoder(resp.Body).Decode(&got)
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("listHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
+func TestClusterKvPairsDeleteHandler(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               name          string
+               version       string
+               expectedCode  int
+               clusterClient *mockClusterManager
+       }{
+               {
+                       label:         "Delete Cluster KV Pairs",
+                       expectedCode:  http.StatusNoContent,
+                       name:          "testClusterKvPairs",
+                       clusterClient: &mockClusterManager{},
+               },
+               {
+                       label:        "Delete Non-Existing Cluster KV Pairs",
+                       expectedCode: http.StatusInternalServerError,
+                       name:         "testClusterKvPairs",
+                       clusterClient: &mockClusterManager{
+                               Err: pkgerrors.New("Internal Error"),
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("DELETE", "/v2/cluster-providers/cp1/clusters/cl1/kv-pairs/"+testCase.name, nil)
+                       resp := executeRequest(request, NewRouter(nil, nil, nil, testCase.clusterClient))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+               })
+       }
+}
index f080410..c91943a 100644 (file)
@@ -110,7 +110,7 @@ func TestControllerCreateHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("POST", "/v2/controllers", testCase.reader)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.controllerClient))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.controllerClient, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -173,7 +173,7 @@ func TestControllerGetHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v2/controllers/"+testCase.name, nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.controllerClient))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.controllerClient, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -222,7 +222,7 @@ func TestControllerDeleteHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("DELETE", "/v2/controllers/"+testCase.name, nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.controllerClient))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.controllerClient, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
index 1e27334..c6da4e0 100644 (file)
@@ -119,7 +119,7 @@ func TestProjectCreateHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("POST", "/v2/projects", testCase.reader)
-                       resp := executeRequest(request, NewRouter(testCase.projectClient, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.projectClient, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -188,7 +188,7 @@ func TestProjectGetHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v2/projects/"+testCase.name, nil)
-                       resp := executeRequest(request, NewRouter(testCase.projectClient, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.projectClient, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -237,7 +237,7 @@ func TestProjectDeleteHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("DELETE", "/v2/projects/"+testCase.name, nil)
-                       resp := executeRequest(request, NewRouter(testCase.projectClient, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.projectClient, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
index a6c72ae..179cf97 100644 (file)
@@ -47,7 +47,7 @@ func main() {
                log.Fatalln("Exiting...")
        }
 
-       httpRouter := api.NewRouter(nil, nil, nil)
+       httpRouter := api.NewRouter(nil, nil, nil, nil)
        loggedRouter := handlers.LoggingHandler(os.Stdout, httpRouter)
        log.Println("Starting Kubernetes Multicloud API")
 
diff --git a/src/orchestrator/pkg/module/cluster.go b/src/orchestrator/pkg/module/cluster.go
new file mode 100644 (file)
index 0000000..c9ddad6
--- /dev/null
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * 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 module
+
+import (
+       "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
+
+       pkgerrors "github.com/pkg/errors"
+)
+
+// ClusterProvider contains the parameters needed for ClusterProviders
+// It implements the interface for managing the ClusterProviders
+type Metadata struct {
+       Name        string `json:"name"`
+       Description string `json:"description"`
+       UserData1   string `json:"userData1"`
+       UserData2   string `json:"userData2"`
+}
+
+type ClusterProvider struct {
+       Metadata Metadata `json:"metadata"`
+}
+
+type Cluster struct {
+       Metadata Metadata `json:"metadata"`
+}
+
+type ClusterContent struct {
+       Kubeconfig string `json:"kubeconfig"`
+}
+
+type ClusterLabel struct {
+       LabelName string `json:"label-name"`
+}
+
+type ClusterKvPairs struct {
+       Metadata Metadata      `json:"metadata"`
+       Spec     ClusterKvSpec `json:"spec"`
+}
+
+type ClusterKvSpec struct {
+       Kv []map[string]interface{} `json:"kv"`
+}
+
+// ClusterProviderKey is the key structure that is used in the database
+type ClusterProviderKey struct {
+       ClusterProviderName string `json:"provider"`
+}
+
+// ClusterKey is the key structure that is used in the database
+type ClusterKey struct {
+       ClusterProviderName string `json:"provider"`
+       ClusterName         string `json:"cluster"`
+}
+
+// ClusterLabelKey is the key structure that is used in the database
+type ClusterLabelKey struct {
+       ClusterProviderName string `json:"provider"`
+       ClusterName         string `json:"cluster"`
+       ClusterLabelName    string `json:"label"`
+}
+
+// ClusterKvPairsKey is the key structure that is used in the database
+type ClusterKvPairsKey struct {
+       ClusterProviderName string `json:"provider"`
+       ClusterName         string `json:"cluster"`
+       ClusterKvPairsName  string `json:"kvname"`
+}
+
+// Manager is an interface exposes the Cluster functionality
+type ClusterManager interface {
+       CreateClusterProvider(pr ClusterProvider) (ClusterProvider, error)
+       GetClusterProvider(name string) (ClusterProvider, error)
+       GetClusterProviders() ([]ClusterProvider, error)
+       DeleteClusterProvider(name string) error
+       CreateCluster(provider string, pr Cluster, qr ClusterContent) (Cluster, error)
+       GetCluster(provider, name string) (Cluster, error)
+       GetClusterContent(provider, name string) (ClusterContent, error)
+       GetClusters(provider string) ([]Cluster, error)
+       DeleteCluster(provider, name string) error
+       CreateClusterLabel(provider, cluster string, pr ClusterLabel) (ClusterLabel, error)
+       GetClusterLabel(provider, cluster, label string) (ClusterLabel, error)
+       GetClusterLabels(provider, cluster string) ([]ClusterLabel, error)
+       DeleteClusterLabel(provider, cluster, label string) error
+       CreateClusterKvPairs(provider, cluster string, pr ClusterKvPairs) (ClusterKvPairs, error)
+       GetClusterKvPairs(provider, cluster, kvpair string) (ClusterKvPairs, error)
+       GetAllClusterKvPairs(provider, cluster string) ([]ClusterKvPairs, error)
+       DeleteClusterKvPairs(provider, cluster, kvpair string) error
+}
+
+// ClusterClient implements the Manager
+// It will also be used to maintain some localized state
+type ClusterClient struct {
+       storeName  string
+       tagMeta    string
+       tagContent string
+}
+
+// NewClusterClient returns an instance of the ClusterClient
+// which implements the Manager
+func NewClusterClient() *ClusterClient {
+       return &ClusterClient{
+               storeName:  "cluster",
+               tagMeta:    "clustermetadata",
+               tagContent: "clustercontent",
+       }
+}
+
+// CreateClusterProvider - create a new Cluster Provider
+func (v *ClusterClient) CreateClusterProvider(p ClusterProvider) (ClusterProvider, error) {
+
+       //Construct key and tag to select the entry
+       key := ClusterProviderKey{
+               ClusterProviderName: p.Metadata.Name,
+       }
+
+       //Check if this ClusterProvider already exists
+       _, err := v.GetClusterProvider(p.Metadata.Name)
+       if err == nil {
+               return ClusterProvider{}, pkgerrors.New("ClusterProvider already exists")
+       }
+
+       err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, p)
+       if err != nil {
+               return ClusterProvider{}, pkgerrors.Wrap(err, "Creating DB Entry")
+       }
+
+       return p, nil
+}
+
+// GetClusterProvider returns the ClusterProvider for corresponding name
+func (v *ClusterClient) GetClusterProvider(name string) (ClusterProvider, error) {
+
+       //Construct key and tag to select the entry
+       key := ClusterProviderKey{
+               ClusterProviderName: name,
+       }
+
+       value, err := db.DBconn.Find(v.storeName, key, v.tagMeta)
+       if err != nil {
+               return ClusterProvider{}, pkgerrors.Wrap(err, "Get ClusterProvider")
+       }
+
+       //value is a byte array
+       if value != nil {
+               cp := ClusterProvider{}
+               err = db.DBconn.Unmarshal(value[0], &cp)
+               if err != nil {
+                       return ClusterProvider{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+               }
+               return cp, nil
+       }
+
+       return ClusterProvider{}, pkgerrors.New("Error getting ClusterProvider")
+}
+
+// GetClusterProviderList returns all of the ClusterProvider for corresponding name
+func (v *ClusterClient) GetClusterProviders() ([]ClusterProvider, error) {
+
+       //Construct key and tag to select the entry
+       key := ClusterProviderKey{
+               ClusterProviderName: "",
+       }
+
+       var resp []ClusterProvider
+       values, err := db.DBconn.Find(v.storeName, key, v.tagMeta)
+       if err != nil {
+               return []ClusterProvider{}, pkgerrors.Wrap(err, "Get ClusterProviders")
+       }
+
+       for _, value := range values {
+               cp := ClusterProvider{}
+               err = db.DBconn.Unmarshal(value, &cp)
+               if err != nil {
+                       return []ClusterProvider{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+               }
+               resp = append(resp, cp)
+       }
+
+       return resp, nil
+}
+
+// DeleteClusterProvider the  ClusterProvider from database
+func (v *ClusterClient) DeleteClusterProvider(name string) error {
+
+       //Construct key and tag to select the entry
+       key := ClusterProviderKey{
+               ClusterProviderName: name,
+       }
+
+       err := db.DBconn.Remove(v.storeName, key)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Delete ClusterProvider Entry;")
+       }
+
+       return nil
+}
+
+// CreateCluster - create a new Cluster for a cluster-provider
+func (v *ClusterClient) CreateCluster(provider string, p Cluster, q ClusterContent) (Cluster, error) {
+
+       //Construct key and tag to select the entry
+       key := ClusterKey{
+               ClusterProviderName: provider,
+               ClusterName:         p.Metadata.Name,
+       }
+
+       //Verify ClusterProvider already exists
+       _, err := v.GetClusterProvider(provider)
+       if err != nil {
+               return Cluster{}, pkgerrors.New("ClusterProvider does not exist")
+       }
+
+       //Check if this Cluster already exists
+       _, err = v.GetCluster(provider, p.Metadata.Name)
+       if err == nil {
+               return Cluster{}, pkgerrors.New("Cluster already exists")
+       }
+
+       err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, p)
+       if err != nil {
+               return Cluster{}, pkgerrors.Wrap(err, "Creating DB Entry")
+       }
+       err = db.DBconn.Insert(v.storeName, key, nil, v.tagContent, q)
+       if err != nil {
+               return Cluster{}, pkgerrors.Wrap(err, "Creating DB Entry")
+       }
+
+       return p, nil
+}
+
+// GetCluster returns the Cluster for corresponding provider and name
+func (v *ClusterClient) GetCluster(provider, name string) (Cluster, error) {
+       //Construct key and tag to select the entry
+       key := ClusterKey{
+               ClusterProviderName: provider,
+               ClusterName:         name,
+       }
+
+       value, err := db.DBconn.Find(v.storeName, key, v.tagMeta)
+       if err != nil {
+               return Cluster{}, pkgerrors.Wrap(err, "Get Cluster")
+       }
+
+       //value is a byte array
+       if value != nil {
+               cl := Cluster{}
+               err = db.DBconn.Unmarshal(value[0], &cl)
+               if err != nil {
+                       return Cluster{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+               }
+               return cl, nil
+       }
+
+       return Cluster{}, pkgerrors.New("Error getting Cluster")
+}
+
+// GetClusterContent returns the ClusterContent for corresponding provider and name
+func (v *ClusterClient) GetClusterContent(provider, name string) (ClusterContent, error) {
+       //Construct key and tag to select the entry
+       key := ClusterKey{
+               ClusterProviderName: provider,
+               ClusterName:         name,
+       }
+
+       value, err := db.DBconn.Find(v.storeName, key, v.tagContent)
+       if err != nil {
+               return ClusterContent{}, pkgerrors.Wrap(err, "Get Cluster Content")
+       }
+
+       //value is a byte array
+       if value != nil {
+               cc := ClusterContent{}
+               err = db.DBconn.Unmarshal(value[0], &cc)
+               if err != nil {
+                       return ClusterContent{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+               }
+               return cc, nil
+       }
+
+       return ClusterContent{}, pkgerrors.New("Error getting Cluster Content")
+}
+
+// GetClusters returns all the Clusters for corresponding provider
+func (v *ClusterClient) GetClusters(provider string) ([]Cluster, error) {
+       //Construct key and tag to select the entry
+       key := ClusterKey{
+               ClusterProviderName: provider,
+               ClusterName:         "",
+       }
+
+       values, err := db.DBconn.Find(v.storeName, key, v.tagMeta)
+       if err != nil {
+               return []Cluster{}, pkgerrors.Wrap(err, "Get Clusters")
+       }
+
+       var resp []Cluster
+
+       for _, value := range values {
+               cp := Cluster{}
+               err = db.DBconn.Unmarshal(value, &cp)
+               if err != nil {
+                       return []Cluster{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+               }
+               resp = append(resp, cp)
+       }
+
+       return resp, nil
+}
+
+// DeleteCluster the  Cluster from database
+func (v *ClusterClient) DeleteCluster(provider, name string) error {
+       //Construct key and tag to select the entry
+       key := ClusterKey{
+               ClusterProviderName: provider,
+               ClusterName:         name,
+       }
+
+       err := db.DBconn.Remove(v.storeName, key)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Delete Cluster Entry;")
+       }
+
+       return nil
+}
+
+// CreateClusterLabel - create a new Cluster Label mongo document for a cluster-provider/cluster
+func (v *ClusterClient) CreateClusterLabel(provider string, cluster string, p ClusterLabel) (ClusterLabel, error) {
+       //Construct key and tag to select the entry
+       key := ClusterLabelKey{
+               ClusterProviderName: provider,
+               ClusterName:         cluster,
+               ClusterLabelName:    p.LabelName,
+       }
+
+       //Verify Cluster already exists
+       _, err := v.GetCluster(provider, cluster)
+       if err != nil {
+               return ClusterLabel{}, pkgerrors.New("Cluster does not exist")
+       }
+
+       //Check if this ClusterLabel already exists
+       _, err = v.GetClusterLabel(provider, cluster, p.LabelName)
+       if err == nil {
+               return ClusterLabel{}, pkgerrors.New("Cluster Label already exists")
+       }
+
+       err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, p)
+       if err != nil {
+               return ClusterLabel{}, pkgerrors.Wrap(err, "Creating DB Entry")
+       }
+
+       return p, nil
+}
+
+// GetClusterLabel returns the Cluster for corresponding provider, cluster and label
+func (v *ClusterClient) GetClusterLabel(provider, cluster, label string) (ClusterLabel, error) {
+       //Construct key and tag to select the entry
+       key := ClusterLabelKey{
+               ClusterProviderName: provider,
+               ClusterName:         cluster,
+               ClusterLabelName:    label,
+       }
+
+       value, err := db.DBconn.Find(v.storeName, key, v.tagMeta)
+       if err != nil {
+               return ClusterLabel{}, pkgerrors.Wrap(err, "Get Cluster")
+       }
+
+       //value is a byte array
+       if value != nil {
+               cl := ClusterLabel{}
+               err = db.DBconn.Unmarshal(value[0], &cl)
+               if err != nil {
+                       return ClusterLabel{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+               }
+               return cl, nil
+       }
+
+       return ClusterLabel{}, pkgerrors.New("Error getting Cluster")
+}
+
+// GetClusterLabels returns the Cluster Labels for corresponding provider and cluster
+func (v *ClusterClient) GetClusterLabels(provider, cluster string) ([]ClusterLabel, error) {
+       //Construct key and tag to select the entry
+       key := ClusterLabelKey{
+               ClusterProviderName: provider,
+               ClusterName:         cluster,
+               ClusterLabelName:    "",
+       }
+
+       values, err := db.DBconn.Find(v.storeName, key, v.tagMeta)
+       if err != nil {
+               return []ClusterLabel{}, pkgerrors.Wrap(err, "Get Cluster Labels")
+       }
+
+       var resp []ClusterLabel
+
+       for _, value := range values {
+               cp := ClusterLabel{}
+               err = db.DBconn.Unmarshal(value, &cp)
+               if err != nil {
+                       return []ClusterLabel{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+               }
+               resp = append(resp, cp)
+       }
+
+       return resp, nil
+}
+
+// Delete the Cluster Label from database
+func (v *ClusterClient) DeleteClusterLabel(provider, cluster, label string) error {
+       //Construct key and tag to select the entry
+       key := ClusterLabelKey{
+               ClusterProviderName: provider,
+               ClusterName:         cluster,
+               ClusterLabelName:    label,
+       }
+
+       err := db.DBconn.Remove(v.storeName, key)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Delete ClusterLabel Entry;")
+       }
+
+       return nil
+}
+
+// CreateClusterKvPairs - Create a New Cluster KV pairs document
+func (v *ClusterClient) CreateClusterKvPairs(provider string, cluster string, p ClusterKvPairs) (ClusterKvPairs, error) {
+       key := ClusterKvPairsKey{
+               ClusterProviderName: provider,
+               ClusterName:         cluster,
+               ClusterKvPairsName:  p.Metadata.Name,
+       }
+
+       //Verify Cluster already exists
+       _, err := v.GetCluster(provider, cluster)
+       if err != nil {
+               return ClusterKvPairs{}, pkgerrors.New("Cluster does not exist")
+       }
+
+       //Check if this ClusterKvPairs already exists
+       _, err = v.GetClusterKvPairs(provider, cluster, p.Metadata.Name)
+       if err == nil {
+               return ClusterKvPairs{}, pkgerrors.New("Cluster KV Pair already exists")
+       }
+
+       err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, p)
+       if err != nil {
+               return ClusterKvPairs{}, pkgerrors.Wrap(err, "Creating DB Entry")
+       }
+
+       return p, nil
+}
+
+// GetClusterKvPairs returns the Cluster KeyValue pair for corresponding provider, cluster and KV pair name
+func (v *ClusterClient) GetClusterKvPairs(provider, cluster, kvpair string) (ClusterKvPairs, error) {
+       //Construct key and tag to select entry
+       key := ClusterKvPairsKey{
+               ClusterProviderName: provider,
+               ClusterName:         cluster,
+               ClusterKvPairsName:  kvpair,
+       }
+
+       value, err := db.DBconn.Find(v.storeName, key, v.tagMeta)
+       if err != nil {
+               return ClusterKvPairs{}, pkgerrors.Wrap(err, "Get Cluster")
+       }
+
+       //value is a byte array
+       if value != nil {
+               ckvp := ClusterKvPairs{}
+               err = db.DBconn.Unmarshal(value[0], &ckvp)
+               if err != nil {
+                       return ClusterKvPairs{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+               }
+               return ckvp, nil
+       }
+
+       return ClusterKvPairs{}, pkgerrors.New("Error getting Cluster")
+}
+
+// GetAllClusterKvPairs returns the Cluster Kv Pairs for corresponding provider and cluster
+func (v *ClusterClient) GetAllClusterKvPairs(provider, cluster string) ([]ClusterKvPairs, error) {
+       //Construct key and tag to select the entry
+       key := ClusterKvPairsKey{
+               ClusterProviderName: provider,
+               ClusterName:         cluster,
+               ClusterKvPairsName:  "",
+       }
+
+       values, err := db.DBconn.Find(v.storeName, key, v.tagMeta)
+       if err != nil {
+               return []ClusterKvPairs{}, pkgerrors.Wrap(err, "Get Cluster KV Pairs")
+       }
+
+       var resp []ClusterKvPairs
+
+       for _, value := range values {
+               cp := ClusterKvPairs{}
+               err = db.DBconn.Unmarshal(value, &cp)
+               if err != nil {
+                       return []ClusterKvPairs{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+               }
+               resp = append(resp, cp)
+       }
+
+       return resp, nil
+}
+
+// DeleteClusterKvPairs the  ClusterKvPairs from database
+func (v *ClusterClient) DeleteClusterKvPairs(provider, cluster, kvpair string) error {
+       //Construct key and tag to select entry
+       key := ClusterKvPairsKey{
+               ClusterProviderName: provider,
+               ClusterName:         cluster,
+               ClusterKvPairsName:  kvpair,
+       }
+
+       err := db.DBconn.Remove(v.storeName, key)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Delete ClusterKvPairs Entry;")
+       }
+
+       return nil
+}
index a94a420..f80ff55 100644 (file)
@@ -21,6 +21,7 @@ type Client struct {
        Project      *ProjectClient
        CompositeApp *CompositeAppClient
        Controller *ControllerClient
+       Cluster *ClusterClient
        // Add Clients for API's here
 }
 
@@ -30,6 +31,7 @@ func NewClient() *Client {
        c.Project = NewProjectClient()
        c.CompositeApp = NewCompositeAppClient()
        c.Controller = NewControllerClient()
+       c.Cluster = NewClusterClient()
        // Add Client API handlers here
        return c
 }