aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xkud/hosting_providers/baremetal/aio.sh6
-rw-r--r--kud/hosting_providers/vagrant/Vagrantfile2
-rwxr-xr-xkud/hosting_providers/vagrant/installer.sh43
-rwxr-xr-xkud/tests/_functions.sh23
-rw-r--r--src/k8splugin/go.sum2
-rw-r--r--src/k8splugin/internal/app/client.go181
-rw-r--r--src/k8splugin/internal/app/client_test.go2
-rw-r--r--src/k8splugin/internal/plugin/helpers.go92
-rw-r--r--src/k8splugin/mock_files/mock_plugins/mockplugin.go31
-rw-r--r--src/k8splugin/plugins/generic/plugin.go68
-rw-r--r--src/k8splugin/plugins/namespace/plugin.go60
-rw-r--r--src/k8splugin/plugins/namespace/plugin_test.go106
-rw-r--r--src/k8splugin/plugins/network/plugin.go39
-rw-r--r--src/k8splugin/plugins/network/plugin_test.go33
-rw-r--r--src/k8splugin/plugins/service/plugin.go58
-rw-r--r--src/k8splugin/plugins/service/plugin_test.go141
16 files changed, 515 insertions, 372 deletions
diff --git a/kud/hosting_providers/baremetal/aio.sh b/kud/hosting_providers/baremetal/aio.sh
index 416a1fef..c9903cd3 100755
--- a/kud/hosting_providers/baremetal/aio.sh
+++ b/kud/hosting_providers/baremetal/aio.sh
@@ -15,9 +15,13 @@ set -o pipefail
aio_dir=$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)
cd ${aio_dir}/../vagrant
+# For aio inventory by default get ovn central ip from local host default interface.
+# This variable used only in this file, but env variable defined to enable user to override it prior calling aio.sh.
+OVN_CENTRAL_IP_ADDRESS=${OVN_CENTRAL_IP_ADDRESS:-$(hostname -I | cut -d ' ' -f 1)}
+
cat <<EOL > inventory/hosts.ini
[all]
-localhost
+localhost ansible_ssh_host=${OVN_CENTRAL_IP_ADDRESS} ansible_ssh_port=22
[kube-master]
localhost
diff --git a/kud/hosting_providers/vagrant/Vagrantfile b/kud/hosting_providers/vagrant/Vagrantfile
index d068b84a..2d1b5ab4 100644
--- a/kud/hosting_providers/vagrant/Vagrantfile
+++ b/kud/hosting_providers/vagrant/Vagrantfile
@@ -120,7 +120,7 @@ Vagrant.configure("2") do |config|
installer.vm.network :private_network, :ip => "10.10.10.2", :type => :static
installer.vm.synced_folder '../../../', '/home/vagrant/multicloud-k8s/', type: sync_type
installer.vm.provision 'shell', privileged: false do |sh|
- sh.env = {'KUD_PLUGIN_ENABLED': 'false'}
+ sh.env = {'KUD_PLUGIN_ENABLED': 'false', 'OVN_CENTRAL_INTERFACE': 'eth1'}
sh.inline = <<-SHELL
cp /vagrant/insecure_keys/key.pub /home/vagrant/.ssh/id_rsa.pub
cp /vagrant/insecure_keys/key /home/vagrant/.ssh/id_rsa
diff --git a/kud/hosting_providers/vagrant/installer.sh b/kud/hosting_providers/vagrant/installer.sh
index c7715b59..ca14bad7 100755
--- a/kud/hosting_providers/vagrant/installer.sh
+++ b/kud/hosting_providers/vagrant/installer.sh
@@ -9,8 +9,13 @@
##############################################################################
set -o errexit
+set -o nounset
set -o pipefail
+INSTALLER_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)
+
+source ${INSTALLER_DIR}/../../tests/_functions.sh
+
# _install_go() - Install GoLang package
function _install_go {
version=$(grep "go_version" ${kud_playbooks}/kud-vars.yml | awk -F "'" '{print $2}')
@@ -63,15 +68,15 @@ function _install_docker {
sudo apt-get install -y docker-ce
sudo mkdir -p /etc/systemd/system/docker.service.d
- if [ $http_proxy ]; then
+ if [ ${http_proxy:-} ]; then
echo "[Service]" | sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf
echo "Environment=\"HTTP_PROXY=$http_proxy\"" | sudo tee --append /etc/systemd/system/docker.service.d/http-proxy.conf
fi
- if [ $https_proxy ]; then
+ if [ ${https_proxy:-} ]; then
echo "[Service]" | sudo tee /etc/systemd/system/docker.service.d/https-proxy.conf
echo "Environment=\"HTTPS_PROXY=$https_proxy\"" | sudo tee --append /etc/systemd/system/docker.service.d/https-proxy.conf
fi
- if [ $no_proxy ]; then
+ if [ ${no_proxy:-} ]; then
echo "[Service]" | sudo tee /etc/systemd/system/docker.service.d/no-proxy.conf
echo "Environment=\"NO_PROXY=$no_proxy\"" | sudo tee --append /etc/systemd/system/docker.service.d/no-proxy.conf
fi
@@ -86,13 +91,12 @@ function _install_docker {
}
function _set_environment_file {
- ansible_ifconfig=$(ansible ovn-central[0] -i $kud_inventory -m shell -a "ifconfig eth1 |grep \"inet addr\" |awk '{print \$2}' |awk -F: '{print \$2}'")
- if [[ $ansible_ifconfig != *CHANGED* ]]; then
- echo "Fail to get the OVN central IP address from eth1 nic"
- exit
- fi
- echo "export OVN_CENTRAL_ADDRESS=$(echo ${ansible_ifconfig#*>>} | tr '\n' ':')6641" | sudo tee --append /etc/environment
+ # By default ovn central interface is the first active network interface on localhost. If other wanted, need to export this variable in aio.sh or Vagrant file.
+ OVN_CENTRAL_INTERFACE=${OVN_CENTRAL_INTERFACE:-$(ip addr show | awk '/inet.*brd/{print $NF; exit}')}
+ echo "export OVN_CENTRAL_INTERFACE=${OVN_CENTRAL_INTERFACE}" | sudo tee --append /etc/environment
+ echo "export OVN_CENTRAL_ADDRESS=$(get_ovn_central_address)" | sudo tee --append /etc/environment
echo "export KUBE_CONFIG_DIR=/opt/kubeconfig" | sudo tee --append /etc/environment
+ echo "export CSAR_DIR=/opt/csar" | sudo tee --append /etc/environment
}
# install_k8s() - Install Kubernetes using kubespray tool
@@ -102,7 +106,7 @@ function install_k8s {
version=$(grep "kubespray_version" ${kud_playbooks}/kud-vars.yml | awk -F ': ' '{print $2}')
local_release_dir=$(grep "local_release_dir" $kud_inventory_folder/group_vars/k8s-cluster.yml | awk -F "\"" '{print $2}')
local tarball=v$version.tar.gz
- sudo apt-get install -y sshpass
+ sudo apt-get install -y sshpass make unzip # install make to run mitogen target and unzip is mitogen playbook dependency
_install_docker
_install_ansible
wget https://github.com/kubernetes-incubator/kubespray/archive/$tarball
@@ -112,18 +116,21 @@ function install_k8s {
sudo mkdir -p ${local_release_dir}/containers
rm $tarball
- sudo -E pip install -r $dest_folder/kubespray-$version/requirements.txt
+ pushd $dest_folder/kubespray-$version/
+ sudo -E pip install -r ./requirements.txt
+ make mitogen
+ popd
rm -f $kud_inventory_folder/group_vars/all.yml 2> /dev/null
- if [[ -n "${verbose}" ]]; then
+ if [[ -n "${verbose:-}" ]]; then
echo "kube_log_level: 5" | tee $kud_inventory_folder/group_vars/all.yml
else
echo "kube_log_level: 2" | tee $kud_inventory_folder/group_vars/all.yml
fi
echo "kubeadm_enabled: true" | tee --append $kud_inventory_folder/group_vars/all.yml
- if [[ -n "${http_proxy}" ]]; then
+ if [[ -n "${http_proxy:-}" ]]; then
echo "http_proxy: \"$http_proxy\"" | tee --append $kud_inventory_folder/group_vars/all.yml
fi
- if [[ -n "${https_proxy}" ]]; then
+ if [[ -n "${https_proxy:-}" ]]; then
echo "https_proxy: \"$https_proxy\"" | tee --append $kud_inventory_folder/group_vars/all.yml
fi
ansible-playbook $verbose -i $kud_inventory $dest_folder/kubespray-$version/cluster.yml --become --become-user=root | sudo tee $log_folder/setup-kubernetes.log
@@ -162,7 +169,6 @@ function install_plugin {
sudo mkdir -p /opt/{kubeconfig,consul/config}
sudo cp $HOME/.kube/config /opt/kubeconfig/kud
- _set_environment_file
source /etc/environment
pushd $kud_folder/../../../deployments
@@ -207,14 +213,15 @@ if ! sudo -n "true"; then
exit 1
fi
-if [[ -n "${KUD_DEBUG}" ]]; then
+verbose=""
+if [[ -n "${KUD_DEBUG:-}" ]]; then
set -o xtrace
verbose="-vvv"
fi
# Configuration values
log_folder=/var/log/kud
-kud_folder=$(pwd)
+kud_folder=${INSTALLER_DIR}
kud_infra_folder=$kud_folder/../../deployment_infra
export kud_inventory_folder=$kud_folder/inventory
kud_inventory=$kud_inventory_folder/hosts.ini
@@ -226,7 +233,6 @@ testing_enabled=${KUD_ENABLE_TESTS:-false}
sudo mkdir -p $log_folder
sudo mkdir -p /opt/csar
sudo chown -R $USER /opt/csar
-echo "export CSAR_DIR=/opt/csar" | sudo tee --append /etc/environment
# Install dependencies
# Setup proxy variables
@@ -237,6 +243,7 @@ fi
sudo apt-get update
install_k8s
install_addons
+_set_environment_file
if ${KUD_PLUGIN_ENABLED:-false}; then
install_plugin
fi
diff --git a/kud/tests/_functions.sh b/kud/tests/_functions.sh
index 542443d6..5e6314ce 100755
--- a/kud/tests/_functions.sh
+++ b/kud/tests/_functions.sh
@@ -12,6 +12,8 @@ set -o errexit
set -o nounset
set -o pipefail
+FUNCTIONS_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)
+
source /etc/environment
function print_msg {
@@ -22,10 +24,10 @@ function print_msg {
echo -e "${RED} $msg ---------------------------------------${NC}"
}
-function _get_ovn_central_address {
- ansible_ifconfig=$(ansible ovn-central[0] -i $test_folder/../hosting_providers/vagrant/inventory/hosts.ini -m shell -a "ifconfig eth1 |grep \"inet addr\" |awk '{print \$2}' |awk -F: '{print \$2}'")
+function get_ovn_central_address {
+ ansible_ifconfig=$(ansible ovn-central[0] -i ${FUNCTIONS_DIR}/../hosting_providers/vagrant/inventory/hosts.ini -m shell -a "ifconfig ${OVN_CENTRAL_INTERFACE} |grep \"inet addr\" |awk '{print \$2}' |awk -F: '{print \$2}'")
if [[ $ansible_ifconfig != *CHANGED* ]]; then
- echo "Fail to get the OVN central IP address from eth1 nic"
+ echo "Fail to get the OVN central IP address from ${OVN_CENTRAL_INTERFACE} nic"
exit
fi
echo "$(echo ${ansible_ifconfig#*>>} | tr '\n' ':')6641"
@@ -39,7 +41,7 @@ function init_network {
name=$(cat $fname | yq '.spec.name' | xargs)
subnet=$(cat $fname | yq '.spec.subnet' | xargs)
gateway=$(cat $fname | yq '.spec.gateway' | xargs)
- ovn_central_address=$(_get_ovn_central_address)
+ ovn_central_address=$(get_ovn_central_address)
router_mac=$(printf '00:00:00:%02X:%02X:%02X' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)))
ovn-nbctl --may-exist --db tcp:$ovn_central_address ls-add $name -- set logical_switch $name other-config:subnet=$subnet external-ids:gateway_ip=$gateway
@@ -52,7 +54,7 @@ function cleanup_network {
local fname=$1
name=$(cat $fname | yq '.spec.name' | xargs)
- ovn_central_address=$(_get_ovn_central_address)
+ ovn_central_address=$(get_ovn_central_address)
for cmd in "ls-del $name" "lrp-del rtos-$name" "lsp-del stor-$name"; do
ovn-nbctl --if-exist --db tcp:$ovn_central_address $cmd
@@ -111,6 +113,10 @@ function wait_deployment {
# setup() - Base testing setup shared among functional tests
function setup {
+ if ! $(kubectl version &>/dev/null); then
+ echo "This funtional test requires kubectl client"
+ exit 1
+ fi
for deployment_name in $@; do
recreate_deployment $deployment_name
done
@@ -126,9 +132,4 @@ function teardown {
destroy_deployment $deployment_name
done
}
-
-if ! $(kubectl version &>/dev/null); then
- echo "This funtional test requires kubectl client"
- exit 1
-fi
-test_folder=$(pwd)
+test_folder=${FUNCTIONS_DIR}
diff --git a/src/k8splugin/go.sum b/src/k8splugin/go.sum
index cf605d2f..0047e332 100644
--- a/src/k8splugin/go.sum
+++ b/src/k8splugin/go.sum
@@ -129,6 +129,7 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
@@ -147,6 +148,7 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/technosophos/moniker v0.0.0-20180509230615-a5dbd03a2245 h1:DNVk+NIkGS0RbLkjQOLCJb/759yfCysThkMbl7EXxyY=
github.com/technosophos/moniker v0.0.0-20180509230615-a5dbd03a2245/go.mod h1:O1c8HleITsZqzNZDjSNzirUGsMT0oGu9LhHKoJrqO+A=
diff --git a/src/k8splugin/internal/app/client.go b/src/k8splugin/internal/app/client.go
index 158d21de..9a5aa9e9 100644
--- a/src/k8splugin/internal/app/client.go
+++ b/src/k8splugin/internal/app/client.go
@@ -16,15 +16,16 @@ package app
import (
"log"
"os"
- "strings"
+ "time"
- utils "k8splugin/internal"
"k8splugin/internal/config"
"k8splugin/internal/connection"
"k8splugin/internal/helm"
+ "k8splugin/internal/plugin"
pkgerrors "github.com/pkg/errors"
"k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
@@ -32,16 +33,12 @@ import (
"k8s.io/client-go/tools/clientcmd"
)
-// PluginReference is the interface that is implemented
-type PluginReference interface {
- Create(yamlFilePath string, namespace string, client *KubernetesClient) (string, error)
- Delete(resource helm.KubernetesResource, namespace string, client *KubernetesClient) error
-}
-
+// KubernetesClient encapsulates the different clients' interfaces
+// we need when interacting with a Kubernetes cluster
type KubernetesClient struct {
- clientSet *kubernetes.Clientset
+ clientSet kubernetes.Interface
dynamicClient dynamic.Interface
- discoverClient *discovery.DiscoveryClient
+ discoverClient discovery.CachedDiscoveryInterface
restMapper meta.RESTMapper
}
@@ -86,40 +83,35 @@ func (k *KubernetesClient) init(cloudregion string) error {
return pkgerrors.Wrap(err, "Creating dynamic client")
}
- k.discoverClient, err = discovery.NewDiscoveryClientForConfig(config)
+ k.discoverClient, err = discovery.NewCachedDiscoveryClientForConfig(config, os.TempDir(), "", 10*time.Minute)
if err != nil {
return pkgerrors.Wrap(err, "Creating discovery client")
}
+ k.restMapper = restmapper.NewDeferredDiscoveryRESTMapper(k.discoverClient)
return nil
}
func (k *KubernetesClient) ensureNamespace(namespace string) error {
- namespacePlugin, ok := utils.LoadedPlugins["namespace"]
- if !ok {
- return pkgerrors.New("No plugin for namespace resource found")
- }
- symGetNamespaceFunc, err := namespacePlugin.Lookup("Get")
+ pluginImpl, err := plugin.GetPluginByKind("Namespace")
if err != nil {
- return pkgerrors.Wrap(err, "Error fetching get namespace function")
+ return pkgerrors.Wrap(err, "Loading Namespace Plugin")
}
- ns, _ := symGetNamespaceFunc.(func(string, string, kubernetes.Interface) (string, error))(
- namespace, namespace, k.clientSet)
+ ns, err := pluginImpl.Get(helm.KubernetesResource{
+ Name: namespace,
+ GVK: schema.GroupVersionKind{
+ Group: "",
+ Version: "v1",
+ Kind: "Namespace",
+ },
+ }, namespace, k)
if ns == "" {
log.Println("Creating " + namespace + " namespace")
- symGetNamespaceFunc, err := namespacePlugin.Lookup("Create")
- if err != nil {
- return pkgerrors.Wrap(err, "Error fetching create namespace plugin")
- }
- namespaceResource := &utils.ResourceData{
- Namespace: namespace,
- }
- _, err = symGetNamespaceFunc.(func(*utils.ResourceData, kubernetes.Interface) (string, error))(
- namespaceResource, k.clientSet)
+ _, err = pluginImpl.Create("", namespace, k)
if err != nil {
return pkgerrors.Wrap(err, "Error creating "+namespace+" namespace")
}
@@ -127,50 +119,6 @@ func (k *KubernetesClient) ensureNamespace(namespace string) error {
return nil
}
-func (k *KubernetesClient) createGeneric(resTempl helm.KubernetesResourceTemplate,
- namespace string) (helm.KubernetesResource, error) {
-
- log.Println("Processing Kind: " + resTempl.GVK.Kind)
-
- //Check if have the mapper before loading the plugin
- err := k.updateMapper()
- if err != nil {
- return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Unable to create RESTMapper")
- }
-
- pluginObject, ok := utils.LoadedPlugins["generic"]
- if !ok {
- return helm.KubernetesResource{}, pkgerrors.New("No generic plugin found")
- }
-
- symbol, err := pluginObject.Lookup("ExportedVariable")
- if err != nil {
- return helm.KubernetesResource{}, pkgerrors.Wrap(err, "No ExportedVariable symbol found")
- }
-
- //Assert if it implements the PluginReference interface
- genericPlugin, ok := symbol.(PluginReference)
- if !ok {
- return helm.KubernetesResource{}, pkgerrors.New("ExportedVariable is not PluginReference type")
- }
-
- if _, err := os.Stat(resTempl.FilePath); os.IsNotExist(err) {
- return helm.KubernetesResource{}, pkgerrors.New("File " + resTempl.FilePath + "does not exists")
- }
-
- log.Println("Processing file: " + resTempl.FilePath)
-
- name, err := genericPlugin.Create(resTempl.FilePath, namespace, k)
- if err != nil {
- return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error in generic plugin")
- }
-
- return helm.KubernetesResource{
- GVK: resTempl.GVK,
- Name: name,
- }, nil
-}
-
func (k *KubernetesClient) createKind(resTempl helm.KubernetesResourceTemplate,
namespace string) (helm.KubernetesResource, error) {
@@ -182,28 +130,16 @@ func (k *KubernetesClient) createKind(resTempl helm.KubernetesResourceTemplate,
log.Println("Processing file: " + resTempl.FilePath)
- //Populate the namespace from profile instead of instance body
- genericKubeData := &utils.ResourceData{
- YamlFilePath: resTempl.FilePath,
- Namespace: namespace,
- }
-
- typePlugin, ok := utils.LoadedPlugins[strings.ToLower(resTempl.GVK.Kind)]
- if !ok {
- log.Println("No plugin for kind " + resTempl.GVK.Kind + " found. Using generic Plugin")
- return k.createGeneric(resTempl, namespace)
- }
-
- symCreateResourceFunc, err := typePlugin.Lookup("Create")
+ pluginImpl, err := plugin.GetPluginByKind(resTempl.GVK.Kind)
if err != nil {
- return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error fetching "+resTempl.GVK.Kind+" plugin")
+ return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error loading plugin")
}
- createdResourceName, err := symCreateResourceFunc.(func(*utils.ResourceData, kubernetes.Interface) (string, error))(
- genericKubeData, k.clientSet)
+ createdResourceName, err := pluginImpl.Create(resTempl.FilePath, namespace, k)
if err != nil {
return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error in plugin "+resTempl.GVK.Kind+" plugin")
}
+
log.Print(createdResourceName + " created")
return helm.KubernetesResource{
GVK: resTempl.GVK,
@@ -231,58 +167,18 @@ func (k *KubernetesClient) createResources(sortedTemplates []helm.KubernetesReso
return createdResources, nil
}
-func (k *KubernetesClient) deleteGeneric(resource helm.KubernetesResource, namespace string) error {
- log.Println("Deleting Kind: " + resource.GVK.Kind)
-
- pluginObject, ok := utils.LoadedPlugins["generic"]
- if !ok {
- return pkgerrors.New("No generic plugin found")
- }
-
- //Check if have the mapper before loading the plugin
- err := k.updateMapper()
- if err != nil {
- return pkgerrors.Wrap(err, "Unable to create RESTMapper")
- }
-
- symbol, err := pluginObject.Lookup("ExportedVariable")
- if err != nil {
- return pkgerrors.Wrap(err, "No ExportedVariable symbol found")
- }
-
- //Assert that it implements the PluginReference interface
- genericPlugin, ok := symbol.(PluginReference)
- if !ok {
- return pkgerrors.New("ExportedVariable is not PluginReference type")
- }
-
- err = genericPlugin.Delete(resource, namespace, k)
- if err != nil {
- return pkgerrors.Wrap(err, "Error in generic plugin")
- }
-
- return nil
-}
-
func (k *KubernetesClient) deleteKind(resource helm.KubernetesResource, namespace string) error {
log.Println("Deleting Kind: " + resource.GVK.Kind)
- typePlugin, ok := utils.LoadedPlugins[strings.ToLower(resource.GVK.Kind)]
- if !ok {
- log.Println("No plugin for kind " + resource.GVK.Kind + " found. Using generic Plugin")
- return k.deleteGeneric(resource, namespace)
- }
-
- symDeleteResourceFunc, err := typePlugin.Lookup("Delete")
+ pluginImpl, err := plugin.GetPluginByKind(resource.GVK.Kind)
if err != nil {
- return pkgerrors.Wrap(err, "Error finding Delete symbol in plugin")
+ return pkgerrors.Wrap(err, "Error loading plugin")
}
log.Println("Deleting resource: " + resource.Name)
- err = symDeleteResourceFunc.(func(string, string, kubernetes.Interface) error)(
- resource.Name, namespace, k.clientSet)
+ err = pluginImpl.Delete(resource, namespace, k)
if err != nil {
- return pkgerrors.Wrap(err, "Error destroying "+resource.Name)
+ return pkgerrors.Wrap(err, "Error deleting "+resource.Name)
}
return nil
@@ -300,21 +196,6 @@ func (k *KubernetesClient) deleteResources(resources []helm.KubernetesResource,
return nil
}
-func (k *KubernetesClient) updateMapper() error {
- //Create restMapper if not already done
- if k.restMapper != nil {
- return nil
- }
-
- groupResources, err := restmapper.GetAPIGroupResources(k.discoverClient)
- if err != nil {
- return pkgerrors.Wrap(err, "Get GroupResources")
- }
-
- k.restMapper = restmapper.NewDiscoveryRESTMapper(groupResources)
- return nil
-}
-
//GetMapper returns the RESTMapper that was created for this client
func (k *KubernetesClient) GetMapper() meta.RESTMapper {
return k.restMapper
@@ -325,3 +206,9 @@ func (k *KubernetesClient) GetMapper() meta.RESTMapper {
func (k *KubernetesClient) GetDynamicClient() dynamic.Interface {
return k.dynamicClient
}
+
+// GetStandardClient returns the standard client that can be used to handle
+// standard kubernetes kinds
+func (k *KubernetesClient) GetStandardClient() kubernetes.Interface {
+ return k.clientSet
+}
diff --git a/src/k8splugin/internal/app/client_test.go b/src/k8splugin/internal/app/client_test.go
index 4bfbcb18..e52aa7f8 100644
--- a/src/k8splugin/internal/app/client_test.go
+++ b/src/k8splugin/internal/app/client_test.go
@@ -42,7 +42,7 @@ func LoadMockPlugins(krdLoadedPlugins map[string]*plugin.Plugin) error {
}
krdLoadedPlugins["namespace"] = mockPlugin
- krdLoadedPlugins["deployment"] = mockPlugin
+ krdLoadedPlugins["generic"] = mockPlugin
krdLoadedPlugins["service"] = mockPlugin
return nil
diff --git a/src/k8splugin/internal/plugin/helpers.go b/src/k8splugin/internal/plugin/helpers.go
new file mode 100644
index 00000000..efcd18c1
--- /dev/null
+++ b/src/k8splugin/internal/plugin/helpers.go
@@ -0,0 +1,92 @@
+/*
+ * 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 plugin
+
+import (
+ "log"
+ "strings"
+
+ utils "k8splugin/internal"
+ "k8splugin/internal/helm"
+
+ pkgerrors "github.com/pkg/errors"
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/client-go/dynamic"
+ "k8s.io/client-go/kubernetes"
+)
+
+// KubernetesConnector is an interface that is expected to be implemented
+// by any code that calls the plugin framework functions.
+// It implements methods that are needed by the plugins to get Kubernetes
+// clients and other information needed to interface with Kubernetes
+type KubernetesConnector interface {
+ //GetMapper returns the RESTMapper that was created for this client
+ GetMapper() meta.RESTMapper
+
+ //GetDynamicClient returns the dynamic client that is needed for
+ //unstructured REST calls to the apiserver
+ GetDynamicClient() dynamic.Interface
+
+ // GetStandardClient returns the standard client that can be used to handle
+ // standard kubernetes kinds
+ GetStandardClient() kubernetes.Interface
+}
+
+// Reference is the interface that is implemented
+type Reference interface {
+ //Create a kubernetes resource described by the yaml in yamlFilePath
+ Create(yamlFilePath string, namespace string, client KubernetesConnector) (string, error)
+
+ //Get a kubernetes resource based on the groupVersionKind and resourceName provided in resource
+ Get(resource helm.KubernetesResource, namespace string, client KubernetesConnector) (string, error)
+
+ //List all resources of the specified GroupVersionKind in the given namespace
+ //If gvk is empty, the plugin will return all supported objects in the namespace
+ List(gvk schema.GroupVersionKind, namespace string, client KubernetesConnector) ([]helm.KubernetesResource, error)
+
+ //Delete a kubernetes resource described in the provided namespace
+ Delete(resource helm.KubernetesResource, namespace string, client KubernetesConnector) error
+}
+
+// GetPluginByKind returns a plugin by the kind name
+// If plugin does not exist, it will return the generic plugin
+// TODO: Change this once we have a plugin registration mechanism
+func GetPluginByKind(kind string) (Reference, error) {
+
+ typePlugin, ok := utils.LoadedPlugins[strings.ToLower(kind)]
+ if !ok {
+ log.Println("No plugin for kind " + kind + " found. Using generic Plugin")
+ typePlugin, ok = utils.LoadedPlugins["generic"]
+ if !ok {
+ return nil, pkgerrors.New("No generic plugin found")
+ }
+ }
+
+ symbol, err := typePlugin.Lookup("ExportedVariable")
+ if err != nil {
+ return nil, pkgerrors.Wrap(err, "No ExportedVariable symbol found")
+ }
+
+ //Assert if it implements the PluginReference interface
+ pluginImpl, ok := symbol.(Reference)
+ if !ok {
+ return nil, pkgerrors.New("ExportedVariable does not implement plugins.Reference interface type")
+ }
+
+ return pluginImpl, nil
+}
diff --git a/src/k8splugin/mock_files/mock_plugins/mockplugin.go b/src/k8splugin/mock_files/mock_plugins/mockplugin.go
index bdc2130c..0c3d246d 100644
--- a/src/k8splugin/mock_files/mock_plugins/mockplugin.go
+++ b/src/k8splugin/mock_files/mock_plugins/mockplugin.go
@@ -14,30 +14,43 @@ limitations under the License.
package main
import (
- "k8s.io/client-go/kubernetes"
+ "k8splugin/internal/helm"
+ "k8splugin/internal/plugin"
- utils "k8splugin/internal"
+ "k8s.io/apimachinery/pkg/runtime/schema"
)
-func main() {}
+// ExportedVariable is what we will look for when calling the plugin
+var ExportedVariable mockPlugin
+
+type mockPlugin struct {
+}
// Create object in a specific Kubernetes resource
-func Create(data *utils.ResourceData, client kubernetes.Interface) (string, error) {
+func (p mockPlugin) Create(yamlFilePath string, namespace string, client plugin.KubernetesConnector) (string, error) {
return "resource-name", nil
}
// List of existing resources
-func List(namespace string, client kubernetes.Interface) ([]string, error) {
- returnVal := []string{"resource-name-1", "resource-name-2"}
+func (p mockPlugin) List(gvk schema.GroupVersionKind, namespace string,
+ client plugin.KubernetesConnector) ([]helm.KubernetesResource, error) {
+ returnVal := []helm.KubernetesResource{
+ {
+ Name: "resource-name-1",
+ },
+ {
+ Name: "resource-name-2",
+ },
+ }
return returnVal, nil
}
// Delete existing resources
-func Delete(name string, namespace string, client kubernetes.Interface) error {
+func (p mockPlugin) Delete(resource helm.KubernetesResource, namespace string, client plugin.KubernetesConnector) error {
return nil
}
// Get existing resource host
-func Get(name string, namespace string, client kubernetes.Interface) (string, error) {
- return name, nil
+func (p mockPlugin) Get(resource helm.KubernetesResource, namespace string, client plugin.KubernetesConnector) (string, error) {
+ return resource.Name, nil
}
diff --git a/src/k8splugin/plugins/generic/plugin.go b/src/k8splugin/plugins/generic/plugin.go
index 9ecaf68c..31c65d05 100644
--- a/src/k8splugin/plugins/generic/plugin.go
+++ b/src/k8splugin/plugins/generic/plugin.go
@@ -23,15 +23,18 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
utils "k8splugin/internal"
- "k8splugin/internal/app"
+ "k8splugin/internal/plugin"
"k8splugin/internal/helm"
)
+// ExportedVariable is what we will look for when calling the generic plugin
+var ExportedVariable genericPlugin
+
type genericPlugin struct {
}
// Create deployment object in a specific Kubernetes cluster
-func (g genericPlugin) Create(yamlFilePath string, namespace string, client *app.KubernetesClient) (string, error) {
+func (g genericPlugin) Create(yamlFilePath string, namespace string, client plugin.KubernetesConnector) (string, error) {
if namespace == "" {
namespace = "default"
}
@@ -72,15 +75,58 @@ func (g genericPlugin) Create(yamlFilePath string, namespace string, client *app
return createdObj.GetName(), nil
}
-// Delete an existing deployment hosted in a specific Kubernetes cluster
-func (g genericPlugin) Delete(resource helm.KubernetesResource, namespace string, client *app.KubernetesClient) error {
+// Get an existing resource hosted in a specific Kubernetes cluster
+func (g genericPlugin) Get(resource helm.KubernetesResource,
+ namespace string, client plugin.KubernetesConnector) (string, error) {
if namespace == "" {
namespace = "default"
}
- deletePolicy := metav1.DeletePropagationForeground
- opts := &metav1.DeleteOptions{
- PropagationPolicy: &deletePolicy,
+ dynClient := client.GetDynamicClient()
+ mapper := client.GetMapper()
+
+ mapping, err := mapper.RESTMapping(schema.GroupKind{
+ Group: resource.GVK.Group,
+ Kind: resource.GVK.Kind,
+ }, resource.GVK.Version)
+ if err != nil {
+ return "", pkgerrors.Wrap(err, "Mapping kind to resource error")
+ }
+
+ gvr := mapping.Resource
+ log.Printf("Using gvr: %s, %s, %s", gvr.Group, gvr.Version, gvr.Resource)
+
+ opts := metav1.GetOptions{}
+ var unstruct *unstructured.Unstructured
+ switch mapping.Scope.Name() {
+ case meta.RESTScopeNameNamespace:
+ unstruct, err = dynClient.Resource(gvr).Namespace(namespace).Get(resource.Name, opts)
+ case meta.RESTScopeNameRoot:
+ unstruct, err = dynClient.Resource(gvr).Get(resource.Name, opts)
+ default:
+ return "", pkgerrors.New("Got an unknown RESTSCopeName for mapping: " + resource.GVK.String())
+ }
+
+ if err != nil {
+ return "", pkgerrors.Wrap(err, "Delete object error")
+ }
+
+ return unstruct.GetName(), nil
+}
+
+// List all existing resources of the GroupVersionKind
+// TODO: Implement in seperate patch
+func (g genericPlugin) List(gvk schema.GroupVersionKind, namespace string,
+ client plugin.KubernetesConnector) ([]helm.KubernetesResource, error) {
+
+ var returnData []helm.KubernetesResource
+ return returnData, nil
+}
+
+// Delete an existing resource hosted in a specific Kubernetes cluster
+func (g genericPlugin) Delete(resource helm.KubernetesResource, namespace string, client plugin.KubernetesConnector) error {
+ if namespace == "" {
+ namespace = "default"
}
dynClient := client.GetDynamicClient()
@@ -97,6 +143,11 @@ func (g genericPlugin) Delete(resource helm.KubernetesResource, namespace string
gvr := mapping.Resource
log.Printf("Using gvr: %s, %s, %s", gvr.Group, gvr.Version, gvr.Resource)
+ deletePolicy := metav1.DeletePropagationForeground
+ opts := &metav1.DeleteOptions{
+ PropagationPolicy: &deletePolicy,
+ }
+
switch mapping.Scope.Name() {
case meta.RESTScopeNameNamespace:
err = dynClient.Resource(gvr).Namespace(namespace).Delete(resource.Name, opts)
@@ -111,6 +162,3 @@ func (g genericPlugin) Delete(resource helm.KubernetesResource, namespace string
}
return nil
}
-
-// ExportedVariable is what we will look for when calling the generic plugin
-var ExportedVariable genericPlugin
diff --git a/src/k8splugin/plugins/namespace/plugin.go b/src/k8splugin/plugins/namespace/plugin.go
index 6f823918..2d5d2ab8 100644
--- a/src/k8splugin/plugins/namespace/plugin.go
+++ b/src/k8splugin/plugins/namespace/plugin.go
@@ -16,39 +16,42 @@ package main
import (
"log"
- "k8s.io/client-go/kubernetes"
-
pkgerrors "github.com/pkg/errors"
-
coreV1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime/schema"
utils "k8splugin/internal"
+ "k8splugin/internal/helm"
+ "k8splugin/internal/plugin"
)
+// ExportedVariable is what we will look for when calling the plugin
+var ExportedVariable namespacePlugin
+
+type namespacePlugin struct {
+}
+
// Create a namespace object in a specific Kubernetes cluster
-func Create(data *utils.ResourceData, client kubernetes.Interface) (string, error) {
- namespace := &coreV1.Namespace{
+func (p namespacePlugin) Create(yamlFilePath string, namespace string, client plugin.KubernetesConnector) (string, error) {
+ namespaceObj := &coreV1.Namespace{
ObjectMeta: metaV1.ObjectMeta{
- Name: data.Namespace,
+ Name: namespace,
},
}
- _, err := client.CoreV1().Namespaces().Create(namespace)
+ _, err := client.GetStandardClient().CoreV1().Namespaces().Create(namespaceObj)
if err != nil {
return "", pkgerrors.Wrap(err, "Create Namespace error")
}
- log.Printf("Namespace (%s) created", data.Namespace)
+ log.Printf("Namespace (%s) created", namespace)
- return data.Namespace, nil
+ return namespace, nil
}
// Get an existing namespace hosted in a specific Kubernetes cluster
-func Get(name string, namespace string, client kubernetes.Interface) (string, error) {
+func (p namespacePlugin) Get(resource helm.KubernetesResource, namespace string, client plugin.KubernetesConnector) (string, error) {
opts := metaV1.GetOptions{}
- opts.APIVersion = "apps/v1"
- opts.Kind = "Deployment"
-
- ns, err := client.CoreV1().Namespaces().Get(name, opts)
+ ns, err := client.GetStandardClient().CoreV1().Namespaces().Get(resource.Name, opts)
if err != nil {
return "", pkgerrors.Wrap(err, "Get Namespace error")
}
@@ -57,14 +60,14 @@ func Get(name string, namespace string, client kubernetes.Interface) (string, er
}
// Delete an existing namespace hosted in a specific Kubernetes cluster
-func Delete(name string, namespace string, client kubernetes.Interface) error {
+func (p namespacePlugin) Delete(resource helm.KubernetesResource, namespace string, client plugin.KubernetesConnector) error {
deletePolicy := metaV1.DeletePropagationForeground
opts := &metaV1.DeleteOptions{
PropagationPolicy: &deletePolicy,
}
- log.Println("Deleting namespace: " + name)
- if err := client.CoreV1().Namespaces().Delete(name, opts); err != nil {
+ log.Println("Deleting namespace: " + resource.Name)
+ if err := client.GetStandardClient().CoreV1().Namespaces().Delete(resource.Name, opts); err != nil {
return pkgerrors.Wrap(err, "Delete namespace error")
}
@@ -72,23 +75,30 @@ func Delete(name string, namespace string, client kubernetes.Interface) error {
}
// List of existing namespaces hosted in a specific Kubernetes cluster
-func List(namespace string, client kubernetes.Interface) ([]string, error) {
+// This plugin ignores both gvk and namespace arguments
+func (p namespacePlugin) List(gvk schema.GroupVersionKind, namespace string, client plugin.KubernetesConnector) ([]helm.KubernetesResource, error) {
opts := metaV1.ListOptions{
Limit: utils.ResourcesListLimit,
}
- opts.APIVersion = "apps/v1"
- opts.Kind = "Namespace"
- list, err := client.CoreV1().Namespaces().List(opts)
+ list, err := client.GetStandardClient().CoreV1().Namespaces().List(opts)
if err != nil {
return nil, pkgerrors.Wrap(err, "Get Namespace list error")
}
- result := make([]string, 0, utils.ResourcesListLimit)
+ result := make([]helm.KubernetesResource, 0, utils.ResourcesListLimit)
if list != nil {
- for _, deployment := range list.Items {
- log.Printf("%v", deployment.Name)
- result = append(result, deployment.Name)
+ for _, ns := range list.Items {
+ log.Printf("%v", ns.Name)
+ result = append(result,
+ helm.KubernetesResource{
+ GVK: schema.GroupVersionKind{
+ Group: "",
+ Version: "v1",
+ Kind: "Namespace",
+ },
+ Name: ns.Name,
+ })
}
}
diff --git a/src/k8splugin/plugins/namespace/plugin_test.go b/src/k8splugin/plugins/namespace/plugin_test.go
index 0019df1c..9e57b971 100644
--- a/src/k8splugin/plugins/namespace/plugin_test.go
+++ b/src/k8splugin/plugins/namespace/plugin_test.go
@@ -18,36 +18,54 @@ import (
"strings"
"testing"
- utils "k8splugin/internal"
+ "k8splugin/internal/helm"
coreV1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/meta"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- testclient "k8s.io/client-go/kubernetes/fake"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/client-go/dynamic"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/kubernetes/fake"
)
+type TestKubernetesConnector struct {
+ object runtime.Object
+}
+
+func (t TestKubernetesConnector) GetMapper() meta.RESTMapper {
+ return nil
+}
+
+func (t TestKubernetesConnector) GetDynamicClient() dynamic.Interface {
+ return nil
+}
+
+func (t TestKubernetesConnector) GetStandardClient() kubernetes.Interface {
+ return fake.NewSimpleClientset(t.object)
+}
+
func TestCreateNamespace(t *testing.T) {
- namespace := "test1"
testCases := []struct {
label string
- input *utils.ResourceData
- clientOutput *coreV1.Namespace
+ input string
+ object *coreV1.Namespace
expectedResult string
expectedError string
}{
{
- label: "Successfully create a namespace",
- input: &utils.ResourceData{
- Namespace: namespace,
- },
- clientOutput: &coreV1.Namespace{},
- expectedResult: namespace,
+ label: "Successfully create a namespace",
+ input: "test1",
+ object: &coreV1.Namespace{},
+ expectedResult: "test1",
},
}
for _, testCase := range testCases {
- client := testclient.NewSimpleClientset(testCase.clientOutput)
+ client := TestKubernetesConnector{testCase.object}
t.Run(testCase.label, func(t *testing.T) {
- result, err := Create(testCase.input, client)
+ result, err := namespacePlugin{}.Create("", testCase.input, client)
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("Create method return an un-expected (%s)", err)
@@ -72,39 +90,47 @@ func TestCreateNamespace(t *testing.T) {
}
func TestListNamespace(t *testing.T) {
- namespace := "test1"
testCases := []struct {
label string
input string
- clientOutput *coreV1.NamespaceList
- expectedResult []string
+ object *coreV1.NamespaceList
+ expectedResult []helm.KubernetesResource
}{
{
label: "Sucessfully to display an empty namespace list",
- input: namespace,
- clientOutput: &coreV1.NamespaceList{},
- expectedResult: []string{},
+ input: "",
+ object: &coreV1.NamespaceList{},
+ expectedResult: []helm.KubernetesResource{},
},
{
label: "Sucessfully to display a list of existing namespaces",
- input: namespace,
- clientOutput: &coreV1.NamespaceList{
+ input: "test1",
+ object: &coreV1.NamespaceList{
Items: []coreV1.Namespace{
coreV1.Namespace{
ObjectMeta: metaV1.ObjectMeta{
- Name: namespace,
+ Name: "test1",
},
},
},
},
- expectedResult: []string{namespace},
+ expectedResult: []helm.KubernetesResource{
+ {
+ Name: "test1",
+ GVK: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"},
+ },
+ },
},
}
for _, testCase := range testCases {
- client := testclient.NewSimpleClientset(testCase.clientOutput)
+ client := TestKubernetesConnector{testCase.object}
t.Run(testCase.label, func(t *testing.T) {
- result, err := List(testCase.input, client)
+ result, err := namespacePlugin{}.List(schema.GroupVersionKind{
+ Group: "",
+ Version: "v1",
+ Kind: "Namespace",
+ }, testCase.input, client)
if err != nil {
t.Fatalf("List method returned an error (%s)", err)
} else {
@@ -113,7 +139,7 @@ func TestListNamespace(t *testing.T) {
}
if !reflect.DeepEqual(testCase.expectedResult, result) {
- t.Fatalf("List method returned: \n%v\n and it was expected: \n%v", result, testCase.expectedResult)
+ t.Fatalf("List method returned: \n%+v\n and it was expected: \n%+v", result, testCase.expectedResult)
}
}
})
@@ -122,14 +148,14 @@ func TestListNamespace(t *testing.T) {
func TestDeleteNamespace(t *testing.T) {
testCases := []struct {
- label string
- input map[string]string
- clientOutput *coreV1.Namespace
+ label string
+ input map[string]string
+ object *coreV1.Namespace
}{
{
label: "Sucessfully to delete an existing namespace",
input: map[string]string{"name": "test-name", "namespace": "test-namespace"},
- clientOutput: &coreV1.Namespace{
+ object: &coreV1.Namespace{
ObjectMeta: metaV1.ObjectMeta{
Name: "test-name",
},
@@ -138,9 +164,12 @@ func TestDeleteNamespace(t *testing.T) {
}
for _, testCase := range testCases {
- client := testclient.NewSimpleClientset(testCase.clientOutput)
+ client := TestKubernetesConnector{testCase.object}
t.Run(testCase.label, func(t *testing.T) {
- err := Delete(testCase.input["name"], testCase.input["namespace"], client)
+ err := namespacePlugin{}.Delete(helm.KubernetesResource{
+ GVK: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"},
+ Name: testCase.input["name"],
+ }, testCase.input["namespace"], client)
if err != nil {
t.Fatalf("Delete method returned an error (%s)", err)
}
@@ -152,14 +181,14 @@ func TestGetNamespace(t *testing.T) {
testCases := []struct {
label string
input map[string]string
- clientOutput *coreV1.Namespace
+ object *coreV1.Namespace
expectedResult string
expectedError string
}{
{
label: "Sucessfully to get an existing namespace",
input: map[string]string{"name": "test-name", "namespace": "test-namespace"},
- clientOutput: &coreV1.Namespace{
+ object: &coreV1.Namespace{
ObjectMeta: metaV1.ObjectMeta{
Name: "test-name",
},
@@ -169,7 +198,7 @@ func TestGetNamespace(t *testing.T) {
{
label: "Fail to get an non-existing namespace",
input: map[string]string{"name": "test-name", "namespace": "test-namespace"},
- clientOutput: &coreV1.Namespace{
+ object: &coreV1.Namespace{
ObjectMeta: metaV1.ObjectMeta{
Name: "test-name2",
},
@@ -179,9 +208,12 @@ func TestGetNamespace(t *testing.T) {
}
for _, testCase := range testCases {
- client := testclient.NewSimpleClientset(testCase.clientOutput)
+ client := TestKubernetesConnector{testCase.object}
t.Run(testCase.label, func(t *testing.T) {
- result, err := Get(testCase.input["name"], testCase.input["namespace"], client)
+ result, err := namespacePlugin{}.Get(helm.KubernetesResource{
+ GVK: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"},
+ Name: testCase.input["name"],
+ }, testCase.input["namespace"], client)
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("Get method return an un-expected (%s)", err)
diff --git a/src/k8splugin/plugins/network/plugin.go b/src/k8splugin/plugins/network/plugin.go
index 74ac3473..5cc57e87 100644
--- a/src/k8splugin/plugins/network/plugin.go
+++ b/src/k8splugin/plugins/network/plugin.go
@@ -14,31 +14,38 @@ limitations under the License.
package main
import (
- "k8splugin/plugins/network/v1"
+ v1 "k8splugin/plugins/network/v1"
"regexp"
utils "k8splugin/internal"
+ "k8splugin/internal/app"
+ "k8splugin/internal/helm"
pkgerrors "github.com/pkg/errors"
- "k8s.io/client-go/kubernetes"
+ "k8s.io/apimachinery/pkg/runtime/schema"
)
-func extractData(data string) (vnfID, cniType, networkName string) {
+// ExportedVariable is what we will look for when calling the plugin
+var ExportedVariable networkPlugin
+
+type networkPlugin struct {
+}
+
+func extractData(data string) (cniType, networkName string) {
re := regexp.MustCompile("_")
split := re.Split(data, -1)
if len(split) != 3 {
return
}
- vnfID = split[0]
cniType = split[1]
networkName = split[2]
return
}
// Create an ONAP Network object
-func Create(data *utils.ResourceData, client kubernetes.Interface) (string, error) {
+func (p networkPlugin) Create(yamlFilePath string, namespace string, client *app.KubernetesClient) (string, error) {
network := &v1.OnapNetwork{}
- if _, err := utils.DecodeYAML(data.YamlFilePath, network); err != nil {
+ if _, err := utils.DecodeYAML(yamlFilePath, network); err != nil {
return "", pkgerrors.Wrap(err, "Decode network object error")
}
@@ -58,17 +65,24 @@ func Create(data *utils.ResourceData, client kubernetes.Interface) (string, erro
return "", pkgerrors.Wrap(err, "Error during the creation for "+cniType+" plugin")
}
- return data.VnfId + "_" + cniType + "_" + name, nil
+ return cniType + "_" + name, nil
+}
+
+// Get a Network
+func (p networkPlugin) Get(resource helm.KubernetesResource, namespace string, client *app.KubernetesClient) (string, error) {
+ return "", nil
}
// List of Networks
-func List(namespace string, kubeclient kubernetes.Interface) ([]string, error) {
+func (p networkPlugin) List(gvk schema.GroupVersionKind, namespace string,
+ client *app.KubernetesClient) ([]helm.KubernetesResource, error) {
+
return nil, nil
}
// Delete an existing Network
-func Delete(name string, namespace string, kubeclient kubernetes.Interface) error {
- _, cniType, networkName := extractData(name)
+func (p networkPlugin) Delete(resource helm.KubernetesResource, namespace string, client *app.KubernetesClient) error {
+ cniType, networkName := extractData(resource.Name)
typePlugin, ok := utils.LoadedPlugins[cniType+"-network"]
if !ok {
return pkgerrors.New("No plugin for resource " + cniType + " found")
@@ -85,8 +99,3 @@ func Delete(name string, namespace string, kubeclient kubernetes.Interface) erro
return nil
}
-
-// Get an existing Network
-func Get(name string, namespace string, kubeclient kubernetes.Interface) (string, error) {
- return "", nil
-}
diff --git a/src/k8splugin/plugins/network/plugin_test.go b/src/k8splugin/plugins/network/plugin_test.go
index e8e113b2..5a8ce4db 100644
--- a/src/k8splugin/plugins/network/plugin_test.go
+++ b/src/k8splugin/plugins/network/plugin_test.go
@@ -15,6 +15,7 @@ package main
import (
utils "k8splugin/internal"
+ "k8splugin/internal/helm"
"os"
"plugin"
"reflect"
@@ -22,6 +23,7 @@ import (
"testing"
pkgerrors "github.com/pkg/errors"
+ "k8s.io/apimachinery/pkg/runtime/schema"
)
func LoadMockNetworkPlugins(krdLoadedPlugins *map[string]*plugin.Plugin, networkName, errMsg string) error {
@@ -51,7 +53,6 @@ func LoadMockNetworkPlugins(krdLoadedPlugins *map[string]*plugin.Plugin, network
}
func TestCreateNetwork(t *testing.T) {
- internalVNFID := "1"
oldkrdPluginData := utils.LoadedPlugins
defer func() {
@@ -60,34 +61,27 @@ func TestCreateNetwork(t *testing.T) {
testCases := []struct {
label string
- input *utils.ResourceData
+ input string
mockError string
mockOutput string
expectedResult string
expectedError string
}{
{
- label: "Fail to decode a network object",
- input: &utils.ResourceData{
- YamlFilePath: "../../mock_files/mock_yamls/service.yaml",
- },
+ label: "Fail to decode a network object",
+ input: "../../mock_files/mock_yamls/service.yaml",
expectedError: "No plugin for resource",
},
{
- label: "Fail to create a network",
- input: &utils.ResourceData{
- YamlFilePath: "../../mock_files/mock_yamls/ovn4nfvk8s.yaml",
- },
+ label: "Fail to create a network",
+ input: "../../mock_files/mock_yamls/ovn4nfvk8s.yaml",
mockError: "Internal error",
expectedError: "Error during the creation for ovn4nfvk8s plugin: Internal error",
},
{
- label: "Successfully create a ovn4nfv network",
- input: &utils.ResourceData{
- VnfId: internalVNFID,
- YamlFilePath: "../../mock_files/mock_yamls/ovn4nfvk8s.yaml",
- },
- expectedResult: internalVNFID + "_ovn4nfvk8s_myNetwork",
+ label: "Successfully create a ovn4nfv network",
+ input: "../../mock_files/mock_yamls/ovn4nfvk8s.yaml",
+ expectedResult: "ovn4nfvk8s_myNetwork",
mockOutput: "myNetwork",
},
}
@@ -98,7 +92,7 @@ func TestCreateNetwork(t *testing.T) {
if err != nil {
t.Fatalf("TestCreateNetwork returned an error (%s)", err)
}
- result, err := Create(testCase.input, nil)
+ result, err := networkPlugin{}.Create(testCase.input, "", nil)
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("Create method return an un-expected (%s)", err)
@@ -157,7 +151,10 @@ func TestDeleteNetwork(t *testing.T) {
if err != nil {
t.Fatalf("TestDeleteNetwork returned an error (%s)", err)
}
- err = Delete(testCase.input, "", nil)
+ err = networkPlugin{}.Delete(helm.KubernetesResource{
+ GVK: schema.GroupVersionKind{Group: "", Version: "", Kind: "Network"},
+ Name: testCase.input,
+ }, "", nil)
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("Create method return an un-expected (%s)", err)
diff --git a/src/k8splugin/plugins/service/plugin.go b/src/k8splugin/plugins/service/plugin.go
index ea5aecad..2957c441 100644
--- a/src/k8splugin/plugins/service/plugin.go
+++ b/src/k8splugin/plugins/service/plugin.go
@@ -16,23 +16,29 @@ package main
import (
"log"
- "k8s.io/client-go/kubernetes"
-
pkgerrors "github.com/pkg/errors"
-
coreV1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime/schema"
utils "k8splugin/internal"
+ "k8splugin/internal/helm"
+ "k8splugin/internal/plugin"
)
+// ExportedVariable is what we will look for when calling the plugin
+var ExportedVariable servicePlugin
+
+type servicePlugin struct {
+}
+
// Create a service object in a specific Kubernetes cluster
-func Create(data *utils.ResourceData, client kubernetes.Interface) (string, error) {
- namespace := data.Namespace
+func (p servicePlugin) Create(yamlFilePath string, namespace string, client plugin.KubernetesConnector) (string, error) {
if namespace == "" {
namespace = "default"
}
- obj, err := utils.DecodeYAML(data.YamlFilePath, nil)
+
+ obj, err := utils.DecodeYAML(yamlFilePath, nil)
if err != nil {
return "", pkgerrors.Wrap(err, "Decode service object error")
}
@@ -43,7 +49,7 @@ func Create(data *utils.ResourceData, client kubernetes.Interface) (string, erro
}
service.Namespace = namespace
- result, err := client.CoreV1().Services(namespace).Create(service)
+ result, err := client.GetStandardClient().CoreV1().Services(namespace).Create(service)
if err != nil {
return "", pkgerrors.Wrap(err, "Create Service error")
}
@@ -52,7 +58,8 @@ func Create(data *utils.ResourceData, client kubernetes.Interface) (string, erro
}
// List of existing services hosted in a specific Kubernetes cluster
-func List(namespace string, kubeclient kubernetes.Interface) ([]string, error) {
+// gvk parameter is not used as this plugin is specific to services only
+func (p servicePlugin) List(gvk schema.GroupVersionKind, namespace string, client plugin.KubernetesConnector) ([]helm.KubernetesResource, error) {
if namespace == "" {
namespace = "default"
}
@@ -60,19 +67,25 @@ func List(namespace string, kubeclient kubernetes.Interface) ([]string, error) {
opts := metaV1.ListOptions{
Limit: utils.ResourcesListLimit,
}
- opts.APIVersion = "apps/v1"
- opts.Kind = "Service"
- list, err := kubeclient.CoreV1().Services(namespace).List(opts)
+ list, err := client.GetStandardClient().CoreV1().Services(namespace).List(opts)
if err != nil {
return nil, pkgerrors.Wrap(err, "Get Service list error")
}
- result := make([]string, 0, utils.ResourcesListLimit)
+ result := make([]helm.KubernetesResource, 0, utils.ResourcesListLimit)
if list != nil {
- for _, deployment := range list.Items {
- log.Printf("%v", deployment.Name)
- result = append(result, deployment.Name)
+ for _, service := range list.Items {
+ log.Printf("%v", service.Name)
+ result = append(result,
+ helm.KubernetesResource{
+ GVK: schema.GroupVersionKind{
+ Group: "",
+ Version: "v1",
+ Kind: "Service",
+ },
+ Name: service.GetName(),
+ })
}
}
@@ -80,7 +93,7 @@ func List(namespace string, kubeclient kubernetes.Interface) ([]string, error) {
}
// Delete an existing service hosted in a specific Kubernetes cluster
-func Delete(name string, namespace string, kubeclient kubernetes.Interface) error {
+func (p servicePlugin) Delete(resource helm.KubernetesResource, namespace string, client plugin.KubernetesConnector) error {
if namespace == "" {
namespace = "default"
}
@@ -90,8 +103,8 @@ func Delete(name string, namespace string, kubeclient kubernetes.Interface) erro
PropagationPolicy: &deletePolicy,
}
- log.Println("Deleting service: " + name)
- if err := kubeclient.CoreV1().Services(namespace).Delete(name, opts); err != nil {
+ log.Println("Deleting service: " + resource.Name)
+ if err := client.GetStandardClient().CoreV1().Services(namespace).Delete(resource.Name, opts); err != nil {
return pkgerrors.Wrap(err, "Delete service error")
}
@@ -99,18 +112,15 @@ func Delete(name string, namespace string, kubeclient kubernetes.Interface) erro
}
// Get an existing service hosted in a specific Kubernetes cluster
-func Get(name string, namespace string, kubeclient kubernetes.Interface) (string, error) {
+func (p servicePlugin) Get(resource helm.KubernetesResource, namespace string, client plugin.KubernetesConnector) (string, error) {
if namespace == "" {
namespace = "default"
}
opts := metaV1.GetOptions{}
- opts.APIVersion = "apps/v1"
- opts.Kind = "Service"
-
- service, err := kubeclient.CoreV1().Services(namespace).Get(name, opts)
+ service, err := client.GetStandardClient().CoreV1().Services(namespace).Get(resource.Name, opts)
if err != nil {
- return "", pkgerrors.Wrap(err, "Get Deployment error")
+ return "", pkgerrors.Wrap(err, "Get Service error")
}
return service.Name, nil
diff --git a/src/k8splugin/plugins/service/plugin_test.go b/src/k8splugin/plugins/service/plugin_test.go
index d3614860..66703089 100644
--- a/src/k8splugin/plugins/service/plugin_test.go
+++ b/src/k8splugin/plugins/service/plugin_test.go
@@ -14,54 +14,67 @@ limitations under the License.
package main
import (
+ "k8splugin/internal/helm"
"reflect"
"strings"
"testing"
- utils "k8splugin/internal"
-
coreV1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/meta"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- testclient "k8s.io/client-go/kubernetes/fake"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/client-go/dynamic"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/kubernetes/fake"
)
+type TestKubernetesConnector struct {
+ object runtime.Object
+}
+
+func (t TestKubernetesConnector) GetMapper() meta.RESTMapper {
+ return nil
+}
+
+func (t TestKubernetesConnector) GetDynamicClient() dynamic.Interface {
+ return nil
+}
+
+func (t TestKubernetesConnector) GetStandardClient() kubernetes.Interface {
+ return fake.NewSimpleClientset(t.object)
+}
+
func TestCreateService(t *testing.T) {
- namespace := "test1"
name := "mock-service"
testCases := []struct {
label string
- input *utils.ResourceData
- clientOutput *coreV1.Service
+ input string
+ namespace string
+ object *coreV1.Service
expectedResult string
expectedError string
}{
{
- label: "Fail to create a service with invalid type",
- input: &utils.ResourceData{
- YamlFilePath: "../../mock_files/mock_yamls/deployment.yaml",
- },
- clientOutput: &coreV1.Service{},
+ label: "Fail to create a service with invalid type",
+ input: "../../mock_files/mock_yamls/deployment.yaml",
+ namespace: "test1",
+ object: &coreV1.Service{},
expectedError: "contains another resource different than Service",
},
{
- label: "Successfully create a service",
- input: &utils.ResourceData{
- YamlFilePath: "../../mock_files/mock_yamls/service.yaml",
- },
- clientOutput: &coreV1.Service{
- ObjectMeta: metaV1.ObjectMeta{
- Name: name,
- Namespace: namespace,
- },
- },
+ label: "Successfully create a service",
+ input: "../../mock_files/mock_yamls/service.yaml",
+ namespace: "test1",
+ object: &coreV1.Service{},
expectedResult: name,
},
}
for _, testCase := range testCases {
- client := testclient.NewSimpleClientset(testCase.clientOutput)
+ client := TestKubernetesConnector{testCase.object}
t.Run(testCase.label, func(t *testing.T) {
- result, err := Create(testCase.input, client)
+ result, err := servicePlugin{}.Create(testCase.input, testCase.namespace, client)
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("Create method return an un-expected (%s)", err)
@@ -86,38 +99,42 @@ func TestCreateService(t *testing.T) {
}
func TestListService(t *testing.T) {
- namespace := "test1"
testCases := []struct {
label string
- input string
- clientOutput *coreV1.ServiceList
- expectedResult []string
+ namespace string
+ object *coreV1.ServiceList
+ expectedResult []helm.KubernetesResource
}{
{
label: "Sucessfully to display an empty service list",
- input: namespace,
- clientOutput: &coreV1.ServiceList{},
- expectedResult: []string{},
+ namespace: "test1",
+ object: &coreV1.ServiceList{},
+ expectedResult: []helm.KubernetesResource{},
},
{
- label: "Sucessfully to display a list of existing services",
- input: namespace,
- clientOutput: &coreV1.ServiceList{
+ label: "Sucessfully to display a list of existing services",
+ namespace: "test1",
+ object: &coreV1.ServiceList{
Items: []coreV1.Service{
coreV1.Service{
ObjectMeta: metaV1.ObjectMeta{
Name: "test",
- Namespace: namespace,
+ Namespace: "test1",
},
},
},
},
- expectedResult: []string{"test"},
+ expectedResult: []helm.KubernetesResource{
+ {
+ Name: "test",
+ GVK: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"},
+ },
+ },
},
{
- label: "Sucessfully display a list of existing services in default namespace",
- input: "",
- clientOutput: &coreV1.ServiceList{
+ label: "Sucessfully display a list of existing services in default namespace",
+ namespace: "",
+ object: &coreV1.ServiceList{
Items: []coreV1.Service{
coreV1.Service{
ObjectMeta: metaV1.ObjectMeta{
@@ -128,19 +145,27 @@ func TestListService(t *testing.T) {
coreV1.Service{
ObjectMeta: metaV1.ObjectMeta{
Name: "test2",
- Namespace: namespace,
+ Namespace: "test1",
},
},
},
},
- expectedResult: []string{"test"},
+ expectedResult: []helm.KubernetesResource{
+ {
+ Name: "test",
+ GVK: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"},
+ },
+ },
},
}
for _, testCase := range testCases {
- client := testclient.NewSimpleClientset(testCase.clientOutput)
+ client := TestKubernetesConnector{testCase.object}
t.Run(testCase.label, func(t *testing.T) {
- result, err := List(testCase.input, client)
+ result, err := servicePlugin{}.List(schema.GroupVersionKind{
+ Group: "",
+ Version: "v1",
+ Kind: "Service"}, testCase.namespace, client)
if err != nil {
t.Fatalf("List method returned an error (%s)", err)
} else {
@@ -158,14 +183,14 @@ func TestListService(t *testing.T) {
func TestDeleteService(t *testing.T) {
testCases := []struct {
- label string
- input map[string]string
- clientOutput *coreV1.Service
+ label string
+ input map[string]string
+ object *coreV1.Service
}{
{
label: "Sucessfully to delete an existing service",
input: map[string]string{"name": "test-service", "namespace": "test-namespace"},
- clientOutput: &coreV1.Service{
+ object: &coreV1.Service{
ObjectMeta: metaV1.ObjectMeta{
Name: "test-service",
Namespace: "test-namespace",
@@ -175,7 +200,7 @@ func TestDeleteService(t *testing.T) {
{
label: "Sucessfully delete an existing service in default namespace",
input: map[string]string{"name": "test-service", "namespace": ""},
- clientOutput: &coreV1.Service{
+ object: &coreV1.Service{
ObjectMeta: metaV1.ObjectMeta{
Name: "test-service",
Namespace: "default",
@@ -185,9 +210,12 @@ func TestDeleteService(t *testing.T) {
}
for _, testCase := range testCases {
- client := testclient.NewSimpleClientset(testCase.clientOutput)
+ client := TestKubernetesConnector{testCase.object}
t.Run(testCase.label, func(t *testing.T) {
- err := Delete(testCase.input["name"], testCase.input["namespace"], client)
+ err := servicePlugin{}.Delete(helm.KubernetesResource{
+ GVK: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"},
+ Name: testCase.input["name"],
+ }, testCase.input["namespace"], client)
if err != nil {
t.Fatalf("Delete method returned an error (%s)", err)
}
@@ -199,14 +227,14 @@ func TestGetService(t *testing.T) {
testCases := []struct {
label string
input map[string]string
- clientOutput *coreV1.Service
+ object *coreV1.Service
expectedResult string
expectedError string
}{
{
label: "Sucessfully to get an existing service",
input: map[string]string{"name": "test-service", "namespace": "test-namespace"},
- clientOutput: &coreV1.Service{
+ object: &coreV1.Service{
ObjectMeta: metaV1.ObjectMeta{
Name: "test-service",
Namespace: "test-namespace",
@@ -217,7 +245,7 @@ func TestGetService(t *testing.T) {
{
label: "Sucessfully get an existing service from default namespaces",
input: map[string]string{"name": "test-service", "namespace": ""},
- clientOutput: &coreV1.Service{
+ object: &coreV1.Service{
ObjectMeta: metaV1.ObjectMeta{
Name: "test-service",
Namespace: "default",
@@ -228,7 +256,7 @@ func TestGetService(t *testing.T) {
{
label: "Fail to get an non-existing namespace",
input: map[string]string{"name": "test-name", "namespace": "test-namespace"},
- clientOutput: &coreV1.Service{
+ object: &coreV1.Service{
ObjectMeta: metaV1.ObjectMeta{
Name: "test-service",
Namespace: "default",
@@ -239,9 +267,12 @@ func TestGetService(t *testing.T) {
}
for _, testCase := range testCases {
- client := testclient.NewSimpleClientset(testCase.clientOutput)
+ client := TestKubernetesConnector{testCase.object}
t.Run(testCase.label, func(t *testing.T) {
- result, err := Get(testCase.input["name"], testCase.input["namespace"], client)
+ result, err := servicePlugin{}.Get(helm.KubernetesResource{
+ GVK: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"},
+ Name: testCase.input["name"],
+ }, testCase.input["namespace"], client)
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("Get method return an un-expected (%s)", err)