k9s/internal/port/tunnel.go

69 lines
1.5 KiB
Go

// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of K9s
package port
import (
"context"
"fmt"
"log/slog"
"net"
"github.com/derailed/k9s/internal/slogs"
)
// PortTunnels represents a collection of tunnels.
type PortTunnels []PortTunnel
// CheckAvailable checks if all port tunnels are available.
func (t PortTunnels) CheckAvailable(ctx context.Context) error {
for _, pt := range t {
if !IsPortFree(ctx, pt) {
return fmt.Errorf("port %s is not available on host", pt.LocalPort)
}
}
return nil
}
// PortTunnel represents a host tunnel port mapper.
type PortTunnel struct {
Address, Container, LocalPort, ContainerPort string
}
// NewPortTunnel returns a new instance.
func NewPortTunnel(a, co, lp, cp string) PortTunnel {
return PortTunnel{
Address: a,
Container: co,
LocalPort: lp,
ContainerPort: cp,
}
}
// String dumps as string.
func (t PortTunnel) String() string {
return fmt.Sprintf("%s|%s|%s:%s", t.Address, t.Container, t.LocalPort, t.ContainerPort)
}
// PortMap returns a port mapping.
func (t PortTunnel) PortMap() string {
if t.LocalPort == "" {
t.LocalPort = t.ContainerPort
}
return t.LocalPort + ":" + t.ContainerPort
}
// IsPortFree checks if a address/port pair is available on host.
func IsPortFree(ctx context.Context, t PortTunnel) bool {
var ncfg net.ListenConfig
s, err := ncfg.Listen(ctx, "tcp", fmt.Sprintf("%s:%s", t.Address, t.LocalPort))
if err != nil {
slog.Warn("Port is not available", slogs.Port, t.LocalPort, slogs.Address, t.Address)
return false
}
return s.Close() == nil
}