diff --git a/commands/commands.go b/commands/commands.go index afeb6d30e1..665d38411e 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -11,8 +11,9 @@ import ( "github.com/dotcloud/docker/future" "github.com/dotcloud/docker/rcli" "io" - "net/http" + "io/ioutil" "net/url" + "net/http" "os" "path" "strconv" @@ -425,10 +426,12 @@ func (srv *Server) CmdKill(stdin io.ReadCloser, stdout io.Writer, args ...string func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...string) error { cmd := rcli.Subcmd(stdout, "import", "[OPTIONS] NAME", "Create a new filesystem image from the contents of a tarball") fl_stdin := cmd.Bool("stdin", false, "Read tarball from stdin") + var archive io.Reader + var resp *http.Response + if err := cmd.Parse(args); err != nil { return nil } - var archive io.Reader name := cmd.Arg(0) if name == "" { return errors.New("Not enough arguments") @@ -451,14 +454,11 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...stri fmt.Fprintf(stdout, "Downloading from %s\n", u.String()) // Download with curl (pretty progress bar) // If curl is not available, fallback to http.Get() - archive, err = future.Curl(u.String(), stdout) + resp, err = future.Download(u.String(), stdout) if err != nil { - if resp, err := http.Get(u.String()); err != nil { - return err - } else { - archive = resp.Body - } + return err } + archive = future.ProgressReader(resp.Body, int(resp.ContentLength), stdout) } fmt.Fprintf(stdout, "Unpacking to %s\n", name) img, err := srv.images.Create(archive, nil, name, "") @@ -838,12 +838,16 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) fl_comment := cmd.String("c", "", "Comment") fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)") var fl_ports ports + cmd.Var(&fl_ports, "p", "Map a network port to the container") if err := cmd.Parse(args); err != nil { return nil } name := cmd.Arg(0) + var img_name string + //var img_version string // Only here for reference var cmdline []string + if len(cmd.Args()) >= 2 { cmdline = cmd.Args()[1:] } @@ -851,6 +855,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) if name == "" { name = "base" } + // Choose a default command if needed if len(cmdline) == 0 { *fl_stdin = true @@ -858,13 +863,31 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) *fl_attach = true cmdline = []string{"/bin/bash", "-i"} } + // Find the image img, err := srv.images.Find(name) if err != nil { return err } else if img == nil { - return errors.New("No such image: " + name) + // Separate the name:version tag + if strings.Contains(name, ":") { + parts := strings.SplitN(name, ":", 2) + img_name = parts[0] + //img_version = parts[1] // Only here for reference + } else { + img_name = name + } + + stdin_noclose := ioutil.NopCloser(stdin) + if err := srv.CmdImport(stdin_noclose, stdout, img_name); err != nil { + return err + } + img, err = srv.images.Find(name) + if err != nil || img == nil { + return errors.New("Could not find image after downloading: " + name) + } } + // Create new container container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty, *fl_stdin, *fl_memory, *fl_comment, cmdline[0], cmdline[1:]...) diff --git a/future/future.go b/future/future.go index 33f1f8925c..e7f3d9289c 100644 --- a/future/future.go +++ b/future/future.go @@ -3,10 +3,11 @@ package future import ( "bytes" "crypto/sha256" + "errors" "fmt" "io" "math/rand" - "os/exec" + "net/http" "time" ) @@ -85,18 +86,46 @@ func Pv(src io.Reader, info io.Writer) io.Reader { return r } -// Curl makes an http request by executing the unix command 'curl', and returns -// the body of the response. If `stderr` is not nil, a progress bar will be -// written to it. -func Curl(url string, stderr io.Writer) (io.Reader, error) { - curl := exec.Command("curl", "-#", "-L", url) - output, err := curl.StdoutPipe() - if err != nil { +// Request a given URL and return an io.Reader +func Download(url string, stderr io.Writer) (*http.Response, error) { + var resp *http.Response + var err error = nil + if resp, err = http.Get(url); err != nil { return nil, err } - curl.Stderr = stderr - if err := curl.Start(); err != nil { - return nil, err + if resp.StatusCode >= 400 { + return nil, errors.New("Got HTTP status code >= 400: " + resp.Status) } - return output, nil + return resp, nil +} + +// Reader with progress bar +type progressReader struct { + reader io.ReadCloser // Stream to read from + output io.Writer // Where to send progress bar to + read_total int // Expected stream length (bytes) + read_progress int // How much has been read so far (bytes) + last_update int // How many bytes read at least update +} +func (r *progressReader) Read(p []byte) (n int, err error) { + read, err := io.ReadCloser(r.reader).Read(p) + r.read_progress += read + + // Only update progress for every 1% read + update_every := int(0.01 * float64(r.read_total)) + if r.read_progress - r.last_update > update_every { + fmt.Fprintf(r.output, "%d/%d (%.0f%%)\r", + r.read_progress, + r.read_total, + float64(r.read_progress) / float64(r.read_total) * 100) + r.last_update = r.read_progress + } + + return read, err +} +func (r *progressReader) Close() error { + return io.ReadCloser(r.reader).Close() +} +func ProgressReader(r io.ReadCloser, size int, output io.Writer) *progressReader { + return &progressReader{r, output, size, 0, 0} }