ConfigAPI and Query API improvements 54/127254/1
authorLukasz Rajewski <lukasz.rajewski@orange.com>
Thu, 3 Feb 2022 18:18:07 +0000 (19:18 +0100)
committerLukasz Rajewski <lukasz.rajewski@orange.com>
Wed, 23 Feb 2022 13:27:01 +0000 (14:27 +0100)
- Config Template create from the definition content
- Missing CRUD Config handlers added
- Improved Rollback and Config delete
- Query API name filtering improved

Issue-ID: MULTICLOUD-1437
Signed-off-by: Lukasz Rajewski <lukasz.rajewski@orange.com>
Change-Id: Iec8ec6d03746085f294d9318a252f1ae45d3b9c8

src/k8splugin/api/api.go
src/k8splugin/api/confighandler.go
src/k8splugin/api/instancehandler.go
src/k8splugin/api/queryhandler.go
src/k8splugin/internal/app/config.go
src/k8splugin/internal/app/config_backend.go
src/k8splugin/internal/app/config_test.go
src/k8splugin/internal/app/query.go
src/k8splugin/internal/db/etcd.go
src/k8splugin/internal/db/etcd_testing.go
src/k8splugin/internal/rb/config_template.go

index a3e53dc..4e84de7 100644 (file)
@@ -143,6 +143,10 @@ func NewRouter(defClient rb.DefinitionManager,
        instRouter.HandleFunc("/instance/{instID}/config/{cfgname}", configHandler.deleteAllHandler).Methods("DELETE")
        instRouter.HandleFunc("/instance/{instID}/config/{cfgname}/delete", configHandler.deleteHandler).Methods("POST")
        instRouter.HandleFunc("/instance/{instID}/config/{cfgname}/rollback", configHandler.rollbackHandler).Methods("POST")
+       instRouter.HandleFunc("/instance/{instID}/config/{cfgname}/tag", configHandler.tagListHandler).Methods("GET")
+       instRouter.HandleFunc("/instance/{instID}/config/{cfgname}/tag/{tagname}", configHandler.getTagHandler).Methods("GET")
+       instRouter.HandleFunc("/instance/{instID}/config/{cfgname}/version", configHandler.versionListHandler).Methods("GET")
+       instRouter.HandleFunc("/instance/{instID}/config/{cfgname}/version/{cfgversion}", configHandler.getVersionHandler).Methods("GET")
        instRouter.HandleFunc("/instance/{instID}/config/{cfgname}/tagit", configHandler.tagitHandler).Methods("POST")
 
        // Instance Healthcheck API
index a4f0813..e398806 100644 (file)
@@ -74,7 +74,7 @@ func (h rbConfigHandler) createHandler(w http.ResponseWriter, r *http.Request) {
 }
 
 // getHandler handles GET operations on a particular config
-// Returns a app.Definition
+// Returns a config
 func (h rbConfigHandler) getHandler(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
        instanceID := vars["instID"]
@@ -95,8 +95,76 @@ func (h rbConfigHandler) getHandler(w http.ResponseWriter, r *http.Request) {
        }
 }
 
-// listHandler handles GET operations for all configs of instance
+// getVersionHandler handles GET operations on a particular config
+// Returns a config
+func (h rbConfigHandler) getVersionHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       instanceID := vars["instID"]
+       cfgName := vars["cfgname"]
+       cfgVersion := vars["cfgversion"]
+
+       ret, err := h.client.GetVersion(instanceID, cfgName, cfgVersion)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusOK)
+       err = json.NewEncoder(w).Encode(ret)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
+
+// getTagHandler handles GET operations on a particular config
+// Returns a config
+func (h rbConfigHandler) getTagHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       instanceID := vars["instID"]
+       cfgName := vars["cfgname"]
+       tagName := vars["tagname"]
+
+       ret, err := h.client.GetTag(instanceID, cfgName, tagName)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusOK)
+       err = json.NewEncoder(w).Encode(ret)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
+
+// getTagListHandler handles GET tag list operations on a particular config
 // Returns a app.Definition
+func (h rbConfigHandler) tagListHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       instanceID := vars["instID"]
+       cfgName := vars["cfgname"]
+
+       ret, err := h.client.TagList(instanceID, cfgName)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusOK)
+       err = json.NewEncoder(w).Encode(ret)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
+
+// listHandler handles GET operations for all configs of instance
+// Returns a config list
 func (h rbConfigHandler) listHandler(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
        instanceID := vars["instID"]
@@ -116,13 +184,41 @@ func (h rbConfigHandler) listHandler(w http.ResponseWriter, r *http.Request) {
        }
 }
 
+// listHandler handles GET operations for all configs of instance
+// Returns a config list
+func (h rbConfigHandler) versionListHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       instanceID := vars["instID"]
+       cfgName := vars["cfgname"]
+
+       ret, err := h.client.VersionList(instanceID, cfgName)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusOK)
+       err = json.NewEncoder(w).Encode(ret)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
+
 // deleteHandler handles DELETE operations on a config
 func (h rbConfigHandler) deleteAllHandler(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
        instanceID := vars["instID"]
        cfgName := vars["cfgname"]
+       var err error
+
+       if r.URL.Query().Get("deleteConfigOnly") == "true" {
+               err = h.client.DeleteAll(instanceID, cfgName, true)
+       } else {
+               err = h.client.DeleteAll(instanceID, cfgName, false)
+       }
 
-       err := h.client.DeleteAll(instanceID, cfgName)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
@@ -205,13 +301,18 @@ func (h rbConfigHandler) rollbackHandler(w http.ResponseWriter, r *http.Request)
                http.Error(w, err.Error(), http.StatusUnprocessableEntity)
                return
        }
-       err = h.client.Rollback(instanceID, cfgName, p)
+       ret, err := h.client.Rollback(instanceID, cfgName, p, false)
        //err = h.client.Cleanup(instanceID)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }
-       w.WriteHeader(http.StatusNoContent)
+       w.WriteHeader(http.StatusOK)
+       err = json.NewEncoder(w).Encode(ret)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
 }
 
 // tagitHandler handles TAGIT operation
@@ -232,10 +333,15 @@ func (h rbConfigHandler) tagitHandler(w http.ResponseWriter, r *http.Request) {
                return
        }
 
-       err = h.client.Tagit(instanceID, cfgName, p)
+       ret, err := h.client.Tagit(instanceID, cfgName, p)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+       w.WriteHeader(http.StatusOK)
+       err = json.NewEncoder(w).Encode(ret)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }
-       w.WriteHeader(http.StatusNoContent)
 }
