Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: beam shell #817

Merged
merged 45 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
3cec36f
lay out shell abstraction
luke-lombardi Dec 27, 2024
f2b7ad3
move ssh to runner image
luke-lombardi Dec 27, 2024
c2fca12
lay out cli command a bit more
luke-lombardi Dec 30, 2024
eae46da
clean up stub type spacing
luke-lombardi Dec 30, 2024
f4ffbd9
start building realistic urls for CONNECT
luke-lombardi Dec 30, 2024
a29ef53
clean up
luke-lombardi Dec 30, 2024
d38445a
working end to end
luke-lombardi Dec 31, 2024
9dd303f
remove debug flag
luke-lombardi Dec 31, 2024
e39639c
cleanup
luke-lombardi Dec 31, 2024
8d5cb47
consolidate stub types
luke-lombardi Dec 31, 2024
a694632
remove debug print
luke-lombardi Dec 31, 2024
b891b62
remove debug print
luke-lombardi Dec 31, 2024
5613d8c
refactor logic into shell.py
luke-lombardi Dec 31, 2024
5be482f
add shell support everywhere
luke-lombardi Dec 31, 2024
3f84425
revert makefile change
luke-lombardi Dec 31, 2024
b736b0e
wip
luke-lombardi Dec 31, 2024
f296b47
auth by stub id
luke-lombardi Dec 31, 2024
e703380
more cleanup
luke-lombardi Dec 31, 2024
f8ba29e
wip
luke-lombardi Dec 31, 2024
f8bc6e3
use tailscale dialer
luke-lombardi Dec 31, 2024
b509fd7
refactor buffer size
luke-lombardi Dec 31, 2024
92e9e45
remove extra perms
luke-lombardi Jan 1, 2025
e857abe
clean up
luke-lombardi Jan 1, 2025
8c1bee6
rename user
luke-lombardi Jan 1, 2025
4d0a891
fix socket closure
luke-lombardi Jan 1, 2025
80cdd8d
more cleanup
luke-lombardi Jan 1, 2025
1a2cba3
add license back
luke-lombardi Jan 1, 2025
4a2cf38
copy changes
luke-lombardi Jan 1, 2025
a62cce6
actually disconnect
luke-lombardi Jan 1, 2025
b19db57
reconnect logic and ttls
luke-lombardi Jan 1, 2025
1ce8330
more cleanup
luke-lombardi Jan 1, 2025
edf2d31
small dx improvements
luke-lombardi Jan 1, 2025
28c5fe3
fix ttl
luke-lombardi Jan 1, 2025
4567596
chdir to /mnt/code
luke-lombardi Jan 1, 2025
ceb3bd2
small copy change
luke-lombardi Jan 1, 2025
a98f1b1
switch to root user
luke-lombardi Jan 2, 2025
e9dff62
wip
luke-lombardi Jan 2, 2025
4ae292d
pass full env
luke-lombardi Jan 2, 2025
2fb9cb3
fix urls
luke-lombardi Jan 2, 2025
db32869
fix function call
luke-lombardi Jan 2, 2025
7ecfa9d
address comment
luke-lombardi Jan 2, 2025
1e99524
address another comment
luke-lombardi Jan 2, 2025
e98684d
invert logic;
luke-lombardi Jan 2, 2025
5feceb1
add comment
luke-lombardi Jan 2, 2025
e0d6b28
add contexts
luke-lombardi Jan 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bin/gen_proto.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@ protoc -I ./pkg/abstractions/experimental/signal/ --python_betterproto_beta9_out

protoc -I ./pkg/abstractions/experimental/bot/ --go_out=./proto --go_opt=paths=source_relative --go-grpc_out=./proto --go-grpc_opt=paths=source_relative ./pkg/abstractions/experimental/bot/bot.proto
protoc -I ./pkg/abstractions/experimental/bot/ --python_betterproto_beta9_out=./sdk/src/beta9/clients/ ./pkg/abstractions/experimental/bot/bot.proto

