cli: show available cli upgrades on upgrade check command (#1394)

* cli: upgrade check show cli upgrades

* only check compatibility for valid upgrades

* use semver.Sort

* extend unit tests

* add unit test for new compatible cli versions

* adapt to feedback

* fix rebase

* rework output

* minor -> major

Co-authored-by: Otto Bittner <cobittner@posteo.net>

* minor -> major

Co-authored-by: Otto Bittner <cobittner@posteo.net>

* dynamic major version

Co-authored-by: Otto Bittner <cobittner@posteo.net>

* remove currentK8sVer argument

* bazel gen & tidy

* bazel update

---------

Co-authored-by: Otto Bittner <cobittner@posteo.net>
This commit is contained in:
Moritz Sanft 2023-04-03 14:31:17 +02:00 committed by GitHub
parent 33d0b8f59d
commit 46f5b1734e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 364 additions and 56 deletions

View file

@ -42,26 +42,51 @@ func TestBuildString(t *testing.T) {
"v2.5.0": measurements.DefaultsFor(cloudprovider.QEMU),
},
newKubernetes: []string{"v1.24.12", "v1.25.6"},
newCLI: []string{"v2.5.0", "v2.6.0"},
currentServices: "v2.4.0",
currentImage: "v2.4.0",
currentKubernetes: "v1.24.5",
currentCLI: "v2.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",
},
"cli incompatible with K8s": {
upgrade: versionUpgrade{
newCLI: []string{"v2.5.0", "v2.6.0"},
currentCLI: "v2.4.0",
},
expected: "There are newer CLIs available (v2.5.0 v2.6.0), however, you need to upgrade your cluster's Kubernetes version first.\n",
},
"cli compatible with K8s": {
upgrade: versionUpgrade{
newCompatibleCLI: []string{"v2.5.0", "v2.6.0"},
currentCLI: "v2.4.0",
},
expected: "Newer CLI versions that are compatible with your cluster are: v2.5.0 v2.6.0\n",
},
"k8s only": {
upgrade: versionUpgrade{
newKubernetes: []string{"v1.24.12", "v1.25.6"},
currentKubernetes: "v1.24.5",
},
expected: "The following updates are available with this CLI:\n Kubernetes: v1.24.5 --> v1.24.12 v1.25.6\n",
},
"no upgrades": {
upgrade: versionUpgrade{
newServices: "",
newImages: map[string]measurements.M{},
newKubernetes: []string{},
newCLI: []string{},
currentServices: "v2.5.0",
currentImage: "v2.5.0",
currentKubernetes: "v1.25.6",
currentCLI: "v2.5.0",
},
expected: "No upgrades available with this CLI.\nNewer versions may be available at: https://github.com/edgelesssys/constellation/releases\n",
expected: "You are up to date.\n",
},
"no upgrades #2": {
upgrade: versionUpgrade{},
expected: "No upgrades available with this CLI.\nNewer versions may be available at: https://github.com/edgelesssys/constellation/releases\n",
expected: "You are up to date.\n",
},
}
@ -217,8 +242,9 @@ func TestUpgradeCheck(t *testing.T) {
currentServicesVersions: "v2.4.0",
currentImageVersion: "v2.4.0",
currentK8sVersion: "v1.24.5",
currentCLIVersion: "v2.4.0",
images: []versionsapi.Version{v2_5},
newCLIVersions: []string{"v2.5.0", "v2.6.0"},
newCLIVersionsList: []string{"v2.5.0", "v2.6.0"},
},
flags: upgradeCheckFlags{
configPath: constants.ConfigFilename,
@ -255,28 +281,41 @@ func TestUpgradeCheck(t *testing.T) {
}
type stubVersionCollector struct {
supportedServicesVersions string
supportedImages []versionsapi.Version
supportedImageVersions map[string]measurements.M
supportedK8sVersions []string
currentServicesVersions string
currentImageVersion string
currentK8sVersion string
images []versionsapi.Version
newCLIVersions []string
someErr error
supportedServicesVersions string
supportedImages []versionsapi.Version
supportedImageVersions map[string]measurements.M
supportedK8sVersions []string
supportedCLIVersions []string
currentServicesVersions string
currentImageVersion string
currentK8sVersion string
currentCLIVersion string
images []versionsapi.Version
newCLIVersionsList []string
newCompatibleCLIVersionsList []string
someErr error
}
func (s *stubVersionCollector) newMeasurementes(_ context.Context, _ cloudprovider.Provider, _ []versionsapi.Version) (map[string]measurements.M, error) {
func (s *stubVersionCollector) newMeasurements(ctx context.Context, csp cloudprovider.Provider, images []versionsapi.Version) (map[string]measurements.M, error) {
return s.supportedImageVersions, nil
}
func (s *stubVersionCollector) currentVersions(_ context.Context) (serviceVersions string, imageVersion string, k8sVersion string, err error) {
return s.currentServicesVersions, s.currentImageVersion, s.currentK8sVersion, s.someErr
func (s *stubVersionCollector) currentVersions(ctx context.Context) (currentVersionInfo, error) {
return currentVersionInfo{
service: s.currentServicesVersions,
image: s.currentImageVersion,
k8s: s.currentK8sVersion,
cli: s.currentCLIVersion,
}, s.someErr
}
func (s *stubVersionCollector) supportedVersions(_ context.Context, _ string) (serviceVersions string, imageVersions []versionsapi.Version, k8sVersions []string, err error) {
return s.supportedServicesVersions, s.supportedImages, s.supportedK8sVersions, s.someErr
func (s *stubVersionCollector) supportedVersions(ctx context.Context, version, currentK8sVersion string) (supportedVersionInfo, error) {
return supportedVersionInfo{
service: s.supportedServicesVersions,
image: s.supportedImages,
k8s: s.supportedK8sVersions,
cli: s.supportedCLIVersions,
}, s.someErr
}
func (s *stubVersionCollector) newImages(_ context.Context, _ string) ([]versionsapi.Version, error) {
@ -287,6 +326,14 @@ func (s *stubVersionCollector) newerVersions(_ context.Context, _ []string) ([]v
return s.images, nil
}
func (s *stubVersionCollector) newCLIVersions(ctx context.Context) ([]string, error) {
return s.newCLIVersionsList, nil
}
func (s *stubVersionCollector) filterCompatibleCLIVersions(ctx context.Context, cliPatchVersions []string, currentK8sVersion string) ([]string, error) {
return s.newCompatibleCLIVersionsList, nil
}
type stubUpgradeChecker struct {
image string
k8sVersion string
@ -300,3 +347,121 @@ func (u stubUpgradeChecker) CurrentImage(context.Context) (string, error) {
func (u stubUpgradeChecker) CurrentKubernetesVersion(_ context.Context) (string, error) {
return u.k8sVersion, u.err
}
func TestNewCLIVersions(t *testing.T) {
someErr := errors.New("some error")
minorList := func() versionsapi.List {
return versionsapi.List{
Versions: []string{"v0.2.0"},
}
}
patchList := func() versionsapi.List {
return versionsapi.List{
Versions: []string{"v0.2.1"},
}
}
emptyVerList := func() versionsapi.List {
return versionsapi.List{}
}
verCollector := func(minorList, patchList versionsapi.List, verListErr error) versionCollector {
return versionCollector{
cliVersion: "v0.1.0",
versionsapi: stubVersionFetcher{
minorList: minorList,
patchList: patchList,
versionListErr: verListErr,
},
}
}
testCases := map[string]struct {
verCollector versionCollector
wantErr bool
}{
"works": {
verCollector: verCollector(minorList(), patchList(), nil),
},
"empty versions list": {
verCollector: verCollector(emptyVerList(), emptyVerList(), nil),
},
"version list error": {
verCollector: verCollector(minorList(), patchList(), someErr),
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
require := require.New(t)
_, err := tc.verCollector.newCLIVersions(context.Background())
if tc.wantErr {
require.Error(err)
return
}
require.NoError(err)
})
}
}
func TestFilterCompatibleCLIVersions(t *testing.T) {
someErr := errors.New("some error")
verCollector := func(cliInfoErr error) versionCollector {
return versionCollector{
cliVersion: "v0.1.0",
versionsapi: stubVersionFetcher{
cliInfoErr: cliInfoErr,
},
}
}
testCases := map[string]struct {
verCollector versionCollector
cliPatchVersions []string
wantErr bool
}{
"works": {
verCollector: verCollector(nil),
cliPatchVersions: []string{"v0.1.1"},
},
"cli info error": {
verCollector: verCollector(someErr),
cliPatchVersions: []string{"v0.1.1"},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
require := require.New(t)
_, err := tc.verCollector.filterCompatibleCLIVersions(context.Background(), tc.cliPatchVersions, "v1.24.5")
if tc.wantErr {
require.Error(err)
return
}
require.NoError(err)
})
}
}
type stubVersionFetcher struct {
minorList versionsapi.List
patchList versionsapi.List
versionListErr error
cliInfoErr error
}
func (f stubVersionFetcher) FetchVersionList(ctx context.Context, list versionsapi.List) (versionsapi.List, error) {
switch list.Granularity {
case versionsapi.GranularityMajor:
return f.minorList, f.versionListErr
case versionsapi.GranularityMinor:
return f.patchList, f.versionListErr
}
return versionsapi.List{}, f.versionListErr
}
func (f stubVersionFetcher) FetchCLIInfo(ctx context.Context, cliInfo versionsapi.CLIInfo) (versionsapi.CLIInfo, error) {
return versionsapi.CLIInfo{}, f.cliInfoErr
}