summaryrefslogtreecommitdiffstats
path: root/src/ncm
diff options
context:
space:
mode:
authorEric Multanen <eric.w.multanen@intel.com>2020-03-12 15:59:24 -0700
committerEric Multanen <eric.w.multanen@intel.com>2020-03-23 14:06:22 -0700
commite201896e60374698da18cf4258448a0d97617e37 (patch)
tree639a6f5d1198590c1ae44a587721a95ef198756e /src/ncm
parent2bd2f4e76bbf7d5bce6a320ca8958fa547958a8b (diff)
Create network service with cluster API
Creates a new network customization and configuration service. Seed it by moving the cluster-provider and cluster API from the orchestrator service to this new service. More APIs to follow. Just put a Dockerfile in the ncm/scripts directory for now. See: https://wiki.onap.org/pages/viewpage.action?pageId=79201398 Issue-ID: MULTICLOUD-1029 Signed-off-by: Eric Multanen <eric.w.multanen@intel.com> Change-Id: Iea1b1151e5f87fecaca9cfe8c9746a59eb447a7a
Diffstat (limited to 'src/ncm')
-rw-r--r--src/ncm/Makefile36
-rw-r--r--src/ncm/api/api.go76
-rw-r--r--src/ncm/api/clusterhandler.go468
-rw-r--r--src/ncm/api/clusterhandler_test.go1412
-rw-r--r--src/ncm/api/testing.go31
-rw-r--r--src/ncm/cmd/main.go77
-rw-r--r--src/ncm/go.mod31
-rw-r--r--src/ncm/pkg/module/cluster.go540
-rw-r--r--src/ncm/pkg/module/module.go31
-rw-r--r--src/ncm/scripts/Dockerfile30
-rw-r--r--src/ncm/tests/certs/auth_test_certificate.pem21
-rw-r--r--src/ncm/tests/certs/auth_test_key.pem28
-rw-r--r--src/ncm/tests/configs/mock_config.json5
13 files changed, 2786 insertions, 0 deletions
diff --git a/src/ncm/Makefile b/src/ncm/Makefile
new file mode 100644
index 00000000..fbdd53f7
--- /dev/null
+++ b/src/ncm/Makefile
@@ -0,0 +1,36 @@
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright (c) 2020 Intel Corporation
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+export GO111MODULE=on
+
+all: clean
+ CGO_ENABLED=1 GOOS=linux GOARCH=amd64
+ @go build -tags netgo -o ./ncm ./cmd/main.go
+
+# The following is done this way as each patch on CI runs build and each merge runs deploy. So for build we don't need to build binary and hence
+# no need to create a static binary with additional flags. However, for generating binary, additional build flags are necessary. This if used with
+# mock plugin errors out for unit tests. So the seperation avoids the error.
+
+build: clean test cover
+deploy: build
+
+.PHONY: test
+test: clean
+ @go test -race ./...
+
+format:
+ @go fmt ./...
+
+clean:
+ @rm -f ncm coverage.html coverage.out
+
+.PHONY: cover
+cover:
+ @go test -race ./... -coverprofile=coverage.out
+ @go tool cover -html=coverage.out -o coverage.html
diff --git a/src/ncm/api/api.go b/src/ncm/api/api.go
new file mode 100644
index 00000000..c26f54e9
--- /dev/null
+++ b/src/ncm/api/api.go
@@ -0,0 +1,76 @@
+/*
+ * 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 (
+ "fmt"
+ "reflect"
+
+ "github.com/gorilla/mux"
+ moduleLib "github.com/onap/multicloud-k8s/src/ncm/pkg/module"
+)
+
+var moduleClient *moduleLib.Client
+
+// for the given client and testClient, if the testClient is not null and
+// implements the client manager interface corresponding to client, then
+// return the testClient, otherwise return the client.
+func setClient(client, testClient interface{}) interface{} {
+ switch cl := client.(type) {
+ case *moduleLib.ClusterClient:
+ if testClient != nil && reflect.TypeOf(testClient).Implements(reflect.TypeOf((*moduleLib.ClusterManager)(nil)).Elem()) {
+ c, ok := testClient.(moduleLib.ClusterManager)
+ if ok {
+ return c
+ }
+ }
+ default:
+ fmt.Printf("unknown type %T\n", cl)
+ }
+ return client
+}
+
+// NewRouter creates a router that registers the various urls that are supported
+// testClient parameter allows unit testing for a given client
+func NewRouter(testClient interface{}) *mux.Router {
+
+ moduleClient = moduleLib.NewClient()
+
+ clusterHandler := clusterHandler{
+ client: setClient(moduleClient.Cluster, testClient).(moduleLib.ClusterManager),
+ }
+
+ router := mux.NewRouter().PathPrefix("/v2").Subrouter()
+
+ 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/ncm/api/clusterhandler.go b/src/ncm/api/clusterhandler.go
new file mode 100644
index 00000000..cb147a8a
--- /dev/null
+++ b/src/ncm/api/clusterhandler.go
@@ -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/ncm/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
+ }
+ kcBytes, err := base64.StdEncoding.DecodeString(retKubeconfig.Kubeconfig)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ _, err = pw.Write(kcBytes)
+ 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)
+ kcBytes, err := base64.StdEncoding.DecodeString(retKubeconfig.Kubeconfig)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ _, err = w.Write(kcBytes)
+ 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/ncm/api/clusterhandler_test.go b/src/ncm/api/clusterhandler_test.go
new file mode 100644
index 00000000..af5bd160
--- /dev/null
+++ b/src/ncm/api/clusterhandler_test.go
@@ -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/ncm/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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(testCase.clusterClient))
+
+ //Check returned code
+ if resp.StatusCode != testCase.expectedCode {
+ t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+ }
+ })
+ }
+}
diff --git a/src/ncm/api/testing.go b/src/ncm/api/testing.go
new file mode 100644
index 00000000..fed5c080
--- /dev/null
+++ b/src/ncm/api/testing.go
@@ -0,0 +1,31 @@
+/*
+ * 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 (
+ "net/http"
+ "net/http/httptest"
+
+ "github.com/gorilla/mux"
+)
+
+func executeRequest(request *http.Request, router *mux.Router) *http.Response {
+ recorder := httptest.NewRecorder()
+ router.ServeHTTP(recorder, request)
+ resp := recorder.Result()
+ return resp
+}
diff --git a/src/ncm/cmd/main.go b/src/ncm/cmd/main.go
new file mode 100644
index 00000000..c4ae423f
--- /dev/null
+++ b/src/ncm/cmd/main.go
@@ -0,0 +1,77 @@
+/*
+Copyright 2020 Intel Corporation.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+ "context"
+ "log"
+ "math/rand"
+ "net/http"
+ "os"
+ "os/signal"
+ "time"
+
+ "github.com/gorilla/handlers"
+ "github.com/onap/multicloud-k8s/src/ncm/api"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/auth"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/config"
+ contextDb "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/contextdb"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
+)
+
+func main() {
+
+ rand.Seed(time.Now().UnixNano())
+
+ err := db.InitializeDatabaseConnection("mco")
+ if err != nil {
+ log.Println("Unable to initialize database connection...")
+ log.Println(err)
+ log.Fatalln("Exiting...")
+ }
+ err = contextDb.InitializeContextDatabase()
+ if err != nil {
+ log.Println("Unable to initialize database connection...")
+ log.Println(err)
+ log.Fatalln("Exiting...")
+ }
+
+ httpRouter := api.NewRouter(nil)
+ loggedRouter := handlers.LoggingHandler(os.Stdout, httpRouter)
+ log.Println("Starting Network Customization Manager")
+
+ httpServer := &http.Server{
+ Handler: loggedRouter,
+ Addr: ":" + config.GetConfiguration().ServicePort,
+ }
+
+ connectionsClose := make(chan struct{})
+ go func() {
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, os.Interrupt)
+ <-c
+ httpServer.Shutdown(context.Background())
+ close(connectionsClose)
+ }()
+
+ tlsConfig, err := auth.GetTLSConfig("ca.cert", "server.cert", "server.key")
+ if err != nil {
+ log.Println("Error Getting TLS Configuration. Starting without TLS...")
+ log.Fatal(httpServer.ListenAndServe())
+ } else {
+ httpServer.TLSConfig = tlsConfig
+ // empty strings because tlsconfig already has this information
+ err = httpServer.ListenAndServeTLS("", "")
+ }
+}
diff --git a/src/ncm/go.mod b/src/ncm/go.mod
new file mode 100644
index 00000000..32ff481a
--- /dev/null
+++ b/src/ncm/go.mod
@@ -0,0 +1,31 @@
+module github.com/onap/multicloud-k8s/src/ncm
+
+require (
+ github.com/coreos/etcd v3.3.12+incompatible
+ github.com/docker/engine v0.0.0-20190620014054-c513a4c6c298
+ github.com/ghodss/yaml v1.0.0
+ github.com/gorilla/handlers v1.3.0
+ github.com/gorilla/mux v1.6.2
+ github.com/hashicorp/consul v1.4.0
+ github.com/pkg/errors v0.8.1
+ github.com/sirupsen/logrus v1.4.2
+ go.etcd.io/etcd v3.3.12+incompatible
+ go.mongodb.org/mongo-driver v1.0.0
+ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
+ k8s.io/api v0.0.0-20190831074750-7364b6bdad65
+ k8s.io/apimachinery v0.0.0-20190831074630-461753078381
+ k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
+ k8s.io/helm v2.14.3+incompatible
+)
+
+replace (
+ k8s.io/api => k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b
+ k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8
+ k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d
+ k8s.io/apiserver => k8s.io/apiserver v0.0.0-20190409021813-1ec86e4da56c
+ k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20190409023024-d644b00f3b79
+ k8s.io/client-go => k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
+ k8s.io/cloud-provider => k8s.io/cloud-provider v0.0.0-20190409023720-1bc0c81fa51d
+)
+
+go 1.13
diff --git a/src/ncm/pkg/module/cluster.go b/src/ncm/pkg/module/cluster.go
new file mode 100644
index 00000000..c9ddad6e
--- /dev/null
+++ b/src/ncm/pkg/module/cluster.go
@@ -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
+}
diff --git a/src/ncm/pkg/module/module.go b/src/ncm/pkg/module/module.go
new file mode 100644
index 00000000..c1c24510
--- /dev/null
+++ b/src/ncm/pkg/module/module.go
@@ -0,0 +1,31 @@
+/*
+ * 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
+
+// Client for using the services in the orchestrator
+type Client struct {
+ Cluster *ClusterClient
+ // Add Clients for API's here
+}
+
+// NewClient creates a new client for using the services
+func NewClient() *Client {
+ c := &Client{}
+ c.Cluster = NewClusterClient()
+ // Add Client API handlers here
+ return c
+}
diff --git a/src/ncm/scripts/Dockerfile b/src/ncm/scripts/Dockerfile
new file mode 100644
index 00000000..d1b58677
--- /dev/null
+++ b/src/ncm/scripts/Dockerfile
@@ -0,0 +1,30 @@
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright (c) 2020
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+FROM ubuntu:18.04
+
+ARG HTTP_PROXY=${HTTP_PROXY}
+ARG HTTPS_PROXY=${HTTPS_PROXY}
+
+ENV http_proxy $HTTP_PROXY
+ENV https_proxy $HTTPS_PROXY
+ENV no_proxy $NO_PROXY
+
+EXPOSE 9016
+
+RUN groupadd -r onap && useradd -r -g onap onap
+
+WORKDIR /opt/multicloud/k8s/ncm
+RUN chown onap:onap /opt/multicloud/k8s/ncm -R
+
+ADD --chown=onap ./ncm ./
+
+USER onap
+
+CMD ["./ncm"]
diff --git a/src/ncm/tests/certs/auth_test_certificate.pem b/src/ncm/tests/certs/auth_test_certificate.pem
new file mode 100644
index 00000000..42e77491
--- /dev/null
+++ b/src/ncm/tests/certs/auth_test_certificate.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDXTCCAkWgAwIBAgIJAKAHJi8eUs73MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTgwNTE1MDQ0MDQwWhcNMTkwNTE1MDQ0MDQwWjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA5PHDk+RRFh5o3Xe2nZuLn0Vo+5BjnHp/ul2NNYSG00Slc8F86gW4xcNA
+wm6xC8tYCSangV2lFG3E8H2L7SCEVaM5VDV2GCOpOoMihc+2Qenk/YbHwuYenwjo
+OgTK4aCItqjcAJ2DB1KC7AxARxHBDu9Kif+M/pc49so+G9ObQuS8k2vmTTaRYkMK
+ZvbTJcWsc0vbNnPhxcG5PVj4unlaREM+yQDm18/glUkkBNnYKMHABRvPnBrDktTT
+BQWsqkbQTw7ZuLOrl9rCzVTstZX9wEXarrstIdQJj3KZjbFOp2opND8bjNIjcdVt
+iRFnP1nHQYr7EgRqcx/YMJZ+gmCy3wIDAQABo1AwTjAdBgNVHQ4EFgQU9qPNwwIu
+kZh41sJqFtnMC2blSYMwHwYDVR0jBBgwFoAU9qPNwwIukZh41sJqFtnMC2blSYMw
+DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA4+daLY1wE10IMPaOKurG
+QBvfYeO/rgNXGeg0TisTIKAfx/We9Hmwo/37Bd2Nk5gxfy/DIJ4lMbrzXjgWITlm
+XOrS5QfluwvaEcREtHFtPFa3NZqn2VzKNDFTR+rJj7I5o600NKdcPrGeQ1i/vny2
+K0g68ogw2jfufcuePvZTYGst8RclomPr7ZXxI24kIjcE1MbiViy68sQueTXBEr5s
+Th6RsvPfVnLxjR/m/V6VJl31nn4T6hbmKzXCHo/X7aC3I8Isui4bQGKgfAxyBkhE
+0T7tP+GgymiEKQ6qJ/1c4HFFSuFRUQjLnK7uJu9jM/HMKoLCPayx6birHZRIMF94
+pg==
+-----END CERTIFICATE-----
diff --git a/src/ncm/tests/certs/auth_test_key.pem b/src/ncm/tests/certs/auth_test_key.pem
new file mode 100644
index 00000000..5f01f572
--- /dev/null
+++ b/src/ncm/tests/certs/auth_test_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDk8cOT5FEWHmjd
+d7adm4ufRWj7kGOcen+6XY01hIbTRKVzwXzqBbjFw0DCbrELy1gJJqeBXaUUbcTw
+fYvtIIRVozlUNXYYI6k6gyKFz7ZB6eT9hsfC5h6fCOg6BMrhoIi2qNwAnYMHUoLs
+DEBHEcEO70qJ/4z+lzj2yj4b05tC5LyTa+ZNNpFiQwpm9tMlxaxzS9s2c+HFwbk9
+WPi6eVpEQz7JAObXz+CVSSQE2dgowcAFG8+cGsOS1NMFBayqRtBPDtm4s6uX2sLN
+VOy1lf3ARdquuy0h1AmPcpmNsU6naik0PxuM0iNx1W2JEWc/WcdBivsSBGpzH9gw
+ln6CYLLfAgMBAAECggEAYB3H2D0QddLKf8AUoNJ+qZ1AV+zkhPtAyIMiF4fN+sBl
+HdXrlWxViGFSvM4v8h2qlhzuUfd4qLz042ox5pmyNSnTlbDkJXpDP9dyFO+BOubx
+Ribhksd9r5LTvBfq/RKikt0NkAyQx/AyGtuB2NRxUs3PY2QwU2o1dhauQIx0MH5/
+6D8PgQf6+5njKQaKa4e8Kp4kB+KjnALvt6JgYuNJUHWap+nnDbuuVy5dl1bKkAZ+
+qa7CITKWO4kE2EqaCb2asFc2w3538+w72UJZtwQCmOaxtKpRSl9fQXu54N8jIGoZ
+1FvEj5x3X6QkglE+iVJYaX3RmiJ3uzZ2LICDr89vEQKBgQD7fquIw4p1idSxz3Cm
+5o3Y5kD0CKm61ZaRJWKd+tNlSsxubmV9HROYW6vj2xEPSDvyp1na00pDXxMJQLLc
+O5Awd1SaU+d45jnoi70fsEY8X0WH1rDTYfnU+zQBmpbGqX5qTIfpy4yoADiUD1CQ
+EBdaSBWiKPx2jFSct58TwDP9YwKBgQDpC64TScZYz7uQq4gAbDso/7TjNwgt/Bw8
+JgLSdx1UdUclh81smTujsouyCFwJSvRjKik8e/Qt0f5patukFbFRINxUGUDhOKbA
+7zqeNQbeYaP7Rvw+3z01CU2BTBmB/EWa2xWDam8B9xQvjiHSOrubqkt3sIQJb045
+hzuigdV7VQKBgQD7Gnd0nyCwyMSIIMGuswYv6X4y6i9lr3qdQ4GakOTe/vbsz+cf
+K5f0CJuwbnszEgFg/zzVIx/D8rqUA3hSMlp+ObdMO7gi22Q4TsWvTRZjkxBeV7rH
+48xJneNIMqyWgIcK5YzSn3y6BTZ4hm3+2UInz09iUJ/6UZTtwNzhIIgIVwKBgQCT
+LxRHDE4gIzrT+PHRSonmr/DfnA8nc9WlS2B26lH02IkRs/5Su0iGb6p4y3zNRbCp
+vKQElki2c60ZiSqlLCosEfP1jWmDlRMEQVMlPlpTMxmtBr0jPDzc9T4lDhoCFYEk
+d3/T2vG3LQRrsHm92+hHPTuioTIS/2BJRxar4RIibQKBgQC8zoayoQ7zfEYxy3Ax
+OSao8g85hj0EAJk/VKQP2POgz6KoPay3JE9D7P7OvkebTyv/pijAuTPby4XipCNI
+K0JbFC2Kn7RW/ZV23UdnoO9crh2omOh+/52prStWXKoc+/pJe70Af+4rU7FUiI7F
+y1mIE9krIoVis6iYsyFEmkP7iw==
+-----END PRIVATE KEY-----
diff --git a/src/ncm/tests/configs/mock_config.json b/src/ncm/tests/configs/mock_config.json
new file mode 100644
index 00000000..47a6b627
--- /dev/null
+++ b/src/ncm/tests/configs/mock_config.json
@@ -0,0 +1,5 @@
+{
+ "database-type": "mock_db_test",
+ "database-ip": "127.0.0.1",
+ "plugin-dir": "."
+} \ No newline at end of file