From 9c942a11c14836630ba528b75bdcb2790045b91f Mon Sep 17 00:00:00 2001 From: "vamshi.nemalikonda" Date: Fri, 24 Jul 2020 12:05:51 +0000 Subject: Improve configure API to support k8s resource updations Code fore review. Issue-ID: MULTICLOUD-1124 Change-Id: I6bb9786660f6760c15008132f1c254e7f9b39561 Signed-off-by: vamshi.nemalikonda Improve configure API to support k8s resource updations create resource enabled. Issue-ID: MULTICLOUD-1124 Change-Id: I6bb9786660f6760c15008132f1c254e7f9b39561 Signed-off-by: vamshi.nemalikonda Improve configure API to support k8s resource updations code improvements. Issue-ID: MULTICLOUD-1124 Change-Id: I6bb9786660f6760c15008132f1c254e7f9b39561 Signed-off-by: vamshi.nemalikonda Improve configure API to support k8s resource updations fixing unit test failures. Issue-ID: MULTICLOUD-1124 Change-Id: I6bb9786660f6760c15008132f1c254e7f9b39561 Signed-off-by: vamshi.nemalikonda --- src/k8splugin/go.mod | 2 + src/k8splugin/internal/app/client.go | 57 ++++++++++++++++++++++ src/k8splugin/internal/app/config_backend.go | 11 ++++- src/k8splugin/internal/plugin/helpers.go | 3 ++ .../mock_files/mock_plugins/mockplugin.go | 7 +++ src/k8splugin/plugins/generic/plugin.go | 56 +++++++++++++++++++++ src/k8splugin/plugins/namespace/plugin.go | 5 ++ src/k8splugin/plugins/service/plugin.go | 6 +++ 8 files changed, 145 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/k8splugin/go.mod b/src/k8splugin/go.mod index f924828d..d26de6ef 100644 --- a/src/k8splugin/go.mod +++ b/src/k8splugin/go.mod @@ -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 diff --git a/src/k8splugin/internal/app/client.go b/src/k8splugin/internal/app/client.go index d3e5081a..ed606444 100644 --- a/src/k8splugin/internal/app/client.go +++ b/src/k8splugin/internal/app/client.go @@ -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, diff --git a/src/k8splugin/internal/app/config_backend.go b/src/k8splugin/internal/app/config_backend.go index 6bc145ee..4cbe1da3 100644 --- a/src/k8splugin/internal/app/config_backend.go +++ b/src/k8splugin/internal/app/config_backend.go @@ -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 diff --git a/src/k8splugin/internal/plugin/helpers.go b/src/k8splugin/internal/plugin/helpers.go index ad785ab7..19ff03ab 100644 --- a/src/k8splugin/internal/plugin/helpers.go +++ b/src/k8splugin/internal/plugin/helpers.go @@ -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 diff --git a/src/k8splugin/mock_files/mock_plugins/mockplugin.go b/src/k8splugin/mock_files/mock_plugins/mockplugin.go index 0b5b851e..48133c3e 100644 --- a/src/k8splugin/mock_files/mock_plugins/mockplugin.go +++ b/src/k8splugin/mock_files/mock_plugins/mockplugin.go @@ -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 +} + diff --git a/src/k8splugin/plugins/generic/plugin.go b/src/k8splugin/plugins/generic/plugin.go index 0711466f..aa503097 100644 --- a/src/k8splugin/plugins/generic/plugin.go +++ b/src/k8splugin/plugins/generic/plugin.go @@ -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) { diff --git a/src/k8splugin/plugins/namespace/plugin.go b/src/k8splugin/plugins/namespace/plugin.go index feb2aa61..cfc395bb 100644 --- a/src/k8splugin/plugins/namespace/plugin.go +++ b/src/k8splugin/plugins/namespace/plugin.go @@ -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 +} diff --git a/src/k8splugin/plugins/service/plugin.go b/src/k8splugin/plugins/service/plugin.go index 4c1f37be..28191767 100644 --- a/src/k8splugin/plugins/service/plugin.go +++ b/src/k8splugin/plugins/service/plugin.go @@ -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 + +} -- cgit 1.2.3-korg