Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 34 additions & 10 deletions api/internal/managers/app/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,24 @@ 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
if err := m.installHelmCharts(ctx, installableCharts); err != nil {
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{
Expand All @@ -83,11 +86,25 @@ func (m *appInstallManager) install(ctx context.Context, installableCharts []typ
}
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
Expand Down Expand Up @@ -119,6 +136,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 {
Expand All @@ -132,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
}

Expand Down
14 changes: 13 additions & 1 deletion api/internal/managers/app/install/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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},
}))
Expand All @@ -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),
Expand All @@ -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)
})

Expand Down Expand Up @@ -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},
Expand All @@ -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)
Expand All @@ -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)
})
}