diff options
-rw-r--r-- | kud/hosting_providers/vagrant/Vagrantfile | 4 | ||||
-rw-r--r-- | kud/tests/vIPSec/README.md | 36 | ||||
-rwxr-xr-x | kud/tests/vIPSec/ipsec | 163 | ||||
-rwxr-xr-x | kud/tests/vIPSec/pktgen | 77 | ||||
-rwxr-xr-x | kud/tests/vIPSec/remote_ipsec | 164 | ||||
-rwxr-xr-x | kud/tests/vIPSec/sink | 48 | ||||
-rw-r--r-- | src/k8splugin/api/brokerhandler.go | 14 | ||||
-rw-r--r-- | src/k8splugin/api/brokerhandler_test.go | 49 | ||||
-rw-r--r-- | src/k8splugin/api/profilehandler.go | 29 | ||||
-rw-r--r-- | src/k8splugin/api/profilehandler_test.go | 13 | ||||
-rw-r--r-- | src/k8splugin/internal/app/instance.go | 27 | ||||
-rw-r--r-- | src/k8splugin/internal/db/README.md | 123 | ||||
-rw-r--r-- | src/k8splugin/internal/db/testing.go | 14 | ||||
-rw-r--r-- | src/k8splugin/internal/rb/definition.go | 48 | ||||
-rw-r--r-- | src/k8splugin/internal/rb/profile.go | 28 | ||||
-rw-r--r-- | src/orchestrator/go.sum | 1 | ||||
-rw-r--r-- | src/orchestrator/internal/db/README.md | 123 | ||||
-rw-r--r-- | src/orchestrator/internal/logutils/logger.go | 28 |
18 files changed, 923 insertions, 66 deletions
diff --git a/kud/hosting_providers/vagrant/Vagrantfile b/kud/hosting_providers/vagrant/Vagrantfile index f771d549..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 => 'generic/ubuntu1810'} + :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/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/k8splugin/api/brokerhandler.go b/src/k8splugin/api/brokerhandler.go index b377baf1..7671db44 100644 --- a/src/k8splugin/api/brokerhandler.go +++ b/src/k8splugin/api/brokerhandler.go @@ -134,15 +134,13 @@ func (b brokerInstanceHandler) createHandler(w http.ResponseWriter, r *http.Requ return } - rbName := req.getAttributeValue(req.SDNCDirectives, "k8s-rb-definition-name") - if rbName == "" { - http.Error(w, "k8s-rb-definition-name is missing from sdnc-directives", http.StatusBadRequest) + if req.VFModuleModelInvariantID == "" { + http.Error(w, "vf-module-model-invariant-id is empty", http.StatusBadRequest) return } - rbVersion := req.getAttributeValue(req.SDNCDirectives, "k8s-rb-definition-version") - if rbVersion == "" { - http.Error(w, "k8s-rb-definition-version is missing from sdnc-directives", http.StatusBadRequest) + if req.VFModuleModelVersionID == "" { + http.Error(w, "vf-module-model-version-id is empty", 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 00ca3b7b..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": "k8s-rb-definition-name", - "attribute_value": "test-rbdef" + "attribute_name": "vf_module_name", + "attribute_value": "test-vf-module-name" }, { - "attribute_name": "k8s-rb-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: "Deprecated parameters passed (user_directives)", + label: "Missing parameter from sdnc_directives", input: bytes.NewBuffer([]byte(`{ - "vf-module-model-customization-id": "97sdfkio168", + "vf-module-model-customization-id": "84sdfkio938", + "vf-module-model-invariant-id": "123456qwerty", + "vf-module-model-version-id": "123qweasdzxc", "sdnc_directives": { "attributes": [ { @@ -77,22 +80,6 @@ func TestBrokerCreateHandler(t *testing.T) { "attribute_value": "test-vf-module-name" } ] - }, - "user_directives": { - "attributes": [ - { - "attribute_name": "rb-definition-name", - "attribute_value": "test-rbdef" - }, - { - "attribute_name": "rb-definition-version", - "attribute_value": "v1" - }, - { - "attribute_name": "rb-profile-name", - "attribute_value": "profile1" - } - ] } }`)), expectedCode: http.StatusBadRequest, @@ -101,6 +88,8 @@ func TestBrokerCreateHandler(t *testing.T) { 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": [ { @@ -108,14 +97,6 @@ func TestBrokerCreateHandler(t *testing.T) { "attribute_value": "test-vf-module-name" }, { - "attribute_name": "k8s-rb-definition-name", - "attribute_value": "test-rbdef" - }, - { - "attribute_name": "k8s-rb-definition-version", - "attribute_value": "v1" - }, - { "attribute_name": "k8s-rb-profile-name", "attribute_value": "profile1" } @@ -149,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/profilehandler.go b/src/k8splugin/api/profilehandler.go index 68ab77a4..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" @@ -30,12 +31,12 @@ import ( // Used to store backend implementations objects // Also simplifies mocking for unit testing purposes type rbProfileHandler struct { - // Interface that implements bundle Definition operations - // We will set this variable with a mock interface for testing + // Interface that implements bundle profile operations + // Set this variable with a mock interface for testing client rb.ProfileManager } -// createHandler handles creation of the definition entry in the database +// createHandler creates a profile entry in the database func (h rbProfileHandler) createHandler(w http.ResponseWriter, r *http.Request) { var p rb.Profile @@ -70,7 +71,7 @@ func (h rbProfileHandler) createHandler(w http.ResponseWriter, r *http.Request) } } -// uploadHandler handles upload of the bundle tar file into the database +// uploadHandler uploads the profile artifact tar file into the database func (h rbProfileHandler) uploadHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) rbName := vars["rbname"] @@ -97,8 +98,8 @@ func (h rbProfileHandler) uploadHandler(w http.ResponseWriter, r *http.Request) w.WriteHeader(http.StatusOK) } -// getHandler handles GET operations on a particular ids -// Returns a rb.Definition +// getHandler gets a Profile Key in the database +// Returns an rb.Profile func (h rbProfileHandler) getHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) rbName := vars["rbname"] @@ -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") @@ -120,8 +127,8 @@ func (h rbProfileHandler) getHandler(w http.ResponseWriter, r *http.Request) { } } -// getHandler handles GET operations on a particular ids -// Returns a rb.Definition +// getHandler gets all profiles of a Resource Bundle Key in the database +// Returns a list of rb.Profile func (h rbProfileHandler) listHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) rbName := vars["rbname"] @@ -142,7 +149,7 @@ func (h rbProfileHandler) listHandler(w http.ResponseWriter, r *http.Request) { } } -// deleteHandler handles DELETE operations on a particular bundle definition id +// deleteHandler deletes a particular Profile Key in the database func (h rbProfileHandler) deleteHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) rbName := vars["rbname"] 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/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/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/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/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/go.sum b/src/orchestrator/go.sum index dcda41d4..732bc280 100644 --- a/src/orchestrator/go.sum +++ b/src/orchestrator/go.sum @@ -201,6 +201,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR 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= 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/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) +} |