Skip to content

Commit

Permalink
List modules based on ModuleTemplates (#2261)
Browse files Browse the repository at this point in the history
  • Loading branch information
pPrecel authored Nov 26, 2024
1 parent 7d05c83 commit cc5ffe9
Show file tree
Hide file tree
Showing 9 changed files with 710 additions and 125 deletions.
45 changes: 45 additions & 0 deletions internal/cmd/alpha/modules/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package modules

import (
"github.com/kyma-project/cli.v3/internal/clierror"
"github.com/kyma-project/cli.v3/internal/cmdcommon"
"github.com/kyma-project/cli.v3/internal/modules"
"github.com/spf13/cobra"
)

type modulesConfig struct {
*cmdcommon.KymaConfig
}

func NewListCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command {
cfg := modulesConfig{
KymaConfig: kymaConfig,
}

cmd := &cobra.Command{
Use: "list",
Short: "List modules.",
Long: `List either installed, managed or available Kyma modules.`,
Run: func(_ *cobra.Command, _ []string) {
clierror.Check(listModules(&cfg))
},
}

return cmd
}

// listModules collects all the methods responsible for the command and its flags
func listModules(cfg *modulesConfig) clierror.Error {
client, clierr := cfg.GetKubeClientWithClierr()
if clierr != nil {
return clierr
}

modulesList, err := modules.List(cfg.Ctx, client.Kyma())
if err != nil {
return clierror.Wrap(err, clierror.New("failed to list available modules from the cluster"))
}

modules.Render(modulesList, modules.ModulesTableInfo)
return nil
}
128 changes: 3 additions & 125 deletions internal/cmd/alpha/modules/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,146 +3,24 @@ package modules
import (
"github.com/kyma-project/cli.v3/internal/clierror"
"github.com/kyma-project/cli.v3/internal/cmdcommon"
"github.com/kyma-project/cli.v3/internal/communitymodules"
"github.com/spf13/cobra"
)

type modulesConfig struct {
*cmdcommon.KymaConfig

catalog bool
managed bool
installed bool
raw bool
}

func NewModulesCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command {
cfg := modulesConfig{
KymaConfig: kymaConfig,
}

cmd := &cobra.Command{
Use: "modules",
Short: "List modules.",
Long: `List either installed, managed or available Kyma modules.`,
Short: "Manage kyma modules.",
Long: `Use this command to manage modules on a kyma cluster.`,
Run: func(_ *cobra.Command, _ []string) {
clierror.Check(listModules(&cfg))
},
}

cmd.Flags().BoolVar(&cfg.catalog, "catalog", false, "List of al available Kyma modules.")
cmd.Flags().BoolVar(&cfg.managed, "managed", false, "List of all Kyma modules managed by central control-plane.")
cmd.Flags().BoolVar(&cfg.installed, "installed", false, "List of all currently installed Kyma modules.")
cmd.Flags().BoolVar(&cfg.raw, "raw", false, "Simple output format without table rendering.")

cmd.MarkFlagsMutuallyExclusive("catalog", "managed", "installed")
cmd.AddCommand(NewListCMD(kymaConfig))

return cmd
}

// listModules collects all the methods responsible for the command and its flags
func listModules(cfg *modulesConfig) clierror.Error {
var err clierror.Error

if cfg.catalog {
err = listModulesCatalog(cfg)
if err != nil {
return clierror.WrapE(err, clierror.New("failed to list all Kyma modules"))
}
return nil
}

if cfg.managed {
err = listManagedModules(cfg)
if err != nil {
return clierror.WrapE(err, clierror.New("failed to list managed Kyma modules"))
}
return nil
}

if cfg.installed {
err = listInstalledModules(cfg)
if err != nil {
return clierror.WrapE(err, clierror.New("failed to list installed Kyma modules"))
}
return nil
}

err = collectiveView(cfg)
if err != nil {
return clierror.WrapE(err, clierror.New("failed to list modules"))
}

return nil
}

// collectiveView combines the list of all available, installed and managed modules
func collectiveView(cfg *modulesConfig) clierror.Error {
catalog, err := communitymodules.ModulesCatalog()
if err != nil {
return clierror.WrapE(err, clierror.New("failed to get all Kyma catalog"))
}

client, err := cfg.GetKubeClientWithClierr()
if err != nil {
return err
}

installedWith, err := communitymodules.InstalledModules(cfg.Ctx, client)
if err != nil {
return clierror.WrapE(err, clierror.New("failed to get installed Kyma modules"))
}

managedWith, err := communitymodules.ManagedModules(cfg.Ctx, client)
if err != nil {
return clierror.WrapE(err, clierror.New("failed to get managed Kyma modules"))
}

combinedData := communitymodules.MergeRowMaps(catalog, installedWith, managedWith)

communitymodules.RenderModules(cfg.raw, combinedData, communitymodules.CollectiveTableInfo)
return nil
}

// listInstalledModules lists all installed modules
func listInstalledModules(cfg *modulesConfig) clierror.Error {
client, err := cfg.GetKubeClientWithClierr()
if err != nil {
return err
}

installed, err := communitymodules.InstalledModules(cfg.Ctx, client)
if err != nil {
return clierror.WrapE(err, clierror.New("failed to get installed Kyma modules"))
}

communitymodules.RenderModules(cfg.raw, installed, communitymodules.InstalledTableInfo)
return nil
}

// listManagedModules lists all managed modules
func listManagedModules(cfg *modulesConfig) clierror.Error {
client, err := cfg.GetKubeClientWithClierr()
if err != nil {
return err
}

managed, err := communitymodules.ManagedModules(cfg.Ctx, client)
if err != nil {
return clierror.WrapE(err, clierror.New("failed to get managed Kyma modules"))
}

communitymodules.RenderModules(cfg.raw, managed, communitymodules.ManagedTableInfo)
return nil
}

// listModulesCatalog lists all available modules
func listModulesCatalog(cfg *modulesConfig) clierror.Error {
catalog, err := communitymodules.ModulesCatalog()
if err != nil {
return clierror.WrapE(err, clierror.New("failed to get all Kyma catalog"))
}

communitymodules.RenderModules(cfg.raw, catalog, communitymodules.CatalogTableInfo)
return nil
}
23 changes: 23 additions & 0 deletions internal/kube/kyma/kyma.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
)

