Skip to content

Commit

Permalink
Support for field "interface" (#1136)
Browse files Browse the repository at this point in the history
* Improve port error messages

Signed-off-by: Pablo Chico de Guzman <pchico83@gmail.com>

* Support for "interface"

Signed-off-by: Pablo Chico de Guzman <pchico83@gmail.com>
  • Loading branch information
pchico83 committed Oct 28, 2020
1 parent bbdd416 commit 1dfa22e
Show file tree
Hide file tree
Showing 15 changed files with 91 additions and 105 deletions.
2 changes: 1 addition & 1 deletion cmd/exec.go
Expand Up @@ -110,7 +110,7 @@ func executeExec(ctx context.Context, dev *model.Dev, args []string) error {
if dev.RemoteModeEnabled() {
log.Infof("executing remote command over SSH")
dev.LoadRemote(ssh.GetPublicKey())
return ssh.Exec(ctx, dev.RemotePort, true, os.Stdin, os.Stdout, os.Stderr, wrapped)
return ssh.Exec(ctx, dev.Interface, dev.RemotePort, true, os.Stdin, os.Stdout, os.Stderr, wrapped)
}

return exec.Exec(ctx, client, cfg, dev.Namespace, p.Name, dev.Container, true, os.Stdin, os.Stdout, os.Stderr, wrapped)
Expand Down
12 changes: 6 additions & 6 deletions cmd/up/up.go
Expand Up @@ -54,7 +54,7 @@ import (
const ReconnectingMessage = "Trying to reconnect to your cluster. File synchronization will automatically resume when the connection improves."

var (
localClusters = []string{"127.", "172.", "192.", "169.", "localhost", "::1", "fe80::", "fc00::"}
localClusters = []string{"127.", "172.", "192.", "169.", model.Localhost, "::1", "fe80::", "fc00::"}
)

//Up starts a development container
Expand Down Expand Up @@ -588,7 +588,7 @@ func (up *upContext) forwards(ctx context.Context) error {
}

log.Infof("starting port forwards")
up.Forwarder = forward.NewPortForwardManager(ctx, up.RestConfig, up.Client)
up.Forwarder = forward.NewPortForwardManager(ctx, up.Dev.Interface, up.RestConfig, up.Client)

for _, f := range up.Dev.Forward {
if err := up.Forwarder.Add(f); err != nil {
Expand All @@ -609,12 +609,12 @@ func (up *upContext) forwards(ctx context.Context) error {

func (up *upContext) sshForwards(ctx context.Context) error {
log.Infof("starting SSH port forwards")
f := forward.NewPortForwardManager(ctx, up.RestConfig, up.Client)
f := forward.NewPortForwardManager(ctx, up.Dev.Interface, up.RestConfig, up.Client)
if err := f.Add(model.Forward{Local: up.Dev.RemotePort, Remote: up.Dev.SSHServerPort}); err != nil {
return err
}

up.Forwarder = ssh.NewForwardManager(ctx, fmt.Sprintf(":%d", up.Dev.RemotePort), "localhost", "0.0.0.0", f)
up.Forwarder = ssh.NewForwardManager(ctx, fmt.Sprintf(":%d", up.Dev.RemotePort), up.Dev.Interface, "0.0.0.0", f)

if err := up.Forwarder.Add(model.Forward{Local: up.Sy.RemotePort, Remote: syncthing.ClusterPort}); err != nil {
return err
Expand All @@ -636,7 +636,7 @@ func (up *upContext) sshForwards(ctx context.Context) error {
}
}

if err := ssh.AddEntry(up.Dev.Name, up.Dev.RemotePort); err != nil {
if err := ssh.AddEntry(up.Dev.Name, up.Dev.Interface, up.Dev.RemotePort); err != nil {
log.Infof("failed to add entry to your SSH config file: %s", err)
return fmt.Errorf("failed to add entry to your SSH config file")
}
Expand Down Expand Up @@ -843,7 +843,7 @@ func (up *upContext) runCommand(ctx context.Context) error {
up.updateStateFile(ready)

if up.Dev.RemoteModeEnabled() {
return ssh.Exec(ctx, up.Dev.RemotePort, true, os.Stdin, os.Stdout, os.Stderr, up.Dev.Command.Values)
return ssh.Exec(ctx, up.Dev.Interface, up.Dev.RemotePort, true, os.Stdin, os.Stdout, os.Stderr, up.Dev.Command.Values)
}

return exec.Exec(
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/build/image.go
Expand Up @@ -35,7 +35,7 @@ func GetRepoNameWithoutTag(name string) string {
return name[:i]
}
i = strings.IndexRune(name, '/')
if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != model.Localhost) {
domain, remainder = "", name
} else {
domain, remainder = name[:i], name[i+1:]
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/login/login.go
Expand Up @@ -80,7 +80,7 @@ func StartWithBrowser(ctx context.Context, url string) (*Handler, error) {
return nil, fmt.Errorf("couldn't generate a random token, please try again")
}

port, err := model.GetAvailablePort()
port, err := model.GetAvailablePort(model.Localhost)

if err != nil {
log.Infof("couldn't access the network: %s", err)
Expand Down
32 changes: 16 additions & 16 deletions pkg/k8s/forward/manager.go
Expand Up @@ -19,7 +19,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
"os"
"runtime"
"time"

"github.com/okteto/okteto/pkg/k8s/pods"
Expand All @@ -39,6 +39,7 @@ const devName = "okteto-development"
// PortForwardManager keeps a list of all the active port forwards
type PortForwardManager struct {
stopped bool
iface string
ports map[int]model.Forward
services map[string]struct{}
activeDev *active
Expand Down Expand Up @@ -78,9 +79,10 @@ func (a *active) error() error {
}

// NewPortForwardManager initializes a new instance
func NewPortForwardManager(ctx context.Context, restConfig *rest.Config, c kubernetes.Interface) *PortForwardManager {
func NewPortForwardManager(ctx context.Context, iface string, restConfig *rest.Config, c kubernetes.Interface) *PortForwardManager {
return &PortForwardManager{
ctx: ctx,
iface: iface,
ports: make(map[int]model.Forward),
services: make(map[string]struct{}),
restConfig: restConfig,
Expand All @@ -94,8 +96,17 @@ func (p *PortForwardManager) Add(f model.Forward) error {
return fmt.Errorf("port %d is listed multiple times, please check your configuration", f.Local)
}

if !model.IsPortAvailable(f.Local) {
return fmt.Errorf("port %d is already in use in your local machine, please check your configuration", f.Local)
if !model.IsPortAvailable(p.iface, f.Local) {
if f.Local <= 1024 {
os := runtime.GOOS
switch os {
case "darwin":
return fmt.Errorf("local port %d is privileged. Define 'interface: 0.0.0.0' in your okteto manifest and try again", f.Local)
case "linux":
return fmt.Errorf("local port %d is privileged. Try running \"sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/okteto\" and try again", f.Local)
}
}
return fmt.Errorf("local port %d is already in-use in your local machine", f.Local)
}

p.ports[f.Local] = f
Expand Down Expand Up @@ -170,7 +181,6 @@ func (p *PortForwardManager) buildForwarderToDevPod(namespace, pod string) (*act
}

func (p *PortForwardManager) buildForwarder(name, namespace, pod string, ports []string) (*active, *portforward.PortForwarder, error) {
addresses := getListenAddresses()
dialer, err := p.buildDialer(namespace, pod)
if err != nil {
return nil, nil, err
Expand All @@ -184,7 +194,7 @@ func (p *PortForwardManager) buildForwarder(name, namespace, pod string, ports [

pf, err := portforward.NewOnAddresses(
dialer,
addresses,
[]string{p.iface},
ports,
a.stopChan,
a.readyChan,
Expand Down Expand Up @@ -275,13 +285,3 @@ func (p *PortForwardManager) forwardService(ctx context.Context, namespace, serv
<-t.C
}
}

func getListenAddresses() []string {
addresses := []string{"localhost"}
extraAddress := os.Getenv("OKTETO_ADDRESS")
if len(extraAddress) > 0 {
addresses = append(addresses, extraAddress)
}

return addresses
}
35 changes: 2 additions & 33 deletions pkg/k8s/forward/manager_test.go
Expand Up @@ -15,7 +15,6 @@ package forward

import (
"context"
"os"
"reflect"
"sort"
"testing"
Expand All @@ -25,7 +24,7 @@ import (

func TestAdd(t *testing.T) {

pf := NewPortForwardManager(context.Background(), nil, nil)
pf := NewPortForwardManager(context.Background(), model.Localhost, nil, nil)
if err := pf.Add(model.Forward{Local: 10100, Remote: 1010}); err != nil {
t.Fatal(err)
}
Expand All @@ -52,7 +51,7 @@ func TestAdd(t *testing.T) {
}

func TestStop(t *testing.T) {
pf := NewPortForwardManager(context.Background(), nil, nil)
pf := NewPortForwardManager(context.Background(), model.Localhost, nil, nil)
pf.activeDev = &active{
readyChan: make(chan struct{}, 1),
stopChan: make(chan struct{}, 1),
Expand Down Expand Up @@ -147,36 +146,6 @@ func Test_active_closeReady(t *testing.T) {
}
}

func Test_getListenAddresses(t *testing.T) {
tests := []struct {
name string
want []string
extra string
}{
{
name: "default",
want: []string{"localhost"},
},
{
name: "from-env",
want: []string{"localhost", "0.0.0.0"},
extra: "0.0.0.0",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
os.Unsetenv("OKTETO_ADDRESS")
if len(tt.extra) > 0 {
os.Setenv("OKTETO_ADDRESS", tt.extra)
}

if got := getListenAddresses(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("getListenAddresses() = %v, want %v", got, tt.want)
}
})
}
}

func Test_getServicePorts(t *testing.T) {
tests := []struct {
name string
Expand Down
8 changes: 7 additions & 1 deletion pkg/model/dev.go
Expand Up @@ -36,6 +36,8 @@ import (
)

const (
//Localhost localhost
Localhost = "localhost"
oktetoSSHServerPortVariable = "OKTETO_REMOTE_PORT"
oktetoDefaultSSHServerPort = 2222
//OktetoDefaultPVSize default volume size
Expand Down Expand Up @@ -131,6 +133,7 @@ type Dev struct {
parentSyncFolder string `json:"-" yaml:"-"`
Forward []Forward `json:"forward,omitempty" yaml:"forward,omitempty"`
Reverse []Reverse `json:"reverse,omitempty" yaml:"reverse,omitempty"`
Interface string `json:"interface,omitempty" yaml:"interface,omitempty"`
Resources ResourceRequirements `json:"resources,omitempty" yaml:"resources,omitempty"`
Services []*Dev `json:"services,omitempty" yaml:"services,omitempty"`
PersistentVolumeInfo *PersistentVolumeInfo `json:"persistentVolume,omitempty" yaml:"persistentVolume,omitempty"`
Expand Down Expand Up @@ -438,6 +441,9 @@ func (dev *Dev) setDefaults() error {
if dev.Annotations == nil {
dev.Annotations = map[string]string{}
}
if dev.Interface == "" {
dev.Interface = Localhost
}
if dev.SSHServerPort == 0 {
dev.SSHServerPort = oktetoDefaultSSHServerPort
}
Expand Down Expand Up @@ -576,7 +582,7 @@ func validateSecrets(secrets []Secret) error {
//LoadRemote configures remote execution
func (dev *Dev) LoadRemote(pubKeyPath string) {
if dev.RemotePort == 0 {
p, err := GetAvailablePort()
p, err := GetAvailablePort(dev.Interface)
if err != nil {
log.Infof("failed to get random port for SSH connection: %s", err)
p = 2222
Expand Down
8 changes: 4 additions & 4 deletions pkg/model/port.go
Expand Up @@ -21,8 +21,8 @@ import (
)

// GetAvailablePort returns a random port that's available
func GetAvailablePort() (int, error) {
address, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
func GetAvailablePort(iface string) (int, error) {
address, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:0", iface))
if err != nil {
return 0, err
}
Expand All @@ -38,8 +38,8 @@ func GetAvailablePort() (int, error) {
}

// IsPortAvailable returns true if the port is already taken
func IsPortAvailable(port int) bool {
address := fmt.Sprintf("127.0.0.1:%d", port)
func IsPortAvailable(iface string, port int) bool {
address := fmt.Sprintf("%s:%d", iface, port)
listener, err := net.Listen("tcp", address)
if err != nil {
log.Infof("port %s is taken: %s", address, err)
Expand Down
10 changes: 5 additions & 5 deletions pkg/model/port_test.go
Expand Up @@ -20,7 +20,7 @@ import (
)

func TestGetAvailablePort(t *testing.T) {
p, err := GetAvailablePort()
p, err := GetAvailablePort(Localhost)
if err != nil {
t.Fatal(err)
}
Expand All @@ -31,23 +31,23 @@ func TestGetAvailablePort(t *testing.T) {
}

func TestIsPortAvailable(t *testing.T) {
p, err := GetAvailablePort()
p, err := GetAvailablePort(Localhost)
if err != nil {
t.Fatal(err)
}

if !IsPortAvailable(p) {
if !IsPortAvailable(Localhost, p) {
t.Fatalf("port %d wasn't available", p)
}

l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", p))
l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", Localhost, p))
if err != nil {
t.Fatal(err)
}

defer l.Close()

if IsPortAvailable(p) {
if IsPortAvailable(Localhost, p) {
t.Fatalf("port %d was available", p)
}
}
4 changes: 2 additions & 2 deletions pkg/ssh/exec.go
Expand Up @@ -29,7 +29,7 @@ import (
)

// Exec executes the command over SSH
func Exec(ctx context.Context, remotePort int, tty bool, inR io.Reader, outW, errW io.Writer, command []string) error {
func Exec(ctx context.Context, iface string, remotePort int, tty bool, inR io.Reader, outW, errW io.Writer, command []string) error {
log.Info("starting SSH connection")
sshConfig, err := getSSHClientConfig()
if err != nil {
Expand All @@ -39,7 +39,7 @@ func Exec(ctx context.Context, remotePort int, tty bool, inR io.Reader, outW, er
var connection *ssh.Client
t := time.NewTicker(100 * time.Millisecond)
for i := 0; i < 100; i++ {
connection, err = ssh.Dial("tcp", fmt.Sprintf("localhost:%d", remotePort), sshConfig)
connection, err = ssh.Dial("tcp", fmt.Sprintf("%s:%d", iface, remotePort), sshConfig)
if err == nil {
break
}
Expand Down
14 changes: 12 additions & 2 deletions pkg/ssh/manager.go
Expand Up @@ -16,6 +16,7 @@ package ssh
import (
"context"
"fmt"
"runtime"

k8sforward "github.com/okteto/okteto/pkg/k8s/forward"
"github.com/okteto/okteto/pkg/log"
Expand Down Expand Up @@ -55,8 +56,17 @@ func (fm *ForwardManager) canAdd(localPort int) error {
return fmt.Errorf("port %d is listed multiple times, please check your forwards configuration", localPort)
}

if !model.IsPortAvailable(localPort) {
return fmt.Errorf("port %d is already in use in your local machine, please check your configuration", localPort)
if !model.IsPortAvailable(fm.localInterface, localPort) {
if localPort <= 1024 {
os := runtime.GOOS
switch os {
case "darwin":
return fmt.Errorf("local port %d is privileged. Define 'interface: 0.0.0.0' in your okteto manifest and try again", localPort)
case "linux":
return fmt.Errorf("local port %d is privileged. Try running \"sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/okteto\" and try again", localPort)
}
}
return fmt.Errorf("local port %d is already in-use in your local machine", localPort)
}

return nil
Expand Down

0 comments on commit 1dfa22e

Please sign in to comment.