func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.StreamFormatter) (string, error) { out = utils.NewWriteFlusher(out) img, err := srv.runtime.repositories.LookupImage(name) if err != nil { return "", err } file, err := utils.Download(url, out) if err != nil { return "", err } defer file.Body.Close() config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.capabilities) if err != nil { return "", err } b := NewBuilder(srv.runtime) c, err := b.Create(config) if err != nil { return "", err } if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), path); err != nil { return "", err } // FIXME: Handle custom repo, tag comment, author img, err = b.Commit(c, "", "", img.Comment, img.Author, nil) if err != nil { return "", err } out.Write(sf.FormatStatus(img.ID)) return img.ShortID(), nil }
func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error { out = utils.NewWriteFlusher(out) img, err := srv.runtime.repositories.LookupImage(name) if err != nil { return err } file, err := utils.Download(url, out) if err != nil { return err } defer file.Body.Close() config, _, err := ParseRun([]string{img.Id, "echo", "insert", url, path}, srv.runtime.capabilities) if err != nil { return err } b := NewBuilder(srv.runtime) c, err := b.Create(config) if err != nil { return err } if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)\r", false), path); err != nil { return err } // FIXME: Handle custom repo, tag comment, author img, err = b.Commit(c, "", "", img.Comment, img.Author, nil) if err != nil { return err } fmt.Fprintf(out, "%s\n", img.Id) return nil }
func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoint string, token []string, sf *utils.StreamFormatter) error { history, err := r.GetRemoteHistory(imgId, endpoint, token) if err != nil { return err } // FIXME: Try to stream the images? // FIXME: Launch the getRemoteImage() in goroutines for _, id := range history { if !srv.runtime.graph.Exists(id) { out.Write(sf.FormatStatus("Pulling %s metadata", id)) imgJSON, imgSize, err := r.GetRemoteImageJSON(id, endpoint, token) if err != nil { // FIXME: Keep goging in case of error? return err } img, err := NewImgJSON(imgJSON) if err != nil { return fmt.Errorf("Failed to parse json: %s", err) } // Get the layer out.Write(sf.FormatStatus("Pulling %s fs layer", id)) layer, err := r.GetRemoteImageLayer(img.ID, endpoint, token) if err != nil { return err } defer layer.Close() if err := srv.runtime.graph.Register(utils.ProgressReader(layer, imgSize, out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), false, img); err != nil { return err } } } return nil }
func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []string, json bool) error { history, err := srv.registry.GetRemoteHistory(imgId, registry, token) if err != nil { return err } // FIXME: Try to stream the images? // FIXME: Launch the getRemoteImage() in goroutines for _, id := range history { if !srv.runtime.graph.Exists(id) { fmt.Fprintf(out, utils.FormatStatus("Pulling %s metadata", json), id) imgJson, err := srv.registry.GetRemoteImageJson(id, registry, token) if err != nil { // FIXME: Keep goging in case of error? return err } img, err := NewImgJson(imgJson) if err != nil { return fmt.Errorf("Failed to parse json: %s", err) } // Get the layer fmt.Fprintf(out, utils.FormatStatus("Pulling %s fs layer", json), id) layer, contentLength, err := srv.registry.GetRemoteImageLayer(img.Id, registry, token) if err != nil { return err } if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, utils.FormatProgress("%v/%v (%v)", json), json), false, img); err != nil { return err } } } return nil }
func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId, ep string, token []string, sf *utils.StreamFormatter) error { out = utils.NewWriteFlusher(out) jsonRaw, err := ioutil.ReadFile(path.Join(srv.runtime.graph.Root, imgId, "json")) if err != nil { return fmt.Errorf("Error while retreiving the path for {%s}: %s", imgId, err) } out.Write(sf.FormatStatus("Pushing %s", imgId)) // Make sure we have the image's checksum checksum, err := srv.getChecksum(imgId) if err != nil { return err } imgData := ®istry.ImgData{ ID: imgId, Checksum: checksum, } // Send the json if err := r.PushImageJSONRegistry(imgData, jsonRaw, ep, token); err != nil { if err == registry.ErrAlreadyExists { out.Write(sf.FormatStatus("Image %s already uploaded ; skipping", imgData.ID)) return nil } return err } // Retrieve the tarball to be sent var layerData *TempArchive // If the archive exists, use it file, err := os.Open(layerArchivePath(srv.runtime.graph.imageRoot(imgId))) if err != nil { if os.IsNotExist(err) { // If the archive does not exist, create one from the layer layerData, err = srv.runtime.graph.TempLayerArchive(imgId, Xz, out) if err != nil { return fmt.Errorf("Failed to generate layer archive: %s", err) } } else { return err } } else { defer file.Close() st, err := file.Stat() if err != nil { return err } layerData = &TempArchive{ File: file, Size: st.Size(), } } // Send the layer if err := r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf.FormatProgress("Pushing", "%v/%v (%v)"), sf), ep, token); err != nil { return err } return nil }
func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgID, endpoint string, token []string, sf *utils.StreamFormatter) error { history, err := r.GetRemoteHistory(imgID, endpoint, token) if err != nil { return err } out.Write(sf.FormatProgress(utils.TruncateID(imgID), "Pulling", "dependend layers")) // FIXME: Try to stream the images? // FIXME: Launch the getRemoteImage() in goroutines for _, id := range history { // ensure no two downloads of the same layer happen at the same time if err := srv.poolAdd("pull", "layer:"+id); err != nil { utils.Debugf("Image (id: %s) pull is already running, skipping: %v", id, err) return nil } defer srv.poolRemove("pull", "layer:"+id) if !srv.runtime.graph.Exists(id) { out.Write(sf.FormatProgress(utils.TruncateID(id), "Pulling", "metadata")) imgJSON, imgSize, err := r.GetRemoteImageJSON(id, endpoint, token) if err != nil { out.Write(sf.FormatProgress(utils.TruncateID(id), "Error", "pulling dependend layers")) // FIXME: Keep going in case of error? return err } img, err := NewImgJSON(imgJSON) if err != nil { out.Write(sf.FormatProgress(utils.TruncateID(id), "Error", "pulling dependend layers")) return fmt.Errorf("Failed to parse json: %s", err) } // Get the layer out.Write(sf.FormatProgress(utils.TruncateID(id), "Pulling", "fs layer")) layer, err := r.GetRemoteImageLayer(img.ID, endpoint, token) if err != nil { out.Write(sf.FormatProgress(utils.TruncateID(id), "Error", "pulling dependend layers")) return err } defer layer.Close() if err := srv.runtime.graph.Register(imgJSON, utils.ProgressReader(layer, imgSize, out, sf.FormatProgress(utils.TruncateID(id), "Downloading", "%8v/%v (%v)"), sf, false), img); err != nil { out.Write(sf.FormatProgress(utils.TruncateID(id), "Error", "downloading dependend layers")) return err } } out.Write(sf.FormatProgress(utils.TruncateID(id), "Download", "complete")) } return nil }
// TempLayerArchive creates a temporary archive of the given image's filesystem layer. // The archive is stored on disk and will be automatically deleted as soon as has been read. // If output is not nil, a human-readable progress bar will be written to it. // FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives? func (graph *Graph) TempLayerArchive(id string, compression archive.Compression, sf *utils.StreamFormatter, output io.Writer) (*archive.TempArchive, error) { image, err := graph.Get(id) if err != nil { return nil, err } tmp, err := graph.tmp() if err != nil { return nil, err } a, err := image.TarLayer(compression) if err != nil { return nil, err } return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf.FormatProgress("", "Buffering to disk", "%v/%v (%v)"), sf, true), tmp.Root) }
// TempLayerArchive creates a temporary archive of the given image's filesystem layer. // The archive is stored on disk and will be automatically deleted as soon as has been read. // If output is not nil, a human-readable progress bar will be written to it. // FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives? func (graph *Graph) TempLayerArchive(id string, compression archive.Compression, sf *utils.StreamFormatter, output io.Writer) (*archive.TempArchive, error) { image, err := graph.Get(id) if err != nil { return nil, err } tmp, err := graph.Mktemp("") if err != nil { return nil, err } a, err := image.TarLayer() if err != nil { return nil, err } return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf, false, utils.TruncateID(id), "Buffering to disk"), tmp) }
// TempLayerArchive creates a temporary archive of the given image's filesystem layer. // The archive is stored on disk and will be automatically deleted as soon as has been read. // If output is not nil, a human-readable progress bar will be written to it. // FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives? func (graph *Graph) TempLayerArchive(id string, compression Compression, output io.Writer) (*TempArchive, error) { image, err := graph.Get(id) if err != nil { return nil, err } tmp, err := graph.tmp() if err != nil { return nil, err } archive, err := image.TarLayer(compression) if err != nil { return nil, err } return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)", false), tmp.Root) }
func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgID, ep string, token []string, sf *utils.StreamFormatter) (checksum string, err error) { out = utils.NewWriteFlusher(out) jsonRaw, err := ioutil.ReadFile(path.Join(srv.runtime.graph.Root, imgID, "json")) if err != nil { return "", fmt.Errorf("Cannot retrieve the path for {%s}: %s", imgID, err) } out.Write(sf.FormatStatus("", "Pushing %s", imgID)) imgData := ®istry.ImgData{ ID: imgID, } // Send the json if err := r.PushImageJSONRegistry(imgData, jsonRaw, ep, token); err != nil { if err == registry.ErrAlreadyExists { out.Write(sf.FormatStatus("", "Image %s already pushed, skipping", imgData.ID)) return "", nil } return "", err } layerData, err := srv.runtime.graph.TempLayerArchive(imgID, archive.Uncompressed, sf, out) if err != nil { return "", fmt.Errorf("Failed to generate layer archive: %s", err) } defer os.RemoveAll(layerData.Name()) // Send the layer checksum, err = r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf.FormatProgress("", "Pushing", "%8v/%v (%v)"), sf, false), ep, token, jsonRaw) if err != nil { return "", err } imgData.Checksum = checksum out.Write(sf.FormatStatus("", "")) // Send the checksum if err := r.PushImageChecksumRegistry(imgData, ep, token); err != nil { return "", err } return imgData.Checksum, nil }
func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Writer, sf *utils.StreamFormatter) error { var archive io.Reader var resp *http.Response if src == "-" { archive = in } else { u, err := url.Parse(src) if err != nil { return err } if u.Scheme == "" { u.Scheme = "http" u.Host = src u.Path = "" } out.Write(sf.FormatStatus("Downloading from %s", u)) // Download with curl (pretty progress bar) // If curl is not available, fallback to http.Get() resp, err = utils.Download(u.String(), out) if err != nil { return err } archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, sf.FormatProgress("Importing", "%v/%v (%v)"), sf) } img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil) if err != nil { return err } // Optionally register the image at REPO/TAG if repo != "" { if err := srv.runtime.repositories.Set(repo, tag, img.ID, true); err != nil { return err } } out.Write(sf.FormatStatus(img.ShortID())) return nil }
func (cli *DockerCli) CmdBuild(args ...string) error { cmd := Subcmd("build", "[OPTIONS] PATH | -", "Build a new container image from the source code at PATH") tag := cmd.String("t", "", "Tag to be applied to the resulting image in case of success") if err := cmd.Parse(args); err != nil { return nil } if cmd.NArg() != 1 { cmd.Usage() return nil } var ( multipartBody io.Reader file io.ReadCloser contextPath string ) // Init the needed component for the Multipart buff := bytes.NewBuffer([]byte{}) multipartBody = buff w := multipart.NewWriter(buff) boundary := strings.NewReader("\r\n--" + w.Boundary() + "--\r\n") compression := Bzip2 if cmd.Arg(0) == "-" { file = os.Stdin } else { // Send Dockerfile from arg/Dockerfile (deprecate later) if f, err := os.Open(path.Join(cmd.Arg(0), "Dockerfile")); err != nil { return err } else { file = f } // Send context from arg // Create a FormFile multipart for the context if needed // FIXME: Use NewTempArchive in order to have the size and avoid too much memory usage? context, err := Tar(cmd.Arg(0), compression) if err != nil { return err } // NOTE: Do this in case '.' or '..' is input absPath, err := filepath.Abs(cmd.Arg(0)) if err != nil { return err } if wField, err := w.CreateFormFile("Context", filepath.Base(absPath)+"."+compression.Extension()); err != nil { return err } else { // FIXME: Find a way to have a progressbar for the upload too sf := utils.NewStreamFormatter(false) io.Copy(wField, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, sf.FormatProgress("Caching Context", "%v/%v (%v)"), sf)) } multipartBody = io.MultiReader(multipartBody, boundary) } // Create a FormFile multipart for the Dockerfile if wField, err := w.CreateFormFile("Dockerfile", "Dockerfile"); err != nil { return err } else { io.Copy(wField, file) } multipartBody = io.MultiReader(multipartBody, boundary) v := &url.Values{} v.Set("t", *tag) // Send the multipart request with correct content-type req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s?%s", cli.host, cli.port, "/build", v.Encode()), multipartBody) if err != nil { return err } req.Header.Set("Content-Type", w.FormDataContentType()) if contextPath != "" { req.Header.Set("X-Docker-Context-Compression", compression.Flag()) fmt.Println("Uploading Context...") } resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() // Check for errors if resp.StatusCode < 200 || resp.StatusCode >= 400 { body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } return fmt.Errorf("error: %s", body) } // Output the result if _, err := io.Copy(os.Stdout, resp.Body); err != nil { return err } return nil }
func (cli *DockerCli) CmdBuild(args ...string) error { cmd := Subcmd("build", "[OPTIONS] PATH | URL | -", "Build a new container image from the source code at PATH") tag := cmd.String("t", "", "Tag to be applied to the resulting image in case of success") suppressOutput := cmd.Bool("q", false, "Suppress verbose build output") if err := cmd.Parse(args); err != nil { return nil } if cmd.NArg() != 1 { cmd.Usage() return nil } var ( context Archive isRemote bool err error ) if cmd.Arg(0) == "-" { // As a special case, 'docker build -' will build from an empty context with the // contents of stdin as a Dockerfile dockerfile, err := ioutil.ReadAll(cli.in) if err != nil { return err } context, err = mkBuildContext(string(dockerfile), nil) } else if utils.IsURL(cmd.Arg(0)) || utils.IsGIT(cmd.Arg(0)) { isRemote = true } else { context, err = Tar(cmd.Arg(0), Uncompressed) } var body io.Reader // Setup an upload progress bar // FIXME: ProgressReader shouldn't be this annoyning to use if context != nil { sf := utils.NewStreamFormatter(false) body = utils.ProgressReader(ioutil.NopCloser(context), 0, cli.err, sf.FormatProgress("Uploading context", "%v bytes%0.0s%0.0s"), sf) } // Upload the build context v := &url.Values{} v.Set("t", *tag) if *suppressOutput { v.Set("q", "1") } if isRemote { v.Set("remote", cmd.Arg(0)) } req, err := http.NewRequest("POST", fmt.Sprintf("/v%g/build?%s", APIVERSION, v.Encode()), body) if err != nil { return err } if context != nil { req.Header.Set("Content-Type", "application/tar") } dial, err := net.Dial(cli.proto, cli.addr) if err != nil { return err } clientconn := httputil.NewClientConn(dial, nil) resp, err := clientconn.Do(req) defer clientconn.Close() if err != nil { return err } defer resp.Body.Close() // Check for errors if resp.StatusCode < 200 || resp.StatusCode >= 400 { body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } if len(body) == 0 { return fmt.Errorf("Error: %s", http.StatusText(resp.StatusCode)) } return fmt.Errorf("Error: %s", body) } // Output the result if _, err := io.Copy(cli.out, resp.Body); err != nil { return err } return nil }
func (cli *DockerCli) CmdBuild(args ...string) error { cmd := Subcmd("build", "[OPTIONS] [CONTEXT]", "Build an image from a Dockerfile") fileName := cmd.String("f", "Dockerfile", "Use `file` as Dockerfile. Can be '-' for stdin") if err := cmd.Parse(args); err != nil { return nil } var ( file io.ReadCloser multipartBody io.Reader err error ) // Init the needed component for the Multipart buff := bytes.NewBuffer([]byte{}) multipartBody = buff w := multipart.NewWriter(buff) boundary := strings.NewReader("\r\n--" + w.Boundary() + "--\r\n") // Create a FormFile multipart for the Dockerfile if *fileName == "-" { file = os.Stdin } else { file, err = os.Open(*fileName) if err != nil { return err } defer file.Close() } if wField, err := w.CreateFormFile("Dockerfile", *fileName); err != nil { return err } else { io.Copy(wField, file) } multipartBody = io.MultiReader(multipartBody, boundary) compression := Bzip2 // Create a FormFile multipart for the context if needed if cmd.Arg(0) != "" { // FIXME: Use NewTempArchive in order to have the size and avoid too much memory usage? context, err := Tar(cmd.Arg(0), compression) if err != nil { return err } // NOTE: Do this in case '.' or '..' is input absPath, err := filepath.Abs(cmd.Arg(0)) if err != nil { return err } if wField, err := w.CreateFormFile("Context", filepath.Base(absPath)+"."+compression.Extension()); err != nil { return err } else { // FIXME: Find a way to have a progressbar for the upload too io.Copy(wField, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, "Caching Context %v/%v (%v)\r", false)) } multipartBody = io.MultiReader(multipartBody, boundary) } // Send the multipart request with correct content-type req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s", cli.host, cli.port, "/build"), multipartBody) if err != nil { return err } req.Header.Set("Content-Type", w.FormDataContentType()) if cmd.Arg(0) != "" { req.Header.Set("X-Docker-Context-Compression", compression.Flag()) fmt.Println("Uploading Context...") } resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() // Check for errors if resp.StatusCode < 200 || resp.StatusCode >= 400 { body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } return fmt.Errorf("error: %s", body) } // Output the result if _, err := io.Copy(os.Stdout, resp.Body); err != nil { return err } return nil }