Fix: add auto refresh to topology view and fix the refresh bug in resource view

Signed-off-by: HanMengnan <1448189829@qq.com>
This commit is contained in:
HanMengnan
2022-12-03 11:06:21 +08:00
parent e54abd7a40
commit f6dcc8f6ed
10 changed files with 136 additions and 58 deletions
@@ -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)
}
}
}()
}
+3 -2
View File
@@ -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
@@ -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
+3 -2
View File
@@ -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
+3 -2
View File
@@ -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
@@ -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
+3 -2
View File
@@ -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
+3 -2
View File
@@ -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
+22 -8
View File
@@ -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)
}
}
}()
@@ -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"})