Skip to content

Commit

Permalink
Implement tinkerbell stack upgrade and migration to upstream chart
Browse files Browse the repository at this point in the history
  • Loading branch information
ahreehong committed Jun 11, 2024
1 parent 2d13634 commit 1e99b69
Show file tree
Hide file tree
Showing 30 changed files with 1,673 additions and 771 deletions.
53 changes: 48 additions & 5 deletions pkg/executables/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,11 @@ func (h *Helm) InstallChart(ctx context.Context, chart, ociURI, version, kubecon

// InstallChartWithValuesFile installs a helm chart with the provided values file and waits for the chart deployment to be ready
// The default timeout for the chart to reach ready state is 5m.
func (h *Helm) InstallChartWithValuesFile(ctx context.Context, chart, ociURI, version, kubeconfigFilePath, valuesFilePath string) error {
func (h *Helm) InstallChartWithValuesFile(ctx context.Context, chart, ociURI, version, kubeconfigFilePath, namespace, valuesFilePath string) error {
params := []string{"upgrade", "--install", chart, ociURI, "--version", version, "--values", valuesFilePath, "--kubeconfig", kubeconfigFilePath, "--wait"}
if len(namespace) > 0 {
params = append(params, "--namespace", namespace)
}
params = h.addInsecureFlagIfProvided(params)
_, err := h.executable.Command(ctx, params...).WithEnvVars(h.env).Run()
return err
Expand All @@ -181,8 +184,13 @@ func (h *Helm) Delete(ctx context.Context, kubeconfigFilePath, installName, name
return nil
}

func (h *Helm) ListCharts(ctx context.Context, kubeconfigFilePath string) ([]string, error) {
func (h *Helm) ListCharts(ctx context.Context, kubeconfigFilePath, filter string) ([]string, error) {

Check failure on line 187 in pkg/executables/helm.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported method Helm.ListCharts should have comment or be unexported (revive)
params := []string{"list", "-q", "--kubeconfig", kubeconfigFilePath}

if len(filter) > 0 {
params = append(params, "--filter", filter)
}

out, err := h.executable.Command(ctx, params...).WithEnvVars(h.env).Run()
if err != nil {
return nil, err
Expand All @@ -200,6 +208,13 @@ func (h *Helm) addInsecureFlagIfProvided(params []string) []string {
return params
}

func (h *Helm) addExtraFlagsIfProvided(params []string) []string {
if len(h.helmConfig.ExtraFlags) != 0 {
return append(params, h.helmConfig.ExtraFlags...)
}
return params
}

func (h *Helm) url(originalURL string) string {
registryMirror := h.helmConfig.RegistryMirror
return registryMirror.ReplaceRegistry(originalURL)
Expand All @@ -214,17 +229,21 @@ func GetHelmValueArgs(values []string) []string {
return valueArgs
}

// UpgradeChartWithValuesFile tuns a helm upgrade with the provided values file and waits for the
// UpgradeChartWithValuesFile runs a helm upgrade with the provided values file and waits for the
// chart deployment to be ready.
func (h *Helm) UpgradeChartWithValuesFile(ctx context.Context, chart, ociURI, version, kubeconfigFilePath, valuesFilePath string, opts ...helm.Opt) error {
func (h *Helm) UpgradeChartWithValuesFile(ctx context.Context, chart, ociURI, version, kubeconfigFilePath, namespace, valuesFilePath string, opts ...helm.Opt) error {
params := []string{
"upgrade", chart, ociURI,
"upgrade",
chart, ociURI,
"--version", version,
"--values", valuesFilePath,
"--kubeconfig", kubeconfigFilePath,
"--wait",
}

if len(namespace) > 0 {
params = append(params, "--namespace", namespace)
}
// TODO: we should not update the receiver here, so this needs to change.
// This is not thread safe.
// https://github.com/aws/eks-anywhere/issues/7176
Expand All @@ -235,6 +254,30 @@ func (h *Helm) UpgradeChartWithValuesFile(ctx context.Context, chart, ociURI, ve
mergeMaps(h.env, h.helmConfig.ProxyConfig)

params = h.addInsecureFlagIfProvided(params)
params = h.addExtraFlagsIfProvided(params)
_, err := h.executable.Command(ctx, params...).WithEnvVars(h.env).Run()
return err
}

func (h *Helm) Uninstall(ctx context.Context, chart, kubeconfigFilePath, namespace string, opts ...helm.Opt) error {
params := []string{
"uninstall", chart,
"--kubeconfig", kubeconfigFilePath,
"--wait",
}

if len(namespace) > 0 {
params = append(params, "--namespace", namespace)
}

for _, opt := range opts {
opt(h.helmConfig)
}

params = h.addInsecureFlagIfProvided(params)
params = h.addExtraFlagsIfProvided(params)

logger.Info("Uninstalling helm chart on cluster", "chart", chart)
_, err := h.executable.Command(ctx, params...).WithEnvVars(h.env).Run()
return err
}
126 changes: 121 additions & 5 deletions pkg/executables/helm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ func TestHelmInstallChartWithValuesFileSuccess(t *testing.T) {
tt.e, tt.ctx, "upgrade", "--install", chart, url, "--version", version, "--values", valuesFileName, "--kubeconfig", kubeconfig, "--wait",
).withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, nil)

tt.Expect(tt.h.InstallChartWithValuesFile(tt.ctx, chart, url, version, kubeconfig, valuesFileName)).To(Succeed())
tt.Expect(tt.h.InstallChartWithValuesFile(tt.ctx, chart, url, version, kubeconfig, "", valuesFileName)).To(Succeed())
}

func TestHelmInstallChartWithValuesFileSuccessWithInsecure(t *testing.T) {
Expand All @@ -265,7 +265,22 @@ func TestHelmInstallChartWithValuesFileSuccessWithInsecure(t *testing.T) {
tt.e, tt.ctx, "upgrade", "--install", chart, url, "--version", version, "--values", valuesFileName, "--kubeconfig", kubeconfig, "--wait", "--insecure-skip-tls-verify",
).withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, nil)

tt.Expect(tt.h.InstallChartWithValuesFile(tt.ctx, chart, url, version, kubeconfig, valuesFileName)).To(Succeed())
tt.Expect(tt.h.InstallChartWithValuesFile(tt.ctx, chart, url, version, kubeconfig, "", valuesFileName)).To(Succeed())
}

func TestHelmInstallChartWithValuesFileSuccessWithNamespace(t *testing.T) {
tt := newHelmTest(t)
chart := "chart"
url := "url"
version := "1.1"
kubeconfig := "/root/.kube/config"
valuesFileName := "values.yaml"
namespace := "test"
expectCommand(
tt.e, tt.ctx, "upgrade", "--install", chart, url, "--version", version, "--values", valuesFileName, "--kubeconfig", kubeconfig, "--wait", "--namespace", namespace,
).withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, nil)

tt.Expect(tt.h.InstallChartWithValuesFile(tt.ctx, chart, url, version, kubeconfig, namespace, valuesFileName)).To(Succeed())
}

func TestHelmListCharts(t *testing.T) {
Expand All @@ -275,20 +290,28 @@ func TestHelmListCharts(t *testing.T) {
output := []byte("eks-anywhere-packages\n")
expected := []string{"eks-anywhere-packages"}
expectCommand(tt.e, tt.ctx, "list", "-q", "--kubeconfig", kubeconfig).withEnvVars(tt.envVars).to().Return(*bytes.NewBuffer(output), nil)
tt.Expect(tt.h.ListCharts(tt.ctx, kubeconfig)).To(Equal(expected))
tt.Expect(tt.h.ListCharts(tt.ctx, kubeconfig, "")).To(Equal(expected))
})

t.Run("With selectors", func(t *testing.T) {
filter := "--test=123"
output := []byte("eks-anywhere-packages\n")
expected := []string{"eks-anywhere-packages"}
expectCommand(tt.e, tt.ctx, "list", "-q", "--kubeconfig", kubeconfig, "--filter", "--test=123").withEnvVars(tt.envVars).to().Return(*bytes.NewBuffer(output), nil)
tt.Expect(tt.h.ListCharts(tt.ctx, kubeconfig, filter)).To(Equal(expected))
})

t.Run("Empty output", func(t *testing.T) {
expected := []string{}
expectCommand(tt.e, tt.ctx, "list", "-q", "--kubeconfig", kubeconfig).withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, nil)
tt.Expect(tt.h.ListCharts(tt.ctx, kubeconfig)).To(Equal(expected))
tt.Expect(tt.h.ListCharts(tt.ctx, kubeconfig, "")).To(Equal(expected))
})

t.Run("Errored out", func(t *testing.T) {
output := errors.New("Error")
var expected []string
expectCommand(tt.e, tt.ctx, "list", "-q", "--kubeconfig", kubeconfig).withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, output)
result, err := tt.h.ListCharts(tt.ctx, kubeconfig)
result, err := tt.h.ListCharts(tt.ctx, kubeconfig, "")
tt.Expect(err).To(HaveOccurred())
tt.Expect(result).To(Equal(expected))
})
Expand Down Expand Up @@ -350,3 +373,96 @@ func TestHelmRegistryLoginSuccessWithInsecure(t *testing.T) {
expectCommand(tt.e, tt.ctx, "registry", "login", registry, "--username", username, "--password-stdin", "--insecure").withEnvVars(tt.envVars).withStdIn([]byte(password)).to().Return(bytes.Buffer{}, nil)
tt.Expect(tt.h.RegistryLogin(tt.ctx, registry, username, password)).To(Succeed())
}

func TestHelmUpgradeChartWithValues(s *testing.T) {
url := "url"
version := "1.1"
kubeconfig := "/root/.kube/config"
valuesFileName := "values.yaml"

s.Run("Success", func(t *testing.T) {
tt := newHelmTest(s)
installName := "test-install"
expectCommand(tt.e, tt.ctx, "upgrade", installName, url, "--version", version, "--values", valuesFileName, "--kubeconfig", kubeconfig, "--wait").withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, nil)
err := tt.h.UpgradeChartWithValuesFile(tt.ctx, installName, url, version, kubeconfig, "", valuesFileName)
tt.Expect(err).NotTo(HaveOccurred())
})

s.Run("passes the namespace, if present", func(t *testing.T) {
tt := newHelmTest(s)
testNamespace := "testing"
installName := "test-install"
expectCommand(tt.e, tt.ctx, "upgrade", installName, url, "--version", version, "--values", valuesFileName, "--kubeconfig", kubeconfig, "--wait", "--namespace", testNamespace).withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, nil)
err := tt.h.UpgradeChartWithValuesFile(tt.ctx, installName, url, version, kubeconfig, testNamespace, valuesFileName)
tt.Expect(err).NotTo(HaveOccurred())
})

s.Run("passes the insecure skip flag", func(t *testing.T) {
tt := newHelmTest(t, helm.WithInsecure())
installName := "test-install"
expectCommand(tt.e, tt.ctx, "upgrade", installName, url, "--version", version, "--values", valuesFileName, "--kubeconfig", kubeconfig, "--wait", "--insecure-skip-tls-verify").withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, nil)
err := tt.h.UpgradeChartWithValuesFile(tt.ctx, installName, url, version, kubeconfig, "", valuesFileName)
tt.Expect(err).NotTo(HaveOccurred())
})

s.Run("passes extra flags", func(t *testing.T) {
tt := newHelmTest(t, helm.WithExtraFlags([]string{"--test", "--install"}))
installName := "test-install"
expectCommand(tt.e, tt.ctx, "upgrade", installName, url, "--version", version, "--values", valuesFileName, "--kubeconfig", kubeconfig, "--wait", "--test", "--install").withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, nil)
err := tt.h.UpgradeChartWithValuesFile(tt.ctx, installName, url, version, kubeconfig, "", valuesFileName)
tt.Expect(err).NotTo(HaveOccurred())
})

