Skip to content

Commit

Permalink
Consolidate the logic for determining downstream version status (#4987)
Browse files Browse the repository at this point in the history
* Consolidate the logic for determining downstream version status
  • Loading branch information
sgalsaleh authored Nov 6, 2024
1 parent d3507ce commit 239b0e5
Show file tree
Hide file tree
Showing 24 changed files with 772 additions and 564 deletions.
2 changes: 1 addition & 1 deletion e2e/playwright/tests/@change-license/test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ test('change license', async ({ page }) => {
await login(page);
await uploadLicense(page, expect, "community-license.yaml");
await expect(page.locator('#app')).toContainText('Change License', { timeout: 10000 });
await expect(page.locator('#app')).toContainText('Currently deployed version', { timeout: 15000 });
await expect(page.locator('#app')).toContainText('Ready', { timeout: 30000 });
await expect(page.locator('#app')).toContainText('Currently deployed version');
await page.getByRole('link', { name: 'License', exact: true }).click();
await expect(page.locator('#app')).toContainText('change-license-community', { timeout: 10000 });
await expect(page.locator('#app')).toContainText('Community license');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ test('multi app backup and restore', async ({ page }) => {
await login(page);
await uploadLicense(page, expect, "app1-license.yaml");
await expect(page.locator('#app')).toContainText('Multi App Backup and Restore 1', { timeout: 10000 });
await expect(page.locator('#app')).toContainText('Currently deployed version', { timeout: 15000 });
await expect(page.locator('#app')).toContainText('Ready', { timeout: 30000 });
await expect(page.locator('#app')).toContainText('Currently deployed version');
await page.locator('div').filter({ hasText: /^Change passwordAdd new applicationLog out$/ }).getByRole('img').click();
await page.getByText('Add new application').click();
await uploadLicense(page, expect, "app2-license.yaml");
await expect(page.locator('#app')).toContainText('Multi App Backup and Restore 2', { timeout: 10000 });
await expect(page.locator('#app')).toContainText('Currently deployed version', { timeout: 15000 });
await expect(page.locator('#app')).toContainText('Ready', { timeout: 30000 });
await expect(page.locator('#app')).toContainText('Currently deployed version');
await page.locator('.NavItem').getByText('Snapshots', { exact: true }).click();
await page.getByRole('link', { name: 'Partial Snapshots' }).click({ timeout: 10000 });
await page.getByRole('button', { name: 'Start a snapshot' }).click({ timeout: 15000 });
Expand Down
4 changes: 2 additions & 2 deletions e2e/playwright/tests/@multi-app-install/test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ test('multi app install', async ({ page }) => {
await expect(page.locator('#app')).toContainText('Your cluster meets the recommended and required versions of Kubernetes');
await page.getByRole('button', { name: 'Deploy' }).click();
await expect(page.locator('#app')).toContainText('Multi App Install 1');
await expect(page.locator('#app')).toContainText('Currently deployed version', { timeout: 15000 });
await expect(page.locator('#app')).toContainText('Ready', { timeout: 30000 });
await expect(page.locator('#app')).toContainText('Currently deployed version');
await expect(page.locator('#app')).toContainText('0.1.3');
const app1Status = execSync(`kubectl kots get apps -n ${process.env.NAMESPACE} | grep mutli-app-install | awk '{print $2}'`).toString().trim();
expect(app1Status).toBe('ready');
Expand All @@ -27,8 +27,8 @@ test('multi app install', async ({ page }) => {
await expect(page.locator('#app')).toContainText('Your cluster meets the recommended and required versions of Kubernetes');
await page.getByRole('button', { name: 'Deploy' }).click();
await expect(page.locator('#app')).toContainText('Multi App Install 2');
await expect(page.locator('#app')).toContainText('Currently deployed version', { timeout: 15000 });
await expect(page.locator('#app')).toContainText('Ready', { timeout: 30000 });
await expect(page.locator('#app')).toContainText('Currently deployed version');
await expect(page.locator('#app')).toContainText('2.1.2');
const app2Status = execSync(`kubectl kots get apps -n ${process.env.NAMESPACE} | grep multi-app-install-2 | awk '{print $2}'`).toString().trim();
expect(app2Status).toBe('ready');
Expand Down
2 changes: 1 addition & 1 deletion e2e/playwright/tests/@no-required-config/test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ test('no required config', async ({ page }) => {
await page.getByRole('button', { name: 'Continue' }).click();
await expect(page.locator('#app')).toContainText('Ready', { timeout: 30000 });
await page.getByRole('link', { name: 'Version history' }).click();
await expect(page.locator('#app')).toContainText('Currently deployed version');
await expect(page.locator('#app')).toContainText('Currently deployed version', { timeout: 15000 });
});
69 changes: 4 additions & 65 deletions pkg/airgap/airgap.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ func CreateAppFromAirgap(opts CreateAirgapAppOpts) (finalError error) {
return errors.Wrap(err, "failed to set app is airgap the second time")
}

newSequence, err := store.GetStore().CreateAppVersion(a.ID, nil, tmpRoot, "Airgap Install", opts.SkipPreflights, render.Renderer{})
newSequence, err := store.GetStore().CreateAppVersion(a.ID, nil, tmpRoot, "Airgap Install", true, opts.IsAutomated, configFile, opts.SkipPreflights, render.Renderer{})
if err != nil {
return errors.Wrap(err, "failed to create new version")
}
Expand All @@ -270,77 +270,16 @@ func CreateAppFromAirgap(opts CreateAirgapAppOpts) (finalError error) {
return errors.Wrap(err, "failed to create support bundle dependencies")
}

kotsKinds, err := kotsutil.LoadKotsKinds(tmpRoot)
if err != nil {
return errors.Wrap(err, "failed to load kotskinds from path")
}

status, err := store.GetStore().GetDownstreamVersionStatus(opts.PendingApp.ID, newSequence)
if err != nil {
return errors.Wrap(err, "failed to get downstream version status")
}

if status == storetypes.VersionPendingClusterManagement {
if configFile != "" {
// if there is a config file, then we should proceed with the installation normally, not wait for the user
// to click through the UI to add nodes and configure the app
status = storetypes.VersionPendingConfig
} else {
// if pending cluster management, we don't want to deploy the app
return nil
}
}

hasStrictPreflights, err := store.GetStore().HasStrictPreflights(a.ID, newSequence)
if err != nil {
return errors.Wrap(err, "failed to check if app preflight has strict analyzers")
}

if hasStrictPreflights && opts.SkipPreflights {
logger.Warnf("preflights will not be skipped, strict preflights are set to %t", hasStrictPreflights)
}

if opts.IsAutomated && kotsKinds.IsConfigurable() {
// bypass the config screen if no configuration is required
registrySettings := registrytypes.RegistrySettings{
Hostname: opts.RegistryHost,
Namespace: opts.RegistryNamespace,
Username: opts.RegistryUsername,
Password: opts.RegistryPassword,
IsReadOnly: opts.RegistryIsReadOnly,
}
needsConfig, err := kotsadmconfig.NeedsConfiguration(a.Slug, newSequence, a.IsAirgap, kotsKinds, registrySettings)
if err != nil {
return errors.Wrap(err, "failed to check if app needs configuration")
}
if !needsConfig {
if err := store.GetStore().SetDownstreamVersionStatus(opts.PendingApp.ID, newSequence, storetypes.VersionPending, ""); err != nil {
return errors.Wrap(err, "failed to set downstream version status to pending")
}
if opts.SkipPreflights && !hasStrictPreflights {
if err := version.DeployVersion(opts.PendingApp.ID, newSequence); err != nil {
return errors.Wrap(err, "failed to deploy version")
}
} else {
err := store.GetStore().SetDownstreamVersionStatus(opts.PendingApp.ID, newSequence, storetypes.VersionPendingPreflight, "")
if err != nil {
return errors.Wrap(err, "failed to set downstream version status to 'pending preflight'")
}
}
}
}

if !opts.SkipPreflights || hasStrictPreflights {
switch status {
case storetypes.VersionPendingPreflight:
if err := preflight.Run(opts.PendingApp.ID, opts.PendingApp.Slug, newSequence, true, opts.SkipPreflights, tmpRoot); err != nil {
return errors.Wrap(err, "failed to start preflights")
}
}

if !kotsKinds.IsConfigurable() && opts.SkipPreflights && !hasStrictPreflights {
// app is not configurable and preflights are skipped, so just deploy the app
if err := store.GetStore().SetDownstreamVersionStatus(opts.PendingApp.ID, newSequence, storetypes.VersionPending, ""); err != nil {
return errors.Wrap(err, "failed to set downstream version status to pending")
}
case storetypes.VersionPending:
if err := version.DeployVersion(opts.PendingApp.ID, newSequence); err != nil {
return errors.Wrap(err, "failed to deploy version")
}
Expand Down
31 changes: 4 additions & 27 deletions pkg/airgap/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ func UpdateAppFromPath(a *apptypes.App, airgapRoot string, airgapBundlePath stri
}

// Create the app in the db
newSequence, err := store.GetStore().CreateAppVersion(a.ID, &baseSequence, archiveDir, "Airgap Update", skipPreflights, render.Renderer{})
newSequence, err := store.GetStore().CreateAppVersion(a.ID, &baseSequence, archiveDir, "Airgap Update", false, false, "", skipPreflights, render.Renderer{})
if err != nil {
return errors.Wrap(err, "failed to create new version")
}
Expand All @@ -239,40 +239,17 @@ func UpdateAppFromPath(a *apptypes.App, airgapRoot string, airgapBundlePath stri
logger.Error(errors.Wrapf(err, "failed to reset channel changed flag"))
}

hasStrictPreflights, err := store.GetStore().HasStrictPreflights(a.ID, newSequence)
status, err := store.GetStore().GetDownstreamVersionStatus(a.ID, newSequence)
if err != nil {
return errors.Wrap(err, "failed to check if app preflight has strict analyzers")
return errors.Wrap(err, "failed to get downstream version status")
}

if hasStrictPreflights && skipPreflights {
logger.Warnf("preflights will not be skipped, strict preflights are set to %t", hasStrictPreflights)
}

if !skipPreflights || hasStrictPreflights {
if status == storetypes.VersionPendingPreflight {
if err := preflight.Run(a.ID, a.Slug, newSequence, true, skipPreflights, archiveDir); err != nil {
return errors.Wrap(err, "failed to start preflights")
}
}

if deploy {
downstreams, err := store.GetStore().ListDownstreamsForApp(a.ID)
if err != nil {
return errors.Wrap(err, "failed to fetch downstreams")
}
if len(downstreams) == 0 {
return errors.Errorf("no downstreams found for app %q", a.Slug)
}
downstream := downstreams[0]

status, err := store.GetStore().GetStatusForVersion(a.ID, downstream.ClusterID, newSequence)
if err != nil {
return errors.Wrapf(err, "failed to get status for version %d", newSequence)
}

if status == storetypes.VersionPendingConfig {
return errors.Errorf("not deploying version %d because it's %s", newSequence, status)
}

if err := version.DeployVersion(a.ID, newSequence); err != nil {
return errors.Wrap(err, "failed to deploy app version")
}
Expand Down
26 changes: 13 additions & 13 deletions pkg/handlers/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ func updateAppConfig(updateApp *apptypes.App, sequence int64, configGroups []kot
}

if createNewVersion {
newSequence, err := store.GetStore().CreateAppVersion(updateApp.ID, &sequence, archiveDir, "Config Change", skipPreflights, render.Renderer{})
newSequence, err := store.GetStore().CreateAppVersion(updateApp.ID, &sequence, archiveDir, "Config Change", false, false, "", skipPreflights, render.Renderer{})
if err != nil {
updateAppConfigResponse.Error = "failed to create an app version"
return updateAppConfigResponse, err
Expand All @@ -767,31 +767,31 @@ func updateAppConfig(updateApp *apptypes.App, sequence int64, configGroups []kot
}
}

if err := store.GetStore().SetDownstreamVersionStatus(updateApp.ID, int64(sequence), storetypes.VersionPendingPreflight, ""); err != nil {
updateAppConfigResponse.Error = "failed to set downstream status to 'pending preflight'"
return updateAppConfigResponse, err
}

hasStrictPreflights, err := store.GetStore().HasStrictPreflights(updateApp.ID, sequence)
status, err := store.GetStore().GetDownstreamVersionStatus(updateApp.ID, sequence)
if err != nil {
updateAppConfigResponse.Error = "failed to check if version has strict preflights"
updateAppConfigResponse.Error = "failed to get downstream version status"
return updateAppConfigResponse, err
}

if hasStrictPreflights && skipPreflights {
logger.Warnf("preflights will not be skipped, strict preflights are set to %t", hasStrictPreflights)
if sequence == 0 && status == storetypes.VersionPending {
// we're in the initial config page and the app is now ready to be deployed
if err := version.DeployVersion(updateApp.ID, sequence); err != nil {
updateAppConfigResponse.Error = "failed to deploy"
return updateAppConfigResponse, err
}
updateAppConfigResponse.Success = true
return updateAppConfigResponse, nil
}

if !skipPreflights || hasStrictPreflights {
if status == storetypes.VersionPendingPreflight {
if err := preflight.Run(updateApp.ID, updateApp.Slug, int64(sequence), updateApp.IsAirgap, skipPreflights, archiveDir); err != nil {
updateAppConfigResponse.Error = errors.Cause(err).Error()
return updateAppConfigResponse, err
}
}

if deploy {
err := version.DeployVersion(updateApp.ID, sequence)
if err != nil {
if err := version.DeployVersion(updateApp.ID, sequence); err != nil {
updateAppConfigResponse.Error = "failed to deploy"
return updateAppConfigResponse, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/handlers/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ func (h *Handler) ConfigureAppIdentityService(w http.ResponseWriter, r *http.Req
return
}

newSequence, err := store.GetStore().CreateAppVersion(a.ID, &latestSequence, archiveDir, "Identity Service", false, render.Renderer{})
newSequence, err := store.GetStore().CreateAppVersion(a.ID, &latestSequence, archiveDir, "Identity Service", false, false, "", false, render.Renderer{})
if err != nil {
err = errors.Wrap(err, "failed to create an app version")
logger.Error(err)
Expand Down
2 changes: 1 addition & 1 deletion pkg/handlers/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ func (h *Handler) UpdateAppRegistry(w http.ResponseWriter, r *http.Request) {
}
defer os.RemoveAll(appDir)

newSequence, err := store.GetStore().CreateAppVersion(foundApp.ID, &latestSequence, appDir, "Registry Change", false, render.Renderer{})
newSequence, err := store.GetStore().CreateAppVersion(foundApp.ID, &latestSequence, appDir, "Registry Change", false, false, "", false, render.Renderer{})
if err != nil {
logger.Error(errors.Wrap(err, "failed to create app version"))
return
Expand Down
28 changes: 4 additions & 24 deletions pkg/handlers/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,27 +190,22 @@ func (h *Handler) UploadExistingApp(w http.ResponseWriter, r *http.Request) {
return
}

newSequence, err := store.GetStore().CreateAppVersion(a.ID, &baseSequence, archiveDir, "KOTS Upload", uploadExistingAppRequest.SkipPreflights, render.Renderer{})
newSequence, err := store.GetStore().CreateAppVersion(a.ID, &baseSequence, archiveDir, "KOTS Upload", false, false, "", uploadExistingAppRequest.SkipPreflights, render.Renderer{})
if err != nil {
uploadResponse.Error = util.StrPointer("failed to create app version")
logger.Error(errors.Wrap(err, *uploadResponse.Error))
JSON(w, http.StatusInternalServerError, uploadResponse)
return
}

hasStrictPreflights, err := store.GetStore().HasStrictPreflights(a.ID, newSequence)
status, err := store.GetStore().GetDownstreamVersionStatus(a.ID, newSequence)
if err != nil {
uploadResponse.Error = util.StrPointer("failed to check if app preflight has strict analyzers")
uploadResponse.Error = util.StrPointer("failed to get downstream version status")
logger.Error(errors.Wrap(err, *uploadResponse.Error))
JSON(w, http.StatusInternalServerError, uploadResponse)
return
}

if hasStrictPreflights && uploadExistingAppRequest.SkipPreflights {
logger.Warnf("preflights will not be skipped, strict preflights are set to %t", hasStrictPreflights)
}

if !uploadExistingAppRequest.SkipPreflights || hasStrictPreflights {
if status == storetypes.VersionPendingPreflight {
if err := preflight.Run(a.ID, a.Slug, newSequence, a.IsAirgap, uploadExistingAppRequest.SkipPreflights, archiveDir); err != nil {
uploadResponse.Error = util.StrPointer("failed to get run preflights")
logger.Error(errors.Wrap(err, *uploadResponse.Error))
Expand All @@ -220,21 +215,6 @@ func (h *Handler) UploadExistingApp(w http.ResponseWriter, r *http.Request) {
}

if uploadExistingAppRequest.Deploy {
status, err := store.GetStore().GetStatusForVersion(a.ID, downstreams[0].ClusterID, newSequence)
if err != nil {
uploadResponse.Error = util.StrPointer("failed to get update downstream status")
logger.Error(errors.Wrap(err, *uploadResponse.Error))
JSON(w, http.StatusInternalServerError, uploadResponse)
return
}

if status == storetypes.VersionPendingConfig {
uploadResponse.Error = util.StrPointer(fmt.Sprintf("not deploying version %d because it's %s", newSequence, status))
logger.Error(errors.Wrap(err, *uploadResponse.Error))
JSON(w, http.StatusInternalServerError, uploadResponse)
return
}

if err := version.DeployVersion(a.ID, newSequence); err != nil {
cause := errors.Cause(err)
if _, ok := cause.(util.ActionableError); ok {
Expand Down
14 changes: 5 additions & 9 deletions pkg/kotsadmupstream/upstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/replicatedhq/kots/pkg/render"
"github.com/replicatedhq/kots/pkg/reporting"
"github.com/replicatedhq/kots/pkg/store"
storetypes "github.com/replicatedhq/kots/pkg/store/types"
"github.com/replicatedhq/kots/pkg/tasks"
"github.com/replicatedhq/kots/pkg/upstream"
"github.com/replicatedhq/kots/pkg/upstream/types"
Expand Down Expand Up @@ -265,7 +266,7 @@ func DownloadUpdate(appID string, update types.Update, skipPreflights bool, skip
if afterKotsKinds.Installation.Spec.UpdateCursor == beforeInstallation.UpdateCursor && afterKotsKinds.Installation.Spec.ChannelID == beforeInstallation.ChannelID {
return
}
newSequence, err := store.GetStore().CreateAppVersion(a.ID, &baseSequence, archiveDir, "Upstream Update", skipPreflights, render.Renderer{})
newSequence, err := store.GetStore().CreateAppVersion(a.ID, &baseSequence, archiveDir, "Upstream Update", false, false, "", skipPreflights, render.Renderer{})
if err != nil {
finalError = errors.Wrap(err, "failed to create version")
return
Expand All @@ -280,17 +281,12 @@ func DownloadUpdate(appID string, update types.Update, skipPreflights bool, skip
finalSequence = update.AppSequence
}

hasStrictPreflights, err := store.GetStore().HasStrictPreflights(a.ID, *finalSequence)
status, err := store.GetStore().GetDownstreamVersionStatus(a.ID, *finalSequence)
if err != nil {
finalError = errors.Wrap(err, "failed to check if app preflight has strict analyzers")
finalError = errors.Wrap(err, "failed to get downstream version status")
return
}

if hasStrictPreflights && skipPreflights {
logger.Warnf("preflights will not be skipped, strict preflights are set to %t", hasStrictPreflights)
}

if !skipPreflights || hasStrictPreflights {
if status == storetypes.VersionPendingPreflight {
if err := preflight.Run(appID, a.Slug, *finalSequence, a.IsAirgap, skipPreflights, archiveDir); err != nil {
finalError = errors.Wrap(err, "failed to run preflights")
return
Expand Down
Loading

0 comments on commit 239b0e5

Please sign in to comment.