// Stream the tarball from docker and translate it into the portable repo format // Note that its easier to handle as a stream on the way out. func (cli *DogestryCli) exportImageToFiles(image, root string, saveIds set) error { fmt.Printf("Exporting image: %v to: %v\n", image, root) reader, writer := io.Pipe() defer writer.Close() defer reader.Close() tarball := tar.NewReader(reader) errch := make(chan error) go func() { defer close(errch) for { header, err := tarball.Next() if err == io.EOF { break } if err != nil { errch <- err return } parts := strings.Split(header.Name, "/") idFromFile := remote.ID(parts[0]) if _, ok := saveIds[idFromFile]; ok { if err := cli.createFileFromTar(root, header, tarball); err != nil { errch <- err return } } else { // Drain the reader. Is this necessary? if _, err := io.Copy(ioutil.Discard, tarball); err != nil { errch <- err return } } } errch <- nil }() if err := cli.Client.ExportImage(docker.ExportImageOptions{image, writer}); err != nil { return err } // wait for the tar reader if err := <-errch; err != nil { return err } return nil }
func (cli *DogestryCli) exportToFiles(image string, r remote.Remote, imageRoot string) error { imageHistory, err := cli.Client.ImageHistory(image) if err != nil { fmt.Printf("Error getting image history: %v\n", err) return err } fmt.Println("Checking layers on remote") imageID := remote.ID(imageHistory[0].ID) repoName, repoTag := remote.NormaliseImageName(image) // Check the remote to see what layers are missing. Only missing Ids will // need to be saved to disk when exporting the docker image. missingIds := make(set) for _, i := range imageHistory { id := remote.ID(i.ID) _, err = r.ImageMetadata(id) if err == nil { fmt.Printf(" exists : %v\n", id) } else { fmt.Printf(" not found: %v\n", id) missingIds[id] = empty } } if len(missingIds) > 0 { if err := cli.exportImageToFiles(image, imageRoot, missingIds); err != nil { return err } } if err := cli.exportMetaDataToFiles(repoName, repoTag, imageID, imageRoot); err != nil { return err } return nil }