cli: parse image and k8s versions as semver (#2235)

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2023-08-23 14:37:53 +02:00 committed by GitHub
parent 6d7c7f71a4
commit 47fc676927
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 43 additions and 35 deletions

View file

@ -286,10 +286,10 @@ func sortedMapKeys[T any](a map[string]T) []string {
} }
// filterImageUpgrades filters out image versions that are not valid upgrades. // filterImageUpgrades filters out image versions that are not valid upgrades.
func filterImageUpgrades(currentVersion string, newVersions []versionsapi.Version) []versionsapi.Version { func filterImageUpgrades(currentVersion consemver.Semver, newVersions []versionsapi.Version) []versionsapi.Version {
newImages := []versionsapi.Version{} newImages := []versionsapi.Version{}
for i := range newVersions { for i := range newVersions {
if err := compatibility.IsValidUpgrade(currentVersion, newVersions[i].Version()); err != nil { if err := compatibility.IsValidUpgrade(currentVersion.String(), newVersions[i].Version()); err != nil {
continue continue
} }
newImages = append(newImages, newVersions[i]) newImages = append(newImages, newVersions[i])
@ -298,10 +298,10 @@ func filterImageUpgrades(currentVersion string, newVersions []versionsapi.Versio
} }
// filterK8sUpgrades filters out K8s versions that are not valid upgrades. // filterK8sUpgrades filters out K8s versions that are not valid upgrades.
func filterK8sUpgrades(currentVersion string, newVersions []string) []string { func filterK8sUpgrades(currentVersion consemver.Semver, newVersions []string) []string {
result := []string{} result := []string{}
for i := range newVersions { for i := range newVersions {
if err := compatibility.IsValidUpgrade(currentVersion, newVersions[i]); err != nil { if err := compatibility.IsValidUpgrade(currentVersion.String(), newVersions[i]); err != nil {
continue continue
} }
result = append(result, newVersions[i]) result = append(result, newVersions[i])
@ -312,12 +312,12 @@ func filterK8sUpgrades(currentVersion string, newVersions []string) []string {
type collector interface { type collector interface {
currentVersions(ctx context.Context) (currentVersionInfo, error) currentVersions(ctx context.Context) (currentVersionInfo, error)
supportedVersions(ctx context.Context, currentImageVersion, currentK8sVersion string) (supportedVersionInfo, error) supportedVersions(ctx context.Context, currentImageVersion, currentK8sVersion consemver.Semver) (supportedVersionInfo, error)
newImages(ctx context.Context, currentImageVersion string) ([]versionsapi.Version, error) newImages(ctx context.Context, currentImageVersion consemver.Semver) ([]versionsapi.Version, error)
newMeasurements(ctx context.Context, csp cloudprovider.Provider, attestationVariant variant.Variant, images []versionsapi.Version) (map[string]measurements.M, error) newMeasurements(ctx context.Context, csp cloudprovider.Provider, attestationVariant variant.Variant, images []versionsapi.Version) (map[string]measurements.M, error)
newerVersions(ctx context.Context, allowedVersions []string) ([]versionsapi.Version, error) newerVersions(ctx context.Context, allowedVersions []string) ([]versionsapi.Version, error)
newCLIVersions(ctx context.Context) ([]consemver.Semver, error) newCLIVersions(ctx context.Context) ([]consemver.Semver, error)
filterCompatibleCLIVersions(ctx context.Context, cliPatchVersions []consemver.Semver, currentK8sVersion string) ([]consemver.Semver, error) filterCompatibleCLIVersions(ctx context.Context, cliPatchVersions []consemver.Semver, currentK8sVersion consemver.Semver) ([]consemver.Semver, error)
} }
type versionCollector struct { type versionCollector struct {
@ -365,8 +365,8 @@ func (v *versionCollector) newMeasurements(ctx context.Context, csp cloudprovide
type currentVersionInfo struct { type currentVersionInfo struct {
service consemver.Semver service consemver.Semver
image string image consemver.Semver
k8s string k8s consemver.Semver
cli consemver.Semver cli consemver.Semver
} }
@ -385,17 +385,19 @@ func (v *versionCollector) currentVersions(ctx context.Context) (currentVersionI
if err != nil { if err != nil {
return currentVersionInfo{}, fmt.Errorf("getting cluster versions: %w", err) return currentVersionInfo{}, fmt.Errorf("getting cluster versions: %w", err)
} }
if !semver.IsValid(clusterVersions.ImageVersion()) { imageVersion, err := consemver.New(clusterVersions.ImageVersion())
return currentVersionInfo{}, fmt.Errorf("checking image for valid semantic version: %w", err) if err != nil {
return currentVersionInfo{}, fmt.Errorf("parsing image semantic version: %w", err)
} }
if !semver.IsValid(clusterVersions.KubernetesVersion()) { k8sVersion, err := consemver.New(clusterVersions.KubernetesVersion())
return currentVersionInfo{}, fmt.Errorf("checking Kubernetes for valid semantic version: %w", err) if err != nil {
return currentVersionInfo{}, fmt.Errorf("parsing Kubernetes semantic version: %w", err)
} }
return currentVersionInfo{ return currentVersionInfo{
service: serviceVersions.ConstellationServices(), service: serviceVersions.ConstellationServices(),
image: clusterVersions.ImageVersion(), image: imageVersion,
k8s: clusterVersions.KubernetesVersion(), k8s: k8sVersion,
cli: v.cliVersion, cli: v.cliVersion,
}, nil }, nil
} }
@ -411,7 +413,7 @@ type supportedVersionInfo struct {
} }
// supportedVersions returns slices of supported versions. // supportedVersions returns slices of supported versions.
func (v *versionCollector) supportedVersions(ctx context.Context, currentImageVersion, currentK8sVersion string) (supportedVersionInfo, error) { func (v *versionCollector) supportedVersions(ctx context.Context, currentImageVersion, currentK8sVersion consemver.Semver) (supportedVersionInfo, error) {
k8sVersions := versions.SupportedK8sVersions() k8sVersions := versions.SupportedK8sVersions()
imageVersions, err := v.newImages(ctx, currentImageVersion) imageVersions, err := v.newImages(ctx, currentImageVersion)
@ -437,13 +439,13 @@ func (v *versionCollector) supportedVersions(ctx context.Context, currentImageVe
}, nil }, nil
} }
func (v *versionCollector) newImages(ctx context.Context, currentImageVersion string) ([]versionsapi.Version, error) { func (v *versionCollector) newImages(ctx context.Context, currentImageVersion consemver.Semver) ([]versionsapi.Version, error) {
// find compatible images // find compatible images
// image updates should always be possible for the current minor version of the cluster // image updates should always be possible for the current minor version of the cluster
// (e.g. 0.1.0 -> 0.1.1, 0.1.2, 0.1.3, etc.) // (e.g. 0.1.0 -> 0.1.1, 0.1.2, 0.1.3, etc.)
// additionally, we allow updates to the next minor version (e.g. 0.1.0 -> 0.2.0) // additionally, we allow updates to the next minor version (e.g. 0.1.0 -> 0.2.0)
// if the CLI minor version is newer than the cluster minor version // if the CLI minor version is newer than the cluster minor version
currentImageMinorVer := semver.MajorMinor(currentImageVersion) currentImageMinorVer := semver.MajorMinor(currentImageVersion.String())
currentCLIMinorVer := semver.MajorMinor(v.cliVersion.String()) currentCLIMinorVer := semver.MajorMinor(v.cliVersion.String())
nextImageMinorVer, err := compatibility.NextMinorVersion(currentImageMinorVer) nextImageMinorVer, err := compatibility.NextMinorVersion(currentImageMinorVer)
if err != nil { if err != nil {
@ -510,8 +512,8 @@ type versionUpgrade struct {
newCLI []consemver.Semver newCLI []consemver.Semver
newCompatibleCLI []consemver.Semver newCompatibleCLI []consemver.Semver
currentServices consemver.Semver currentServices consemver.Semver
currentImage string currentImage consemver.Semver
currentKubernetes string currentKubernetes consemver.Semver
currentCLI consemver.Semver currentCLI consemver.Semver
} }
@ -678,7 +680,7 @@ func (v *versionCollector) newCLIVersions(ctx context.Context) ([]consemver.Semv
} }
// filterCompatibleCLIVersions filters a list of CLI versions which are compatible with the current Kubernetes version. // filterCompatibleCLIVersions filters a list of CLI versions which are compatible with the current Kubernetes version.
func (v *versionCollector) filterCompatibleCLIVersions(ctx context.Context, cliPatchVersions []consemver.Semver, currentK8sVersion string, func (v *versionCollector) filterCompatibleCLIVersions(ctx context.Context, cliPatchVersions []consemver.Semver, currentK8sVersion consemver.Semver,
) ([]consemver.Semver, error) { ) ([]consemver.Semver, error) {
// filter out invalid upgrades and versions which are not compatible with the current Kubernetes version // filter out invalid upgrades and versions which are not compatible with the current Kubernetes version
var compatibleVersions []consemver.Semver var compatibleVersions []consemver.Semver
@ -698,7 +700,11 @@ func (v *versionCollector) filterCompatibleCLIVersions(ctx context.Context, cliP
} }
for _, k8sVersion := range info.Kubernetes { for _, k8sVersion := range info.Kubernetes {
if k8sVersion == currentK8sVersion { k8sVersionSem, err := consemver.New(k8sVersion)
if err != nil {
return nil, fmt.Errorf("parsing Kubernetes version %s: %w", k8sVersion, err)
}
if k8sVersionSem.Compare(currentK8sVersion) == 0 {
compatibleVersions = append(compatibleVersions, version) compatibleVersions = append(compatibleVersions, version)
} }
} }

View file

@ -24,6 +24,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/semver"
consemver "github.com/edgelesssys/constellation/v2/internal/semver" consemver "github.com/edgelesssys/constellation/v2/internal/semver"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -46,8 +47,8 @@ func TestBuildString(t *testing.T) {
newKubernetes: []string{"v1.24.12", "v1.25.6"}, newKubernetes: []string{"v1.24.12", "v1.25.6"},
newCLI: []consemver.Semver{consemver.NewFromInt(2, 5, 0, ""), consemver.NewFromInt(2, 6, 0, "")}, newCLI: []consemver.Semver{consemver.NewFromInt(2, 5, 0, ""), consemver.NewFromInt(2, 6, 0, "")},
currentServices: consemver.NewFromInt(2, 4, 0, ""), currentServices: consemver.NewFromInt(2, 4, 0, ""),
currentImage: "v2.4.0", currentImage: semver.NewFromInt(2, 4, 0, ""),
currentKubernetes: "v1.24.5", currentKubernetes: semver.NewFromInt(1, 24, 5, ""),
currentCLI: consemver.NewFromInt(2, 4, 0, ""), currentCLI: consemver.NewFromInt(2, 4, 0, ""),
}, },
expected: "The following updates are available with this CLI:\n Kubernetes: v1.24.5 --> v1.24.12 v1.25.6\n Images:\n v2.4.0 --> v2.5.0\n Includes these measurements:\n 4:\n expected: \"1234123412341234123412341234123412341234123412341234123412341234\"\n warnOnly: false\n 8:\n expected: \"0000000000000000000000000000000000000000000000000000000000000000\"\n warnOnly: false\n 9:\n expected: \"1234123412341234123412341234123412341234123412341234123412341234\"\n warnOnly: false\n 11:\n expected: \"0000000000000000000000000000000000000000000000000000000000000000\"\n warnOnly: false\n 12:\n expected: \"1234123412341234123412341234123412341234123412341234123412341234\"\n warnOnly: false\n 13:\n expected: \"0000000000000000000000000000000000000000000000000000000000000000\"\n warnOnly: false\n 15:\n expected: \"0000000000000000000000000000000000000000000000000000000000000000\"\n warnOnly: false\n \n Services: v2.4.0 --> v2.5.0\n", expected: "The following updates are available with this CLI:\n Kubernetes: v1.24.5 --> v1.24.12 v1.25.6\n Images:\n v2.4.0 --> v2.5.0\n Includes these measurements:\n 4:\n expected: \"1234123412341234123412341234123412341234123412341234123412341234\"\n warnOnly: false\n 8:\n expected: \"0000000000000000000000000000000000000000000000000000000000000000\"\n warnOnly: false\n 9:\n expected: \"1234123412341234123412341234123412341234123412341234123412341234\"\n warnOnly: false\n 11:\n expected: \"0000000000000000000000000000000000000000000000000000000000000000\"\n warnOnly: false\n 12:\n expected: \"1234123412341234123412341234123412341234123412341234123412341234\"\n warnOnly: false\n 13:\n expected: \"0000000000000000000000000000000000000000000000000000000000000000\"\n warnOnly: false\n 15:\n expected: \"0000000000000000000000000000000000000000000000000000000000000000\"\n warnOnly: false\n \n Services: v2.4.0 --> v2.5.0\n",
@ -69,7 +70,7 @@ func TestBuildString(t *testing.T) {
"k8s only": { "k8s only": {
upgrade: versionUpgrade{ upgrade: versionUpgrade{
newKubernetes: []string{"v1.24.12", "v1.25.6"}, newKubernetes: []string{"v1.24.12", "v1.25.6"},
currentKubernetes: "v1.24.5", currentKubernetes: semver.NewFromInt(1, 24, 5, ""),
}, },
expected: "The following updates are available with this CLI:\n Kubernetes: v1.24.5 --> v1.24.12 v1.25.6\n", expected: "The following updates are available with this CLI:\n Kubernetes: v1.24.5 --> v1.24.12 v1.25.6\n",
}, },
@ -80,8 +81,8 @@ func TestBuildString(t *testing.T) {
newKubernetes: []string{}, newKubernetes: []string{},
newCLI: []consemver.Semver{}, newCLI: []consemver.Semver{},
currentServices: consemver.NewFromInt(2, 5, 0, ""), currentServices: consemver.NewFromInt(2, 5, 0, ""),
currentImage: "v2.5.0", currentImage: semver.NewFromInt(2, 5, 0, ""),
currentKubernetes: "v1.25.6", currentKubernetes: semver.NewFromInt(1, 25, 6, ""),
currentCLI: consemver.NewFromInt(2, 5, 0, ""), currentCLI: consemver.NewFromInt(2, 5, 0, ""),
}, },
expected: "You are up to date.\n", expected: "You are up to date.\n",
@ -164,8 +165,8 @@ func TestUpgradeCheck(t *testing.T) {
}, },
supportedK8sVersions: []string{"v1.24.5", "v1.24.12", "v1.25.6"}, supportedK8sVersions: []string{"v1.24.5", "v1.24.12", "v1.25.6"},
currentServicesVersions: consemver.NewFromInt(2, 4, 0, ""), currentServicesVersions: consemver.NewFromInt(2, 4, 0, ""),
currentImageVersion: "v2.4.0", currentImageVersion: semver.NewFromInt(2, 4, 0, ""),
currentK8sVersion: "v1.24.5", currentK8sVersion: semver.NewFromInt(1, 24, 5, ""),
currentCLIVersion: consemver.NewFromInt(2, 4, 0, ""), currentCLIVersion: consemver.NewFromInt(2, 4, 0, ""),
images: []versionsapi.Version{v2_5}, images: []versionsapi.Version{v2_5},
newCLIVersionsList: []consemver.Semver{consemver.NewFromInt(2, 5, 0, ""), consemver.NewFromInt(2, 6, 0, "")}, newCLIVersionsList: []consemver.Semver{consemver.NewFromInt(2, 5, 0, ""), consemver.NewFromInt(2, 6, 0, "")},
@ -230,8 +231,8 @@ type stubVersionCollector struct {
supportedK8sVersions []string supportedK8sVersions []string
supportedCLIVersions []consemver.Semver supportedCLIVersions []consemver.Semver
currentServicesVersions consemver.Semver currentServicesVersions consemver.Semver
currentImageVersion string currentImageVersion consemver.Semver
currentK8sVersion string currentK8sVersion consemver.Semver
currentCLIVersion consemver.Semver currentCLIVersion consemver.Semver
images []versionsapi.Version images []versionsapi.Version
newCLIVersionsList []consemver.Semver newCLIVersionsList []consemver.Semver
@ -252,7 +253,7 @@ func (s *stubVersionCollector) currentVersions(_ context.Context) (currentVersio
}, s.someErr }, s.someErr
} }
func (s *stubVersionCollector) supportedVersions(_ context.Context, _, _ string) (supportedVersionInfo, error) { func (s *stubVersionCollector) supportedVersions(_ context.Context, _, _ consemver.Semver) (supportedVersionInfo, error) {
return supportedVersionInfo{ return supportedVersionInfo{
service: s.supportedServicesVersions, service: s.supportedServicesVersions,
image: s.supportedImages, image: s.supportedImages,
@ -261,7 +262,7 @@ func (s *stubVersionCollector) supportedVersions(_ context.Context, _, _ string)
}, s.someErr }, s.someErr
} }
func (s *stubVersionCollector) newImages(_ context.Context, _ string) ([]versionsapi.Version, error) { func (s *stubVersionCollector) newImages(_ context.Context, _ consemver.Semver) ([]versionsapi.Version, error) {
return s.images, nil return s.images, nil
} }
@ -273,7 +274,7 @@ func (s *stubVersionCollector) newCLIVersions(_ context.Context) ([]consemver.Se
return s.newCLIVersionsList, nil return s.newCLIVersionsList, nil
} }
func (s *stubVersionCollector) filterCompatibleCLIVersions(_ context.Context, _ []consemver.Semver, _ string) ([]consemver.Semver, error) { func (s *stubVersionCollector) filterCompatibleCLIVersions(_ context.Context, _ []consemver.Semver, _ consemver.Semver) ([]consemver.Semver, error) {
return s.newCompatibleCLIVersionsList, nil return s.newCompatibleCLIVersionsList, nil
} }
@ -373,7 +374,7 @@ func TestFilterCompatibleCLIVersions(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
require := require.New(t) require := require.New(t)
_, err := tc.verCollector.filterCompatibleCLIVersions(context.Background(), tc.cliPatchVersions, "v1.24.5") _, err := tc.verCollector.filterCompatibleCLIVersions(context.Background(), tc.cliPatchVersions, semver.NewFromInt(1, 24, 5, ""))
if tc.wantErr { if tc.wantErr {
require.Error(err) require.Error(err)
return return

View file

@ -131,6 +131,7 @@ func (v Semver) String() string {
} }
// Compare compares two versions. It relies on the semver.Compare function internally. // Compare compares two versions. It relies on the semver.Compare function internally.
// The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
func (v Semver) Compare(other Semver) int { func (v Semver) Compare(other Semver) int {
return semver.Compare(v.String(), other.String()) return semver.Compare(v.String(), other.String())
} }