protoc -I ./pkg/abstractions/shell/ --go_out=./proto --go_opt=paths=source_relative --go-grpc_out=./proto --go-grpc_opt=paths=source_relative ./pkg/abstractions/shell/shell.proto
protoc -I ./pkg/abstractions/shell/ --python_betterproto_beta9_out=./sdk/src/beta9/clients/ ./pkg/abstractions/shell/shell.proto
2 changes: 1 addition & 1 deletion docker/Dockerfile.runner
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ENV DEBIAN_FRONTEND=noninteractive
RUN <<EOT
echo 'Acquire::ForceIPv4 "true";' | tee /etc/apt/apt.conf.d/1000-force-ipv4-transport
apt-get update
apt-get install -y software-properties-common curl git gcc python3-dev bzip2
apt-get install -y software-properties-common curl git gcc python3-dev bzip2 openssh-server
add-apt-repository ppa:deadsnakes/ppa
apt-get update
EOT
Expand Down
105 changes: 105 additions & 0 deletions pkg/abstractions/shell/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package shell

import (
"context"
"io"
"net/http"
"sync"

apiv1 "github.com/beam-cloud/beta9/pkg/api/v1"
"github.com/beam-cloud/beta9/pkg/auth"
"github.com/beam-cloud/beta9/pkg/network"
"github.com/beam-cloud/beta9/pkg/types"
"github.com/labstack/echo/v4"
)

type shellGroup struct {
routerGroup *echo.Group
ss *SSHShellService
}

func registerShellRoutes(g *echo.Group, ss *SSHShellService) *shellGroup {
group := &shellGroup{routerGroup: g, ss: ss}
g.CONNECT("/id/:stubId/:containerId", auth.WithAuth(group.ShellConnect))
return group
}

func (g *shellGroup) ShellConnect(ctx echo.Context) error {
cc, _ := ctx.(*auth.HttpAuthContext)

containerId := ctx.Param("containerId")
stubId := ctx.Param("stubId")

stub, err := g.ss.backendRepo.GetStubByExternalId(ctx.Request().Context(), stubId, types.QueryFilter{
Field: "workspace_id",
Value: cc.AuthInfo.Token.Workspace.ExternalId,
})
if err != nil {
return apiv1.HTTPInternalServerError("Failed to retrieve stub")
} else if stub == nil {
return apiv1.HTTPNotFound()
}

containerAddress, err := g.ss.containerRepo.GetContainerAddress(containerId)
if err != nil {
return ctx.String(http.StatusBadGateway, "Failed to connect to container")
}

// Channel to signal when either connection is closed
done := make(chan struct{})
var once sync.Once

go g.ss.keepAlive(ctx.Request().Context(), containerId, done)

// Send a 200 OK before hijacking
ctx.Response().WriteHeader(http.StatusOK)
ctx.Response().Flush()

// Hijack the connection
luke-lombardi marked this conversation as resolved.
Show resolved Hide resolved
hijacker, ok := ctx.Response().Writer.(http.Hijacker)
if !ok {
return ctx.String(http.StatusInternalServerError, "Failed to create tunnel")
}

conn, _, err := hijacker.Hijack()
if err != nil {
return ctx.String(http.StatusInternalServerError, "Failed to create tunnel")
}
defer conn.Close()

// Dial ssh server in the container
containerConn, err := network.ConnectToHost(ctx.Request().Context(), containerAddress, containerDialTimeoutDurationS, g.ss.tailscale, g.ss.config.Tailscale)
if err != nil {
return ctx.String(http.StatusBadGateway, "Failed to connect to container")
}
defer containerConn.Close()

// Create a context that will be canceled when the client disconnects
clientCtx, clientCancel := context.WithCancel(ctx.Request().Context())
defer clientCancel()

defer func() {
containerConn.Close()
conn.Close()
}()

go func() {
buf := make([]byte, shellProxyBufferSizeKb)
_, _ = io.CopyBuffer(containerConn, conn, buf)
luke-lombardi marked this conversation as resolved.
Show resolved Hide resolved
once.Do(func() { close(done) })
}()

go func() {
buf := make([]byte, shellProxyBufferSizeKb)
_, _ = io.CopyBuffer(conn, containerConn, buf)
once.Do(func() { close(done) })
}()
luke-lombardi marked this conversation as resolved.
Show resolved Hide resolved

// Wait for either connection to close
select {
case <-done:
return nil
case <-clientCtx.Done():
return nil
}
luke-lombardi marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading