Skip to content

Instantly share code, notes, and snippets.

@coryb
Last active October 25, 2021 06:27
Show Gist options
  • Save coryb/8d733b7f1f2398a7b85a640d1f38fdc0 to your computer and use it in GitHub Desktop.
Save coryb/8d733b7f1f2398a7b85a640d1f38fdc0 to your computer and use it in GitHub Desktop.
Crude buildkit gateway exec with interactive tty
package main
import (
"context"
"os"
"os/signal"
"syscall"
client "github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/progress/progressui"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/sync/errgroup"
"golang.org/x/sys/unix"
_ "github.com/moby/buildkit/client/connhelper/dockercontainer"
)
func main() {
st := llb.Image("ubuntu:focal", llb.LinuxAmd64)
runInteractive(st, "/bin/bash")
}
func panicOnErr(err error) {
if err != nil {
panic(err)
}
}
func runInteractive(st llb.State, command string) {
ctx := context.Background()
c, err := client.New(ctx, os.Getenv("BUILDKIT_HOST"))
panicOnErr(err)
g, ctx := errgroup.WithContext(ctx)
displayCh := make(chan *client.SolveStatus)
g.Go(func() error {
return progressui.DisplaySolveStatus(context.TODO(), "", nil, os.Stderr, displayCh)
})
_, err = c.Build(ctx, client.SolveOpt{}, "", func(ctx context.Context, c gwclient.Client) (*gwclient.Result, error) {
def, err := st.Marshal(ctx)
panicOnErr(err)
r, err := c.Solve(ctx, gwclient.SolveRequest{
Definition: def.ToPB(),
})
panicOnErr(err)
execCtx, cancel := context.WithCancel(ctx)
ctr, err := c.NewContainer(execCtx, gwclient.NewContainerRequest{Mounts: []gwclient.Mount{{
Dest: "/",
MountType: pb.MountType_BIND,
Ref: r.Ref,
}}})
panicOnErr(err)
go func() {
<-execCtx.Done()
ctr.Release(context.TODO())
}()
oldState, err := terminal.MakeRaw(int(os.Stdin.Fd()))
panicOnErr(err)
defer terminal.Restore(int(os.Stdin.Fd()), oldState)
defer cancel()
pid, err := ctr.Start(execCtx, gwclient.StartRequest{
Args: []string{command},
Cwd: "/",
Tty: true,
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
})
panicOnErr(err)
watchResize(ctx, int(os.Stdin.Fd()), pid)
return gwclient.NewResult(), pid.Wait()
}, displayCh)
panicOnErr(err)
}
func watchResize(ctx context.Context, fd int, proc gwclient.ContainerProcess) {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGWINCH)
go func() {
defer close(ch)
for {
select {
case <-ctx.Done():
case <-ch:
ws, err := unix.IoctlGetWinsize(int(os.Stdin.Fd()), unix.TIOCGWINSZ)
panicOnErr(err)
proc.Resize(ctx, gwclient.WinSize{
Cols: uint32(ws.Col),
Rows: uint32(ws.Row),
})
}
}
}()
ch <- syscall.SIGWINCH // Initial resize.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment