Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move all methods related to model.json to a separate file #2132

Merged
merged 3 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 30 additions & 138 deletions internal/cmd/modules/modules.go
Original file line number Diff line number Diff line change
@@ -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"
)

Expand All @@ -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,
Expand All @@ -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)
Expand All @@ -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
}
138 changes: 138 additions & 0 deletions internal/model/modules.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading