From f6dcc8f6edd16d395223e5e243ea604e54d2c86f Mon Sep 17 00:00:00 2001 From: HanMengnan <1448189829@qq.com> Date: Thu, 1 Dec 2022 19:47:20 +0800 Subject: [PATCH] Fix: add auto refresh to topology view and fix the refresh bug in resource view Signed-off-by: HanMengnan <1448189829@qq.com> --- .../cli/top/view/application_topology_view.go | 125 +++++++++++++----- references/cli/top/view/application_view.go | 5 +- .../cli/top/view/cluster_namespace_view.go | 5 +- references/cli/top/view/cluster_view.go | 5 +- references/cli/top/view/container_view.go | 5 +- .../cli/top/view/managed_resource_view.go | 5 +- references/cli/top/view/namespace_view.go | 5 +- references/cli/top/view/pod_view.go | 5 +- references/cli/top/view/resource_view.go | 30 +++-- references/cli/top/view/resource_view_test.go | 4 +- 10 files changed, 136 insertions(+), 58 deletions(-) diff --git a/references/cli/top/view/application_topology_view.go b/references/cli/top/view/application_topology_view.go index f3f91d7c8..bc22e3c46 100644 --- a/references/cli/top/view/application_topology_view.go +++ b/references/cli/top/view/application_topology_view.go @@ -38,9 +38,10 @@ type TopologyView struct { actions model.KeyActions ctx context.Context focusTopology bool - cache gcache.Cache + cache gcache.Cache // lru cache with expired time appTopologyInstance *TopologyTree resourceTopologyInstance *TopologyTree + cancelFunc func() // auto refresh cancel function } type cacheView struct { @@ -55,7 +56,10 @@ type TopologyTree struct { const ( numberOfCacheView = 10 - expireTime = 10 + // cache expire time + expireTime = 10 + // request timeout time + topologyReqTimeout = 5 ) var ( @@ -85,49 +89,22 @@ func (v *TopologyView) Init() { v.SetRows(0).SetColumns(-1, -1) v.SetBorder(true) v.SetBorderAttributes(tcell.AttrItalic) - v.SetTitle(title).SetTitleColor(config.ResourceTableTitleColor) + v.SetTitle(title) + v.SetTitleColor(config.ResourceTableTitleColor) v.bindKeys() v.SetInputCapture(v.keyboard) } // Start the topology view func (v *TopologyView) Start() { - appName := v.ctx.Value(&model.CtxKeyAppName).(string) - namespace := v.ctx.Value(&model.CtxKeyNamespace).(string) - key := fmt.Sprintf("%s-%s", appName, namespace) - - value, err := v.cache.Get(key) - if err != nil { - v.resourceTopologyInstance = v.NewResourceTopologyView() - v.appTopologyInstance = v.NewAppTopologyView() - _ = v.cache.Set(key, &cacheView{ - resourceTopologyInstance: v.resourceTopologyInstance, - appTopologyInstance: v.appTopologyInstance, - }) - } else { - view, ok := value.(*cacheView) - if ok { - v.appTopologyInstance = view.appTopologyInstance - v.resourceTopologyInstance = view.resourceTopologyInstance - } else { - v.resourceTopologyInstance = v.NewResourceTopologyView() - v.appTopologyInstance = v.NewAppTopologyView() - _ = v.cache.Set(key, &cacheView{ - resourceTopologyInstance: v.resourceTopologyInstance, - appTopologyInstance: v.appTopologyInstance, - }) - } - } - - v.Grid.AddItem(v.appTopologyInstance, 0, 0, 1, 1, 0, 0, true) - v.Grid.AddItem(v.resourceTopologyInstance, 0, 1, 1, 1, 0, 0, true) - - v.app.SetFocus(v.appTopologyInstance) + v.Update(func() {}) + v.AutoRefresh(v.Update) } // Stop the topology view func (v *TopologyView) Stop() { v.Grid.Clear() + v.cancelFunc() } // Hint return the menu hints of topology view @@ -140,6 +117,49 @@ func (v *TopologyView) Name() string { return "Topology" } +// Update the topology view +func (v *TopologyView) Update(timeoutCancel func()) { + appName := v.ctx.Value(&model.CtxKeyAppName).(string) + namespace := v.ctx.Value(&model.CtxKeyNamespace).(string) + key := fmt.Sprintf("%s-%s", appName, namespace) + + value, err := v.cache.Get(key) + + generateTopology := func() { + v.resourceTopologyInstance = v.NewResourceTopologyView() + v.appTopologyInstance = v.NewAppTopologyView() + // add new topology view to cache + _ = v.cache.Set(key, &cacheView{ + resourceTopologyInstance: v.resourceTopologyInstance, + appTopologyInstance: v.appTopologyInstance, + }) + } + + if err != nil { + generateTopology() + } else { + view, ok := value.(*cacheView) + if ok { + v.appTopologyInstance = view.appTopologyInstance + v.resourceTopologyInstance = view.resourceTopologyInstance + } else { + generateTopology() + } + } + + v.Grid.AddItem(v.appTopologyInstance, 0, 0, 1, 1, 0, 0, true) + v.Grid.AddItem(v.resourceTopologyInstance, 0, 1, 1, 1, 0, 0, true) + + // reset focus + if v.focusTopology { + v.app.SetFocus(v.resourceTopologyInstance) + } else { + v.app.SetFocus(v.appTopologyInstance) + } + // ctx done + timeoutCancel() +} + func (v *TopologyView) keyboard(event *tcell.EventKey) *tcell.EventKey { key := event.Key() if key == tcell.KeyUp || key == tcell.KeyDown { @@ -281,3 +301,40 @@ func buildTopology(node *types.ResourceTreeNode) *tview.TreeNode { return rootNode } + +// Refresh the topology view +func (v *TopologyView) Refresh(clear bool, update func(timeoutCancel func())) { + if clear { + v.Grid.Clear() + } + + updateWithTimeout := func() { + ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*topologyReqTimeout) + defer cancelFunc() + go update(cancelFunc) + + select { + case <-time.After(time.Second * topologyReqTimeout): // timeout + case <-ctx.Done(): // success + } + } + + v.app.QueueUpdateDraw(updateWithTimeout) +} + +// AutoRefresh will refresh the view in every RefreshDelay delay +func (v *TopologyView) AutoRefresh(update func(timeoutCancel func())) { + var ctx context.Context + ctx, v.cancelFunc = context.WithCancel(context.Background()) + go func() { + for { + time.Sleep(RefreshDelay * time.Second) + select { + case <-ctx.Done(): + return + default: + v.Refresh(true, update) + } + } + }() +} diff --git a/references/cli/top/view/application_view.go b/references/cli/top/view/application_view.go index af9aa2a0e..310b4237b 100644 --- a/references/cli/top/view/application_view.go +++ b/references/cli/top/view/application_view.go @@ -49,7 +49,7 @@ func (v *ApplicationView) Init() { // Start the application view func (v *ApplicationView) Start() { v.Clear() - v.Update() + v.Update(func() {}) v.CommonResourceView.AutoRefresh(v.Update) } @@ -78,9 +78,10 @@ func (v *ApplicationView) Refresh(_ *tcell.EventKey) *tcell.EventKey { } // Update refresh the content of body of view -func (v *ApplicationView) Update() { +func (v *ApplicationView) Update(timeoutCancel func()) { v.BuildHeader() v.BuildBody() + timeoutCancel() } // BuildHeader render the header of table diff --git a/references/cli/top/view/cluster_namespace_view.go b/references/cli/top/view/cluster_namespace_view.go index f433ca0d0..02599de2c 100644 --- a/references/cli/top/view/cluster_namespace_view.go +++ b/references/cli/top/view/cluster_namespace_view.go @@ -49,7 +49,7 @@ func (v *ClusterNamespaceView) Init() { // Start the cluster namespace view func (v *ClusterNamespaceView) Start() { v.Clear() - v.Update() + v.Update(func() {}) v.AutoRefresh(v.Update) } @@ -78,9 +78,10 @@ func (v *ClusterNamespaceView) Refresh(_ *tcell.EventKey) *tcell.EventKey { } // Update refresh the content of body of view -func (v *ClusterNamespaceView) Update() { +func (v *ClusterNamespaceView) Update(timeoutCancel func()) { v.BuildHeader() v.BuildBody() + timeoutCancel() } // BuildHeader render the header of table diff --git a/references/cli/top/view/cluster_view.go b/references/cli/top/view/cluster_view.go index f026d267a..0becf82b0 100644 --- a/references/cli/top/view/cluster_view.go +++ b/references/cli/top/view/cluster_view.go @@ -48,7 +48,7 @@ func (v *ClusterView) Name() string { // Start the cluster view func (v *ClusterView) Start() { v.Clear() - v.Update() + v.Update(func() {}) v.CommonResourceView.AutoRefresh(v.Update) } @@ -77,9 +77,10 @@ func (v *ClusterView) Refresh(_ *tcell.EventKey) *tcell.EventKey { } // Update refresh the content of body of view -func (v *ClusterView) Update() { +func (v *ClusterView) Update(timeoutCancel func()) { v.BuildHeader() v.BuildBody() + timeoutCancel() } // BuildHeader render the header of table diff --git a/references/cli/top/view/container_view.go b/references/cli/top/view/container_view.go index b5c092128..ac2bbab13 100644 --- a/references/cli/top/view/container_view.go +++ b/references/cli/top/view/container_view.go @@ -49,7 +49,7 @@ func (v *ContainerView) Name() string { // Start the container view func (v *ContainerView) Start() { v.Clear() - v.Update() + v.Update(func() {}) v.CommonResourceView.AutoRefresh(v.Update) } @@ -78,9 +78,10 @@ func (v *ContainerView) Refresh(_ *tcell.EventKey) *tcell.EventKey { } // Update refresh the content of body of view -func (v *ContainerView) Update() { +func (v *ContainerView) Update(timeoutCancel func()) { v.BuildHeader() v.BuildBody() + timeoutCancel() } // BuildHeader render the header of table diff --git a/references/cli/top/view/managed_resource_view.go b/references/cli/top/view/managed_resource_view.go index e33b22ca2..6fc9601f9 100644 --- a/references/cli/top/view/managed_resource_view.go +++ b/references/cli/top/view/managed_resource_view.go @@ -50,7 +50,7 @@ func (v *ManagedResourceView) Init() { // Start the managed resource view func (v *ManagedResourceView) Start() { v.Clear() - v.Update() + v.Update(func() {}) v.CommonResourceView.AutoRefresh(v.Update) } @@ -92,9 +92,10 @@ func (v *ManagedResourceView) Refresh(_ *tcell.EventKey) *tcell.EventKey { } // Update refresh the content of body of view -func (v *ManagedResourceView) Update() { +func (v *ManagedResourceView) Update(timeoutCancel func()) { v.BuildHeader() v.BuildBody() + timeoutCancel() } // BuildHeader render the header of table diff --git a/references/cli/top/view/namespace_view.go b/references/cli/top/view/namespace_view.go index 62a01f06d..717384abd 100644 --- a/references/cli/top/view/namespace_view.go +++ b/references/cli/top/view/namespace_view.go @@ -49,7 +49,7 @@ func (v *NamespaceView) Name() string { // Start the managed namespace view func (v *NamespaceView) Start() { v.Clear() - v.Update() + v.Update(func() {}) v.CommonResourceView.AutoRefresh(v.Update) } @@ -78,9 +78,10 @@ func (v *NamespaceView) Refresh(_ *tcell.EventKey) *tcell.EventKey { } // Update refresh the content of body of view -func (v *NamespaceView) Update() { +func (v *NamespaceView) Update(timeoutCancel func()) { v.BuildHeader() v.BuildBody() + timeoutCancel() } // BuildHeader render the header of table diff --git a/references/cli/top/view/pod_view.go b/references/cli/top/view/pod_view.go index eaee90848..995371fb8 100644 --- a/references/cli/top/view/pod_view.go +++ b/references/cli/top/view/pod_view.go @@ -42,7 +42,7 @@ func (v *PodView) Name() string { // Start the pod view func (v *PodView) Start() { v.Clear() - v.Update() + v.Update(func() {}) v.AutoRefresh(v.Update) } @@ -78,9 +78,10 @@ func (v *PodView) Refresh(_ *tcell.EventKey) *tcell.EventKey { } // Update refresh the content of body of view -func (v *PodView) Update() { +func (v *PodView) Update(timeoutCancel func()) { v.BuildHeader() v.BuildBody() + timeoutCancel() } // BuildHeader render the header of table diff --git a/references/cli/top/view/resource_view.go b/references/cli/top/view/resource_view.go index 180c1ad78..fd10220bc 100644 --- a/references/cli/top/view/resource_view.go +++ b/references/cli/top/view/resource_view.go @@ -28,15 +28,18 @@ import ( "github.com/oam-dev/kubevela/references/cli/top/model" ) -// RefreshDelay is refresh delay -const RefreshDelay = 10 * time.Second +const ( + // RefreshDelay is refresh delay + RefreshDelay = 10 + resourceReqTimeout = 3 +) // ResourceView is the interface to abstract resource view type ResourceView interface { model.View InitView(ctx context.Context, app *App) Refresh(event *tcell.EventKey) *tcell.EventKey - Update() + Update(timeoutCancel func()) BuildHeader() BuildBody() } @@ -115,25 +118,36 @@ func (v *CommonResourceView) Stop() { } // Refresh the base resource view -func (v *CommonResourceView) Refresh(clear bool, update func()) { +func (v *CommonResourceView) Refresh(clear bool, update func(timeoutCancel func())) { if clear { v.Clear() } - v.app.QueueUpdateDraw(update) + updateWithTimeout := func() { + ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*resourceReqTimeout) + defer cancelFunc() + go update(cancelFunc) + + select { + case <-time.After(time.Second * resourceReqTimeout): // timeout + case <-ctx.Done(): // success + } + } + + v.app.QueueUpdateDraw(updateWithTimeout) } // AutoRefresh will refresh the view in every RefreshDelay delay -func (v *CommonResourceView) AutoRefresh(update func()) { +func (v *CommonResourceView) AutoRefresh(update func(timeoutCancel func())) { var ctx context.Context ctx, v.cancelFunc = context.WithCancel(context.Background()) go func() { for { + time.Sleep(RefreshDelay * time.Second) select { case <-ctx.Done(): return default: - v.Refresh(false, update) - time.Sleep(RefreshDelay) + v.Refresh(true, update) } } }() diff --git a/references/cli/top/view/resource_view_test.go b/references/cli/top/view/resource_view_test.go index ea24fbe86..ef6d730e1 100644 --- a/references/cli/top/view/resource_view_test.go +++ b/references/cli/top/view/resource_view_test.go @@ -39,11 +39,11 @@ func TestResourceView(t *testing.T) { assert.Equal(t, view.GetCell(1, 0).Text, "Name1") assert.Equal(t, view.GetCell(1, 1).Text, "Data1") - view.Refresh(true, func() {}) + view.Refresh(true, func(func()) {}) assert.Equal(t, view.GetCell(0, 0).Text, "") view.BuildHeader([]string{"Name", "Data"}) - view.Refresh(false, func() {}) + view.Refresh(false, func(func()) {}) assert.Equal(t, view.GetCell(0, 0).Text, "Name") view.BuildHeader([]string{"Name", "Data"})