Improve configure API to support k8s resource updations 50/110550/5
authorvamshi.nemalikonda <vn00480215@techmahindra.com>
Fri, 24 Jul 2020 12:05:51 +0000 (12:05 +0000)
committerRitu Sood <Ritu.Sood@intel.com>
Wed, 26 Aug 2020 15:59:47 +0000 (15:59 +0000)
Code fore review. Issue-ID: MULTICLOUD-1124

Change-Id: I6bb9786660f6760c15008132f1c254e7f9b39561
Signed-off-by: vamshi.nemalikonda <vn00480215@techmahindra.com>
Improve configure API to support k8s resource updations

create resource enabled. Issue-ID: MULTICLOUD-1124

Change-Id: I6bb9786660f6760c15008132f1c254e7f9b39561
Signed-off-by: vamshi.nemalikonda <vn00480215@techmahindra.com>
Improve configure API to support k8s resource updations

code improvements. Issue-ID: MULTICLOUD-1124

Change-Id: I6bb9786660f6760c15008132f1c254e7f9b39561
Signed-off-by: vamshi.nemalikonda <vn00480215@techmahindra.com>
Improve configure API to support k8s resource updations

fixing unit test failures. Issue-ID: MULTICLOUD-1124

Change-Id: I6bb9786660f6760c15008132f1c254e7f9b39561
Signed-off-by: vamshi.nemalikonda <vn00480215@techmahindra.com>
src/k8splugin/go.mod
src/k8splugin/internal/app/client.go
src/k8splugin/internal/app/config_backend.go
src/k8splugin/internal/plugin/helpers.go
src/k8splugin/mock_files/mock_plugins/mockplugin.go
src/k8splugin/plugins/generic/plugin.go
src/k8splugin/plugins/namespace/plugin.go
src/k8splugin/plugins/service/plugin.go

index f924828..d26de6e 100644 (file)
@@ -108,3 +108,5 @@ replace (
        k8s.io/client-go => k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
        k8s.io/cloud-provider => k8s.io/cloud-provider v0.0.0-20190409023720-1bc0c81fa51d
 )
+
+go 1.13
index d3e5081..ed60644 100644 (file)
@@ -181,6 +181,43 @@ func (k *KubernetesClient) createKind(resTempl helm.KubernetesResourceTemplate,
        }, nil
 }
 
