2 ==================================================================================
3 Copyright (c) 2019 AT&T Intellectual Property.
4 Copyright (c) 2019 Nokia
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 ==================================================================================
26 "github.com/spf13/viper"
37 var execCommand = exec.Command
39 func Exec(args string) (out []byte, err error) {
40 cmd := execCommand("/bin/sh", "-c", args)
42 var stdout bytes.Buffer
43 var stderr bytes.Buffer
47 log.Println("Running command: ", cmd)
48 for i := 0; i < viper.GetInt("helm.retry"); i++ {
51 Logger.Error("Command '%s' failed with error: %v, retrying", args, err.Error()+stderr.String())
52 time.Sleep(time.Duration(2) * time.Second)
58 if err == nil && !strings.HasSuffix(os.Args[0], ".test") {
59 Logger.Info("command success: %s", stdout.String())
60 return stdout.Bytes(), nil
63 return stdout.Bytes(), errors.New(stderr.String())
66 var HelmExec = func(args string) (out []byte, err error) {
67 return Exec(strings.Join([]string{"helm", args}, " "))
70 var KubectlExec = func(args string) (out []byte, err error) {
71 return Exec(strings.Join([]string{"kubectl", args}, " "))
74 func (h *Helm) SetCM(cm ConfigMapper) {
78 func (h *Helm) Initialize() {
79 if h.initDone == true {
84 if _, err := h.Init(); err == nil {
85 Logger.Info("Helm init done successfully!")
88 Logger.Error("helm init failed, retyring ...")
89 time.Sleep(time.Duration(10) * time.Second)
93 if _, err := h.AddRepo(); err == nil {
94 Logger.Info("Helm repo added successfully")
97 Logger.Error("Helm repo addition failed, retyring ...")
98 time.Sleep(time.Duration(10) * time.Second)
104 func (h *Helm) Run(args string) (out []byte, err error) {
105 return HelmExec(args)
109 func (h *Helm) Init() (out []byte, err error) {
110 // Add Tiller address as environment variable
111 if err := addTillerEnv(); err != nil {
115 return HelmExec(strings.Join([]string{"init -c --skip-refresh"}, ""))
118 func (h *Helm) AddRepo() (out []byte, err error) {
119 // Get helm repo user name and password from files mounted by secret object
120 credFile, err := ioutil.ReadFile(viper.GetString("helm.helm-username-file"))
122 Logger.Error("helm_repo_username ReadFile failed: %v", err.Error())
126 username := " --username " + string(credFile)
128 credFile, err = ioutil.ReadFile(viper.GetString("helm.helm-password-file"))
130 Logger.Error("helm_repo_password ReadFile failed: %v", err.Error())
134 pwd := " --password " + string(credFile)
136 // Get internal helm repo name
137 rname := viper.GetString("helm.repo-name")
139 // Get helm repo address from values.yaml
140 repo := viper.GetString("helm.repo")
142 return HelmExec(strings.Join([]string{"repo add ", rname, " ", repo, username, pwd}, ""))
145 func (h *Helm) Install(m XappDeploy) (xapp Xapp, err error) {
146 out, err := h.Run(strings.Join([]string{"repo update "}, ""))
152 m.Namespace = h.cm.GetNamespace(m.Namespace)
154 if err = h.cm.GetConfigMap(m, &cm); err != nil {
155 out, err = h.Run(getInstallArgs(m, false))
159 return h.ParseStatus(m.Name, string(out))
162 // ConfigMap exists, try to override
163 out, err = h.Run(getInstallArgs(m, true))
165 return h.ParseStatus(m.Name, string(out))
168 cm, cmErr := h.cm.PurgeConfigMap(m)
169 out, err = h.Run(getInstallArgs(m, false))
175 cmErr = h.cm.RestoreConfigMap(m, cm)
177 return h.ParseStatus(m.Name, string(out))
180 func (h *Helm) Status(name string) (xapp Xapp, err error) {
181 out, err := h.Run(strings.Join([]string{"status ", name}, ""))
183 Logger.Error("Getting xapps status: %v", err.Error())
187 return h.ParseStatus(name, string(out))
190 func (h *Helm) StatusAll() (xapps []Xapp, err error) {
191 xappNameList, err := h.List()
193 Logger.Error("Helm list failed: %v", err.Error())
197 return h.parseAllStatus(xappNameList)
200 func (h *Helm) List() (names []string, err error) {
201 ns := h.cm.GetNamespace("")
202 out, err := h.Run(strings.Join([]string{"list --all --output yaml --namespace=", ns}, ""))
204 Logger.Error("Listing deployed xapps failed: %v", err.Error())
208 return h.GetNames(string(out))
211 func (h *Helm) SearchAll() (names []string) {
212 return h.cm.GetNamesFromHelmRepo()
215 func (h *Helm) Delete(name string) (xapp Xapp, err error) {
216 xapp, err = h.Status(name)
218 Logger.Error("Fetching xapp status failed: %v", err.Error())
222 _, err = h.Run(strings.Join([]string{"del --purge ", name}, ""))
226 func (h *Helm) Fetch(name, tarDir string) error {
227 if strings.HasSuffix(os.Args[0], ".test") {
231 rname := viper.GetString("helm.repo-name") + "/"
233 _, err := h.Run(strings.Join([]string{"fetch --untar --untardir ", tarDir, " ", rname, name}, ""))
238 func (h *Helm) GetVersion(name string) (version string) {
239 ns := h.cm.GetNamespace("")
240 out, err := h.Run(strings.Join([]string{"list --output yaml --namespace=", ns, " ", name}, ""))
245 var re = regexp.MustCompile(`AppVersion: .*`)
246 ver := re.FindStringSubmatch(string(out))
248 version = strings.Split(ver[0], ": ")[1]
249 version, _ = strconv.Unquote(version)
255 func (h *Helm) GetState(out string) (status string) {
256 re := regexp.MustCompile(`STATUS: .*`)
257 result := re.FindStringSubmatch(string(out))
259 status = strings.ToLower(strings.Split(result[0], ": ")[1])
265 func (h *Helm) GetAddress(out string) (ip, port string) {
267 re := regexp.MustCompile(`ClusterIP.*`)
268 addr := re.FindStringSubmatch(string(out))
270 fmt.Sscanf(addr[0], "%s %s %s %s", &tmp, &ip, &tmp, &port)
276 func (h *Helm) GetEndpointInfo(name string) (ip string, port int) {
277 ns := h.cm.GetNamespace("")
278 args := fmt.Sprintf(" get endpoints -o=jsonpath='{.subsets[*].addresses[*].ip}' service-%s-%s-rmr -n %s", ns, name, ns)
279 out, err := KubectlExec(args)
283 Logger.Info("Endpoint IP address of %s: %s", name, string(out))
285 // "service-<namespace>-<chartname>-rmr.<namespace>"
286 return "service-" + ns + "-" + name + "-rmr." + ns, 4560
289 func (h *Helm) GetNames(out string) (names []string, err error) {
290 re := regexp.MustCompile(`Name: .*`)
291 result := re.FindAllStringSubmatch(out, -1)
296 for _, v := range result {
297 xappName := strings.Split(v[0], ": ")[1]
298 if strings.Contains(xappName, "appmgr") == false {
299 names = append(names, xappName)
305 func (h *Helm) FillInstanceData(name string, out string, xapp *Xapp, msgs MessageTypes) {
306 ip, port := h.GetEndpointInfo(name)
308 Logger.Info("Endpoint IP address not found, using CluserIP")
309 ip, _ = h.GetAddress(out)
313 r := regexp.MustCompile(`.*(?s)(Running|Pending|Succeeded|Failed|Unknown).*?\r?\n\r?\n`)
314 result := r.FindStringSubmatch(string(out))
319 re := regexp.MustCompile(name + "-(\\w+-\\w+).*")
320 resources := re.FindAllStringSubmatch(string(result[0]), -1)
321 if resources != nil {
322 for _, v := range resources {
324 fmt.Sscanf(v[0], "%s %s %s", &x.Name, &tmp, &x.Status)
325 x.Status = strings.ToLower(x.Status)
328 x.TxMessages = msgs.TxMessages
329 x.RxMessages = msgs.RxMessages
330 xapp.Instances = append(xapp.Instances, x)
335 func (h *Helm) ParseStatus(name string, out string) (xapp Xapp, err error) {
337 xapp.Version = h.GetVersion(name)
338 xapp.Status = h.GetState(out)
340 h.FillInstanceData(name, out, &xapp, h.cm.GetMessages(name))
345 func (h *Helm) parseAllStatus(names []string) (xapps []Xapp, err error) {
348 for _, name := range names {
349 err := h.cm.ReadSchema(name, &XAppConfig{})
354 x, err := h.Status(name)
356 xapps = append(xapps, x)
363 func addTillerEnv() (err error) {
364 service := viper.GetString("helm.tiller-service")
365 namespace := viper.GetString("helm.tiller-namespace")
366 port := viper.GetString("helm.tiller-port")
368 if err = os.Setenv("HELM_HOST", service+"."+namespace+":"+port); err != nil {
369 Logger.Error("Tiller Env Setting Failed: %v", err.Error())
375 func getInstallArgs(x XappDeploy, cmOverride bool) (args string) {
376 args = args + " --namespace=" + x.Namespace
378 if x.ImageRepo != "" {
379 args = args + " --set global.repository=" + x.ImageRepo
382 if x.ServiceName != "" {
383 args = args + " --set ricapp.service.name=" + x.ServiceName
386 if x.Hostname != "" {
387 args = args + " --set ricapp.hostname=" + x.Hostname
390 if cmOverride == true {
391 args = args + " --set ricapp.appconfig.override=" + x.Name + "-appconfig"
394 rname := viper.GetString("helm.repo-name")
395 return fmt.Sprintf("install %s/%s --name=%s %s", rname, x.Name, x.Name, args)