From 03251c602458c3eba945b71b532781d60745f2dc Mon Sep 17 00:00:00 2001 From: Ethan Mosbaugh Date: Wed, 27 Aug 2025 14:10:20 -0700 Subject: [PATCH 1/2] feat(v3): rely on kots to process the app airgap bundle and create the image pull secrets --- api/internal/managers/app/install/install.go | 24 ++++++++++++------- .../managers/app/install/install_test.go | 14 ++++++++++- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/api/internal/managers/app/install/install.go b/api/internal/managers/app/install/install.go index acd20a852c..c47af16e8c 100644 --- a/api/internal/managers/app/install/install.go +++ b/api/internal/managers/app/install/install.go @@ -49,14 +49,8 @@ func (m *appInstallManager) Install(ctx context.Context, installableCharts []typ } func (m *appInstallManager) install(ctx context.Context, installableCharts []types.InstallableHelmChart, kotsConfigValues kotsv1beta1.ConfigValues) error { - license := &kotsv1beta1.License{} - if err := kyaml.Unmarshal(m.license, license); err != nil { - return fmt.Errorf("parse license: %w", err) - } - - // Setup Helm client - if err := m.setupHelmClient(); err != nil { - return fmt.Errorf("setup helm client: %w", err) + if err := m.installKots(kotsConfigValues); err != nil { + return fmt.Errorf("install kots: %w", err) } // Install Helm charts @@ -64,6 +58,15 @@ func (m *appInstallManager) install(ctx context.Context, installableCharts []typ return fmt.Errorf("install helm charts: %w", err) } + return nil +} + +func (m *appInstallManager) installKots(kotsConfigValues kotsv1beta1.ConfigValues) error { + license := &kotsv1beta1.License{} + if err := kyaml.Unmarshal(m.license, license); err != nil { + return fmt.Errorf("parse license: %w", err) + } + ecDomains := utils.GetDomains(m.releaseData) installOpts := kotscli.InstallOptions{ @@ -119,6 +122,11 @@ func (m *appInstallManager) installHelmCharts(ctx context.Context, installableCh return fmt.Errorf("no helm charts found") } + // Setup Helm client + if err := m.setupHelmClient(); err != nil { + return fmt.Errorf("setup helm client: %w", err) + } + logFn("installing %d helm charts", len(installableCharts)) for _, installableChart := range installableCharts { diff --git a/api/internal/managers/app/install/install_test.go b/api/internal/managers/app/install/install_test.go index bf089e294e..9ea185f898 100644 --- a/api/internal/managers/app/install/install_test.go +++ b/api/internal/managers/app/install/install_test.go @@ -257,7 +257,11 @@ func TestAppInstallManager_Install(t *testing.T) { return opts.ChartPath != "" && opts.ReleaseName == "fluentd" && opts.Namespace == "logging" })).Return((*helmrelease.Release)(nil), assert.AnError) - // Create manager with initialized store (no need for KOTS installer mock since Helm fails first) + // Create mock installer that succeeds (so we get to Helm charts) + mockInstaller := &MockKotsCLIInstaller{} + mockInstaller.On("Install", mock.Anything).Return(nil) + + // Create manager with initialized store store := appinstallstore.NewMemoryStore(appinstallstore.WithAppInstall(types.AppInstall{ Status: types.Status{State: types.StatePending}, })) @@ -266,6 +270,7 @@ func TestAppInstallManager_Install(t *testing.T) { WithClusterID("test-cluster"), WithReleaseData(releaseData), WithK8sVersion("v1.33.0"), + WithKotsCLI(mockInstaller), WithHelmClient(mockHelmClient), WithLogger(logger.NewDiscardLogger()), WithAppInstallStore(store), @@ -282,6 +287,7 @@ func TestAppInstallManager_Install(t *testing.T) { assert.Equal(t, types.StateFailed, appInstall.Status.State) assert.Contains(t, appInstall.Status.Description, "install helm charts") + mockInstaller.AssertExpectations(t) mockHelmClient.AssertExpectations(t) }) @@ -491,6 +497,10 @@ func TestComponentStatusTracking(t *testing.T) { return opts.ReleaseName == "failing-app" })).Return((*helmrelease.Release)(nil), errors.New("helm install failed")) + // Create mock installer that succeeds (so we get to Helm charts) + mockInstaller := &MockKotsCLIInstaller{} + mockInstaller.On("Install", mock.Anything).Return(nil) + // Create manager with in-memory store appInstallStore := appinstallstore.NewMemoryStore(appinstallstore.WithAppInstall(types.AppInstall{ Status: types.Status{State: types.StatePending}, @@ -501,6 +511,7 @@ func TestComponentStatusTracking(t *testing.T) { WithK8sVersion("v1.33.0"), WithLicense([]byte(`{"spec":{"appSlug":"test-app"}}`)), WithClusterID("test-cluster"), + WithKotsCLI(mockInstaller), WithHelmClient(mockHelmClient), ) require.NoError(t, err) @@ -525,6 +536,7 @@ func TestComponentStatusTracking(t *testing.T) { // Overall status should be failed assert.Equal(t, types.StateFailed, appInstall.Status.State) + mockInstaller.AssertExpectations(t) mockHelmClient.AssertExpectations(t) }) } From 1bb0be72e08549edd31aba8cbfef74e176765c36 Mon Sep 17 00:00:00 2001 From: Ethan Mosbaugh Date: Wed, 27 Aug 2025 15:21:46 -0700 Subject: [PATCH 2/2] f --- api/internal/managers/app/install/install.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/api/internal/managers/app/install/install.go b/api/internal/managers/app/install/install.go index c47af16e8c..97b73646c1 100644 --- a/api/internal/managers/app/install/install.go +++ b/api/internal/managers/app/install/install.go @@ -86,11 +86,25 @@ func (m *appInstallManager) installKots(kotsConfigValues kotsv1beta1.ConfigValue } installOpts.ConfigValuesFile = configValuesFile + logFn := m.logFn("app") + + logFn("preparing the app for installation") + if m.kotsCLI != nil { - return m.kotsCLI.Install(installOpts) + err := m.kotsCLI.Install(installOpts) + if err != nil { + return fmt.Errorf("install kots: %w", err) + } + } else { + err := kotscli.Install(installOpts) + if err != nil { + return fmt.Errorf("install kots: %w", err) + } } - return kotscli.Install(installOpts) + logFn("successfully prepared the app for installation") + + return nil } // createConfigValuesFile creates a temporary file with the config values @@ -140,6 +154,8 @@ func (m *appInstallManager) installHelmCharts(ctx context.Context, installableCh logFn("successfully installed %s chart", chartName) } + logFn("successfully installed all %d helm charts", len(installableCharts)) + return nil }