+func (k *KubernetesClient) updateKind(resTempl helm.KubernetesResourceTemplate,
+        namespace string) (helm.KubernetesResource, error) {
+
+        if _, err := os.Stat(resTempl.FilePath); os.IsNotExist(err) {
+                return helm.KubernetesResource{}, pkgerrors.New("File " + resTempl.FilePath + "does not exists")
+        }
+
+        log.Info("Processing Kubernetes Resource", log.Fields{
+                "filepath": resTempl.FilePath,
+        })
+
+        pluginImpl, err := plugin.GetPluginByKind(resTempl.GVK.Kind)
+        if err != nil {
+                return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error loading plugin")
+        }
+
+        updatedResourceName, err := pluginImpl.Update(resTempl.FilePath, namespace, k)
+        if err != nil {
+                log.Error("Error Updating Resource", log.Fields{
+                        "error":    err,
+                        "gvk":      resTempl.GVK,
+                        "filepath": resTempl.FilePath,
+                })
+                return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error in plugin "+resTempl.GVK.Kind+" plugin")
+        }
+
+        log.Info("Updated Kubernetes Resource", log.Fields{
+                "resource": updatedResourceName,
+                "gvk":      resTempl.GVK,
+        })
+
+        return helm.KubernetesResource{
+                GVK:  resTempl.GVK,
+                Name: updatedResourceName,
+        }, nil
+}
+
 func (k *KubernetesClient) createResources(sortedTemplates []helm.KubernetesResourceTemplate,
        namespace string) ([]helm.KubernetesResource, error) {
 
@@ -201,6 +238,26 @@ func (k *KubernetesClient) createResources(sortedTemplates []helm.KubernetesReso
        return createdResources, nil
 }
 
+func (k *KubernetesClient) updateResources(sortedTemplates []helm.KubernetesResourceTemplate,
+        namespace string) ([]helm.KubernetesResource, error) {
+
+        err := k.ensureNamespace(namespace)
+        if err != nil {
+                return nil, pkgerrors.Wrap(err, "Creating Namespace")
+        }
+
+        var updatedResources []helm.KubernetesResource
+        for _, resTempl := range sortedTemplates {
+                resUpdated, err := k.updateKind(resTempl, namespace)
+                if err != nil {
+                        return nil, pkgerrors.Wrapf(err, "Error updating kind: %+v", resTempl.GVK)
+                }
+                updatedResources = append(updatedResources, resUpdated)
+        }
+
+        return updatedResources, nil
+}
+
 func (k *KubernetesClient) deleteKind(resource helm.KubernetesResource, namespace string) error {
        log.Warn("Deleting Resource", log.Fields{
                "gvk":      resource.GVK,
index 6bc145e..4cbe1da 100644 (file)
@@ -360,10 +360,17 @@ func scheduleResources(c chan configResourceList) {
                                        //Move onto the next cloud region
                                        continue
                                }
+                               //assuming - the resource is not exist already
                                data.createdResources, err = k8sClient.createResources(data.resourceTemplates, inst.Namespace)
+                                errCreate := err
                                if err != nil {
-                                       log.Printf("Error Creating resources: %s", err.Error())
-                                       continue
+                                       // assuming - the err represent the resource is already exist, so going for update
+                                       data.createdResources, err = k8sClient.updateResources(data.resourceTemplates, inst.Namespace)
+                                       if err != nil {
+                                                log.Printf("Error Creating resources: %s", errCreate.Error())
+                                               log.Printf("Error Updating resources: %s", err.Error())
+                                               continue
+                                       }
                                }
                        }
                        //TODO: Needs to add code to call Kubectl create
index ad785ab..19ff03a 100644 (file)
@@ -68,6 +68,9 @@ type Reference interface {
 
        //Delete a kubernetes resource described in the provided namespace
        Delete(resource helm.KubernetesResource, namespace string, client KubernetesConnector) error
+
+        //Update kubernetes resource based on the groupVersionKind and resourceName provided in resource
+        Update(yamlFilePath string, namespace string, client KubernetesConnector) (string, error)
 }
 
 // GetPluginByKind returns a plugin by the kind name
index 0b5b851..48133c3 100644 (file)
@@ -54,3 +54,10 @@ func (p mockPlugin) Delete(resource helm.KubernetesResource, namespace string, c
 func (p mockPlugin) Get(resource helm.KubernetesResource, namespace string, client plugin.KubernetesConnector) (string, error) {
        return resource.Name, nil
 }
+
+// Update existing resources
+func (p mockPlugin) Update(yamlFilePath string, namespace string, client plugin.KubernetesConnector) (string, error) {
+
+        return "", nil
+}
+
index 0711466..aa50309 100644 (file)
@@ -91,6 +91,62 @@ func (g genericPlugin) Create(yamlFilePath string, namespace string, client plug
        return createdObj.GetName(), nil
 }
 
+// Update deployment object in a specific Kubernetes cluster
+func (g genericPlugin) Update(yamlFilePath string, namespace string, client plugin.KubernetesConnector) (string, error) {
+        if namespace == "" {
+                namespace = "default"
+        }
+
+        //Decode the yaml file to create a runtime.Object
+        unstruct := &unstructured.Unstructured{}
+        //Ignore the returned obj as we expect the data in unstruct
+        _, err := utils.DecodeYAML(yamlFilePath, unstruct)
+        if err != nil {
+                return "", pkgerrors.Wrap(err, "Decode deployment object error")
+        }
+
+        dynClient := client.GetDynamicClient()
+        mapper := client.GetMapper()
+
+        gvk := unstruct.GroupVersionKind()
+        mapping, err := mapper.RESTMapping(schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, gvk.Version)
+        if err != nil {
+                return "", pkgerrors.Wrap(err, "Mapping kind to resource error")
+        }
+
+        //Add the tracking label to all resources created here
+        labels := unstruct.GetLabels()
+        //Check if labels exist for this object
+        if labels == nil {
+                labels = map[string]string{}
+        }
+        labels[config.GetConfiguration().KubernetesLabelName] = client.GetInstanceID()
+        unstruct.SetLabels(labels)
+
+        // This checks if the resource we are creating has a podSpec in it
+        // Eg: Deployment, StatefulSet, Job etc..
+        // If a PodSpec is found, the label will be added to it too.
+        plugin.TagPodsIfPresent(unstruct, client.GetInstanceID())
+
+        gvr := mapping.Resource
+        var updatedObj *unstructured.Unstructured
+
+        switch mapping.Scope.Name() {
+        case meta.RESTScopeNameNamespace:
+                updatedObj, err = dynClient.Resource(gvr).Namespace(namespace).Update(unstruct, metav1.UpdateOptions{})
+        case meta.RESTScopeNameRoot:
+                updatedObj, err = dynClient.Resource(gvr).Update(unstruct, metav1.UpdateOptions{})
+        default:
+                return "", pkgerrors.New("Got an unknown RESTSCopeName for mapping: " + gvk.String())
+        }
+
+        if err != nil {
+                return "", pkgerrors.Wrap(err, "Update object error")
+        }
+
+        return updatedObj.GetName(), nil
+}
+
 // Get an existing resource hosted in a specific Kubernetes cluster
 func (g genericPlugin) Get(resource helm.KubernetesResource,
        namespace string, client plugin.KubernetesConnector) (string, error) {
index feb2aa6..cfc395b 100644 (file)
@@ -107,3 +107,8 @@ func (p namespacePlugin) List(gvk schema.GroupVersionKind, namespace string, cli
 
        return result, nil
 }
+
+func (p namespacePlugin) Update(yamlFilePath string, namespace string, client plugin.KubernetesConnector) (string, error) {
+
+   return "", nil
+}
index 4c1f37b..2819176 100644 (file)
@@ -137,3 +137,9 @@ func (p servicePlugin) Get(resource helm.KubernetesResource, namespace string, c
 
        return service.Name, nil
 }
+
+func (p servicePlugin) Update(yamlFilePath string, namespace string, client plugin.KubernetesConnector) (string, error) {
+
+        return "", nil
+
+}