From: Alok Bhatt Date: Wed, 11 Nov 2020 06:52:26 +0000 (+0000) Subject: Enabling helm3 Support for appmgr. X-Git-Tag: 0.5.3~14 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=6ff31e2779146c816ead84113877372af09bfbcc;p=ric-plt%2Fappmgr.git Enabling helm3 Support for appmgr. Added env variable in Dockerfile & used it in application code. The helm2 will still be working Issue-Id: RIC-680 Signed-off-by: Alok Bhatt Change-Id: I28dfdfa3f5869f6ab17da4000cac62cc37546d51 --- diff --git a/Dockerfile b/Dockerfile index 130b79f..bace8a8 100755 --- a/Dockerfile +++ b/Dockerfile @@ -13,17 +13,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -#---------------------------------------------------------- +#----------------------------------------------------------- +ARG HELMVERSION=v2.12.3 FROM nexus3.o-ran-sc.org:10004/o-ran-sc/bldr-ubuntu18-c-go:9-u18.04 AS appmgr-build RUN apt-get update -y && apt-get install -y jq ENV PATH="/usr/local/go/bin:${PATH}" -ARG HELMVERSION=v2.12.3 +ARG HELMVERSION # Install helm -RUN wget -nv https://storage.googleapis.com/kubernetes-helm/helm-${HELMVERSION}-linux-amd64.tar.gz \ +RUN wget -nv https://get.helm.sh/helm-${HELMVERSION}-linux-amd64.tar.gz \ && tar -zxvf helm-${HELMVERSION}-linux-amd64.tar.gz \ && cp linux-amd64/helm /usr/local/bin/helm \ && rm -rf helm-${HELMVERSION}-linux-amd64.tar.gz \ @@ -84,6 +85,8 @@ COPY --from=appmgr-build /usr/local/bin/kubectl /usr/local/bin/kubectl RUN ldconfig +ARG HELMVERSION +ENV HELMVERSION=$HELMVERSION # # xApp Manager # diff --git a/pkg/cm/cm.go b/pkg/cm/cm.go index 6200180..0356b19 100755 --- a/pkg/cm/cm.go +++ b/pkg/cm/cm.go @@ -20,22 +20,22 @@ package cm import ( - "encoding/json" - "errors" - "fmt" - "github.com/spf13/viper" - "github.com/valyala/fastjson" - "github.com/xeipuuv/gojsonschema" - "io/ioutil" - "os" - "path" - "regexp" - "strconv" - "strings" - - "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr" - "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/models" - "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/util" + "encoding/json" + "errors" + "fmt" + "github.com/spf13/viper" + "github.com/valyala/fastjson" + "github.com/xeipuuv/gojsonschema" + "io/ioutil" + "os" + "path" + "regexp" + "strconv" + "strings" + + "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr" + "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/models" + "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/util" ) var kubeExec = util.KubectlExec @@ -43,292 +43,303 @@ var helmExec = util.HelmExec type CM struct{} +const HELM_VERSION_3 = "3" +const HELM_VERSION_2 = "2" +var EnvHelmVersion string = "" + + func NewCM() *CM { - return &CM{} + return &CM{} } func (cm *CM) UploadConfigAll() (configList models.AllXappConfig) { - return cm.UploadConfigElement("") + return cm.UploadConfigElement("") } func (cm *CM) UploadConfigElement(Element string) (configList models.AllXappConfig) { - namespace := cm.GetNamespace("") - for _, name := range cm.GetNamesFromHelmRepo() { - var activeConfig interface{} - xAppName := name - if err := cm.GetConfigmap(xAppName, namespace, &activeConfig); err != nil { - appmgr.Logger.Info("No active configMap found for '%s', ignoring ...", xAppName) - continue - } - - if Element != "" { - m := activeConfig.(map[string]interface{}) - if m[Element] == nil { - appmgr.Logger.Info("xApp '%s' doesn't have requested element '%s' in config", name, Element) - continue - } - activeConfig = m[Element] - } - - c := models.XAppConfig{ - Metadata: &models.ConfigMetadata{XappName: &xAppName, Namespace: &namespace}, - Config: activeConfig, - } - configList = append(configList, &c) - } - return + namespace := cm.GetNamespace("") + for _, name := range cm.GetNamesFromHelmRepo() { + var activeConfig interface{} + xAppName := name + if err := cm.GetConfigmap(xAppName, namespace, &activeConfig); err != nil { + appmgr.Logger.Info("No active configMap found for '%s', ignoring ...", xAppName) + continue + } + + if Element != "" { + m := activeConfig.(map[string]interface{}) + if m[Element] == nil { + appmgr.Logger.Info("xApp '%s' doesn't have requested element '%s' in config", name, Element) + continue + } + activeConfig = m[Element] + } + + c := models.XAppConfig{ + Metadata: &models.ConfigMetadata{XappName: &xAppName, Namespace: &namespace}, + Config: activeConfig, + } + configList = append(configList, &c) + } + return } func (cm *CM) GetConfigmap(name, namespace string, c *interface{}) (err error) { - cmJson, err := cm.ReadConfigmap(name, namespace) - if err != nil { - return err - } + cmJson, err := cm.ReadConfigmap(name, namespace) + if err != nil { + return err + } - return json.Unmarshal([]byte(cmJson), &c) + return json.Unmarshal([]byte(cmJson), &c) } func (cm *CM) ReadSchema(name string, desc *interface{}) (err error) { - if err = cm.FetchChart(name); err != nil { - return - } + if err = cm.FetchChart(name); err != nil { + return + } - tarDir := viper.GetString("xapp.tarDir") - err = cm.ReadFile(path.Join(tarDir, name, viper.GetString("xapp.schema")), desc) - if err != nil { - return - } + tarDir := viper.GetString("xapp.tarDir") + err = cm.ReadFile(path.Join(tarDir, name, viper.GetString("xapp.schema")), desc) + if err != nil { + return + } - if err = os.RemoveAll(path.Join(tarDir, name)); err != nil { - appmgr.Logger.Info("RemoveAll failed: %v", err) - } + if err = os.RemoveAll(path.Join(tarDir, name)); err != nil { + appmgr.Logger.Info("RemoveAll failed: %v", err) + } - return + return } func (cm *CM) UpdateConfigMap(r models.XAppConfig) (models.ConfigValidationErrors, error) { - fmt.Printf("Configmap update: xappName=%s namespace=%s config: %v\n", *r.Metadata.XappName, *r.Metadata.Namespace, r.Config) - if validationErrors, err := cm.Validate(r); err != nil { - return validationErrors, err - } - - cmContent, err := cm.BuildConfigMap(r) - if err != nil { - return nil, err - } - - if err := cm.GenerateJSONFile(cmContent); err != nil { - return nil, err - } - err = cm.ReplaceConfigMap(*r.Metadata.XappName, *r.Metadata.Namespace) - - return nil, err + fmt.Printf("Configmap update: xappName=%s namespace=%s config: %v\n", *r.Metadata.XappName, *r.Metadata.Namespace, r.Config) + if validationErrors, err := cm.Validate(r); err != nil { + return validationErrors, err + } + + cmContent, err := cm.BuildConfigMap(r) + if err != nil { + return nil, err + } + + if err := cm.GenerateJSONFile(cmContent); err != nil { + return nil, err + } + err = cm.ReplaceConfigMap(*r.Metadata.XappName, *r.Metadata.Namespace) + + return nil, err } func (cm *CM) BuildConfigMap(r models.XAppConfig) (string, error) { - configJson, err := json.Marshal(r.Config) - if err != nil { - appmgr.Logger.Info("Config marshalling failed: %v", err) - return "", err - } - - cmContent, err := cm.ReadConfigmap(*r.Metadata.XappName, *r.Metadata.Namespace) - if err != nil { - return "", err - } - - v, err := cm.ParseJson(cmContent) - if err == nil { - v.Set("controls", fastjson.MustParse(string(configJson))) - fmt.Println(v.String()) - return v.String(), nil - } - - return "", err + configJson, err := json.Marshal(r.Config) + if err != nil { + appmgr.Logger.Info("Config marshalling failed: %v", err) + return "", err + } + + cmContent, err := cm.ReadConfigmap(*r.Metadata.XappName, *r.Metadata.Namespace) + if err != nil { + return "", err + } + + v, err := cm.ParseJson(cmContent) + if err == nil { + v.Set("controls", fastjson.MustParse(string(configJson))) + fmt.Println(v.String()) + return v.String(), nil + } + + return "", err } func (cm *CM) ParseJson(dsContent string) (*fastjson.Value, error) { - var p fastjson.Parser - v, err := p.Parse(dsContent) - if err != nil { - appmgr.Logger.Info("fastjson.Parser failed: %v", err) - } - return v, err + var p fastjson.Parser + v, err := p.Parse(dsContent) + if err != nil { + appmgr.Logger.Info("fastjson.Parser failed: %v", err) + } + return v, err } func (cm *CM) GenerateJSONFile(jsonString string) error { - cmJson, err := json.RawMessage(jsonString).MarshalJSON() - if err != nil { - appmgr.Logger.Error("Config marshalling failed: %v", err) - return err - } - - err = ioutil.WriteFile(viper.GetString("xapp.tmpConfig"), cmJson, 0644) - if err != nil { - appmgr.Logger.Error("WriteFile failed: %v", err) - return err - } - - return nil + cmJson, err := json.RawMessage(jsonString).MarshalJSON() + if err != nil { + appmgr.Logger.Error("Config marshalling failed: %v", err) + return err + } + + err = ioutil.WriteFile(viper.GetString("xapp.tmpConfig"), cmJson, 0644) + if err != nil { + appmgr.Logger.Error("WriteFile failed: %v", err) + return err + } + + return nil } func (cm *CM) ReadFile(name string, data interface{}) (err error) { - f, err := ioutil.ReadFile(name) - if err != nil { - appmgr.Logger.Info("Reading '%s' file failed: %v", name, err) - return - } - - err = json.Unmarshal(f, &data) - if err != nil { - appmgr.Logger.Info("Unmarshalling '%s' file failed: %v", name, err) - return - } - - return + f, err := ioutil.ReadFile(name) + if err != nil { + appmgr.Logger.Info("Reading '%s' file failed: %v", name, err) + return + } + + err = json.Unmarshal(f, &data) + if err != nil { + appmgr.Logger.Info("Unmarshalling '%s' file failed: %v", name, err) + return + } + + return } func (cm *CM) ReadConfigmap(name string, ns string) (string, error) { - args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns)) - out, err := kubeExec(args) - return string(out), err + args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns)) + out, err := kubeExec(args) + return string(out), err } func (cm *CM) ReplaceConfigMap(name, ns string) error { - cmd := " create configmap -n %s %s --from-file=%s -o json --dry-run | kubectl replace -f -" - args := fmt.Sprintf(cmd, ns, cm.GetConfigMapName(name, ns), viper.GetString("xapp.tmpConfig")) - _, err := kubeExec(args) - return err + cmd := " create configmap -n %s %s --from-file=%s -o json --dry-run | kubectl replace -f -" + args := fmt.Sprintf(cmd, ns, cm.GetConfigMapName(name, ns), viper.GetString("xapp.tmpConfig")) + _, err := kubeExec(args) + return err } func (cm *CM) FetchChart(name string) (err error) { - tarDir := viper.GetString("xapp.tarDir") - repo := viper.GetString("helm.repo-name") - fetchArgs := fmt.Sprintf("--untar --untardir %s %s/%s", tarDir, repo, name) + tarDir := viper.GetString("xapp.tarDir") + repo := viper.GetString("helm.repo-name") + fetchArgs := fmt.Sprintf("--untar --untardir %s %s/%s", tarDir, repo, name) - _, err = helmExec(strings.Join([]string{"fetch ", fetchArgs}, "")) - return + _, err = helmExec(strings.Join([]string{"fetch ", fetchArgs}, "")) + return } func (cm *CM) GetRtmData(name string) (msgs appmgr.RtmData) { - appmgr.Logger.Info("Fetching RT data for xApp=%s", name) - - ns := cm.GetNamespace("") - args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns)) - out, err := kubeExec(args) - if err != nil { - return - } - - var p fastjson.Parser - v, err := p.Parse(string(out)) - if err != nil { - appmgr.Logger.Info("fastjson.Parser for '%s' failed: %v", name, err) - return - } - - if v.Exists("rmr") { - for _, m := range v.GetArray("rmr", "txMessages") { - msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`)) - } - - for _, m := range v.GetArray("rmr", "rxMessages") { - msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`)) - } - - for _, m := range v.GetArray("rmr", "policies") { - if val, err := strconv.Atoi(strings.Trim(m.String(), `"`)); err == nil { - msgs.Policies = append(msgs.Policies, int64(val)) - } - } - } else { - for _, p := range v.GetArray("messaging", "ports") { - appmgr.Logger.Info("txMessages=%v, rxMessages=%v", p.GetArray("txMessages"), p.GetArray("rxMessages")) - for _, m := range p.GetArray("txMessages") { - msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`)) - } - - for _, m := range p.GetArray("rxMessages") { - msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`)) - } - - for _, m := range p.GetArray("policies") { - if val, err := strconv.Atoi(strings.Trim(m.String(), `"`)); err == nil { - msgs.Policies = append(msgs.Policies, int64(val)) - } - } - } - } - return + appmgr.Logger.Info("Fetching RT data for xApp=%s", name) + + ns := cm.GetNamespace("") + args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns)) + out, err := kubeExec(args) + if err != nil { + return + } + + var p fastjson.Parser + v, err := p.Parse(string(out)) + if err != nil { + appmgr.Logger.Info("fastjson.Parser for '%s' failed: %v", name, err) + return + } + + if v.Exists("rmr") { + for _, m := range v.GetArray("rmr", "txMessages") { + msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`)) + } + + for _, m := range v.GetArray("rmr", "rxMessages") { + msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`)) + } + + for _, m := range v.GetArray("rmr", "policies") { + if val, err := strconv.Atoi(strings.Trim(m.String(), `"`)); err == nil { + msgs.Policies = append(msgs.Policies, int64(val)) + } + } + } else { + for _, p := range v.GetArray("messaging", "ports") { + appmgr.Logger.Info("txMessages=%v, rxMessages=%v", p.GetArray("txMessages"), p.GetArray("rxMessages")) + for _, m := range p.GetArray("txMessages") { + msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`)) + } + + for _, m := range p.GetArray("rxMessages") { + msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`)) + } + + for _, m := range p.GetArray("policies") { + if val, err := strconv.Atoi(strings.Trim(m.String(), `"`)); err == nil { + msgs.Policies = append(msgs.Policies, int64(val)) + } + } + } + } + return } func (cm *CM) GetConfigMapName(xappName, namespace string) string { - return " configmap-" + namespace + "-" + xappName + "-appconfig" + return " configmap-" + namespace + "-" + xappName + "-appconfig" } func (cm *CM) GetNamespace(ns string) string { - if ns != "" { - return ns - } - - ns = viper.GetString("xapp.namespace") - if ns == "" { - ns = "ricxapp" - } - return ns + if ns != "" { + return ns + } + + ns = viper.GetString("xapp.namespace") + if ns == "" { + ns = "ricxapp" + } + return ns } func (cm *CM) GetNamesFromHelmRepo() (names []string) { - rname := viper.GetString("helm.repo-name") - - cmdArgs := strings.Join([]string{"search ", rname}, "") - out, err := helmExec(cmdArgs) - if err != nil { - return - } - - re := regexp.MustCompile(rname + `/.*`) - result := re.FindAllStringSubmatch(string(out), -1) - if result != nil { - var tmp string - for _, v := range result { - fmt.Sscanf(v[0], "%s", &tmp) - names = append(names, strings.Split(tmp, "/")[1]) - } - } - return names + rname := viper.GetString("helm.repo-name") + + var cmdArgs string = "" + if EnvHelmVersion == HELM_VERSION_3 { + cmdArgs = strings.Join([]string{"search repo ", rname}, "") + }else { + cmdArgs = strings.Join([]string{"search ", rname}, "") + } + + out, err := helmExec(cmdArgs) + if err != nil { + return + } + + re := regexp.MustCompile(rname + `/.*`) + result := re.FindAllStringSubmatch(string(out), -1) + if result != nil { + var tmp string + for _, v := range result { + fmt.Sscanf(v[0], "%s", &tmp) + names = append(names, strings.Split(tmp, "/")[1]) + } + } + return names } func (cm *CM) Validate(req models.XAppConfig) (errList models.ConfigValidationErrors, err error) { - var desc interface{} - err = cm.ReadSchema(*req.Metadata.XappName, &desc) - if err != nil { - appmgr.Logger.Info("No schema file found for '%s', aborting ...", *req.Metadata.XappName) - return - } - return cm.doValidate(desc, req.Config) + var desc interface{} + err = cm.ReadSchema(*req.Metadata.XappName, &desc) + if err != nil { + appmgr.Logger.Info("No schema file found for '%s', aborting ...", *req.Metadata.XappName) + return + } + return cm.doValidate(desc, req.Config) } func (cm *CM) doValidate(schema, cfg interface{}) (errList models.ConfigValidationErrors, err error) { - schemaLoader := gojsonschema.NewGoLoader(schema) - documentLoader := gojsonschema.NewGoLoader(cfg) - - result, err := gojsonschema.Validate(schemaLoader, documentLoader) - if err != nil { - appmgr.Logger.Info("Validation failed: %v", err) - return - } - - if result.Valid() == false { - appmgr.Logger.Info("The document is not valid, Errors: %v", result.Errors()) - for _, desc := range result.Errors() { - field := desc.Field() - validationError := desc.Description() - errList = append(errList, &models.ConfigValidationError{Field: &field, Error: &validationError}) - } - return errList, errors.New("Validation failed!") - } - appmgr.Logger.Info("Config validation successful!") - - return + schemaLoader := gojsonschema.NewGoLoader(schema) + documentLoader := gojsonschema.NewGoLoader(cfg) + + result, err := gojsonschema.Validate(schemaLoader, documentLoader) + if err != nil { + appmgr.Logger.Info("Validation failed: %v", err) + return + } + + if result.Valid() == false { + appmgr.Logger.Info("The document is not valid, Errors: %v", result.Errors()) + for _, desc := range result.Errors() { + field := desc.Field() + validationError := desc.Description() + errList = append(errList, &models.ConfigValidationError{Field: &field, Error: &validationError}) + } + return errList, errors.New("Validation failed!") + } + appmgr.Logger.Info("Config validation successful!") + + return } diff --git a/pkg/helm/helm.go b/pkg/helm/helm.go index e777403..806a6c8 100755 --- a/pkg/helm/helm.go +++ b/pkg/helm/helm.go @@ -20,346 +20,407 @@ package helm import ( - "fmt" - "github.com/ghodss/yaml" - "github.com/spf13/viper" - "io/ioutil" - "os" - "regexp" - "strconv" - "strings" - "time" - - "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr" - "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/cm" - "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/models" - "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/util" + "fmt" + "github.com/ghodss/yaml" + "github.com/spf13/viper" + "io/ioutil" + "os" + "regexp" + "strconv" + "strings" + "time" + + "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr" + "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/cm" + "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/models" + "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/util" ) var kubeExec = util.KubectlExec var helmExec = util.HelmExec type Helm struct { - initDone bool - cm *cm.CM + initDone bool + cm *cm.CM } +func GetHelmVersion() { + var temp string + temp = os.Getenv("HELMVERSION") + appmgr.Logger.Info("Inside new Helm") + temp = strings.TrimLeft(temp,"v") + cm.EnvHelmVersion = string(temp[0:1]) //s.TrimRight(temp, ".") + appmgr.Logger.Info(cm.EnvHelmVersion) +} + + func NewHelm() *Helm { - return &Helm{initDone: false, cm: cm.NewCM()} + GetHelmVersion() + return &Helm{initDone: false, cm: cm.NewCM()} } func (h *Helm) Initialize() { - if h.initDone == true { - return - } - - for { - if _, err := h.Init(); err == nil { - appmgr.Logger.Info("Helm init done successfully!") - break - } - appmgr.Logger.Info("helm init failed, retyring ...") - time.Sleep(time.Duration(10) * time.Second) - } - - for { - if _, err := h.AddRepo(); err == nil { - appmgr.Logger.Info("Helm repo added successfully") - break - } - appmgr.Logger.Info("Helm repo addition failed, retyring ...") - time.Sleep(time.Duration(10) * time.Second) - } - h.initDone = true + if h.initDone == true { + return + } + appmgr.Logger.Info("START") + for { + if _, err := h.Init(); err == nil { + appmgr.Logger.Info("Helm init done successfully!") + break + } + appmgr.Logger.Info("helm init failed, retyring ...") + time.Sleep(time.Duration(10) * time.Second) + } + if cm.EnvHelmVersion == cm.HELM_VERSION_3 { + appmgr.Logger.Info("Codintion met for HELM3") + util.HelmExec(strings.Join([]string{"repo add stable https://kubernetes-charts.storage.googleapis.com/ "}, "")) + } + + for { + if _, err := h.AddRepo(); err == nil { + appmgr.Logger.Info("Helm repo added successfully") + break + } + appmgr.Logger.Info("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 helmExec(args) + return helmExec(args) } // API functions func (h *Helm) Init() (out []byte, err error) { - if err := h.AddTillerEnv(); err != nil { - return out, err - } - - return helmExec(strings.Join([]string{"init -c --skip-refresh"}, "")) + if err := h.AddTillerEnv(); err != nil { + return out, err + } + if cm.EnvHelmVersion == cm.HELM_VERSION_2{ + appmgr.Logger.Info("Init for Version 2") + return helmExec(strings.Join([]string{"init -c --skip-refresh"}, "")) + } + + return out, err } func (h *Helm) AddRepo() (out []byte, err error) { - // Get helm repo user name and password from files mounted by secret object - username, err := ioutil.ReadFile(viper.GetString("helm.helm-username-file")) - if err != nil { - appmgr.Logger.Info("helm_repo_username ReadFile failed: %v", err.Error()) - return - } - - password, err := ioutil.ReadFile(viper.GetString("helm.helm-password-file")) - if err != nil { - appmgr.Logger.Info("helm_repo_password ReadFile failed: %v", err.Error()) - return - } - - repoArgs := fmt.Sprintf(" %s %s ", viper.GetString("helm.repo-name"), viper.GetString("helm.repo")) - credentials := fmt.Sprintf(" --username %s --password %s", string(username), string(password)) - - return helmExec(strings.Join([]string{"repo add ", repoArgs, credentials}, "")) + // Get helm repo user name and password from files mounted by secret object + username, err := ioutil.ReadFile(viper.GetString("helm.helm-username-file")) + if err != nil { + appmgr.Logger.Info("helm_repo_username ReadFile failed: %v", err.Error()) + return + } + + password, err := ioutil.ReadFile(viper.GetString("helm.helm-password-file")) + if err != nil { + appmgr.Logger.Info("helm_repo_password ReadFile failed: %v", err.Error()) + return + } + + repoArgs := fmt.Sprintf(" %s %s ", viper.GetString("helm.repo-name"), viper.GetString("helm.repo")) + credentials := fmt.Sprintf(" --username %s --password %s", string(username), string(password)) + + return helmExec(strings.Join([]string{"repo add ", repoArgs, credentials}, "")) } func (h *Helm) Install(m models.XappDescriptor) (xapp models.Xapp, err error) { - m.Namespace = h.cm.GetNamespace(m.Namespace) - - out, err := h.Run(strings.Join([]string{"repo update "}, "")) - if err != nil { - return - } - - out, err = h.Run(h.GetInstallArgs(m, false)) - if err != nil { - return - } - return h.ParseStatus(*m.XappName, string(out)) + m.Namespace = h.cm.GetNamespace(m.Namespace) + + out, err := h.Run(strings.Join([]string{"repo update "}, "")) + if err != nil { + return + } + + out, err = h.Run(h.GetInstallArgs(m, false)) + if err != nil { + return + } + return h.ParseStatus(*m.XappName, string(out)) } func (h *Helm) Status(name string) (xapp models.Xapp, err error) { - out, err := h.Run(strings.Join([]string{"status ", name}, "")) - if err != nil { - appmgr.Logger.Info("Getting xapps status: %v", err.Error()) - return - } - return h.ParseStatus(name, string(out)) + var command string + if cm.EnvHelmVersion == cm.HELM_VERSION_3 { + names := h.cm.GetNamespace("") + command = strings.Join([]string{"status ", name," --namespace ", names}, "") + appmgr.Logger.Info ("Status: Version 3") + }else { + command = strings.Join([]string{"status ", name}, "") + appmgr.Logger.Info ("Status: Version 2") + } + out, err := h.Run(command) + + if err != nil { + appmgr.Logger.Info("Getting xapps status: %v", err.Error()) + return + } + return h.ParseStatus(name, string(out)) } func (h *Helm) StatusAll() (xapps models.AllDeployedXapps, err error) { - xappNameList, err := h.List() - if err != nil { - appmgr.Logger.Info("Helm list failed: %v", err.Error()) - return - } + xappNameList, err := h.List() + if err != nil { + appmgr.Logger.Info("Helm list failed: %v", err.Error()) + return + } - return h.parseAllStatus(xappNameList) + return h.parseAllStatus(xappNameList) } func (h *Helm) List() (names []string, err error) { - ns := h.cm.GetNamespace("") - out, err := h.Run(strings.Join([]string{"list --all --deployed --output yaml --namespace=", ns}, "")) - if err != nil { - appmgr.Logger.Info("Listing deployed xapps failed: %v", err.Error()) - return - } - - return h.GetNames(string(out)) + ns := h.cm.GetNamespace("") + out, err := h.Run(strings.Join([]string{"list --all --deployed --output yaml --namespace=", ns}, "")) + if err != nil { + appmgr.Logger.Info("Listing deployed xapps failed: %v", err.Error()) + return + } + + return h.GetNames(string(out)) } func (h *Helm) SearchAll() models.AllDeployableXapps { - return h.cm.GetNamesFromHelmRepo() + return h.cm.GetNamesFromHelmRepo() } func (h *Helm) Delete(name string) (xapp models.Xapp, err error) { - xapp, err = h.Status(name) - if err != nil { - appmgr.Logger.Info("Fetching xapp status failed: %v", err.Error()) - return - } - - _, err = h.Run(strings.Join([]string{"del --purge ", name}, "")) - return xapp, err + xapp, err = h.Status(name) + var command string = "" + ns := h.cm.GetNamespace("") + + if err != nil { + appmgr.Logger.Info("Fetching xapp status failed: %v", err.Error()) + return + } + if cm.EnvHelmVersion == cm.HELM_VERSION_3 { + command = strings.Join([]string{"uninstall ", name," -n ", ns}, "") + appmgr.Logger.Info ("DELETE: Version 3") + } else { + command = strings.Join([]string{"del --purge ", name}, "") + appmgr.Logger.Info ("DELETE: Version 2") + } + _, err = h.Run (command) + + return xapp, err } func (h *Helm) Fetch(name, tarDir string) error { - if strings.HasSuffix(os.Args[0], ".test") { - return nil - } + if strings.HasSuffix(os.Args[0], ".test") { + return nil + } - rname := viper.GetString("helm.repo-name") + "/" + rname := viper.GetString("helm.repo-name") + "/" - _, err := h.Run(strings.Join([]string{"fetch --untar --untardir ", tarDir, " ", rname, name}, "")) - return err + _, err := h.Run(strings.Join([]string{"fetch --untar --untardir ", tarDir, " ", rname, name}, "")) + return err } // Helper functions func (h *Helm) GetVersion(name string) (version string) { - ns := h.cm.GetNamespace("") - out, err := h.Run(strings.Join([]string{"list --deployed --output yaml --namespace=", ns, " ", name}, "")) - if err != nil { - return - } - - var re = regexp.MustCompile(`AppVersion: .*`) - ver := re.FindStringSubmatch(string(out)) - if ver != nil { - version = strings.Split(ver[0], ": ")[1] - version, _ = strconv.Unquote(version) - } - - return + ns := h.cm.GetNamespace("") + var command string = "" + if cm.EnvHelmVersion == cm.HELM_VERSION_3 { + command = strings.Join([]string{"list --deployed --output yaml --namespace=", ns, " ","-f ",name}, "") + appmgr.Logger.Info ("GetVersion: Version 3") + } else { + command = strings.Join([]string{"list --deployed --output yaml --namespace=", ns, " ", name}, "") + appmgr.Logger.Info ("GetVersion: Version 2") + } + out, err := h.Run(command) + + if err != nil { + return + } + + var re = regexp.MustCompile(`AppVersion: .*`) + ver := re.FindStringSubmatch(string(out)) + if ver != nil { + version = strings.Split(ver[0], ": ")[1] + version, _ = strconv.Unquote(version) + } + + return } func (h *Helm) GetState(out string) (status string) { - re := regexp.MustCompile(`STATUS: .*`) - result := re.FindStringSubmatch(string(out)) - if result != nil { - status = strings.ToLower(strings.Split(result[0], ": ")[1]) - } + re := regexp.MustCompile(`STATUS: .*`) + result := re.FindStringSubmatch(string(out)) + if result != nil { + status = strings.ToLower(strings.Split(result[0], ": ")[1]) + } - return + return } func (h *Helm) GetAddress(out string) (ip, port string) { - var tmp string - re := regexp.MustCompile(`ClusterIP.*`) - addr := re.FindStringSubmatch(string(out)) - if addr != nil { - fmt.Sscanf(addr[0], "%s %s %s %s", &tmp, &ip, &tmp, &port) - } - - return + var tmp string + re := regexp.MustCompile(`ClusterIP.*`) + addr := re.FindStringSubmatch(string(out)) + if addr != nil { + fmt.Sscanf(addr[0], "%s %s %s %s", &tmp, &ip, &tmp, &port) + } + + return } func (h *Helm) GetEndpointInfo(name string) (svc string, port int) { - port = 4560 // Default - ns := h.cm.GetNamespace("") - args := fmt.Sprintf(" get service -n %s service-%s-%s-rmr -o json", ns, ns, name) - out, err := kubeExec(args) - if err != nil { - return fmt.Sprintf("service-%s-%s-rmr.%s", ns, name, ns), 4560 - } - appmgr.Logger.Debug("Endpoint IP address of %s: %s", name, string(out)) - - v, err := h.cm.ParseJson(string(out)) - if err != nil { - return fmt.Sprintf("service-%s-%s-rmr.%s", ns, name, ns), 4560 - } - - for _, p := range v.GetArray("spec", "ports") { - if string(p.GetStringBytes("name")) == "rmrdata" { - port = int(p.GetInt("port")) - break - } - } - appmgr.Logger.Info("service-%s-%s-rmr.%s %d", ns, name, ns, port) - - return fmt.Sprintf("service-%s-%s-rmr.%s", ns, name, ns), port + port = 4560 // Default + ns := h.cm.GetNamespace("") + args := fmt.Sprintf(" get service -n %s service-%s-%s-rmr -o json", ns, ns, name) + out, err := kubeExec(args) + if err != nil { + return fmt.Sprintf("service-%s-%s-rmr.%s", ns, name, ns), 4560 + } + appmgr.Logger.Debug("Endpoint IP address of %s: %s", name, string(out)) + + v, err := h.cm.ParseJson(string(out)) + if err != nil { + return fmt.Sprintf("service-%s-%s-rmr.%s", ns, name, ns), 4560 + } + + for _, p := range v.GetArray("spec", "ports") { + if string(p.GetStringBytes("name")) == "rmrdata" { + port = int(p.GetInt("port")) + break + } + } + appmgr.Logger.Info("service-%s-%s-rmr.%s %d", ns, name, ns, port) + + return fmt.Sprintf("service-%s-%s-rmr.%s", ns, name, ns), port } func (h *Helm) GetNames(out string) (names []string, err error) { - re := regexp.MustCompile(`Name: .*`) - result := re.FindAllStringSubmatch(out, -1) - if result == nil { - return - } - - for _, v := range result { - xappName := strings.Split(v[0], ": ")[1] - if strings.Contains(xappName, "appmgr") == false { - names = append(names, xappName) - } - } - return names, nil + re := regexp.MustCompile(`Name: .*`) + result := re.FindAllStringSubmatch(out, -1) + if result == nil { + return + } + + for _, v := range result { + xappName := strings.Split(v[0], ": ")[1] + if strings.Contains(xappName, "appmgr") == false { + names = append(names, xappName) + } + } + return names, nil } func (h *Helm) FillInstanceData(name string, out string, xapp *models.Xapp, rtData appmgr.RtmData) { - ip, port := h.GetEndpointInfo(name) - if ip == "" { - appmgr.Logger.Info("Endpoint IP address not found, using CluserIP") - ip, _ = h.GetAddress(out) - } - - var tmp string - 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 + "-(\\w+-\\w+).*") - resources := re.FindAllStringSubmatch(string(result[0]), -1) - if resources != nil { - for _, v := range resources { - var x models.XappInstance - var name string - fmt.Sscanf(v[0], "%s %s %s", &name, &tmp, &x.Status) - x.Name = &name - x.Status = strings.ToLower(x.Status) - x.IP = ip - x.Port = int64(port) - x.TxMessages = rtData.TxMessages - x.RxMessages = rtData.RxMessages - x.Policies = rtData.Policies - xapp.Instances = append(xapp.Instances, &x) - } - } + ip, port := h.GetEndpointInfo(name) + if ip == "" { + appmgr.Logger.Info("Endpoint IP address not found, using CluserIP") + ip, _ = h.GetAddress(out) + } + + var tmp string + 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 + "-(\\w+-\\w+).*") + resources := re.FindAllStringSubmatch(string(result[0]), -1) + if resources != nil { + for _, v := range resources { + var x models.XappInstance + var name string + fmt.Sscanf(v[0], "%s %s %s", &name, &tmp, &x.Status) + x.Name = &name + x.Status = strings.ToLower(x.Status) + x.IP = ip + x.Port = int64(port) + x.TxMessages = rtData.TxMessages + x.RxMessages = rtData.RxMessages + x.Policies = rtData.Policies + xapp.Instances = append(xapp.Instances, &x) + } + } } func (h *Helm) ParseStatus(name string, out string) (xapp models.Xapp, err error) { - xapp.Name = &name - xapp.Version = h.GetVersion(name) - xapp.Status = h.GetState(out) + xapp.Name = &name + xapp.Version = h.GetVersion(name) + xapp.Status = h.GetState(out) - h.FillInstanceData(name, out, &xapp, h.cm.GetRtmData(name)) - return + h.FillInstanceData(name, out, &xapp, h.cm.GetRtmData(name)) + return } func (h *Helm) parseAllStatus(names []string) (xapps models.AllDeployedXapps, err error) { - xapps = models.AllDeployedXapps{} - for _, name := range names { - var desc interface{} - err := h.cm.ReadSchema(name, &desc) - if err != nil { - continue - } - - x, err := h.Status(name) - if err == nil { - xapps = append(xapps, &x) - } - } - return + xapps = models.AllDeployedXapps{} + for _, name := range names { + var desc interface{} + err := h.cm.ReadSchema(name, &desc) + if err != nil { + continue + } + + x, err := h.Status(name) + if err == nil { + xapps = append(xapps, &x) + } + } + return } func (h *Helm) 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 { - appmgr.Logger.Info("Tiller Env Setting Failed: %v", err.Error()) - } - return err + 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 { + appmgr.Logger.Info("Tiller Env Setting Failed: %v", err.Error()) + } + return err } func (h *Helm) GetInstallArgs(x models.XappDescriptor, cmOverride bool) (args string) { - args = fmt.Sprintf("%s --namespace=%s", args, x.Namespace) - if x.HelmVersion != "" { - args = fmt.Sprintf("%s --version=%s", args, x.HelmVersion) - } - - if x.ReleaseName != "" { - args = fmt.Sprintf("%s --name=%s", args, x.ReleaseName) - } else { - args = fmt.Sprintf("%s --name=%s", args, *x.XappName) - } - - if cmOverride == true { - args = fmt.Sprintf("%s --set ricapp.appconfig.override=%s-appconfig", args, *x.XappName) - } - - if x.OverrideFile != nil { - if overrideYaml, err := yaml.JSONToYAML([]byte(x.OverrideFile.(string))); err == nil { - err = ioutil.WriteFile("/tmp/appmgr_override.yaml", overrideYaml, 0644) - if err != nil { - appmgr.Logger.Info("ioutil.WriteFile(/tmp/appmgr_override.yaml) failed: %v", err) - } else { - args = args + " -f=/tmp/appmgr_override.yaml" - } - } else { - appmgr.Logger.Info("yaml.JSONToYAML failed: %v", err) - } - } - - repoName := viper.GetString("helm.repo-name") - if repoName == "" { - repoName = "helm-repo" - } - return fmt.Sprintf("install %s/%s %s", repoName, *x.XappName, args) + args = fmt.Sprintf("%s--namespace=%s", args, x.Namespace) + if x.HelmVersion != "" { + args = fmt.Sprintf("%s --version=%s", args, x.HelmVersion) + } + + + if cm.EnvHelmVersion == cm.HELM_VERSION_2 { + if x.ReleaseName != "" { + args = fmt.Sprintf("%s --name=%s", args, x.ReleaseName) + } else { + args = fmt.Sprintf("%s --name=%s", args, *x.XappName) + appmgr.Logger.Info("") + } + appmgr.Logger.Info ("GetInstallArgs: Version 2") + } + + if cmOverride == true { + args = fmt.Sprintf("%s --set ricapp.appconfig.override=%s-appconfig", args, *x.XappName) + } + + if x.OverrideFile != nil { + if overrideYaml, err := yaml.JSONToYAML([]byte(x.OverrideFile.(string))); err == nil { + err = ioutil.WriteFile("/tmp/appmgr_override.yaml", overrideYaml, 0644) + if err != nil { + appmgr.Logger.Info("ioutil.WriteFile(/tmp/appmgr_override.yaml) failed: %v", err) + } else { + args = args + " -f=/tmp/appmgr_override.yaml" + } + } else { + appmgr.Logger.Info("yaml.JSONToYAML failed: %v", err) + } + } + + repoName := viper.GetString("helm.repo-name") + if repoName == "" { + repoName = "helm-repo" + } + + + if cm.EnvHelmVersion == cm.HELM_VERSION_3 { + appmgr.Logger.Info ("GetInstallArgs last: Version 3") + return fmt.Sprintf("install %s %s/%s %s",*x.XappName, repoName, *x.XappName, args) + } else { + appmgr.Logger.Info ("GetInstallArgs last: Version 2") + return fmt.Sprintf("install %s/%s %s", repoName, *x.XappName, args) + } } diff --git a/pkg/helm/helm_test.go b/pkg/helm/helm_test.go index 1bfdb22..1f54c29 100755 --- a/pkg/helm/helm_test.go +++ b/pkg/helm/helm_test.go @@ -20,17 +20,17 @@ package helm import ( - "errors" - "github.com/spf13/viper" - "os" - "reflect" - "strconv" - "strings" - "testing" - - "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr" - "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/models" - "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/util" + "errors" + "github.com/spf13/viper" + "os" + "reflect" + "strconv" + "strings" + "testing" + "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/cm" + "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr" + "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/models" + "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/util" ) var caughtKubeExecArgs string @@ -143,473 +143,507 @@ var kubeServiceOutput = `{ // Test cases func TestMain(m *testing.M) { - appmgr.Init() - appmgr.Logger.SetLevel(0) + appmgr.Init() + appmgr.Logger.SetLevel(0) - code := m.Run() - os.Exit(code) + code := m.Run() + os.Exit(code) } func TestInit(t *testing.T) { - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec + defer func() { resetHelmExecMock() }() + var expectedHelmCommand string = "" + helmExec = mockedHelmExec - NewHelm().Init() + NewHelm().Init() - expectedHelmCommand := "init -c --skip-refresh" - if caughtHelmExecArgs != expectedHelmCommand { - t.Errorf("Init failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs) - } + if cm.EnvHelmVersion == cm.HELM_VERSION_2{ + expectedHelmCommand = "init -c --skip-refresh" + if caughtHelmExecArgs != expectedHelmCommand { + t.Errorf("Init failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs) + } + } } func TestAddRepoSuccess(t *testing.T) { - defer func() { - resetHelmExecMock() - removeTestUsernameFile() - removeTestPasswordFile() - }() - helmExec = mockedHelmExec - - if err := writeTestUsernameFile(); err != nil { - t.Errorf("AddRepo username file create failed: %s", err) - return - } - if err := writeTestPasswordFile(); err != nil { - t.Errorf("AddRepo password file create failed: %s", err) - return - } - - if _, err := NewHelm().AddRepo(); err != nil { - t.Errorf("AddRepo failed: %v", err) - } - - if !strings.Contains(caughtHelmExecArgs, "repo add") { - t.Errorf("AddRepo failed: expected %v, got %v", "repo add", caughtHelmExecArgs) - } + defer func() { + resetHelmExecMock() + removeTestUsernameFile() + removeTestPasswordFile() + }() + helmExec = mockedHelmExec + + if err := writeTestUsernameFile(); err != nil { + t.Errorf("AddRepo username file create failed: %s", err) + return + } + if err := writeTestPasswordFile(); err != nil { + t.Errorf("AddRepo password file create failed: %s", err) + return + } + + if _, err := NewHelm().AddRepo(); err != nil { + t.Errorf("AddRepo failed: %v", err) + } + + if !strings.Contains(caughtHelmExecArgs, "repo add") { + t.Errorf("AddRepo failed: expected %v, got %v", "repo add", caughtHelmExecArgs) + } } func TestAddRepoReturnsErrorIfNoUsernameFile(t *testing.T) { - if _, err := NewHelm().AddRepo(); err == nil { - t.Errorf("AddRepo expected to fail but it didn't") - } + if _, err := NewHelm().AddRepo(); err == nil { + t.Errorf("AddRepo expected to fail but it didn't") + } } func TestAddRepoReturnsErrorIfNoPasswordFile(t *testing.T) { - defer func() { resetHelmExecMock(); removeTestUsernameFile() }() - helmExec = mockedHelmExec + defer func() { resetHelmExecMock(); removeTestUsernameFile() }() + helmExec = mockedHelmExec - if err := writeTestUsernameFile(); err != nil { - t.Errorf("AddRepo username file create failed: %s", err) - return - } - if _, err := NewHelm().AddRepo(); err == nil { - t.Errorf("AddRepo expected to fail but it didn't") - } + if err := writeTestUsernameFile(); err != nil { + t.Errorf("AddRepo username file create failed: %s", err) + return + } + if _, err := NewHelm().AddRepo(); err == nil { + t.Errorf("AddRepo expected to fail but it didn't") + } } func TestInstallSuccess(t *testing.T) { - name := "dummy-xapp" - xappDesc := models.XappDescriptor{XappName: &name, Namespace: "ricxapp"} + name := "dummy-xapp" + xappDesc := models.XappDescriptor{XappName: &name, Namespace: "ricxapp"} - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec - helmExecRetOut = helmStatusOutput + defer func() { resetHelmExecMock() }() + helmExec = mockedHelmExec + helmExecRetOut = helmStatusOutput - defer func() { resetKubeExecMock() }() - kubeExec = mockedKubeExec - kubeExecRetOut = kubeServiceOutput + defer func() { resetKubeExecMock() }() + kubeExec = mockedKubeExec + kubeExecRetOut = kubeServiceOutput - xapp, err := NewHelm().Install(xappDesc) - if err != nil { - t.Errorf("Install failed: %v", err) - } - validateXappModel(t, xapp) + xapp, err := NewHelm().Install(xappDesc) + if err != nil { + t.Errorf("Install failed: %v", err) + } + validateXappModel(t, xapp) } func TestInstallReturnsErrorIfHelmRepoUpdateFails(t *testing.T) { - name := "dummy-xapp" - xappDesc := models.XappDescriptor{XappName: &name, Namespace: "ricxapp"} + name := "dummy-xapp" + xappDesc := models.XappDescriptor{XappName: &name, Namespace: "ricxapp"} - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec - helmExecRetErr = errors.New("some helm command error") + defer func() { resetHelmExecMock() }() + helmExec = mockedHelmExec + helmExecRetErr = errors.New("some helm command error") - if _, err := NewHelm().Install(xappDesc); err == nil { - t.Errorf("Install expected to fail but it didn't") - } + if _, err := NewHelm().Install(xappDesc); err == nil { + t.Errorf("Install expected to fail but it didn't") + } } func TestStatusSuccess(t *testing.T) { - name := "dummy-xapp" + name := "dummy-xapp" - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec - helmExecRetOut = helmStatusOutput + defer func() { resetHelmExecMock() }() + helmExec = mockedHelmExec + helmExecRetOut = helmStatusOutput - xapp, err := NewHelm().Status(name) - if err != nil { - t.Errorf("Status failed: %v", err) - } - validateXappModel(t, xapp) + xapp, err := NewHelm().Status(name) + if err != nil { + t.Errorf("Status failed: %v", err) + } + validateXappModel(t, xapp) } func TestStatusReturnsErrorIfHelmStatusFails(t *testing.T) { - name := "dummy-xapp" + name := "dummy-xapp" - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec - helmExecRetErr = errors.New("some helm command error") + defer func() { resetHelmExecMock() }() + helmExec = mockedHelmExec + helmExecRetErr = errors.New("some helm command error") - if _, err := NewHelm().Status(name); err == nil { - t.Errorf("Status expected to fail but it didn't") - } + if _, err := NewHelm().Status(name); err == nil { + t.Errorf("Status expected to fail but it didn't") + } } func TestParseStatusSuccess(t *testing.T) { - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec - helmExecRetOut = helListOutput + defer func() { resetHelmExecMock() }() + var expectedHelmCommand string = "" + helmExec = mockedHelmExec + helmExecRetOut = helListOutput - defer func() { resetKubeExecMock() }() - kubeExec = mockedKubeExec - kubeExecRetOut = kubeServiceOutput + defer func() { resetKubeExecMock() }() + kubeExec = mockedKubeExec + kubeExecRetOut = kubeServiceOutput - xapp, err := NewHelm().ParseStatus("dummy-xapp", helmStatusOutput) - if err != nil { - t.Errorf("ParseStatus failed: %v", err) - } + xapp, err := NewHelm().ParseStatus("dummy-xapp", helmStatusOutput) + if err != nil { + t.Errorf("ParseStatus failed: %v", err) + } - validateXappModel(t, xapp) + validateXappModel(t, xapp) - expectedHelmCommand := "list --deployed --output yaml --namespace=ricxapp dummy-xapp" - if caughtHelmExecArgs != expectedHelmCommand { - t.Errorf("ParseStatus failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs) - } + if cm.EnvHelmVersion == cm.HELM_VERSION_2 { + expectedHelmCommand = "list --deployed --output yaml --namespace=ricxapp dummy-xapp" + }else { + expectedHelmCommand = "list --deployed --output yaml --namespace=ricxapp -f dummy-xapp" + } + if caughtHelmExecArgs != expectedHelmCommand { + t.Errorf("ParseStatus failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs) + } } func TestListSuccess(t *testing.T) { - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec - helmExecRetOut = helListAllOutput + defer func() { resetHelmExecMock() }() + helmExec = mockedHelmExec + helmExecRetOut = helListAllOutput - names, err := NewHelm().List() - if err != nil { - t.Errorf("List failed: %v", err) - } + names, err := NewHelm().List() + if err != nil { + t.Errorf("List failed: %v", err) + } - if !reflect.DeepEqual(names, []string{"dummy-xapp", "dummy-xapp2"}) { - t.Errorf("List failed: %v", err) - } - expectedHelmCommand := "list --all --deployed --output yaml --namespace=ricxapp" - if caughtHelmExecArgs != expectedHelmCommand { - t.Errorf("List: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs) - } + if !reflect.DeepEqual(names, []string{"dummy-xapp", "dummy-xapp2"}) { + t.Errorf("List failed: %v", err) + } + expectedHelmCommand := "list --all --deployed --output yaml --namespace=ricxapp" + if caughtHelmExecArgs != expectedHelmCommand { + t.Errorf("List: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs) + } } func TestListReturnsErrorIfHelmListFails(t *testing.T) { - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec - helmExecRetErr = errors.New("some helm command error") + defer func() { resetHelmExecMock() }() + helmExec = mockedHelmExec + helmExecRetErr = errors.New("some helm command error") - if _, err := NewHelm().List(); err == nil { - t.Errorf("List expected to fail but it didn't") - } + if _, err := NewHelm().List(); err == nil { + t.Errorf("List expected to fail but it didn't") + } } func TestDeleteSuccess(t *testing.T) { - name := "dummy-xapp" + name := "dummy-xapp" - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec - helmExecRetOut = helmStatusOutput + var expectedHelmCommand string = "" + defer func() { resetHelmExecMock() }() + helmExec = mockedHelmExec + helmExecRetOut = helmStatusOutput - defer func() { resetKubeExecMock() }() - kubeExec = mockedKubeExec - kubeExecRetOut = kubeServiceOutput + defer func() { resetKubeExecMock() }() + kubeExec = mockedKubeExec + kubeExecRetOut = kubeServiceOutput - xapp, err := NewHelm().Delete(name) - if err != nil { - t.Errorf("Delete failed: %v", err) - } + xapp, err := NewHelm().Delete(name) + if err != nil { + t.Errorf("Delete failed: %v", err) + } - validateXappModel(t, xapp) + validateXappModel(t, xapp) - expectedHelmCommand := "del --purge dummy-xapp" - if caughtHelmExecArgs != expectedHelmCommand { - t.Errorf("Delete failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs) - } + if cm.EnvHelmVersion == cm.HELM_VERSION_2 { + expectedHelmCommand = "del --purge dummy-xapp" + } else { + expectedHelmCommand = "uninstall dummy-xapp -n ricxapp" + } + if caughtHelmExecArgs != expectedHelmCommand { + t.Errorf("Delete failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs) + } } func TestDeleteReturnsErrorIfHelmStatusFails(t *testing.T) { - name := "dummy-xapp" + name := "dummy-xapp" - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec - helmExecRetErr = errors.New("some helm command error") + defer func() { resetHelmExecMock() }() + helmExec = mockedHelmExec + helmExecRetErr = errors.New("some helm command error") - if _, err := NewHelm().Delete(name); err == nil { - t.Errorf("Delete expected to fail but it didn't") - } + if _, err := NewHelm().Delete(name); err == nil { + t.Errorf("Delete expected to fail but it didn't") + } } func TestFetchSuccessIfCmdArgHasTestSuffix(t *testing.T) { - if err := NewHelm().Fetch("kissa", "koira"); err != nil { - t.Errorf("Fetch failed: %v", err) - } + if err := NewHelm().Fetch("kissa", "koira"); err != nil { + t.Errorf("Fetch failed: %v", err) + } } func TestGetVersionSuccess(t *testing.T) { - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec - helmExecRetOut = helListOutput + defer func() { resetHelmExecMock() }() + var expectedHelmCommand string = "" + helmExec = mockedHelmExec + helmExecRetOut = helListOutput - if version := NewHelm().GetVersion("dummy-xapp"); version != "1.0" { - t.Errorf("GetVersion failed: expected 1.0, got %v", version) - } + if version := NewHelm().GetVersion("dummy-xapp"); version != "1.0" { + t.Errorf("GetVersion failed: expected 1.0, got %v", version) + } - expectedHelmCommand := "list --deployed --output yaml --namespace=ricxapp dummy-xapp" - if caughtHelmExecArgs != expectedHelmCommand { - t.Errorf("GetVersion failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs) - } + if cm.EnvHelmVersion == cm.HELM_VERSION_2{ + expectedHelmCommand = "list --deployed --output yaml --namespace=ricxapp dummy-xapp" + }else { + expectedHelmCommand = "list --deployed --output yaml --namespace=ricxapp -f dummy-xapp" + } + if caughtHelmExecArgs != expectedHelmCommand { + t.Errorf("GetVersion failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs) + } } func TestGetVersionReturnsEmptyStringIfHelmListFails(t *testing.T) { - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec - helmExecRetErr = errors.New("some helm command error") + defer func() { resetHelmExecMock() }() + helmExec = mockedHelmExec + helmExecRetErr = errors.New("some helm command error") - if version := NewHelm().GetVersion("dummy-xapp"); version != "" { - t.Errorf("GetVersion expected to return empty string, got %v", version) - } + if version := NewHelm().GetVersion("dummy-xapp"); version != "" { + t.Errorf("GetVersion expected to return empty string, got %v", version) + } } func TestGetAddressSuccess(t *testing.T) { - ip, port := NewHelm().GetAddress(helmStatusOutput) - if ip != "10.102.184.212" { - t.Errorf("GetAddress failed: expected 10.102.184.212, got %v", ip) - } - if port != "80/TCP" { - t.Errorf("GetAddress failed: expected 80/TCP, got %v", port) - } + ip, port := NewHelm().GetAddress(helmStatusOutput) + if ip != "10.102.184.212" { + t.Errorf("GetAddress failed: expected 10.102.184.212, got %v", ip) + } + if port != "80/TCP" { + t.Errorf("GetAddress failed: expected 80/TCP, got %v", port) + } } func TestGetEndpointInfoSuccess(t *testing.T) { - defer func() { resetKubeExecMock() }() - kubeExec = mockedKubeExec - kubeExecRetOut = kubeServiceOutput - - svc, port := NewHelm().GetEndpointInfo("dummy-xapp") - expectedSvc := "service-ricxapp-dummy-xapp-rmr.ricxapp" - if svc != expectedSvc { - t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedSvc, svc) - } - if port != 4560 { - t.Errorf("GetEndpointInfo failed: expected port 4560, got %v", port) - } - expectedKubeCommand := " get service -n ricxapp service-ricxapp-dummy-xapp-rmr -o json" - if caughtKubeExecArgs != expectedKubeCommand { - t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedKubeCommand, caughtKubeExecArgs) - } + defer func() { resetKubeExecMock() }() + kubeExec = mockedKubeExec + kubeExecRetOut = kubeServiceOutput + + svc, port := NewHelm().GetEndpointInfo("dummy-xapp") + expectedSvc := "service-ricxapp-dummy-xapp-rmr.ricxapp" + if svc != expectedSvc { + t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedSvc, svc) + } + if port != 4560 { + t.Errorf("GetEndpointInfo failed: expected port 4560, got %v", port) + } + expectedKubeCommand := " get service -n ricxapp service-ricxapp-dummy-xapp-rmr -o json" + if caughtKubeExecArgs != expectedKubeCommand { + t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedKubeCommand, caughtKubeExecArgs) + } } func TestGetEndpointInfoReturnsDefaultPortIfJsonParseFails(t *testing.T) { - defer func() { resetKubeExecMock() }() - kubeExec = mockedKubeExec - kubeExecRetOut = "not-json-syntax" + defer func() { resetKubeExecMock() }() + kubeExec = mockedKubeExec + kubeExecRetOut = "not-json-syntax" - svc, port := NewHelm().GetEndpointInfo("dummy-xapp") - expectedSvc := "service-ricxapp-dummy-xapp-rmr.ricxapp" - if svc != expectedSvc { - t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedSvc, svc) - } - if port != 4560 { - t.Errorf("GetEndpointInfo failed: expected port 4560, got %v", port) - } + svc, port := NewHelm().GetEndpointInfo("dummy-xapp") + expectedSvc := "service-ricxapp-dummy-xapp-rmr.ricxapp" + if svc != expectedSvc { + t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedSvc, svc) + } + if port != 4560 { + t.Errorf("GetEndpointInfo failed: expected port 4560, got %v", port) + } } func TestGetEndpointInfoReturnsDefaultPortIfKubeGetServiceFails(t *testing.T) { - defer func() { resetKubeExecMock() }() - kubeExec = mockedKubeExec - kubeExecRetErr = errors.New("some helm command error") + defer func() { resetKubeExecMock() }() + kubeExec = mockedKubeExec + kubeExecRetErr = errors.New("some helm command error") - svc, port := NewHelm().GetEndpointInfo("dummy-xapp") - expectedSvc := "service-ricxapp-dummy-xapp-rmr.ricxapp" - if svc != expectedSvc { - t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedSvc, svc) - } - if port != 4560 { - t.Errorf("GetEndpointInfo failed: expected port 4560, got %v", port) - } + svc, port := NewHelm().GetEndpointInfo("dummy-xapp") + expectedSvc := "service-ricxapp-dummy-xapp-rmr.ricxapp" + if svc != expectedSvc { + t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedSvc, svc) + } + if port != 4560 { + t.Errorf("GetEndpointInfo failed: expected port 4560, got %v", port) + } } func TestHelmStatusAllSuccess(t *testing.T) { - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec - helmExecRetOut = helListAllOutput + defer func() { resetHelmExecMock() }() + helmExec = mockedHelmExec + helmExecRetOut = helListAllOutput - if _, err := NewHelm().StatusAll(); err != nil { - t.Errorf("StatusAll failed: %v", err) - } - // Todo: check StatusAll response content + if _, err := NewHelm().StatusAll(); err != nil { + t.Errorf("StatusAll failed: %v", err) + } + // Todo: check StatusAll response content } func TestStatusAllReturnsErrorIfHelmListFails(t *testing.T) { - defer func() { resetHelmExecMock() }() - helmExec = mockedHelmExec - helmExecRetErr = errors.New("some helm command error") + defer func() { resetHelmExecMock() }() + helmExec = mockedHelmExec + helmExecRetErr = errors.New("some helm command error") - if _, err := NewHelm().StatusAll(); err == nil { - t.Errorf("StatusAll expected to fail but it didn't") - } + if _, err := NewHelm().StatusAll(); err == nil { + t.Errorf("StatusAll expected to fail but it didn't") + } } func TestGetNamesSuccess(t *testing.T) { - names, err := NewHelm().GetNames(helListAllOutput) - if err != nil { - t.Errorf("GetNames failed: %v", err) - } - if !reflect.DeepEqual(names, []string{"dummy-xapp", "dummy-xapp2"}) { - t.Errorf("GetNames failed: %v", err) - } + names, err := NewHelm().GetNames(helListAllOutput) + if err != nil { + t.Errorf("GetNames failed: %v", err) + } + if !reflect.DeepEqual(names, []string{"dummy-xapp", "dummy-xapp2"}) { + t.Errorf("GetNames failed: %v", err) + } } func TestAddTillerEnv(t *testing.T) { - if NewHelm().AddTillerEnv() != nil { - t.Errorf("AddTillerEnv failed!") - } + if NewHelm().AddTillerEnv() != nil { + t.Errorf("AddTillerEnv failed!") + } } func TestGetInstallArgs(t *testing.T) { - name := "dummy-xapp" - x := models.XappDescriptor{XappName: &name, Namespace: "ricxapp"} - - expectedArgs := "install helm-repo/dummy-xapp --namespace=ricxapp --name=dummy-xapp" - if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs { - t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args) - } - - expectedArgs += " --set ricapp.appconfig.override=dummy-xapp-appconfig" - if args := NewHelm().GetInstallArgs(x, true); args != expectedArgs { - t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args) - } - - x.HelmVersion = "1.2.3" - expectedArgs = "install helm-repo/dummy-xapp --namespace=ricxapp --version=1.2.3 --name=dummy-xapp" - if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs { - t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args) - } - - x.ReleaseName = "ueec-xapp" - expectedArgs = "install helm-repo/dummy-xapp --namespace=ricxapp --version=1.2.3 --name=ueec-xapp" - if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs { - t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args) - } - - x.OverrideFile = "../../test/dummy-xapp_values.json" - expectedArgs += " -f=/tmp/appmgr_override.yaml" - if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs { - t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args) - } + name := "dummy-xapp" + var expectedArgs string = "" + + x := models.XappDescriptor{XappName: &name, Namespace: "ricxapp"} + + if cm.EnvHelmVersion == cm.HELM_VERSION_3 { + expectedArgs = "install dummy-xapp helm-repo/dummy-xapp --namespace=ricxapp" + }else { + expectedArgs = "install helm-repo/dummy-xapp --namespace=ricxapp --name=dummy-xapp" + } + + if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs { + t.Errorf("GetInstallArgs failed: expected '%v', got '%v'", expectedArgs, args) + } + + expectedArgs += " --set ricapp.appconfig.override=dummy-xapp-appconfig" + if args := NewHelm().GetInstallArgs(x, true); args != expectedArgs { + t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args) + } + + x.HelmVersion = "1.2.3" + if cm.EnvHelmVersion == cm.HELM_VERSION_3 { + expectedArgs = "install dummy-xapp helm-repo/dummy-xapp --namespace=ricxapp --version=1.2.3" + } else { + expectedArgs = "install helm-repo/dummy-xapp --namespace=ricxapp --version=1.2.3 --name=dummy-xapp" + } + if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs { + t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args) + } + + + x.ReleaseName = "ueec-xapp" + if cm.EnvHelmVersion == cm.HELM_VERSION_3 { + expectedArgs = "install dummy-xapp helm-repo/dummy-xapp --namespace=ricxapp --version=1.2.3" + } else { + expectedArgs = "install helm-repo/dummy-xapp --namespace=ricxapp --version=1.2.3 --name=ueec-xapp" + } + if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs { + t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args) + } + + x.OverrideFile = "../../test/dummy-xapp_values.json" + expectedArgs += " -f=/tmp/appmgr_override.yaml" + if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs { + t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args) + } } func writeTestUsernameFile() error { - f, err := os.Create(viper.GetString("helm.helm-username-file")) - if err != nil { - return err - } - _, err = f.WriteString("some-username") - f.Close() - return err + f, err := os.Create(viper.GetString("helm.helm-username-file")) + if err != nil { + return err + } + _, err = f.WriteString("some-username") + f.Close() + return err } func removeTestUsernameFile() error { - return os.Remove(viper.GetString("helm.helm-username-file")) + return os.Remove(viper.GetString("helm.helm-username-file")) } func writeTestPasswordFile() (err error) { - f, err := os.Create(viper.GetString("helm.helm-password-file")) - if err != nil { - return err - } + f, err := os.Create(viper.GetString("helm.helm-password-file")) + if err != nil { + return err + } - _, err = f.WriteString("some-password") - f.Close() - return err + _, err = f.WriteString("some-password") + f.Close() + return err } func removeTestPasswordFile() error { - return os.Remove(viper.GetString("helm.helm-password-file")) + return os.Remove(viper.GetString("helm.helm-password-file")) } func getXappData() (x models.Xapp) { - //name1 := "dummy-xapp-8984fc9fd-l6xch" - //name2 := "dummy-xapp-8984fc9fd-pp4hg" - x = generateXapp("dummy-xapp", "deployed", "1.0", "dummy-xapp-8984fc9fd-bkcbp", "running", "service-ricxapp-dummy-xapp-rmr.ricxapp", "4560") - //x.Instances = append(x.Instances, x.Instances[0]) - //x.Instances = append(x.Instances, x.Instances[0]) - //x.Instances[1].Name = &name1 - //x.Instances[2].Name = &name2 + //name1 := "dummy-xapp-8984fc9fd-l6xch" + //name2 := "dummy-xapp-8984fc9fd-pp4hg" + x = generateXapp("dummy-xapp", "deployed", "1.0", "dummy-xapp-8984fc9fd-bkcbp", "running", "service-ricxapp-dummy-xapp-rmr.ricxapp", "4560") + //x.Instances = append(x.Instances, x.Instances[0]) + //x.Instances = append(x.Instances, x.Instances[0]) + //x.Instances[1].Name = &name1 + //x.Instances[2].Name = &name2 - return x + return x } func generateXapp(name, status, ver, iname, istatus, ip, port string) (x models.Xapp) { - x.Name = &name - x.Status = status - x.Version = ver - p, _ := strconv.Atoi(port) - var msgs appmgr.RtmData + x.Name = &name + x.Status = status + x.Version = ver + p, _ := strconv.Atoi(port) + var msgs appmgr.RtmData - instance := &models.XappInstance{ - Name: &iname, - Status: istatus, - IP: ip, - Port: int64(p), - TxMessages: msgs.TxMessages, - RxMessages: msgs.RxMessages, - } - x.Instances = append(x.Instances, instance) + instance := &models.XappInstance{ + Name: &iname, + Status: istatus, + IP: ip, + Port: int64(p), + TxMessages: msgs.TxMessages, + RxMessages: msgs.RxMessages, + } + x.Instances = append(x.Instances, instance) - return + return } func mockedKubeExec(args string) (out []byte, err error) { - caughtKubeExecArgs = args - return []byte(kubeExecRetOut), kubeExecRetErr + caughtKubeExecArgs = args + return []byte(kubeExecRetOut), kubeExecRetErr } func resetKubeExecMock() { - kubeExec = util.KubectlExec - caughtKubeExecArgs = "" - kubeExecRetOut = "" - kubeExecRetErr = nil + kubeExec = util.KubectlExec + caughtKubeExecArgs = "" + kubeExecRetOut = "" + kubeExecRetErr = nil } func mockedHelmExec(args string) (out []byte, err error) { - caughtHelmExecArgs = args - return []byte(helmExecRetOut), helmExecRetErr + caughtHelmExecArgs = args + return []byte(helmExecRetOut), helmExecRetErr } func resetHelmExecMock() { - helmExec = util.HelmExec - caughtHelmExecArgs = "" - helmExecRetOut = "" - helmExecRetErr = nil + helmExec = util.HelmExec + caughtHelmExecArgs = "" + helmExecRetOut = "" + helmExecRetErr = nil } func validateXappModel(t *testing.T, xapp models.Xapp) { - expXapp := getXappData() - xapp.Version = "1.0" + expXapp := getXappData() + xapp.Version = "1.0" - if *expXapp.Name != *xapp.Name || expXapp.Status != xapp.Status || expXapp.Version != xapp.Version { - t.Errorf("\n%v \n%v", *xapp.Name, *expXapp.Name) - } + if *expXapp.Name != *xapp.Name || expXapp.Status != xapp.Status || expXapp.Version != xapp.Version { + t.Errorf("\n%v \n%v", *xapp.Name, *expXapp.Name) + } - if *expXapp.Instances[0].Name != *xapp.Instances[0].Name || expXapp.Instances[0].Status != xapp.Instances[0].Status { - t.Errorf("\n1:%v 2:%v", *expXapp.Instances[0].Name, *xapp.Instances[0].Name) - } + if *expXapp.Instances[0].Name != *xapp.Instances[0].Name || expXapp.Instances[0].Status != xapp.Instances[0].Status { + t.Errorf("\n1:%v 2:%v", *expXapp.Instances[0].Name, *xapp.Instances[0].Name) + } - if expXapp.Instances[0].IP != xapp.Instances[0].IP || expXapp.Instances[0].Port != xapp.Instances[0].Port { - t.Errorf("\n%v - %v, %v - %v", expXapp.Instances[0].IP, xapp.Instances[0].IP, expXapp.Instances[0].Port, xapp.Instances[0].Port) - } + if expXapp.Instances[0].IP != xapp.Instances[0].IP || expXapp.Instances[0].Port != xapp.Instances[0].Port { + t.Errorf("\n%v - %v, %v - %v", expXapp.Instances[0].IP, xapp.Instances[0].IP, expXapp.Instances[0].Port, xapp.Instances[0].Port) + } }