k9s/internal/port/co_portspec.go

168 lines
3.4 KiB
Go

package port
import (
"strconv"
"strings"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
// ContainerPortSpecs represents a container exposed ports.
type ContainerPortSpecs []ContainerPortSpec
func (c ContainerPortSpecs) Dump() string {
ss := make([]string, 0, len(c))
for _, spec := range c {
ss = append(ss, spec.String())
}
return strings.Join(ss, "\n")
}
// InSpecs checks if given port matches a spec.
func (c ContainerPortSpecs) MatchSpec(s string) bool {
for _, spec := range c {
if spec.MatchSpec(s) {
return true
}
}
return false
}
// ToTunnels convert port specs to tunnels.
func (c ContainerPortSpecs) ToTunnels(address string) PortTunnels {
tt := make(PortTunnels, 0, len(c))
for _, spec := range c {
tt = append(tt, spec.ToTunnel(address))
}
return tt
}
// Find finds a matching container port.
func (c ContainerPortSpecs) Find(pf *PFAnn) (ContainerPortSpec, bool) {
for _, spec := range c {
if spec.Match(pf) {
return spec, true
}
}
return ContainerPortSpec{}, false
}
// Match checks if container ports match a pf annotation.
func (c ContainerPortSpecs) Match(pf *PFAnn) bool {
for _, spec := range c {
if spec.Match(pf) {
return true
}
}
return false
}
func (c ContainerPortSpecs) MatchAnnotations(s string) PFAnns {
pfs, err := ParsePFs(s)
if err != nil {
return nil
}
mm := make(PFAnns, 0, len(c))
for _, pf := range pfs {
if pf.Match(c) {
mm = append(mm, pf)
}
}
return mm
}
// FromContainerPorts hydrates from a pod container specification.
func FromContainerPorts(co string, pp []v1.ContainerPort) ContainerPortSpecs {
specs := make(ContainerPortSpecs, 0, len(pp))
for _, p := range pp {
if p.Protocol != v1.ProtocolTCP {
continue
}
specs = append(specs, NewPortSpec(co, p.Name, p.ContainerPort))
}
return specs
}
// ContainerPortSpec represents a container port specification.
type ContainerPortSpec struct {
Container string
PortName string
PortNum string
}
// NewPortSpec returns a new instance.
func NewPortSpec(co, portName string, port int32) ContainerPortSpec {
return ContainerPortSpec{
Container: co,
PortName: portName,
PortNum: strconv.Itoa(int(port)),
}
}
func (c ContainerPortSpec) MatchSpec(s string) bool {
tokens := strings.Split(s, "::")
if len(tokens) < 2 {
return false
}
return tokens[0] == c.Container && tokens[1] == c.PortNum
}
func (c ContainerPortSpec) ToTunnel(address string) PortTunnel {
return PortTunnel{
Address: address,
LocalPort: c.PortNum,
ContainerPort: c.PortNum,
}
}
func (c ContainerPortSpec) Port() intstr.IntOrString {
if c.PortName != "" {
return intstr.Parse(c.PortName)
}
return intstr.Parse(c.PortNum)
}
func (c ContainerPortSpec) ToPFAnn() *PFAnn {
return &PFAnn{
Container: c.Container,
ContainerPort: c.Port(),
LocalPort: c.PortNum,
}
}
// Match checks if the container spec matches an annotation.
func (c ContainerPortSpec) Match(ann *PFAnn) bool {
if c.Container != ann.Container {
return false
}
switch ann.ContainerPort.Type {
case intstr.String:
return c.PortName == ann.ContainerPort.String()
case intstr.Int:
return c.PortNum == ann.ContainerPort.String()
default:
return false
}
}
// String dumps spec to string.
func (c ContainerPortSpec) String() string {
s := c.Container + "::" + c.PortNum
if c.PortName != "" {
s += "(" + c.PortName + ")"
}
return s
}