mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
cli: save Helm charts to disk before running upgrades (#2305)
Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
parent
6cb506bca7
commit
94a7b9e7b2
@ -232,6 +232,10 @@ func (s stubRunner) Apply(_ context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s stubRunner) SaveCharts(_ string, _ file.Handler) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetLogs(t *testing.T) {
|
func TestGetLogs(t *testing.T) {
|
||||||
someErr := errors.New("failed")
|
someErr := errors.New("failed")
|
||||||
|
|
||||||
|
@ -422,6 +422,13 @@ func (u *upgradeApplyCmd) handleServiceUpgrade(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save the Helm charts for the upgrade to disk
|
||||||
|
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", chartDir)
|
||||||
|
|
||||||
if includesUpgrades {
|
if includesUpgrades {
|
||||||
u.log.Debugf("Creating backup of CRDs and CRs")
|
u.log.Debugf("Creating backup of CRDs and CRs")
|
||||||
crds, err := u.kubeUpgrader.BackupCRDs(cmd.Context(), upgradeDir)
|
crds, err := u.kubeUpgrader.BackupCRDs(cmd.Context(), upgradeDir)
|
||||||
|
@ -6,6 +6,7 @@ go_library(
|
|||||||
srcs = [
|
srcs = [
|
||||||
"action.go",
|
"action.go",
|
||||||
"actionfactory.go",
|
"actionfactory.go",
|
||||||
|
"chartutil.go",
|
||||||
"ciliumhelper.go",
|
"ciliumhelper.go",
|
||||||
"helm.go",
|
"helm.go",
|
||||||
"loader.go",
|
"loader.go",
|
||||||
@ -429,6 +430,7 @@ go_library(
|
|||||||
"//internal/compatibility",
|
"//internal/compatibility",
|
||||||
"//internal/config",
|
"//internal/config",
|
||||||
"//internal/constants",
|
"//internal/constants",
|
||||||
|
"//internal/file",
|
||||||
"//internal/kms/uri",
|
"//internal/kms/uri",
|
||||||
"//internal/kubernetes/kubectl",
|
"//internal/kubernetes/kubectl",
|
||||||
"//internal/retry",
|
"//internal/retry",
|
||||||
@ -444,6 +446,7 @@ go_library(
|
|||||||
"@sh_helm_helm_v3//pkg/action",
|
"@sh_helm_helm_v3//pkg/action",
|
||||||
"@sh_helm_helm_v3//pkg/chart",
|
"@sh_helm_helm_v3//pkg/chart",
|
||||||
"@sh_helm_helm_v3//pkg/chart/loader",
|
"@sh_helm_helm_v3//pkg/chart/loader",
|
||||||
|
"@sh_helm_helm_v3//pkg/chartutil",
|
||||||
"@sh_helm_helm_v3//pkg/cli",
|
"@sh_helm_helm_v3//pkg/cli",
|
||||||
"@sh_helm_helm_v3//pkg/release",
|
"@sh_helm_helm_v3//pkg/release",
|
||||||
],
|
],
|
||||||
|
@ -9,9 +9,11 @@ package helm
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
|
|
||||||
"helm.sh/helm/v3/pkg/action"
|
"helm.sh/helm/v3/pkg/action"
|
||||||
"helm.sh/helm/v3/pkg/cli"
|
"helm.sh/helm/v3/pkg/cli"
|
||||||
@ -24,6 +26,7 @@ const (
|
|||||||
|
|
||||||
type applyAction interface {
|
type applyAction interface {
|
||||||
Apply(context.Context) error
|
Apply(context.Context) error
|
||||||
|
SaveChart(chartsDir string, fileHandler file.Handler) error
|
||||||
ReleaseName() string
|
ReleaseName() string
|
||||||
IsAtomic() bool
|
IsAtomic() bool
|
||||||
}
|
}
|
||||||
@ -94,6 +97,11 @@ func (a *installAction) Apply(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveChart saves the chart to the given directory under the `install/<chart-name>` subdirectory.
|
||||||
|
func (a *installAction) SaveChart(chartsDir string, fileHandler file.Handler) error {
|
||||||
|
return saveChart(a.release, chartsDir, fileHandler)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *installAction) apply(ctx context.Context) error {
|
func (a *installAction) apply(ctx context.Context) error {
|
||||||
_, err := a.helmAction.RunWithContext(ctx, a.release.Chart, a.release.Values)
|
_, err := a.helmAction.RunWithContext(ctx, a.release.Chart, a.release.Values)
|
||||||
return err
|
return err
|
||||||
@ -145,6 +153,11 @@ func (a *upgradeAction) Apply(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveChart saves the chart to the given directory under the `upgrade/<chart-name>` subdirectory.
|
||||||
|
func (a *upgradeAction) SaveChart(chartsDir string, fileHandler file.Handler) error {
|
||||||
|
return saveChart(a.release, chartsDir, fileHandler)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *upgradeAction) apply(ctx context.Context) error {
|
func (a *upgradeAction) apply(ctx context.Context) error {
|
||||||
_, err := a.helmAction.RunWithContext(ctx, a.release.ReleaseName, a.release.Chart, a.release.Values)
|
_, err := a.helmAction.RunWithContext(ctx, a.release.ReleaseName, a.release.Chart, a.release.Values)
|
||||||
return err
|
return err
|
||||||
@ -159,3 +172,14 @@ func (a *upgradeAction) ReleaseName() string {
|
|||||||
func (a *upgradeAction) IsAtomic() bool {
|
func (a *upgradeAction) IsAtomic() bool {
|
||||||
return a.helmAction.Atomic
|
return a.helmAction.Atomic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(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
|
||||||
|
}
|
||||||
|
86
cli/internal/helm/chartutil.go
Normal file
86
cli/internal/helm/chartutil.go
Normal file
@ -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)
|
||||||
|
}
|
@ -36,6 +36,7 @@ import (
|
|||||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
|
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/kubernetes/kubectl"
|
"github.com/edgelesssys/constellation/v2/internal/kubernetes/kubectl"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/semver"
|
"github.com/edgelesssys/constellation/v2/internal/semver"
|
||||||
@ -51,7 +52,6 @@ const (
|
|||||||
|
|
||||||
type debugLog interface {
|
type debugLog interface {
|
||||||
Debugf(format string, args ...any)
|
Debugf(format string, args ...any)
|
||||||
Sync()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client is a Helm client to apply charts.
|
// Client is a Helm client to apply charts.
|
||||||
@ -87,7 +87,10 @@ type Options struct {
|
|||||||
|
|
||||||
// PrepareApply loads the charts and returns the executor to apply them.
|
// PrepareApply loads the charts and returns the executor to apply them.
|
||||||
// TODO(elchead): remove validK8sVersion by putting ValidK8sVersion into config.Config, see AB#3374.
|
// TODO(elchead): remove validK8sVersion by putting ValidK8sVersion into config.Config, see AB#3374.
|
||||||
func (h Client) PrepareApply(conf *config.Config, validK8sversion versions.ValidK8sVersion, idFile clusterid.File, flags Options, tfOutput terraform.ApplyOutput, serviceAccURI string, masterSecret uri.MasterSecret) (Applier, bool, error) {
|
func (h Client) PrepareApply(
|
||||||
|
conf *config.Config, validK8sversion versions.ValidK8sVersion, idFile clusterid.File,
|
||||||
|
flags Options, tfOutput terraform.ApplyOutput, serviceAccURI string, masterSecret uri.MasterSecret,
|
||||||
|
) (Applier, bool, error) {
|
||||||
releases, err := h.loadReleases(conf, masterSecret, validK8sversion, idFile, flags, tfOutput, serviceAccURI)
|
releases, err := h.loadReleases(conf, masterSecret, validK8sversion, idFile, flags, tfOutput, serviceAccURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, fmt.Errorf("loading Helm releases: %w", err)
|
return nil, false, fmt.Errorf("loading Helm releases: %w", err)
|
||||||
@ -97,7 +100,10 @@ func (h Client) PrepareApply(conf *config.Config, validK8sversion versions.Valid
|
|||||||
return &ChartApplyExecutor{actions: actions, log: h.log}, includesUpgrades, err
|
return &ChartApplyExecutor{actions: actions, log: h.log}, includesUpgrades, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h Client) loadReleases(conf *config.Config, secret uri.MasterSecret, validK8sVersion versions.ValidK8sVersion, idFile clusterid.File, flags Options, tfOutput terraform.ApplyOutput, serviceAccURI string) ([]Release, error) {
|
func (h Client) loadReleases(
|
||||||
|
conf *config.Config, secret uri.MasterSecret, validK8sVersion versions.ValidK8sVersion,
|
||||||
|
idFile clusterid.File, flags Options, tfOutput terraform.ApplyOutput, serviceAccURI string,
|
||||||
|
) ([]Release, error) {
|
||||||
helmLoader := newLoader(conf, idFile, validK8sVersion, h.cliVersion)
|
helmLoader := newLoader(conf, idFile, validK8sVersion, h.cliVersion)
|
||||||
h.log.Debugf("Created new Helm loader")
|
h.log.Debugf("Created new Helm loader")
|
||||||
return helmLoader.loadReleases(flags.Conformance, flags.HelmWaitMode, secret,
|
return helmLoader.loadReleases(flags.Conformance, flags.HelmWaitMode, secret,
|
||||||
@ -107,6 +113,7 @@ func (h Client) loadReleases(conf *config.Config, secret uri.MasterSecret, valid
|
|||||||
// Applier runs the Helm actions.
|
// Applier runs the Helm actions.
|
||||||
type Applier interface {
|
type Applier interface {
|
||||||
Apply(ctx context.Context) error
|
Apply(ctx context.Context) error
|
||||||
|
SaveCharts(chartsDir string, fileHandler file.Handler) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChartApplyExecutor is a Helm action executor that applies all actions.
|
// ChartApplyExecutor is a Helm action executor that applies all actions.
|
||||||
@ -126,6 +133,16 @@ func (c ChartApplyExecutor) Apply(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveCharts saves all Helm charts and their values to the given directory.
|
||||||
|
func (c ChartApplyExecutor) SaveCharts(chartsDir string, fileHandler file.Handler) error {
|
||||||
|
for _, action := range c.actions {
|
||||||
|
if err := action.SaveChart(chartsDir, fileHandler); err != nil {
|
||||||
|
return fmt.Errorf("saving chart %s: %w", action.ReleaseName(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// mergeMaps returns a new map that is the merger of it's inputs.
|
// mergeMaps returns a new map that is the merger of it's inputs.
|
||||||
// Key collisions are resolved by taking the value of the second argument (map b).
|
// Key collisions are resolved by taking the value of the second argument (map b).
|
||||||
// Taken from: https://github.com/helm/helm/blob/dbc6d8e20fe1d58d50e6ed30f09a04a77e4c68db/pkg/cli/values/options.go#L91-L108.
|
// Taken from: https://github.com/helm/helm/blob/dbc6d8e20fe1d58d50e6ed30f09a04a77e4c68db/pkg/cli/values/options.go#L91-L108.
|
||||||
|
Loading…
Reference in New Issue
Block a user