Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save egeneralov/c5b546b12739dd8607ec662f4218c2cf to your computer and use it in GitHub Desktop.
Save egeneralov/c5b546b12739dd8607ec662f4218c2cf to your computer and use it in GitHub Desktop.
package main
import (
"context"
"fmt"
"io"
"log"
"encoding/json"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
"github.com/gliderlabs/ssh"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
)
func main() {
ssh.Handle(func(sess ssh.Session) {
j, je := json.Marshal(sess)
if je == nil {
fmt.Println(string(j))
}
_, _, isTty := sess.Pty()
cfg := &container.Config{
Image: "debian:buster",
// Image: sess.User(),
Cmd: sess.Command(),
Env: sess.Environ(),
Tty: isTty,
OpenStdin: true,
AttachStderr: true,
AttachStdin: true,
AttachStdout: true,
StdinOnce: true,
}
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()
hostConfig := container.HostConfig{}
networkingConfig := network.NetworkingConfig{}
platformConfig := v1.Platform{
OS: "linux",
Architecture: "amd64",
// Variant: "minimal",
}
res, err := docker.ContainerCreate(ctx, cfg, &hostConfig, &networkingConfig, &platformConfig, "")
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
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment