diff --git a/graph/export.go b/graph/export.go new file mode 100644 index 0000000000..81b78ca50d --- /dev/null +++ b/graph/export.go @@ -0,0 +1,147 @@ +package graph + +import ( + "encoding/json" + "io" + "io/ioutil" + "os" + "path" + + "github.com/docker/docker/archive" + "github.com/docker/docker/engine" + "github.com/docker/docker/pkg/parsers" + "github.com/docker/docker/utils" +) + +// CmdImageExport exports all images with the given tag. All versions +// containing the same tag are exported. The resulting output is an +// uncompressed tar ball. +// name is the set of tags to export. +// out is the writer where the images are written to. +func (s *TagStore) CmdImageExport(job *engine.Job) engine.Status { + if len(job.Args) != 1 { + return job.Errorf("Usage: %s IMAGE\n", job.Name) + } + name := job.Args[0] + // get image json + tempdir, err := ioutil.TempDir("", "docker-export-") + if err != nil { + return job.Error(err) + } + defer os.RemoveAll(tempdir) + + utils.Debugf("Serializing %s", name) + + rootRepoMap := map[string]Repository{} + rootRepo, err := s.Get(name) + if err != nil { + return job.Error(err) + } + if rootRepo != nil { + // this is a base repo name, like 'busybox' + + for _, id := range rootRepo { + if err := s.exportImage(job.Eng, id, tempdir); err != nil { + return job.Error(err) + } + } + rootRepoMap[name] = rootRepo + } else { + img, err := s.LookupImage(name) + if err != nil { + return job.Error(err) + } + if img != nil { + // This is a named image like 'busybox:latest' + repoName, repoTag := parsers.ParseRepositoryTag(name) + if err := s.exportImage(job.Eng, img.ID, tempdir); err != nil { + return job.Error(err) + } + // check this length, because a lookup of a truncated has will not have a tag + // and will not need to be added to this map + if len(repoTag) > 0 { + rootRepoMap[repoName] = Repository{repoTag: img.ID} + } + } else { + // this must be an ID that didn't get looked up just right? + if err := s.exportImage(job.Eng, name, tempdir); err != nil { + return job.Error(err) + } + } + } + // write repositories, if there is something to write + if len(rootRepoMap) > 0 { + rootRepoJson, _ := json.Marshal(rootRepoMap) + + if err := ioutil.WriteFile(path.Join(tempdir, "repositories"), rootRepoJson, os.FileMode(0644)); err != nil { + return job.Error(err) + } + } else { + utils.Debugf("There were no repositories to write") + } + + fs, err := archive.Tar(tempdir, archive.Uncompressed) + if err != nil { + return job.Error(err) + } + defer fs.Close() + + if _, err := io.Copy(job.Stdout, fs); err != nil { + return job.Error(err) + } + utils.Debugf("End Serializing %s", name) + return engine.StatusOK +} + +// FIXME: this should be a top-level function, not a class method +func (s *TagStore) exportImage(eng *engine.Engine, name, tempdir string) error { + for n := name; n != ""; { + // temporary directory + tmpImageDir := path.Join(tempdir, n) + if err := os.Mkdir(tmpImageDir, os.FileMode(0755)); err != nil { + if os.IsExist(err) { + return nil + } + return err + } + + var version = "1.0" + var versionBuf = []byte(version) + + if err := ioutil.WriteFile(path.Join(tmpImageDir, "VERSION"), versionBuf, os.FileMode(0644)); err != nil { + return err + } + + // serialize json + json, err := os.Create(path.Join(tmpImageDir, "json")) + if err != nil { + return err + } + job := eng.Job("image_inspect", n) + job.SetenvBool("raw", true) + job.Stdout.Add(json) + if err := job.Run(); err != nil { + return err + } + + // serialize filesystem + fsTar, err := os.Create(path.Join(tmpImageDir, "layer.tar")) + if err != nil { + return err + } + job = eng.Job("image_tarlayer", n) + job.Stdout.Add(fsTar) + if err := job.Run(); err != nil { + return err + } + + // find parent + job = eng.Job("image_get", n) + info, _ := job.Stdout.AddEnv() + if err := job.Run(); err != nil { + return err + } + n = info.Get("Parent") + } + return nil +} diff --git a/graph/service.go b/graph/service.go index 75c2b5c846..a2642cce8c 100644 --- a/graph/service.go +++ b/graph/service.go @@ -15,6 +15,7 @@ func (s *TagStore) Install(eng *engine.Engine) error { eng.Register("image_get", s.CmdGet) eng.Register("image_inspect", s.CmdLookup) eng.Register("image_tarlayer", s.CmdTarLayer) + eng.Register("image_export", s.CmdImageExport) return nil } diff --git a/server/image.go b/server/image.go index acc8794985..37060dd009 100644 --- a/server/image.go +++ b/server/image.go @@ -30,138 +30,6 @@ import ( "github.com/docker/docker/utils" ) -// ImageExport exports all images with the given tag. All versions -// containing the same tag are exported. The resulting output is an -// uncompressed tar ball. -// name is the set of tags to export. -// out is the writer where the images are written to. -func (srv *Server) ImageExport(job *engine.Job) engine.Status { - if len(job.Args) != 1 { - return job.Errorf("Usage: %s IMAGE\n", job.Name) - } - name := job.Args[0] - // get image json - tempdir, err := ioutil.TempDir("", "docker-export-") - if err != nil { - return job.Error(err) - } - defer os.RemoveAll(tempdir) - - utils.Debugf("Serializing %s", name) - - rootRepoMap := map[string]graph.Repository{} - rootRepo, err := srv.daemon.Repositories().Get(name) - if err != nil { - return job.Error(err) - } - if rootRepo != nil { - // this is a base repo name, like 'busybox' - - for _, id := range rootRepo { - if err := srv.exportImage(job.Eng, id, tempdir); err != nil { - return job.Error(err) - } - } - rootRepoMap[name] = rootRepo - } else { - img, err := srv.daemon.Repositories().LookupImage(name) - if err != nil { - return job.Error(err) - } - if img != nil { - // This is a named image like 'busybox:latest' - repoName, repoTag := parsers.ParseRepositoryTag(name) - if err := srv.exportImage(job.Eng, img.ID, tempdir); err != nil { - return job.Error(err) - } - // check this length, because a lookup of a truncated has will not have a tag - // and will not need to be added to this map - if len(repoTag) > 0 { - rootRepoMap[repoName] = graph.Repository{repoTag: img.ID} - } - } else { - // this must be an ID that didn't get looked up just right? - if err := srv.exportImage(job.Eng, name, tempdir); err != nil { - return job.Error(err) - } - } - } - // write repositories, if there is something to write - if len(rootRepoMap) > 0 { - rootRepoJson, _ := json.Marshal(rootRepoMap) - - if err := ioutil.WriteFile(path.Join(tempdir, "repositories"), rootRepoJson, os.FileMode(0644)); err != nil { - return job.Error(err) - } - } else { - utils.Debugf("There were no repositories to write") - } - - fs, err := archive.Tar(tempdir, archive.Uncompressed) - if err != nil { - return job.Error(err) - } - defer fs.Close() - - if _, err := io.Copy(job.Stdout, fs); err != nil { - return job.Error(err) - } - utils.Debugf("End Serializing %s", name) - return engine.StatusOK -} - -func (srv *Server) exportImage(eng *engine.Engine, name, tempdir string) error { - for n := name; n != ""; { - // temporary directory - tmpImageDir := path.Join(tempdir, n) - if err := os.Mkdir(tmpImageDir, os.FileMode(0755)); err != nil { - if os.IsExist(err) { - return nil - } - return err - } - - var version = "1.0" - var versionBuf = []byte(version) - - if err := ioutil.WriteFile(path.Join(tmpImageDir, "VERSION"), versionBuf, os.FileMode(0644)); err != nil { - return err - } - - // serialize json - json, err := os.Create(path.Join(tmpImageDir, "json")) - if err != nil { - return err - } - job := eng.Job("image_inspect", n) - job.SetenvBool("raw", true) - job.Stdout.Add(json) - if err := job.Run(); err != nil { - return err - } - - // serialize filesystem - fsTar, err := os.Create(path.Join(tmpImageDir, "layer.tar")) - if err != nil { - return err - } - job = eng.Job("image_tarlayer", n) - job.Stdout.Add(fsTar) - if err := job.Run(); err != nil { - return err - } - - // find parent - job = eng.Job("image_get", n) - info, _ := job.Stdout.AddEnv() - if err := job.Run(); err != nil { - return err - } - n = info.Get("Parent") - } - return nil -} - func (srv *Server) Build(job *engine.Job) engine.Status { if len(job.Args) != 0 { return job.Errorf("Usage: %s\n", job.Name) diff --git a/server/init.go b/server/init.go index cbe5340f21..760e9dbefd 100644 --- a/server/init.go +++ b/server/init.go @@ -88,7 +88,6 @@ func InitServer(job *engine.Job) engine.Status { for name, handler := range map[string]engine.Handler{ "tag": srv.ImageTag, // FIXME merge with "image_tag" "info": srv.DockerInfo, - "image_export": srv.ImageExport, "images": srv.Images, "history": srv.ImageHistory, "viz": srv.ImagesViz,