index 3fc514c..e07bfcb 100644 (file)
@@ -184,10 +184,10 @@ func (i instanceHandler) statusHandler(w http.ResponseWriter, r *http.Request) {
 func (i instanceHandler) queryHandler(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
        id := vars["instID"]
-       apiVersion := r.FormValue("ApiVersion")
-       kind := r.FormValue("Kind")
-       name := r.FormValue("Name")
-       labels := r.FormValue("Labels")
+       apiVersion := r.URL.Query().Get("ApiVersion")
+       kind := r.URL.Query().Get("Kind")
+       name := r.URL.Query().Get("Name")
+       labels := r.URL.Query().Get("Labels")
        if apiVersion == "" {
                http.Error(w, "Missing ApiVersion mandatory parameter", http.StatusBadRequest)
                return
index f5950cd..497767b 100644 (file)
@@ -33,12 +33,12 @@ type queryHandler struct {
 
 // queryHandler retrieves information about specified resources for instance
 func (i queryHandler) queryHandler(w http.ResponseWriter, r *http.Request) {
-       namespace := r.FormValue("Namespace")
-       cloudRegion := r.FormValue("CloudRegion")
-       apiVersion := r.FormValue("ApiVersion")
-       kind := r.FormValue("Kind")
-       name := r.FormValue("Name")
-       labels := r.FormValue("Labels")
+       namespace := r.URL.Query().Get("Namespace")
+       cloudRegion := r.URL.Query().Get("CloudRegion")
+       apiVersion := r.URL.Query().Get("ApiVersion")
+       kind := r.URL.Query().Get("Kind")
+       name := r.URL.Query().Get("Name")
+       labels := r.URL.Query().Get("Labels")
        if cloudRegion == "" {
                http.Error(w, "Missing CloudRegion mandatory parameter", http.StatusBadRequest)
                return
index 8952c16..fce163f 100644 (file)
@@ -22,17 +22,17 @@ import (
        "strconv"
        "strings"
 
-       "github.com/onap/multicloud-k8s/src/k8splugin/internal/helm"
-
        pkgerrors "github.com/pkg/errors"
 )
 
 // Config contains the parameters needed for configuration
 type Config struct {
-       ConfigName   string                 `json:"config-name"`
-       TemplateName string                 `json:"template-name"`
-       Description  string                 `json:"description"`
-       Values       map[string]interface{} `json:"values"`
+       ConfigName    string                 `json:"config-name"`
+       TemplateName  string                 `json:"template-name"`
+       Description   string                 `json:"description"`
+       Values        map[string]interface{} `json:"values"`
+       ConfigVersion uint                   `json:"config-version"`
+       ConfigTag     string                 `json:"config-tag"`
 }
 
 //ConfigResult output for Create, Update and delete
@@ -54,6 +54,12 @@ type ConfigRollback struct {
        } `json:"anyOf"`
 }
 
+//ConfigRollback input
+type ConfigTag struct {
+       ConfigVersion uint   `json:"config-version"`
+       ConfigTag     string `json:"config-tag"`
+}
+
 //ConfigTagit for Tagging configurations
 type ConfigTagit struct {
        TagName string `json:"tag-name"`
@@ -63,14 +69,18 @@ type ConfigTagit struct {
 type ConfigManager interface {
        Create(instanceID string, p Config) (ConfigResult, error)
        Get(instanceID, configName string) (Config, error)
+       GetVersion(instanceID, configName, version string) (Config, error)
+       GetTag(instanceID, configName, tagName string) (Config, error)
        List(instanceID string) ([]Config, error)
+       VersionList(instanceID, configName string) ([]Config, error)
        Help() map[string]string
        Update(instanceID, configName string, p Config) (ConfigResult, error)
        Delete(instanceID, configName string) (ConfigResult, error)
-       DeleteAll(instanceID, configName string) error
-       Rollback(instanceID string, configName string, p ConfigRollback) error
+       DeleteAll(instanceID, configName string, deleteConfigOnly bool) error
+       Rollback(instanceID string, configName string, p ConfigRollback, acceptRevert bool) (ConfigResult, error)
        Cleanup(instanceID string) error
-       Tagit(instanceID string, configName string, p ConfigTagit) error
+       Tagit(instanceID string, configName string, p ConfigTagit) (ConfigTag, error)
+       TagList(instanceID, configName string) ([]ConfigTag, error)
 }
 
 // ConfigClient implements the ConfigManager
@@ -99,7 +109,7 @@ func (v *ConfigClient) Help() map[string]string {
 func (v *ConfigClient) Create(instanceID string, p Config) (ConfigResult, error) {
        log.Printf("[Config Create] Instance %s", instanceID)
        // Check required fields
-       if p.ConfigName == "" || p.TemplateName == "" || len(p.Values) == 0 {
+       if p.ConfigName == "" || p.TemplateName == "" {
                return ConfigResult{}, pkgerrors.New("Incomplete Configuration Provided")
        }
        // Resolving rbName, Version, etc. not to break response
@@ -123,7 +133,7 @@ func (v *ConfigClient) Create(instanceID string, p Config) (ConfigResult, error)
        // Acquire per profile Mutex
        lock.Lock()
        defer lock.Unlock()
-       var appliedResources ([]helm.KubernetesResource)
+       var appliedResources ([]KubernetesConfigResource)
        appliedResources, err = applyConfig(instanceID, p, profileChannel, "POST", nil)
        if err != nil {
                return ConfigResult{}, pkgerrors.Wrap(err, "Apply Config failed")
@@ -160,10 +170,6 @@ func (v *ConfigClient) Create(instanceID string, p Config) (ConfigResult, error)
 // Update an entry for the config in the database
 func (v *ConfigClient) Update(instanceID, configName string, p Config) (ConfigResult, error) {
        log.Printf("[Config Update] Instance %s Config %s", instanceID, configName)
-       // Check required fields
-       if len(p.Values) == 0 {
-               return ConfigResult{}, pkgerrors.New("Incomplete Configuration Provided")
-       }
        // Resolving rbName, Version, etc. not to break response
        rbName, rbVersion, profileName, _, err := resolveModelFromInstance(instanceID)
        if err != nil {
@@ -182,7 +188,7 @@ func (v *ConfigClient) Update(instanceID, configName string, p Config) (ConfigRe
        // Acquire per profile Mutex
        lock.Lock()
        defer lock.Unlock()
-       var appliedResources ([]helm.KubernetesResource)
+       var appliedResources ([]KubernetesConfigResource)
        appliedResources, err = applyConfig(instanceID, p, profileChannel, "PUT", nil)
        if err != nil {
                return ConfigResult{}, pkgerrors.Wrap(err, "Apply Config  failed")
@@ -232,6 +238,59 @@ func (v *ConfigClient) Get(instanceID, configName string) (Config, error) {
        if err != nil {
                return Config{}, pkgerrors.Wrap(err, "Get Config DB Entry")
        }
+
+       cvs := ConfigVersionStore{
+               instanceID: instanceID,
+               configName: configName,
+       }
+       currentVersion, err := cvs.getCurrentVersion(configName)
+       if err != nil {
+               return Config{}, pkgerrors.Wrap(err, "Get Config Version Entry")
+       }
+       cfg.ConfigVersion = currentVersion
+       return cfg, nil
+}
+
+// Get version config entry in the database
+func (v *ConfigClient) GetTag(instanceID, configName, tagName string) (Config, error) {
+       cvs := ConfigVersionStore{
+               instanceID: instanceID,
+               configName: configName,
+       }
+       version, err := cvs.getTagVersion(configName, tagName)
+       if err != nil {
+               return Config{}, pkgerrors.Wrap(err, "Get Config Tag Version Entry")
+       }
+       return v.GetVersion(instanceID, configName, version)
+}
+
+// Get version config entry in the database
+func (v *ConfigClient) GetVersion(instanceID, configName, version string) (Config, error) {
+
+       // Acquire per profile Mutex
+       lock, _ := getProfileData(instanceID)
+       lock.Lock()
+       defer lock.Unlock()
+       // Read Config DB
+       cs := ConfigStore{
+               instanceID: instanceID,
+               configName: configName,
+       }
+       cfg, err := cs.getConfig()
+
+       cvs := ConfigVersionStore{
+               instanceID: instanceID,
+               configName: configName,
+       }
+       versionInt, err := strconv.ParseUint(version, 0, 32)
+       if err != nil {
+               return Config{}, pkgerrors.Wrap(err, "Parsint version string")
+       }
+       _, _, _, _, err = cvs.getConfigVersion(configName, uint(versionInt))
+       if err != nil {
+               return Config{}, pkgerrors.Wrap(err, "Get Config Version Entry")
+       }
+       cfg.ConfigVersion = uint(versionInt)
        return cfg, nil
 }
 
@@ -247,14 +306,100 @@ func (v *ConfigClient) List(instanceID string) ([]Config, error) {
                instanceID: instanceID,
        }
        cfg, err := cs.getConfigList()
+       result := make([]Config, 0)
+       for _, config := range cfg {
+               cvs := ConfigVersionStore{
+                       instanceID: instanceID,
+                       configName: config.ConfigName,
+               }
+               currentVersion, err := cvs.getCurrentVersion(config.ConfigName)
+               if err != nil {
+                       return []Config{}, pkgerrors.Wrap(err, "Get Current Config Version ")
+               }
+               config.ConfigVersion = currentVersion
+               result = append(result, config)
+       }
        if err != nil {
                return []Config{}, pkgerrors.Wrap(err, "Get Config DB Entry")
        }
-       return cfg, nil
+       return result, nil
+}
+
+// Version List config entry in the database
+func (v *ConfigClient) VersionList(instanceID string, configName string) ([]Config, error) {
+
+       // Acquire per profile Mutex
+       lock, _ := getProfileData(instanceID)
+       lock.Lock()
+       defer lock.Unlock()
+
+       cvs := ConfigVersionStore{
+               instanceID: instanceID,
+               configName: configName,
+       }
+       currentVersion, err := cvs.getCurrentVersion(configName)
+       if err != nil {
+               return []Config{}, pkgerrors.Wrap(err, "Get Current Config Version ")
+       }
+       //Get all configurations
+       var i uint
+       cfgList := make([]Config, 0)
+       for i = 1; i <= currentVersion; i++ {
+               config, _, _, _, err := cvs.getConfigVersion(configName, i)
+               config.ConfigVersion = i
+               if err != nil {
+                       return []Config{}, pkgerrors.Wrap(err, "Get Config Version")
+               }
+               cfgList = append(cfgList, config)
+       }
+
+       return cfgList, nil
+}
+
+func (v *ConfigClient) TagList(instanceID, configName string) ([]ConfigTag, error) {
+
+       // Acquire per profile Mutex
+       lock, _ := getProfileData(instanceID)
+       lock.Lock()
+       defer lock.Unlock()
+       // Read Config DB
+       cs := ConfigStore{
+               instanceID: instanceID,
+               configName: configName,
+       }
+       _, err := cs.getConfig()
+       if err != nil {
+               return []ConfigTag{}, pkgerrors.Wrap(err, "Get Config DB Entry")
+       }
+       cvs := ConfigVersionStore{
+               instanceID: instanceID,
+               configName: configName,
+       }
+
+       tagList, err := cvs.getTagList(configName)
+       if err != nil {
+               return []ConfigTag{}, pkgerrors.Wrap(err, "Get Tag list")
+       }
+       result := make([]ConfigTag, 0)
+       for _, tag := range tagList {
+               tagData := ConfigTag{}
+               version, err := cvs.getTagVersion(configName, tag)
+               if err != nil {
+                       return []ConfigTag{}, pkgerrors.Wrap(err, "Get Tag version")
+               }
+               versionInt, err := strconv.ParseUint(version, 0, 32)
+               if err != nil {
+                       return []ConfigTag{}, pkgerrors.Wrap(err, "Parsint version string")
+               }
+               tagData.ConfigTag = tag
+               tagData.ConfigVersion = uint(versionInt)
+               result = append(result, tagData)
+       }
+       return result, nil
 }
 
 // Delete the Config from database
-func (v *ConfigClient) DeleteAll(instanceID, configName string) error {
+func (v *ConfigClient) DeleteAll(instanceID, configName string, deleteConfigOnly bool) error {
        log.Printf("[Config Delete All] Instance %s Config %s", instanceID, configName)
        // Check if Config exists
        cs := ConfigStore{
@@ -270,19 +415,13 @@ func (v *ConfigClient) DeleteAll(instanceID, configName string) error {
                instanceID: instanceID,
                configName: configName,
        }
-       currentVersion, err := cvs.getCurrentVersion(configName)
-       if err != nil {
-               return pkgerrors.Wrap(err, "Current version get failed")
-       }
-       _, _, action, _, err := cvs.getConfigVersion(configName, currentVersion)
-       if err != nil {
-               return pkgerrors.Wrap(err, "Config version get failed")
-       }
 
-       if action != "DELETE" {
-               _, err = v.Delete(instanceID, configName)
+       if !deleteConfigOnly {
+               var rollbackConfig = ConfigRollback{}
+               rollbackConfig.AnyOf.ConfigVersion = "0"
+               _, err = v.Rollback(instanceID, configName, rollbackConfig, true)
                if err != nil {
-                       return pkgerrors.Wrap(err, "Config  DELETE version failed")
+                       return pkgerrors.Wrap(err, "Rollback to base version")
                }
        }
        // Delete Config from DB
@@ -339,7 +478,7 @@ func (v *ConfigClient) Delete(instanceID, configName string) (ConfigResult, erro
        if err != nil {
                return ConfigResult{}, pkgerrors.Wrap(err, "Update Config DB Entry")
        }
-       version, err := cvs.createConfigVersion(p, configPrev, "DELETE", []helm.KubernetesResource{})
+       version, err := cvs.createConfigVersion(p, configPrev, "DELETE", []KubernetesConfigResource{})
        if err != nil {
                return ConfigResult{}, pkgerrors.Wrap(err, "Create Delete Config Version DB Entry")
        }
@@ -358,25 +497,28 @@ func (v *ConfigClient) Delete(instanceID, configName string) (ConfigResult, erro
 }
 
 // Rollback starts from current version and rollbacks to the version desired
-func (v *ConfigClient) Rollback(instanceID string, configName string, rback ConfigRollback) error {
+func (v *ConfigClient) Rollback(instanceID string, configName string, rback ConfigRollback, acceptRevert bool) (ConfigResult, error) {
        log.Printf("[Config Rollback] Instance %s Config %s", instanceID, configName)
        var reqVersion string
        var err error
-
+       rbName, rbVersion, profileName, _, err := resolveModelFromInstance(instanceID)
+       if err != nil {
+               return ConfigResult{}, pkgerrors.Wrap(err, "Retrieving model info")
+       }
        if rback.AnyOf.ConfigTag != "" {
                reqVersion, err = v.GetTagVersion(instanceID, configName, rback.AnyOf.ConfigTag)
                if err != nil {
-                       return pkgerrors.Wrap(err, "Rollback Invalid tag")
+                       return ConfigResult{}, pkgerrors.Wrap(err, "Rollback Invalid tag")
                }
        } else if rback.AnyOf.ConfigVersion != "" {
                reqVersion = rback.AnyOf.ConfigVersion
        } else {
-               return pkgerrors.Errorf("No valid Index for Rollback")
+               return ConfigResult{}, pkgerrors.Errorf("No valid Index for Rollback")
        }
 
        index, err := strconv.Atoi(reqVersion)
        if err != nil {
-               return pkgerrors.Wrap(err, "Rollback Invalid Index")
+               return ConfigResult{}, pkgerrors.Wrap(err, "Rollback Invalid Index")
        }
        rollbackIndex := uint(index)
 
@@ -391,62 +533,114 @@ func (v *ConfigClient) Rollback(instanceID string, configName string, rback Conf
        }
        currentVersion, err := cvs.getCurrentVersion(configName)
        if err != nil {
-               return pkgerrors.Wrap(err, "Rollback Get Current Config Version ")
+               return ConfigResult{}, pkgerrors.Wrap(err, "Rollback Get Current Config Version ")
+       }
+
+       if (rollbackIndex < 1 && !acceptRevert) || rollbackIndex >= currentVersion {
+               return ConfigResult{}, pkgerrors.Wrap(err, "Rollback Invalid Config Version")
        }
 
-       if rollbackIndex < 1 && rollbackIndex >= currentVersion {
-               return pkgerrors.Wrap(err, "Rollback Invalid Config Version")
+       if rollbackIndex < 1 && acceptRevert {
+               rollbackIndex = 0
        }
 
        //Rollback all the intermettinent configurations
        for i := currentVersion; i > rollbackIndex; i-- {
                configNew, configPrev, _, resources, err := cvs.getConfigVersion(configName, i)
                if err != nil {
-                       return pkgerrors.Wrap(err, "Rollback Get Config Version")
+                       return ConfigResult{}, pkgerrors.Wrap(err, "Rollback Get Config Version")
+               }
+               var prevAction string
+               if i == 1 {
+                       prevAction = "POST"
+                       configPrev.ConfigName = ""
+                       configPrev.TemplateName = ""
+                       configPrev.Values = make(map[string]interface{})
+               } else {
+                       _, _, prevAction, _, err = cvs.getConfigVersion(configName, i-1)
                }
-               _, _, prevAction, _, err := cvs.getConfigVersion(configName, i-1)
+               log.Printf("ROLLBACK to version: %d", i-1)
                if err != nil {
-                       return pkgerrors.Wrap(err, "Rollback Get Prev Config Version")
+                       return ConfigResult{}, pkgerrors.Wrap(err, "Rollback Get Prev Config Version")
                }
                cs := ConfigStore{
                        instanceID: instanceID,
                        configName: configNew.ConfigName,
                }
                if prevAction != "DELETE" {
+                       var resourcesToDelete = make([]KubernetesConfigResource, 0)
+                       for _, res := range resources {
+                               if res.Status == "CREATED" {
+                                       resourcesToDelete = append(resourcesToDelete, res)
+                               }
+                       }
+                       if len(resourcesToDelete) > 0 {
+                               _, err := applyConfig(instanceID, configPrev, profileChannel, "DELETE", resources)
+                               if err != nil {
+                                       return ConfigResult{}, pkgerrors.Wrap(err, "Apply Config  failed")
+                               }
+                       }
                        appliedResources, err := applyConfig(instanceID, configPrev, profileChannel, prevAction, nil)
                        if err != nil {
-                               return pkgerrors.Wrap(err, "Apply Config  failed")
+                               return ConfigResult{}, pkgerrors.Wrap(err, "Apply Config  failed")
                        }
                        log.Printf("%s result: %s", prevAction, appliedResources)
                        _, err = cs.updateConfig(configPrev)
                        if err != nil {
-                               return pkgerrors.Wrap(err, "Update Config DB Entry")
+                               return ConfigResult{}, pkgerrors.Wrap(err, "Update Config DB Entry")
                        }
                } else {
                        // POST is always preceeded by Config not existing
-                       _, err := applyConfig(instanceID, configNew, profileChannel, prevAction, resources)
+                       _, err := applyConfig(instanceID, configPrev, profileChannel, prevAction, resources)
                        if err != nil {
-                               return pkgerrors.Wrap(err, "Delete Config  failed")
+                               return ConfigResult{}, pkgerrors.Wrap(err, "Delete Config  failed")
                        }
                        log.Printf("DELETE resources: %s", resources)
                        _, err = cs.updateConfig(configPrev)
                        if err != nil {
-                               return pkgerrors.Wrap(err, "Update Config DB Entry")
+                               return ConfigResult{}, pkgerrors.Wrap(err, "Update Config DB Entry")
                        }
                }
        }
+       if rollbackIndex == 0 {
+               //this is used only for delete config and remianing configuration 1 will be removed there
+               rollbackIndex = 1
+       }
        for i := currentVersion; i > rollbackIndex; i-- {
                // Delete rolled back items
                err = cvs.deleteConfigVersion(configName)
                if err != nil {
-                       return pkgerrors.Wrap(err, "Delete Config Version ")
+                       return ConfigResult{}, pkgerrors.Wrap(err, "Delete Config Version ")
                }
        }
-       return nil
+       currentVersion, err = cvs.getCurrentVersion(configName)
+       if err != nil {
+               return ConfigResult{}, pkgerrors.Wrap(err, "Rollback Get Current Config Version ")
+       }
+       // Check if Config exists
+       cs := ConfigStore{
+               instanceID: instanceID,
+               configName: configName,
+       }
+       currentConfig, err := cs.getConfig()
+       if err != nil {
+               return ConfigResult{}, pkgerrors.Wrap(err, "Update Error - Config doesn't exist")
+       }
+       // Create Result structure
+       cfgRes := ConfigResult{
+               InstanceName:      instanceID,
+               DefinitionName:    rbName,
+               DefinitionVersion: rbVersion,
+               ProfileName:       profileName,
+               ConfigName:        configName,
+               TemplateName:      currentConfig.TemplateName,
+               ConfigVersion:     currentVersion,
+       }
+       return cfgRes, nil
 }
 
 // Tagit tags the current version with the tag provided
-func (v *ConfigClient) Tagit(instanceID string, configName string, tag ConfigTagit) error {
+func (v *ConfigClient) Tagit(instanceID string, configName string, tag ConfigTagit) (ConfigTag, error) {
        log.Printf("[Config Tag It] Instance %s Config %s", instanceID, configName)
        lock, _ := getProfileData(instanceID)
        // Acquire per profile Mutex
@@ -459,9 +653,17 @@ func (v *ConfigClient) Tagit(instanceID string, configName string, tag ConfigTag
        }
        err := cvs.tagCurrentVersion(configName, tag.TagName)
        if err != nil {
-               return pkgerrors.Wrap(err, "Tag of current version failed")
+               return ConfigTag{}, pkgerrors.Wrap(err, "Tag of current version failed")
        }
-       return nil
+       currentVersion, err := cvs.getCurrentVersion(configName)
+       if err != nil {
+               return ConfigTag{}, pkgerrors.Wrap(err, "Rollback Get Current Config Version ")
+       }
+
+       var tagResult = ConfigTag{}
+       tagResult.ConfigVersion = currentVersion
+       tagResult.ConfigTag = tag.TagName
+       return tagResult, nil
 }
 
 // GetTagVersion returns the version associated with the tag
@@ -489,7 +691,11 @@ func (v *ConfigClient) Cleanup(instanceID string) error {
        }
 
        for _, config := range configs {
-               err = v.DeleteAll(instanceID, config.ConfigName)
+               _, err = v.Delete(instanceID, config.ConfigName)
+               if err != nil {
+                       log.Printf("Config %s delete failed: %s", config.ConfigName, err.Error())
+               }
+               err = v.DeleteAll(instanceID, config.ConfigName, true)
                if err != nil {
                        log.Printf("Config %s delete failed: %s", config.ConfigName, err.Error())
                }
@@ -529,7 +735,7 @@ func (v *ConfigClient) ApplyAllConfig(instanceID string, configName string) erro
                if action != "DELETE" {
                        resources = nil
                }
-               var appliedResources ([]helm.KubernetesResource)
+               var appliedResources ([]KubernetesConfigResource)
                appliedResources, err = applyConfig(instanceID, configNew, profileChannel, action, resources)
                if err != nil {
                        return pkgerrors.Wrap(err, "Apply Config  failed")
index c365363..4dcbeb5 100644 (file)
@@ -38,10 +38,10 @@ import (
 
 //ConfigStore contains the values that will be stored in the database
 type configVersionDBContent struct {
-       ConfigNew  Config                    `json:"config-new"`
-       ConfigPrev Config                    `json:"config-prev"`
-       Action     string                    `json:"action"` // CRUD opration for this config
-       Resources  []helm.KubernetesResource `json:"resources"`
+       ConfigNew  Config                     `json:"config-new"`
+       ConfigPrev Config                     `json:"config-prev"`
+       Action     string                     `json:"action"` // CRUD opration for this config
+       Resources  []KubernetesConfigResource `json:"resources"`
 }
 
 //ConfigStore to Store the Config
@@ -56,10 +56,15 @@ type ConfigVersionStore struct {
        configName string
 }
 
+type KubernetesConfigResource struct {
+       Resource helm.KubernetesResource `json:"resource"`
+       Status   string                  `json:"status"`
+}
+
 type configResourceList struct {
        resourceTemplates []helm.KubernetesResourceTemplate
-       resources         []helm.KubernetesResource
-       updatedResources  chan []helm.KubernetesResource
+       resources         []KubernetesConfigResource
+       updatedResources  chan []KubernetesConfigResource
        profile           rb.Profile
        action            string
 }
@@ -181,7 +186,7 @@ func (c ConfigStore) getConfigList() ([]Config, error) {
                return []Config{}, pkgerrors.Wrap(err, "Retrieving model info")
        }
        cfgKey := constructKey(rbName, rbVersion, profileName, c.instanceID, tagConfig)
-       values, err := db.Etcd.GetAll(cfgKey)
+       values, err := db.Etcd.GetValues(cfgKey)
        if err != nil {
                return []Config{}, pkgerrors.Wrap(err, "Get Config DB List")
        }
@@ -194,6 +199,9 @@ func (c ConfigStore) getConfigList() ([]Config, error) {
                        if err != nil {
                                return []Config{}, pkgerrors.Wrap(err, "Unmarshaling Config Value")
                        }
+                       if cfg.ConfigName == "" {
+                               continue
+                       }
                        result = append(result, cfg)
                }
                return result, nil
@@ -256,7 +264,7 @@ func (c ConfigVersionStore) cleanupIstanceTags(configName string) error {
 }
 
 // Create a version for the configuration. If previous config provided that is also stored
-func (c ConfigVersionStore) createConfigVersion(configNew, configPrev Config, action string, resources []helm.KubernetesResource) (uint, error) {
+func (c ConfigVersionStore) createConfigVersion(configNew, configPrev Config, action string, resources []KubernetesConfigResource) (uint, error) {
 
        configName := ""
        if configNew.ConfigName != "" {
@@ -281,7 +289,7 @@ func (c ConfigVersionStore) createConfigVersion(configNew, configPrev Config, ac
        cs.Action = action
        cs.ConfigNew = configNew
        cs.ConfigPrev = configPrev
-       cs.Resources = resources //[]helm.KubernetesResource{}
+       cs.Resources = resources //[]KubernetesConfigResource{}
 
        configValue, err := db.Serialize(cs)
        if err != nil {
@@ -321,27 +329,27 @@ func (c ConfigVersionStore) deleteConfigVersion(configName string) error {
 
 // Read the specified version of the configuration and return its prev and current value.
 // Also returns the action for the config version
-func (c ConfigVersionStore) getConfigVersion(configName string, version uint) (Config, Config, string, []helm.KubernetesResource, error) {
+func (c ConfigVersionStore) getConfigVersion(configName string, version uint) (Config, Config, string, []KubernetesConfigResource, error) {
 
        rbName, rbVersion, profileName, _, err := resolveModelFromInstance(c.instanceID)
        if err != nil {
-               return Config{}, Config{}, "", []helm.KubernetesResource{}, pkgerrors.Wrap(err, "Retrieving model info")
+               return Config{}, Config{}, "", []KubernetesConfigResource{}, pkgerrors.Wrap(err, "Retrieving model info")
        }
        versionKey := constructKey(rbName, rbVersion, profileName, c.instanceID, tagVersion, configName, strconv.Itoa(int(version)))
        configBytes, err := db.Etcd.Get(versionKey)
        if err != nil {
-               return Config{}, Config{}, "", []helm.KubernetesResource{}, pkgerrors.Wrap(err, "Get Config Version ")
+               return Config{}, Config{}, "", []KubernetesConfigResource{}, pkgerrors.Wrap(err, "Get Config Version ")
        }
 
        if configBytes != nil {
                pr := configVersionDBContent{}
                err = db.DeSerialize(string(configBytes), &pr)
                if err != nil {
-                       return Config{}, Config{}, "", []helm.KubernetesResource{}, pkgerrors.Wrap(err, "DeSerialize Config Version")
+                       return Config{}, Config{}, "", []KubernetesConfigResource{}, pkgerrors.Wrap(err, "DeSerialize Config Version")
                }
                return pr.ConfigNew, pr.ConfigPrev, pr.Action, pr.Resources, nil
        }
-       return Config{}, Config{}, "", []helm.KubernetesResource{}, pkgerrors.Wrap(err, "Invalid data ")
+       return Config{}, Config{}, "", []KubernetesConfigResource{}, pkgerrors.Wrap(err, "Invalid data ")
 }
 
 // Get the counter for the version
@@ -419,6 +427,25 @@ func (c ConfigVersionStore) decrementVersion(configName string) error {
        return nil
 }
 
+// Get tag list
+func (c ConfigVersionStore) getTagList(configName string) ([]string, error) {
+       rbName, rbVersion, profileName, _, err := resolveModelFromInstance(c.instanceID)
+       if err != nil {
+               return []string{}, pkgerrors.Wrap(err, "Retrieving model info")
+       }
+       tagKey := constructKey(rbName, rbVersion, profileName, c.instanceID, tagName, configName)
+
+       tagKeyList, err := db.Etcd.GetKeys(tagKey)
+       if err != nil {
+               return []string{}, pkgerrors.Wrap(err, "Config DB Entry Not found")
+       }
+       result := make([]string, 0)
+       for _, tag := range tagKeyList {
+               result = append(result, tag[len(tagKey):len(tag)-1])
+       }
+       return result, nil
+}
+
 // Get tag version
 func (c ConfigVersionStore) getTagVersion(configName, tagNameValue string) (string, error) {
        rbName, rbVersion, profileName, _, err := resolveModelFromInstance(c.instanceID)
@@ -444,28 +471,44 @@ func (c ConfigVersionStore) tagCurrentVersion(configName, tagNameValue string) e
        if err != nil {
                return pkgerrors.Wrap(err, "Retrieving model info")
        }
+       currentConfig, _, _, _, err := c.getConfigVersion(configName, currentVersion)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Retrieving current configuration")
+       }
+
        tagKey := constructKey(rbName, rbVersion, profileName, c.instanceID, tagName, configName, tagNameValue)
 
        err = db.Etcd.Put(tagKey, strconv.Itoa(int(currentVersion)))
        if err != nil {
                return pkgerrors.Wrap(err, "TagIt store DB")
        }
+
+       currentConfig.ConfigTag = tagNameValue
+       configValue, err := db.Serialize(currentConfig)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Serialize Config Value")
+       }
+       cfgKey := constructKey(rbName, rbVersion, profileName, c.instanceID, tagConfig, configName)
+       err = db.Etcd.Put(cfgKey, configValue)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Config DB Entry")
+       }
        return nil
 }
 
 // Apply Config
-func applyConfig(instanceID string, p Config, pChannel chan configResourceList, action string, resources []helm.KubernetesResource) ([]helm.KubernetesResource, error) {
+func applyConfig(instanceID string, p Config, pChannel chan configResourceList, action string, resources []KubernetesConfigResource) ([]KubernetesConfigResource, error) {
 
        rbName, rbVersion, profileName, releaseName, err := resolveModelFromInstance(instanceID)
        if err != nil {
-               return []helm.KubernetesResource{}, pkgerrors.Wrap(err, "Retrieving model info")
+               return []KubernetesConfigResource{}, pkgerrors.Wrap(err, "Retrieving model info")
        }
        // Get Template and Resolve the template with values
        crl, err := resolve(rbName, rbVersion, profileName, instanceID, p, releaseName)
        if err != nil {
-               return []helm.KubernetesResource{}, pkgerrors.Wrap(err, "Resolve Config")
+               return []KubernetesConfigResource{}, pkgerrors.Wrap(err, "Resolve Config")
        }
-       var updatedResources (chan []helm.KubernetesResource) = make(chan []helm.KubernetesResource)
+       var updatedResources (chan []KubernetesConfigResource) = make(chan []KubernetesConfigResource)
        crl.action = action
        crl.resources = resources
        crl.updatedResources = updatedResources
@@ -477,7 +520,7 @@ func applyConfig(instanceID string, p Config, pChannel chan configResourceList,
        default:
        }
 
-       var resultResources []helm.KubernetesResource = <-updatedResources
+       var resultResources []KubernetesConfigResource = <-updatedResources
        return resultResources, nil
 }
 
@@ -492,14 +535,14 @@ func scheduleResources(c chan configResourceList) {
                resp, err := ic.Find(data.profile.RBName, data.profile.RBVersion, data.profile.ProfileName, nil)
                if (err != nil || len(resp) == 0) && data.action != "STOP" {
                        log.Println("Error finding a running instance. Retrying later...")
-                       data.updatedResources <- []helm.KubernetesResource{}
+                       data.updatedResources <- []KubernetesConfigResource{}
                        continue
                }
                breakThread := false
                switch {
-               case data.action == "POST":
-                       log.Printf("[scheduleResources]: POST %v %v", data.profile, data.resourceTemplates)
-                       var resources []helm.KubernetesResource
+               case data.action == "PUT" || data.action == "POST":
+                       log.Printf("[scheduleResources]: %v %v %v", data.action, data.profile, data.resourceTemplates)
+                       resources := []KubernetesConfigResource{}
                        for _, inst := range resp {
                                k8sClient := KubernetesClient{}
                                err = k8sClient.Init(inst.Request.CloudRegion, inst.ID)
@@ -508,36 +551,30 @@ func scheduleResources(c chan configResourceList) {
                                        //Move onto the next cloud region
                                        continue
                                }
-                               //assuming - the resource is not exist already
-                               resources, err = k8sClient.createResources(data.resourceTemplates, inst.Namespace)
-                               errCreate := err
-                               if err != nil {
-                                       // assuming - the err represent the resource is already exist, so going for update
-                                       resources, err = k8sClient.updateResources(data.resourceTemplates, inst.Namespace)
+                               for _, res := range data.resourceTemplates {
+                                       var resToCreateOrUpdate = []helm.KubernetesResourceTemplate{res}
+                                       resProceeded, err := k8sClient.createResources(resToCreateOrUpdate, inst.Namespace)
+                                       errCreate := err
+                                       var status string = ""
                                        if err != nil {
-                                               log.Printf("Error Creating resources: %s", errCreate.Error())
-                                               log.Printf("Error Updating resources: %s", err.Error())
-                                               continue
+                                               // assuming - the err represent the resource already exist, so going for update
+                                               resProceeded, err = k8sClient.updateResources(resToCreateOrUpdate, inst.Namespace)
+                                               if err != nil {
+                                                       log.Printf("Error Creating resources: %s", errCreate.Error())
+                                                       log.Printf("Error Updating resources: %s", err.Error())
+                                                       break
+                                               } else {
+                                                       status = "UPDATED"
+                                               }
+                                       } else {
+                                               status = "CREATED"
+                                       }
+                                       for _, resCreated := range resProceeded {
+                                               resource := KubernetesConfigResource{}
+                                               resource.Resource = resCreated
+                                               resource.Status = status
+                                               resources = append(resources, resource)
                                        }
-                               }
-                       }
-                       data.updatedResources <- resources
-               case data.action == "PUT":
-                       log.Printf("[scheduleResources]: PUT %v %v", data.profile, data.resourceTemplates)
-                       var resources []helm.KubernetesResource
-                       for _, inst := range resp {
-                               k8sClient := KubernetesClient{}
-                               err = k8sClient.Init(inst.Request.CloudRegion, inst.ID)
-                               if err != nil {
-                                       log.Printf("Getting CloudRegion Information: %s", err.Error())
-                                       //Move onto the next cloud region
-                                       continue
-                               }
-
-                               resources, err = k8sClient.updateResources(data.resourceTemplates, inst.Namespace)
-                               if err != nil {
-                                       log.Printf("Error Updating resources: %s", err.Error())
-                                       continue
                                }
                        }
                        data.updatedResources <- resources
@@ -551,13 +588,17 @@ func scheduleResources(c chan configResourceList) {
                                        //Move onto the next cloud region
                                        continue
                                }
-                               err = k8sClient.deleteResources(helm.GetReverseK8sResources(data.resources), inst.Namespace)
+                               var tmpResources []helm.KubernetesResource = []helm.KubernetesResource{}
+                               for _, res := range data.resources {
+                                       tmpResources = append(tmpResources, res.Resource)
+                               }
+                               err = k8sClient.deleteResources(helm.GetReverseK8sResources(tmpResources), inst.Namespace)
                                if err != nil {
                                        log.Printf("Error Deleting resources: %s", err.Error())
                                        continue
                                }
                        }
-                       data.updatedResources <- []helm.KubernetesResource{}
+                       data.updatedResources <- []KubernetesConfigResource{}
 
                case data.action == "STOP":
                        breakThread = true
@@ -581,17 +622,40 @@ var resolve = func(rbName, rbVersion, profileName, instanceId string, p Config,
                return configResourceList{}, pkgerrors.Wrap(err, "Reading Profile Data")
        }
 
-       t, err := rb.NewConfigTemplateClient().Get(rbName, rbVersion, p.TemplateName)
-       if err != nil {
-               return configResourceList{}, pkgerrors.Wrap(err, "Getting Template")
-       }
-       if t.ChartName == "" {
-               return configResourceList{}, pkgerrors.New("Invalid template no Chart.yaml file found")
+       var t rb.ConfigTemplate
+       if p.TemplateName == "" && p.ConfigName == "" {
+               //for rollback to base definition
+               t = rb.ConfigTemplate{}
+               t.HasContent = false
+       } else {
+               t, err = rb.NewConfigTemplateClient().Get(rbName, rbVersion, p.TemplateName)
+               if err != nil {
+                       return configResourceList{}, pkgerrors.Wrap(err, "Getting Template")
+               }
+               if t.ChartName == "" {
+                       return configResourceList{}, pkgerrors.New("Invalid template no Chart.yaml file found")
+               }
        }
 
-       def, err := rb.NewConfigTemplateClient().Download(rbName, rbVersion, p.TemplateName)
-       if err != nil {
-               return configResourceList{}, pkgerrors.Wrap(err, "Downloading Template")
+       var def []byte
+
+       if t.HasContent {
+               def, err = rb.NewConfigTemplateClient().Download(rbName, rbVersion, p.TemplateName)
+               if err != nil {
+                       return configResourceList{}, pkgerrors.Wrap(err, "Downloading Template")
+               }
+       } else {
+               log.Printf("Using Definition Template as a Configuration Template")
+               defClient := rb.NewDefinitionClient()
+               definition, err := defClient.Get(rbName, rbVersion)
+               if err != nil {
+                       return configResourceList{}, pkgerrors.Wrap(err, "Get RB Definition")
+               }
+               def, err = defClient.Download(rbName, rbVersion)
+               if err != nil {
+                       return configResourceList{}, pkgerrors.Wrap(err, "Downloading RB Definition Template")
+               }
+               t.ChartName = definition.ChartName
        }
 
        ic := NewInstanceClient()
@@ -625,8 +689,10 @@ var resolve = func(rbName, rbVersion, profileName, instanceId string, p Config,
                return configResourceList{}, pkgerrors.Wrap(err, "Processing values")
        }
 
-       for k, v := range p.Values {
-               rawValues[k] = v
+       if p.Values != nil {
+               for k, v := range p.Values {
+                       rawValues[k] = v
+               }
        }
        //Create a temp file in the system temp folder for values input
        b, err := json.Marshal(rawValues)
index 1aef665..3b673b8 100644 (file)
@@ -138,20 +138,23 @@ func TestRollbackConfig(t *testing.T) {
                        profileName: "testprofile1",
                        instanceID:  "testinstance1",
                        inp: Config{
-                               ConfigName:   "testconfig1",
-                               TemplateName: "testtemplate1",
+                               ConfigName:    "testconfig1",
+                               TemplateName:  "testtemplate1",
+                               ConfigVersion: 1,
                                Values: map[string]interface{}{
                                        "values": "{\"namespace\": \"kafka\", \"topic\": {\"name\":\"orders\", \"cluster\":\"my-cluster\", \"partitions\": 10,\"replicas\":   2, }}"},
                        },
                        inpUpdate1: Config{
-                               ConfigName:   "testconfig1",
-                               TemplateName: "testtemplate1",
+                               ConfigName:    "testconfig1",
+                               TemplateName:  "testtemplate1",
+                               ConfigVersion: 2,
                                Values: map[string]interface{}{
                                        "values": "{\"namespace\": \"kafka\", \"topic\": {\"name\":\"orders\", \"cluster\":\"my-cluster\", \"partitions\": 20,\"replicas\":   2, }}"},
                        },
                        inpUpdate2: Config{
-                               ConfigName:   "testconfig1",
-                               TemplateName: "testtemplate1",
+                               ConfigName:    "testconfig1",
+                               TemplateName:  "testtemplate1",
+                               ConfigVersion: 3,
                                Values: map[string]interface{}{
                                        "values": "{\"namespace\": \"kafka\", \"topic\": {\"name\":\"orders\", \"cluster\":\"my-cluster\", \"partitions\": 30,\"replicas\":   2, }}"},
                        },
@@ -293,7 +296,7 @@ func TestRollbackConfig(t *testing.T) {
                                }
                        }
                        testCase.rollbackConfig.AnyOf.ConfigVersion = "2"
-                       err = impl.Rollback(testCase.instanceID, testCase.inp.ConfigName, testCase.rollbackConfig)
+                       _, err = impl.Rollback(testCase.instanceID, testCase.inp.ConfigName, testCase.rollbackConfig, false)
                        if err != nil {
                                if testCase.expectedError == "" {
                                        t.Fatalf("Create returned an unexpected error %s", err)
index 251b14e..6a1b351 100644 (file)
@@ -19,9 +19,6 @@
 package app
 
 import (
-       "github.com/onap/multicloud-k8s/src/k8splugin/internal/helm"
-       "k8s.io/apimachinery/pkg/runtime/schema"
-
        pkgerrors "github.com/pkg/errors"
 )
 
@@ -64,7 +61,7 @@ func (v *QueryClient) Query(namespace, cloudRegion, apiVersion, kind, name, labe
        }
 
        var resourcesStatus []ResourceStatus
-       if labels != "" {
+       if name != "" {
                resList, err := k8sClient.queryResources(apiVersion, kind, labels, namespace)
                if err != nil {
                        return QueryStatus{}, pkgerrors.Wrap(err, "Querying Resources")
@@ -76,22 +73,11 @@ func (v *QueryClient) Query(namespace, cloudRegion, apiVersion, kind, name, labe
                        for _, res := range resList {
                                if res.Name == name {
                                        resourcesStatus = append(resourcesStatus, res)
-                                       break
                                }
                        }
                } else {
                        resourcesStatus = resList
                }
-       } else if name != "" {
-               resIdentifier := helm.KubernetesResource{
-                       Name: name,
-                       GVK:  schema.FromAPIVersionAndKind(apiVersion, kind),
-               }
-               res, err := k8sClient.GetResourceStatus(resIdentifier, namespace)
-               if err != nil {
-                       return QueryStatus{}, pkgerrors.Wrap(err, "Querying Resource")
-               }
-               resourcesStatus = []ResourceStatus{res}
        } else {
                resList, err := k8sClient.queryResources(apiVersion, kind, labels, namespace)
                if err != nil {
index e455cc1..5ce8135 100644 (file)
@@ -36,7 +36,8 @@ type EtcdConfig struct {
 // EtcdStore Interface needed for mocking
 type EtcdStore interface {
        Get(key string) ([]byte, error)
-       GetAll(key string) ([][]byte, error)
+       GetKeys(key string) ([]string, error)
+       GetValues(key string) ([][]byte, error)
        Put(key, value string) error
        Delete(key string) error
        DeletePrefix(keyPrefix string) error
@@ -124,8 +125,24 @@ func (e EtcdClient) Get(key string) ([]byte, error) {
        return getResp.Kvs[0].Value, nil
 }
 
-// GetAll sub values from Etcd DB
-func (e EtcdClient) GetAll(key string) ([][]byte, error) {
+// GetKeys sub values from Etcd DB
+func (e EtcdClient) GetKeys(key string) ([]string, error) {
+       if e.cli == nil {
+               return nil, pkgerrors.Errorf("Etcd Client not initialized")
+       }
+       getResp, err := e.cli.Get(context.Background(), key, clientv3.WithPrefix())
+       if err != nil {
+               return nil, pkgerrors.Errorf("Error getting etcd entry: %s", err.Error())
+       }
+       result := make([]string, 0)
+       for _, v := range getResp.Kvs {
+               result = append(result, string(v.Key))
+       }
+       return result, nil
+}
+
+// GetValues sub values from Etcd DB
+func (e EtcdClient) GetValues(key string) ([][]byte, error) {
        if e.cli == nil {
                return nil, pkgerrors.Errorf("Etcd Client not initialized")
        }
index 4b4dfe3..2f62d36 100644 (file)
@@ -41,7 +41,17 @@ func (c *MockEtcdClient) Get(key string) ([]byte, error) {
        return nil, pkgerrors.Errorf("Key doesn't exist")
 }
 
-func (c *MockEtcdClient) GetAll(key string) ([][]byte, error) {
+func (c *MockEtcdClient) GetKeys(key string) ([]string, error) {
+       result := make([]string, 0)
+       for kvKey := range c.Items {
+               if strings.HasPrefix(kvKey, key) {
+                       result = append(result, kvKey)
+               }
+       }
+       return result, nil
+}
+
+func (c *MockEtcdClient) GetValues(key string) ([][]byte, error) {
        result := make([][]byte, 0)
        for kvKey, kvValue := range c.Items {
                if strings.HasPrefix(kvKey, key) {
index 97fe0fb..06576e5 100644 (file)
@@ -36,14 +36,20 @@ import (
 type ConfigTemplate struct {
        TemplateName string `json:"template-name"`
        Description  string `json:"description"`
-       ChartName    string
+       ChartName    string `json:"chart-name"`
+       HasContent   bool   `json:"has-content"`
+}
+
+type ConfigTemplateList struct {
+       TemplateName string `json:"template-name"`
+       Description  string `json:"description"`
 }
 
 // ConfigTemplateManager is an interface exposes the resource bundle  ConfigTemplate functionality
 type ConfigTemplateManager interface {
        CreateOrUpdate(rbName, rbVersion string, p ConfigTemplate, update bool) error
        Get(rbName, rbVersion, templateName string) (ConfigTemplate, error)
-       List(rbName, rbVersion string) ([]ConfigTemplate, error)
+       List(rbName, rbVersion string) ([]ConfigTemplateList, error)
        Delete(rbName, rbVersion, templateName string) error
        Upload(rbName, rbVersion, templateName string, inp []byte) error
 }
@@ -94,7 +100,7 @@ func (v *ConfigTemplateClient) CreateOrUpdate(rbName, rbVersion string, p Config
        }
 
        //Check if  ConfigTemplate already exists
-       _, err := v.Get(rbName, rbVersion, p.TemplateName)
+       prev, err := v.Get(rbName, rbVersion, p.TemplateName)
        if err == nil && !update {
                return pkgerrors.New(" ConfigTemplate already exists for this Definition")
        }
@@ -103,7 +109,7 @@ func (v *ConfigTemplateClient) CreateOrUpdate(rbName, rbVersion string, p Config
        }
 
        //Check if provided resource bundle information is valid
-       _, err = NewDefinitionClient().Get(rbName, rbVersion)
+       rbDef, err := NewDefinitionClient().Get(rbName, rbVersion)
        if err != nil {
                return pkgerrors.Errorf("Invalid Resource Bundle ID provided: %s", err.Error())
        }
@@ -115,11 +121,15 @@ func (v *ConfigTemplateClient) CreateOrUpdate(rbName, rbVersion string, p Config
        }
 
        if update {
+               p.ChartName = prev.ChartName
+               p.HasContent = prev.HasContent
                err = db.DBconn.Update(v.storeName, key, v.tagMeta, p)
                if err != nil {
                        return pkgerrors.Wrap(err, "Updating  ConfigTemplate DB Entry")
                }
        } else {
+               p.ChartName = rbDef.ChartName
+               p.HasContent = false
                err = db.DBconn.Create(v.storeName, key, v.tagMeta, p)
                if err != nil {
                        return pkgerrors.Wrap(err, "Creating  ConfigTemplate DB Entry")
@@ -155,19 +165,19 @@ func (v *ConfigTemplateClient) Get(rbName, rbVersion, templateName string) (Conf
 }
 
 // List returns the Resource Bundle ConfigTemplate for corresponding ID
-func (v *ConfigTemplateClient) List(rbName, rbVersion string) ([]ConfigTemplate, error) {
+func (v *ConfigTemplateClient) List(rbName, rbVersion string) ([]ConfigTemplateList, error) {
 
        //Get all config templates
        dbres, err := db.DBconn.ReadAll(v.storeName, v.tagMeta)
        if err != nil || len(dbres) == 0 {
-               return []ConfigTemplate{}, pkgerrors.Wrap(err, "No Config Templates Found")
+               return []ConfigTemplateList{}, pkgerrors.Wrap(err, "No Config Templates Found")
        }
 
-       var results []ConfigTemplate
+       var results []ConfigTemplateList
        for key, value := range dbres {
                //value is a byte array
                if value != nil {
-                       tmp := ConfigTemplate{}
+                       tmp := ConfigTemplateList{}
                        err = db.DBconn.Unmarshal(value, &tmp)
                        if err != nil {
                                log.Printf("[ConfigTemplate] Error: %s Unmarshaling value for: %s", err.Error(), key)
@@ -267,6 +277,11 @@ func (v *ConfigTemplateClient) Upload(rbName, rbVersion, templateName string, in
        if err != nil {
                return pkgerrors.Errorf("Error uploading data to db %s", err.Error())
        }
+       t.HasContent = true
+       err = db.DBconn.Update(v.storeName, key, v.tagMeta, t)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Updating  ConfigTemplate DB Entry")
+       }
 
        return nil
 }