зеркало из https://github.com/microsoft/docker.git
fix missing layers when exporting a full repository
Therer is a bug in the 'skip' decision when exporting a repository (`docker save repo`) Only the layers of the first image are included in the archive (the layers of the next images are missing) Signed-off-by: Anthony Baire <Anthony.Baire@irisa.fr>
This commit is contained in:
Родитель
7b7af6dbae
Коммит
b37fdc5dd1
|
@ -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 {
|
for tag, id := range rootRepo {
|
||||||
if _, ok := rootRepoMap[name]; !ok {
|
addKey(name, tag, id)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
addKey(repoName, repoTag, img.ID)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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)
|
||||||
|
|
|
@ -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"
|
||||||
|
@ -257,6 +259,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"}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче