fix: ssh not proxied when workspace restarts
This commit is contained in:
@@ -11,9 +11,12 @@ type proxyConnection struct {
|
|||||||
internalPort int
|
internalPort int
|
||||||
externalPort int
|
externalPort int
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
|
|
||||||
|
// closedChan is used to notify that the proxy connection is closed
|
||||||
|
closedChan chan<- *proxyConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
func newProxyConnection(toPort int) (*proxyConnection, error) {
|
func newProxyConnection(toPort int, closedChan chan *proxyConnection) (*proxyConnection, error) {
|
||||||
l, err := net.Listen("tcp", ":0")
|
l, err := net.Listen("tcp", ":0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -25,6 +28,7 @@ func newProxyConnection(toPort int) (*proxyConnection, error) {
|
|||||||
internalPort: toPort,
|
internalPort: toPort,
|
||||||
externalPort: externalPort,
|
externalPort: externalPort,
|
||||||
listener: l,
|
listener: l,
|
||||||
|
closedChan: closedChan,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +48,9 @@ func (c *proxyConnection) forwardConnectionToSSH(conn net.Conn) {
|
|||||||
fmt.Printf("error connecting to container ssh at port %d\n", c.internalPort)
|
fmt.Printf("error connecting to container ssh at port %d\n", c.internalPort)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
c.closedChan <- c
|
||||||
|
}()
|
||||||
defer containerConn.Close()
|
defer containerConn.Close()
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
@@ -6,17 +6,24 @@ type SSHProxy struct {
|
|||||||
internalPorts map[int]int
|
internalPorts map[int]int
|
||||||
|
|
||||||
connections map[int]*proxyConnection
|
connections map[int]*proxyConnection
|
||||||
|
|
||||||
|
closedConnections chan *proxyConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *SSHProxy {
|
func New() *SSHProxy {
|
||||||
return &SSHProxy{
|
p := &SSHProxy{
|
||||||
internalPorts: map[int]int{},
|
internalPorts: map[int]int{},
|
||||||
connections: map[int]*proxyConnection{},
|
connections: map[int]*proxyConnection{},
|
||||||
|
closedConnections: make(chan *proxyConnection),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go p.handleClosedConnections()
|
||||||
|
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *SSHProxy) NewProxyEntryTo(toPort int) error {
|
func (p *SSHProxy) NewProxyEntryTo(toPort int) error {
|
||||||
c, err := newProxyConnection(toPort)
|
c, err := newProxyConnection(toPort, p.closedConnections)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -35,3 +42,10 @@ func (p *SSHProxy) FindExternalPort(internalPort int) int {
|
|||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *SSHProxy) handleClosedConnections() {
|
||||||
|
for c := range p.closedConnections {
|
||||||
|
delete(p.internalPorts, c.internalPort)
|
||||||
|
delete(p.connections, c.internalPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -11,7 +11,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
docker2 "tesseract/internal/docker"
|
"tesseract/internal/docker"
|
||||||
"tesseract/internal/service"
|
"tesseract/internal/service"
|
||||||
"tesseract/internal/template"
|
"tesseract/internal/template"
|
||||||
"time"
|
"time"
|
||||||
@@ -42,7 +42,7 @@ func fetchAllWorkspaces(c echo.Context) error {
|
|||||||
return c.JSON(http.StatusOK, make([]workspace, 0))
|
return c.JSON(http.StatusOK, make([]workspace, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
docker := service.DockerClient(c)
|
dockerClient := service.DockerClient(c)
|
||||||
sshProxy := service.SSHProxy(c)
|
sshProxy := service.SSHProxy(c)
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
@@ -54,7 +54,7 @@ func fetchAllWorkspaces(c echo.Context) error {
|
|||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
inspect, err := docker.ContainerInspect(ctx, w.ContainerID)
|
inspect, err := dockerClient.ContainerInspect(ctx, w.ContainerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
@@ -73,7 +73,7 @@ func fetchAllWorkspaces(c echo.Context) error {
|
|||||||
workspaces[i].Status = statusUnknown
|
workspaces[i].Status = statusUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
if internalPort := docker2.ContainerSSHHostPort(ctx, inspect); internalPort > 0 {
|
if internalPort := docker.ContainerSSHHostPort(ctx, inspect); internalPort > 0 {
|
||||||
if port := sshProxy.FindExternalPort(internalPort); port > 0 {
|
if port := sshProxy.FindExternalPort(internalPort); port > 0 {
|
||||||
workspaces[i].SSHPort = port
|
workspaces[i].SSHPort = port
|
||||||
}
|
}
|
||||||
@@ -120,20 +120,36 @@ func updateOrCreateWorkspace(c echo.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
docker := service.DockerClient(c)
|
dockerClient := service.DockerClient(c)
|
||||||
|
sshProxy := service.SSHProxy(c)
|
||||||
|
|
||||||
switch status(body.Status) {
|
switch status(body.Status) {
|
||||||
case statusStopped:
|
case statusStopped:
|
||||||
if err = stopContainer(ctx, docker, workspaceName); err != nil {
|
if err = stopContainer(ctx, dockerClient, workspaceName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
w.Status = statusStopped
|
w.Status = statusStopped
|
||||||
break
|
break
|
||||||
|
|
||||||
case statusRunning:
|
case statusRunning:
|
||||||
if err = startContainer(ctx, docker, workspaceName); err != nil {
|
if err = startContainer(ctx, dockerClient, workspaceName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inspect, err := dockerClient.ContainerInspect(ctx, w.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sshPort := docker.ContainerSSHHostPort(ctx, inspect)
|
||||||
|
if sshPort > 0 {
|
||||||
|
if err = sshProxy.NewProxyEntryTo(sshPort); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
w.Status = statusRunning
|
w.Status = statusRunning
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +181,7 @@ func createWorkspace(c echo.Context, workspaceName string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
docker := service.DockerClient(c)
|
dockerClient := service.DockerClient(c)
|
||||||
|
|
||||||
containerSSHPort := nat.Port("22/tcp")
|
containerSSHPort := nat.Port("22/tcp")
|
||||||
containerConfig := &container.Config{
|
containerConfig := &container.Config{
|
||||||
@@ -184,17 +200,17 @@ func createWorkspace(c echo.Context, workspaceName string) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := docker.ContainerCreate(ctx, containerConfig, hostConfig, nil, nil, workspaceName)
|
res, err := dockerClient.ContainerCreate(ctx, containerConfig, hostConfig, nil, nil, workspaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = docker.ContainerStart(ctx, res.ID, container.StartOptions{})
|
err = dockerClient.ContainerStart(ctx, res.ID, container.StartOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
inspect, err := docker.ContainerInspect(ctx, res.ID)
|
inspect, err := dockerClient.ContainerInspect(ctx, res.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -248,7 +264,7 @@ func deleteWorkspace(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
db := service.Database(c)
|
db := service.Database(c)
|
||||||
docker := service.DockerClient(c)
|
dockerClient := service.DockerClient(c)
|
||||||
ctx := c.Request().Context()
|
ctx := c.Request().Context()
|
||||||
|
|
||||||
tx, err := db.BeginTx(ctx, nil)
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
@@ -262,20 +278,20 @@ func deleteWorkspace(c echo.Context) error {
|
|||||||
return echo.NewHTTPError(http.StatusNotFound)
|
return echo.NewHTTPError(http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
inspect, err := inspectContainer(ctx, docker, w.ContainerID)
|
inspect, err := inspectContainer(ctx, dockerClient, w.ContainerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tx.Rollback()
|
_ = tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if inspect.State.Running {
|
if inspect.State.Running {
|
||||||
if err = stopContainer(ctx, docker, w.ContainerID); err != nil {
|
if err = stopContainer(ctx, dockerClient, w.ContainerID); err != nil {
|
||||||
_ = tx.Rollback()
|
_ = tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = deleteContainer(ctx, docker, w.ContainerID); err != nil {
|
if err = deleteContainer(ctx, dockerClient, w.ContainerID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user