summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rwxr-xr-xdeployments/build.sh4
-rw-r--r--deployments/docker-compose.yml15
-rwxr-xr-xdeployments/start.sh8
-rw-r--r--docs/bare_metal_provisioning.rst148
-rw-r--r--docs/img/installer_workflow.pngbin0 -> 64943 bytes
-rw-r--r--docs/index.rst5
-rw-r--r--src/k8splugin/api/api.go16
-rw-r--r--src/k8splugin/api/defhandler.go (renamed from src/k8splugin/api/vnfdhandler.go)69
-rw-r--r--src/k8splugin/api/defhandler_test.go (renamed from src/k8splugin/api/vnfdhandler_test.go)201
-rw-r--r--src/k8splugin/api/handler.go55
-rw-r--r--src/k8splugin/api/handler_test.go101
-rw-r--r--src/k8splugin/db/consul.go38
-rw-r--r--src/k8splugin/db/consul_test.go63
-rw-r--r--src/k8splugin/db/mongo.go323
-rw-r--r--src/k8splugin/db/mongo_test.go530
-rw-r--r--src/k8splugin/db/store.go27
-rw-r--r--src/k8splugin/db/store_test.go4
-rw-r--r--src/k8splugin/db/testing.go42
-rw-r--r--src/k8splugin/go.mod60
-rw-r--r--src/k8splugin/go.sum46
-rw-r--r--src/k8splugin/rb/archive.go65
-rw-r--r--src/k8splugin/rb/archive_test.go66
-rw-r--r--src/k8splugin/rb/definition.go155
-rw-r--r--src/k8splugin/rb/definition_test.go396
-rw-r--r--src/k8splugin/vnfd/vnfd.go134
-rw-r--r--src/k8splugin/vnfd/vnfd_test.go262
-rw-r--r--tox.ini1
-rw-r--r--vagrant/Vagrantfile17
-rwxr-xr-xvagrant/aio.sh58
-rw-r--r--vagrant/galaxy-requirements.yml4
-rw-r--r--vagrant/insecure_keys/key27
-rw-r--r--vagrant/insecure_keys/key.pub1
-rwxr-xr-xvagrant/installer.sh208
-rw-r--r--vagrant/inventory/group_vars/k8s-cluster.yml17
-rw-r--r--vagrant/playbooks/configure-istio.yml7
-rw-r--r--vagrant/playbooks/configure-krd.yml2
-rw-r--r--vagrant/playbooks/configure-multus.yml11
-rw-r--r--vagrant/playbooks/configure-nfd.yml8
-rw-r--r--vagrant/playbooks/configure-ovn-kubernetes.yml5
-rw-r--r--vagrant/playbooks/configure-ovn4nfv.yml98
-rw-r--r--vagrant/playbooks/configure-virtlet.yml20
-rw-r--r--vagrant/playbooks/krd-vars.yml26
-rwxr-xr-xvagrant/setup.sh7
-rwxr-xr-xvagrant/tests/_common.sh334
-rwxr-xr-xvagrant/tests/_functions.sh62
-rwxr-xr-xvagrant/tests/integration_cFW.sh4
-rwxr-xr-xvagrant/tests/integration_vFW.sh2
-rwxr-xr-xvagrant/tests/integration_vcFW.sh13
-rwxr-xr-xvagrant/tests/ovn4nfv.sh46
-rwxr-xr-xvagrant/tests/plugin.sh2
51 files changed, 2826 insertions, 988 deletions
diff --git a/.gitignore b/.gitignore
index b32f4c13..1e570c18 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
.*.swp
*.log
coverage.html
+docs/build
# Directories
pkg
diff --git a/deployments/build.sh b/deployments/build.sh
index 90da6f95..c6d4a244 100755
--- a/deployments/build.sh
+++ b/deployments/build.sh
@@ -35,7 +35,9 @@ function _cleanup {
echo "Cleaning previous execution"
docker-compose kill
image=$(grep "image.*k8plugin" docker-compose.yml)
- docker images ${image#*:} -q | xargs docker rmi -f
+ if [[ -n ${image} ]]; then
+ docker images ${image#*:} -q | xargs docker rmi -f
+ fi
docker ps -a --filter "status=exited" -q | xargs docker rm
}
diff --git a/deployments/docker-compose.yml b/deployments/docker-compose.yml
index 73d5651c..a72bd096 100644
--- a/deployments/docker-compose.yml
+++ b/deployments/docker-compose.yml
@@ -28,33 +28,28 @@ services:
environment:
- CSAR_DIR=/opt/csar
- KUBE_CONFIG_DIR=/opt/kubeconfig
- - DATABASE_TYPE=consul
+ - DATABASE_TYPE=mongo
- DATABASE_IP=172.19.0.2
- PLUGINS_DIR=/opt/multicloud/k8s
- HTTP_PROXY=$HTTP_PROXY
- HTTPS_PROXY=$HTTPS_PROXY
- NO_PROXY=$NO_PROXY,172.19.0.2
depends_on:
- - consul
+ - mongo
links:
- - consul
+ - mongo
volumes:
- /opt/csar:/opt/csar
- /opt/kubeconfig:/opt/kubeconfig
- consul:
- image: consul
+ mongo:
+ image: mongo
networks:
multicloud_net:
ipv4_address: 172.19.0.2
environment:
- CONSUL_CLIENT_INTERFACE: 'eth0'
- CONSUL_BIND_INTERFACE: 'eth0'
HTTP_PROXY: $HTTP_PROXY
HTTPS_PROXY: $HTTPS_PROXY
NO_PROXY: $NO_PROXY
- command: ["agent", "-server", "-bootstrap-expect=1"]
- volumes:
- - /opt/consul/config:/consul/config
networks:
multicloud_net:
diff --git a/deployments/start.sh b/deployments/start.sh
index da2eacee..d1b9f68a 100755
--- a/deployments/start.sh
+++ b/deployments/start.sh
@@ -19,13 +19,13 @@ export IMAGE_NAME="nexus3.onap.org:10003/onap/multicloud/k8s"
export CSAR_DIR=/opt/csar
export KUBE_CONFIG_DIR=/opt/kubeconfig
-export DATABASE_TYPE=consul
+export DATABASE_TYPE=mongo
export PLUGINS_DIR=$k8s_path/src/k8splugin/plugins
-echo "Starting consul services"
+echo "Starting mongo services"
docker-compose kill
-docker-compose up -d consul
-export DATABASE_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aqf "name=consul"))
+docker-compose up -d mongo
+export DATABASE_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aqf "name=mongo"))
export no_proxy=$no_proxy,$DATABASE_IP
export NO_PROXY=$NO_PROXY,$DATABASE_IP
diff --git a/docs/bare_metal_provisioning.rst b/docs/bare_metal_provisioning.rst
new file mode 100644
index 00000000..2cb74afe
--- /dev/null
+++ b/docs/bare_metal_provisioning.rst
@@ -0,0 +1,148 @@
+.. Copyright 2018 Intel Corporation.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+***********************
+Bare-Metal Provisioning
+***********************
+
+The Kubernetes Reference Deployment, aka KRD, has been designed to be consumed
+by Virtual Machines as well as Bare-Metal servers. The *vagrant/aio.sh*
+script contains the bash instructions for provisioning an All-in-One Kubernetes
+deployment in a Bare-Metal server. This document lists the Hardware & Software
+requirements and walkthrough the instructions that *vagrant/aio.sh* contains.
+
+Hardware Requirements
+#####################
+
++-----------+--------+
+| Concept | Amount |
++===========+========+
+| CPUs | 8 |
++-----------+--------+
+| Memory | 32GB |
++-----------+--------+
+| Hard Disk | 150GB |
++-----------+--------+
+
+Software Requirements
+#####################
+
+- Ubuntu Server 16.04 LTS
+
+vagrant/aio.sh
+##############
+
+This bash script provides an automated process for deploying an All-in-One
+Kubernetes cluster. Given that the ansible inventory file created by this
+script doesn't specify any information about user and password, it's necessary
+to execute this script as root user.
+
+The following two instructions start the provisioning process.
+
+.. code-block:: bash
+
+ $ sudo su
+ # wget -O - https://git.onap.org/multicloud/k8s/plain/vagrant/aio.sh | bash
+
+In overall, this script can be summarized in three general phases:
+
+1. Cloning and configuring the KRD project.
+2. Enabiling Nested-Virtualization.
+3. Deploying KRD services.
+
+**Cloning and configuring the KRD project**
+
+KRD requires multiple files(bash scripts and ansible playbooks) to operate.
+Therefore, it's necessary to clone the *ONAP multicloud/k8s* project to get
+access to the *vagrant* folder.
+
+.. code-block:: bash
+
+ git clone https://git.onap.org/multicloud/k8s/
+
+Ansible works agains multiple systems, the way for selecting them is through the
+usage of the inventory. The inventory file is a static source for determining the
+target servers used for the execution of ansible tasks. The *aio.sh* script creates
+an inventory file for addressing those tasks to localhost.
+
+.. code-block:: bash
+
+ cat <<EOL > inventory/hosts.ini
+ [all]
+ localhost
+
+ [kube-master]
+ localhost
+
+ [kube-node]
+ localhost
+
+ [etcd]
+ localhost
+
+ [ovn-central]
+ localhost
+
+ [ovn-controller]
+ localhost
+
+ [virtlet]
+ localhost
+
+ [k8s-cluster:children]
+ kube-node
+ kube-master
+ EOL
+
+KRD consumes kubespray_ for provisioning a Kubernetes base deployment. As part
+of the deployment process, this tool downloads and configures *kubectl* binary.
+This action conflicts with *andrewrothstein.kubectl* ansible role. Therefore is
+necessary to remove those instructions from all the ansible playbooks.
+
+.. _kubespray: https://github.com/kubernetes-incubator/kubespray
+
+.. code-block:: bash
+
+ # sed -i '/andrewrothstein.kubectl/d' playbooks/configure-*.yml
+
+Ansible uses SSH protocol for executing remote instructions. The following
+instructions create and register ssh keys which avoid the usage of passwords.
+
+.. code-block:: bash
+
+ # echo -e "\n\n\n" | ssh-keygen -t rsa -N ""
+ # cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
+ # chmod og-wx ~/.ssh/authorized_keys
+
+**Enabling Nested-Virtualization**
+
+KRD installs Virtlet_ Kubernetes CRI for running Virtual Machine workloads.
+Nested-virtualization gives the ability of running a Virtual Machine within
+another. The *node.sh* bash script contains the instructions for enabling
+Nested-Virtualization.
+
+.. _Virtlet : https://github.com/Mirantis/virtlet
+
+.. code-block:: bash
+
+ # ./node.sh
+
+**Deploying KRD services**
+
+Finally, the KRD provisioning process can be started through the use of
+*installer.sh* bash script. The output of this script is collected in the
+*krd_installer.log* file for future reference.
+
+.. code-block:: bash
+
+ # ./installer.sh | tee krd_installer.log
+
+.. image:: ./img/installer_workflow.png
diff --git a/docs/img/installer_workflow.png b/docs/img/installer_workflow.png
new file mode 100644
index 00000000..95d1bdb5
--- /dev/null
+++ b/docs/img/installer_workflow.png
Binary files differ
diff --git a/docs/index.rst b/docs/index.rst
index 127f0b0e..173076b4 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -18,5 +18,6 @@ Table of contents
.. toctree::
:maxdepth: 3
- Project Architecture <krd_architecture>
- Sample Commands <sampleCommands>
+ KRD Project Architecture <krd_architecture>
+ Bare Metal All-in-One KRD deployment<bare_metal_provisioning>
+ Kubernetes MultiCloud API sample ommands <sampleCommands>
diff --git a/src/k8splugin/api/api.go b/src/k8splugin/api/api.go
index 46afadd6..06f5009f 100644
--- a/src/k8splugin/api/api.go
+++ b/src/k8splugin/api/api.go
@@ -14,7 +14,7 @@ limitations under the License.
package api
import (
- "k8splugin/vnfd"
+ "k8splugin/rb"
"os"
"path/filepath"
"plugin"
@@ -106,13 +106,13 @@ func NewRouter(kubeconfig string) *mux.Router {
vnfInstanceHandler.HandleFunc("/{cloudRegionID}/{namespace}/{externalVNFID}", DeleteHandler).Methods("DELETE")
vnfInstanceHandler.HandleFunc("/{cloudRegionID}/{namespace}/{externalVNFID}", GetHandler).Methods("GET")
- vnfdRouter := router.PathPrefix("/v1/vnfd").Subrouter()
- vh := vnfdHandler{vnfdClient: vnfd.GetVNFDClient()}
- vnfdRouter.HandleFunc("", vh.vnfdCreateHandler).Methods("POST")
- vnfdRouter.HandleFunc("/{vnfdID}/upload", vh.vnfdUploadHandler).Methods("POST")
- vnfdRouter.HandleFunc("", vh.vnfdListHandler).Methods("GET")
- vnfdRouter.HandleFunc("/{vnfdID}", vh.vnfdGetHandler).Methods("GET")
- vnfdRouter.HandleFunc("/{vnfdID}", vh.vnfdDeleteHandler).Methods("DELETE")
+ resRouter := router.PathPrefix("/v1/rb").Subrouter()
+ rbdef := rbDefinitionHandler{client: rb.NewDefinitionClient()}
+ resRouter.HandleFunc("/definition", rbdef.createHandler).Methods("POST")
+ resRouter.HandleFunc("/definition/{rbdID}/content", rbdef.uploadHandler).Methods("POST")
+ resRouter.HandleFunc("/definition", rbdef.listHandler).Methods("GET")
+ resRouter.HandleFunc("/definition/{rbdID}", rbdef.getHandler).Methods("GET")
+ resRouter.HandleFunc("/definition/{rbdID}", rbdef.deleteHandler).Methods("DELETE")
// (TODO): Fix update method
// vnfInstanceHandler.HandleFunc("/{vnfInstanceId}", UpdateHandler).Methods("PUT")
diff --git a/src/k8splugin/api/vnfdhandler.go b/src/k8splugin/api/defhandler.go
index ff777826..222baaee 100644
--- a/src/k8splugin/api/vnfdhandler.go
+++ b/src/k8splugin/api/defhandler.go
@@ -18,24 +18,24 @@ package api
import (
"encoding/json"
+ "io/ioutil"
+ "k8splugin/rb"
"net/http"
- "k8splugin/vnfd"
-
"github.com/gorilla/mux"
)
// Used to store backend implementations objects
// Also simplifies mocking for unit testing purposes
-type vnfdHandler struct {
- // Interface that implements vnfDefinition operations
+type rbDefinitionHandler struct {
+ // Interface that implements bundle Definition operations
// We will set this variable with a mock interface for testing
- vnfdClient vnfd.VNFDefinitionInterface
+ client rb.DefinitionManager
}
-// vnfdCreateHandler handles creation of the vnfd entry in the database
-func (h vnfdHandler) vnfdCreateHandler(w http.ResponseWriter, r *http.Request) {
- var v vnfd.VNFDefinition
+// createHandler handles creation of the definition entry in the database
+func (h rbDefinitionHandler) createHandler(w http.ResponseWriter, r *http.Request) {
+ var v rb.Definition
if r.Body == nil {
http.Error(w, "Empty body", http.StatusBadRequest)
@@ -54,7 +54,7 @@ func (h vnfdHandler) vnfdCreateHandler(w http.ResponseWriter, r *http.Request) {
return
}
- ret, err := h.vnfdClient.Create(v)
+ ret, err := h.client.Create(v)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -69,15 +69,36 @@ func (h vnfdHandler) vnfdCreateHandler(w http.ResponseWriter, r *http.Request) {
}
}
-// vnfdUploadHandler handles upload of the vnf tar file into the database
+// uploadHandler handles upload of the bundle tar file into the database
// Note: This will be implemented in a different patch
-func (h vnfdHandler) vnfdUploadHandler(w http.ResponseWriter, r *http.Request) {
+func (h rbDefinitionHandler) uploadHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ uuid := vars["rbdID"]
+
+ if r.Body == nil {
+ http.Error(w, "Empty Body", http.StatusBadRequest)
+ return
+ }
+
+ inpBytes, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ http.Error(w, "Unable to read body", http.StatusBadRequest)
+ return
+ }
+
+ err = h.client.Upload(uuid, inpBytes)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusOK)
}
-// vnfdListHandler handles GET (list) operations on the /v1/vnfd endpoint
-// Returns a list of vnfd.VNFDefinitions
-func (h vnfdHandler) vnfdListHandler(w http.ResponseWriter, r *http.Request) {
- ret, err := h.vnfdClient.List()
+// listHandler handles GET (list) operations on the endpoint
+// Returns a list of rb.Definitions
+func (h rbDefinitionHandler) listHandler(w http.ResponseWriter, r *http.Request) {
+ ret, err := h.client.List()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -92,13 +113,13 @@ func (h vnfdHandler) vnfdListHandler(w http.ResponseWriter, r *http.Request) {
}
}
-// vnfdGetHandler handles GET operations on a particular VNFID
-// Returns a vnfd.VNFDefinition
-func (h vnfdHandler) vnfdGetHandler(w http.ResponseWriter, r *http.Request) {
+// getHandler handles GET operations on a particular ids
+// Returns a rb.Definition
+func (h rbDefinitionHandler) getHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
- vnfdID := vars["vnfdID"]
+ id := vars["rbdID"]
- ret, err := h.vnfdClient.Get(vnfdID)
+ ret, err := h.client.Get(id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -113,12 +134,12 @@ func (h vnfdHandler) vnfdGetHandler(w http.ResponseWriter, r *http.Request) {
}
}
-// vnfdDeleteHandler handles DELETE operations on a particular VNFID
-func (h vnfdHandler) vnfdDeleteHandler(w http.ResponseWriter, r *http.Request) {
+// deleteHandler handles DELETE operations on a particular bundle definition id
+func (h rbDefinitionHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
- vnfdID := vars["vnfdID"]
+ id := vars["rbdID"]
- err := h.vnfdClient.Delete(vnfdID)
+ err := h.client.Delete(id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
diff --git a/src/k8splugin/api/vnfdhandler_test.go b/src/k8splugin/api/defhandler_test.go
index e393be6f..9739ab12 100644
--- a/src/k8splugin/api/vnfdhandler_test.go
+++ b/src/k8splugin/api/defhandler_test.go
@@ -20,7 +20,7 @@ import (
"bytes"
"encoding/json"
"io"
- "k8splugin/vnfd"
+ "k8splugin/rb"
"net/http"
"net/http/httptest"
"reflect"
@@ -32,54 +32,58 @@ import (
//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 mockVNFDefinition struct {
- vnfd.VNFDefinitionInterface
+type mockRBDefinition struct {
+ rb.DefinitionManager
// Items and err will be used to customize each test
- // via a localized instantiation of mockVNFDefinition
- Items []vnfd.VNFDefinition
+ // via a localized instantiation of mockRBDefinition
+ Items []rb.Definition
Err error
}
-func (m *mockVNFDefinition) Create(inp vnfd.VNFDefinition) (vnfd.VNFDefinition, error) {
+func (m *mockRBDefinition) Create(inp rb.Definition) (rb.Definition, error) {
if m.Err != nil {
- return vnfd.VNFDefinition{}, m.Err
+ return rb.Definition{}, m.Err
}
return m.Items[0], nil
}
-func (m *mockVNFDefinition) List() ([]vnfd.VNFDefinition, error) {
+func (m *mockRBDefinition) List() ([]rb.Definition, error) {
if m.Err != nil {
- return []vnfd.VNFDefinition{}, m.Err
+ return []rb.Definition{}, m.Err
}
return m.Items, nil
}
-func (m *mockVNFDefinition) Get(vnfID string) (vnfd.VNFDefinition, error) {
+func (m *mockRBDefinition) Get(id string) (rb.Definition, error) {
if m.Err != nil {
- return vnfd.VNFDefinition{}, m.Err
+ return rb.Definition{}, m.Err
}
return m.Items[0], nil
}
-func (m *mockVNFDefinition) Delete(vnfID string) error {
+func (m *mockRBDefinition) Delete(id string) error {
return m.Err
}
-func TestVnfdCreateHandler(t *testing.T) {
+func (m *mockRBDefinition) Upload(id string, inp []byte) error {
+ return m.Err
+}
+
+func TestRBDefCreateHandler(t *testing.T) {
testCases := []struct {
label string
reader io.Reader
- expected vnfd.VNFDefinition
+ expected rb.Definition
expectedCode int
- vnfdClient *mockVNFDefinition
+ rbDefClient *mockRBDefinition
}{
{
label: "Missing Body Failure",
expectedCode: http.StatusBadRequest,
- vnfdClient: &mockVNFDefinition{},
+ rbDefClient: &mockRBDefinition{},
},
{
label: "Create without UUID",
@@ -89,18 +93,18 @@ func TestVnfdCreateHandler(t *testing.T) {
"description":"test description",
"service-type":"firewall"
}`)),
- expected: vnfd.VNFDefinition{
+ expected: rb.Definition{
UUID: "123e4567-e89b-12d3-a456-426655440000",
- Name: "testvnf",
+ Name: "testresourcebundle",
Description: "test description",
ServiceType: "firewall",
},
- vnfdClient: &mockVNFDefinition{
+ rbDefClient: &mockRBDefinition{
//Items that will be returned by the mocked Client
- Items: []vnfd.VNFDefinition{
+ Items: []rb.Definition{
{
UUID: "123e4567-e89b-12d3-a456-426655440000",
- Name: "testvnf",
+ Name: "testresourcebundle",
Description: "test description",
ServiceType: "firewall",
},
@@ -111,15 +115,15 @@ func TestVnfdCreateHandler(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
- vh := vnfdHandler{vnfdClient: testCase.vnfdClient}
- req, err := http.NewRequest("POST", "/v1/vnfd", testCase.reader)
+ vh := rbDefinitionHandler{client: testCase.rbDefClient}
+ req, err := http.NewRequest("POST", "/v1/resource/definition", testCase.reader)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
- hr := http.HandlerFunc(vh.vnfdCreateHandler)
+ hr := http.HandlerFunc(vh.createHandler)
hr.ServeHTTP(rr, req)
//Check returned code
@@ -129,11 +133,11 @@ func TestVnfdCreateHandler(t *testing.T) {
//Check returned body only if statusCreated
if rr.Code == http.StatusCreated {
- got := vnfd.VNFDefinition{}
+ got := rb.Definition{}
json.NewDecoder(rr.Body).Decode(&got)
if reflect.DeepEqual(testCase.expected, got) == false {
- t.Errorf("vnfdCreateHandler returned unexpected body: got %v;"+
+ t.Errorf("createHandler returned unexpected body: got %v;"+
" expected %v", got, testCase.expected)
}
}
@@ -141,43 +145,43 @@ func TestVnfdCreateHandler(t *testing.T) {
}
}
-func TestVnfdListHandler(t *testing.T) {
+func TestRBDefListHandler(t *testing.T) {
testCases := []struct {
label string
- expected []vnfd.VNFDefinition
+ expected []rb.Definition
expectedCode int
- vnfdClient *mockVNFDefinition
+ rbDefClient *mockRBDefinition
}{
{
- label: "List VNF Definitions",
+ label: "List Bundle Definitions",
expectedCode: http.StatusOK,
- expected: []vnfd.VNFDefinition{
+ expected: []rb.Definition{
{
UUID: "123e4567-e89b-12d3-a456-426655440000",
- Name: "testvnf",
+ Name: "testresourcebundle",
Description: "test description",
ServiceType: "firewall",
},
{
UUID: "123e4567-e89b-12d3-a456-426655441111",
- Name: "testvnf2",
+ Name: "testresourcebundle2",
Description: "test description",
ServiceType: "dns",
},
},
- vnfdClient: &mockVNFDefinition{
+ rbDefClient: &mockRBDefinition{
// list of definitions that will be returned by the mockclient
- Items: []vnfd.VNFDefinition{
+ Items: []rb.Definition{
{
UUID: "123e4567-e89b-12d3-a456-426655440000",
- Name: "testvnf",
+ Name: "testresourcebundle",
Description: "test description",
ServiceType: "firewall",
},
{
UUID: "123e4567-e89b-12d3-a456-426655441111",
- Name: "testvnf2",
+ Name: "testresourcebundle2",
Description: "test description",
ServiceType: "dns",
},
@@ -188,14 +192,14 @@ func TestVnfdListHandler(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
- vh := vnfdHandler{vnfdClient: testCase.vnfdClient}
- req, err := http.NewRequest("GET", "/v1/vnfd", nil)
+ vh := rbDefinitionHandler{client: testCase.rbDefClient}
+ req, err := http.NewRequest("GET", "/v1/resource/definition", nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
- hr := http.HandlerFunc(vh.vnfdListHandler)
+ hr := http.HandlerFunc(vh.listHandler)
hr.ServeHTTP(rr, req)
//Check returned code
@@ -205,11 +209,11 @@ func TestVnfdListHandler(t *testing.T) {
//Check returned body only if statusOK
if rr.Code == http.StatusOK {
- got := []vnfd.VNFDefinition{}
+ got := []rb.Definition{}
json.NewDecoder(rr.Body).Decode(&got)
if reflect.DeepEqual(testCase.expected, got) == false {
- t.Errorf("vnfdListHandler returned unexpected body: got %v;"+
+ t.Errorf("listHandler returned unexpected body: got %v;"+
" expected %v", got, testCase.expected)
}
}
@@ -217,31 +221,31 @@ func TestVnfdListHandler(t *testing.T) {
}
}
-func TestVnfdGetHandler(t *testing.T) {
+func TestRBDefGetHandler(t *testing.T) {
testCases := []struct {
label string
- expected vnfd.VNFDefinition
+ expected rb.Definition
inpUUID string
expectedCode int
- vnfdClient *mockVNFDefinition
+ rbDefClient *mockRBDefinition
}{
{
- label: "Get VNF Definition",
+ label: "Get Bundle Definition",
expectedCode: http.StatusOK,
- expected: vnfd.VNFDefinition{
+ expected: rb.Definition{
UUID: "123e4567-e89b-12d3-a456-426655441111",
- Name: "testvnf2",
+ Name: "testresourcebundle2",
Description: "test description",
ServiceType: "dns",
},
inpUUID: "123e4567-e89b-12d3-a456-426655441111",
- vnfdClient: &mockVNFDefinition{
+ rbDefClient: &mockRBDefinition{
// list of definitions that will be returned by the mockclient
- Items: []vnfd.VNFDefinition{
+ Items: []rb.Definition{
{
UUID: "123e4567-e89b-12d3-a456-426655441111",
- Name: "testvnf2",
+ Name: "testresourcebundle2",
Description: "test description",
ServiceType: "dns",
},
@@ -249,12 +253,12 @@ func TestVnfdGetHandler(t *testing.T) {
},
},
{
- label: "Get Non-Exiting VNF Definition",
+ label: "Get Non-Exiting Bundle Definition",
expectedCode: http.StatusInternalServerError,
inpUUID: "123e4567-e89b-12d3-a456-426655440000",
- vnfdClient: &mockVNFDefinition{
+ rbDefClient: &mockRBDefinition{
// list of definitions that will be returned by the mockclient
- Items: []vnfd.VNFDefinition{},
+ Items: []rb.Definition{},
Err: pkgerrors.New("Internal Error"),
},
},
@@ -262,14 +266,14 @@ func TestVnfdGetHandler(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
- vh := vnfdHandler{vnfdClient: testCase.vnfdClient}
- req, err := http.NewRequest("GET", "/v1/vnfd/"+testCase.inpUUID, nil)
+ vh := rbDefinitionHandler{client: testCase.rbDefClient}
+ req, err := http.NewRequest("GET", "/v1/resource/definition/"+testCase.inpUUID, nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
- hr := http.HandlerFunc(vh.vnfdGetHandler)
+ hr := http.HandlerFunc(vh.getHandler)
hr.ServeHTTP(rr, req)
//Check returned code
@@ -279,11 +283,11 @@ func TestVnfdGetHandler(t *testing.T) {
//Check returned body only if statusOK
if rr.Code == http.StatusOK {
- got := vnfd.VNFDefinition{}
+ got := rb.Definition{}
json.NewDecoder(rr.Body).Decode(&got)
if reflect.DeepEqual(testCase.expected, got) == false {
- t.Errorf("vnfdListHandler returned unexpected body: got %v;"+
+ t.Errorf("listHandler returned unexpected body: got %v;"+
" expected %v", got, testCase.expected)
}
}
@@ -291,25 +295,25 @@ func TestVnfdGetHandler(t *testing.T) {
}
}
-func TestVnfdDeleteHandler(t *testing.T) {
+func TestRBDefDeleteHandler(t *testing.T) {
testCases := []struct {
label string
inpUUID string
expectedCode int
- vnfdClient *mockVNFDefinition
+ rbDefClient *mockRBDefinition
}{
{
- label: "Delete VNF Definition",
+ label: "Delete Bundle Definition",
expectedCode: http.StatusNoContent,
inpUUID: "123e4567-e89b-12d3-a456-426655441111",
- vnfdClient: &mockVNFDefinition{},
+ rbDefClient: &mockRBDefinition{},
},
{
- label: "Delete Non-Exiting VNF Definition",
+ label: "Delete Non-Exiting Bundle Definition",
expectedCode: http.StatusInternalServerError,
inpUUID: "123e4567-e89b-12d3-a456-426655440000",
- vnfdClient: &mockVNFDefinition{
+ rbDefClient: &mockRBDefinition{
Err: pkgerrors.New("Internal Error"),
},
},
@@ -317,14 +321,75 @@ func TestVnfdDeleteHandler(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
- vh := vnfdHandler{vnfdClient: testCase.vnfdClient}
- req, err := http.NewRequest("GET", "/v1/vnfd/"+testCase.inpUUID, nil)
+ vh := rbDefinitionHandler{client: testCase.rbDefClient}
+ req, err := http.NewRequest("GET", "/v1/resource/definition/"+testCase.inpUUID, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ rr := httptest.NewRecorder()
+ hr := http.HandlerFunc(vh.deleteHandler)
+
+ hr.ServeHTTP(rr, req)
+ //Check returned code
+ if rr.Code != testCase.expectedCode {
+ t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, rr.Code)
+ }
+ })
+ }
+}
+
+func TestRBDefUploadHandler(t *testing.T) {
+
+ testCases := []struct {
+ label string
+ inpUUID string
+ body io.Reader
+ expectedCode int
+ rbDefClient *mockRBDefinition
+ }{
+ {
+ label: "Upload Bundle Definition Content",
+ expectedCode: http.StatusOK,
+ inpUUID: "123e4567-e89b-12d3-a456-426655441111",
+ body: bytes.NewBuffer([]byte{
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0xf2, 0x48, 0xcd,
+ }),
+ rbDefClient: &mockRBDefinition{},
+ },
+ {
+ label: "Upload Invalid Bundle Definition Content",
+ expectedCode: http.StatusInternalServerError,
+ inpUUID: "123e4567-e89b-12d3-a456-426655440000",
+ body: bytes.NewBuffer([]byte{
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0xf2, 0x48, 0xcd,
+ }),
+ rbDefClient: &mockRBDefinition{
+ Err: pkgerrors.New("Internal Error"),
+ },
+ },
+ {
+ label: "Upload Empty Body Content",
+ expectedCode: http.StatusBadRequest,
+ inpUUID: "123e4567-e89b-12d3-a456-426655440000",
+ rbDefClient: &mockRBDefinition{},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ vh := rbDefinitionHandler{client: testCase.rbDefClient}
+ req, err := http.NewRequest("POST",
+ "/v1/resource/definition/"+testCase.inpUUID+"/content", testCase.body)
+
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
- hr := http.HandlerFunc(vh.vnfdDeleteHandler)
+ hr := http.HandlerFunc(vh.uploadHandler)
hr.ServeHTTP(rr, req)
//Check returned code
diff --git a/src/k8splugin/api/handler.go b/src/k8splugin/api/handler.go
index 53fa2317..4c49ba78 100644
--- a/src/k8splugin/api/handler.go
+++ b/src/k8splugin/api/handler.go
@@ -30,6 +30,10 @@ import (
"k8splugin/krd"
)
+//TODO: Separate the http handler code and backend code out
+var storeName = "rbinst"
+var tagData = "data"
+
// GetVNFClient retrieves the client used to communicate with a Kubernetes Cluster
var GetVNFClient = func(kubeConfigPath string) (kubernetes.Clientset, error) {
client, err := krd.GetKubeClient(kubeConfigPath)
@@ -117,17 +121,9 @@ func CreateHandler(w http.ResponseWriter, r *http.Request) {
// TODO: Uncomment when annotations are done
// krd.AddNetworkAnnotationsToPod(kubeData, resource.Networks)
- // "{"deployment":<>,"service":<>}"
- serializedResourceNameMap, err := db.Serialize(resourceNameMap)
- if err != nil {
- werr := pkgerrors.Wrap(err, "Create VNF deployment JSON Marshalling error")
- http.Error(w, werr.Error(), http.StatusInternalServerError)
- return
- }
-
// key: cloud1-default-uuid
// value: "{"deployment":<>,"service":<>}"
- err = db.DBconn.Create(internalVNFID, serializedResourceNameMap)
+ err = db.DBconn.Create(storeName, internalVNFID, tagData, resourceNameMap)
if err != nil {
werr := pkgerrors.Wrap(err, "Create VNF deployment DB error")
http.Error(w, werr.Error(), http.StatusInternalServerError)
@@ -154,27 +150,22 @@ func ListHandler(w http.ResponseWriter, r *http.Request) {
namespace := vars["namespace"]
prefix := cloudRegionID + "-" + namespace
- internalVNFIDs, err := db.DBconn.ReadAll(prefix)
+ res, err := db.DBconn.ReadAll(storeName, tagData)
if err != nil {
http.Error(w, pkgerrors.Wrap(err, "Get VNF list error").Error(),
http.StatusInternalServerError)
return
}
- if len(internalVNFIDs) == 0 {
- w.WriteHeader(http.StatusNotFound)
- return
- }
-
// TODO: There is an edge case where if namespace is passed but is missing some characters
// trailing, it will print the result with those excluding characters. This is because of
// the way I am trimming the Prefix. This fix is needed.
var editedList []string
- for _, id := range internalVNFIDs {
- if len(id) > 0 {
- editedList = append(editedList, strings.TrimPrefix(id, prefix)[1:])
+ for key, value := range res {
+ if len(value) > 0 {
+ editedList = append(editedList, strings.TrimPrefix(key, prefix)[1:])
}
}
@@ -204,25 +195,20 @@ func DeleteHandler(w http.ResponseWriter, r *http.Request) {
// key: cloud1-default-uuid
// value: "{"deployment":<>,"service":<>}"
- serializedResourceNameMap, err := db.DBconn.Read(internalVNFID)
+ res, err := db.DBconn.Read(storeName, internalVNFID, tagData)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- if serializedResourceNameMap == "" {
- w.WriteHeader(http.StatusNotFound)
- return
- }
-
/*
{
"deployment": ["cloud1-default-uuid-sisedeploy1", "cloud1-default-uuid-sisedeploy2", ... ]
"service": ["cloud1-default-uuid-sisesvc1", "cloud1-default-uuid-sisesvc2", ... ]
},
*/
- deserializedResourceNameMap := make(map[string][]string)
- err = db.DeSerialize(serializedResourceNameMap, &deserializedResourceNameMap)
+ data := make(map[string][]string)
+ err = db.DBconn.Unmarshal(res, &data)
if err != nil {
werr := pkgerrors.Wrap(err, "Unmarshal VNF error")
http.Error(w, werr.Error(), http.StatusInternalServerError)
@@ -237,14 +223,14 @@ func DeleteHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- err = csar.DestroyVNF(deserializedResourceNameMap, namespace, &kubeclient)
+ err = csar.DestroyVNF(data, namespace, &kubeclient)
if err != nil {
werr := pkgerrors.Wrap(err, "Delete VNF error")
http.Error(w, werr.Error(), http.StatusInternalServerError)
return
}
- err = db.DBconn.Delete(internalVNFID)
+ err = db.DBconn.Delete(storeName, internalVNFID, tagData)
if err != nil {
werr := pkgerrors.Wrap(err, "Delete VNF db record error")
http.Error(w, werr.Error(), http.StatusInternalServerError)
@@ -337,25 +323,20 @@ func GetHandler(w http.ResponseWriter, r *http.Request) {
// key: cloud1-default-uuid
// value: "{"deployment":<>,"service":<>}"
- serializedResourceNameMap, err := db.DBconn.Read(internalVNFID)
+ res, err := db.DBconn.Read(storeName, internalVNFID, tagData)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- if serializedResourceNameMap == "" {
- w.WriteHeader(http.StatusNotFound)
- return
- }
-
/*
{
"deployment": ["cloud1-default-uuid-sisedeploy1", "cloud1-default-uuid-sisedeploy2", ... ]
"service": ["cloud1-default-uuid-sisesvc1", "cloud1-default-uuid-sisesvc2", ... ]
},
*/
- deserializedResourceNameMap := make(map[string][]string)
- err = db.DeSerialize(serializedResourceNameMap, &deserializedResourceNameMap)
+ data := make(map[string][]string)
+ err = db.DBconn.Unmarshal(res, &data)
if err != nil {
werr := pkgerrors.Wrap(err, "Unmarshal VNF error")
http.Error(w, werr.Error(), http.StatusInternalServerError)
@@ -366,7 +347,7 @@ func GetHandler(w http.ResponseWriter, r *http.Request) {
VNFID: externalVNFID,
CloudRegionID: cloudRegionID,
Namespace: namespace,
- VNFComponents: deserializedResourceNameMap,
+ VNFComponents: data,
}
w.Header().Set("Content-Type", "application/json")
diff --git a/src/k8splugin/api/handler_test.go b/src/k8splugin/api/handler_test.go
index 3336bbc2..a3aeff7a 100644
--- a/src/k8splugin/api/handler_test.go
+++ b/src/k8splugin/api/handler_test.go
@@ -24,7 +24,6 @@ import (
"reflect"
"testing"
- "github.com/hashicorp/consul/api"
pkgerrors "github.com/pkg/errors"
"k8s.io/client-go/kubernetes"
@@ -194,37 +193,18 @@ func TestListHandler(t *testing.T) {
},
},
{
- label: "Get result from DB non-records",
- expectedCode: http.StatusNotFound,
- mockStore: &db.MockDB{},
- },
- {
label: "Get empty list",
expectedCode: http.StatusOK,
expectedResponse: []string{""},
- mockStore: &db.MockDB{
- Items: api.KVPairs{
- &api.KVPair{
- Key: "",
- Value: []byte("{}"),
- },
- },
- },
+ mockStore: &db.MockDB{},
},
{
label: "Succesful get a list of VNF",
expectedCode: http.StatusOK,
- expectedResponse: []string{"uid1", "uid2"},
+ expectedResponse: []string{"uid1"},
mockStore: &db.MockDB{
- Items: api.KVPairs{
- &api.KVPair{
- Key: "uuid1",
- Value: []byte("{}"),
- },
- &api.KVPair{
- Key: "uuid2",
- Value: []byte("{}"),
- },
+ Items: map[string][]byte{
+ "uuid1": []byte("{}"),
},
},
},
@@ -275,20 +255,17 @@ func TestDeleteHandler(t *testing.T) {
},
{
label: "Fail to find VNF record be deleted",
- expectedCode: http.StatusNotFound,
+ expectedCode: http.StatusInternalServerError,
mockStore: &db.MockDB{
- Items: api.KVPairs{},
+ Items: map[string][]byte{},
},
},
{
label: "Fail to unmarshal the DB record",
expectedCode: http.StatusInternalServerError,
mockStore: &db.MockDB{
- Items: api.KVPairs{
- &api.KVPair{
- Key: "cloudregion1-testnamespace-uuid1",
- Value: []byte("{invalid format}"),
- },
+ Items: map[string][]byte{
+ "cloudregion1-testnamespace-uuid1": []byte("{invalid format}"),
},
},
},
@@ -297,14 +274,10 @@ func TestDeleteHandler(t *testing.T) {
expectedCode: http.StatusInternalServerError,
mockGetVNFClientErr: pkgerrors.New("Get VNF client error"),
mockStore: &db.MockDB{
- Items: api.KVPairs{
- &api.KVPair{
- Key: "cloudregion1-testnamespace-uuid1",
- Value: []byte("{" +
- "\"deployment\": [\"deploy1\", \"deploy2\"]," +
- "\"service\": [\"svc1\", \"svc2\"]" +
- "}"),
- },
+ Items: map[string][]byte{
+ "cloudregion1-testnamespace-uuid1": []byte(
+ "{\"deployment\": [\"deploy1\", \"deploy2\"]," +
+ "\"service\": [\"svc1\", \"svc2\"]}"),
},
},
},
@@ -312,14 +285,10 @@ func TestDeleteHandler(t *testing.T) {
label: "Fail to destroy VNF",
expectedCode: http.StatusInternalServerError,
mockStore: &db.MockDB{
- Items: api.KVPairs{
- &api.KVPair{
- Key: "cloudregion1-testnamespace-uuid1",
- Value: []byte("{" +
- "\"deployment\": [\"deploy1\", \"deploy2\"]," +
- "\"service\": [\"svc1\", \"svc2\"]" +
- "}"),
- },
+ Items: map[string][]byte{
+ "cloudregion1-testnamespace-uuid1": []byte(
+ "{\"deployment\": [\"deploy1\", \"deploy2\"]," +
+ "\"service\": [\"svc1\", \"svc2\"]}"),
},
},
mockDeleteVNF: &mockCSAR{
@@ -330,14 +299,10 @@ func TestDeleteHandler(t *testing.T) {
label: "Succesful delete a VNF",
expectedCode: http.StatusAccepted,
mockStore: &db.MockDB{
- Items: api.KVPairs{
- &api.KVPair{
- Key: "cloudregion1-testnamespace-uuid1",
- Value: []byte("{" +
- "\"deployment\": [\"deploy1\", \"deploy2\"]," +
- "\"service\": [\"svc1\", \"svc2\"]" +
- "}"),
- },
+ Items: map[string][]byte{
+ "cloudregion1-testnamespace-uuid1": []byte(
+ "{\"deployment\": [\"deploy1\", \"deploy2\"]," +
+ "\"service\": [\"svc1\", \"svc2\"]}"),
},
},
mockDeleteVNF: &mockCSAR{},
@@ -440,18 +405,15 @@ func TestGetHandler(t *testing.T) {
},
{
label: "Not found DB record",
- expectedCode: http.StatusNotFound,
+ expectedCode: http.StatusInternalServerError,
mockStore: &db.MockDB{},
},
{
label: "Fail to unmarshal the DB record",
expectedCode: http.StatusInternalServerError,
mockStore: &db.MockDB{
- Items: api.KVPairs{
- &api.KVPair{
- Key: "cloud1-default-1",
- Value: []byte("{invalid-format}"),
- },
+ Items: map[string][]byte{
+ "cloud1-default-1": []byte("{invalid-format}"),
},
},
},
@@ -468,18 +430,11 @@ func TestGetHandler(t *testing.T) {
},
},
mockStore: &db.MockDB{
- Items: api.KVPairs{
- &api.KVPair{
- Key: "cloud1-default-1",
- Value: []byte("{" +
- "\"deployment\": [\"deploy1\", \"deploy2\"]," +
- "\"service\": [\"svc1\", \"svc2\"]" +
- "}"),
- },
- &api.KVPair{
- Key: "cloud1-default-2",
- Value: []byte("{}"),
- },
+ Items: map[string][]byte{
+ "cloud1-default-1": []byte(
+ "{\"deployment\": [\"deploy1\", \"deploy2\"]," +
+ "\"service\": [\"svc1\", \"svc2\"]}"),
+ "cloud1-default-2": []byte("{}"),
},
},
},
diff --git a/src/k8splugin/db/consul.go b/src/k8splugin/db/consul.go
index d7507242..a61a4c10 100644
--- a/src/k8splugin/db/consul.go
+++ b/src/k8splugin/db/consul.go
@@ -54,50 +54,64 @@ func NewConsulStore(store ConsulKVStore) (Store, error) {
// HealthCheck verifies if the database is up and running
func (c *ConsulStore) HealthCheck() error {
- _, err := c.Read("test")
+ _, err := c.Read("test", "test", "test")
if err != nil {
return pkgerrors.New("[ERROR] Cannot talk to Datastore. Check if it is running/reachable.")
}
return nil
}
+// Unmarshal implements any unmarshaling that is needed when using consul
+func (c *ConsulStore) Unmarshal(inp []byte, out interface{}) error {
+ return nil
+}
+
// Create is used to create a DB entry
-func (c *ConsulStore) Create(key, value string) error {
+func (c *ConsulStore) Create(root, key, tag string, data interface{}) error {
+
+ value, err := Serialize(data)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Serializing input data")
+ }
+
p := &api.KVPair{
Key: key,
Value: []byte(value),
}
- _, err := c.client.Put(p, nil)
+ _, err = c.client.Put(p, nil)
return err
}
// Read method returns the internalID for a particular externalID
-func (c *ConsulStore) Read(key string) (string, error) {
+func (c *ConsulStore) Read(root, key, tag string) ([]byte, error) {
+ key = root + "/" + key + "/" + tag
pair, _, err := c.client.Get(key, nil)
if err != nil {
- return "", err
+ return nil, err
}
if pair == nil {
- return "", nil
+ return nil, nil
}
- return string(pair.Value), nil
+ return pair.Value, nil
}
// Delete method removes an internalID from the Database
-func (c *ConsulStore) Delete(key string) error {
+func (c *ConsulStore) Delete(root, key, tag string) error {
_, err := c.client.Delete(key, nil)
return err
}
// ReadAll is used to get all ExternalIDs in a namespace
-func (c *ConsulStore) ReadAll(prefix string) ([]string, error) {
- pairs, _, err := c.client.List(prefix, nil)
+func (c *ConsulStore) ReadAll(root, tag string) (map[string][]byte, error) {
+ pairs, _, err := c.client.List(root, nil)
if err != nil {
return nil, err
}
- var result []string
+
+ //TODO: Filter results by tag and return it
+ result := make(map[string][]byte)
for _, keypair := range pairs {
- result = append(result, keypair.Key)
+ result[keypair.Key] = keypair.Value
}
return result, nil
diff --git a/src/k8splugin/db/consul_test.go b/src/k8splugin/db/consul_test.go
index ede1a5e9..754112ad 100644
--- a/src/k8splugin/db/consul_test.go
+++ b/src/k8splugin/db/consul_test.go
@@ -107,12 +107,14 @@ func TestConsulCreate(t *testing.T) {
}{
{
label: "Sucessful register a record to Consul Database",
- input: map[string]string{"key": "test-key", "value": "test-value"},
- mock: &mockConsulKVStore{},
+ input: map[string]string{"root": "rbinst", "key": "test-key",
+ "tag": "data", "value": "test-value"},
+ mock: &mockConsulKVStore{},
},
{
label: "Fail to create a new record in Consul Database",
- input: map[string]string{"key": "test-key", "value": "test-value"},
+ input: map[string]string{"root": "rbinst", "key": "test-key",
+ "tag": "data", "value": "test-value"},
mock: &mockConsulKVStore{
Err: pkgerrors.New("DB error"),
},
@@ -123,7 +125,8 @@ func TestConsulCreate(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
client, _ := NewConsulStore(testCase.mock)
- err := client.Create(testCase.input["key"], testCase.input["value"])
+ err := client.Create(testCase.input["root"], testCase.input["key"],
+ testCase.input["tag"], testCase.input["value"])
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("Create method return an un-expected (%s)", err)
@@ -139,18 +142,19 @@ func TestConsulCreate(t *testing.T) {
func TestConsulRead(t *testing.T) {
testCases := []struct {
label string
- input string
+ input map[string]string
mock *mockConsulKVStore
expectedError string
expectedResult string
}{
{
label: "Sucessful retrieve a record from Consul Database",
- input: "test",
+ input: map[string]string{"root": "rbinst", "key": "test",
+ "tag": "data"},
mock: &mockConsulKVStore{
Items: api.KVPairs{
&api.KVPair{
- Key: "test",
+ Key: "rbinst/test/data",
Value: []byte("test-value"),
},
},
@@ -159,12 +163,14 @@ func TestConsulRead(t *testing.T) {
},
{
label: "Fail retrieve a non-existing record from Consul Database",
- input: "test",
- mock: &mockConsulKVStore{},
+ input: map[string]string{"root": "rbinst", "key": "test-key",
+ "tag": "data"},
+ mock: &mockConsulKVStore{},
},
{
label: "Fail retrieve a record from Consul Database",
- input: "test",
+ input: map[string]string{"root": "rbinst", "key": "test-key",
+ "tag": "data"},
mock: &mockConsulKVStore{
Err: pkgerrors.New("DB error"),
},
@@ -175,7 +181,8 @@ func TestConsulRead(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
client, _ := NewConsulStore(testCase.mock)
- result, err := client.Read(testCase.input)
+ result, err := client.Read(testCase.input["root"], testCase.input["key"],
+ testCase.input["tag"])
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("Read method return an un-expected (%s)", err)
@@ -187,7 +194,7 @@ func TestConsulRead(t *testing.T) {
if testCase.expectedError != "" && testCase.expectedResult == "" {
t.Fatalf("Read method was expecting \"%s\" error message", testCase.expectedError)
}
- if !reflect.DeepEqual(testCase.expectedResult, result) {
+ if !reflect.DeepEqual(testCase.expectedResult, string(result)) {
t.Fatalf("Read method returned: \n%v\n and it was expected: \n%v", result, testCase.expectedResult)
}
@@ -199,14 +206,15 @@ func TestConsulRead(t *testing.T) {
func TestConsulDelete(t *testing.T) {
testCases := []struct {
label string
- input string
+ input map[string]string
mock *mockConsulKVStore
expectedError string
}{
{
label: "Sucessful delete a record to Consul Database",
- input: "test",
- mock: &mockConsulKVStore{},
+ input: map[string]string{"root": "rbinst", "key": "test-key",
+ "tag": "data"},
+ mock: &mockConsulKVStore{},
},
{
label: "Fail to delete a record in Consul Database",
@@ -220,7 +228,8 @@ func TestConsulDelete(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
client, _ := NewConsulStore(testCase.mock)
- err := client.Delete(testCase.input)
+ err := client.Delete(testCase.input["root"], testCase.input["key"],
+ testCase.input["tag"])
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("Delete method return an un-expected (%s)", err)
@@ -236,14 +245,15 @@ func TestConsulDelete(t *testing.T) {
func TestConsulReadAll(t *testing.T) {
testCases := []struct {
label string
- input string
+ input map[string]string
mock *mockConsulKVStore
expectedError string
- expectedResult []string
+ expectedResult map[string][]byte
}{
{
label: "Sucessful retrieve a list from Consul Database",
- input: "test",
+ input: map[string]string{"root": "rbinst", "key": "test-key",
+ "tag": "data"},
mock: &mockConsulKVStore{
Items: api.KVPairs{
&api.KVPair{
@@ -256,16 +266,20 @@ func TestConsulReadAll(t *testing.T) {
},
},
},
- expectedResult: []string{"test", "test2"},
+ expectedResult: map[string][]byte{"test": []byte("test-value"),
+ "test2": []byte("test-value2")},
},
{
label: "Sucessful retrieve an empty list from Consul Database",
- input: "test",
- mock: &mockConsulKVStore{},
+ input: map[string]string{"root": "rbinst", "key": "test-key",
+ "tag": "data"},
+ mock: &mockConsulKVStore{},
+ expectedResult: map[string][]byte{},
},
{
label: "Fail retrieve a record from Consul Database",
- input: "test",
+ input: map[string]string{"root": "rbinst", "key": "test-key",
+ "tag": "data"},
mock: &mockConsulKVStore{
Err: pkgerrors.New("DB error"),
},
@@ -276,7 +290,8 @@ func TestConsulReadAll(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
client, _ := NewConsulStore(testCase.mock)
- result, err := client.ReadAll(testCase.input)
+ result, err := client.ReadAll(testCase.input["root"],
+ testCase.input["tag"])
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("ReadAll method return an un-expected (%s)", err)
diff --git a/src/k8splugin/db/mongo.go b/src/k8splugin/db/mongo.go
new file mode 100644
index 00000000..311f044c
--- /dev/null
+++ b/src/k8splugin/db/mongo.go
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2018 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 db
+
+import (
+ "github.com/mongodb/mongo-go-driver/bson"
+ "github.com/mongodb/mongo-go-driver/bson/primitive"
+ "github.com/mongodb/mongo-go-driver/mongo"
+ "github.com/mongodb/mongo-go-driver/mongo/options"
+ pkgerrors "github.com/pkg/errors"
+ "golang.org/x/net/context"
+ "log"
+ "os"
+)
+
+// MongoCollection defines the a subset of MongoDB operations
+// Note: This interface is defined mainly for mock testing
+type MongoCollection interface {
+ InsertOne(ctx context.Context, document interface{},
+ opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error)
+ FindOne(ctx context.Context, filter interface{},
+ opts ...*options.FindOneOptions) *mongo.SingleResult
+ FindOneAndUpdate(ctx context.Context, filter interface{},
+ update interface{}, opts ...*options.FindOneAndUpdateOptions) *mongo.SingleResult
+ DeleteOne(ctx context.Context, filter interface{},
+ opts ...*options.DeleteOptions) (*mongo.DeleteResult, error)
+ Find(ctx context.Context, filter interface{},
+ opts ...*options.FindOptions) (mongo.Cursor, error)
+}
+
+// MongoStore is an implementation of the db.Store interface
+type MongoStore struct {
+ db *mongo.Database
+}
+
+// This exists only for allowing us to mock the collection object
+// for testing purposes
+var getCollection = func(coll string, m *MongoStore) MongoCollection {
+ return m.db.Collection(coll)
+}
+
+// This exists only for allowing us to mock the DecodeBytes function
+// Mainly because we cannot construct a SingleResult struct from our
+// tests. All fields in that struct are private.
+var decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) {
+ return sr.DecodeBytes()
+}
+
+// NewMongoStore initializes a Mongo Database with the name provided
+// If a database with that name exists, it will be returned
+func NewMongoStore(name string, store *mongo.Database) (Store, error) {
+ if store == nil {
+ ip := "mongodb://" + os.Getenv("DATABASE_IP") + ":27017"
+ mongoClient, err := mongo.NewClient(ip)
+ if err != nil {
+ return nil, err
+ }
+
+ err = mongoClient.Connect(context.Background())
+ if err != nil {
+ return nil, err
+ }
+ store = mongoClient.Database(name)
+ }
+
+ return &MongoStore{
+ db: store,
+ }, nil
+}
+
+// HealthCheck verifies if the database is up and running
+func (m *MongoStore) HealthCheck() error {
+
+ _, err := decodeBytes(m.db.RunCommand(context.Background(), bson.D{{"serverStatus", 1}}))
+ if err != nil {
+ return pkgerrors.Wrap(err, "Error getting server status")
+ }
+
+ return nil
+}
+
+// validateParams checks to see if any parameters are empty
+func (m *MongoStore) validateParams(args ...string) bool {
+ for _, v := range args {
+ if v == "" {
+ return false
+ }
+ }
+
+ return true
+}
+
+// Create is used to create a DB entry
+func (m *MongoStore) Create(coll, key, tag string, data interface{}) error {
+ if data == nil || !m.validateParams(coll, key, tag) {
+ return pkgerrors.New("No Data to store")
+ }
+
+ c := getCollection(coll, m)
+ ctx := context.Background()
+
+ //Insert the data and then add the objectID to the masterTable
+ res, err := c.InsertOne(ctx, bson.D{
+ {tag, data},
+ })
+ if err != nil {
+ return pkgerrors.Errorf("Error inserting into database: %s", err.Error())
+ }
+
+ //Add objectID of created data to masterKey document
+ //Create masterkey document if it does not exist
+ filter := bson.D{{"key", key}}
+
+ _, err = decodeBytes(
+ c.FindOneAndUpdate(
+ ctx,
+ filter,
+ bson.D{
+ {"$set", bson.D{
+ {tag, res.InsertedID},
+ }},
+ },
+ options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(options.After)))
+
+ if err != nil {
+ return pkgerrors.Errorf("Error updating master table: %s", err.Error())
+ }
+
+ return nil
+}
+
+// Unmarshal implements an unmarshaler for bson data that
+// is produced from the mongo database
+func (m *MongoStore) Unmarshal(inp []byte, out interface{}) error {
+ err := bson.Unmarshal(inp, out)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Unmarshaling bson")
+ }
+ return nil
+}
+
+// Read method returns the data stored for this key and for this particular tag
+func (m *MongoStore) Read(coll, key, tag string) ([]byte, error) {
+ if !m.validateParams(coll, key, tag) {
+ return nil, pkgerrors.New("Mandatory fields are missing")
+ }
+
+ c := getCollection(coll, m)
+ ctx := context.Background()
+
+ //Get the masterkey document based on given key
+ filter := bson.D{{"key", key}}
+ keydata, err := decodeBytes(c.FindOne(context.Background(), filter))
+ if err != nil {
+ return nil, pkgerrors.Errorf("Error finding master table: %s", err.Error())
+ }
+
+ //Read the tag objectID from document
+ tagoid, ok := keydata.Lookup(tag).ObjectIDOK()
+ if !ok {
+ return nil, pkgerrors.Errorf("Error finding objectID for tag %s", tag)
+ }
+
+ //Use tag objectID to read the data from store
+ filter = bson.D{{"_id", tagoid}}
+ tagdata, err := decodeBytes(c.FindOne(ctx, filter))
+ if err != nil {
+ return nil, pkgerrors.Errorf("Error reading found object: %s", err.Error())
+ }
+
+ //Return the data as a byte array
+ return tagdata.Lookup(tag).Value, nil
+}
+
+// Helper function that deletes an object by its ID
+func (m *MongoStore) deleteObjectByID(coll string, objID primitive.ObjectID) error {
+
+ c := getCollection(coll, m)
+ ctx := context.Background()
+
+ _, err := c.DeleteOne(ctx, bson.D{{"_id", objID}})
+ if err != nil {
+ return pkgerrors.Errorf("Error Deleting from database: %s", err.Error())
+ }
+
+ log.Printf("Deleted Obj with ID %s", objID.String())
+ return nil
+}
+
+// Delete method removes a document from the Database that matches key
+// TODO: delete all referenced docs if tag is empty string
+func (m *MongoStore) Delete(coll, key, tag string) error {
+ if !m.validateParams(coll, key, tag) {
+ return pkgerrors.New("Mandatory fields are missing")
+ }
+
+ c := getCollection(coll, m)
+ ctx := context.Background()
+
+ //Get the masterkey document based on given key
+ filter := bson.D{{"key", key}}
+ //Remove the tag ID entry from masterkey table
+ update := bson.D{
+ {
+ "$unset", bson.D{
+ {tag, ""},
+ },
+ },
+ }
+ keydata, err := decodeBytes(c.FindOneAndUpdate(ctx, filter, update,
+ options.FindOneAndUpdate().SetReturnDocument(options.Before)))
+ if err != nil {
+ return pkgerrors.Errorf("Error decoding master table after update: %s",
+ err.Error())
+ }
+
+ //Read the tag objectID from document
+ elems, err := keydata.Elements()
+ if err != nil {
+ return pkgerrors.Errorf("Error reading elements from database: %s", err.Error())
+ }
+
+ tagoid, ok := keydata.Lookup(tag).ObjectIDOK()
+ if !ok {
+ return pkgerrors.Errorf("Error finding objectID for tag %s", tag)
+ }
+
+ //Use tag objectID to read the data from store
+ err = m.deleteObjectByID(coll, tagoid)
+ if err != nil {
+ return pkgerrors.Errorf("Error deleting from database: %s", err.Error())
+ }
+
+ //Delete master table if no more tags left
+ //_id, key and tag should be elements in before doc
+ //if master table needs to be removed too
+ if len(elems) == 3 {
+ keyid, ok := keydata.Lookup("_id").ObjectIDOK()
+ if !ok {
+ return pkgerrors.Errorf("Error finding objectID for key %s", key)
+ }
+ err = m.deleteObjectByID(coll, keyid)
+ if err != nil {
+ return pkgerrors.Errorf("Error deleting master table from database: %s", err.Error())
+ }
+ }
+
+ return nil
+}
+
+// ReadAll is used to get all documents in db of a particular tag
+func (m *MongoStore) ReadAll(coll, tag string) (map[string][]byte, error) {
+ if !m.validateParams(coll, tag) {
+ return nil, pkgerrors.New("Missing collection or tag name")
+ }
+
+ c := getCollection(coll, m)
+ ctx := context.Background()
+
+ //Get all master tables in this collection
+ filter := bson.D{
+ {"key", bson.D{
+ {"$exists", true},
+ }},
+ }
+ cursor, err := c.Find(ctx, filter)
+ if err != nil {
+ return nil, pkgerrors.Errorf("Error reading from database: %s", err.Error())
+ }
+ defer cursor.Close(ctx)
+
+ //Iterate over all the master tables
+ result := make(map[string][]byte)
+ for cursor.Next(ctx) {
+ d, err := cursor.DecodeBytes()
+ if err != nil {
+ log.Printf("Unable to decode data in Readall: %s", err.Error())
+ continue
+ }
+
+ //Read key of each master table
+ key, ok := d.Lookup("key").StringValueOK()
+ if !ok {
+ log.Printf("Unable to read key string from mastertable %s", err.Error())
+ continue
+ }
+
+ //Get objectID of tag document
+ tid, ok := d.Lookup(tag).ObjectIDOK()
+ if !ok {
+ log.Printf("Did not find tag: %s", tag)
+ continue
+ }
+
+ //Find tag document and unmarshal it into []byte
+ tagData, err := decodeBytes(c.FindOne(ctx, bson.D{{"_id", tid}}))
+ if err != nil {
+ log.Printf("Unable to decode tag data %s", err.Error())
+ continue
+ }
+ result[key] = tagData.Lookup(tag).Value
+ }
+
+ if len(result) == 0 {
+ return result, pkgerrors.Errorf("Did not find any objects with tag: %s", tag)
+ }
+
+ return result, nil
+}
diff --git a/src/k8splugin/db/mongo_test.go b/src/k8splugin/db/mongo_test.go
new file mode 100644
index 00000000..1663e774
--- /dev/null
+++ b/src/k8splugin/db/mongo_test.go
@@ -0,0 +1,530 @@
+// +build unit
+
+/*
+ * Copyright 2018 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 db
+
+import (
+ "bytes"
+ "context"
+ "github.com/mongodb/mongo-go-driver/bson"
+ "github.com/mongodb/mongo-go-driver/mongo"
+ "github.com/mongodb/mongo-go-driver/mongo/options"
+ pkgerrors "github.com/pkg/errors"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+// Implements the mongo.Cursor interface
+type mockCursor struct {
+ mongo.Cursor
+ err error
+ bson bson.Raw
+ count int
+}
+
+func (mc *mockCursor) Next(ctx context.Context) bool {
+ if mc.count > 0 {
+ mc.count = mc.count - 1
+ return true
+ }
+ return false
+}
+
+func (mc *mockCursor) DecodeBytes() (bson.Raw, error) {
+ return mc.bson, mc.err
+}
+
+func (mc *mockCursor) Close(ctx context.Context) error {
+ return nil
+}
+
+//Implements the functions used currently in mongo.go
+type mockCollection struct {
+ Err error
+ mCursor mongo.Cursor
+}
+
+func (c *mockCollection) InsertOne(ctx context.Context, document interface{},
+ opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) {
+
+ if c.Err != nil {
+ return nil, c.Err
+ }
+
+ return &mongo.InsertOneResult{InsertedID: "_id1234"}, nil
+}
+
+func (c *mockCollection) FindOne(ctx context.Context, filter interface{},
+ opts ...*options.FindOneOptions) *mongo.SingleResult {
+
+ return &mongo.SingleResult{}
+}
+
+func (c *mockCollection) FindOneAndUpdate(ctx context.Context, filter interface{},
+ update interface{}, opts ...*options.FindOneAndUpdateOptions) *mongo.SingleResult {
+
+ return &mongo.SingleResult{}
+}
+
+func (c *mockCollection) DeleteOne(ctx context.Context, filter interface{},
+ opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) {
+
+ return nil, c.Err
+}
+
+func (c *mockCollection) Find(ctx context.Context, filter interface{},
+ opts ...*options.FindOptions) (mongo.Cursor, error) {
+
+ return c.mCursor, c.Err
+}
+
+func TestCreate(t *testing.T) {
+ testCases := []struct {
+ label string
+ input map[string]interface{}
+ mockColl *mockCollection
+ bson bson.Raw
+ expectedError string
+ }{
+ {
+ label: "Successfull creation of entry",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": "keyvalue",
+ "tag": "tagName",
+ "data": "Data In String Format",
+ },
+ bson: bson.Raw{'\x08', '\x00', '\x00', '\x00', '\x0A', 'x', '\x00', '\x00'},
+ mockColl: &mockCollection{},
+ },
+ {
+ label: "UnSuccessfull creation of entry",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": "keyvalue",
+ "tag": "tagName",
+ "data": "Data In String Format",
+ },
+ mockColl: &mockCollection{
+ Err: pkgerrors.New("DB Error"),
+ },
+ expectedError: "DB Error",
+ },
+ {
+ label: "Missing input fields",
+ input: map[string]interface{}{
+ "coll": "",
+ "key": "",
+ "tag": "",
+ "data": "",
+ },
+ expectedError: "No Data to store",
+ mockColl: &mockCollection{},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ m, _ := NewMongoStore("name", &mongo.Database{})
+ // Override the getCollection function with our mocked version
+ getCollection = func(coll string, m *MongoStore) MongoCollection {
+ return testCase.mockColl
+ }
+
+ decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) {
+ return testCase.bson, testCase.mockColl.Err
+ }
+
+ err := m.Create(testCase.input["coll"].(string), testCase.input["key"].(string),
+ testCase.input["tag"].(string), testCase.input["data"])
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Create method returned an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Create method returned an error (%s)", err)
+ }
+ }
+ })
+ }
+}
+
+func TestRead(t *testing.T) {
+ testCases := []struct {
+ label string
+ input map[string]interface{}
+ mockColl *mockCollection
+ bson bson.Raw
+ expectedError string
+ expected []byte
+ }{
+ {
+ label: "Successfull Read of entry",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": "keyvalue",
+ "tag": "metadata",
+ },
+ // Binary form of
+ // {
+ // "_id" : ObjectId("5c115156777ff85654248ae1"),
+ // "key" : "b82c4bb1-09ff-6093-4d58-8327b94e1e20",
+ // "metadata" : ObjectId("5c115156c9755047e318bbfd")
+ // }
+ bson: bson.Raw{
+ '\x5a', '\x00', '\x00', '\x00', '\x07', '\x5f', '\x69', '\x64',
+ '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', '\xf8',
+ '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x02', '\x6b', '\x65',
+ '\x79', '\x00', '\x25', '\x00', '\x00', '\x00', '\x62', '\x38',
+ '\x32', '\x63', '\x34', '\x62', '\x62', '\x31', '\x2d', '\x30',
+ '\x39', '\x66', '\x66', '\x2d', '\x36', '\x30', '\x39', '\x33',
+ '\x2d', '\x34', '\x64', '\x35', '\x38', '\x2d', '\x38', '\x33',
+ '\x32', '\x37', '\x62', '\x39', '\x34', '\x65', '\x31', '\x65',
+ '\x32', '\x30', '\x00', '\x07', '\x6d', '\x65', '\x74', '\x61',
+ '\x64', '\x61', '\x74', '\x61', '\x00', '\x5c', '\x11', '\x51',
+ '\x56', '\xc9', '\x75', '\x50', '\x47', '\xe3', '\x18', '\xbb',
+ '\xfd', '\x00',
+ },
+ mockColl: &mockCollection{},
+ // This is not the document because we are mocking decodeBytes
+ expected: []byte{92, 17, 81, 86, 201, 117, 80, 71, 227, 24, 187, 253},
+ },
+ {
+ label: "UnSuccessfull Read of entry: object not found",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": "keyvalue",
+ "tag": "badtag",
+ },
+ // Binary form of
+ // {
+ // "_id" : ObjectId("5c115156777ff85654248ae1"),
+ // "key" : "b82c4bb1-09ff-6093-4d58-8327b94e1e20",
+ // "metadata" : ObjectId("5c115156c9755047e318bbfd")
+ // }
+ bson: bson.Raw{
+ '\x5a', '\x00', '\x00', '\x00', '\x07', '\x5f', '\x69', '\x64',
+ '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', '\xf8',
+ '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x02', '\x6b', '\x65',
+ '\x79', '\x00', '\x25', '\x00', '\x00', '\x00', '\x62', '\x38',
+ '\x32', '\x63', '\x34', '\x62', '\x62', '\x31', '\x2d', '\x30',
+ '\x39', '\x66', '\x66', '\x2d', '\x36', '\x30', '\x39', '\x33',
+ '\x2d', '\x34', '\x64', '\x35', '\x38', '\x2d', '\x38', '\x33',
+ '\x32', '\x37', '\x62', '\x39', '\x34', '\x65', '\x31', '\x65',
+ '\x32', '\x30', '\x00', '\x07', '\x6d', '\x65', '\x74', '\x61',
+ '\x64', '\x61', '\x74', '\x61', '\x00', '\x5c', '\x11', '\x51',
+ '\x56', '\xc9', '\x75', '\x50', '\x47', '\xe3', '\x18', '\xbb',
+ '\xfd', '\x00',
+ },
+ mockColl: &mockCollection{},
+ expectedError: "Error finding objectID",
+ },
+ {
+ label: "UnSuccessfull Read of entry",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": "keyvalue",
+ "tag": "tagName",
+ },
+ mockColl: &mockCollection{
+ Err: pkgerrors.New("DB Error"),
+ },
+ expectedError: "DB Error",
+ },
+ {
+ label: "Missing input fields",
+ input: map[string]interface{}{
+ "coll": "",
+ "key": "",
+ "tag": "",
+ },
+ expectedError: "Mandatory fields are missing",
+ mockColl: &mockCollection{},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ m, _ := NewMongoStore("name", &mongo.Database{})
+ // Override the getCollection function with our mocked version
+ getCollection = func(coll string, m *MongoStore) MongoCollection {
+ return testCase.mockColl
+ }
+
+ decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) {
+ return testCase.bson, testCase.mockColl.Err
+ }
+ got, err := m.Read(testCase.input["coll"].(string), testCase.input["key"].(string),
+ testCase.input["tag"].(string))
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Read method returned an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Read method returned an error (%s)", err)
+ }
+ } else {
+ if bytes.Compare(got, testCase.expected) != 0 {
+ t.Fatalf("Read returned unexpected data: %s, expected: %s",
+ string(got), testCase.expected)
+ }
+ }
+ })
+ }
+}
+
+func TestDelete(t *testing.T) {
+ testCases := []struct {
+ label string
+ input map[string]interface{}
+ mockColl *mockCollection
+ bson bson.Raw
+ expectedError string
+ }{
+ {
+ label: "Successfull Delete of entry",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": "keyvalue",
+ "tag": "metadata",
+ },
+ // Binary form of
+ // {
+ // "_id" : ObjectId("5c115156777ff85654248ae1"),
+ // "key" : "b82c4bb1-09ff-6093-4d58-8327b94e1e20",
+ // "metadata" : ObjectId("5c115156c9755047e318bbfd")
+ // }
+ bson: bson.Raw{
+ '\x5a', '\x00', '\x00', '\x00', '\x07', '\x5f', '\x69', '\x64',
+ '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', '\xf8',
+ '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x02', '\x6b', '\x65',
+ '\x79', '\x00', '\x25', '\x00', '\x00', '\x00', '\x62', '\x38',
+ '\x32', '\x63', '\x34', '\x62', '\x62', '\x31', '\x2d', '\x30',
+ '\x39', '\x66', '\x66', '\x2d', '\x36', '\x30', '\x39', '\x33',
+ '\x2d', '\x34', '\x64', '\x35', '\x38', '\x2d', '\x38', '\x33',
+ '\x32', '\x37', '\x62', '\x39', '\x34', '\x65', '\x31', '\x65',
+ '\x32', '\x30', '\x00', '\x07', '\x6d', '\x65', '\x74', '\x61',
+ '\x64', '\x61', '\x74', '\x61', '\x00', '\x5c', '\x11', '\x51',
+ '\x56', '\xc9', '\x75', '\x50', '\x47', '\xe3', '\x18', '\xbb',
+ '\xfd', '\x00',
+ },
+ mockColl: &mockCollection{},
+ },
+ {
+ label: "UnSuccessfull Delete of entry",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": "keyvalue",
+ "tag": "tagName",
+ },
+ mockColl: &mockCollection{
+ Err: pkgerrors.New("DB Error"),
+ },
+ expectedError: "DB Error",
+ },
+ {
+ label: "UnSuccessfull Delete, key not found",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": "keyvalue",
+ "tag": "tagName",
+ },
+ bson: bson.Raw{
+ '\x5a', '\x00', '\x00', '\x00', '\x07', '\x5f', '\x69', '\x64',
+ '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', '\xf8',
+ '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x02', '\x6b', '\x65',
+ '\x79', '\x00', '\x25', '\x00', '\x00', '\x00', '\x62', '\x38',
+ '\x32', '\x63', '\x34', '\x62', '\x62', '\x31', '\x2d', '\x30',
+ '\x39', '\x66', '\x66', '\x2d', '\x36', '\x30', '\x39', '\x33',
+ '\x2d', '\x34', '\x64', '\x35', '\x38', '\x2d', '\x38', '\x33',
+ '\x32', '\x37', '\x62', '\x39', '\x34', '\x65', '\x31', '\x65',
+ '\x32', '\x30', '\x00', '\x07', '\x6d', '\x65', '\x74', '\x61',
+ '\x64', '\x61', '\x74', '\x61', '\x00', '\x5c', '\x11', '\x51',
+ '\x56', '\xc9', '\x75', '\x50', '\x47', '\xe3', '\x18', '\xbb',
+ '\xfd', '\x00',
+ },
+ mockColl: &mockCollection{},
+ expectedError: "Error finding objectID",
+ },
+ {
+ label: "Missing input fields",
+ input: map[string]interface{}{
+ "coll": "",
+ "key": "",
+ "tag": "",
+ },
+ expectedError: "Mandatory fields are missing",
+ mockColl: &mockCollection{},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ m, _ := NewMongoStore("name", &mongo.Database{})
+ // Override the getCollection function with our mocked version
+ getCollection = func(coll string, m *MongoStore) MongoCollection {
+ return testCase.mockColl
+ }
+
+ decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) {
+ return testCase.bson, testCase.mockColl.Err
+ }
+ err := m.Delete(testCase.input["coll"].(string), testCase.input["key"].(string),
+ testCase.input["tag"].(string))
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Delete method returned an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Delete method returned an error (%s)", err)
+ }
+ }
+ })
+ }
+}
+
+func TestReadAll(t *testing.T) {
+ testCases := []struct {
+ label string
+ input map[string]interface{}
+ mockColl *mockCollection
+ bson bson.Raw
+ expectedError string
+ expected map[string][]byte
+ }{
+ {
+ label: "Successfully Read all entries",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "tag": "metadata",
+ },
+ mockColl: &mockCollection{
+ mCursor: &mockCursor{
+ // Binary form of
+ // {
+ // "_id" : ObjectId("5c115156777ff85654248ae1"),
+ // "key" : "b82c4bb1-09ff-6093-4d58-8327b94e1e20",
+ // "metadata" : ObjectId("5c115156c9755047e318bbfd")
+ // }
+ bson: bson.Raw{
+ '\x5a', '\x00', '\x00', '\x00', '\x07', '\x5f', '\x69', '\x64',
+ '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', '\xf8',
+ '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x02', '\x6b', '\x65',
+ '\x79', '\x00', '\x25', '\x00', '\x00', '\x00', '\x62', '\x38',
+ '\x32', '\x63', '\x34', '\x62', '\x62', '\x31', '\x2d', '\x30',
+ '\x39', '\x66', '\x66', '\x2d', '\x36', '\x30', '\x39', '\x33',
+ '\x2d', '\x34', '\x64', '\x35', '\x38', '\x2d', '\x38', '\x33',
+ '\x32', '\x37', '\x62', '\x39', '\x34', '\x65', '\x31', '\x65',
+ '\x32', '\x30', '\x00', '\x07', '\x6d', '\x65', '\x74', '\x61',
+ '\x64', '\x61', '\x74', '\x61', '\x00', '\x5c', '\x11', '\x51',
+ '\x56', '\xc9', '\x75', '\x50', '\x47', '\xe3', '\x18', '\xbb',
+ '\xfd', '\x00',
+ },
+ count: 1,
+ },
+ },
+ expected: map[string][]byte{
+ "b82c4bb1-09ff-6093-4d58-8327b94e1e20": []byte{
+ 92, 17, 81, 86, 201, 117, 80, 71, 227, 24, 187, 253},
+ },
+ },
+ {
+ label: "UnSuccessfully Read of all entries",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "tag": "tagName",
+ },
+ mockColl: &mockCollection{
+ Err: pkgerrors.New("DB Error"),
+ },
+ expectedError: "DB Error",
+ },
+ {
+ label: "UnSuccessfull Readall, tag not found",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "tag": "tagName",
+ },
+ mockColl: &mockCollection{
+ mCursor: &mockCursor{
+ // Binary form of
+ // {
+ // "_id" : ObjectId("5c115156777ff85654248ae1"),
+ // "key" : "b82c4bb1-09ff-6093-4d58-8327b94e1e20",
+ // "metadata" : ObjectId("5c115156c9755047e318bbfd")
+ // }
+ bson: bson.Raw{
+ '\x5a', '\x00', '\x00', '\x00', '\x07', '\x5f', '\x69', '\x64',
+ '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f', '\xf8',
+ '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x02', '\x6b', '\x65',
+ '\x79', '\x00', '\x25', '\x00', '\x00', '\x00', '\x62', '\x38',
+ '\x32', '\x63', '\x34', '\x62', '\x62', '\x31', '\x2d', '\x30',
+ '\x39', '\x66', '\x66', '\x2d', '\x36', '\x30', '\x39', '\x33',
+ '\x2d', '\x34', '\x64', '\x35', '\x38', '\x2d', '\x38', '\x33',
+ '\x32', '\x37', '\x62', '\x39', '\x34', '\x65', '\x31', '\x65',
+ '\x32', '\x30', '\x00', '\x07', '\x6d', '\x65', '\x74', '\x61',
+ '\x64', '\x61', '\x74', '\x61', '\x00', '\x5c', '\x11', '\x51',
+ '\x56', '\xc9', '\x75', '\x50', '\x47', '\xe3', '\x18', '\xbb',
+ '\xfd', '\x00',
+ },
+ count: 1,
+ },
+ },
+ expectedError: "Did not find any objects with tag",
+ },
+ {
+ label: "Missing input fields",
+ input: map[string]interface{}{
+ "coll": "",
+ "tag": "",
+ },
+ expectedError: "Missing collection or tag name",
+ mockColl: &mockCollection{},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ m, _ := NewMongoStore("name", &mongo.Database{})
+ // Override the getCollection function with our mocked version
+ getCollection = func(coll string, m *MongoStore) MongoCollection {
+ return testCase.mockColl
+ }
+
+ decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) {
+ return testCase.mockColl.mCursor.DecodeBytes()
+ }
+
+ got, err := m.ReadAll(testCase.input["coll"].(string), testCase.input["tag"].(string))
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Readall method returned an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Readall method returned an error (%s)", err)
+ }
+ } else {
+ if reflect.DeepEqual(got, testCase.expected) == false {
+ t.Fatalf("Readall returned unexpected data: %v, expected: %v",
+ got, testCase.expected)
+ }
+ }
+ })
+ }
+}
diff --git a/src/k8splugin/db/store.go b/src/k8splugin/db/store.go
index c1a8b31f..a235597a 100644
--- a/src/k8splugin/db/store.go
+++ b/src/k8splugin/db/store.go
@@ -25,21 +25,38 @@ var DBconn Store
// Store is an interface for accessing a database
type Store interface {
+ // Returns nil if db health is good
HealthCheck() error
- Create(string, string) error
- Read(string) (string, error)
- // Update(string) (string, error)
- Delete(string) error
+ // Unmarshal implements any unmarshaling needed for the database
+ Unmarshal(inp []byte, out interface{}) error
- ReadAll(string) ([]string, error)
+ // Creates a new master table with key and links data with tag and
+ // creates a pointer to the newly added data in the master table
+ Create(table, key, tag string, data interface{}) error
+
+ // Reads data for a particular key with specific tag.
+ Read(table, key, tag string) ([]byte, error)
+
+ //TODO: Update(context.Context, string, interface{}) error
+
+ // Deletes a specific tag data for key.
+ // TODO: If tag is empty, it will delete all tags under key.
+ Delete(table, key, tag string) error
+
+ // Reads all master tables and data from the specified tag in table
+ ReadAll(table, tag string) (map[string][]byte, error)
}
// CreateDBClient creates the DB client
func CreateDBClient(dbType string) error {
var err error
switch dbType {
+ case "mongo":
+ // create a mongodb database with k8splugin as the name
+ DBconn, err = NewMongoStore("k8splugin", nil)
case "consul":
+ // create a consul kv store
DBconn, err = NewConsulStore(nil)
default:
return pkgerrors.New(dbType + "DB not supported")
diff --git a/src/k8splugin/db/store_test.go b/src/k8splugin/db/store_test.go
index 9bbe4a92..eed7065f 100644
--- a/src/k8splugin/db/store_test.go
+++ b/src/k8splugin/db/store_test.go
@@ -23,9 +23,9 @@ import (
func TestCreateDBClient(t *testing.T) {
t.Run("Successfully create DB client", func(t *testing.T) {
- expected := &ConsulStore{}
+ expected := &MongoStore{}
- err := CreateDBClient("consul")
+ err := CreateDBClient("mongo")
if err != nil {
t.Fatalf("CreateDBClient returned an error (%s)", err)
}
diff --git a/src/k8splugin/db/testing.go b/src/k8splugin/db/testing.go
index 672fcbfb..4b7e6078 100644
--- a/src/k8splugin/db/testing.go
+++ b/src/k8splugin/db/testing.go
@@ -16,7 +16,8 @@ limitations under the License.
package db
import (
- "github.com/hashicorp/consul/api"
+ "encoding/json"
+ pkgerrors "github.com/pkg/errors"
)
//Creating an embedded interface via anonymous variable
@@ -24,42 +25,45 @@ import (
//interface even if we are not implementing all the methods in it
type MockDB struct {
Store
- Items api.KVPairs
+ Items map[string][]byte
Err error
}
-func (m *MockDB) Create(key string, value string) error {
+func (m *MockDB) Create(table, key, tag string, data interface{}) error {
return m.Err
}
-func (m *MockDB) Read(key string) (string, error) {
+// MockDB uses simple JSON and not BSON
+func (m *MockDB) Unmarshal(inp []byte, out interface{}) error {
+ err := json.Unmarshal(inp, out)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Unmarshaling json")
+ }
+ return nil
+}
+
+func (m *MockDB) Read(table, key, tag string) ([]byte, error) {
if m.Err != nil {
- return "", m.Err
+ return nil, m.Err
}
- for _, kvpair := range m.Items {
- if kvpair.Key == key {
- return string(kvpair.Value), nil
+ for k, v := range m.Items {
+ if k == key {
+ return v, nil
}
}
- return "", nil
+ return nil, m.Err
}
-func (m *MockDB) Delete(key string) error {
+func (m *MockDB) Delete(table, key, tag string) error {
return m.Err
}
-func (m *MockDB) ReadAll(prefix string) ([]string, error) {
+func (m *MockDB) ReadAll(table, tag string) (map[string][]byte, error) {
if m.Err != nil {
- return []string{}, m.Err
- }
-
- var res []string
-
- for _, keypair := range m.Items {
- res = append(res, keypair.Key)
+ return nil, m.Err
}
- return res, nil
+ return m.Items, nil
}
diff --git a/src/k8splugin/go.mod b/src/k8splugin/go.mod
index d0b32af4..a4f86586 100644
--- a/src/k8splugin/go.mod
+++ b/src/k8splugin/go.mod
@@ -1,35 +1,45 @@
module k8splugin
require (
- github.com/ghodss/yaml v1.0.0
- github.com/gogo/protobuf v1.0.0
- github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
- github.com/golang/protobuf v1.1.0
- github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf
- github.com/googleapis/gnostic v0.2.0
- github.com/gorilla/context v1.1.1
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/ghodss/yaml v1.0.0 // indirect
+ github.com/go-stack/stack v1.8.0 // indirect
+ github.com/gogo/protobuf v1.0.0 // indirect
+ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
+ github.com/golang/protobuf v1.2.0 // indirect
+ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
+ github.com/google/go-cmp v0.2.0 // indirect
+ github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
+ github.com/googleapis/gnostic v0.2.0 // indirect
+ github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/handlers v1.3.0
github.com/gorilla/mux v1.6.2
- github.com/hashicorp/consul v1.2.2
- github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186
- github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90
+ github.com/hashicorp/consul v1.4.0
+ github.com/hashicorp/go-cleanhttp v0.5.0 // indirect
+ github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 // indirect
github.com/hashicorp/go-uuid v1.0.0
- github.com/hashicorp/serf v0.8.1
- github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c
- github.com/imdario/mergo v0.3.5
- github.com/json-iterator/go v0.0.0-20180315132816-ca39e5af3ece
- github.com/mitchellh/go-homedir v0.0.0-20180801233206-58046073cbff
- github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699
- github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
- github.com/modern-go/reflect2 v0.0.0-20180228065516-1df9eeb2bb81
+ github.com/hashicorp/serf v0.8.1 // indirect
+ github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c // indirect
+ github.com/imdario/mergo v0.3.5 // indirect
+ github.com/json-iterator/go v0.0.0-20180315132816-ca39e5af3ece // indirect
+ github.com/mitchellh/mapstructure v1.1.2 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v0.0.0-20180228065516-1df9eeb2bb81 // indirect
+ github.com/mongodb/mongo-go-driver v0.1.0
github.com/pkg/errors v0.8.0
- github.com/spf13/pflag v1.0.1
- golang.org/x/crypto v0.0.0-20180608092829-8ac0e0d97ce4
- golang.org/x/net v0.0.0-20180611182652-db08ff08e862
- golang.org/x/sys v0.0.0-20180611080425-bff228c7b664
- golang.org/x/text v0.3.0
- golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2
- gopkg.in/inf.v0 v0.9.1
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/spf13/pflag v1.0.1 // indirect
+ github.com/stretchr/testify v1.2.2 // indirect
+ github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51 // indirect
+ github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect
+ github.com/xdg/stringprep v1.0.0 // indirect
+ golang.org/x/crypto v0.0.0-20180608092829-8ac0e0d97ce4 // indirect
+ golang.org/x/net v0.0.0-20180724234803-3673e40ba225
+ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
+ golang.org/x/sys v0.0.0-20180611080425-bff228c7b664 // indirect
+ golang.org/x/text v0.3.0 // indirect
+ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect
+ gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.2.1
k8s.io/api v0.0.0-20180607235014-72d6e4405f81
k8s.io/apimachinery v0.0.0-20180515182440-31dade610c05
diff --git a/src/k8splugin/go.sum b/src/k8splugin/go.sum
index 6e46c11c..f742e1f3 100644
--- a/src/k8splugin/go.sum
+++ b/src/k8splugin/go.sum
@@ -1,24 +1,33 @@
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.0.0 h1:2jyBKDKU/8v3v2xVR2PtiWQviFUyiaGk2rpfyFT8rTM=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/protobuf v1.1.0 h1:0iH4Ffd/meGoXqF2lSAhZHt8X+cPgkfn/cb6Cce5Vpc=
-github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
+github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/handlers v1.3.0 h1:tsg9qP3mjt1h4Roxp+M1paRjrVBfPSOpBuVclh6YluI=
github.com/gorilla/handlers v1.3.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
-github.com/hashicorp/consul v1.2.2 h1:C5FurAZWLQ+XAjmL9g6rXbPlwxyyz8DvTL0WCAxTLAo=
-github.com/hashicorp/consul v1.2.2/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
-github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186 h1:URgjUo+bs1KwatoNbwG0uCO4dHN4r1jsp4a5AGgHRjo=
-github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/consul v1.4.0 h1:PQTW4xCuAExEiSbhrsFsikzbW5gVBoi74BjUvYFyKHw=
+github.com/hashicorp/consul v1.4.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
+github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
+github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 h1:9HVkPxOpo+yO93Ah4yrO67d/qh0fbLLWbKqhYjyHq9A=
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90/go.mod h1:o4zcYY1e0GEZI6eSEr+43QDYmuGglw1qSO6qdHUHCgg=
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
@@ -31,28 +40,41 @@ github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/json-iterator/go v0.0.0-20180315132816-ca39e5af3ece h1:3HJXp/18JmMk5sjBP3LDUBtWjczCvynxaeAF6b6kWp8=
github.com/json-iterator/go v0.0.0-20180315132816-ca39e5af3ece/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/mitchellh/go-homedir v0.0.0-20180801233206-58046073cbff h1:jM4Eo4qMmmcqePS3u6X2lcEELtVuXWkWJIS/pRI3oSk=
-github.com/mitchellh/go-homedir v0.0.0-20180801233206-58046073cbff/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 h1:KXZJFdun9knAVAR8tg/aHJEr5DgtcbqyvzacK+CDCaI=
-github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180228065516-1df9eeb2bb81 h1:ImOHKpmdLPXWX5KSYquUWXKaopEPuY7TPPUo18u9aOI=
github.com/modern-go/reflect2 v0.0.0-20180228065516-1df9eeb2bb81/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mongodb/mongo-go-driver v0.1.0 h1:LcpPFw0tNumIAakvNrkI9S9wdX0iOxvMLw/+hcAdHaU=
+github.com/mongodb/mongo-go-driver v0.1.0/go.mod h1:NK/HWDIIZkaYsnYa0hmtP443T5ELr0KDecmIioVuuyU=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51 h1:BP2bjP495BBPaBcS5rmqviTfrOkN5rO5ceKAMRZCRFc=
+github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
+github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0=
+github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
golang.org/x/crypto v0.0.0-20180608092829-8ac0e0d97ce4 h1:wviDUSmtheHRBfoY8B9U8ELl2USoXi2YFwdGdpIIkzI=
golang.org/x/crypto v0.0.0-20180608092829-8ac0e0d97ce4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/net v0.0.0-20180611182652-db08ff08e862 h1:JZi6BqOZ+iSgmLWe6llhGrNnEnK+YB/MRkStwnEfbqM=
-golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225 h1:kNX+jCowfMYzvlSvJu5pQWEmyWFrBXJ3PBy10xKMXK8=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180611080425-bff228c7b664 h1:GvcVmbE8Pa64iW3MTrVA9mxHx1HEjSSWV6zF1JSlFcg=
golang.org/x/sys v0.0.0-20180611080425-bff228c7b664/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
diff --git a/src/k8splugin/rb/archive.go b/src/k8splugin/rb/archive.go
new file mode 100644
index 00000000..8eb0fbed
--- /dev/null
+++ b/src/k8splugin/rb/archive.go
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 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 rb
+
+import (
+ "archive/tar"
+ "compress/gzip"
+ pkgerrors "github.com/pkg/errors"
+ "io"
+)
+
+func isTarGz(r io.Reader) error {
+ //Check if it is a valid gz
+ gzf, err := gzip.NewReader(r)
+ if err != nil {
+ return pkgerrors.Errorf("Invalid gz format %s", err.Error())
+ }
+
+ //Check if it is a valid tar file
+ //Unfortunately this can only be done by inspecting all the tar contents
+ tarR := tar.NewReader(gzf)
+ first := true
+
+ for true {
+ header, err := tarR.Next()
+
+ if err == io.EOF {
+ //Check if we have just a gzip file without a tar archive inside
+ if first {
+ return pkgerrors.New("Empty or non-existant Tar file found")
+ }
+ //End of archive
+ break
+ }
+
+ if err != nil {
+ return pkgerrors.Errorf("Error reading tar file %s", err.Error())
+ }
+
+ //Check if files are of type directory and regular file
+ if header.Typeflag != tar.TypeDir &&
+ header.Typeflag != tar.TypeReg {
+ return pkgerrors.Errorf("Unknown header in tar %s, %s",
+ header.Name, string(header.Typeflag))
+ }
+
+ first = false
+ }
+
+ return nil
+}
diff --git a/src/k8splugin/rb/archive_test.go b/src/k8splugin/rb/archive_test.go
new file mode 100644
index 00000000..a327dfd4
--- /dev/null
+++ b/src/k8splugin/rb/archive_test.go
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 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 rb
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestIsTarGz(t *testing.T) {
+
+ t.Run("Valid tar.gz", func(t *testing.T) {
+ content := []byte{
+ 0x1f, 0x8b, 0x08, 0x08, 0xb0, 0x6b, 0xf4, 0x5b,
+ 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74,
+ 0x61, 0x72, 0x00, 0xed, 0xce, 0x41, 0x0a, 0xc2,
+ 0x30, 0x10, 0x85, 0xe1, 0xac, 0x3d, 0x45, 0x4e,
+ 0x50, 0x12, 0xd2, 0xc4, 0xe3, 0x48, 0xa0, 0x01,
+ 0x4b, 0x52, 0x0b, 0xed, 0x88, 0x1e, 0xdf, 0x48,
+ 0x11, 0x5c, 0x08, 0xa5, 0x8b, 0x52, 0x84, 0xff,
+ 0xdb, 0xbc, 0x61, 0x66, 0x16, 0x4f, 0xd2, 0x2c,
+ 0x8d, 0x3c, 0x45, 0xed, 0xc8, 0x54, 0x21, 0xb4,
+ 0xef, 0xb4, 0x67, 0x6f, 0xbe, 0x73, 0x61, 0x9d,
+ 0xb2, 0xce, 0xd5, 0x55, 0xf0, 0xde, 0xd7, 0x3f,
+ 0xdb, 0xd6, 0x49, 0x69, 0xb3, 0x67, 0xa9, 0x8f,
+ 0xfb, 0x2c, 0x71, 0xd2, 0x5a, 0xc5, 0xee, 0x92,
+ 0x73, 0x8e, 0x43, 0x7f, 0x4b, 0x3f, 0xff, 0xd6,
+ 0xee, 0x7f, 0xea, 0x9a, 0x4a, 0x19, 0x1f, 0xe3,
+ 0x54, 0xba, 0xd3, 0xd1, 0x55, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1b, 0xbc, 0x00, 0xb5, 0xe8,
+ 0x4a, 0xf9, 0x00, 0x28, 0x00, 0x00,
+ }
+
+ err := isTarGz(bytes.NewBuffer(content))
+ if err != nil {
+ t.Errorf("Error reading valid Zip file %s", err.Error())
+ }
+ })
+
+ t.Run("Invalid tar.gz", func(t *testing.T) {
+ content := []byte{
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0xf2, 0x48, 0xcd,
+ }
+
+ err := isTarGz(bytes.NewBuffer(content))
+ if err == nil {
+ t.Errorf("Error should NOT be nil")
+ }
+ })
+}
diff --git a/src/k8splugin/rb/definition.go b/src/k8splugin/rb/definition.go
new file mode 100644
index 00000000..02ecc5ae
--- /dev/null
+++ b/src/k8splugin/rb/definition.go
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2018 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 rb
+
+import (
+ "bytes"
+ "encoding/base64"
+ "k8splugin/db"
+ "log"
+
+ uuid "github.com/hashicorp/go-uuid"
+ pkgerrors "github.com/pkg/errors"
+)
+
+// Definition contains the parameters needed for resource bundle (rb) definitions
+// It implements the interface for managing the definitions
+type Definition struct {
+ Name string `json:"name"`
+ Description string `json:"description"`
+ UUID string `json:"uuid,omitempty"`
+ ServiceType string `json:"service-type"`
+}
+
+// DefinitionManager is an interface exposes the resource bundle definition functionality
+type DefinitionManager interface {
+ Create(def Definition) (Definition, error)
+ List() ([]Definition, error)
+ Get(resID string) (Definition, error)
+ Delete(resID string) error
+ Upload(resID string, inp []byte) error
+}
+
+// DefinitionClient implements the DefinitionManager
+// It will also be used to maintain some localized state
+type DefinitionClient struct {
+ storeName string
+ tagMeta, tagContent string
+}
+
+// NewDefinitionClient returns an instance of the DefinitionClient
+// which implements the DefinitionManager
+// Uses rbdef collection in underlying db
+func NewDefinitionClient() *DefinitionClient {
+ return &DefinitionClient{
+ storeName: "rbdef",
+ tagMeta: "metadata",
+ tagContent: "content",
+ }
+}
+
+// Create an entry for the resource in the database
+func (v *DefinitionClient) Create(def Definition) (Definition, error) {
+ // If UUID is empty, we will generate one
+ if def.UUID == "" {
+ def.UUID, _ = uuid.GenerateUUID()
+ }
+ key := def.UUID
+
+ err := db.DBconn.Create(v.storeName, key, v.tagMeta, def)
+ if err != nil {
+ return Definition{}, pkgerrors.Wrap(err, "Creating DB Entry")
+ }
+
+ return def, nil
+}
+
+// List all resource entries in the database
+func (v *DefinitionClient) List() ([]Definition, error) {
+ res, err := db.DBconn.ReadAll(v.storeName, v.tagMeta)
+ if err != nil || len(res) == 0 {
+ return []Definition{}, pkgerrors.Wrap(err, "Listing Resource Bundle Definitions")
+ }
+
+ var results []Definition
+ for key, value := range res {
+ if len(value) > 0 {
+ def := Definition{}
+ err = db.DBconn.Unmarshal(value, &def)
+ if err != nil {
+ log.Printf("Error Unmarshaling value for: %s", key)
+ continue
+ }
+ results = append(results, def)
+ }
+ }
+
+ return results, nil
+}
+
+// Get returns the Resource Bundle Definition for corresponding ID
+func (v *DefinitionClient) Get(id string) (Definition, error) {
+ value, err := db.DBconn.Read(v.storeName, id, v.tagMeta)
+ if err != nil {
+ return Definition{}, pkgerrors.Wrap(err, "Get Resource Bundle definition")
+ }
+
+ if value != nil {
+ def := Definition{}
+ err = db.DBconn.Unmarshal(value, &def)
+ if err != nil {
+ return Definition{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+ }
+ return def, nil
+ }
+
+ return Definition{}, pkgerrors.New("Error getting Resource Bundle Definition")
+}
+
+// Delete the Resource Bundle definition from database
+func (v *DefinitionClient) Delete(id string) error {
+ err := db.DBconn.Delete(v.storeName, id, v.tagMeta)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Delete Resource Bundle Definitions")
+ }
+
+ return nil
+}
+
+// Upload the contents of resource bundle into database
+func (v *DefinitionClient) Upload(id string, inp []byte) error {
+
+ //ignore the returned data here
+ _, err := v.Get(id)
+ if err != nil {
+ return pkgerrors.Errorf("Invalid ID provided: %s", err.Error())
+ }
+
+ err = isTarGz(bytes.NewBuffer(inp))
+ if err != nil {
+ return pkgerrors.Errorf("Error in file format: %s", err.Error())
+ }
+
+ //Encode given byte stream to text for storage
+ encodedStr := base64.StdEncoding.EncodeToString(inp)
+ err = db.DBconn.Create(v.storeName, id, encodedStr, v.tagContent)
+ if err != nil {
+ return pkgerrors.Errorf("Error uploading data to db: %s", err.Error())
+ }
+
+ return nil
+}
diff --git a/src/k8splugin/rb/definition_test.go b/src/k8splugin/rb/definition_test.go
new file mode 100644
index 00000000..1e488678
--- /dev/null
+++ b/src/k8splugin/rb/definition_test.go
@@ -0,0 +1,396 @@
+// +build unit
+
+/*
+ * Copyright 2018 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 rb
+
+import (
+ "k8splugin/db"
+ "reflect"
+ "strings"
+ "testing"
+
+ pkgerrors "github.com/pkg/errors"
+)
+
+func TestCreate(t *testing.T) {
+ testCases := []struct {
+ label string
+ inp Definition
+ expectedError string
+ mockdb *db.MockDB
+ expected Definition
+ }{
+ {
+ label: "Create Resource Bundle Definition",
+ inp: Definition{
+ UUID: "123e4567-e89b-12d3-a456-426655440000",
+ Name: "testresourcebundle",
+ Description: "testresourcebundle",
+ ServiceType: "firewall",
+ },
+ expected: Definition{
+ UUID: "123e4567-e89b-12d3-a456-426655440000",
+ Name: "testresourcebundle",
+ Description: "testresourcebundle",
+ ServiceType: "firewall",
+ },
+ expectedError: "",
+ mockdb: &db.MockDB{},
+ },
+ {
+ label: "Failed Create Resource Bundle Definition",
+ expectedError: "Error Creating Definition",
+ mockdb: &db.MockDB{
+ Err: pkgerrors.New("Error Creating Definition"),
+ },
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ db.DBconn = testCase.mockdb
+ impl := NewDefinitionClient()
+ got, err := impl.Create(testCase.inp)
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Create returned an unexpected error %s", err)
+ }
+ if strings.Contains(err.Error(), testCase.expectedError) == false {
+ t.Fatalf("Create returned an unexpected error %s", err)
+ }
+ } else {
+ if reflect.DeepEqual(testCase.expected, got) == false {
+ t.Errorf("Create Resource Bundle returned unexpected body: got %v;"+
+ " expected %v", got, testCase.expected)
+ }
+ }
+ })
+ }
+}
+
+func TestList(t *testing.T) {
+
+ testCases := []struct {
+ label string
+ expectedError string
+ mockdb *db.MockDB
+ expected []Definition
+ }{
+ {
+ label: "List Resource Bundle Definition",
+ expected: []Definition{
+ {
+ UUID: "123e4567-e89b-12d3-a456-426655440000",
+ Name: "testresourcebundle",
+ Description: "testresourcebundle",
+ ServiceType: "firewall",
+ },
+ {
+ UUID: "123e4567-e89b-12d3-a456-426655441111",
+ Name: "testresourcebundle2",
+ Description: "testresourcebundle2",
+ ServiceType: "dns",
+ },
+ },
+ expectedError: "",
+ mockdb: &db.MockDB{
+ Items: map[string][]byte{
+ "123e4567-e89b-12d3-a456-426655440000": []byte(
+ "{\"name\":\"testresourcebundle\"," +
+ "\"description\":\"testresourcebundle\"," +
+ "\"uuid\":\"123e4567-e89b-12d3-a456-426655440000\"," +
+ "\"service-type\":\"firewall\"}"),
+ "123e4567-e89b-12d3-a456-426655441111": []byte(
+ "{\"name\":\"testresourcebundle2\"," +
+ "\"description\":\"testresourcebundle2\"," +
+ "\"uuid\":\"123e4567-e89b-12d3-a456-426655441111\"," +
+ "\"service-type\":\"dns\"}"),
+ },
+ },
+ },
+ {
+ label: "List Error",
+ expectedError: "DB Error",
+ mockdb: &db.MockDB{
+ Err: pkgerrors.New("DB Error"),
+ },
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ db.DBconn = testCase.mockdb
+ impl := NewDefinitionClient()
+ got, err := impl.List()
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("List returned an unexpected error %s", err)
+ }
+ if strings.Contains(err.Error(), testCase.expectedError) == false {
+ t.Fatalf("List returned an unexpected error %s", err)
+ }
+ } else {
+ if reflect.DeepEqual(testCase.expected, got) == false {
+ t.Errorf("List Resource Bundle returned unexpected body: got %v;"+
+ " expected %v", got, testCase.expected)
+ }
+ }
+ })
+ }
+}
+
+func TestGet(t *testing.T) {
+
+ testCases := []struct {
+ label string
+ expectedError string
+ mockdb *db.MockDB
+ inp string
+ expected Definition
+ }{
+ {
+ label: "Get Resource Bundle Definition",
+ inp: "123e4567-e89b-12d3-a456-426655440000",
+ expected: Definition{
+ UUID: "123e4567-e89b-12d3-a456-426655440000",
+ Name: "testresourcebundle",
+ Description: "testresourcebundle",
+ ServiceType: "firewall",
+ },
+ expectedError: "",
+ mockdb: &db.MockDB{
+ Items: map[string][]byte{
+ "123e4567-e89b-12d3-a456-426655440000": []byte(
+ "{\"name\":\"testresourcebundle\"," +
+ "\"description\":\"testresourcebundle\"," +
+ "\"uuid\":\"123e4567-e89b-12d3-a456-426655440000\"," +
+ "\"service-type\":\"firewall\"}"),
+ },
+ },
+ },
+ {
+ label: "Get Error",
+ expectedError: "DB Error",
+ mockdb: &db.MockDB{
+ Err: pkgerrors.New("DB Error"),
+ },
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ db.DBconn = testCase.mockdb
+ impl := NewDefinitionClient()
+ got, err := impl.Get(testCase.inp)
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Get returned an unexpected error %s", err)
+ }
+ if strings.Contains(err.Error(), testCase.expectedError) == false {
+ t.Fatalf("Get returned an unexpected error %s", err)
+ }
+ } else {
+ if reflect.DeepEqual(testCase.expected, got) == false {
+ t.Errorf("Get Resource Bundle returned unexpected body: got %v;"+
+ " expected %v", got, testCase.expected)
+ }
+ }
+ })
+ }
+}
+
+func TestDelete(t *testing.T) {
+
+ testCases := []struct {
+ label string
+ inp string
+ expectedError string
+ mockdb *db.MockDB
+ }{
+ {
+ label: "Delete Resource Bundle Definition",
+ inp: "123e4567-e89b-12d3-a456-426655440000",
+ mockdb: &db.MockDB{},
+ },
+ {
+ label: "Delete Error",
+ expectedError: "DB Error",
+ mockdb: &db.MockDB{
+ Err: pkgerrors.New("DB Error"),
+ },
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ db.DBconn = testCase.mockdb
+ impl := NewDefinitionClient()
+ err := impl.Delete(testCase.inp)
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Delete returned an unexpected error %s", err)
+ }
+ if strings.Contains(err.Error(), testCase.expectedError) == false {
+ t.Fatalf("Delete returned an unexpected error %s", err)
+ }
+ }
+ })
+ }
+}
+
+func TestUpload(t *testing.T) {
+ testCases := []struct {
+ label string
+ inp string
+ content []byte
+ expectedError string
+ mockdb *db.MockDB
+ }{
+ {
+ label: "Upload Resource Bundle Definition",
+ inp: "123e4567-e89b-12d3-a456-426655440000",
+ content: []byte{
+ 0x1f, 0x8b, 0x08, 0x08, 0xb0, 0x6b, 0xf4, 0x5b,
+ 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74,
+ 0x61, 0x72, 0x00, 0xed, 0xce, 0x41, 0x0a, 0xc2,
+ 0x30, 0x10, 0x85, 0xe1, 0xac, 0x3d, 0x45, 0x4e,
+ 0x50, 0x12, 0xd2, 0xc4, 0xe3, 0x48, 0xa0, 0x01,
+ 0x4b, 0x52, 0x0b, 0xed, 0x88, 0x1e, 0xdf, 0x48,
+ 0x11, 0x5c, 0x08, 0xa5, 0x8b, 0x52, 0x84, 0xff,
+ 0xdb, 0xbc, 0x61, 0x66, 0x16, 0x4f, 0xd2, 0x2c,
+ 0x8d, 0x3c, 0x45, 0xed, 0xc8, 0x54, 0x21, 0xb4,
+ 0xef, 0xb4, 0x67, 0x6f, 0xbe, 0x73, 0x61, 0x9d,
+ 0xb2, 0xce, 0xd5, 0x55, 0xf0, 0xde, 0xd7, 0x3f,
+ 0xdb, 0xd6, 0x49, 0x69, 0xb3, 0x67, 0xa9, 0x8f,
+ 0xfb, 0x2c, 0x71, 0xd2, 0x5a, 0xc5, 0xee, 0x92,
+ 0x73, 0x8e, 0x43, 0x7f, 0x4b, 0x3f, 0xff, 0xd6,
+ 0xee, 0x7f, 0xea, 0x9a, 0x4a, 0x19, 0x1f, 0xe3,
+ 0x54, 0xba, 0xd3, 0xd1, 0x55, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1b, 0xbc, 0x00, 0xb5, 0xe8,
+ 0x4a, 0xf9, 0x00, 0x28, 0x00, 0x00,
+ },
+ mockdb: &db.MockDB{
+ Items: map[string][]byte{
+ "123e4567-e89b-12d3-a456-426655440000": []byte(
+ "{\"name\":\"testresourcebundle\"," +
+ "\"description\":\"testresourcebundle\"," +
+ "\"uuid\":\"123e4567-e89b-12d3-a456-426655440000\"," +
+ "\"service-type\":\"firewall\"}"),
+ },
+ },
+ },
+ {
+ label: "Upload with an Invalid Resource Bundle Definition",
+ inp: "123e4567-e89b-12d3-a456-426655440000",
+ expectedError: "Invalid ID provided",
+ content: []byte{
+ 0x1f, 0x8b, 0x08, 0x08, 0xb0, 0x6b, 0xf4, 0x5b,
+ 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74,
+ 0x61, 0x72, 0x00, 0xed, 0xce, 0x41, 0x0a, 0xc2,
+ 0x30, 0x10, 0x85, 0xe1, 0xac, 0x3d, 0x45, 0x4e,
+ 0x50, 0x12, 0xd2, 0xc4, 0xe3, 0x48, 0xa0, 0x01,
+ 0x4b, 0x52, 0x0b, 0xed, 0x88, 0x1e, 0xdf, 0x48,
+ 0x11, 0x5c, 0x08, 0xa5, 0x8b, 0x52, 0x84, 0xff,
+ 0xdb, 0xbc, 0x61, 0x66, 0x16, 0x4f, 0xd2, 0x2c,
+ 0x8d, 0x3c, 0x45, 0xed, 0xc8, 0x54, 0x21, 0xb4,
+ 0xef, 0xb4, 0x67, 0x6f, 0xbe, 0x73, 0x61, 0x9d,
+ 0xb2, 0xce, 0xd5, 0x55, 0xf0, 0xde, 0xd7, 0x3f,
+ 0xdb, 0xd6, 0x49, 0x69, 0xb3, 0x67, 0xa9, 0x8f,
+ 0xfb, 0x2c, 0x71, 0xd2, 0x5a, 0xc5, 0xee, 0x92,
+ 0x73, 0x8e, 0x43, 0x7f, 0x4b, 0x3f, 0xff, 0xd6,
+ 0xee, 0x7f, 0xea, 0x9a, 0x4a, 0x19, 0x1f, 0xe3,
+ 0x54, 0xba, 0xd3, 0xd1, 0x55, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1b, 0xbc, 0x00, 0xb5, 0xe8,
+ 0x4a, 0xf9, 0x00, 0x28, 0x00, 0x00,
+ },
+ mockdb: &db.MockDB{
+ Items: map[string][]byte{
+ "123e4567-e89b-12d3-a456-426655441111": []byte(
+ "{\"name\":\"testresourcebundle\"," +
+ "\"description\":\"testresourcebundle\"," +
+ "\"uuid\":\"123e4567-e89b-12d3-a456-426655440000\"," +
+ "\"service-type\":\"firewall\"}"),
+ },
+ },
+ },
+ {
+ label: "Invalid File Format Error",
+ inp: "123e4567-e89b-12d3-a456-426655440000",
+ expectedError: "Error in file format",
+ content: []byte{
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0xf2, 0x48, 0xcd,
+ },
+ mockdb: &db.MockDB{
+ Items: map[string][]byte{
+ "123e4567-e89b-12d3-a456-426655440000": []byte(
+ "{\"name\":\"testresourcebundle\"," +
+ "\"description\":\"testresourcebundle\"," +
+ "\"uuid\":\"123e4567-e89b-12d3-a456-426655440000\"," +
+ "\"service-type\":\"firewall\"}"),
+ },
+ },
+ },
+ {
+ label: "Upload Error",
+ expectedError: "DB Error",
+ content: []byte{
+ 0x1f, 0x8b, 0x08, 0x08, 0xb0, 0x6b, 0xf4, 0x5b,
+ 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74,
+ 0x61, 0x72, 0x00, 0xed, 0xce, 0x41, 0x0a, 0xc2,
+ 0x30, 0x10, 0x85, 0xe1, 0xac, 0x3d, 0x45, 0x4e,
+ 0x50, 0x12, 0xd2, 0xc4, 0xe3, 0x48, 0xa0, 0x01,
+ 0x4b, 0x52, 0x0b, 0xed, 0x88, 0x1e, 0xdf, 0x48,
+ 0x11, 0x5c, 0x08, 0xa5, 0x8b, 0x52, 0x84, 0xff,
+ 0xdb, 0xbc, 0x61, 0x66, 0x16, 0x4f, 0xd2, 0x2c,
+ 0x8d, 0x3c, 0x45, 0xed, 0xc8, 0x54, 0x21, 0xb4,
+ 0xef, 0xb4, 0x67, 0x6f, 0xbe, 0x73, 0x61, 0x9d,
+ 0xb2, 0xce, 0xd5, 0x55, 0xf0, 0xde, 0xd7, 0x3f,
+ 0xdb, 0xd6, 0x49, 0x69, 0xb3, 0x67, 0xa9, 0x8f,
+ 0xfb, 0x2c, 0x71, 0xd2, 0x5a, 0xc5, 0xee, 0x92,
+ 0x73, 0x8e, 0x43, 0x7f, 0x4b, 0x3f, 0xff, 0xd6,
+ 0xee, 0x7f, 0xea, 0x9a, 0x4a, 0x19, 0x1f, 0xe3,
+ 0x54, 0xba, 0xd3, 0xd1, 0x55, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1b, 0xbc, 0x00, 0xb5, 0xe8,
+ 0x4a, 0xf9, 0x00, 0x28, 0x00, 0x00,
+ },
+ mockdb: &db.MockDB{
+ Err: pkgerrors.New("DB Error"),
+ },
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ db.DBconn = testCase.mockdb
+ impl := NewDefinitionClient()
+ err := impl.Upload(testCase.inp, testCase.content)
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Errorf("Upload returned an unexpected error %s", err)
+ }
+ if strings.Contains(err.Error(), testCase.expectedError) == false {
+ t.Errorf("Upload returned an unexpected error %s", err)
+ }
+ }
+ })
+ }
+}
diff --git a/src/k8splugin/vnfd/vnfd.go b/src/k8splugin/vnfd/vnfd.go
deleted file mode 100644
index 0fb81dbd..00000000
--- a/src/k8splugin/vnfd/vnfd.go
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2018 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 vnfd
-
-import (
- "k8splugin/db"
- "log"
-
- uuid "github.com/hashicorp/go-uuid"
- pkgerrors "github.com/pkg/errors"
-)
-
-// VNFDefinition contains the parameters needed for VNF Definitions
-// It implements the interface for managing the definitions
-type VNFDefinition struct {
- Name string `json:"name"`
- Description string `json:"description"`
- UUID string `json:"uuid,omitempty"`
- ServiceType string `json:"service-type"`
-}
-
-// VNFDefinitionInterface is an interface exposes the VNFDefinition functionality
-type VNFDefinitionInterface interface {
- Create(vnfd VNFDefinition) (VNFDefinition, error)
- List() ([]VNFDefinition, error)
- Get(vnfID string) (VNFDefinition, error)
- Delete(vnfID string) error
-}
-
-// VNFDefinitionClient implements the VNFDefinitionInterface
-// It will also be used to maintain some localized state
-type VNFDefinitionClient struct {
- keyPrefix string
-}
-
-// GetVNFDClient Returns an instance of the VNFDefinitionClient
-// which implements the VNFDefinitionInterface interface
-func GetVNFDClient() *VNFDefinitionClient {
- return &VNFDefinitionClient{
- keyPrefix: "vnfd/"}
-}
-
-// Create creates an entry for the VNF in the database
-func (v *VNFDefinitionClient) Create(vnfd VNFDefinition) (VNFDefinition, error) {
- // If UUID is empty, we will generate one
- if vnfd.UUID == "" {
- vnfd.UUID, _ = uuid.GenerateUUID()
- }
- key := v.keyPrefix + vnfd.UUID
-
- serData, err := db.Serialize(v)
- if err != nil {
- return VNFDefinition{}, pkgerrors.Wrap(err, "Serialize VNF Definition")
- }
-
- err = db.DBconn.Create(key, serData)
- if err != nil {
- return VNFDefinition{}, pkgerrors.Wrap(err, "Creating DB Entry")
- }
-
- return vnfd, nil
-}
-
-// List lists all vnf entries in the database
-func (v *VNFDefinitionClient) List() ([]VNFDefinition, error) {
- strArray, err := db.DBconn.ReadAll(v.keyPrefix)
- if err != nil {
- return []VNFDefinition{}, pkgerrors.Wrap(err, "Listing VNF Definitions")
- }
-
- var retData []VNFDefinition
-
- for _, key := range strArray {
- value, err := db.DBconn.Read(key)
- if err != nil {
- log.Printf("Error Reading Key: %s", key)
- continue
- }
- if value != "" {
- vnfd := VNFDefinition{}
- err = db.DeSerialize(value, &vnfd)
- if err != nil {
- log.Printf("Error Deserializing Value: %s", value)
- continue
- }
- retData = append(retData, vnfd)
- }
- }
-
- return retData, nil
-}
-
-// Get returns the VNF Definition for corresponding ID
-func (v *VNFDefinitionClient) Get(vnfID string) (VNFDefinition, error) {
- value, err := db.DBconn.Read(v.keyPrefix + vnfID)
- if err != nil {
- return VNFDefinition{}, pkgerrors.Wrap(err, "Get VNF Definitions")
- }
-
- if value != "" {
- vnfd := VNFDefinition{}
- err = db.DeSerialize(value, &vnfd)
- if err != nil {
- return VNFDefinition{}, pkgerrors.Wrap(err, "Deserializing Value")
- }
- return vnfd, nil
- }
-
- return VNFDefinition{}, pkgerrors.New("Error getting VNF Definition")
-}
-
-// Delete deletes the VNF Definition from database
-func (v *VNFDefinitionClient) Delete(vnfID string) error {
- err := db.DBconn.Delete(v.keyPrefix + vnfID)
- if err != nil {
- return pkgerrors.Wrap(err, "Delete VNF Definitions")
- }
-
- return nil
-}
diff --git a/src/k8splugin/vnfd/vnfd_test.go b/src/k8splugin/vnfd/vnfd_test.go
deleted file mode 100644
index 3230d3ef..00000000
--- a/src/k8splugin/vnfd/vnfd_test.go
+++ /dev/null
@@ -1,262 +0,0 @@
-// +build unit
-
-/*
- * Copyright 2018 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 vnfd
-
-import (
- "k8splugin/db"
- "reflect"
- "strings"
- "testing"
-
- "github.com/hashicorp/consul/api"
- pkgerrors "github.com/pkg/errors"
-)
-
-func TestCreate(t *testing.T) {
- testCases := []struct {
- label string
- inp VNFDefinition
- expectedError string
- mockdb *db.MockDB
- expected VNFDefinition
- }{
- {
- label: "Create VNF Definition",
- inp: VNFDefinition{
- UUID: "123e4567-e89b-12d3-a456-426655440000",
- Name: "testvnf",
- Description: "testvnf",
- ServiceType: "firewall",
- },
- expected: VNFDefinition{
- UUID: "123e4567-e89b-12d3-a456-426655440000",
- Name: "testvnf",
- Description: "testvnf",
- ServiceType: "firewall",
- },
- expectedError: "",
- mockdb: &db.MockDB{},
- },
- {
- label: "Failed Create VNF Definition",
- expectedError: "Error Creating Definition",
- mockdb: &db.MockDB{
- Err: pkgerrors.New("Error Creating Definition"),
- },
- },
- }
-
- for _, testCase := range testCases {
- t.Run(testCase.label, func(t *testing.T) {
- db.DBconn = testCase.mockdb
- vimpl := GetVNFDClient()
- got, err := vimpl.Create(testCase.inp)
- if err != nil {
- if testCase.expectedError == "" {
- t.Fatalf("Create returned an unexpected error %s", err)
- }
- if strings.Contains(err.Error(), testCase.expectedError) == false {
- t.Fatalf("Create returned an unexpected error %s", err)
- }
- } else {
- if reflect.DeepEqual(testCase.expected, got) == false {
- t.Errorf("Create VNF returned unexpected body: got %v;"+
- " expected %v", got, testCase.expected)
- }
- }
- })
- }
-}
-
-func TestList(t *testing.T) {
-
- testCases := []struct {
- label string
- expectedError string
- mockdb *db.MockDB
- expected []VNFDefinition
- }{
- {
- label: "List VNF Definition",
- expected: []VNFDefinition{
- {
- UUID: "123e4567-e89b-12d3-a456-426655440000",
- Name: "testvnf",
- Description: "testvnf",
- ServiceType: "firewall",
- },
- {
- UUID: "123e4567-e89b-12d3-a456-426655441111",
- Name: "testvnf2",
- Description: "testvnf2",
- ServiceType: "dns",
- },
- },
- expectedError: "",
- mockdb: &db.MockDB{
- Items: api.KVPairs{
- &api.KVPair{
- Key: "vnfd/123e4567-e89b-12d3-a456-426655440000",
- Value: []byte("{\"name\":\"testvnf\"," +
- "\"description\":\"testvnf\"," +
- "\"uuid\":\"123e4567-e89b-12d3-a456-426655440000\"," +
- "\"service-type\":\"firewall\"}"),
- },
- &api.KVPair{
- Key: "vnfd/123e4567-e89b-12d3-a456-426655441111",
- Value: []byte("{\"name\":\"testvnf2\"," +
- "\"description\":\"testvnf2\"," +
- "\"uuid\":\"123e4567-e89b-12d3-a456-426655441111\"," +
- "\"service-type\":\"dns\"}"),
- },
- },
- },
- },
- {
- label: "List Error",
- expectedError: "DB Error",
- mockdb: &db.MockDB{
- Err: pkgerrors.New("DB Error"),
- },
- },
- }
-
- for _, testCase := range testCases {
- t.Run(testCase.label, func(t *testing.T) {
- db.DBconn = testCase.mockdb
- vimpl := GetVNFDClient()
- got, err := vimpl.List()
- if err != nil {
- if testCase.expectedError == "" {
- t.Fatalf("List returned an unexpected error %s", err)
- }
- if strings.Contains(err.Error(), testCase.expectedError) == false {
- t.Fatalf("List returned an unexpected error %s", err)
- }
- } else {
- if reflect.DeepEqual(testCase.expected, got) == false {
- t.Errorf("List VNF returned unexpected body: got %v;"+
- " expected %v", got, testCase.expected)
- }
- }
- })
- }
-}
-
-func TestGet(t *testing.T) {
-
- testCases := []struct {
- label string
- expectedError string
- mockdb *db.MockDB
- inp string
- expected VNFDefinition
- }{
- {
- label: "Get VNF Definition",
- inp: "123e4567-e89b-12d3-a456-426655440000",
- expected: VNFDefinition{
- UUID: "123e4567-e89b-12d3-a456-426655440000",
- Name: "testvnf",
- Description: "testvnf",
- ServiceType: "firewall",
- },
- expectedError: "",
- mockdb: &db.MockDB{
- Items: api.KVPairs{
- &api.KVPair{
- Key: "vnfd/123e4567-e89b-12d3-a456-426655440000",
- Value: []byte("{\"name\":\"testvnf\"," +
- "\"description\":\"testvnf\"," +
- "\"uuid\":\"123e4567-e89b-12d3-a456-426655440000\"," +
- "\"service-type\":\"firewall\"}"),
- },
- },
- },
- },
- {
- label: "Get Error",
- expectedError: "DB Error",
- mockdb: &db.MockDB{
- Err: pkgerrors.New("DB Error"),
- },
- },
- }
-
- for _, testCase := range testCases {
- t.Run(testCase.label, func(t *testing.T) {
- db.DBconn = testCase.mockdb
- vimpl := GetVNFDClient()
- got, err := vimpl.Get(testCase.inp)
- if err != nil {
- if testCase.expectedError == "" {
- t.Fatalf("Get returned an unexpected error %s", err)
- }
- if strings.Contains(err.Error(), testCase.expectedError) == false {
- t.Fatalf("Get returned an unexpected error %s", err)
- }
- } else {
- if reflect.DeepEqual(testCase.expected, got) == false {
- t.Errorf("Get VNF returned unexpected body: got %v;"+
- " expected %v", got, testCase.expected)
- }
- }
- })
- }
-}
-
-func TestDelete(t *testing.T) {
-
- testCases := []struct {
- label string
- inp string
- expectedError string
- mockdb *db.MockDB
- expected []VNFDefinition
- }{
- {
- label: "Delete VNF Definition",
- inp: "123e4567-e89b-12d3-a456-426655440000",
- mockdb: &db.MockDB{},
- },
- {
- label: "Delete Error",
- expectedError: "DB Error",
- mockdb: &db.MockDB{
- Err: pkgerrors.New("DB Error"),
- },
- },
- }
-
- for _, testCase := range testCases {
- t.Run(testCase.label, func(t *testing.T) {
- db.DBconn = testCase.mockdb
- vimpl := GetVNFDClient()
- err := vimpl.Delete(testCase.inp)
- if err != nil {
- if testCase.expectedError == "" {
- t.Fatalf("Delete returned an unexpected error %s", err)
- }
- if strings.Contains(err.Error(), testCase.expectedError) == false {
- t.Fatalf("Delete returned an unexpected error %s", err)
- }
- }
- })
- }
-}
diff --git a/tox.ini b/tox.ini
index b3a24218..5cebea87 100644
--- a/tox.ini
+++ b/tox.ini
@@ -24,6 +24,7 @@ deps =
whitelist_externals = bash
commands = bash -c "find {toxinidir} -not -path {toxinidir}/.tox/\* \
-not -path {toxinidir}/pkg/dep/\* \
+ -not -path {toxinidir}/pkg/mod/\* \
-not -path {toxinidir}/src/k8splugin/vendor/\* \
-not -path {toxinidir}/src/github.com/\* \
-name \*.sh -type f \
diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile
index 735e750e..3314fe94 100644
--- a/vagrant/Vagrantfile
+++ b/vagrant/Vagrantfile
@@ -23,7 +23,7 @@ nodes = YAML.load_file(pdf)
# Inventory file creation
File.open(File.dirname(__FILE__) + "/inventory/hosts.ini", "w") do |inventory_file|
- inventory_file.puts("[all:vars]\nansible_connection=ssh\nansible_ssh_user=vagrant\nansible_ssh_pass=vagrant\n\n[all]")
+ inventory_file.puts("[all]")
nodes.each do |node|
inventory_file.puts("#{node['name']}\tansible_ssh_host=#{node['ip']} ansible_ssh_port=22")
end
@@ -59,6 +59,7 @@ end
Vagrant.configure("2") do |config|
config.vm.box = box[provider][:name]
config.vm.box_version = box[provider][:version]
+ config.ssh.insert_key = false
if ENV['http_proxy'] != nil and ENV['https_proxy'] != nil
if Vagrant.has_plugin?('vagrant-proxyconf')
@@ -114,10 +115,16 @@ Vagrant.configure("2") do |config|
config.vm.define :installer, primary: true, autostart: false do |installer|
installer.vm.hostname = "multicloud"
installer.vm.network :private_network, :ip => "10.10.10.2", :type => :static
- installer.vm.synced_folder '../', '/root/go/src/k8-plugin-multicloud/', type: sync_type
- installer.vm.provision 'shell' do |sh|
- sh.path = "installer.sh"
- sh.args = ['-p', '-v', '-w', '/root/go/src/k8-plugin-multicloud/vagrant']
+ installer.vm.synced_folder '../', '/home/vagrant/multicloud-k8s/', type: sync_type
+ installer.vm.provision 'shell', privileged: false do |sh|
+ sh.env = {'KRD_PLUGIN_ENABLED': 'true'}
+ sh.inline = <<-SHELL
+ cp /vagrant/insecure_keys/key.pub /home/vagrant/.ssh/id_rsa.pub
+ cp /vagrant/insecure_keys/key /home/vagrant/.ssh/id_rsa
+ chown vagrant /home/vagrant/.ssh/id_rsa
+ chmod 400 /home/vagrant/.ssh/id_rsa
+ cd /home/vagrant/multicloud-k8s/vagrant/ && ./installer.sh | tee krd_installer.log
+ SHELL
end
end
end
diff --git a/vagrant/aio.sh b/vagrant/aio.sh
new file mode 100755
index 00000000..413e4672
--- /dev/null
+++ b/vagrant/aio.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright (c) 2018
+# 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
+##############################################################################
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+if [[ $(whoami) != 'root' ]];then
+ echo "This bash script must be executed as root user"
+ exit 1
+fi
+
+echo "Cloning and configuring KRD project..."
+git clone https://git.onap.org/multicloud/k8s/
+cd k8s/vagrant/
+cat <<EOL > inventory/hosts.ini
+[all]
+localhost
+
+[kube-master]
+localhost
+
+[kube-node]
+localhost
+
+[etcd]
+localhost
+
+[ovn-central]
+localhost
+
+[ovn-controller]
+localhost
+
+[virtlet]
+localhost
+
+[k8s-cluster:children]
+kube-node
+kube-master
+EOL
+sed -i '/andrewrothstein.kubectl/d' playbooks/configure-*.yml
+echo -e "\n\n\n" | ssh-keygen -t rsa -N ""
+cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
+chmod og-wx ~/.ssh/authorized_keys
+
+echo "Enabling nested-virtualization"
+./node.sh
+
+echo "Deploying KRD project"
+./installer.sh | tee krd_installer.log
diff --git a/vagrant/galaxy-requirements.yml b/vagrant/galaxy-requirements.yml
index 4b252964..55e105a6 100644
--- a/vagrant/galaxy-requirements.yml
+++ b/vagrant/galaxy-requirements.yml
@@ -10,8 +10,8 @@
- src: andrewrothstein.go
version: v2.1.10
- src: andrewrothstein.kubectl
- version: v1.1.12
+ version: v1.1.16
- src: andrewrothstein.kubernetes-helm
version: v1.2.9
- src: geerlingguy.docker
- version: 2.5.1
+ version: 2.5.2
diff --git a/vagrant/insecure_keys/key b/vagrant/insecure_keys/key
new file mode 100644
index 00000000..7d6a0839
--- /dev/null
+++ b/vagrant/insecure_keys/key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzI
+w+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoP
+kcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2
+hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NO
+Td0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcW
+yLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQIBIwKCAQEA4iqWPJXtzZA68mKd
+ELs4jJsdyky+ewdZeNds5tjcnHU5zUYE25K+ffJED9qUWICcLZDc81TGWjHyAqD1
+Bw7XpgUwFgeUJwUlzQurAv+/ySnxiwuaGJfhFM1CaQHzfXphgVml+fZUvnJUTvzf
+TK2Lg6EdbUE9TarUlBf/xPfuEhMSlIE5keb/Zz3/LUlRg8yDqz5w+QWVJ4utnKnK
+iqwZN0mwpwU7YSyJhlT4YV1F3n4YjLswM5wJs2oqm0jssQu/BT0tyEXNDYBLEF4A
+sClaWuSJ2kjq7KhrrYXzagqhnSei9ODYFShJu8UWVec3Ihb5ZXlzO6vdNQ1J9Xsf
+4m+2ywKBgQD6qFxx/Rv9CNN96l/4rb14HKirC2o/orApiHmHDsURs5rUKDx0f9iP
+cXN7S1uePXuJRK/5hsubaOCx3Owd2u9gD6Oq0CsMkE4CUSiJcYrMANtx54cGH7Rk
+EjFZxK8xAv1ldELEyxrFqkbE4BKd8QOt414qjvTGyAK+OLD3M2QdCQKBgQDtx8pN
+CAxR7yhHbIWT1AH66+XWN8bXq7l3RO/ukeaci98JfkbkxURZhtxV/HHuvUhnPLdX
+3TwygPBYZFNo4pzVEhzWoTtnEtrFueKxyc3+LjZpuo+mBlQ6ORtfgkr9gBVphXZG
+YEzkCD3lVdl8L4cw9BVpKrJCs1c5taGjDgdInQKBgHm/fVvv96bJxc9x1tffXAcj
+3OVdUN0UgXNCSaf/3A/phbeBQe9xS+3mpc4r6qvx+iy69mNBeNZ0xOitIjpjBo2+
+dBEjSBwLk5q5tJqHmy/jKMJL4n9ROlx93XS+njxgibTvU6Fp9w+NOFD/HvxB3Tcz
+6+jJF85D5BNAG3DBMKBjAoGBAOAxZvgsKN+JuENXsST7F89Tck2iTcQIT8g5rwWC
+P9Vt74yboe2kDT531w8+egz7nAmRBKNM751U/95P9t88EDacDI/Z2OwnuFQHCPDF
+llYOUI+SpLJ6/vURRbHSnnn8a/XG+nzedGH5JGqEJNQsz+xT2axM0/W/CRknmGaJ
+kda/AoGANWrLCz708y7VYgAtW2Uf1DPOIYMdvo6fxIB5i9ZfISgcJ/bbCUkFrhoH
++vq/5CIWxCPp0f85R4qxxQ5ihxJ0YDQT9Jpx4TMss4PSavPaBH3RXow5Ohe+bYoQ
+NE5OgEXk2wVfZczCZpigBKbKZHNYcelXtTt/nP3rsCuGcM4h53s=
+-----END RSA PRIVATE KEY-----
diff --git a/vagrant/insecure_keys/key.pub b/vagrant/insecure_keys/key.pub
new file mode 100644
index 00000000..18a9c00f
--- /dev/null
+++ b/vagrant/insecure_keys/key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key
diff --git a/vagrant/installer.sh b/vagrant/installer.sh
index 5fdcaeb5..271f44f5 100755
--- a/vagrant/installer.sh
+++ b/vagrant/installer.sh
@@ -9,25 +9,11 @@
##############################################################################
set -o errexit
-set -o nounset
set -o pipefail
-# usage() - Prints the usage of the program
-function usage {
- cat <<EOF
-usage: $0 [-a addons] [-p] [-v] [-w dir ]
-Optional Argument:
- -a List of Kubernetes AddOns to be installed ( e.g. "ovn-kubernetes virtlet multus")
- -p Installation of ONAP MultiCloud Kubernetes plugin
- -v Enable verbosity
- -w Working directory
- -t Running healthchecks
-EOF
-}
-
# _install_go() - Install GoLang package
function _install_go {
- version=$(grep "go_version" ${krd_playbooks}/krd-vars.yml | awk -F ': ' '{print $2}')
+ version=$(grep "go_version" ${krd_playbooks}/krd-vars.yml | awk -F "'" '{print $2}')
local tarball=go$version.linux-amd64.tar.gz
if $(go version &>/dev/null); then
@@ -35,37 +21,31 @@ function _install_go {
fi
wget https://dl.google.com/go/$tarball
- tar -C /usr/local -xzf $tarball
+ sudo tar -C /usr/local -xzf $tarball
rm $tarball
export PATH=$PATH:/usr/local/go/bin
- sed -i "s|^PATH=.*|PATH=\"$PATH\"|" /etc/environment
- export INSTALL_DIRECTORY=/usr/local/bin
- curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
+ sudo sed -i "s|^PATH=.*|PATH=\"$PATH\"|" /etc/environment
}
# _install_pip() - Install Python Package Manager
function _install_pip {
if $(pip --version &>/dev/null); then
- return
+ sudo apt-get install -y python-dev
+ curl -sL https://bootstrap.pypa.io/get-pip.py | sudo python
+ else
+ sudo -E pip install --upgrade pip
fi
- apt-get install -y python-dev
- curl -sL https://bootstrap.pypa.io/get-pip.py | python
- pip install --upgrade pip
}
# _install_ansible() - Install and Configure Ansible program
function _install_ansible {
- mkdir -p /etc/ansible/
- cat <<EOL > /etc/ansible/ansible.cfg
-[defaults]
-host_key_checking = false
-EOL
+ sudo mkdir -p /etc/ansible/
if $(ansible --version &>/dev/null); then
return
fi
_install_pip
- pip install ansible
+ sudo -E pip install ansible
}
# _install_docker() - Download and install docker-engine
@@ -75,36 +55,33 @@ function _install_docker {
if $(docker version &>/dev/null); then
return
fi
- apt-get install -y software-properties-common linux-image-extra-$(uname -r) linux-image-extra-virtual apt-transport-https ca-certificates curl
+ sudo apt-get install -y software-properties-common linux-image-extra-$(uname -r) linux-image-extra-virtual apt-transport-https ca-certificates curl
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
- add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
- apt-get update
- apt-get install -y docker-ce
+ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
+ sudo apt-get update
+ sudo apt-get install -y docker-ce
- mkdir -p /etc/systemd/system/docker.service.d
+ sudo mkdir -p /etc/systemd/system/docker.service.d
if [ $http_proxy ]; then
- cat <<EOL > /etc/systemd/system/docker.service.d/http-proxy.conf
-[Service]
-Environment="HTTP_PROXY=$http_proxy"
-EOL
+ echo "[Service]" | sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf
+ echo "Environment=\"HTTP_PROXY=$http_proxy\"" | sudo tee --append /etc/systemd/system/docker.service.d/http-proxy.conf
fi
if [ $https_proxy ]; then
- cat <<EOL > /etc/systemd/system/docker.service.d/https-proxy.conf
-[Service]
-Environment="HTTPS_PROXY=$https_proxy"
-EOL
+ echo "[Service]" | sudo tee /etc/systemd/system/docker.service.d/https-proxy.conf
+ echo "Environment=\"HTTPS_PROXY=$https_proxy\"" | sudo tee --append /etc/systemd/system/docker.service.d/https-proxy.conf
fi
if [ $no_proxy ]; then
- cat <<EOL > /etc/systemd/system/docker.service.d/no-proxy.conf
-[Service]
-Environment="NO_PROXY=$no_proxy"
-EOL
+ echo "[Service]" | sudo tee /etc/systemd/system/docker.service.d/no-proxy.conf
+ echo "Environment=\"NO_PROXY=$no_proxy\"" | sudo tee --append /etc/systemd/system/docker.service.d/no-proxy.conf
+ fi
+ sudo systemctl daemon-reload
+ echo "DOCKER_OPTS=\"-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --max-concurrent-downloads $max_concurrent_downloads \"" | sudo tee --append /etc/default/docker
+ if [[ -z $(groups | grep docker) ]]; then
+ sudo usermod -aG docker $USER
+ newgrp docker
fi
- systemctl daemon-reload
- echo "DOCKER_OPTS=\"-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --max-concurrent-downloads $max_concurrent_downloads \"" >> /etc/default/docker
- usermod -aG docker $USER
- systemctl restart docker
+ sudo systemctl restart docker
sleep 10
}
@@ -113,48 +90,51 @@ function install_k8s {
echo "Deploying kubernetes"
local dest_folder=/opt
version=$(grep "kubespray_version" ${krd_playbooks}/krd-vars.yml | awk -F ': ' '{print $2}')
+ local_release_dir=$(grep "local_release_dir" $krd_inventory_folder/group_vars/k8s-cluster.yml | awk -F "\"" '{print $2}')
local tarball=v$version.tar.gz
- apt-get install -y sshpass
+ sudo apt-get install -y sshpass
+ _install_docker
_install_ansible
wget https://github.com/kubernetes-incubator/kubespray/archive/$tarball
- tar -C $dest_folder -xzf $tarball
+ sudo tar -C $dest_folder -xzf $tarball
+ sudo mv $dest_folder/kubespray-$version/ansible.cfg /etc/ansible/ansible.cfg
+ sudo chown -R $USER $dest_folder/kubespray-$version
+ sudo mkdir -p ${local_release_dir}/containers
rm $tarball
- pushd $dest_folder/kubespray-$version
- pip install -r requirements.txt
- rm -f $krd_inventory_folder/group_vars/all.yml 2> /dev/null
- if [[ -n "${verbose+x}" ]]; then
- echo "kube_log_level: 5" >> $krd_inventory_folder/group_vars/all.yml
- else
- echo "kube_log_level: 2" >> $krd_inventory_folder/group_vars/all.yml
- fi
- if [[ -n "${http_proxy+x}" ]]; then
- echo "http_proxy: \"$http_proxy\"" >> $krd_inventory_folder/group_vars/all.yml
- fi
- if [[ -n "${https_proxy+x}" ]]; then
- echo "https_proxy: \"$https_proxy\"" >> $krd_inventory_folder/group_vars/all.yml
- fi
- ansible-playbook $verbose -i $krd_inventory cluster.yml -b | tee $log_folder/setup-kubernetes.log
- popd
+ sudo -E pip install -r $dest_folder/kubespray-$version/requirements.txt
+ rm -f $krd_inventory_folder/group_vars/all.yml 2> /dev/null
+ if [[ -n "${verbose}" ]]; then
+ echo "kube_log_level: 5" | tee $krd_inventory_folder/group_vars/all.yml
+ else
+ echo "kube_log_level: 2" | tee $krd_inventory_folder/group_vars/all.yml
+ fi
+ echo "kubeadm_enabled: true" | tee --append $krd_inventory_folder/group_vars/all.yml
+ if [[ -n "${http_proxy}" ]]; then
+ echo "http_proxy: \"$http_proxy\"" | tee --append $krd_inventory_folder/group_vars/all.yml
+ fi
+ if [[ -n "${https_proxy}" ]]; then
+ echo "https_proxy: \"$https_proxy\"" | tee --append $krd_inventory_folder/group_vars/all.yml
+ fi
+ ansible-playbook $verbose -i $krd_inventory $dest_folder/kubespray-$version/cluster.yml --become --become-user=root | sudo tee $log_folder/setup-kubernetes.log
# Configure environment
mkdir -p $HOME/.kube
- mv $krd_inventory_folder/artifacts/admin.conf $HOME/.kube/config
+ cp $krd_inventory_folder/artifacts/admin.conf $HOME/.kube/config
}
# install_addons() - Install Kubenertes AddOns
function install_addons {
echo "Installing Kubernetes AddOns"
- apt-get install -y sshpass
_install_ansible
- ansible-galaxy install -r $krd_folder/galaxy-requirements.yml --ignore-errors
+ sudo ansible-galaxy install $verbose -r $krd_folder/galaxy-requirements.yml --ignore-errors
- ansible-playbook $verbose -i $krd_inventory $krd_playbooks/configure-krd.yml | tee $log_folder/setup-krd.log
- for addon in $addons; do
+ ansible-playbook $verbose -i $krd_inventory $krd_playbooks/configure-krd.yml | sudo tee $log_folder/setup-krd.log
+ for addon in ${KRD_ADDONS:-virtlet ovn4nfv}; do
echo "Deploying $addon using configure-$addon.yml playbook.."
- ansible-playbook $verbose -i $krd_inventory $krd_playbooks/configure-${addon}.yml | tee $log_folder/setup-${addon}.log
- if [[ -n "${testing_enabled+x}" ]]; then
+ ansible-playbook $verbose -i $krd_inventory $krd_playbooks/configure-${addon}.yml | sudo tee $log_folder/setup-${addon}.log
+ if [[ "${testing_enabled}" == "true" ]]; then
pushd $krd_tests
bash ${addon}.sh
popd
@@ -167,18 +147,16 @@ function install_plugin {
echo "Installing multicloud/k8s plugin"
_install_go
_install_docker
- pip install docker-compose
+ sudo -E pip install docker-compose
- mkdir -p /opt/{kubeconfig,consul/config}
- cp $HOME/.kube/config /opt/kubeconfig/krd
+ sudo mkdir -p /opt/{kubeconfig,consul/config}
+ sudo cp $HOME/.kube/config /opt/kubeconfig/krd
export KUBE_CONFIG_DIR=/opt/kubeconfig
- echo "export KUBE_CONFIG_DIR=${KUBE_CONFIG_DIR}" >> /etc/environment
+ echo "export KUBE_CONFIG_DIR=${KUBE_CONFIG_DIR}" | sudo tee --append /etc/environment
- GOPATH=$(go env GOPATH)
- pushd $GOPATH/src/k8-plugin-multicloud/deployments
- ./build.sh
-
- if [[ -n "${testing_enabled+x}" ]]; then
+ pushd $krd_folder/../deployments
+ sudo ./build.sh
+ if [[ "${testing_enabled}" == "true" ]]; then
docker-compose up -d
pushd $krd_tests
for functional_test in plugin plugin_edgex; do
@@ -207,57 +185,47 @@ function _print_kubernetes_info {
echo "Admin password: secret" >> $k8s_info_file
}
-# Configuration values
-addons="virtlet ovn-kubernetes multus"
-krd_folder="$(dirname "$0")"
-verbose=""
+if ! sudo -n "true"; then
+ echo ""
+ echo "passwordless sudo is needed for '$(id -nu)' user."
+ echo "Please fix your /etc/sudoers file. You likely want an"
+ echo "entry like the following one..."
+ echo ""
+ echo "$(id -nu) ALL=(ALL) NOPASSWD: ALL"
+ exit 1
+fi
+
+if [[ -n "${KRD_DEBUG}" ]]; then
+ set -o xtrace
+ verbose="-vvv"
+fi
-while getopts "a:pvw:t" opt; do
- case $opt in
- a)
- addons="$OPTARG"
- ;;
- p)
- plugin_enabled="true"
- ;;
- v)
- set -o xtrace
- verbose="-vvv"
- ;;
- w)
- krd_folder="$OPTARG"
- ;;
- t)
- testing_enabled="true"
- ;;
- ?)
- usage
- exit
- ;;
- esac
-done
+# Configuration values
log_folder=/var/log/krd
-krd_inventory_folder=$krd_folder/inventory
+krd_folder=$(pwd)
+export krd_inventory_folder=$krd_folder/inventory
krd_inventory=$krd_inventory_folder/hosts.ini
krd_playbooks=$krd_folder/playbooks
krd_tests=$krd_folder/tests
k8s_info_file=$krd_folder/k8s_info.log
+testing_enabled=${KRD_ENABLE_TESTS:-false}
-mkdir -p $log_folder
-mkdir -p /opt/csar
+sudo mkdir -p $log_folder
+sudo mkdir -p /opt/csar
+sudo chown -R $USER /opt/csar
export CSAR_DIR=/opt/csar
-echo "export CSAR_DIR=${CSAR_DIR}" >> /etc/environment
+echo "export CSAR_DIR=${CSAR_DIR}" | sudo tee --append /etc/environment
# Install dependencies
# Setup proxy variables
if [ -f $krd_folder/sources.list ]; then
- mv /etc/apt/sources.list /etc/apt/sources.list.backup
- cp $krd_folder/sources.list /etc/apt/sources.list
+ sudo mv /etc/apt/sources.list /etc/apt/sources.list.backup
+ sudo cp $krd_folder/sources.list /etc/apt/sources.list
fi
-apt-get update
+sudo apt-get update
install_k8s
install_addons
-if [[ -n "${plugin_enabled+x}" ]]; then
+if [[ "${KRD_PLUGIN_ENABLED:-false}" ]]; then
install_plugin
fi
_print_kubernetes_info
diff --git a/vagrant/inventory/group_vars/k8s-cluster.yml b/vagrant/inventory/group_vars/k8s-cluster.yml
index f038d4f2..4de3a276 100644
--- a/vagrant/inventory/group_vars/k8s-cluster.yml
+++ b/vagrant/inventory/group_vars/k8s-cluster.yml
@@ -57,7 +57,7 @@ kubeconfig_localhost: true
local_volumes_enabled: true
## Change this to use another Kubernetes version, e.g. a current beta release
-kube_version: v1.11.3
+kube_version: v1.12.3
# Helm deployment
helm_enabled: true
@@ -66,4 +66,17 @@ helm_enabled: true
# NOTE: Ipvs is based on netfilter hook function, but uses hash table as the underlying data structure and
# works in the kernel space
# https://kubernetes.io/docs/concepts/services-networking/service/#proxy-mode-ipvs
-kube_proxy_mode: ipvs
+#kube_proxy_mode: ipvs
+
+# Download container images only once then push to cluster nodes in batches
+download_run_once: true
+
+# Where the binaries will be downloaded.
+# Note: ensure that you've enough disk space (about 1G)
+local_release_dir: "/tmp/releases"
+
+# Makes the installer node a delegate for pushing images while running
+# the deployment with ansible. This maybe the case if cluster nodes
+# cannot access each over via ssh or you want to use local docker
+# images as a cache for multiple clusters.
+download_localhost: true
diff --git a/vagrant/playbooks/configure-istio.yml b/vagrant/playbooks/configure-istio.yml
index 25a343f0..2bd4e853 100644
--- a/vagrant/playbooks/configure-istio.yml
+++ b/vagrant/playbooks/configure-istio.yml
@@ -9,15 +9,15 @@
##############################################################################
- hosts: localhost
- become: yes
pre_tasks:
- name: Load krd variables
include_vars:
file: krd-vars.yml
roles:
- - andrewrothstein.kubectl
+ - role: andrewrothstein.kubectl
+ kubectl_ver: "v{{ kubectl_version }}"
- role: andrewrothstein.kubernetes-helm
- kubernetes_helm_ver: v2.9.1
+ kubernetes_helm_ver: "v{{ helm_client_version }}"
tasks:
- name: create istio folder
file:
@@ -35,6 +35,7 @@
dest: "{{ istio_dest }}"
remote_src: yes
- name: copy istioctl binary to usr/local/bin folder
+ become: yes
command: "mv {{ istio_dest }}/istio-{{ istio_version }}/bin/istioctl /usr/local/bin/"
when: istio_source_type == "tarball"
- name: create network objects
diff --git a/vagrant/playbooks/configure-krd.yml b/vagrant/playbooks/configure-krd.yml
index c8146ed8..22e6419f 100644
--- a/vagrant/playbooks/configure-krd.yml
+++ b/vagrant/playbooks/configure-krd.yml
@@ -12,5 +12,5 @@
tasks:
- name: copy admin.conf file to kube-nodes
copy:
- src: "{{ ansible_env.HOME}}/.kube/config"
+ src: "{{ lookup('env','krd_inventory_folder') }}/artifacts/admin.conf"
dest: "/etc/kubernetes/admin.conf"
diff --git a/vagrant/playbooks/configure-multus.yml b/vagrant/playbooks/configure-multus.yml
index 33e72757..23fe546a 100644
--- a/vagrant/playbooks/configure-multus.yml
+++ b/vagrant/playbooks/configure-multus.yml
@@ -14,7 +14,9 @@
include_vars:
file: krd-vars.yml
roles:
- - { role: andrewrothstein.go, when: multus_source_type == "source" }
+ - role: andrewrothstein.go
+ go_ver: "{{ go_version }}"
+ when: multus_source_type == "source"
environment:
PATH: "{{ ansible_env.PATH }}:/usr/local/go/bin/"
tasks:
@@ -78,8 +80,13 @@
}
- hosts: localhost
+ pre_tasks:
+ - name: Load krd variables
+ include_vars:
+ file: krd-vars.yml
roles:
- - andrewrothstein.kubectl
+ - role: andrewrothstein.kubectl
+ kubectl_ver: "v{{ kubectl_version }}"
tasks:
- name: define a CRD network object specification
blockinfile:
diff --git a/vagrant/playbooks/configure-nfd.yml b/vagrant/playbooks/configure-nfd.yml
index 90bad671..d47a7bcc 100644
--- a/vagrant/playbooks/configure-nfd.yml
+++ b/vagrant/playbooks/configure-nfd.yml
@@ -46,9 +46,13 @@
- node-feature-discovery-daemonset.json.template
- hosts: localhost
- become: yes
+ pre_tasks:
+ - name: Load krd variables
+ include_vars:
+ file: krd-vars.yml
roles:
- - andrewrothstein.kubectl
+ - role: andrewrothstein.kubectl
+ kubectl_ver: "v{{ kubectl_version }}"
tasks:
- name: create service accounts
command: "/usr/local/bin/kubectl apply -f /tmp/{{ item }}"
diff --git a/vagrant/playbooks/configure-ovn-kubernetes.yml b/vagrant/playbooks/configure-ovn-kubernetes.yml
index cea102f2..e3042ff4 100644
--- a/vagrant/playbooks/configure-ovn-kubernetes.yml
+++ b/vagrant/playbooks/configure-ovn-kubernetes.yml
@@ -14,8 +14,13 @@
central_node_ip: "{{ hostvars[groups['ovn-central'][0]]['ansible_ssh_host'] }}"
environment:
PATH: "{{ ansible_env.PATH }}:/usr/local/go/bin/"
+ pre_tasks:
+ - name: Load krd variables
+ include_vars:
+ file: krd-vars.yml
roles:
- role: andrewrothstein.go
+ go_ver: "{{ go_version }}"
tasks:
- name: Load krd variables
include_vars:
diff --git a/vagrant/playbooks/configure-ovn4nfv.yml b/vagrant/playbooks/configure-ovn4nfv.yml
new file mode 100644
index 00000000..c864b8c3
--- /dev/null
+++ b/vagrant/playbooks/configure-ovn4nfv.yml
@@ -0,0 +1,98 @@
+---
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright (c) 2018
+# 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
+##############################################################################
+- import_playbook: configure-ovn.yml
+- import_playbook: configure-multus.yml
+
+- hosts: kube-master:kube-node
+ environment:
+ PATH: "{{ ansible_env.PATH }}:/usr/local/go/bin/"
+ roles:
+ - role: andrewrothstein.go
+ tasks:
+ - name: Load krd variables
+ include_vars:
+ file: krd-vars.yml
+ - name: clone ovn4nfv-k8s-plugin repo
+ git:
+ repo: "{{ ovn4nfv_url }}"
+ dest: "{{ ovn4nfv_dest }}"
+ version: "{{ ovn4nfv_version }}"
+ force: yes
+ when: ovn4nfv_source_type == "source"
+ - name: clean ovn4nfvk8s left over files
+ make:
+ chdir: "{{ ovn4nfv_dest }}"
+ target: clean
+ - name: build ovn4nfvk8s-cni
+ make:
+ chdir: "{{ ovn4nfv_dest }}"
+ target: ovn4nfvk8s-cni
+ become: yes
+ environment:
+ GOPATH: "{{ go_path }}"
+ - name: copy ovn4nfvk8s-cni to cni folder
+ command: "mv {{ ovn4nfv_dest }}/ovn4nfvk8s-cni /opt/cni/bin/ovn4nfvk8s-cni"
+ become: yes
+ - name: create ovn4k8s config file
+ become: yes
+ blockinfile:
+ path: /etc/openvswitch/ovn4nfv_k8s.conf
+ create: yes
+ block: |
+ [logging]
+ loglevel=5
+ logfile=/var/log/openvswitch/ovn4k8s.log
+
+ [cni]
+ conf-dir=/etc/cni/net.d
+ plugin=ovn4nfvk8s-cni
+
+ [kubernetes]
+ kubeconfig=/etc/kubernetes/admin.conf
+ - name: create ovnkube logging directory
+ file:
+ path: /var/log/openvswitch
+ state: directory
+
+- hosts: kube-master
+ environment:
+ PATH: "{{ ansible_env.PATH }}:/usr/local/go/bin/"
+ become: yes
+ tasks:
+ - name: Load krd variables
+ include_vars:
+ file: krd-vars.yml
+ - name: build ovn4nfvk8s
+ make:
+ chdir: "{{ ovn4nfv_dest }}"
+ target: ovn4nfvk8s
+ environment:
+ GOPATH: "{{ go_path }}"
+ - name: copy ovn4nfvk8s to /usr/bin folder
+ command: "mv {{ ovn4nfv_dest }}/ovn4nfvk8s /usr/bin/ovn4nfvk8s"
+ - name: create ovn4nfvk8s systemd service
+ blockinfile:
+ path: /etc/systemd/system/ovn4nfvk8s.service
+ create: yes
+ block: |
+ [Unit]
+ Description=OVN4NFV Kubernetes Daemon
+
+ [Service]
+ ExecStart=/usr/bin/ovn4nfvk8s \
+ -k8s-kubeconfig=/etc/kubernetes/admin.conf
+
+ [Install]
+ WantedBy=multi-user.target
+ - name: start ovn4nfvk8s systemd service
+ service:
+ name: ovn4nfvk8s
+ state: started
+ enabled: yes
diff --git a/vagrant/playbooks/configure-virtlet.yml b/vagrant/playbooks/configure-virtlet.yml
index 98aa74cc..66deb5cb 100644
--- a/vagrant/playbooks/configure-virtlet.yml
+++ b/vagrant/playbooks/configure-virtlet.yml
@@ -8,7 +8,6 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
- hosts: localhost
- become: yes
vars:
images_file: /tmp/images.yaml
pre_tasks:
@@ -16,8 +15,10 @@
include_vars:
file: krd-vars.yml
roles:
- - andrewrothstein.kubectl
- - { role: geerlingguy.docker, when: virtlet_source_type == "source" }
+ - role: andrewrothstein.kubectl
+ kubectl_ver: "v{{ kubectl_version }}"
+ - role: geerlingguy.docker
+ when: virtlet_source_type == "source"
tasks:
- name: create Virtlet binary folder
file:
@@ -65,10 +66,12 @@
- name: configure proxy values for docker service
block:
- name: create docker config folder
+ become: yes
file:
state: directory
path: "/etc/systemd/system/docker.service.d"
- name: Configure docker service to use http_proxy env value
+ become: yes
blockinfile:
dest: "/etc/systemd/system/docker.service.d/http-proxy.conf"
create: yes
@@ -78,6 +81,7 @@
when:
- lookup('env','http_proxy') != "fooproxy"
- name: Configure docker service to use https_proxy env value
+ become: yes
blockinfile:
dest: "/etc/systemd/system/docker.service.d/https-proxy.conf"
create: yes
@@ -87,6 +91,7 @@
when:
- lookup('env','https_proxy') != "fooproxy"
- name: Configure docker service to use no_proxy env value
+ become: yes
blockinfile:
dest: "/etc/systemd/system/docker.service.d/no-proxy.conf"
create: yes
@@ -96,8 +101,10 @@
when:
- lookup('env','no_proxy') != "fooproxy"
- name: reload systemd
+ become: yes
command: systemctl daemon-reload
- name: restart docker service
+ become: yes
service:
name: docker
state: restarted
@@ -134,7 +141,6 @@
delay: 10
- hosts: virtlet
- become: yes
tasks:
- name: Load krd variables
include_vars:
@@ -144,18 +150,21 @@
state: directory
path: "{{ criproxy_dest }}"
- name: disable AppArmor in all nodes
+ become: yes
service:
name: apparmor
state: stopped
enabled: no
when: ansible_os_family == "Debian"
- name: modify args for kubelet service
+ become: yes
lineinfile:
dest: /etc/systemd/system/kubelet.service
line: " --container-runtime=remote --container-runtime-endpoint=unix:///run/criproxy.sock --image-service-endpoint=unix:///run/criproxy.sock --enable-controller-attach-detach=false \\"
insertafter: '^ExecStart=/usr/local/bin/kubelet *'
state: present
- name: create dockershim service
+ become: yes
blockinfile:
path: /etc/systemd/system/dockershim.service
create: yes
@@ -208,6 +217,7 @@
path: "{{ criproxy_dest }}/criproxy"
mode: "+x"
- name: create criproxy service
+ become: yes
blockinfile:
path: /etc/systemd/system/criproxy.service
create: yes
@@ -224,6 +234,7 @@
[Install]
WantedBy=kubelet.service
- name: start criproxy and dockershim services
+ become: yes
service:
name: "{{ item }}"
state: started
@@ -232,6 +243,7 @@
- dockershim
- criproxy
- name: restart kubelet services
+ become: yes
service:
name: kubelet
state: restarted
diff --git a/vagrant/playbooks/krd-vars.yml b/vagrant/playbooks/krd-vars.yml
index 9c2de308..15b7a1a4 100644
--- a/vagrant/playbooks/krd-vars.yml
+++ b/vagrant/playbooks/krd-vars.yml
@@ -11,12 +11,12 @@
base_dest: /tmp
multus_dest: "{{ base_dest }}/multus-cni"
-multus_source_type: "tarball"
-multus_version: 3.1
-multus_url: "https://github.com/intel/multus-cni/releases/download/v{{ multus_version }}/multus-cni_v{{ multus_version }}_linux_amd64.tar.gz"
-#multus_source_type: "source"
-#multus_version: def72938cd2fb272eb3a6f64a8162b1049404357
-#multus_url: "https://github.com/intel/multus-cni"
+#multus_source_type: "tarball"
+#multus_version: 3.1
+#multus_url: "https://github.com/intel/multus-cni/releases/download/v{{ multus_version }}/multus-cni_v{{ multus_version }}_linux_amd64.tar.gz"
+multus_source_type: "source"
+multus_version: 366f2120cb88c85deab6343b7062fd38fdb0ece9
+multus_url: "https://github.com/ritusood/multus-cni"
ovn_kubernetes_dest: "{{ base_dest }}/ovn-kubernetes"
ovn_kubernetes_source_type: "tarball"
@@ -35,7 +35,7 @@ criproxy_url: "https://github.com/Mirantis/criproxy/releases/download/v{{ cripro
#criproxy_url: "https://github.com/Mirantis/criproxy"
virtlet_dest: "{{ base_dest }}/virtlet"
virtlet_source_type: "binary"
-virtlet_version: 1.4.1
+virtlet_version: 1.4.2
virtlet_url: "https://github.com/Mirantis/virtlet/releases/download/v{{ virtlet_version }}/virtletctl"
#virtlet_source_type: "source"
#virtlet_version: 68e11b8f1db2c78b063126899f0e60910700975d
@@ -51,5 +51,13 @@ istio_source_type: "tarball"
istio_version: 1.0.3
istio_url: "https://github.com/istio/istio/releases/download/{{ istio_version }}/istio-{{ istio_version }}-linux.tar.gz"
-go_version: 1.11.1
-kubespray_version: 2.7.0
+go_path: "{{ base_dest }}/go"
+ovn4nfv_dest: "{{ go_path }}/src/ovn4nfv-k8s-plugin"
+ovn4nfv_source_type: "source"
+ovn4nfv_version: 5026d1d89b05eac5e004279b742df6745a73d93a
+ovn4nfv_url: "https://git.opnfv.org/ovn4nfv-k8s-plugin/"
+
+go_version: '1.11'
+kubespray_version: 2.8.0
+kubectl_version: 1.11.2
+helm_client_version: 2.9.1
diff --git a/vagrant/setup.sh b/vagrant/setup.sh
index c8fe2e28..674462e7 100755
--- a/vagrant/setup.sh
+++ b/vagrant/setup.sh
@@ -11,7 +11,7 @@
set -o nounset
set -o pipefail
-vagrant_version=2.2.0
+vagrant_version=2.2.2
if ! $(vagrant version &>/dev/null); then
enable_vagrant_install=true
else
@@ -177,9 +177,10 @@ modprobe vhost_net
${INSTALLER_CMD} ${packages[@]}
if ! which pip; then
curl -sL https://bootstrap.pypa.io/get-pip.py | sudo python
+else
+ sudo -H -E pip install --upgrade pip
fi
-sudo -H pip install --upgrade pip
-sudo -H pip install tox
+sudo -H -E pip install tox
if [[ ${http_proxy+x} ]]; then
vagrant plugin install vagrant-proxyconf
fi
diff --git a/vagrant/tests/_common.sh b/vagrant/tests/_common.sh
index ac226da0..620c00af 100755
--- a/vagrant/tests/_common.sh
+++ b/vagrant/tests/_common.sh
@@ -21,6 +21,27 @@ virtlet_image=virtlet.cloud/fedora
virtlet_deployment_name=virtlet-deployment
plugin_deployment_name=plugin-deployment
plugin_service_name=plugin-service
+ovn4nfv_deployment_name=ovn4nfv-deployment
+onap_private_net=onap-private-net
+unprotected_private_net=unprotected-private-net
+protected_private_net=protected-private-net
+ovn_multus_network_name=ovn-networkobj
+
+# vFirewall vars
+demo_artifacts_version=1.3.0
+vfw_private_ip_0='192.168.10.3'
+vfw_private_ip_1='192.168.20.2'
+vfw_private_ip_2='10.10.100.3'
+vpg_private_ip_0='192.168.10.2'
+vpg_private_ip_1='10.0.100.2'
+vsn_private_ip_0='192.168.20.3'
+vsn_private_ip_1='10.10.100.4'
+dcae_collector_ip='10.0.4.1'
+dcae_collector_port='8081'
+protected_net_gw='192.168.20.100'
+protected_net_cidr='192.168.20.0/24'
+protected_private_net_cidr='192.168.10.0/24'
+onap_private_net_cidr='10.10.0.0/16'
# populate_CSAR_containers_vFW() - This function creates the content of CSAR file
# required for vFirewal using only containers
@@ -33,59 +54,59 @@ function populate_CSAR_containers_vFW {
cat << META > metadata.yaml
resources:
network:
- - unprotected-private-net-cidr-network.yaml
- - protected-private-net-cidr-network.yaml
- - onap-private-net-cidr-network.yaml
+ - $unprotected_private_net.yaml
+ - $protected_private_net.yaml
+ - $onap_private_net.yaml
deployment:
- $packetgen_deployment_name.yaml
- $firewall_deployment_name.yaml
- $sink_deployment_name.yaml
META
- cat << NET > unprotected-private-net-cidr-network.yaml
+ cat << NET > $unprotected_private_net.yaml
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
- name: unprotected-private-net-cidr
+ name: $unprotected_private_net
spec:
config: '{
"name": "unprotected",
"type": "bridge",
"ipam": {
"type": "host-local",
- "subnet": "192.168.10.0/24"
+ "subnet": "$protected_private_net_cidr"
}
}'
NET
- cat << NET > protected-private-net-cidr-network.yaml
+ cat << NET > $protected_private_net.yaml
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
- name: protected-private-net-cidr
+ name: $protected_private_net
spec:
config: '{
"name": "protected",
"type": "bridge",
"ipam": {
"type": "host-local",
- "subnet": "192.168.20.0/24"
+ "subnet": "$protected_net_cidr"
}
}'
NET
- cat << NET > onap-private-net-cidr-network.yaml
+ cat << NET > $onap_private_net.yaml
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
- name: onap-private-net-cidr
+ name: $onap_private_net
spec:
config: '{
"name": "onap",
"type": "bridge",
"ipam": {
"type": "host-local",
- "subnet": "10.10.0.0/16"
+ "subnet": "$onap_private_net_cidr"
}
}'
NET
@@ -108,8 +129,8 @@ spec:
app: vFirewall
annotations:
k8s.v1.cni.cncf.io/networks: '[
- { "name": "unprotected-private-net-cidr", "interfaceRequest": "eth1" },
- { "name": "onap-private-net-cidr", "interfaceRequest": "eth2" }
+ { "name": "$unprotected_private_net", "interfaceRequest": "eth1" },
+ { "name": "$onap_private_net", "interfaceRequest": "eth2" }
]'
spec:
containers:
@@ -141,9 +162,9 @@ spec:
app: vFirewall
annotations:
k8s.v1.cni.cncf.io/networks: '[
- { "name": "unprotected-private-net-cidr", "interfaceRequest": "eth1" },
- { "name": "protected-private-net-cidr", "interfaceRequest": "eth2" },
- { "name": "onap-private-net-cidr", "interfaceRequest": "eth3" }
+ { "name": "$unprotected_private_net", "interfaceRequest": "eth1" },
+ { "name": "$protected_private_net", "interfaceRequest": "eth2" },
+ { "name": "$onap_private_net", "interfaceRequest": "eth3" }
]'
spec:
containers:
@@ -166,14 +187,16 @@ spec:
selector:
matchLabels:
app: vFirewall
+ context: darkstat
template:
metadata:
labels:
app: vFirewall
+ context: darkstat
annotations:
k8s.v1.cni.cncf.io/networks: '[
- { "name": "protected-private-net-cidr", "interfaceRequest": "eth1" },
- { "name": "onap-private-net-cidr", "interfaceRequest": "eth2" }
+ { "name": "$protected_private_net", "interfaceRequest": "eth1" },
+ { "name": "$onap_private_net", "interfaceRequest": "eth2" }
]'
spec:
containers:
@@ -182,6 +205,15 @@ spec:
imagePullPolicy: IfNotPresent
tty: true
stdin: true
+ securityContext:
+ privileged: true
+ - name: darkstat
+ image: electrocucaracha/darkstat
+ imagePullPolicy: IfNotPresent
+ tty: true
+ stdin: true
+ ports:
+ - containerPort: 667
DEPLOYMENT
popd
}
@@ -199,17 +231,17 @@ function populate_CSAR_vms_containers_vFW {
cat << META > metadata.yaml
resources:
network:
- - unprotected-private-net-cidr-network.yaml
- - protected-private-net-cidr-network.yaml
- - onap-private-net-cidr-network.yaml
+ - onap-ovn4nfvk8s-network.yaml
+ onapNetwork:
+ - $unprotected_private_net.yaml
+ - $protected_private_net.yaml
+ - $onap_private_net.yaml
deployment:
- $packetgen_deployment_name.yaml
- $firewall_deployment_name.yaml
- $sink_deployment_name.yaml
service:
- sink-service.yaml
- ingress:
- - sink-ingress.yaml
META
cat << SERVICE > sink-service.yaml
@@ -217,8 +249,6 @@ apiVersion: v1
kind: Service
metadata:
name: sink-service
- labels:
- app: vFirewall
spec:
type: NodePort
ports:
@@ -229,71 +259,66 @@ spec:
context: darkstat
SERVICE
- cat << INGRESS > sink-ingress.yaml
-apiVersion: extensions/v1beta1
-kind: Ingress
-metadata:
- name: sink-ingress
-spec:
- rules:
- - host: sink.vfirewall.demo.com
- http:
- paths:
- - backend:
- serviceName: sink-service
- servicePort: 667
-INGRESS
-
- cat << NET > unprotected-private-net-cidr-network.yaml
+ cat << MULTUS_NET > onap-ovn4nfvk8s-network.yaml
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
- name: unprotected-private-net-cidr
+ name: $ovn_multus_network_name
spec:
config: '{
- "name": "unprotected",
- "type": "bridge",
- "ipam": {
- "type": "host-local",
- "subnet": "192.168.10.0/24"
- }
-}'
+ "cniVersion": "0.3.1",
+ "name": "ovn4nfv-k8s-plugin",
+ "type": "ovn4nfvk8s-cni"
+ }'
+MULTUS_NET
+
+ cat << NET > $unprotected_private_net.yaml
+apiVersion: v1
+kind: onapNetwork
+metadata:
+ name: $unprotected_private_net
+ cnitype : ovn4nfvk8s
+spec:
+ name: $unprotected_private_net
+ subnet: $protected_private_net_cidr
+ gateway: 192.168.10.1/24
NET
- cat << NET > protected-private-net-cidr-network.yaml
-apiVersion: "k8s.cni.cncf.io/v1"
-kind: NetworkAttachmentDefinition
+ cat << NET > $protected_private_net.yaml
+apiVersion: v1
+kind: onapNetwork
metadata:
- name: protected-private-net-cidr
+ name: $protected_private_net
+ cnitype : ovn4nfvk8s
spec:
- config: '{
- "name": "protected",
- "type": "bridge",
- "ipam": {
- "type": "host-local",
- "subnet": "192.168.20.0/24"
- }
-}'
+ name: $protected_private_net
+ subnet: $protected_net_cidr
+ gateway: $protected_net_gw/24
NET
- cat << NET > onap-private-net-cidr-network.yaml
-apiVersion: "k8s.cni.cncf.io/v1"
-kind: NetworkAttachmentDefinition
+ cat << NET > $onap_private_net.yaml
+apiVersion: v1
+kind: onapNetwork
metadata:
- name: onap-private-net-cidr
+ name: $onap_private_net
+ cnitype : ovn4nfvk8s
spec:
- config: '{
- "name": "onap",
- "type": "bridge",
- "ipam": {
- "type": "host-local",
- "subnet": "10.10.0.0/16"
- }
-}'
+ name: $onap_private_net
+ subnet: $onap_private_net_cidr
+ gateway: 10.10.0.1/16
NET
proxy="apt:"
- cloud_init_proxy=""
+ cloud_init_proxy="
+ - export demo_artifacts_version=$demo_artifacts_version
+ - export vfw_private_ip_0=$vfw_private_ip_0
+ - export vsn_private_ip_0=$vsn_private_ip_0
+ - export protected_net_cidr=$protected_net_cidr
+ - export dcae_collector_ip=$dcae_collector_ip
+ - export dcae_collector_port=$dcae_collector_port
+ - export protected_net_gw=$protected_net_gw
+ - export protected_private_net_cidr=$protected_private_net_cidr
+"
if [[ -n "${http_proxy+x}" ]]; then
proxy+="
http_proxy: $http_proxy"
@@ -350,9 +375,10 @@ spec:
VirtletSSHKeys: |
$ssh_key
VirtletRootVolumeSize: 5Gi
- k8s.v1.cni.cncf.io/networks: '[
- { "name": "unprotected-private-net-cidr", "interfaceRequest": "eth1" },
- { "name": "onap-private-net-cidr", "interfaceRequest": "eth2" }
+ k8s.v1.cni.cncf.io/networks: '[{ "name": "$ovn_multus_network_name"}]'
+ ovnNetwork: '[
+ { "name": "$unprotected_private_net", "ipAddress": "$vpg_private_ip_0", "interface": "eth1" , "defaultGateway": "false"},
+ { "name": "$onap_private_net", "ipAddress": "$vpg_private_ip_1", "interface": "eth2" , "defaultGateway": "false"}
]'
kubernetes.io/target-runtime: virtlet.cloud
spec:
@@ -417,10 +443,11 @@ spec:
VirtletSSHKeys: |
$ssh_key
VirtletRootVolumeSize: 5Gi
- k8s.v1.cni.cncf.io/networks: '[
- { "name": "unprotected-private-net-cidr", "interfaceRequest": "eth1" },
- { "name": "protected-private-net-cidr", "interfaceRequest": "eth2" },
- { "name": "onap-private-net-cidr", "interfaceRequest": "eth3" }
+ k8s.v1.cni.cncf.io/networks: '[{ "name": "$ovn_multus_network_name"}]'
+ ovnNetwork: '[
+ { "name": "$unprotected_private_net", "ipAddress": "$vfw_private_ip_0", "interface": "eth1" , "defaultGateway": "false"},
+ { "name": "$protected_private_net", "ipAddress": "$vfw_private_ip_1", "interface": "eth2", "defaultGateway": "false" },
+ { "name": "$onap_private_net", "ipAddress": "$vfw_private_ip_2", "interface": "eth3" , "defaultGateway": "false"}
]'
kubernetes.io/target-runtime: virtlet.cloud
spec:
@@ -463,9 +490,10 @@ spec:
app: vFirewall
context: darkstat
annotations:
- k8s.v1.cni.cncf.io/networks: '[
- { "name": "protected-private-net-cidr", "interfaceRequest": "eth1" },
- { "name": "onap-private-net-cidr", "interfaceRequest": "eth2" }
+ k8s.v1.cni.cncf.io/networks: '[{ "name": "$ovn_multus_network_name"}]'
+ ovnNetwork: '[
+ { "name": "$protected_private_net", "ipAddress": "$vsn_private_ip_0", "interface": "eth1", "defaultGateway": "false" },
+ { "name": "$onap_private_net", "ipAddress": "$vsn_private_ip_1", "interface": "eth2" , "defaultGateway": "false"}
]'
spec:
containers:
@@ -499,65 +527,74 @@ function populate_CSAR_vms_vFW {
cat << META > metadata.yaml
resources:
network:
- - unprotected-private-net-cidr-network.yaml
- - protected-private-net-cidr-network.yaml
- - onap-private-net-cidr-network.yaml
+ - $unprotected_private_net.yaml
+ - $protected_private_net.yaml
+ - $onap_private_net.yaml
deployment:
- $packetgen_deployment_name.yaml
- $firewall_deployment_name.yaml
- $sink_deployment_name.yaml
META
- cat << NET > unprotected-private-net-cidr-network.yaml
+ cat << NET > $unprotected_private_net.yaml
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
- name: unprotected-private-net-cidr
+ name: $unprotected_private_net
spec:
config: '{
"name": "unprotected",
"type": "bridge",
"ipam": {
"type": "host-local",
- "subnet": "192.168.10.0/24"
+ "subnet": "$protected_private_net_cidr"
}
}'
NET
- cat << NET > protected-private-net-cidr-network.yaml
+ cat << NET > $protected_private_net.yaml
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
- name: protected-private-net-cidr
+ name: $protected_private_net
spec:
config: '{
"name": "protected",
"type": "bridge",
"ipam": {
"type": "host-local",
- "subnet": "192.168.20.0/24"
+ "subnet": "$protected_net_cidr"
}
}'
NET
- cat << NET > onap-private-net-cidr-network.yaml
+ cat << NET > $onap_private_net.yaml
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
- name: onap-private-net-cidr
+ name: $onap_private_net
spec:
config: '{
"name": "onap",
"type": "bridge",
"ipam": {
"type": "host-local",
- "subnet": "10.10.0.0/16"
+ "subnet": "$onap_private_net_cidr"
}
}'
NET
proxy="apt:"
- cloud_init_proxy=""
+ cloud_init_proxy="
+ - export demo_artifacts_version=$demo_artifacts_version
+ - export vfw_private_ip_0=$vfw_private_ip_0
+ - export vsn_private_ip_0=$vsn_private_ip_0
+ - export protected_net_cidr=$protected_net_cidr
+ - export dcae_collector_ip=$dcae_collector_ip
+ - export dcae_collector_port=$dcae_collector_port
+ - export protected_net_gw=$protected_net_gw
+ - export protected_private_net_cidr=$protected_private_net_cidr
+"
if [[ -n "${http_proxy+x}" ]]; then
proxy+="
http_proxy: $http_proxy"
@@ -615,8 +652,8 @@ spec:
$ssh_key
VirtletRootVolumeSize: 5Gi
k8s.v1.cni.cncf.io/networks: '[
- { "name": "unprotected-private-net-cidr", "interfaceRequest": "eth1" },
- { "name": "onap-private-net-cidr", "interfaceRequest": "eth2" }
+ { "name": "$unprotected_private_net", "interfaceRequest": "eth1" },
+ { "name": "$onap_private_net", "interfaceRequest": "eth2" }
]'
kubernetes.io/target-runtime: virtlet.cloud
spec:
@@ -682,9 +719,9 @@ spec:
$ssh_key
VirtletRootVolumeSize: 5Gi
k8s.v1.cni.cncf.io/networks: '[
- { "name": "unprotected-private-net-cidr", "interfaceRequest": "eth1" },
- { "name": "protected-private-net-cidr", "interfaceRequest": "eth2" },
- { "name": "onap-private-net-cidr", "interfaceRequest": "eth3" }
+ { "name": "$unprotected_private_net", "interfaceRequest": "eth1" },
+ { "name": "$protected_private_net", "interfaceRequest": "eth2" },
+ { "name": "$onap_private_net", "interfaceRequest": "eth3" }
]'
kubernetes.io/target-runtime: virtlet.cloud
spec:
@@ -748,8 +785,8 @@ spec:
$ssh_key
VirtletRootVolumeSize: 5Gi
k8s.v1.cni.cncf.io/networks: '[
- { "name": "protected-private-net-cidr", "interfaceRequest": "eth1" },
- { "name": "onap-private-net-cidr", "interfaceRequest": "eth2" }
+ { "name": "$protected_private_net", "interfaceRequest": "eth1" },
+ { "name": "$onap_private_net", "interfaceRequest": "eth2" }
]'
kubernetes.io/target-runtime: virtlet.cloud
spec:
@@ -805,7 +842,7 @@ spec:
"type": "bridge",
"ipam": {
"type": "host-local",
- "subnet": "10.10.0.0/16"
+ "subnet": "$onap_private_net_cidr"
}
}'
NET
@@ -972,3 +1009,88 @@ SERVICE
popd
}
+# populate_CSAR_ovn4nfv() - Create content used for OVN4NFV functional test
+function populate_CSAR_ovn4nfv {
+ local csar_id=$1
+
+ _checks_args $csar_id
+ pushd ${CSAR_DIR}/${csar_id}
+
+ cat << META > metadata.yaml
+resources:
+ onap_network:
+ - ovn-port-net.yaml
+ - ovn-priv-net.yaml
+ network:
+ - onap-ovn4nfvk8s-network.yaml
+ deployment:
+ - $ovn4nfv_deployment_name.yaml
+META
+
+ cat << MULTUS_NET > onap-ovn4nfvk8s-network.yaml
+apiVersion: "k8s.cni.cncf.io/v1"
+kind: NetworkAttachmentDefinition
+metadata:
+ name: $ovn_multus_network_name
+spec:
+ config: '{
+ "cniVersion": "0.3.1",
+ "name": "ovn4nfv-k8s-plugin",
+ "type": "ovn4nfvk8s-cni"
+ }'
+MULTUS_NET
+
+ cat << NETWORK > ovn-port-net.yaml
+apiVersion: v1
+kind: onapNetwork
+metadata:
+ name: ovn-port-net
+ cnitype : ovn4nfvk8s
+spec:
+ name: ovn-port-net
+ subnet: 172.16.33.0/24
+ gateway: 172.16.33.1/24
+NETWORK
+
+ cat << NETWORK > ovn-priv-net.yaml
+apiVersion: v1
+kind: onapNetwork
+metadata:
+ name: ovn-priv-net
+ cnitype : ovn4nfvk8s
+spec:
+ name: ovn-priv-net
+ subnet: 172.16.44.0/24
+ gateway: 172.16.44.1/24
+NETWORK
+
+ cat << DEPLOYMENT > $ovn4nfv_deployment_name.yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: $ovn4nfv_deployment_name
+ labels:
+ app: ovn4nfv
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: ovn4nfv
+ template:
+ metadata:
+ labels:
+ app: ovn4nfv
+ annotations:
+ k8s.v1.cni.cncf.io/networks: '[{ "name": "$ovn_multus_network_name"}]'
+ ovnNetwork: '[{ "name": "ovn-port-net", "interface": "net0" , "defaultGateway": "false"},
+ { "name": "ovn-priv-net", "interface": "net1" , "defaultGateway": "false"}]'
+ spec:
+ containers:
+ - name: $ovn4nfv_deployment_name
+ image: "busybox"
+ command: ["top"]
+ stdin: true
+ tty: true
+DEPLOYMENT
+ popd
+}
diff --git a/vagrant/tests/_functions.sh b/vagrant/tests/_functions.sh
index c359e729..fe69b07b 100755
--- a/vagrant/tests/_functions.sh
+++ b/vagrant/tests/_functions.sh
@@ -12,6 +12,66 @@ set -o errexit
set -o nounset
set -o pipefail
+function _get_ovn_central_address {
+ ansible_ifconfig=$(ansible ovn-central[0] -i $test_folder/../inventory/hosts.ini -m shell -a "ifconfig eth1 |grep \"inet addr\" |awk '{print \$2}' |awk -F: '{print \$2}'")
+ if [[ $ansible_ifconfig != *CHANGED* ]]; then
+ echo "Fail to get the OVN central IP address from eth1 nic"
+ exit
+ fi
+ echo "$(echo ${ansible_ifconfig#*>>} | tr '\n' ':')6641"
+}
+
+# install_ovn_deps() - Install dependencies required for tests that require OVN
+function install_ovn_deps {
+ if ! $(yq --version &>/dev/null); then
+ sudo -E pip install yq
+ fi
+ if ! $(ovn-nbctl --version &>/dev/null); then
+ source /etc/os-release || source /usr/lib/os-release
+ case ${ID,,} in
+ *suse)
+ ;;
+ ubuntu|debian)
+ sudo apt-get install -y apt-transport-https
+ echo "deb https://packages.wand.net.nz $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/wand.list
+ sudo curl https://packages.wand.net.nz/keyring.gpg -o /etc/apt/trusted.gpg.d/wand.gpg
+ sudo apt-get update
+ sudo apt install -y ovn-common
+ ;;
+ rhel|centos|fedora)
+ ;;
+ esac
+ fi
+}
+
+# init_network() - This function creates the OVN resouces required by the test
+function init_network {
+ local fname=$1
+ local router_name="ovn4nfv-master"
+
+ name=$(cat $fname | yq '.spec.name' | xargs)
+ subnet=$(cat $fname | yq '.spec.subnet' | xargs)
+ gateway=$(cat $fname | yq '.spec.gateway' | xargs)
+ ovn_central_address=$(_get_ovn_central_address)
+
+ router_mac=$(printf '00:00:00:%02X:%02X:%02X' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)))
+ ovn-nbctl --may-exist --db tcp:$ovn_central_address ls-add $name -- set logical_switch $name other-config:subnet=$subnet external-ids:gateway_ip=$gateway
+ ovn-nbctl --may-exist --db tcp:$ovn_central_address lrp-add $router_name rtos-$name $router_mac $gateway
+ ovn-nbctl --may-exist --db tcp:$ovn_central_address lsp-add $name stor-$name -- set logical_switch_port stor-$name type=router options:router-port=rtos-$name addresses=\"$router_mac\"
+}
+
+# cleanup_network() - This function removes the OVN resources created for the test
+function cleanup_network {
+ local fname=$1
+
+ name=$(cat $fname | yq '.spec.name' | xargs)
+ ovn_central_address=$(_get_ovn_central_address)
+
+ for cmd in "ls-del $name" "lrp-del rtos-$name" "lsp-del stor-$name"; do
+ ovn-nbctl --if-exist --db tcp:$ovn_central_address $cmd
+ done
+}
+
function _checks_args {
if [[ -z $1 ]]; then
echo "Missing CSAR ID argument"
@@ -67,7 +127,7 @@ function setup {
for deployment_name in $@; do
recreate_deployment $deployment_name
done
-
+ sleep 5
for deployment_name in $@; do
wait_deployment $deployment_name
done
diff --git a/vagrant/tests/integration_cFW.sh b/vagrant/tests/integration_cFW.sh
index 0077c73d..92c280b9 100755
--- a/vagrant/tests/integration_cFW.sh
+++ b/vagrant/tests/integration_cFW.sh
@@ -21,8 +21,8 @@ csar_id=4f726e2a-b74a-11e8-ad7c-525400feed2
populate_CSAR_containers_vFW $csar_id
pushd ${CSAR_DIR}/${csar_id}
-for network in unprotected-private-net-cidr-network protected-private-net-cidr-network onap-private-net-cidr-network; do
- kubectl apply -f $network.yaml
+for resource in $unprotected_private_net $protected_private_net $onap_private_net; do
+ kubectl apply -f $resource.yaml
done
setup $packetgen_deployment_name $firewall_deployment_name $sink_deployment_name
diff --git a/vagrant/tests/integration_vFW.sh b/vagrant/tests/integration_vFW.sh
index e0f7075e..962f9f75 100755
--- a/vagrant/tests/integration_vFW.sh
+++ b/vagrant/tests/integration_vFW.sh
@@ -24,7 +24,7 @@ fi
populate_CSAR_vms_vFW $csar_id
pushd ${CSAR_DIR}/${csar_id}
-for resource in unprotected-private-net-cidr-network protected-private-net-cidr-network onap-private-net-cidr-network; do
+for resource in $unprotected_private_net $protected_private_net $onap_private_net; do
kubectl apply -f $resource.yaml
done
setup $packetgen_deployment_name $firewall_deployment_name $sink_deployment_name
diff --git a/vagrant/tests/integration_vcFW.sh b/vagrant/tests/integration_vcFW.sh
index 4fadfa23..15cffcb8 100755
--- a/vagrant/tests/integration_vcFW.sh
+++ b/vagrant/tests/integration_vcFW.sh
@@ -18,13 +18,19 @@ source _functions.sh
csar_id=aa443e7e-c8ba-11e8-8877-525400b164ff
# Setup
+install_ovn_deps
if [[ ! -f $HOME/.ssh/id_rsa.pub ]]; then
echo -e "\n\n\n" | ssh-keygen -t rsa -N ""
fi
populate_CSAR_vms_containers_vFW $csar_id
pushd ${CSAR_DIR}/${csar_id}
-for resource in unprotected-private-net-cidr-network protected-private-net-cidr-network onap-private-net-cidr-network sink-service sink-ingress; do
+for net in $unprotected_private_net $protected_private_net $onap_private_net; do
+ cleanup_network $net.yaml
+ echo "Create OVN Network $net network"
+ init_network $net.yaml
+done
+for resource in onap-ovn4nfvk8s-network sink-service; do
kubectl apply -f $resource.yaml
done
setup $packetgen_deployment_name $firewall_deployment_name $sink_deployment_name
@@ -40,7 +46,10 @@ for deployment_name in $packetgen_deployment_name $firewall_deployment_name; do
echo "=== Virtlet details ===="
echo "$(kubectl plugin virt virsh dumpxml $vm | grep VIRTLET_)\n"
done
-popd
# Teardown
#teardown $packetgen_deployment_name $firewall_deployment_name $sink_deployment_name
+#for net in $unprotected_private_net $protected_private_net $onap_private_net; do
+# cleanup_network $net.yaml
+#done
+popd
diff --git a/vagrant/tests/ovn4nfv.sh b/vagrant/tests/ovn4nfv.sh
new file mode 100755
index 00000000..37fddfd8
--- /dev/null
+++ b/vagrant/tests/ovn4nfv.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+##############################################################################
+# Copyright (c) 2018
+# 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
+##############################################################################
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+source _common.sh
+source _functions.sh
+
+csar_id=a1c5b53e-d7ab-11e8-85b7-525400e8c29a
+
+# Setup
+install_ovn_deps
+populate_CSAR_ovn4nfv $csar_id
+
+pushd ${CSAR_DIR}/${csar_id}
+for net in ovn-priv-net ovn-port-net; do
+ cleanup_network $net.yaml
+ echo "Create OVN Network $net network"
+ init_network $net.yaml
+done
+kubectl apply -f onap-ovn4nfvk8s-network.yaml
+setup $ovn4nfv_deployment_name
+
+# Test
+deployment_pod=$(kubectl get pods | grep $ovn4nfv_deployment_name | awk '{print $1}')
+echo "===== $deployment_pod details ====="
+kubectl exec -it $deployment_pod -- ip a
+multus_nic=$(kubectl exec -it $deployment_pod -- ifconfig | grep "net1")
+if [ -z "$multus_nic" ]; then
+ echo "The $deployment_pod pod doesn't contain the net1 nic"
+ exit 1
+fi
+
+# Teardown
+teardown $ovn4nfv_deployment_name
+cleanup_network ovn-priv-net.yaml
+cleanup_network ovn-port-net.yaml
+popd
diff --git a/vagrant/tests/plugin.sh b/vagrant/tests/plugin.sh
index 16d8d306..55be1686 100755
--- a/vagrant/tests/plugin.sh
+++ b/vagrant/tests/plugin.sh
@@ -88,7 +88,7 @@ echo "VNF details $vnf_details"
echo "Deleting $vnf_id VNF Instance"
curl -X DELETE "${base_url}${cloud_region_id}/${namespace}/${vnf_id}"
-if [[ -n $(curl -s -X GET "${base_url}${cloud_region_id}/${namespace}/${vnf_id}") ]]; then
+if [[ 200 -eq $(curl -o /dev/null -w %{http_code} -s -X GET "${base_url}${cloud_region_id}/${namespace}/${vnf_id}") ]]; then
echo "VNF Instance not deleted"
exit 1
fi