Update regexp for helm command
[ric-plt/appmgr.git] / cmd / appmgr / helm.go
index 453ff29..5a5c28a 100755 (executable)
@@ -24,12 +24,10 @@ import (
        "errors"
        "fmt"
        "github.com/spf13/viper"
-       "gopkg.in/yaml.v2"
        "io/ioutil"
        "log"
        "os"
        "os/exec"
-       "path"
        "regexp"
        "strconv"
        "strings"
@@ -39,40 +37,44 @@ import (
 var execCommand = exec.Command
 
 func Exec(args string) (out []byte, err error) {
-       cmd := execCommand("/bin/sh", "-c", strings.Join([]string{"helm", args}, " "))
-
-       if !strings.HasSuffix(os.Args[0], ".test") {
-               out, err = cmd.CombinedOutput()
-               if err != nil {
-                       mdclog(MdclogErr, formatLog("Command failed", args, err.Error()))
-               }
-               return out, err
-       }
+       cmd := execCommand("/bin/sh", "-c", args)
 
        var stdout bytes.Buffer
        var stderr bytes.Buffer
        cmd.Stdout = &stdout
        cmd.Stderr = &stderr
 
-       log.Printf("Running command: %v", cmd)
-       for i := 0; i < 3; i++ {
+       log.Println("Running command: ", cmd)
+       for i := 0; i < viper.GetInt("helm.retry"); i++ {
                err = cmd.Run()
                if err != nil {
-                       mdclog(MdclogErr, formatLog("Command failed, retrying", args, err.Error()+stderr.String()))
-                       time.Sleep(time.Duration(5) * time.Second)
+                       Logger.Error("Command '%s' failed with error: %v, retrying", args, err.Error()+stderr.String())
+                       time.Sleep(time.Duration(2) * time.Second)
                        continue
                }
                break
        }
 
        if err == nil && !strings.HasSuffix(os.Args[0], ".test") {
-               mdclog(MdclogDebug, formatLog("command success", stdout.String(), ""))
+               Logger.Info("command success: %s", stdout.String())
                return stdout.Bytes(), nil
        }
 
        return stdout.Bytes(), errors.New(stderr.String())
 }
 
+var HelmExec = func(args string) (out []byte, err error) {
+       return Exec(strings.Join([]string{"helm", args}, " "))
+}
+
+var KubectlExec = func(args string) (out []byte, err error) {
+       return Exec(strings.Join([]string{"kubectl", args}, " "))
+}
+
+func (h *Helm) SetCM(cm ConfigMapper) {
+       h.cm = cm
+}
+
 func (h *Helm) Initialize() {
        if h.initDone == true {
                return
@@ -80,45 +82,44 @@ func (h *Helm) Initialize() {
 
        for {
                if _, err := h.Init(); err == nil {
-                       mdclog(MdclogDebug, formatLog("Helm init done successfully!", "", ""))
+                       Logger.Info("Helm init done successfully!")
                        break
                }
-               mdclog(MdclogErr, formatLog("helm init failed, retyring ...", "", ""))
+               Logger.Error("helm init failed, retyring ...")
                time.Sleep(time.Duration(10) * time.Second)
        }
 
        for {
                if _, err := h.AddRepo(); err == nil {
-                       mdclog(MdclogDebug, formatLog("Helm repo added successfully", "", ""))
+                       Logger.Info("Helm repo added successfully")
                        break
                }
-               mdclog(MdclogErr, formatLog("Helm repo addition failed, retyring ...", "", ""))
+               Logger.Error("Helm repo addition failed, retyring ...")
                time.Sleep(time.Duration(10) * time.Second)
        }
+
        h.initDone = true
 }
 
 func (h *Helm) Run(args string) (out []byte, err error) {
-       return Exec(args)
+       return HelmExec(args)
 }
 
 // API functions
 func (h *Helm) Init() (out []byte, err error) {
-
        // Add Tiller address as environment variable
        if err := addTillerEnv(); err != nil {
                return out, err
        }
 
-       return Exec(strings.Join([]string{"init -c"}, ""))
+       return HelmExec(strings.Join([]string{"init -c"}, ""))
 }
 
 func (h *Helm) AddRepo() (out []byte, err error) {
-
        // Get helm repo user name and password from files mounted by secret object
        credFile, err := ioutil.ReadFile(viper.GetString("helm.helm-username-file"))
        if err != nil {
-               mdclog(MdclogErr, formatLog("helm_repo_username ReadFile failed", "", err.Error()))
+               Logger.Error("helm_repo_username ReadFile failed: %v", err.Error())
                return
        }
 
@@ -126,7 +127,7 @@ func (h *Helm) AddRepo() (out []byte, err error) {
 
        credFile, err = ioutil.ReadFile(viper.GetString("helm.helm-password-file"))
        if err != nil {
-               mdclog(MdclogErr, formatLog("helm_repo_password ReadFile failed", "", err.Error()))
+               Logger.Error("helm_repo_password ReadFile failed: %v", err.Error())
                return
        }
 
@@ -138,31 +139,48 @@ func (h *Helm) AddRepo() (out []byte, err error) {
        // Get helm repo address from values.yaml
        repo := viper.GetString("helm.repo")
 
-       return Exec(strings.Join([]string{"repo add ", rname, " ", repo, username, pwd}, ""))
+       return HelmExec(strings.Join([]string{"repo add ", rname, " ", repo, username, pwd}, ""))
 }
 
-func (h *Helm) Install(name string) (xapp Xapp, err error) {
+func (h *Helm) Install(m XappDeploy) (xapp Xapp, err error) {
        out, err := h.Run(strings.Join([]string{"repo update "}, ""))
        if err != nil {
                return
        }
 
-       rname := viper.GetString("helm.repo-name")
+       var cm interface{}
+       m.Namespace = h.cm.GetNamespace(m.Namespace)
+
+       if err = h.cm.GetConfigMap(m, &cm); err != nil {
+               out, err = h.Run(getInstallArgs(m, false))
+               if err != nil {
+                       return
+               }
+               return h.ParseStatus(m.Name, string(out))
+       }
 
-       ns := getNamespaceArgs()
-       out, err = h.Run(strings.Join([]string{"install ", rname, "/", name, " --name ", name, ns}, ""))
+       // ConfigMap exists, try to override
+       out, err = h.Run(getInstallArgs(m, true))
+       if err == nil {
+               return h.ParseStatus(m.Name, string(out))
+       }
+
+       cm, cmErr := h.cm.PurgeConfigMap(m)
+       out, err = h.Run(getInstallArgs(m, false))
        if err != nil {
                return
        }
 
-       return h.ParseStatus(name, string(out))
+       if cmErr == nil {
+               cmErr = h.cm.RestoreConfigMap(m, cm)
+       }
+       return h.ParseStatus(m.Name, string(out))
 }
 
 func (h *Helm) Status(name string) (xapp Xapp, err error) {
-
        out, err := h.Run(strings.Join([]string{"status ", name}, ""))
        if err != nil {
-               mdclog(MdclogErr, formatLog("Getting xapps status", "", err.Error()))
+               Logger.Error("Getting xapps status: %v", err.Error())
                return
        }
 
@@ -172,7 +190,7 @@ func (h *Helm) Status(name string) (xapp Xapp, err error) {
 func (h *Helm) StatusAll() (xapps []Xapp, err error) {
        xappNameList, err := h.List()
        if err != nil {
-               mdclog(MdclogErr, formatLog("Helm list failed", "", err.Error()))
+               Logger.Error("Helm list failed: %v", err.Error())
                return
        }
 
@@ -180,21 +198,24 @@ func (h *Helm) StatusAll() (xapps []Xapp, err error) {
 }
 
 func (h *Helm) List() (names []string, err error) {
-
-       ns := getNamespaceArgs()
-       out, err := h.Run(strings.Join([]string{"list --all --output yaml ", ns}, ""))
+       ns := h.cm.GetNamespace("")
+       out, err := h.Run(strings.Join([]string{"list --all --output yaml --namespace=", ns}, ""))
        if err != nil {
-               mdclog(MdclogErr, formatLog("Listing deployed xapps failed", "", err.Error()))
+               Logger.Error("Listing deployed xapps failed: %v", err.Error())
                return
        }
 
        return h.GetNames(string(out))
 }
 
+func (h *Helm) SearchAll() (names []string) {
+       return h.cm.GetNamesFromHelmRepo()
+}
+
 func (h *Helm) Delete(name string) (xapp Xapp, err error) {
        xapp, err = h.Status(name)
        if err != nil {
-               mdclog(MdclogErr, formatLog("Fetching xapp status failed", "", err.Error()))
+               Logger.Error("Fetching xapp status failed: %v", err.Error())
                return
        }
 
@@ -214,45 +235,9 @@ func (h *Helm) Fetch(name, tarDir string) error {
 }
 
 // Helper functions
-func (h *Helm) GetMessages(name string) (msgs MessageTypes, err error) {
-       tarDir := viper.GetString("xapp.tarDir")
-       if tarDir == "" {
-               tarDir = "/tmp"
-       }
-
-       if h.Fetch(name, tarDir); err != nil {
-               mdclog(MdclogWarn, formatLog("Fetch chart failed", "", err.Error()))
-               return
-       }
-
-       return h.ParseMessages(name, tarDir, viper.GetString("xapp.msg_type_file"))
-
-}
-
-func (h *Helm) ParseMessages(name string, chartDir, msgFile string) (msgs MessageTypes, err error) {
-       yamlFile, err := ioutil.ReadFile(path.Join(chartDir, name, msgFile))
-       if err != nil {
-               mdclog(MdclogWarn, formatLog("ReadFile failed", "", err.Error()))
-               return
-       }
-
-       err = yaml.Unmarshal(yamlFile, &msgs)
-       if err != nil {
-               mdclog(MdclogWarn, formatLog("Unmarshal failed", "", err.Error()))
-               return
-       }
-
-       if err = os.RemoveAll(path.Join(chartDir, name)); err != nil {
-               mdclog(MdclogWarn, formatLog("RemoveAll failed", "", err.Error()))
-       }
-
-       return
-}
-
 func (h *Helm) GetVersion(name string) (version string) {
-
-       ns := getNamespaceArgs()
-       out, err := h.Run(strings.Join([]string{"list --output yaml ", name, ns}, ""))
+       ns := h.cm.GetNamespace("")
+       out, err := h.Run(strings.Join([]string{"list --output yaml --namespace=", ns, " ", name}, ""))
        if err != nil {
                return
        }
@@ -308,13 +293,13 @@ func (h *Helm) FillInstanceData(name string, out string, xapp *Xapp, msgs Messag
        ip, port := h.GetAddress(out)
 
        var tmp string
-       r := regexp.MustCompile(`(?s)\/Pod.*?\/Service`)
+       r := regexp.MustCompile(`.*(?s)(Running|Pending|Succeeded|Failed|Unknown).*?\r?\n\r?\n`)
        result := r.FindStringSubmatch(string(out))
        if result == nil {
                return
        }
 
-       re := regexp.MustCompile(name + "-(\\d+).*")
+       re := regexp.MustCompile(name + "-(\\w+-\\w+).*")
        resources := re.FindAllStringSubmatch(string(result[0]), -1)
        if resources != nil {
                for _, v := range resources {
@@ -331,21 +316,11 @@ func (h *Helm) FillInstanceData(name string, out string, xapp *Xapp, msgs Messag
 }
 
 func (h *Helm) ParseStatus(name string, out string) (xapp Xapp, err error) {
-
        xapp.Name = name
        xapp.Version = h.GetVersion(name)
        xapp.Status = h.GetState(out)
 
-       types, err := h.GetMessages(name)
-       if err != nil {
-               // xAPP can still be deployed if the msg_type file is missing.
-               mdclog(MdclogWarn, formatLog("method GetMessages Failed....", "", err.Error()))
-
-               //Set err back to nil, so it does not cause issues in called functions.
-               err = nil
-       }
-
-       h.FillInstanceData(name, out, &xapp, types)
+       h.FillInstanceData(name, out, &xapp, h.cm.GetMessages(name))
 
        return
 }
@@ -354,6 +329,11 @@ func (h *Helm) parseAllStatus(names []string) (xapps []Xapp, err error) {
        xapps = []Xapp{}
 
        for _, name := range names {
+               err := h.cm.ReadSchema(name, &XAppConfig{})
+               if err != nil {
+                       continue
+               }
+
                x, err := h.Status(name)
                if err == nil {
                        xapps = append(xapps, x)
@@ -364,26 +344,36 @@ func (h *Helm) parseAllStatus(names []string) (xapps []Xapp, err error) {
 }
 
 func addTillerEnv() (err error) {
-
        service := viper.GetString("helm.tiller-service")
        namespace := viper.GetString("helm.tiller-namespace")
        port := viper.GetString("helm.tiller-port")
 
        if err = os.Setenv("HELM_HOST", service+"."+namespace+":"+port); err != nil {
-               mdclog(MdclogErr, formatLog("Tiller Env Setting Failed", "", err.Error()))
+               Logger.Error("Tiller Env Setting Failed: %v", err.Error())
        }
 
        return err
 }
 
-func getNamespaceArgs() string {
-       ns := viper.GetString("xapp.namespace")
-       if ns == "" {
-               ns = "ricxapp"
+func getInstallArgs(x XappDeploy, cmOverride bool) (args string) {
+       args = args + " --namespace=" + x.Namespace
+
+       if x.ImageRepo != "" {
+               args = args + " --set global.repository=" + x.ImageRepo
+       }
+
+       if x.ServiceName != "" {
+               args = args + " --set ricapp.service.name=" + x.ServiceName
+       }
+
+       if x.Hostname != "" {
+               args = args + " --set ricapp.hostname=" + x.Hostname
+       }
+
+       if cmOverride == true {
+               args = args + " --set ricapp.appconfig.override=" + x.Name + "-appconfig"
        }
-       return " --namespace=" + ns
-}
 
-func formatLog(text string, args string, err string) string {
-       return fmt.Sprintf("Helm: %s: args=%s err=%s\n", text, args, err)
+       rname := viper.GetString("helm.repo-name")
+       return fmt.Sprintf("install %s/%s --name=%s %s", rname, x.Name, x.Name, args)
 }