diff --git a/internal/cmd/modules/modules.go b/internal/cmd/modules/modules.go index ce7b9c69e..e685cbd1c 100644 --- a/internal/cmd/modules/modules.go +++ b/internal/cmd/modules/modules.go @@ -1,19 +1,10 @@ package modules import ( - "encoding/json" "fmt" + "github.com/kyma-project/cli.v3/internal/clierror" "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" - "net/http" - "strings" - - "github.com/kyma-project/cli.v3/internal/clierror" "github.com/spf13/cobra" ) @@ -26,8 +17,6 @@ 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, @@ -42,7 +31,7 @@ func NewModulesCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { clierror.Check(cfg.KubeClientConfig.Complete()) }, Run: func(_ *cobra.Command, _ []string) { - clierror.Check(runModules(&cfg)) + clierror.Check(listModules(&cfg)) }, } cfg.KubeClientConfig.AddFlag(cmd) @@ -59,165 +48,68 @@ func NewModulesCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { return cmd } -func runModules(cfg *modulesConfig) clierror.Error { - var err error +func listModules(cfg *modulesConfig) clierror.Error { + var err clierror.Error if cfg.catalog { - modules, err := listAllModules() + err = listAllModules() if err != nil { return clierror.WrapE(err, clierror.New("failed to list all Kyma modules")) } - fmt.Println("Available modules:\n") - for _, rec := range modules { - fmt.Println(rec) - } return nil } + if cfg.managed { - managed, err := listManagedModules(cfg) + err = listManagedModules(cfg) if err != nil { return clierror.WrapE(err, clierror.New("failed to list managed Kyma modules")) } - fmt.Println("Managed modules:\n") - for _, rec := range managed { - fmt.Println(rec) - } return nil } if cfg.installed { - installed, err := listInstalledModules(cfg) + 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 } - return clierror.Wrap(err, clierror.New("failed to get modules", "please use one of: catalog, managed or installed flags")) -} - -func listAllModules() ([]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 { - out = append(out, rec.Name) - } - return out, nil + return clierror.WrapE(err, clierror.New("failed to get modules", "please use one of: catalog, managed or installed flags")) } -func handleResponse(err error, resp *http.Response, template model.Module) (model.Module, clierror.Error) { - bodyText, err := io.ReadAll(resp.Body) +func listInstalledModules(cfg *modulesConfig) clierror.Error { + installed, err := model.GetInstalledModules(cfg.KubeClientConfig, *cfg.KymaConfig) if err != nil { - return nil, clierror.Wrap(err, clierror.New("while reading http response")) + return clierror.WrapE(err, clierror.New("failed to get installed Kyma modules")) } - err = json.Unmarshal(bodyText, &template) - if err != nil { - return nil, clierror.Wrap(err, clierror.New("while unmarshalling")) + fmt.Println("Installed modules:\n") + for _, rec := range installed { + fmt.Println(rec) } - return template, nil + return nil } -func listManagedModules(cfg *modulesConfig) ([]string, clierror.Error) { - GVRKyma := schema.GroupVersionResource{ - Group: "operator.kyma-project.io", - Version: "v1beta2", - Resource: "kymas", - } - - unstruct, err := cfg.KubeClient.Dynamic().Resource(GVRKyma).Namespace("kyma-system").Get(cfg.Ctx, "default", metav1.GetOptions{}) +func listManagedModules(cfg *modulesConfig) clierror.Error { + managed, err := model.GetManagedModules(cfg.KubeClientConfig, *cfg.KymaConfig) if err != nil { - return nil, clierror.Wrap(err, clierror.New("while getting Kyma CR")) + return clierror.WrapE(err, clierror.New("failed to get managed Kyma modules")) } - - moduleNames, err := getModuleNames(unstruct) - if err != nil { - return nil, clierror.Wrap(err, clierror.New("while getting module names from CR")) + fmt.Println("Managed modules:\n") + for _, rec := range managed { + fmt.Println(rec) } - - return moduleNames, nil + return nil } -func listInstalledModules(cfg *modulesConfig) ([]string, clierror.Error) { - resp, err := http.Get(URL) +func listAllModules() clierror.Error { + modules, err := model.GetAllModules() 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 clierror.WrapE(err, clierror.New("failed to get all Kyma modules")) } - return out, nil -} - -func getModuleNames(unstruct *unstructured.Unstructured) ([]string, error) { - var moduleNames []string - managedFields := unstruct.GetManagedFields() - for _, field := range managedFields { - var data map[string]interface{} - err := json.Unmarshal(field.FieldsV1.Raw, &data) - if err != nil { - return nil, err - } - - spec, ok := data["f:spec"].(map[string]interface{}) - if !ok { - continue - } - - modules, ok := spec["f:modules"].(map[string]interface{}) - if !ok { - continue - } - - for key := range modules { - if strings.Contains(key, "name") { - name := strings.TrimPrefix(key, "k:{\"name\":\"") - name = strings.Trim(name, "\"}") - moduleNames = append(moduleNames, name) - } - } + fmt.Println("Available modules:\n") + for _, rec := range modules { + fmt.Println(rec) } - return moduleNames, nil + return nil } diff --git a/internal/model/modules.go b/internal/model/modules.go new file mode 100644 index 000000000..f74cd5285 --- /dev/null +++ b/internal/model/modules.go @@ -0,0 +1,138 @@ +package model + +import ( + "encoding/json" + "github.com/kyma-project/cli.v3/internal/clierror" + "github.com/kyma-project/cli.v3/internal/cmdcommon" + "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" + "net/http" + "strings" +) + +const URL = "https://raw.githubusercontent.com/kyma-project/community-modules/main/model.json" + +func GetAllModules() ([]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 Module + + template, respErr := handleResponse(err, resp, template) + if respErr != nil { + return nil, clierror.WrapE(respErr, clierror.New("while handling response")) + } + + var modules []string + for _, rec := range template { + modules = append(modules, rec.Name) + } + return modules, nil +} + +func GetManagedModules(client cmdcommon.KubeClientConfig, cfg cmdcommon.KymaConfig) ([]string, clierror.Error) { + GVRKyma := schema.GroupVersionResource{ + Group: "operator.kyma-project.io", + Version: "v1beta2", + Resource: "kymas", + } + + unstruct, err := client.KubeClient.Dynamic().Resource(GVRKyma).Namespace("kyma-system").Get(cfg.Ctx, "default", metav1.GetOptions{}) + if err != nil { + return nil, clierror.Wrap(err, clierror.New("while getting Kyma CR")) + } + + managed, err := getModuleNames(unstruct) + if err != nil { + return nil, clierror.Wrap(err, clierror.New("while getting module names from CR")) + } + + return managed, nil +} + +func GetInstalledModules(client cmdcommon.KubeClientConfig, cfg cmdcommon.KymaConfig) ([]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 Module + + template, respErr := handleResponse(err, resp, template) + if respErr != nil { + return nil, clierror.WrapE(respErr, clierror.New("while handling response")) + } + + var installed []string + for _, rec := range template { + managerPath := strings.Split(rec.Versions[0].ManagerPath, "/") + managerName := managerPath[len(managerPath)-1] + version := rec.Versions[0].Version + deployment, err := client.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] { + installed = append(installed, rec.Name+" - "+installedVersion[len(installedVersion)-1]) + } else { + installed = append(installed, rec.Name+" - "+"outdated version, latest version is "+version) + } + } + } + return installed, nil +} + +func handleResponse(err error, resp *http.Response, template Module) (Module, clierror.Error) { + bodyText, err := io.ReadAll(resp.Body) + if err != nil { + return nil, clierror.Wrap(err, clierror.New("while reading http response")) + } + err = json.Unmarshal(bodyText, &template) + if err != nil { + return nil, clierror.Wrap(err, clierror.New("while unmarshalling")) + } + return template, nil +} + +func getModuleNames(unstruct *unstructured.Unstructured) ([]string, error) { + var moduleNames []string + managedFields := unstruct.GetManagedFields() + for _, field := range managedFields { + var data map[string]interface{} + err := json.Unmarshal(field.FieldsV1.Raw, &data) + if err != nil { + return nil, err + } + + spec, ok := data["f:spec"].(map[string]interface{}) + if !ok { + continue + } + + modules, ok := spec["f:modules"].(map[string]interface{}) + if !ok { + continue + } + + for key := range modules { + if strings.Contains(key, "name") { + name := strings.TrimPrefix(key, "k:{\"name\":\"") + name = strings.Trim(name, "\"}") + moduleNames = append(moduleNames, name) + } + } + } + return moduleNames, nil +}