-
Notifications
You must be signed in to change notification settings - Fork 8
feat(upgrade): add verification release is deployable #3136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
012b892
e6d0c86
2fabba8
df62f74
0249e25
041d8ec
473d223
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -16,6 +16,8 @@ import ( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/replicatedhq/embedded-cluster/cmd/installer/goods" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/replicatedhq/embedded-cluster/cmd/installer/kotscli" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/replicatedhq/embedded-cluster/pkg-new/replicatedapi" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/replicatedhq/embedded-cluster/pkg-new/validation" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/replicatedhq/embedded-cluster/pkg/airgap" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/replicatedhq/embedded-cluster/pkg/helpers" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/replicatedhq/embedded-cluster/pkg/kubeutils" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -59,6 +61,8 @@ type upgradeConfig struct { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| managerPort int | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requiresInfraUpgrade bool | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| kotsadmNamespace string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| currentAppVersion *kotscli.AppVersionInfo | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| replicatedAPIClient replicatedapi.Client | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // UpgradeCmd returns a cobra command for upgrading the embedded cluster application. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -110,7 +114,7 @@ func UpgradeCmd(ctx context.Context, appSlug, appTitle string) *cobra.Command { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := preRunUpgrade(ctx, flags, &upgradeConfig, existingRC, kcli, appSlug); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := verifyAndPromptUpgrade(ctx, flags, upgradeConfig, prompts.New()); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := verifyAndPromptUpgrade(ctx, flags, upgradeConfig, prompts.New(), kcli); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -254,8 +258,8 @@ func preRunUpgrade(ctx context.Context, flags UpgradeCmdFlags, upgradeConfig *up | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| upgradeConfig.license = l | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // sync the license if a license is provided and we are not in airgap mode | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if upgradeConfig.license != nil && flags.airgapBundle == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // sync the license and initialize the replicated api client if we are not in airgap mode | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if flags.airgapBundle == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| replicatedAPI, err := newReplicatedAPIClient(upgradeConfig.license, upgradeConfig.clusterID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to create replicated API client: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -267,6 +271,7 @@ func preRunUpgrade(ctx context.Context, flags UpgradeCmdFlags, upgradeConfig *up | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| upgradeConfig.license = updatedLicense | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| upgradeConfig.licenseBytes = licenseBytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| upgradeConfig.replicatedAPIClient = replicatedAPI | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Continue using "kotsadm" namespace if it exists for backwards compatibility, otherwise use the appSlug | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -337,10 +342,17 @@ func preRunUpgrade(ctx context.Context, flags UpgradeCmdFlags, upgradeConfig *up | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| upgradeConfig.requiresInfraUpgrade = requiresInfraUpgrade | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Get current app version for deployability validation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| currentAppVersion, err := kotscli.GetCurrentAppVersion(appSlug, upgradeConfig.kotsadmNamespace) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to get current app version: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| upgradeConfig.currentAppVersion = currentAppVersion | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func verifyAndPromptUpgrade(ctx context.Context, flags UpgradeCmdFlags, upgradeConfig upgradeConfig, prompt prompts.Prompt) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func verifyAndPromptUpgrade(ctx context.Context, flags UpgradeCmdFlags, upgradeConfig upgradeConfig, prompt prompts.Prompt, kcli client.Client) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isAirgap := flags.airgapBundle != "" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err := verifyChannelRelease("upgrade", isAirgap, flags.assumeYes) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -355,6 +367,16 @@ func verifyAndPromptUpgrade(ctx context.Context, flags UpgradeCmdFlags, upgradeC | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Validate release upgradable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := validateIsReleaseUpgradable(ctx, upgradeConfig, kcli, isAirgap); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var ve *validation.ValidationError | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if errors.As(err, &ve) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This is a validation error that prevents the upgrade from proceeding, expose the error directly | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ve | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("upgrade validation execution failed: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !isAirgap { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := maybePromptForAppUpdate(ctx, prompt, upgradeConfig.license, flags.assumeYes); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if errors.As(err, &ErrorNothingElseToAdd{}) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -554,3 +576,67 @@ func checkRequiresInfraUpgrade(ctx context.Context) (bool, error) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return !bytes.Equal(currentJSON, targetJSON), nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // validateIsReleaseUpgradable validates that the target release can be safely deployed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func validateIsReleaseUpgradable(ctx context.Context, upgradeConfig upgradeConfig, kcli client.Client, isAirgap bool) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Get current installation for version information | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| currentInstallation, err := kubeutils.GetLatestInstallation(ctx, kcli) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("get current installation: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Get target release data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| releaseData := release.GetReleaseData() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if releaseData == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("release data not found") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Get channel release info | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channelRelease := releaseData.ChannelRelease | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if channelRelease == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("channel release not found in release data") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Get current and target EC/K8s versions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var currentECVersion string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if currentInstallation.Spec.Config != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| currentECVersion = currentInstallation.Spec.Config.Version | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| targetECVersion := versions.Version | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Build validation options | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| opts := validation.UpgradableOptions{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CurrentECVersion: currentECVersion, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TargetECVersion: targetECVersion, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| License: upgradeConfig.license, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Add current app version info if available | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if upgradeConfig.currentAppVersion != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| opts.CurrentAppVersion = upgradeConfig.currentAppVersion.VersionLabel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| opts.CurrentAppSequence = upgradeConfig.currentAppVersion.ChannelSequence | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Add target app version info | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| opts.TargetAppVersion = channelRelease.VersionLabel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| opts.TargetAppSequence = channelRelease.ChannelSequence | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Extract the required releases depending on if it's airgap or online | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if isAirgap { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := opts.WithAirgapRequiredReleases(upgradeConfig.airgapMetadata); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to extract required releases from airgap metadata: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := opts.WithOnlineRequiredReleases(ctx, upgradeConfig.replicatedAPIClient); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to extract required releases from replidated API's pending release call: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Perform validation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := validation.ValidateIsReleaseUpgradable(ctx, opts); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the logic that builds these There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sgalsaleh, we cannot unit test this method, it has a replicated API dependency. Internally we've added a function that we unit test:
This method is unit tested:
So I'm trying to understand what we need to unit test, is it this logic?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or we only add a test for airgap? |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be taken out of the conditional as it is only set in airgap mode
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the other way around, it's only set in online mode right? And isn't that what we want? In airgap there's no point in initialising the replicated API client right?