Merge pull request #21385 from tonistiigi/load-parent-chain
Add parent references support to load/save
This commit is contained in:
commit
acaec7f67c
4 changed files with 110 additions and 0 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -18,6 +18,7 @@ type manifestItem struct {
|
|||
Config string
|
||||
RepoTags []string
|
||||
Layers []string
|
||||
Parent image.ID `json:",omitempty"`
|
||||
}
|
||||
|
||||
type tarexporter struct {
|
||||
|
|
|
@ -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, "")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue