From fd7ab143bf1ecfbf20df90edd876f73dc28c264d Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 20 Nov 2013 17:28:19 -0800 Subject: [PATCH] Allow images to be saved and loaded by id and repository --- api.go | 12 +--- commands.go | 13 ++--- server.go | 163 ++++++++++++++++++++++++++++++++-------------------- 3 files changed, 108 insertions(+), 80 deletions(-) diff --git a/api.go b/api.go index 3bbcdfd43d..1104b787b8 100644 --- a/api.go +++ b/api.go @@ -539,19 +539,11 @@ func getImagesGet(srv *Server, version float64, w http.ResponseWriter, r *http.R if version > 1.0 { w.Header().Set("Content-Type", "application/x-tar") } - err := srv.ImageExport(name, w) - if err != nil { - return err - } - return nil + return srv.ImageExport(name, w) } func postImagesLoad(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - err := srv.ImageLoad(r.Body) - if err != nil { - return err - } - return nil + return srv.ImageLoad(r.Body) } func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { diff --git a/commands.go b/commands.go index 752ed82906..3b56f74e5d 100644 --- a/commands.go +++ b/commands.go @@ -1969,8 +1969,7 @@ func (cli *DockerCli) CmdCp(args ...string) error { func (cli *DockerCli) CmdSave(args ...string) error { cmd := Subcmd("save", "IMAGE DESTINATION", "Save an image to a tar archive") if err := cmd.Parse(args); err != nil { - cmd.Usage() - return nil + return err } if cmd.NArg() != 1 { @@ -1979,7 +1978,6 @@ func (cli *DockerCli) CmdSave(args ...string) error { } image := cmd.Arg(0) - if err := cli.stream("GET", "/images/"+image+"/get", nil, cli.out, nil); err != nil { return err } @@ -1988,17 +1986,18 @@ func (cli *DockerCli) CmdSave(args ...string) error { func (cli *DockerCli) CmdLoad(args ...string) error { cmd := Subcmd("load", "SOURCE", "Load an image from a tar archive") + if err := cmd.Parse(args); err != nil { + return err + } if cmd.NArg() != 0 { cmd.Usage() return nil } - err := cli.stream("POST", "/images/load", cli.in, cli.out, nil) - if err != nil { - fmt.Println("Send failed", err) + if err := cli.stream("POST", "/images/load", cli.in, cli.out, nil); err != nil { + return err } - return nil } diff --git a/server.go b/server.go index 4cc808bb52..39c6dfdc6d 100644 --- a/server.go +++ b/server.go @@ -212,67 +212,38 @@ func (srv *Server) ImageExport(name string, out io.Writer) error { utils.Debugf("Serializing %s", name) - rootRepo := srv.runtime.repositories.Repositories[name] - for _, rootImage := range rootRepo { - image, _ := srv.ImageInspect(rootImage) - for i := image; i != nil; { - // temporary directory - tmpImageDir := path.Join(tempdir, i.ID) - if err := os.Mkdir(tmpImageDir, os.ModeDir); err != nil { - return err - } - defer os.RemoveAll(tmpImageDir) - - var version = "1.0" - var versionBuf = []byte(version) - - if err := ioutil.WriteFile(path.Join(tmpImageDir, "VERSION"), versionBuf, os.ModeAppend); err != nil { - return err - } - - // serialize json - b, err := json.Marshal(i) - if err != nil { - return err - } - if err := ioutil.WriteFile(path.Join(tmpImageDir, "json"), b, os.ModeAppend); err != nil { - return err - } - - // serialize filesystem - fs, err := archive.Tar(path.Join(srv.runtime.graph.Root, i.ID, "layer"), archive.Uncompressed) + rootRepo, err := srv.runtime.repositories.Get(name) + if err != nil { + return err + } + if rootRepo != nil { + for _, id := range rootRepo { + image, err := srv.ImageInspect(id) if err != nil { return err } - fsTar, err := os.Create(path.Join(tmpImageDir, "layer.tar")) - if err != nil { + if err := srv.exportImage(image, tempdir); err != nil { return err } - if _, err = io.Copy(fsTar, fs); err != nil { - return err - } - fsTar.Close() - - // find parent - if i.Parent != "" { - i, err = srv.ImageInspect(i.Parent) - if err != nil { - return err - } - } else { - i = nil - } } - } - // write repositories - rootRepoMap := map[string]Repository{} - rootRepoMap[name] = rootRepo - rootRepoJson, _ := json.Marshal(rootRepoMap) + // write repositories + rootRepoMap := map[string]Repository{} + rootRepoMap[name] = rootRepo + rootRepoJson, _ := json.Marshal(rootRepoMap) - if err := ioutil.WriteFile(path.Join(tempdir, "repositories"), rootRepoJson, os.ModeAppend); err != nil { - return err + if err := ioutil.WriteFile(path.Join(tempdir, "repositories"), rootRepoJson, os.ModeAppend); err != nil { + return err + } + } else { + image, err := srv.ImageInspect(name) + if err != nil { + return err + } + if err := srv.exportImage(image, tempdir); err != nil { + return err + } } fs, err := archive.Tar(tempdir, archive.Uncompressed) @@ -286,6 +257,58 @@ func (srv *Server) ImageExport(name string, out io.Writer) error { return nil } +func (srv *Server) exportImage(image *Image, tempdir string) error { + for i := image; i != nil; { + // temporary directory + tmpImageDir := path.Join(tempdir, i.ID) + if err := os.Mkdir(tmpImageDir, os.ModeDir); err != nil { + return err + } + + var version = "1.0" + var versionBuf = []byte(version) + + if err := ioutil.WriteFile(path.Join(tmpImageDir, "VERSION"), versionBuf, os.ModeAppend); err != nil { + return err + } + + // serialize json + b, err := json.Marshal(i) + if err != nil { + return err + } + if err := ioutil.WriteFile(path.Join(tmpImageDir, "json"), b, os.ModeAppend); err != nil { + return err + } + + // serialize filesystem + fs, err := archive.Tar(path.Join(srv.runtime.graph.Root, i.ID, "layer"), archive.Uncompressed) + if err != nil { + return err + } + + fsTar, err := os.Create(path.Join(tmpImageDir, "layer.tar")) + if err != nil { + return err + } + if _, err = io.Copy(fsTar, fs); err != nil { + return err + } + fsTar.Close() + + // find parent + if i.Parent != "" { + i, err = srv.ImageInspect(i.Parent) + if err != nil { + return err + } + } else { + i = nil + } + } + return nil +} + // Loads a set of images into the repository. This is the complementary of ImageExport. // The input stream is an uncompressed tar ball containing images and metadata. func (srv *Server) ImageLoad(in io.Reader) error { @@ -319,25 +342,38 @@ func (srv *Server) ImageLoad(in io.Reader) error { if err := archive.Untar(repoFile, repoDir); err != nil { return err } - repositoriesJson, err := ioutil.ReadFile(path.Join(tmpImageDir, "repo", "repositories")) + + dirs, err := ioutil.ReadDir(repoDir) if err != nil { return err } - repositories := map[string]Repository{} - if err := json.Unmarshal(repositoriesJson, &repositories); err != nil { - return err - } - for imageName, tagMap := range repositories { - for tag, address := range tagMap { - if err := srv.recursiveLoad(address, tmpImageDir); err != nil { - return err - } - if err := srv.runtime.repositories.Set(imageName, tag, address, true); err != nil { + for _, d := range dirs { + if d.IsDir() { + if err := srv.recursiveLoad(d.Name(), tmpImageDir); err != nil { return err } } } + + repositoriesJson, err := ioutil.ReadFile(path.Join(tmpImageDir, "repo", "repositories")) + if err == nil { + repositories := map[string]Repository{} + if err := json.Unmarshal(repositoriesJson, &repositories); err != nil { + return err + } + + for imageName, tagMap := range repositories { + for tag, address := range tagMap { + if err := srv.runtime.repositories.Set(imageName, tag, address, true); err != nil { + return err + } + } + } + } else if !os.IsNotExist(err) { + return err + } + return nil } @@ -373,6 +409,7 @@ func (srv *Server) recursiveLoad(address, tmpImageDir string) error { } } utils.Debugf("Completed processing %s", address) + return nil }