From dc07ebe88a1171f78d4f4591884661f533a70f63 Mon Sep 17 00:00:00 2001 From: Andrzej Pankowski Date: Tue, 11 Jun 2024 11:54:39 +0200 Subject: [PATCH] Add flag --installed to command modules (#2130) Co-authored-by: MichalKalke --- internal/cmd/modules/modules.go | 76 ++++++++++++++++++++++++++++----- internal/model/types.go | 37 ++++++++++++++++ 2 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 internal/model/types.go diff --git a/internal/cmd/modules/modules.go b/internal/cmd/modules/modules.go index 8013c9405..ce7b9c69e 100644 --- a/internal/cmd/modules/modules.go +++ b/internal/cmd/modules/modules.go @@ -4,7 +4,9 @@ import ( "encoding/json" "fmt" "github.com/kyma-project/cli.v3/internal/cmdcommon" + "github.com/kyma-project/cli.v3/internal/model" "io" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -24,6 +26,8 @@ type modulesConfig struct { installed bool } +const URL = "https://raw.githubusercontent.com/kyma-project/community-modules/main/model.json" + func NewModulesCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { cfg := modulesConfig{ KymaConfig: kymaConfig, @@ -57,6 +61,7 @@ func NewModulesCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { func runModules(cfg *modulesConfig) clierror.Error { var err error + if cfg.catalog { modules, err := listAllModules() if err != nil { @@ -81,7 +86,14 @@ func runModules(cfg *modulesConfig) clierror.Error { } if cfg.installed { - clierror.Wrap(err, clierror.New("not implemented yet, please use the catalog or managed flag")) + installed, err := listInstalledModules(cfg) + if err != nil { + return clierror.WrapE(err, clierror.New("failed to list installed Kyma modules")) + } + fmt.Println("Installed modules:\n") + for _, rec := range installed { + fmt.Println(rec) + } return nil } @@ -89,16 +101,27 @@ func runModules(cfg *modulesConfig) clierror.Error { } func listAllModules() ([]string, clierror.Error) { - resp, err := http.Get("https://raw.githubusercontent.com/kyma-project/community-modules/main/model.json") + resp, err := http.Get(URL) if err != nil { return nil, clierror.Wrap(err, clierror.New("while getting modules list from github")) } defer resp.Body.Close() - var template []struct { - Name string `json:"name"` + var template model.Module + + template, respErr := handleResponse(err, resp, template) + if respErr != nil { + return nil, clierror.WrapE(respErr, clierror.New("while handling response")) } + var out []string + for _, rec := range template { + out = append(out, rec.Name) + } + return out, nil +} + +func handleResponse(err error, resp *http.Response, template model.Module) (model.Module, clierror.Error) { bodyText, err := io.ReadAll(resp.Body) if err != nil { return nil, clierror.Wrap(err, clierror.New("while reading http response")) @@ -107,12 +130,7 @@ func listAllModules() ([]string, clierror.Error) { if err != nil { return nil, clierror.Wrap(err, clierror.New("while unmarshalling")) } - - var out []string - for _, rec := range template { - out = append(out, rec.Name) - } - return out, nil + return template, nil } func listManagedModules(cfg *modulesConfig) ([]string, clierror.Error) { @@ -135,6 +153,44 @@ func listManagedModules(cfg *modulesConfig) ([]string, clierror.Error) { return moduleNames, nil } +func listInstalledModules(cfg *modulesConfig) ([]string, clierror.Error) { + resp, err := http.Get(URL) + if err != nil { + return nil, clierror.Wrap(err, clierror.New("while getting modules list from github")) + } + defer resp.Body.Close() + + var template model.Module + + template, respErr := handleResponse(err, resp, template) + if respErr != nil { + return nil, clierror.WrapE(respErr, clierror.New("while handling response")) + } + + var out []string + for _, rec := range template { + managerPath := strings.Split(rec.Versions[0].ManagerPath, "/") + managerName := managerPath[len(managerPath)-1] + version := rec.Versions[0].Version + deployment, err := cfg.KubeClient.Static().AppsV1().Deployments("kyma-system").Get(cfg.Ctx, managerName, metav1.GetOptions{}) + if err != nil && !errors.IsNotFound(err) { + msg := "while getting the " + managerName + " deployment" + return nil, clierror.Wrap(err, clierror.New(msg)) + } + if !errors.IsNotFound(err) { + deploymentImage := strings.Split(deployment.Spec.Template.Spec.Containers[0].Image, "/") + installedVersion := strings.Split(deploymentImage[len(deploymentImage)-1], ":") + + if version == installedVersion[len(installedVersion)-1] { + out = append(out, rec.Name+" - "+installedVersion[len(installedVersion)-1]) + } else { + out = append(out, rec.Name+" - "+"outdated version, latest version is "+version) + } + } + } + return out, nil +} + func getModuleNames(unstruct *unstructured.Unstructured) ([]string, error) { var moduleNames []string managedFields := unstruct.GetManagedFields() diff --git a/internal/model/types.go b/internal/model/types.go new file mode 100644 index 000000000..d2f344772 --- /dev/null +++ b/internal/model/types.go @@ -0,0 +1,37 @@ +package model + +type Module []struct { + Name string `json:"name,omitempty"` + Manageable bool `json:"manageable,omitempty"` + Versions []Versions `json:"versions,omitempty"` +} + +type Versions struct { + Version string `json:"version,omitempty"` + Channels []string `json:"channels,omitempty"` + ManagerPath string `json:"managerPath,omitempty"` + ManagerImage string `json:"managerImage,omitempty"` + Resources []Resources `json:"resources,omitempty"` +} + +type Resources struct { + Spec Spec `json:"spec,omitempty"` +} + +type Spec struct { + Group string `json:"group,omitempty"` + Names Names `json:"names,omitempty"` + Scope string `json:"scope,omitempty"` + ApiVersions []ApiVersions `json:"versions,omitempty"` +} + +type Names struct { + Kind string `json:"kind,omitempty"` + ListKind string `json:"listKind,omitempty"` + Plural string `json:"plural,omitempty"` + Singular string `json:"singular,omitempty"` +} + +type ApiVersions struct { + Name string `json:"name,omitempty"` +}