cli: use Semver type to represent microservice versions (#2125)

Previously we used strings to pass microservice versions. This invited
bugs due to missing input validation.
This commit is contained in:
Otto Bittner 2023-07-25 14:20:25 +02:00 committed by GitHub
parent 2d3999440d
commit 1d5a8283e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 612 additions and 318 deletions

View file

@ -459,6 +459,7 @@ go_test(
"//internal/deploy/helm",
"//internal/file",
"//internal/logger",
"//internal/semver",
"@com_github_pkg_errors//:errors",
"@com_github_spf13_afero//:afero",
"@com_github_stretchr_testify//assert",

View file

@ -77,7 +77,7 @@ func NewClient(client crdClient, kubeConfigPath, helmNamespace string, log debug
return &Client{kubectl: client, fs: fileHandler, actions: actions{config: actionConfig}, log: log}, nil
}
func (c *Client) shouldUpgrade(releaseName, newVersion string, force bool) error {
func (c *Client) shouldUpgrade(releaseName string, newVersion semver.Semver, force bool) error {
currentVersion, err := c.currentVersion(releaseName)
if err != nil {
return fmt.Errorf("getting version for %s: %w", releaseName, err)
@ -88,14 +88,15 @@ func (c *Client) shouldUpgrade(releaseName, newVersion string, force bool) error
// 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 := compatibility.IsValidUpgrade(currentVersion, newVersion); err != nil {
if err := newVersion.IsUpgradeTo(currentVersion); err != nil {
return err
}
}
cliVersion := constants.BinaryVersion()
// at this point we conclude that the release should be upgraded. check that this CLI supports the upgrade.
if releaseName == constellationOperatorsInfo.releaseName || releaseName == constellationServicesInfo.releaseName {
if compatibility.EnsurePrefixV(constants.VersionInfo()) != compatibility.EnsurePrefixV(newVersion) {
return fmt.Errorf("this CLI only supports microservice version %s for upgrading", constants.VersionInfo())
if cliVersion.Compare(newVersion) != 0 {
return fmt.Errorf("this CLI only supports microservice version %s for upgrading", cliVersion.String())
}
}
c.log.Debugf("Upgrading %s from %s to %s", releaseName, currentVersion, newVersion)
@ -118,13 +119,17 @@ func (c *Client) Upgrade(ctx context.Context, config *config.Config, idFile clus
}
// define target version the chart is upgraded to
var upgradeVersion string
var upgradeVersion semver.Semver
if info == constellationOperatorsInfo || info == constellationServicesInfo {
// ensure that the services chart has the same version as the CLI
updateVersions(chart, compatibility.EnsurePrefixV(constants.VersionInfo()))
updateVersions(chart, constants.BinaryVersion())
upgradeVersion = config.MicroserviceVersion
} else {
upgradeVersion = chart.Metadata.Version
chartVersion, err := semver.New(chart.Metadata.Version)
if err != nil {
return fmt.Errorf("parsing chart version: %w", err)
}
upgradeVersion = chartVersion
}
var invalidUpgrade *compatibility.InvalidUpgradeError
@ -222,49 +227,51 @@ func (c *Client) Versions() (ServiceVersions, error) {
}
res := ServiceVersions{
cilium: compatibility.EnsurePrefixV(ciliumVersion),
certManager: compatibility.EnsurePrefixV(certManagerVersion),
constellationOperators: compatibility.EnsurePrefixV(operatorsVersion),
constellationServices: compatibility.EnsurePrefixV(servicesVersion),
cilium: ciliumVersion,
certManager: certManagerVersion,
constellationOperators: operatorsVersion,
constellationServices: servicesVersion,
awsLBController: awsLBVersion,
}
if awsLBVersion != "" {
res.awsLBController = compatibility.EnsurePrefixV(awsLBVersion)
if awsLBVersion != (semver.Semver{}) {
res.awsLBController = awsLBVersion
}
return res, nil
}
// currentVersion returns the version of the currently installed helm release.
func (c *Client) currentVersion(release string) (string, error) {
func (c *Client) currentVersion(release string) (semver.Semver, error) {
rel, err := c.actions.listAction(release)
if err != nil {
return "", err
return semver.Semver{}, err
}
if len(rel) == 0 {
return "", errReleaseNotFound
return semver.Semver{}, errReleaseNotFound
}
if len(rel) > 1 {
return "", fmt.Errorf("multiple releases found for %s", release)
return semver.Semver{}, fmt.Errorf("multiple releases found for %s", release)
}
if rel[0] == nil || rel[0].Chart == nil || rel[0].Chart.Metadata == nil {
return "", fmt.Errorf("received invalid release %s", release)
return semver.Semver{}, fmt.Errorf("received invalid release %s", release)
}
return rel[0].Chart.Metadata.Version, nil
return semver.New(rel[0].Chart.Metadata.Version)
}
// ServiceVersions bundles the versions of all services that are part of Constellation.
type ServiceVersions struct {
cilium string
certManager string
constellationOperators string
constellationServices string
awsLBController string
cilium semver.Semver
certManager semver.Semver
constellationOperators semver.Semver
constellationServices semver.Semver
awsLBController semver.Semver
}
// NewServiceVersions returns a new ServiceVersions struct.
func NewServiceVersions(cilium, certManager, constellationOperators, constellationServices string) ServiceVersions {
func NewServiceVersions(cilium, certManager, constellationOperators, constellationServices semver.Semver) ServiceVersions {
return ServiceVersions{
cilium: cilium,
certManager: certManager,
@ -274,22 +281,22 @@ func NewServiceVersions(cilium, certManager, constellationOperators, constellati
}
// Cilium returns the version of the Cilium release.
func (s ServiceVersions) Cilium() string {
func (s ServiceVersions) Cilium() semver.Semver {
return s.cilium
}
// CertManager returns the version of the cert-manager release.
func (s ServiceVersions) CertManager() string {
func (s ServiceVersions) CertManager() semver.Semver {
return s.certManager
}
// ConstellationOperators returns the version of the constellation-operators release.
func (s ServiceVersions) ConstellationOperators() string {
func (s ServiceVersions) ConstellationOperators() semver.Semver {
return s.constellationOperators
}
// ConstellationServices returns the version of the constellation-services release.
func (s ServiceVersions) ConstellationServices() string {
func (s ServiceVersions) ConstellationServices() semver.Semver {
return s.constellationServices
}
@ -385,12 +392,8 @@ func (c *Client) applyMigrations(ctx context.Context, releaseName string, values
if err != nil {
return fmt.Errorf("getting %s version: %w", releaseName, err)
}
currentV, err := semver.New(current)
if err != nil {
return fmt.Errorf("parsing current version: %w", err)
}
if currentV.Major == 2 && currentV.Minor == 8 {
if current.Major() == 2 && current.Minor() == 8 {
// Rename/change the following function to implement any necessary migrations.
return migrateFrom2_8(ctx, values, conf, c.kubectl)
}

View file

@ -15,6 +15,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/compatibility"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/semver"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"helm.sh/helm/v3/pkg/chart"
@ -49,7 +50,9 @@ func TestShouldUpgrade(t *testing.T) {
chart, err := loadChartsDir(helmFS, certManagerInfo.path)
require.NoError(err)
err = client.shouldUpgrade(certManagerInfo.releaseName, chart.Metadata.Version, false)
chartVersion, err := semver.New(chart.Metadata.Version)
require.NoError(err)
err = client.shouldUpgrade(certManagerInfo.releaseName, chartVersion, false)
if tc.wantError {
tc.assertCorrectError(t, err)
return

View file

@ -25,10 +25,10 @@ import (
"github.com/edgelesssys/constellation/v2/cli/internal/helm/imageversion"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/compatibility"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/deploy/helm"
"github.com/edgelesssys/constellation/v2/internal/semver"
"github.com/edgelesssys/constellation/v2/internal/versions"
)
@ -175,15 +175,15 @@ func (i *ChartLoader) loadRelease(info chartInfo, helmWaitMode helm.WaitMode) (h
case certManagerInfo.releaseName:
values = i.loadCertManagerValues()
case constellationOperatorsInfo.releaseName:
updateVersions(chart, compatibility.EnsurePrefixV(constants.VersionInfo()))
updateVersions(chart, constants.BinaryVersion())
values = i.loadOperatorsValues()
case constellationServicesInfo.releaseName:
updateVersions(chart, compatibility.EnsurePrefixV(constants.VersionInfo()))
updateVersions(chart, constants.BinaryVersion())
values = i.loadConstellationServicesValues()
case awsLBControllerInfo.releaseName:
values = i.loadAWSLBControllerValues()
case csiInfo.releaseName:
updateVersions(chart, compatibility.EnsurePrefixV(constants.VersionInfo()))
updateVersions(chart, constants.BinaryVersion())
values = i.loadCSIValues()
}
@ -378,19 +378,19 @@ func extendConstellationServicesValues(
}
// updateVersions changes all versions of direct dependencies that are set to "0.0.0" to newVersion.
func updateVersions(chart *chart.Chart, newVersion string) {
chart.Metadata.Version = newVersion
func updateVersions(chart *chart.Chart, newVersion semver.Semver) {
chart.Metadata.Version = newVersion.String()
selectedDeps := chart.Metadata.Dependencies
for i := range selectedDeps {
if selectedDeps[i].Version == "0.0.0" {
selectedDeps[i].Version = newVersion
selectedDeps[i].Version = newVersion.String()
}
}
deps := chart.Dependencies()
for i := range deps {
if deps[i].Metadata.Version == "0.0.0" {
deps[i].Metadata.Version = newVersion
deps[i].Metadata.Version = newVersion.String()
}
}
}