diff options
author | Kiran Kamineni <kiran.k.kamineni@intel.com> | 2019-05-30 14:43:06 -0700 |
---|---|---|
committer | Kiran Kamineni <kiran.k.kamineni@intel.com> | 2019-06-06 17:32:41 -0700 |
commit | d780f1b30c98a27d269e3e05423e9e54e0e022f6 (patch) | |
tree | 3d01a1fab1a846206ae714f94838de2f0a659e13 /src/k8splugin/plugins | |
parent | f006c55c0793a0cacac5aa45ba7f13fd5c6ef5f4 (diff) |
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 <kiran.k.kamineni@intel.com>
Diffstat (limited to 'src/k8splugin/plugins')
-rw-r--r-- | src/k8splugin/plugins/generic/plugin.go | 68 | ||||
-rw-r--r-- | src/k8splugin/plugins/namespace/plugin.go | 60 | ||||
-rw-r--r-- | src/k8splugin/plugins/namespace/plugin_test.go | 106 | ||||
-rw-r--r-- | src/k8splugin/plugins/network/plugin.go | 39 | ||||
-rw-r--r-- | src/k8splugin/plugins/network/plugin_test.go | 33 | ||||
-rw-r--r-- | src/k8splugin/plugins/service/plugin.go | 58 | ||||
-rw-r--r-- | src/k8splugin/plugins/service/plugin_test.go | 141 |
7 files changed, 321 insertions, 184 deletions
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) |