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.
|
// SupportsResource checks for resource supported version against the server.
|
||||||
func (a *APIClient) SupportsResource(group string) bool {
|
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 {
|
for _, l := range list {
|
||||||
log.Debug().Msgf(">>> Group %s", l.GroupVersion)
|
log.Debug().Msgf(">>> Group %s", l.GroupVersion)
|
||||||
if l.GroupVersion == group {
|
if l.GroupVersion == group {
|
||||||
|
|
|
||||||
|
|
@ -115,8 +115,6 @@ func (b *Base) Describe(kind, pa string, flags *genericclioptions.ConfigFlags) (
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Msgf("Describer %#v", d)
|
|
||||||
|
|
||||||
return d.Describe(ns, n, describe.DescriberSettings{ShowEvents: true})
|
return d.Describe(ns, n, describe.DescriberSettings{ShowEvents: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,7 @@ func (r *Container) Fields(ns string) Row {
|
||||||
}
|
}
|
||||||
cs = &c
|
cs = &c
|
||||||
}
|
}
|
||||||
|
|
||||||
if cs == nil {
|
if cs == nil {
|
||||||
for _, c := range r.pod.Status.InitContainerStatuses {
|
for _, c := range r.pod.Status.InitContainerStatuses {
|
||||||
if c.Name != i.Name {
|
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,
|
return append(ff,
|
||||||
i.Name,
|
i.Name,
|
||||||
i.Image,
|
i.Image,
|
||||||
boolToStr(cs.Ready),
|
ready,
|
||||||
toState(cs.State),
|
state,
|
||||||
strconv.Itoa(int(cs.RestartCount)),
|
restarts,
|
||||||
probe(i.LivenessProbe),
|
probe(i.LivenessProbe),
|
||||||
probe(i.ReadinessProbe),
|
probe(i.ReadinessProbe),
|
||||||
cpu,
|
cpu,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/k8s"
|
"github.com/derailed/k9s/internal/k8s"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/util/node"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -212,7 +213,7 @@ func (r *Pod) Fields(ns string) Row {
|
||||||
return append(ff,
|
return append(ff,
|
||||||
i.ObjectMeta.Name,
|
i.ObjectMeta.Name,
|
||||||
strconv.Itoa(cr)+"/"+strconv.Itoa(len(ss)),
|
strconv.Itoa(cr)+"/"+strconv.Itoa(len(ss)),
|
||||||
r.phase(i.Status),
|
r.phase(i),
|
||||||
strconv.Itoa(rc),
|
strconv.Itoa(rc),
|
||||||
ToMillicore(r.metrics.CurrentCPU),
|
ToMillicore(r.metrics.CurrentCPU),
|
||||||
ToMi(r.metrics.CurrentMEM),
|
ToMi(r.metrics.CurrentMEM),
|
||||||
|
|
@ -251,21 +252,76 @@ func (r *Pod) statuses(ss []v1.ContainerStatus) (cr, ct, rc int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Pod) phase(s v1.PodStatus) string {
|
func isSet(s *string) bool {
|
||||||
status := "Pending"
|
return s != nil && *s != ""
|
||||||
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 (*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 status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return "Terminated"
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@ package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPodStatuses(t *testing.T) {
|
func TestPodStatuses(t *testing.T) {
|
||||||
|
|
@ -49,69 +51,132 @@ func TestPodStatuses(t *testing.T) {
|
||||||
|
|
||||||
func TestPodPhase(t *testing.T) {
|
func TestPodPhase(t *testing.T) {
|
||||||
uu := []struct {
|
uu := []struct {
|
||||||
s v1.PodStatus
|
p *v1.Pod
|
||||||
e string
|
e string
|
||||||
}{
|
}{
|
||||||
{
|
{makePodStatus("p1", v1.PodRunning, ""), "Running"},
|
||||||
v1.PodStatus{
|
{makePodStatus("p1", v1.PodRunning, "Evicted"), "Evicted"},
|
||||||
ContainerStatuses: []v1.ContainerStatus{
|
{makePodStatus("p1", v1.PodPending, ""), "Pending"},
|
||||||
{
|
{makePodStatus("p1", v1.PodSucceeded, ""), "Succeeded"},
|
||||||
Name: "c1",
|
{makePodStatus("p1", v1.PodFailed, ""), "Failed"},
|
||||||
State: v1.ContainerState{
|
{makePodStatus("p1", v1.PodUnknown, ""), "Unknown"},
|
||||||
Running: &v1.ContainerStateRunning{},
|
{makePodCoInitTerminated("p1"), "Init:OOMKilled"},
|
||||||
},
|
{makePodCoInitWaiting("p1", ""), "Init:0/1"},
|
||||||
},
|
{makePodCoInitWaiting("p1", "Waiting"), "Init:Waiting"},
|
||||||
},
|
{makePodCoInitWaiting("p1", "PodInitializing"), "Init:0/1"},
|
||||||
},
|
{makePodCoWaiting("p1", "Waiting"), "Waiting"},
|
||||||
"Running",
|
{makePodCoWaiting("p1", ""), ""},
|
||||||
},
|
{makePodCoTerminated("p1", "OOMKilled", 0, true), "Terminated"},
|
||||||
{
|
{makePodCoTerminated("p1", "OOMKilled", 0, false), "OOMKilled"},
|
||||||
v1.PodStatus{
|
{makePodCoTerminated("p1", "", 0, true), "Terminated"},
|
||||||
ContainerStatuses: []v1.ContainerStatus{
|
{makePodCoTerminated("p1", "", 0, false), "ExitCode:1"},
|
||||||
{
|
{makePodCoTerminated("p1", "", 1, true), "Terminated"},
|
||||||
Name: "c1",
|
{makePodCoTerminated("p1", "", 1, false), "Signal:1"},
|
||||||
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",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var p Pod
|
var p Pod
|
||||||
for _, u := range uu {
|
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"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/k9s/internal/k8s"
|
"github.com/derailed/k9s/internal/k8s"
|
||||||
|
|
@ -60,6 +61,7 @@ type (
|
||||||
cmdBuff *cmdBuff
|
cmdBuff *cmdBuff
|
||||||
cmdView *cmdView
|
cmdView *cmdView
|
||||||
actions keyActions
|
actions keyActions
|
||||||
|
mx sync.Mutex
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -143,6 +145,9 @@ func (a *appView) Run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *appView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
func (a *appView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
a.mx.Lock()
|
||||||
|
defer a.mx.Unlock()
|
||||||
|
|
||||||
key := evt.Key()
|
key := evt.Key()
|
||||||
if key == tcell.KeyRune {
|
if key == tcell.KeyRune {
|
||||||
if a.cmdBuff.isActive() {
|
if a.cmdBuff.isActive() {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
|
|
@ -24,6 +25,7 @@ type detailsView struct {
|
||||||
cmdBuff *cmdBuff
|
cmdBuff *cmdBuff
|
||||||
backFn actionHandler
|
backFn actionHandler
|
||||||
numSelections int
|
numSelections int
|
||||||
|
mx sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDetailsView(app *appView, backFn actionHandler) *detailsView {
|
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 {
|
func (v *detailsView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
v.mx.Lock()
|
||||||
|
defer v.mx.Unlock()
|
||||||
|
|
||||||
key := evt.Key()
|
key := evt.Key()
|
||||||
if key == tcell.KeyRune {
|
if key == tcell.KeyRune {
|
||||||
if v.cmdBuff.isActive() {
|
if v.cmdBuff.isActive() {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/resource"
|
"github.com/derailed/k9s/internal/resource"
|
||||||
|
|
@ -28,6 +29,7 @@ type logsView struct {
|
||||||
cancelFunc context.CancelFunc
|
cancelFunc context.CancelFunc
|
||||||
autoScroll bool
|
autoScroll bool
|
||||||
showPrevious bool
|
showPrevious bool
|
||||||
|
mx sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLogsView(pview string, parent loggable) *logsView {
|
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 {
|
func (v *logsView) doLoad(path, co string) error {
|
||||||
|
v.mx.Lock()
|
||||||
|
defer v.mx.Unlock()
|
||||||
|
|
||||||
v.stop()
|
v.stop()
|
||||||
|
|
||||||
c := make(chan string)
|
c := make(chan string)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/resource"
|
"github.com/derailed/k9s/internal/resource"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
|
|
@ -77,7 +78,7 @@ func newKeyAction(d string, a actionHandler, display bool) keyAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMenuView() *menuView {
|
func newMenuView() *menuView {
|
||||||
v := menuView{tview.NewTable()}
|
v := menuView{Table: tview.NewTable()}
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,9 +107,14 @@ func (a keyActions) toHints() hints {
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
type menuView struct {
|
type menuView struct {
|
||||||
*tview.Table
|
*tview.Table
|
||||||
|
|
||||||
|
mx sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *menuView) populateMenu(hh hints) {
|
func (v *menuView) populateMenu(hh hints) {
|
||||||
|
v.mx.Lock()
|
||||||
|
defer v.mx.Unlock()
|
||||||
|
|
||||||
v.Clear()
|
v.Clear()
|
||||||
sort.Sort(hh)
|
sort.Sort(hh)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ type loggable interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPodView(t string, app *appView, list resource.List) resourceViewer {
|
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.extraActionsFn = v.extraActions
|
||||||
v.enterFn = v.listContainers
|
v.enterFn = v.listContainers
|
||||||
|
|
|
||||||
|
|
@ -70,11 +70,6 @@ func newResourceView(title string, app *appView, list resource.List) resourceVie
|
||||||
details := newDetailsView(app, v.backCmd)
|
details := newDetailsView(app, v.backCmd)
|
||||||
v.AddPage("details", details, true, false)
|
v.AddPage("details", details, true, false)
|
||||||
|
|
||||||
confirm := tview.NewModal().
|
|
||||||
AddButtons([]string{"Cancel", "OK"}).
|
|
||||||
SetTextColor(tcell.ColorFuchsia)
|
|
||||||
v.AddPage("confirm", confirm, false, false)
|
|
||||||
|
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,9 +175,7 @@ func (v *resourceView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
sel := v.getSelectedItem()
|
sel := v.getSelectedItem()
|
||||||
confirm := v.GetPrimitive("confirm").(*tview.Modal)
|
v.showModal(fmt.Sprintf("Delete %s %s?", v.list.GetName(), sel), func(_ int, button string) {
|
||||||
confirm.SetText(fmt.Sprintf("Delete %s %s?", v.list.GetName(), sel))
|
|
||||||
confirm.SetDoneFunc(func(_ int, button string) {
|
|
||||||
if button == "OK" {
|
if button == "OK" {
|
||||||
v.getTV().setDeleted()
|
v.getTV().setDeleted()
|
||||||
v.app.flash(flashInfo, fmt.Sprintf("Deleting %s %s", v.list.GetName(), sel))
|
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.refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
v.switchPage(v.list.GetName())
|
v.dismissModal()
|
||||||
})
|
})
|
||||||
v.SwitchToPage("confirm")
|
|
||||||
|
|
||||||
return nil
|
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) {
|
func (v *resourceView) defaultEnter(app *appView, ns, resource, selection string) {
|
||||||
sel := v.getSelectedItem()
|
sel := v.getSelectedItem()
|
||||||
yaml, err := v.list.Resource().Describe(v.title, sel, v.app.flags)
|
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()))
|
i, _ := strconv.Atoi(string(evt.Rune()))
|
||||||
ns := v.namespaces[i]
|
ns := v.namespaces[i]
|
||||||
v.doSwitchNamespace(ns)
|
v.doSwitchNamespace(ns)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -284,6 +292,7 @@ func (v *resourceView) doSwitchNamespace(ns string) {
|
||||||
v.list.SetNamespace(v.selectedNS)
|
v.list.SetNamespace(v.selectedNS)
|
||||||
}
|
}
|
||||||
v.update.Unlock()
|
v.update.Unlock()
|
||||||
|
|
||||||
v.refresh()
|
v.refresh()
|
||||||
v.selectItem(0, 0)
|
v.selectItem(0, 0)
|
||||||
v.getTV().resetTitle()
|
v.getTV().resetTitle()
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import (
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/k8s"
|
"github.com/derailed/k9s/internal/k8s"
|
||||||
"github.com/derailed/k9s/internal/resource"
|
"github.com/derailed/k9s/internal/resource"
|
||||||
"github.com/derailed/tview"
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
|
@ -85,17 +84,14 @@ func (v *replicaSetView) rollbackCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
confirm := v.GetPrimitive("confirm").(*tview.Modal)
|
v.showModal(fmt.Sprintf("Rollback %s %s?", v.list.GetName(), v.selectedItem), func(_ int, button string) {
|
||||||
confirm.SetText(fmt.Sprintf("Rollback %s %s?", v.list.GetName(), v.selectedItem))
|
|
||||||
confirm.SetDoneFunc(func(_ int, button string) {
|
|
||||||
if button == "OK" {
|
if button == "OK" {
|
||||||
v.app.flash(flashInfo, fmt.Sprintf("Rolling back %s %s", v.list.GetName(), v.selectedItem))
|
v.app.flash(flashInfo, fmt.Sprintf("Rolling back %s %s", v.list.GetName(), v.selectedItem))
|
||||||
rollback(v.app, v.selectedItem)
|
rollback(v.app, v.selectedItem)
|
||||||
v.refresh()
|
v.refresh()
|
||||||
}
|
}
|
||||||
v.switchPage(v.list.GetName())
|
v.dismissModal()
|
||||||
})
|
})
|
||||||
v.SwitchToPage("confirm")
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package views
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -111,6 +112,7 @@ func (v *tableView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
||||||
if a, ok := v.actions[key]; ok {
|
if a, ok := v.actions[key]; ok {
|
||||||
log.Debug().Msgf(">> TableView handled %s", tcell.KeyNames[key])
|
log.Debug().Msgf(">> TableView handled %s", tcell.KeyNames[key])
|
||||||
|
log.Debug().Msgf("Go Routine %d", runtime.NumGoroutine())
|
||||||
return a.action(evt)
|
return a.action(evt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue