updates
parent
947af513ff
commit
b723bd6a65
|
|
@ -79,5 +79,6 @@ func (p *Pod) Containers(ns, n string, includeInit bool) ([]string, error) {
|
|||
|
||||
// Logs fetch container logs for a given pod and container.
|
||||
func (p *Pod) Logs(ns, n string, opts *v1.PodLogOptions) *restclient.Request {
|
||||
log.Debug().Msgf("Log Options %#v", *opts.TailLines)
|
||||
return p.DialOrDie().CoreV1().Pods(ns).GetLogs(n, opts)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,6 +85,11 @@ func (p *PortForward) Stop() {
|
|||
close(p.stopChan)
|
||||
}
|
||||
|
||||
// FQN returns the portforward unique id.
|
||||
func (p *PortForward) FQN() string {
|
||||
return p.path + ":" + p.container
|
||||
}
|
||||
|
||||
// Start initiates a port forward session for a given pod and ports.
|
||||
func (p *PortForward) Start(path, co string, ports []string) (*portforward.PortForwarder, error) {
|
||||
p.path, p.container, p.ports, p.age = path, co, ports, time.Now()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
|
|
@ -211,12 +210,17 @@ func readLogs(ctx context.Context, stream io.ReadCloser, c chan<- string, opts L
|
|||
|
||||
head := opts.NormalizeName()
|
||||
scanner := bufio.NewScanner(stream)
|
||||
// count := 0
|
||||
for scanner.Scan() {
|
||||
txt := scanner.Text()
|
||||
// log.Debug().Msgf("Pushing %d: %s", count, txt)
|
||||
// count++
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case c <- head + strings.TrimSpace(scanner.Text()):
|
||||
case c <- head + txt:
|
||||
default:
|
||||
// Ensures we get back to scanning
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package views
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/gdamore/tcell"
|
||||
|
|
@ -95,7 +94,7 @@ func (v *aliasView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
func (v *aliasView) runCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
r, _ := v.GetSelection()
|
||||
if r > 0 {
|
||||
v.app.gotoResource(strings.TrimSpace(v.GetCell(r, 0).Text), true)
|
||||
v.app.gotoResource(trimCell(v.tableView, r, 0), true)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -55,25 +55,25 @@ type (
|
|||
cancel context.CancelFunc
|
||||
informer *watch.Informer
|
||||
stopCh chan struct{}
|
||||
forwarders []forwarder
|
||||
forwarders map[string]forwarder
|
||||
}
|
||||
)
|
||||
|
||||
// NewApp returns a K9s app instance.
|
||||
func NewApp(cfg *config.Config) *appView {
|
||||
v := appView{
|
||||
shellView: newShellView(),
|
||||
cmdBuff: newCmdBuff(':'),
|
||||
shellView: newShellView(),
|
||||
cmdBuff: newCmdBuff(':'),
|
||||
forwarders: make(map[string]forwarder),
|
||||
}
|
||||
|
||||
v.config = cfg
|
||||
v.initBench(cfg.K9s.CurrentCluster)
|
||||
v.refreshStyles()
|
||||
v.command = newCommand(&v)
|
||||
|
||||
v.views["menu"] = newMenuView(v.styles)
|
||||
v.views["logo"] = newLogoView(v.styles)
|
||||
v.views["cmd"] = newCmdView(v.styles, '🐶')
|
||||
v.command = newCommand(&v)
|
||||
v.views["flash"] = newFlashView(&v, "Initializing...")
|
||||
v.views["crumbs"] = newCrumbsView(v.styles)
|
||||
v.views["clusterInfo"] = newClusterInfoView(&v, k8s.NewMetricsServer(cfg.GetConnection()))
|
||||
|
|
@ -173,11 +173,11 @@ func (a *appView) BailOut() {
|
|||
}
|
||||
|
||||
func (a *appView) stopForwarders() {
|
||||
for _, f := range a.forwarders {
|
||||
for k, f := range a.forwarders {
|
||||
log.Debug().Msgf("Deleting forwarder %s", f.Path())
|
||||
f.Stop()
|
||||
delete(a.forwarders, k)
|
||||
}
|
||||
a.forwarders = []forwarder{}
|
||||
}
|
||||
|
||||
func (a *appView) conn() k8s.Connection {
|
||||
|
|
|
|||
|
|
@ -109,14 +109,13 @@ func (v *benchView) getTitle() string {
|
|||
}
|
||||
|
||||
func (v *benchView) selChanged(r, c int) {
|
||||
log.Info().Msgf("Bench sel changed %d:%d", r, c)
|
||||
tv := v.getTV()
|
||||
if r == 0 || tv.GetCell(r, 0) == nil {
|
||||
v.selectedItem = ""
|
||||
return
|
||||
}
|
||||
v.selectedRow = r
|
||||
v.selectedItem = strings.TrimSpace(tv.GetCell(r, 7).Text)
|
||||
v.selectedItem = trimCell(tv, r, 7)
|
||||
}
|
||||
|
||||
func (v *benchView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||
|
|
@ -157,7 +156,7 @@ func (v *benchView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
}
|
||||
|
||||
dir := filepath.Join(K9sBenchDir, v.app.config.K9s.CurrentCluster)
|
||||
showModal(v.Pages, fmt.Sprintf("Deleting `%s are you sure?", sel), "table", func() {
|
||||
showModal(v.Pages, fmt.Sprintf("Delete benchmark `%s?", sel), "table", func() {
|
||||
if err := os.Remove(filepath.Join(dir, sel)); err != nil {
|
||||
v.app.flash().errf("Unable to delete file %s", err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package views
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
|
|
@ -55,7 +56,7 @@ func (v *containerView) selectedContainer() string {
|
|||
}
|
||||
|
||||
func (v *containerView) viewLogs(app *appView, _, res, sel string) {
|
||||
status := strings.TrimSpace(v.masterPage().GetCell(v.selectedRow, 3).Text)
|
||||
status := trimCell(v.masterPage(), v.selectedRow, 3)
|
||||
if status == "Running" || status == "Completed" {
|
||||
v.showLogs(false)
|
||||
return
|
||||
|
|
@ -81,8 +82,19 @@ func (v *containerView) portFwdCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
return evt
|
||||
}
|
||||
|
||||
portC := v.masterPage().GetCell(v.selectedRow, 10)
|
||||
ports := strings.Split(portC.Text, ",")
|
||||
if _, ok := v.app.forwarders[fwFQN(*v.path, v.selectedItem)]; ok {
|
||||
v.app.flash().err(fmt.Errorf("A PortForward already exist on container %s", *v.path))
|
||||
return nil
|
||||
}
|
||||
|
||||
state := trimCell(v.masterPage(), v.selectedRow, 3)
|
||||
if state != "Running" {
|
||||
v.app.flash().err(fmt.Errorf("Container %s is not running?", v.selectedItem))
|
||||
return nil
|
||||
}
|
||||
|
||||
portC := trimCell(v.masterPage(), v.selectedRow, 10)
|
||||
ports := strings.Split(portC, ",")
|
||||
if len(ports) == 0 {
|
||||
v.app.flash().err(errors.New("Container exposes no ports"))
|
||||
return nil
|
||||
|
|
@ -123,20 +135,18 @@ func (v *containerView) portForward(lport, cport string) {
|
|||
|
||||
func (v *containerView) runForward(pf *k8s.PortForward, f *portforward.PortForwarder) {
|
||||
v.app.QueueUpdateDraw(func() {
|
||||
v.app.forwarders = append(v.app.forwarders, pf)
|
||||
v.app.forwarders[pf.FQN()] = pf
|
||||
v.app.flash().infof("PortForward activated %s:%s", pf.Path(), pf.Ports()[0])
|
||||
v.dismissModal()
|
||||
})
|
||||
|
||||
pf.SetActive(true)
|
||||
if err := f.ForwardPorts(); err == nil {
|
||||
if err := f.ForwardPorts(); err != nil {
|
||||
v.app.flash().err(err)
|
||||
return
|
||||
}
|
||||
v.app.QueueUpdateDraw(func() {
|
||||
if len(v.app.forwarders) > 0 {
|
||||
v.app.forwarders = v.app.forwarders[:len(v.app.forwarders)-1]
|
||||
}
|
||||
delete(v.app.forwarders, pf.FQN())
|
||||
pf.SetActive(false)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
|
|
@ -110,7 +109,7 @@ func (v *dumpView) selChanged(r, c int) {
|
|||
return
|
||||
}
|
||||
v.selectedRow = r
|
||||
v.selectedItem = strings.TrimSpace(tv.GetCell(r, 0).Text)
|
||||
v.selectedItem = trimCell(tv, r, 0)
|
||||
}
|
||||
|
||||
func (v *dumpView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||
|
|
@ -118,7 +117,6 @@ func (v *dumpView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcel
|
|||
tv := v.getTV()
|
||||
tv.sortCol.index, tv.sortCol.asc = tv.nameColIndex()+col, asc
|
||||
tv.refresh()
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -147,7 +145,7 @@ func (v *dumpView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
}
|
||||
|
||||
dir := filepath.Join(config.K9sDumpDir, v.app.config.K9s.CurrentCluster)
|
||||
showModal(v.Pages, fmt.Sprintf("Deleting `%s are you sure?", sel), "table", func() {
|
||||
showModal(v.Pages, fmt.Sprintf("Delete screen dump `%s?", sel), "table", func() {
|
||||
if err := os.Remove(filepath.Join(dir, sel)); err != nil {
|
||||
v.app.flash().errf("Unable to delete file %s", err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -151,13 +151,13 @@ func (v *forwardView) benchCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
Path: "/",
|
||||
},
|
||||
}
|
||||
co := strings.TrimSpace(tv.GetCell(r, 2).Text)
|
||||
co := trimCell(tv, r, 2)
|
||||
if b, ok := v.app.bench.Benchmarks.Containers[containerID(sel, co)]; ok {
|
||||
cfg = b
|
||||
}
|
||||
cfg.Name = sel
|
||||
|
||||
base := strings.TrimSpace(tv.GetCell(r, 4).Text)
|
||||
base := trimCell(tv, r, 4)
|
||||
var err error
|
||||
if v.bench, err = newBenchmark(base, cfg); err != nil {
|
||||
v.app.flash().errf("Bench failed %v", err)
|
||||
|
|
@ -196,8 +196,7 @@ func (v *forwardView) getSelectedItem() string {
|
|||
if r == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fqn(strings.TrimSpace(tv.GetCell(r, 0).Text), strings.TrimSpace(tv.GetCell(r, 1).Text))
|
||||
return fwFQN(fqn(trimCell(tv, r, 0), trimCell(tv, r, 1)), trimCell(tv, r, 2))
|
||||
}
|
||||
|
||||
func (v *forwardView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
|
|
@ -206,27 +205,21 @@ func (v *forwardView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
tv.cmdBuff.reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
sel := v.getSelectedItem()
|
||||
if sel == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
showModal(v.Pages, fmt.Sprintf("Deleting `%s are you sure?", sel), "table", func() {
|
||||
index := -1
|
||||
for i, f := range v.app.forwarders {
|
||||
if sel == f.Path() {
|
||||
index = i
|
||||
}
|
||||
}
|
||||
if index == -1 {
|
||||
showModal(v.Pages, fmt.Sprintf("Delete PortForward `%s?", sel), "table", func() {
|
||||
fw, ok := v.app.forwarders[sel]
|
||||
if !ok {
|
||||
log.Debug().Msgf("Unable to find forwarder %s", sel)
|
||||
return
|
||||
}
|
||||
v.app.forwarders[index].Stop()
|
||||
if index == 0 && len(v.app.forwarders) == 1 {
|
||||
v.app.forwarders = []forwarder{}
|
||||
} else {
|
||||
v.app.forwarders = append(v.app.forwarders[:index], v.app.forwarders[index+1:]...)
|
||||
}
|
||||
fw.Stop()
|
||||
delete(v.app.forwarders, sel)
|
||||
|
||||
log.Debug().Msgf("PortForwards after delete: %#v", v.app.forwarders)
|
||||
v.getTV().update(v.hydrate())
|
||||
v.app.flash().infof("PortForward %s deleted!", sel)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ const (
|
|||
minusSign = "↓"
|
||||
)
|
||||
|
||||
// FwFQN returns a fully qualified ns/name:container id.
|
||||
func fwFQN(po, co string) string {
|
||||
return po + ":" + co
|
||||
}
|
||||
|
||||
func isTCPPort(p string) bool {
|
||||
return !strings.Contains(p, "UDP")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import (
|
|||
type (
|
||||
logFrame struct {
|
||||
*tview.Flex
|
||||
|
||||
app *appView
|
||||
actions keyActions
|
||||
backFn actionHandler
|
||||
|
|
@ -25,6 +26,7 @@ type (
|
|||
|
||||
logView struct {
|
||||
*logFrame
|
||||
|
||||
logs *detailsView
|
||||
status *statusView
|
||||
ansiWriter io.Writer
|
||||
|
|
@ -119,12 +121,14 @@ func (v *logView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
|||
|
||||
func (v *logView) logLine(line string) {
|
||||
fmt.Fprintln(v.ansiWriter, tview.Escape(line))
|
||||
log.Debug().Msgf("LOG LINES %d", v.logs.GetLineCount())
|
||||
}
|
||||
|
||||
func (v *logView) flush(index int, buff []string) {
|
||||
if index == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
v.logLine(strings.Join(buff[:index], "\n"))
|
||||
if atomic.LoadInt32(&v.autoScroll) == 1 {
|
||||
v.app.QueueUpdateDraw(func() {
|
||||
|
|
|
|||
|
|
@ -12,11 +12,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
maxBuff1 int64 = 200
|
||||
refreshRate = 200 * time.Millisecond
|
||||
maxCleanse = 100
|
||||
logBuffSize = 100
|
||||
flushTimeout = 200 * time.Millisecond
|
||||
logBuffSize = 100
|
||||
flushTimeout = 200 * time.Millisecond
|
||||
|
||||
logCoFmt = " Logs([fg:bg:]%s:[hilite:bg:b]%s[-:bg:-]) "
|
||||
logFmt = " Logs([fg:bg:]%s) "
|
||||
|
|
@ -103,10 +100,11 @@ func (v *logsView) doLoad(path, co string, prevLogs bool) error {
|
|||
l.setTitle(path, co)
|
||||
|
||||
c := make(chan string, 10)
|
||||
go updateLogs(c, l)
|
||||
go updateLogs(c, l, logBuffSize)
|
||||
|
||||
res, ok := v.parent.getList().Resource().(resource.Tailable)
|
||||
if !ok {
|
||||
close(c)
|
||||
return fmt.Errorf("Resource %T is not tailable", v.parent.getList().Resource())
|
||||
}
|
||||
|
||||
|
|
@ -132,19 +130,18 @@ func (v *logsView) logOpts(path, co string, prevLogs bool) resource.LogOptions {
|
|||
Lines: int64(v.app.config.K9s.LogRequestSize),
|
||||
Previous: prevLogs,
|
||||
}
|
||||
|
||||
}
|
||||
func updateLogs(c <-chan string, l *logView) {
|
||||
buff, index := make([]string, logBuffSize), 0
|
||||
|
||||
func updateLogs(c <-chan string, l *logView, buffSize int) {
|
||||
buff, index := make([]string, buffSize), 0
|
||||
for {
|
||||
select {
|
||||
case line, ok := <-c:
|
||||
if !ok {
|
||||
l.flush(index, buff)
|
||||
index = 0
|
||||
return
|
||||
}
|
||||
if index < logBuffSize {
|
||||
if index < buffSize {
|
||||
buff[index] = line
|
||||
index++
|
||||
continue
|
||||
|
|
@ -152,6 +149,7 @@ func updateLogs(c <-chan string, l *logView) {
|
|||
l.flush(index, buff)
|
||||
index = 0
|
||||
buff[index] = line
|
||||
index++
|
||||
case <-time.After(flushTimeout):
|
||||
l.flush(index, buff)
|
||||
index = 0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package views
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUpdateLogs(t *testing.T) {
|
||||
v := newLogView("test", NewApp(config.NewConfig(ks{})), nil)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
c := make(chan string, 10)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
updateLogs(c, v, 10)
|
||||
}()
|
||||
|
||||
for i := 0; i < 500; i++ {
|
||||
c <- fmt.Sprintf("log %d", i)
|
||||
}
|
||||
close(c)
|
||||
wg.Wait()
|
||||
|
||||
assert.Equal(t, 500, v.logs.GetLineCount())
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package views
|
|||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/derailed/tview"
|
||||
|
|
@ -107,12 +106,12 @@ func (v *masterDetail) selectItem(r, c int) {
|
|||
return
|
||||
}
|
||||
|
||||
col0 := cleanCell(t, r, 0)
|
||||
col0 := trimCell(t, r, 0)
|
||||
switch v.currentNS {
|
||||
case resource.NotNamespaced:
|
||||
v.selectedItem = col0
|
||||
case resource.AllNamespace, resource.AllNamespaces:
|
||||
v.selectedItem = path.Join(col0, cleanCell(t, r, 1))
|
||||
v.selectedItem = path.Join(col0, trimCell(t, r, 1))
|
||||
default:
|
||||
v.selectedItem = path.Join(v.currentNS, col0)
|
||||
}
|
||||
|
|
@ -126,7 +125,3 @@ func (v *masterDetail) defaultActions() {
|
|||
v.extraActionsFn(v.actions)
|
||||
}
|
||||
}
|
||||
|
||||
func cleanCell(v *tableView, r, c int) string {
|
||||
return strings.TrimSpace(v.GetCell(r, c).Text)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
|
|
@ -152,12 +151,7 @@ func (v *podView) viewLogs(prev bool) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
r := v.selectedRow
|
||||
col := 2
|
||||
if v.list.AllNamespaces() {
|
||||
col = 3
|
||||
}
|
||||
status := strings.TrimSpace(v.masterPage().GetCell(r, col).Text)
|
||||
status := trimCellRelative(v.masterPage(), v.selectedRow, 2)
|
||||
if status == "Running" || status == "Completed" {
|
||||
v.showLogs(v.selectedItem, "", v, prev)
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ func (v *resourceView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
msg := fmt.Sprintf("Delete %s %s?", v.list.GetName(), sel)
|
||||
showDeleteDialog(v.Pages, msg, func(cascade, force bool) {
|
||||
v.masterPage().setDeleted()
|
||||
v.app.flash().infof("Deleting %s %s", v.list.GetName(), sel)
|
||||
v.app.flash().infof("Delete resource %s %s", v.list.GetName(), sel)
|
||||
if err := v.list.Resource().Delete(sel, cascade, force); err != nil {
|
||||
v.app.flash().errf("Delete failed with %s", err)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
|
|
@ -107,7 +106,7 @@ func (v *subjectView) selChanged(r, _ int) {
|
|||
v.selectedItem = ""
|
||||
return
|
||||
}
|
||||
v.selectedItem = strings.TrimSpace(v.GetCell(r, 0).Text)
|
||||
v.selectedItem = trimCell(v.tableView, r, 0)
|
||||
}
|
||||
|
||||
func (v *subjectView) SetSubject(s string) {
|
||||
|
|
|
|||
|
|
@ -102,19 +102,8 @@ func (v *svcView) benchStopCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
return nil
|
||||
}
|
||||
|
||||
func trimCell(tv *tableView, row, col int) (string, error) {
|
||||
c := tv.GetCell(row, tv.nameColIndex()+col)
|
||||
if c == nil {
|
||||
return "", fmt.Errorf("No cell at location [%d:%d]", row, col)
|
||||
}
|
||||
return strings.TrimSpace(c.Text), nil
|
||||
}
|
||||
|
||||
func (v *svcView) checkSvc(row int) error {
|
||||
svcType, err := trimCell(v.masterPage(), row, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
svcType := trimCellRelative(v.masterPage(), row, 1)
|
||||
if svcType != "NodePort" && svcType != "LoadBalancer" {
|
||||
return errors.New("You must select a reachable service")
|
||||
}
|
||||
|
|
@ -122,10 +111,7 @@ func (v *svcView) checkSvc(row int) error {
|
|||
}
|
||||
|
||||
func (v *svcView) getExternalPort(row int) (string, error) {
|
||||
ports, err := trimCell(v.masterPage(), row, 5)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ports := trimCellRelative(v.masterPage(), row, 5)
|
||||
|
||||
pp := strings.Split(ports, " ")
|
||||
if len(pp) == 0 {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -33,6 +34,19 @@ var (
|
|||
|
||||
type cleanseFn func(string) string
|
||||
|
||||
func trimCellRelative(tv *tableView, row, col int) string {
|
||||
return trimCell(tv, row, tv.nameColIndex()+col)
|
||||
}
|
||||
|
||||
func trimCell(tv *tableView, row, col int) string {
|
||||
c := tv.GetCell(row, col)
|
||||
if c == nil {
|
||||
log.Error().Err(fmt.Errorf("No cell at location [%d:%d]", row, col)).Msg("Trim cell failed!")
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(c.Text)
|
||||
}
|
||||
|
||||
func saveTable(cluster, name string, data resource.TableData) (string, error) {
|
||||
dir := filepath.Join(config.K9sDumpDir, cluster)
|
||||
if err := ensureDir(dir); err != nil {
|
||||
|
|
|
|||
Loading…
Reference in New Issue