浏览代码

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

fix missing layers when exporting a full repository
Vincent Batts 10 年之前
父节点
当前提交
a5277764ae
共有 2 个文件被更改,包括 73 次插入25 次删除
  1. 11 25
      graph/export.go
  2. 62 0
      integration-cli/docker_cli_save_load_test.go

+ 11 - 25
graph/export.go

@@ -30,24 +30,21 @@ func (s *TagStore) CmdImageExport(job *engine.Job) engine.Status {
 	defer os.RemoveAll(tempdir)
 	defer os.RemoveAll(tempdir)
 
 
 	rootRepoMap := map[string]Repository{}
 	rootRepoMap := map[string]Repository{}
+	addKey := func(name string, tag string, id string) {
+		log.Debugf("add key [%s:%s]", name, tag)
+		if repo, ok := rootRepoMap[name]; !ok {
+			rootRepoMap[name] = Repository{tag: id}
+		} else {
+			repo[tag] = id
+		}
+	}
 	for _, name := range job.Args {
 	for _, name := range job.Args {
 		log.Debugf("Serializing %s", name)
 		log.Debugf("Serializing %s", name)
 		rootRepo := s.Repositories[name]
 		rootRepo := s.Repositories[name]
 		if rootRepo != nil {
 		if rootRepo != nil {
 			// this is a base repo name, like 'busybox'
 			// this is a base repo name, like 'busybox'
-			for _, id := range rootRepo {
-				if _, ok := rootRepoMap[name]; !ok {
-					rootRepoMap[name] = rootRepo
-				} else {
-					log.Debugf("Duplicate key [%s]", name)
-					if rootRepoMap[name].Contains(rootRepo) {
-						log.Debugf("skipping, because it is present [%s:%q]", name, rootRepo)
-						continue
-					}
-					log.Debugf("updating [%s]: [%q] with [%q]", name, rootRepoMap[name], rootRepo)
-					rootRepoMap[name].Update(rootRepo)
-				}
-
+			for tag, id := range rootRepo {
+				addKey(name, tag, id)
 				if err := s.exportImage(job.Eng, id, tempdir); err != nil {
 				if err := s.exportImage(job.Eng, id, tempdir); err != nil {
 					return job.Error(err)
 					return job.Error(err)
 				}
 				}
@@ -65,18 +62,7 @@ func (s *TagStore) CmdImageExport(job *engine.Job) engine.Status {
 				// check this length, because a lookup of a truncated has will not have a tag
 				// check this length, because a lookup of a truncated has will not have a tag
 				// and will not need to be added to this map
 				// and will not need to be added to this map
 				if len(repoTag) > 0 {
 				if len(repoTag) > 0 {
-					if _, ok := rootRepoMap[repoName]; !ok {
-						rootRepoMap[repoName] = Repository{repoTag: img.ID}
-					} else {
-						log.Debugf("Duplicate key [%s]", repoName)
-						newRepo := Repository{repoTag: img.ID}
-						if rootRepoMap[repoName].Contains(newRepo) {
-							log.Debugf("skipping, because it is present [%s:%q]", repoName, newRepo)
-							continue
-						}
-						log.Debugf("updating [%s]: [%q] with [%q]", repoName, rootRepoMap[repoName], newRepo)
-						rootRepoMap[repoName].Update(newRepo)
-					}
+					addKey(repoName, repoTag, img.ID)
 				}
 				}
 				if err := s.exportImage(job.Eng, img.ID, tempdir); err != nil {
 				if err := s.exportImage(job.Eng, img.ID, tempdir); err != nil {
 					return job.Error(err)
 					return job.Error(err)

+ 62 - 0
integration-cli/docker_cli_save_load_test.go

@@ -8,6 +8,8 @@ import (
 	"os/exec"
 	"os/exec"
 	"path/filepath"
 	"path/filepath"
 	"reflect"
 	"reflect"
+	"sort"
+	"strings"
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/vendor/src/github.com/kr/pty"
 	"github.com/docker/docker/vendor/src/github.com/kr/pty"
@@ -260,6 +262,66 @@ func TestSaveMultipleNames(t *testing.T) {
 	logDone("save - save by multiple names")
 	logDone("save - save by multiple names")
 }
 }
 
 
+func TestSaveRepoWithMultipleImages(t *testing.T) {
+
+	makeImage := func(from string, tag string) string {
+		runCmd := exec.Command(dockerBinary, "run", "-d", from, "true")
+		var (
+			out string
+			err error
+		)
+		if out, _, err = runCommandWithOutput(runCmd); err != nil {
+			t.Fatalf("failed to create a container: %v %v", out, err)
+		}
+		cleanedContainerID := stripTrailingCharacters(out)
+
+		commitCmd := exec.Command(dockerBinary, "commit", cleanedContainerID, tag)
+		if out, _, err = runCommandWithOutput(commitCmd); err != nil {
+			t.Fatalf("failed to commit container: %v %v", out, err)
+		}
+		imageID := stripTrailingCharacters(out)
+
+		deleteContainer(cleanedContainerID)
+		return imageID
+	}
+
+	repoName := "foobar-save-multi-images-test"
+	tagFoo := repoName + ":foo"
+	tagBar := repoName + ":bar"
+
+	idFoo := makeImage("busybox:latest", tagFoo)
+	idBar := makeImage("busybox:latest", tagBar)
+
+	deleteImages(repoName)
+
+	// create the archive
+	saveCmdFinal := fmt.Sprintf("%v save %v | tar t | grep 'VERSION' |cut -d / -f1", dockerBinary, repoName)
+	saveCmd := exec.Command("bash", "-c", saveCmdFinal)
+	out, _, err := runCommandWithOutput(saveCmd)
+	if err != nil {
+		t.Fatalf("failed to save multiple images: %s, %v", out, err)
+	}
+	actual := strings.Split(stripTrailingCharacters(out), "\n")
+
+	// make the list of expected layers
+	historyCmdFinal := fmt.Sprintf("%v history -q --no-trunc %v", dockerBinary, "busybox:latest")
+	historyCmd := exec.Command("bash", "-c", historyCmdFinal)
+	out, _, err = runCommandWithOutput(historyCmd)
+	if err != nil {
+		t.Fatalf("failed to get history: %s, %v", out, err)
+	}
+
+	expected := append(strings.Split(stripTrailingCharacters(out), "\n"), idFoo, idBar)
+
+	sort.Strings(actual)
+	sort.Strings(expected)
+	if !reflect.DeepEqual(expected, actual) {
+		t.Fatalf("achive does not contains the right layers: got %v, expected %v", actual, expected)
+	}
+
+	logDone("save - save repository with multiple images")
+}
+
 // Issue #6722 #5892 ensure directories are included in changes
 // Issue #6722 #5892 ensure directories are included in changes
 func TestSaveDirectoryPermissions(t *testing.T) {
 func TestSaveDirectoryPermissions(t *testing.T) {
 	layerEntries := []string{"opt/", "opt/a/", "opt/a/b/", "opt/a/b/c"}
 	layerEntries := []string{"opt/", "opt/a/", "opt/a/b/", "opt/a/b/c"}