Logging & error handling
[ealt-edge.git] / mecm / mepm / applcm / k8shelm / pkg / plugin / helmclient.go
1 /*
2  * Copyright 2020 Huawei Technologies Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package plugin
17
18 import (
19         "bytes"
20         "fmt"
21         "github.com/sirupsen/logrus"
22         "helm.sh/helm/v3/pkg/action"
23         "helm.sh/helm/v3/pkg/chart/loader"
24         "helm.sh/helm/v3/pkg/kube"
25         "os"
26 )
27
28 // Constants to be taken from deployment file
29 const (
30         releaseNamespace  = "default"
31         chartPath  = "/go/release/charts/"
32         kubeconfigPath  = "/go/release/kubeconfig/"
33 )
34
35 // Helm client
36 type HelmClient struct {
37         hostIP string
38         kubeconfig string
39         logger *logrus.Logger
40 }
41
42 // Constructor of helm client for a given host IP
43 func NewHelmClient(hostIP string, logger *logrus.Logger) (*HelmClient, error) {
44         // Kubeconfig file will be picked based on host IP and will be check for existence
45         exists, err := fileExists(kubeconfigPath + hostIP)
46         if exists {
47                 return &HelmClient{hostIP: hostIP, kubeconfig: kubeconfigPath + hostIP, logger: logger}, nil
48         } else {
49                 logger.Errorf("No file exist with name: %s. Err: %s", kubeconfigPath + hostIP)
50                 return nil, err
51         }
52 }
53
54 // Install a given helm chart
55 func (hc *HelmClient) installChart(helmPkg bytes.Buffer) (string, error) {
56         hc.logger.Info("Inside helm client")
57
58         // Create temporary file to hold helm chart
59         file, err := os.Create(chartPath + "temp.tar.gz")
60         if err != nil {
61                 hc.logger.Errorf("Unable to create file: %s. Err: %s", chartPath + "temp.tar.gz", err)
62                 return "", err
63         }
64         defer os.Remove(chartPath + "temp.tar.gz")
65
66         // Write input bytes to temp file
67         _, err = helmPkg.WriteTo(file)
68         if err != nil {
69                 hc.logger.Errorf("Unable to write to file: %s. Err: %s", chartPath + "temp.tar.gz", err)
70                 return "", err
71         }
72
73         // Load the file to chart
74         chart, err := loader.Load(chartPath + "temp.tar.gz")
75         if err != nil {
76                 hc.logger.Errorf("Unable to load chart from file: %s. Err: %s", chartPath + "temp.tar.gz", err)
77                 return "", err
78         }
79
80         // Release name will be taken from the name in chart's metadata
81         relName := chart.Metadata.Name
82
83         // Initialize action config
84         actionConfig := new(action.Configuration)
85         if err := actionConfig.Init(kube.GetConfig(hc.kubeconfig, "", releaseNamespace), releaseNamespace,
86                 os.Getenv("HELM_DRIVER"), func(format string, v ...interface{}) {
87                 fmt.Sprintf(format, v)
88                 }); err != nil {
89                 hc.logger.Errorf("Unable to initialize action config Err: %s", err)
90                 return "", err
91                 }
92
93         // Prepare chart install action and install chart
94         installer := action.NewInstall(actionConfig)
95         installer.Namespace = releaseNamespace
96         installer.ReleaseName = relName
97         rel, err := installer.Run(chart, nil)
98         if err != nil {
99                 hc.logger.Errorf("Unable to install chart with release name: %s. Err: %s", relName, err)
100                 return "", err
101         }
102         hc.logger.Info("Successfully create chart with release name: %s", relName)
103         return rel.Name, err
104 }
105
106 // Un-Install a given helm chart
107 func (hc *HelmClient) uninstallChart(relName string) (error) {
108         // Prepare action config and uninstall chart
109         actionConfig := new(action.Configuration)
110         if err := actionConfig.Init(kube.GetConfig(hc.kubeconfig, "", releaseNamespace), releaseNamespace,
111                 os.Getenv("HELM_DRIVER"), func(format string, v ...interface{}) {
112                 fmt.Sprintf(format, v)
113                 }); err != nil {
114                 hc.logger.Errorf("Unable to initialize action config Err: %s", err)
115                 return err
116                 }
117
118         ui := action.NewUninstall(actionConfig)
119         res, err := ui.Run(relName);
120         if err != nil {
121                 hc.logger.Errorf("Unable to uninstall chart with release name: %s. Err: %s", relName, err)
122                 return err
123         }
124         hc.logger.Info("Successfully uninstalled chart with release name: %s. Response Info: %s", res.Release.Name, res.Info)
125         return nil
126 }
127
128 // Query a given chart
129 func (hc *HelmClient) queryChart(relName string) (string, error)  {
130         actionConfig := new(action.Configuration)
131         if err := actionConfig.Init(kube.GetConfig(hc.kubeconfig, "", releaseNamespace), releaseNamespace,
132                 os.Getenv("HELM_DRIVER"), func(format string, v ...interface{}) {
133                 fmt.Sprintf(format, v)
134         }); err != nil {
135                 hc.logger.Errorf("Unable to initialize action config Err: %s", err)
136                 return "", err
137         }
138         s := action.NewStatus(actionConfig)
139         res, err := s.Run(relName)
140         if err != nil {
141                 hc.logger.Errorf("Unable to query chart with release name: %s. Err: %s", relName, err)
142                 return "", err
143         }
144         return res.Info.Status.String(), nil
145 }
146
147 // fileExists checks if a file exists and is not a directory before we
148 // try using it to prevent further errors.
149 func fileExists(filename string) (bool, error) {
150         info, err := os.Stat(filename)
151         if os.IsNotExist(err) {
152                 return false, err
153         }
154         return !info.IsDir(), nil
155 }
156