From d780f1b30c98a27d269e3e05423e9e54e0e022f6 Mon Sep 17 00:00:00 2001 From: Kiran Kamineni Date: Thu, 30 May 2019 14:43:06 -0700 Subject: Plugin code refactoring The plugin code has been refactored to implement a common interface. This will allow us to do plugin validation at loadtime of the plugin instead of at runtime. This also makes the code calling the plugins cleaner and easier to read. Issue-ID: MULTICLOUD-557 Change-Id: Ice2bcc9b850d7c0e1707dcc42132c63dd77472a7 Signed-off-by: Kiran Kamineni --- src/k8splugin/go.sum | 2 + src/k8splugin/internal/app/client.go | 181 ++++----------------- src/k8splugin/internal/app/client_test.go | 2 +- src/k8splugin/internal/plugin/helpers.go | 92 +++++++++++ .../mock_files/mock_plugins/mockplugin.go | 31 +++- src/k8splugin/plugins/generic/plugin.go | 68 ++++++-- src/k8splugin/plugins/namespace/plugin.go | 60 ++++--- src/k8splugin/plugins/namespace/plugin_test.go | 106 +++++++----- src/k8splugin/plugins/network/plugin.go | 39 +++-- src/k8splugin/plugins/network/plugin_test.go | 33 ++-- src/k8splugin/plugins/service/plugin.go | 58 ++++--- src/k8splugin/plugins/service/plugin_test.go | 141 +++++++++------- 12 files changed, 472 insertions(+), 341 deletions(-) create mode 100644 src/k8splugin/internal/plugin/helpers.go (limited to 'src/k8splugin') 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) -- cgit 1.2.3-korg