misc bug fixes and cleanup
parent
ee61a1b578
commit
f770348f74
|
|
@ -0,0 +1,30 @@
|
|||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
|
||||
|
||||
# Release v0.5.2
|
||||
|
||||
## Notes
|
||||
|
||||
Thank you to all that contributed with flushing out issues with K9s! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes!
|
||||
|
||||
If you've filed an issue please help me verify and close.
|
||||
|
||||
Thank you so much for your support and awesome suggestions to make K9s better!!
|
||||
|
||||
Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
|
||||
|
||||
---
|
||||
|
||||
## Change Logs
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Resolved Bugs
|
||||
|
||||
+ [Issue #171](https://github.com/derailed/k9s/issues/171)
|
||||
+ [Issue #173](https://github.com/derailed/k9s/issues/173)
|
||||
+ [Issue #174](https://github.com/derailed/k9s/issues/174)
|
||||
|
||||
---
|
||||
|
||||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
|
@ -135,7 +135,11 @@ func (a *APIClient) IsNamespaced(res string) bool {
|
|||
|
||||
// SupportsResource checks for resource supported version against the server.
|
||||
func (a *APIClient) SupportsResource(group string) bool {
|
||||
list, _ := a.DialOrDie().Discovery().ServerPreferredResources()
|
||||
list, err := a.DialOrDie().Discovery().ServerPreferredResources()
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("Unable to dial api server")
|
||||
return false
|
||||
}
|
||||
for _, l := range list {
|
||||
log.Debug().Msgf(">>> Group %s", l.GroupVersion)
|
||||
if l.GroupVersion == group {
|
||||
|
|
|
|||
|
|
@ -115,8 +115,6 @@ func (b *Base) Describe(kind, pa string, flags *genericclioptions.ConfigFlags) (
|
|||
return "", err
|
||||
}
|
||||
|
||||
log.Debug().Msgf("Describer %#v", d)
|
||||
|
||||
return d.Describe(ns, n, describe.DescriberSettings{ShowEvents: true})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ func (r *Container) Fields(ns string) Row {
|
|||
}
|
||||
cs = &c
|
||||
}
|
||||
|
||||
if cs == nil {
|
||||
for _, c := range r.pod.Status.InitContainerStatuses {
|
||||
if c.Name != i.Name {
|
||||
|
|
@ -188,12 +189,17 @@ func (r *Container) Fields(ns string) Row {
|
|||
}
|
||||
}
|
||||
|
||||
ready, state, restarts := "false", MissingValue, "0"
|
||||
if cs != nil {
|
||||
ready, state, restarts = boolToStr(cs.Ready), toState(cs.State), strconv.Itoa(int(cs.RestartCount))
|
||||
}
|
||||
|
||||
return append(ff,
|
||||
i.Name,
|
||||
i.Image,
|
||||
boolToStr(cs.Ready),
|
||||
toState(cs.State),
|
||||
strconv.Itoa(int(cs.RestartCount)),
|
||||
ready,
|
||||
state,
|
||||
restarts,
|
||||
probe(i.LivenessProbe),
|
||||
probe(i.ReadinessProbe),
|
||||
cpu,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/k8s"
|
||||
"github.com/rs/zerolog/log"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/kubernetes/pkg/util/node"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -212,7 +213,7 @@ func (r *Pod) Fields(ns string) Row {
|
|||
return append(ff,
|
||||
i.ObjectMeta.Name,
|
||||
strconv.Itoa(cr)+"/"+strconv.Itoa(len(ss)),
|
||||
r.phase(i.Status),
|
||||
r.phase(i),
|
||||
strconv.Itoa(rc),
|
||||
ToMillicore(r.metrics.CurrentCPU),
|
||||
ToMi(r.metrics.CurrentMEM),
|
||||
|
|
@ -251,21 +252,76 @@ func (r *Pod) statuses(ss []v1.ContainerStatus) (cr, ct, rc int) {
|
|||
return
|
||||
}
|
||||
|
||||
func (*Pod) phase(s v1.PodStatus) string {
|
||||
status := "Pending"
|
||||
for _, cs := range s.ContainerStatuses {
|
||||
switch {
|
||||
case cs.State.Running != nil:
|
||||
status = "Running"
|
||||
case cs.State.Waiting != nil:
|
||||
status = cs.State.Waiting.Reason
|
||||
case cs.State.Terminated != nil:
|
||||
status = "Terminating"
|
||||
if len(cs.State.Terminated.Reason) != 0 {
|
||||
status = cs.State.Terminated.Reason
|
||||
func isSet(s *string) bool {
|
||||
return s != nil && *s != ""
|
||||
}
|
||||
|
||||
func (*Pod) phase(po *v1.Pod) string {
|
||||
status := string(po.Status.Phase)
|
||||
if po.Status.Reason != "" {
|
||||
if po.DeletionTimestamp != nil && po.Status.Reason == node.NodeUnreachablePodReason {
|
||||
return "Unknown"
|
||||
}
|
||||
status = po.Status.Reason
|
||||
}
|
||||
|
||||
var init bool
|
||||
for i, cs := range po.Status.InitContainerStatuses {
|
||||
switch {
|
||||
case cs.State.Terminated != nil:
|
||||
if cs.State.Terminated.ExitCode == 0 {
|
||||
continue
|
||||
}
|
||||
if cs.State.Terminated.Reason != "" {
|
||||
status = "Init:" + cs.State.Terminated.Reason
|
||||
init = true
|
||||
break
|
||||
}
|
||||
|
||||
if cs.State.Terminated.Signal != 0 {
|
||||
status = fmt.Sprintf("Init:Signal:%d", cs.State.Terminated.Signal)
|
||||
} else {
|
||||
status = fmt.Sprintf("Init:ExitCode:%d", cs.State.Terminated.ExitCode)
|
||||
}
|
||||
case cs.State.Waiting != nil && cs.State.Waiting.Reason != "" && cs.State.Waiting.Reason != "PodInitializing":
|
||||
status = "Init:" + cs.State.Waiting.Reason
|
||||
default:
|
||||
status = fmt.Sprintf("Init:%d/%d", i, len(po.Spec.InitContainers))
|
||||
}
|
||||
init = true
|
||||
break
|
||||
}
|
||||
|
||||
if init {
|
||||
return status
|
||||
}
|
||||
|
||||
var running bool
|
||||
for i := len(po.Status.ContainerStatuses) - 1; i >= 0; i-- {
|
||||
cs := po.Status.ContainerStatuses[i]
|
||||
switch {
|
||||
case cs.State.Waiting != nil && cs.State.Waiting.Reason != "":
|
||||
status = cs.State.Waiting.Reason
|
||||
case cs.State.Terminated != nil && cs.State.Terminated.Reason != "":
|
||||
status = cs.State.Terminated.Reason
|
||||
case cs.State.Terminated != nil:
|
||||
if cs.State.Terminated.Signal != 0 {
|
||||
status = fmt.Sprintf("Signal:%d", cs.State.Terminated.Signal)
|
||||
} else {
|
||||
status = fmt.Sprintf("ExitCode:%d", cs.State.Terminated.ExitCode)
|
||||
}
|
||||
case cs.Ready && cs.State.Running != nil:
|
||||
running = true
|
||||
}
|
||||
}
|
||||
|
||||
if status == "Completed" && running {
|
||||
status = "Running"
|
||||
}
|
||||
|
||||
if po.DeletionTimestamp == nil {
|
||||
return status
|
||||
}
|
||||
|
||||
return "Terminated"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ package resource
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestPodStatuses(t *testing.T) {
|
||||
|
|
@ -49,69 +51,132 @@ func TestPodStatuses(t *testing.T) {
|
|||
|
||||
func TestPodPhase(t *testing.T) {
|
||||
uu := []struct {
|
||||
s v1.PodStatus
|
||||
p *v1.Pod
|
||||
e string
|
||||
}{
|
||||
{
|
||||
v1.PodStatus{
|
||||
ContainerStatuses: []v1.ContainerStatus{
|
||||
{
|
||||
Name: "c1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"Running",
|
||||
},
|
||||
{
|
||||
v1.PodStatus{
|
||||
ContainerStatuses: []v1.ContainerStatus{
|
||||
{
|
||||
Name: "c1",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{
|
||||
Reason: "blee",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"blee",
|
||||
},
|
||||
{
|
||||
v1.PodStatus{
|
||||
ContainerStatuses: []v1.ContainerStatus{
|
||||
{
|
||||
Name: "c1",
|
||||
State: v1.ContainerState{
|
||||
Terminated: &v1.ContainerStateTerminated{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"Terminating",
|
||||
},
|
||||
{
|
||||
v1.PodStatus{
|
||||
ContainerStatuses: []v1.ContainerStatus{
|
||||
{
|
||||
Name: "c1",
|
||||
State: v1.ContainerState{
|
||||
Terminated: &v1.ContainerStateTerminated{
|
||||
Reason: "blee",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"blee",
|
||||
},
|
||||
{makePodStatus("p1", v1.PodRunning, ""), "Running"},
|
||||
{makePodStatus("p1", v1.PodRunning, "Evicted"), "Evicted"},
|
||||
{makePodStatus("p1", v1.PodPending, ""), "Pending"},
|
||||
{makePodStatus("p1", v1.PodSucceeded, ""), "Succeeded"},
|
||||
{makePodStatus("p1", v1.PodFailed, ""), "Failed"},
|
||||
{makePodStatus("p1", v1.PodUnknown, ""), "Unknown"},
|
||||
{makePodCoInitTerminated("p1"), "Init:OOMKilled"},
|
||||
{makePodCoInitWaiting("p1", ""), "Init:0/1"},
|
||||
{makePodCoInitWaiting("p1", "Waiting"), "Init:Waiting"},
|
||||
{makePodCoInitWaiting("p1", "PodInitializing"), "Init:0/1"},
|
||||
{makePodCoWaiting("p1", "Waiting"), "Waiting"},
|
||||
{makePodCoWaiting("p1", ""), ""},
|
||||
{makePodCoTerminated("p1", "OOMKilled", 0, true), "Terminated"},
|
||||
{makePodCoTerminated("p1", "OOMKilled", 0, false), "OOMKilled"},
|
||||
{makePodCoTerminated("p1", "", 0, true), "Terminated"},
|
||||
{makePodCoTerminated("p1", "", 0, false), "ExitCode:1"},
|
||||
{makePodCoTerminated("p1", "", 1, true), "Terminated"},
|
||||
{makePodCoTerminated("p1", "", 1, false), "Signal:1"},
|
||||
}
|
||||
|
||||
var p Pod
|
||||
for _, u := range uu {
|
||||
assert.Equal(t, u.e, p.phase(u.s))
|
||||
assert.Equal(t, u.e, p.phase(u.p))
|
||||
}
|
||||
}
|
||||
|
||||
func makePodStatus(n string, phase v1.PodPhase, reason string) *v1.Pod {
|
||||
po := makePod(n)
|
||||
po.Status = v1.PodStatus{
|
||||
Phase: phase,
|
||||
Reason: reason,
|
||||
}
|
||||
|
||||
return po
|
||||
}
|
||||
|
||||
func makePodCoInitTerminated(n string) *v1.Pod {
|
||||
po := makePod(n)
|
||||
|
||||
po.Status.InitContainerStatuses = []v1.ContainerStatus{
|
||||
{
|
||||
State: v1.ContainerState{
|
||||
Terminated: &v1.ContainerStateTerminated{
|
||||
Reason: "OOMKilled",
|
||||
ExitCode: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return po
|
||||
}
|
||||
|
||||
func makePodCoInitWaiting(n, reason string) *v1.Pod {
|
||||
po := makePod(n)
|
||||
|
||||
po.Status.InitContainerStatuses = []v1.ContainerStatus{
|
||||
{
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{
|
||||
Reason: reason,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return po
|
||||
}
|
||||
|
||||
func makePodCoTerminated(n, reason string, signal int32, deleted bool) *v1.Pod {
|
||||
po := makePod(n)
|
||||
|
||||
if deleted {
|
||||
po.DeletionTimestamp = &metav1.Time{time.Now()}
|
||||
}
|
||||
po.Status.ContainerStatuses = []v1.ContainerStatus{
|
||||
{
|
||||
State: v1.ContainerState{
|
||||
Terminated: &v1.ContainerStateTerminated{
|
||||
Reason: reason,
|
||||
Signal: signal,
|
||||
ExitCode: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return po
|
||||
}
|
||||
|
||||
func makePodCoWaiting(n, reason string) *v1.Pod {
|
||||
po := makePod(n)
|
||||
|
||||
po.Status.ContainerStatuses = []v1.ContainerStatus{
|
||||
{
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{
|
||||
Reason: reason,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return po
|
||||
}
|
||||
|
||||
func makePod(n string) *v1.Pod {
|
||||
return &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: n,
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
InitContainers: []v1.Container{
|
||||
{
|
||||
Name: "ic1",
|
||||
},
|
||||
},
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "c1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
"sync"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
|
|
@ -60,6 +61,7 @@ type (
|
|||
cmdBuff *cmdBuff
|
||||
cmdView *cmdView
|
||||
actions keyActions
|
||||
mx sync.Mutex
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -143,6 +145,9 @@ func (a *appView) Run() {
|
|||
}
|
||||
|
||||
func (a *appView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||
a.mx.Lock()
|
||||
defer a.mx.Unlock()
|
||||
|
||||
key := evt.Key()
|
||||
if key == tcell.KeyRune {
|
||||
if a.cmdBuff.isActive() {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
|
|
@ -24,6 +25,7 @@ type detailsView struct {
|
|||
cmdBuff *cmdBuff
|
||||
backFn actionHandler
|
||||
numSelections int
|
||||
mx sync.Mutex
|
||||
}
|
||||
|
||||
func newDetailsView(app *appView, backFn actionHandler) *detailsView {
|
||||
|
|
@ -63,6 +65,9 @@ func (v *detailsView) setCategory(n string) {
|
|||
}
|
||||
|
||||
func (v *detailsView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||
v.mx.Lock()
|
||||
defer v.mx.Unlock()
|
||||
|
||||
key := evt.Key()
|
||||
if key == tcell.KeyRune {
|
||||
if v.cmdBuff.isActive() {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
|
|
@ -28,6 +29,7 @@ type logsView struct {
|
|||
cancelFunc context.CancelFunc
|
||||
autoScroll bool
|
||||
showPrevious bool
|
||||
mx sync.Mutex
|
||||
}
|
||||
|
||||
func newLogsView(pview string, parent loggable) *logsView {
|
||||
|
|
@ -148,6 +150,9 @@ func (v *logsView) load(i int) {
|
|||
}
|
||||
|
||||
func (v *logsView) doLoad(path, co string) error {
|
||||
v.mx.Lock()
|
||||
defer v.mx.Unlock()
|
||||
|
||||
v.stop()
|
||||
|
||||
c := make(chan string)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/derailed/tview"
|
||||
|
|
@ -77,7 +78,7 @@ func newKeyAction(d string, a actionHandler, display bool) keyAction {
|
|||
}
|
||||
|
||||
func newMenuView() *menuView {
|
||||
v := menuView{tview.NewTable()}
|
||||
v := menuView{Table: tview.NewTable()}
|
||||
return &v
|
||||
}
|
||||
|
||||
|
|
@ -106,9 +107,14 @@ func (a keyActions) toHints() hints {
|
|||
// -----------------------------------------------------------------------------
|
||||
type menuView struct {
|
||||
*tview.Table
|
||||
|
||||
mx sync.Mutex
|
||||
}
|
||||
|
||||
func (v *menuView) populateMenu(hh hints) {
|
||||
v.mx.Lock()
|
||||
defer v.mx.Unlock()
|
||||
|
||||
v.Clear()
|
||||
sort.Sort(hh)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ type loggable interface {
|
|||
}
|
||||
|
||||
func newPodView(t string, app *appView, list resource.List) resourceViewer {
|
||||
v := podView{newResourceView(t, app, list).(*resourceView)}
|
||||
v := podView{resourceView: newResourceView(t, app, list).(*resourceView)}
|
||||
{
|
||||
v.extraActionsFn = v.extraActions
|
||||
v.enterFn = v.listContainers
|
||||
|
|
|
|||
|
|
@ -70,11 +70,6 @@ func newResourceView(title string, app *appView, list resource.List) resourceVie
|
|||
details := newDetailsView(app, v.backCmd)
|
||||
v.AddPage("details", details, true, false)
|
||||
|
||||
confirm := tview.NewModal().
|
||||
AddButtons([]string{"Cancel", "OK"}).
|
||||
SetTextColor(tcell.ColorFuchsia)
|
||||
v.AddPage("confirm", confirm, false, false)
|
||||
|
||||
return &v
|
||||
}
|
||||
|
||||
|
|
@ -180,9 +175,7 @@ func (v *resourceView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
}
|
||||
|
||||
sel := v.getSelectedItem()
|
||||
confirm := v.GetPrimitive("confirm").(*tview.Modal)
|
||||
confirm.SetText(fmt.Sprintf("Delete %s %s?", v.list.GetName(), sel))
|
||||
confirm.SetDoneFunc(func(_ int, button string) {
|
||||
v.showModal(fmt.Sprintf("Delete %s %s?", v.list.GetName(), sel), func(_ int, button string) {
|
||||
if button == "OK" {
|
||||
v.getTV().setDeleted()
|
||||
v.app.flash(flashInfo, fmt.Sprintf("Deleting %s %s", v.list.GetName(), sel))
|
||||
|
|
@ -192,13 +185,27 @@ func (v *resourceView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
v.refresh()
|
||||
}
|
||||
}
|
||||
v.switchPage(v.list.GetName())
|
||||
v.dismissModal()
|
||||
})
|
||||
v.SwitchToPage("confirm")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *resourceView) showModal(msg string, done func(int, string)) {
|
||||
confirm := tview.NewModal().
|
||||
AddButtons([]string{"Cancel", "OK"}).
|
||||
SetTextColor(tcell.ColorFuchsia).
|
||||
SetText(msg).
|
||||
SetDoneFunc(done)
|
||||
v.AddPage("confirm", confirm, false, false)
|
||||
v.ShowPage("confirm")
|
||||
}
|
||||
|
||||
func (v *resourceView) dismissModal() {
|
||||
v.RemovePage("confirm")
|
||||
v.switchPage(v.list.GetName())
|
||||
}
|
||||
|
||||
func (v *resourceView) defaultEnter(app *appView, ns, resource, selection string) {
|
||||
sel := v.getSelectedItem()
|
||||
yaml, err := v.list.Resource().Describe(v.title, sel, v.app.flags)
|
||||
|
|
@ -270,6 +277,7 @@ func (v *resourceView) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
i, _ := strconv.Atoi(string(evt.Rune()))
|
||||
ns := v.namespaces[i]
|
||||
v.doSwitchNamespace(ns)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -284,6 +292,7 @@ func (v *resourceView) doSwitchNamespace(ns string) {
|
|||
v.list.SetNamespace(v.selectedNS)
|
||||
}
|
||||
v.update.Unlock()
|
||||
|
||||
v.refresh()
|
||||
v.selectItem(0, 0)
|
||||
v.getTV().resetTitle()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
|
|
@ -85,17 +84,14 @@ func (v *replicaSetView) rollbackCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
return evt
|
||||
}
|
||||
|
||||
confirm := v.GetPrimitive("confirm").(*tview.Modal)
|
||||
confirm.SetText(fmt.Sprintf("Rollback %s %s?", v.list.GetName(), v.selectedItem))
|
||||
confirm.SetDoneFunc(func(_ int, button string) {
|
||||
v.showModal(fmt.Sprintf("Rollback %s %s?", v.list.GetName(), v.selectedItem), func(_ int, button string) {
|
||||
if button == "OK" {
|
||||
v.app.flash(flashInfo, fmt.Sprintf("Rolling back %s %s", v.list.GetName(), v.selectedItem))
|
||||
rollback(v.app, v.selectedItem)
|
||||
v.refresh()
|
||||
}
|
||||
v.switchPage(v.list.GetName())
|
||||
v.dismissModal()
|
||||
})
|
||||
v.SwitchToPage("confirm")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package views
|
|||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -111,6 +112,7 @@ func (v *tableView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
|||
|
||||
if a, ok := v.actions[key]; ok {
|
||||
log.Debug().Msgf(">> TableView handled %s", tcell.KeyNames[key])
|
||||
log.Debug().Msgf("Go Routine %d", runtime.NumGoroutine())
|
||||
return a.action(evt)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue