99c27cc9a2f5390086db3207f49346ec50a5ff6d
[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         SetUpRepo(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         repo     *repo.ChartRepository
52 }
53
54 func NewHelmManager(s *cli.EnvSettings) *helmManagerImpl {
55         return &helmManagerImpl{
56                 settings: s,
57         }
58 }
59
60 func (hm *helmManagerImpl) SetUpRepo(repoName, url string) error {
61         repoFile := hm.settings.RepositoryConfig
62
63         //Ensure the file directory exists as it is required for file locking
64         err := os.MkdirAll(filepath.Dir(repoFile), os.ModePerm)
65         if err != nil && !errors.Is(err, fs.ErrNotExist) {
66                 return err
67         }
68
69         b, err := os.ReadFile(repoFile)
70         if err != nil {
71                 return err
72         }
73
74         var f repo.File
75         if err := yaml.Unmarshal(b, &f); err != nil {
76                 return err
77         }
78
79         if f.Has(repoName) {
80                 log.Debugf("repository name (%s) already exists\n", repoName)
81                 return nil
82         }
83
84         c := repo.Entry{
85                 Name: repoName,
86                 URL:  url,
87         }
88
89         r := hm.repo
90         if r == nil {
91                 r, err = repo.NewChartRepository(&c, getter.All(hm.settings))
92                 if err != nil {
93                         return err
94                 }
95         }
96
97         if _, err := r.DownloadIndexFile(); err != nil {
98                 err := errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", url)
99                 return err
100         }
101
102         f.Update(&c)
103
104         if err := f.WriteFile(repoFile, 0644); err != nil {
105                 return err
106         }
107         log.Debugf("%q has been added to your repositories\n", repoName)
108         return nil
109 }
110
111 func (hm *helmManagerImpl) InstallHelmChart(namespace, repoName, chartName, releaseName string) error {
112         actionConfig, err := getActionConfig(namespace)
113         if err != nil {
114                 return err
115         }
116
117         install := action.NewInstall(actionConfig)
118
119         cp, err := install.ChartPathOptions.LocateChart(fmt.Sprintf("%s/%s", repoName, chartName), hm.settings)
120         if err != nil {
121                 log.Error("Unable to locate chart!")
122                 return err
123         }
124
125         chartRequested, err := loader.Load(cp)
126         if err != nil {
127                 log.Error("Unable to load chart path!")
128                 return err
129         }
130
131         install.Namespace = namespace
132         install.ReleaseName = releaseName
133         _, err = install.Run(chartRequested, nil)
134         if err != nil {
135                 log.Error("Unable to run chart!")
136                 return err
137         }
138         log.Debug("Successfully onboarded ", namespace, repoName, chartName, releaseName)
139         return nil
140 }
141
142 func (hm *helmManagerImpl) UninstallHelmChart(namespace, chartName string) {
143         actionConfig, err := getActionConfig(namespace)
144         if err != nil {
145                 log.Error("unable to get action config: ", err)
146                 return
147         }
148
149         iCli := action.NewUninstall(actionConfig)
150
151         resp, err := iCli.Run(chartName)
152         if err != nil {
153                 log.Error("Unable to uninstall chart ", chartName, err)
154                 return
155         }
156         log.Debug("Successfully uninstalled chart: ", resp.Release.Name)
157 }
158
159 func getActionConfig(namespace string) (*action.Configuration, error) {
160         actionConfig := new(action.Configuration)
161         // Create the rest config instance with ServiceAccount values loaded in them
162         config, err := rest.InClusterConfig()
163         if err != nil {
164                 // fallback to kubeconfig
165                 home, exists := os.LookupEnv("HOME")
166                 if !exists {
167                         home = "/root"
168                 }
169                 kubeconfigPath := filepath.Join(home, ".kube", "config")
170                 if envvar := os.Getenv("KUBECONFIG"); len(envvar) > 0 {
171                         kubeconfigPath = envvar
172                 }
173                 if err := actionConfig.Init(kube.GetConfig(kubeconfigPath, "", namespace), namespace, os.Getenv("HELM_DRIVER"), log.Debugf); err != nil {
174                         log.Error(err)
175                 }
176         } else {
177                 // Create the ConfigFlags struct instance with initialized values from ServiceAccount
178                 kubeConfig := genericclioptions.NewConfigFlags(false)
179                 kubeConfig.APIServer = &config.Host
180                 kubeConfig.BearerToken = &config.BearerToken
181                 kubeConfig.CAFile = &config.CAFile
182                 kubeConfig.Namespace = &namespace
183                 if err := actionConfig.Init(kubeConfig, namespace, os.Getenv("HELM_DRIVER"), log.Debugf); err != nil {
184                         log.Error(err)
185                 }
186         }
187         return actionConfig, err
188 }