Fernand Galiana 2021-10-02 13:00:05 -06:00 committed by GitHub
parent 994a6dba65
commit a637d494bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 119 additions and 21 deletions

2
go.mod
View File

@ -157,7 +157,7 @@ require (
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 // indirect
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
google.golang.org/appengine v1.6.7 // indirect

4
go.sum
View File

@ -97,8 +97,8 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:H
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/adrg/xdg v0.3.0 h1:BO+k4wFj0IoTolBF1Apn8oZrX3LQrEbBA8+/9vyW9J4=
github.com/adrg/xdg v0.3.0/go.mod h1:7I2hH/IT30IsupOpKZ5ue7/qNi3CoKzD6tL3HwpaRMQ=
github.com/adrg/xdg v0.3.4 h1:0BivHfQ0LSGQrFTaEZ0hyQLm/HAidci7m+1cT6wKKdA=
github.com/adrg/xdg v0.3.4/go.mod h1:61xAR2VZcggl2St4O9ohF5qCKe08+JDmE4VNzPFQvOQ=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=

View File

@ -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
}

View File

@ -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 {

View File

@ -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))

View File

@ -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)

View File

@ -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
}