s.Run("returns errors from the helm executable", func(t *testing.T) {
tt := newHelmTest(s)
installName := "test-install"
expectCommand(tt.e, tt.ctx, "upgrade", installName, url, "--version", version, "--values", valuesFileName, "--kubeconfig", kubeconfig, "--wait").withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, fmt.Errorf("test error"))
err := tt.h.UpgradeChartWithValuesFile(tt.ctx, installName, url, version, kubeconfig, "", valuesFileName)
tt.Expect(err).To(HaveOccurred())
})
}

func TestHelmUninstall(s *testing.T) {
kubeconfig := "/root/.kube/config"

s.Run("Success", func(t *testing.T) {
tt := newHelmTest(t)
installName := "test-install"
expectCommand(tt.e, tt.ctx, "uninstall", installName, "--kubeconfig", kubeconfig, "--wait").withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, nil)
err := tt.h.Uninstall(tt.ctx, installName, kubeconfig, "")
tt.Expect(err).NotTo(HaveOccurred())
})

s.Run("passes the namespace, if present", func(t *testing.T) {
tt := newHelmTest(t)
testNamespace := "testing"
installName := "test-install"
expectCommand(tt.e, tt.ctx, "uninstall", installName, "--kubeconfig", kubeconfig, "--wait", "--namespace", testNamespace).withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, nil)
err := tt.h.Uninstall(tt.ctx, installName, kubeconfig, testNamespace)
tt.Expect(err).NotTo(HaveOccurred())
})

