Skip to content

Commit

Permalink
port forwards over SSH (#755)
Browse files Browse the repository at this point in the history
* switch to forward via SSH instead of using client-go's port-forwards

Signed-off-by: Ramiro Berrelleza <rberrelleza@gmail.com>
  • Loading branch information
rberrelleza committed Mar 17, 2020
1 parent 8d1d9af commit 2062e42
Show file tree
Hide file tree
Showing 15 changed files with 783 additions and 147 deletions.
93 changes: 83 additions & 10 deletions cmd/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ type UpContext struct {
Client *kubernetes.Clientset
RestConfig *rest.Config
Pod string
Forwarder *forward.PortForwardManager
Forwarder forwarder
Disconnect chan error
Running chan error
Exit chan error
Expand All @@ -78,6 +78,14 @@ type UpContext struct {
success bool
}

// Forwarder is an interface for the port-forwarding features
type forwarder interface {
Add(model.Forward) error
AddReverse(model.Reverse) error
Start(string, string) error
Stop()
}

//Up starts a cloud dev environment
func Up() *cobra.Command {
var devPath string
Expand Down Expand Up @@ -154,6 +162,10 @@ func RunUp(dev *model.Dev, autoDeploy bool, forcePull, resetSyncthing bool) erro
Exit: make(chan error, 1),
}

if up.Dev.ExecuteOverSSHEnabled() {
log.Success("Experimental SSH mode enabled")
}

defer up.shutdown()

if up.Dev.RemoteModeEnabled() {
Expand Down Expand Up @@ -247,12 +259,18 @@ func (up *UpContext) Activate(autoDeploy, resetSyncthing bool) {
analytics.TrackUp(true, up.Dev.Name, up.getClusterType(), len(up.Dev.Services) == 0, up.isSwap, up.Dev.RemoteModeEnabled())
}

err = up.devMode(d, create)
if err != nil {
if err := up.devMode(d, create); err != nil {
up.Exit <- fmt.Errorf("couldn't activate your development environment: %s", err)
return
}

if err := up.forwards(); err != nil {
up.Exit <- fmt.Errorf("couldn't forward traffic to your development environment: %s", err)
return
}

go up.cleanCommand()

log.Success("Development environment activated")

err = up.sync(resetSyncthing && !up.retry)
Expand All @@ -265,7 +283,6 @@ func (up *UpContext) Activate(autoDeploy, resetSyncthing bool) {
up.Exit <- err
return
}

up.success = true
if up.retry {
analytics.TrackReconnect(true, up.getClusterType(), up.isSwap)
Expand Down Expand Up @@ -356,11 +373,11 @@ func (up *UpContext) WaitUntilExitOrInterrupt() error {
case err := <-up.Running:
fmt.Println()
if err != nil {
log.Infof("Command execution error: %s", err)
log.Infof("command failed: %s", err)
return errors.ErrCommandFailed
}

log.Info("Command finished execution without any errors")
log.Info("command completed")
return nil

case err := <-up.ErrChan:
Expand Down Expand Up @@ -483,21 +500,37 @@ func (up *UpContext) devMode(d *appsv1.Deployment, create bool) error {
}

up.Pod = pod.Name
go up.cleanCommand()
return nil
}

func (up *UpContext) forwards() error {
if up.Dev.ExecuteOverSSHEnabled() {
return up.sshForwards()
}

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

for _, f := range up.Dev.Forward {
if err := up.Forwarder.Add(f); err != nil {
return err
}
}

if err := up.Forwarder.Add(model.Forward{Local: up.Sy.RemotePort, Remote: syncthing.ClusterPort}); err != nil {
return err
}

if err := up.Forwarder.Add(model.Forward{Local: up.Sy.RemoteGUIPort, Remote: syncthing.GUIPort}); err != nil {
return err
}

if up.Dev.RemoteModeEnabled() {
if err := up.Forwarder.Add(model.Forward{Local: up.Dev.RemotePort, Remote: up.Dev.SSHServerPort}); err != nil {
return err
}
}

if err := up.Forwarder.Start(up.Pod, up.Dev.Namespace); err != nil {
return err
}
Expand All @@ -507,21 +540,57 @@ func (up *UpContext) devMode(d *appsv1.Deployment, create bool) error {
return err
}

reverseManager := ssh.NewReverseManager(up.Context, up.Dev.RemotePort)
reverseManager := ssh.NewForwardManager(up.Context, fmt.Sprintf(":%d", up.Dev.RemotePort), "localhost", "0.0.0.0", nil)
for _, f := range up.Dev.Reverse {
if err := reverseManager.Add(&f); err != nil {
if err := reverseManager.AddReverse(f); err != nil {
return err
}
}

if err := reverseManager.Start(); err != nil {
if err := reverseManager.Start("", ""); err != nil {
return err
}
}

return nil
}

func (up *UpContext) sshForwards() error {
log.Infof("starting SSH port forwards")
f := forward.NewPortForwardManager(up.Context, 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(up.Context, fmt.Sprintf(":%d", up.Dev.RemotePort), "localhost", "0.0.0.0", f)

if err := up.Forwarder.Add(model.Forward{Local: up.Sy.RemotePort, Remote: syncthing.ClusterPort}); err != nil {
return err
}

if err := up.Forwarder.Add(model.Forward{Local: up.Sy.RemoteGUIPort, Remote: syncthing.GUIPort}); err != nil {
return err
}

for _, f := range up.Dev.Forward {
if err := up.Forwarder.Add(f); err != nil {
return err
}
}

for _, r := range up.Dev.Reverse {
if err := up.Forwarder.AddReverse(r); err != nil {
return err
}
}

if err := ssh.AddEntry(up.Dev.Name, up.Dev.RemotePort); err != nil {
return err
}

return up.Forwarder.Start(up.Pod, up.Dev.Namespace)
}

func (up *UpContext) sync(resetSyncthing bool) error {
if err := up.startSyncthing(resetSyncthing); err != nil {
return err
Expand Down Expand Up @@ -728,6 +797,10 @@ func (up *UpContext) shutdown() {
func printDisplayContext(dev *model.Dev) {
log.Println(fmt.Sprintf(" %s %s", log.BlueString("Namespace:"), dev.Namespace))
log.Println(fmt.Sprintf(" %s %s", log.BlueString("Name:"), dev.Name))
if dev.RemoteModeEnabled() {
log.Println(fmt.Sprintf(" %s %d -> %d", log.BlueString("SSH:"), dev.RemotePort, dev.SSHServerPort))
}

if len(dev.Forward) > 0 {
log.Println(fmt.Sprintf(" %s %d -> %d", log.BlueString("Forward:"), dev.Forward[0].Local, dev.Forward[0].Remote))
for i := 1; i < len(dev.Forward); i++ {
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ require (
github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e // indirect
github.com/Masterminds/semver v1.4.2
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect
github.com/briandowns/spinner v1.7.0
github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect
github.com/cheggaaa/pb/v3 v3.0.1
Expand All @@ -22,7 +23,9 @@ require (
github.com/dukex/mixpanel v0.0.0-20180925151559-f8d5594f958e
github.com/elazarl/goproxy v0.0.0-20181111060418-2ce16c963a8a // indirect
github.com/fatih/color v1.7.0
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
github.com/frankban/quicktest v1.7.3 // indirect
github.com/gliderlabs/ssh v0.2.2
github.com/gogo/googleapis v1.3.0 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/google/go-github/v28 v28.1.1
Expand Down Expand Up @@ -72,6 +75,7 @@ require (
replace (
github.com/containerd/containerd v1.3.0-0.20190507210959-7c1e88399ec0 => github.com/containerd/containerd v1.3.0
github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309
github.com/gliderlabs/ssh v0.2.2 => github.com/rberrelleza/ssh v0.2.3-0.20191129151128-337be1657602
github.com/hashicorp/go-immutable-radix => github.com/tonistiigi/go-immutable-radix v0.0.0-20170803185627-826af9ccf0fe
github.com/jaguilar/vt100 => github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305
github.com/moby/buildkit => github.com/okteto/buildkit v0.6.4-0.20200224171345-78a8fe571c17
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ github.com/alecthomas/gometalinter v2.0.11+incompatible/go.mod h1:qfIpQGGz3d+Nmg
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go v1.15.78 h1:LaXy6lWR0YK7LKyuU0QWy2ws/LWTPfYV/UgfiBu4tvY=
Expand Down Expand Up @@ -148,6 +150,8 @@ github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/frankban/quicktest v1.7.3 h1:kV0lw0TH1j1hozahVmcpFCsbV5hcS4ZalH+U7UoeTow=
github.com/frankban/quicktest v1.7.3/go.mod h1:V1d2J5pfxYH6EjBAgSK7YNXcXlTWxUHdE1sVDXkjnig=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
Expand Down Expand Up @@ -420,6 +424,8 @@ github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1 h1:Lo6mRUjdS99f3
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/rberrelleza/ssh v0.2.3-0.20191129151128-337be1657602 h1:9tsMHlmqQnHcQGMxs44dh2prV2WkX0s/QmaSVFeSfvs=
github.com/rberrelleza/ssh v0.2.3-0.20191129151128-337be1657602/go.mod h1:tfHQw+B2gAi+CuaPnjEoTQfoKEMobJVF6DKG337yI2U=
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
Expand Down
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ func init() {
// override client-go error handlers to downgrade the "logging before flag.Parse" error
errorHandlers := []func(error){
func(e error) {

// Error when there's no service on the other side: an error occurred forwarding 8080 -> 8080: error forwarding port 8080 to pod df05e7e1c85d6b256df779c0b2deb417eb53a299c8581fc0945264301d8fa4b3, uid : exit status 1: 2020/03/16 02:11:09 socat[42490] E connect(5, AF=2 127.0.0.1:8080, 16): Connection refused\n
log.Debugf("unhandled error: %s", e)
},
}
Expand Down
7 changes: 5 additions & 2 deletions pkg/cmd/status/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"context"
"fmt"

"github.com/okteto/okteto/pkg/log"
"github.com/okteto/okteto/pkg/model"
"github.com/okteto/okteto/pkg/syncthing"
)
Expand All @@ -25,11 +26,13 @@ import (
func Run(ctx context.Context, dev *model.Dev, sy *syncthing.Syncthing) (float64, error) {
progressLocal, err := sy.GetCompletionProgress(ctx, dev, true)
if err != nil {
return 0, fmt.Errorf("error accessing local syncthing status: %s", err)
log.Infof("error accessing local syncthing status: %s", err)
return 0, fmt.Errorf("error accessing local syncthing status")
}
progressRemote, err := sy.GetCompletionProgress(ctx, dev, false)
if err != nil {
return 0, fmt.Errorf("error accessing remote syncthing status: %s", err)
log.Infof("error accessing remote syncthing status: %s", err)
return 0, fmt.Errorf("error accessing remote syncthing status")
}
progress := (progressLocal + progressRemote) / 2
return progress, nil
Expand Down
5 changes: 5 additions & 0 deletions pkg/k8s/forward/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ func (p *PortForwardManager) Add(f model.Forward) error {
return nil
}

// AddReverse is not implemented
func (p *PortForwardManager) AddReverse(_ model.Reverse) error {
return fmt.Errorf("not implemented")
}

// Start starts all the port forwarders to the dev environment
func (p *PortForwardManager) Start(devPod, namespace string) error {
p.stopped = false
Expand Down
18 changes: 9 additions & 9 deletions pkg/model/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,14 +380,6 @@ func (dev *Dev) LoadRemote() {
log.Infof("remote port not set, using %d", dev.RemotePort)
}

dev.Forward = append(
dev.Forward,
Forward{
Local: dev.RemotePort,
Remote: dev.SSHServerPort,
},
)

log.Infof("enabled remote mode")
}

Expand Down Expand Up @@ -573,6 +565,10 @@ func (dev *Dev) RemoteModeEnabled() bool {
return false
}

if dev.ExecuteOverSSHEnabled() {
return true
}

if dev.RemotePort > 0 {
return true
}
Expand All @@ -582,7 +578,11 @@ func (dev *Dev) RemoteModeEnabled() bool {

// ExecuteOverSSHEnabled returns true if execute over SSH is enabled
func (dev *Dev) ExecuteOverSSHEnabled() bool {
return dev.RemoteModeEnabled() && len(os.Getenv("OKTETO_EXECUTE_SSH")) > 0
if _, ok := os.LookupEnv("OKTETO_EXECUTE_SSH"); ok {
return true
}

return false
}

// GetKeyName returns the secret key name
Expand Down
17 changes: 7 additions & 10 deletions pkg/model/dev_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,15 +399,16 @@ func Test_LoadRemote(t *testing.T) {
t.Errorf("command wasn't set: %s", dev.Command)
}

if len(dev.Forward) != 2 {
t.Errorf("forward wasn't injected")
if len(dev.Forward) != 1 {
t.Errorf("forward was injected")
}

if dev.Forward[1].Local != 22100 {
t.Errorf("local forward wasn't 22100 it was %d", dev.Forward[1].Local)
if dev.RemotePort != 22100 {
t.Errorf("local remote port wasn't 22100 it was %d", dev.RemotePort)
}
if e, a := 2222, dev.Forward[1].Remote; e != a {
t.Errorf("expected local forward remote %d, got %d", e, a)

if dev.SSHServerPort != 2222 {
t.Errorf("server remote port wasn't 2222 it was %d", dev.SSHServerPort)
}
}

Expand All @@ -433,10 +434,6 @@ func Test_Reverse(t *testing.T) {
t.Error("remote port was not automatically enabled")
}

if len(dev.Forward) != 1 {
t.Errorf("forward wasn't injected")
}

if dev.Reverse[0].Local != 8080 {
t.Errorf("remote forward local wasn't 8080 it was %d", dev.Reverse[0].Local)
}
Expand Down

0 comments on commit 2062e42

Please sign in to comment.