From 9089b2b0cfa0f429f4db7427b724fbf29acbc891 Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Sun, 1 Dec 2024 17:22:54 +0100 Subject: [PATCH] feat(upgrade): add force-upgrade flag for failed installations - Add --force-upgrade flag to porter upgrade command - Implement ForceUpgrade option in UpgradeOptions struct - Allow upgrades on failed installations when force-upgrade is true - Add integration test for force-upgrade functionality Signed-off-by: Kim Christensen --- cmd/porter/installations.go | 2 ++ pkg/porter/upgrade.go | 5 ++++- tests/integration/upgrade_test.go | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/cmd/porter/installations.go b/cmd/porter/installations.go index 30e6586d8..cab97934e 100644 --- a/cmd/porter/installations.go +++ b/cmd/porter/installations.go @@ -304,6 +304,8 @@ The docker driver runs the bundle container using the local Docker host. To use "Namespace of the specified installation. Defaults to the global namespace.") f.StringVar(&opts.Version, "version", "", "Version to which the installation should be upgraded. This represents the version of the bundle, which assumes the convention of setting the bundle tag to its version.") + f.BoolVar(&opts.ForceUpgrade, "force-upgrade", false, + "Force the upgrade to run even if the current installation is marked as failed.") addBundleActionFlags(f, opts) // Allow configuring the --driver flag with runtime-driver, to avoid conflicts with other commands diff --git a/pkg/porter/upgrade.go b/pkg/porter/upgrade.go index 3fed12d48..826e44012 100644 --- a/pkg/porter/upgrade.go +++ b/pkg/porter/upgrade.go @@ -19,6 +19,9 @@ type UpgradeOptions struct { // Version of the bundle to upgrade to Version string + + // ForceUpgrade allows the upgrade to run even if the current installation is marked as failed. + ForceUpgrade bool } func NewUpgradeOptions() *UpgradeOptions { @@ -64,7 +67,7 @@ func (p *Porter) UpgradeBundle(ctx context.Context, opts *UpgradeOptions) error return span.Errorf("could not find installation %s/%s: %w", opts.Namespace, opts.Name, err) } - if !i.IsInstalled() { + if !i.IsInstalled() && !opts.ForceUpgrade { return span.Errorf("The installation cannot be upgraded, because it is not installed. Verify the installation name and namespace, and if correct, use porter install.") } diff --git a/tests/integration/upgrade_test.go b/tests/integration/upgrade_test.go index dd0e215a4..c768bef98 100644 --- a/tests/integration/upgrade_test.go +++ b/tests/integration/upgrade_test.go @@ -31,6 +31,29 @@ func TestUpgrade_failedInstallation(t *testing.T) { require.Error(t, err, "Upgrade should fail, because the installation failed") } +func TestUpgrade_failedInstallation_withForceUpgrade(t *testing.T) { + p := porter.NewTestPorter(t) + defer p.Close() + ctx := p.SetupIntegrationTest() + + p.AddTestBundleDir("testdata/bundles/bundle-with-failing-install", false) + + installOpts := porter.NewInstallOptions() + err := installOpts.Validate(ctx, []string{}, p.Porter) + require.NoError(t, err) + + err = p.InstallBundle(ctx, installOpts) + require.Error(t, err, "Installation should fail") + + upgradeOpts := porter.NewUpgradeOptions() + upgradeOpts.ForceUpgrade = true + err = upgradeOpts.Validate(ctx, []string{}, p.Porter) + require.NoError(t, err) + + err = p.UpgradeBundle(ctx, upgradeOpts) + require.NoError(t, err, "Upgrade should succeed, because force-upgrade is true") +} + func TestUpgrade_DebugModeAppliesToSingleInvocation(t *testing.T) { p := porter.NewTestPorter(t) defer p.Close()