Merge branch 'master' of github.com-derailed:derailed/k9s
commit
0059a57abc
|
|
@ -182,7 +182,7 @@ func (c *Container) portFwdCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
if !ok {
|
||||
return nil
|
||||
}
|
||||
ShowPortForwards(c, c.GetTable().Path, ports, startFwdCB)
|
||||
ShowPortForwards(c, c.GetTable().Path, ports, "", startFwdCB)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -193,8 +193,8 @@ func (c *Container) isForwardable(path string) ([]string, bool) {
|
|||
return nil, false
|
||||
}
|
||||
|
||||
cc := po.Spec.Containers
|
||||
var co *v1.Container
|
||||
cc := po.Spec.Containers
|
||||
for i := range cc {
|
||||
if cc[i].Name == path {
|
||||
co = &cc[i]
|
||||
|
|
@ -230,6 +230,14 @@ func (c *Container) isForwardable(path string) ([]string, bool) {
|
|||
}
|
||||
|
||||
pp := make([]string, 0, len(ports))
|
||||
container, port, ok := parsePFAnn(po.Annotations[AnnDefaultPF])
|
||||
if ok && container == path {
|
||||
if index := indexOfPort(ports, port); index != -1 {
|
||||
pp = append(pp, path+"/"+port)
|
||||
ports = append(ports[:index], ports[index+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range ports {
|
||||
if !isTCPPort(p) {
|
||||
continue
|
||||
|
|
@ -243,3 +251,16 @@ func (c *Container) isForwardable(path string) ([]string, bool) {
|
|||
|
||||
return pp, true
|
||||
}
|
||||
|
||||
func indexOfPort(pp []string, port string) int {
|
||||
for i, p := range pp {
|
||||
tokens := strings.Split(p, ":")
|
||||
if len(tokens) == 2 {
|
||||
if tokens[0] == port || tokens[1] == port {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,15 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func parsePFAnn(s string) (string, string, bool) {
|
||||
tokens := strings.Split(s, ":")
|
||||
if len(tokens) != 2 {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
return tokens[0], tokens[1], true
|
||||
}
|
||||
|
||||
func k8sEnv(c *client.Config) Env {
|
||||
ctx, err := c.CurrentContextName()
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,41 @@ func init() {
|
|||
zerolog.SetGlobalLevel(zerolog.Disabled)
|
||||
}
|
||||
|
||||
func TestParsePFAnn(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
ann, co, port string
|
||||
ok bool
|
||||
}{
|
||||
"named-port": {
|
||||
ann: "fred:blee",
|
||||
co: "fred",
|
||||
port: "blee",
|
||||
ok: true,
|
||||
},
|
||||
"port-num": {
|
||||
ann: "fred:1234",
|
||||
co: "fred",
|
||||
port: "1234",
|
||||
ok: true,
|
||||
},
|
||||
"toast": {
|
||||
ann: "zorg",
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
co, port, ok := parsePFAnn(u.ann)
|
||||
if u.ok {
|
||||
assert.Equal(t, u.co, co)
|
||||
assert.Equal(t, u.port, port)
|
||||
assert.Equal(t, u.ok, ok)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractApp(t *testing.T) {
|
||||
app := NewApp(config.NewConfig(nil))
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package view
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
|
|
@ -17,7 +18,7 @@ const portForwardKey = "portforward"
|
|||
type PortForwardCB func(v ResourceViewer, path, co string, mapper []client.PortTunnel)
|
||||
|
||||
// ShowPortForwards pops a port forwarding configuration dialog.
|
||||
func ShowPortForwards(v ResourceViewer, path string, ports []string, okFn PortForwardCB) {
|
||||
func ShowPortForwards(v ResourceViewer, path string, ports []string, ann string, okFn PortForwardCB) {
|
||||
styles := v.App().Styles.Dialog()
|
||||
|
||||
f := tview.NewForm()
|
||||
|
|
@ -34,15 +35,35 @@ func ShowPortForwards(v ResourceViewer, path string, ports []string, okFn PortFo
|
|||
var p1, p2 string
|
||||
if len(ports) > 0 {
|
||||
p1, p2 = ports[0], extractPort(ports[0])
|
||||
if len(ann) != 0 {
|
||||
container, port, ok := parsePFAnn(ann)
|
||||
if ok {
|
||||
for _, p := range ports {
|
||||
co, po, portNum := parsePort(p)
|
||||
if co == container && port == po || port == portNum {
|
||||
p1, p2 = p, extractPort(p)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f.AddInputField("Container Port:", p1, 30, nil, func(p string) {
|
||||
fieldLen := int(math.Max(30, float64(len(p1))))
|
||||
f.AddInputField("Container Port:", p1, fieldLen, nil, func(p string) {
|
||||
p1 = p
|
||||
})
|
||||
f.AddInputField("Local Port:", p2, 30, nil, func(p string) {
|
||||
field := f.GetFormItemByLabel("Container Port:").(*tview.InputField)
|
||||
if field.GetText() == "" {
|
||||
field.SetPlaceholder("Enter a container name/port")
|
||||
}
|
||||
f.AddInputField("Local Port:", p2, fieldLen, nil, func(p string) {
|
||||
p2 = p
|
||||
})
|
||||
f.AddInputField("Address:", address, 30, nil, func(h string) {
|
||||
field = f.GetFormItemByLabel("Local Port:").(*tview.InputField)
|
||||
if field.GetText() == "" {
|
||||
field.SetPlaceholder("Enter a local port")
|
||||
}
|
||||
f.AddInputField("Address:", address, fieldLen, nil, func(h string) {
|
||||
address = h
|
||||
})
|
||||
for i := 0; i < 3; i++ {
|
||||
|
|
@ -84,12 +105,12 @@ func ShowPortForwards(v ResourceViewer, path string, ports []string, okFn PortFo
|
|||
b.SetLabelColorActivated(styles.ButtonFocusFgColor.Color())
|
||||
}
|
||||
|
||||
modal := tview.NewModalForm(fmt.Sprintf("<PortForward on %s>", path), f)
|
||||
|
||||
if len(ports) != 0 {
|
||||
modal.SetText("Exposed Ports: " + strings.Join(ports, ","))
|
||||
modal := tview.NewModalForm("<PortForward>", f)
|
||||
msg := path
|
||||
if len(ports) > 1 {
|
||||
msg += "\n\nExposed Ports:\n" + strings.Join(ports, "\n")
|
||||
}
|
||||
|
||||
modal.SetText(msg)
|
||||
modal.SetTextColor(styles.FgColor.Color())
|
||||
modal.SetBackgroundColor(styles.BgColor.Color())
|
||||
modal.SetDoneFunc(func(_ int, b string) {
|
||||
|
|
@ -110,6 +131,16 @@ func DismissPortForwards(v ResourceViewer, p *ui.Pages) {
|
|||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func parsePort(p string) (string, string, string) {
|
||||
rx := regexp.MustCompile(`\A([\w|-]+)/?([\w|-]+)?:?(\d+)?(╱UDP)?\z`)
|
||||
mm := rx.FindStringSubmatch(p)
|
||||
if len(mm) != 5 {
|
||||
return "", "", ""
|
||||
}
|
||||
|
||||
return mm[1], mm[2], mm[3]
|
||||
}
|
||||
|
||||
func extractPort(p string) string {
|
||||
rx := regexp.MustCompile(`\A([\w|-]+)/?([\w|-]+)?:?(\d+)?(╱UDP)?\z`)
|
||||
mm := rx.FindStringSubmatch(p)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ import (
|
|||
"k8s.io/client-go/tools/portforward"
|
||||
)
|
||||
|
||||
const AnnDefaultPF = "k9s.imhotep.io/default-portforward-container"
|
||||
|
||||
// PortForwardExtender adds port-forward extensions.
|
||||
type PortForwardExtender struct {
|
||||
ResourceViewer
|
||||
|
|
@ -130,7 +132,7 @@ func startFwdCB(v ResourceViewer, path, co string, tt []client.PortTunnel) {
|
|||
}
|
||||
|
||||
func showFwdDialog(v ResourceViewer, path string, cb PortForwardCB) error {
|
||||
mm, err := fetchPodPorts(v.App().factory, path)
|
||||
mm, coPort, err := fetchPodPorts(v.App().factory, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -143,22 +145,22 @@ func showFwdDialog(v ResourceViewer, path string, cb PortForwardCB) error {
|
|||
ports = append(ports, client.FQN(co, p.Name)+":"+strconv.Itoa(int(p.ContainerPort)))
|
||||
}
|
||||
}
|
||||
ShowPortForwards(v, path, ports, cb)
|
||||
ShowPortForwards(v, path, ports, coPort, cb)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func fetchPodPorts(f *watch.Factory, path string) (map[string][]v1.ContainerPort, error) {
|
||||
func fetchPodPorts(f *watch.Factory, path string) (map[string][]v1.ContainerPort, string, error) {
|
||||
log.Debug().Msgf("Fetching ports on pod %q", path)
|
||||
o, err := f.Get("v1/pods", path, true, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var pod v1.Pod
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
pp := make(map[string][]v1.ContainerPort)
|
||||
|
|
@ -166,5 +168,5 @@ func fetchPodPorts(f *watch.Factory, path string) (map[string][]v1.ContainerPort
|
|||
pp[co.Name] = co.Ports
|
||||
}
|
||||
|
||||
return pp, nil
|
||||
return pp, pod.Annotations[AnnDefaultPF], nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue