Sfoglia il codice sorgente

Merge pull request #21385 from tonistiigi/load-parent-chain

Add parent references support to load/save
David Calavera 9 anni fa
parent
commit
acaec7f67c

+ 60 - 0
image/tarexport/load.go

@@ -7,6 +7,7 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"reflect"
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/image"
@@ -58,6 +59,8 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
 		return err
 	}
 
+	var parentLinks []parentLink
+
 	for _, m := range manifest {
 		configPath, err := safePath(tmpDir, m.Config)
 		if err != nil {
@@ -117,11 +120,35 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
 			l.setLoadedTag(ref, imgID, outStream)
 		}
 
+		parentLinks = append(parentLinks, parentLink{imgID, m.Parent})
+	}
+
+	for _, p := range validatedParentLinks(parentLinks) {
+		if p.parentID != "" {
+			if err := l.setParentID(p.id, p.parentID); err != nil {
+				return err
+			}
+		}
 	}
 
 	return nil
 }
 
+func (l *tarexporter) setParentID(id, parentID image.ID) error {
+	img, err := l.is.Get(id)
+	if err != nil {
+		return err
+	}
+	parent, err := l.is.Get(parentID)
+	if err != nil {
+		return err
+	}
+	if !checkValidParent(img, parent) {
+		return fmt.Errorf("image %v is not a valid parent for %v", parent.ID, img.ID)
+	}
+	return l.is.SetParent(id, parentID)
+}
+
 func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, progressOutput progress.Output) (layer.Layer, error) {
 	rawTar, err := os.Open(filename)
 	if err != nil {
@@ -309,3 +336,36 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str
 func safePath(base, path string) (string, error) {
 	return symlink.FollowSymlinkInScope(filepath.Join(base, path), base)
 }
+
+type parentLink struct {
+	id, parentID image.ID
+}
+
+func validatedParentLinks(pl []parentLink) (ret []parentLink) {
+mainloop:
+	for i, p := range pl {
+		ret = append(ret, p)
+		for _, p2 := range pl {
+			if p2.id == p.parentID && p2.id != p.id {
+				continue mainloop
+			}
+		}
+		ret[i].parentID = ""
+	}
+	return
+}
+
+func checkValidParent(img, parent *image.Image) bool {
+	if len(img.History) == 0 && len(parent.History) == 0 {
+		return true // having history is not mandatory
+	}
+	if len(img.History)-len(parent.History) != 1 {
+		return false
+	}
+	for i, h := range parent.History {
+		if !reflect.DeepEqual(h, img.History[i]) {
+			return false
+		}
+	}
+	return true
+}

+ 10 - 0
image/tarexport/save.go

@@ -128,6 +128,7 @@ func (s *saveSession) save(outStream io.Writer) error {
 	reposLegacy := make(map[string]map[string]string)
 
 	var manifest []manifestItem
+	var parentLinks []parentLink
 
 	for id, imageDescr := range s.images {
 		if err = s.saveImage(id); err != nil {
@@ -154,6 +155,15 @@ func (s *saveSession) save(outStream io.Writer) error {
 			RepoTags: repoTags,
 			Layers:   layers,
 		})
+
+		parentID, _ := s.is.GetParent(id)
+		parentLinks = append(parentLinks, parentLink{id, parentID})
+	}
+
+	for i, p := range validatedParentLinks(parentLinks) {
+		if p.parentID != "" {
+			manifest[i].Parent = p.parentID
+		}
 	}
 
 	if len(reposLegacy) > 0 {

+ 1 - 0
image/tarexport/tarexport.go

@@ -18,6 +18,7 @@ type manifestItem struct {
 	Config   string
 	RepoTags []string
 	Layers   []string
+	Parent   image.ID `json:",omitempty"`
 }
 
 type tarexporter struct {

+ 39 - 0
integration-cli/docker_cli_save_load_test.go

@@ -311,3 +311,42 @@ func (s *DockerSuite) TestLoadZeroSizeLayer(c *check.C) {
 
 	dockerCmd(c, "load", "-i", "fixtures/load/emptyLayer.tar")
 }
+
+func (s *DockerSuite) TestSaveLoadParents(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+
+	makeImage := func(from string, addfile string) string {
+		var (
+			out string
+		)
+		out, _ = dockerCmd(c, "run", "-d", from, "touch", addfile)
+		cleanedContainerID := strings.TrimSpace(out)
+
+		out, _ = dockerCmd(c, "commit", cleanedContainerID)
+		imageID := strings.TrimSpace(out)
+
+		dockerCmd(c, "rm", cleanedContainerID)
+		return imageID
+	}
+
+	idFoo := makeImage("busybox", "foo")
+	idBar := makeImage(idFoo, "bar")
+
+	tmpDir, err := ioutil.TempDir("", "save-load-parents")
+	c.Assert(err, checker.IsNil)
+	defer os.RemoveAll(tmpDir)
+
+	c.Log("tmpdir", tmpDir)
+
+	outfile := filepath.Join(tmpDir, "out.tar")
+
+	dockerCmd(c, "save", "-o", outfile, idBar, idFoo)
+	dockerCmd(c, "rmi", idBar)
+	dockerCmd(c, "load", "-i", outfile)
+
+	inspectOut := inspectField(c, idBar, "Parent")
+	c.Assert(inspectOut, checker.Equals, idFoo)
+
+	inspectOut = inspectField(c, idFoo, "Parent")
+	c.Assert(inspectOut, checker.Equals, "")
+}