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