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