Improve logging in helmmanager
[nonrtric/plt/sme.git] / capifcore / internal / helmmanagement / helm.go
1 // -
2 //   ========================LICENSE_START=================================
3 //   O-RAN-SC
4 //   %%
5 //   Copyright (C) 2022: Nordix Foundation
6 //   %%
7 //   Licensed under the Apache License, Version 2.0 (the "License");
8 //   you may not use this file except in compliance with the License.
9 //   You may obtain a copy of the License at
10 //
11 //        http://www.apache.org/licenses/LICENSE-2.0
12 //
13 //   Unless required by applicable law or agreed to in writing, software
14 //   distributed under the License is distributed on an "AS IS" BASIS,
15 //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 //   See the License for the specific language governing permissions and
17 //   limitations under the License.
18 //   ========================LICENSE_END===================================
19 //
20
21 package helmmanagement
22
23 import (
24         "fmt"
25         "io/fs"
26         "os"
27         "path/filepath"
28
29         "github.com/pkg/errors"
30         log "github.com/sirupsen/logrus"
31         "gopkg.in/yaml.v2"
32         "helm.sh/helm/v3/pkg/action"
33         "helm.sh/helm/v3/pkg/chart/loader"
34         "helm.sh/helm/v3/pkg/cli"
35         "helm.sh/helm/v3/pkg/getter"
36         "helm.sh/helm/v3/pkg/kube"
37         "helm.sh/helm/v3/pkg/repo"
38         "k8s.io/cli-runtime/pkg/genericclioptions"
39         "k8s.io/client-go/rest"
40 )
41
42 //go:generate mockery --name HelmManager
43 type HelmManager interface {
44         AddToRepo(repoName, url string) error
45         InstallHelmChart(namespace, repoName, chartName, releaseName string) error
46         UninstallHelmChart(namespace, chartName string)
47 }
48
49 type helmManagerImpl struct {
50         settings *cli.EnvSettings
51 }
52
53 func NewHelmManager(s *cli.EnvSettings) *helmManagerImpl {
54         return &helmManagerImpl{
55                 settings: s,
56         }
57 }
58
59 func (hm *helmManagerImpl) AddToRepo(repoName, url string) error {
60         repoFile := hm.settings.RepositoryConfig
61
62         //Ensure the file directory exists as it is required for file locking
63         err := os.MkdirAll(filepath.Dir(repoFile), os.ModePerm)
64         if err != nil && !errors.Is(err, fs.ErrNotExist) {
65                 return err
66         }
67
68         b, err := os.ReadFile(repoFile)
69         if err != nil {
70                 return err
71         }
72
73         var f repo.File
74         if err := yaml.Unmarshal(b, &f); err != nil {
75                 return err
76         }
77
78         if f.Has(repoName) {
79                 log.Debugf("repository name (%s) already exists\n", repoName)
80                 return nil
81         }
82
83         c := repo.Entry{
84                 Name: repoName,
85                 URL:  url,
86         }
87
88         r, err := repo.NewChartRepository(&c, getter.All(hm.settings))
89         if err != nil {
90                 return err
91         }
92
93         if _, err := r.DownloadIndexFile(); err != nil {
94                 err := errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", url)
95                 return err
96         }
97
98         f.Update(&c)
99
100         if err := f.WriteFile(repoFile, 0644); err != nil {
101                 return err
102         }
103         log.Debugf("%q has been added to your repositories\n", repoName)
104         return nil
105 }
106
107 func (hm *helmManagerImpl) InstallHelmChart(namespace, repoName, chartName, releaseName string) error {
108         actionConfig, err := getActionConfig(namespace)
109         if err != nil {
110                 return err
111         }
112
113         install := action.NewInstall(actionConfig)
114
115         cp, err := install.ChartPathOptions.LocateChart(fmt.Sprintf("%s/%s", repoName, chartName), hm.settings)
116         if err != nil {
117                 log.Error("Unable to locate chart!")
118                 return err
119         }
120
121         chartRequested, err := loader.Load(cp)
122         if err != nil {
123                 log.Error("Unable to load chart path!")
124                 return err
125         }
126
127         install.Namespace = namespace
128         install.ReleaseName = releaseName
129         _, err = install.Run(chartRequested, nil)
130         if err != nil {
131                 log.Error("Unable to run chart!")
132                 return err
133         }
134         log.Debug("Successfully onboarded ", namespace, repoName, chartName, releaseName)
135         return nil
136 }
137
138 func (hm *helmManagerImpl) UninstallHelmChart(namespace, chartName string) {
139         actionConfig, err := getActionConfig(namespace)
140         if err != nil {
141                 log.Error("unable to get action config: ", err)
142                 return
143         }
144
145         iCli := action.NewUninstall(actionConfig)
146
147         resp, err := iCli.Run(chartName)
148         if err != nil {
149                 log.Error("Unable to uninstall chart ", chartName, err)
150                 return
151         }
152         log.Debug("Successfully uninstalled chart: ", resp.Release.Name)
153 }
154
155 func getActionConfig(namespace string) (*action.Configuration, error) {
156         actionConfig := new(action.Configuration)
157         // Create the rest config instance with ServiceAccount values loaded in them
158         config, err := rest.InClusterConfig()
159         if err != nil {
160                 // fallback to kubeconfig
161                 home, exists := os.LookupEnv("HOME")
162                 if !exists {
163                         home = "/root"
164                 }
165                 kubeconfigPath := filepath.Join(home, ".kube", "config")
166                 if envvar := os.Getenv("KUBECONFIG"); len(envvar) > 0 {
167                         kubeconfigPath = envvar
168                 }
169                 if err := actionConfig.Init(kube.GetConfig(kubeconfigPath, "", namespace), namespace, os.Getenv("HELM_DRIVER"), log.Debugf); err != nil {
170                         log.Error(err)
171                 }
172         } else {
173                 // Create the ConfigFlags struct instance with initialized values from ServiceAccount
174                 kubeConfig := genericclioptions.NewConfigFlags(false)
175                 kubeConfig.APIServer = &config.Host
176                 kubeConfig.BearerToken = &config.BearerToken
177                 kubeConfig.CAFile = &config.CAFile
178                 kubeConfig.Namespace = &namespace
179                 if err := actionConfig.Init(kubeConfig, namespace, os.Getenv("HELM_DRIVER"), log.Debugf); err != nil {
180                         log.Error(err)
181                 }
182         }
183         return actionConfig, err
184 }