github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/integration-cli/docker_cli_save_load_test.go (about) 1 package main 2 3 import ( 4 "archive/tar" 5 "encoding/json" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "reflect" 13 "regexp" 14 "sort" 15 "strings" 16 "time" 17 18 "github.com/docker/docker/integration-cli/checker" 19 "github.com/docker/docker/integration-cli/cli/build" 20 "github.com/go-check/check" 21 "github.com/opencontainers/go-digest" 22 "gotest.tools/assert" 23 is "gotest.tools/assert/cmp" 24 "gotest.tools/icmd" 25 ) 26 27 // save a repo using gz compression and try to load it using stdout 28 func (s *DockerSuite) TestSaveXzAndLoadRepoStdout(c *check.C) { 29 testRequires(c, DaemonIsLinux) 30 name := "test-save-xz-and-load-repo-stdout" 31 dockerCmd(c, "run", "--name", name, "busybox", "true") 32 33 repoName := "foobar-save-load-test-xz-gz" 34 out, _ := dockerCmd(c, "commit", name, repoName) 35 36 dockerCmd(c, "inspect", repoName) 37 38 repoTarball, err := RunCommandPipelineWithOutput( 39 exec.Command(dockerBinary, "save", repoName), 40 exec.Command("xz", "-c"), 41 exec.Command("gzip", "-c")) 42 assert.NilError(c, err, "failed to save repo: %v %v", out, err) 43 deleteImages(repoName) 44 45 icmd.RunCmd(icmd.Cmd{ 46 Command: []string{dockerBinary, "load"}, 47 Stdin: strings.NewReader(repoTarball), 48 }).Assert(c, icmd.Expected{ 49 ExitCode: 1, 50 }) 51 52 after, _, err := dockerCmdWithError("inspect", repoName) 53 assert.ErrorContains(c, err, "", "the repo should not exist: %v", after) 54 } 55 56 // save a repo using xz+gz compression and try to load it using stdout 57 func (s *DockerSuite) TestSaveXzGzAndLoadRepoStdout(c *check.C) { 58 testRequires(c, DaemonIsLinux) 59 name := "test-save-xz-gz-and-load-repo-stdout" 60 dockerCmd(c, "run", "--name", name, "busybox", "true") 61 62 repoName := "foobar-save-load-test-xz-gz" 63 dockerCmd(c, "commit", name, repoName) 64 65 dockerCmd(c, "inspect", repoName) 66 67 out, err := RunCommandPipelineWithOutput( 68 exec.Command(dockerBinary, "save", repoName), 69 exec.Command("xz", "-c"), 70 exec.Command("gzip", "-c")) 71 assert.NilError(c, err, "failed to save repo: %v %v", out, err) 72 73 deleteImages(repoName) 74 75 icmd.RunCmd(icmd.Cmd{ 76 Command: []string{dockerBinary, "load"}, 77 Stdin: strings.NewReader(out), 78 }).Assert(c, icmd.Expected{ 79 ExitCode: 1, 80 }) 81 82 after, _, err := dockerCmdWithError("inspect", repoName) 83 assert.ErrorContains(c, err, "", "the repo should not exist: %v", after) 84 } 85 86 func (s *DockerSuite) TestSaveSingleTag(c *check.C) { 87 testRequires(c, DaemonIsLinux) 88 repoName := "foobar-save-single-tag-test" 89 dockerCmd(c, "tag", "busybox:latest", fmt.Sprintf("%v:latest", repoName)) 90 91 out, _ := dockerCmd(c, "images", "-q", "--no-trunc", repoName) 92 cleanedImageID := strings.TrimSpace(out) 93 94 out, err := RunCommandPipelineWithOutput( 95 exec.Command(dockerBinary, "save", fmt.Sprintf("%v:latest", repoName)), 96 exec.Command("tar", "t"), 97 exec.Command("grep", "-E", fmt.Sprintf("(^repositories$|%v)", cleanedImageID))) 98 assert.NilError(c, err, "failed to save repo with image ID and 'repositories' file: %s, %v", out, err) 99 } 100 101 func (s *DockerSuite) TestSaveCheckTimes(c *check.C) { 102 testRequires(c, DaemonIsLinux) 103 repoName := "busybox:latest" 104 out, _ := dockerCmd(c, "inspect", repoName) 105 var data []struct { 106 ID string 107 Created time.Time 108 } 109 err := json.Unmarshal([]byte(out), &data) 110 assert.NilError(c, err, "failed to marshal from %q: err %v", repoName, err) 111 assert.Assert(c, len(data) != 0, "failed to marshal the data from %q", repoName) 112 tarTvTimeFormat := "2006-01-02 15:04" 113 out, err = RunCommandPipelineWithOutput( 114 exec.Command(dockerBinary, "save", repoName), 115 exec.Command("tar", "tv"), 116 exec.Command("grep", "-E", fmt.Sprintf("%s %s", data[0].Created.Format(tarTvTimeFormat), digest.Digest(data[0].ID).Hex()))) 117 assert.NilError(c, err, "failed to save repo with image ID and 'repositories' file: %s, %v", out, err) 118 } 119 120 func (s *DockerSuite) TestSaveImageId(c *check.C) { 121 testRequires(c, DaemonIsLinux) 122 repoName := "foobar-save-image-id-test" 123 dockerCmd(c, "tag", "emptyfs:latest", fmt.Sprintf("%v:latest", repoName)) 124 125 out, _ := dockerCmd(c, "images", "-q", "--no-trunc", repoName) 126 cleanedLongImageID := strings.TrimPrefix(strings.TrimSpace(out), "sha256:") 127 128 out, _ = dockerCmd(c, "images", "-q", repoName) 129 cleanedShortImageID := strings.TrimSpace(out) 130 131 // Make sure IDs are not empty 132 c.Assert(cleanedLongImageID, checker.Not(check.Equals), "", check.Commentf("Id should not be empty.")) 133 c.Assert(cleanedShortImageID, checker.Not(check.Equals), "", check.Commentf("Id should not be empty.")) 134 135 saveCmd := exec.Command(dockerBinary, "save", cleanedShortImageID) 136 tarCmd := exec.Command("tar", "t") 137 138 var err error 139 tarCmd.Stdin, err = saveCmd.StdoutPipe() 140 c.Assert(err, checker.IsNil, check.Commentf("cannot set stdout pipe for tar: %v", err)) 141 grepCmd := exec.Command("grep", cleanedLongImageID) 142 grepCmd.Stdin, err = tarCmd.StdoutPipe() 143 c.Assert(err, checker.IsNil, check.Commentf("cannot set stdout pipe for grep: %v", err)) 144 145 c.Assert(tarCmd.Start(), checker.IsNil, check.Commentf("tar failed with error: %v", err)) 146 c.Assert(saveCmd.Start(), checker.IsNil, check.Commentf("docker save failed with error: %v", err)) 147 defer func() { 148 saveCmd.Wait() 149 tarCmd.Wait() 150 dockerCmd(c, "rmi", repoName) 151 }() 152 153 out, _, err = runCommandWithOutput(grepCmd) 154 155 c.Assert(err, checker.IsNil, check.Commentf("failed to save repo with image ID: %s, %v", out, err)) 156 } 157 158 // save a repo and try to load it using flags 159 func (s *DockerSuite) TestSaveAndLoadRepoFlags(c *check.C) { 160 testRequires(c, DaemonIsLinux) 161 name := "test-save-and-load-repo-flags" 162 dockerCmd(c, "run", "--name", name, "busybox", "true") 163 164 repoName := "foobar-save-load-test" 165 166 deleteImages(repoName) 167 dockerCmd(c, "commit", name, repoName) 168 169 before, _ := dockerCmd(c, "inspect", repoName) 170 171 out, err := RunCommandPipelineWithOutput( 172 exec.Command(dockerBinary, "save", repoName), 173 exec.Command(dockerBinary, "load")) 174 assert.NilError(c, err, "failed to save and load repo: %s, %v", out, err) 175 176 after, _ := dockerCmd(c, "inspect", repoName) 177 assert.Equal(c, before, after, "inspect is not the same after a save / load") 178 } 179 180 func (s *DockerSuite) TestSaveWithNoExistImage(c *check.C) { 181 testRequires(c, DaemonIsLinux) 182 183 imgName := "foobar-non-existing-image" 184 185 out, _, err := dockerCmdWithError("save", "-o", "test-img.tar", imgName) 186 assert.ErrorContains(c, err, "", "save image should fail for non-existing image") 187 assert.Assert(c, strings.Contains(out, fmt.Sprintf("No such image: %s", imgName))) 188 } 189 190 func (s *DockerSuite) TestSaveMultipleNames(c *check.C) { 191 testRequires(c, DaemonIsLinux) 192 repoName := "foobar-save-multi-name-test" 193 194 // Make one image 195 dockerCmd(c, "tag", "emptyfs:latest", fmt.Sprintf("%v-one:latest", repoName)) 196 197 // Make two images 198 dockerCmd(c, "tag", "emptyfs:latest", fmt.Sprintf("%v-two:latest", repoName)) 199 200 out, err := RunCommandPipelineWithOutput( 201 exec.Command(dockerBinary, "save", fmt.Sprintf("%v-one", repoName), fmt.Sprintf("%v-two:latest", repoName)), 202 exec.Command("tar", "xO", "repositories"), 203 exec.Command("grep", "-q", "-E", "(-one|-two)"), 204 ) 205 assert.NilError(c, err, "failed to save multiple repos: %s, %v", out, err) 206 } 207 208 func (s *DockerSuite) TestSaveRepoWithMultipleImages(c *check.C) { 209 testRequires(c, DaemonIsLinux) 210 makeImage := func(from string, tag string) string { 211 var ( 212 out string 213 ) 214 out, _ = dockerCmd(c, "run", "-d", from, "true") 215 cleanedContainerID := strings.TrimSpace(out) 216 217 out, _ = dockerCmd(c, "commit", cleanedContainerID, tag) 218 imageID := strings.TrimSpace(out) 219 return imageID 220 } 221 222 repoName := "foobar-save-multi-images-test" 223 tagFoo := repoName + ":foo" 224 tagBar := repoName + ":bar" 225 226 idFoo := makeImage("busybox:latest", tagFoo) 227 idBar := makeImage("busybox:latest", tagBar) 228 229 deleteImages(repoName) 230 231 // create the archive 232 out, err := RunCommandPipelineWithOutput( 233 exec.Command(dockerBinary, "save", repoName, "busybox:latest"), 234 exec.Command("tar", "t")) 235 assert.NilError(c, err, "failed to save multiple images: %s, %v", out, err) 236 237 lines := strings.Split(strings.TrimSpace(out), "\n") 238 var actual []string 239 for _, l := range lines { 240 if regexp.MustCompile("^[a-f0-9]{64}\\.json$").Match([]byte(l)) { 241 actual = append(actual, strings.TrimSuffix(l, ".json")) 242 } 243 } 244 245 // make the list of expected layers 246 out = inspectField(c, "busybox:latest", "Id") 247 expected := []string{strings.TrimSpace(out), idFoo, idBar} 248 249 // prefixes are not in tar 250 for i := range expected { 251 expected[i] = digest.Digest(expected[i]).Hex() 252 } 253 254 sort.Strings(actual) 255 sort.Strings(expected) 256 assert.Assert(c, is.DeepEqual(actual, expected), "archive does not contains the right layers: got %v, expected %v, output: %q", actual, expected, out) 257 } 258 259 // Issue #6722 #5892 ensure directories are included in changes 260 func (s *DockerSuite) TestSaveDirectoryPermissions(c *check.C) { 261 testRequires(c, DaemonIsLinux) 262 layerEntries := []string{"opt/", "opt/a/", "opt/a/b/", "opt/a/b/c"} 263 layerEntriesAUFS := []string{"./", ".wh..wh.aufs", ".wh..wh.orph/", ".wh..wh.plnk/", "opt/", "opt/a/", "opt/a/b/", "opt/a/b/c"} 264 265 name := "save-directory-permissions" 266 tmpDir, err := ioutil.TempDir("", "save-layers-with-directories") 267 c.Assert(err, checker.IsNil, check.Commentf("failed to create temporary directory: %s", err)) 268 extractionDirectory := filepath.Join(tmpDir, "image-extraction-dir") 269 os.Mkdir(extractionDirectory, 0777) 270 271 defer os.RemoveAll(tmpDir) 272 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 273 RUN adduser -D user && mkdir -p /opt/a/b && chown -R user:user /opt/a 274 RUN touch /opt/a/b/c && chown user:user /opt/a/b/c`)) 275 276 out, err := RunCommandPipelineWithOutput( 277 exec.Command(dockerBinary, "save", name), 278 exec.Command("tar", "-xf", "-", "-C", extractionDirectory), 279 ) 280 assert.NilError(c, err, "failed to save and extract image: %s", out) 281 282 dirs, err := ioutil.ReadDir(extractionDirectory) 283 assert.NilError(c, err, "failed to get a listing of the layer directories: %s", err) 284 285 found := false 286 for _, entry := range dirs { 287 var entriesSansDev []string 288 if entry.IsDir() { 289 layerPath := filepath.Join(extractionDirectory, entry.Name(), "layer.tar") 290 291 f, err := os.Open(layerPath) 292 assert.NilError(c, err, "failed to open %s: %s", layerPath, err) 293 294 defer f.Close() 295 296 entries, err := listTar(f) 297 for _, e := range entries { 298 if !strings.Contains(e, "dev/") { 299 entriesSansDev = append(entriesSansDev, e) 300 } 301 } 302 assert.NilError(c, err, "encountered error while listing tar entries: %s", err) 303 304 if reflect.DeepEqual(entriesSansDev, layerEntries) || reflect.DeepEqual(entriesSansDev, layerEntriesAUFS) { 305 found = true 306 break 307 } 308 } 309 } 310 311 assert.Assert(c, found, "failed to find the layer with the right content listing") 312 } 313 314 func listTar(f io.Reader) ([]string, error) { 315 tr := tar.NewReader(f) 316 var entries []string 317 318 for { 319 th, err := tr.Next() 320 if err == io.EOF { 321 // end of tar archive 322 return entries, nil 323 } 324 if err != nil { 325 return entries, err 326 } 327 entries = append(entries, th.Name) 328 } 329 } 330 331 // Test loading a weird image where one of the layers is of zero size. 332 // The layer.tar file is actually zero bytes, no padding or anything else. 333 // See issue: 18170 334 func (s *DockerSuite) TestLoadZeroSizeLayer(c *check.C) { 335 // this will definitely not work if using remote daemon 336 // very weird test 337 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 338 339 dockerCmd(c, "load", "-i", "testdata/emptyLayer.tar") 340 } 341 342 func (s *DockerSuite) TestSaveLoadParents(c *check.C) { 343 testRequires(c, DaemonIsLinux) 344 345 makeImage := func(from string, addfile string) string { 346 var ( 347 out string 348 ) 349 out, _ = dockerCmd(c, "run", "-d", from, "touch", addfile) 350 cleanedContainerID := strings.TrimSpace(out) 351 352 out, _ = dockerCmd(c, "commit", cleanedContainerID) 353 imageID := strings.TrimSpace(out) 354 355 dockerCmd(c, "rm", "-f", cleanedContainerID) 356 return imageID 357 } 358 359 idFoo := makeImage("busybox", "foo") 360 idBar := makeImage(idFoo, "bar") 361 362 tmpDir, err := ioutil.TempDir("", "save-load-parents") 363 assert.NilError(c, err) 364 defer os.RemoveAll(tmpDir) 365 366 c.Log("tmpdir", tmpDir) 367 368 outfile := filepath.Join(tmpDir, "out.tar") 369 370 dockerCmd(c, "save", "-o", outfile, idBar, idFoo) 371 dockerCmd(c, "rmi", idBar) 372 dockerCmd(c, "load", "-i", outfile) 373 374 inspectOut := inspectField(c, idBar, "Parent") 375 assert.Equal(c, inspectOut, idFoo) 376 377 inspectOut = inspectField(c, idFoo, "Parent") 378 assert.Equal(c, inspectOut, "") 379 } 380 381 func (s *DockerSuite) TestSaveLoadNoTag(c *check.C) { 382 testRequires(c, DaemonIsLinux) 383 384 name := "saveloadnotag" 385 386 buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nENV foo=bar")) 387 id := inspectField(c, name, "Id") 388 389 // Test to make sure that save w/o name just shows imageID during load 390 out, err := RunCommandPipelineWithOutput( 391 exec.Command(dockerBinary, "save", id), 392 exec.Command(dockerBinary, "load")) 393 assert.NilError(c, err, "failed to save and load repo: %s, %v", out, err) 394 395 // Should not show 'name' but should show the image ID during the load 396 c.Assert(out, checker.Not(checker.Contains), "Loaded image: ") 397 c.Assert(out, checker.Contains, "Loaded image ID:") 398 c.Assert(out, checker.Contains, id) 399 400 // Test to make sure that save by name shows that name during load 401 out, err = RunCommandPipelineWithOutput( 402 exec.Command(dockerBinary, "save", name), 403 exec.Command(dockerBinary, "load")) 404 assert.NilError(c, err, "failed to save and load repo: %s, %v", out, err) 405 406 c.Assert(out, checker.Contains, "Loaded image: "+name+":latest") 407 c.Assert(out, checker.Not(checker.Contains), "Loaded image ID:") 408 }