Browse code

Merge pull request #9009 from a-ba/fix-export-repo

fix missing layers when exporting a full repository

Vincent Batts authored on 2014/11/22 05:44:27
Showing 2 changed files
... ...
@@ -30,24 +30,21 @@ func (s *TagStore) CmdImageExport(job *engine.Job) engine.Status {
30 30
 	defer os.RemoveAll(tempdir)
31 31
 
32 32
 	rootRepoMap := map[string]Repository{}
33
+	addKey := func(name string, tag string, id string) {
34
+		log.Debugf("add key [%s:%s]", name, tag)
35
+		if repo, ok := rootRepoMap[name]; !ok {
36
+			rootRepoMap[name] = Repository{tag: id}
37
+		} else {
38
+			repo[tag] = id
39
+		}
40
+	}
33 41
 	for _, name := range job.Args {
34 42
 		log.Debugf("Serializing %s", name)
35 43
 		rootRepo := s.Repositories[name]
36 44
 		if rootRepo != nil {
37 45
 			// this is a base repo name, like 'busybox'
38
-			for _, id := range rootRepo {
39
-				if _, ok := rootRepoMap[name]; !ok {
40
-					rootRepoMap[name] = rootRepo
41
-				} else {
42
-					log.Debugf("Duplicate key [%s]", name)
43
-					if rootRepoMap[name].Contains(rootRepo) {
44
-						log.Debugf("skipping, because it is present [%s:%q]", name, rootRepo)
45
-						continue
46
-					}
47
-					log.Debugf("updating [%s]: [%q] with [%q]", name, rootRepoMap[name], rootRepo)
48
-					rootRepoMap[name].Update(rootRepo)
49
-				}
50
-
46
+			for tag, id := range rootRepo {
47
+				addKey(name, tag, id)
51 48
 				if err := s.exportImage(job.Eng, id, tempdir); err != nil {
52 49
 					return job.Error(err)
53 50
 				}
... ...
@@ -65,18 +62,7 @@ func (s *TagStore) CmdImageExport(job *engine.Job) engine.Status {
65 65
 				// check this length, because a lookup of a truncated has will not have a tag
66 66
 				// and will not need to be added to this map
67 67
 				if len(repoTag) > 0 {
68
-					if _, ok := rootRepoMap[repoName]; !ok {
69
-						rootRepoMap[repoName] = Repository{repoTag: img.ID}
70
-					} else {
71
-						log.Debugf("Duplicate key [%s]", repoName)
72
-						newRepo := Repository{repoTag: img.ID}
73
-						if rootRepoMap[repoName].Contains(newRepo) {
74
-							log.Debugf("skipping, because it is present [%s:%q]", repoName, newRepo)
75
-							continue
76
-						}
77
-						log.Debugf("updating [%s]: [%q] with [%q]", repoName, rootRepoMap[repoName], newRepo)
78
-						rootRepoMap[repoName].Update(newRepo)
79
-					}
68
+					addKey(repoName, repoTag, img.ID)
80 69
 				}
81 70
 				if err := s.exportImage(job.Eng, img.ID, tempdir); err != nil {
82 71
 					return job.Error(err)
... ...
@@ -8,6 +8,8 @@ import (
8 8
 	"os/exec"
9 9
 	"path/filepath"
10 10
 	"reflect"
11
+	"sort"
12
+	"strings"
11 13
 	"testing"
12 14
 
13 15
 	"github.com/docker/docker/vendor/src/github.com/kr/pty"
... ...
@@ -260,6 +262,66 @@ func TestSaveMultipleNames(t *testing.T) {
260 260
 	logDone("save - save by multiple names")
261 261
 }
262 262
 
263
+func TestSaveRepoWithMultipleImages(t *testing.T) {
264
+
265
+	makeImage := func(from string, tag string) string {
266
+		runCmd := exec.Command(dockerBinary, "run", "-d", from, "true")
267
+		var (
268
+			out string
269
+			err error
270
+		)
271
+		if out, _, err = runCommandWithOutput(runCmd); err != nil {
272
+			t.Fatalf("failed to create a container: %v %v", out, err)
273
+		}
274
+		cleanedContainerID := stripTrailingCharacters(out)
275
+
276
+		commitCmd := exec.Command(dockerBinary, "commit", cleanedContainerID, tag)
277
+		if out, _, err = runCommandWithOutput(commitCmd); err != nil {
278
+			t.Fatalf("failed to commit container: %v %v", out, err)
279
+		}
280
+		imageID := stripTrailingCharacters(out)
281
+
282
+		deleteContainer(cleanedContainerID)
283
+		return imageID
284
+	}
285
+
286
+	repoName := "foobar-save-multi-images-test"
287
+	tagFoo := repoName + ":foo"
288
+	tagBar := repoName + ":bar"
289
+
290
+	idFoo := makeImage("busybox:latest", tagFoo)
291
+	idBar := makeImage("busybox:latest", tagBar)
292
+
293
+	deleteImages(repoName)
294
+
295
+	// create the archive
296
+	saveCmdFinal := fmt.Sprintf("%v save %v | tar t | grep 'VERSION' |cut -d / -f1", dockerBinary, repoName)
297
+	saveCmd := exec.Command("bash", "-c", saveCmdFinal)
298
+	out, _, err := runCommandWithOutput(saveCmd)
299
+	if err != nil {
300
+		t.Fatalf("failed to save multiple images: %s, %v", out, err)
301
+	}
302
+	actual := strings.Split(stripTrailingCharacters(out), "\n")
303
+
304
+	// make the list of expected layers
305
+	historyCmdFinal := fmt.Sprintf("%v history -q --no-trunc %v", dockerBinary, "busybox:latest")
306
+	historyCmd := exec.Command("bash", "-c", historyCmdFinal)
307
+	out, _, err = runCommandWithOutput(historyCmd)
308
+	if err != nil {
309
+		t.Fatalf("failed to get history: %s, %v", out, err)
310
+	}
311
+
312
+	expected := append(strings.Split(stripTrailingCharacters(out), "\n"), idFoo, idBar)
313
+
314
+	sort.Strings(actual)
315
+	sort.Strings(expected)
316
+	if !reflect.DeepEqual(expected, actual) {
317
+		t.Fatalf("achive does not contains the right layers: got %v, expected %v", actual, expected)
318
+	}
319
+
320
+	logDone("save - save repository with multiple images")
321
+}
322
+
263 323
 // Issue #6722 #5892 ensure directories are included in changes
264 324
 func TestSaveDirectoryPermissions(t *testing.T) {
265 325
 	layerEntries := []string{"opt/", "opt/a/", "opt/a/b/", "opt/a/b/c"}