diff --git a/go.mod b/go.mod index 43e05637d..23a5159fa 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/gboddin/go-www-authenticate-parser v0.0.0-20230926203616-ec0b649bb077 github.com/google/go-containerregistry v0.19.2 github.com/olekukonko/tablewriter v0.0.5 + github.com/rogpeppe/go-internal v1.10.0 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 k8s.io/api v0.30.2 diff --git a/internal/communitymodules/modules.go b/internal/communitymodules/modules.go index 47d66eb42..4f40b2534 100644 --- a/internal/communitymodules/modules.go +++ b/internal/communitymodules/modules.go @@ -5,11 +5,13 @@ import ( "fmt" "io" "net/http" + "slices" "strings" "github.com/kyma-project/cli.v3/internal/clierror" "github.com/kyma-project/cli.v3/internal/cmdcommon" "github.com/kyma-project/cli.v3/internal/kyma" + "github.com/rogpeppe/go-internal/semver" v1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -41,15 +43,30 @@ func modulesCatalog(url string) (moduleMap, clierror.Error) { catalog := make(moduleMap) for _, rec := range modules { + latestVersion := getLatestVersion(rec.Versions) catalog[rec.Name] = row{ Name: rec.Name, - Repository: rec.Versions[0].Repository, - LatestVersion: rec.Versions[0].Version, + Repository: latestVersion.Repository, + LatestVersion: latestVersion.Version, } } return catalog, nil } +func getLatestVersion(versions []Version) Version { + return slices.MaxFunc(versions, func(a, b Version) int { + cmpA := a.Version + if !semver.IsValid(cmpA) { + cmpA = fmt.Sprintf("v%s", cmpA) + } + cmpB := b.Version + if !semver.IsValid(cmpB) { + cmpB = fmt.Sprintf("v%s", cmpB) + } + return semver.Compare(cmpA, cmpB) + }) +} + // getCommunityModules returns a list of all available modules from the community-modules repository func getCommunityModules(url string) (Modules, clierror.Error) { resp, err := http.Get(url) @@ -179,7 +196,8 @@ func installedModules(url string, client cmdcommon.KubeClientConfig, cfg cmdcomm func getInstalledModules(modules Modules, client cmdcommon.KubeClientConfig, cfg cmdcommon.KymaConfig) (moduleMap, clierror.Error) { installed := make(moduleMap) for _, module := range modules { - managerName := getManagerName(module) + latestVersion := getLatestVersion(module.Versions) + managerName := getManagerName(latestVersion) deployment, err := client.KubeClient.Static().AppsV1().Deployments("kyma-system"). Get(cfg.Ctx, managerName, metav1.GetOptions{}) if err != nil && !errors.IsNotFound(err) { @@ -191,7 +209,7 @@ func getInstalledModules(modules Modules, client cmdcommon.KubeClientConfig, cfg } installedVersion := getInstalledVersion(deployment) - moduleVersion := module.Versions[0].Version + moduleVersion := latestVersion.Version installed[module.Name] = row{ Name: module.Name, Version: calculateVersion(moduleVersion, installedVersion), @@ -206,8 +224,8 @@ func getInstalledVersion(deployment *v1.Deployment) string { return nameAndTag[len(nameAndTag)-1] } -func getManagerName(module Module) string { - managerPath := strings.Split(module.Versions[0].ManagerPath, "/") +func getManagerName(version Version) string { + managerPath := strings.Split(version.ManagerPath, "/") return managerPath[len(managerPath)-1] } diff --git a/internal/communitymodules/modules_test.go b/internal/communitymodules/modules_test.go index 5113c3979..804e9a599 100644 --- a/internal/communitymodules/modules_test.go +++ b/internal/communitymodules/modules_test.go @@ -25,8 +25,8 @@ func Test_modulesCatalog(t *testing.T) { expectedResult := moduleMap{ "module1": row{ Name: "module1", - Repository: "https://repo/path/module1.git", - LatestVersion: "1.2.3", + Repository: "https://repo2/path/module1.git", + LatestVersion: "1.7.0", Version: "", Managed: "", }, @@ -66,6 +66,30 @@ func Test_modulesCatalog(t *testing.T) { require.Contains(t, err.String(), "while handling response") require.Contains(t, err.String(), "while unmarshalling") }) + t.Run("greatest version is latest", func(t *testing.T) { + expectedResult := moduleMap{ + "module1": row{ + Name: "module1", + Repository: "https://repo2/path/module1.git", + LatestVersion: "1.7.0", + Version: "", + Managed: "", + }, + "module2": row{ + Name: "module2", + Repository: "https://repo/path/module2.git", + LatestVersion: "4.5.6", + Version: "", + Managed: "", + }, + } + + httpServer := httptest.NewServer(http.HandlerFunc(fixHttpResponseHandler(200, fixCommunityModulesResponse()))) + defer httpServer.Close() + modules, err := modulesCatalog(httpServer.URL) + require.Nil(t, err) + require.Equal(t, expectedResult, modules) + }) } func Test_ManagedModules(t *testing.T) { @@ -137,7 +161,7 @@ func Test_installedModules(t *testing.T) { expectedResult := moduleMap{ "module1": row{ Name: "module1", - Version: "1.2.3", + Version: "1.7.0", }, "module2": row{ Name: "module2", @@ -150,7 +174,7 @@ func Test_installedModules(t *testing.T) { defer httpServer.Close() staticClient := k8s_fake.NewSimpleClientset( - fixTestDeployment("module1-controller-manager", "kyma-system", "1.2.3"), + fixTestDeployment("module1-controller-manager", "kyma-system", "1.7.0"), fixTestDeployment("module2-manager", "kyma-system", "6.7.8"), // outdated fixTestDeployment("other-deployment", "kyma-system", "1.2.3")) kubeClient := &kube_fake.FakeKubeClient{ @@ -286,14 +310,19 @@ func fixCommunityModulesResponse() string { "name": "module1", "versions": [ { - "version": "1.2.3", - "repository": "https://repo/path/module1.git", - "managerPath": "/some/path/module1-controller-manager" + "version": "1.5.3", + "repository": "https://repo1/path/module1.git", + "managerPath": "/some/path1/module1-controller-manager" }, { "version": "1.7.0", - "repository": "https://other/repo/path/module1.git", - "managerPath": "/other/path/module1-controller-manager" + "repository": "https://repo2/path/module1.git", + "managerPath": "/other/path2/module1-controller-manager" + }, + { + "version": "1.3.4", + "repository": "https://repo3/path/module1.git", + "managerPath": "/some/path3/module1-controller-manager" } ] }, @@ -309,3 +338,57 @@ func fixCommunityModulesResponse() string { } ]` } + +func Test_getLatestVersion(t *testing.T) { + t.Run("simple versions", func(t *testing.T) { + result := getLatestVersion([]Version{ + { + Version: "1.5.3", + }, + { + Version: "1.7.1", + }, + { + Version: "1.4.3", + }, + }) + assert.Equal(t, Version{ + Version: "1.7.1", + }, result) + }) + t.Run("v prefix", func(t *testing.T) { + result := getLatestVersion([]Version{ + { + Version: "v1.5.3", + }, + { + Version: "v1.7.1", + }, + { + Version: "1.4.3", + }, + }) + assert.Equal(t, Version{ + Version: "v1.7.1", + }, result) + }) + t.Run("with suffix", func(t *testing.T) { + result := getLatestVersion([]Version{ + { + Version: "1.5.3-experimental", + }, + { + Version: "1.7.1-dev", + }, + { + Version: "1.7.1", + }, + { + Version: "1.4.3", + }, + }) + assert.Equal(t, Version{ + Version: "1.7.1", + }, result) + }) +}