diff options
Diffstat (limited to 'src/k8splugin/internal')
-rw-r--r-- | src/k8splugin/internal/app/client.go | 176 | ||||
-rw-r--r-- | src/k8splugin/internal/app/client_test.go | 92 | ||||
-rw-r--r-- | src/k8splugin/internal/app/instance.go | 206 | ||||
-rw-r--r-- | src/k8splugin/internal/app/instance_test.go (renamed from src/k8splugin/internal/app/vnfhelper_test.go) | 232 | ||||
-rw-r--r-- | src/k8splugin/internal/app/vnfhelper.go | 195 |
5 files changed, 613 insertions, 288 deletions
diff --git a/src/k8splugin/internal/app/client.go b/src/k8splugin/internal/app/client.go index 3555afdd..fa5fdfd5 100644 --- a/src/k8splugin/internal/app/client.go +++ b/src/k8splugin/internal/app/client.go @@ -14,31 +14,187 @@ limitations under the License. package app import ( - "errors" + "log" + "os" + "strings" - pkgerrors "github.com/pkg/errors" + utils "k8splugin/internal" + pkgerrors "github.com/pkg/errors" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" + "k8s.io/helm/pkg/tiller" ) -// GetKubeClient loads the Kubernetes configuation values stored into the local configuration file -var GetKubeClient = func(configPath string) (kubernetes.Clientset, error) { - var clientset *kubernetes.Clientset +type kubernetesClient struct { + clientSet *kubernetes.Clientset +} +// GetKubeClient loads the Kubernetes configuation values stored into the local configuration file +func (k *kubernetesClient) init(configPath string) error { if configPath == "" { - return *clientset, errors.New("config not passed and is not found in ~/.kube. ") + return pkgerrors.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") + return pkgerrors.Wrap(err, "setConfig: Build config from flags raised an error") + } + + k.clientSet, err = kubernetes.NewForConfig(config) + if err != nil { + return err + } + + 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") + if err != nil { + return pkgerrors.Wrap(err, "Error fetching get namespace function") + } + + ns, _ := symGetNamespaceFunc.(func(string, string, kubernetes.Interface) (string, error))( + namespace, namespace, k.clientSet) + + 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) + if err != nil { + return pkgerrors.Wrap(err, "Error creating "+namespace+" namespace") + } + } + return nil +} + +func (k *kubernetesClient) createKind(kind string, files []string, namespace string) ([]string, error) { + + log.Println("Processing items of Kind: " + kind) + + //Iterate over each file of a particular kind here + var resourcesCreated []string + for _, f := range files { + if _, err := os.Stat(f); os.IsNotExist(err) { + return nil, pkgerrors.New("File " + f + "does not exists") + } + + log.Println("Processing file: " + f) + + //Populate the namespace from profile instead of instance body + genericKubeData := &utils.ResourceData{ + YamlFilePath: f, + Namespace: namespace, + } + + typePlugin, ok := utils.LoadedPlugins[strings.ToLower(kind)] + if !ok { + return nil, pkgerrors.New("No plugin for kind " + kind + " found") + } + + symCreateResourceFunc, err := typePlugin.Lookup("Create") + if err != nil { + return nil, pkgerrors.Wrap(err, "Error fetching "+kind+" plugin") + } + + createdResourceName, err := symCreateResourceFunc.(func(*utils.ResourceData, kubernetes.Interface) (string, error))( + genericKubeData, k.clientSet) + if err != nil { + return nil, pkgerrors.Wrap(err, "Error in plugin "+kind+" plugin") + } + log.Print(createdResourceName + " created") + resourcesCreated = append(resourcesCreated, createdResourceName) + } + + return resourcesCreated, nil +} + +func (k *kubernetesClient) createResources(resMap map[string][]string, + namespace string) (map[string][]string, error) { + + err := k.ensureNamespace(namespace) + if err != nil { + return nil, pkgerrors.Wrap(err, "Creating Namespace") + } + + createdResourceMap := make(map[string][]string) + // Create all the known kinds in the InstallOrder + for _, kind := range tiller.InstallOrder { + files, ok := resMap[kind] + if !ok { + log.Println("Kind " + kind + " not found. Skipping...") + continue + } + + resourcesCreated, err := k.createKind(kind, files, namespace) + if err != nil { + return nil, pkgerrors.Wrap(err, "Error creating kind: "+kind) + } + + createdResourceMap[kind] = resourcesCreated + delete(resMap, kind) } - clientset, err = kubernetes.NewForConfig(config) + //Create the remaining kinds from the resMap + for kind, files := range resMap { + resourcesCreated, err := k.createKind(kind, files, namespace) + if err != nil { + return nil, pkgerrors.Wrap(err, "Error creating kind: "+kind) + } + + createdResourceMap[kind] = resourcesCreated + delete(resMap, kind) + } + + return createdResourceMap, nil +} + +func (k *kubernetesClient) deleteKind(kind string, resources []string, namespace string) error { + log.Println("Deleting items of Kind: " + kind) + + typePlugin, ok := utils.LoadedPlugins[strings.ToLower(kind)] + if !ok { + return pkgerrors.New("No plugin for resource " + kind + " found") + } + + symDeleteResourceFunc, err := typePlugin.Lookup("Delete") if err != nil { - return *clientset, err + return pkgerrors.Wrap(err, "Error fetching "+kind+" plugin") + } + + for _, res := range resources { + log.Println("Deleting resource: " + res) + err = symDeleteResourceFunc.(func(string, string, kubernetes.Interface) error)( + res, namespace, k.clientSet) + if err != nil { + return pkgerrors.Wrap(err, "Error destroying "+res) + } + } + return nil +} + +func (k *kubernetesClient) deleteResources(resMap map[string][]string, namespace string) error { + //TODO: Investigate if deletion should be in a particular order + for kind, resourceNames := range resMap { + err := k.deleteKind(kind, resourceNames, namespace) + if err != nil { + return pkgerrors.Wrap(err, "Deleting resources") + } } - return *clientset, nil + return nil } diff --git a/src/k8splugin/internal/app/client_test.go b/src/k8splugin/internal/app/client_test.go index 71892725..5999cfa0 100644 --- a/src/k8splugin/internal/app/client_test.go +++ b/src/k8splugin/internal/app/client_test.go @@ -14,21 +14,105 @@ limitations under the License. package app import ( + "os" + "plugin" "reflect" "testing" + + utils "k8splugin/internal" + + pkgerrors "github.com/pkg/errors" + "k8s.io/client-go/kubernetes" ) -func TestGetKubeClient(t *testing.T) { +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.Wrap(err, "Opening mock plugins") + } + + krdLoadedPlugins["namespace"] = mockPlugin + krdLoadedPlugins["deployment"] = mockPlugin + krdLoadedPlugins["service"] = mockPlugin + + return nil +} + +func TestInit(t *testing.T) { t.Run("Successfully create Kube Client", func(t *testing.T) { - clientset, err := GetKubeClient("../../mock_files/mock_configs/mock_config") + kubeClient := kubernetesClient{} + err := kubeClient.init("../../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") + name := reflect.TypeOf(kubeClient.clientSet).Elem().Name() + if name != "Clientset" { + t.Fatalf("TestGetKubeClient returned :\n result=%v\n expected=%v", name, "Clientset") } }) } + +func TestCreateResources(t *testing.T) { + oldkrdPluginData := utils.LoadedPlugins + + defer func() { + utils.LoadedPlugins = oldkrdPluginData + }() + + err := LoadMockPlugins(utils.LoadedPlugins) + if err != nil { + t.Fatalf("LoadMockPlugins returned an error (%s)", err) + } + + k8 := kubernetesClient{ + clientSet: &kubernetes.Clientset{}, + } + + t.Run("Successfully delete resources", func(t *testing.T) { + data := map[string][]string{ + "Deployment": []string{"../../mock_files/mock_yamls/deployment.yaml"}, + "Service": []string{"../../mock_files/mock_yamls/service.yaml"}, + } + + _, err := k8.createResources(data, "testnamespace") + if err != nil { + t.Fatalf("TestCreateResources returned an error (%s)", err) + } + }) +} + +func TestDeleteResources(t *testing.T) { + oldkrdPluginData := utils.LoadedPlugins + + defer func() { + utils.LoadedPlugins = oldkrdPluginData + }() + + err := LoadMockPlugins(utils.LoadedPlugins) + if err != nil { + t.Fatalf("LoadMockPlugins returned an error (%s)", err) + } + + k8 := kubernetesClient{ + clientSet: &kubernetes.Clientset{}, + } + + t.Run("Successfully delete resources", func(t *testing.T) { + data := map[string][]string{ + "Deployment": []string{"deployment-1", "deployment-2"}, + "Service": []string{"service-1", "service-2"}, + } + + err := k8.deleteResources(data, "test") + if err != nil { + t.Fatalf("TestCreateVNF returned an error (%s)", err) + } + }) +} diff --git a/src/k8splugin/internal/app/instance.go b/src/k8splugin/internal/app/instance.go new file mode 100644 index 00000000..a5b35fef --- /dev/null +++ b/src/k8splugin/internal/app/instance.go @@ -0,0 +1,206 @@ +/* + * Copyright 2018 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 app + +import ( + "encoding/base64" + "encoding/json" + "math/rand" + "os" + + "k8splugin/internal/db" + "k8splugin/internal/rb" + + pkgerrors "github.com/pkg/errors" +) + +// InstanceRequest contains the parameters needed for instantiation +// of profiles +type InstanceRequest struct { + RBName string `json:"rb-name"` + RBVersion string `json:"rb-version"` + ProfileName string `json:"profile-name"` + CloudRegion string `json:"cloud-region"` + Labels map[string]string `json:"labels"` +} + +// InstanceResponse contains the response from instantiation +type InstanceResponse struct { + ID string `json:"id"` + RBName string `json:"rb-name"` + RBVersion string `json:"rb-version"` + ProfileName string `json:"profile-name"` + CloudRegion string `json:"cloud-region"` + Namespace string `json:"namespace"` + Resources map[string][]string `json:"resources"` +} + +// InstanceManager is an interface exposes the instantiation functionality +type InstanceManager interface { + Create(i InstanceRequest) (InstanceResponse, error) + Get(id string) (InstanceResponse, error) + Delete(id string) error +} + +// InstanceKey is used as the primary key in the db +type InstanceKey struct { + ID string `json:"id"` +} + +// We will use json marshalling to convert to string to +// preserve the underlying structure. +func (dk InstanceKey) String() string { + out, err := json.Marshal(dk) + if err != nil { + return "" + } + + return string(out) +} + +// InstanceClient implements the InstanceManager interface +// It will also be used to maintain some localized state +type InstanceClient struct { + storeName string + tagInst string +} + +// Using 6 bytes of randomness to generate an 8 character string +func generateInstanceID() string { + b := make([]byte, 6) + rand.Read(b) + return base64.URLEncoding.EncodeToString(b) +} + +// NewInstanceClient returns an instance of the InstanceClient +// which implements the InstanceManager +func NewInstanceClient() *InstanceClient { + return &InstanceClient{ + storeName: "rbdef", + tagInst: "instance", + } +} + +// Create an entry for the resource bundle profile in the database +func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) { + + // Name is required + if i.RBName == "" || i.RBVersion == "" || i.ProfileName == "" || i.CloudRegion == "" { + return InstanceResponse{}, + pkgerrors.New("RBName, RBversion, ProfileName, CloudRegion are required to create a new instance") + } + + //Check if profile exists + profile, err := rb.NewProfileClient().Get(i.RBName, i.RBVersion, i.ProfileName) + if err != nil { + return InstanceResponse{}, pkgerrors.New("Unable to find Profile to create instance") + } + + overrideValues := []string{} + + //Execute the kubernetes create command + resMap, err := rb.NewProfileClient().Resolve(i.RBName, i.RBVersion, i.ProfileName, overrideValues) + if err != nil { + return InstanceResponse{}, pkgerrors.Wrap(err, "Error resolving helm charts") + } + + k8sClient := kubernetesClient{} + err = k8sClient.init(os.Getenv("KUBE_CONFIG_DIR") + "/" + i.CloudRegion) + if err != nil { + return InstanceResponse{}, pkgerrors.Wrap(err, "Getting CloudRegion Information") + } + + createdResources, err := k8sClient.createResources(resMap, profile.Namespace) + if err != nil { + return InstanceResponse{}, pkgerrors.Wrap(err, "Create Kubernetes Resources") + } + + id := generateInstanceID() + + //Compose the return response + resp := InstanceResponse{ + ID: id, + RBName: i.RBName, + RBVersion: i.RBVersion, + ProfileName: i.ProfileName, + CloudRegion: i.CloudRegion, + Namespace: profile.Namespace, + Resources: createdResources, + } + + key := InstanceKey{ + ID: id, + } + err = db.DBconn.Create(v.storeName, key, v.tagInst, resp) + if err != nil { + return InstanceResponse{}, pkgerrors.Wrap(err, "Creating Instance DB Entry") + } + + return resp, nil +} + +// Get returns the instance for corresponding ID +func (v *InstanceClient) Get(id string) (InstanceResponse, error) { + key := InstanceKey{ + ID: id, + } + value, err := db.DBconn.Read(v.storeName, key, v.tagInst) + if err != nil { + return InstanceResponse{}, pkgerrors.Wrap(err, "Get Instance") + } + + //value is a byte array + if value != nil { + resp := InstanceResponse{} + err = db.DBconn.Unmarshal(value, &resp) + if err != nil { + return InstanceResponse{}, pkgerrors.Wrap(err, "Unmarshaling Instance Value") + } + return resp, nil + } + + return InstanceResponse{}, pkgerrors.New("Error getting Instance") +} + +// Delete the Instance from database +func (v *InstanceClient) Delete(id string) error { + inst, err := v.Get(id) + if err != nil { + return pkgerrors.Wrap(err, "Error getting Instance") + } + + k8sClient := kubernetesClient{} + err = k8sClient.init(os.Getenv("KUBE_CONFIG_DIR") + "/" + inst.CloudRegion) + if err != nil { + return pkgerrors.Wrap(err, "Getting CloudRegion Information") + } + + err = k8sClient.deleteResources(inst.Resources, inst.Namespace) + if err != nil { + return pkgerrors.Wrap(err, "Deleting Instance Resources") + } + + key := InstanceKey{ + ID: id, + } + err = db.DBconn.Delete(v.storeName, key, v.tagInst) + if err != nil { + return pkgerrors.Wrap(err, "Delete Instance") + } + + return nil +} diff --git a/src/k8splugin/internal/app/vnfhelper_test.go b/src/k8splugin/internal/app/instance_test.go index 7587e5d6..480569c5 100644 --- a/src/k8splugin/internal/app/vnfhelper_test.go +++ b/src/k8splugin/internal/app/instance_test.go @@ -14,73 +14,27 @@ limitations under the License. package app import ( - "io/ioutil" "log" "os" - "plugin" + "reflect" "testing" - pkgerrors "github.com/pkg/errors" - yaml "gopkg.in/yaml.v2" - "k8s.io/client-go/kubernetes" - utils "k8splugin/internal" "k8splugin/internal/db" "k8splugin/internal/rb" ) -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) { +func TestInstanceCreate(t *testing.T) { oldkrdPluginData := utils.LoadedPlugins - oldReadMetadataFile := ReadMetadataFile - defer func() { utils.LoadedPlugins = oldkrdPluginData - ReadMetadataFile = oldReadMetadataFile }() - - err := LoadMockPlugins(&utils.LoadedPlugins) + err := LoadMockPlugins(utils.LoadedPlugins) if err != nil { - t.Fatalf("TestCreateVNF returned an error (%s)", err) + t.Fatalf("LoadMockPlugins 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) { + t.Run("Successfully create Instance", func(t *testing.T) { db.DBconn = &db.MockDB{ Items: map[string]map[string][]byte{ rb.ProfileKey{RBName: "test-rbdef", RBVersion: "v1", @@ -190,60 +144,180 @@ func TestCreateVNF(t *testing.T) { }, }, } - externaluuid, data, err := CreateVNF("uuid", "cloudregion1", - rb.Profile{ - RBName: "test-rbdef", - RBVersion: "v1", - ProfileName: "profile1", - ReleaseName: "testprofilereleasename", - Namespace: "testnamespace", - KubernetesVersion: "1.12.3", - }, &kubeclient) + + ic := NewInstanceClient() + input := InstanceRequest{ + RBName: "test-rbdef", + RBVersion: "v1", + ProfileName: "profile1", + CloudRegion: "mock_config", + } + + err := os.Setenv("KUBE_CONFIG_DIR", "../../mock_files/mock_configs") if err != nil { - t.Fatalf("TestCreateVNF returned an error (%s)", err) + t.Fatalf("TestInstanceCreate returned an error (%s)", err) } - log.Println(externaluuid) + ir, err := ic.Create(input) + if err != nil { + t.Fatalf("TestInstanceCreate returned an error (%s)", err) + } + + log.Println(ir) - if data == nil { - t.Fatalf("TestCreateVNF returned empty data (%s)", data) + if len(ir.Resources) == 0 { + t.Fatalf("TestInstanceCreate returned empty data (%s)", ir) } }) } -func TestDeleteVNF(t *testing.T) { +func TestInstanceGet(t *testing.T) { oldkrdPluginData := utils.LoadedPlugins defer func() { utils.LoadedPlugins = oldkrdPluginData }() - err := LoadMockPlugins(&utils.LoadedPlugins) + err := LoadMockPlugins(utils.LoadedPlugins) if err != nil { - t.Fatalf("TestCreateVNF returned an error (%s)", err) + t.Fatalf("LoadMockPlugins 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"}, + t.Run("Successfully Get Instance", func(t *testing.T) { + db.DBconn = &db.MockDB{ + Items: map[string]map[string][]byte{ + InstanceKey{ID: "HaKpys8e"}.String(): { + "instance": []byte( + "{\"profile-name\":\"profile1\"," + + "\"id\":\"HaKpys8e\"," + + "\"namespace\":\"testnamespace\"," + + "\"rb-name\":\"test-rbdef\"," + + "\"rb-version\":\"v1\"," + + "\"cloud-region\":\"region1\"," + + "\"resources\": {" + + "\"deployment\": [\"test-deployment\"]," + + "\"service\": [\"test-service\"]" + + "}}"), + }, + }, } - err := DestroyVNF(data, "test", &kubeclient) + expected := InstanceResponse{ + ID: "HaKpys8e", + RBName: "test-rbdef", + RBVersion: "v1", + ProfileName: "profile1", + CloudRegion: "region1", + Namespace: "testnamespace", + Resources: map[string][]string{ + "deployment": []string{"test-deployment"}, + "service": []string{"test-service"}, + }, + } + ic := NewInstanceClient() + id := "HaKpys8e" + data, err := ic.Get(id) if err != nil { - t.Fatalf("TestCreateVNF returned an error (%s)", err) + t.Fatalf("TestInstanceDelete returned an error (%s)", err) + } + if !reflect.DeepEqual(expected, data) { + t.Fatalf("TestInstanceGet returned:\n result=%v\n expected=%v", + data, expected) + } + }) + + t.Run("Get non-existing Instance", func(t *testing.T) { + db.DBconn = &db.MockDB{ + Items: map[string]map[string][]byte{ + InstanceKey{ID: "HaKpys8e"}.String(): { + "instance": []byte( + "{\"profile-name\":\"profile1\"," + + "\"id\":\"HaKpys8e\"," + + "\"namespace\":\"testnamespace\"," + + "\"rb-name\":\"test-rbdef\"," + + "\"rb-version\":\"v1\"," + + "\"cloud-region\":\"mock_config\"," + + "\"resources\": {" + + "\"deployment\": [\"deployment-1\",\"deployment-2\"]," + + "\"service\": [\"service-1\",\"service-2\"]" + + "}}"), + }, + }, + } + + ic := NewInstanceClient() + id := "non-existing" + _, err := ic.Get(id) + if err == nil { + t.Fatal("Expected error, got pass", 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") +func TestInstanceDelete(t *testing.T) { + oldkrdPluginData := utils.LoadedPlugins + + defer func() { + utils.LoadedPlugins = oldkrdPluginData + }() + + err := LoadMockPlugins(utils.LoadedPlugins) + if err != nil { + t.Fatalf("TestInstanceDelete returned an error (%s)", err) + } + + t.Run("Successfully delete Instance", func(t *testing.T) { + db.DBconn = &db.MockDB{ + Items: map[string]map[string][]byte{ + InstanceKey{ID: "HaKpys8e"}.String(): { + "instance": []byte( + "{\"profile-name\":\"profile1\"," + + "\"id\":\"HaKpys8e\"," + + "\"namespace\":\"testnamespace\"," + + "\"rb-name\":\"test-rbdef\"," + + "\"rb-version\":\"v1\"," + + "\"cloud-region\":\"mock_config\"," + + "\"resources\": {" + + "\"deployment\": [\"deployment-1\",\"deployment-2\"]," + + "\"service\": [\"service-1\",\"service-2\"]" + + "}}"), + }, + }, + } + + ic := NewInstanceClient() + id := "HaKpys8e" + err := ic.Delete(id) if err != nil { - t.Fatalf("TestReadMetadataFile returned an error (%s)", err) + t.Fatalf("TestInstanceDelete returned an error (%s)", err) + } + }) + + t.Run("Delete non-existing Instance", func(t *testing.T) { + db.DBconn = &db.MockDB{ + Items: map[string]map[string][]byte{ + InstanceKey{ID: "HaKpys8e"}.String(): { + "instance": []byte( + "{\"profile-name\":\"profile1\"," + + "\"id\":\"HaKpys8e\"," + + "\"namespace\":\"testnamespace\"," + + "\"rb-name\":\"test-rbdef\"," + + "\"rb-version\":\"v1\"," + + "\"cloud-region\":\"mock_config\"," + + "\"resources\": {" + + "\"deployment\": [\"deployment-1\",\"deployment-2\"]," + + "\"service\": [\"service-1\",\"service-2\"]" + + "}}"), + }, + }, + } + + ic := NewInstanceClient() + id := "non-existing" + err := ic.Delete(id) + if err == nil { + t.Fatal("Expected error, got pass", err) } }) } diff --git a/src/k8splugin/internal/app/vnfhelper.go b/src/k8splugin/internal/app/vnfhelper.go deleted file mode 100644 index c5783d69..00000000 --- a/src/k8splugin/internal/app/vnfhelper.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -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" - "strings" - - "k8s.io/client-go/kubernetes" - - pkgerrors "github.com/pkg/errors" - yaml "gopkg.in/yaml.v2" - - utils "k8splugin/internal" - "k8splugin/internal/rb" -) - -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, _ := symGetNamespaceFunc.(func(string, string, kubernetes.Interface) (string, error))( - namespace, namespace, kubeclient) - - 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, profile rb.Profile, kubeclient *kubernetes.Clientset) (string, map[string][]string, error) { - - overrideValues := []string{} - //Make sure that the namespace exists before trying to create any resources - if err := ensuresNamespace(profile.Namespace, kubeclient); err != nil { - return "", nil, pkgerrors.Wrap(err, "Error while ensuring namespace: "+profile.Namespace) - } - externalVNFID := generateExternalVNFID() - internalVNFID := cloudRegionID + "-" + profile.Namespace + "-" + externalVNFID - - metaMap, err := rb.NewProfileClient().Resolve(profile.RBName, profile.RBVersion, profile.ProfileName, overrideValues) - if err != nil { - return "", nil, pkgerrors.Wrap(err, "Error resolving helm charts") - } - - resourceYAMLNameMap := make(map[string][]string) - // Iterates over the resources defined in the metadata map to create kubernetes resources - log.Printf("%d resource(s) type(s) to be processed", len(metaMap)) - for res, filePaths := range metaMap { - //Convert resource to lower case as the map index is lowercase - resource := strings.ToLower(res) - log.Println("Processing items of " + string(resource) + " resource") - var resourcesCreated []string - for _, filepath := range filePaths { - - if _, err := os.Stat(filepath); os.IsNotExist(err) { - return "", nil, pkgerrors.New("File " + filepath + "does not exists") - } - log.Println("Processing file: " + filepath) - - //Populate the namespace from profile instead of instance body - genericKubeData := &utils.ResourceData{ - YamlFilePath: filepath, - Namespace: profile.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 -} |