Skip to content

Instantly share code, notes, and snippets.

@coryb
Created August 31, 2020 20:34
Show Gist options
  • Save coryb/e9a74902f172c57cefe258c525735b94 to your computer and use it in GitHub Desktop.
Save coryb/e9a74902f172c57cefe258c525735b94 to your computer and use it in GitHub Desktop.
sample main for getting interactive tty in buildkit container
package main
import (
"context"
"os"
"os/signal"
"syscall"
buildkitClient "github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb"
client "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/progress/progressui"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/sync/errgroup"
"golang.org/x/sys/unix"
)
func main() {
ctx := context.Background()
c, err := buildkitClient.New(ctx, os.Getenv("BUILDKIT_HOST"), buildkitClient.WithFailFast())
if err != nil {
panic(err)
}
g, ctx := errgroup.WithContext(ctx)
displayCh := make(chan *buildkitClient.SolveStatus)
g.Go(func() error {
// not using shared context to not disrupt display but let is finish reporting errors
return progressui.DisplaySolveStatus(context.TODO(), "", nil, os.Stderr, displayCh)
})
_, err = c.Build(ctx, buildkitClient.SolveOpt{}, "", func(ctx context.Context, c client.Client) (*client.Result, error) {
st := llb.Image("busybox:latest")
def, err := st.Marshal(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal state")
}
r, err := c.Solve(ctx, client.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, errors.Wrap(err, "failed to solve")
}
ctr, err := c.NewContainer(ctx, client.NewContainerRequest{
Mounts: []client.Mount{{
Dest: "/",
MountType: pb.MountType_BIND,
Ref: r.Ref,
}},
})
if err != nil {
return nil, err
}
defer ctr.Release(ctx)
pid1, err := ctr.Start(ctx, client.StartRequest{
Args: []string{"sh"},
Cwd: "/",
Tty: true,
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
})
if err != nil {
return nil, err
}
oldState, err := terminal.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
return nil, err
}
defer terminal.Restore(int(os.Stdin.Fd()), oldState)
watchResize(ctx, int(os.Stdin.Fd()), pid1)
return &client.Result{}, pid1.Wait()
}, displayCh)
if err != nil {
panic(err)
}
}
func watchResize(ctx context.Context, fd int, proc client.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)
if err != nil {
return
}
proc.Resize(ctx, client.WinSize{
Cols: uint32(ws.Col),
Rows: uint32(ws.Row),
})
if err != nil {
return
}
}
}
}()
ch <- syscall.SIGWINCH // Initial resize.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment