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>
| ... | ... |
@@ -7,6 +7,7 @@ import ( |
| 7 | 7 |
"io/ioutil" |
| 8 | 8 |
"os" |
| 9 | 9 |
"path/filepath" |
| 10 |
+ "reflect" |
|
| 10 | 11 |
|
| 11 | 12 |
"github.com/Sirupsen/logrus" |
| 12 | 13 |
"github.com/docker/docker/image" |
| ... | ... |
@@ -58,6 +59,8 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool) |
| 58 | 58 |
return err |
| 59 | 59 |
} |
| 60 | 60 |
|
| 61 |
+ var parentLinks []parentLink |
|
| 62 |
+ |
|
| 61 | 63 |
for _, m := range manifest {
|
| 62 | 64 |
configPath, err := safePath(tmpDir, m.Config) |
| 63 | 65 |
if err != nil {
|
| ... | ... |
@@ -117,11 +120,35 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool) |
| 117 | 117 |
l.setLoadedTag(ref, imgID, outStream) |
| 118 | 118 |
} |
| 119 | 119 |
|
| 120 |
+ parentLinks = append(parentLinks, parentLink{imgID, m.Parent})
|
|
| 121 |
+ } |
|
| 122 |
+ |
|
| 123 |
+ for _, p := range validatedParentLinks(parentLinks) {
|
|
| 124 |
+ if p.parentID != "" {
|
|
| 125 |
+ if err := l.setParentID(p.id, p.parentID); err != nil {
|
|
| 126 |
+ return err |
|
| 127 |
+ } |
|
| 128 |
+ } |
|
| 120 | 129 |
} |
| 121 | 130 |
|
| 122 | 131 |
return nil |
| 123 | 132 |
} |
| 124 | 133 |
|
| 134 |
+func (l *tarexporter) setParentID(id, parentID image.ID) error {
|
|
| 135 |
+ img, err := l.is.Get(id) |
|
| 136 |
+ if err != nil {
|
|
| 137 |
+ return err |
|
| 138 |
+ } |
|
| 139 |
+ parent, err := l.is.Get(parentID) |
|
| 140 |
+ if err != nil {
|
|
| 141 |
+ return err |
|
| 142 |
+ } |
|
| 143 |
+ if !checkValidParent(img, parent) {
|
|
| 144 |
+ return fmt.Errorf("image %v is not a valid parent for %v", parent.ID, img.ID)
|
|
| 145 |
+ } |
|
| 146 |
+ return l.is.SetParent(id, parentID) |
|
| 147 |
+} |
|
| 148 |
+ |
|
| 125 | 149 |
func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, progressOutput progress.Output) (layer.Layer, error) {
|
| 126 | 150 |
rawTar, err := os.Open(filename) |
| 127 | 151 |
if err != nil {
|
| ... | ... |
@@ -309,3 +336,36 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str |
| 309 | 309 |
func safePath(base, path string) (string, error) {
|
| 310 | 310 |
return symlink.FollowSymlinkInScope(filepath.Join(base, path), base) |
| 311 | 311 |
} |
| 312 |
+ |
|
| 313 |
+type parentLink struct {
|
|
| 314 |
+ id, parentID image.ID |
|
| 315 |
+} |
|
| 316 |
+ |
|
| 317 |
+func validatedParentLinks(pl []parentLink) (ret []parentLink) {
|
|
| 318 |
+mainloop: |
|
| 319 |
+ for i, p := range pl {
|
|
| 320 |
+ ret = append(ret, p) |
|
| 321 |
+ for _, p2 := range pl {
|
|
| 322 |
+ if p2.id == p.parentID && p2.id != p.id {
|
|
| 323 |
+ continue mainloop |
|
| 324 |
+ } |
|
| 325 |
+ } |
|
| 326 |
+ ret[i].parentID = "" |
|
| 327 |
+ } |
|
| 328 |
+ return |
|
| 329 |
+} |
|
| 330 |
+ |
|
| 331 |
+func checkValidParent(img, parent *image.Image) bool {
|
|
| 332 |
+ if len(img.History) == 0 && len(parent.History) == 0 {
|
|
| 333 |
+ return true // having history is not mandatory |
|
| 334 |
+ } |
|
| 335 |
+ if len(img.History)-len(parent.History) != 1 {
|
|
| 336 |
+ return false |
|
| 337 |
+ } |
|
| 338 |
+ for i, h := range parent.History {
|
|
| 339 |
+ if !reflect.DeepEqual(h, img.History[i]) {
|
|
| 340 |
+ return false |
|
| 341 |
+ } |
|
| 342 |
+ } |
|
| 343 |
+ return true |
|
| 344 |
+} |
| ... | ... |
@@ -128,6 +128,7 @@ func (s *saveSession) save(outStream io.Writer) error {
|
| 128 | 128 |
reposLegacy := make(map[string]map[string]string) |
| 129 | 129 |
|
| 130 | 130 |
var manifest []manifestItem |
| 131 |
+ var parentLinks []parentLink |
|
| 131 | 132 |
|
| 132 | 133 |
for id, imageDescr := range s.images {
|
| 133 | 134 |
if err = s.saveImage(id); err != nil {
|
| ... | ... |
@@ -154,6 +155,15 @@ func (s *saveSession) save(outStream io.Writer) error {
|
| 154 | 154 |
RepoTags: repoTags, |
| 155 | 155 |
Layers: layers, |
| 156 | 156 |
}) |
| 157 |
+ |
|
| 158 |
+ parentID, _ := s.is.GetParent(id) |
|
| 159 |
+ parentLinks = append(parentLinks, parentLink{id, parentID})
|
|
| 160 |
+ } |
|
| 161 |
+ |
|
| 162 |
+ for i, p := range validatedParentLinks(parentLinks) {
|
|
| 163 |
+ if p.parentID != "" {
|
|
| 164 |
+ manifest[i].Parent = p.parentID |
|
| 165 |
+ } |
|
| 157 | 166 |
} |
| 158 | 167 |
|
| 159 | 168 |
if len(reposLegacy) > 0 {
|
| ... | ... |
@@ -311,3 +311,42 @@ func (s *DockerSuite) TestLoadZeroSizeLayer(c *check.C) {
|
| 311 | 311 |
|
| 312 | 312 |
dockerCmd(c, "load", "-i", "fixtures/load/emptyLayer.tar") |
| 313 | 313 |
} |
| 314 |
+ |
|
| 315 |
+func (s *DockerSuite) TestSaveLoadParents(c *check.C) {
|
|
| 316 |
+ testRequires(c, DaemonIsLinux) |
|
| 317 |
+ |
|
| 318 |
+ makeImage := func(from string, addfile string) string {
|
|
| 319 |
+ var ( |
|
| 320 |
+ out string |
|
| 321 |
+ ) |
|
| 322 |
+ out, _ = dockerCmd(c, "run", "-d", from, "touch", addfile) |
|
| 323 |
+ cleanedContainerID := strings.TrimSpace(out) |
|
| 324 |
+ |
|
| 325 |
+ out, _ = dockerCmd(c, "commit", cleanedContainerID) |
|
| 326 |
+ imageID := strings.TrimSpace(out) |
|
| 327 |
+ |
|
| 328 |
+ dockerCmd(c, "rm", cleanedContainerID) |
|
| 329 |
+ return imageID |
|
| 330 |
+ } |
|
| 331 |
+ |
|
| 332 |
+ idFoo := makeImage("busybox", "foo")
|
|
| 333 |
+ idBar := makeImage(idFoo, "bar") |
|
| 334 |
+ |
|
| 335 |
+ tmpDir, err := ioutil.TempDir("", "save-load-parents")
|
|
| 336 |
+ c.Assert(err, checker.IsNil) |
|
| 337 |
+ defer os.RemoveAll(tmpDir) |
|
| 338 |
+ |
|
| 339 |
+ c.Log("tmpdir", tmpDir)
|
|
| 340 |
+ |
|
| 341 |
+ outfile := filepath.Join(tmpDir, "out.tar") |
|
| 342 |
+ |
|
| 343 |
+ dockerCmd(c, "save", "-o", outfile, idBar, idFoo) |
|
| 344 |
+ dockerCmd(c, "rmi", idBar) |
|
| 345 |
+ dockerCmd(c, "load", "-i", outfile) |
|
| 346 |
+ |
|
| 347 |
+ inspectOut := inspectField(c, idBar, "Parent") |
|
| 348 |
+ c.Assert(inspectOut, checker.Equals, idFoo) |
|
| 349 |
+ |
|
| 350 |
+ inspectOut = inspectField(c, idFoo, "Parent") |
|
| 351 |
+ c.Assert(inspectOut, checker.Equals, "") |
|
| 352 |
+} |