chore(port-forward): support multiple port forwarded
This commit is contained in:
@@ -8,7 +8,7 @@ import (
|
||||
type Service interface {
|
||||
Start(ctx context.Context) (runError <-chan error, err error)
|
||||
Stop() (err error)
|
||||
GetPortForwarded() (port uint16)
|
||||
GetPortsForwarded() (ports []uint16)
|
||||
}
|
||||
|
||||
type Routing interface {
|
||||
|
||||
@@ -150,11 +150,11 @@ func (l *Loop) Stop() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Loop) GetPortForwarded() (port uint16) {
|
||||
func (l *Loop) GetPortsForwarded() (ports []uint16) {
|
||||
if l.service == nil {
|
||||
return 0
|
||||
return nil
|
||||
}
|
||||
return l.service.GetPortForwarded()
|
||||
return l.service.GetPortsForwarded()
|
||||
}
|
||||
|
||||
func ptrTo[T any](value T) *T {
|
||||
|
||||
@@ -3,13 +3,20 @@ package service
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (s *Service) writePortForwardedFile(port uint16) (err error) {
|
||||
func (s *Service) writePortForwardedFile(ports []uint16) (err error) {
|
||||
portStrings := make([]string, len(ports))
|
||||
for i, port := range ports {
|
||||
portStrings[i] = fmt.Sprint(int(port))
|
||||
}
|
||||
fileData := []byte(strings.Join(portStrings, "\n"))
|
||||
|
||||
filepath := s.settings.Filepath
|
||||
s.logger.Info("writing port file " + filepath)
|
||||
const perms = os.FileMode(0644)
|
||||
err = os.WriteFile(filepath, []byte(fmt.Sprint(port)), perms)
|
||||
err = os.WriteFile(filepath, fileData, perms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("writing file: %w", err)
|
||||
}
|
||||
|
||||
22
internal/portforward/service/helpers.go
Normal file
22
internal/portforward/service/helpers.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func portsToString(ports []uint16) (s string) {
|
||||
switch len(ports) {
|
||||
case 0:
|
||||
return "no port forwarded"
|
||||
case 1:
|
||||
return "port forwarded is " + fmt.Sprint(int(ports[0]))
|
||||
default:
|
||||
portStrings := make([]string, len(ports))
|
||||
for i, port := range ports {
|
||||
portStrings[i] = fmt.Sprint(int(port))
|
||||
}
|
||||
return "ports forwarded are " + strings.Join(portStrings[:len(portStrings)-1], ", ") +
|
||||
" and " + portStrings[len(portStrings)-1]
|
||||
}
|
||||
}
|
||||
43
internal/portforward/service/helpers_test.go
Normal file
43
internal/portforward/service/helpers_test.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_portsToString(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := map[string]struct {
|
||||
ports []uint16
|
||||
s string
|
||||
}{
|
||||
"no_port": {
|
||||
s: "no port forwarded",
|
||||
},
|
||||
"one_port": {
|
||||
ports: []uint16{123},
|
||||
s: "port forwarded is 123",
|
||||
},
|
||||
"two_ports": {
|
||||
ports: []uint16{123, 456},
|
||||
s: "ports forwarded are 123 and 456",
|
||||
},
|
||||
"three_ports": {
|
||||
ports: []uint16{123, 456, 789},
|
||||
s: "ports forwarded are 123, 456 and 789",
|
||||
},
|
||||
}
|
||||
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := portsToString(testCase.ports)
|
||||
|
||||
assert.Equal(t, testCase.s, s)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,6 @@ type Logger interface {
|
||||
type PortForwarder interface {
|
||||
Name() string
|
||||
PortForward(ctx context.Context, objects utils.PortForwardObjects) (
|
||||
port uint16, err error)
|
||||
ports []uint16, err error)
|
||||
KeepPortForward(ctx context.Context, objects utils.PortForwardObjects) (err error)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
type Service struct {
|
||||
// State
|
||||
portMutex sync.RWMutex
|
||||
port uint16
|
||||
ports []uint16
|
||||
// Fixed parameters
|
||||
settings Settings
|
||||
puid int
|
||||
@@ -40,8 +40,10 @@ func New(settings Settings, routing Routing, client *http.Client,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) GetPortForwarded() (port uint16) {
|
||||
func (s *Service) GetPortsForwarded() (ports []uint16) {
|
||||
s.portMutex.RLock()
|
||||
defer s.portMutex.RUnlock()
|
||||
return s.port
|
||||
ports = make([]uint16, len(s.ports))
|
||||
copy(ports, s.ports)
|
||||
return ports
|
||||
}
|
||||
|
||||
@@ -31,33 +31,35 @@ func (s *Service) Start(ctx context.Context) (runError <-chan error, err error)
|
||||
Username: s.settings.Username,
|
||||
Password: s.settings.Password,
|
||||
}
|
||||
port, err := s.settings.PortForwarder.PortForward(ctx, obj)
|
||||
ports, err := s.settings.PortForwarder.PortForward(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("port forwarding for the first time: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("port forwarded is " + fmt.Sprint(int(port)))
|
||||
s.logger.Info(portsToString(ports))
|
||||
|
||||
err = s.portAllower.SetAllowedPort(ctx, port, s.settings.Interface)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("allowing port in firewall: %w", err)
|
||||
}
|
||||
|
||||
if s.settings.ListeningPort != 0 {
|
||||
err = s.portAllower.RedirectPort(ctx, s.settings.Interface, port, s.settings.ListeningPort)
|
||||
for _, port := range ports {
|
||||
err = s.portAllower.SetAllowedPort(ctx, port, s.settings.Interface)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("redirecting port in firewall: %w", err)
|
||||
return nil, fmt.Errorf("allowing port in firewall: %w", err)
|
||||
}
|
||||
|
||||
if s.settings.ListeningPort != 0 {
|
||||
err = s.portAllower.RedirectPort(ctx, s.settings.Interface, port, s.settings.ListeningPort)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("redirecting port in firewall: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = s.writePortForwardedFile(port)
|
||||
err = s.writePortForwardedFile(ports)
|
||||
if err != nil {
|
||||
_ = s.cleanup()
|
||||
return nil, fmt.Errorf("writing port file: %w", err)
|
||||
}
|
||||
|
||||
s.portMutex.Lock()
|
||||
s.port = port
|
||||
s.ports = ports
|
||||
s.portMutex.Unlock()
|
||||
|
||||
keepPortCtx, keepPortCancel := context.WithCancel(context.Background())
|
||||
|
||||
@@ -11,7 +11,7 @@ func (s *Service) Stop() (err error) {
|
||||
defer s.startStopMutex.Unlock()
|
||||
|
||||
s.portMutex.RLock()
|
||||
serviceNotRunning := s.port == 0
|
||||
serviceNotRunning := len(s.ports) == 0
|
||||
s.portMutex.RUnlock()
|
||||
if serviceNotRunning {
|
||||
// TODO replace with goservices.ErrAlreadyStopped
|
||||
@@ -30,21 +30,23 @@ func (s *Service) cleanup() (err error) {
|
||||
s.portMutex.Lock()
|
||||
defer s.portMutex.Unlock()
|
||||
|
||||
err = s.portAllower.RemoveAllowedPort(context.Background(), s.port)
|
||||
if err != nil {
|
||||
return fmt.Errorf("blocking previous port in firewall: %w", err)
|
||||
}
|
||||
|
||||
if s.settings.ListeningPort != 0 {
|
||||
ctx := context.Background()
|
||||
const listeningPort = 0 // 0 to clear the redirection
|
||||
err = s.portAllower.RedirectPort(ctx, s.settings.Interface, s.port, listeningPort)
|
||||
for _, port := range s.ports {
|
||||
err = s.portAllower.RemoveAllowedPort(context.Background(), port)
|
||||
if err != nil {
|
||||
return fmt.Errorf("removing previous port redirection in firewall: %w", err)
|
||||
return fmt.Errorf("blocking previous port in firewall: %w", err)
|
||||
}
|
||||
|
||||
if s.settings.ListeningPort != 0 {
|
||||
ctx := context.Background()
|
||||
const listeningPort = 0 // 0 to clear the redirection
|
||||
err = s.portAllower.RedirectPort(ctx, s.settings.Interface, port, listeningPort)
|
||||
if err != nil {
|
||||
return fmt.Errorf("removing previous port redirection in firewall: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.port = 0
|
||||
s.ports = nil
|
||||
|
||||
filepath := s.settings.Filepath
|
||||
s.logger.Info("removing port file " + filepath)
|
||||
|
||||
Reference in New Issue
Block a user