Refactor: refactor the source of all kinds of views, use the "Factory Method" design patterns to rewrite the code to upgrade the quality of code (#4679)

* Fix: refactor the source of all kinds of views, use the "Factory Method" design patterns to rewrite the code to upgrade the quality of code

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

* Fix: fix test case of the refactored code and some bugs

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

* Fix: rename the interface of ResourceView

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

Signed-off-by: HanMengnan <1448189829@qq.com>
This commit is contained in:
Siege Lion
2022-09-02 17:29:12 +08:00
committed by GitHub
parent 239c5474dd
commit 8df436aa3b
31 changed files with 613 additions and 518 deletions

View File

@@ -56,6 +56,7 @@ func (t *Table) Start() {
// Stop table component
func (t *Table) Stop() {
t.Clear()
}
// Hint return key action menu hints of the component

View File

@@ -43,6 +43,10 @@ const (
ApplicationWorkflowTerminatedPhaseColor = "[red::]"
// ApplicationRunningPhaseColor application Running phase text color
ApplicationRunningPhaseColor = "[green::]"
// NamespaceActiveStatusColor is namespace active status text color
NamespaceActiveStatusColor = "[green::]"
// NamespaceTerminateStatusColor is namespace terminate status text color
NamespaceTerminateStatusColor = "[red::]"
// ObjectHealthyStatusColor is object Healthy status text color
ObjectHealthyStatusColor = "[green::]"
// ObjectUnhealthyStatusColor is object Unhealthy status text color

View File

@@ -34,38 +34,30 @@ type Application struct {
}
// ApplicationList is application resource list
type ApplicationList struct {
title []string
data []Application
}
type ApplicationList []Application
// Header generate header of table in application view
func (l *ApplicationList) Header() []string {
return l.title
}
// Body generate body of table in application view
func (l *ApplicationList) Body() [][]string {
data := make([][]string, 0)
for _, app := range l.data {
data = append(data, []string{app.name, app.namespace, app.phase, app.createTime})
// ToTableBody generate body of table in application view
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}
}
return data
}
// ListApplications list all apps in all namespaces
func ListApplications(ctx context.Context, c client.Reader) (*ApplicationList, error) {
list := &ApplicationList{title: []string{"Name", "Namespace", "Phase", "CreateTime"}}
func ListApplications(ctx context.Context, c client.Reader) (ApplicationList, error) {
apps := v1beta1.ApplicationList{}
namespace := ctx.Value(&CtxKeyNamespace).(string)
if err := c.List(ctx, &apps, client.InNamespace(namespace)); err != nil {
return list, err
return ApplicationList{}, err
}
for _, app := range apps.Items {
list.data = append(list.data, Application{app.Name, app.Namespace, string(app.Status.Phase), app.CreationTimestamp.String()})
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()}
}
return list, nil
return appList, nil
}
// LoadApplication load the corresponding application according to name and namespace

View File

@@ -25,15 +25,9 @@ import (
"github.com/stretchr/testify/assert"
)
func TestApplicationList_Header(t *testing.T) {
appList := &ApplicationList{title: []string{"Name", "Namespace", "Phase", "CreateTime"}}
assert.Equal(t, appList.Header(), []string{"Name", "Namespace", "Phase", "CreateTime"})
}
func TestApplicationList_Body(t *testing.T) {
appList := &ApplicationList{data: []Application{{"name", "namespace", "phase", "createTime"}}}
assert.Equal(t, len(appList.data), 1)
assert.Equal(t, appList.Body()[0], []string{"name", "namespace", "phase", "createTime"})
func TestApplicationList_ToTableBody(t *testing.T) {
appList := &ApplicationList{{"Name", "Namespace", "Phase", "CreateTime"}}
assert.Equal(t, appList.ToTableBody(), [][]string{{"Name", "Namespace", "Phase", "CreateTime"}})
}
var _ = Describe("test Application", func() {
@@ -57,8 +51,7 @@ var _ = Describe("test Application", func() {
It("list applications", func() {
applicationsList, err := ListApplications(ctx, k8sClient)
Expect(err).NotTo(HaveOccurred())
Expect(applicationsList.Header()).To(Equal([]string{"Name", "Namespace", "Phase", "CreateTime"}))
Expect(len(applicationsList.Body())).To(Equal(1))
Expect(len(applicationsList)).To(Equal(1))
})
It("load application info", func() {
application, err := LoadApplication(k8sClient, "first-vela-app", "default")

View File

@@ -38,36 +38,15 @@ type Cluster struct {
}
// ClusterList is cluster resource list
type ClusterList struct {
title []string
data []Cluster
}
// Header generate header of table in application view
func (l *ClusterList) Header() []string {
return l.title
}
// Body generate body of table in application view
func (l *ClusterList) Body() [][]string {
data := make([][]string, 0)
for _, cluster := range l.data {
data = append(data, []string{cluster.name, cluster.alias, cluster.clusterType, cluster.endpoint, cluster.labels})
}
return data
}
type ClusterList []Cluster
// ListClusters list clusters where application deploys resource
func ListClusters(ctx context.Context, c client.Client) (*ClusterList, error) {
list := &ClusterList{
title: []string{"Name", "Alias", "Type", "EndPoint", "Labels"},
data: []Cluster{{"all", "*", "*", "*", "*"}},
}
func ListClusters(ctx context.Context, c client.Client) (ClusterList, error) {
name := ctx.Value(&CtxKeyAppName).(string)
ns := ctx.Value(&CtxKeyNamespace).(string)
app, err := LoadApplication(c, name, ns)
if err != nil {
return list, err
return ClusterList{}, err
}
clusterSet := make(map[string]interface{})
@@ -80,8 +59,8 @@ func ListClusters(ctx context.Context, c client.Client) (*ClusterList, error) {
}
clusters, _ := prismclusterv1alpha1.NewClusterClient(c).List(context.Background())
for _, cluster := range clusters.Items {
list := make(ClusterList, len(clusters.Items))
for index, cluster := range clusters.Items {
if _, ok := clusterSet[cluster.Name]; ok {
clusterInfo := Cluster{
name: cluster.Name,
@@ -96,8 +75,18 @@ func ListClusters(ctx context.Context, c client.Client) (*ClusterList, error) {
}
}
clusterInfo.labels = strings.Join(labels, ",")
list.data = append(list.data, clusterInfo)
list[index] = clusterInfo
}
}
return list, nil
}
// ToTableBody generate body of table in cluster view
func (l ClusterList) ToTableBody() [][]string {
data := make([][]string, len(l)+1)
data[0] = []string{AllCluster, "*", "*", "*", "*"}
for index, cluster := range l {
data[index+1] = []string{cluster.name, cluster.alias, cluster.clusterType, cluster.endpoint, cluster.labels}
}
return data
}

View File

@@ -27,23 +27,13 @@ import (
"github.com/oam-dev/kubevela/references/cli/top/utils"
)
// NamespaceList is namespace list
type NamespaceList struct {
title []string
data []Namespace
}
// ListClusterNamespaces return namespace of application's resource
func ListClusterNamespaces(ctx context.Context, c client.Client) (*NamespaceList, error) {
list := &NamespaceList{
title: []string{"Name", "Status", "Age"},
data: []Namespace{{"all", "*", "*"}},
}
func ListClusterNamespaces(ctx context.Context, c client.Client) (NamespaceList, error) {
name := ctx.Value(&CtxKeyAppName).(string)
ns := ctx.Value(&CtxKeyNamespace).(string)
app, err := LoadApplication(c, name, ns)
if err != nil {
return list, err
return NamespaceList{}, err
}
clusterNSSet := make(map[string]interface{})
for _, svc := range app.Status.AppliedResources {
@@ -51,20 +41,22 @@ func ListClusterNamespaces(ctx context.Context, c client.Client) (*NamespaceList
clusterNSSet[svc.Namespace] = struct{}{}
}
}
nsList := make(NamespaceList, len(clusterNSSet))
index := 0
for clusterNS := range clusterNSSet {
namespaceInfo, err := LoadNamespaceDetail(ctx, c, clusterNS)
if err != nil {
continue
}
list.data = append(list.data, Namespace{
Name: namespaceInfo.Name,
Status: string(namespaceInfo.Status.Phase),
Age: utils.TimeFormat(time.Since(namespaceInfo.CreationTimestamp.Time)),
})
nsList[index] = Namespace{
name: namespaceInfo.Name,
status: string(namespaceInfo.Status.Phase),
age: utils.TimeFormat(time.Since(namespaceInfo.CreationTimestamp.Time)),
}
index++
}
return list, nil
return nsList, nil
}
// LoadNamespaceDetail query detail info of a namespace by name

View File

@@ -30,10 +30,8 @@ var _ = Describe("test cluster namespace", func() {
It("list cluster namespace", func() {
cnsList, err := ListClusterNamespaces(ctx, k8sClient)
Expect(err).NotTo(HaveOccurred())
Expect(len(cnsList.Header())).To(Equal(3))
Expect(cnsList.Header()).To(Equal([]string{"Name", "Status", "Age"}))
Expect(len(cnsList.Body())).To(Equal(2))
Expect(cnsList.Body()[1][0]).To(Equal("default"))
Expect(len(cnsList.ToTableBody())).To(Equal(2))
Expect(cnsList.ToTableBody()[1]).To(Equal([]string{"default", "Active", ""}))
})
It("load cluster namespace detail info", func() {
ns, err := LoadNamespaceDetail(ctx, k8sClient, "default")

View File

@@ -25,28 +25,20 @@ import (
. "github.com/onsi/gomega"
)
func TestClusterList_Header(t *testing.T) {
clusterList := &ClusterList{title: []string{"Name", "Alias", "Type", "EndPoint", "Labels"}}
assert.Equal(t, len(clusterList.Header()), 5)
assert.Equal(t, clusterList.Header(), []string{"Name", "Alias", "Type", "EndPoint", "Labels"})
func TestClusterList_ToTableBody(t *testing.T) {
clusterList := &ClusterList{{"local", "", "", "", ""}}
assert.Equal(t, len(clusterList.ToTableBody()), 2)
assert.Equal(t, clusterList.ToTableBody()[1], []string{"local", "", "", "", ""})
}
func TestClusterList_Body(t *testing.T) {
clusterList := &ClusterList{data: []Cluster{{"local", "", "", "", ""}}}
assert.Equal(t, len(clusterList.Body()), 1)
assert.Equal(t, clusterList.Body()[0], []string{"local", "", "", "", ""})
}
var _ = Describe("test cluster", func() {
var _ = Describe("test cluster list", func() {
ctx := context.WithValue(context.Background(), &CtxKeyAppName, "first-vela-app")
ctx = context.WithValue(ctx, &CtxKeyNamespace, "default")
It("list clusters", func() {
clusterList, err := ListClusters(ctx, k8sClient)
Expect(err).NotTo(HaveOccurred())
Expect(len(clusterList.Header())).To(Equal(5))
Expect(clusterList.Header()).To(Equal([]string{"Name", "Alias", "Type", "EndPoint", "Labels"}))
Expect(len(clusterList.Body())).To(Equal(2))
Expect(clusterList.Body()[1]).To(Equal([]string{"local", "", "Internal", "-", ""}))
Expect(len(clusterList.ToTableBody())).To(Equal(2))
Expect(clusterList.ToTableBody()[1]).To(Equal([]string{"local", "", "Internal", "-", ""}))
})
})

View File

@@ -34,56 +34,13 @@ type ManagedResource struct {
status string
}
// ManagedResourceList is k8s struct resource list
type ManagedResourceList struct {
title []string
data []ManagedResource
}
// Header generate header of table in k8s object view
func (l *ManagedResourceList) Header() []string {
return l.title
}
// Body generate header of table in k8s object view
func (l *ManagedResourceList) Body() [][]string {
data := make([][]string, 0)
for _, app := range l.data {
data = append(data, []string{app.name, app.namespace, app.kind, app.apiVersion, app.cluster, app.status})
}
return data
}
// FilterCluster filter out objects that belong to the target cluster
func (l *ManagedResourceList) FilterCluster(cluster string) {
data := make([]ManagedResource, 0)
for _, app := range l.data {
if app.cluster == cluster {
data = append(data, ManagedResource{app.name, app.namespace, app.kind, app.apiVersion, app.cluster, app.status})
}
}
l.data = data
}
// FilterClusterNamespace filter out objects that belong to the target namespace
func (l *ManagedResourceList) FilterClusterNamespace(clusterNS string) {
data := make([]ManagedResource, 0)
for _, app := range l.data {
if app.namespace == clusterNS {
data = append(data, ManagedResource{app.name, app.namespace, app.kind, app.apiVersion, app.cluster, app.status})
}
}
l.data = data
}
// ManagedResourceList is managed resource list
type ManagedResourceList []ManagedResource
// ListManagedResource return managed resources of application
func ListManagedResource(ctx context.Context, c client.Client) (*ManagedResourceList, error) {
list := &ManagedResourceList{
title: []string{"Name", "Namespace", "Kind", "APIVersion", "Cluster", "Status"},
}
func ListManagedResource(ctx context.Context, c client.Client) (ManagedResourceList, error) {
name := ctx.Value(&CtxKeyAppName).(string)
namespace := ctx.Value(&CtxKeyNamespace).(string)
opt := query.Option{
Name: name,
Namespace: namespace,
@@ -92,19 +49,21 @@ func ListManagedResource(ctx context.Context, c client.Client) (*ManagedResource
collector := query.NewAppCollector(c, opt)
appResList, err := collector.CollectResourceFromApp(ctx)
if err != nil {
return list, err
return ManagedResourceList{}, err
}
for _, resource := range appResList {
list.data = append(list.data, LoadResourceDetail(resource))
list := make(ManagedResourceList, len(appResList))
for index, resource := range appResList {
list[index] = LoadResourceDetail(resource)
}
cluster, ok := ctx.Value(&CtxKeyCluster).(string)
if ok && cluster != "" {
list.FilterCluster(cluster)
}
clusterNamespace, ok := ctx.Value(&CtxKeyClusterNamespace).(string)
if ok && clusterNamespace != "" {
list.FilterClusterNamespace(clusterNamespace)
@@ -113,6 +72,15 @@ func ListManagedResource(ctx context.Context, c client.Client) (*ManagedResource
return list, nil
}
// ToTableBody generate header of table in managed resource view
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})
}
return data
}
// LoadResourceDetail return the aim resource detail info
func LoadResourceDetail(resource query.Resource) ManagedResource {
object := ManagedResource{
@@ -128,3 +96,25 @@ func LoadResourceDetail(resource query.Resource) ManagedResource {
}
return object
}
// FilterCluster filter out objects that belong to the target cluster
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})
}
}
*l = data
}
// 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})
}
}
*l = data
}

View File

@@ -25,16 +25,27 @@ import (
"github.com/stretchr/testify/assert"
)
func TestManagedResource_Header(t *testing.T) {
list := ManagedResourceList{title: []string{"name", "namespace", "kind", "APIVersion", "cluster", "status"}}
assert.Equal(t, len(list.Header()), 6)
assert.Equal(t, list.Header(), []string{"name", "namespace", "kind", "APIVersion", "cluster", "status"})
func TestListManagedResource(t *testing.T) {
list := ManagedResourceList{{"", "", "", "", "", ""}}
assert.Equal(t, list.ToTableBody(), [][]string{{"", "", "", "", "", ""}})
}
func TestManagedResource_Body(t *testing.T) {
list := ManagedResourceList{data: []ManagedResource{{"", "", "", "", "", ""}}}
assert.Equal(t, len(list.Body()), 1)
assert.Equal(t, list.Body(), [][]string{{"", "", "", "", "", ""}})
func TestManagedResourceList_FilterCluster(t *testing.T) {
list := ManagedResourceList{
{"", "", "", "", "1", ""},
{"", "", "", "", "2", ""},
}
list.FilterCluster("1")
assert.Equal(t, len(list), 1)
}
func TestManagedResourceList_FilterClusterNamespace(t *testing.T) {
list := ManagedResourceList{
{"", "1", "", "", "1", ""},
{"", "2", "", "", "2", ""},
}
list.FilterClusterNamespace("2")
assert.Equal(t, len(list), 1)
}
var _ = Describe("test managed resource", func() {
@@ -46,7 +57,6 @@ var _ = Describe("test managed resource", func() {
It("list managed resource", func() {
list, err := ListManagedResource(ctx, k8sClient)
Expect(err).NotTo(HaveOccurred())
Expect(len(list.Header())).To(Equal(6))
Expect(len(list.Body())).To(Equal(4))
Expect(len(list.ToTableBody())).To(Equal(4))
})
})

View File

@@ -28,41 +28,37 @@ import (
// Namespace is namespace struct
type Namespace struct {
Name string
Status string
Age string
name string
status string
age string
}
// AllNamespace is the key which represents all namespaces
const AllNamespace = "all"
// NamespaceList is namespace list
type NamespaceList []Namespace
// ListNamespaces return all namespaces
func ListNamespaces(ctx context.Context, c client.Reader) *NamespaceList {
list := &NamespaceList{title: []string{"Name", "Status", "Age"}, data: []Namespace{{Name: AllNamespace, Status: "*", Age: "*"}}}
func ListNamespaces(ctx context.Context, c client.Client) (NamespaceList, error) {
var nsList v1.NamespaceList
if err := c.List(ctx, &nsList); err != nil {
return list
return NamespaceList{}, err
}
for _, ns := range nsList.Items {
list.data = append(list.data, Namespace{
Name: ns.Name,
Status: string(ns.Status.Phase),
Age: utils.TimeFormat(time.Since(ns.CreationTimestamp.Time)),
})
nsInfoList := make(NamespaceList, len(nsList.Items))
for index, ns := range nsList.Items {
nsInfoList[index] = Namespace{
name: ns.Name,
status: string(ns.Status.Phase),
age: utils.TimeFormat(time.Since(ns.CreationTimestamp.Time)),
}
}
return list
return nsInfoList, nil
}
// Header generate header of table in namespace view
func (l *NamespaceList) Header() []string {
return l.title
}
// Body generate body of table in namespace view
func (l *NamespaceList) Body() [][]string {
data := make([][]string, 0)
for _, ns := range l.data {
data = append(data, []string{ns.Name, ns.Status, ns.Age})
// ToTableBody generate body of table in namespace view
func (l NamespaceList) ToTableBody() [][]string {
data := make([][]string, len(l)+1)
data[0] = []string{"all", "*", "*"}
for index, ns := range l {
data[index+1] = []string{ns.name, ns.status, ns.age}
}
return data
}

View File

@@ -25,30 +25,18 @@ import (
"github.com/stretchr/testify/assert"
)
func TestNamespaceList_Header(t *testing.T) {
nsList := NamespaceList{
title: []string{"Name", "Status", "Age"},
data: []Namespace{{Name: AllNamespace, Status: "*", Age: "*"}},
}
assert.Equal(t, nsList.Header(), []string{"Name", "Status", "Age"})
}
func TestNamespaceList_Body(t *testing.T) {
nsList := NamespaceList{
title: []string{"Name", "Status", "Age"},
data: []Namespace{{Name: AllNamespace, Status: "*", Age: "*"}},
}
assert.Equal(t, len(nsList.Body()), 1)
assert.Equal(t, nsList.Body()[0], []string{AllNamespace, "*", "*"})
nsList := NamespaceList{}
assert.Equal(t, len(nsList.ToTableBody()), 1)
assert.Equal(t, nsList.ToTableBody()[0], []string{AllNamespace, "*", "*"})
}
var _ = Describe("test namespace", func() {
ctx := context.Background()
It("list namespace", func() {
nsList := ListNamespaces(ctx, k8sClient)
Expect(len(nsList.Header())).To(Equal(3))
Expect(nsList.Header()).To(Equal([]string{"Name", "Status", "Age"}))
Expect(len(nsList.Body())).To(Equal(6))
Expect(nsList.Body()[0]).To(Equal([]string{"all", "*", "*"}))
nsList, err := ListNamespaces(ctx, k8sClient)
Expect(err).NotTo(HaveOccurred())
Expect(len(nsList.ToTableBody())).To(Equal(6))
Expect(nsList.ToTableBody()[0]).To(Equal([]string{"all", "*", "*"}))
})
})

View File

@@ -49,14 +49,10 @@ type Pod struct {
}
// PodList is pod list
type PodList struct {
title []string
data []Pod
}
type PodList []Pod
// ListPods return pod list of component
func ListPods(ctx context.Context, cfg *rest.Config, c client.Client) (*PodList, error) {
list := &PodList{title: []string{"Name", "Namespace", "Ready", "Status", "CPU", "MEM", "%CPU/R", "%CPU/L", "%MEM/R", "%MEM/L", "IP", "Node", "Age"}, data: []Pod{}}
func ListPods(ctx context.Context, cfg *rest.Config, c client.Client) (PodList, error) {
appName := ctx.Value(&CtxKeyAppName).(string)
appNamespace := ctx.Value(&CtxKeyNamespace).(string)
compCluster := ctx.Value(&CtxKeyCluster).(string)
@@ -76,16 +72,18 @@ func ListPods(ctx context.Context, cfg *rest.Config, c client.Client) (*PodList,
WithTree: true,
}
resource, err := collectResource(ctx, c, opt)
if err != nil {
return list, err
return PodList{}, err
}
for _, object := range resource {
list := make(PodList, len(resource))
for index, object := range resource {
pod := &v1.Pod{}
err = runtime.DefaultUnstructuredConverter.FromUnstructured(object.UnstructuredContent(), pod)
if err != nil {
continue
}
list.data = append(list.data, LoadPodDetail(cfg, pod))
list[index] = LoadPodDetail(cfg, pod)
}
return list, nil
}
@@ -127,16 +125,11 @@ func readyContainerNum(pod *v1.Pod) string {
return fmt.Sprintf("%d/%d", ready, total)
}
// Header generate header of table in pod view
func (l *PodList) Header() []string {
return l.title
}
// Body generate body of table in pod view
func (l *PodList) Body() [][]string {
data := make([][]string, 0)
for _, pod := range l.data {
data = append(data, []string{pod.Name, pod.Namespace, pod.Ready, pod.Status, pod.CPU, pod.Mem, pod.CPUR, pod.MemR, pod.CPUL, pod.MemL, pod.IP, pod.NodeName, pod.Age})
// ToTableBody generate body of table in pod view
func (l PodList) ToTableBody() [][]string {
data := make([][]string, len(l))
for index, pod := range l {
data[index] = []string{pod.Name, pod.Namespace, pod.Ready, pod.Status, pod.CPU, pod.Mem, pod.CPUR, pod.MemR, pod.CPUL, pod.MemL, pod.IP, pod.NodeName, pod.Age}
}
return data
}

View File

@@ -28,7 +28,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestPod(t *testing.T) {
func TestPodList_ToTableBody(t *testing.T) {
pod := Pod{
Name: "",
Namespace: "",
@@ -44,10 +44,8 @@ func TestPod(t *testing.T) {
NodeName: "",
Age: "",
}
podList := &PodList{title: []string{"Name", "Namespace", "Ready", "Status", "CPU", "MEM", "%CPU/R", "%CPU/L", "%MEM/R", "%MEM/L", "IP", "Node", "Age"}, data: []Pod{pod}}
assert.Equal(t, len(podList.Header()), 13)
assert.Equal(t, podList.Header()[0], "Name")
assert.Equal(t, len(podList.Body()), 1)
podList := &PodList{pod}
assert.Equal(t, len(podList.ToTableBody()), 1)
}
var _ = Describe("test pod", func() {
@@ -61,7 +59,7 @@ var _ = Describe("test pod", func() {
It("list pods", func() {
podList, err := ListPods(ctx, cfg, k8sClient)
Expect(err).NotTo(HaveOccurred())
Expect(len(podList.Body())).To(Equal(1))
Expect(len(podList.ToTableBody())).To(Equal(1))
})
It("load pod detail", func() {

View File

@@ -65,6 +65,8 @@ var (
)
const (
// AllNamespace represent all namespaces
AllNamespace = "all"
// AllClusterNamespace represent all cluster namespace
AllClusterNamespace = "all"
// AllCluster represent all cluster

View File

@@ -19,7 +19,6 @@ package view
import (
"context"
"fmt"
"log"
"github.com/gdamore/tcell/v2"
@@ -31,40 +30,75 @@ import (
// ApplicationView is the application view, this view display info of application of KubeVela
type ApplicationView struct {
*ResourceView
*CommonResourceView
ctx context.Context
}
// NewApplicationView return a new application view
func NewApplicationView(ctx context.Context, app *App) model.Component {
v := &ApplicationView{
ResourceView: NewResourceView(app),
ctx: ctx,
}
return v
// Name return application view name
func (v *ApplicationView) Name() string {
return "Application"
}
// Init the application view
func (v *ApplicationView) Init() {
// set title of view
title := fmt.Sprintf("[ %s ]", v.Title())
v.SetTitle(title).SetTitleColor(config.ResourceTableTitleColor)
v.CommonResourceView.Init()
v.SetTitle(fmt.Sprintf("[ %s ]", v.Title()))
v.BuildHeader()
v.bindKeys()
}
// ListApplications list all applications
func (v *ApplicationView) ListApplications() model.ResourceList {
list, err := model.ListApplications(v.ctx, v.app.client)
if err != nil {
log.Println(err)
// Start the application view
func (v *ApplicationView) Start() {
v.Update()
}
// Stop the application view
func (v *ApplicationView) Stop() {
v.Table.Stop()
}
// Hint return key action menu hints of the application view
func (v *ApplicationView) Hint() []model.MenuHint {
return v.Actions().Hint()
}
// InitView return a new application view
func (v *ApplicationView) InitView(ctx context.Context, app *App) {
if v.CommonResourceView == nil {
v.CommonResourceView = NewCommonView(app)
v.ctx = ctx
} else {
v.ctx = ctx
}
return list
}
// Update refresh the content of body of view
func (v *ApplicationView) Update() {
v.BuildBody()
}
// BuildHeader render the header of table
func (v *ApplicationView) BuildHeader() {
header := []string{"Name", "Namespace", "Phase", "CreateTime"}
v.CommonResourceView.BuildHeader(header)
}
// BuildBody render the body of table
func (v *ApplicationView) BuildBody() {
apps, err := model.ListApplications(v.ctx, v.app.client)
if err != nil {
return
}
appInfos := apps.ToTableBody()
v.CommonResourceView.BuildBody(appInfos)
rowNum := len(appInfos)
v.ColorizeStatusText(rowNum)
}
// ColorizeStatusText colorize the status column text
func (v *ApplicationView) ColorizeStatusText(rowNum int) {
for i := 1; i < rowNum+1; i++ {
status := v.Table.GetCell(i, 2).Text
for i := 0; i < rowNum; i++ {
status := v.Table.GetCell(i+1, 2).Text
switch common.ApplicationPhase(status) {
case common.ApplicationRendering, common.ApplicationStarting:
status = config.ApplicationStartingAndRenderingPhaseColor + status
@@ -76,7 +110,7 @@ func (v *ApplicationView) ColorizeStatusText(rowNum int) {
status = config.ApplicationRunningPhaseColor + status
default:
}
v.Table.GetCell(i, 2).SetText(status)
v.Table.GetCell(i+1, 2).SetText(status)
}
}
@@ -89,28 +123,6 @@ func (v *ApplicationView) Title() string {
return fmt.Sprintf("Application"+" (%s)", namespace)
}
// Name return application view name
func (v *ApplicationView) Name() string {
return "Application"
}
// Start the application view
func (v *ApplicationView) Start() {
resourceList := v.ListApplications()
v.ResourceView.Init(resourceList)
v.ColorizeStatusText(len(resourceList.Body()))
}
// Stop the application view
func (v *ApplicationView) Stop() {
v.Table.Stop()
}
// Hint return key action menu hints of the application view
func (v *ApplicationView) Hint() []model.MenuHint {
return v.Actions().Hint()
}
func (v *ApplicationView) bindKeys() {
v.Actions().Delete([]tcell.Key{tcell.KeyEnter})
v.Actions().Add(model.KeyActions{
@@ -127,6 +139,7 @@ func (v *ApplicationView) managedResourceView(event *tcell.EventKey) *tcell.Even
if row == 0 {
return event
}
name, namespace := v.GetCell(row, 0).Text, v.GetCell(row, 1).Text
v.ctx = context.WithValue(v.ctx, &model.CtxKeyAppName, name)
v.ctx = context.WithValue(v.ctx, &model.CtxKeyNamespace, namespace)

View File

@@ -43,22 +43,30 @@ func TestApplicationView(t *testing.T) {
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")
assert.Equal(t, len(app.Components()), 4)
ctx := context.Background()
ctx = context.WithValue(ctx, &model.CtxKeyNamespace, "")
view := NewApplicationView(ctx, app)
appView, ok := (view).(*ApplicationView)
assert.Equal(t, ok, true)
appView := new(ApplicationView)
t.Run("init view", func(t *testing.T) {
assert.Empty(t, appView.CommonResourceView)
appView.InitView(ctx, app)
assert.NotEmpty(t, appView.CommonResourceView)
})
t.Run("init", func(t *testing.T) {
appView.Init()
assert.Equal(t, appView.Table.GetTitle(), "[ Application (all) ]")
})
t.Run("start", func(t *testing.T) {
appView.Start()
assert.Equal(t, appView.GetCell(0, 0).Text, "Name")
})
t.Run("start", func(t *testing.T) {
appView.Start()
})
t.Run("stop", func(t *testing.T) {
appView.Stop()
assert.Equal(t, appView.GetCell(0, 0).Text, "")
})
t.Run("colorize text", func(t *testing.T) {
@@ -79,7 +87,17 @@ func TestApplicationView(t *testing.T) {
assert.Equal(t, len(appView.Hint()), 5)
})
t.Run("object view", func(t *testing.T) {
t.Run("managed resource view", func(t *testing.T) {
appView.Table.Table = appView.Table.Select(1, 1)
assert.Empty(t, appView.managedResourceView(nil))
})
t.Run("namespace view", func(t *testing.T) {
appView.Table.Table = appView.Table.Select(1, 1)
assert.Empty(t, appView.namespaceView(nil))
})
t.Run("yaml view", func(t *testing.T) {
appView.Table.Table = appView.Table.Select(1, 1)
assert.Empty(t, appView.managedResourceView(nil))
})

View File

@@ -19,9 +19,9 @@ package view
import (
"context"
"fmt"
"log"
"github.com/gdamore/tcell/v2"
v1 "k8s.io/api/core/v1"
"github.com/oam-dev/kubevela/references/cli/top/component"
"github.com/oam-dev/kubevela/references/cli/top/config"
@@ -30,44 +30,26 @@ import (
// ClusterNamespaceView is the cluster namespace, which display the namespace info of application's resource
type ClusterNamespaceView struct {
*ResourceView
*CommonResourceView
ctx context.Context
}
// NewClusterNamespaceView return a new cluster namespace view
func NewClusterNamespaceView(ctx context.Context, app *App) model.Component {
v := &ClusterNamespaceView{
ResourceView: NewResourceView(app),
ctx: ctx,
}
return v
// Name return cluster namespace view name
func (v *ClusterNamespaceView) Name() string {
return "ClusterNamespace"
}
// Init the cluster namespace view
func (v *ClusterNamespaceView) Init() {
title := fmt.Sprintf("[ %s ]", v.Name())
v.SetTitle(title).SetTitleColor(config.ResourceTableTitleColor)
v.CommonResourceView.Init()
v.SetTitle(fmt.Sprintf("[ %s ]", v.Name())).SetTitleColor(config.ResourceTableTitleColor)
v.BuildHeader()
v.bindKeys()
}
// ListClusterNamespaces return the namespace of application's resource
func (v *ClusterNamespaceView) ListClusterNamespaces() model.ResourceList {
list, err := model.ListClusterNamespaces(v.ctx, v.app.client)
if err != nil {
log.Println(err)
}
return list
}
// Hint return key action menu hints of the cluster namespace view
func (v *ClusterNamespaceView) Hint() []model.MenuHint {
return v.Actions().Hint()
}
// Start the cluster namespace view
func (v *ClusterNamespaceView) Start() {
resourceList := v.ListClusterNamespaces()
v.ResourceView.Init(resourceList)
v.Update()
}
// Stop the cluster namespace view
@@ -75,9 +57,56 @@ func (v *ClusterNamespaceView) Stop() {
v.Table.Stop()
}
// Name return cluster namespace view name
func (v *ClusterNamespaceView) Name() string {
return "ClusterNamespace"
// Hint return key action menu hints of the cluster namespace view
func (v *ClusterNamespaceView) Hint() []model.MenuHint {
return v.Actions().Hint()
}
// InitView init a new cluster namespace view
func (v *ClusterNamespaceView) InitView(ctx context.Context, app *App) {
if v.CommonResourceView == nil {
v.CommonResourceView = NewCommonView(app)
v.ctx = ctx
} else {
v.ctx = ctx
}
}
// Update refresh the content of body of view
func (v *ClusterNamespaceView) Update() {
v.BuildBody()
}
// BuildHeader render the header of table
func (v *ClusterNamespaceView) BuildHeader() {
header := []string{"Name", "Status", "Age"}
v.CommonResourceView.BuildHeader(header)
}
// BuildBody render the body of table
func (v *ClusterNamespaceView) BuildBody() {
cnList, err := model.ListClusterNamespaces(v.ctx, v.app.client)
if err != nil {
return
}
cnInfos := cnList.ToTableBody()
v.CommonResourceView.BuildBody(cnInfos)
rowNum := len(cnInfos)
v.ColorizeStatusText(rowNum)
}
// ColorizeStatusText colorize the status column text
func (v *ClusterNamespaceView) ColorizeStatusText(rowNum int) {
for i := 0; i < rowNum; i++ {
status := v.Table.GetCell(i+1, 2).Text
switch v1.NamespacePhase(status) {
case v1.NamespaceActive:
status = config.NamespaceActiveStatusColor + status
case v1.NamespaceTerminating:
status = config.NamespaceTerminateStatusColor + status
}
v.Table.GetCell(i+1, 2).SetText(status)
}
}
func (v *ClusterNamespaceView) bindKeys() {
@@ -95,11 +124,12 @@ func (v *ClusterNamespaceView) managedResourceView(event *tcell.EventKey) *tcell
if row == 0 {
return event
}
v.app.content.PopComponent()
clusterNamespace := v.GetCell(row, 0).Text
if clusterNamespace == model.AllClusterNamespace {
clusterNamespace = ""
}
v.app.content.PopComponent()
v.ctx = context.WithValue(v.ctx, &model.CtxKeyClusterNamespace, clusterNamespace)
v.app.command.run(v.ctx, "resource")
return event

View File

@@ -42,31 +42,39 @@ func TestClusterNamespaceView(t *testing.T) {
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")
assert.Equal(t, len(app.Components()), 4)
ctx := context.Background()
ctx = context.WithValue(ctx, &model.CtxKeyAppName, "")
ctx = context.WithValue(ctx, &model.CtxKeyNamespace, "")
cnsView, ok := NewClusterNamespaceView(ctx, app).(*ClusterNamespaceView)
assert.Equal(t, ok, true)
cnsView := new(ClusterNamespaceView)
t.Run("init view", func(t *testing.T) {
assert.Empty(t, cnsView.CommonResourceView)
cnsView.InitView(ctx, app)
assert.NotEmpty(t, cnsView.CommonResourceView)
})
t.Run("init", func(t *testing.T) {
cnsView.Init()
assert.Equal(t, cnsView.GetTitle(), "[ ClusterNamespace ]")
assert.Equal(t, cnsView.GetCell(0, 0).Text, "Name")
})
t.Run("start", func(t *testing.T) {
cnsView.Start()
assert.Equal(t, cnsView.GetCell(0, 0).Text, "Name")
assert.Equal(t, cnsView.GetCell(1, 0).Text, "all")
})
t.Run("stop", func(t *testing.T) {
cnsView.Stop()
assert.Equal(t, cnsView.GetCell(0, 0).Text, "")
})
t.Run("hint", func(t *testing.T) {
assert.Equal(t, len(cnsView.Hint()), 3)
})
t.Run("object view", func(t *testing.T) {
t.Run("managed resource view", func(t *testing.T) {
cnsView.Table.Table = cnsView.Table.Select(1, 1)
assert.Empty(t, cnsView.managedResourceView(nil))
})

View File

@@ -19,7 +19,6 @@ package view
import (
"context"
"fmt"
"log"
"github.com/gdamore/tcell/v2"
@@ -30,36 +29,18 @@ import (
// ClusterView is the cluster view, this view display info of cluster where selected application deployed
type ClusterView struct {
*ResourceView
*CommonResourceView
ctx context.Context
}
// NewClusterView return a new cluster view
func NewClusterView(ctx context.Context, app *App) model.Component {
v := &ClusterView{
ResourceView: NewResourceView(app),
ctx: ctx,
}
return v
}
// Init cluster view init
func (v *ClusterView) Init() {
// set title of view
title := fmt.Sprintf("[ %s ]", v.Name())
v.SetTitle(title).SetTitleColor(config.ResourceTableTitleColor)
v.CommonResourceView.Init()
v.SetTitle(fmt.Sprintf("[ %s ]", v.Name())).SetTitleColor(config.ResourceTableTitleColor)
v.BuildHeader()
v.bindKeys()
}
// ListClusters list clusters where application deployed
func (v *ClusterView) ListClusters() model.ResourceList {
list, err := model.ListClusters(v.ctx, v.app.client)
if err != nil {
log.Println(err)
}
return list
}
// Name return cluster view name
func (v *ClusterView) Name() string {
return "Cluster"
@@ -67,8 +48,7 @@ func (v *ClusterView) Name() string {
// Start the cluster view
func (v *ClusterView) Start() {
resourceList := v.ListClusters()
v.ResourceView.Init(resourceList)
v.Update()
}
// Stop the cluster view
@@ -81,6 +61,37 @@ func (v *ClusterView) Hint() []model.MenuHint {
return v.Actions().Hint()
}
// InitView init a new cluster view
func (v *ClusterView) InitView(ctx context.Context, app *App) {
if v.CommonResourceView == nil {
v.CommonResourceView = NewCommonView(app)
v.ctx = ctx
} else {
v.ctx = ctx
}
}
// Update refresh the content of body of view
func (v *ClusterView) Update() {
v.BuildBody()
}
// BuildHeader render the header of table
func (v *ClusterView) BuildHeader() {
header := []string{"Name", "Alias", "Type", "EndPoint", "Labels"}
v.CommonResourceView.BuildHeader(header)
}
// BuildBody render the body of table
func (v *ClusterView) BuildBody() {
clusterList, err := model.ListClusters(v.ctx, v.app.client)
if err != nil {
return
}
clusterInfos := clusterList.ToTableBody()
v.CommonResourceView.BuildBody(clusterInfos)
}
func (v *ClusterView) bindKeys() {
v.Actions().Delete([]tcell.Key{tcell.KeyEnter})
v.Actions().Add(model.KeyActions{
@@ -96,11 +107,11 @@ func (v *ClusterView) managedResourceView(event *tcell.EventKey) *tcell.EventKey
if row == 0 {
return event
}
v.app.content.PopComponent()
clusterName := v.GetCell(row, 0).Text
if clusterName == model.AllCluster {
clusterName = ""
}
v.app.content.PopComponent()
v.ctx = context.WithValue(v.ctx, &model.CtxKeyCluster, clusterName)
v.app.command.run(v.ctx, "resource")
return event

View File

@@ -43,16 +43,23 @@ func TestClusterView(t *testing.T) {
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")
assert.Equal(t, len(app.Components()), 4)
ctx := context.Background()
ctx = context.WithValue(ctx, &model.CtxKeyAppName, "")
ctx = context.WithValue(ctx, &model.CtxKeyNamespace, "")
view := NewClusterView(ctx, app)
clusterView, ok := (view).(*ClusterView)
assert.Equal(t, ok, true)
clusterView := new(ClusterView)
t.Run("init view", func(t *testing.T) {
assert.Empty(t, clusterView.CommonResourceView)
clusterView.InitView(ctx, app)
assert.NotEmpty(t, clusterView.CommonResourceView)
})
t.Run("init", func(t *testing.T) {
clusterView.Init()
assert.Equal(t, clusterView.Table.GetTitle(), "[ Cluster ]")
assert.Equal(t, clusterView.GetCell(0, 0).Text, "Name")
})
t.Run("hint", func(t *testing.T) {
@@ -61,14 +68,14 @@ func TestClusterView(t *testing.T) {
t.Run("start", func(t *testing.T) {
clusterView.Start()
assert.Equal(t, clusterView.GetCell(0, 0).Text, "Name")
})
t.Run("stop", func(t *testing.T) {
clusterView.Stop()
assert.Equal(t, clusterView.GetCell(0, 0).Text, "")
})
t.Run("managedResourceView", func(t *testing.T) {
t.Run("managed resource view", func(t *testing.T) {
testData := []string{"local", "", "", "", ""}
for j := 0; j < 5; j++ {
clusterView.Table.SetCell(1, j, tview.NewTableCell(testData[j]))

View File

@@ -52,8 +52,9 @@ func (c *Command) run(ctx context.Context, cmd string) {
case cmd == "yaml":
component = NewYamlView(ctx, c.app)
default:
if resource, ok := ResourceMap[cmd]; ok {
component = resource.viewFunc(ctx, c.app)
if resourceView, ok := ResourceViewMap[cmd]; ok {
resourceView.InitView(ctx, c.app)
component = resourceView
} else {
return
}

View File

@@ -19,7 +19,6 @@ package view
import (
"context"
"fmt"
"log"
"github.com/gdamore/tcell/v2"
@@ -31,34 +30,37 @@ import (
// ManagedResourceView is a view which displays info of application's managed resource including CRDs and k8s objects
type ManagedResourceView struct {
*ResourceView
*CommonResourceView
ctx context.Context
}
// NewManagedResourceView return a new managed resource view
func NewManagedResourceView(ctx context.Context, app *App) model.Component {
v := &ManagedResourceView{
ResourceView: NewResourceView(app),
ctx: ctx,
}
return v
// Name return managed resource view name
func (v *ManagedResourceView) Name() string {
return "Managed Resource"
}
// Init managed resource view
func (v *ManagedResourceView) Init() {
v.CommonResourceView.Init()
// set title of view
title := fmt.Sprintf("[ %s ]", v.Title())
v.SetTitle(title).SetTitleColor(config.ResourceTableTitleColor)
v.SetTitle(fmt.Sprintf("[ %s ]", v.Title())).SetTitleColor(config.ResourceTableTitleColor)
v.BuildHeader()
v.bindKeys()
}
// ListManagedResources return managed resource of the aimed application
func (v *ManagedResourceView) ListManagedResources() model.ResourceList {
list, err := model.ListManagedResource(v.ctx, v.app.client)
if err != nil {
log.Println(err)
}
return list
// Start the managed resource view
func (v *ManagedResourceView) Start() {
v.Update()
}
// Stop the managed resource view
func (v *ManagedResourceView) Stop() {
v.Table.Stop()
}
// Hint return key action menu hints of the managed resource view
func (v *ManagedResourceView) Hint() []model.MenuHint {
return v.Actions().Hint()
}
// Title return the table title of managed resource view
@@ -74,26 +76,37 @@ func (v *ManagedResourceView) Title() string {
return fmt.Sprintf("Managed Resource"+" (%s/%s)", namespace, clusterNS)
}
// Name return managed resource view name
func (v *ManagedResourceView) Name() string {
return "Managed Resource"
// InitView init a new managed resource view
func (v *ManagedResourceView) InitView(ctx context.Context, app *App) {
if v.CommonResourceView == nil {
v.CommonResourceView = NewCommonView(app)
v.ctx = ctx
} else {
v.ctx = ctx
}
}
// Start the managed resource view
func (v *ManagedResourceView) Start() {
resourceList := v.ListManagedResources()
v.ResourceView.Init(resourceList)
v.ColorizeStatusText(len(resourceList.Body()))
// Update refresh the content of body of view
func (v *ManagedResourceView) Update() {
v.BuildBody()
}
// Stop the managed resource view
func (v *ManagedResourceView) Stop() {
v.Table.Stop()
// BuildHeader render the header of table
func (v *ManagedResourceView) BuildHeader() {
header := []string{"Name", "Namespace", "Kind", "APIVersion", "Cluster", "Status"}
v.CommonResourceView.BuildHeader(header)
}
// Hint return key action menu hints of the managed resource view
func (v *ManagedResourceView) Hint() []model.MenuHint {
return v.Actions().Hint()
// BuildBody render the body of table
func (v *ManagedResourceView) BuildBody() {
resourceList, err := model.ListManagedResource(v.ctx, v.app.client)
if err != nil {
return
}
resourceInfos := resourceList.ToTableBody()
v.CommonResourceView.BuildBody(resourceInfos)
rowNum := len(resourceInfos)
v.ColorizeStatusText(rowNum)
}
// ColorizeStatusText colorize the status column text

View File

@@ -43,28 +43,33 @@ func TestManagedResourceView(t *testing.T) {
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")
assert.Equal(t, len(app.Components()), 4)
ctx := context.Background()
ctx = context.WithValue(ctx, &model.CtxKeyAppName, "")
ctx = context.WithValue(ctx, &model.CtxKeyNamespace, "")
ctx = context.WithValue(ctx, &model.CtxKeyCluster, "")
view := NewManagedResourceView(ctx, app)
resourceView, ok := (view).(*ManagedResourceView)
assert.Equal(t, ok, true)
resourceView := new(ManagedResourceView)
t.Run("init view", func(t *testing.T) {
assert.Empty(t, resourceView.CommonResourceView)
resourceView.InitView(ctx, app)
assert.NotEmpty(t, resourceView.CommonResourceView)
})
t.Run("init", func(t *testing.T) {
resourceView.Init()
assert.Equal(t, resourceView.Table.GetTitle(), "[ Managed Resource (all/all) ]")
assert.Equal(t, resourceView.GetCell(0, 0).Text, "Name")
})
t.Run("start", func(t *testing.T) {
resourceView.Start()
assert.Equal(t, resourceView.GetCell(0, 0).Text, "Name")
})
t.Run("stop", func(t *testing.T) {
resourceView.Stop()
assert.Equal(t, resourceView.GetCell(0, 0).Text, "")
})
t.Run("colorize text", func(t *testing.T) {

View File

@@ -21,6 +21,7 @@ import (
"fmt"
"github.com/gdamore/tcell/v2"
v1 "k8s.io/api/core/v1"
"github.com/oam-dev/kubevela/references/cli/top/component"
"github.com/oam-dev/kubevela/references/cli/top/config"
@@ -29,31 +30,18 @@ import (
// NamespaceView is namespace view struct
type NamespaceView struct {
*ResourceView
*CommonResourceView
ctx context.Context
}
// NewNamespaceView return a new namespace view
func NewNamespaceView(ctx context.Context, app *App) model.Component {
v := &NamespaceView{
ResourceView: NewResourceView(app),
ctx: ctx,
}
return v
}
// Init a namespace view
func (v *NamespaceView) Init() {
title := fmt.Sprintf("[ %s ]", v.Name())
v.SetTitle(title).SetTitleColor(config.ResourceTableTitleColor)
v.CommonResourceView.Init()
v.SetTitle(fmt.Sprintf("[ %s ]", v.Name())).SetTitleColor(config.ResourceTableTitleColor)
v.BuildHeader()
v.bindKeys()
}
// ListNamespaces return all namespaces
func (v *NamespaceView) ListNamespaces() model.ResourceList {
return model.ListNamespaces(v.ctx, v.app.client)
}
// Name return k8s view name
func (v *NamespaceView) Name() string {
return "Namespace"
@@ -61,8 +49,7 @@ func (v *NamespaceView) Name() string {
// Start the managed namespace view
func (v *NamespaceView) Start() {
resourceList := v.ListNamespaces()
v.ResourceView.Init(resourceList)
v.Update()
}
// Stop the managed namespace view
@@ -75,6 +62,53 @@ func (v *NamespaceView) Hint() []model.MenuHint {
return v.Actions().Hint()
}
// InitView init a new namespace view
func (v *NamespaceView) InitView(ctx context.Context, app *App) {
if v.CommonResourceView == nil {
v.CommonResourceView = NewCommonView(app)
v.ctx = ctx
} else {
v.ctx = ctx
}
}
// Update refresh the content of body of view
func (v *NamespaceView) Update() {
v.BuildBody()
}
// BuildHeader render the header of table
func (v *NamespaceView) BuildHeader() {
header := []string{"Name", "Status", "Age"}
v.CommonResourceView.BuildHeader(header)
}
// BuildBody render the body of table
func (v *NamespaceView) BuildBody() {
nsList, err := model.ListNamespaces(v.ctx, v.app.client)
if err != nil {
return
}
nsInfos := nsList.ToTableBody()
v.CommonResourceView.BuildBody(nsInfos)
rowNum := len(nsInfos)
v.ColorizeStatusText(rowNum)
}
// ColorizeStatusText colorize the status column text
func (v *NamespaceView) ColorizeStatusText(rowNum int) {
for i := 0; i < rowNum; i++ {
status := v.Table.GetCell(i+1, 2).Text
switch v1.NamespacePhase(status) {
case v1.NamespaceActive:
status = config.NamespaceActiveStatusColor + status
case v1.NamespaceTerminating:
status = config.NamespaceTerminateStatusColor + status
}
v.Table.GetCell(i+1, 2).SetText(status)
}
}
func (v *NamespaceView) bindKeys() {
v.Actions().Delete([]tcell.Key{tcell.KeyEnter})
v.Actions().Add(model.KeyActions{
@@ -89,13 +123,12 @@ func (v *NamespaceView) applicationView(event *tcell.EventKey) *tcell.EventKey {
if row == 0 {
return event
}
v.app.content.PopComponent()
ns := v.Table.GetCell(row, 0).Text
if ns == model.AllNamespace {
ns = ""
}
v.app.content.PopComponent()
v.ctx = context.WithValue(v.ctx, &model.CtxKeyNamespace, ns)
v.app.command.run(v.ctx, "app")
return event
}

View File

@@ -43,16 +43,23 @@ func TestNamespaceView(t *testing.T) {
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")
assert.Equal(t, len(app.Components()), 4)
ctx := context.Background()
ctx = context.WithValue(ctx, &model.CtxKeyAppName, "")
ctx = context.WithValue(ctx, &model.CtxKeyNamespace, "")
view := NewNamespaceView(ctx, app)
nsView, ok := (view).(*NamespaceView)
assert.Equal(t, ok, true)
nsView := new(NamespaceView)
t.Run("init view", func(t *testing.T) {
assert.Empty(t, nsView.CommonResourceView)
nsView.InitView(ctx, app)
assert.NotEmpty(t, nsView.CommonResourceView)
})
t.Run("init", func(t *testing.T) {
nsView.Init()
assert.Equal(t, nsView.Table.GetTitle(), "[ Namespace ]")
assert.Equal(t, nsView.GetCell(0, 0).Text, "Name")
})
t.Run("hint", func(t *testing.T) {
@@ -61,14 +68,14 @@ func TestNamespaceView(t *testing.T) {
t.Run("start", func(t *testing.T) {
nsView.Start()
assert.Equal(t, nsView.GetCell(0, 0).Text, "Name")
})
t.Run("stop", func(t *testing.T) {
nsView.Stop()
assert.Equal(t, nsView.GetCell(0, 0).Text, "")
})
t.Run("appView", func(t *testing.T) {
t.Run("app view", func(t *testing.T) {
testData := []string{"local", "", "", "", ""}
for j := 0; j < 5; j++ {
nsView.Table.SetCell(1, j, tview.NewTableCell(testData[j]))

View File

@@ -48,6 +48,7 @@ func (ps *PageStack) StackPop(old, new model.Component) {
if new == nil {
return
}
new.Start()
ps.app.SetFocus(new)
}

View File

@@ -19,7 +19,6 @@ package view
import (
"context"
"fmt"
"log"
"github.com/gdamore/tcell/v2"
v1 "k8s.io/api/core/v1"
@@ -31,34 +30,69 @@ import (
// PodView is the pod view, this view display info of pod belonging to component
type PodView struct {
*ResourceView
*CommonResourceView
ctx context.Context
}
// NewPodView return a new pod view
func NewPodView(ctx context.Context, app *App) model.Component {
v := &PodView{
ResourceView: NewResourceView(app),
ctx: ctx,
}
return v
// Name return pod view name
func (v *PodView) Name() string {
return "Pod"
}
// Start the pod view
func (v *PodView) Start() {
v.Update()
}
// Stop the pod view
func (v *PodView) Stop() {
v.Table.Stop()
}
// Hint return key action menu hints of the pod view
func (v *PodView) Hint() []model.MenuHint {
return v.Actions().Hint()
}
// Init cluster view init
func (v *PodView) Init() {
// set title of view
title := fmt.Sprintf("[ %s ]", v.Name())
v.SetTitle(title).SetTitleColor(config.ResourceTableTitleColor)
v.CommonResourceView.Init()
v.SetTitle(fmt.Sprintf("[ %s ]", v.Name()))
v.BuildHeader()
v.bindKeys()
}
// ListPods list pods of component
func (v *PodView) ListPods() model.ResourceList {
list, err := model.ListPods(v.ctx, v.app.config.RestConfig, v.app.client)
if err != nil {
log.Println(err)
// InitView init a new pod view
func (v *PodView) InitView(ctx context.Context, app *App) {
if v.CommonResourceView == nil {
v.CommonResourceView = NewCommonView(app)
v.ctx = ctx
} else {
v.ctx = ctx
}
return list
}
// Update refresh the content of body of view
func (v *PodView) Update() {
v.BuildBody()
}
// BuildHeader render the header of table
func (v *PodView) BuildHeader() {
header := []string{"Name", "Namespace", "Ready", "Status", "CPU", "MEM", "%CPU/R", "%CPU/L", "%MEM/R", "%MEM/L", "IP", "Node", "Age"}
v.CommonResourceView.BuildHeader(header)
}
// BuildBody render the body of table
func (v *PodView) BuildBody() {
podList, err := model.ListPods(v.ctx, v.app.config.RestConfig, v.app.client)
if err != nil {
return
}
podInfos := podList.ToTableBody()
v.CommonResourceView.BuildBody(podInfos)
rowNum := len(podInfos)
v.ColorizePhaseText(rowNum)
}
// ColorizePhaseText colorize the phase column text
@@ -80,28 +114,6 @@ func (v *PodView) ColorizePhaseText(rowNum int) {
}
}
// Name return pod view name
func (v *PodView) Name() string {
return "Pod"
}
// Start the pod view
func (v *PodView) Start() {
resourceList := v.ListPods()
v.ResourceView.Init(resourceList)
v.ColorizePhaseText(len(resourceList.Body()))
}
// Stop the pod view
func (v *PodView) Stop() {
v.Table.Stop()
}
// Hint return key action menu hints of the pod view
func (v *PodView) Hint() []model.MenuHint {
return v.Actions().Hint()
}
func (v *PodView) bindKeys() {
v.Actions().Delete([]tcell.Key{tcell.KeyEnter})
v.Actions().Add(model.KeyActions{

View File

@@ -44,6 +44,7 @@ func TestPodView(t *testing.T) {
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")
assert.Equal(t, len(app.Components()), 4)
ctx := context.Background()
ctx = context.WithValue(ctx, &model.CtxKeyAppName, "")
ctx = context.WithValue(ctx, &model.CtxKeyNamespace, "")
@@ -51,20 +52,26 @@ func TestPodView(t *testing.T) {
ctx = context.WithValue(ctx, &model.CtxKeyClusterNamespace, "")
ctx = context.WithValue(ctx, &model.CtxKeyComponentName, "")
view, ok := NewPodView(ctx, app).(*PodView)
assert.Equal(t, ok, true)
podView := new(PodView)
t.Run("init view", func(t *testing.T) {
assert.Empty(t, podView.CommonResourceView)
podView.InitView(ctx, app)
assert.NotEmpty(t, podView.CommonResourceView)
})
t.Run("init", func(t *testing.T) {
view.Init()
assert.Equal(t, view.Table.GetTitle(), "[ Pod ]")
podView.Init()
assert.Equal(t, podView.Table.GetTitle(), "[ Pod ]")
assert.Equal(t, podView.GetCell(0, 0).Text, "Name")
})
t.Run("start", func(t *testing.T) {
view.Start()
assert.Equal(t, view.GetCell(0, 0).Text, "Name")
podView.Start()
})
t.Run("stop", func(t *testing.T) {
view.Stop()
podView.Stop()
assert.Equal(t, podView.GetCell(0, 0).Text, "")
})
t.Run("colorize text", func(t *testing.T) {
@@ -76,17 +83,17 @@ func TestPodView(t *testing.T) {
}
for i := 0; i < len(testData); i++ {
for j := 0; j < len(testData[i]); j++ {
view.Table.SetCell(1+i, j, tview.NewTableCell(testData[i][j]))
podView.Table.SetCell(1+i, j, tview.NewTableCell(testData[i][j]))
}
}
view.ColorizePhaseText(5)
assert.Equal(t, view.GetCell(1, 3).Text, "[green::]Running")
assert.Equal(t, view.GetCell(2, 3).Text, "[yellow::]Pending")
assert.Equal(t, view.GetCell(3, 3).Text, "[purple::]Succeeded")
assert.Equal(t, view.GetCell(4, 3).Text, "[red::]Failed")
podView.ColorizePhaseText(5)
assert.Equal(t, podView.GetCell(1, 3).Text, "[green::]Running")
assert.Equal(t, podView.GetCell(2, 3).Text, "[yellow::]Pending")
assert.Equal(t, podView.GetCell(3, 3).Text, "[purple::]Succeeded")
assert.Equal(t, podView.GetCell(4, 3).Text, "[red::]Failed")
})
t.Run("hint", func(t *testing.T) {
assert.Equal(t, len(view.Hint()), 3)
assert.Equal(t, len(podView.Hint()), 3)
})
}

View File

@@ -19,81 +19,77 @@ package view
import (
"context"
"github.com/oam-dev/kubevela/references/cli/top/component"
"github.com/rivo/tview"
"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"
)
// ResourceView is an abstract of resource view
type ResourceView struct {
// ResourceView is the interface to abstract resource view
type ResourceView interface {
model.Component
InitView(ctx context.Context, app *App)
Update()
BuildHeader()
BuildBody()
}
// ResourceViewMap is a map from resource name to resource view
var ResourceViewMap = map[string]ResourceView{
"app": new(ApplicationView),
"cluster": new(ClusterView),
"resource": new(ManagedResourceView),
"ns": new(NamespaceView),
"cns": new(ClusterNamespaceView),
"pod": new(PodView),
}
// CommonResourceView is an abstract of resource view
type CommonResourceView struct {
*component.Table
app *App
}
// ResourceViewer is resource's renderer
type ResourceViewer struct {
viewFunc func(context.Context, *App) model.Component
}
// ResourceMap is a map from resource name to resource's renderer
var ResourceMap = map[string]ResourceViewer{
"app": {
viewFunc: NewApplicationView,
},
"cluster": {
viewFunc: NewClusterView,
},
"resource": {
viewFunc: NewManagedResourceView,
},
"ns": {
viewFunc: NewNamespaceView,
},
"cns": {
viewFunc: NewClusterNamespaceView,
},
"pod": {
viewFunc: NewPodView,
},
}
// NewResourceView return a new resource view
func NewResourceView(app *App) *ResourceView {
v := &ResourceView{
// NewCommonView return a new common view
func NewCommonView(app *App) *CommonResourceView {
v := &CommonResourceView{
Table: component.NewTable(),
app: app,
}
return v
}
// Init the resource view
func (v *ResourceView) Init(list model.ResourceList) {
v.SetSelectable(true, false)
v.buildTable(list)
}
func (v *ResourceView) buildTable(list model.ResourceList) {
// Init the common resource view
func (v *CommonResourceView) Init() {
v.Table.Init()
v.buildTableHeader(list.Header())
v.buildTableBody(list.Body())
v.SetBorder(true)
v.SetTitleColor(config.ResourceTableTitleColor)
v.SetSelectable(true, false)
}
// buildTableHeader render the resource table header
func (v *ResourceView) buildTableHeader(header []string) {
// Name return the name of common view
func (v *CommonResourceView) Name() string {
return "Resource"
}
// BuildHeader render the header of table
func (v *CommonResourceView) BuildHeader(header []string) {
for i := 0; i < len(header); i++ {
c := tview.NewTableCell(header[i]).SetTextColor(config.ResourceTableHeaderColor)
c := tview.NewTableCell(header[i])
c.SetTextColor(config.ResourceTableHeaderColor)
c.SetExpansion(3)
v.SetCell(0, i, c)
}
}
// buildTableBody render the resource table body
func (v *ResourceView) buildTableBody(body [][]string) {
for i := 0; i < len(body); i++ {
for j := 0; j < len(body[i]); j++ {
// BuildBody render the body of table
func (v *CommonResourceView) BuildBody(body [][]string) {
rowNum := len(body)
for i := 0; i < rowNum; i++ {
columnNum := len(body[i])
for j := 0; j < columnNum; j++ {
c := tview.NewTableCell(body[i][j])
c.SetTextColor(config.ResourceTableBodyColor)
c.SetExpansion(3)
@@ -101,8 +97,3 @@ func (v *ResourceView) buildTableBody(body [][]string) {
}
}
}
// Name return the name of view
func (v *ResourceView) Name() string {
return "Resource"
}

View File

@@ -22,7 +22,7 @@ import (
"github.com/stretchr/testify/assert"
)
func TestResourceView(t *testing.T) {
view := NewResourceView(nil)
func TestResourceView_Name(t *testing.T) {
view := NewCommonView(nil)
assert.Equal(t, view.Name(), "Resource")
}