summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.readthedocs.yaml20
-rw-r--r--docs/.gitignore3
-rw-r--r--docs/_static/css/ribbon.css63
-rwxr-xr-xdocs/_static/favicon.icobin0 -> 2102 bytes
-rw-r--r--docs/_static/logo_onap_2017.pngbin0 -> 12278 bytes
-rw-r--r--docs/conf.py43
-rw-r--r--docs/conf.yaml7
-rw-r--r--docs/requirements-docs.txt15
-rw-r--r--docs/tox.ini22
-rw-r--r--kud/deployment_infra/images/nfn.yml228
-rw-r--r--kud/deployment_infra/playbooks/configure-ovn.yml8
-rw-r--r--kud/tests/ovn4nfv_pn_test.yml51
-rw-r--r--src/k8splugin/api/brokerhandler.go139
-rw-r--r--src/k8splugin/api/brokerhandler_test.go209
-rw-r--r--src/k8splugin/internal/helm/helm_test.go11
-rw-r--r--src/orchestrator/api/api.go29
-rw-r--r--src/orchestrator/api/composite_app_handler.go113
-rw-r--r--src/orchestrator/api/projecthandler.go16
-rw-r--r--src/orchestrator/api/projecthandler_test.go92
-rw-r--r--src/orchestrator/cmd/main.go16
-rw-r--r--src/orchestrator/examples/example_module.go48
-rw-r--r--src/orchestrator/go.sum1
-rw-r--r--src/orchestrator/pkg/infra/auth/auth.go (renamed from src/orchestrator/internal/auth/auth.go)0
-rw-r--r--src/orchestrator/pkg/infra/auth/auth_test.go (renamed from src/orchestrator/internal/auth/auth_test.go)6
-rw-r--r--src/orchestrator/pkg/infra/config/config.go (renamed from src/orchestrator/internal/config/config.go)6
-rw-r--r--src/orchestrator/pkg/infra/config/config_test.go (renamed from src/orchestrator/internal/config/config_test.go)2
-rw-r--r--src/orchestrator/pkg/infra/contextdb/contextdb.go72
-rw-r--r--src/orchestrator/pkg/infra/contextdb/etcd.go175
-rw-r--r--src/orchestrator/pkg/infra/contextdb/etcd_test.go276
-rw-r--r--src/orchestrator/pkg/infra/contextdb/mock.go58
-rw-r--r--src/orchestrator/pkg/infra/db/README.md (renamed from src/orchestrator/internal/db/README.md)0
-rw-r--r--src/orchestrator/pkg/infra/db/mock.go (renamed from src/orchestrator/internal/db/mock.go)0
-rw-r--r--src/orchestrator/pkg/infra/db/mongo.go (renamed from src/orchestrator/internal/db/mongo.go)2
-rw-r--r--src/orchestrator/pkg/infra/db/mongo_test.go (renamed from src/orchestrator/internal/db/mongo_test.go)0
-rw-r--r--src/orchestrator/pkg/infra/db/store.go (renamed from src/orchestrator/internal/db/store.go)2
-rw-r--r--src/orchestrator/pkg/infra/db/store_test.go (renamed from src/orchestrator/internal/db/store_test.go)0
-rw-r--r--src/orchestrator/pkg/infra/logutils/logger.go (renamed from src/orchestrator/internal/logutils/logger.go)0
-rw-r--r--src/orchestrator/pkg/module/compositeapp.go158
-rw-r--r--src/orchestrator/pkg/module/module.go33
-rw-r--r--src/orchestrator/pkg/module/project.go (renamed from src/orchestrator/internal/project/project.go)51
-rw-r--r--src/orchestrator/pkg/module/project_test.go (renamed from src/orchestrator/internal/project/project_test.go)45
-rw-r--r--src/orchestrator/scripts/Dockerfile30
-rwxr-xr-xsrc/orchestrator/scripts/_functions.sh49
-rwxr-xr-xsrc/orchestrator/scripts/build.sh68
-rw-r--r--src/orchestrator/scripts/docker-compose.yml57
-rwxr-xr-xsrc/orchestrator/scripts/start-dev.sh32
-rwxr-xr-xsrc/orchestrator/scripts/start-docker.sh24
47 files changed, 2055 insertions, 225 deletions
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644
index 00000000..3797dc8b
--- /dev/null
+++ b/.readthedocs.yaml
@@ -0,0 +1,20 @@
+---
+# .readthedocs.yml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+# Required
+version: 2
+
+formats:
+ - htmlzip
+
+build:
+ image: latest
+
+python:
+ version: 3.7
+ install:
+ - requirements: docs/requirements-docs.txt
+
+sphinx:
+ configuration: docs/conf.py
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 00000000..43ca5b67
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,3 @@
+/.tox
+/_build/*
+/__pycache__/*
diff --git a/docs/_static/css/ribbon.css b/docs/_static/css/ribbon.css
new file mode 100644
index 00000000..6008cb1a
--- /dev/null
+++ b/docs/_static/css/ribbon.css
@@ -0,0 +1,63 @@
+.ribbon {
+ z-index: 1000;
+ background-color: #a00;
+ overflow: hidden;
+ white-space: nowrap;
+ position: fixed;
+ top: 25px;
+ right: -50px;
+ -webkit-transform: rotate(45deg);
+ -moz-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ -o-transform: rotate(45deg);
+ transform: rotate(45deg);
+ -webkit-box-shadow: 0 0 10px #888;
+ -moz-box-shadow: 0 0 10px #888;
+ box-shadow: 0 0 10px #888;
+
+}
+
+.ribbon a {
+ border: 1px solid #faa;
+ color: #fff;
+ display: block;
+ font: bold 81.25% 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ margin: 1px 0;
+ padding: 10px 50px;
+ text-align: center;
+ text-decoration: none;
+ text-shadow: 0 0 5px #444;
+ transition: 0.5s;
+}
+
+.ribbon a:hover {
+ background: #c11;
+ color: #fff;
+}
+
+
+/* override table width restrictions */
+@media screen and (min-width: 767px) {
+
+ .wy-table-responsive table td, .wy-table-responsive table th {
+ /* !important prevents the common CSS stylesheets from overriding
+ this as on RTD they are loaded after this stylesheet */
+ white-space: normal !important;
+ }
+
+ .wy-table-responsive {
+ overflow: visible !important;
+ }
+}
+
+@media screen and (max-width: 767px) {
+ .wy-table-responsive table td {
+ white-space: nowrap;
+ }
+}
+
+/* fix width of the screen */
+
+.wy-nav-content {
+ max-width: none;
+}
diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico
new file mode 100755
index 00000000..cb712ebd
--- /dev/null
+++ b/docs/_static/favicon.ico
Binary files differ
diff --git a/docs/_static/logo_onap_2017.png b/docs/_static/logo_onap_2017.png
new file mode 100644
index 00000000..5d064f43
--- /dev/null
+++ b/docs/_static/logo_onap_2017.png
Binary files differ
diff --git a/docs/conf.py b/docs/conf.py
index 2e30879a..8f40e8b8 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,40 +1,15 @@
-# -*- coding: utf-8 -*-
-# 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.
+from docs_conf.conf import *
-import os
-import sys
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", ".."))
-
-sys.path.insert(0, ROOT)
-sys.path.insert(0, BASE_DIR)
-
-# -- General configuration ----------------------------------------------------
+branch = 'latest'
+master_doc = 'index'
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = [
- 'sphinx.ext.autodoc',
- #'sphinx.ext.intersphinx'
+linkcheck_ignore = [
+ 'http://localhost',
]
-# The suffix of source filenames.
-source_suffix = '.rst'
+intersphinx_mapping = {}
-# The master toctree document.
-master_doc = 'index'
+html_last_updated_fmt = '%d-%b-%y %H:%M'
-# General information about the project.
-project = u'MultiCloud Kubernetes Plugin'
+def setup(app):
+ app.add_stylesheet("css/ribbon_onap.css")
diff --git a/docs/conf.yaml b/docs/conf.yaml
new file mode 100644
index 00000000..ab592813
--- /dev/null
+++ b/docs/conf.yaml
@@ -0,0 +1,7 @@
+---
+project_cfg: onap
+project: onap
+
+# Change this to ReleaseBranchName to modify the header
+default-version: latest
+#
diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt
new file mode 100644
index 00000000..b3188ddd
--- /dev/null
+++ b/docs/requirements-docs.txt
@@ -0,0 +1,15 @@
+tox
+Sphinx
+doc8
+docutils
+setuptools
+six
+sphinx_rtd_theme>=0.4.3
+sphinxcontrib-blockdiag
+sphinxcontrib-needs>=0.2.3
+sphinxcontrib-nwdiag
+sphinxcontrib-seqdiag
+sphinxcontrib-swaggerdoc
+sphinxcontrib-plantuml
+sphinx_bootstrap_theme
+lfdocs-conf
diff --git a/docs/tox.ini b/docs/tox.ini
new file mode 100644
index 00000000..edac8c35
--- /dev/null
+++ b/docs/tox.ini
@@ -0,0 +1,22 @@
+[tox]
+minversion = 1.6
+envlist = docs,
+skipsdist = true
+
+[testenv:docs]
+basepython = python3
+deps = -r{toxinidir}/requirements-docs.txt
+commands =
+ sphinx-build -b html -n -d {envtmpdir}/doctrees ./ {toxinidir}/_build/html
+ echo "Generated docs available in {toxinidir}/_build/html"
+whitelist_externals =
+ echo
+ git
+ sh
+
+[testenv:docs-linkcheck]
+basepython = python3
+#deps = -r{toxinidir}/requirements-docs.txt
+commands = echo "Link Checking not enforced"
+#commands = sphinx-build -b linkcheck -d {envtmpdir}/doctrees ./ {toxinidir}/_build/linkcheck
+whitelist_externals = echo
diff --git a/kud/deployment_infra/images/nfn.yml b/kud/deployment_infra/images/nfn.yml
index 6e583b95..23d8773a 100644
--- a/kud/deployment_infra/images/nfn.yml
+++ b/kud/deployment_infra/images/nfn.yml
@@ -119,6 +119,156 @@ spec:
served: true
storage: true
+
+---
+
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ name: providernetworks.k8s.plugin.opnfv.org
+spec:
+ group: k8s.plugin.opnfv.org
+ names:
+ kind: ProviderNetwork
+ listKind: ProviderNetworkList
+ plural: providernetworks
+ singular: providernetwork
+ scope: Namespaced
+ subresources:
+ status: {}
+ validation:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ cniType:
+ description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
+ Important: Run "operator-sdk generate k8s" to regenerate code after
+ modifying this file Add custom validation using kubebuilder tags:
+ https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html'
+ type: string
+ dns:
+ properties:
+ domain:
+ type: string
+ nameservers:
+ items:
+ type: string
+ type: array
+ options:
+ items:
+ type: string
+ type: array
+ search:
+ items:
+ type: string
+ type: array
+ type: object
+ ipv4Subnets:
+ items:
+ properties:
+ excludeIps:
+ type: string
+ gateway:
+ type: string
+ name:
+ type: string
+ subnet:
+ type: string
+ required:
+ - name
+ - subnet
+ type: object
+ type: array
+ ipv6Subnets:
+ items:
+ properties:
+ excludeIps:
+ type: string
+ gateway:
+ type: string
+ name:
+ type: string
+ subnet:
+ type: string
+ required:
+ - name
+ - subnet
+ type: object
+ type: array
+ providerNetType:
+ type: string
+ routes:
+ items:
+ properties:
+ dst:
+ type: string
+ gw:
+ type: string
+ required:
+ - dst
+ type: object
+ type: array
+ vlan:
+ properties:
+ logicalInterfaceName:
+ type: string
+ nodeLabelList:
+ description: '"all"/"any"(in which case a node will be randomly
+ selected)/"specific"(see below)'
+ items:
+ type: string
+ type: array
+ providerInterfaceName:
+ description: if VlanNodeSelector is value "specific" then this array
+ provides a list of nodes labels
+ type: string
+ vlanId:
+ type: string
+ vlanNodeSelector:
+ type: string
+ required:
+ - vlanId
+ - vlanNodeSelector
+ - providerInterfaceName
+ type: object
+ required:
+ - cniType
+ - ipv4Subnets
+ - providerNetType
+ - vlan
+ type: object
+ status:
+ properties:
+ state:
+ description: 'INSERT ADDITIONAL STATUS FIELD - define observed state
+ of cluster Important: Run "operator-sdk generate k8s" to regenerate
+ code after modifying this file Add custom validation using kubebuilder
+ tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html'
+ type: string
+ required:
+ - state
+ type: object
+ version: v1alpha1
+ versions:
+ - name: v1alpha1
+ served: true
+ storage: true
+
+
+
---
apiVersion: v1
@@ -145,6 +295,7 @@ rules:
- events
- configmaps
- secrets
+ - nodes
verbs:
- '*'
- apiGroups:
@@ -194,6 +345,24 @@ roleRef:
name: k8s-nfn-cr
apiGroup: rbac.authorization.k8s.io
+
+---
+
+apiVersion: v1
+kind: Service
+metadata:
+ name: nfn-operator
+ namespace: operator
+spec:
+ type: NodePort
+ ports:
+ - port: 50000
+ protocol: TCP
+ targetPort: 50000
+ selector:
+ name: nfn-operator
+
+
---
apiVersion: apps/v1
@@ -227,9 +396,12 @@ spec:
serviceAccountName: k8s-nfn-sa
containers:
- name: nfn-operator
- image: rtsood/nfn-operator:latest
+ image: rtsood/nfn-operator:0.4
command: ["/usr/local/bin/entrypoint", "operator"]
imagePullPolicy: IfNotPresent
+ ports:
+ - containerPort: 50000
+ protocol: TCP
env:
- name: HOST_IP
valueFrom:
@@ -287,7 +459,7 @@ spec:
effect: NoSchedule
containers:
- name: ovn4nfv
- image: rtsood/nfn-operator:latest
+ image: rtsood/nfn-operator:0.4
command: ["/usr/local/bin/entrypoint", "cni"]
resources:
requests:
@@ -319,4 +491,56 @@ spec:
- key: ovn4nfv_k8s.conf
path: ovn4nfv_k8s.conf
+---
+apiVersion: extensions/v1beta1
+kind: DaemonSet
+metadata:
+ name: nfn-agent
+ namespace: operator
+ labels:
+ app: nfn-agent
+spec:
+ updateStrategy:
+ type: RollingUpdate
+ template:
+ metadata:
+ labels:
+ app: nfn-agent
+ spec:
+ hostNetwork: true
+ nodeSelector:
+ beta.kubernetes.io/arch: amd64
+ tolerations:
+ - operator: Exists
+ effect: NoSchedule
+ containers:
+ - name: nfn-agent
+ image: rtsood/nfn-operator:0.4
+ command: ["/usr/local/bin/entrypoint", "agent"]
+ resources:
+ requests:
+ cpu: "100m"
+ memory: "50Mi"
+ limits:
+ cpu: "100m"
+ memory: "50Mi"
+ env:
+ - name: NFN_NODE_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: spec.nodeName
+ securityContext:
+ privileged: true
+ volumeMounts:
+ - mountPath: /run/openvswitch
+ name: host-run-ovs
+ - mountPath: /var/run/openvswitch
+ name: host-var-run-ovs
+ volumes:
+ - name: host-run-ovs
+ hostPath:
+ path: /run/openvswitch
+ - name: host-var-run-ovs
+ hostPath:
+ path: /var/run/openvswitch
diff --git a/kud/deployment_infra/playbooks/configure-ovn.yml b/kud/deployment_infra/playbooks/configure-ovn.yml
index 28de6e94..fc4c21a4 100644
--- a/kud/deployment_infra/playbooks/configure-ovn.yml
+++ b/kud/deployment_infra/playbooks/configure-ovn.yml
@@ -15,6 +15,14 @@
file: "{{ item }}"
with_items:
- "{{ ansible_os_family }}.yml"
+ - name: get Wand GPI files
+ get_url:
+ url: https://packages.wand.net.nz/keyring.gpg
+ dest: /etc/apt/trusted.gpg.d/wand.gpg
+ - name: add WAND Debian Repo
+ apt_repository:
+ repo: "deb https://packages.wand.net.nz {{ ansible_lsb.codename }} ovs-2.10"
+ state: present
- name: install OpenVSwitch packages
package:
name: "{{ item }}"
diff --git a/kud/tests/ovn4nfv_pn_test.yml b/kud/tests/ovn4nfv_pn_test.yml
new file mode 100644
index 00000000..0d02fad5
--- /dev/null
+++ b/kud/tests/ovn4nfv_pn_test.yml
@@ -0,0 +1,51 @@
+apiVersion: k8s.plugin.opnfv.org/v1alpha1
+kind: ProviderNetwork
+metadata:
+ name: pnetwork
+spec:
+ cniType: ovn4nfv
+ ipv4Subnets:
+ - subnet: 172.16.33.0/24
+ name: subnet1
+ gateway: 172.16.33.1/24
+ excludeIps: 172.16.33.2 172.16.33.5..172.16.33.10
+ providerNetType: VLAN
+ vlan:
+ vlanId: "100"
+ providerInterfaceName: eth1
+ logicalInterfaceName: eth1.100
+ vlanNodeSelector: specific
+ nodeLabelList:
+ - kubernetes.io/hostname=localhost
+
+---
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: pnw
+ labels:
+ app: pnw
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: pnw
+ template:
+ metadata:
+ labels:
+ app: pnw
+ annotations:
+ k8s.v1.cni.cncf.io/networks: '[{ "name": "ovn-networkobj"}]'
+ k8s.plugin.opnfv.org/nfn-network: '{ "type": "ovn4nfv", "interface": [{ "name": "pnetwork", "interface": "net0" }]}'
+
+ spec:
+ containers:
+ - name: pnw
+ image: "busybox"
+ imagePullPolicy: Always
+ stdin: true
+ tty: true
+ securityContext:
+ privileged: true
+
diff --git a/src/k8splugin/api/brokerhandler.go b/src/k8splugin/api/brokerhandler.go
index 7671db44..c98e1c48 100644
--- a/src/k8splugin/api/brokerhandler.go
+++ b/src/k8splugin/api/brokerhandler.go
@@ -16,11 +16,11 @@ package api
import (
"encoding/json"
"io"
- "log"
"net/http"
"github.com/onap/multicloud-k8s/src/k8splugin/internal/app"
"github.com/onap/multicloud-k8s/src/k8splugin/internal/helm"
+ log "github.com/onap/multicloud-k8s/src/k8splugin/internal/logutils"
"github.com/gorilla/mux"
)
@@ -33,16 +33,34 @@ type brokerInstanceHandler struct {
}
type brokerRequest struct {
- GenericVnfID string `json:"generic-vnf-id"`
- VFModuleID string `json:"vf-module-id"`
- VFModuleModelInvariantID string `json:"vf-module-model-invariant-id"`
- VFModuleModelVersionID string `json:"vf-module-model-version-id"`
- VFModuleModelCustomizationID string `json:"vf-module-model-customization-id"`
- OOFDirectives map[string]interface{} `json:"oof_directives"`
- SDNCDirectives map[string]interface{} `json:"sdnc_directives"`
- UserDirectives map[string]interface{} `json:"user_directives"`
- TemplateType string `json:"template_type"`
- TemplateData map[string]interface{} `json:"template_data"`
+ GenericVnfID string `json:"generic-vnf-id"`
+ VFModuleID string `json:"vf-module-id"`
+ VFModuleModelInvariantID string `json:"vf-module-model-invariant-id"`
+ VFModuleModelVersionID string `json:"vf-module-model-version-id"`
+ VFModuleModelCustomizationID string `json:"vf-module-model-customization-id"`
+ OOFDirectives directive `json:"oof_directives"`
+ SDNCDirectives directive `json:"sdnc_directives"`
+ UserDirectives directive `json:"user_directives"`
+ TemplateType string `json:"template_type"`
+ TemplateData templateData `json:"template_data"`
+}
+
+type directive struct {
+ Attributes []attribute `json:"attributes"`
+}
+
+type attribute struct {
+ Key string `json:"attribute_name"`
+ Value string `json:"attribute_value"`
+}
+
+type templateData struct {
+ StackName string `json:"stack_name"` //Only this property is relevant (exported)
+ disableRollback string `json:"disable_rollback"`
+ environment string `json:"environment"`
+ parameters string `json:"parameters"`
+ template string `json:"template"`
+ timeoutMins string `json:"timeout_mins"`
}
type brokerPOSTResponse struct {
@@ -67,50 +85,16 @@ type brokerDELETEResponse struct {
WorkloadStatusReason map[string]interface{} `json:"workload_status_reason"`
}
-// getUserDirectiveValue parses the following kind of json
-// "user_attributes": {
-// "attributes": [
-// {
-// "attribute_value": "foo",
-// "attribute_name": "bar"
-// },
-// {
-// "attribute_value": "value2",
-// "attribute_name": "name2"
-// }
-// ]
-// }
-func (b brokerRequest) getAttributeValue(directives map[string]interface{}, inp string) string {
- attributes, ok := directives["attributes"].([]interface{})
- if !ok {
- log.Println("Unable to cast attributes to []interface{}")
- return ""
- }
-
- for _, value := range attributes {
-
- attribute, ok := value.(map[string]interface{})
- if !ok {
- log.Println("Unable to cast attribute to map[string]interface{}")
- return ""
- }
-
- attributeName, ok := attribute["attribute_name"].(string)
- if !ok {
- log.Println("Unable to cast attribute_name to string")
- return ""
- }
- if attributeName == inp {
- attributevalue, ok := attribute["attribute_value"].(string)
- if !ok {
- log.Println("Unable to cast attribute_value to string")
- return ""
- }
-
- return attributevalue
+// Convert directives stored in broker request to map[string]string format with
+// merge including precedence provided
+func (b brokerRequest) convertDirectives() map[string]string {
+ extractedAttributes := make(map[string]string)
+ for _, section := range [3]directive{b.SDNCDirectives, b.OOFDirectives, b.UserDirectives} {
+ for _, attribute := range section.Attributes {
+ extractedAttributes[attribute.Key] = attribute.Value
}
}
- return ""
+ return extractedAttributes
}
func (b brokerInstanceHandler) createHandler(w http.ResponseWriter, r *http.Request) {
@@ -119,6 +103,10 @@ func (b brokerInstanceHandler) createHandler(w http.ResponseWriter, r *http.Requ
var req brokerRequest
err := json.NewDecoder(r.Body).Decode(&req)
+ log.Info("Broker API Payload", log.Fields{
+ "Body": r.Body,
+ "Payload": req,
+ })
switch {
case err == io.EOF:
http.Error(w, "Body empty", http.StatusBadRequest)
@@ -144,15 +132,24 @@ func (b brokerInstanceHandler) createHandler(w http.ResponseWriter, r *http.Requ
return
}
- profileName := req.getAttributeValue(req.SDNCDirectives, "k8s-rb-profile-name")
- if profileName == "" {
- http.Error(w, "k8s-rb-profile-name is missing from sdnc-directives", http.StatusBadRequest)
+ if req.GenericVnfID == "" {
+ http.Error(w, "generic-vnf-id is empty", http.StatusBadRequest)
+ return
+ }
+ if req.VFModuleID == "" {
+ http.Error(w, "vf-module-id is empty", http.StatusBadRequest)
+ return
+ }
+
+ if req.TemplateData.StackName == "" {
+ http.Error(w, "stack_name is missing from template_data", http.StatusBadRequest)
return
}
- vfModuleName := req.getAttributeValue(req.SDNCDirectives, "vf_module_name")
- if vfModuleName == "" {
- http.Error(w, "vf_module_name is missing from sdnc-directives", http.StatusBadRequest)
+ directives := req.convertDirectives()
+ profileName, ok := directives["k8s-rb-profile-name"]
+ if !ok {
+ http.Error(w, "k8s-rb-profile-name is missing from directives", http.StatusBadRequest)
return
}
@@ -163,9 +160,13 @@ func (b brokerInstanceHandler) createHandler(w http.ResponseWriter, r *http.Requ
instReq.ProfileName = profileName
instReq.CloudRegion = cloudRegion
instReq.Labels = map[string]string{
- "vf_module_name": vfModuleName,
+ "stack-name": req.TemplateData.StackName,
}
+ instReq.OverrideValues = directives
+ log.Info("Instance API Payload", log.Fields{
+ "payload": instReq,
+ })
resp, err := b.client.Create(instReq)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -178,6 +179,9 @@ func (b brokerInstanceHandler) createHandler(w http.ResponseWriter, r *http.Requ
TemplateResponse: resp.Resources,
WorkloadStatus: "CREATE_COMPLETE",
}
+ log.Info("Broker API Response", log.Fields{
+ "response": brokerResp,
+ })
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
@@ -205,6 +209,9 @@ func (b brokerInstanceHandler) getHandler(w http.ResponseWriter, r *http.Request
WorkloadStatus: "CREATE_COMPLETE",
}
+ log.Info("Broker API Response", log.Fields{
+ "response": brokerResp,
+ })
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
err = json.NewEncoder(w).Encode(brokerResp)
@@ -214,12 +221,12 @@ func (b brokerInstanceHandler) getHandler(w http.ResponseWriter, r *http.Request
}
}
-// getHandler retrieves information about an instance via the ID
+// findHandler retrieves information about an instance via the ID
func (b brokerInstanceHandler) findHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
- //name is an alias for vf_module_name from the so adapter
+ //name is an alias for stack-name from the so adapter
name := vars["name"]
- responses, _ := b.client.Find("", "", "", map[string]string{"vf_module_name": name})
+ responses, _ := b.client.Find("", "", "", map[string]string{"stack-name": name})
brokerResp := brokerGETResponse{
TemplateType: "heat",
@@ -244,6 +251,9 @@ func (b brokerInstanceHandler) findHandler(w http.ResponseWriter, r *http.Reques
}
}
+ log.Info("Broker API Response", log.Fields{
+ "response": brokerResp,
+ })
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
err := json.NewEncoder(w).Encode(brokerResp)
@@ -269,6 +279,9 @@ func (b brokerInstanceHandler) deleteHandler(w http.ResponseWriter, r *http.Requ
WorkloadID: instanceID,
WorkloadStatus: "DELETE_COMPLETE",
}
+ log.Info("Broker API Response", log.Fields{
+ "response": brokerResp,
+ })
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
diff --git a/src/k8splugin/api/brokerhandler_test.go b/src/k8splugin/api/brokerhandler_test.go
index 83ff588b..c822f6d1 100644
--- a/src/k8splugin/api/brokerhandler_test.go
+++ b/src/k8splugin/api/brokerhandler_test.go
@@ -3,7 +3,7 @@ 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
+ 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.
@@ -21,6 +21,7 @@ import (
"net/http"
"net/http/httptest"
"reflect"
+ "strings"
"testing"
"github.com/onap/multicloud-k8s/src/k8splugin/internal/app"
@@ -30,13 +31,114 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
)
+func TestConvertDirectives(t *testing.T) {
+ testCases := []struct {
+ label string
+ input brokerRequest
+ expected map[string]string
+ }{
+ {
+ label: "Single variable",
+ expected: map[string]string{"test": "true"},
+ input: brokerRequest{SDNCDirectives: directive{[]attribute{{
+ Key: "test",
+ Value: "true",
+ }}}},
+ },
+ {
+ label: "Empty parameter",
+ expected: map[string]string{"test": ""},
+ input: brokerRequest{OOFDirectives: directive{[]attribute{{
+ Key: "test",
+ Value: "",
+ }}}},
+ },
+ {
+ label: "Null entry",
+ input: brokerRequest{},
+ expected: make(map[string]string),
+ },
+ {
+ label: "Complex helm overrides",
+ /*
+ String with int will be later treated as int in helm.TemplateClient
+ (see helm/pkg/strvals/parser.go)
+ If unsure, convert type in helm chart like `{{ toString $value }}` or `{{ int $value }}`
+ (see http://masterminds.github.io/sprig/conversion.html)
+ */
+ expected: map[string]string{"name": "{a, b, c}", "servers[0].port": "80"},
+ input: brokerRequest{UserDirectives: directive{[]attribute{
+ {
+ Key: "name",
+ Value: "{a, b, c}",
+ },
+ {
+ Key: "servers[0].port",
+ Value: "80",
+ },
+ }}},
+ },
+ {
+ label: "Override variables",
+ expected: map[string]string{"empty": "", "sdnc": "sdnc", "user": "user", "oof": "oof"},
+ input: brokerRequest{
+ SDNCDirectives: directive{[]attribute{
+ {
+ Key: "empty",
+ Value: "sdnc",
+ },
+ {
+ Key: "sdnc",
+ Value: "sdnc",
+ },
+ {
+ Key: "oof",
+ Value: "sdnc",
+ },
+ }},
+ OOFDirectives: directive{[]attribute{
+ {
+ Key: "oof",
+ Value: "oof",
+ },
+ {
+ Key: "user",
+ Value: "oof",
+ },
+ }},
+ UserDirectives: directive{[]attribute{
+ {
+ Key: "user",
+ Value: "user",
+ },
+ {
+ Key: "empty",
+ Value: "",
+ },
+ }},
+ },
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ result := testCase.input.convertDirectives()
+ if !reflect.DeepEqual(result, testCase.expected) {
+ t.Fatalf("Unexpected result. Wanted '%v', retrieved '%v'",
+ testCase.expected, result)
+ }
+ })
+ }
+}
+
func TestBrokerCreateHandler(t *testing.T) {
testCases := []struct {
- label string
- input io.Reader
- expected brokerPOSTResponse
- expectedCode int
- instClient *mockInstanceClient
+ label string
+ input io.Reader
+ expected brokerPOSTResponse
+ expectedError string
+ expectedCode int
+ instClient *mockInstanceClient
}{
{
label: "Missing body failure",
@@ -48,41 +150,89 @@ func TestBrokerCreateHandler(t *testing.T) {
expectedCode: http.StatusUnprocessableEntity,
},
{
- label: "Missing vf-module-*-id parameter",
+ label: "Missing vf-module-*-id parameter",
+ expectedError: "vf-module-model-customization-id is empty",
+ expectedCode: http.StatusBadRequest,
input: bytes.NewBuffer([]byte(`{
- "vf-module-model-customization-id": "84sdfkio938",
"vf-module-model-invariant-id": "123456qwerty",
+ "vf-module-model-version-id": "123qweasdzxc",
+ "generic-vnf-id": "dummy-vnf-id",
+ "vf-module-id": "dummy-vfm-id",
+ "template_data": {
+ "stack_name": "dummy-stack-name"
+ },
"sdnc_directives": {
"attributes": [
{
- "attribute_name": "vf_module_name",
- "attribute_value": "test-vf-module-name"
- },
+ "attribute_name": "k8s-rb-profile-name",
+ "attribute_value": "dummy-profile"
+ }
+ ]
+ }
+ }`)),
+ },
+ {
+ label: "Missing stack name parameter",
+ expectedError: "stack_name is missing from template_data",
+ expectedCode: http.StatusBadRequest,
+ input: bytes.NewBuffer([]byte(`{
+ "vf-module-model-customization-id": "84sdfkio938",
+ "vf-module-model-invariant-id": "123456qwerty",
+ "vf-module-model-version-id": "123qweasdzxc",
+ "generic-vnf-id": "dummy-vnf-id",
+ "vf-module-id": "dummy-vfm-id",
+ "template_data": {
+ },
+ "sdnc_directives": {
+ "attributes": [
{
"attribute_name": "k8s-rb-profile-name",
- "attribute_value": "profile1"
+ "attribute_value": "dummy-profile"
}
]
}
}`)),
- expectedCode: http.StatusBadRequest,
},
{
- label: "Missing parameter from sdnc_directives",
+ label: "Missing profile name directive",
+ expectedError: "k8s-rb-profile-name is missing from directives",
+ expectedCode: http.StatusBadRequest,
input: bytes.NewBuffer([]byte(`{
"vf-module-model-customization-id": "84sdfkio938",
"vf-module-model-invariant-id": "123456qwerty",
"vf-module-model-version-id": "123qweasdzxc",
+ "generic-vnf-id": "dummy-vnf-id",
+ "vf-module-id": "dummy-vfm-id",
+ "template_data": {
+ "stack_name": "dummy-stack-name"
+ },
+ "sdnc_directives": {
+ "attributes": [
+ ]
+ }
+ }`)),
+ },
+ {
+ label: "Missing vf-module-id parameter",
+ expectedError: "vf-module-id is empty",
+ expectedCode: http.StatusBadRequest,
+ input: bytes.NewBuffer([]byte(`{
+ "vf-module-model-customization-id": "84sdfkio938",
+ "vf-module-model-invariant-id": "123456qwerty",
+ "vf-module-model-version-id": "123qweasdzxc",
+ "generic-vnf-id": "dummy-vnf-id",
+ "template_data": {
+ "stack_name": "dummy-stack-name"
+ },
"sdnc_directives": {
"attributes": [
{
- "attribute_name": "vf_module_name",
- "attribute_value": "test-vf-module-name"
+ "attribute_name": "k8s-rb-profile-name",
+ "attribute_value": "dummy-profile"
}
]
}
}`)),
- expectedCode: http.StatusBadRequest,
},
{
label: "Succesfully create an Instance",
@@ -90,15 +240,16 @@ func TestBrokerCreateHandler(t *testing.T) {
"vf-module-model-customization-id": "84sdfkio938",
"vf-module-model-invariant-id": "123456qwerty",
"vf-module-model-version-id": "123qweasdzxc",
+ "generic-vnf-id": "dummy-vnf-id",
+ "vf-module-id": "dummy-vfm-id",
+ "template_data": {
+ "stack_name": "dummy-stack-name"
+ },
"sdnc_directives": {
"attributes": [
{
- "attribute_name": "vf_module_name",
- "attribute_value": "test-vf-module-name"
- },
- {
"attribute_name": "k8s-rb-profile-name",
- "attribute_value": "profile1"
+ "attribute_value": "dummy-profile"
}
]
}
@@ -163,11 +314,13 @@ func TestBrokerCreateHandler(t *testing.T) {
request := httptest.NewRequest("POST", "/cloudowner/cloudregion/infra_workload", testCase.input)
resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil))
+ defer resp.Body.Close()
if testCase.expectedCode != resp.StatusCode {
body, _ := ioutil.ReadAll(resp.Body)
t.Log(string(body))
- t.Fatalf("Request method returned: \n%v\n and it was expected: \n%v", resp.StatusCode, testCase.expectedCode)
+ t.Fatalf("Request method returned code '%v', but '%v' was expected",
+ resp.StatusCode, testCase.expectedCode)
}
if resp.StatusCode == http.StatusCreated {
@@ -180,6 +333,16 @@ func TestBrokerCreateHandler(t *testing.T) {
t.Fatalf("TestGetHandler returned:\n result=%v\n expected=%v",
response, testCase.expected)
}
+ } else if testCase.expectedError != "" {
+ body, err := ioutil.ReadAll(resp.Body)
+ if err == nil {
+ if !strings.Contains(string(body), testCase.expectedError) {
+ t.Fatalf("Request method returned body '%s', but '%s' wasn't found",
+ body, testCase.expectedError)
+ }
+ } else {
+ t.Fatalf("Request method returned malformed body")
+ }
}
})
}
diff --git a/src/k8splugin/internal/helm/helm_test.go b/src/k8splugin/internal/helm/helm_test.go
index f95c0fd5..1e676c52 100644
--- a/src/k8splugin/internal/helm/helm_test.go
+++ b/src/k8splugin/internal/helm/helm_test.go
@@ -75,6 +75,15 @@ func TestProcessValues(t *testing.T) {
expectedHash: "516fab4ab7b76ba2ff35a97c2a79b74302543f532857b945f2fe25e717e755be",
expectedError: "",
},
+ {
+ label: "Process complex Pair Override",
+ values: []string{
+ "name={a,b,c}",
+ "servers[0].port=80",
+ },
+ expectedError: "",
+ expectedHash: "50d9401b003f65c1ccfd1c5155106fff88c8201ab8b7d66bd6ffa4fe2883bead",
+ },
}
h := sha256.New()
@@ -96,7 +105,7 @@ func TestProcessValues(t *testing.T) {
gotHash := fmt.Sprintf("%x", h.Sum(nil))
h.Reset()
if gotHash != testCase.expectedHash {
- t.Fatalf("Got unexpected values.yaml %s", out)
+ t.Fatalf("Got unexpected hash '%s' of values.yaml:\n%s", gotHash, out)
}
}
})
diff --git a/src/orchestrator/api/api.go b/src/orchestrator/api/api.go
index 83f17bbe..1cb4299e 100644
--- a/src/orchestrator/api/api.go
+++ b/src/orchestrator/api/api.go
@@ -1,5 +1,5 @@
/*
-Copyright 2018 Intel Corporation.
+Copyright 2020 Intel Corporation.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -14,25 +14,38 @@ limitations under the License.
package api
import (
- "github.com/onap/multicloud-k8s/src/orchestrator/internal/project"
+ moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
"github.com/gorilla/mux"
)
+var moduleClient *moduleLib.Client
+
// NewRouter creates a router that registers the various urls that are supported
-func NewRouter(projectClient project.ProjectManager) *mux.Router {
+func NewRouter(projectClient moduleLib.ProjectManager, compositeAppClient moduleLib.CompositeAppManager) *mux.Router {
router := mux.NewRouter().PathPrefix("/v2").Subrouter()
-
+ moduleClient = moduleLib.NewClient()
if projectClient == nil {
- projectClient = project.NewProjectClient()
+ projectClient = moduleClient.Project
}
projHandler := projectHandler{
client: projectClient,
}
- router.HandleFunc("/project", projHandler.createHandler).Methods("POST")
- router.HandleFunc("/project/{project-name}", projHandler.getHandler).Methods("GET")
- router.HandleFunc("/project/{project-name}", projHandler.deleteHandler).Methods("DELETE")
+ router.HandleFunc("/projects", projHandler.createHandler).Methods("POST")
+ router.HandleFunc("/projects/{project-name}", projHandler.getHandler).Methods("GET")
+ router.HandleFunc("/projects/{project-name}", projHandler.deleteHandler).Methods("DELETE")
+
+ if compositeAppClient == nil {
+ compositeAppClient = moduleClient.CompositeApp
+ }
+ compAppHandler := compositeAppHandler{
+ client: compositeAppClient,
+ }
+
+ router.HandleFunc("/projects/{project-name}/composite-apps", compAppHandler.createHandler).Methods("POST")
+ router.HandleFunc("/projects/{project-name}/composite-apps/{composite-app-name}/{version}", compAppHandler.getHandler).Methods("GET")
+ router.HandleFunc("/projects/{project-name}/composite-apps/{composite-app-name}/{version}", compAppHandler.deleteHandler).Methods("DELETE")
return router
}
diff --git a/src/orchestrator/api/composite_app_handler.go b/src/orchestrator/api/composite_app_handler.go
new file mode 100644
index 00000000..42c72cdb
--- /dev/null
+++ b/src/orchestrator/api/composite_app_handler.go
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package api
+
+import (
+ "encoding/json"
+ "io"
+ "net/http"
+
+ moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
+
+ "github.com/gorilla/mux"
+)
+
+// compositeAppHandler to store backend implementations objects
+// Also simplifies mocking for unit testing purposes
+type compositeAppHandler struct {
+ // Interface that implements CompositeApp operations
+ // We will set this variable with a mock interface for testing
+ client moduleLib.CompositeAppManager
+}
+
+// createHandler handles creation of the CompositeApp entry in the database
+// This is a multipart handler
+func (h compositeAppHandler) createHandler(w http.ResponseWriter, r *http.Request) {
+ var c moduleLib.CompositeApp
+
+ err := json.NewDecoder(r.Body).Decode(&c)
+ switch {
+ case err == io.EOF:
+ http.Error(w, "Empty body", http.StatusBadRequest)
+ return
+ case err != nil:
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ // Name is required.
+ if c.Metadata.Name == "" {
+ http.Error(w, "Missing name in POST request", http.StatusBadRequest)
+ return
+ }
+
+ vars := mux.Vars(r)
+ projectName := vars["project-name"]
+
+ ret, err := h.client.CreateCompositeApp(c, projectName)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// getHandler handles GET operations on a particular CompositeApp Name
+// Returns a compositeApp
+func (h compositeAppHandler) getHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["composite-app-name"]
+ version := vars["version"]
+ projectName := vars["project-name"]
+
+ ret, err := h.client.GetCompositeApp(name, version, projectName)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// deleteHandler handles DELETE operations on a particular CompositeApp Name
+func (h compositeAppHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["composite-app-name"]
+ version := vars["version"]
+ projectName := vars["project-name"]
+
+ err := h.client.DeleteCompositeApp(name, version, projectName)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
diff --git a/src/orchestrator/api/projecthandler.go b/src/orchestrator/api/projecthandler.go
index 30f21de3..1e78c676 100644
--- a/src/orchestrator/api/projecthandler.go
+++ b/src/orchestrator/api/projecthandler.go
@@ -21,7 +21,7 @@ import (
"io"
"net/http"
- "github.com/onap/multicloud-k8s/src/orchestrator/internal/project"
+ moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
"github.com/gorilla/mux"
)
@@ -31,12 +31,12 @@ import (
type projectHandler struct {
// Interface that implements Project operations
// We will set this variable with a mock interface for testing
- client project.ProjectManager
+ client moduleLib.ProjectManager
}
// Create handles creation of the Project entry in the database
func (h projectHandler) createHandler(w http.ResponseWriter, r *http.Request) {
- var p project.Project
+ var p moduleLib.Project
err := json.NewDecoder(r.Body).Decode(&p)
switch {
@@ -49,12 +49,12 @@ func (h projectHandler) createHandler(w http.ResponseWriter, r *http.Request) {
}
// Name is required.
- if p.ProjectName == "" {
+ if p.MetaData.Name == "" {
http.Error(w, "Missing name in POST request", http.StatusBadRequest)
return
}
- ret, err := h.client.Create(p)
+ ret, err := h.client.CreateProject(p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -70,12 +70,12 @@ func (h projectHandler) createHandler(w http.ResponseWriter, r *http.Request) {
}
// Get handles GET operations on a particular Project Name
-// Returns a rb.Project
+// Returns a Project
func (h projectHandler) getHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["project-name"]
- ret, err := h.client.Get(name)
+ ret, err := h.client.GetProject(name)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -95,7 +95,7 @@ func (h projectHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["project-name"]
- err := h.client.Delete(name)
+ err := h.client.DeleteProject(name)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
diff --git a/src/orchestrator/api/projecthandler_test.go b/src/orchestrator/api/projecthandler_test.go
index 2699f2e3..c76764b3 100644
--- a/src/orchestrator/api/projecthandler_test.go
+++ b/src/orchestrator/api/projecthandler_test.go
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Intel Corporation, Inc
+ * Copyright 2020 Intel Corporation, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,7 @@ import (
"reflect"
"testing"
- "github.com/onap/multicloud-k8s/src/orchestrator/internal/project"
+ moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
pkgerrors "github.com/pkg/errors"
)
@@ -36,27 +36,27 @@ import (
type mockProjectManager struct {
// Items and err will be used to customize each test
// via a localized instantiation of mockProjectManager
- Items []project.Project
+ Items []moduleLib.Project
Err error
}
-func (m *mockProjectManager) Create(inp project.Project) (project.Project, error) {
+func (m *mockProjectManager) CreateProject(inp moduleLib.Project) (moduleLib.Project, error) {
if m.Err != nil {
- return project.Project{}, m.Err
+ return moduleLib.Project{}, m.Err
}
return m.Items[0], nil
}
-func (m *mockProjectManager) Get(name string) (project.Project, error) {
+func (m *mockProjectManager) GetProject(name string) (moduleLib.Project, error) {
if m.Err != nil {
- return project.Project{}, m.Err
+ return moduleLib.Project{}, m.Err
}
return m.Items[0], nil
}
-func (m *mockProjectManager) Delete(name string) error {
+func (m *mockProjectManager) DeleteProject(name string) error {
return m.Err
}
@@ -64,7 +64,7 @@ func TestProjectCreateHandler(t *testing.T) {
testCases := []struct {
label string
reader io.Reader
- expected project.Project
+ expected moduleLib.Project
expectedCode int
projectClient *mockProjectManager
}{
@@ -77,19 +77,31 @@ func TestProjectCreateHandler(t *testing.T) {
label: "Create Project",
expectedCode: http.StatusCreated,
reader: bytes.NewBuffer([]byte(`{
- "project-name":"testProject",
- "description":"Test Project used for unit testing"
- }`)),
- expected: project.Project{
- ProjectName: "testProject",
- Description: "Test Project used for unit testing",
+ "metadata" : {
+ "name": "testProject",
+ "description": "Test Project used for unit testing",
+ "userData1": "data1",
+ "userData2": "data2"
+ }
+ }`)),
+ expected: moduleLib.Project{
+ MetaData: moduleLib.ProjectMetaData{
+ Name: "testProject",
+ Description: "Test Project used for unit testing",
+ UserData1: "data1",
+ UserData2: "data2",
+ },
},
projectClient: &mockProjectManager{
//Items that will be returned by the mocked Client
- Items: []project.Project{
- {
- ProjectName: "testProject",
- Description: "Test Project used for unit testing",
+ Items: []moduleLib.Project{
+ moduleLib.Project{
+ MetaData: moduleLib.ProjectMetaData{
+ Name: "testProject",
+ Description: "Test Project used for unit testing",
+ UserData1: "data1",
+ UserData2: "data2",
+ },
},
},
},
@@ -106,8 +118,8 @@ func TestProjectCreateHandler(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
- request := httptest.NewRequest("POST", "/v2/project", testCase.reader)
- resp := executeRequest(request, NewRouter(testCase.projectClient))
+ request := httptest.NewRequest("POST", "/v2/projects", testCase.reader)
+ resp := executeRequest(request, NewRouter(testCase.projectClient, nil))
//Check returned code
if resp.StatusCode != testCase.expectedCode {
@@ -116,7 +128,7 @@ func TestProjectCreateHandler(t *testing.T) {
//Check returned body only if statusCreated
if resp.StatusCode == http.StatusCreated {
- got := project.Project{}
+ got := moduleLib.Project{}
json.NewDecoder(resp.Body).Decode(&got)
if reflect.DeepEqual(testCase.expected, got) == false {
@@ -132,7 +144,7 @@ func TestProjectGetHandler(t *testing.T) {
testCases := []struct {
label string
- expected project.Project
+ expected moduleLib.Project
name, version string
expectedCode int
projectClient *mockProjectManager
@@ -140,16 +152,24 @@ func TestProjectGetHandler(t *testing.T) {
{
label: "Get Project",
expectedCode: http.StatusOK,
- expected: project.Project{
- ProjectName: "testProject",
- Description: "A Test project for unit testing",
+ expected: moduleLib.Project{
+ MetaData: moduleLib.ProjectMetaData{
+ Name: "testProject",
+ Description: "Test Project used for unit testing",
+ UserData1: "data1",
+ UserData2: "data2",
+ },
},
name: "testProject",
projectClient: &mockProjectManager{
- Items: []project.Project{
- {
- ProjectName: "testProject",
- Description: "A Test project for unit testing",
+ Items: []moduleLib.Project{
+ moduleLib.Project{
+ MetaData: moduleLib.ProjectMetaData{
+ Name: "testProject",
+ Description: "Test Project used for unit testing",
+ UserData1: "data1",
+ UserData2: "data2",
+ },
},
},
},
@@ -159,7 +179,7 @@ func TestProjectGetHandler(t *testing.T) {
expectedCode: http.StatusInternalServerError,
name: "nonexistingproject",
projectClient: &mockProjectManager{
- Items: []project.Project{},
+ Items: []moduleLib.Project{},
Err: pkgerrors.New("Internal Error"),
},
},
@@ -167,8 +187,8 @@ func TestProjectGetHandler(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
- request := httptest.NewRequest("GET", "/v2/project/"+testCase.name, nil)
- resp := executeRequest(request, NewRouter(testCase.projectClient))
+ request := httptest.NewRequest("GET", "/v2/projects/"+testCase.name, nil)
+ resp := executeRequest(request, NewRouter(testCase.projectClient, nil))
//Check returned code
if resp.StatusCode != testCase.expectedCode {
@@ -177,7 +197,7 @@ func TestProjectGetHandler(t *testing.T) {
//Check returned body only if statusOK
if resp.StatusCode == http.StatusOK {
- got := project.Project{}
+ got := moduleLib.Project{}
json.NewDecoder(resp.Body).Decode(&got)
if reflect.DeepEqual(testCase.expected, got) == false {
@@ -216,8 +236,8 @@ func TestProjectDeleteHandler(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.label, func(t *testing.T) {
- request := httptest.NewRequest("DELETE", "/v2/project/"+testCase.name, nil)
- resp := executeRequest(request, NewRouter(testCase.projectClient))
+ request := httptest.NewRequest("DELETE", "/v2/projects/"+testCase.name, nil)
+ resp := executeRequest(request, NewRouter(testCase.projectClient, nil))
//Check returned code
if resp.StatusCode != testCase.expectedCode {
diff --git a/src/orchestrator/cmd/main.go b/src/orchestrator/cmd/main.go
index 657d5bf5..087caba3 100644
--- a/src/orchestrator/cmd/main.go
+++ b/src/orchestrator/cmd/main.go
@@ -23,10 +23,10 @@ import (
"time"
"github.com/onap/multicloud-k8s/src/orchestrator/api"
- "github.com/onap/multicloud-k8s/src/orchestrator/internal/auth"
- "github.com/onap/multicloud-k8s/src/orchestrator/internal/config"
- "github.com/onap/multicloud-k8s/src/orchestrator/internal/db"
-
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/auth"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/config"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
+ contextDb "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/contextdb"
"github.com/gorilla/handlers"
)
@@ -40,8 +40,14 @@ func main() {
log.Println(err)
log.Fatalln("Exiting...")
}
+ err = contextDb.InitializeContextDatabase()
+ if err != nil {
+ log.Println("Unable to initialize database connection...")
+ log.Println(err)
+ log.Fatalln("Exiting...")
+ }
- httpRouter := api.NewRouter(nil)
+ httpRouter := api.NewRouter(nil, nil)
loggedRouter := handlers.LoggingHandler(os.Stdout, httpRouter)
log.Println("Starting Kubernetes Multicloud API")
diff --git a/src/orchestrator/examples/example_module.go b/src/orchestrator/examples/example_module.go
new file mode 100644
index 00000000..9138b085
--- /dev/null
+++ b/src/orchestrator/examples/example_module.go
@@ -0,0 +1,48 @@
+/*
+ * 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 test
+
+import (
+ moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
+ "log"
+)
+
+// ExampleClient_Project to test Project
+func ExampleClient_Project() {
+ // Get handle to the client
+ c := moduleLib.NewClient()
+ // Check if project is initialized
+ if c.Project == nil {
+ log.Println("Project is Uninitialized")
+ return
+ }
+ // Perform operations on Project Module
+ _, err := c.Project.CreateProject(moduleLib.Project{MetaData: moduleLib.ProjectMetaData{Name: "test", Description: "test", UserData1: "userData1", UserData2: "userData2"}})
+ if err != nil {
+ log.Println(err)
+ return
+ }
+ _, err = c.Project.GetProject("test")
+ if err != nil {
+ log.Println(err)
+ return
+ }
+ err = c.Project.DeleteProject("test")
+ if err != nil {
+ log.Println(err)
+ }
+}
diff --git a/src/orchestrator/go.sum b/src/orchestrator/go.sum
index 732bc280..d2015406 100644
--- a/src/orchestrator/go.sum
+++ b/src/orchestrator/go.sum
@@ -171,6 +171,7 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onap/multicloud-k8s v0.0.0-20191115005109-f168ebb73d8d h1:3uFucXVv6gqa3H1u85CjoLOvGraREfD8/NL7m/9W9tc=
+github.com/onap/multicloud-k8s v0.0.0-20200131010833-90e13d101cf0 h1:2qDo6s4pdg/g7Vj6QGrCK02EP4jjwVehgEObnAfipSM=
github.com/onap/multicloud-k8s/src/k8splugin v0.0.0-20191115005109-f168ebb73d8d h1:ucIEjqzNVeFPnQofeuBfUqro0OnilX//fajEFxuLsgA=
github.com/onap/multicloud-k8s/src/k8splugin v0.0.0-20191115005109-f168ebb73d8d/go.mod h1:EnQd/vQGZR1/55IihaHxiux4ZUig/zfXZux7bfmU0S8=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
diff --git a/src/orchestrator/internal/auth/auth.go b/src/orchestrator/pkg/infra/auth/auth.go
index 3da8f2af..3da8f2af 100644
--- a/src/orchestrator/internal/auth/auth.go
+++ b/src/orchestrator/pkg/infra/auth/auth.go
diff --git a/src/orchestrator/internal/auth/auth_test.go b/src/orchestrator/pkg/infra/auth/auth_test.go
index e41cb1ac..fdf81e6d 100644
--- a/src/orchestrator/internal/auth/auth_test.go
+++ b/src/orchestrator/pkg/infra/auth/auth_test.go
@@ -28,9 +28,9 @@ func TestGetTLSConfig(t *testing.T) {
if err == nil {
t.Errorf("Test failed, expected error but got none")
}
- tlsConfig, err := GetTLSConfig("../../tests/certs/auth_test_certificate.pem",
- "../../tests/certs/auth_test_certificate.pem",
- "../../tests/certs/auth_test_key.pem")
+ tlsConfig, err := GetTLSConfig("../../../tests/certs/auth_test_certificate.pem",
+ "../../../tests/certs/auth_test_certificate.pem",
+ "../../../tests/certs/auth_test_key.pem")
if err != nil {
t.Fatal("Test Failed as GetTLSConfig returned error: " + err.Error())
}
diff --git a/src/orchestrator/internal/config/config.go b/src/orchestrator/pkg/infra/config/config.go
index cb4656f0..df9cec92 100644
--- a/src/orchestrator/internal/config/config.go
+++ b/src/orchestrator/pkg/infra/config/config.go
@@ -83,9 +83,9 @@ func defaultConfiguration() *Configuration {
DatabaseType: "mongo",
PluginDir: cwd,
EtcdIP: "127.0.0.1",
- EtcdCert: "etcd.cert",
- EtcdKey: "etcd.key",
- EtcdCAFile: "etcd-ca.cert",
+ EtcdCert: "",
+ EtcdKey: "",
+ EtcdCAFile: "",
ServicePort: "9015",
KubernetesLabelName: "orchestrator.io/rb-instance-id",
}
diff --git a/src/orchestrator/internal/config/config_test.go b/src/orchestrator/pkg/infra/config/config_test.go
index ce7641ae..dce37e76 100644
--- a/src/orchestrator/internal/config/config_test.go
+++ b/src/orchestrator/pkg/infra/config/config_test.go
@@ -29,7 +29,7 @@ func TestReadConfigurationFile(t *testing.T) {
})
t.Run("Read Configuration File", func(t *testing.T) {
- conf, err := readConfigFile("../../tests/configs/mock_config.json")
+ conf, err := readConfigFile("../../../tests/configs/mock_config.json")
if err != nil {
t.Fatal("ReadConfigurationFile: Error reading file: ", err)
}
diff --git a/src/orchestrator/pkg/infra/contextdb/contextdb.go b/src/orchestrator/pkg/infra/contextdb/contextdb.go
new file mode 100644
index 00000000..d18af227
--- /dev/null
+++ b/src/orchestrator/pkg/infra/contextdb/contextdb.go
@@ -0,0 +1,72 @@
+/*
+Copyright 2020 Intel Corporation.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package contextdb
+
+import (
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/config"
+ pkgerrors "github.com/pkg/errors"
+)
+
+// Db interface used to talk a concrete Database connection
+var Db ContextDb
+
+// ContextDb is an interface for accessing the context database
+type ContextDb interface {
+ // Returns nil if db health is good
+ HealthCheck() error
+ // Puts Json Struct in db with key
+ Put(key string, value interface{}) error
+ // Delete k,v
+ Delete(key string) error
+ // Gets Json Struct from db
+ Get(key string, value interface{}) error
+ // Returns all keys with a prefix
+ GetAllKeys(path string) ([]string, error)
+}
+
+// createContextDBClient creates the DB client
+func createContextDBClient(dbType string) error {
+ var err error
+ switch dbType {
+ case "etcd":
+ c := EtcdConfig{
+ Endpoint: config.GetConfiguration().EtcdIP,
+ CertFile: config.GetConfiguration().EtcdCert,
+ KeyFile: config.GetConfiguration().EtcdKey,
+ CAFile: config.GetConfiguration().EtcdCAFile,
+ }
+ Db, err = NewEtcdClient(nil, c)
+ if err != nil {
+ pkgerrors.Wrap(err, "Etcd Client Initialization failed with error")
+ }
+ default:
+ return pkgerrors.New(dbType + "DB not supported")
+ }
+ return err
+}
+
+// InitializeContextDatabase sets up the connection to the
+// configured database to allow the application to talk to it.
+func InitializeContextDatabase() error {
+ // Only support Etcd for now
+ err := createContextDBClient("etcd")
+ if err != nil {
+ return pkgerrors.Cause(err)
+ }
+ err = Db.HealthCheck()
+ if err != nil {
+ return pkgerrors.Cause(err)
+ }
+ return nil
+}
diff --git a/src/orchestrator/pkg/infra/contextdb/etcd.go b/src/orchestrator/pkg/infra/contextdb/etcd.go
new file mode 100644
index 00000000..a1922d3b
--- /dev/null
+++ b/src/orchestrator/pkg/infra/contextdb/etcd.go
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package contextdb
+
+import (
+ "context"
+ "encoding/json"
+ pkgerrors "github.com/pkg/errors"
+ "go.etcd.io/etcd/clientv3"
+ "go.etcd.io/etcd/pkg/transport"
+ "time"
+)
+
+// EtcdConfig Configuration values needed for Etcd Client
+type EtcdConfig struct {
+ Endpoint string
+ CertFile string
+ KeyFile string
+ CAFile string
+}
+
+// EtcdClient for Etcd
+type EtcdClient struct {
+ cli *clientv3.Client
+ endpoint string
+}
+
+// Etcd For Mocking purposes
+type Etcd interface {
+ Put(ctx context.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error)
+ Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error)
+ Delete(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.DeleteResponse, error)
+}
+
+var getEtcd = func(e *EtcdClient) Etcd {
+ return e.cli
+}
+
+// NewEtcdClient function initializes Etcd client
+func NewEtcdClient(store *clientv3.Client, c EtcdConfig) (ContextDb, error) {
+ var endpoint string
+ if store == nil {
+ tlsInfo := transport.TLSInfo{
+ CertFile: c.CertFile,
+ KeyFile: c.KeyFile,
+ CAFile: c.CAFile,
+ }
+ tlsConfig, err := tlsInfo.ClientConfig()
+ if err != nil {
+ return nil, pkgerrors.Errorf("Error creating etcd TLSInfo: %s", err.Error())
+ }
+ // NOTE: Client relies on nil tlsConfig
+ // for non-secure connections, update the implicit variable
+ if len(c.CertFile) == 0 && len(c.KeyFile) == 0 && len(c.CAFile) == 0 {
+ tlsConfig = nil
+ }
+ endpoint = ""
+ if tlsConfig == nil {
+ endpoint = "http://" + c.Endpoint + ":2379"
+ } else {
+ endpoint = "https://" + c.Endpoint + ":2379"
+ }
+
+ store, err = clientv3.New(clientv3.Config{
+ Endpoints: []string{endpoint},
+ DialTimeout: 5 * time.Second,
+ TLS: tlsConfig,
+ })
+ if err != nil {
+ return nil, pkgerrors.Errorf("Error creating etcd client: %s", err.Error())
+ }
+ }
+
+ return &EtcdClient{
+ cli: store,
+ endpoint: endpoint,
+ }, nil
+}
+
+// Put values in Etcd DB
+func (e *EtcdClient) Put(key string, value interface{}) error {
+ cli := getEtcd(e)
+ if cli == nil {
+ return pkgerrors.Errorf("Etcd Client not initialized")
+ }
+ if key == "" {
+ return pkgerrors.Errorf("Key is null")
+ }
+ if value == nil {
+ return pkgerrors.Errorf("Value is nil")
+ }
+ v, err := json.Marshal(value)
+ if err != nil {
+ return pkgerrors.Errorf("Json Marshal error: %s", err.Error())
+ }
+ _, err = cli.Put(context.Background(), key, string(v))
+ if err != nil {
+ return pkgerrors.Errorf("Error creating etcd entry: %s", err.Error())
+ }
+ return nil
+}
+
+// Get values from Etcd DB and decodes from json
+func (e *EtcdClient) Get(key string, value interface{}) error {
+ cli := getEtcd(e)
+ if cli == nil {
+ return pkgerrors.Errorf("Etcd Client not initialized")
+ }
+ if key == "" {
+ return pkgerrors.Errorf("Key is null")
+ }
+ if value == nil {
+ return pkgerrors.Errorf("Value is nil")
+ }
+ getResp, err := cli.Get(context.Background(), key)
+ if err != nil {
+ return pkgerrors.Errorf("Error getting etcd entry: %s", err.Error())
+ }
+ if getResp.Count == 0 {
+ return pkgerrors.Errorf("Key doesn't exist")
+ }
+ return json.Unmarshal(getResp.Kvs[0].Value, value)
+}
+
+// GetAllKeys values from Etcd DB
+func (e *EtcdClient) GetAllKeys(key string) ([]string, error) {
+ cli := getEtcd(e)
+ if cli == nil {
+ return nil, pkgerrors.Errorf("Etcd Client not initialized")
+ }
+ getResp, err := cli.Get(context.Background(), key, clientv3.WithPrefix())
+ if err != nil {
+ return nil, pkgerrors.Errorf("Error getting etcd entry: %s", err.Error())
+ }
+ if getResp.Count == 0 {
+ return nil, pkgerrors.Errorf("Key doesn't exist")
+ }
+ var keys []string
+ for _, ev := range getResp.Kvs {
+ keys = append(keys, string(ev.Key))
+ }
+ return keys, nil
+}
+
+// Delete values from Etcd DB
+func (e *EtcdClient) Delete(key string) error {
+ cli := getEtcd(e)
+ if cli == nil {
+ return pkgerrors.Errorf("Etcd Client not initialized")
+ }
+ _, err := cli.Delete(context.Background(), key, clientv3.WithPrefix())
+ if err != nil {
+ return pkgerrors.Errorf("Delete failed etcd entry: %s", err.Error())
+ }
+ return nil
+}
+
+// HealthCheck for checking health of the etcd cluster
+func (e *EtcdClient) HealthCheck() error {
+ return nil
+}
diff --git a/src/orchestrator/pkg/infra/contextdb/etcd_test.go b/src/orchestrator/pkg/infra/contextdb/etcd_test.go
new file mode 100644
index 00000000..17b7a5d5
--- /dev/null
+++ b/src/orchestrator/pkg/infra/contextdb/etcd_test.go
@@ -0,0 +1,276 @@
+/*
+Copyright 2020 Intel Corporation.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package contextdb
+
+import (
+ "context"
+ mvccpb "github.com/coreos/etcd/mvcc/mvccpb"
+ pkgerrors "github.com/pkg/errors"
+ "go.etcd.io/etcd/clientv3"
+ "strings"
+ "testing"
+)
+
+type kv struct {
+ Key []byte
+ Value []byte
+}
+
+// MockEtcdClient for mocking etcd
+type MockEtcdClient struct {
+ Kvs []*mvccpb.KeyValue
+ Count int64
+ Err error
+}
+
+// Mocking only Single Value
+// Put function
+func (e *MockEtcdClient) Put(ctx context.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error) {
+ var m mvccpb.KeyValue
+ m.Key = []byte(key)
+ m.Value = []byte(val)
+ e.Count = e.Count + 1
+ e.Kvs = append(e.Kvs, &m)
+ return &clientv3.PutResponse{}, e.Err
+}
+
+// Get function
+func (e *MockEtcdClient) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) {
+ var g clientv3.GetResponse
+ g.Kvs = e.Kvs
+ g.Count = e.Count
+ return &g, e.Err
+}
+
+// Delete function
+func (e *MockEtcdClient) Delete(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.DeleteResponse, error) {
+ return &clientv3.DeleteResponse{}, e.Err
+}
+
+type testStruct struct {
+ Name string `json:"name"`
+ Num int `json:"num"`
+}
+
+// TestPut test Put
+func TestPut(t *testing.T) {
+ testCases := []struct {
+ label string
+ mockEtcd *MockEtcdClient
+ expectedError string
+ key string
+ value *testStruct
+ }{
+ {
+ label: "Success Case",
+ mockEtcd: &MockEtcdClient{},
+ key: "test1",
+ value: &testStruct{Name: "test", Num: 5},
+ },
+ {
+ label: "Key is null",
+ mockEtcd: &MockEtcdClient{},
+ key: "",
+ expectedError: "Key is null",
+ },
+ {
+ label: "Value is nil",
+ mockEtcd: &MockEtcdClient{},
+ key: "test1",
+ value: nil,
+ expectedError: "Value is nil",
+ },
+ {
+ label: "Error creating etcd entry",
+ mockEtcd: &MockEtcdClient{Err: pkgerrors.New("DB Error")},
+ key: "test1",
+ value: &testStruct{Name: "test", Num: 5},
+ expectedError: "Error creating etcd entry: DB Error",
+ },
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ cli, _ := NewEtcdClient(&clientv3.Client{}, EtcdConfig{})
+ getEtcd = func(e *EtcdClient) Etcd {
+ return testCase.mockEtcd
+ }
+ err := cli.Put(testCase.key, testCase.value)
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Method returned an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Method returned an error (%s)", err)
+ }
+ }
+
+ })
+ }
+}
+
+func TestGet(t *testing.T) {
+ testCases := []struct {
+ label string
+ mockEtcd *MockEtcdClient
+ expectedError string
+ key string
+ value *testStruct
+ }{
+ {
+ label: "Key is null",
+ mockEtcd: &MockEtcdClient{},
+ key: "",
+ value: nil,
+ expectedError: "Key is null",
+ },
+ {
+ label: "Key doesn't exist",
+ mockEtcd: &MockEtcdClient{},
+ key: "test1",
+ value: &testStruct{},
+ expectedError: "Key doesn't exist",
+ },
+ {
+ label: "Error getting etcd entry",
+ mockEtcd: &MockEtcdClient{Err: pkgerrors.New("DB Error")},
+ key: "test1",
+ value: &testStruct{},
+ expectedError: "Error getting etcd entry: DB Error",
+ },
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ cli, _ := NewEtcdClient(&clientv3.Client{}, EtcdConfig{})
+ getEtcd = func(e *EtcdClient) Etcd {
+ return testCase.mockEtcd
+ }
+ err := cli.Get(testCase.key, testCase.value)
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Method returned an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Method returned an error (%s)", err)
+ }
+ }
+
+ })
+ }
+}
+
+func TestGetString(t *testing.T) {
+ testCases := []struct {
+ label string
+ mockEtcd *MockEtcdClient
+ expectedError string
+ value string
+ }{
+ {
+ label: "Success Case",
+ mockEtcd: &MockEtcdClient{},
+ },
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ cli, _ := NewEtcdClient(&clientv3.Client{}, EtcdConfig{})
+ getEtcd = func(e *EtcdClient) Etcd {
+ return testCase.mockEtcd
+ }
+ err := cli.Put("test", "test1")
+ if err != nil {
+ t.Error("Test failed", err)
+ }
+ var s string
+ err = cli.Get("test", &s)
+ if err != nil {
+ t.Error("Test failed", err)
+ }
+ if "test1" != s {
+ t.Error("Get Failed")
+ }
+ })
+ }
+}
+
+func TestDelete(t *testing.T) {
+ testCases := []struct {
+ label string
+ mockEtcd *MockEtcdClient
+ expectedError string
+ }{
+ {
+ label: "Success Case",
+ mockEtcd: &MockEtcdClient{},
+ },
+ {
+ label: "Delete failed etcd entry",
+ mockEtcd: &MockEtcdClient{Err: pkgerrors.New("DB Error")},
+ expectedError: "Delete failed etcd entry: DB Error",
+ },
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ cli, _ := NewEtcdClient(&clientv3.Client{}, EtcdConfig{})
+ getEtcd = func(e *EtcdClient) Etcd {
+ return testCase.mockEtcd
+ }
+ err := cli.Delete("test")
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Method returned an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Method returned an error (%s)", err)
+ }
+ }
+
+ })
+ }
+}
+
+func TestGetAll(t *testing.T) {
+ testCases := []struct {
+ label string
+ mockEtcd *MockEtcdClient
+ expectedError string
+ }{
+ {
+ label: "Key doesn't exist",
+ mockEtcd: &MockEtcdClient{},
+ expectedError: "Key doesn't exist",
+ },
+ {
+ label: "Error getting etcd entry",
+ mockEtcd: &MockEtcdClient{Err: pkgerrors.New("DB Error")},
+ expectedError: "Error getting etcd entry: DB Error",
+ },
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ cli, _ := NewEtcdClient(&clientv3.Client{}, EtcdConfig{})
+ getEtcd = func(e *EtcdClient) Etcd {
+ return testCase.mockEtcd
+ }
+ _, err := cli.GetAllKeys("test")
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Method returned an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Method returned an error (%s)", err)
+ }
+ }
+ })
+ }
+}
diff --git a/src/orchestrator/pkg/infra/contextdb/mock.go b/src/orchestrator/pkg/infra/contextdb/mock.go
new file mode 100644
index 00000000..fc0f8ff7
--- /dev/null
+++ b/src/orchestrator/pkg/infra/contextdb/mock.go
@@ -0,0 +1,58 @@
+/*
+Copyright 2020 Intel Corporation.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package contextdb
+
+import (
+ pkgerrors "github.com/pkg/errors"
+)
+
+type MockEtcd struct {
+ Items map[string]interface{}
+ Err error
+}
+
+func (c *MockEtcd) Put(key string, value interface{}) error {
+ if c.Items == nil {
+ c.Items = make(map[string]interface{})
+ }
+ c.Items[key] = value
+ return c.Err
+}
+
+func (c *MockEtcd) Get(key string, value interface{}) error {
+ for kvKey, kvValue := range c.Items {
+ if kvKey == key {
+ value = kvValue
+ return nil
+ }
+ }
+ return pkgerrors.Errorf("Key doesn't exist")
+}
+
+func (c *MockEtcd) Delete(key string) error {
+ delete(c.Items, key)
+ return c.Err
+}
+
+func (c *MockEtcd) GetAllKeys(path string) ([]string, error) {
+ var keys []string
+ for k, _ := range c.Items {
+ keys = append(keys, string(k))
+ }
+ return keys, nil
+}
+
+func (e *MockEtcd) HealthCheck() error {
+ return nil
+}
diff --git a/src/orchestrator/internal/db/README.md b/src/orchestrator/pkg/infra/db/README.md
index cba1b7ea..cba1b7ea 100644
--- a/src/orchestrator/internal/db/README.md
+++ b/src/orchestrator/pkg/infra/db/README.md
diff --git a/src/orchestrator/internal/db/mock.go b/src/orchestrator/pkg/infra/db/mock.go
index 1dbca4b4..1dbca4b4 100644
--- a/src/orchestrator/internal/db/mock.go
+++ b/src/orchestrator/pkg/infra/db/mock.go
diff --git a/src/orchestrator/internal/db/mongo.go b/src/orchestrator/pkg/infra/db/mongo.go
index 3720a4f2..32d0b549 100644
--- a/src/orchestrator/internal/db/mongo.go
+++ b/src/orchestrator/pkg/infra/db/mongo.go
@@ -21,7 +21,7 @@ import (
"golang.org/x/net/context"
- "github.com/onap/multicloud-k8s/src/orchestrator/internal/config"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/config"
pkgerrors "github.com/pkg/errors"
"go.mongodb.org/mongo-driver/bson"
diff --git a/src/orchestrator/internal/db/mongo_test.go b/src/orchestrator/pkg/infra/db/mongo_test.go
index 171c908f..171c908f 100644
--- a/src/orchestrator/internal/db/mongo_test.go
+++ b/src/orchestrator/pkg/infra/db/mongo_test.go
diff --git a/src/orchestrator/internal/db/store.go b/src/orchestrator/pkg/infra/db/store.go
index ed394205..1a9632e7 100644
--- a/src/orchestrator/internal/db/store.go
+++ b/src/orchestrator/pkg/infra/db/store.go
@@ -17,7 +17,7 @@ import (
"encoding/json"
"reflect"
- "github.com/onap/multicloud-k8s/src/orchestrator/internal/config"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/config"
pkgerrors "github.com/pkg/errors"
)
diff --git a/src/orchestrator/internal/db/store_test.go b/src/orchestrator/pkg/infra/db/store_test.go
index 42a41787..42a41787 100644
--- a/src/orchestrator/internal/db/store_test.go
+++ b/src/orchestrator/pkg/infra/db/store_test.go
diff --git a/src/orchestrator/internal/logutils/logger.go b/src/orchestrator/pkg/infra/logutils/logger.go
index 2e8f9969..2e8f9969 100644
--- a/src/orchestrator/internal/logutils/logger.go
+++ b/src/orchestrator/pkg/infra/logutils/logger.go
diff --git a/src/orchestrator/pkg/module/compositeapp.go b/src/orchestrator/pkg/module/compositeapp.go
new file mode 100644
index 00000000..0a4e158c
--- /dev/null
+++ b/src/orchestrator/pkg/module/compositeapp.go
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governinog permissions and
+ * limitations under the License.
+ */
+
+package module
+
+import (
+ "encoding/json"
+
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
+
+ pkgerrors "github.com/pkg/errors"
+)
+
+// CompositeApp contains metadata and spec for CompositeApps
+type CompositeApp struct {
+ Metadata CompositeAppMetaData `json:"metadata"`
+ Spec CompositeAppSpec `json:"spec"`
+}
+
+//CompositeAppMetaData contains the parameters needed for CompositeApps
+type CompositeAppMetaData struct {
+ Name string `json:"name"`
+ Description string `json:"description"`
+ UserData1 string `userData1:"userData1"`
+ UserData2 string `userData2:"userData2"`
+}
+
+//CompositeAppSpec contains the Version of the CompositeApp
+type CompositeAppSpec struct {
+ Version string `json:"version"`
+}
+
+// CompositeAppKey is the key structure that is used in the database
+type CompositeAppKey struct {
+ CompositeAppName string `json:"compositeappname"`
+ Version string `json:"version"`
+ Project string `json:"project"`
+}
+
+// We will use json marshalling to convert to string to
+// preserve the underlying structure.
+func (cK CompositeAppKey) String() string {
+ out, err := json.Marshal(cK)
+ if err != nil {
+ return ""
+ }
+ return string(out)
+}
+
+// CompositeAppManager is an interface exposes the CompositeApp functionality
+type CompositeAppManager interface {
+ CreateCompositeApp(c CompositeApp, p string) (CompositeApp, error)
+ GetCompositeApp(name string, version string, p string) (CompositeApp, error)
+ DeleteCompositeApp(name string, version string, p string) error
+}
+
+// CompositeAppClient implements the CompositeAppManager
+// It will also be used to maintain some localized state
+type CompositeAppClient struct {
+ storeName string
+ tagMeta, tagContent string
+}
+
+// NewCompositeAppClient returns an instance of the CompositeAppClient
+// which implements the CompositeAppManager
+func NewCompositeAppClient() *CompositeAppClient {
+ return &CompositeAppClient{
+ storeName: "orchestrator",
+ tagMeta: "compositeAppmetadata",
+ }
+}
+
+// CreateCompositeApp creates a new collection based on the CompositeApp
+func (v *CompositeAppClient) CreateCompositeApp(c CompositeApp, p string) (CompositeApp, error) {
+
+ //Construct the composite key to select the entry
+ key := CompositeAppKey{
+ CompositeAppName: c.Metadata.Name,
+ Version: c.Spec.Version,
+ Project: p,
+ }
+
+ //Check if this CompositeApp already exists
+ _, err := v.GetCompositeApp(c.Metadata.Name, c.Spec.Version, p)
+ if err == nil {
+ return CompositeApp{}, pkgerrors.New("CompositeApp already exists")
+ }
+
+ //Check if Project exists
+ _, err = NewProjectClient().GetProject(p)
+ if err != nil {
+ return CompositeApp{}, pkgerrors.New("Unable to find the project")
+ }
+
+ err = db.DBconn.Create(v.storeName, key, v.tagMeta, c)
+ if err != nil {
+ return CompositeApp{}, pkgerrors.Wrap(err, "Creating DB Entry")
+ }
+
+ return c, nil
+}
+
+// GetCompositeApp returns the CompositeApp for corresponding name
+func (v *CompositeAppClient) GetCompositeApp(name string, version string, p string) (CompositeApp, error) {
+
+ //Construct the composite key to select the entry
+ key := CompositeAppKey{
+ CompositeAppName: name,
+ Version: version,
+ Project: p,
+ }
+ value, err := db.DBconn.Read(v.storeName, key, v.tagMeta)
+ if err != nil {
+ return CompositeApp{}, pkgerrors.Wrap(err, "Get composite application")
+ }
+
+ //value is a byte array
+ if value != nil {
+ compApp := CompositeApp{}
+ err = db.DBconn.Unmarshal(value, &compApp)
+ if err != nil {
+ return CompositeApp{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+ }
+ return compApp, nil
+ }
+
+ return CompositeApp{}, pkgerrors.New("Error getting composite application")
+}
+
+// DeleteCompositeApp deletes the CompositeApp from database
+func (v *CompositeAppClient) DeleteCompositeApp(name string, version string, p string) error {
+
+ //Construct the composite key to select the entry
+ key := CompositeAppKey{
+ CompositeAppName: name,
+ Version: version,
+ Project: p,
+ }
+ err := db.DBconn.Delete(v.storeName, key, v.tagMeta)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Delete CompositeApp Entry;")
+ }
+
+ return nil
+}
diff --git a/src/orchestrator/pkg/module/module.go b/src/orchestrator/pkg/module/module.go
new file mode 100644
index 00000000..d03e5ffb
--- /dev/null
+++ b/src/orchestrator/pkg/module/module.go
@@ -0,0 +1,33 @@
+/*
+ * 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 module
+
+// Client for using the services in the orchestrator
+type Client struct {
+ Project *ProjectClient
+ CompositeApp *CompositeAppClient
+ // Add Clients for API's here
+}
+
+// NewClient creates a new client for using the services
+func NewClient() *Client {
+ c := &Client{}
+ c.Project = NewProjectClient()
+ c.CompositeApp = NewCompositeAppClient()
+ // Add Client API handlers here
+ return c
+}
diff --git a/src/orchestrator/internal/project/project.go b/src/orchestrator/pkg/module/project.go
index f0c50065..a95251b5 100644
--- a/src/orchestrator/internal/project/project.go
+++ b/src/orchestrator/pkg/module/project.go
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Intel Corporation, Inc
+ * Copyright 2020 Intel Corporation, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,26 +14,32 @@
* limitations under the License.
*/
-package project
+package module
import (
"encoding/json"
- "github.com/onap/multicloud-k8s/src/orchestrator/internal/db"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
pkgerrors "github.com/pkg/errors"
)
-// Project contains the parameters needed for Projects
-// It implements the interface for managing the Projects
+// Project contains the metaData for Projects
type Project struct {
- ProjectName string `json:"project-name"`
+ MetaData ProjectMetaData `json:"metadata"`
+}
+
+// ProjectMetaData contains the parameters for creating a project
+type ProjectMetaData struct {
+ Name string `json:"name"`
Description string `json:"description"`
+ UserData1 string `userData1:"userData1"`
+ UserData2 string `userData2:"userData2"`
}
// ProjectKey is the key structure that is used in the database
type ProjectKey struct {
- ProjectName string `json:"rb-name"`
+ ProjectName string `json:"project"`
}
// We will use json marshalling to convert to string to
@@ -49,9 +55,9 @@ func (pk ProjectKey) String() string {
// ProjectManager is an interface exposes the Project functionality
type ProjectManager interface {
- Create(pr Project) (Project, error)
- Get(name string) (Project, error)
- Delete(name string) error
+ CreateProject(pr Project) (Project, error)
+ GetProject(name string) (Project, error)
+ DeleteProject(name string) error
}
// ProjectClient implements the ProjectManager
@@ -65,25 +71,26 @@ type ProjectClient struct {
// which implements the ProjectManager
func NewProjectClient() *ProjectClient {
return &ProjectClient{
- tagMeta: "projectmetadata",
+ storeName: "orchestrator",
+ tagMeta: "projectmetadata",
}
}
-// Create a new collection based on the project
-func (v *ProjectClient) Create(p Project) (Project, error) {
+// CreateProject a new collection based on the project
+func (v *ProjectClient) CreateProject(p Project) (Project, error) {
//Construct the composite key to select the entry
key := ProjectKey{
- ProjectName: p.ProjectName,
+ ProjectName: p.MetaData.Name,
}
//Check if this Project already exists
- _, err := v.Get(p.ProjectName)
+ _, err := v.GetProject(p.MetaData.Name)
if err == nil {
return Project{}, pkgerrors.New("Project already exists")
}
- err = db.DBconn.Create(p.ProjectName, key, v.tagMeta, p)
+ err = db.DBconn.Create(v.storeName, key, v.tagMeta, p)
if err != nil {
return Project{}, pkgerrors.Wrap(err, "Creating DB Entry")
}
@@ -91,14 +98,14 @@ func (v *ProjectClient) Create(p Project) (Project, error) {
return p, nil
}
-// Get returns the Project for corresponding name
-func (v *ProjectClient) Get(name string) (Project, error) {
+// GetProject returns the Project for corresponding name
+func (v *ProjectClient) GetProject(name string) (Project, error) {
//Construct the composite key to select the entry
key := ProjectKey{
ProjectName: name,
}
- value, err := db.DBconn.Read(name, key, v.tagMeta)
+ value, err := db.DBconn.Read(v.storeName, key, v.tagMeta)
if err != nil {
return Project{}, pkgerrors.Wrap(err, "Get Project")
}
@@ -116,14 +123,14 @@ func (v *ProjectClient) Get(name string) (Project, error) {
return Project{}, pkgerrors.New("Error getting Project")
}
-// Delete the Project from database
-func (v *ProjectClient) Delete(name string) error {
+// DeleteProject the Project from database
+func (v *ProjectClient) DeleteProject(name string) error {
//Construct the composite key to select the entry
key := ProjectKey{
ProjectName: name,
}
- err := db.DBconn.Delete(name, key, v.tagMeta)
+ err := db.DBconn.Delete(v.storeName, key, v.tagMeta)
if err != nil {
return pkgerrors.Wrap(err, "Delete Project Entry;")
}
diff --git a/src/orchestrator/internal/project/project_test.go b/src/orchestrator/pkg/module/project_test.go
index cc691e33..f6856f86 100644
--- a/src/orchestrator/internal/project/project_test.go
+++ b/src/orchestrator/pkg/module/project_test.go
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Intel Corporation, Inc
+ * Copyright 2020 Intel Corporation, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package project
+package module
import (
"reflect"
"strings"
"testing"
- "github.com/onap/multicloud-k8s/src/orchestrator/internal/db"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
pkgerrors "github.com/pkg/errors"
)
@@ -37,12 +37,20 @@ func TestCreateProject(t *testing.T) {
{
label: "Create Project",
inp: Project{
- ProjectName: "testProject",
- Description: "A sample Project used for unit testing",
+ MetaData: ProjectMetaData{
+ Name: "testProject",
+ Description: "A sample Project used for unit testing",
+ UserData1: "data1",
+ UserData2: "data2",
+ },
},
expected: Project{
- ProjectName: "testProject",
- Description: "A sample Project used for unit testing",
+ MetaData: ProjectMetaData{
+ Name: "testProject",
+ Description: "A sample Project used for unit testing",
+ UserData1: "data1",
+ UserData2: "data2",
+ },
},
expectedError: "",
mockdb: &db.MockDB{},
@@ -60,7 +68,7 @@ func TestCreateProject(t *testing.T) {
t.Run(testCase.label, func(t *testing.T) {
db.DBconn = testCase.mockdb
impl := NewProjectClient()
- got, err := impl.Create(testCase.inp)
+ got, err := impl.CreateProject(testCase.inp)
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("Create returned an unexpected error %s", err)
@@ -92,16 +100,25 @@ func TestGetProject(t *testing.T) {
label: "Get Project",
name: "testProject",
expected: Project{
- ProjectName: "testProject",
- Description: "Test project for unit testing",
+ MetaData: ProjectMetaData{
+ Name: "testProject",
+ Description: "Test project for unit testing",
+ UserData1: "userData1",
+ UserData2: "userData2",
+ },
},
expectedError: "",
mockdb: &db.MockDB{
Items: map[string]map[string][]byte{
ProjectKey{ProjectName: "testProject"}.String(): {
"projectmetadata": []byte(
- "{\"project-name\":\"testProject\"," +
- "\"description\":\"Test project for unit testing\"}"),
+ "{" +
+ "\"metadata\" : {" +
+ "\"Name\":\"testProject\"," +
+ "\"Description\":\"Test project for unit testing\"," +
+ "\"UserData1\": \"userData1\"," +
+ "\"UserData2\":\"userData2\"}" +
+ "}"),
},
},
},
@@ -119,7 +136,7 @@ func TestGetProject(t *testing.T) {
t.Run(testCase.label, func(t *testing.T) {
db.DBconn = testCase.mockdb
impl := NewProjectClient()
- got, err := impl.Get(testCase.name)
+ got, err := impl.GetProject(testCase.name)
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("Get returned an unexpected error: %s", err)
@@ -163,7 +180,7 @@ func TestDeleteProject(t *testing.T) {
t.Run(testCase.label, func(t *testing.T) {
db.DBconn = testCase.mockdb
impl := NewProjectClient()
- err := impl.Delete(testCase.name)
+ err := impl.DeleteProject(testCase.name)
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("Delete returned an unexpected error %s", err)
diff --git a/src/orchestrator/scripts/Dockerfile b/src/orchestrator/scripts/Dockerfile
new file mode 100644
index 00000000..b894f47c
--- /dev/null
+++ b/src/orchestrator/scripts/Dockerfile
@@ -0,0 +1,30 @@
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright (c) 2020
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+FROM ubuntu:18.04
+
+ARG HTTP_PROXY=${HTTP_PROXY}
+ARG HTTPS_PROXY=${HTTPS_PROXY}
+
+ENV http_proxy $HTTP_PROXY
+ENV https_proxy $HTTPS_PROXY
+ENV no_proxy $NO_PROXY
+
+EXPOSE 9015
+
+RUN groupadd -r onap && useradd -r -g onap onap
+
+WORKDIR /opt/multicloud/k8s/orchestrator
+RUN chown onap:onap /opt/multicloud/k8s/orchestrator -R
+
+ADD --chown=onap ./orchestrator ./
+
+USER onap
+
+CMD ["./orchestrator"] \ No newline at end of file
diff --git a/src/orchestrator/scripts/_functions.sh b/src/orchestrator/scripts/_functions.sh
new file mode 100755
index 00000000..1200f71f
--- /dev/null
+++ b/src/orchestrator/scripts/_functions.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright 2020 Intel Corporation.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+function stop_all {
+ docker-compose kill
+ docker-compose down
+}
+
+function start_mongo {
+ 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}
+}
+
+function start_etcd {
+ docker-compose up -d etcd
+}
+
+
+function generate_config {
+cat << EOF > config.json
+{
+ "ca-file": "ca.cert",
+ "server-cert": "server.cert",
+ "server-key": "server.key",
+ "password": "",
+ "database-ip": "${DATABASE_IP}",
+ "database-type": "mongo",
+ "plugin-dir": "plugins",
+ "etcd-ip": "127.0.0.1",
+ "etcd-cert": "",
+ "etcd-key": "",
+ "etcd-ca-file": "",
+ "service-port": "9015"
+}
+EOF
+}
+
+function start_all {
+ docker-compose up -d
+}
diff --git a/src/orchestrator/scripts/build.sh b/src/orchestrator/scripts/build.sh
new file mode 100755
index 00000000..5446dac0
--- /dev/null
+++ b/src/orchestrator/scripts/build.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright (c) 2020 Intel Corporation
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+set -o nounset
+set -o pipefail
+
+k8s_path="$(git rev-parse --show-toplevel)"
+
+VERSION="0.0.1-SNAPSHOT"
+export IMAGE_NAME="nexus3.onap.org:10003/onap/multicloud/k8s/orchestrator"
+
+function _compile_src {
+ echo "Compiling source code"
+ pushd $k8s_path/src/orchestrator/
+ make
+ popd
+}
+
+function _move_bin {
+ echo "Moving binaries"
+ mv $k8s_path/src/orchestrator/orchestrator .
+}
+
+function _cleanup {
+ echo "Cleaning previous execution"
+ docker-compose kill
+ image=$(grep "image.*orchestrator" docker-compose.yml)
+ if [[ -n ${image} ]]; then
+ docker images ${image#*:} -q | xargs docker rmi -f
+ fi
+ docker ps -a --filter "status=exited" -q | xargs docker rm
+}
+
+function _build_docker {
+ echo "Building docker image"
+ docker-compose build --no-cache
+}
+
+function _push_image {
+ local tag_name=${IMAGE_NAME}:${1:-latest}
+
+ echo "Start push {$tag_name}"
+ docker push ${IMAGE_NAME}:latest
+ docker tag ${IMAGE_NAME}:latest ${tag_name}
+ docker push ${tag_name}
+}
+
+if [[ -n "${JENKINS_HOME+x}" ]]; then
+ set -o xtrace
+ _compile_src
+ _move_bin
+ _build_docker
+ _push_image $VERSION
+else
+ source /etc/environment
+
+ _compile_src
+ _move_bin
+ _cleanup
+ _build_docker
+fi \ No newline at end of file
diff --git a/src/orchestrator/scripts/docker-compose.yml b/src/orchestrator/scripts/docker-compose.yml
new file mode 100644
index 00000000..3bb7bdaa
--- /dev/null
+++ b/src/orchestrator/scripts/docker-compose.yml
@@ -0,0 +1,57 @@
+# Copyright 2020 Intel Corporation.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+version: '3.0'
+
+services:
+ orchestrator:
+ image: nexus3.onap.org:10003/onap/multicloud/k8s/orchestrator
+ build:
+ context: ./
+ args:
+ - HTTP_PROXY=${HTTP_PROXY}
+ - HTTPS_PROXY=${HTTPS_PROXY}
+ - NO_PROXY=${NO_PROXY}
+ environment:
+ - HTTP_PROXY=${HTTP_PROXY}
+ - HTTPS_PROXY=${HTTPS_PROXY}
+ - NO_PROXY=${NO_PROXY},mongo
+ depends_on:
+ - mongo
+ network_mode: host
+ volumes:
+ - /opt/csar:/opt/csar
+ - ${PWD}/config.json:/opt/multicloud/k8s/orchestrator/config.json:ro
+ ports:
+ - 9015:9015
+ mongo:
+ image: mongo
+ environment:
+ - HTTP_PROXY=${HTTP_PROXY}
+ - HTTPS_PROXY=${HTTPS_PROXY}
+ - NO_PROXY=${NO_PROXY}
+ ports:
+ - 27017:27017
+ etcd:
+ image: bitnami/etcd:3
+ environment:
+ - ALLOW_NONE_AUTHENTICATION=yes
+ - HTTP_PROXY=${HTTP_PROXY}
+ - HTTPS_PROXY=${HTTPS_PROXY}
+ - NO_PROXY=${NO_PROXY}
+ volumes:
+ - etcd_data:/bitnami/etcd
+ ports:
+ - 2379:2379
+ - 2380:2380
+volumes:
+ etcd_data:
+ driver: local
diff --git a/src/orchestrator/scripts/start-dev.sh b/src/orchestrator/scripts/start-dev.sh
new file mode 100755
index 00000000..c97451a6
--- /dev/null
+++ b/src/orchestrator/scripts/start-dev.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright 2020 Intel Corporation.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+source _functions.sh
+
+#
+# Start from compiled binaries to foreground. This is usable for development use.
+#
+source /etc/environment
+opath="$(git rev-parse --show-toplevel)"/src/orchestrator
+
+stop_all
+start_mongo
+start_etcd
+
+echo "Compiling source code"
+pushd $opath
+generate_config
+make all
+./orchestrator
+popd
diff --git a/src/orchestrator/scripts/start-docker.sh b/src/orchestrator/scripts/start-docker.sh
new file mode 100755
index 00000000..c6f53c6c
--- /dev/null
+++ b/src/orchestrator/scripts/start-docker.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright (c) 2020 Intel Corporation
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+source _functions.sh
+
+#
+# Start from containers. build.sh should be run prior this script.
+#
+stop_all
+start_mongo
+start_etcd
+generate_config
+start_all \ No newline at end of file