mirror of https://github.com/docker/docs.git
Rough implementation of 'docker run'
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com> (github: aanand)
This commit is contained in:
parent
188d75e0ab
commit
fc41d09026
|
|
@ -1,11 +1,18 @@
|
||||||
package backends
|
package backends
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/docker/libswarm/beam"
|
"github.com/docker/libswarm/beam"
|
||||||
"github.com/dotcloud/docker/engine"
|
"github.com/dotcloud/docker/engine"
|
||||||
|
"github.com/dotcloud/docker/runconfig"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -26,6 +33,7 @@ func Forward() beam.Sender {
|
||||||
instance.OnAttach(beam.Handler(f.attach))
|
instance.OnAttach(beam.Handler(f.attach))
|
||||||
instance.OnStart(beam.Handler(f.start))
|
instance.OnStart(beam.Handler(f.start))
|
||||||
instance.OnLs(beam.Handler(f.ls))
|
instance.OnLs(beam.Handler(f.ls))
|
||||||
|
instance.OnSpawn(beam.Handler(f.spawn))
|
||||||
_, err = ctx.Ret.Send(&beam.Message{Verb: beam.Ack, Ret: instance})
|
_, err = ctx.Ret.Send(&beam.Message{Verb: beam.Ack, Ret: instance})
|
||||||
return err
|
return err
|
||||||
}))
|
}))
|
||||||
|
|
@ -74,6 +82,104 @@ func (f *forwarder) ls(ctx *beam.Message) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *forwarder) spawn(ctx *beam.Message) error {
|
||||||
|
if len(ctx.Args) < 1 {
|
||||||
|
return fmt.Errorf("forward: spawn takes at least 1 argument, got %d", len(ctx.Args))
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&runconfig.Config{
|
||||||
|
Image: ctx.Args[0],
|
||||||
|
Cmd: ctx.Args[1:],
|
||||||
|
AttachStdin: false,
|
||||||
|
AttachStdout: true,
|
||||||
|
AttachStderr: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp, err := f.client.call("POST", "/containers/create", string(body))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
respBody, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 201 {
|
||||||
|
return fmt.Errorf("expected status code 201, got %d:\n%s", resp.StatusCode, respBody)
|
||||||
|
}
|
||||||
|
var respJson struct{ Id string }
|
||||||
|
if err = json.Unmarshal(respBody, &respJson); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c := f.newContainer(respJson.Id)
|
||||||
|
if _, err = ctx.Ret.Send(&beam.Message{Verb: beam.Ack, Ret: c}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *forwarder) newContainer(id string) beam.Sender {
|
||||||
|
c := &container{forwarder: f, id: id}
|
||||||
|
instance := beam.NewServer()
|
||||||
|
instance.OnAttach(beam.Handler(c.attach))
|
||||||
|
instance.OnStart(beam.Handler(c.start))
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
type container struct {
|
||||||
|
forwarder *forwarder
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *container) attach(ctx *beam.Message) error {
|
||||||
|
if _, err := ctx.Ret.Send(&beam.Message{Verb: beam.Ack}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("/containers/%s/attach?stdout=1&stderr=0&stream=1&logs=1", c.id)
|
||||||
|
|
||||||
|
stdoutR, stdoutW := io.Pipe()
|
||||||
|
_, stderrW := io.Pipe()
|
||||||
|
go copyOutput(ctx.Ret, stdoutR)
|
||||||
|
c.forwarder.client.hijack("POST", path, nil, stdoutW, stderrW)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyOutput(sender beam.Sender, reader io.Reader) {
|
||||||
|
chunk := make([]byte, 4096)
|
||||||
|
for {
|
||||||
|
n, err := reader.Read(chunk)
|
||||||
|
if n > 0 {
|
||||||
|
sender.Send(&beam.Message{Verb: beam.Log, Args: []string{string(chunk[0:n])}})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
message := fmt.Sprintf("Error reading from stream: %v", err)
|
||||||
|
sender.Send(&beam.Message{Verb: beam.Error, Args: []string{message}})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *container) start(ctx *beam.Message) error {
|
||||||
|
path := fmt.Sprintf("/containers/%s/start", c.id)
|
||||||
|
resp, err := c.forwarder.client.call("POST", path, "{}")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
respBody, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 204 {
|
||||||
|
return fmt.Errorf("expected status code 204, got %d:\n%s", resp.StatusCode, respBody)
|
||||||
|
}
|
||||||
|
if _, err := ctx.Ret.Send(&beam.Message{Verb: beam.Ack}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
URL *url.URL
|
URL *url.URL
|
||||||
proto string
|
proto string
|
||||||
|
|
@ -111,3 +217,61 @@ func (c *client) call(method, path, body string) (*http.Response, error) {
|
||||||
}
|
}
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *client) hijack(method, path string, in io.ReadCloser, stdout, stderr io.Writer) error {
|
||||||
|
dial, err := net.Dial("tcp", c.URL.Host)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(method, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
clientconn := httputil.NewClientConn(dial, nil)
|
||||||
|
defer clientconn.Close()
|
||||||
|
|
||||||
|
clientconn.Do(req)
|
||||||
|
|
||||||
|
rwc, br := clientconn.Hijack()
|
||||||
|
defer rwc.Close()
|
||||||
|
|
||||||
|
receiveStdout := utils.Go(func() (err error) {
|
||||||
|
defer func() {
|
||||||
|
if in != nil {
|
||||||
|
in.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// _, err = utils.StdCopy(stdout, stderr, br)
|
||||||
|
_, err = io.Copy(stdout, br)
|
||||||
|
log.Println("[hijack] End of stdout")
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
sendStdin := utils.Go(func() error {
|
||||||
|
if in != nil {
|
||||||
|
io.Copy(rwc, in)
|
||||||
|
log.Println("[hijack] End of stdin")
|
||||||
|
}
|
||||||
|
if tcpc, ok := rwc.(*net.TCPConn); ok {
|
||||||
|
if err := tcpc.CloseWrite(); err != nil {
|
||||||
|
log.Printf("Couldn't send EOF: %s\n", err)
|
||||||
|
}
|
||||||
|
} else if unixc, ok := rwc.(*net.UnixConn); ok {
|
||||||
|
if err := unixc.CloseWrite(); err != nil {
|
||||||
|
log.Printf("Couldn't send EOF: %s\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Discard errors due to pipe interruption
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err := <-receiveStdout; err != nil {
|
||||||
|
log.Printf("Error receiveStdout: %s\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := <-sendStdin; err != nil {
|
||||||
|
log.Printf("Error sendStdin: %s\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,36 @@ func doCmd(instance *beam.Object, args []string) error {
|
||||||
fmt.Println(strings.Join(names, "\n"))
|
fmt.Println(strings.Join(names, "\n"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if args[0] == "run" {
|
||||||
|
if len(args) < 3 {
|
||||||
|
return fmt.Errorf("usage: run IMAGE COMMAND...")
|
||||||
|
}
|
||||||
|
container, err := instance.Spawn(args[1:]...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("spawn: %v", err)
|
||||||
|
}
|
||||||
|
logs, _, err := container.Attach("")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("attach: %v", err)
|
||||||
|
}
|
||||||
|
if err = container.Start(); err != nil {
|
||||||
|
return fmt.Errorf("start: %v", err)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
msg, err := logs.Receive(beam.Ret)
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == "EOF" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return fmt.Errorf("error reading from container: %v", err)
|
||||||
|
}
|
||||||
|
if msg.Verb != beam.Log {
|
||||||
|
return fmt.Errorf("unexpected message reading from container: %v", msg)
|
||||||
|
}
|
||||||
|
fmt.Print(msg.Args[0])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return fmt.Errorf("unrecognised command: %s", args[0])
|
return fmt.Errorf("unrecognised command: %s", args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue