github.com/verrazzano/verrazzano@v1.7.1/tools/charts-manager/vcm/pkg/helm/helm.go (about) 1 // Copyright (c) 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package helm 5 6 import ( 7 "fmt" 8 "os" 9 10 "github.com/verrazzano/verrazzano/tools/vz/pkg/helpers" 11 "helm.sh/helm/v3/pkg/action" 12 "helm.sh/helm/v3/pkg/cli" 13 "helm.sh/helm/v3/pkg/getter" 14 "helm.sh/helm/v3/pkg/registry" 15 helmrepo "helm.sh/helm/v3/pkg/repo" 16 ) 17 18 // HelmConfig represents any helm and related operations done for a helm chart. 19 type HelmConfig interface { 20 AddAndUpdateChartRepo(string, string) (string, error) 21 DownloadChart(string, string, string, string, string) error 22 GetChartProvenance(string, string, string) (*ChartProvenance, error) 23 } 24 25 // VerrazzanoHelmConfig is default implementation of HelmConfig. 26 type VerrazzanoHelmConfig struct { 27 settings *cli.EnvSettings 28 helmRepoFile *helmrepo.File 29 vzHelper helpers.VZHelper 30 } 31 32 // ChartProvenance represents the upstream provenance information about a chart version by reading/creating 33 // the helm repository config and cache. 34 type ChartProvenance struct { 35 // UpstreamVersion is version of the upstream chart. 36 UpstreamVersion string `yaml:"upstreamVersion"` 37 // UpstreamChartLocalPath contains relative path to upstream chart directory. 38 UpstreamChartLocalPath string `yaml:"upstreamChartLocalPath"` 39 // UpstreamIndexEntry is the index.yaml entry for the upstream chart. 40 UpstreamIndexEntry *helmrepo.ChartVersion `yaml:"upstreamIndexEntry"` 41 } 42 43 // NewHelmConfig initializes the default HelmConfig instance. 44 func NewHelmConfig(vzHelper helpers.VZHelper) (*VerrazzanoHelmConfig, error) { 45 helmConfig := &VerrazzanoHelmConfig{vzHelper: vzHelper} 46 helmConfig.settings = cli.New() 47 if helmConfig.settings.RepositoryConfig == "" { 48 helmConfig.settings.RepositoryConfig = "/tmp/vz_helm_repo.yaml" 49 } 50 51 if helmConfig.settings.RepositoryCache == "" { 52 helmConfig.settings.RepositoryCache = "/tmp/vz_helm_repo_cache" 53 } 54 55 err := os.MkdirAll(helmConfig.settings.RepositoryCache, 0755) 56 if err != nil { 57 return nil, err 58 } 59 60 if _, err = os.Stat(helmConfig.settings.RepositoryConfig); err != nil { 61 err = helmrepo.NewFile().WriteFile(helmConfig.settings.RepositoryConfig, 0o644) 62 if err != nil { 63 return nil, err 64 } 65 } 66 67 helmConfig.helmRepoFile, err = helmrepo.LoadFile(helmConfig.settings.RepositoryConfig) 68 if err != nil { 69 return nil, err 70 } 71 return helmConfig, nil 72 } 73 74 // AddAndUpdateChartRepo creates/updates the local helm repo for the given repoURL and 75 // downloads the index file. 76 func (h VerrazzanoHelmConfig) AddAndUpdateChartRepo(chart string, repoURL string) (string, error) { 77 repoEntry, err := h.getRepoEntry(repoURL) 78 if err != nil { 79 return "", err 80 } 81 82 if repoEntry == nil { 83 repoEntry = &helmrepo.Entry{ 84 Name: fmt.Sprintf("%s-provider", chart), 85 URL: repoURL, 86 } 87 fmt.Fprintf(h.vzHelper.GetOutputStream(), "Adding helm repo %s.\n", repoEntry.Name) 88 } else { 89 fmt.Fprintf(h.vzHelper.GetOutputStream(), "Using helm repo %s\n", repoEntry.Name) 90 } 91 92 chartRepo, err := helmrepo.NewChartRepository(repoEntry, getter.All(h.settings)) 93 if err != nil { 94 return "", err 95 } 96 97 _, err = chartRepo.DownloadIndexFile() 98 if err != nil { 99 return "", err 100 } 101 102 h.helmRepoFile.Update(repoEntry) 103 return repoEntry.Name, h.helmRepoFile.WriteFile(h.settings.RepositoryConfig, 0o644) 104 } 105 106 // DownloadChart pulls a chart from the remote helm repo and untars into the chart ditrectory. 107 func (h VerrazzanoHelmConfig) DownloadChart(chart string, repo string, version string, targetVersion string, chartDir string) error { 108 registryClient, err := registry.NewClient( 109 registry.ClientOptDebug(h.settings.Debug), 110 registry.ClientOptCredentialsFile(h.settings.RegistryConfig), 111 ) 112 if err != nil { 113 return err 114 } 115 116 config := &action.Configuration{ 117 RegistryClient: registryClient, 118 } 119 120 pull := action.NewPullWithOpts(action.WithConfig(config)) 121 pull.Untar = true 122 pull.UntarDir = fmt.Sprintf("%s/%s/%s", chartDir, chart, targetVersion) 123 pull.Settings = cli.New() 124 pull.Version = version 125 err = os.RemoveAll(pull.UntarDir) 126 if err != nil { 127 return err 128 } 129 130 out, err := pull.Run(fmt.Sprintf("%s/%s", repo, chart)) 131 if out != "" { 132 fmt.Fprintln(h.vzHelper.GetOutputStream(), out) 133 } 134 135 return err 136 } 137 138 // GetChartProvenance creates the provenance data for a chart version against its upstream. 139 func (h VerrazzanoHelmConfig) GetChartProvenance(chart string, repo string, version string) (*ChartProvenance, error) { 140 repoEntry, err := h.getRepoEntry(repo) 141 if err != nil { 142 return nil, err 143 } 144 145 indexPath := fmt.Sprintf("%s/%s-index.yaml", h.settings.RepositoryCache, repoEntry.Name) 146 if _, err = os.Stat(indexPath); err != nil { 147 return nil, err 148 } 149 150 indexFile, err := helmrepo.LoadIndexFile(indexPath) 151 if err != nil { 152 return nil, err 153 } 154 155 chartVersion, err := indexFile.Get(chart, version) 156 if err != nil { 157 return nil, err 158 } 159 160 return &ChartProvenance{ 161 UpstreamVersion: version, 162 UpstreamChartLocalPath: fmt.Sprintf("upstreams/%s", version), 163 UpstreamIndexEntry: chartVersion, 164 }, nil 165 } 166 167 func (h VerrazzanoHelmConfig) getRepoEntry(repoURL string) (*helmrepo.Entry, error) { 168 for _, repoEntry := range h.helmRepoFile.Repositories { 169 if repoEntry.URL == repoURL { 170 return repoEntry, nil 171 } 172 } 173 174 return nil, fmt.Errorf("could not find repo entry") 175 }