github.com/noxiouz/docker@v0.7.3-0.20160629055221-3d231c78e8c5/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) TestSaveWithNoExistImage(c *check.C) { 171 testRequires(c, DaemonIsLinux) 172 173 imgName := "foobar-non-existing-image" 174 175 out, _, err := dockerCmdWithError("save", "-o", "test-img.tar", imgName) 176 c.Assert(err, checker.NotNil, check.Commentf("save image should fail for non-existing image")) 177 c.Assert(out, checker.Contains, fmt.Sprintf("No such image: %s", imgName)) 178 } 179 180 func (s *DockerSuite) TestSaveMultipleNames(c *check.C) { 181 testRequires(c, DaemonIsLinux) 182 repoName := "foobar-save-multi-name-test" 183 184 // Make one image 185 dockerCmd(c, "tag", "emptyfs:latest", fmt.Sprintf("%v-one:latest", repoName)) 186 187 // Make two images 188 dockerCmd(c, "tag", "emptyfs:latest", fmt.Sprintf("%v-two:latest", repoName)) 189 190 out, _, err := runCommandPipelineWithOutput( 191 exec.Command(dockerBinary, "save", fmt.Sprintf("%v-one", repoName), fmt.Sprintf("%v-two:latest", repoName)), 192 exec.Command("tar", "xO", "repositories"), 193 exec.Command("grep", "-q", "-E", "(-one|-two)"), 194 ) 195 c.Assert(err, checker.IsNil, check.Commentf("failed to save multiple repos: %s, %v", out, err)) 196 } 197 198 func (s *DockerSuite) TestSaveRepoWithMultipleImages(c *check.C) { 199 testRequires(c, DaemonIsLinux) 200 makeImage := func(from string, tag string) string { 201 var ( 202 out string 203 ) 204 out, _ = dockerCmd(c, "run", "-d", from, "true") 205 cleanedContainerID := strings.TrimSpace(out) 206 207 out, _ = dockerCmd(c, "commit", cleanedContainerID, tag) 208 imageID := strings.TrimSpace(out) 209 return imageID 210 } 211 212 repoName := "foobar-save-multi-images-test" 213 tagFoo := repoName + ":foo" 214 tagBar := repoName + ":bar" 215 216 idFoo := makeImage("busybox:latest", tagFoo) 217 idBar := makeImage("busybox:latest", tagBar) 218 219 deleteImages(repoName) 220 221 // create the archive 222 out, _, err := runCommandPipelineWithOutput( 223 exec.Command(dockerBinary, "save", repoName, "busybox:latest"), 224 exec.Command("tar", "t")) 225 c.Assert(err, checker.IsNil, check.Commentf("failed to save multiple images: %s, %v", out, err)) 226 227 lines := strings.Split(strings.TrimSpace(out), "\n") 228 var actual []string 229 for _, l := range lines { 230 if regexp.MustCompile("^[a-f0-9]{64}\\.json$").Match([]byte(l)) { 231 actual = append(actual, strings.TrimSuffix(l, ".json")) 232 } 233 } 234 235 // make the list of expected layers 236 out = inspectField(c, "busybox:latest", "Id") 237 expected := []string{strings.TrimSpace(out), idFoo, idBar} 238 239 // prefixes are not in tar 240 for i := range expected { 241 expected[i] = digest.Digest(expected[i]).Hex() 242 } 243 244 sort.Strings(actual) 245 sort.Strings(expected) 246 c.Assert(actual, checker.DeepEquals, expected, check.Commentf("archive does not contains the right layers: got %v, expected %v, output: %q", actual, expected, out)) 247 } 248 249 // Issue #6722 #5892 ensure directories are included in changes 250 func (s *DockerSuite) TestSaveDirectoryPermissions(c *check.C) { 251 testRequires(c, DaemonIsLinux) 252 layerEntries := []string{"opt/", "opt/a/", "opt/a/b/", "opt/a/b/c"} 253 layerEntriesAUFS := []string{"./", ".wh..wh.aufs", ".wh..wh.orph/", ".wh..wh.plnk/", "opt/", "opt/a/", "opt/a/b/", "opt/a/b/c"} 254 255 name := "save-directory-permissions" 256 tmpDir, err := ioutil.TempDir("", "save-layers-with-directories") 257 c.Assert(err, checker.IsNil, check.Commentf("failed to create temporary directory: %s", err)) 258 extractionDirectory := filepath.Join(tmpDir, "image-extraction-dir") 259 os.Mkdir(extractionDirectory, 0777) 260 261 defer os.RemoveAll(tmpDir) 262 _, err = buildImage(name, 263 `FROM busybox 264 RUN adduser -D user && mkdir -p /opt/a/b && chown -R user:user /opt/a 265 RUN touch /opt/a/b/c && chown user:user /opt/a/b/c`, 266 true) 267 c.Assert(err, checker.IsNil, check.Commentf("%v", err)) 268 269 out, _, err := runCommandPipelineWithOutput( 270 exec.Command(dockerBinary, "save", name), 271 exec.Command("tar", "-xf", "-", "-C", extractionDirectory), 272 ) 273 c.Assert(err, checker.IsNil, check.Commentf("failed to save and extract image: %s", out)) 274 275 dirs, err := ioutil.ReadDir(extractionDirectory) 276 c.Assert(err, checker.IsNil, check.Commentf("failed to get a listing of the layer directories: %s", err)) 277 278 found := false 279 for _, entry := range dirs { 280 var entriesSansDev []string 281 if entry.IsDir() { 282 layerPath := filepath.Join(extractionDirectory, entry.Name(), "layer.tar") 283 284 f, err := os.Open(layerPath) 285 c.Assert(err, checker.IsNil, check.Commentf("failed to open %s: %s", layerPath, err)) 286 287 entries, err := listTar(f) 288 for _, e := range entries { 289 if !strings.Contains(e, "dev/") { 290 entriesSansDev = append(entriesSansDev, e) 291 } 292 } 293 c.Assert(err, checker.IsNil, check.Commentf("encountered error while listing tar entries: %s", err)) 294 295 if reflect.DeepEqual(entriesSansDev, layerEntries) || reflect.DeepEqual(entriesSansDev, layerEntriesAUFS) { 296 found = true 297 break 298 } 299 } 300 } 301 302 c.Assert(found, checker.Equals, true, check.Commentf("failed to find the layer with the right content listing")) 303 304 } 305 306 // Test loading a weird image where one of the layers is of zero size. 307 // The layer.tar file is actually zero bytes, no padding or anything else. 308 // See issue: 18170 309 func (s *DockerSuite) TestLoadZeroSizeLayer(c *check.C) { 310 testRequires(c, DaemonIsLinux) 311 312 dockerCmd(c, "load", "-i", "fixtures/load/emptyLayer.tar") 313 } 314 315 func (s *DockerSuite) TestSaveLoadParents(c *check.C) { 316 testRequires(c, DaemonIsLinux) 317 318 makeImage := func(from string, addfile string) string { 319 var ( 320 out string 321 ) 322 out, _ = dockerCmd(c, "run", "-d", from, "touch", addfile) 323 cleanedContainerID := strings.TrimSpace(out) 324 325 out, _ = dockerCmd(c, "commit", cleanedContainerID) 326 imageID := strings.TrimSpace(out) 327 328 dockerCmd(c, "rm", "-f", cleanedContainerID) 329 return imageID 330 } 331 332 idFoo := makeImage("busybox", "foo") 333 idBar := makeImage(idFoo, "bar") 334 335 tmpDir, err := ioutil.TempDir("", "save-load-parents") 336 c.Assert(err, checker.IsNil) 337 defer os.RemoveAll(tmpDir) 338 339 c.Log("tmpdir", tmpDir) 340 341 outfile := filepath.Join(tmpDir, "out.tar") 342 343 dockerCmd(c, "save", "-o", outfile, idBar, idFoo) 344 dockerCmd(c, "rmi", idBar) 345 dockerCmd(c, "load", "-i", outfile) 346 347 inspectOut := inspectField(c, idBar, "Parent") 348 c.Assert(inspectOut, checker.Equals, idFoo) 349 350 inspectOut = inspectField(c, idFoo, "Parent") 351 c.Assert(inspectOut, checker.Equals, "") 352 } 353 354 func (s *DockerSuite) TestSaveLoadNoTag(c *check.C) { 355 testRequires(c, DaemonIsLinux) 356 357 name := "saveloadnotag" 358 359 _, err := buildImage(name, "FROM busybox\nENV foo=bar", true) 360 c.Assert(err, checker.IsNil, check.Commentf("%v", err)) 361 362 id := inspectField(c, name, "Id") 363 364 // Test to make sure that save w/o name just shows imageID during load 365 out, _, err := runCommandPipelineWithOutput( 366 exec.Command(dockerBinary, "save", id), 367 exec.Command(dockerBinary, "load")) 368 c.Assert(err, checker.IsNil, check.Commentf("failed to save and load repo: %s, %v", out, err)) 369 370 // Should not show 'name' but should show the image ID during the load 371 c.Assert(out, checker.Not(checker.Contains), "Loaded image: ") 372 c.Assert(out, checker.Contains, "Loaded image ID:") 373 c.Assert(out, checker.Contains, id) 374 375 // Test to make sure that save by name shows that name during load 376 out, _, err = runCommandPipelineWithOutput( 377 exec.Command(dockerBinary, "save", name), 378 exec.Command(dockerBinary, "load")) 379 c.Assert(err, checker.IsNil, check.Commentf("failed to save and load repo: %s, %v", out, err)) 380 c.Assert(out, checker.Contains, "Loaded image: "+name+":latest") 381 c.Assert(out, checker.Not(checker.Contains), "Loaded image ID:") 382 }