Expand All @@ -16,6 +17,8 @@ const (
)

type Interface interface {
ListModuleReleaseMeta(context.Context) (*ModuleReleaseMetaList, error)
ListModuleTemplate(context.Context) (*ModuleTemplateList, error)
GetDefaultKyma(context.Context) (*Kyma, error)
UpdateDefaultKyma(context.Context, *Kyma) error
EnableModule(context.Context, string, string) error
Expand All @@ -32,6 +35,26 @@ func NewClient(dynamic dynamic.Interface) Interface {
}
}

func (c *client) ListModuleReleaseMeta(ctx context.Context) (*ModuleReleaseMetaList, error) {
return list[ModuleReleaseMetaList](ctx, c.dynamic, GVRModuleReleaseMeta)
}

func (c *client) ListModuleTemplate(ctx context.Context) (*ModuleTemplateList, error) {
return list[ModuleTemplateList](ctx, c.dynamic, GVRModuleTemplate)
}

func list[T any](ctx context.Context, client dynamic.Interface, gvr schema.GroupVersionResource) (*T, error) {
list, err := client.Resource(gvr).
List(ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}

structuredList := new(T)
err = runtime.DefaultUnstructuredConverter.FromUnstructured(list.UnstructuredContent(), structuredList)
return structuredList, err
}

