k9s/internal/view/container.go

181 lines
4.7 KiB
Go

package view
import (
"context"
"errors"
"fmt"
"strings"
"github.com/derailed/k9s/internal/k8s"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/resource"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
"k8s.io/client-go/tools/portforward"
)
const containerTitle = "Containers"
// Container represents a container view.
type Container struct {
ResourceViewer
podPath string
}
// New Container returns a new container view.
func NewContainer(path string, list resource.List) ResourceViewer {
return &Container{
ResourceViewer: NewResource(containerTitle, "containers", list),
podPath: path,
}
}
// Init initializes the viewer.
func (c *Container) Init(ctx context.Context) error {
c.ResourceViewer = NewLogsExtender(c.ResourceViewer, c.selectedContainer)
c.ResourceViewer.SetPath(c.podPath)
c.GetTable().Path = c.podPath
if err := c.ResourceViewer.Init(ctx); err != nil {
return err
}
c.SetEnvFn(c.k9sEnv)
c.GetTable().SetEnterFn(c.viewLogs)
c.GetTable().SetColorerFn(render.Container{}.ColorerFunc())
c.bindKeys()
return nil
}
// Name returns the component name.
func (c *Container) Name() string { return containerTitle }
func (c *Container) bindKeys() {
c.Actions().Delete(tcell.KeyCtrlSpace, ui.KeySpace)
c.Actions().Add(ui.KeyActions{
tcell.KeyCtrlF: ui.NewKeyAction("PortForward", c.portFwdCmd, true),
ui.KeyS: ui.NewKeyAction("Shell", c.shellCmd, true),
ui.KeyShiftC: ui.NewKeyAction("Sort CPU", c.GetTable().SortColCmd(6, false), false),
ui.KeyShiftM: ui.NewKeyAction("Sort MEM", c.GetTable().SortColCmd(7, false), false),
ui.KeyShiftX: ui.NewKeyAction("Sort CPU%", c.GetTable().SortColCmd(8, false), false),
ui.KeyShiftZ: ui.NewKeyAction("Sort MEM%", c.GetTable().SortColCmd(9, false), false),
})
}
func (c *Container) k9sEnv() K9sEnv {
env := defaultK9sEnv(c.App(), c.GetTable().GetSelectedItem(), c.GetTable().GetRow())
ns, n := namespaced(c.podPath)
env["POD"] = n
env["NAMESPACE"] = ns
return env
}
func (c *Container) selectedContainer() string {
log.Debug().Msgf("Container SELECTED %s", c.GetTable().GetSelectedItem())
tokens := strings.Split(c.GetTable().GetSelectedItem(), "/")
return tokens[0]
}
func (c *Container) viewLogs(_ *App, ns, res, path string) {
log.Debug().Msgf(">>>>>>>> ViewLOgs %q -- %q -- %q", ns, res, path)
status := c.GetTable().GetSelectedCell(3)
if status != "Running" && status != "Completed" {
c.App().Flash().Err(errors.New("No logs available"))
return
}
c.ResourceViewer.(*LogsExtender).showLogs(c.podPath, false)
}
// Handlers...
func (c *Container) shellCmd(evt *tcell.EventKey) *tcell.EventKey {
sel := c.GetTable().GetSelectedItem()
if sel == "" {
return evt
}
c.Stop()
defer c.Start()
shellIn(c.App(), c.podPath, sel)
return nil
}
func (c *Container) portFwdCmd(evt *tcell.EventKey) *tcell.EventKey {
sel := c.GetTable().GetSelectedItem()
if sel == "" {
return evt
}
if _, ok := c.App().forwarders[fwFQN(c.podPath, sel)]; ok {
c.App().Flash().Err(fmt.Errorf("A PortForward already exist on container %s", c.podPath))
return nil
}
state := c.GetTable().GetSelectedCell(3)
if state != "Running" {
c.App().Flash().Err(fmt.Errorf("Container %s is not running?", sel))
return nil
}
portC := c.GetTable().GetSelectedCell(11)
ports := strings.Split(portC, ",")
if len(ports) == 0 {
c.App().Flash().Err(errors.New("Container exposes no ports"))
return nil
}
var port string
for _, p := range ports {
log.Debug().Msgf("Checking port %q", p)
if !isTCPPort(p) {
continue
}
port = strings.TrimSpace(p)
break
}
if port == "" {
c.App().Flash().Warn("No valid TCP port found on this container. User will specify...")
port = "MY_TCP_PORT!"
}
dialog.ShowPortForward(c.App().Content.Pages, port, c.portForward)
return nil
}
func (c *Container) portForward(lport, cport string) {
co := c.GetTable().GetSelectedCell(0)
pf := k8s.NewPortForward(c.App().Conn(), &log.Logger)
ports := []string{lport + ":" + cport}
fw, err := pf.Start(c.podPath, co, ports)
if err != nil {
c.App().Flash().Err(err)
return
}
log.Debug().Msgf(">>> Starting port forward %q %v", c.podPath, ports)
go c.runForward(pf, fw)
}
func (c *Container) runForward(pf *k8s.PortForward, f *portforward.PortForwarder) {
c.App().QueueUpdateDraw(func() {
c.App().forwarders[pf.FQN()] = pf
c.App().Flash().Infof("PortForward activated %s:%s", pf.Path(), pf.Ports()[0])
dialog.DismissPortForward(c.App().Content.Pages)
})
pf.SetActive(true)
if err := f.ForwardPorts(); err != nil {
c.App().Flash().Err(err)
return
}
c.App().QueueUpdateDraw(func() {
delete(c.App().forwarders, pf.FQN())
pf.SetActive(false)
})
}