github.com/ncdc/docker@v0.10.1-0.20160129113957-6c6729ef5b74/integration-cli/docker_cli_save_load_test.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "reflect" 11 "regexp" 12 "sort" 13 "strings" 14 "time" 15 16 "github.com/docker/distribution/digest" 17 "github.com/docker/docker/pkg/integration/checker" 18 "github.com/go-check/check" 19 ) 20 21 // save a repo using gz compression and try to load it using stdout 22 func (s *DockerSuite) TestSaveXzAndLoadRepoStdout(c *check.C) { 23 testRequires(c, DaemonIsLinux) 24 name := "test-save-xz-and-load-repo-stdout" 25 dockerCmd(c, "run", "--name", name, "busybox", "true") 26 27 repoName := "foobar-save-load-test-xz-gz" 28 out, _ := dockerCmd(c, "commit", name, repoName) 29 30 dockerCmd(c, "inspect", repoName) 31 32 repoTarball, _, err := runCommandPipelineWithOutput( 33 exec.Command(dockerBinary, "save", repoName), 34 exec.Command("xz", "-c"), 35 exec.Command("gzip", "-c")) 36 c.Assert(err, checker.IsNil, check.Commentf("failed to save repo: %v %v", out, err)) 37 deleteImages(repoName) 38 39 loadCmd := exec.Command(dockerBinary, "load") 40 loadCmd.Stdin = strings.NewReader(repoTarball) 41 out, _, err = runCommandWithOutput(loadCmd) 42 c.Assert(err, checker.NotNil, check.Commentf("expected error, but succeeded with no error and output: %v", out)) 43 44 after, _, err := dockerCmdWithError("inspect", repoName) 45 c.Assert(err, checker.NotNil, check.Commentf("the repo should not exist: %v", after)) 46 } 47 48 // save a repo using xz+gz compression and try to load it using stdout 49 func (s *DockerSuite) TestSaveXzGzAndLoadRepoStdout(c *check.C) { 50 testRequires(c, DaemonIsLinux) 51 name := "test-save-xz-gz-and-load-repo-stdout" 52 dockerCmd(c, "run", "--name", name, "busybox", "true") 53 54 repoName := "foobar-save-load-test-xz-gz" 55 dockerCmd(c, "commit", name, repoName) 56 57 dockerCmd(c, "inspect", repoName) 58 59 out, _, err := runCommandPipelineWithOutput( 60 exec.Command(dockerBinary, "save", repoName), 61 exec.Command("xz", "-c"), 62 exec.Command("gzip", "-c")) 63 c.Assert(err, checker.IsNil, check.Commentf("failed to save repo: %v %v", out, err)) 64 65 deleteImages(repoName) 66 67 loadCmd := exec.Command(dockerBinary, "load") 68 loadCmd.Stdin = strings.NewReader(out) 69 out, _, err = runCommandWithOutput(loadCmd) 70 c.Assert(err, checker.NotNil, check.Commentf("expected error, but succeeded with no error and output: %v", out)) 71 72 after, _, err := dockerCmdWithError("inspect", repoName) 73 c.Assert(err, checker.NotNil, check.Commentf("the repo should not exist: %v", after)) 74 } 75 76 func (s *DockerSuite) TestSaveSingleTag(c *check.C) { 77 testRequires(c, DaemonIsLinux) 78 repoName := "foobar-save-single-tag-test" 79 dockerCmd(c, "tag", "busybox:latest", fmt.Sprintf("%v:latest", repoName)) 80 81 out, _ := dockerCmd(c, "images", "-q", "--no-trunc", repoName) 82 cleanedImageID := strings.TrimSpace(out) 83 84 out, _, err := runCommandPipelineWithOutput( 85 exec.Command(dockerBinary, "save", fmt.Sprintf("%v:latest", repoName)), 86 exec.Command("tar", "t"), 87 exec.Command("grep", "-E", fmt.Sprintf("(^repositories$|%v)", cleanedImageID))) 88 c.Assert(err, checker.IsNil, check.Commentf("failed to save repo with image ID and 'repositories' file: %s, %v", out, err)) 89 } 90 91 func (s *DockerSuite) TestSaveCheckTimes(c *check.C) { 92 testRequires(c, DaemonIsLinux) 93 repoName := "busybox:latest" 94 out, _ := dockerCmd(c, "inspect", repoName) 95 data := []struct { 96 ID string 97 Created time.Time 98 }{} 99 err := json.Unmarshal([]byte(out), &data) 100 c.Assert(err, checker.IsNil, check.Commentf("failed to marshal from %q: err %v", repoName, err)) 101 c.Assert(len(data), checker.Not(checker.Equals), 0, check.Commentf("failed to marshal the data from %q", repoName)) 102 tarTvTimeFormat := "2006-01-02 15:04" 103 out, _, err = runCommandPipelineWithOutput( 104 exec.Command(dockerBinary, "save", repoName), 105 exec.Command("tar", "tv"), 106 exec.Command("grep", "-E", fmt.Sprintf("%s %s", data[0].Created.Format(tarTvTimeFormat), digest.Digest(data[0].ID).Hex()))) 107 c.Assert(err, checker.IsNil, check.Commentf("failed to save repo with image ID and 'repositories' file: %s, %v", out, err)) 108 } 109 110 func (s *DockerSuite) TestSaveImageId(c *check.C) { 111 testRequires(c, DaemonIsLinux) 112 repoName := "foobar-save-image-id-test" 113 dockerCmd(c, "tag", "emptyfs:latest", fmt.Sprintf("%v:latest", repoName)) 114 115 out, _ := dockerCmd(c, "images", "-q", "--no-trunc", repoName) 116 cleanedLongImageID := strings.TrimPrefix(strings.TrimSpace(out), "sha256:") 117 118 out, _ = dockerCmd(c, "images", "-q", repoName) 119 cleanedShortImageID := strings.TrimSpace(out) 120 121 // Make sure IDs are not empty 122 c.Assert(cleanedLongImageID, checker.Not(check.Equals), "", check.Commentf("Id should not be empty.")) 123 c.Assert(cleanedShortImageID, checker.Not(check.Equals), "", check.Commentf("Id should not be empty.")) 124 125 saveCmd := exec.Command(dockerBinary, "save", cleanedShortImageID) 126 tarCmd := exec.Command("tar", "t") 127 128 var err error 129 tarCmd.Stdin, err = saveCmd.StdoutPipe() 130 c.Assert(err, checker.IsNil, check.Commentf("cannot set stdout pipe for tar: %v", err)) 131 grepCmd := exec.Command("grep", cleanedLongImageID) 132 grepCmd.Stdin, err = tarCmd.StdoutPipe() 133 c.Assert(err, checker.IsNil, check.Commentf("cannot set stdout pipe for grep: %v", err)) 134 135 c.Assert(tarCmd.Start(), checker.IsNil, check.Commentf("tar failed with error: %v", err)) 136 c.Assert(saveCmd.Start(), checker.IsNil, check.Commentf("docker save failed with error: %v", err)) 137 defer func() { 138 saveCmd.Wait() 139 tarCmd.Wait() 140 dockerCmd(c, "rmi", repoName) 141 }() 142 143 out, _, err = runCommandWithOutput(grepCmd) 144 145 c.Assert(err, checker.IsNil, check.Commentf("failed to save repo with image ID: %s, %v", out, err)) 146 } 147 148 // save a repo and try to load it using flags 149 func (s *DockerSuite) TestSaveAndLoadRepoFlags(c *check.C) { 150 testRequires(c, DaemonIsLinux) 151 name := "test-save-and-load-repo-flags" 152 dockerCmd(c, "run", "--name", name, "busybox", "true") 153 154 repoName := "foobar-save-load-test" 155 156 deleteImages(repoName) 157 dockerCmd(c, "commit", name, repoName) 158 159 before, _ := dockerCmd(c, "inspect", repoName) 160 161 out, _, err := runCommandPipelineWithOutput( 162 exec.Command(dockerBinary, "save", repoName), 163 exec.Command(dockerBinary, "load")) 164 c.Assert(err, checker.IsNil, check.Commentf("failed to save and load repo: %s, %v", out, err)) 165 166 after, _ := dockerCmd(c, "inspect", repoName) 167 c.Assert(before, checker.Equals, after, check.Commentf("inspect is not the same after a save / load")) 168 } 169 170 func (s *DockerSuite) TestSaveMultipleNames(c *check.C) { 171 testRequires(c, DaemonIsLinux) 172 repoName := "foobar-save-multi-name-test" 173 174 // Make one image 175 dockerCmd(c, "tag", "emptyfs:latest", fmt.Sprintf("%v-one:latest", repoName)) 176 177 // Make two images 178 dockerCmd(c, "tag", "emptyfs:latest", fmt.Sprintf("%v-two:latest", repoName)) 179 180 out, _, err := runCommandPipelineWithOutput( 181 exec.Command(dockerBinary, "save", fmt.Sprintf("%v-one", repoName), fmt.Sprintf("%v-two:latest", repoName)), 182 exec.Command("tar", "xO", "repositories"), 183 exec.Command("grep", "-q", "-E", "(-one|-two)"), 184 ) 185 c.Assert(err, checker.IsNil, check.Commentf("failed to save multiple repos: %s, %v", out, err)) 186 } 187 188 func (s *DockerSuite) TestSaveRepoWithMultipleImages(c *check.C) { 189 testRequires(c, DaemonIsLinux) 190 makeImage := func(from string, tag string) string { 191 var ( 192 out string 193 ) 194 out, _ = dockerCmd(c, "run", "-d", from, "true") 195 cleanedContainerID := strings.TrimSpace(out) 196 197 out, _ = dockerCmd(c, "commit", cleanedContainerID, tag) 198 imageID := strings.TrimSpace(out) 199 return imageID 200 } 201 202 repoName := "foobar-save-multi-images-test" 203 tagFoo := repoName + ":foo" 204 tagBar := repoName + ":bar" 205 206 idFoo := makeImage("busybox:latest", tagFoo) 207 idBar := makeImage("busybox:latest", tagBar) 208 209 deleteImages(repoName) 210 211 // create the archive 212 out, _, err := runCommandPipelineWithOutput( 213 exec.Command(dockerBinary, "save", repoName, "busybox:latest"), 214 exec.Command("tar", "t")) 215 c.Assert(err, checker.IsNil, check.Commentf("failed to save multiple images: %s, %v", out, err)) 216 217 lines := strings.Split(strings.TrimSpace(out), "\n") 218 var actual []string 219 for _, l := range lines { 220 if regexp.MustCompile("^[a-f0-9]{64}\\.json$").Match([]byte(l)) { 221 actual = append(actual, strings.TrimSuffix(l, ".json")) 222 } 223 } 224 225 // make the list of expected layers 226 out, _ = dockerCmd(c, "inspect", "-f", "{{.Id}}", "busybox:latest") 227 expected := []string{strings.TrimSpace(out), idFoo, idBar} 228 229 // prefixes are not in tar 230 for i := range expected { 231 expected[i] = digest.Digest(expected[i]).Hex() 232 } 233 234 sort.Strings(actual) 235 sort.Strings(expected) 236 c.Assert(actual, checker.DeepEquals, expected, check.Commentf("archive does not contains the right layers: got %v, expected %v, output: %q", actual, expected, out)) 237 } 238 239 // Issue #6722 #5892 ensure directories are included in changes 240 func (s *DockerSuite) TestSaveDirectoryPermissions(c *check.C) { 241 testRequires(c, DaemonIsLinux) 242 layerEntries := []string{"opt/", "opt/a/", "opt/a/b/", "opt/a/b/c"} 243 layerEntriesAUFS := []string{"./", ".wh..wh.aufs", ".wh..wh.orph/", ".wh..wh.plnk/", "opt/", "opt/a/", "opt/a/b/", "opt/a/b/c"} 244 245 name := "save-directory-permissions" 246 tmpDir, err := ioutil.TempDir("", "save-layers-with-directories") 247 c.Assert(err, checker.IsNil, check.Commentf("failed to create temporary directory: %s", err)) 248 extractionDirectory := filepath.Join(tmpDir, "image-extraction-dir") 249 os.Mkdir(extractionDirectory, 0777) 250 251 defer os.RemoveAll(tmpDir) 252 _, err = buildImage(name, 253 `FROM busybox 254 RUN adduser -D user && mkdir -p /opt/a/b && chown -R user:user /opt/a 255 RUN touch /opt/a/b/c && chown user:user /opt/a/b/c`, 256 true) 257 c.Assert(err, checker.IsNil, check.Commentf("%v", err)) 258 259 out, _, err := runCommandPipelineWithOutput( 260 exec.Command(dockerBinary, "save", name), 261 exec.Command("tar", "-xf", "-", "-C", extractionDirectory), 262 ) 263 c.Assert(err, checker.IsNil, check.Commentf("failed to save and extract image: %s", out)) 264 265 dirs, err := ioutil.ReadDir(extractionDirectory) 266 c.Assert(err, checker.IsNil, check.Commentf("failed to get a listing of the layer directories: %s", err)) 267 268 found := false 269 for _, entry := range dirs { 270 var entriesSansDev []string 271 if entry.IsDir() { 272 layerPath := filepath.Join(extractionDirectory, entry.Name(), "layer.tar") 273 274 f, err := os.Open(layerPath) 275 c.Assert(err, checker.IsNil, check.Commentf("failed to open %s: %s", layerPath, err)) 276 277 entries, err := listTar(f) 278 for _, e := range entries { 279 if !strings.Contains(e, "dev/") { 280 entriesSansDev = append(entriesSansDev, e) 281 } 282 } 283 c.Assert(err, checker.IsNil, check.Commentf("encountered error while listing tar entries: %s", err)) 284 285 if reflect.DeepEqual(entriesSansDev, layerEntries) || reflect.DeepEqual(entriesSansDev, layerEntriesAUFS) { 286 found = true 287 break 288 } 289 } 290 } 291 292 c.Assert(found, checker.Equals, true, check.Commentf("failed to find the layer with the right content listing")) 293 294 } 295 296 // Test loading a weird image where one of the layers is of zero size. 297 // The layer.tar file is actually zero bytes, no padding or anything else. 298 // See issue: 18170 299 func (s *DockerSuite) TestLoadZeroSizeLayer(c *check.C) { 300 testRequires(c, DaemonIsLinux) 301 302 dockerCmd(c, "load", "-i", "fixtures/load/emptyLayer.tar") 303 }