cli: check chart versions against target version in users config before upgrading (#2319)

* Check chart versions against target in users config

Signed-off-by: Daniel Weiße <dw@edgeless.systems>

* Cleaner cli-config version support checking

Signed-off-by: Daniel Weiße <dw@edgeless.systems>

* Return InvalidUpgradeError

Signed-off-by: Daniel Weiße <dw@edgeless.systems>

---------

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2023-09-08 23:09:02 +02:00 committed by GitHub
parent 5706e69091
commit 2a1996dbe1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 318 additions and 24 deletions

View file

@ -29,7 +29,6 @@ type actionFactory struct {
versionLister releaseVersionLister
cfg *action.Configuration
kubeClient crdClient
cliVersion semver.Semver
log debugLog
}
@ -38,9 +37,8 @@ type crdClient interface {
}
// newActionFactory creates a new action factory for managing helm releases.
func newActionFactory(kubeClient crdClient, lister releaseVersionLister, actionConfig *action.Configuration, cliVersion semver.Semver, log debugLog) *actionFactory {
func newActionFactory(kubeClient crdClient, lister releaseVersionLister, actionConfig *action.Configuration, log debugLog) *actionFactory {
return &actionFactory{
cliVersion: cliVersion,
versionLister: lister,
cfg: actionConfig,
kubeClient: kubeClient,
@ -49,10 +47,10 @@ func newActionFactory(kubeClient crdClient, lister releaseVersionLister, actionC
}
// GetActions returns a list of actions to apply the given releases.
func (a actionFactory) GetActions(releases []Release, force, allowDestructive bool) (actions []applyAction, includesUpgrade bool, err error) {
func (a actionFactory) GetActions(releases []Release, configTargetVersion semver.Semver, force, allowDestructive bool) (actions []applyAction, includesUpgrade bool, err error) {
upgradeErrs := []error{}
for _, release := range releases {
err := a.appendNewAction(release, force, allowDestructive, &actions)
err := a.appendNewAction(release, configTargetVersion, force, allowDestructive, &actions)
var invalidUpgrade *compatibility.InvalidUpgradeError
if errors.As(err, &invalidUpgrade) {
upgradeErrs = append(upgradeErrs, err)
@ -71,13 +69,24 @@ func (a actionFactory) GetActions(releases []Release, force, allowDestructive bo
return actions, includesUpgrade, errors.Join(upgradeErrs...)
}
func (a actionFactory) appendNewAction(release Release, force, allowDestructive bool, actions *[]applyAction) error {
func (a actionFactory) appendNewAction(release Release, configTargetVersion semver.Semver, force, allowDestructive bool, actions *[]applyAction) error {
newVersion, err := semver.New(release.Chart.Metadata.Version)
if err != nil {
return fmt.Errorf("parsing chart version: %w", err)
}
cliSupportsConfigVersion := configTargetVersion.Compare(newVersion) != 0
currentVersion, err := a.versionLister.currentVersion(release.ReleaseName)
if errors.Is(err, errReleaseNotFound) {
// Don't install a new release if the user's config specifies a different version than the CLI offers.
if !force && isCLIVersionedRelease(release.ReleaseName) && cliSupportsConfigVersion {
return compatibility.NewInvalidUpgradeError(
currentVersion.String(),
configTargetVersion.String(),
fmt.Errorf("this CLI only supports installing microservice version %s", newVersion),
)
}
a.log.Debugf("Release %s not found, adding to new releases...", release.ReleaseName)
*actions = append(*actions, a.newInstall(release))
return nil
@ -88,18 +97,31 @@ func (a actionFactory) appendNewAction(release Release, force, allowDestructive
a.log.Debugf("Current %s version: %s", release.ReleaseName, currentVersion)
a.log.Debugf("New %s version: %s", release.ReleaseName, newVersion)
// This may break for cert-manager or cilium if we decide to upgrade more than one minor version at a time.
// Leaving it as is since it is not clear to me what kind of sanity check we could do.
if !force {
if err := newVersion.IsUpgradeTo(currentVersion); err != nil {
return fmt.Errorf("invalid upgrade for %s: %w", release.ReleaseName, err)
// For charts we package ourselves, the version is equal to the CLI version (charts are embedded in the binary).
// We need to make sure this matches with the version in a user's config, if an upgrade should be applied.
if isCLIVersionedRelease(release.ReleaseName) {
// If target version is not a valid upgrade, don't upgrade any charts.
if err := configTargetVersion.IsUpgradeTo(currentVersion); err != nil {
return fmt.Errorf("invalid upgrade for %s: %w", release.ReleaseName, err)
}
// Target version is newer than current version, so we should perform an upgrade.
// Now make sure the target version is equal to the the CLI version.
if cliSupportsConfigVersion {
return compatibility.NewInvalidUpgradeError(
currentVersion.String(),
configTargetVersion.String(),
fmt.Errorf("this CLI only supports upgrading to microservice version %s", newVersion),
)
}
} else {
// This may break for external chart dependencies if we decide to upgrade more than one minor version at a time.
if err := newVersion.IsUpgradeTo(currentVersion); err != nil {
return fmt.Errorf("invalid upgrade for %s: %w", release.ReleaseName, err)
}
}
}
// at this point we conclude that the release should be upgraded. check that this CLI supports the upgrade.
if isCLIVersionedRelease(release.ReleaseName) && a.cliVersion.Compare(newVersion) != 0 {
return fmt.Errorf("this CLI only supports microservice version %s for upgrading", a.cliVersion.String())
}
if !allowDestructive &&
release.ReleaseName == certManagerInfo.releaseName {
return ErrConfirmationMissing