From 083465d10c8fdeaffa89aa7daa93def3eca77df1 Mon Sep 17 00:00:00 2001 From: Victor Morales Date: Thu, 24 Jan 2019 17:46:43 -0800 Subject: Use a standard Go project layout This project wasn't following some Standard Go Project Layout guidelines(https://github.com/golang-standards/project-layout). This change pretends to organize the source code and following those guidelines. Change-Id: I61085ac20f28069cede013f83034bed06892d87c Signed-off-by: Victor Morales Issue-ID: MULTICLOUD-301 --- src/k8splugin/internal/app/client.go | 44 ++++++ src/k8splugin/internal/app/client_test.go | 36 +++++ src/k8splugin/internal/app/vnfhelper.go | 196 +++++++++++++++++++++++++++ src/k8splugin/internal/app/vnfhelper_test.go | 133 ++++++++++++++++++ 4 files changed, 409 insertions(+) create mode 100644 src/k8splugin/internal/app/client.go create mode 100644 src/k8splugin/internal/app/client_test.go create mode 100644 src/k8splugin/internal/app/vnfhelper.go create mode 100644 src/k8splugin/internal/app/vnfhelper_test.go (limited to 'src/k8splugin/internal/app') diff --git a/src/k8splugin/internal/app/client.go b/src/k8splugin/internal/app/client.go new file mode 100644 index 00000000..3555afdd --- /dev/null +++ b/src/k8splugin/internal/app/client.go @@ -0,0 +1,44 @@ +/* +Copyright 2018 Intel Corporation. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package app + +import ( + "errors" + + pkgerrors "github.com/pkg/errors" + + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + +// GetKubeClient loads the Kubernetes configuation values stored into the local configuration file +var GetKubeClient = func(configPath string) (kubernetes.Clientset, error) { + var clientset *kubernetes.Clientset + + if configPath == "" { + return *clientset, errors.New("config not passed and is not found in ~/.kube. ") + } + + config, err := clientcmd.BuildConfigFromFlags("", configPath) + if err != nil { + return kubernetes.Clientset{}, pkgerrors.Wrap(err, "setConfig: Build config from flags raised an error") + } + + clientset, err = kubernetes.NewForConfig(config) + if err != nil { + return *clientset, err + } + + return *clientset, nil +} diff --git a/src/k8splugin/internal/app/client_test.go b/src/k8splugin/internal/app/client_test.go new file mode 100644 index 00000000..9d892633 --- /dev/null +++ b/src/k8splugin/internal/app/client_test.go @@ -0,0 +1,36 @@ +// +build unit + +/* +Copyright 2018 Intel Corporation. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package app + +import ( + "reflect" + "testing" +) + +func TestGetKubeClient(t *testing.T) { + t.Run("Successfully create Kube Client", func(t *testing.T) { + + clientset, err := GetKubeClient("../../mock_files/mock_configs/mock_config") + if err != nil { + t.Fatalf("TestGetKubeClient returned an error (%s)", err) + } + + if reflect.TypeOf(clientset).Name() != "Clientset" { + t.Fatalf("TestGetKubeClient returned :\n result=%v\n expected=%v", clientset, "Clientset") + } + + }) +} diff --git a/src/k8splugin/internal/app/vnfhelper.go b/src/k8splugin/internal/app/vnfhelper.go new file mode 100644 index 00000000..3deb9f21 --- /dev/null +++ b/src/k8splugin/internal/app/vnfhelper.go @@ -0,0 +1,196 @@ +/* +Copyright 2018 Intel Corporation. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package app + +import ( + "encoding/hex" + "io/ioutil" + "log" + "math/rand" + "os" + + "k8s.io/client-go/kubernetes" + + pkgerrors "github.com/pkg/errors" + yaml "gopkg.in/yaml.v2" + + utils "k8splugin/internal" +) + +func generateExternalVNFID() string { + b := make([]byte, 2) + rand.Read(b) + return hex.EncodeToString(b) +} + +func ensuresNamespace(namespace string, kubeclient kubernetes.Interface) error { + namespacePlugin, ok := utils.LoadedPlugins["namespace"] + if !ok { + return pkgerrors.New("No plugin for namespace resource found") + } + + symGetNamespaceFunc, err := namespacePlugin.Lookup("Get") + if err != nil { + return pkgerrors.Wrap(err, "Error fetching get namespace function") + } + + ns, err := symGetNamespaceFunc.(func(string, string, kubernetes.Interface) (string, error))( + namespace, namespace, kubeclient) + if err != nil { + return pkgerrors.Wrap(err, "An error ocurred during the get namespace execution") + } + + 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, kubeclient) + if err != nil { + return pkgerrors.Wrap(err, "Error creating "+namespace+" namespace") + } + } + return nil +} + +// CreateVNF reads the CSAR files from the files system and creates them one by one +var CreateVNF = func(csarID string, cloudRegionID string, namespace string, kubeclient *kubernetes.Clientset) (string, map[string][]string, error) { + if err := ensuresNamespace(namespace, kubeclient); err != nil { + return "", nil, pkgerrors.Wrap(err, "Error while ensuring namespace: "+namespace) + } + externalVNFID := generateExternalVNFID() + internalVNFID := cloudRegionID + "-" + namespace + "-" + externalVNFID + + csarDirPath := os.Getenv("CSAR_DIR") + "/" + csarID + metadataYAMLPath := csarDirPath + "/metadata.yaml" + + log.Println("Reading " + metadataYAMLPath + " file") + metadataFile, err := ReadMetadataFile(metadataYAMLPath) + if err != nil { + return "", nil, pkgerrors.Wrap(err, "Error while reading Metadata File: "+metadataYAMLPath) + } + + var path string + resourceYAMLNameMap := make(map[string][]string) + // Iterates over the resources defined in the metadata file to create kubernetes resources + log.Println(string(len(metadataFile.ResourceTypePathMap)) + " resource(s) type(s) to be processed") + for resource, fileNames := range metadataFile.ResourceTypePathMap { + log.Println("Processing items of " + string(resource) + " resource") + var resourcesCreated []string + for _, filename := range fileNames { + path = csarDirPath + "/" + filename + + if _, err := os.Stat(path); os.IsNotExist(err) { + return "", nil, pkgerrors.New("File " + path + "does not exists") + } + log.Println("Processing file: " + path) + + genericKubeData := &utils.ResourceData{ + YamlFilePath: path, + Namespace: namespace, + VnfId: internalVNFID, + } + + typePlugin, ok := utils.LoadedPlugins[resource] + if !ok { + return "", nil, pkgerrors.New("No plugin for resource " + resource + " found") + } + + symCreateResourceFunc, err := typePlugin.Lookup("Create") + if err != nil { + return "", nil, pkgerrors.Wrap(err, "Error fetching "+resource+" plugin") + } + + internalResourceName, err := symCreateResourceFunc.(func(*utils.ResourceData, kubernetes.Interface) (string, error))( + genericKubeData, kubeclient) + if err != nil { + return "", nil, pkgerrors.Wrap(err, "Error in plugin "+resource+" plugin") + } + log.Print(internalResourceName + " succesful resource created") + resourcesCreated = append(resourcesCreated, internalResourceName) + } + resourceYAMLNameMap[resource] = resourcesCreated + } + + return externalVNFID, resourceYAMLNameMap, nil +} + +// DestroyVNF deletes VNFs based on data passed +var DestroyVNF = func(data map[string][]string, namespace string, kubeclient *kubernetes.Clientset) error { + /* data: + { + "deployment": ["cloud1-default-uuid-sisedeploy1", "cloud1-default-uuid-sisedeploy2", ... ] + "service": ["cloud1-default-uuid-sisesvc1", "cloud1-default-uuid-sisesvc2", ... ] + }, + */ + + for resourceName, resourceList := range data { + typePlugin, ok := utils.LoadedPlugins[resourceName] + if !ok { + return pkgerrors.New("No plugin for resource " + resourceName + " found") + } + + symDeleteResourceFunc, err := typePlugin.Lookup("Delete") + if err != nil { + return pkgerrors.Wrap(err, "Error fetching "+resourceName+" plugin") + } + + for _, resourceName := range resourceList { + + log.Println("Deleting resource: " + resourceName) + + err = symDeleteResourceFunc.(func(string, string, kubernetes.Interface) error)( + resourceName, namespace, kubeclient) + if err != nil { + return pkgerrors.Wrap(err, "Error destroying "+resourceName) + } + } + } + + return nil +} + +// MetadataFile stores the metadata of execution +type MetadataFile struct { + ResourceTypePathMap map[string][]string `yaml:"resources"` +} + +// ReadMetadataFile reads the metadata yaml to return the order or reads +var ReadMetadataFile = func(path string) (MetadataFile, error) { + var metadataFile MetadataFile + + if _, err := os.Stat(path); os.IsNotExist(err) { + return metadataFile, pkgerrors.Wrap(err, "Metadata YAML file does not exist") + } + + log.Println("Reading metadata YAML: " + path) + yamlFile, err := ioutil.ReadFile(path) + if err != nil { + return metadataFile, pkgerrors.Wrap(err, "Metadata YAML file read error") + } + + err = yaml.Unmarshal(yamlFile, &metadataFile) + if err != nil { + return metadataFile, pkgerrors.Wrap(err, "Metadata YAML file unmarshal error") + } + log.Printf("metadata:\n%v", metadataFile) + + return metadataFile, nil +} diff --git a/src/k8splugin/internal/app/vnfhelper_test.go b/src/k8splugin/internal/app/vnfhelper_test.go new file mode 100644 index 00000000..81bea627 --- /dev/null +++ b/src/k8splugin/internal/app/vnfhelper_test.go @@ -0,0 +1,133 @@ +// +build integration + +/* +Copyright 2018 Intel Corporation. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package app + +import ( + "io/ioutil" + "log" + "os" + "plugin" + "testing" + + yaml "gopkg.in/yaml.v2" + "k8s.io/client-go/kubernetes" + + pkgerrors "github.com/pkg/errors" + + utils "k8splugin/internal" +) + +func LoadMockPlugins(krdLoadedPlugins *map[string]*plugin.Plugin) error { + if _, err := os.Stat("../../mock_files/mock_plugins/mockplugin.so"); os.IsNotExist(err) { + return pkgerrors.New("mockplugin.so does not exist. Please compile mockplugin.go to generate") + } + + mockPlugin, err := plugin.Open("../../mock_files/mock_plugins/mockplugin.so") + if err != nil { + return pkgerrors.Cause(err) + } + + (*krdLoadedPlugins)["namespace"] = mockPlugin + (*krdLoadedPlugins)["deployment"] = mockPlugin + (*krdLoadedPlugins)["service"] = mockPlugin + + return nil +} + +func TestCreateVNF(t *testing.T) { + oldkrdPluginData := utils.LoadedPlugins + oldReadMetadataFile := ReadMetadataFile + + defer func() { + utils.LoadedPlugins = oldkrdPluginData + ReadMetadataFile = oldReadMetadataFile + }() + + err := LoadMockPlugins(&utils.LoadedPlugins) + if err != nil { + t.Fatalf("TestCreateVNF returned an error (%s)", err) + } + + ReadMetadataFile = func(yamlFilePath string) (MetadataFile, error) { + var seqFile MetadataFile + + if _, err := os.Stat(yamlFilePath); err == nil { + rawBytes, err := ioutil.ReadFile("../../mock_files/mock_yamls/metadata.yaml") + if err != nil { + return seqFile, pkgerrors.Wrap(err, "Metadata YAML file read error") + } + + err = yaml.Unmarshal(rawBytes, &seqFile) + if err != nil { + return seqFile, pkgerrors.Wrap(err, "Metadata YAML file unmarshall error") + } + } + + return seqFile, nil + } + + kubeclient := kubernetes.Clientset{} + + t.Run("Successfully create VNF", func(t *testing.T) { + externaluuid, data, err := CreateVNF("uuid", "cloudregion1", "test", &kubeclient) + if err != nil { + t.Fatalf("TestCreateVNF returned an error (%s)", err) + } + + log.Println(externaluuid) + + if data == nil { + t.Fatalf("TestCreateVNF returned empty data (%s)", data) + } + }) + +} + +func TestDeleteVNF(t *testing.T) { + oldkrdPluginData := utils.LoadedPlugins + + defer func() { + utils.LoadedPlugins = oldkrdPluginData + }() + + err := LoadMockPlugins(&utils.LoadedPlugins) + if err != nil { + t.Fatalf("TestCreateVNF returned an error (%s)", err) + } + + kubeclient := kubernetes.Clientset{} + + t.Run("Successfully delete VNF", func(t *testing.T) { + data := map[string][]string{ + "deployment": []string{"cloud1-default-uuid-sisedeploy"}, + "service": []string{"cloud1-default-uuid-sisesvc"}, + } + + err := DestroyVNF(data, "test", &kubeclient) + if err != nil { + t.Fatalf("TestCreateVNF returned an error (%s)", err) + } + }) +} + +func TestReadMetadataFile(t *testing.T) { + t.Run("Successfully read Metadata YAML file", func(t *testing.T) { + _, err := ReadMetadataFile("../../mock_files//mock_yamls/metadata.yaml") + if err != nil { + t.Fatalf("TestReadMetadataFile returned an error (%s)", err) + } + }) +} -- cgit 1.2.3-korg