ConfigAPI and Query API improvements
[multicloud/k8s.git] / src / k8splugin / internal / app / config.go
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")