From 7fb8e1fd90c4ccbc6295c2da4ee5f151c7be55f4 Mon Sep 17 00:00:00 2001 From: Tam Mach Date: Sun, 4 Sep 2022 20:38:15 +1000 Subject: [PATCH] helm: Add support for remote chart version Currently, the cilium charts are embedded inside cilium cli binary, which will require a new cilium/charts bump in go.mod and then a new release for cilium-cli. It's making sense to do so if some other code changes are required to make installation working for new chart version, but most of the time, only go.mod upgrade is required. This commit is to add the capability to install new chart version without the need of releasing or installing new cilium-cli. The new chart will be downloaded and stored in user cache dir. Signed-off-by: Tam Mach --- internal/helm/helm.go | 96 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 10 deletions(-) diff --git a/internal/helm/helm.go b/internal/helm/helm.go index d818a2989f..28ebbd56ad 100644 --- a/internal/helm/helm.go +++ b/internal/helm/helm.go @@ -7,7 +7,11 @@ package helm import ( "bytes" "context" + "errors" "fmt" + "io/fs" + "os" + "path" "regexp" "sort" "strings" @@ -30,6 +34,8 @@ import ( corev1 "k8s.io/api/core/v1" ) +const ciliumChart = "https://helm.cilium.io" + var settings = cli.New() // State contains Helm state for the current Cilium installation. Cilium CLI retrieves this @@ -186,7 +192,7 @@ func newClient(namespace, k8sVersion string) (*action.Install, error) { func newChartFromEmbeddedFile(ciliumVersion semver2.Version) (*chart.Chart, error) { helmTgz, err := helm.HelmFS.ReadFile(fmt.Sprintf("cilium-%s.tgz", ciliumVersion)) if err != nil { - return nil, fmt.Errorf("cilium version not found: %s", err) + return nil, fmt.Errorf("cilium version not found: %w", err) } // Check chart dependencies to make sure all are present in /charts @@ -197,6 +203,55 @@ func newChartFromDirectory(directory string) (*chart.Chart, error) { return loader.LoadDir(directory) } +// newChartFromRemoteWithCache fetches the chart from remote repository, the chart file +// is then stored in the local cache directory for future usage. +func newChartFromRemoteWithCache(ciliumVersion semver2.Version) (*chart.Chart, error) { + cacheDir, err := ciliumCacheDir() + if err != nil { + return nil, err + } + + file := path.Join(cacheDir, fmt.Sprintf("cilium-%s.tgz", ciliumVersion)) + if _, err = os.Stat(file); err != nil { + if !errors.Is(err, fs.ErrNotExist) { + return nil, err + } + + // Download the chart from remote repository + pull := action.NewPullWithOpts(action.WithConfig(new(action.Configuration))) + pull.Settings = settings + pull.RepoURL = ciliumChart + pull.Version = ciliumVersion.String() + pull.DestDir = cacheDir + + if _, err = pull.Run("cilium"); err != nil { + return nil, err + } + } + + f, err := os.Open(file) + if err != nil { + return nil, err + } + defer f.Close() + return loader.LoadArchive(f) +} + +func ciliumCacheDir() (string, error) { + cacheDir, err := os.UserCacheDir() + if err != nil { + return "", err + } + + res := path.Join(cacheDir, "cilium-cli") + err = os.MkdirAll(res, 0755) + if err != nil && !os.IsExist(err) { + return "", err + } + + return res, nil +} + // GenManifests returns the generated manifests in a map that maps the manifest // name to its contents. func GenManifests( @@ -218,7 +273,13 @@ func GenManifests( } else { helmChart, err = newChartFromEmbeddedFile(ciliumVer) if err != nil { - return nil, err + if !errors.Is(err, fs.ErrNotExist) { + return nil, err + } + helmChart, err = newChartFromRemoteWithCache(ciliumVer) + if err != nil { + return nil, err + } } } @@ -328,14 +389,7 @@ func ListVersions() ([]string, error) { func ResolveHelmChartVersion(versionFlag, chartDirectoryFlag string) (semver2.Version, error) { if chartDirectoryFlag == "" { // If --chart-directory flag is not specified, use the version specified with --version flag. - version, err := utils.ParseCiliumVersion(versionFlag) - if err != nil { - return semver2.Version{}, err - } - if _, err = newChartFromEmbeddedFile(version); err != nil { - return semver2.Version{}, err - } - return version, nil + return resolveChartVersion(versionFlag) } // Get the chart version from the local Helm chart specified with --chart-directory flag. @@ -345,3 +399,25 @@ func ResolveHelmChartVersion(versionFlag, chartDirectoryFlag string) (semver2.Ve } return versioncheck.MustVersion(localChart.Metadata.Version), nil } + +func resolveChartVersion(versionFlag string) (semver2.Version, error) { + version, err := utils.ParseCiliumVersion(versionFlag) + if err != nil { + return semver2.Version{}, err + } + + _, err = newChartFromEmbeddedFile(version) + if err == nil { + return version, nil + } + + if !errors.Is(err, fs.ErrNotExist) { + return semver2.Version{}, err + } + + _, err = newChartFromRemoteWithCache(version) + if err != nil { + return semver2.Version{}, err + } + return version, nil +}