glider-ssh/_examples/ssh-docker/docker.go
2017-09-01 09:07:24 -07:00

120 lines
2.5 KiB
Go

package main
import (
"context"
"fmt"
"io"
"log"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
"github.com/gliderlabs/ssh"
)
func main() {
ssh.Handle(func(sess ssh.Session) {
_, _, isTty := sess.Pty()
cfg := &container.Config{
Image: sess.User(),
Cmd: sess.Command(),
Env: sess.Environ(),
Tty: isTty,
OpenStdin: true,
AttachStderr: true,
AttachStdin: true,
AttachStdout: true,
StdinOnce: true,
Volumes: make(map[string]struct{}),
}
status, cleanup, err := dockerRun(cfg, sess)
defer cleanup()
if err != nil {
fmt.Fprintln(sess, err)
log.Println(err)
}
sess.Exit(int(status))
})
log.Println("starting ssh server on port 2222...")
log.Fatal(ssh.ListenAndServe(":2222", nil))
}
func dockerRun(cfg *container.Config, sess ssh.Session) (status int64, cleanup func(), err error) {
docker, err := client.NewEnvClient()
if err != nil {
panic(err)
}
status = 255
cleanup = func() {}
ctx := context.Background()
res, err := docker.ContainerCreate(ctx, cfg, nil, nil, "")
if err != nil {
return
}
cleanup = func() {
docker.ContainerRemove(ctx, res.ID, types.ContainerRemoveOptions{})
}
opts := types.ContainerAttachOptions{
Stdin: cfg.AttachStdin,
Stdout: cfg.AttachStdout,
Stderr: cfg.AttachStderr,
Stream: true,
}
stream, err := docker.ContainerAttach(ctx, res.ID, opts)
if err != nil {
return
}
cleanup = func() {
docker.ContainerRemove(ctx, res.ID, types.ContainerRemoveOptions{})
stream.Close()
}
outputErr := make(chan error)
go func() {
var err error
if cfg.Tty {
_, err = io.Copy(sess, stream.Reader)
} else {
_, err = stdcopy.StdCopy(sess, sess.Stderr(), stream.Reader)
}
outputErr <- err
}()
go func() {
defer stream.CloseWrite()
io.Copy(stream.Conn, sess)
}()
err = docker.ContainerStart(ctx, res.ID, types.ContainerStartOptions{})
if err != nil {
return
}
if cfg.Tty {
_, winCh, _ := sess.Pty()
go func() {
for win := range winCh {
err := docker.ContainerResize(ctx, res.ID, types.ResizeOptions{
Height: uint(win.Height),
Width: uint(win.Width),
})
if err != nil {
log.Println(err)
break
}
}
}()
}
resultC, errC := docker.ContainerWait(ctx, res.ID, container.WaitConditionNotRunning)
select {
case err = <-errC:
return
case result := <-resultC:
status = result.StatusCode
}
err = <-outputErr
return
}