// GetDefaultKyma gets the default Kyma CR from the kyma-system namespace and cast it to the Kyma structure
func (c *client) GetDefaultKyma(ctx context.Context) (*Kyma, error) {
u, err := c.dynamic.Resource(GVRKyma).
Expand Down
124 changes: 124 additions & 0 deletions internal/kube/kyma/kyma_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,3 +387,127 @@ func fixDefaultKyma() *unstructured.Unstructured {
},
}
}

func Test_client_ListModuleReleaseMeta(t *testing.T) {
t.Run("list ModuleReleaseMeta", func(t *testing.T) {
scheme := runtime.NewScheme()
scheme.AddKnownTypes(GVRModuleReleaseMeta.GroupVersion())
client := NewClient(dynamic_fake.NewSimpleDynamicClient(scheme,
fixModuleReleaseMeta("test-1"),
fixModuleReleaseMeta("test-2"),
))

list, err := client.ListModuleReleaseMeta(context.Background())

require.NoError(t, err)
require.Len(t, list.Items, 2)
require.Contains(t, list.Items, fixModuleReleaseMetaStruct("test-1"))
require.Contains(t, list.Items, fixModuleReleaseMetaStruct("test-2"))

})
}

func Test_client_ListModuleTemplate(t *testing.T) {
t.Run("list ModuleReleaseMeta", func(t *testing.T) {
scheme := runtime.NewScheme()
scheme.AddKnownTypes(GVRModuleTemplate.GroupVersion())
client := NewClient(dynamic_fake.NewSimpleDynamicClient(scheme,
fixModuleTemplate("test-1"),
fixModuleTemplate("test-2"),
))

list, err := client.ListModuleTemplate(context.Background())

require.NoError(t, err)
require.Len(t, list.Items, 2)
require.Contains(t, list.Items, fixModuleTemplateStruct("test-1"))
require.Contains(t, list.Items, fixModuleTemplateStruct("test-2"))

})
}

func fixModuleReleaseMetaStruct(moduleName string) ModuleReleaseMeta {
return ModuleReleaseMeta{
TypeMeta: v1.TypeMeta{
APIVersion: "operator.kyma-project.io/v1beta2",
Kind: "ModuleReleaseMeta",
},
ObjectMeta: v1.ObjectMeta{
Name: moduleName,
Namespace: "kyma-system",
},
Spec: ModuleReleaseMetaSpec{
ModuleName: moduleName,
Channels: []ChannelVersionAssignment{
{
Version: "0.1",
Channel: "regular",
},
{
Version: "0.2",
Channel: "fast",
},
},
},
}
}

func fixModuleTemplateStruct(moduleName string) ModuleTemplate {
return ModuleTemplate{
TypeMeta: v1.TypeMeta{
APIVersion: "operator.kyma-project.io/v1beta2",
Kind: "ModuleTemplate",
},
ObjectMeta: v1.ObjectMeta{
Name: moduleName,
Namespace: "kyma-system",
},
Spec: ModuleTemplateSpec{
ModuleName: moduleName,
Version: "0.1",
},
}
}

func fixModuleReleaseMeta(moduleName string) *unstructured.Unstructured {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "operator.kyma-project.io/v1beta2",
"kind": "ModuleReleaseMeta",
"metadata": map[string]interface{}{
"name": moduleName,
"namespace": "kyma-system",
},
"spec": map[string]interface{}{
"moduleName": moduleName,
"channels": []interface{}{
map[string]interface{}{
"version": "0.1",
"channel": "regular",
},
map[string]interface{}{
"version": "0.2",
"channel": "fast",
},
},
},
},
}
}

func fixModuleTemplate(moduleName string) *unstructured.Unstructured {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "operator.kyma-project.io/v1beta2",
"kind": "ModuleTemplate",
"metadata": map[string]interface{}{
"name": moduleName,
"namespace": "kyma-system",
},
"spec": map[string]interface{}{
"moduleName": moduleName,
"version": "0.1",
},
},
}
}
Loading

0 comments on commit cc5ffe9

Please sign in to comment.