2 // ========================LICENSE_START=================================
5 // Copyright (C) 2022: Nordix Foundation
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
11 // http://www.apache.org/licenses/LICENSE-2.0
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===================================
21 package helmmanagement
29 "github.com/pkg/errors"
30 log "github.com/sirupsen/logrus"
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"
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)
49 type helmManagerImpl struct {
50 settings *cli.EnvSettings
51 repo *repo.ChartRepository
55 func NewHelmManager(s *cli.EnvSettings) *helmManagerImpl {
56 return &helmManagerImpl{
61 func (hm *helmManagerImpl) SetUpRepo(repoName, url string) error {
62 if len(strings.TrimSpace(url)) == 0 {
63 log.Info("No ChartMuseum repo set up.")
66 log.Debugf("Adding %s to Helm Repo\n", url)
67 repoFile := filepath.Join(filepath.Dir(hm.settings.RepositoryConfig), "index.yaml")
68 log.Debug("Repo file: ", repoFile)
71 Name: filepath.Dir(repoFile),
78 r, err = repo.NewChartRepository(&c, getter.All(hm.settings))
84 //Ensure the file directory exists as it is required for file locking
85 err = os.MkdirAll(filepath.Dir(repoFile), os.ModePerm)
86 if err != nil && !errors.Is(err, os.ErrNotExist) {
87 log.Error("Unable to create folder for Helm.")
91 b, err := os.ReadFile(repoFile)
93 log.Info("Creating repo file: ", repoFile)
96 log.Error("Unable to create repo file: ", repoFile)
99 b, err = os.ReadFile(repoFile)
101 log.Error("Unable to read repo file: ", repoFile)
107 if err := yaml.Unmarshal(b, &f); err != nil {
112 log.Debugf("repository name (%s) already exists\n", repoName)
116 if _, err := r.DownloadIndexFile(); err != nil {
117 err = errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", url)
123 if err := f.WriteFile(repoFile, 0644); err != nil {
127 log.Debugf("%q has been added to your repositories\n", repoName)
131 func (hm *helmManagerImpl) InstallHelmChart(namespace, repoName, chartName, releaseName string) error {
133 log.Warnf("Helm repo not added, so chart %s not installed", chartName)
136 actionConfig, err := getActionConfig(namespace)
141 install := action.NewInstall(actionConfig)
143 cp, err := install.ChartPathOptions.LocateChart(fmt.Sprintf("%s/%s", repoName, chartName), hm.settings)
145 log.Error("Unable to locate chart!")
149 chartRequested, err := loader.Load(cp)
151 log.Error("Unable to load chart path!")
155 install.Namespace = namespace
156 install.ReleaseName = releaseName
157 _, err = install.Run(chartRequested, nil)
159 log.Error("Unable to run chart!")
162 log.Debug("Successfully onboarded ", namespace, repoName, chartName, releaseName)
166 func (hm *helmManagerImpl) UninstallHelmChart(namespace, chartName string) {
167 actionConfig, err := getActionConfig(namespace)
169 log.Error("unable to get action config: ", err)
173 iCli := action.NewUninstall(actionConfig)
175 resp, err := iCli.Run(chartName)
177 log.Error("Unable to uninstall chart ", chartName, err)
180 log.Debug("Successfully uninstalled chart: ", resp.Release.Name)
183 func getActionConfig(namespace string) (*action.Configuration, error) {
184 actionConfig := new(action.Configuration)
185 // Create the rest config instance with ServiceAccount values loaded in them
186 config, err := rest.InClusterConfig()
188 // fallback to kubeconfig
189 home, exists := os.LookupEnv("HOME")
193 kubeconfigPath := filepath.Join(home, ".kube", "config")
194 if envvar := os.Getenv("KUBECONFIG"); len(envvar) > 0 {
195 kubeconfigPath = envvar
197 if err := actionConfig.Init(kube.GetConfig(kubeconfigPath, "", namespace), namespace, os.Getenv("HELM_DRIVER"), log.Debugf); err != nil {
201 // Create the ConfigFlags struct instance with initialized values from ServiceAccount
202 kubeConfig := genericclioptions.NewConfigFlags(false)
203 kubeConfig.APIServer = &config.Host
204 kubeConfig.BearerToken = &config.BearerToken
205 kubeConfig.CAFile = &config.CAFile
206 kubeConfig.Namespace = &namespace
207 if err := actionConfig.Init(kubeConfig, namespace, os.Getenv("HELM_DRIVER"), log.Debugf); err != nil {
211 return actionConfig, err