Merge "mecm-mepm uninstall playbook added"
[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
17 package plugin
18
19 import (
20         "bytes"
21         "fmt"
22         "os"
23
24         "github.com/sirupsen/logrus"
25         "helm.sh/helm/v3/pkg/action"
26         "helm.sh/helm/v3/pkg/chart/loader"
27         "helm.sh/helm/v3/pkg/kube"
28 )
29
30 // Variables to be defined in deployment file
31 var (
32         chartPath        = os.Getenv("CHART_PATH")
33         kubeconfigPath   = os.Getenv("KUBECONFIG_DIR_PATH")
34         releaseNamespace = os.Getenv("RELEASE_NAMESPACE")
35 )
36
37 // Helm client
38 type HelmClient struct {
39         hostIP     string
40         kubeconfig string
41         logger     *logrus.Logger
42 }
43
44 // Constructor of helm client for a given host IP
45 func NewHelmClient(hostIP string, logger *logrus.Logger) (*HelmClient, error) {
46         // Kubeconfig file will be picked based on host IP and will be check for existence
47         exists, err := fileExists(kubeconfigPath + hostIP)
48         if exists {
49                 return &HelmClient{hostIP: hostIP, kubeconfig: kubeconfigPath + hostIP, logger: logger}, nil
50         } else {
51                 logger.Errorf("No file exist with name: %s. Err: %s", kubeconfigPath+hostIP)
52                 return nil, err
53         }
54 }
55
56 // Install a given helm chart
57 func (hc *HelmClient) installChart(helmPkg bytes.Buffer) (string, error) {
58         hc.logger.Debug("Inside helm client")
59
60         // Create temporary file to hold helm chart
61         file, err := os.Create(chartPath + "temp.tar.gz")
62         if err != nil {
63                 hc.logger.Errorf("Unable to create file: %s. Err: %s", chartPath+"temp.tar.gz", err)
64                 return "", err
65         }
66         defer os.Remove(chartPath + "temp.tar.gz")
67
68         // Write input bytes to temp file
69         _, err = helmPkg.WriteTo(file)
70         if err != nil {
71                 hc.logger.Errorf("Unable to write to file: %s. Err: %s", chartPath+"temp.tar.gz", err)
72                 return "", err
73         }
74
75         // Load the file to chart
76         chart, err := loader.Load(chartPath + "temp.tar.gz")
77         if err != nil {
78                 hc.logger.Errorf("Unable to load chart from file: %s. Err: %s", chartPath+"temp.tar.gz", err)
79                 return "", err
80         }
81
82         // Release name will be taken from the name in chart's metadata
83         relName := chart.Metadata.Name
84
85         // Initialize action config
86         actionConfig := new(action.Configuration)
87         if err := actionConfig.Init(kube.GetConfig(hc.kubeconfig, "", releaseNamespace), releaseNamespace,
88                 os.Getenv("HELM_DRIVER"), func(format string, v ...interface{}) {
89                         fmt.Sprintf(format, v)
90                 }); err != nil {
91                 hc.logger.Errorf("Unable to initialize action config Err: %s", err)
92                 return "", err
93         }
94
95         // Prepare chart install action and install chart
96         installer := action.NewInstall(actionConfig)
97         installer.Namespace = releaseNamespace
98         installer.ReleaseName = relName
99         rel, err := installer.Run(chart, nil)
100         if err != nil {
101                 hc.logger.Errorf("Unable to install chart with release name: %s. Err: %s", relName, err)
102                 return "", err
103         }
104         hc.logger.Infof("Successfully create chart with release name: %s", relName)
105         return rel.Name, err
106 }
107
108 // Un-Install a given helm chart
109 func (hc *HelmClient) uninstallChart(relName string) error {
110         // Prepare action config and uninstall chart
111         actionConfig := new(action.Configuration)
112         if err := actionConfig.Init(kube.GetConfig(hc.kubeconfig, "", releaseNamespace), releaseNamespace,
113                 os.Getenv("HELM_DRIVER"), func(format string, v ...interface{}) {
114                         fmt.Sprintf(format, v)
115                 }); err != nil {
116                 hc.logger.Errorf("Unable to initialize action config Err: %s", err)
117                 return err
118         }
119
120         ui := action.NewUninstall(actionConfig)
121         res, err := ui.Run(relName)
122         if err != nil {
123                 hc.logger.Errorf("Unable to uninstall chart with release name: %s. Err: %s", relName, err)
124                 return err
125         }
126         hc.logger.Infof("Successfully uninstalled chart with release name: %s. Response Info: %s", res.Release.Name, res.Info)
127         return nil
128 }
129
130 // Query a given chart
131 func (hc *HelmClient) queryChart(relName string) (string, error) {
132         actionConfig := new(action.Configuration)
133         if err := actionConfig.Init(kube.GetConfig(hc.kubeconfig, "", releaseNamespace), releaseNamespace,
134                 os.Getenv("HELM_DRIVER"), func(format string, v ...interface{}) {
135                         fmt.Sprintf(format, v)
136                 }); err != nil {
137                 hc.logger.Errorf("Unable to initialize action config Err: %s", err)
138                 return "", err
139         }
140         s := action.NewStatus(actionConfig)
141         res, err := s.Run(relName)
142         if err != nil {
143                 hc.logger.Errorf("Unable to query chart with release name: %s. Err: %s", relName, err)
144                 return "", err
145         }
146         return res.Info.Status.String(), nil
147 }
148
149 // fileExists checks if a file exists and is not a directory before we
150 // try using it to prevent further errors.
151 func fileExists(filename string) (bool, error) {
152         info, err := os.Stat(filename)
153         if os.IsNotExist(err) {
154                 return false, err
155         }
156         return !info.IsDir(), nil
157 }