aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--deployments/helm/servicemesh/istio-operator/Chart.yaml7
-rw-r--r--deployments/helm/servicemesh/istio-operator/README.md19
-rw-r--r--deployments/helm/servicemesh/istio-operator/templates/authproxy-rbac.yaml1
-rw-r--r--deployments/helm/servicemesh/istio-operator/templates/authproxy-service.yaml2
-rw-r--r--deployments/helm/servicemesh/istio-operator/templates/namespace.yaml14
-rw-r--r--deployments/helm/servicemesh/istio-operator/templates/operator-istio-1.1-crd.yaml565
-rw-r--r--deployments/helm/servicemesh/istio-operator/templates/operator-istio-1.2-crd.yaml2
-rw-r--r--deployments/helm/servicemesh/istio-operator/templates/operator-istio-1.3-crd.yaml889
-rw-r--r--deployments/helm/servicemesh/istio-operator/templates/operator-psp-basic.yaml97
-rw-r--r--deployments/helm/servicemesh/istio-operator/templates/operator-rbac.yaml1
-rw-r--r--deployments/helm/servicemesh/istio-operator/templates/operator-remoteistio-1.1-crd.yaml208
-rw-r--r--deployments/helm/servicemesh/istio-operator/templates/operator-remoteistio-1.2-crd.yaml2
-rw-r--r--deployments/helm/servicemesh/istio-operator/templates/operator-remoteistio-1.3-crd.yaml369
-rw-r--r--deployments/helm/servicemesh/istio-operator/templates/operator-service.yaml5
-rw-r--r--deployments/helm/servicemesh/istio-operator/templates/operator-statefulset.yaml1
-rw-r--r--deployments/helm/servicemesh/istio-operator/values.yaml39
-rw-r--r--deployments/helm/servicemesh/istio/README.md2
-rw-r--r--deployments/helm/servicemesh/istio/istio-instance/templates/istio-sds.yaml3
-rw-r--r--deployments/helm/servicemesh/istio/istio-instance/values.yaml12
-rw-r--r--deployments/helm/servicemesh/rbac/.helmignore22
-rw-r--r--deployments/helm/servicemesh/rbac/Chart.yaml18
-rw-r--r--deployments/helm/servicemesh/rbac/templates/_helpers.tpl69
-rw-r--r--deployments/helm/servicemesh/rbac/templates/rbacenablement.yaml23
-rw-r--r--deployments/helm/servicemesh/rbac/templates/servicerole.yaml24
-rw-r--r--deployments/helm/servicemesh/rbac/templates/servicerolebinding.yaml26
-rw-r--r--deployments/helm/servicemesh/rbac/values.yaml26
-rw-r--r--kud/build/Dockerfile4
-rw-r--r--kud/deployment_infra/images/sriov-daemonset.yml4
-rw-r--r--kud/deployment_infra/playbooks/configure-onap4k8s.yml8
-rw-r--r--kud/deployment_infra/playbooks/configure-ovn.yml8
-rw-r--r--kud/deployment_infra/playbooks/configure-sriov.yml19
-rwxr-xr-xkud/deployment_infra/playbooks/install_iavf_drivers.sh47
-rw-r--r--kud/deployment_infra/playbooks/preconfigure-sriov.yml44
-rw-r--r--kud/deployment_infra/playbooks/sriov_hardware_check.sh4
-rw-r--r--kud/hosting_providers/containerized/README.md6
-rwxr-xr-xkud/hosting_providers/containerized/installer.sh75
-rw-r--r--kud/hosting_providers/vagrant/Vagrantfile4
-rwxr-xr-xkud/hosting_providers/vagrant/installer.sh21
-rwxr-xr-xkud/tests/plugin_edgex.sh14
-rwxr-xr-xkud/tests/plugin_fw.sh14
-rwxr-xr-xkud/tests/sdwan.sh25
-rw-r--r--kud/tests/sdwan/build/Dockerfile_1806_mwan3.tpl26
-rw-r--r--kud/tests/sdwan/build/Dockerfile_1806_mwan3_noproxy.tpl19
-rw-r--r--kud/tests/sdwan/build/README.md10
-rw-r--r--kud/tests/sdwan/build/build_image.sh39
-rw-r--r--kud/tests/sdwan/build/commands.lua43
-rw-r--r--kud/tests/sdwan/build/set_proxy2
-rw-r--r--kud/tests/sdwan/build/system7
-rw-r--r--kud/tests/sdwan/ovn-pod.yml40
-rw-r--r--kud/tests/sdwan/sdwan-openwrt-ovn.yml82
-rw-r--r--kud/tests/sdwan/sdwan.yml44
-rwxr-xr-xkud/tests/sdwan/test.sh120
-rwxr-xr-xkud/tests/sriov.sh4
-rw-r--r--kud/tests/vIPSec/README.md36
-rwxr-xr-xkud/tests/vIPSec/ipsec163
-rwxr-xr-xkud/tests/vIPSec/pktgen77
-rwxr-xr-xkud/tests/vIPSec/remote_ipsec164
-rwxr-xr-xkud/tests/vIPSec/sink48
-rw-r--r--src/Makefile4
-rw-r--r--src/k8splugin/Makefile6
-rw-r--r--src/k8splugin/api/brokerhandler.go18
-rw-r--r--src/k8splugin/api/brokerhandler_test.go44
-rw-r--r--src/k8splugin/api/instancehandler.go80
-rw-r--r--src/k8splugin/api/profilehandler.go11
-rw-r--r--src/k8splugin/api/profilehandler_test.go13
-rw-r--r--src/k8splugin/internal/app/client.go43
-rw-r--r--src/k8splugin/internal/app/instance.go27
-rw-r--r--src/k8splugin/internal/config/config.go6
-rw-r--r--src/k8splugin/internal/db/README.md123
-rw-r--r--src/k8splugin/internal/db/etcd.go7
-rw-r--r--src/k8splugin/internal/db/testing.go14
-rw-r--r--src/k8splugin/internal/logutils/logger.go17
-rw-r--r--src/k8splugin/internal/rb/definition.go48
-rw-r--r--src/k8splugin/internal/rb/profile.go28
-rw-r--r--src/orchestrator/Makefile37
-rw-r--r--src/orchestrator/api/api.go38
-rw-r--r--src/orchestrator/api/projecthandler.go105
-rw-r--r--src/orchestrator/api/projecthandler_test.go228
-rw-r--r--src/orchestrator/api/testing.go31
-rw-r--r--src/orchestrator/cmd/main.go71
-rw-r--r--src/orchestrator/go.mod34
-rw-r--r--src/orchestrator/go.sum362
-rw-r--r--src/orchestrator/internal/auth/auth.go107
-rw-r--r--src/orchestrator/internal/auth/auth_test.go47
-rw-r--r--src/orchestrator/internal/config/config.go130
-rw-r--r--src/orchestrator/internal/config/config_test.go40
-rw-r--r--src/orchestrator/internal/db/README.md123
-rw-r--r--src/orchestrator/internal/db/mock.go94
-rw-r--r--src/orchestrator/internal/db/mongo.go396
-rw-r--r--src/orchestrator/internal/db/mongo_test.go597
-rw-r--r--src/orchestrator/internal/db/store.go106
-rw-r--r--src/orchestrator/internal/db/store_test.go121
-rw-r--r--src/orchestrator/internal/logutils/logger.go28
-rw-r--r--src/orchestrator/internal/project/project.go133
-rw-r--r--src/orchestrator/internal/project/project_test.go177
-rw-r--r--src/orchestrator/tests/certs/auth_test_certificate.pem21
-rw-r--r--src/orchestrator/tests/certs/auth_test_key.pem28
-rw-r--r--src/orchestrator/tests/configs/mock_config.json5
98 files changed, 7017 insertions, 198 deletions
diff --git a/deployments/helm/servicemesh/istio-operator/Chart.yaml b/deployments/helm/servicemesh/istio-operator/Chart.yaml
index 1da83af4..1ef9650f 100644
--- a/deployments/helm/servicemesh/istio-operator/Chart.yaml
+++ b/deployments/helm/servicemesh/istio-operator/Chart.yaml
@@ -1,5 +1,3 @@
-
-
#/*Copyright 2019 Intel Corporation, Inc
# *
# * Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,7 +12,8 @@
# * See the License for the specific language governing permissions and
# * limitations under the License.
# */
+apiVersion: v1
name: istio-operator
-version: 0.0.15
+version: 0.0.22
description: istio-operator manages Istio deployments on Kubernetes
-appVersion: 0.2.1
+appVersion: 0.3.3
diff --git a/deployments/helm/servicemesh/istio-operator/README.md b/deployments/helm/servicemesh/istio-operator/README.md
index 4611a81e..c32e016d 100644
--- a/deployments/helm/servicemesh/istio-operator/README.md
+++ b/deployments/helm/servicemesh/istio-operator/README.md
@@ -14,42 +14,43 @@
* limitations under the License.
*/
+# Istio-operator chart
+
## Prerequisites
- Kubernetes 1.10.0+
## Installing the chart
-To install the chart from local directory:
+To install the chart:
-```
-helm install --name=istio-operator --namespace=istio-system istio-operator
+```bash
+❯ helm install --name=istio-operator --namespace=istio-system istio-operator
```
## Uninstalling the Chart
To uninstall/delete the `istio-operator` release:
-```
-$ helm del --purge istio-operator
+```bash
+❯ helm del --purge istio-operator
```
The command removes all the Kubernetes components associated with the chart and deletes the release.
## Configuration
-The following table lists the configurable parameters of the Banzaicloud Istio Operator chart and their default values.
-
Parameter | Description | Default
--------- | ----------- | -------
`operator.image.repository` | Operator container image repository | `banzaicloud/istio-operator`
-`operator.image.tag` | Operator container image tag | `0.2.1`
+`operator.image.tag` | Operator container image tag | `0.3.3`
`operator.image.pullPolicy` | Operator container image pull policy | `IfNotPresent`
`operator.resources` | CPU/Memory resource requests/limits (YAML) | Memory: `128Mi/256Mi`, CPU: `100m/200m`
-`istioVersion` | Supported Istio version | `1.2`
+`istioVersion` | Supported Istio version | `1.3`
`prometheusMetrics.enabled` | If true, use direct access for Prometheus metrics | `false`
`prometheusMetrics.authProxy.enabled` | If true, use auth proxy for Prometheus metrics | `true`
`prometheusMetrics.authProxy.image.repository` | Auth proxy container image repository | `gcr.io/kubebuilder/kube-rbac-proxy`
`prometheusMetrics.authProxy.image.tag` | Auth proxy container image tag | `v0.4.0`
`prometheusMetrics.authProxy.image.pullPolicy` | Auth proxy container image pull policy | `IfNotPresent`
`rbac.enabled` | Create rbac service account and roles | `true`
+`rbac.psp.enabled` | Create pod security policy and binding | `false`
diff --git a/deployments/helm/servicemesh/istio-operator/templates/authproxy-rbac.yaml b/deployments/helm/servicemesh/istio-operator/templates/authproxy-rbac.yaml
index 8a047e03..aee3aeef 100644
--- a/deployments/helm/servicemesh/istio-operator/templates/authproxy-rbac.yaml
+++ b/deployments/helm/servicemesh/istio-operator/templates/authproxy-rbac.yaml
@@ -3,6 +3,7 @@ apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "istio-operator.fullname" . }}-authproxy
+ namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: {{ include "istio-operator.name" . }}
helm.sh/chart: {{ include "istio-operator.chart" . }}
diff --git a/deployments/helm/servicemesh/istio-operator/templates/authproxy-service.yaml b/deployments/helm/servicemesh/istio-operator/templates/authproxy-service.yaml
index aad8a2be..6f7e6f9a 100644
--- a/deployments/helm/servicemesh/istio-operator/templates/authproxy-service.yaml
+++ b/deployments/helm/servicemesh/istio-operator/templates/authproxy-service.yaml
@@ -3,6 +3,7 @@ apiVersion: v1
kind: Service
metadata:
name: {{ include "istio-operator.fullname" . }}-authproxy
+ namespace: {{ .Release.Namespace }}
annotations:
prometheus.io/port: "8443"
prometheus.io/scheme: https
@@ -20,6 +21,7 @@ spec:
ports:
- name: https
port: 8443
+ protocol: TCP
targetPort: https
selector:
control-plane: controller-manager
diff --git a/deployments/helm/servicemesh/istio-operator/templates/namespace.yaml b/deployments/helm/servicemesh/istio-operator/templates/namespace.yaml
new file mode 100644
index 00000000..daaf21d3
--- /dev/null
+++ b/deployments/helm/servicemesh/istio-operator/templates/namespace.yaml
@@ -0,0 +1,14 @@
+{{- if .Values.useNamespaceResource }}
+apiVersion: v1
+kind: Namespace
+metadata:
+ labels:
+ app: {{ include "istio-operator.name" . }}
+ app.kubernetes.io/name: {{ include "istio-operator.name" . }}
+ helm.sh/chart: {{ include "istio-operator.chart" . }}
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/version: "{{ .Chart.AppVersion | replace "+" "_" }}"
+ app.kubernetes.io/part-of: {{ include "istio-operator.name" . }}
+ name: {{ .Release.Namespace }}
+{{- end }}
diff --git a/deployments/helm/servicemesh/istio-operator/templates/operator-istio-1.1-crd.yaml b/deployments/helm/servicemesh/istio-operator/templates/operator-istio-1.1-crd.yaml
new file mode 100644
index 00000000..287b9879
--- /dev/null
+++ b/deployments/helm/servicemesh/istio-operator/templates/operator-istio-1.1-crd.yaml
@@ -0,0 +1,565 @@
+{{ if eq .Values.istioVersion "1.1" }}
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ name: istios.istio.banzaicloud.io
+ labels:
+ controller-tools.k8s.io: "1.0"
+ app.kubernetes.io/name: {{ include "istio-operator.name" . }}
+ helm.sh/chart: {{ include "istio-operator.chart" . }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/version: {{ .Chart.AppVersion }}
+ app.kubernetes.io/component: operator
+spec:
+ additionalPrinterColumns:
+ - JSONPath: .status.Status
+ description: Status of the resource
+ name: Status
+ type: string
+ - JSONPath: .status.ErrorMessage
+ description: Error message
+ name: Error
+ type: string
+ - JSONPath: .status.GatewayAddress
+ description: Ingress gateways of the resource
+ name: Gateways
+ type: string
+ - JSONPath: .metadata.creationTimestamp
+ name: Age
+ type: date
+ group: istio.banzaicloud.io
+ names:
+ kind: Istio
+ plural: istios
+ 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:
+ autoInjectionNamespaces:
+ description: List of namespaces to label with sidecar auto injection
+ enabled
+ items:
+ type: string
+ type: array
+ citadel:
+ description: Citadel configuration options
+ properties:
+ affinity:
+ type: object
+ caSecretName:
+ type: string
+ enabled:
+ type: boolean
+ image:
+ type: string
+ nodeSelector:
+ type: object
+ resources:
+ type: object
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ controlPlaneSecurityEnabled:
+ description: ControlPlaneSecurityEnabled control plane services are
+ communicating through mTLS
+ type: boolean
+ defaultConfigVisibility:
+ description: Set the default set of namespaces to which services, service
+ entries, virtual services, destination rules should be exported to
+ type: string
+ defaultPodDisruptionBudget:
+ description: Enable pod disruption budget for the control plane, which
+ is used to ensure Istio control plane components are gradually upgraded
+ or recovered
+ properties:
+ enabled:
+ type: boolean
+ type: object
+ defaultResources:
+ description: DefaultResources are applied for all Istio components by
+ default, can be overridden for each component
+ type: object
+ excludeIPRanges:
+ description: ExcludeIPRanges the range where not to capture egress traffic
+ type: string
+ galley:
+ description: Galley configuration options
+ properties:
+ affinity:
+ type: object
+ enabled:
+ type: boolean
+ image:
+ type: string
+ nodeSelector:
+ type: object
+ replicaCount:
+ format: int32
+ type: integer
+ resources:
+ type: object
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ gateways:
+ description: Gateways configuration options
+ properties:
+ egress:
+ properties:
+ affinity:
+ type: object
+ applicationPorts:
+ type: string
+ enabled:
+ type: boolean
+ loadBalancerIP:
+ type: string
+ maxReplicas:
+ format: int32
+ type: integer
+ minReplicas:
+ format: int32
+ type: integer
+ nodeSelector:
+ type: object
+ ports:
+ items:
+ type: object
+ type: array
+ replicaCount:
+ format: int32
+ type: integer
+ requestedNetworkView:
+ type: string
+ resources:
+ type: object
+ sds:
+ properties:
+ enabled:
+ type: boolean
+ image:
+ type: string
+ resources:
+ type: object
+ type: object
+ serviceAnnotations:
+ type: object
+ serviceLabels:
+ type: object
+ serviceType:
+ enum:
+ - ClusterIP
+ - NodePort
+ - LoadBalancer
+ type: string
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ enabled:
+ type: boolean
+ ingress:
+ properties:
+ affinity:
+ type: object
+ applicationPorts:
+ type: string
+ enabled:
+ type: boolean
+ loadBalancerIP:
+ type: string
+ maxReplicas:
+ format: int32
+ type: integer
+ minReplicas:
+ format: int32
+ type: integer
+ nodeSelector:
+ type: object
+ ports:
+ items:
+ type: object
+ type: array
+ replicaCount:
+ format: int32
+ type: integer
+ requestedNetworkView:
+ type: string
+ resources:
+ type: object
+ sds:
+ properties:
+ enabled:
+ type: boolean
+ image:
+ type: string
+ resources:
+ type: object
+ type: object
+ serviceAnnotations:
+ type: object
+ serviceLabels:
+ type: object
+ serviceType:
+ enum:
+ - ClusterIP
+ - NodePort
+ - LoadBalancer
+ type: string
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ type: object
+ imagePullPolicy:
+ description: ImagePullPolicy describes a policy for if/when to pull
+ a container image
+ enum:
+ - Always
+ - Never
+ - IfNotPresent
+ type: string
+ includeIPRanges:
+ description: IncludeIPRanges the range where to capture egress traffic
+ type: string
+ istioCoreDNS:
+ description: Istio CoreDNS provides DNS resolution for services in multi
+ mesh setups
+ properties:
+ affinity:
+ type: object
+ enabled:
+ type: boolean
+ image:
+ type: string
+ nodeSelector:
+ type: object
+ pluginImage:
+ type: string
+ replicaCount:
+ format: int32
+ type: integer
+ resources:
+ type: object
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ meshExpansion:
+ description: If set to true, the pilot and citadel mtls will be exposed
+ on the ingress gateway also the remote istios will be connected through
+ gateways
+ type: boolean
+ mixer:
+ description: Mixer configuration options
+ properties:
+ affinity:
+ type: object
+ enabled:
+ type: boolean
+ image:
+ type: string
+ maxReplicas:
+ format: int32
+ type: integer
+ minReplicas:
+ format: int32
+ type: integer
+ nodeSelector:
+ type: object
+ replicaCount:
+ format: int32
+ type: integer
+ resources:
+ type: object
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ mtls:
+ description: MTLS enables or disables global mTLS
+ type: boolean
+ multiMesh:
+ description: Set to true to connect two or more meshes via their respective
+ ingressgateway services when workloads in each cluster cannot directly
+ talk to one another. All meshes should be using Istio mTLS and must
+ have a shared root CA for this model to work.
+ type: boolean
+ nodeAgent:
+ description: NodeAgent configuration options
+ properties:
+ affinity:
+ type: object
+ enabled:
+ type: boolean
+ image:
+ type: string
+ nodeSelector:
+ type: object
+ resources:
+ type: object
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ outboundTrafficPolicy:
+ description: Set the default behavior of the sidecar for handling outbound
+ traffic from the application (ALLOW_ANY or REGISTRY_ONLY)
+ properties:
+ mode:
+ enum:
+ - ALLOW_ANY
+ - REGISTRY_ONLY
+ type: string
+ type: object
+ pilot:
+ description: Pilot configuration options
+ properties:
+ affinity:
+ type: object
+ enabled:
+ type: boolean
+ image:
+ type: string
+ maxReplicas:
+ format: int32
+ type: integer
+ minReplicas:
+ format: int32
+ type: integer
+ nodeSelector:
+ type: object
+ replicaCount:
+ format: int32
+ type: integer
+ resources:
+ type: object
+ sidecar:
+ type: boolean
+ tolerations:
+ items:
+ type: object
+ type: array
+ traceSampling:
+ format: float
+ type: number
+ type: object
+ proxy:
+ description: Proxy configuration options
+ properties:
+ enableCoreDump:
+ description: If set, newly injected sidecars will have core dumps
+ enabled.
+ type: boolean
+ image:
+ type: string
+ privileged:
+ description: If set to true, istio-proxy container will have privileged
+ securityContext
+ type: boolean
+ resources:
+ type: object
+ type: object
+ proxyInit:
+ description: Proxy Init configuration options
+ properties:
+ image:
+ type: string
+ type: object
+ sds:
+ description: If SDS is configured, mTLS certificates for the sidecars
+ will be distributed through the SecretDiscoveryService instead of
+ using K8S secrets to mount the certificates
+ properties:
+ enabled:
+ description: If set to true, mTLS certificates for the sidecars
+ will be distributed through the SecretDiscoveryService instead
+ of using K8S secrets to mount the certificates.
+ type: boolean
+ udsPath:
+ description: Unix Domain Socket through which envoy communicates
+ with NodeAgent SDS to get key/cert for mTLS. Use secret-mount
+ files instead of SDS if set to empty.
+ type: string
+ useNormalJwt:
+ description: If set to true, envoy will fetch normal k8s service
+ account JWT from '/var/run/secrets/kubernetes.io/serviceaccount/token'
+ (https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod)
+ and pass to sds server, which will be used to request key/cert
+ eventually this flag is ignored if UseTrustworthyJwt is set
+ type: boolean
+ useTrustworthyJwt:
+ description: 'If set to true, Istio will inject volumes mount for
+ k8s service account JWT, so that K8s API server mounts k8s service
+ account JWT to envoy container, which will be used to generate
+ key/cert eventually. (prerequisite: https://kubernetes.io/docs/concepts/storage/volumes/#projected)'
+ type: boolean
+ type: object
+ sidecarInjector:
+ description: SidecarInjector configuration options
+ properties:
+ affinity:
+ type: object
+ autoInjectionPolicyEnabled:
+ description: This controls the 'policy' in the sidecar injector
+ type: boolean
+ enabled:
+ type: boolean
+ image:
+ type: string
+ init:
+ properties:
+ resources:
+ type: object
+ type: object
+ initCNIConfiguration:
+ properties:
+ affinity:
+ type: object
+ binDir:
+ description: Must be the same as the environment’s --cni-bin-dir
+ setting (kubelet parameter)
+ type: string
+ confDir:
+ description: Must be the same as the environment’s --cni-conf-dir
+ setting (kubelet parameter)
+ type: string
+ enabled:
+ description: If true, the privileged initContainer istio-init
+ is not needed to perform the traffic redirect settings for
+ the istio-proxy
+ type: boolean
+ excludeNamespaces:
+ description: List of namespaces to exclude from Istio pod check
+ items:
+ type: string
+ type: array
+ image:
+ type: string
+ logLevel:
+ description: Logging level for CNI binary
+ type: string
+ type: object
+ nodeSelector:
+ type: object
+ replicaCount:
+ format: int32
+ type: integer
+ resources:
+ type: object
+ rewriteAppHTTPProbe:
+ description: If true, sidecar injector will rewrite PodSpec for
+ liveness health check to redirect request to sidecar. This makes
+ liveness check work even when mTLS is enabled.
+ type: boolean
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ tracing:
+ description: Configuration for each of the supported tracers
+ properties:
+ datadog:
+ properties:
+ address:
+ description: Host:Port for submitting traces to the Datadog
+ agent.
+ pattern: ^[^\:]+:[0-9]{1,5}$
+ type: string
+ type: object
+ enabled:
+ type: boolean
+ lightstep:
+ properties:
+ accessToken:
+ description: required for sending data to the pool
+ type: string
+ address:
+ description: the <host>:<port> of the satellite pool
+ pattern: ^[^\:]+:[0-9]{1,5}$
+ type: string
+ cacertPath:
+ description: the path to the file containing the cacert to use
+ when verifying TLS. If secure is true, this is required. If
+ a value is specified then a secret called "lightstep.cacert"
+ must be created in the destination namespace with the key
+ matching the base of the provided cacertPath and the value
+ being the cacert itself.
+ type: string
+ secure:
+ description: specifies whether data should be sent with TLS
+ type: boolean
+ type: object
+ tracer:
+ enum:
+ - zipkin
+ - lightstep
+ - datadog
+ type: string
+ zipkin:
+ properties:
+ address:
+ description: Host:Port for reporting trace data in zipkin format.
+ If not specified, will default to zipkin service (port 9411)
+ in the same namespace as the other istio components.
+ pattern: ^[^\:]+:[0-9]{1,5}$
+ type: string
+ type: object
+ type: object
+ useMCP:
+ description: Use the Mesh Control Protocol (MCP) for configuring Mixer
+ and Pilot. Requires galley.
+ type: boolean
+ version:
+ description: Contains the intended Istio version
+ pattern: ^1.1
+ type: string
+ watchAdapterCRDs:
+ description: Whether or not to establish watches for adapter-specific
+ CRDs
+ type: boolean
+ watchOneNamespace:
+ description: Whether to restrict the applications namespace the controller
+ manages
+ type: boolean
+ required:
+ - version
+ - mtls
+ type: object
+ status:
+ type: object
+ version: v1beta1
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
+{{- end }}
diff --git a/deployments/helm/servicemesh/istio-operator/templates/operator-istio-1.2-crd.yaml b/deployments/helm/servicemesh/istio-operator/templates/operator-istio-1.2-crd.yaml
index b52ffc39..31aae495 100644
--- a/deployments/helm/servicemesh/istio-operator/templates/operator-istio-1.2-crd.yaml
+++ b/deployments/helm/servicemesh/istio-operator/templates/operator-istio-1.2-crd.yaml
@@ -1,4 +1,4 @@
-{{ if eq .Values.istioVersion 1.2 }}
+{{ if eq .Values.istioVersion "1.2" }}
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
diff --git a/deployments/helm/servicemesh/istio-operator/templates/operator-istio-1.3-crd.yaml b/deployments/helm/servicemesh/istio-operator/templates/operator-istio-1.3-crd.yaml
new file mode 100644
index 00000000..540d43bd
--- /dev/null
+++ b/deployments/helm/servicemesh/istio-operator/templates/operator-istio-1.3-crd.yaml
@@ -0,0 +1,889 @@
+{{ if eq .Values.istioVersion "1.3" }}
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ name: istios.istio.banzaicloud.io
+ labels:
+ controller-tools.k8s.io: "1.0"
+ app.kubernetes.io/name: {{ include "istio-operator.name" . }}
+ helm.sh/chart: {{ include "istio-operator.chart" . }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/version: {{ .Chart.AppVersion }}
+ app.kubernetes.io/component: operator
+spec:
+ additionalPrinterColumns:
+ - JSONPath: .status.Status
+ description: Status of the resource
+ name: Status
+ type: string
+ - JSONPath: .status.ErrorMessage
+ description: Error message
+ name: Error
+ type: string
+ - JSONPath: .status.GatewayAddress
+ description: Ingress gateways of the resource
+ name: Gateways
+ type: string
+ - JSONPath: .metadata.creationTimestamp
+ name: Age
+ type: date
+ group: istio.banzaicloud.io
+ names:
+ kind: Istio
+ plural: istios
+ 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:
+ autoInjectionNamespaces:
+ description: List of namespaces to label with sidecar auto injection
+ enabled
+ items:
+ type: string
+ type: array
+ citadel:
+ description: Citadel configuration options
+ properties:
+ affinity:
+ type: object
+ caSecretName:
+ type: string
+ enableNamespacesByDefault:
+ description: 'Determines Citadel default behavior if the ca.istio.io/env
+ or ca.istio.io/override labels are not found on a given namespace. For
+ example: consider a namespace called "target", which has neither
+ the "ca.istio.io/env" nor the "ca.istio.io/override" namespace
+ labels. To decide whether or not to generate secrets for service
+ accounts created in this "target" namespace, Citadel will defer
+ to this option. If the value of this option is "true" in this
+ case, secrets will be generated for the "target" namespace. If
+ the value of this option is "false" Citadel will not generate
+ secrets upon service account creation.'
+ type: boolean
+ enabled:
+ type: boolean
+ healthCheck:
+ description: Enable health checking on the Citadel CSR signing API.
+ https://istio.io/docs/tasks/security/health-check/
+ type: boolean
+ image:
+ type: string
+ maxWorkloadCertTTL:
+ description: Citadel uses a flag max-workload-cert-ttl to control
+ the maximum lifetime for Istio certificates issued to workloads.
+ The default value is 90 days. If workload-cert-ttl on Citadel
+ or node agent is greater than max-workload-cert-ttl, Citadel will
+ fail issuing the certificate.
+ type: string
+ nodeSelector:
+ type: object
+ resources:
+ type: object
+ tolerations:
+ items:
+ type: object
+ type: array
+ workloadCertTTL:
+ description: For the workloads running in Kubernetes, the lifetime
+ of their Istio certificates is controlled by the workload-cert-ttl
+ flag on Citadel. The default value is 90 days. This value should
+ be no greater than max-workload-cert-ttl of Citadel.
+ type: string
+ type: object
+ clusterName:
+ description: Should be set to the name of the cluster this installation
+ will run in. This is required for sidecar injection to properly label
+ proxies
+ type: string
+ controlPlaneSecurityEnabled:
+ description: ControlPlaneSecurityEnabled control plane services are
+ communicating through mTLS
+ type: boolean
+ defaultConfigVisibility:
+ description: Set the default set of namespaces to which services, service
+ entries, virtual services, destination rules should be exported to
+ type: string
+ defaultPodDisruptionBudget:
+ description: Enable pod disruption budget for the control plane, which
+ is used to ensure Istio control plane components are gradually upgraded
+ or recovered
+ properties:
+ enabled:
+ type: boolean
+ type: object
+ defaultResources:
+ description: DefaultResources are applied for all Istio components by
+ default, can be overridden for each component
+ type: object
+ excludeIPRanges:
+ description: ExcludeIPRanges the range where not to capture egress traffic
+ type: string
+ galley:
+ description: Galley configuration options
+ properties:
+ affinity:
+ type: object
+ configValidation:
+ type: boolean
+ enabled:
+ type: boolean
+ image:
+ type: string
+ nodeSelector:
+ type: object
+ replicaCount:
+ format: int32
+ type: integer
+ resources:
+ type: object
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ gateways:
+ description: Gateways configuration options
+ properties:
+ egress:
+ properties:
+ affinity:
+ type: object
+ applicationPorts:
+ type: string
+ enabled:
+ type: boolean
+ loadBalancerIP:
+ type: string
+ maxReplicas:
+ format: int32
+ type: integer
+ minReplicas:
+ format: int32
+ type: integer
+ nodeSelector:
+ type: object
+ ports:
+ items:
+ type: object
+ type: array
+ replicaCount:
+ format: int32
+ type: integer
+ requestedNetworkView:
+ type: string
+ resources:
+ type: object
+ sds:
+ properties:
+ enabled:
+ type: boolean
+ image:
+ type: string
+ resources:
+ type: object
+ type: object
+ serviceAnnotations:
+ type: object
+ serviceLabels:
+ type: object
+ serviceType:
+ enum:
+ - ClusterIP
+ - NodePort
+ - LoadBalancer
+ type: string
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ enabled:
+ type: boolean
+ ingress:
+ properties:
+ affinity:
+ type: object
+ applicationPorts:
+ type: string
+ enabled:
+ type: boolean
+ loadBalancerIP:
+ type: string
+ maxReplicas:
+ format: int32
+ type: integer
+ minReplicas:
+ format: int32
+ type: integer
+ nodeSelector:
+ type: object
+ ports:
+ items:
+ type: object
+ type: array
+ replicaCount:
+ format: int32
+ type: integer
+ requestedNetworkView:
+ type: string
+ resources:
+ type: object
+ sds:
+ properties:
+ enabled:
+ type: boolean
+ image:
+ type: string
+ resources:
+ type: object
+ type: object
+ serviceAnnotations:
+ type: object
+ serviceLabels:
+ type: object
+ serviceType:
+ enum:
+ - ClusterIP
+ - NodePort
+ - LoadBalancer
+ type: string
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ type: object
+ imagePullPolicy:
+ description: ImagePullPolicy describes a policy for if/when to pull
+ a container image
+ enum:
+ - Always
+ - Never
+ - IfNotPresent
+ type: string
+ includeIPRanges:
+ description: IncludeIPRanges the range where to capture egress traffic
+ type: string
+ istioCoreDNS:
+ description: Istio CoreDNS provides DNS resolution for services in multi
+ mesh setups
+ properties:
+ affinity:
+ type: object
+ enabled:
+ type: boolean
+ image:
+ type: string
+ nodeSelector:
+ type: object
+ pluginImage:
+ type: string
+ replicaCount:
+ format: int32
+ type: integer
+ resources:
+ type: object
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ localityLB:
+ description: Locality based load balancing distribution or failover
+ settings.
+ properties:
+ distribute:
+ description: 'Optional: only one of distribute or failover can be
+ set. Explicitly specify loadbalancing weight across different
+ zones and geographical locations. Refer to [Locality weighted
+ load balancing](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/load_balancing/locality_weight)
+ If empty, the locality weight is set according to the endpoints
+ number within it.'
+ items:
+ properties:
+ from:
+ description: Originating locality, '/' separated, e.g. 'region/zone'.
+ type: string
+ to:
+ description: Map of upstream localities to traffic distribution
+ weights. The sum of all weights should be == 100. Any locality
+ not assigned a weight will receive no traffic.
+ type: object
+ type: object
+ type: array
+ enabled:
+ description: If set to true, locality based load balancing will
+ be enabled
+ type: boolean
+ failover:
+ description: 'Optional: only failover or distribute can be set.
+ Explicitly specify the region traffic will land on when endpoints
+ in local region becomes unhealthy. Should be used together with
+ OutlierDetection to detect unhealthy endpoints. Note: if no OutlierDetection
+ specified, this will not take effect.'
+ items:
+ properties:
+ from:
+ description: Originating region.
+ type: string
+ to:
+ description: Destination region the traffic will fail over
+ to when endpoints in the 'from' region becomes unhealthy.
+ type: string
+ type: object
+ type: array
+ type: object
+ meshExpansion:
+ description: If set to true, the pilot and citadel mtls will be exposed
+ on the ingress gateway also the remote istios will be connected through
+ gateways
+ type: boolean
+ meshID:
+ description: Mesh ID means Mesh Identifier. It should be unique within
+ the scope where meshes will interact with each other, but it is not
+ required to be globally/universally unique.
+ type: string
+ trustDomain:
+ description: The domain serves to identify the system with SPIFFE. (default "cluster.local")
+ type: string
+ mixer:
+ description: Mixer configuration options
+ properties:
+ affinity:
+ type: object
+ checksEnabled:
+ type: boolean
+ enabled:
+ type: boolean
+ image:
+ type: string
+ maxReplicas:
+ format: int32
+ type: integer
+ minReplicas:
+ format: int32
+ type: integer
+ multiClusterSupport:
+ description: Turn it on if you use mixer that supports multi cluster
+ telemetry
+ type: boolean
+ nodeSelector:
+ type: object
+ replicaCount:
+ format: int32
+ type: integer
+ reportBatchMaxEntries:
+ description: Set reportBatchMaxEntries to 0 to use the default batching
+ behavior (i.e., every 100 requests). A positive value indicates
+ the number of requests that are batched before telemetry data
+ is sent to the mixer server
+ format: int32
+ type: integer
+ reportBatchMaxTime:
+ description: Set reportBatchMaxTime to 0 to use the default batching
+ behavior (i.e., every 1 second). A positive time value indicates
+ the maximum wait time since the last request will telemetry data
+ be batched before being sent to the mixer server
+ type: string
+ resources:
+ type: object
+ sessionAffinityEnabled:
+ description: Set whether to create a STRICT_DNS type cluster for
+ istio-telemetry.
+ type: boolean
+ stdioAdapterEnabled:
+ description: stdio is a debug adapter in Istio telemetry, it is
+ not recommended for production use
+ type: boolean
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ mixerlessTelemetry:
+ description: Mixerless telemetry configuration
+ properties:
+ enabled:
+ description: If set to true, experimental Mixerless http telemetry
+ will be enabled
+ type: boolean
+ type: object
+ mtls:
+ description: MTLS enables or disables global mTLS
+ type: boolean
+ multiMesh:
+ description: Set to true to connect two or more meshes via their respective
+ ingressgateway services when workloads in each cluster cannot directly
+ talk to one another. All meshes should be using Istio mTLS and must
+ have a shared root CA for this model to work.
+ type: boolean
+ nodeAgent:
+ description: NodeAgent configuration options
+ properties:
+ affinity:
+ type: object
+ enabled:
+ type: boolean
+ image:
+ type: string
+ nodeSelector:
+ type: object
+ resources:
+ type: object
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ outboundTrafficPolicy:
+ description: Set the default behavior of the sidecar for handling outbound
+ traffic from the application (ALLOW_ANY or REGISTRY_ONLY)
+ properties:
+ mode:
+ enum:
+ - ALLOW_ANY
+ - REGISTRY_ONLY
+ type: string
+ type: object
+ pilot:
+ description: Pilot configuration options
+ properties:
+ affinity:
+ type: object
+ enableProtocolSniffing:
+ type: boolean
+ enabled:
+ type: boolean
+ image:
+ type: string
+ maxReplicas:
+ format: int32
+ type: integer
+ minReplicas:
+ format: int32
+ type: integer
+ nodeSelector:
+ type: object
+ replicaCount:
+ format: int32
+ type: integer
+ resources:
+ type: object
+ sidecar:
+ type: boolean
+ tolerations:
+ items:
+ type: object
+ type: array
+ traceSampling:
+ format: float
+ type: number
+ type: object
+ policy:
+ description: Policy configuration options
+ properties:
+ affinity:
+ type: object
+ checksEnabled:
+ type: boolean
+ enabled:
+ type: boolean
+ image:
+ type: string
+ maxReplicas:
+ format: int32
+ type: integer
+ minReplicas:
+ format: int32
+ type: integer
+ nodeSelector:
+ type: object
+ replicaCount:
+ format: int32
+ type: integer
+ resources:
+ type: object
+ sessionAffinityEnabled:
+ description: Set whether to create a STRICT_DNS type cluster for
+ istio-telemetry.
+ type: boolean
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ proxy:
+ description: Proxy configuration options
+ properties:
+ accessLogEncoding:
+ description: Configure the access log for sidecar to JSON or TEXT.
+ enum:
+ - JSON
+ - TEXT
+ type: string
+ accessLogFile:
+ description: 'Configures the access log for each sidecar. Options: ""
+ - disables access log "/dev/stdout" - enables access log'
+ enum:
+ - ""
+ - /dev/stdout
+ type: string
+ accessLogFormat:
+ description: 'Configure how and what fields are displayed in sidecar
+ access log. Setting to empty string will result in default log
+ format. If accessLogEncoding is TEXT, value will be used directly
+ as the log format example: "[%START_TIME%] %REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%
+ %PROTOCOL%\n" If AccessLogEncoding is JSON, value will be parsed
+ as map[string]string example: ''{"start_time": "%START_TIME%",
+ "req_method": "%REQ(:METHOD)%"}'''
+ type: string
+ componentLogLevel:
+ description: Per Component log level for proxy, applies to gateways
+ and sidecars. If a component level is not set, then the "LogLevel"
+ will be used. If left empty, "misc:error" is used.
+ type: string
+ coreDumpImage:
+ description: Image used to enable core dumps. This is only used,
+ when "EnableCoreDump" is set to true.
+ type: string
+ dnsRefreshRate:
+ description: Configure the DNS refresh rate for Envoy cluster of
+ type STRICT_DNS This must be given it terms of seconds. For example,
+ 300s is valid but 5m is invalid.
+ pattern: ^[0-9]{1,5}s$
+ type: string
+ enableCoreDump:
+ description: If set, newly injected sidecars will have core dumps
+ enabled.
+ type: boolean
+ envoyAccessLogService:
+ properties:
+ enabled:
+ type: boolean
+ host:
+ type: string
+ port:
+ format: int32
+ type: integer
+ tcpKeepalive:
+ properties:
+ interval:
+ type: string
+ probes:
+ format: int32
+ type: integer
+ time:
+ type: string
+ type: object
+ tlsSettings:
+ properties:
+ caCertificates:
+ type: string
+ clientCertificate:
+ type: string
+ mode:
+ type: string
+ privateKey:
+ type: string
+ sni:
+ type: string
+ subjectAltNames:
+ items:
+ type: string
+ type: array
+ type: object
+ type: object
+ envoyMetricsService:
+ properties:
+ enabled:
+ type: boolean
+ host:
+ type: string
+ port:
+ format: int32
+ type: integer
+ type: object
+ envoyStatsD:
+ properties:
+ enabled:
+ type: boolean
+ host:
+ type: string
+ port:
+ format: int32
+ type: integer
+ type: object
+ image:
+ type: string
+ logLevel:
+ description: 'Log level for proxy, applies to gateways and sidecars.
+ If left empty, "warning" is used. Expected values are: trace|debug|info|warning|error|critical|off'
+ enum:
+ - trace
+ - debug
+ - info
+ - warning
+ - error
+ - critical
+ - "off"
+ type: string
+ privileged:
+ description: If set to true, istio-proxy container will have privileged
+ securityContext
+ type: boolean
+ protocolDetectionTimeout:
+ type: string
+ resources:
+ type: object
+ type: object
+ proxyInit:
+ description: Proxy Init configuration options
+ properties:
+ image:
+ type: string
+ type: object
+ sds:
+ description: If SDS is configured, mTLS certificates for the sidecars
+ will be distributed through the SecretDiscoveryService instead of
+ using K8S secrets to mount the certificates
+ properties:
+ customTokenDirectory:
+ type: string
+ enabled:
+ description: If set to true, mTLS certificates for the sidecars
+ will be distributed through the SecretDiscoveryService instead
+ of using K8S secrets to mount the certificates.
+ type: boolean
+ tokenAudience:
+ description: "The JWT token for SDS and the aud field of such JWT.
+ See RFC 7519, section 4.1.3. When a CSR is sent from Citadel Agent
+ to the CA (e.g. Citadel), this aud is to make sure the \tJWT is
+ intended for the CA."
+ type: string
+ udsPath:
+ description: Unix Domain Socket through which envoy communicates
+ with NodeAgent SDS to get key/cert for mTLS. Use secret-mount
+ files instead of SDS if set to empty.
+ type: string
+ type: object
+ sidecarInjector:
+ description: SidecarInjector configuration options
+ properties:
+ affinity:
+ type: object
+ alwaysInjectSelector:
+ description: 'AlwaysInjectSelector: Forces the injection on pods
+ whose labels match this selector. It''s an array of label selectors,
+ that will be OR''ed, meaning we will iterate over it and stop
+ at the first match'
+ items:
+ type: object
+ type: array
+ autoInjectionPolicyEnabled:
+ description: This controls the 'policy' in the sidecar injector
+ type: boolean
+ enableNamespacesByDefault:
+ description: This controls whether the webhook looks for namespaces
+ for injection enabled or disabled
+ type: boolean
+ enabled:
+ type: boolean
+ image:
+ type: string
+ init:
+ properties:
+ resources:
+ type: object
+ type: object
+ initCNIConfiguration:
+ properties:
+ affinity:
+ type: object
+ binDir:
+ description: Must be the same as the environment’s --cni-bin-dir
+ setting (kubelet parameter)
+ type: string
+ confDir:
+ description: Must be the same as the environment’s --cni-conf-dir
+ setting (kubelet parameter)
+ type: string
+ enabled:
+ description: If true, the privileged initContainer istio-init
+ is not needed to perform the traffic redirect settings for
+ the istio-proxy
+ type: boolean
+ excludeNamespaces:
+ description: List of namespaces to exclude from Istio pod check
+ items:
+ type: string
+ type: array
+ image:
+ type: string
+ logLevel:
+ description: Logging level for CNI binary
+ type: string
+ type: object
+ neverInjectSelector:
+ description: 'NeverInjectSelector: Refuses the injection on pods
+ whose labels match this selector. It''s an array of label selectors,
+ that will be OR''ed, meaning we will iterate over it and stop
+ at the first match Takes precedence over AlwaysInjectSelector.'
+ items:
+ type: object
+ type: array
+ nodeSelector:
+ type: object
+ replicaCount:
+ format: int32
+ type: integer
+ resources:
+ type: object
+ rewriteAppHTTPProbe:
+ description: If true, sidecar injector will rewrite PodSpec for
+ liveness health check to redirect request to sidecar. This makes
+ liveness check work even when mTLS is enabled.
+ type: boolean
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ telemetry:
+ description: Telemetry configuration options
+ properties:
+ affinity:
+ type: object
+ enabled:
+ type: boolean
+ image:
+ type: string
+ maxReplicas:
+ format: int32
+ type: integer
+ minReplicas:
+ format: int32
+ type: integer
+ nodeSelector:
+ type: object
+ replicaCount:
+ format: int32
+ type: integer
+ reportBatchMaxEntries:
+ description: Set reportBatchMaxEntries to 0 to use the default batching
+ behavior (i.e., every 100 requests). A positive value indicates
+ the number of requests that are batched before telemetry data
+ is sent to the mixer server
+ format: int32
+ type: integer
+ reportBatchMaxTime:
+ description: Set reportBatchMaxTime to 0 to use the default batching
+ behavior (i.e., every 1 second). A positive time value indicates
+ the maximum wait time since the last request will telemetry data
+ be batched before being sent to the mixer server
+ type: string
+ resources:
+ type: object
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ tracing:
+ description: Configuration for each of the supported tracers
+ properties:
+ datadog:
+ properties:
+ address:
+ description: Host:Port for submitting traces to the Datadog
+ agent.
+ pattern: ^[^\:]+:[0-9]{1,5}$
+ type: string
+ type: object
+ enabled:
+ type: boolean
+ lightstep:
+ properties:
+ accessToken:
+ description: required for sending data to the pool
+ type: string
+ address:
+ description: the <host>:<port> of the satellite pool
+ pattern: ^[^\:]+:[0-9]{1,5}$
+ type: string
+ cacertPath:
+ description: the path to the file containing the cacert to use
+ when verifying TLS. If secure is true, this is required. If
+ a value is specified then a secret called "lightstep.cacert"
+ must be created in the destination namespace with the key
+ matching the base of the provided cacertPath and the value
+ being the cacert itself.
+ type: string
+ secure:
+ description: specifies whether data should be sent with TLS
+ type: boolean
+ type: object
+ stackdriver:
+ type: object
+ tracer:
+ enum:
+ - zipkin
+ - lightstep
+ - datadog
+ type: string
+ zipkin:
+ properties:
+ address:
+ description: Host:Port for reporting trace data in zipkin format.
+ If not specified, will default to zipkin service (port 9411)
+ in the same namespace as the other istio components.
+ pattern: ^[^\:]+:[0-9]{1,5}$
+ type: string
+ type: object
+ type: object
+ useMCP:
+ description: Use the Mesh Control Protocol (MCP) for configuring Mixer
+ and Pilot. Requires galley.
+ type: boolean
+ version:
+ description: Contains the intended Istio version
+ pattern: ^1.3
+ type: string
+ watchAdapterCRDs:
+ description: Whether or not to establish watches for adapter-specific
+ CRDs
+ type: boolean
+ watchOneNamespace:
+ description: Whether to restrict the applications namespace the controller
+ manages
+ type: boolean
+ required:
+ - version
+ - mtls
+ type: object
+ status:
+ type: object
+ version: v1beta1
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
+{{- end }}
diff --git a/deployments/helm/servicemesh/istio-operator/templates/operator-psp-basic.yaml b/deployments/helm/servicemesh/istio-operator/templates/operator-psp-basic.yaml
new file mode 100644
index 00000000..b6e5eac6
--- /dev/null
+++ b/deployments/helm/servicemesh/istio-operator/templates/operator-psp-basic.yaml
@@ -0,0 +1,97 @@
+{{- if and .Values.rbac.enabled .Values.rbac.psp.enabled }}
+apiVersion: policy/v1beta1
+kind: PodSecurityPolicy
+metadata:
+ name: {{ include "istio-operator.fullname" . }}-basic
+ labels:
+ app.kubernetes.io/name: {{ include "istio-operator.name" . }}
+ helm.sh/chart: {{ include "istio-operator.chart" . }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/version: {{ .Chart.AppVersion }}
+ app.kubernetes.io/component: operator
+spec:
+ fsGroup:
+ rule: RunAsAny
+ runAsUser:
+ rule: RunAsAny
+ seLinux:
+ rule: RunAsAny
+ supplementalGroups:
+ rule: RunAsAny
+ volumes:
+ - secret
+ - configMap
+ - emptyDir
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: psp:{{ include "istio-operator.fullname" . }}-basic
+ labels:
+ app.kubernetes.io/name: {{ include "istio-operator.name" . }}
+ helm.sh/chart: {{ include "istio-operator.chart" . }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/version: {{ .Chart.AppVersion }}
+ app.kubernetes.io/component: operator
+rules:
+- apiGroups:
+ - policy
+ resourceNames:
+ - {{ include "istio-operator.fullname" . }}-basic
+ resources:
+ - podsecuritypolicies
+ verbs:
+ - use
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: psp:{{ include "istio-operator.fullname" . }}-basic
+ labels:
+ app.kubernetes.io/name: {{ include "istio-operator.name" . }}
+ helm.sh/chart: {{ include "istio-operator.chart" . }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/version: {{ .Chart.AppVersion }}
+ app.kubernetes.io/component: operator
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: psp:{{ include "istio-operator.fullname" . }}-basic
+subjects:
+ - kind: ServiceAccount
+ name: istio-citadel-service-account
+ namespace: {{ .Release.Namespace }}
+ - kind: ServiceAccount
+ name: istio-galley-service-account
+ namespace: {{ .Release.Namespace }}
+ - kind: ServiceAccount
+ name: istio-egressgateway-service-account
+ namespace: {{ .Release.Namespace }}
+ - kind: ServiceAccount
+ name: istio-ingressgateway-service-account
+ namespace: {{ .Release.Namespace }}
+ - kind: ServiceAccount
+ name: istio-mixer-service-account
+ namespace: {{ .Release.Namespace }}
+ - kind: ServiceAccount
+ name: istio-operator-authproxy
+ namespace: {{ .Release.Namespace }}
+ - kind: ServiceAccount
+ name: istio-pilot-service-account
+ namespace: {{ .Release.Namespace }}
+ - kind: ServiceAccount
+ name: istio-sidecar-injector-service-account
+ namespace: {{ .Release.Namespace }}
+ - kind: ServiceAccount
+ name: istiocoredns-service-account
+ namespace: {{ .Release.Namespace }}
+ - kind: ServiceAccount
+ name: istio-nodeagent-service-account
+ namespace: {{ .Release.Namespace }}
+ - kind: ServiceAccount
+ name: istio-operator-operator
+ namespace: {{ .Release.Namespace }}
+{{- end }}
diff --git a/deployments/helm/servicemesh/istio-operator/templates/operator-rbac.yaml b/deployments/helm/servicemesh/istio-operator/templates/operator-rbac.yaml
index d506ee41..e25462af 100644
--- a/deployments/helm/servicemesh/istio-operator/templates/operator-rbac.yaml
+++ b/deployments/helm/servicemesh/istio-operator/templates/operator-rbac.yaml
@@ -3,6 +3,7 @@ apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "istio-operator.fullname" . }}-operator
+ namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: {{ include "istio-operator.name" . }}
helm.sh/chart: {{ include "istio-operator.chart" . }}
diff --git a/deployments/helm/servicemesh/istio-operator/templates/operator-remoteistio-1.1-crd.yaml b/deployments/helm/servicemesh/istio-operator/templates/operator-remoteistio-1.1-crd.yaml
new file mode 100644
index 00000000..f298ccde
--- /dev/null
+++ b/deployments/helm/servicemesh/istio-operator/templates/operator-remoteistio-1.1-crd.yaml
@@ -0,0 +1,208 @@
+{{ if eq .Values.istioVersion "1.1" }}
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ name: remoteistios.istio.banzaicloud.io
+ labels:
+ controller-tools.k8s.io: "1.0"
+ app.kubernetes.io/name: {{ include "istio-operator.name" . }}
+ helm.sh/chart: {{ include "istio-operator.chart" . }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/version: {{ .Chart.AppVersion }}
+ app.kubernetes.io/component: operator
+spec:
+ additionalPrinterColumns:
+ - JSONPath: .status.Status
+ description: Status of the resource
+ name: Status
+ type: string
+ - JSONPath: .status.ErrorMessage
+ description: Error message
+ name: Error
+ type: string
+ - JSONPath: .status.GatewayAddress
+ description: Ingress gateways of the resource
+ name: Gateways
+ type: string
+ - JSONPath: .metadata.creationTimestamp
+ name: Age
+ type: date
+ group: istio.banzaicloud.io
+ names:
+ kind: RemoteIstio
+ plural: remoteistios
+ 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:
+ autoInjectionNamespaces:
+ description: List of namespaces to label with sidecar auto injection
+ enabled
+ items:
+ type: string
+ type: array
+ citadel:
+ description: Citadel configuration options
+ properties:
+ affinity:
+ type: object
+ caSecretName:
+ type: string
+ enabled:
+ type: boolean
+ image:
+ type: string
+ nodeSelector:
+ type: object
+ resources:
+ type: object
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ defaultResources:
+ description: DefaultResources are applied for all Istio components by
+ default, can be overridden for each component
+ type: object
+ enabledServices:
+ description: EnabledServices the Istio component services replicated
+ to remote side
+ items:
+ properties:
+ labelSelector:
+ type: string
+ name:
+ type: string
+ podIPs:
+ items:
+ type: string
+ type: array
+ ports:
+ items:
+ type: object
+ type: array
+ required:
+ - name
+ type: object
+ type: array
+ excludeIPRanges:
+ description: ExcludeIPRanges the range where not to capture egress traffic
+ type: string
+ includeIPRanges:
+ description: IncludeIPRanges the range where to capture egress traffic
+ type: string
+ proxy:
+ description: Proxy configuration options
+ properties:
+ enableCoreDump:
+ description: If set, newly injected sidecars will have core dumps
+ enabled.
+ type: boolean
+ image:
+ type: string
+ privileged:
+ description: If set to true, istio-proxy container will have privileged
+ securityContext
+ type: boolean
+ resources:
+ type: object
+ type: object
+ proxyInit:
+ description: Proxy Init configuration options
+ properties:
+ image:
+ type: string
+ type: object
+ sidecarInjector:
+ description: SidecarInjector configuration options
+ properties:
+ affinity:
+ type: object
+ autoInjectionPolicyEnabled:
+ description: This controls the 'policy' in the sidecar injector
+ type: boolean
+ enabled:
+ type: boolean
+ image:
+ type: string
+ init:
+ properties:
+ resources:
+ type: object
+ type: object
+ initCNIConfiguration:
+ properties:
+ affinity:
+ type: object
+ binDir:
+ description: Must be the same as the environment’s --cni-bin-dir
+ setting (kubelet parameter)
+ type: string
+ confDir:
+ description: Must be the same as the environment’s --cni-conf-dir
+ setting (kubelet parameter)
+ type: string
+ enabled:
+ description: If true, the privileged initContainer istio-init
+ is not needed to perform the traffic redirect settings for
+ the istio-proxy
+ type: boolean
+ excludeNamespaces:
+ description: List of namespaces to exclude from Istio pod check
+ items:
+ type: string
+ type: array
+ image:
+ type: string
+ logLevel:
+ description: Logging level for CNI binary
+ type: string
+ type: object
+ nodeSelector:
+ type: object
+ replicaCount:
+ format: int32
+ type: integer
+ resources:
+ type: object
+ rewriteAppHTTPProbe:
+ description: If true, sidecar injector will rewrite PodSpec for
+ liveness health check to redirect request to sidecar. This makes
+ liveness check work even when mTLS is enabled.
+ type: boolean
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ required:
+ - enabledServices
+ type: object
+ status:
+ type: object
+ version: v1beta1
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
+{{- end }}
diff --git a/deployments/helm/servicemesh/istio-operator/templates/operator-remoteistio-1.2-crd.yaml b/deployments/helm/servicemesh/istio-operator/templates/operator-remoteistio-1.2-crd.yaml
index 37741898..6df6ba72 100644
--- a/deployments/helm/servicemesh/istio-operator/templates/operator-remoteistio-1.2-crd.yaml
+++ b/deployments/helm/servicemesh/istio-operator/templates/operator-remoteistio-1.2-crd.yaml
@@ -1,4 +1,4 @@
-{{ if eq .Values.istioVersion 1.2 }}
+{{ if eq .Values.istioVersion "1.2" }}
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
diff --git a/deployments/helm/servicemesh/istio-operator/templates/operator-remoteistio-1.3-crd.yaml b/deployments/helm/servicemesh/istio-operator/templates/operator-remoteistio-1.3-crd.yaml
new file mode 100644
index 00000000..bb411904
--- /dev/null
+++ b/deployments/helm/servicemesh/istio-operator/templates/operator-remoteistio-1.3-crd.yaml
@@ -0,0 +1,369 @@
+{{ if eq .Values.istioVersion "1.3" }}
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ name: remoteistios.istio.banzaicloud.io
+ labels:
+ controller-tools.k8s.io: "1.0"
+ app.kubernetes.io/name: {{ include "istio-operator.name" . }}
+ helm.sh/chart: {{ include "istio-operator.chart" . }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/version: {{ .Chart.AppVersion }}
+ app.kubernetes.io/component: operator
+spec:
+ additionalPrinterColumns:
+ - JSONPath: .status.Status
+ description: Status of the resource
+ name: Status
+ type: string
+ - JSONPath: .status.ErrorMessage
+ description: Error message
+ name: Error
+ type: string
+ - JSONPath: .status.GatewayAddress
+ description: Ingress gateways of the resource
+ name: Gateways
+ type: string
+ - JSONPath: .metadata.creationTimestamp
+ name: Age
+ type: date
+ group: istio.banzaicloud.io
+ names:
+ kind: RemoteIstio
+ plural: remoteistios
+ 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:
+ autoInjectionNamespaces:
+ description: List of namespaces to label with sidecar auto injection
+ enabled
+ items:
+ type: string
+ type: array
+ citadel:
+ description: Citadel configuration options
+ properties:
+ affinity:
+ type: object
+ caSecretName:
+ type: string
+ enableNamespacesByDefault:
+ description: 'Determines Citadel default behavior if the ca.istio.io/env
+ or ca.istio.io/override labels are not found on a given namespace. For
+ example: consider a namespace called "target", which has neither
+ the "ca.istio.io/env" nor the "ca.istio.io/override" namespace
+ labels. To decide whether or not to generate secrets for service
+ accounts created in this "target" namespace, Citadel will defer
+ to this option. If the value of this option is "true" in this
+ case, secrets will be generated for the "target" namespace. If
+ the value of this option is "false" Citadel will not generate
+ secrets upon service account creation.'
+ type: boolean
+ enabled:
+ type: boolean
+ healthCheck:
+ description: Enable health checking on the Citadel CSR signing API.
+ https://istio.io/docs/tasks/security/health-check/
+ type: boolean
+ image:
+ type: string
+ maxWorkloadCertTTL:
+ description: Citadel uses a flag max-workload-cert-ttl to control
+ the maximum lifetime for Istio certificates issued to workloads.
+ The default value is 90 days. If workload-cert-ttl on Citadel
+ or node agent is greater than max-workload-cert-ttl, Citadel will
+ fail issuing the certificate.
+ type: string
+ nodeSelector:
+ type: object
+ resources:
+ type: object
+ tolerations:
+ items:
+ type: object
+ type: array
+ workloadCertTTL:
+ description: For the workloads running in Kubernetes, the lifetime
+ of their Istio certificates is controlled by the workload-cert-ttl
+ flag on Citadel. The default value is 90 days. This value should
+ be no greater than max-workload-cert-ttl of Citadel.
+ type: string
+ type: object
+ clusterName:
+ description: Should be set to the name of the cluster, this is required
+ for sidecar injection to properly label proxies
+ type: string
+ defaultResources:
+ description: DefaultResources are applied for all Istio components by
+ default, can be overridden for each component
+ type: object
+ enabledServices:
+ description: EnabledServices the Istio component services replicated
+ to remote side
+ items:
+ properties:
+ labelSelector:
+ type: string
+ name:
+ type: string
+ podIPs:
+ items:
+ type: string
+ type: array
+ ports:
+ items:
+ type: object
+ type: array
+ required:
+ - name
+ type: object
+ type: array
+ excludeIPRanges:
+ description: ExcludeIPRanges the range where not to capture egress traffic
+ type: string
+ includeIPRanges:
+ description: IncludeIPRanges the range where to capture egress traffic
+ type: string
+ proxy:
+ description: Proxy configuration options
+ properties:
+ accessLogEncoding:
+ description: Configure the access log for sidecar to JSON or TEXT.
+ enum:
+ - JSON
+ - TEXT
+ type: string
+ accessLogFile:
+ description: 'Configures the access log for each sidecar. Options: ""
+ - disables access log "/dev/stdout" - enables access log'
+ enum:
+ - ""
+ - /dev/stdout
+ type: string
+ accessLogFormat:
+ description: 'Configure how and what fields are displayed in sidecar
+ access log. Setting to empty string will result in default log
+ format. If accessLogEncoding is TEXT, value will be used directly
+ as the log format example: "[%START_TIME%] %REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%
+ %PROTOCOL%\n" If AccessLogEncoding is JSON, value will be parsed
+ as map[string]string example: ''{"start_time": "%START_TIME%",
+ "req_method": "%REQ(:METHOD)%"}'''
+ type: string
+ componentLogLevel:
+ description: Per Component log level for proxy, applies to gateways
+ and sidecars. If a component level is not set, then the "LogLevel"
+ will be used. If left empty, "misc:error" is used.
+ type: string
+ coreDumpImage:
+ description: Image used to enable core dumps. This is only used,
+ when "EnableCoreDump" is set to true.
+ type: string
+ dnsRefreshRate:
+ description: Configure the DNS refresh rate for Envoy cluster of
+ type STRICT_DNS This must be given it terms of seconds. For example,
+ 300s is valid but 5m is invalid.
+ pattern: ^[0-9]{1,5}s$
+ type: string
+ enableCoreDump:
+ description: If set, newly injected sidecars will have core dumps
+ enabled.
+ type: boolean
+ envoyAccessLogService:
+ properties:
+ enabled:
+ type: boolean
+ host:
+ type: string
+ port:
+ format: int32
+ type: integer
+ tcpKeepalive:
+ properties:
+ interval:
+ type: string
+ probes:
+ format: int32
+ type: integer
+ time:
+ type: string
+ type: object
+ tlsSettings:
+ properties:
+ caCertificates:
+ type: string
+ clientCertificate:
+ type: string
+ mode:
+ type: string
+ privateKey:
+ type: string
+ sni:
+ type: string
+ subjectAltNames:
+ items:
+ type: string
+ type: array
+ type: object
+ type: object
+ envoyMetricsService:
+ properties:
+ enabled:
+ type: boolean
+ host:
+ type: string
+ port:
+ format: int32
+ type: integer
+ type: object
+ envoyStatsD:
+ properties:
+ enabled:
+ type: boolean
+ host:
+ type: string
+ port:
+ format: int32
+ type: integer
+ type: object
+ image:
+ type: string
+ logLevel:
+ description: 'Log level for proxy, applies to gateways and sidecars.
+ If left empty, "warning" is used. Expected values are: trace|debug|info|warning|error|critical|off'
+ enum:
+ - trace
+ - debug
+ - info
+ - warning
+ - error
+ - critical
+ - "off"
+ type: string
+ privileged:
+ description: If set to true, istio-proxy container will have privileged
+ securityContext
+ type: boolean
+ protocolDetectionTimeout:
+ type: string
+ resources:
+ type: object
+ type: object
+ proxyInit:
+ description: Proxy Init configuration options
+ properties:
+ image:
+ type: string
+ type: object
+ sidecarInjector:
+ description: SidecarInjector configuration options
+ properties:
+ affinity:
+ type: object
+ alwaysInjectSelector:
+ description: 'AlwaysInjectSelector: Forces the injection on pods
+ whose labels match this selector. It''s an array of label selectors,
+ that will be OR''ed, meaning we will iterate over it and stop
+ at the first match'
+ items:
+ type: object
+ type: array
+ autoInjectionPolicyEnabled:
+ description: This controls the 'policy' in the sidecar injector
+ type: boolean
+ enableNamespacesByDefault:
+ description: This controls whether the webhook looks for namespaces
+ for injection enabled or disabled
+ type: boolean
+ enabled:
+ type: boolean
+ image:
+ type: string
+ init:
+ properties:
+ resources:
+ type: object
+ type: object
+ initCNIConfiguration:
+ properties:
+ affinity:
+ type: object
+ binDir:
+ description: Must be the same as the environment’s --cni-bin-dir
+ setting (kubelet parameter)
+ type: string
+ confDir:
+ description: Must be the same as the environment’s --cni-conf-dir
+ setting (kubelet parameter)
+ type: string
+ enabled:
+ description: If true, the privileged initContainer istio-init
+ is not needed to perform the traffic redirect settings for
+ the istio-proxy
+ type: boolean
+ excludeNamespaces:
+ description: List of namespaces to exclude from Istio pod check
+ items:
+ type: string
+ type: array
+ image:
+ type: string
+ logLevel:
+ description: Logging level for CNI binary
+ type: string
+ type: object
+ neverInjectSelector:
+ description: 'NeverInjectSelector: Refuses the injection on pods
+ whose labels match this selector. It''s an array of label selectors,
+ that will be OR''ed, meaning we will iterate over it and stop
+ at the first match Takes precedence over AlwaysInjectSelector.'
+ items:
+ type: object
+ type: array
+ nodeSelector:
+ type: object
+ replicaCount:
+ format: int32
+ type: integer
+ resources:
+ type: object
+ rewriteAppHTTPProbe:
+ description: If true, sidecar injector will rewrite PodSpec for
+ liveness health check to redirect request to sidecar. This makes
+ liveness check work even when mTLS is enabled.
+ type: boolean
+ tolerations:
+ items:
+ type: object
+ type: array
+ type: object
+ required:
+ - enabledServices
+ type: object
+ status:
+ type: object
+ version: v1beta1
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
+{{- end }}
diff --git a/deployments/helm/servicemesh/istio-operator/templates/operator-service.yaml b/deployments/helm/servicemesh/istio-operator/templates/operator-service.yaml
index 04ffc835..bc49dcfd 100644
--- a/deployments/helm/servicemesh/istio-operator/templates/operator-service.yaml
+++ b/deployments/helm/servicemesh/istio-operator/templates/operator-service.yaml
@@ -2,6 +2,7 @@ apiVersion: v1
kind: Service
metadata:
name: "{{ include "istio-operator.fullname" . }}-operator"
+ namespace: {{ .Release.Namespace }}
{{- if and .Values.prometheusMetrics.enabled (not .Values.prometheusMetrics.authProxy.enabled) }}
annotations:
prometheus.io/scrape: "true"
@@ -26,8 +27,12 @@ spec:
app.kubernetes.io/component: operator
ports:
- name: https
+ protocol: TCP
port: 443
+ targetPort: 443
{{- if and .Values.prometheusMetrics.enabled (not .Values.prometheusMetrics.authProxy.enabled) }}
- name: metrics
+ protocol: TCP
port: 8080
+ targetPort: 8080
{{- end }}
diff --git a/deployments/helm/servicemesh/istio-operator/templates/operator-statefulset.yaml b/deployments/helm/servicemesh/istio-operator/templates/operator-statefulset.yaml
index 9e90ee80..0b59d23f 100644
--- a/deployments/helm/servicemesh/istio-operator/templates/operator-statefulset.yaml
+++ b/deployments/helm/servicemesh/istio-operator/templates/operator-statefulset.yaml
@@ -2,6 +2,7 @@ apiVersion: apps/v1
kind: StatefulSet
metadata:
name: "{{ include "istio-operator.fullname" . }}-operator"
+ namespace: {{ .Release.Namespace }}
labels:
control-plane: controller-manager
controller-tools.k8s.io: "1.0"
diff --git a/deployments/helm/servicemesh/istio-operator/values.yaml b/deployments/helm/servicemesh/istio-operator/values.yaml
index cb937c11..f1ff6575 100644
--- a/deployments/helm/servicemesh/istio-operator/values.yaml
+++ b/deployments/helm/servicemesh/istio-operator/values.yaml
@@ -1,12 +1,26 @@
-
-
+#/*Copyright 2019 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.
+# */
+
+# Default values for istio-operator.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
operator:
image:
repository: banzaicloud/istio-operator
- tag: 0.2.1
+ tag: 0.3.3
pullPolicy: IfNotPresent
resources:
limits:
@@ -16,21 +30,30 @@ operator:
cpu: 100m
memory: 128Mi
-istioVersion: 1.2
+istioVersion: "1.3"
-## Prometheus Metrics
+# If you want the operator to expose the /metrics
prometheusMetrics:
enabled: false
-# Enable or disable the auth proxy (https://github.com/brancz/kube-rbac-proxy)
-# which protects your /metrics endpoint.
+ # Enable or disable the auth proxy (https://github.com/brancz/kube-rbac-proxy)
+ # which protects your /metrics endpoint.
authProxy:
- enabled: false
+ enabled: true
+ image:
+ repository: gcr.io/kubebuilder/kube-rbac-proxy
+ tag: v0.4.0
+ pullPolicy: IfNotPresent
## Role Based Access
## Ref: https://kubernetes.io/docs/admin/authorization/rbac/
##
rbac:
enabled: true
+ ## Pod Security Policies
+ ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/
+ ##
+ psp:
+ enabled: true
nameOverride: ""
fullnameOverride: ""
diff --git a/deployments/helm/servicemesh/istio/README.md b/deployments/helm/servicemesh/istio/README.md
index 8fcba4f8..868c116a 100644
--- a/deployments/helm/servicemesh/istio/README.md
+++ b/deployments/helm/servicemesh/istio/README.md
@@ -17,4 +17,6 @@
# Steps for Instaling Istio with Istio- Operator
# Step 1 - Add the helm chart to install Istio in sds configuration
+# NOTE - Edit the namespaces in istio/istio-instance/values.yaml
+# to enable istio-injection
helm install istio-instance --name istio --namespace istio-system
diff --git a/deployments/helm/servicemesh/istio/istio-instance/templates/istio-sds.yaml b/deployments/helm/servicemesh/istio/istio-instance/templates/istio-sds.yaml
index 8c440a4e..4a8b11b2 100644
--- a/deployments/helm/servicemesh/istio/istio-instance/templates/istio-sds.yaml
+++ b/deployments/helm/servicemesh/istio/istio-instance/templates/istio-sds.yaml
@@ -29,8 +29,7 @@ spec:
sds:
enabled: {{ .Values.spec.sds.enabled }}
udsPath: {{ .Values.spec.sds.udsPath | quote }}
- useTrustworthyJwt: {{ .Values.spec.sds.useTrustworthyJwt }}
- useNormalJwt: {{ .Values.spec.sds.useNormalJwt }}
+ tokenAudience: {{ .Values.spec.sds.tokenAudience | quote}}
gateways:
enabled: {{ .Values.spec.gateways.enabled }}
ingress:
diff --git a/deployments/helm/servicemesh/istio/istio-instance/values.yaml b/deployments/helm/servicemesh/istio/istio-instance/values.yaml
index 091999ac..dd77ef1b 100644
--- a/deployments/helm/servicemesh/istio/istio-instance/values.yaml
+++ b/deployments/helm/servicemesh/istio/istio-instance/values.yaml
@@ -15,25 +15,25 @@
# * limitations under the License.
# */
#Declare variables to be passed into Istio SDS template file.
+#NOTE : EDit the namespace for which you need Istio injection
metadata:
name: "istio-sample"
spec:
- version: "1.2.2"
+ version: "1.3.3"
mtls: true
autoInjectionNamespaces:
- -
+ - "default"
sds:
enabled: true
udsPath: "unix:/var/run/sds/uds_path"
- useTrustworthyJwt: false
- useNormalJwt: true
+ tokenAudience: "istio-ca"
gateways:
enabled: true
ingress:
enabled: true
sds:
enabled: true
- image: "docker.io/istio/node-agent-k8s:1.2.2"
+ image: "docker.io/istio/node-agent-k8s:1.3.3"
nodeAgent:
enabled: true
- image : "docker.io/istio/node-agent-k8s:1.2.2"
+ image : "docker.io/istio/node-agent-k8s:1.3.3"
diff --git a/deployments/helm/servicemesh/rbac/.helmignore b/deployments/helm/servicemesh/rbac/.helmignore
new file mode 100644
index 00000000..50af0317
--- /dev/null
+++ b/deployments/helm/servicemesh/rbac/.helmignore
@@ -0,0 +1,22 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/deployments/helm/servicemesh/rbac/Chart.yaml b/deployments/helm/servicemesh/rbac/Chart.yaml
new file mode 100644
index 00000000..8b3bfdc1
--- /dev/null
+++ b/deployments/helm/servicemesh/rbac/Chart.yaml
@@ -0,0 +1,18 @@
+# Copyright @ 2019 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.
+apiVersion: v1
+appVersion: "1.0"
+description: A Helm chart for Istio Rbac Rules
+name: rbac
+version: 0.1.0
diff --git a/deployments/helm/servicemesh/rbac/templates/_helpers.tpl b/deployments/helm/servicemesh/rbac/templates/_helpers.tpl
new file mode 100644
index 00000000..866dd71e
--- /dev/null
+++ b/deployments/helm/servicemesh/rbac/templates/_helpers.tpl
@@ -0,0 +1,69 @@
+# Copyright @ 2019 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.
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "rbacname" -}}
+ {{ default "default" .Values.rbacName }}
+{{- end -}}
+
+{{- define "servicerolename" -}}
+ {{ default "default" .Values.serviceRoleRule.name }}
+{{- end -}}
+
+{{- define "servicerolebindingname" -}}
+ {{ default "default" .Values.serviceRoleBinding.name }}
+{{- end -}}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Common labels
+*/}}
+{{- define "labels" -}}
+app.kubernetes.io/name: {{ include "name" . }}
+helm.sh/chart: {{ include "chart" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end -}}
diff --git a/deployments/helm/servicemesh/rbac/templates/rbacenablement.yaml b/deployments/helm/servicemesh/rbac/templates/rbacenablement.yaml
new file mode 100644
index 00000000..486993a3
--- /dev/null
+++ b/deployments/helm/servicemesh/rbac/templates/rbacenablement.yaml
@@ -0,0 +1,23 @@
+#{{/*
+# Copyright @ 2019 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
+# imitations under the License.
+#*/}}
+apiVersion: "rbac.istio.io/v1alpha1"
+kind: ClusterRbacConfig
+metadata:
+ name: {{ template "rbacname" . }}
+spec:
+ mode: 'ON_WITH_INCLUSION'
+ inclusion:
+ namespaces: [{{ .Values.namespace | quote }}]
+ enforcement_mode: {{ .Values.policyEnforcementMode }}
diff --git a/deployments/helm/servicemesh/rbac/templates/servicerole.yaml b/deployments/helm/servicemesh/rbac/templates/servicerole.yaml
new file mode 100644
index 00000000..d2791379
--- /dev/null
+++ b/deployments/helm/servicemesh/rbac/templates/servicerole.yaml
@@ -0,0 +1,24 @@
+#{{/*
+# Copyright @ 2019 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
+# imitations under the License.
+#*/}}
+apiVersion: "rbac.istio.io/v1alpha1"
+kind: ServiceRole
+metadata:
+ name: {{ template "servicerolename" . }}
+ namespace: {{ .Values.namespace }}
+spec:
+ rules:
+ - services: [{{ .Values.serviceRoleRule.services | quote }}]
+ paths: [{{ .Values.serviceRoleRule.paths | quote }}]
+ methods: {{ .Values.serviceRoleRule.methods| toJson }}
diff --git a/deployments/helm/servicemesh/rbac/templates/servicerolebinding.yaml b/deployments/helm/servicemesh/rbac/templates/servicerolebinding.yaml
new file mode 100644
index 00000000..c17adf7e
--- /dev/null
+++ b/deployments/helm/servicemesh/rbac/templates/servicerolebinding.yaml
@@ -0,0 +1,26 @@
+#{{/*
+# Copyright @ 2019 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
+# imitations under the License.
+#*/}}
+apiVersion: "rbac.istio.io/v1alpha1"
+kind: ServiceRoleBinding
+metadata:
+ name: {{ template "servicerolebindingname" . }}
+ namespace: {{ .Values.namespace }}
+spec:
+ subjects:
+ - user: {{ .Values.serviceRoleBinding.users | quote }}
+ roleRef:
+ kind: ServiceRole
+ name: {{ .Values.serviceRoleBinding.serviceRoleName | quote }}
+ mode: {{ .Values.policyEnforcementMode }}
diff --git a/deployments/helm/servicemesh/rbac/values.yaml b/deployments/helm/servicemesh/rbac/values.yaml
new file mode 100644
index 00000000..45208ffa
--- /dev/null
+++ b/deployments/helm/servicemesh/rbac/values.yaml
@@ -0,0 +1,26 @@
+# Copyright @ 2019 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.
+
+namespace: multicloud
+policyEnforcementMode: PERMISSIVE
+rbacName: ""
+serviceRoleRule:
+ name: ""
+ service: multicloud-k8s.multicloud.svc.cluster.local
+ paths: "*"
+ methods: [ "GET","HEAD"]
+serviceRoleBinding:
+ name: ""
+ users: "*"
+ serviceRoleName: ""
diff --git a/kud/build/Dockerfile b/kud/build/Dockerfile
index da100bb7..38c63295 100644
--- a/kud/build/Dockerfile
+++ b/kud/build/Dockerfile
@@ -1,4 +1,8 @@
FROM ubuntu:18.04 as base
+ARG KUD_ENABLE_TESTS=false
+ARG KUD_PLUGIN_ENABLED=false
+ENV KUD_ENABLE_TESTS=$KUD_ENABLE_TESTS
+ENV KUD_PLUGIN_ENABLED=$KUD_PLUGIN_ENABLED
ADD . /usr/src/multicloud-k8s
USER root
SHELL ["/bin/bash", "-c"]
diff --git a/kud/deployment_infra/images/sriov-daemonset.yml b/kud/deployment_infra/images/sriov-daemonset.yml
index 1edbc6c3..72f33869 100644
--- a/kud/deployment_infra/images/sriov-daemonset.yml
+++ b/kud/deployment_infra/images/sriov-daemonset.yml
@@ -13,8 +13,8 @@ data:
"resourceList": [{
"resourceName": "intel_sriov_700",
"selectors": {
- "vendors": ["8086"]
- "devices": ["37cd"]
+ "vendors": ["8086"],
+ "drivers": ["i40evf", "iavf"]
}
}]
}
diff --git a/kud/deployment_infra/playbooks/configure-onap4k8s.yml b/kud/deployment_infra/playbooks/configure-onap4k8s.yml
index cacb41c9..11729171 100644
--- a/kud/deployment_infra/playbooks/configure-onap4k8s.yml
+++ b/kud/deployment_infra/playbooks/configure-onap4k8s.yml
@@ -19,6 +19,14 @@
repo: 'https://github.com/onap/multicloud-k8s.git'
dest: /opt/multicloud
+ - name: install make package for ubuntu systems
+ apt: name=make state=present update_cache=yes
+ when: ansible_distribution == "Ubuntu"
+
+ - name: install make package for centos systems
+ yum: name=make state=present update_cache=yes
+ when: ansible_distribution == "CentOS"
+
- name: Change the onap4k8s directory and run the command make repo
command: /usr/bin/make repo
register: make_repo
diff --git a/kud/deployment_infra/playbooks/configure-ovn.yml b/kud/deployment_infra/playbooks/configure-ovn.yml
index 3fd2c765..28de6e94 100644
--- a/kud/deployment_infra/playbooks/configure-ovn.yml
+++ b/kud/deployment_infra/playbooks/configure-ovn.yml
@@ -15,14 +15,6 @@
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 }} main"
- state: present
- name: install OpenVSwitch packages
package:
name: "{{ item }}"
diff --git a/kud/deployment_infra/playbooks/configure-sriov.yml b/kud/deployment_infra/playbooks/configure-sriov.yml
index 8ba6cf48..45f276c6 100644
--- a/kud/deployment_infra/playbooks/configure-sriov.yml
+++ b/kud/deployment_infra/playbooks/configure-sriov.yml
@@ -12,25 +12,18 @@
- hosts: localhost
become: yes
- pre_tasks:
- - block:
- - name: "End play if SRIOV is False"
- debug:
- msg: "SRIOV option not available, ending play"
- - meta: end_play
- when: SRIOV_NODE == "False"
tasks:
- debug:
var: SRIOV_NODE
- name: Apply Multus
shell: "/usr/local/bin/kubectl apply -f {{ playbook_dir }}/../images/multus-daemonset.yml"
- when: SRIOV_NODE==True
+ when: SRIOV_NODE
- name: Apply SRIOV CNI
- shell: "/usr/local/bin/kubectl apply -f {{ playbook_dir }}/../images/sriov-cni.yaml"
- when: SRIOV_NODE==True
+ shell: "/usr/local/bin/kubectl apply -f {{ playbook_dir }}/../images/sriov-cni.yml"
+ when: SRIOV_NODE
- name: Apply SRIOV DaemonSet
- shell: "/usr/local/bin/kubectl apply -f {{ playbook_dir }}/../images/sriov-daemonset.yaml"
- when: SRIOV_NODE==True
+ shell: "/usr/local/bin/kubectl apply -f {{ playbook_dir }}/../images/sriov-daemonset.yml"
+ when: SRIOV_NODE
- name: Apply SRIOV Network Attachment definition
shell: "/usr/local/bin/kubectl apply -f {{ playbook_dir }}/sriov-nad.yml"
- when: SRIOV_NODE==True
+ when: SRIOV_NODE
diff --git a/kud/deployment_infra/playbooks/install_iavf_drivers.sh b/kud/deployment_infra/playbooks/install_iavf_drivers.sh
index d44483de..7a54e9f2 100755
--- a/kud/deployment_infra/playbooks/install_iavf_drivers.sh
+++ b/kud/deployment_infra/playbooks/install_iavf_drivers.sh
@@ -3,6 +3,10 @@
# Based on:
# https://gerrit.akraino.org/r/#/c/icn/+/1359/1/deploy/kud-plugin-addons/device-plugins/sriov/driver/install_iavf_drivers.sh
+nic_models=(XL710 X722)
+nic_drivers=(i40e)
+device_checkers=(is_not_used is_driver_match is_model_match)
+
function install_iavf_driver {
local ifname=$1
@@ -27,22 +31,55 @@ function install_iavf_driver {
echo '8' > /sys/class/net/$ifname/device/sriov_numvfs
}
-function is_used {
+function is_not_used {
local ifname=$1
route_info=`ip route show | grep $ifname`
if [ -z "$route_info" ]; then
- return 0
- else
return 1
+ else
+ return 0
+ fi
+}
+
+function is_driver_match {
+ local ifname=$1
+ driver=`cat /sys/class/net/$ifname/device/uevent | grep DRIVER | cut -f2 -d "="`
+ if [ ! -z "$driver" ]; then
+ for nic_driver in ${nic_drivers[@]}; do
+ if [ "$driver" = "$nic_driver" ]; then
+ return 1
+ fi
+ done
+ fi
+ return 0
+}
+
+function is_model_match {
+ local ifname=$1
+ pci_addr=`cat /sys/class/net/$ifname/device/uevent | grep PCI_SLOT_NAME | cut -f2 -d "=" | cut -f2,3 -d ":"`
+ if [ ! -z "$pci_addr" ]; then
+ for nic_model in ${nic_models[@]}; do
+ model_match=$(lspci | grep $pci_addr | grep $nic_model)
+ if [ ! -z "$model_match" ]; then
+ return 1
+ fi
+ done
fi
+ return 0
}
function get_sriov_ifname {
for net_device in /sys/class/net/*/ ; do
if [ -e $net_device/device/sriov_numvfs ] ; then
ifname=$(basename $net_device)
- is_used $ifname
- if [ "$?" = "0" ]; then
+ for device_checker in ${device_checkers[@]}; do
+ eval $device_checker $ifname
+ if [ "$?" = "0" ]; then
+ ifname=""
+ break
+ fi
+ done
+ if [ ! -z "$ifname" ]; then
echo $ifname
return
fi
diff --git a/kud/deployment_infra/playbooks/preconfigure-sriov.yml b/kud/deployment_infra/playbooks/preconfigure-sriov.yml
index c4276e1b..fd16d935 100644
--- a/kud/deployment_infra/playbooks/preconfigure-sriov.yml
+++ b/kud/deployment_infra/playbooks/preconfigure-sriov.yml
@@ -31,7 +31,7 @@
command: sriov/sriov_hardware_check.sh
register: output
- set_fact:
- SRIOV: "{{ output.stdout }}"
+ _SRIOV: "{{ output.stdout }}"
- name: Recreate the conf file for every host
file:
path: /tmp/sriov.conf
@@ -40,7 +40,7 @@
- lineinfile : >
dest=/tmp/sriov.conf
create=yes
- line='{{SRIOV}}'
+ line='{{_SRIOV}}'
delegate_to: localhost
- name: Clean the script and folder.
file:
@@ -58,32 +58,30 @@
become: yes
- set_fact:
SRIOV_NODE: "{{ installer_output.stdout }}"
- - meta: end_play
- when: SRIOV_NODE == "False"
- name: Load kud variables
include_vars:
file: kud-vars.yml
- when: SRIOV_NODE == "True"
+ when: SRIOV_NODE
tasks:
- name: Create sriov folder
file:
state: directory
path: "{{ sriov_dest }}"
- when: SRIOV_NODE == "True"
ignore_errors: yes
+ when: SRIOV_NODE
- name: Get SRIOV compatible driver
get_url: "url={{ driver_url }} dest=/tmp/{{ package }}.tar.gz"
- when: SRIOV_NODE == "True"
+ when: SRIOV_NODE
- name: Extract sriov source code
unarchive:
src: "/tmp/{{ package }}.tar.gz"
dest: "{{ sriov_dest }}"
- when: SRIOV_NODE == "True"
+ when: SRIOV_NODE
- name: Build the default target
make:
chdir: "/tmp/sriov/{{ package }}/src"
become: yes
- when: SRIOV_NODE == "True"
+ when: SRIOV_NODE
# Copy all the driver and install script into target node
- hosts: kube-node
become: yes
@@ -91,7 +89,7 @@
- name: Load kud variables
include_vars:
file: kud-vars.yml
- when: SRIOV == "True"
+ when: _SRIOV
tasks:
- name: create SRIOV driver folder in the target destination
file:
@@ -99,18 +97,22 @@
path: "{{ item }}"
with_items:
- sriov_driver
- when: SRIOV == "True"
- - name: Copy SRIOV driver to target destination
- command: "cp {{ sriov_dest }}/{{ package }}/src/iavf.ko /root/sriov_driver/"
- when: SRIOV == "True"
- - name: Copy SRIOV driver install script to target folder
- command: "cp {{ playbook_dir }}/install_iavf_drivers.sh /root/sriov_driver/install.sh"
- when: SRIOV == "True"
+ when: _SRIOV
+ - copy:
+ src: "{{ sriov_dest }}/{{ package }}/src/iavf.ko"
+ dest: sriov_driver
+ remote_src: no
+ when: _SRIOV
+ - copy:
+ src: "{{ playbook_dir }}/install_iavf_drivers.sh"
+ dest: sriov_driver/install.sh
+ remote_src: no
+ when: _SRIOV
- name: Changing perm of "install.sh", adding "+x"
- file: dest=/root/sriov_driver/install.sh mode=a+x
- when: SRIOV == "True"
+ file: dest=sriov_driver/install.sh mode=a+x
+ when: _SRIOV
- name: Run a script with arguments
shell: ./install.sh
args:
- chdir: "/root/sriov_driver"
- when: SRIOV == "True"
+ chdir: "sriov_driver"
+ when: _SRIOV
diff --git a/kud/deployment_infra/playbooks/sriov_hardware_check.sh b/kud/deployment_infra/playbooks/sriov_hardware_check.sh
index ea1b7b0c..662c28c8 100644
--- a/kud/deployment_infra/playbooks/sriov_hardware_check.sh
+++ b/kud/deployment_infra/playbooks/sriov_hardware_check.sh
@@ -12,14 +12,14 @@ set -o pipefail
source /etc/environment
-ethernet_adpator_version=$( lspci | grep "Ethernet Controller X710" | head -n 1 | cut -d " " -f 8 )
+ethernet_adpator_version=$( lspci | grep "Ethernet Controller XL710" | head -n 1 | cut -d " " -f 8 )
if [ -z "$ethernet_adpator_version" ]; then
echo "False"
exit 0
fi
SRIOV_ENABLED=${ethernet_adpator_version:-"false"}
#checking for the right hardware version of NIC on the machine
-if [ "$ethernet_adpator_version" == "X710" ]; then
+if [ "$ethernet_adpator_version" == "XL710" ]; then
echo "True"
else
echo "False"
diff --git a/kud/hosting_providers/containerized/README.md b/kud/hosting_providers/containerized/README.md
index 4119ca78..12ce1a19 100644
--- a/kud/hosting_providers/containerized/README.md
+++ b/kud/hosting_providers/containerized/README.md
@@ -27,7 +27,7 @@ Kubernetes jobs(a cluster per job) are used to install multiple clusters and log
## Quickstart Installation Guide
-Build the kud docker images as follows:
+Build the kud docker images as follows, add KUD_ENABLE_TESTS & KUD_PLUGIN_ENABLED for the testing only:
```
$ git clone https://github.com/onap/multicloud-k8s.git && cd multicloud-k8s
@@ -38,6 +38,8 @@ $ docker build --rm \
--build-arg HTTPS_PROXY=${HTTPS_PROXY} \
--build-arg no_proxy=${no_proxy} \
--build-arg NO_PROXY=${NO_PROXY} \
+ --build-arg KUD_ENABLE_TESTS=true \
+ --build-arg KUD_PLUGIN_ENABLED=true \
-t github.com/onap/multicloud-k8s:latest . -f build/Dockerfile
```
Let's create a cluster-101 and cluster-102 hosts.ini as follows
@@ -100,7 +102,7 @@ spec:
- name: secret-volume
mountPath: "/.ssh"
command: ["/bin/sh","-c"]
- args: ["cp -r /.ssh /root/; chmod -R 600 /root/.ssh; ./installer --cluster $CLUSTER_NAME"]
+ args: ["cp -r /.ssh /root/; chmod -R 600 /root/.ssh; ./installer --cluster $CLUSTER_NAME --plugins onap4k8s"]
securityContext:
privileged: true
volumes:
diff --git a/kud/hosting_providers/containerized/installer.sh b/kud/hosting_providers/containerized/installer.sh
index 52fe6279..8739ca23 100755
--- a/kud/hosting_providers/containerized/installer.sh
+++ b/kud/hosting_providers/containerized/installer.sh
@@ -17,10 +17,13 @@ INSTALLER_DIR="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")")"
function install_prerequisites {
#install package for docker images
+ echo "Removing ppa for jonathonf/python-3.6"
+ ls /etc/apt/sources.list.d/ || true
+ find /etc/apt/sources.list.d -maxdepth 1 -name '*jonathonf*' -delete || true
apt-get update
apt-get install -y curl vim wget git \
- software-properties-common python-pip
- add-apt-repository ppa:longsleep/golang-backports
+ software-properties-common python-pip sudo
+ add-apt-repository -y ppa:longsleep/golang-backports
apt-get update
apt-get install -y golang-go rsync
}
@@ -100,7 +103,14 @@ function install_k8s {
# install_addons() - Install Kubenertes AddOns
function install_addons {
- local plugins_name=$1
+ if [ ${1:+1} ]; then
+ local plugins_name="$1"
+ echo "additional addons plugins $1"
+ else
+ local plugins_name=""
+ echo "no additional addons pluigns"
+ fi
+
source /etc/environment
echo "Installing Kubernetes AddOns"
ansible-galaxy install $verbose -r \
@@ -109,36 +119,52 @@ function install_addons {
ansible-playbook $verbose -i \
$kud_inventory $kud_playbooks/configure-kud.yml | \
tee $cluster_log/setup-kud.log
- for addon in ${KUD_ADDONS:-virtlet ovn4nfv nfd $plugins_name}; do
+ for addon in ${KUD_ADDONS:-virtlet ovn4nfv nfd sriov $plugins_name}; do
echo "Deploying $addon using configure-$addon.yml playbook.."
ansible-playbook $verbose -i \
$kud_inventory $kud_playbooks/configure-${addon}.yml | \
tee $cluster_log/setup-${addon}.log
- if [[ "${testing_enabled}" == "true" ]]; then
+ done
+
+ echo "Run the test cases if testing_enabled is set to true."
+ if [[ "${testing_enabled}" == "true" ]]; then
+ for addon in ${KUD_ADDONS:-virtlet ovn4nfv nfd sriov $plugins_name}; do
pushd $kud_tests
bash ${addon}.sh
popd
- fi
- done
+ done
+ fi
+ echo "Add-ons deployment complete..."
}
# install_plugin() - Install ONAP Multicloud Kubernetes plugin
function install_plugin {
- echo "Installing multicloud/k8s plugin"
- mkdir -p /opt/{kubeconfig,consul/config}
- cp $HOME/.kube/config /opt/kubeconfig/kud
-
- pushd $kud_folder/../../../deployments
- ./build.sh
+ echo "Installing multicloud/k8s onap4k8s plugin"
if [[ "${testing_enabled}" == "true" ]]; then
- ./start.sh
pushd $kud_tests
- for functional_test in plugin plugin_edgex plugin_fw; do
- bash ${functional_test}.sh
+ echo "Test the onap4k8s installation"
+ bash onap4k8s.sh
+ echo "Test the onap4k8s plugin installation"
+ for functional_test in plugin_edgex plugin_fw; do
+ bash ${functional_test}.sh --external
done
popd
fi
- popd
+}
+
+# install_controllers() - Install ONAP Multicloud Kubernetes controllers
+function install_controllers {
+ echo "Installing multicloud/k8s onap4k8s controllers"
+ if [[ "${testing_enabled}" == "true" ]]; then
+ echo "Test controllers installation"
+ for controller_test in sdwan; do
+ pushd $kud_tests/$controller_test
+ ansible-playbook $verbose -i \
+ $kud_inventory ${controller_test}.yml | \
+ tee $cluster_log/test-${controller_test}.log
+ popd
+ done
+ fi
}
# _print_kubernetes_info() - Prints the login Kubernetes information
@@ -179,6 +205,7 @@ k8s_info_file=$kud_folder/k8s_info.log
testing_enabled=${KUD_ENABLE_TESTS:-false}
mkdir -p /opt/csar
+export CSAR_DIR=/opt/csar
function install_pkg {
# Install dependencies
@@ -189,11 +216,19 @@ function install_pkg {
function install_cluster {
install_k8s $1
- install_addons $2
+ if [ ${2:+1} ]; then
+ echo "install default addons and $2"
+ install_addons "$2"
+ else
+ install_addons
+ fi
+
echo "installed the addons"
if ${KUD_PLUGIN_ENABLED:-false}; then
install_plugin
echo "installed the install_plugin"
+ install_controllers
+ echo "installed controllers"
fi
_print_kubernetes_info
}
@@ -254,7 +289,7 @@ if [ "$1" == "--cluster" ]; then
cp $kud_multi_cluster_path/$cluster_name/hosts.ini $kud_inventory_folder/
cp -rf $kud_folder/inventory/group_vars $kud_inventory_folder/
- if [ -n "$3" ]; then
+ if [ ${3:+1} ]; then
if [ "$3" == "--plugins" ]; then
if [ -z "${4-}" ]; then
echo "Error: plugins arguments is null; Refer the usage"
@@ -262,7 +297,7 @@ if [ "$1" == "--cluster" ]; then
exit 1
fi
plugins_name=${@:4:$#}
- install_cluster $cluster_name $plugins_name
+ install_cluster $cluster_name "$plugins_name"
exit 0
else
echo "Error: cluster argument should have plugins; \
diff --git a/kud/hosting_providers/vagrant/Vagrantfile b/kud/hosting_providers/vagrant/Vagrantfile
index 58251fe9..2d1b5ab4 100644
--- a/kud/hosting_providers/vagrant/Vagrantfile
+++ b/kud/hosting_providers/vagrant/Vagrantfile
@@ -10,8 +10,8 @@
##############################################################################
box = {
- :virtualbox => { :name => 'elastic/ubuntu-18.04-x86_64', :version => '20191013.0.0' },
- :libvirt => { :name => 'peru/ubuntu-18.04-server-amd64'}
+ :virtualbox => { :name => 'elastic/ubuntu-16.04-x86_64', :version => '20180708.0.0' },
+ :libvirt => { :name => 'elastic/ubuntu-16.04-x86_64', :version=> '20180210.0.0'}
}
require 'yaml'
diff --git a/kud/hosting_providers/vagrant/installer.sh b/kud/hosting_providers/vagrant/installer.sh
index e5138c24..15974863 100755
--- a/kud/hosting_providers/vagrant/installer.sh
+++ b/kud/hosting_providers/vagrant/installer.sh
@@ -154,23 +154,19 @@ function install_addons {
echo "Installing Kubernetes AddOns"
_install_ansible
sudo ansible-galaxy install $verbose -r $kud_infra_folder/galaxy-requirements.yml --ignore-errors
-
ansible-playbook $verbose -i $kud_inventory $kud_playbooks/configure-kud.yml | sudo tee $log_folder/setup-kud.log
- for addon in ${KUD_ADDONS:-virtlet ovn4nfv nfd}; do
+ for addon in ${KUD_ADDONS:-virtlet ovn4nfv nfd sriov}; do
echo "Deploying $addon using configure-$addon.yml playbook.."
ansible-playbook $verbose -i $kud_inventory $kud_playbooks/configure-${addon}.yml | sudo tee $log_folder/setup-${addon}.log
- if [[ "${testing_enabled}" == "true" ]]; then
- pushd $kud_tests
- bash ${addon}.sh
- popd
- fi
done
- ansible-playbook $verbose -i $kud_inventory $kud_playbooks/configure-sriov.yml | sudo tee $log_folder/setup-sriov.log
- if [[ "${testing_enabled}" == "true" ]]; then
+ echo "Run the test cases if testing_enabled is set to true."
+ if [[ "${testing_enabled}" == "true" ]]; then
+ for addon in ${KUD_ADDONS:-virtlet ovn4nfv nfd sriov}; do
pushd $kud_tests
- bash sriov.sh
+ bash ${addon}.sh
popd
- fi
+ done
+ fi
echo "Add-ons deployment complete..."
}
@@ -251,6 +247,9 @@ if [ -f $kud_folder/sources.list ]; then
sudo mv /etc/apt/sources.list /etc/apt/sources.list.backup
sudo cp $kud_folder/sources.list /etc/apt/sources.list
fi
+echo "Removing ppa for jonathonf/python-3.6"
+sudo ls /etc/apt/sources.list.d/ || true
+sudo find /etc/apt/sources.list.d -maxdepth 1 -name '*jonathonf*' -delete || true
sudo apt-get update
install_k8s
_set_environment_file
diff --git a/kud/tests/plugin_edgex.sh b/kud/tests/plugin_edgex.sh
index 8eae5692..ae390add 100755
--- a/kud/tests/plugin_edgex.sh
+++ b/kud/tests/plugin_edgex.sh
@@ -17,7 +17,16 @@ source _common_test.sh
source _functions.sh
source _common.sh
-base_url="http://localhost:9015/v1"
+if [ ${1:+1} ]; then
+ if [ "$1" == "--external" ]; then
+ master_ip=$(kubectl cluster-info | grep "Kubernetes master" | \
+ awk -F ":" '{print $2}' | awk -F "//" '{print $2}')
+ onap_svc_node_port=30498
+ base_url="http://$master_ip:$onap_svc_node_port/v1"
+ fi
+fi
+
+base_url=${base_url:-"http://localhost:9015/v1"}
kubeconfig_path="$HOME/.kube/config"
csar_id=cb009bfe-bbee-11e8-9766-525400435678
rb_name="edgex"
@@ -91,6 +100,9 @@ response="$(call_api -d "${payload}" "${base_url}/instance")"
echo "$response"
vnf_id="$(jq -r '.id' <<< "${response}")"
+print_msg "Waiting for EdgeX instances"
+sleep 240
+
print_msg "Validating Kubernetes"
kubectl get --no-headers=true --namespace=${namespace} deployment edgex-core-command
kubectl get --no-headers=true --namespace=${namespace} service edgex-core-command
diff --git a/kud/tests/plugin_fw.sh b/kud/tests/plugin_fw.sh
index d7bed4fd..eec467c3 100755
--- a/kud/tests/plugin_fw.sh
+++ b/kud/tests/plugin_fw.sh
@@ -17,7 +17,16 @@ source _common_test.sh
source _functions.sh
source _common.sh
-base_url="http://localhost:9015/v1"
+if [ ${1:+1} ]; then
+ if [ "$1" == "--external" ]; then
+ master_ip=$(kubectl cluster-info | grep "Kubernetes master" | \
+ awk -F ":" '{print $2}' | awk -F "//" '{print $2}')
+ onap_svc_node_port=30498
+ base_url="http://$master_ip:$onap_svc_node_port/v1"
+ fi
+fi
+
+base_url=${base_url:-"http://localhost:9015/v1"}
kubeconfig_path="$HOME/.kube/config"
csar_id=cc009bfe-bbee-11e8-9766-525400435678
rb_name="vfw"
@@ -98,6 +107,9 @@ wait_for_pod -n "${namespace}" -l app=firewall
wait_for_pod -n "${namespace}" -l app=packetgen
# TODO: Provide some health check to verify vFW work
+print_msg "Waiting for VNF instances"
+sleep 480
+
print_msg "Retrieving VNF details"
call_api "${base_url}/instance/${vnf_id}"
diff --git a/kud/tests/sdwan.sh b/kud/tests/sdwan.sh
new file mode 100755
index 00000000..64b10f22
--- /dev/null
+++ b/kud/tests/sdwan.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright (c) 2018
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+echo "Create pods ..."
+kubectl apply -f sdwan/ovn-pod.yml
+kubectl apply -f sdwan/sdwan-openwrt-ovn.yml
+
+bash sdwan/test.sh
+
+echo "Clear pods ..."
+kubectl delete -f sdwan/ovn-pod.yml
+kubectl delete -f sdwan/sdwan-openwrt-ovn.yml
+
+echo "Test Completed!"
diff --git a/kud/tests/sdwan/build/Dockerfile_1806_mwan3.tpl b/kud/tests/sdwan/build/Dockerfile_1806_mwan3.tpl
new file mode 100644
index 00000000..85c7d358
--- /dev/null
+++ b/kud/tests/sdwan/build/Dockerfile_1806_mwan3.tpl
@@ -0,0 +1,26 @@
+FROM openwrt-1806-4-base
+
+#EXPOSE 80
+ENV http_proxy={docker_proxy}
+ENV https_proxy={docker_proxy}
+ENV no_proxy=localhost,120.0.0.1,192.168.*
+
+RUN mkdir /var/lock && \
+ opkg update && \
+ opkg install uhttpd-mod-lua && \
+ uci set uhttpd.main.interpreter='.lua=/usr/bin/lua' && \
+ uci commit uhttpd && \
+ opkg install mwan3 && \
+ opkg install luci-app-mwan3; exit 0
+
+COPY system /etc/config/system
+COPY commands.lua /usr/lib/lua/luci/controller/
+
+ENV http_proxy=
+ENV https_proxy=
+ENV no_proxy=
+
+USER root
+
+# using exec format so that /sbin/init is proc 1 (see procd docs)
+CMD ["/sbin/init"]
diff --git a/kud/tests/sdwan/build/Dockerfile_1806_mwan3_noproxy.tpl b/kud/tests/sdwan/build/Dockerfile_1806_mwan3_noproxy.tpl
new file mode 100644
index 00000000..8b5c57d2
--- /dev/null
+++ b/kud/tests/sdwan/build/Dockerfile_1806_mwan3_noproxy.tpl
@@ -0,0 +1,19 @@
+FROM openwrt-1806-4-base
+
+#EXPOSE 80
+
+RUN mkdir /var/lock && \
+ opkg update && \
+ opkg install uhttpd-mod-lua && \
+ uci set uhttpd.main.interpreter='.lua=/usr/bin/lua' && \
+ uci commit uhttpd && \
+ opkg install mwan3 && \
+ opkg install luci-app-mwan3; exit 0
+
+COPY system /etc/config/system
+COPY commands.lua /usr/lib/lua/luci/controller/
+
+USER root
+
+# using exec format so that /sbin/init is proc 1 (see procd docs)
+CMD ["/sbin/init"]
diff --git a/kud/tests/sdwan/build/README.md b/kud/tests/sdwan/build/README.md
new file mode 100644
index 00000000..87e21956
--- /dev/null
+++ b/kud/tests/sdwan/build/README.md
@@ -0,0 +1,10 @@
+# Introduction:
+Please refer ICN SDWAN Module Design for architecture introduction
+link:https://wiki.akraino.org/display/AK/SDWAN+Module+Design
+
+# SDWAN Docker Image build instructions:
+Use below steps to build openwrt docker image: openwrt-1806-mwan3
+(1) update set_proxy file with proxy used for docker build
+(2) execute build_image.sh
+cd build
+sudo bash build_image.sh
diff --git a/kud/tests/sdwan/build/build_image.sh b/kud/tests/sdwan/build/build_image.sh
new file mode 100644
index 00000000..7ff6e20b
--- /dev/null
+++ b/kud/tests/sdwan/build/build_image.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+# usage: build_images.sh
+
+set -ex
+base_image_tag=openwrt-1806-4-base
+docker_file=Dockerfile_1806_mwan3
+image_tag=openwrt-1806-mwan3
+package=openwrt-18.06.4-x86-64-generic-rootfs
+
+# build openwrt base docker images
+base_image=`docker images | grep $base_image_tag | awk '{print $1}'`
+if [ -z "$base_image" ]; then
+ # download driver source package
+ if [ ! -e /tmp/$package.tar.gz ]; then
+ wget -P /tmp https://downloads.openwrt.org/releases/18.06.4/targets/x86/64/$package.tar.gz
+ fi
+ cp /tmp/$package.tar.gz .
+
+ docker import $package.tar.gz $base_image_tag
+fi
+
+# generate Dockerfile
+test -f ./set_proxy && . set_proxy
+docker_proxy=${docker_proxy-""}
+if [ -z "$docker_proxy" ]; then
+ cp ${docker_file}_noproxy.tpl $docker_file
+else
+ cp $docker_file.tpl $docker_file
+ sed -i "s,{docker_proxy},$docker_proxy,g" $docker_file
+fi
+
+# build docker images for openwrt with wman3
+docker build --network=host -f $docker_file -t $image_tag .
+
+# clear
+docker image rm $base_image_tag
+rm -rf $docker_file
+rm -rf $package.tar.gz
diff --git a/kud/tests/sdwan/build/commands.lua b/kud/tests/sdwan/build/commands.lua
new file mode 100644
index 00000000..d99f4579
--- /dev/null
+++ b/kud/tests/sdwan/build/commands.lua
@@ -0,0 +1,43 @@
+-- Licensed to the public under the GNU General Public License v2.
+
+module("luci.controller.commands", package.seeall)
+
+sys = require "luci.sys"
+ut = require "luci.util"
+io = require "io"
+
+ip = "ip -4 "
+
+function index()
+ entry({"admin", "config", "command"},
+ call("execute")).dependent = false
+end
+
+function trim(s)
+ return s:match("^%s*(.-)%s*$")
+end
+
+function split_and_trim(str, sep)
+ local array = {}
+ local reg = string.format("([^%s]+)", sep)
+ for item in string.gmatch(str, reg) do
+ item_trimed = trim(item)
+ if string.len(item_trimed) > 0 then
+ table.insert(array, item_trimed)
+ end
+ end
+ return array
+end
+
+function execute()
+ local commands = luci.http.formvalue("command")
+ io.stderr:write("Execute command: %s\n" % commands)
+
+ local command_array = split_and_trim(commands, ";")
+ for index, command in ipairs(command_array) do
+ sys.exec(command)
+ end
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json("{'status':'ok'}")
+end
diff --git a/kud/tests/sdwan/build/set_proxy b/kud/tests/sdwan/build/set_proxy
new file mode 100644
index 00000000..7a195fe5
--- /dev/null
+++ b/kud/tests/sdwan/build/set_proxy
@@ -0,0 +1,2 @@
+# set docker proxy with below line, the build script will use this info
+#docker_proxy=
diff --git a/kud/tests/sdwan/build/system b/kud/tests/sdwan/build/system
new file mode 100644
index 00000000..5165430f
--- /dev/null
+++ b/kud/tests/sdwan/build/system
@@ -0,0 +1,7 @@
+config system
+ option log_file '/var/log/mylog'
+ option timezone 'UTC'
+ option ttylogin '0'
+ option log_size '64'
+ option urandom_seed '0'
+EOF
diff --git a/kud/tests/sdwan/ovn-pod.yml b/kud/tests/sdwan/ovn-pod.yml
new file mode 100644
index 00000000..0715c030
--- /dev/null
+++ b/kud/tests/sdwan/ovn-pod.yml
@@ -0,0 +1,40 @@
+# Create 2 ovn4nfv network attachment definition
+---
+apiVersion: k8s.plugin.opnfv.org/v1alpha1
+kind: Network
+metadata:
+ name: ovn-port-net
+spec:
+ cniType : ovn4nfv
+ ipv4Subnets:
+ - subnet: 172.16.33.0/24
+ name: subnet1
+ gateway: 172.16.33.1/24
+
+---
+apiVersion: k8s.plugin.opnfv.org/v1alpha1
+kind: Network
+metadata:
+ name: ovn-priv-net
+spec:
+ cniType : ovn4nfv
+ ipv4Subnets:
+ - subnet: 172.16.44.0/24
+ name: subnet1
+ gateway: 172.16.44.1/24
+
+---
+apiVersion: v1
+kind: Pod
+metadata:
+ name: ovn-pod
+ annotations:
+ k8s.v1.cni.cncf.io/networks: '[{ "name": "ovn-networkobj"}]'
+ k8s.plugin.opnfv.org/nfn-network: '{ "type": "ovn4nfv", "interface": [{ "name": "ovn-port-net", "interface": "net0" , "defaultGateway": "false"},
+ { "name": "ovn-priv-net", "interface": "net1" , "defaultGateway": "false"}]}'
+spec:
+ containers:
+ - name: ovn-pod
+ image: docker.io/centos/tools:latest
+ command:
+ - /sbin/init
diff --git a/kud/tests/sdwan/sdwan-openwrt-ovn.yml b/kud/tests/sdwan/sdwan-openwrt-ovn.yml
new file mode 100644
index 00000000..2accdc6c
--- /dev/null
+++ b/kud/tests/sdwan/sdwan-openwrt-ovn.yml
@@ -0,0 +1,82 @@
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: sdwan-config-ovn
+data:
+ entrypoint.sh: |
+ #!/bin/bash
+ # Always exit on errors.
+ set -e
+
+ interface0=net0
+ ipaddr0=`ifconfig $interface0 | awk '/inet/{print $2}' | cut -f2 -d ":" | awk 'NR==1 {print $1}'`
+
+ interface1=net1
+ ipaddr1=`ifconfig $interface1 | awk '/inet/{print $2}' | cut -f2 -d ":" | awk 'NR==1 {print $1}'`
+
+ net_config=/etc/config/network
+ cat >> $net_config << EOF
+ config interface 'wan'
+ option ifname '$interface0'
+ option proto 'static'
+ option ipaddr '$ipaddr0'
+ option netmask '255.255.255.0'
+
+ config interface 'wanb'
+ option ifname '$interface1'
+ option proto 'static'
+ option ipaddr '$ipaddr1'
+ option netmask '255.255.255.0'
+ EOF
+
+ /sbin/procd &
+ /sbin/ubusd &
+ iptables -S
+ sleep 1
+ /etc/init.d/rpcd start
+ /etc/init.d/dnsmasq start
+ /etc/init.d/network start
+ /etc/init.d/odhcpd start
+ /etc/init.d/uhttpd start
+ /etc/init.d/log start
+ /etc/init.d/dropbear start
+ /etc/init.d/mwan3 restart
+
+ echo "Entering sleep... (success)"
+
+ # Sleep forever.
+ while true; do sleep 100; done
+
+---
+apiVersion: v1
+kind: Pod
+metadata:
+ name: sdwan-ovn-pod
+ annotations:
+ k8s.v1.cni.cncf.io/networks: '[{ "name": "ovn-networkobj"}]'
+ k8s.plugin.opnfv.org/nfn-network: '{ "type": "ovn4nfv", "interface": [{ "name": "ovn-port-net", "interface": "net0" , "defaultGateway": "false"},
+ { "name": "ovn-priv-net", "interface": "net1" , "defaultGateway": "false"}]}'
+spec:
+ containers:
+ - name: sdwan-ovn-pod
+ image: hle2/openwrt-1806-mwan3:v0.1.0
+ ports:
+ - containerPort: 22
+ - containerPort: 80
+ command:
+ - /bin/sh
+ - /init/entrypoint.sh
+ imagePullPolicy: IfNotPresent
+ securityContext:
+ privileged: true
+ volumeMounts:
+ - name: entrypoint-sh
+ mountPath: /init
+ volumes:
+ - name: entrypoint-sh
+ configMap:
+ name: sdwan-config-ovn
+ items:
+ - key: entrypoint.sh
+ path: entrypoint.sh
diff --git a/kud/tests/sdwan/sdwan.yml b/kud/tests/sdwan/sdwan.yml
new file mode 100644
index 00000000..760d8599
--- /dev/null
+++ b/kud/tests/sdwan/sdwan.yml
@@ -0,0 +1,44 @@
+---
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright (c) 2018
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+- hosts: localhost
+ become: yes
+ tasks:
+ - name: create ovn network and client workload
+ command: "/usr/local/bin/kubectl apply -f {{ playbook_dir }}/ovn-pod.yml"
+
+ - name: create sdwan controller
+ command: "/usr/local/bin/kubectl apply -f {{ playbook_dir }}/sdwan-openwrt-ovn.yml"
+
+- hosts: kube-master
+ become: yes
+ tasks:
+ - name: install wget package for ubuntu systems
+ apt: name=wget state=present update_cache=yes
+ when: ansible_distribution == "Ubuntu"
+
+ - name: install wget package for centos systems
+ yum: name=wget state=present update_cache=yes
+ when: ansible_distribution == "CentOS"
+
+ - name: Execute sdwan test script in cluster master
+ script: test.sh
+ register: sdwan
+
+ - debug:
+ var: sdwan.stdout_lines
+
+- hosts: localhost
+ become: yes
+ tasks:
+ - name: delete ovn network and client workload
+ command: "/usr/local/bin/kubectl delete -f {{ playbook_dir }}/ovn-pod.yml"
+
+ - name: delete sdwan controller
+ command: "/usr/local/bin/kubectl delete -f {{ playbook_dir }}/sdwan-openwrt-ovn.yml"
diff --git a/kud/tests/sdwan/test.sh b/kud/tests/sdwan/test.sh
new file mode 100755
index 00000000..ba4b4173
--- /dev/null
+++ b/kud/tests/sdwan/test.sh
@@ -0,0 +1,120 @@
+#!/bin/bash
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright (c) 2018
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+sdwan_pod_name=sdwan-ovn-pod
+ovn_pod_name=ovn-pod
+wan_interface=net0
+
+function login {
+ login_url=http://$1/cgi-bin/luci/
+ echo $(wget -S --spider --post-data "luci_username=root&luci_password=" $login_url 2>&1 | grep sysauth= | sed -r 's/.*sysauth=([^;]+);.*/\1/')
+}
+
+function disable_ping {
+ command_url=http://$2/cgi-bin/luci/admin/config/command
+ command="uci set firewall.@rule[1].target='REJECT';fw3 reload"
+ echo $(wget -S --spider --header="Cookie:sysauth=$1" --post-data "command=$command" $command_url 2>&1)
+}
+
+function enable_ping {
+ command_url=http://$2/cgi-bin/luci/admin/config/command
+ command="uci set firewall.@rule[1].target='ACCEPT';fw3 reload"
+ echo $(wget -S --spider --header="Cookie:sysauth=$1" --post-data "command=$command" $command_url 2>&1)
+}
+
+function wait_for_pod {
+ status_phase=""
+ while [[ "$status_phase" != "Running" ]]; do
+ new_phase="$(kubectl get pods -o wide | grep ^$1 | awk '{print $3}')"
+ if [[ "$new_phase" != "$status_phase" ]]; then
+ status_phase="$new_phase"
+ fi
+ if [[ "$new_phase" == "Err"* ]]; then
+ exit 1
+ fi
+ sleep 2
+ done
+}
+
+function wait_for_pod_namespace {
+ status_phase=""
+ while [[ "$status_phase" != "Running" ]]; do
+ new_phase="$(kubectl get pods -o wide -n $2 | grep ^$1 | awk '{print $3}')"
+ if [[ "$new_phase" != "$status_phase" ]]; then
+ status_phase="$new_phase"
+ fi
+ if [[ "$new_phase" == "Err"* ]]; then
+ exit 1
+ fi
+ sleep 2
+ done
+}
+
+echo "Waiting for pods to be ready ..."
+wait_for_pod $ovn_pod_name
+wait_for_pod $sdwan_pod_name
+echo "* Create pods success"
+
+sdwan_pod_ip=$(kubectl get pods -o wide | grep ^$sdwan_pod_name | awk '{print $6}')
+ovn_pod_ip=$(kubectl get pods -o wide | grep ^$ovn_pod_name | awk '{print $6}')
+echo "SDWAN pod ip:"$sdwan_pod_ip
+echo "OVN pod ip:"$ovn_pod_ip
+
+echo "Login to sdwan ..."
+security_token=""
+while [[ "$security_token" == "" ]]; do
+ echo "Get Security Token ..."
+ security_token=$(login $sdwan_pod_ip)
+ sleep 2
+done
+echo "* Security Token: "$security_token
+
+kubectl exec $sdwan_pod_name ifconfig
+
+sdwan_pod_wan_ip=$(kubectl exec $sdwan_pod_name ifconfig $wan_interface | awk '/inet/{print $2}' | cut -f2 -d ":" | awk 'NR==1 {print $1}')
+echo "Verify ping is work through wan interface between $sdwan_pod_name and $ovn_pod_name"
+ping_result=$(kubectl exec $ovn_pod_name -- ping -c 3 $sdwan_pod_wan_ip)
+if [[ $ping_result == *", 0% packet loss"* ]]; then
+ echo "* Ping is work through wan interface"
+else
+ echo "* Test failed!"
+ exit 1
+fi
+
+echo "Disable ping rule of wan interface ..."
+ret=$(disable_ping $security_token $sdwan_pod_ip)
+
+echo "Verify ping is not work through wan interface after ping rule disabled"
+ping_result=$(kubectl exec $ovn_pod_name -- ping -c 3 $sdwan_pod_wan_ip 2>&1 || true)
+if [[ $ping_result == *", 100% packet loss"* ]]; then
+ echo "* Ping is disabled"
+else
+ echo "* Test failed!"
+ exit 1
+fi
+
+echo "Enable ping rule of wan interface ..."
+ret=$(enable_ping $security_token $sdwan_pod_ip)
+
+echo "Verify ping is work through wan interface after ping rule enabled"
+ping_result=$(kubectl exec $ovn_pod_name -- ping -c 3 $sdwan_pod_wan_ip)
+if [[ $ping_result == *", 0% packet loss"* ]]; then
+ echo "* Ping is enabled"
+else
+ echo "* Test failed!"
+ exit 1
+fi
+
+
+echo "Test Completed!"
diff --git a/kud/tests/sriov.sh b/kud/tests/sriov.sh
index c66f5db8..a721b722 100755
--- a/kud/tests/sriov.sh
+++ b/kud/tests/sriov.sh
@@ -10,13 +10,13 @@
set -o pipefail
-ethernet_adpator_version=$( lspci | grep "Ethernet Controller X710" | head -n 1 | cut -d " " -f 8 )
+ethernet_adpator_version=$( lspci | grep "Ethernet Controller XL710" | head -n 1 | cut -d " " -f 8 )
if [ -z "$ethernet_adpator_version" ]; then
echo " Ethernet adapator version is not set. SRIOV test case cannot run on this machine"
exit 0
fi
#checking for the right hardware version of NIC on the machine
-if [ $ethernet_adpator_version == "X710" ]; then
+if [ $ethernet_adpator_version == "XL710" ]; then
echo "NIC card specs match. SRIOV option avaiable for this version."
else
echo -e "Failed. The version supplied does not match.\nTest cannot be executed."
diff --git a/kud/tests/vIPSec/README.md b/kud/tests/vIPSec/README.md
new file mode 100644
index 00000000..3046db7a
--- /dev/null
+++ b/kud/tests/vIPSec/README.md
@@ -0,0 +1,36 @@
+# vIPSec use case in ONAP
+This use case is composed of four virtual functions (VFs) including two
+IPSec gateways, a packet generator and a traffic sink, each running in
+separate Ubuntu Virtual Machines:
+
+ * [Packet generator][1]: Sends packets to the packet sink through the
+tunnel constructed thru IPSec. This includes a script that installs the
+packet generator based on packetgen[4].
+ * [IPsec gateways][2]: Two IPSec gateways constructed the secure tunnel
+for traffic transportation. This includes a script to install and configure
+the IPSec gateways thru VPP.
+ * [Traffic sink][3]: Displays the traffic volume that lands at the sink
+VM using the link http://192.168.80.250:667 through your browser
+and enable automatic page refresh by clicking the "Off" button. You
+can see the traffic volume in the charts.
+
+This set of scripts aims to construct the vIPSec use case in order to set
+up a secure tunnel between peers and improve its performance along with
+hardware acceleration technologies such as SRIOV and QAT.
+
+User can apply the helm chart named 'vipsec' inside the k8s/kud/demo folder
+to set up the whole use case. A fully-functional Kubernetes cluster, Virtlet
+as well as ovn4nfv-k8s[5] plugin need to be pre-installed for the usage.
+*[Place needs improvements] After having the virtual machines ready, please
+manually change the MAC address inside the ipsec.conf to enable the routing.
+And also start up the packetgen to send packet with src and dst defined in
+the templates/values.yaml inside the helm chart. Detail instructions will be
+put inside the helm chart.
+
+If you'd like to test the performance with QAT/SRIOV involved, first get
+these hardwares pre-configured. Then change the value of 'qat_enabled' and
+'sriov_enabled' inside templates/values.yaml of the helm chart accordingly.
+User could observe variance in throughput inside the traffic sink.
+
+[4] https://pktgen-dpdk.readthedocs.io/en/latest/
+[5] https://github.com/opnfv/ovn4nfv-k8s-plugin
diff --git a/kud/tests/vIPSec/ipsec b/kud/tests/vIPSec/ipsec
new file mode 100755
index 00000000..4b278574
--- /dev/null
+++ b/kud/tests/vIPSec/ipsec
@@ -0,0 +1,163 @@
+#!/bin/bash
+# COPYRIGHT NOTICE STARTS HERE
+#
+# Copyright 2019 Intel Co., Ltd.
+#
+# 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.
+#
+# COPYRIGHT NOTICE ENDS HERE
+
+# This script prepares the runtime environment
+# for running vIPSec shell scripts on Ubuntu 18.04
+
+set -o nounset
+set -o pipefail
+set -o xtrace
+set -o errexit
+
+function setup_dependencies {
+ apt-get update
+ apt-get install -y curl gnupg2 pciutils make gcc libnuma-dev python git linux-headers-`uname -r` module-init-tools libssl-dev
+ echo "deb [trusted=yes] https://packagecloud.io/fdio/release/ubuntu bionic main" >> /etc/apt/sources.list.d/99fd.io.list
+ curl -L https://packagecloud.io/fdio/master/gpgkey | apt-key add -
+}
+
+function install_vpp {
+ apt-get update
+ apt-get install -y vpp vpp-plugin-core vpp-plugin-dpdk
+}
+
+function install_dpdk {
+ cd /opt
+ git clone http://dpdk.org/git/dpdk
+ cd /opt/dpdk
+ export RTE_TARGET=x86_64-native-linux-gcc/ && export DESTDIR=/opt/dpdk && export RTE_SDK=/opt/dpdk && make install T=x86_64-native-linux-gcc
+ modprobe uio
+ insmod x86_64-native-linux-gcc/kmod/igb_uio.ko
+}
+
+function ipsec_settings {
+# Create vpp configuration file
+ cat > /opt/config/vpp.config << EOF
+ unix {
+ exec /opt/config/ipsec.conf
+ nodaemon
+ cli-listen /run/vpp/cli.sock
+ log /tmp/vpp.log
+ }
+
+ cpu {
+ main-core 0
+ corelist-workers 1
+ }
+
+ dpdk {
+ socket-mem 512
+ log-level debug
+ no-tx-checksum-offload
+ dev default{
+ num-tx-desc 512
+ num-rx-desc 512
+ }
+ dev interfaceABus
+ {
+ workers 0
+ }
+ dev interfaceBBus
+ {
+ workers 0
+ }
+ vdev crypto_aesni_mb0
+
+ no-multi-seg
+
+ #enable_cryptodev
+
+ }
+EOF
+
+# Check if sriov and qat are enabled, bind the pci devices with igb_uio driver
+ if [ "$sriov_enabled" = true ]; then
+ export interfaceABus=$(lspci -D -nn | grep -m1 '8086:154c' | cut -d ' ' -f 1)
+ export interfaceBBus=$(lspci -D -nn | grep -m2 '8086:154c' | cut -d ' ' -f 1 | tail -n1)
+ else
+ export interfaceABus=$(ls -la /sys/class/net | grep 'eth1' | cut -d '/' -f 5)
+ export interfaceBBus=$(ls -la /sys/class/net | grep 'eth3' | cut -d '/' -f 5)
+ fi
+ sed -i -e "s/interfaceABus/${interfaceABus}/g" -e "s/interfaceBBus/${interfaceBBus}/g" /opt/config/vpp.config
+ python /opt/dpdk/usertools/dpdk-devbind.py -b igb_uio $interfaceABus $interfaceBBus
+ export interfaceA=$(vppctl sh int | awk '$2 == "1"' | cut -d ' ' -f 1)
+ export interfaceB=$(vppctl sh int | awk '$2 == "2"' | cut -d ' ' -f 1)
+
+ if [ "$qat_enabled" = true ]; then
+ export qatABus=$(lspci -D -nn | grep -m1 '8086:37c9' | cut -d ' ' -f 1)
+ export qatBBus=$(lspci -D -nn | grep -m2 '8086:37c9' | cut -d ' ' -f 1 | tail -n1)
+ python /opt/dpdk/usertools/dpdk-devbind.py -b igb_uio $qatABus $qatBBus
+ sed -i "/#enable_cryptodev/a\n dev $qatABus\n dev $qatBBus\n" /opt/config/vpp.config
+ sed -i "/vdev crypto_aesni_mb0/d" /opt/config/vpp.config
+ fi
+
+# Create the sample ipsec configuration file
+ cat > /opt/config/ipsec.conf << EOF
+ set interface state VirtualFunctionEthernet0/5/0 up
+ set interface state VirtualFunctionEthernet0/6/0 up
+
+ set interface ip address VirtualFunctionEthernet0/5/0 input_interface_ip/24
+ set interface ip address VirtualFunctionEthernet0/6/0 output_interface_ip/24
+
+ set int promiscuous on VirtualFunctionEthernet0/5/0
+ set int promiscuous on VirtualFunctionEthernet0/6/0
+
+ set ip arp VirtualFunctionEthernet0/6/0 remote_tunnel_ip fa:16:3e:a6:e4:c7
+ set ip arp VirtualFunctionEthernet0/5/0 input_interface_ip fa:16:3e:f1:65:dc
+
+ ip route add count 1 packet_dst/32 via route_interface VirtualFunctionEthernet0/6/0
+
+ ipsec spd add 1
+ set interface ipsec spd VirtualFunctionEthernet0/6/0 1
+ ipsec sa add 1 spi 1921681003 esp tunnel-src output_interface_ip tunnel-dst remote_tunnel_ip crypto-key 2b7e151628aed2a6abf7158809cf4f3d crypto-alg aes-cbc-128 integ-key 6867666568676665686766656867666568676669 integ-alg sha1-96
+ ipsec policy add spd 1 traffic_direction priority 100 action protect sa 1 local-ip-range packet_src-packet_src remote-ip-range packet_dst-packet_dst
+ ipsec policy add spd 1 traffic_direction priority 90 protocol 50 action bypass local-ip-range packet_src-255.255.255.255 remote-ip-range remote_tunnel_ip-remote_tunnel_ip
+EOF
+
+# Replace all ip and interfaces inside the ipsec configuration file
+ sed -i -e "s/input_interface_ip/${input_interface_ip}/g" -e "s/output_interface_ip/${output_interface_ip}/g" -e "s/remote_tunnel_ip/${remote_tunnel_ip}/g" -e "s/route_interface/${route_interface}/g" -e "s#VirtualFunctionEthernet0/5/0#${interfaceA}#g" -e "s#VirtualFunctionEthernet0/6/0#${interfaceB}/g" -e "s/packet_src/${packet_src}/g" -e "s/packet_dst/${packet_dst}/g" -e "s/traffic_direction/${traffic_direction}/g" /opt/config/ipsec.conf
+ vpp -c /opt/config/vpp.config
+}
+
+
+mkdir /opt/config
+echo "$demo_artifacts_version" > /opt/config/demo_artifacts_version.txt
+echo "$dcae_collector_ip" > /opt/config/dcae_collector_ip.txt
+echo "$dcae_collector_port" > /opt/config/dcae_collector_port.txt
+echo "$ipsec_private_net_gw" > /opt/config/ipsec_private_net_gw_ip.txt
+echo "$ipsec_private_net_cidr" > /opt/config/ipsec_private_net_cidr.txt
+echo "$ipsec_private_network_name" > /opt/config/ipsec_private_network_name.txt
+echo "$packet_src" > /opt/config/packet_source_ip.txt
+echo "$packet_dst" > /opt/config/packet_destination_ip.txt
+echo "$remote_tunnel_ip" > /opt/config/remote_tunnel.txt
+echo "$route_interface" > /opt/config/route_interface.txt
+echo "$traffic_direction" > /opt/config/traffic_direction.txt
+echo "$vipsecA_private_ip_0" > /opt/config/vipsecA_private_ip0.txt
+echo "$vipsecA_private_ip_2" > /opt/config/vipsecA_private_ip2.txt
+echo "$protected_clientA_network_name" > /opt/config/protected_clientA_network_name.txt
+echo "$protected_clientA_net_gw" > /opt/config/protected_clientA_net_gw.txt
+echo "$protected_clientA_net_cidr" > /opt/config/protected_clientA_net_cidr.txt
+
+echo 'vm.nr_hugepages = 1024' >> /etc/sysctl.conf
+sysctl -p
+
+setup_dependencies
+install_vpp
+install_dpdk
+ipsec_settings
diff --git a/kud/tests/vIPSec/pktgen b/kud/tests/vIPSec/pktgen
new file mode 100755
index 00000000..14d7e6ca
--- /dev/null
+++ b/kud/tests/vIPSec/pktgen
@@ -0,0 +1,77 @@
+#!/bin/bash
+
+# COPYRIGHT NOTICE STARTS HERE
+#
+# Copyright 2019 Intel Co., Ltd.
+#
+# 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.
+#
+# COPYRIGHT NOTICE ENDS HERE
+
+# This script prepares the runtime environment
+# for running vIPSec shell scripts on Ubuntu18.04
+
+set -o nounset
+set -o pipefail
+set -o xtrace
+set -o errexit
+
+
+DPDK_DIR=$PWD/dpdk
+Pktgen_Dir=$PWD/pktgen-dpdk
+
+function setup_dependencies {
+ sudo apt-get update
+ git clone http://dpdk.org/git/dpdk
+ git clone http://dpdk.org/git/apps/pktgen-dpdk
+ KERNEL_VERSION=$(uname -r)
+ echo $KERNEL_VERSION
+ sudo apt-get install -y linux-headers-$KERNEL_VERSION libpcap-dev gcc make libnuma-dev liblua5.3-dev python
+}
+
+function build_dpdk {
+ export RTE_SDK=$DPDK_DIR
+ export RTE_TARGET=x86_64-native-linux-gcc
+ export DESTDIR=$DPDK_DIR
+ cd $RTE_SDK
+ make install T=x86_64-native-linux-gcc
+ echo "DPDK install finished"
+ modprobe uio
+ insmod x86_64-native-linux-gcc/kmod/igb_uio.ko
+ export interface=$(lspci -nn | grep -m1 'Ethernet controller' | cut -d ' ' -f 1)
+ python ./usertools/dpdk-devbind.py -b igb_uio $interface
+}
+
+function build_pktgen {
+ cd $Pktgen_Dir
+ export RTE_SDK=$DPDK_DIR
+ export RTE_TARGET=x86_64-native-linux-gcc
+ make
+}
+
+mkdir /opt/config
+echo "$demo_artifacts_version" > /opt/config/demo_artifacts_version.txt
+echo "$vpg_private_ip_0" > /opt/config/vpg_private_ip0.txt
+echo "$ipsec_a_private_ip_0" > /opt/config/ipsec_a_private_ip0.txt
+echo "$protected_clientA_network_name" > /opt/config/protected_clientA_network_name.txt
+echo "$dcae_collector_ip" > /opt/config/dcae_collector_ip.txt
+echo "$dcae_collector_port" > /opt/config/dcae_collector_port.txt
+echo "$protected_clientA_net_gw" > /opt/config/protected_clientA_net_gw.txt
+echo "$protected_clientA_net_cidr" > /opt/config/protected_clientA_net_cidr.txt
+
+echo 'vm.nr_hugepages = 1024' >> /etc/sysctl.conf
+sysctl -p
+
+setup_dependencies
+build_dpdk
+build_pktgen
diff --git a/kud/tests/vIPSec/remote_ipsec b/kud/tests/vIPSec/remote_ipsec
new file mode 100755
index 00000000..6a676c96
--- /dev/null
+++ b/kud/tests/vIPSec/remote_ipsec
@@ -0,0 +1,164 @@
+#!/bin/bash
+
+# COPYRIGHT NOTICE STARTS HERE
+#
+# Copyright 2019 Intel Co., Ltd.
+#
+# 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.
+#
+# COPYRIGHT NOTICE ENDS HERE
+
+# This script prepares the runtime environment
+# for running vIPSec shell scripts on Ubuntu18.04
+
+set -o nounset
+set -o pipefail
+set -o xtrace
+set -o errexit
+
+function setup_dependencies {
+ apt-get update
+ apt-get install -y curl gnupg2 pciutils make gcc libnuma-dev python git linux-headers-`uname -r` module-init-tools libssl-dev
+ echo "deb [trusted=yes] https://packagecloud.io/fdio/release/ubuntu bionic main" >> /etc/apt/sources.list.d/99fd.io.list
+ curl -L https://packagecloud.io/fdio/master/gpgkey | apt-key add -
+}
+
+function install_vpp {
+ apt-get update
+ apt-get install -y vpp vpp-plugin-core vpp-plugin-dpdk
+}
+
+function install_dpdk {
+ cd /opt
+ git clone http://dpdk.org/git/dpdk
+ cd /opt/dpdk
+ export RTE_TARGET=x86_64-native-linux-gcc/ && export DESTDIR=/opt/dpdk && export RTE_SDK=/opt/dpdk && make install T=x86_64-native-linux-gcc
+ modprobe uio
+ insmod x86_64-native-linux-gcc/kmod/igb_uio.ko
+}
+
+function ipsec_settings {
+# Create vpp configuration file
+ cat > /opt/config/vpp.config << EOF
+ unix {
+ exec /opt/config/ipsec.conf
+ nodaemon
+ cli-listen /run/vpp/cli.sock
+ log /tmp/vpp.log
+ }
+
+ cpu {
+ main-core 0
+ corelist-workers 1
+ }
+
+ dpdk {
+ socket-mem 512
+ log-level debug
+ no-tx-checksum-offload
+ dev default{
+ num-tx-desc 512
+ num-rx-desc 512
+ }
+ dev interfaceABus
+ {
+ workers 0
+ }
+ dev interfaceBBus
+ {
+ workers 0
+ }
+ vdev crypto_aesni_mb0
+
+ no-multi-seg
+
+ #enable_cryptodev
+
+ }
+EOF
+
+# Check if sriov and qat are enabled, bind the pci devices with igb_uio driver
+ if [ "$sriov_enabled" = true ]; then
+ export interfaceABus=$(lspci -D -nn | grep -m1 '8086:154c' | cut -d ' ' -f 1)
+ export interfaceBBus=$(lspci -D -nn | grep -m2 '8086:154c' | cut -d ' ' -f 1 | tail -n1)
+ else
+ export interfaceABus=$(ls -la /sys/class/net | grep 'eth1' | cut -d '/' -f 5)
+ export interfaceBBus=$(ls -la /sys/class/net | grep 'eth3' | cut -d '/' -f 5)
+ fi
+ sed -i -e "s/interfaceABus/${interfaceABus}/g" -e "s/interfaceBBus/${interfaceBBus}/g" /opt/config/vpp.config
+ python /opt/dpdk/usertools/dpdk-devbind.py -b igb_uio $interfaceABus $interfaceBBus
+ export interfaceA=$(vppctl sh int | awk '$2 == "1"' | cut -d ' ' -f 1)
+ export interfaceB=$(vppctl sh int | awk '$2 == "2"' | cut -d ' ' -f 1)
+
+ if [ "$qat_enabled" = true ]; then
+ export qatABus=$(lspci -D -nn | grep -m1 '8086:37c9' | cut -d ' ' -f 1)
+ export qatBBus=$(lspci -D -nn | grep -m2 '8086:37c9' | cut -d ' ' -f 1 | tail -n1)
+ python /opt/dpdk/usertools/dpdk-devbind.py -b igb_uio $qatABus $qatBBus
+ sed -i "/#enable_cryptodev/a\n dev $qatABus\n dev $qatBBus\n" /opt/config/vpp.config
+ sed -i "/vdev crypto_aesni_mb0/d" /opt/config/vpp.config
+ fi
+
+# Create ipsec configuration file
+ cat > /opt/config/ipsec.conf << EOF
+ set interface state VirtualFunctionEthernet0/5/0 up
+ set interface state VirtualFunctionEthernet0/6/0 up
+
+ set interface ip address VirtualFunctionEthernet0/5/0 input_interface_ip/24
+ set interface ip address VirtualFunctionEthernet0/6/0 output_interface_ip/24
+
+ set int promiscuous on VirtualFunctionEthernet0/5/0
+ set int promiscuous on VirtualFunctionEthernet0/6/0
+
+ set ip arp VirtualFunctionEthernet0/6/0 remote_tunnel_ip fa:16:3e:a6:e4:c7
+ set ip arp VirtualFunctionEthernet0/5/0 routing_ip fa:16:3e:f1:65:dc
+
+ ip route add count 1 packet_dst/32 via route_interface VirtualFunctionEthernet0/6/0
+
+ ipsec spd add 1
+ set interface ipsec spd VirtualFunctionEthernet0/6/0 1
+ ipsec sa add 1 spi 1921681004 esp tunnel-src local_tunnel_ip tunnel-dst remote_tunnel_ip crypto-key 2b7e151628aed2a6abf7158809cf4f3d crypto-alg aes-cbc-128 integ-key 6867666568676665686766656867666568676669 integ-alg sha1-96
+ ipsec policy add spd 1 traffic_direction priority 100 action protect sa 1 local-ip-range packet_src-packet_src remote-ip-range packet_dst-packet_dst
+ ipsec policy add spd 1 traffic_direction priority 90 protocol 50 action bypass local-ip-range packet_src-255.255.255.255 remote-ip-range remote_tunnel_ip-remote_tunnel_ip
+EOF
+
+# Replace the actual ip and interfaces into the ipsec configuration
+ sed -i -e "s/input_interface_ip/${input_interface_ip}/g" -e "s/output_interface_ip/${output_interface_ip}/g" -e "s/routing_ip/${vsn_private_ip_0}/g" -e "s#VirtualFunctionEthernet0/5/0#${interfaceA}#g" -e "s#VirtualFunctionEthernet0/6/0#${interfaceB}#g" -e "s/local_tunnel_ip/${local_tunnel_ip}/g" -e "s/remote_tunnel_ip/${remote_tunnel_ip}/g" -e "s/route_interface/${route_interface}/g" -e "s/packet_src/${packet_src}/g" -e "s/packet_dst/${packet_dst}/g" -e "s/traffic_direction/${traffic_direction}/g" /opt/config/ipsec.conf
+ vpp -c /opt/config/vpp.config
+}
+
+
+mkdir /opt/config
+echo "$demo_artifacts_version" > /opt/config/demo_artifacts_version.txt
+echo "$dcae_collector_ip" > /opt/config/dcae_collector_ip.txt
+echo "$dcae_collector_port" > /opt/config/dcae_collector_port.txt
+echo "$ipsec_private_net_gw" > /opt/config/ipsec_private_net_gw_ip.txt
+echo "$ipsec_private_net_cidr" > /opt/config/ipsec_private_net_cidr.txt
+echo "$ipsec_private_network_name" > /opt/config/ipsec_private_network_name.txt
+echo "$packet_src" > /opt/config/packet_source_ip.txt
+echo "$packet_dst" > /opt/config/packet_destination_ip.txt
+echo "$remote_tunnel_ip" > /opt/config/remote_tunnel.txt
+echo "$route_interface" > /opt/config/route_interface.txt
+echo "$traffic_direction" > /opt/config/traffic_direction.txt
+echo "$vipsecB_private_ip_0" > /opt/config/vipsecB_private_ip0.txt
+echo "$vipsecB_private_ip_2" > /opt/config/vipsecB_private_ip2.txt
+echo "$protected_clientB_network_name" > /opt/config/protected_clientB_network_name.txt
+echo "$protected_clientB_net_gw" > /opt/config/protected_clientB_net_gw.txt
+echo "$protected_clientB_net_cidr" > /opt/config/protected_clientB_net_cidr.txt
+
+echo 'vm.nr_hugepages = 1024' >> /etc/sysctl.conf
+sysctl -p
+
+setup_dependencies
+install_vpp
+install_dpdk
+ipsec_settings
diff --git a/kud/tests/vIPSec/sink b/kud/tests/vIPSec/sink
new file mode 100755
index 00000000..c180d43c
--- /dev/null
+++ b/kud/tests/vIPSec/sink
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# COPYRIGHT NOTICE STARTS HERE
+#
+# Copyright 2019 Intel Co., Ltd.
+#
+# 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.
+#
+# COPYRIGHT NOTICE ENDS HERE
+
+# This script prepares the runtime environment
+# for running vIPSec shell scripts on Ubuntu 18.04
+
+set -o nounset
+set -o pipefail
+set -o xtrace
+set -o errexit
+
+function setup_dependencies {
+ apt-get update
+ apt install -y wget darkstat net-tools unzip
+
+ # Configure and run Darkstat
+ sed -i "s/START_DARKSTAT=.*/START_DARKSTAT=yes/g;s/INTERFACE=.*/INTERFACE=\"-i eth1\"/g" /etc/darkstat/init.cfg
+
+ systemctl restart darkstat
+}
+
+mkdir -p /opt/config/
+echo "$protected_net_cidr" > /opt/config/protected_net_cidr.txt
+echo "$vfw_private_ip_0" > /opt/config/fw_ipaddr.txt
+echo "$vsn_private_ip_0" > /opt/config/sink_ipaddr.txt
+echo "$demo_artifacts_version" > /opt/config/demo_artifacts_version.txt
+echo "$protected_net_gw" > /opt/config/protected_net_gw.txt
+echo "$protected_private_net_cidr" > /opt/config/unprotected_net.txt
+
+setup_dependencies
+
diff --git a/src/Makefile b/src/Makefile
index 94359287..8d856563 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,15 +1,19 @@
build:
$(MAKE) -C monitor build
$(MAKE) -C k8splugin build
+ $(MAKE) -C orchestrator build
deploy:
$(MAKE) -C monitor deploy
$(MAKE) -C k8splugin deploy
+ $(MAKE) -C orchestrator deploy
all:
$(MAKE) -C monitor all
$(MAKE) -C k8splugin all
+ $(MAKE) -C orchestrator all
clean:
$(MAKE) -C monitor clean
$(MAKE) -C k8splugin clean
+ $(MAKE) -C orchestrator clean
diff --git a/src/k8splugin/Makefile b/src/k8splugin/Makefile
index 7d41158c..77196afa 100644
--- a/src/k8splugin/Makefile
+++ b/src/k8splugin/Makefile
@@ -25,8 +25,8 @@ deploy: build
.PHONY: test
test: clean
- @go build -buildmode=plugin -o ./mock_files/mock_plugins/mockplugin.so ./mock_files/mock_plugins/mockplugin.go
- @go test -v ./...
+ @go build -race -buildmode=plugin -o ./mock_files/mock_plugins/mockplugin.so ./mock_files/mock_plugins/mockplugin.go
+ @go test -race ./...
format:
@go fmt ./...
@@ -40,5 +40,5 @@ clean:
.PHONY: cover
cover:
- @go test ./... -coverprofile=coverage.out
+ @go test -race ./... -coverprofile=coverage.out
@go tool cover -html=coverage.out -o coverage.html
diff --git a/src/k8splugin/api/brokerhandler.go b/src/k8splugin/api/brokerhandler.go
index 669b539f..7671db44 100644
--- a/src/k8splugin/api/brokerhandler.go
+++ b/src/k8splugin/api/brokerhandler.go
@@ -134,21 +134,19 @@ func (b brokerInstanceHandler) createHandler(w http.ResponseWriter, r *http.Requ
return
}
- rbName := req.getAttributeValue(req.UserDirectives, "definition-name")
- if rbName == "" {
- http.Error(w, "definition-name is missing from user-directives", http.StatusBadRequest)
+ if req.VFModuleModelInvariantID == "" {
+ http.Error(w, "vf-module-model-invariant-id is empty", http.StatusBadRequest)
return
}
- rbVersion := req.getAttributeValue(req.UserDirectives, "definition-version")
- if rbVersion == "" {
- http.Error(w, "definition-version is missing from user-directives", http.StatusBadRequest)
+ if req.VFModuleModelVersionID == "" {
+ http.Error(w, "vf-module-model-version-id is empty", http.StatusBadRequest)
return
}
- profileName := req.getAttributeValue(req.UserDirectives, "profile-name")
+ profileName := req.getAttributeValue(req.SDNCDirectives, "k8s-rb-profile-name")
if profileName == "" {
- http.Error(w, "profile-name is missing from user-directives", http.StatusBadRequest)
+ http.Error(w, "k8s-rb-profile-name is missing from sdnc-directives", http.StatusBadRequest)
return
}
@@ -160,8 +158,8 @@ func (b brokerInstanceHandler) createHandler(w http.ResponseWriter, r *http.Requ
// Setup the resource parameters for making the request
var instReq app.InstanceRequest
- instReq.RBName = rbName
- instReq.RBVersion = rbVersion
+ instReq.RBName = req.VFModuleModelInvariantID
+ instReq.RBVersion = req.VFModuleModelVersionID
instReq.ProfileName = profileName
instReq.CloudRegion = cloudRegion
instReq.Labels = map[string]string{
diff --git a/src/k8splugin/api/brokerhandler_test.go b/src/k8splugin/api/brokerhandler_test.go
index 8ef5e184..83ff588b 100644
--- a/src/k8splugin/api/brokerhandler_test.go
+++ b/src/k8splugin/api/brokerhandler_test.go
@@ -48,18 +48,19 @@ func TestBrokerCreateHandler(t *testing.T) {
expectedCode: http.StatusUnprocessableEntity,
},
{
- label: "Missing parameter failure",
+ label: "Missing vf-module-*-id parameter",
input: bytes.NewBuffer([]byte(`{
"vf-module-model-customization-id": "84sdfkio938",
- "user_directives": {
+ "vf-module-model-invariant-id": "123456qwerty",
+ "sdnc_directives": {
"attributes": [
{
- "attribute_name": "definition-name",
- "attribute_value": "test-rbdef"
+ "attribute_name": "vf_module_name",
+ "attribute_value": "test-vf-module-name"
},
{
- "attribute_name": "definition-version",
- "attribute_value": "v1"
+ "attribute_name": "k8s-rb-profile-name",
+ "attribute_value": "profile1"
}
]
}
@@ -67,9 +68,11 @@ func TestBrokerCreateHandler(t *testing.T) {
expectedCode: http.StatusBadRequest,
},
{
- label: "Succesfully create an Instance",
+ label: "Missing parameter from sdnc_directives",
input: bytes.NewBuffer([]byte(`{
"vf-module-model-customization-id": "84sdfkio938",
+ "vf-module-model-invariant-id": "123456qwerty",
+ "vf-module-model-version-id": "123qweasdzxc",
"sdnc_directives": {
"attributes": [
{
@@ -77,19 +80,24 @@ func TestBrokerCreateHandler(t *testing.T) {
"attribute_value": "test-vf-module-name"
}
]
- },
- "user_directives": {
+ }
+ }`)),
+ expectedCode: http.StatusBadRequest,
+ },
+ {
+ label: "Succesfully create an Instance",
+ input: bytes.NewBuffer([]byte(`{
+ "vf-module-model-customization-id": "84sdfkio938",
+ "vf-module-model-invariant-id": "123456qwerty",
+ "vf-module-model-version-id": "123qweasdzxc",
+ "sdnc_directives": {
"attributes": [
{
- "attribute_name": "definition-name",
- "attribute_value": "test-rbdef"
- },
- {
- "attribute_name": "definition-version",
- "attribute_value": "v1"
+ "attribute_name": "vf_module_name",
+ "attribute_value": "test-vf-module-name"
},
{
- "attribute_name": "profile-name",
+ "attribute_name": "k8s-rb-profile-name",
"attribute_value": "profile1"
}
]
@@ -122,8 +130,8 @@ func TestBrokerCreateHandler(t *testing.T) {
{
ID: "HaKpys8e",
Request: app.InstanceRequest{
- RBName: "test-rbdef",
- RBVersion: "v1",
+ RBName: "123456qwerty",
+ RBVersion: "123qweasdzxc",
ProfileName: "profile1",
CloudRegion: "region1",
},
diff --git a/src/k8splugin/api/instancehandler.go b/src/k8splugin/api/instancehandler.go
index 1dcbcda9..b0437426 100644
--- a/src/k8splugin/api/instancehandler.go
+++ b/src/k8splugin/api/instancehandler.go
@@ -20,10 +20,10 @@ import (
"net/http"
"github.com/onap/multicloud-k8s/src/k8splugin/internal/app"
+ log "github.com/onap/multicloud-k8s/src/k8splugin/internal/logutils"
"github.com/gorilla/mux"
pkgerrors "github.com/pkg/errors"
- log "github.com/onap/multicloud-k8s/src/k8splugin/internal/logutils"
)
// Used to store the backend implementation objects
@@ -37,18 +37,25 @@ func (i instanceHandler) validateBody(body interface{}) error {
switch b := body.(type) {
case app.InstanceRequest:
if b.CloudRegion == "" {
- log.WithFields("CreateVnfRequest bad request", "CloudRegion", "Invalid/Missing CloudRegion in POST request")
+ log.Error("CreateVnfRequest Bad Request", log.Fields{
+ "cloudRegion": "Missing CloudRegion in POST request",
+ })
werr := pkgerrors.Wrap(errors.New("Invalid/Missing CloudRegion in POST request"), "CreateVnfRequest bad request")
return werr
}
if b.RBName == "" || b.RBVersion == "" {
- log.WithFields("CreateVnfRequest bad request", "RBName", "Invalid/Missing resource bundle parameters in POST request")
- log.WithFields("CreateVnfRequest bad request", "RBVersion", "Invalid/Missing resource bundle parameters in POST request")
+ log.Error("CreateVnfRequest Bad Request", log.Fields{
+ "message": "One of RBName, RBVersion is missing",
+ "RBName": b.RBName,
+ "RBVersion": b.RBVersion,
+ })
werr := pkgerrors.Wrap(errors.New("Invalid/Missing resource bundle parameters in POST request"), "CreateVnfRequest bad request")
return werr
}
if b.ProfileName == "" {
- log.WithFields("CreateVnfRequest bad request", "ProfileName", "Invalid/Missing profile name in POST request")
+ log.Error("CreateVnfRequest bad request", log.Fields{
+ "ProfileName": "Missing profile name in POST request",
+ })
werr := pkgerrors.Wrap(errors.New("Invalid/Missing profile name in POST request"), "CreateVnfRequest bad request")
return werr
}
@@ -62,11 +69,15 @@ func (i instanceHandler) createHandler(w http.ResponseWriter, r *http.Request) {
err := json.NewDecoder(r.Body).Decode(&resource)
switch {
case err == io.EOF:
- log.WithFields("http.StatusBadRequest", "Error", "Body empty")
+ log.Error("Body Empty", log.Fields{
+ "error": io.EOF,
+ })
http.Error(w, "Body empty", http.StatusBadRequest)
return
case err != nil:
- log.WithFields("http.StatusUnprocessableEntity", "Error", "http.StatusUnprocessableEntity")
+ log.Error("Error unmarshaling Body", log.Fields{
+ "error": err,
+ })
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
return
}
@@ -74,14 +85,19 @@ func (i instanceHandler) createHandler(w http.ResponseWriter, r *http.Request) {
// Check body for expected parameters
err = i.validateBody(resource)
if err != nil {
- log.WithFields("StatusUnprocessableEntity", "Error", "http.StatusUnprocessableEntity")
+ log.Error("Invalid Parameters in Body", log.Fields{
+ "error": err,
+ })
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
return
}
resp, err := i.client.Create(resource)
if err != nil {
- log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError")
+ log.Error("Error Creating Resource", log.Fields{
+ "error": err,
+ "resource": resource,
+ })
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -90,7 +106,10 @@ func (i instanceHandler) createHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated)
err = json.NewEncoder(w).Encode(resp)
if err != nil {
- log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError")
+ log.Error("Error Marshaling Response", log.Fields{
+ "error": err,
+ "response": resp,
+ })
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -103,7 +122,10 @@ func (i instanceHandler) getHandler(w http.ResponseWriter, r *http.Request) {
resp, err := i.client.Get(id)
if err != nil {
- log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError")
+ log.Error("Error getting Instance", log.Fields{
+ "error": err,
+ "id": id,
+ })
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -112,7 +134,10 @@ func (i instanceHandler) getHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
err = json.NewEncoder(w).Encode(resp)
if err != nil {
- log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError")
+ log.Error("Error Marshaling Response", log.Fields{
+ "error": err,
+ "response": resp,
+ })
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -125,7 +150,10 @@ func (i instanceHandler) statusHandler(w http.ResponseWriter, r *http.Request) {
resp, err := i.client.Status(id)
if err != nil {
- log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError")
+ log.Error("Error getting Status", log.Fields{
+ "error": err,
+ "id": id,
+ })
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -134,7 +162,10 @@ func (i instanceHandler) statusHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
err = json.NewEncoder(w).Encode(resp)
if err != nil {
- log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError")
+ log.Error("Error Marshaling Response", log.Fields{
+ "error": err,
+ "response": resp,
+ })
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -147,11 +178,16 @@ func (i instanceHandler) listHandler(w http.ResponseWriter, r *http.Request) {
//Which will list all instances
rbName := r.FormValue("rb-name")
rbVersion := r.FormValue("rb-version")
- ProfileName := r.FormValue("profile-name")
+ profileName := r.FormValue("profile-name")
- resp, err := i.client.List(rbName, rbVersion, ProfileName)
+ resp, err := i.client.List(rbName, rbVersion, profileName)
if err != nil {
- log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError")
+ log.Error("Error listing instances", log.Fields{
+ "error": err,
+ "rb-name": rbName,
+ "rb-version": rbVersion,
+ "profile-name": profileName,
+ })
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -160,7 +196,10 @@ func (i instanceHandler) listHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
err = json.NewEncoder(w).Encode(resp)
if err != nil {
- log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError")
+ log.Error("Error Marshaling Response", log.Fields{
+ "error": err,
+ "response": resp,
+ })
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -173,7 +212,9 @@ func (i instanceHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
err := i.client.Delete(id)
if err != nil {
- log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError")
+ log.Error("Error Deleting Instance", log.Fields{
+ "error": err,
+ })
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -181,4 +222,3 @@ func (i instanceHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
}
-
diff --git a/src/k8splugin/api/profilehandler.go b/src/k8splugin/api/profilehandler.go
index 9aed2990..acd23060 100644
--- a/src/k8splugin/api/profilehandler.go
+++ b/src/k8splugin/api/profilehandler.go
@@ -21,6 +21,7 @@ import (
"io"
"io/ioutil"
"net/http"
+ "strings"
"github.com/onap/multicloud-k8s/src/k8splugin/internal/rb"
@@ -107,8 +108,14 @@ func (h rbProfileHandler) getHandler(w http.ResponseWriter, r *http.Request) {
ret, err := h.client.Get(rbName, rbVersion, prName)
if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
+ // Separate "Not found" from generic DB errors
+ if strings.Contains(err.Error(), "Error finding") {
+ http.Error(w, err.Error(), http.StatusNotFound)
+ return
+ } else {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
}
w.Header().Set("Content-Type", "application/json")
diff --git a/src/k8splugin/api/profilehandler_test.go b/src/k8splugin/api/profilehandler_test.go
index 4dae377c..9ec9c54c 100644
--- a/src/k8splugin/api/profilehandler_test.go
+++ b/src/k8splugin/api/profilehandler_test.go
@@ -184,10 +184,19 @@ func TestRBProfileGetHandler(t *testing.T) {
},
},
{
- label: "Get Non-Exiting Bundle Profile",
- expectedCode: http.StatusInternalServerError,
+ label: "Get Non-Existing Profile",
+ expectedCode: http.StatusNotFound,
prname: "non-existing-profile",
rbProClient: &mockRBProfile{
+ Items: nil,
+ Err: pkgerrors.New("Error finding master table"),
+ },
+ },
+ {
+ label: "Faulty DB response",
+ expectedCode: http.StatusInternalServerError,
+ prname: "profile",
+ rbProClient: &mockRBProfile{
// list of Profiles that will be returned by the mockclient
Items: []rb.Profile{},
Err: pkgerrors.New("Internal Error"),
diff --git a/src/k8splugin/internal/app/client.go b/src/k8splugin/internal/app/client.go
index e52225d4..d3e5081a 100644
--- a/src/k8splugin/internal/app/client.go
+++ b/src/k8splugin/internal/app/client.go
@@ -14,12 +14,13 @@ limitations under the License.
package app
import (
- "log"
"os"
+ "strings"
"time"
"github.com/onap/multicloud-k8s/src/k8splugin/internal/connection"
"github.com/onap/multicloud-k8s/src/k8splugin/internal/helm"
+ log "github.com/onap/multicloud-k8s/src/k8splugin/internal/logutils"
"github.com/onap/multicloud-k8s/src/k8splugin/internal/plugin"
pkgerrors "github.com/pkg/errors"
@@ -116,11 +117,27 @@ func (k *KubernetesClient) ensureNamespace(namespace string) error {
},
}, namespace, k)
+ // Check for errors getting the namespace while ignoring errors where the namespace does not exist
+ // Error message when namespace does not exist: "namespaces "namespace-name" not found"
+ if err != nil && strings.Contains(err.Error(), "not found") == false {
+ log.Error("Error checking for namespace", log.Fields{
+ "error": err,
+ "namespace": namespace,
+ })
+ return pkgerrors.Wrap(err, "Error checking for namespace: "+namespace)
+ }
+
if ns == "" {
- log.Println("Creating " + namespace + " namespace")
+ log.Info("Creating Namespace", log.Fields{
+ "namespace": namespace,
+ })
_, err = pluginImpl.Create("", namespace, k)
if err != nil {
+ log.Error("Error Creating Namespace", log.Fields{
+ "error": err,
+ "namespace": namespace,
+ })
return pkgerrors.Wrap(err, "Error creating "+namespace+" namespace")
}
}
@@ -134,7 +151,9 @@ func (k *KubernetesClient) createKind(resTempl helm.KubernetesResourceTemplate,
return helm.KubernetesResource{}, pkgerrors.New("File " + resTempl.FilePath + "does not exists")
}
- log.Println("Processing file: " + resTempl.FilePath)
+ log.Info("Processing Kubernetes Resource", log.Fields{
+ "filepath": resTempl.FilePath,
+ })
pluginImpl, err := plugin.GetPluginByKind(resTempl.GVK.Kind)
if err != nil {
@@ -143,11 +162,19 @@ func (k *KubernetesClient) createKind(resTempl helm.KubernetesResourceTemplate,
createdResourceName, err := pluginImpl.Create(resTempl.FilePath, namespace, k)
if err != nil {
- log.Printf("Error: %s while creating: %s", err.Error(), resTempl.GVK.Kind)
+ log.Error("Error Creating Resource", log.Fields{
+ "error": err,
+ "gvk": resTempl.GVK,
+ "filepath": resTempl.FilePath,
+ })
return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error in plugin "+resTempl.GVK.Kind+" plugin")
}
- log.Print(createdResourceName + " created")
+ log.Info("Created Kubernetes Resource", log.Fields{
+ "resource": createdResourceName,
+ "gvk": resTempl.GVK,
+ })
+
return helm.KubernetesResource{
GVK: resTempl.GVK,
Name: createdResourceName,
@@ -175,14 +202,16 @@ func (k *KubernetesClient) createResources(sortedTemplates []helm.KubernetesReso
}
func (k *KubernetesClient) deleteKind(resource helm.KubernetesResource, namespace string) error {
- log.Println("Deleting Kind: " + resource.GVK.Kind)
+ log.Warn("Deleting Resource", log.Fields{
+ "gvk": resource.GVK,
+ "resource": resource.Name,
+ })
pluginImpl, err := plugin.GetPluginByKind(resource.GVK.Kind)
if err != nil {
return pkgerrors.Wrap(err, "Error loading plugin")
}
- log.Println("Deleting resource: " + resource.Name)
err = pluginImpl.Delete(resource, namespace, k)
if err != nil {
return pkgerrors.Wrap(err, "Error deleting "+resource.Name)
diff --git a/src/k8splugin/internal/app/instance.go b/src/k8splugin/internal/app/instance.go
index fef9962f..5d8b2100 100644
--- a/src/k8splugin/internal/app/instance.go
+++ b/src/k8splugin/internal/app/instance.go
@@ -32,19 +32,21 @@ import (
// InstanceRequest contains the parameters needed for instantiation
// of profiles
type InstanceRequest struct {
- RBName string `json:"rb-name"`
- RBVersion string `json:"rb-version"`
- ProfileName string `json:"profile-name"`
- CloudRegion string `json:"cloud-region"`
- Labels map[string]string `json:"labels"`
+ RBName string `json:"rb-name"`
+ RBVersion string `json:"rb-version"`
+ ProfileName string `json:"profile-name"`
+ CloudRegion string `json:"cloud-region"`
+ Labels map[string]string `json:"labels"`
+ OverrideValues map[string]string `json:"override-values"`
}
// InstanceResponse contains the response from instantiation
type InstanceResponse struct {
- ID string `json:"id"`
- Request InstanceRequest `json:"request"`
- Namespace string `json:"namespace"`
- Resources []helm.KubernetesResource `json:"resources"`
+ ID string `json:"id"`
+ Request InstanceRequest `json:"request"`
+ Namespace string `json:"namespace"`
+ Resources []helm.KubernetesResource `json:"resources"`
+ OverrideValues map[string]string `json:"override-values"`
}
// InstanceMiniResponse contains the response from instantiation
@@ -133,7 +135,14 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) {
return InstanceResponse{}, pkgerrors.New("Unable to find Profile to create instance")
}
+ //Convert override values from map to array of strings of the following format
+ //foo=bar
overrideValues := []string{}
+ if i.OverrideValues != nil {
+ for k, v := range i.OverrideValues {
+ overrideValues = append(overrideValues, k+"="+v)
+ }
+ }
//Execute the kubernetes create command
sortedTemplates, err := rb.NewProfileClient().Resolve(i.RBName, i.RBVersion, i.ProfileName, overrideValues)
diff --git a/src/k8splugin/internal/config/config.go b/src/k8splugin/internal/config/config.go
index 0e45308c..89f2553d 100644
--- a/src/k8splugin/internal/config/config.go
+++ b/src/k8splugin/internal/config/config.go
@@ -82,9 +82,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: "k8splugin.io/rb-instance-id",
}
diff --git a/src/k8splugin/internal/db/README.md b/src/k8splugin/internal/db/README.md
new file mode 100644
index 00000000..cba1b7ea
--- /dev/null
+++ b/src/k8splugin/internal/db/README.md
@@ -0,0 +1,123 @@
+# Database Abstraction Layer
+
+This package contains implementations of the Database interface defined in `store.go`
+Any database can be used as the backend as long as the following interface is implemented;
+
+```go
+type Store interface {
+ // Returns nil if db health is good
+ HealthCheck() error
+
+ // Unmarshal implements any unmarshaling needed for the database
+ Unmarshal(inp []byte, out interface{}) error
+
+ // Creates a new master table with key and links data with tag and
+ // creates a pointer to the newly added data in the master table
+ Create(table string, key Key, tag string, data interface{}) error
+
+ // Reads data for a particular key with specific tag.
+ Read(table string, key Key, tag string) ([]byte, error)
+
+ // Update data for particular key with specific tag
+ Update(table string, key Key, tag string, data interface{}) error
+
+ // Deletes a specific tag data for key.
+ // TODO: If tag is empty, it will delete all tags under key.
+ Delete(table string, key Key, tag string) error
+
+ // Reads all master tables and data from the specified tag in table
+ ReadAll(table string, tag string) (map[string][]byte, error)
+}
+```
+
+Therefore, `mongo.go`, `consul.go` implement the above interface and can be used as the backend as needed based on initial configuration.
+
+## Details on Mongo Implementation
+
+`mongo.go` implements the above interface using the `go.mongodb.org/mongo-driver` package.
+The code converts incoming binary data and creates a new document in the database.
+
+### Create
+
+Arguments:
+```go
+collection string
+key interface
+tag string
+data []byte
+```
+
+Create inserts the provided `data` into the `collection` which returns an auto-generated (by `mongodb`) ID which we then associate with the `key` that is provided as one of the arguments.
+
+We use the `FindOneAndUpdate` mongo API to achieve this with the `upsert` option set to `true`.
+We create the following documents in mongodb for each new definition added to the database:
+
+There is a Master Key document that contains references to other documents which are related to this `key`.
+
+#### Master Key Entry
+```json
+{
+ "_id" : ObjectId("5e0a8554b78a15f71d2dce7e"),
+ "key" : { "rbname" : "edgex", "rbversion" : "v1"},
+ "defmetadata" : ObjectId("5e0a8554be261ecb57f067eb"),
+ "defcontent" : ObjectId("5e0a8377bcfcdd0f01dc7b0d")
+}
+```
+#### Metadata Key Entry
+```json
+{
+ "_id" : ObjectId("5e0a8554be261ecb57f067eb"),
+ "defmetadata" : { "rbname" : "edgex", "rbversion" : "v1", "chartname" : "", "description" : "", "labels" : null }
+}
+```
+#### Definition Content
+```json
+{
+ "_id" : ObjectId("5e0a8377bcfcdd0f01dc7b0d"),
+ "defcontent" : "H4sICCVd3FwAA3Byb2ZpbGUxLnRhcgDt1NEKgjAUxvFd7ylG98aWOsGXiYELxLRwJvj2rbyoIPDGiuD/uzmwM9iB7Vvruvrgw7CdXHsUn6Ejm2W3aopcP9eZLYRJM1voPN+ZndAm16kVSn9onheXMLheKeGqfdM0rq07/3bfUv9PJUkiR9+H+tSVajRymM6+lEqN7njxoVSbU+z2deX388r9nWzkr8fGSt5d79pnLOZfm0f+dRrzb7P4DZD/LyDJAAAAAAAAAAAAAAAA/+0Ksq1N5QAoAAA="
+}
+```
+
+### Unmarshal
+
+Data in mongo is stored as `bson` which is a compressed form of `json`. We need mongo to convert the stored `bson` data to regular `json`
+that we can use in our code when returned.
+
+We just use the `bson.Unmarshal` API to achieve this.
+
+### Read
+
+Arguments:
+```go
+collection string
+key interface
+tag string
+```
+
+Read is straight forward and it uses the `FindOne` API to find our Mongo document based on the provided `key` and then gets the corresponding data for the given `tag`. It will return []byte which can then be passed to the `Unmarshal` function to get the desired GO object.
+
+### Delete
+
+Delete is similar to Read and deletes all the objectIDs being stored for a given `key` in the collection.
+
+## Testing Interfaces
+
+The following interface exists to allow for the development of unit tests which don't require mongo to be running.
+It is mentioned so in the code as well.
+
+```go
+// MongoCollection defines the a subset of MongoDB operations
+// Note: This interface is defined mainly for mock testing
+type MongoCollection interface {
+ InsertOne(ctx context.Context, document interface{},
+ opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error)
+ FindOne(ctx context.Context, filter interface{},
+ opts ...*options.FindOneOptions) *mongo.SingleResult
+ FindOneAndUpdate(ctx context.Context, filter interface{},
+ update interface{}, opts ...*options.FindOneAndUpdateOptions) *mongo.SingleResult
+ DeleteOne(ctx context.Context, filter interface{},
+ opts ...*options.DeleteOptions) (*mongo.DeleteResult, error)
+ Find(ctx context.Context, filter interface{},
+ opts ...*options.FindOptions) (*mongo.Cursor, error)
+}
+``` \ No newline at end of file
diff --git a/src/k8splugin/internal/db/etcd.go b/src/k8splugin/internal/db/etcd.go
index fda44b2f..97771a07 100644
--- a/src/k8splugin/internal/db/etcd.go
+++ b/src/k8splugin/internal/db/etcd.go
@@ -71,7 +71,12 @@ func newClient(store *clientv3.Client, c EtcdConfig) (EtcdClient, error) {
if len(c.CertFile) == 0 && len(c.KeyFile) == 0 && len(c.CAFile) == 0 {
tlsConfig = nil
}
- endpoint := "https://" + c.Endpoint + ":2379"
+ endpoint := ""
+ if tlsConfig == nil {
+ endpoint = "http://" + c.Endpoint + ":2379"
+ } else {
+ endpoint = "https://" + c.Endpoint + ":2379"
+ }
store, err = clientv3.New(clientv3.Config{
Endpoints: []string{endpoint},
diff --git a/src/k8splugin/internal/db/testing.go b/src/k8splugin/internal/db/testing.go
index 5f69dcb4..9a427e03 100644
--- a/src/k8splugin/internal/db/testing.go
+++ b/src/k8splugin/internal/db/testing.go
@@ -15,6 +15,7 @@ package db
import (
"encoding/json"
+
pkgerrors "github.com/pkg/errors"
)
@@ -40,6 +41,19 @@ func (m *MockDB) HealthCheck() error {
}
func (m *MockDB) Create(table string, key Key, tag string, data interface{}) error {
+ djs, err := json.Marshal(data)
+ if err != nil {
+ return err
+ }
+
+ d := make(map[string][]byte)
+ d[tag] = djs
+
+ if m.Items == nil {
+ m.Items = make(map[string]map[string][]byte)
+ }
+ m.Items[key.String()] = d
+
return m.Err
}
diff --git a/src/k8splugin/internal/logutils/logger.go b/src/k8splugin/internal/logutils/logger.go
index 7df23474..2e8f9969 100644
--- a/src/k8splugin/internal/logutils/logger.go
+++ b/src/k8splugin/internal/logutils/logger.go
@@ -4,12 +4,25 @@ import (
log "github.com/sirupsen/logrus"
)
+//Fields is type that will be used by the calling function
+type Fields map[string]interface{}
+
func init() {
// Log as JSON instead of the default ASCII formatter.
log.SetFormatter(&log.JSONFormatter{})
}
-func WithFields(msg string, fkey string, fvalue string) {
- log.WithFields(log.Fields{fkey: fvalue}).Error(msg)
+// Error uses the fields provided and logs
+func Error(msg string, fields Fields) {
+ log.WithFields(log.Fields(fields)).Error(msg)
+}
+
+// Warn uses the fields provided and logs
+func Warn(msg string, fields Fields) {
+ log.WithFields(log.Fields(fields)).Warn(msg)
}
+// Info uses the fields provided and logs
+func Info(msg string, fields Fields) {
+ log.WithFields(log.Fields(fields)).Info(msg)
+}
diff --git a/src/k8splugin/internal/rb/definition.go b/src/k8splugin/internal/rb/definition.go
index 65ae8e00..73ea44da 100644
--- a/src/k8splugin/internal/rb/definition.go
+++ b/src/k8splugin/internal/rb/definition.go
@@ -26,6 +26,7 @@ import (
"path/filepath"
"github.com/onap/multicloud-k8s/src/k8splugin/internal/db"
+ "github.com/onap/multicloud-k8s/src/k8splugin/internal/logutils"
pkgerrors "github.com/pkg/errors"
)
@@ -101,6 +102,40 @@ func (v *DefinitionClient) Create(def Definition) (Definition, error) {
return Definition{}, pkgerrors.Wrap(err, "Creating DB Entry")
}
+ // Create a default profile automatically
+ prc := NewProfileClient()
+ pr, err := prc.Create(Profile{
+ RBName: def.RBName,
+ RBVersion: def.RBVersion,
+ ProfileName: "default",
+ Namespace: "default",
+ ReleaseName: "default",
+ })
+
+ if err != nil {
+ logutils.Error("Create Default Profile", logutils.Fields{
+ "error": err,
+ "rb-name": def.RBName,
+ "rb-version": def.RBVersion,
+ "profile-name": "default",
+ "namespace": "default",
+ "release-name": "default",
+ })
+ return Definition{}, pkgerrors.Wrap(err, "Creating Default Profile")
+ }
+
+ err = prc.Upload(pr.RBName, pr.RBVersion, pr.ProfileName, prc.getEmptyProfile())
+ if err != nil {
+ logutils.Error("Upload Empty Profile", logutils.Fields{
+ "error": err,
+ "rb-name": pr.RBName,
+ "rb-version": pr.RBVersion,
+ "profile-name": pr.ProfileName,
+ "profile-content": prc.getEmptyProfile(),
+ })
+ return Definition{}, pkgerrors.Wrap(err, "Upload Empty Profile")
+ }
+
return def, nil
}
@@ -173,6 +208,19 @@ func (v *DefinitionClient) Delete(name string, version string) error {
return pkgerrors.Wrap(err, "Delete Resource Bundle Definition Content")
}
+ //Delete the default profile as well
+ prc := NewProfileClient()
+ err = prc.Delete(name, version, "default")
+ if err != nil {
+ logutils.Error("Delete Default Profile", logutils.Fields{
+ "error": err,
+ "rb-name": name,
+ "rb-version": version,
+ "profile-name": "default",
+ })
+ return pkgerrors.Wrap(err, "Deleting default profile")
+ }
+
return nil
}
diff --git a/src/k8splugin/internal/rb/profile.go b/src/k8splugin/internal/rb/profile.go
index 49768d4b..6efa23b8 100644
--- a/src/k8splugin/internal/rb/profile.go
+++ b/src/k8splugin/internal/rb/profile.go
@@ -338,3 +338,31 @@ func (v *ProfileClient) Resolve(rbName string, rbVersion string,
return sortedTemplates, nil
}
+
+// Returns an empty profile with the following contents
+// Contains a manifest.yaml pointing to an override_values.yaml
+// The override_values.yaml file is empty.
+func (v *ProfileClient) getEmptyProfile() []byte {
+ return []byte{
+ 0x1F, 0x8B, 0x08, 0x08, 0x25, 0x5D, 0xDC, 0x5C, 0x00, 0x03, 0x70,
+ 0x72, 0x6F, 0x66, 0x69, 0x6C, 0x65, 0x31, 0x2E, 0x74, 0x61, 0x72,
+ 0x00, 0xED, 0xD4, 0xD1, 0x0A, 0x82, 0x30, 0x14, 0xC6, 0xF1, 0x5D,
+ 0xEF, 0x29, 0x46, 0xF7, 0xC6, 0x96, 0x3A, 0xC1, 0x97, 0x89, 0x81,
+ 0x0B, 0xC4, 0xB4, 0x70, 0x26, 0xF8, 0xF6, 0xAD, 0xBC, 0xA8, 0x20,
+ 0xF0, 0xC6, 0x8A, 0xE0, 0xFF, 0xBB, 0x39, 0xB0, 0x33, 0xD8, 0x81,
+ 0xED, 0x5B, 0xEB, 0xBA, 0xFA, 0xE0, 0xC3, 0xB0, 0x9D, 0x5C, 0x7B,
+ 0x14, 0x9F, 0xA1, 0x23, 0x9B, 0x65, 0xB7, 0x6A, 0x8A, 0x5C, 0x3F,
+ 0xD7, 0x99, 0x2D, 0x84, 0x49, 0x33, 0x5B, 0xE8, 0x3C, 0xDF, 0x99,
+ 0x9D, 0xD0, 0x26, 0xD7, 0xA9, 0x15, 0x4A, 0x7F, 0x68, 0x9E, 0x17,
+ 0x97, 0x30, 0xB8, 0x5E, 0x29, 0xE1, 0xAA, 0x7D, 0xD3, 0x34, 0xAE,
+ 0xAD, 0x3B, 0xFF, 0x76, 0xDF, 0x52, 0xFF, 0x4F, 0x25, 0x49, 0x22,
+ 0x47, 0xDF, 0x87, 0xFA, 0xD4, 0x95, 0x6A, 0x34, 0x72, 0x98, 0xCE,
+ 0xBE, 0x94, 0x4A, 0x8D, 0xEE, 0x78, 0xF1, 0xA1, 0x54, 0x9B, 0x53,
+ 0xEC, 0xF6, 0x75, 0xE5, 0xF7, 0xF3, 0xCA, 0xFD, 0x9D, 0x6C, 0xE4,
+ 0xAF, 0xC7, 0xC6, 0x4A, 0xDE, 0x5D, 0xEF, 0xDA, 0x67, 0x2C, 0xE6,
+ 0x5F, 0x9B, 0x47, 0xFE, 0x75, 0x1A, 0xF3, 0x6F, 0xB3, 0xF8, 0x0D,
+ 0x90, 0xFF, 0x2F, 0x20, 0xC9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x0A, 0xB2, 0xAD,
+ 0x4D, 0xE5, 0x00, 0x28, 0x00, 0x00,
+ }
+}
diff --git a/src/orchestrator/Makefile b/src/orchestrator/Makefile
new file mode 100644
index 00000000..b17485ca
--- /dev/null
+++ b/src/orchestrator/Makefile
@@ -0,0 +1,37 @@
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright (c) 2019 Intel Corporation
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+export GO111MODULE=on
+
+all: clean
+ CGO_ENABLED=1 GOOS=linux GOARCH=amd64
+ @go build -tags netgo -o ./orchestrator ./cmd/main.go
+
+# The following is done this way as each patch on CI runs build and each merge runs deploy. So for build we don't need to build binary and hence
+# no need to create a static binary with additional flags. However, for generating binary, additional build flags are necessary. This if used with
+# mock plugin errors out for unit tests. So the seperation avoids the error.
+
+build: clean test cover
+deploy: build
+
+.PHONY: test
+test: clean
+ @go test -race ./...
+
+format:
+ @go fmt ./...
+
+clean:
+ @find . -name "*so" -delete
+ @rm -f orchestrator coverage.html coverage.out
+
+.PHONY: cover
+cover:
+ @go test -race ./... -coverprofile=coverage.out
+ @go tool cover -html=coverage.out -o coverage.html
diff --git a/src/orchestrator/api/api.go b/src/orchestrator/api/api.go
new file mode 100644
index 00000000..83f17bbe
--- /dev/null
+++ b/src/orchestrator/api/api.go
@@ -0,0 +1,38 @@
+/*
+Copyright 2018 Intel Corporation.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+ "github.com/onap/multicloud-k8s/src/orchestrator/internal/project"
+
+ "github.com/gorilla/mux"
+)
+
+// NewRouter creates a router that registers the various urls that are supported
+func NewRouter(projectClient project.ProjectManager) *mux.Router {
+
+ router := mux.NewRouter().PathPrefix("/v2").Subrouter()
+
+ if projectClient == nil {
+ projectClient = project.NewProjectClient()
+ }
+ 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")
+
+ return router
+}
diff --git a/src/orchestrator/api/projecthandler.go b/src/orchestrator/api/projecthandler.go
new file mode 100644
index 00000000..30f21de3
--- /dev/null
+++ b/src/orchestrator/api/projecthandler.go
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2019 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"
+
+ "github.com/onap/multicloud-k8s/src/orchestrator/internal/project"
+
+ "github.com/gorilla/mux"
+)
+
+// Used to store backend implementations objects
+// Also simplifies mocking for unit testing purposes
+type projectHandler struct {
+ // Interface that implements Project operations
+ // We will set this variable with a mock interface for testing
+ client project.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
+
+ err := json.NewDecoder(r.Body).Decode(&p)
+ switch {
+ case err == io.EOF:
+ http.Error(w, "Empty body", http.StatusBadRequest)
+ return
+ case err != nil:
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ // Name is required.
+ if p.ProjectName == "" {
+ http.Error(w, "Missing name in POST request", http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.Create(p)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Get handles GET operations on a particular Project Name
+// Returns a rb.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)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Delete handles DELETE operations on a particular Project Name
+func (h projectHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["project-name"]
+
+ err := h.client.Delete(name)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
diff --git a/src/orchestrator/api/projecthandler_test.go b/src/orchestrator/api/projecthandler_test.go
new file mode 100644
index 00000000..2699f2e3
--- /dev/null
+++ b/src/orchestrator/api/projecthandler_test.go
@@ -0,0 +1,228 @@
+/*
+ * 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 api
+
+import (
+ "bytes"
+ "encoding/json"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "reflect"
+ "testing"
+
+ "github.com/onap/multicloud-k8s/src/orchestrator/internal/project"
+
+ pkgerrors "github.com/pkg/errors"
+)
+
+//Creating an embedded interface via anonymous variable
+//This allows us to make mockDB satisfy the DatabaseConnection
+//interface even if we are not implementing all the methods in it
+type mockProjectManager struct {
+ // Items and err will be used to customize each test
+ // via a localized instantiation of mockProjectManager
+ Items []project.Project
+ Err error
+}
+
+func (m *mockProjectManager) Create(inp project.Project) (project.Project, error) {
+ if m.Err != nil {
+ return project.Project{}, m.Err
+ }
+
+ return m.Items[0], nil
+}
+
+func (m *mockProjectManager) Get(name string) (project.Project, error) {
+ if m.Err != nil {
+ return project.Project{}, m.Err
+ }
+
+ return m.Items[0], nil
+}
+
+func (m *mockProjectManager) Delete(name string) error {
+ return m.Err
+}
+
+func TestProjectCreateHandler(t *testing.T) {
+ testCases := []struct {
+ label string
+ reader io.Reader
+ expected project.Project
+ expectedCode int
+ projectClient *mockProjectManager
+ }{
+ {
+ label: "Missing Body Failure",
+ expectedCode: http.StatusBadRequest,
+ projectClient: &mockProjectManager{},
+ },
+ {
+ 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",
+ },
+ projectClient: &mockProjectManager{
+ //Items that will be returned by the mocked Client
+ Items: []project.Project{
+ {
+ ProjectName: "testProject",
+ Description: "Test Project used for unit testing",
+ },
+ },
+ },
+ },
+ {
+ label: "Missing Project Name in Request Body",
+ reader: bytes.NewBuffer([]byte(`{
+ "description":"test description"
+ }`)),
+ expectedCode: http.StatusBadRequest,
+ projectClient: &mockProjectManager{},
+ },
+ }
+
+ 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))
+
+ //Check returned code
+ if resp.StatusCode != testCase.expectedCode {
+ t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+ }
+
+ //Check returned body only if statusCreated
+ if resp.StatusCode == http.StatusCreated {
+ got := project.Project{}
+ json.NewDecoder(resp.Body).Decode(&got)
+
+ if reflect.DeepEqual(testCase.expected, got) == false {
+ t.Errorf("createHandler returned unexpected body: got %v;"+
+ " expected %v", got, testCase.expected)
+ }
+ }
+ })
+ }
+}
+
+func TestProjectGetHandler(t *testing.T) {
+
+ testCases := []struct {
+ label string
+ expected project.Project
+ name, version string
+ expectedCode int
+ projectClient *mockProjectManager
+ }{
+ {
+ label: "Get Project",
+ expectedCode: http.StatusOK,
+ expected: project.Project{
+ ProjectName: "testProject",
+ Description: "A Test project for unit testing",
+ },
+ name: "testProject",
+ projectClient: &mockProjectManager{
+ Items: []project.Project{
+ {
+ ProjectName: "testProject",
+ Description: "A Test project for unit testing",
+ },
+ },
+ },
+ },
+ {
+ label: "Get Non-Exiting Project",
+ expectedCode: http.StatusInternalServerError,
+ name: "nonexistingproject",
+ projectClient: &mockProjectManager{
+ Items: []project.Project{},
+ Err: pkgerrors.New("Internal Error"),
+ },
+ },
+ }
+
+ 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))
+
+ //Check returned code
+ if resp.StatusCode != testCase.expectedCode {
+ t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+ }
+
+ //Check returned body only if statusOK
+ if resp.StatusCode == http.StatusOK {
+ got := project.Project{}
+ json.NewDecoder(resp.Body).Decode(&got)
+
+ if reflect.DeepEqual(testCase.expected, got) == false {
+ t.Errorf("listHandler returned unexpected body: got %v;"+
+ " expected %v", got, testCase.expected)
+ }
+ }
+ })
+ }
+}
+
+func TestProjectDeleteHandler(t *testing.T) {
+
+ testCases := []struct {
+ label string
+ name string
+ version string
+ expectedCode int
+ projectClient *mockProjectManager
+ }{
+ {
+ label: "Delete Project",
+ expectedCode: http.StatusNoContent,
+ name: "testProject",
+ projectClient: &mockProjectManager{},
+ },
+ {
+ label: "Delete Non-Exiting Project",
+ expectedCode: http.StatusInternalServerError,
+ name: "testProject",
+ projectClient: &mockProjectManager{
+ Err: pkgerrors.New("Internal Error"),
+ },
+ },
+ }
+
+ 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))
+
+ //Check returned code
+ if resp.StatusCode != testCase.expectedCode {
+ t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+ }
+ })
+ }
+}
diff --git a/src/orchestrator/api/testing.go b/src/orchestrator/api/testing.go
new file mode 100644
index 00000000..e99ec75b
--- /dev/null
+++ b/src/orchestrator/api/testing.go
@@ -0,0 +1,31 @@
+/*
+ * 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 api
+
+import (
+ "net/http"
+ "net/http/httptest"
+
+ "github.com/gorilla/mux"
+)
+
+func executeRequest(request *http.Request, router *mux.Router) *http.Response {
+ recorder := httptest.NewRecorder()
+ router.ServeHTTP(recorder, request)
+ resp := recorder.Result()
+ return resp
+}
diff --git a/src/orchestrator/cmd/main.go b/src/orchestrator/cmd/main.go
new file mode 100644
index 00000000..657d5bf5
--- /dev/null
+++ b/src/orchestrator/cmd/main.go
@@ -0,0 +1,71 @@
+/*
+Copyright 2018 Intel Corporation.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+ "context"
+ "log"
+ "math/rand"
+ "net/http"
+ "os"
+ "os/signal"
+ "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/gorilla/handlers"
+)
+
+func main() {
+
+ rand.Seed(time.Now().UnixNano())
+
+ err := db.InitializeDatabaseConnection()
+ if err != nil {
+ log.Println("Unable to initialize database connection...")
+ log.Println(err)
+ log.Fatalln("Exiting...")
+ }
+
+ httpRouter := api.NewRouter(nil)
+ loggedRouter := handlers.LoggingHandler(os.Stdout, httpRouter)
+ log.Println("Starting Kubernetes Multicloud API")
+
+ httpServer := &http.Server{
+ Handler: loggedRouter,
+ Addr: ":" + config.GetConfiguration().ServicePort,
+ }
+
+ connectionsClose := make(chan struct{})
+ go func() {
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, os.Interrupt)
+ <-c
+ httpServer.Shutdown(context.Background())
+ close(connectionsClose)
+ }()
+
+ tlsConfig, err := auth.GetTLSConfig("ca.cert", "server.cert", "server.key")
+ if err != nil {
+ log.Println("Error Getting TLS Configuration. Starting without TLS...")
+ log.Fatal(httpServer.ListenAndServe())
+ } else {
+ httpServer.TLSConfig = tlsConfig
+ // empty strings because tlsconfig already has this information
+ err = httpServer.ListenAndServeTLS("", "")
+ }
+}
diff --git a/src/orchestrator/go.mod b/src/orchestrator/go.mod
new file mode 100644
index 00000000..d6fada43
--- /dev/null
+++ b/src/orchestrator/go.mod
@@ -0,0 +1,34 @@
+module github.com/onap/multicloud-k8s/src/orchestrator
+
+require (
+ github.com/docker/engine v0.0.0-20190620014054-c513a4c6c298
+ github.com/ghodss/yaml v1.0.0
+ github.com/gogo/protobuf v1.3.1 // indirect
+ github.com/golang/snappy v0.0.1 // indirect
+ github.com/gorilla/handlers v1.3.0
+ github.com/gorilla/mux v1.6.2
+ github.com/hashicorp/consul v1.4.0
+ github.com/json-iterator/go v1.1.8 // indirect
+ github.com/onap/multicloud-k8s/src/k8splugin v0.0.0-20191115005109-f168ebb73d8d // indirect
+ github.com/pkg/errors v0.8.1
+ github.com/sirupsen/logrus v1.4.2
+ go.etcd.io/etcd v3.3.12+incompatible
+ go.mongodb.org/mongo-driver v1.0.0
+ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
+ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
+ k8s.io/api v0.0.0-20190831074750-7364b6bdad65
+ k8s.io/apimachinery v0.0.0-20190831074630-461753078381
+ k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
+ k8s.io/helm v2.14.3+incompatible
+ k8s.io/klog v1.0.0 // indirect
+)
+
+replace (
+ k8s.io/api => k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b
+ k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8
+ k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d
+ k8s.io/apiserver => k8s.io/apiserver v0.0.0-20190409021813-1ec86e4da56c
+ k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20190409023024-d644b00f3b79
+ k8s.io/client-go => k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
+ k8s.io/cloud-provider => k8s.io/cloud-provider v0.0.0-20190409023720-1bc0c81fa51d
+)
diff --git a/src/orchestrator/go.sum b/src/orchestrator/go.sum
new file mode 100644
index 00000000..732bc280
--- /dev/null
+++ b/src/orchestrator/go.sum
@@ -0,0 +1,362 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
+github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
+github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
+github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/Masterminds/sprig v2.17.1+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
+github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
+github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
+github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
+github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/etcd v3.3.12+incompatible h1:pAWNwdf7QiT1zfaWyqCtNZQWCLByQyA3JrSQyuYAqnQ=
+github.com/coreos/etcd v3.3.12+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
+github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/docker v0.7.3-0.20190912223608-ad718029b705/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/engine v0.0.0-20190620014054-c513a4c6c298/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY=
+github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
+github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
+github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
+github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
+github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
+github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
+github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
+github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
+github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
+github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/handlers v1.3.0 h1:tsg9qP3mjt1h4Roxp+M1paRjrVBfPSOpBuVclh6YluI=
+github.com/gorilla/handlers v1.3.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
+github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
+github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.11.1/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/hashicorp/consul v1.4.0 h1:PQTW4xCuAExEiSbhrsFsikzbW5gVBoi74BjUvYFyKHw=
+github.com/hashicorp/consul v1.4.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
+github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
+github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 h1:VBj0QYQ0u2MCJzBfeYXGexnAl17GsH1yidnoxCqqD9E=
+github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90/go.mod h1:o4zcYY1e0GEZI6eSEr+43QDYmuGglw1qSO6qdHUHCgg=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/memberlist v0.1.5/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.1 h1:mYs6SMzu72+90OcPa5wr3nfznA4Dw9UyR791ZFNOIf4=
+github.com/hashicorp/serf v0.8.1/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
+github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
+github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
+github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+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/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=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rubenv/sql-migrate v0.0.0-20190902133344-8926f37f0bc1/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/technosophos/moniker v0.0.0-20180509230615-a5dbd03a2245/go.mod h1:O1c8HleITsZqzNZDjSNzirUGsMT0oGu9LhHKoJrqO+A=
+github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
+github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0=
+github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/etcd v3.3.12+incompatible h1:V6PRYRGpU4k5EajJaaj/GL3hqIdzyPnBU8aPUp+35yw=
+go.etcd.io/etcd v3.3.12+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI=
+go.mongodb.org/mongo-driver v1.0.0 h1:KxPRDyfB2xXnDE2My8acoOWBQkfv3tz0SaWTRZjJR0c=
+go.mongodb.org/mongo-driver v1.0.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b h1:aBGgKJUM9Hk/3AE8WaZIApnTxG35kbuQba2w+SXqezo=
+k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
+k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
+k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d h1:Jmdtdt1ZnoGfWWIIik61Z7nKYgO3J+swQJtPYsP9wHA=
+k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
+k8s.io/apiserver v0.0.0-20190409021813-1ec86e4da56c/go.mod h1:6bqaTSOSJavUIXUtfaR9Os9JtTCm8ZqH2SUl2S60C4w=
+k8s.io/cli-runtime v0.0.0-20190409023024-d644b00f3b79/go.mod h1:qWnH3/b8sp/l7EvlDh7ulDU3UWA4P4N1NFbEEP791tM=
+k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible h1:U5Bt+dab9K8qaUmXINrkXO135kA11/i5Kg1RUydgaMQ=
+k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
+k8s.io/cloud-provider v0.0.0-20190409023720-1bc0c81fa51d/go.mod h1:LlIffnLBu+GG7d4ppPzC8UnA1Ex8S+ntmSRVsnr7Xy4=
+k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/helm v2.14.3+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI=
+k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
+k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
+k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
+k8s.io/kubernetes v1.14.1/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
+k8s.io/utils v0.0.0-20190907131718-3d4f5b7dea0b/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
+sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
+sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
+sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
diff --git a/src/orchestrator/internal/auth/auth.go b/src/orchestrator/internal/auth/auth.go
new file mode 100644
index 00000000..3da8f2af
--- /dev/null
+++ b/src/orchestrator/internal/auth/auth.go
@@ -0,0 +1,107 @@
+/*
+ * 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 auth
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "encoding/base64"
+ "encoding/pem"
+ "io/ioutil"
+ "log"
+
+ pkgerrors "github.com/pkg/errors"
+)
+
+// GetTLSConfig initializes a tlsConfig using the CA's certificate
+// This config is then used to enable the server for mutual TLS
+func GetTLSConfig(caCertFile string, certFile string, keyFile string) (*tls.Config, error) {
+
+ // Initialize tlsConfig once
+ caCert, err := ioutil.ReadFile(caCertFile)
+
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Read CA Cert file")
+ }
+
+ caCertPool := x509.NewCertPool()
+ caCertPool.AppendCertsFromPEM(caCert)
+
+ tlsConfig := &tls.Config{
+ // Change to RequireAndVerify once we have mandatory certs
+ ClientAuth: tls.VerifyClientCertIfGiven,
+ ClientCAs: caCertPool,
+ MinVersion: tls.VersionTLS12,
+ }
+
+ certPEMBlk, err := readPEMBlock(certFile)
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Read Cert File")
+ }
+
+ keyPEMBlk, err := readPEMBlock(keyFile)
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Read Key File")
+ }
+
+ tlsConfig.Certificates = make([]tls.Certificate, 1)
+ tlsConfig.Certificates[0], err = tls.X509KeyPair(certPEMBlk, keyPEMBlk)
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Load x509 cert and key")
+ }
+
+ tlsConfig.BuildNameToCertificate()
+ return tlsConfig, nil
+}
+
+func readPEMBlock(filename string) ([]byte, error) {
+
+ pemData, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Read PEM File")
+ }
+
+ pemBlock, rest := pem.Decode(pemData)
+ if len(rest) > 0 {
+ log.Println("Pemfile has extra data")
+ }
+
+ if x509.IsEncryptedPEMBlock(pemBlock) {
+ password, err := ioutil.ReadFile(filename + ".pass")
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Read Password File")
+ }
+
+ pByte, err := base64.StdEncoding.DecodeString(string(password))
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Decode PEM Password")
+ }
+
+ pemData, err = x509.DecryptPEMBlock(pemBlock, pByte)
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "Decrypt PEM Data")
+ }
+ var newPEMBlock pem.Block
+ newPEMBlock.Type = pemBlock.Type
+ newPEMBlock.Bytes = pemData
+ // Converting back to PEM from DER data you get from
+ // DecryptPEMBlock
+ pemData = pem.EncodeToMemory(&newPEMBlock)
+ }
+
+ return pemData, nil
+}
diff --git a/src/orchestrator/internal/auth/auth_test.go b/src/orchestrator/internal/auth/auth_test.go
new file mode 100644
index 00000000..e41cb1ac
--- /dev/null
+++ b/src/orchestrator/internal/auth/auth_test.go
@@ -0,0 +1,47 @@
+/*
+* Copyright 2018 TechMahindra
+*
+* 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 auth
+
+import (
+ "crypto/tls"
+ "testing"
+)
+
+//Unit test to varify GetTLSconfig func and varify the tls config min version to be 771
+//Assuming cert file name as auth_test.cert
+func TestGetTLSConfig(t *testing.T) {
+ _, err := GetTLSConfig("filedoesnotexist.cert", "filedoesnotexist.cert", "filedoesnotexist.cert")
+ 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")
+ if err != nil {
+ t.Fatal("Test Failed as GetTLSConfig returned error: " + err.Error())
+ }
+ expected := tls.VersionTLS12
+ actual := tlsConfig.MinVersion
+ if tlsConfig != nil {
+ if int(actual) != expected {
+ t.Errorf("Test Failed due to version mismatch")
+ }
+ if tlsConfig == nil {
+ t.Errorf("Test Failed due to GetTLSConfig returned nil")
+ }
+ }
+}
diff --git a/src/orchestrator/internal/config/config.go b/src/orchestrator/internal/config/config.go
new file mode 100644
index 00000000..cb4656f0
--- /dev/null
+++ b/src/orchestrator/internal/config/config.go
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2019 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 config
+
+import (
+ "encoding/json"
+ "log"
+ "os"
+ "reflect"
+)
+
+// Configuration loads up all the values that are used to configure
+// backend implementations
+type Configuration struct {
+ CAFile string `json:"ca-file"`
+ ServerCert string `json:"server-cert"`
+ ServerKey string `json:"server-key"`
+ Password string `json:"password"`
+ DatabaseIP string `json:"database-ip"`
+ DatabaseType string `json:"database-type"`
+ PluginDir string `json:"plugin-dir"`
+ EtcdIP string `json:"etcd-ip"`
+ EtcdCert string `json:"etcd-cert"`
+ EtcdKey string `json:"etcd-key"`
+ EtcdCAFile string `json:"etcd-ca-file"`
+ ServicePort string `json:"service-port"`
+ KubernetesLabelName string `json:"kubernetes-label-name"`
+}
+
+// Config is the structure that stores the configuration
+var gConfig *Configuration
+
+// readConfigFile reads the specified smsConfig file to setup some env variables
+func readConfigFile(file string) (*Configuration, error) {
+ f, err := os.Open(file)
+ if err != nil {
+ return defaultConfiguration(), err
+ }
+ defer f.Close()
+
+ // Setup some defaults here
+ // If the json file has values in it, the defaults will be overwritten
+ conf := defaultConfiguration()
+
+ // Read the configuration from json file
+ decoder := json.NewDecoder(f)
+ decoder.DisallowUnknownFields()
+ err = decoder.Decode(conf)
+ if err != nil {
+ return conf, err
+ }
+
+ return conf, nil
+}
+
+func defaultConfiguration() *Configuration {
+ cwd, err := os.Getwd()
+ if err != nil {
+ log.Println("Error getting cwd. Using .")
+ cwd = "."
+ }
+
+ return &Configuration{
+ CAFile: "ca.cert",
+ ServerCert: "server.cert",
+ ServerKey: "server.key",
+ Password: "",
+ DatabaseIP: "127.0.0.1",
+ DatabaseType: "mongo",
+ PluginDir: cwd,
+ EtcdIP: "127.0.0.1",
+ EtcdCert: "etcd.cert",
+ EtcdKey: "etcd.key",
+ EtcdCAFile: "etcd-ca.cert",
+ ServicePort: "9015",
+ KubernetesLabelName: "orchestrator.io/rb-instance-id",
+ }
+}
+
+// GetConfiguration returns the configuration for the app.
+// It will try to load it if it is not already loaded.
+func GetConfiguration() *Configuration {
+ if gConfig == nil {
+ conf, err := readConfigFile("config.json")
+ if err != nil {
+ log.Println("Error loading config file: ", err)
+ log.Println("Using defaults...")
+ }
+ gConfig = conf
+ }
+
+ return gConfig
+}
+
+// SetConfigValue sets a value in the configuration
+// This is mostly used to customize the application and
+// should be used carefully.
+func SetConfigValue(key string, value string) *Configuration {
+ c := GetConfiguration()
+ if value == "" || key == "" {
+ return c
+ }
+
+ v := reflect.ValueOf(c).Elem()
+ if v.Kind() == reflect.Struct {
+ f := v.FieldByName(key)
+ if f.IsValid() {
+ if f.CanSet() {
+ if f.Kind() == reflect.String {
+ f.SetString(value)
+ }
+ }
+ }
+ }
+ return c
+}
diff --git a/src/orchestrator/internal/config/config_test.go b/src/orchestrator/internal/config/config_test.go
new file mode 100644
index 00000000..ce7641ae
--- /dev/null
+++ b/src/orchestrator/internal/config/config_test.go
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 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 config
+
+import (
+ "testing"
+)
+
+func TestReadConfigurationFile(t *testing.T) {
+ t.Run("Non Existent Configuration File", func(t *testing.T) {
+ _, err := readConfigFile("filedoesnotexist.json")
+ if err == nil {
+ t.Fatal("ReadConfiguationFile: Expected Error, got nil")
+ }
+ })
+
+ t.Run("Read Configuration File", func(t *testing.T) {
+ conf, err := readConfigFile("../../tests/configs/mock_config.json")
+ if err != nil {
+ t.Fatal("ReadConfigurationFile: Error reading file: ", err)
+ }
+ if conf.DatabaseType != "mock_db_test" {
+ t.Fatal("ReadConfigurationFile: Incorrect entry read from file")
+ }
+ })
+}
diff --git a/src/orchestrator/internal/db/README.md b/src/orchestrator/internal/db/README.md
new file mode 100644
index 00000000..cba1b7ea
--- /dev/null
+++ b/src/orchestrator/internal/db/README.md
@@ -0,0 +1,123 @@
+# Database Abstraction Layer
+
+This package contains implementations of the Database interface defined in `store.go`
+Any database can be used as the backend as long as the following interface is implemented;
+
+```go
+type Store interface {
+ // Returns nil if db health is good
+ HealthCheck() error
+
+ // Unmarshal implements any unmarshaling needed for the database
+ Unmarshal(inp []byte, out interface{}) error
+
+ // Creates a new master table with key and links data with tag and
+ // creates a pointer to the newly added data in the master table
+ Create(table string, key Key, tag string, data interface{}) error
+
+ // Reads data for a particular key with specific tag.
+ Read(table string, key Key, tag string) ([]byte, error)
+
+ // Update data for particular key with specific tag
+ Update(table string, key Key, tag string, data interface{}) error
+
+ // Deletes a specific tag data for key.
+ // TODO: If tag is empty, it will delete all tags under key.
+ Delete(table string, key Key, tag string) error
+
+ // Reads all master tables and data from the specified tag in table
+ ReadAll(table string, tag string) (map[string][]byte, error)
+}
+```
+
+Therefore, `mongo.go`, `consul.go` implement the above interface and can be used as the backend as needed based on initial configuration.
+
+## Details on Mongo Implementation
+
+`mongo.go` implements the above interface using the `go.mongodb.org/mongo-driver` package.
+The code converts incoming binary data and creates a new document in the database.
+
+### Create
+
+Arguments:
+```go
+collection string
+key interface
+tag string
+data []byte
+```
+
+Create inserts the provided `data` into the `collection` which returns an auto-generated (by `mongodb`) ID which we then associate with the `key` that is provided as one of the arguments.
+
+We use the `FindOneAndUpdate` mongo API to achieve this with the `upsert` option set to `true`.
+We create the following documents in mongodb for each new definition added to the database:
+
+There is a Master Key document that contains references to other documents which are related to this `key`.
+
+#### Master Key Entry
+```json
+{
+ "_id" : ObjectId("5e0a8554b78a15f71d2dce7e"),
+ "key" : { "rbname" : "edgex", "rbversion" : "v1"},
+ "defmetadata" : ObjectId("5e0a8554be261ecb57f067eb"),
+ "defcontent" : ObjectId("5e0a8377bcfcdd0f01dc7b0d")
+}
+```
+#### Metadata Key Entry
+```json
+{
+ "_id" : ObjectId("5e0a8554be261ecb57f067eb"),
+ "defmetadata" : { "rbname" : "edgex", "rbversion" : "v1", "chartname" : "", "description" : "", "labels" : null }
+}
+```
+#### Definition Content
+```json
+{
+ "_id" : ObjectId("5e0a8377bcfcdd0f01dc7b0d"),
+ "defcontent" : "H4sICCVd3FwAA3Byb2ZpbGUxLnRhcgDt1NEKgjAUxvFd7ylG98aWOsGXiYELxLRwJvj2rbyoIPDGiuD/uzmwM9iB7Vvruvrgw7CdXHsUn6Ejm2W3aopcP9eZLYRJM1voPN+ZndAm16kVSn9onheXMLheKeGqfdM0rq07/3bfUv9PJUkiR9+H+tSVajRymM6+lEqN7njxoVSbU+z2deX388r9nWzkr8fGSt5d79pnLOZfm0f+dRrzb7P4DZD/LyDJAAAAAAAAAAAAAAAA/+0Ksq1N5QAoAAA="
+}
+```
+
+### Unmarshal
+
+Data in mongo is stored as `bson` which is a compressed form of `json`. We need mongo to convert the stored `bson` data to regular `json`
+that we can use in our code when returned.
+
+We just use the `bson.Unmarshal` API to achieve this.
+
+### Read
+
+Arguments:
+```go
+collection string
+key interface
+tag string
+```
+
+Read is straight forward and it uses the `FindOne` API to find our Mongo document based on the provided `key` and then gets the corresponding data for the given `tag`. It will return []byte which can then be passed to the `Unmarshal` function to get the desired GO object.
+
+### Delete
+
+Delete is similar to Read and deletes all the objectIDs being stored for a given `key` in the collection.
+
+## Testing Interfaces
+
+The following interface exists to allow for the development of unit tests which don't require mongo to be running.
+It is mentioned so in the code as well.
+
+```go
+// MongoCollection defines the a subset of MongoDB operations
+// Note: This interface is defined mainly for mock testing
+type MongoCollection interface {
+ InsertOne(ctx context.Context, document interface{},
+ opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error)
+ FindOne(ctx context.Context, filter interface{},
+ opts ...*options.FindOneOptions) *mongo.SingleResult
+ FindOneAndUpdate(ctx context.Context, filter interface{},
+ update interface{}, opts ...*options.FindOneAndUpdateOptions) *mongo.SingleResult
+ DeleteOne(ctx context.Context, filter interface{},
+ opts ...*options.DeleteOptions) (*mongo.DeleteResult, error)
+ Find(ctx context.Context, filter interface{},
+ opts ...*options.FindOptions) (*mongo.Cursor, error)
+}
+``` \ No newline at end of file
diff --git a/src/orchestrator/internal/db/mock.go b/src/orchestrator/internal/db/mock.go
new file mode 100644
index 00000000..1dbca4b4
--- /dev/null
+++ b/src/orchestrator/internal/db/mock.go
@@ -0,0 +1,94 @@
+/*
+Copyright 2018 Intel Corporation.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package db
+
+import (
+ "encoding/json"
+
+ pkgerrors "github.com/pkg/errors"
+)
+
+type MockKey struct {
+ Key string
+}
+
+func (m MockKey) String() string {
+ return m.Key
+}
+
+//Creating an embedded interface via anonymous variable
+//This allows us to make mockDB satisfy the DatabaseConnection
+//interface even if we are not implementing all the methods in it
+type MockDB struct {
+ Store
+ Items map[string]map[string][]byte
+ Err error
+}
+
+func (m *MockDB) HealthCheck() error {
+ return m.Err
+}
+
+func (m *MockDB) Create(table string, key Key, tag string, data interface{}) error {
+ return m.Err
+}
+
+func (m *MockDB) Update(table string, key Key, tag string, data interface{}) error {
+ return m.Err
+}
+
+// MockDB uses simple JSON and not BSON
+func (m *MockDB) Unmarshal(inp []byte, out interface{}) error {
+ err := json.Unmarshal(inp, out)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Unmarshaling json")
+ }
+ return nil
+}
+
+func (m *MockDB) Read(table string, key Key, tag string) ([]byte, error) {
+ if m.Err != nil {
+ return nil, m.Err
+ }
+
+ for k, v := range m.Items {
+ if k == key.String() {
+ return v[tag], nil
+ }
+ }
+
+ return nil, m.Err
+}
+
+func (m *MockDB) Delete(table string, key Key, tag string) error {
+ return m.Err
+}
+
+func (m *MockDB) ReadAll(table string, tag string) (map[string][]byte, error) {
+ if m.Err != nil {
+ return nil, m.Err
+ }
+
+ ret := make(map[string][]byte)
+
+ for k, v := range m.Items {
+ for k1, v1 := range v {
+ if k1 == tag {
+ ret[k] = v1
+ }
+ }
+ }
+
+ return ret, nil
+}
diff --git a/src/orchestrator/internal/db/mongo.go b/src/orchestrator/internal/db/mongo.go
new file mode 100644
index 00000000..3720a4f2
--- /dev/null
+++ b/src/orchestrator/internal/db/mongo.go
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package db
+
+import (
+ "log"
+
+ "golang.org/x/net/context"
+
+ "github.com/onap/multicloud-k8s/src/orchestrator/internal/config"
+
+ pkgerrors "github.com/pkg/errors"
+ "go.mongodb.org/mongo-driver/bson"
+ "go.mongodb.org/mongo-driver/bson/primitive"
+ "go.mongodb.org/mongo-driver/mongo"
+ "go.mongodb.org/mongo-driver/mongo/options"
+)
+
+// MongoCollection defines the a subset of MongoDB operations
+// Note: This interface is defined mainly for mock testing
+type MongoCollection interface {
+ InsertOne(ctx context.Context, document interface{},
+ opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error)
+ FindOne(ctx context.Context, filter interface{},
+ opts ...*options.FindOneOptions) *mongo.SingleResult
+ FindOneAndUpdate(ctx context.Context, filter interface{},
+ update interface{}, opts ...*options.FindOneAndUpdateOptions) *mongo.SingleResult
+ DeleteOne(ctx context.Context, filter interface{},
+ opts ...*options.DeleteOptions) (*mongo.DeleteResult, error)
+ Find(ctx context.Context, filter interface{},
+ opts ...*options.FindOptions) (*mongo.Cursor, error)
+}
+
+// MongoStore is an implementation of the db.Store interface
+type MongoStore struct {
+ db *mongo.Database
+}
+
+// This exists only for allowing us to mock the collection object
+// for testing purposes
+var getCollection = func(coll string, m *MongoStore) MongoCollection {
+ return m.db.Collection(coll)
+}
+
+// This exists only for allowing us to mock the DecodeBytes function
+// Mainly because we cannot construct a SingleResult struct from our
+// tests. All fields in that struct are private.
+var decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) {
+ return sr.DecodeBytes()
+}
+
+// These exists only for allowing us to mock the cursor.Next function
+// Mainly because we cannot construct a mongo.Cursor struct from our
+// tests. All fields in that struct are private and there is no public
+// constructor method.
+var cursorNext = func(ctx context.Context, cursor *mongo.Cursor) bool {
+ return cursor.Next(ctx)
+}
+var cursorClose = func(ctx context.Context, cursor *mongo.Cursor) error {
+ return cursor.Close(ctx)
+}
+
+// NewMongoStore initializes a Mongo Database with the name provided
+// If a database with that name exists, it will be returned
+func NewMongoStore(name string, store *mongo.Database) (Store, error) {
+ if store == nil {
+ ip := "mongodb://" + config.GetConfiguration().DatabaseIP + ":27017"
+ clientOptions := options.Client()
+ clientOptions.ApplyURI(ip)
+ mongoClient, err := mongo.NewClient(clientOptions)
+ if err != nil {
+ return nil, err
+ }
+
+ err = mongoClient.Connect(context.Background())
+ if err != nil {
+ return nil, err
+ }
+ store = mongoClient.Database(name)
+ }
+
+ return &MongoStore{
+ db: store,
+ }, nil
+}
+
+// HealthCheck verifies if the database is up and running
+func (m *MongoStore) HealthCheck() error {
+
+ _, err := decodeBytes(m.db.RunCommand(context.Background(), bson.D{{"serverStatus", 1}}))
+ if err != nil {
+ return pkgerrors.Wrap(err, "Error getting server status")
+ }
+
+ return nil
+}
+
+// validateParams checks to see if any parameters are empty
+func (m *MongoStore) validateParams(args ...interface{}) bool {
+ for _, v := range args {
+ val, ok := v.(string)
+ if ok {
+ if val == "" {
+ return false
+ }
+ } else {
+ if v == nil {
+ return false
+ }
+ }
+ }
+
+ return true
+}
+
+// Create is used to create a DB entry
+func (m *MongoStore) Create(coll string, key Key, tag string, data interface{}) error {
+ if data == nil || !m.validateParams(coll, key, tag) {
+ return pkgerrors.New("No Data to store")
+ }
+
+ c := getCollection(coll, m)
+ ctx := context.Background()
+
+ //Insert the data and then add the objectID to the masterTable
+ res, err := c.InsertOne(ctx, bson.D{
+ {tag, data},
+ })
+ if err != nil {
+ return pkgerrors.Errorf("Error inserting into database: %s", err.Error())
+ }
+
+ //Add objectID of created data to masterKey document
+ //Create masterkey document if it does not exist
+ filter := bson.D{{"key", key}}
+
+ _, err = decodeBytes(
+ c.FindOneAndUpdate(
+ ctx,
+ filter,
+ bson.D{
+ {"$set", bson.D{
+ {tag, res.InsertedID},
+ }},
+ },
+ options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(options.After)))
+
+ if err != nil {
+ return pkgerrors.Errorf("Error updating master table: %s", err.Error())
+ }
+
+ return nil
+}
+
+// Update is used to update a DB entry
+func (m *MongoStore) Update(coll string, key Key, tag string, data interface{}) error {
+ if data == nil || !m.validateParams(coll, key, tag) {
+ return pkgerrors.New("No Data to update")
+ }
+
+ c := getCollection(coll, m)
+ ctx := context.Background()
+
+ //Get the masterkey document based on given key
+ filter := bson.D{{"key", key}}
+ keydata, err := decodeBytes(c.FindOne(context.Background(), filter))
+ if err != nil {
+ return pkgerrors.Errorf("Error finding master table: %s", err.Error())
+ }
+
+ //Read the tag objectID from document
+ tagoid, ok := keydata.Lookup(tag).ObjectIDOK()
+ if !ok {
+ return pkgerrors.Errorf("Error finding objectID for tag %s", tag)
+ }
+
+ //Update the document with new data
+ filter = bson.D{{"_id", tagoid}}
+
+ _, err = decodeBytes(
+ c.FindOneAndUpdate(
+ ctx,
+ filter,
+ bson.D{
+ {"$set", bson.D{
+ {tag, data},
+ }},
+ },
+ options.FindOneAndUpdate().SetReturnDocument(options.After)))
+
+ if err != nil {
+ return pkgerrors.Errorf("Error updating record: %s", err.Error())
+ }
+
+ return nil
+}
+
+// Unmarshal implements an unmarshaler for bson data that
+// is produced from the mongo database
+func (m *MongoStore) Unmarshal(inp []byte, out interface{}) error {
+ err := bson.Unmarshal(inp, out)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Unmarshaling bson")
+ }
+ return nil
+}
+
+// Read method returns the data stored for this key and for this particular tag
+func (m *MongoStore) Read(coll string, key Key, tag string) ([]byte, error) {
+ if !m.validateParams(coll, key, tag) {
+ return nil, pkgerrors.New("Mandatory fields are missing")
+ }
+
+ c := getCollection(coll, m)
+ ctx := context.Background()
+
+ //Get the masterkey document based on given key
+ filter := bson.D{{"key", key}}
+ keydata, err := decodeBytes(c.FindOne(context.Background(), filter))
+ if err != nil {
+ return nil, pkgerrors.Errorf("Error finding master table: %s", err.Error())
+ }
+
+ //Read the tag objectID from document
+ tagoid, ok := keydata.Lookup(tag).ObjectIDOK()
+ if !ok {
+ return nil, pkgerrors.Errorf("Error finding objectID for tag %s", tag)
+ }
+
+ //Use tag objectID to read the data from store
+ filter = bson.D{{"_id", tagoid}}
+ tagdata, err := decodeBytes(c.FindOne(ctx, filter))
+ if err != nil {
+ return nil, pkgerrors.Errorf("Error reading found object: %s", err.Error())
+ }
+
+ //Return the data as a byte array
+ //Convert string data to byte array using the built-in functions
+ switch tagdata.Lookup(tag).Type {
+ case bson.TypeString:
+ return []byte(tagdata.Lookup(tag).StringValue()), nil
+ default:
+ return tagdata.Lookup(tag).Value, nil
+ }
+}
+
+// Helper function that deletes an object by its ID
+func (m *MongoStore) deleteObjectByID(coll string, objID primitive.ObjectID) error {
+
+ c := getCollection(coll, m)
+ ctx := context.Background()
+
+ _, err := c.DeleteOne(ctx, bson.D{{"_id", objID}})
+ if err != nil {
+ return pkgerrors.Errorf("Error Deleting from database: %s", err.Error())
+ }
+
+ log.Printf("Deleted Obj with ID %s", objID.String())
+ return nil
+}
+
+// Delete method removes a document from the Database that matches key
+// TODO: delete all referenced docs if tag is empty string
+func (m *MongoStore) Delete(coll string, key Key, tag string) error {
+ if !m.validateParams(coll, key, tag) {
+ return pkgerrors.New("Mandatory fields are missing")
+ }
+
+ c := getCollection(coll, m)
+ ctx := context.Background()
+
+ //Get the masterkey document based on given key
+ filter := bson.D{{"key", key}}
+ //Remove the tag ID entry from masterkey table
+ update := bson.D{
+ {
+ "$unset", bson.D{
+ {tag, ""},
+ },
+ },
+ }
+ keydata, err := decodeBytes(c.FindOneAndUpdate(ctx, filter, update,
+ options.FindOneAndUpdate().SetReturnDocument(options.Before)))
+ if err != nil {
+ //No document was found. Return nil.
+ if err == mongo.ErrNoDocuments {
+ return nil
+ }
+ //Return any other error that was found.
+ return pkgerrors.Errorf("Error decoding master table after update: %s",
+ err.Error())
+ }
+
+ //Read the tag objectID from document
+ elems, err := keydata.Elements()
+ if err != nil {
+ return pkgerrors.Errorf("Error reading elements from database: %s", err.Error())
+ }
+
+ tagoid, ok := keydata.Lookup(tag).ObjectIDOK()
+ if !ok {
+ return pkgerrors.Errorf("Error finding objectID for tag %s", tag)
+ }
+
+ //Use tag objectID to read the data from store
+ err = m.deleteObjectByID(coll, tagoid)
+ if err != nil {
+ return pkgerrors.Errorf("Error deleting from database: %s", err.Error())
+ }
+
+ //Delete master table if no more tags left
+ //_id, key and tag should be elements in before doc
+ //if master table needs to be removed too
+ if len(elems) == 3 {
+ keyid, ok := keydata.Lookup("_id").ObjectIDOK()
+ if !ok {
+ return pkgerrors.Errorf("Error finding objectID for key %s", key)
+ }
+ err = m.deleteObjectByID(coll, keyid)
+ if err != nil {
+ return pkgerrors.Errorf("Error deleting master table from database: %s", err.Error())
+ }
+ }
+
+ return nil
+}
+
+// ReadAll is used to get all documents in db of a particular tag
+func (m *MongoStore) ReadAll(coll, tag string) (map[string][]byte, error) {
+ if !m.validateParams(coll, tag) {
+ return nil, pkgerrors.New("Missing collection or tag name")
+ }
+
+ c := getCollection(coll, m)
+ ctx := context.Background()
+
+ //Get all master tables in this collection
+ filter := bson.D{
+ {"key", bson.D{
+ {"$exists", true},
+ }},
+ }
+ cursor, err := c.Find(ctx, filter)
+ if err != nil {
+ return nil, pkgerrors.Errorf("Error reading from database: %s", err.Error())
+ }
+ defer cursorClose(ctx, cursor)
+
+ //Iterate over all the master tables
+ result := make(map[string][]byte)
+ for cursorNext(ctx, cursor) {
+ d := cursor.Current
+
+ //Read key of each master table
+ key, ok := d.Lookup("key").DocumentOK()
+ if !ok {
+ //Throw error if key is not found
+ pkgerrors.New("Unable to read key from mastertable")
+ }
+
+ //Get objectID of tag document
+ tid, ok := d.Lookup(tag).ObjectIDOK()
+ if !ok {
+ log.Printf("Did not find tag: %s", tag)
+ continue
+ }
+
+ //Find tag document and unmarshal it into []byte
+ tagData, err := decodeBytes(c.FindOne(ctx, bson.D{{"_id", tid}}))
+ if err != nil {
+ log.Printf("Unable to decode tag data %s", err.Error())
+ continue
+ }
+ result[key.String()] = tagData.Lookup(tag).Value
+ }
+
+ if len(result) == 0 {
+ return result, pkgerrors.Errorf("Did not find any objects with tag: %s", tag)
+ }
+
+ return result, nil
+}
diff --git a/src/orchestrator/internal/db/mongo_test.go b/src/orchestrator/internal/db/mongo_test.go
new file mode 100644
index 00000000..171c908f
--- /dev/null
+++ b/src/orchestrator/internal/db/mongo_test.go
@@ -0,0 +1,597 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package db
+
+import (
+ "bytes"
+ "context"
+ "reflect"
+ "strings"
+ "testing"
+
+ pkgerrors "github.com/pkg/errors"
+ "go.mongodb.org/mongo-driver/bson"
+ "go.mongodb.org/mongo-driver/mongo"
+ "go.mongodb.org/mongo-driver/mongo/options"
+)
+
+//Implements the functions used currently in mongo.go
+type mockCollection struct {
+ Err error
+ mCursor *mongo.Cursor
+ mCursorCount int
+}
+
+func (c *mockCollection) InsertOne(ctx context.Context, document interface{},
+ opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) {
+
+ if c.Err != nil {
+ return nil, c.Err
+ }
+
+ return &mongo.InsertOneResult{InsertedID: "_id1234"}, nil
+}
+
+func (c *mockCollection) FindOne(ctx context.Context, filter interface{},
+ opts ...*options.FindOneOptions) *mongo.SingleResult {
+
+ return &mongo.SingleResult{}
+}
+
+func (c *mockCollection) FindOneAndUpdate(ctx context.Context, filter interface{},
+ update interface{}, opts ...*options.FindOneAndUpdateOptions) *mongo.SingleResult {
+
+ return &mongo.SingleResult{}
+}
+
+func (c *mockCollection) DeleteOne(ctx context.Context, filter interface{},
+ opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) {
+
+ return nil, c.Err
+}
+
+func (c *mockCollection) Find(ctx context.Context, filter interface{},
+ opts ...*options.FindOptions) (*mongo.Cursor, error) {
+
+ return c.mCursor, c.Err
+}
+
+func TestCreate(t *testing.T) {
+ testCases := []struct {
+ label string
+ input map[string]interface{}
+ mockColl *mockCollection
+ bson bson.Raw
+ expectedError string
+ }{
+ {
+ label: "Successfull creation of entry",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": MockKey{Key: "keyvalue"},
+ "tag": "tagName",
+ "data": "Data In String Format",
+ },
+ bson: bson.Raw{'\x08', '\x00', '\x00', '\x00', '\x0A', 'x', '\x00', '\x00'},
+ mockColl: &mockCollection{},
+ },
+ {
+ label: "UnSuccessfull creation of entry",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": MockKey{Key: "keyvalue"},
+ "tag": "tagName",
+ "data": "Data In String Format",
+ },
+ mockColl: &mockCollection{
+ Err: pkgerrors.New("DB Error"),
+ },
+ expectedError: "DB Error",
+ },
+ {
+ label: "Missing input fields",
+ input: map[string]interface{}{
+ "coll": "",
+ "key": MockKey{Key: ""},
+ "tag": "",
+ "data": "",
+ },
+ expectedError: "No Data to store",
+ mockColl: &mockCollection{},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ m, _ := NewMongoStore("name", &mongo.Database{})
+ // Override the getCollection function with our mocked version
+ getCollection = func(coll string, m *MongoStore) MongoCollection {
+ return testCase.mockColl
+ }
+
+ decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) {
+ return testCase.bson, testCase.mockColl.Err
+ }
+
+ err := m.Create(testCase.input["coll"].(string), testCase.input["key"].(Key),
+ testCase.input["tag"].(string), testCase.input["data"])
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Create method returned an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Create method returned an error (%s)", err)
+ }
+ }
+ })
+ }
+}
+
+func TestUpdate(t *testing.T) {
+ testCases := []struct {
+ label string
+ input map[string]interface{}
+ mockColl *mockCollection
+ bson bson.Raw
+ expectedError string
+ }{
+ {
+ label: "Successfull update of entry",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": MockKey{Key: "keyvalue"},
+ "tag": "metadata",
+ "data": "Data In String Format",
+ },
+ // Binary form of
+ // {
+ // "_id" : ObjectId("5c115156777ff85654248ae1"),
+ // "key" : bson.D{{"name","testdef"},{"version","v1"}},
+ // "metadata" : ObjectId("5c115156c9755047e318bbfd")
+ // }
+ bson: bson.Raw{
+ '\x58', '\x00', '\x00', '\x00', '\x03', '\x6b', '\x65', '\x79',
+ '\x00', '\x27', '\x00', '\x00', '\x00', '\x02', '\x6e', '\x61',
+ '\x6d', '\x65', '\x00', '\x08', '\x00', '\x00', '\x00', '\x74',
+ '\x65', '\x73', '\x74', '\x64', '\x65', '\x66', '\x00', '\x02',
+ '\x76', '\x65', '\x72', '\x73', '\x69', '\x6f', '\x6e', '\x00',
+ '\x03', '\x00', '\x00', '\x00', '\x76', '\x31', '\x00', '\x00',
+ '\x07', '\x6d', '\x65', '\x74', '\x61', '\x64', '\x61', '\x74',
+ '\x61', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f',
+ '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x07', '\x5f',
+ '\x69', '\x64', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77',
+ '\x7f', '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x00',
+ },
+ mockColl: &mockCollection{},
+ },
+ {
+ label: "Entry does not exist",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": MockKey{Key: "keyvalue"},
+ "tag": "tagName",
+ "data": "Data In String Format",
+ },
+ mockColl: &mockCollection{
+ Err: pkgerrors.New("DB Error"),
+ },
+ expectedError: "DB Error",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ m, _ := NewMongoStore("name", &mongo.Database{})
+ // Override the getCollection function with our mocked version
+ getCollection = func(coll string, m *MongoStore) MongoCollection {
+ return testCase.mockColl
+ }
+
+ decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) {
+ return testCase.bson, testCase.mockColl.Err
+ }
+
+ err := m.Update(testCase.input["coll"].(string), testCase.input["key"].(Key),
+ testCase.input["tag"].(string), testCase.input["data"])
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Create method returned an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Create method returned an error (%s)", err)
+ }
+ }
+ })
+ }
+}
+
+func TestRead(t *testing.T) {
+ testCases := []struct {
+ label string
+ input map[string]interface{}
+ mockColl *mockCollection
+ bson bson.Raw
+ expectedError string
+ expected []byte
+ }{
+ {
+ label: "Successfull Read of entry",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": MockKey{Key: "keyvalue"},
+ "tag": "metadata",
+ },
+ // Binary form of
+ // {
+ // "_id" : ObjectId("5c115156777ff85654248ae1"),
+ // "key" : bson.D{{"name","testdef"},{"version","v1"}},
+ // "metadata" : ObjectId("5c115156c9755047e318bbfd")
+ // }
+ bson: bson.Raw{
+ '\x58', '\x00', '\x00', '\x00', '\x03', '\x6b', '\x65', '\x79',
+ '\x00', '\x27', '\x00', '\x00', '\x00', '\x02', '\x6e', '\x61',
+ '\x6d', '\x65', '\x00', '\x08', '\x00', '\x00', '\x00', '\x74',
+ '\x65', '\x73', '\x74', '\x64', '\x65', '\x66', '\x00', '\x02',
+ '\x76', '\x65', '\x72', '\x73', '\x69', '\x6f', '\x6e', '\x00',
+ '\x03', '\x00', '\x00', '\x00', '\x76', '\x31', '\x00', '\x00',
+ '\x07', '\x6d', '\x65', '\x74', '\x61', '\x64', '\x61', '\x74',
+ '\x61', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f',
+ '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x07', '\x5f',
+ '\x69', '\x64', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77',
+ '\x7f', '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x00',
+ },
+ mockColl: &mockCollection{},
+ // This is not the document because we are mocking decodeBytes
+ expected: []byte{92, 17, 81, 86, 119, 127, 248, 86, 84, 36, 138, 225},
+ },
+ {
+ label: "UnSuccessfull Read of entry: object not found",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": MockKey{Key: "keyvalue"},
+ "tag": "badtag",
+ },
+ // Binary form of
+ // {
+ // "_id" : ObjectId("5c115156777ff85654248ae1"),
+ // "key" : bson.D{{"name","testdef"},{"version","v1"}},
+ // "metadata" : ObjectId("5c115156c9755047e318bbfd")
+ // }
+ bson: bson.Raw{
+ '\x58', '\x00', '\x00', '\x00', '\x03', '\x6b', '\x65', '\x79',
+ '\x00', '\x27', '\x00', '\x00', '\x00', '\x02', '\x6e', '\x61',
+ '\x6d', '\x65', '\x00', '\x08', '\x00', '\x00', '\x00', '\x74',
+ '\x65', '\x73', '\x74', '\x64', '\x65', '\x66', '\x00', '\x02',
+ '\x76', '\x65', '\x72', '\x73', '\x69', '\x6f', '\x6e', '\x00',
+ '\x03', '\x00', '\x00', '\x00', '\x76', '\x31', '\x00', '\x00',
+ '\x07', '\x6d', '\x65', '\x74', '\x61', '\x64', '\x61', '\x74',
+ '\x61', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f',
+ '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x07', '\x5f',
+ '\x69', '\x64', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77',
+ '\x7f', '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x00',
+ },
+ mockColl: &mockCollection{},
+ expectedError: "Error finding objectID",
+ },
+ {
+ label: "UnSuccessfull Read of entry",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": MockKey{Key: "keyvalue"},
+ "tag": "tagName",
+ },
+ mockColl: &mockCollection{
+ Err: pkgerrors.New("DB Error"),
+ },
+ expectedError: "DB Error",
+ },
+ {
+ label: "Missing input fields",
+ input: map[string]interface{}{
+ "coll": "",
+ "key": MockKey{Key: ""},
+ "tag": "",
+ },
+ expectedError: "Mandatory fields are missing",
+ mockColl: &mockCollection{},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ m, _ := NewMongoStore("name", &mongo.Database{})
+ // Override the getCollection function with our mocked version
+ getCollection = func(coll string, m *MongoStore) MongoCollection {
+ return testCase.mockColl
+ }
+
+ decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) {
+ return testCase.bson, testCase.mockColl.Err
+ }
+ got, err := m.Read(testCase.input["coll"].(string), testCase.input["key"].(Key),
+ testCase.input["tag"].(string))
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Read method returned an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Read method returned an error (%s)", err)
+ }
+ } else {
+ if bytes.Compare(got, testCase.expected) != 0 {
+ t.Fatalf("Read returned unexpected data: %v, expected: %v",
+ string(got), testCase.expected)
+ }
+ }
+ })
+ }
+}
+
+func TestDelete(t *testing.T) {
+ testCases := []struct {
+ label string
+ input map[string]interface{}
+ mockColl *mockCollection
+ bson bson.Raw
+ expectedError string
+ }{
+ {
+ label: "Successfull Delete of entry",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": MockKey{Key: "keyvalue"},
+ "tag": "metadata",
+ },
+ // Binary form of
+ // {
+ // "_id" : ObjectId("5c115156777ff85654248ae1"),
+ // "key" : bson.D{{"name","testdef"},{"version","v1"}},
+ // "metadata" : ObjectId("5c115156c9755047e318bbfd")
+ // }
+ bson: bson.Raw{
+ '\x58', '\x00', '\x00', '\x00', '\x03', '\x6b', '\x65', '\x79',
+ '\x00', '\x27', '\x00', '\x00', '\x00', '\x02', '\x6e', '\x61',
+ '\x6d', '\x65', '\x00', '\x08', '\x00', '\x00', '\x00', '\x74',
+ '\x65', '\x73', '\x74', '\x64', '\x65', '\x66', '\x00', '\x02',
+ '\x76', '\x65', '\x72', '\x73', '\x69', '\x6f', '\x6e', '\x00',
+ '\x03', '\x00', '\x00', '\x00', '\x76', '\x31', '\x00', '\x00',
+ '\x07', '\x6d', '\x65', '\x74', '\x61', '\x64', '\x61', '\x74',
+ '\x61', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f',
+ '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x07', '\x5f',
+ '\x69', '\x64', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77',
+ '\x7f', '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x00',
+ },
+ mockColl: &mockCollection{},
+ },
+ {
+ label: "UnSuccessfull Delete of entry",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": MockKey{Key: "keyvalue"},
+ "tag": "tagName",
+ },
+ mockColl: &mockCollection{
+ Err: pkgerrors.New("DB Error"),
+ },
+ expectedError: "DB Error",
+ },
+ {
+ label: "UnSuccessfull Delete, key not found",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "key": MockKey{Key: "keyvalue"},
+ "tag": "tagName",
+ },
+ // Binary form of
+ // {
+ // "_id" : ObjectId("5c115156777ff85654248ae1"),
+ // "key" : bson.D{{"name","testdef"},{"version","v1"}},
+ // "metadata" : ObjectId("5c115156c9755047e318bbfd")
+ // }
+ bson: bson.Raw{
+ '\x58', '\x00', '\x00', '\x00', '\x03', '\x6b', '\x65', '\x79',
+ '\x00', '\x27', '\x00', '\x00', '\x00', '\x02', '\x6e', '\x61',
+ '\x6d', '\x65', '\x00', '\x08', '\x00', '\x00', '\x00', '\x74',
+ '\x65', '\x73', '\x74', '\x64', '\x65', '\x66', '\x00', '\x02',
+ '\x76', '\x65', '\x72', '\x73', '\x69', '\x6f', '\x6e', '\x00',
+ '\x03', '\x00', '\x00', '\x00', '\x76', '\x31', '\x00', '\x00',
+ '\x07', '\x6d', '\x65', '\x74', '\x61', '\x64', '\x61', '\x74',
+ '\x61', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f',
+ '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x07', '\x5f',
+ '\x69', '\x64', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77',
+ '\x7f', '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x00',
+ },
+ mockColl: &mockCollection{},
+ expectedError: "Error finding objectID",
+ },
+ {
+ label: "Missing input fields",
+ input: map[string]interface{}{
+ "coll": "",
+ "key": MockKey{Key: ""},
+ "tag": "",
+ },
+ expectedError: "Mandatory fields are missing",
+ mockColl: &mockCollection{},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ m, _ := NewMongoStore("name", &mongo.Database{})
+ // Override the getCollection function with our mocked version
+ getCollection = func(coll string, m *MongoStore) MongoCollection {
+ return testCase.mockColl
+ }
+
+ decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) {
+ return testCase.bson, testCase.mockColl.Err
+ }
+ err := m.Delete(testCase.input["coll"].(string), testCase.input["key"].(Key),
+ testCase.input["tag"].(string))
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Delete method returned an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Delete method returned an error (%s)", err)
+ }
+ }
+ })
+ }
+}
+
+func TestReadAll(t *testing.T) {
+ testCases := []struct {
+ label string
+ input map[string]interface{}
+ mockColl *mockCollection
+ bson bson.Raw
+ expectedError string
+ expected map[string][]byte
+ }{
+ {
+ label: "Successfully Read all entries",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "tag": "metadata",
+ },
+ mockColl: &mockCollection{
+ mCursor: &mongo.Cursor{
+ // Binary form of
+ // {
+ // "_id" : ObjectId("5c115156777ff85654248ae1"),
+ // "key" : bson.D{{"name","testdef"},{"version","v1"}},
+ // "metadata" : ObjectId("5c115156c9755047e318bbfd")
+ // }
+
+ Current: bson.Raw{
+ '\x58', '\x00', '\x00', '\x00', '\x03', '\x6b', '\x65', '\x79',
+ '\x00', '\x27', '\x00', '\x00', '\x00', '\x02', '\x6e', '\x61',
+ '\x6d', '\x65', '\x00', '\x08', '\x00', '\x00', '\x00', '\x74',
+ '\x65', '\x73', '\x74', '\x64', '\x65', '\x66', '\x00', '\x02',
+ '\x76', '\x65', '\x72', '\x73', '\x69', '\x6f', '\x6e', '\x00',
+ '\x03', '\x00', '\x00', '\x00', '\x76', '\x31', '\x00', '\x00',
+ '\x07', '\x6d', '\x65', '\x74', '\x61', '\x64', '\x61', '\x74',
+ '\x61', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f',
+ '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x07', '\x5f',
+ '\x69', '\x64', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77',
+ '\x7f', '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x00',
+ },
+ },
+ mCursorCount: 1,
+ },
+ expected: map[string][]byte{
+ `{"name": "testdef","version": "v1"}`: []byte{
+ 92, 17, 81, 86, 119, 127, 248, 86, 84, 36, 138, 225},
+ },
+ },
+ {
+ label: "UnSuccessfully Read of all entries",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "tag": "tagName",
+ },
+ mockColl: &mockCollection{
+ Err: pkgerrors.New("DB Error"),
+ },
+ expectedError: "DB Error",
+ },
+ {
+ label: "UnSuccessfull Readall, tag not found",
+ input: map[string]interface{}{
+ "coll": "collname",
+ "tag": "tagName",
+ },
+ mockColl: &mockCollection{
+ mCursor: &mongo.Cursor{
+ // Binary form of
+ // {
+ // "_id" : ObjectId("5c115156777ff85654248ae1"),
+ // "key" : bson.D{{"name","testdef"},{"version","v1"}},
+ // "metadata" : ObjectId("5c115156c9755047e318bbfd")
+ // }
+ Current: bson.Raw{
+ '\x58', '\x00', '\x00', '\x00', '\x03', '\x6b', '\x65', '\x79',
+ '\x00', '\x27', '\x00', '\x00', '\x00', '\x02', '\x6e', '\x61',
+ '\x6d', '\x65', '\x00', '\x08', '\x00', '\x00', '\x00', '\x74',
+ '\x65', '\x73', '\x74', '\x64', '\x65', '\x66', '\x00', '\x02',
+ '\x76', '\x65', '\x72', '\x73', '\x69', '\x6f', '\x6e', '\x00',
+ '\x03', '\x00', '\x00', '\x00', '\x76', '\x31', '\x00', '\x00',
+ '\x07', '\x6d', '\x65', '\x74', '\x61', '\x64', '\x61', '\x74',
+ '\x61', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77', '\x7f',
+ '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x07', '\x5f',
+ '\x69', '\x64', '\x00', '\x5c', '\x11', '\x51', '\x56', '\x77',
+ '\x7f', '\xf8', '\x56', '\x54', '\x24', '\x8a', '\xe1', '\x00',
+ },
+ },
+ mCursorCount: 1,
+ },
+ expectedError: "Did not find any objects with tag",
+ },
+ {
+ label: "Missing input fields",
+ input: map[string]interface{}{
+ "coll": "",
+ "tag": "",
+ },
+ expectedError: "Missing collection or tag name",
+ mockColl: &mockCollection{},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ m, _ := NewMongoStore("name", &mongo.Database{})
+ // Override the getCollection function with our mocked version
+ getCollection = func(coll string, m *MongoStore) MongoCollection {
+ return testCase.mockColl
+ }
+
+ decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) {
+ return testCase.mockColl.mCursor.Current, testCase.mockColl.Err
+ }
+
+ cursorNext = func(ctx context.Context, cursor *mongo.Cursor) bool {
+ if testCase.mockColl.mCursorCount > 0 {
+ testCase.mockColl.mCursorCount -= 1
+ return true
+ }
+ return false
+ }
+
+ cursorClose = func(ctx context.Context, cursor *mongo.Cursor) error {
+ return nil
+ }
+
+ got, err := m.ReadAll(testCase.input["coll"].(string), testCase.input["tag"].(string))
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Readall method returned an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.expectedError) {
+ t.Fatalf("Readall method returned an error (%s)", err)
+ }
+ } else {
+ if reflect.DeepEqual(got, testCase.expected) == false {
+ t.Fatalf("Readall returned unexpected data: %v, expected: %v",
+ got, testCase.expected)
+ }
+ }
+ })
+ }
+}
diff --git a/src/orchestrator/internal/db/store.go b/src/orchestrator/internal/db/store.go
new file mode 100644
index 00000000..ed394205
--- /dev/null
+++ b/src/orchestrator/internal/db/store.go
@@ -0,0 +1,106 @@
+/*
+Copyright 2018 Intel Corporation.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package db
+
+import (
+ "encoding/json"
+ "reflect"
+
+ "github.com/onap/multicloud-k8s/src/orchestrator/internal/config"
+
+ pkgerrors "github.com/pkg/errors"
+)
+
+// DBconn interface used to talk a concrete Database connection
+var DBconn Store
+
+// Key is an interface that will be implemented by anypackage
+// that wants to use the Store interface. This allows various
+// db backends and key types.
+type Key interface {
+ String() string
+}
+
+// Store is an interface for accessing the database
+type Store interface {
+ // Returns nil if db health is good
+ HealthCheck() error
+
+ // Unmarshal implements any unmarshaling needed for the database
+ Unmarshal(inp []byte, out interface{}) error
+
+ // Creates a new master document with key and links data with tag and
+ // creates a pointer(row) to the newly added data in the master table
+ Create(table string, key Key, tag string, data interface{}) error
+
+ // Reads data for a particular key with specific tag.
+ Read(table string, key Key, tag string) ([]byte, error)
+
+ // Update data for particular key with specific tag
+ Update(table string, key Key, tag string, data interface{}) error
+
+ // Deletes a specific tag data for key.
+ // TODO: If tag is empty, it will delete all tags under key.
+ Delete(table string, key Key, tag string) error
+
+ // Reads all master tables and data from the specified tag in table
+ ReadAll(table string, tag string) (map[string][]byte, error)
+}
+
+// CreateDBClient creates the DB client
+func createDBClient(dbType string) error {
+ var err error
+ switch dbType {
+ case "mongo":
+ // create a mongodb database with orchestrator as the name
+ DBconn, err = NewMongoStore("orchestrator", nil)
+ default:
+ return pkgerrors.New(dbType + "DB not supported")
+ }
+ return err
+}
+
+// Serialize converts given data into a JSON string
+func Serialize(v interface{}) (string, error) {
+ out, err := json.Marshal(v)
+ if err != nil {
+ return "", pkgerrors.Wrap(err, "Error serializing "+reflect.TypeOf(v).String())
+ }
+ return string(out), nil
+}
+
+// DeSerialize converts string to a json object specified by type
+func DeSerialize(str string, v interface{}) error {
+ err := json.Unmarshal([]byte(str), &v)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Error deSerializing "+str)
+ }
+ return nil
+}
+
+// InitializeDatabaseConnection sets up the connection to the
+// configured database to allow the application to talk to it.
+func InitializeDatabaseConnection() error {
+ err := createDBClient(config.GetConfiguration().DatabaseType)
+ if err != nil {
+ return pkgerrors.Cause(err)
+ }
+
+ err = DBconn.HealthCheck()
+ if err != nil {
+ return pkgerrors.Cause(err)
+ }
+
+ return nil
+}
diff --git a/src/orchestrator/internal/db/store_test.go b/src/orchestrator/internal/db/store_test.go
new file mode 100644
index 00000000..42a41787
--- /dev/null
+++ b/src/orchestrator/internal/db/store_test.go
@@ -0,0 +1,121 @@
+/*
+Copyright 2018 Intel Corporation.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package db
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestCreateDBClient(t *testing.T) {
+ t.Run("Successfully create DB client", func(t *testing.T) {
+ expected := &MongoStore{}
+
+ err := createDBClient("mongo")
+ if err != nil {
+ t.Fatalf("CreateDBClient returned an error (%s)", err)
+ }
+ if reflect.TypeOf(DBconn) != reflect.TypeOf(expected) {
+ t.Fatalf("CreateDBClient set DBconn as:\n result=%T\n expected=%T", DBconn, expected)
+ }
+ })
+ t.Run("Fail to create client for unsupported DB", func(t *testing.T) {
+ err := createDBClient("fakeDB")
+ if err == nil {
+ t.Fatal("CreateDBClient didn't return an error")
+ }
+ if !strings.Contains(string(err.Error()), "DB not supported") {
+ t.Fatalf("CreateDBClient method returned an error (%s)", err)
+ }
+ })
+}
+
+func TestSerialize(t *testing.T) {
+
+ inp := map[string]interface{}{
+ "UUID": "123e4567-e89b-12d3-a456-426655440000",
+ "Data": "sdaijsdiodalkfjsdlagf",
+ "Number": 23,
+ "Float": 34.4,
+ "Map": map[string]interface{}{
+ "m1": "m1",
+ "m2": 2,
+ "m3": 3.0,
+ },
+ }
+
+ got, err := Serialize(inp)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expected := "{\"Data\":\"sdaijsdiodalkfjsdlagf\"," +
+ "\"Float\":34.4,\"Map\":{\"m1\":\"m1\",\"m2\":2,\"m3\":3}," +
+ "\"Number\":23,\"UUID\":\"123e4567-e89b-12d3-a456-426655440000\"}"
+
+ if expected != got {
+ t.Errorf("Serialize returned unexpected string: %s;"+
+ " expected %sv", got, expected)
+ }
+}
+
+func TestDeSerialize(t *testing.T) {
+ testCases := []struct {
+ label string
+ input string
+ expected map[string]interface{}
+ errMsg string
+ }{
+ {
+ label: "Sucessful deserialize entry",
+ input: "{\"Data\":\"sdaijsdiodalkfjsdlagf\"," +
+ "\"Float\":34.4,\"Map\":{\"m1\":\"m1\",\"m3\":3}," +
+ "\"UUID\":\"123e4567-e89b-12d3-a456-426655440000\"}",
+ expected: map[string]interface{}{
+ "UUID": "123e4567-e89b-12d3-a456-426655440000",
+ "Data": "sdaijsdiodalkfjsdlagf",
+ "Float": 34.4,
+ "Map": map[string]interface{}{
+ "m1": "m1",
+ "m3": 3.0,
+ },
+ },
+ },
+ {
+ label: "Fail to deserialize invalid entry",
+ input: "{invalid}",
+ errMsg: "Error deSerializing {invalid}: invalid character 'i' looking for beginning of object key string",
+ },
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ got := make(map[string]interface{})
+ err := DeSerialize(testCase.input, &got)
+ if err != nil {
+ if testCase.errMsg == "" {
+ t.Fatalf("DeSerialize method return an un-expected (%s)", err)
+ }
+ if !strings.Contains(string(err.Error()), testCase.errMsg) {
+ t.Fatalf("DeSerialize method returned an error (%s)", err)
+ }
+ } else {
+ if !reflect.DeepEqual(testCase.expected, got) {
+ t.Errorf("Serialize returned unexpected : %v;"+
+ " expected %v", got, testCase.expected)
+ }
+ }
+ })
+ }
+}
diff --git a/src/orchestrator/internal/logutils/logger.go b/src/orchestrator/internal/logutils/logger.go
new file mode 100644
index 00000000..2e8f9969
--- /dev/null
+++ b/src/orchestrator/internal/logutils/logger.go
@@ -0,0 +1,28 @@
+package logutils
+
+import (
+ log "github.com/sirupsen/logrus"
+)
+
+//Fields is type that will be used by the calling function
+type Fields map[string]interface{}
+
+func init() {
+ // Log as JSON instead of the default ASCII formatter.
+ log.SetFormatter(&log.JSONFormatter{})
+}
+
+// Error uses the fields provided and logs
+func Error(msg string, fields Fields) {
+ log.WithFields(log.Fields(fields)).Error(msg)
+}
+
+// Warn uses the fields provided and logs
+func Warn(msg string, fields Fields) {
+ log.WithFields(log.Fields(fields)).Warn(msg)
+}
+
+// Info uses the fields provided and logs
+func Info(msg string, fields Fields) {
+ log.WithFields(log.Fields(fields)).Info(msg)
+}
diff --git a/src/orchestrator/internal/project/project.go b/src/orchestrator/internal/project/project.go
new file mode 100644
index 00000000..f0c50065
--- /dev/null
+++ b/src/orchestrator/internal/project/project.go
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2019 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 project
+
+import (
+ "encoding/json"
+
+ "github.com/onap/multicloud-k8s/src/orchestrator/internal/db"
+
+ pkgerrors "github.com/pkg/errors"
+)
+
+// Project contains the parameters needed for Projects
+// It implements the interface for managing the Projects
+type Project struct {
+ ProjectName string `json:"project-name"`
+ Description string `json:"description"`
+}
+
+// ProjectKey is the key structure that is used in the database
+type ProjectKey struct {
+ ProjectName string `json:"rb-name"`
+}
+
+// We will use json marshalling to convert to string to
+// preserve the underlying structure.
+func (pk ProjectKey) String() string {
+ out, err := json.Marshal(pk)
+ if err != nil {
+ return ""
+ }
+
+ return string(out)
+}
+
+// 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
+}
+
+// ProjectClient implements the ProjectManager
+// It will also be used to maintain some localized state
+type ProjectClient struct {
+ storeName string
+ tagMeta, tagContent string
+}
+
+// NewProjectClient returns an instance of the ProjectClient
+// which implements the ProjectManager
+func NewProjectClient() *ProjectClient {
+ return &ProjectClient{
+ tagMeta: "projectmetadata",
+ }
+}
+
+// Create a new collection based on the project
+func (v *ProjectClient) Create(p Project) (Project, error) {
+
+ //Construct the composite key to select the entry
+ key := ProjectKey{
+ ProjectName: p.ProjectName,
+ }
+
+ //Check if this Project already exists
+ _, err := v.Get(p.ProjectName)
+ if err == nil {
+ return Project{}, pkgerrors.New("Project already exists")
+ }
+
+ err = db.DBconn.Create(p.ProjectName, key, v.tagMeta, p)
+ if err != nil {
+ return Project{}, pkgerrors.Wrap(err, "Creating DB Entry")
+ }
+
+ return p, nil
+}
+
+// Get returns the Project for corresponding name
+func (v *ProjectClient) Get(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)
+ if err != nil {
+ return Project{}, pkgerrors.Wrap(err, "Get Project")
+ }
+
+ //value is a byte array
+ if value != nil {
+ proj := Project{}
+ err = db.DBconn.Unmarshal(value, &proj)
+ if err != nil {
+ return Project{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+ }
+ return proj, nil
+ }
+
+ return Project{}, pkgerrors.New("Error getting Project")
+}
+
+// Delete the Project from database
+func (v *ProjectClient) Delete(name string) error {
+
+ //Construct the composite key to select the entry
+ key := ProjectKey{
+ ProjectName: name,
+ }
+ err := db.DBconn.Delete(name, key, v.tagMeta)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Delete Project Entry;")
+ }
+
+ //TODO: Delete the collection when the project is deleted
+ return nil
+}
diff --git a/src/orchestrator/internal/project/project_test.go b/src/orchestrator/internal/project/project_test.go
new file mode 100644
index 00000000..cc691e33
--- /dev/null
+++ b/src/orchestrator/internal/project/project_test.go
@@ -0,0 +1,177 @@
+/*
+ * 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 project
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/onap/multicloud-k8s/src/orchestrator/internal/db"
+
+ pkgerrors "github.com/pkg/errors"
+)
+
+func TestCreateProject(t *testing.T) {
+ testCases := []struct {
+ label string
+ inp Project
+ expectedError string
+ mockdb *db.MockDB
+ expected Project
+ }{
+ {
+ label: "Create Project",
+ inp: Project{
+ ProjectName: "testProject",
+ Description: "A sample Project used for unit testing",
+ },
+ expected: Project{
+ ProjectName: "testProject",
+ Description: "A sample Project used for unit testing",
+ },
+ expectedError: "",
+ mockdb: &db.MockDB{},
+ },
+ {
+ label: "Failed Create Project",
+ expectedError: "Error Creating Project",
+ mockdb: &db.MockDB{
+ Err: pkgerrors.New("Error Creating Project"),
+ },
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ db.DBconn = testCase.mockdb
+ impl := NewProjectClient()
+ got, err := impl.Create(testCase.inp)
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Create returned an unexpected error %s", err)
+ }
+ if strings.Contains(err.Error(), testCase.expectedError) == false {
+ t.Fatalf("Create returned an unexpected error %s", err)
+ }
+ } else {
+ if reflect.DeepEqual(testCase.expected, got) == false {
+ t.Errorf("Create returned unexpected body: got %v;"+
+ " expected %v", got, testCase.expected)
+ }
+ }
+ })
+ }
+}
+
+func TestGetProject(t *testing.T) {
+
+ testCases := []struct {
+ label string
+ name string
+ expectedError string
+ mockdb *db.MockDB
+ inp string
+ expected Project
+ }{
+ {
+ label: "Get Project",
+ name: "testProject",
+ expected: Project{
+ ProjectName: "testProject",
+ Description: "Test project for unit testing",
+ },
+ 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\"}"),
+ },
+ },
+ },
+ },
+ {
+ label: "Get Error",
+ expectedError: "DB Error",
+ mockdb: &db.MockDB{
+ Err: pkgerrors.New("DB Error"),
+ },
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ db.DBconn = testCase.mockdb
+ impl := NewProjectClient()
+ got, err := impl.Get(testCase.name)
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Get returned an unexpected error: %s", err)
+ }
+ if strings.Contains(err.Error(), testCase.expectedError) == false {
+ t.Fatalf("Get returned an unexpected error: %s", err)
+ }
+ } else {
+ if reflect.DeepEqual(testCase.expected, got) == false {
+ t.Errorf("Get returned unexpected body: got %v;"+
+ " expected %v", got, testCase.expected)
+ }
+ }
+ })
+ }
+}
+
+func TestDeleteProject(t *testing.T) {
+
+ testCases := []struct {
+ label string
+ name string
+ expectedError string
+ mockdb *db.MockDB
+ }{
+ {
+ label: "Delete Project",
+ name: "testProject",
+ mockdb: &db.MockDB{},
+ },
+ {
+ label: "Delete Error",
+ expectedError: "DB Error",
+ mockdb: &db.MockDB{
+ Err: pkgerrors.New("DB Error"),
+ },
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.label, func(t *testing.T) {
+ db.DBconn = testCase.mockdb
+ impl := NewProjectClient()
+ err := impl.Delete(testCase.name)
+ if err != nil {
+ if testCase.expectedError == "" {
+ t.Fatalf("Delete returned an unexpected error %s", err)
+ }
+ if strings.Contains(err.Error(), testCase.expectedError) == false {
+ t.Fatalf("Delete returned an unexpected error %s", err)
+ }
+ }
+ })
+ }
+}
diff --git a/src/orchestrator/tests/certs/auth_test_certificate.pem b/src/orchestrator/tests/certs/auth_test_certificate.pem
new file mode 100644
index 00000000..42e77491
--- /dev/null
+++ b/src/orchestrator/tests/certs/auth_test_certificate.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDXTCCAkWgAwIBAgIJAKAHJi8eUs73MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTgwNTE1MDQ0MDQwWhcNMTkwNTE1MDQ0MDQwWjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA5PHDk+RRFh5o3Xe2nZuLn0Vo+5BjnHp/ul2NNYSG00Slc8F86gW4xcNA
+wm6xC8tYCSangV2lFG3E8H2L7SCEVaM5VDV2GCOpOoMihc+2Qenk/YbHwuYenwjo
+OgTK4aCItqjcAJ2DB1KC7AxARxHBDu9Kif+M/pc49so+G9ObQuS8k2vmTTaRYkMK
+ZvbTJcWsc0vbNnPhxcG5PVj4unlaREM+yQDm18/glUkkBNnYKMHABRvPnBrDktTT
+BQWsqkbQTw7ZuLOrl9rCzVTstZX9wEXarrstIdQJj3KZjbFOp2opND8bjNIjcdVt
+iRFnP1nHQYr7EgRqcx/YMJZ+gmCy3wIDAQABo1AwTjAdBgNVHQ4EFgQU9qPNwwIu
+kZh41sJqFtnMC2blSYMwHwYDVR0jBBgwFoAU9qPNwwIukZh41sJqFtnMC2blSYMw
+DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA4+daLY1wE10IMPaOKurG
+QBvfYeO/rgNXGeg0TisTIKAfx/We9Hmwo/37Bd2Nk5gxfy/DIJ4lMbrzXjgWITlm
+XOrS5QfluwvaEcREtHFtPFa3NZqn2VzKNDFTR+rJj7I5o600NKdcPrGeQ1i/vny2
+K0g68ogw2jfufcuePvZTYGst8RclomPr7ZXxI24kIjcE1MbiViy68sQueTXBEr5s
+Th6RsvPfVnLxjR/m/V6VJl31nn4T6hbmKzXCHo/X7aC3I8Isui4bQGKgfAxyBkhE
+0T7tP+GgymiEKQ6qJ/1c4HFFSuFRUQjLnK7uJu9jM/HMKoLCPayx6birHZRIMF94
+pg==
+-----END CERTIFICATE-----
diff --git a/src/orchestrator/tests/certs/auth_test_key.pem b/src/orchestrator/tests/certs/auth_test_key.pem
new file mode 100644
index 00000000..5f01f572
--- /dev/null
+++ b/src/orchestrator/tests/certs/auth_test_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDk8cOT5FEWHmjd
+d7adm4ufRWj7kGOcen+6XY01hIbTRKVzwXzqBbjFw0DCbrELy1gJJqeBXaUUbcTw
+fYvtIIRVozlUNXYYI6k6gyKFz7ZB6eT9hsfC5h6fCOg6BMrhoIi2qNwAnYMHUoLs
+DEBHEcEO70qJ/4z+lzj2yj4b05tC5LyTa+ZNNpFiQwpm9tMlxaxzS9s2c+HFwbk9
+WPi6eVpEQz7JAObXz+CVSSQE2dgowcAFG8+cGsOS1NMFBayqRtBPDtm4s6uX2sLN
+VOy1lf3ARdquuy0h1AmPcpmNsU6naik0PxuM0iNx1W2JEWc/WcdBivsSBGpzH9gw
+ln6CYLLfAgMBAAECggEAYB3H2D0QddLKf8AUoNJ+qZ1AV+zkhPtAyIMiF4fN+sBl
+HdXrlWxViGFSvM4v8h2qlhzuUfd4qLz042ox5pmyNSnTlbDkJXpDP9dyFO+BOubx
+Ribhksd9r5LTvBfq/RKikt0NkAyQx/AyGtuB2NRxUs3PY2QwU2o1dhauQIx0MH5/
+6D8PgQf6+5njKQaKa4e8Kp4kB+KjnALvt6JgYuNJUHWap+nnDbuuVy5dl1bKkAZ+
+qa7CITKWO4kE2EqaCb2asFc2w3538+w72UJZtwQCmOaxtKpRSl9fQXu54N8jIGoZ
+1FvEj5x3X6QkglE+iVJYaX3RmiJ3uzZ2LICDr89vEQKBgQD7fquIw4p1idSxz3Cm
+5o3Y5kD0CKm61ZaRJWKd+tNlSsxubmV9HROYW6vj2xEPSDvyp1na00pDXxMJQLLc
+O5Awd1SaU+d45jnoi70fsEY8X0WH1rDTYfnU+zQBmpbGqX5qTIfpy4yoADiUD1CQ
+EBdaSBWiKPx2jFSct58TwDP9YwKBgQDpC64TScZYz7uQq4gAbDso/7TjNwgt/Bw8
+JgLSdx1UdUclh81smTujsouyCFwJSvRjKik8e/Qt0f5patukFbFRINxUGUDhOKbA
+7zqeNQbeYaP7Rvw+3z01CU2BTBmB/EWa2xWDam8B9xQvjiHSOrubqkt3sIQJb045
+hzuigdV7VQKBgQD7Gnd0nyCwyMSIIMGuswYv6X4y6i9lr3qdQ4GakOTe/vbsz+cf
+K5f0CJuwbnszEgFg/zzVIx/D8rqUA3hSMlp+ObdMO7gi22Q4TsWvTRZjkxBeV7rH
+48xJneNIMqyWgIcK5YzSn3y6BTZ4hm3+2UInz09iUJ/6UZTtwNzhIIgIVwKBgQCT
+LxRHDE4gIzrT+PHRSonmr/DfnA8nc9WlS2B26lH02IkRs/5Su0iGb6p4y3zNRbCp
+vKQElki2c60ZiSqlLCosEfP1jWmDlRMEQVMlPlpTMxmtBr0jPDzc9T4lDhoCFYEk
+d3/T2vG3LQRrsHm92+hHPTuioTIS/2BJRxar4RIibQKBgQC8zoayoQ7zfEYxy3Ax
+OSao8g85hj0EAJk/VKQP2POgz6KoPay3JE9D7P7OvkebTyv/pijAuTPby4XipCNI
+K0JbFC2Kn7RW/ZV23UdnoO9crh2omOh+/52prStWXKoc+/pJe70Af+4rU7FUiI7F
+y1mIE9krIoVis6iYsyFEmkP7iw==
+-----END PRIVATE KEY-----
diff --git a/src/orchestrator/tests/configs/mock_config.json b/src/orchestrator/tests/configs/mock_config.json
new file mode 100644
index 00000000..47a6b627
--- /dev/null
+++ b/src/orchestrator/tests/configs/mock_config.json
@@ -0,0 +1,5 @@
+{
+ "database-type": "mock_db_test",
+ "database-ip": "127.0.0.1",
+ "plugin-dir": "."
+} \ No newline at end of file