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 6d13aa4
Show file tree
Hide file tree
Showing 30 changed files with 1,575 additions and 762 deletions.
46 changes: 42 additions & 4 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 Down Expand Up @@ -200,6 +203,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 +224,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 +249,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 {

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

View workflow job for this annotation

GitHub Actions / lint

exported: exported method Helm.Uninstall should have comment or be unexported (revive)
params := []string{
"uninstall", chart,
"--kubeconfig", kubeconfigFilePath,
"--wait",
}

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

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

Check warning on line 269 in pkg/executables/helm.go

View check run for this annotation

Codecov / codecov/patch

pkg/executables/helm.go#L269

Added line #L269 was not covered by tests
}

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
}
112 changes: 110 additions & 2 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 Down Expand Up @@ -350,3 +365,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())
})
}
5 changes: 3 additions & 2 deletions pkg/helm/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ type Client interface {
ListCharts(ctx context.Context, kubeconfigFilePath 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))
}
35 changes: 27 additions & 8 deletions pkg/helm/mocks/client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 25 additions & 4 deletions pkg/providers/tinkerbell/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,26 @@ func (p *Provider) PreCAPIInstallOnBootstrap(ctx context.Context, cluster *types
clusterSpec.Cluster.AddTinkerbellIPAnnotation(p.tinkerbellIP)
versionsBundle := clusterSpec.RootVersionsBundle()

err := p.stackInstaller.Install(
err := p.stackInstaller.UpgradeCRDs(
ctx,
versionsBundle.Tinkerbell,
cluster.KubeconfigFile,
)

if err != nil {
return fmt.Errorf("installing tinkerbell crds on bootstrap cluster: %v", err)

Check warning on line 43 in pkg/providers/tinkerbell/create.go

View check run for this annotation

Codecov / codecov/patch

pkg/providers/tinkerbell/create.go#L43

Added line #L43 was not covered by tests
}

err = p.stackInstaller.Install(
ctx,
versionsBundle.Tinkerbell,
p.tinkerbellIP,
cluster.KubeconfigFile,
p.datacenterConfig.Spec.HookImagesURLPath,
stack.WithBootsOnDocker(),
stack.WithHostPortEnabled(true), // enable host port on bootstrap cluster
stack.WithLoadBalancerEnabled(false),
stack.WithStackServiceEnabled(false),
)
if err != nil {
return fmt.Errorf("install Tinkerbell stack on bootstrap cluster: %v", err)
Expand Down Expand Up @@ -81,15 +93,24 @@ func (p *Provider) PostWorkloadInit(ctx context.Context, cluster *types.Cluster,

versionsBundle := clusterSpec.RootVersionsBundle()

err := p.stackInstaller.Install(
err := p.stackInstaller.UpgradeCRDs(
ctx,
versionsBundle.Tinkerbell,
cluster.KubeconfigFile,
)
if err != nil {
return fmt.Errorf("installing tinkerbell crds on workload cluster: %v", err)

Check warning on line 102 in pkg/providers/tinkerbell/create.go

View check run for this annotation

Codecov / codecov/patch

pkg/providers/tinkerbell/create.go#L102

Added line #L102 was not covered by tests
}

err = p.stackInstaller.Install(
ctx,
versionsBundle.Tinkerbell,
p.templateBuilder.datacenterSpec.TinkerbellIP,
cluster.KubeconfigFile,
p.datacenterConfig.Spec.HookImagesURLPath,
stack.WithBootsOnKubernetes(),
stack.WithHostPortEnabled(false), // disable host port on workload cluster
stack.WithEnvoyEnabled(true), // use envoy on workload cluster
stack.WithHostPortEnabled(false), // disable host port on workload cluster
stack.WithStackServiceEnabled(true), // use stack service on workload cluster
stack.WithLoadBalancerEnabled(
len(clusterSpec.Cluster.Spec.WorkerNodeGroupConfigurations) != 0 && // load balancer is handled by kube-vip in control plane nodes
!p.datacenterConfig.Spec.SkipLoadBalancerDeployment), // configure load balancer based on datacenterConfig.Spec.SkipLoadBalancerDeployment
Expand Down
Loading

0 comments on commit 6d13aa4

Please sign in to comment.