s.Run("passes the insecure skip flag", func(t *testing.T) {
tt := newHelmTest(t, helm.WithInsecure())
installName := "test-install"
expectCommand(tt.e, tt.ctx, "uninstall", installName, "--kubeconfig", kubeconfig, "--wait", "--insecure-skip-tls-verify").withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, nil)
err := tt.h.Uninstall(tt.ctx, installName, kubeconfig, "")
tt.Expect(err).NotTo(HaveOccurred())
})

s.Run("passes extra flags", func(t *testing.T) {
tt := newHelmTest(t, helm.WithExtraFlags([]string{"--test"}))
installName := "test-install"
expectCommand(tt.e, tt.ctx, "uninstall", installName, "--kubeconfig", kubeconfig, "--wait", "--test").withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, nil)
err := tt.h.Uninstall(tt.ctx, installName, kubeconfig, "")
tt.Expect(err).NotTo(HaveOccurred())
})

s.Run("returns errors from the helm executable", func(t *testing.T) {
tt := newHelmTest(t)
installName := "test-install"
expectCommand(tt.e, tt.ctx, "uninstall", installName, "--kubeconfig", kubeconfig, "--wait").withEnvVars(tt.envVars).to().Return(bytes.Buffer{}, fmt.Errorf("test error"))
err := tt.h.Uninstall(tt.ctx, installName, kubeconfig, "")
tt.Expect(err).To(HaveOccurred())
})
}
7 changes: 4 additions & 3 deletions pkg/helm/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import "context"
type Client interface {
PushChart(ctx context.Context, chart, registry string) error
PullChart(ctx context.Context, ociURI, version string) error
ListCharts(ctx context.Context, kubeconfigFilePath string) ([]string, error)
ListCharts(ctx context.Context, kubeconfigFilePath, filter string) ([]string, error)
SaveChart(ctx context.Context, ociURI, version, folder string) error
Delete(ctx context.Context, kubeconfigFilePath, installName, namespace string) error
UpgradeChartWithValuesFile(ctx context.Context, chart, ociURI, version, kubeconfigFilePath, valuesFilePath string, opts ...Opt) error
InstallChartWithValuesFile(ctx context.Context, chart, ociURI, version, kubeconfigFilePath, valuesFilePath string) error
UpgradeChartWithValuesFile(ctx context.Context, chart, ociURI, version, kubeconfigFilePath, namespace, valuesFilePath string, opts ...Opt) error
InstallChartWithValuesFile(ctx context.Context, chart, ociURI, version, kubeconfigFilePath, namespace, valuesFilePath string) error
InstallChart(ctx context.Context, chart, ociURI, version, kubeconfigFilePath, namespace, valueFilePath string, skipCRDs bool, values []string) error
Uninstall(ctx context.Context, chart, kubeconfigFilePath, namespace string, opts ...Opt) error
Template(ctx context.Context, ociURI, version, namespace string, values interface{}, kubeVersion string) ([]byte, error)
RegistryLogin(ctx context.Context, registry, username, password string) error
}
8 changes: 8 additions & 0 deletions pkg/helm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type Config struct {
RegistryMirror *registrymirror.RegistryMirror
ProxyConfig map[string]string
Insecure bool
ExtraFlags []string
}

// NewConfig retuns a new helm Config.
Expand Down Expand Up @@ -49,3 +50,10 @@ func WithProxyConfig(proxyConfig map[string]string) Opt {
c.ProxyConfig = proxyConfig
}
}

// WithExtraFlags configures the extra flags to pass to helm.
func WithExtraFlags(extraFlags []string) Opt {
return func(c *Config) {
c.ExtraFlags = extraFlags
}
}
9 changes: 9 additions & 0 deletions pkg/helm/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,12 @@ func TestWithProxyConfig(t *testing.T) {
config := helm.NewConfig(helm.WithProxyConfig(proxyConfigMap))
g.Expect(config.ProxyConfig).To(Equal(proxyConfigMap))
}

func TestWithExtraArgs(t *testing.T) {
g := NewWithT(t)
extraFlags := []string{
"--test-arg",
}
config := helm.NewConfig(helm.WithExtraFlags(extraFlags))
g.Expect(config.ExtraFlags).To(Equal(extraFlags))
}
Loading

0 comments on commit 1e99b69

Please sign in to comment.