diff --git a/cli/internal/cmd/upgradeapply.go b/cli/internal/cmd/upgradeapply.go index a944bb07fa..440c27c88f 100644 --- a/cli/internal/cmd/upgradeapply.go +++ b/cli/internal/cmd/upgradeapply.go @@ -423,10 +423,11 @@ func (u *upgradeApplyCmd) handleServiceUpgrade( } // Save the Helm charts for the upgrade to disk - if err := executor.SaveCharts(upgradeDir, u.fileHandler); err != nil { - return fmt.Errorf("saving Helm charts: %w", err) + chartDir := filepath.Join(upgradeDir, "helm-charts") + if err := executor.SaveCharts(chartDir, u.fileHandler); err != nil { + return fmt.Errorf("saving Helm charts to disk: %w", err) } - u.log.Debugf("Helm charts saved to %s", upgradeDir) + u.log.Debugf("Helm charts saved to %s", chartDir) if includesUpgrades { u.log.Debugf("Creating backup of CRDs and CRs") diff --git a/cli/internal/helm/BUILD.bazel b/cli/internal/helm/BUILD.bazel index 5cecd3f9a0..ef5dd8f7ee 100644 --- a/cli/internal/helm/BUILD.bazel +++ b/cli/internal/helm/BUILD.bazel @@ -6,6 +6,7 @@ go_library( srcs = [ "action.go", "actionfactory.go", + "chartutil.go", "ciliumhelper.go", "helm.go", "loader.go", diff --git a/cli/internal/helm/action.go b/cli/internal/helm/action.go index 1888326a13..98de35266c 100644 --- a/cli/internal/helm/action.go +++ b/cli/internal/helm/action.go @@ -16,21 +16,17 @@ import ( "github.com/edgelesssys/constellation/v2/internal/file" "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/cli" ) const ( // timeout is the maximum time given per helm action. - timeout = 10 * time.Minute - installChartDir = "helm-install" - upgradeChartDir = "helm-upgrade" + timeout = 10 * time.Minute ) type applyAction interface { Apply(context.Context) error - SaveChart(upgradeDir string, fileHandler file.Handler) error + SaveChart(chartsDir string, fileHandler file.Handler) error ReleaseName() string IsAtomic() bool } @@ -102,8 +98,8 @@ func (a *installAction) Apply(ctx context.Context) error { } // SaveChart saves the chart to the given directory under the `install/` subdirectory. -func (a *installAction) SaveChart(upgradeDir string, fileHandler file.Handler) error { - return saveChart(a.release, filepath.Join(upgradeDir, installChartDir), fileHandler) +func (a *installAction) SaveChart(chartsDir string, fileHandler file.Handler) error { + return saveChart(a.release, chartsDir, fileHandler) } func (a *installAction) apply(ctx context.Context) error { @@ -158,8 +154,8 @@ func (a *upgradeAction) Apply(ctx context.Context) error { } // SaveChart saves the chart to the given directory under the `upgrade/` subdirectory. -func (a *upgradeAction) SaveChart(upgradeDir string, fileHandler file.Handler) error { - return saveChart(a.release, filepath.Join(upgradeDir, upgradeChartDir), fileHandler) +func (a *upgradeAction) SaveChart(chartsDir string, fileHandler file.Handler) error { + return saveChart(a.release, chartsDir, fileHandler) } func (a *upgradeAction) apply(ctx context.Context) error { @@ -177,83 +173,13 @@ func (a *upgradeAction) IsAtomic() bool { return a.helmAction.Atomic } -func saveChart(release Release, actionDir string, fileHandler file.Handler) error { - if err := saveChartToDisk(release.Chart, actionDir, fileHandler); err != nil { - return fmt.Errorf("saving chart %s to %q: %w", release.ReleaseName, actionDir, err) +func saveChart(release Release, chartsDir string, fileHandler file.Handler) error { + if err := saveChartToDisk(release.Chart, chartsDir, fileHandler); err != nil { + return fmt.Errorf("saving chart %s to %q: %w", release.ReleaseName, chartsDir, err) } - if err := fileHandler.WriteYAML(filepath.Join(actionDir, release.Chart.Metadata.Name, "overrides.yaml"), release.Values); err != nil { - return fmt.Errorf("saving override values for chart %s to %q: %w", release.ReleaseName, actionDir, err) + if err := fileHandler.WriteYAML(filepath.Join(chartsDir, release.Chart.Metadata.Name, "overrides.yaml"), release.Values); err != nil { + return fmt.Errorf("saving override values for chart %s to %q: %w", release.ReleaseName, filepath.Join(chartsDir, release.Chart.Metadata.Name), err) } return nil } - -// saveChartToDisk saves a chart as files in a directory. -// -// This takes the chart name, and creates a new subdirectory inside of the given dest -// directory, writing the chart's contents to that subdirectory. -// Dependencies are written using the same format, instead of writing them as tar files -// -// View the SaveDir implementation in chartutil as reference: https://github.com/helm/helm/blob/3a31588ad33fe3b89af5a2a54ee1d25bfe6eaa5e/pkg/chartutil/save.go#L40 -func saveChartToDisk(c *chart.Chart, dest string, fileHandler file.Handler) error { - // Create the chart directory - outdir := filepath.Join(dest, c.Name()) - if fi, err := fileHandler.Stat(outdir); err == nil && !fi.IsDir() { - return fmt.Errorf("file %s already exists and is not a directory", outdir) - } - if err := fileHandler.MkdirAll(outdir); err != nil { - return err - } - - // Save the chart file. - if err := chartutil.SaveChartfile(filepath.Join(outdir, chartutil.ChartfileName), c.Metadata); err != nil { - return err - } - - // Save values.yaml - for _, f := range c.Raw { - if f.Name == chartutil.ValuesfileName { - vf := filepath.Join(outdir, chartutil.ValuesfileName) - if err := writeFile(vf, f.Data, fileHandler); err != nil { - return err - } - } - } - - // Save values.schema.json if it exists - if c.Schema != nil { - filename := filepath.Join(outdir, chartutil.SchemafileName) - if err := writeFile(filename, c.Schema, fileHandler); err != nil { - return err - } - } - - // Save templates and files - for _, o := range [][]*chart.File{c.Templates, c.Files} { - for _, f := range o { - n := filepath.Join(outdir, f.Name) - if err := writeFile(n, f.Data, fileHandler); err != nil { - return err - } - } - } - - // Save dependencies - base := filepath.Join(outdir, chartutil.ChartsDir) - for _, dep := range c.Dependencies() { - // Don't write dependencies as tar files - // Instead recursively use saveChartToDisk - if err := saveChartToDisk(dep, base, fileHandler); err != nil { - return fmt.Errorf("saving %s: %w", dep.ChartFullPath(), err) - } - } - - return nil -} - -func writeFile(name string, content []byte, fileHandler file.Handler) error { - if err := fileHandler.MkdirAll(filepath.Dir(name)); err != nil { - return err - } - return fileHandler.Write(name, content) -} diff --git a/cli/internal/helm/chartutil.go b/cli/internal/helm/chartutil.go new file mode 100644 index 0000000000..405b571759 --- /dev/null +++ b/cli/internal/helm/chartutil.go @@ -0,0 +1,86 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package helm + +import ( + "fmt" + "path/filepath" + + "github.com/edgelesssys/constellation/v2/internal/file" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chartutil" +) + +// saveChartToDisk saves a chart as files in a directory. +// +// This takes the chart name, and creates a new subdirectory inside of the given dest +// directory, writing the chart's contents to that subdirectory. +// Dependencies are written using the same format, instead of writing them as tar files +// +// View the SaveDir implementation in chartutil as reference: https://github.com/helm/helm/blob/3a31588ad33fe3b89af5a2a54ee1d25bfe6eaa5e/pkg/chartutil/save.go#L40 +func saveChartToDisk(c *chart.Chart, dest string, fileHandler file.Handler) error { + // Create the chart directory + outdir := filepath.Join(dest, c.Name()) + if fi, err := fileHandler.Stat(outdir); err == nil && !fi.IsDir() { + return fmt.Errorf("file %s already exists and is not a directory", outdir) + } + if err := fileHandler.MkdirAll(outdir); err != nil { + return err + } + + // Save the chart file. + if err := chartutil.SaveChartfile(filepath.Join(outdir, chartutil.ChartfileName), c.Metadata); err != nil { + return err + } + + // Save values.yaml + for _, f := range c.Raw { + if f.Name == chartutil.ValuesfileName { + vf := filepath.Join(outdir, chartutil.ValuesfileName) + if err := writeFile(vf, f.Data, fileHandler); err != nil { + return err + } + } + } + + // Save values.schema.json if it exists + if c.Schema != nil { + filename := filepath.Join(outdir, chartutil.SchemafileName) + if err := writeFile(filename, c.Schema, fileHandler); err != nil { + return err + } + } + + // Save templates and files + for _, o := range [][]*chart.File{c.Templates, c.Files} { + for _, f := range o { + n := filepath.Join(outdir, f.Name) + if err := writeFile(n, f.Data, fileHandler); err != nil { + return err + } + } + } + + // Save dependencies + base := filepath.Join(outdir, chartutil.ChartsDir) + for _, dep := range c.Dependencies() { + // Don't write dependencies as tar files + // Instead recursively use saveChartToDisk + if err := saveChartToDisk(dep, base, fileHandler); err != nil { + return fmt.Errorf("saving %s: %w", dep.ChartFullPath(), err) + } + } + + return nil +} + +func writeFile(name string, content []byte, fileHandler file.Handler) error { + if err := fileHandler.MkdirAll(filepath.Dir(name)); err != nil { + return err + } + return fileHandler.Write(name, content) +} diff --git a/cli/internal/helm/helm.go b/cli/internal/helm/helm.go index 0d5efb0e0e..bc0977d54b 100644 --- a/cli/internal/helm/helm.go +++ b/cli/internal/helm/helm.go @@ -113,7 +113,7 @@ func (h Client) loadReleases( // Applier runs the Helm actions. type Applier interface { Apply(ctx context.Context) error - SaveCharts(upgradeDir string, fileHandler file.Handler) error + SaveCharts(chartsDir string, fileHandler file.Handler) error } // ChartApplyExecutor is a Helm action executor that applies all actions. @@ -134,9 +134,9 @@ func (c ChartApplyExecutor) Apply(ctx context.Context) error { } // SaveCharts saves all Helm charts and their values to the given directory. -func (c ChartApplyExecutor) SaveCharts(upgradeDir string, fileHandler file.Handler) error { +func (c ChartApplyExecutor) SaveCharts(chartsDir string, fileHandler file.Handler) error { for _, action := range c.actions { - if err := action.SaveChart(upgradeDir, fileHandler); err != nil { + if err := action.SaveChart(chartsDir, fileHandler); err != nil { return fmt.Errorf("saving chart %s: %w", action.ReleaseName(), err) } }