mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Add parent references support to load/save
Restores the correct parent chain relationship between images on docker load if multiple images have been saved. Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
parent
c22d09f563
commit
faeff5118f
4 changed files with 110 additions and 0 deletions
|
@ -7,6 +7,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/image"
|
"github.com/docker/docker/image"
|
||||||
|
@ -58,6 +59,8 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parentLinks []parentLink
|
||||||
|
|
||||||
for _, m := range manifest {
|
for _, m := range manifest {
|
||||||
configPath, err := safePath(tmpDir, m.Config)
|
configPath, err := safePath(tmpDir, m.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -117,11 +120,35 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
|
||||||
l.setLoadedTag(ref, imgID, outStream)
|
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
|
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) {
|
func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, progressOutput progress.Output) (layer.Layer, error) {
|
||||||
rawTar, err := os.Open(filename)
|
rawTar, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -309,3 +336,36 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str
|
||||||
func safePath(base, path string) (string, error) {
|
func safePath(base, path string) (string, error) {
|
||||||
return symlink.FollowSymlinkInScope(filepath.Join(base, path), base)
|
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)
|
reposLegacy := make(map[string]map[string]string)
|
||||||
|
|
||||||
var manifest []manifestItem
|
var manifest []manifestItem
|
||||||
|
var parentLinks []parentLink
|
||||||
|
|
||||||
for id, imageDescr := range s.images {
|
for id, imageDescr := range s.images {
|
||||||
if err = s.saveImage(id); err != nil {
|
if err = s.saveImage(id); err != nil {
|
||||||
|
@ -154,6 +155,15 @@ func (s *saveSession) save(outStream io.Writer) error {
|
||||||
RepoTags: repoTags,
|
RepoTags: repoTags,
|
||||||
Layers: layers,
|
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 {
|
if len(reposLegacy) > 0 {
|
||||||
|
|
|
@ -18,6 +18,7 @@ type manifestItem struct {
|
||||||
Config string
|
Config string
|
||||||
RepoTags []string
|
RepoTags []string
|
||||||
Layers []string
|
Layers []string
|
||||||
|
Parent image.ID `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type tarexporter struct {
|
type tarexporter struct {
|
||||||
|
|
|
@ -311,3 +311,42 @@ func (s *DockerSuite) TestLoadZeroSizeLayer(c *check.C) {
|
||||||
|
|
||||||
dockerCmd(c, "load", "-i", "fixtures/load/emptyLayer.tar")
|
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…
Add table
Reference in a new issue