Feat: Add more data field to application view and managed resource view in vela top (#4781)

* Fix: fix the bug of cluster list

Signed-off-by: HanMengnan <1448189829@qq.com>

* Feat: add some feature

1. add more column of application and managed resource

2. alter <ESC> to exist key and add <Q> as the back key
Signed-off-by: HanMengnan <1448189829@qq.com>

Signed-off-by: HanMengnan <1448189829@qq.com>
This commit is contained in:
Siege Lion
2022-09-30 10:45:49 +08:00
committed by GitHub
parent b79dc3bccf
commit bd728cbdbc
13 changed files with 91 additions and 36 deletions

View File

@@ -18,7 +18,9 @@ package model
import (
"context"
"fmt"
workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
@@ -27,10 +29,13 @@ import (
// Application is the application resource object
type Application struct {
name string
namespace string
phase string
createTime string
name string
namespace string
phase string
service string
workflow string
workflowMode string
createTime string
}
// ApplicationList is application resource list
@@ -40,13 +45,13 @@ type ApplicationList []Application
func (l ApplicationList) ToTableBody() [][]string {
data := make([][]string, len(l))
for index, app := range l {
data[index] = []string{app.name, app.namespace, app.phase, app.createTime}
data[index] = []string{app.name, app.namespace, app.phase, app.workflowMode, app.workflow, app.service, app.createTime}
}
return data
}
// ListApplications list all apps in all namespaces
func ListApplications(ctx context.Context, c client.Reader) (ApplicationList, error) {
func ListApplications(ctx context.Context, c client.Client) (ApplicationList, error) {
apps := v1beta1.ApplicationList{}
namespace := ctx.Value(&CtxKeyNamespace).(string)
@@ -55,7 +60,10 @@ func ListApplications(ctx context.Context, c client.Reader) (ApplicationList, er
}
appList := make(ApplicationList, len(apps.Items))
for index, app := range apps.Items {
appList[index] = Application{app.Name, app.Namespace, string(app.Status.Phase), app.CreationTimestamp.String()}
appList[index] = Application{name: app.Name, namespace: app.Namespace, phase: string(app.Status.Phase), createTime: app.CreationTimestamp.String()}
appList[index].service = serviceNum(app)
appList[index].workflow = workflowStepNum(app)
appList[index].workflowMode = workflowMode(app)
}
return appList, nil
}
@@ -96,3 +104,27 @@ func runningApplicationNum(ctx context.Context, c client.Client) (int, error) {
}
return num, nil
}
func serviceNum(app v1beta1.Application) string {
total, healthy := len(app.Status.Services), 0
for _, service := range app.Status.Services {
if service.Healthy {
healthy++
}
}
return fmt.Sprintf("%d/%d", healthy, total)
}
func workflowMode(app v1beta1.Application) string {
return app.Status.Workflow.Mode
}
func workflowStepNum(app v1beta1.Application) string {
total, succeed := len(app.Status.Workflow.Steps), 0
for _, step := range app.Status.Workflow.Steps {
if step.Phase == workflowv1alpha1.WorkflowStepPhaseSucceeded {
succeed++
}
}
return fmt.Sprintf("%d/%d", succeed, total)
}

View File

@@ -26,8 +26,8 @@ import (
)
func TestApplicationList_ToTableBody(t *testing.T) {
appList := &ApplicationList{{"Name", "Namespace", "Phase", "CreateTime"}}
assert.Equal(t, appList.ToTableBody(), [][]string{{"Name", "Namespace", "Phase", "CreateTime"}})
appList := &ApplicationList{{"Name", "Namespace", "Phase", "", "", "", "CreateTime"}}
assert.Equal(t, appList.ToTableBody(), [][]string{{"Name", "Namespace", "Phase", "", "", "", "CreateTime"}})
}
var _ = Describe("test Application", func() {

View File

@@ -31,6 +31,7 @@ type ManagedResource struct {
kind string
apiVersion string
cluster string
component string
status string
}
@@ -48,7 +49,7 @@ func ListManagedResource(ctx context.Context, c client.Client) (ManagedResourceL
}
collector := query.NewAppCollector(c, opt)
appResList, err := collector.CollectResourceFromApp(ctx)
appResList, err := collector.CollectResourceFromApp(context.Background())
if err != nil {
return ManagedResourceList{}, err
}
@@ -57,6 +58,7 @@ func ListManagedResource(ctx context.Context, c client.Client) (ManagedResourceL
for index, resource := range appResList {
list[index] = LoadResourceDetail(resource)
list[index].component = resource.Component
}
cluster, ok := ctx.Value(&CtxKeyCluster).(string)
@@ -73,10 +75,10 @@ func ListManagedResource(ctx context.Context, c client.Client) (ManagedResourceL
}
// ToTableBody generate header of table in managed resource view
func (l ManagedResourceList) ToTableBody() [][]string {
func (l *ManagedResourceList) ToTableBody() [][]string {
data := make([][]string, 0)
for _, resource := range l {
data = append(data, []string{resource.name, resource.namespace, resource.kind, resource.apiVersion, resource.cluster, resource.status})
for _, resource := range *l {
data = append(data, []string{resource.name, resource.namespace, resource.kind, resource.apiVersion, resource.cluster, resource.component, resource.status})
}
return data
}
@@ -102,7 +104,7 @@ func (l *ManagedResourceList) FilterCluster(clusterName string) {
data := make([]ManagedResource, 0)
for _, resource := range *l {
if resource.cluster == clusterName {
data = append(data, ManagedResource{resource.name, resource.namespace, resource.kind, resource.apiVersion, resource.cluster, resource.status})
data = append(data, ManagedResource{resource.name, resource.namespace, resource.kind, resource.apiVersion, resource.cluster, resource.component, resource.status})
}
}
*l = data
@@ -111,9 +113,9 @@ func (l *ManagedResourceList) FilterCluster(clusterName string) {
// FilterClusterNamespace filter out objects that belong to the target namespace
func (l *ManagedResourceList) FilterClusterNamespace(clusterNS string) {
data := make([]ManagedResource, 0)
for _, app := range *l {
if app.namespace == clusterNS {
data = append(data, ManagedResource{app.name, app.namespace, app.kind, app.apiVersion, app.cluster, app.status})
for _, resource := range *l {
if resource.namespace == clusterNS {
data = append(data, ManagedResource{resource.name, resource.namespace, resource.kind, resource.apiVersion, resource.cluster, resource.component, resource.status})
}
}
*l = data

View File

@@ -26,14 +26,14 @@ import (
)
func TestListManagedResource(t *testing.T) {
list := ManagedResourceList{{"", "", "", "", "", ""}}
assert.Equal(t, list.ToTableBody(), [][]string{{"", "", "", "", "", ""}})
list := ManagedResourceList{{"", "", "", "", "", "", ""}}
assert.Equal(t, list.ToTableBody(), [][]string{{"", "", "", "", "", "", ""}})
}
func TestManagedResourceList_FilterCluster(t *testing.T) {
list := ManagedResourceList{
{"", "", "", "", "1", ""},
{"", "", "", "", "2", ""},
{"", "", "", "", "1", "", ""},
{"", "", "", "", "2", "", ""},
}
list.FilterCluster("1")
assert.Equal(t, len(list), 1)
@@ -41,8 +41,8 @@ func TestManagedResourceList_FilterCluster(t *testing.T) {
func TestManagedResourceList_FilterClusterNamespace(t *testing.T) {
list := ManagedResourceList{
{"", "1", "", "", "1", ""},
{"", "2", "", "", "2", ""},
{"", "1", "", "", "1", "", ""},
{"", "2", "", "", "2", "", ""},
}
list.FilterClusterNamespace("2")
assert.Equal(t, len(list), 1)

View File

@@ -23,12 +23,12 @@ import (
"testing"
"time"
"k8s.io/apimachinery/pkg/api/resource"
"github.com/kubevela/workflow/api/v1alpha1"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
@@ -91,6 +91,20 @@ var _ = BeforeSuite(func(done Done) {
Expect(err).Should(BeNil())
testApp.Status = common2.AppStatus{
Phase: common2.ApplicationRunning,
Workflow: &common2.WorkflowStatus{
AppRevision: "",
Mode: "DAG",
Phase: "",
Message: "",
Suspend: false,
SuspendState: "",
Terminated: false,
Finished: false,
ContextBackend: nil,
Steps: []v1alpha1.WorkflowStepStatus{},
StartTime: metav1.Time{},
EndTime: metav1.Time{},
},
AppliedResources: []common2.ClusterObjectReference{
{
Cluster: "",

View File

@@ -146,7 +146,7 @@ func (a *App) inject(v model.View) {
func (a *App) bindKeys() {
a.AddAction(model.KeyActions{
tcell.KeyESC: model.KeyAction{Description: "Back", Action: a.Back, Visible: true, Shared: true},
component.KeyQ: model.KeyAction{Description: "Back", Action: a.Back, Visible: true, Shared: true},
component.KeyHelp: model.KeyAction{Description: "Help", Action: a.helpView, Visible: true, Shared: true},
})
}
@@ -183,3 +183,9 @@ func (a *App) Back(_ *tcell.EventKey) *tcell.EventKey {
}
return nil
}
// Exist the app
func (a *App) Exist(_ *tcell.EventKey) *tcell.EventKey {
a.Stop()
return nil
}

View File

@@ -27,6 +27,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/envtest"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/references/cli/top/component"
)
func TestApp(t *testing.T) {
@@ -45,7 +46,7 @@ func TestApp(t *testing.T) {
t.Run("init", func(t *testing.T) {
app.Init()
assert.Equal(t, app.Main.HasPage("main"), true)
_, ok := app.HasAction(tcell.KeyESC)
_, ok := app.HasAction(component.KeyQ)
assert.Equal(t, ok, true)
app.content.Stack.RemoveListener(app.Crumbs())
assert.NotEmpty(t, app.content.Stack.TopView())
@@ -53,7 +54,7 @@ func TestApp(t *testing.T) {
assert.Equal(t, app.content.Stack.IsLastView(), true)
})
t.Run("keyboard", func(t *testing.T) {
evt1 := tcell.NewEventKey(tcell.KeyEsc, '/', 0)
evt1 := tcell.NewEventKey(component.KeyQ, '/', 0)
assert.Empty(t, app.keyboard(evt1))
evt2 := tcell.NewEventKey(tcell.KeyTAB, '/', 0)
assert.NotEmpty(t, app.keyboard(evt2))

View File

@@ -87,7 +87,7 @@ func (v *ApplicationView) Update() {
// BuildHeader render the header of table
func (v *ApplicationView) BuildHeader() {
header := []string{"Name", "Namespace", "Phase", "CreateTime"}
header := []string{"Name", "Namespace", "Phase", "WorkflowMode", "Workflow", "Service", "CreateTime"}
v.CommonResourceView.BuildHeader(header)
}
@@ -134,6 +134,7 @@ func (v *ApplicationView) Title() string {
func (v *ApplicationView) bindKeys() {
v.Actions().Delete([]tcell.Key{tcell.KeyEnter})
v.Actions().Add(model.KeyActions{
tcell.KeyESC: model.KeyAction{Description: "Exist", Action: v.app.Exist, Visible: true, Shared: true},
tcell.KeyEnter: model.KeyAction{Description: "Enter", Action: v.managedResourceView, Visible: true, Shared: true},
component.KeyN: model.KeyAction{Description: "Select Namespace", Action: v.namespaceView, Visible: true, Shared: true},
component.KeyY: model.KeyAction{Description: "Yaml", Action: v.yamlView, Visible: true, Shared: true},

View File

@@ -88,7 +88,7 @@ func TestApplicationView(t *testing.T) {
})
t.Run("hint", func(t *testing.T) {
assert.Equal(t, len(appView.Hint()), 6)
assert.Equal(t, len(appView.Hint()), 7)
})
t.Run("managed resource view", func(t *testing.T) {

View File

@@ -19,8 +19,6 @@ package view
import (
"fmt"
"github.com/gdamore/tcell/v2"
"github.com/oam-dev/kubevela/references/cli/top/component"
"github.com/oam-dev/kubevela/references/cli/top/config"
"github.com/oam-dev/kubevela/references/cli/top/model"
@@ -56,7 +54,7 @@ func (v *HelpView) Name() string {
func (v *HelpView) bindKeys() {
v.Actions().Add(model.KeyActions{
tcell.KeyESC: model.KeyAction{Description: "Back", Action: v.app.Back, Visible: true, Shared: true},
component.KeyQ: model.KeyAction{Description: "Back", Action: v.app.Back, Visible: true, Shared: true},
component.KeyHelp: model.KeyAction{Description: "Back", Action: v.app.Back, Visible: true, Shared: true},
})
}

View File

@@ -101,7 +101,7 @@ func (v *ManagedResourceView) Update() {
// BuildHeader render the header of table
func (v *ManagedResourceView) BuildHeader() {
header := []string{"Name", "Namespace", "Kind", "APIVersion", "Cluster", "Status"}
header := []string{"Name", "Namespace", "Kind", "APIVersion", "Cluster", "Component", "Status"}
v.CommonResourceView.BuildHeader(header)
}

View File

@@ -139,8 +139,9 @@ func (v *CommonResourceView) AutoRefresh(update func()) {
}
func (v *CommonResourceView) bindKeys() {
v.Actions().Delete([]tcell.Key{tcell.KeyESC})
v.Actions().Add(model.KeyActions{
tcell.KeyESC: model.KeyAction{Description: "Back", Action: v.app.Back, Visible: true, Shared: true},
component.KeyQ: model.KeyAction{Description: "Back", Action: v.app.Back, Visible: true, Shared: true},
component.KeyHelp: model.KeyAction{Description: "Help", Action: v.app.helpView, Visible: true, Shared: true},
})
}

View File

@@ -154,7 +154,7 @@ func (v *YamlView) keyboard(event *tcell.EventKey) *tcell.EventKey {
func (v *YamlView) bindKeys() {
v.actions.Delete([]tcell.Key{tcell.KeyEnter})
v.actions.Add(model.KeyActions{
tcell.KeyESC: model.KeyAction{Description: "Back", Action: v.app.Back, Visible: true, Shared: true},
component.KeyQ: model.KeyAction{Description: "Back", Action: v.app.Back, Visible: true, Shared: true},
component.KeyHelp: model.KeyAction{Description: "Help", Action: v.app.helpView, Visible: true, Shared: true},
})
}