Browse code

make all image ID and container ID API responses use the Long ID (Closes #2098)

Sven Dowideit authored on 2013/10/25 10:59:59
Showing 10 changed files
... ...
@@ -165,6 +165,7 @@ Sridatta Thatipamala <sthatipamala@gmail.com>
165 165
 Sridhar Ratnakumar <sridharr@activestate.com>
166 166
 Steeve Morin <steeve.morin@gmail.com>
167 167
 Stefan Praszalowicz <stefan@greplin.com>
168
+Sven Dowideit <SvenDowideit@home.org.au>
168 169
 Thatcher Peskens <thatcher@dotcloud.com>
169 170
 Thermionix <bond711@gmail.com>
170 171
 Thijs Terlouw <thijsterlouw@gmail.com>
... ...
@@ -6,6 +6,8 @@ import (
6 6
 	"github.com/dotcloud/docker/utils"
7 7
 	"io"
8 8
 	"io/ioutil"
9
+	"os"
10
+	"path"
9 11
 	"regexp"
10 12
 	"strings"
11 13
 	"testing"
... ...
@@ -381,8 +383,8 @@ func TestRunAttachStdin(t *testing.T) {
381 381
 		if err != nil {
382 382
 			t.Fatal(err)
383 383
 		}
384
-		if cmdOutput != container.ShortID()+"\n" {
385
-			t.Fatalf("Wrong output: should be '%s', not '%s'\n", container.ShortID()+"\n", cmdOutput)
384
+		if cmdOutput != container.ID+"\n" {
385
+			t.Fatalf("Wrong output: should be '%s', not '%s'\n", container.ID+"\n", cmdOutput)
386 386
 		}
387 387
 	})
388 388
 
... ...
@@ -459,7 +461,7 @@ func TestRunDetach(t *testing.T) {
459 459
 	})
460 460
 }
461 461
 
462
-// TestAttachDetach checks that attach in tty mode can be detached
462
+// TestAttachDetach checks that attach in tty mode can be detached using the long container ID
463 463
 func TestAttachDetach(t *testing.T) {
464 464
 	stdin, stdinPipe := io.Pipe()
465 465
 	stdout, stdoutPipe := io.Pipe()
... ...
@@ -486,8 +488,8 @@ func TestAttachDetach(t *testing.T) {
486 486
 
487 487
 		container = globalRuntime.List()[0]
488 488
 
489
-		if strings.Trim(string(buf[:n]), " \r\n") != container.ShortID() {
490
-			t.Fatalf("Wrong ID received. Expect %s, received %s", container.ShortID(), buf[:n])
489
+		if strings.Trim(string(buf[:n]), " \r\n") != container.ID {
490
+			t.Fatalf("Wrong ID received. Expect %s, received %s", container.ID, buf[:n])
491 491
 		}
492 492
 	})
493 493
 	setTimeout(t, "Starting container timed out", 10*time.Second, func() {
... ...
@@ -501,7 +503,69 @@ func TestAttachDetach(t *testing.T) {
501 501
 	ch = make(chan struct{})
502 502
 	go func() {
503 503
 		defer close(ch)
504
-		if err := cli.CmdAttach(container.ShortID()); err != nil {
504
+		if err := cli.CmdAttach(container.ID); err != nil {
505
+			if err != io.ErrClosedPipe {
506
+				t.Fatal(err)
507
+			}
508
+		}
509
+	}()
510
+
511
+	setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
512
+		if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
513
+			if err != io.ErrClosedPipe {
514
+				t.Fatal(err)
515
+			}
516
+		}
517
+	})
518
+
519
+	setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
520
+		stdinPipe.Write([]byte{16, 17})
521
+		if err := stdinPipe.Close(); err != nil {
522
+			t.Fatal(err)
523
+		}
524
+	})
525
+	closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
526
+
527
+	// wait for CmdRun to return
528
+	setTimeout(t, "Waiting for CmdAttach timed out", 15*time.Second, func() {
529
+		<-ch
530
+	})
531
+
532
+	time.Sleep(500 * time.Millisecond)
533
+	if !container.State.Running {
534
+		t.Fatal("The detached container should be still running")
535
+	}
536
+
537
+	setTimeout(t, "Waiting for container to die timedout", 5*time.Second, func() {
538
+		container.Kill()
539
+	})
540
+}
541
+
542
+// TestAttachDetachTruncatedID checks that attach in tty mode can be detached
543
+func TestAttachDetachTruncatedID(t *testing.T) {
544
+	stdin, stdinPipe := io.Pipe()
545
+	stdout, stdoutPipe := io.Pipe()
546
+
547
+	cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
548
+	defer cleanup(globalRuntime)
549
+
550
+	go stdout.Read(make([]byte, 1024))
551
+	setTimeout(t, "Starting container timed out", 2*time.Second, func() {
552
+		if err := cli.CmdRun("-i", "-t", "-d", unitTestImageID, "cat"); err != nil {
553
+			t.Fatal(err)
554
+		}
555
+	})
556
+
557
+	container := globalRuntime.List()[0]
558
+
559
+	stdin, stdinPipe = io.Pipe()
560
+	stdout, stdoutPipe = io.Pipe()
561
+	cli = NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
562
+
563
+	ch := make(chan struct{})
564
+	go func() {
565
+		defer close(ch)
566
+		if err := cli.CmdAttach(utils.TruncateID(container.ID)); err != nil {
505 567
 			if err != io.ErrClosedPipe {
506 568
 				t.Fatal(err)
507 569
 			}
... ...
@@ -825,3 +889,55 @@ run    [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
825 825
 
826 826
 	return image
827 827
 }
828
+
829
+// #2098 - Docker cidFiles only contain short version of the containerId
830
+//sudo docker run -cidfile /tmp/docker_test.cid ubuntu echo "test"
831
+// TestRunCidFile tests that run -cidfile returns the longid
832
+func TestRunCidFile(t *testing.T) {
833
+	stdout, stdoutPipe := io.Pipe()
834
+
835
+	tmpDir, err := ioutil.TempDir("", "TestRunCidFile")
836
+	if err != nil {
837
+		t.Fatal(err)
838
+	}
839
+	tmpCidFile := path.Join(tmpDir, "cid")
840
+
841
+	cli := NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
842
+	defer cleanup(globalRuntime)
843
+
844
+	c := make(chan struct{})
845
+	go func() {
846
+		defer close(c)
847
+		if err := cli.CmdRun("-cidfile", tmpCidFile, unitTestImageID, "ls"); err != nil {
848
+			t.Fatal(err)
849
+		}
850
+	}()
851
+
852
+	defer os.RemoveAll(tmpDir)
853
+	setTimeout(t, "Reading command output time out", 2*time.Second, func() {
854
+		cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
855
+		if err != nil {
856
+			t.Fatal(err)
857
+		}
858
+		if len(cmdOutput) < 1 {
859
+			t.Fatalf("'ls' should return something , not '%s'", cmdOutput)
860
+		}
861
+		//read the tmpCidFile
862
+		buffer, err := ioutil.ReadFile(tmpCidFile)
863
+		if err != nil {
864
+			t.Fatal(err)
865
+		}
866
+		id := string(buffer)
867
+
868
+		if len(id) != len("2bf44ea18873287bd9ace8a4cb536a7cbe134bed67e805fdf2f58a57f69b320c") {
869
+			t.Fatalf("-cidfile should be a long id, not '%s'", id)
870
+		}
871
+		//test that its a valid cid? (though the container is gone..)
872
+		//remove the file and dir.
873
+	})
874
+
875
+	setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
876
+		<-c
877
+	})
878
+
879
+}
... ...
@@ -1235,7 +1235,7 @@ func (container *Container) monitor() {
1235 1235
 	container.State.setStopped(exitCode)
1236 1236
 
1237 1237
 	if container.runtime != nil && container.runtime.srv != nil {
1238
-		container.runtime.srv.LogEvent("die", container.ShortID(), container.runtime.repositories.ImageName(container.Image))
1238
+		container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image))
1239 1239
 	}
1240 1240
 
1241 1241
 	// Cleanup
... ...
@@ -1302,7 +1302,7 @@ func (container *Container) kill(sig int) error {
1302 1302
 	}
1303 1303
 
1304 1304
 	if output, err := exec.Command("lxc-kill", "-n", container.ID, strconv.Itoa(sig)).CombinedOutput(); err != nil {
1305
-		log.Printf("error killing container %s (%s, %s)", container.ShortID(), output, err)
1305
+		log.Printf("error killing container %s (%s, %s)", utils.TruncateID(container.ID), output, err)
1306 1306
 		return err
1307 1307
 	}
1308 1308
 
... ...
@@ -1322,9 +1322,9 @@ func (container *Container) Kill() error {
1322 1322
 	// 2. Wait for the process to die, in last resort, try to kill the process directly
1323 1323
 	if err := container.WaitTimeout(10 * time.Second); err != nil {
1324 1324
 		if container.cmd == nil {
1325
-			return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.ShortID())
1325
+			return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", utils.TruncateID(container.ID))
1326 1326
 		}
1327
-		log.Printf("Container %s failed to exit within 10 seconds of lxc-kill %s - trying direct SIGKILL", "SIGKILL", container.ShortID())
1327
+		log.Printf("Container %s failed to exit within 10 seconds of lxc-kill %s - trying direct SIGKILL", "SIGKILL", utils.TruncateID(container.ID))
1328 1328
 		if err := container.cmd.Process.Kill(); err != nil {
1329 1329
 			return err
1330 1330
 		}
... ...
@@ -1460,14 +1460,6 @@ func (container *Container) Unmount() error {
1460 1460
 	return Unmount(container.RootfsPath())
1461 1461
 }
1462 1462
 
1463
-// ShortID returns a shorthand version of the container's id for convenience.
1464
-// A collision with other container shorthands is very unlikely, but possible.
1465
-// In case of a collision a lookup with Runtime.Get() will fail, and the caller
1466
-// will need to use a langer prefix, or the full-length container Id.
1467
-func (container *Container) ShortID() string {
1468
-	return utils.TruncateID(container.ID)
1469
-}
1470
-
1471 1463
 func (container *Container) logPath(name string) string {
1472 1464
 	return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.ID, name))
1473 1465
 }
... ...
@@ -3,6 +3,7 @@ package docker
3 3
 import (
4 4
 	"bufio"
5 5
 	"fmt"
6
+	"github.com/dotcloud/docker/utils"
6 7
 	"io"
7 8
 	"io/ioutil"
8 9
 	"math/rand"
... ...
@@ -1005,7 +1006,7 @@ func TestEnv(t *testing.T) {
1005 1005
 		"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
1006 1006
 		"HOME=/",
1007 1007
 		"container=lxc",
1008
-		"HOSTNAME=" + container.ShortID(),
1008
+		"HOSTNAME=" + utils.TruncateID(container.ID),
1009 1009
 		"FALSE=true",
1010 1010
 		"TRUE=false",
1011 1011
 		"TRICKY=tri",
... ...
@@ -38,3 +38,10 @@ was when the container was stopped.
38 38
 You can promote a container to an :ref:`image_def` with ``docker
39 39
 commit``. Once a container is an image, you can use it as a parent for
40 40
 new containers.
41
+
42
+Container IDs
43
+.............
44
+All containers are identified by a 64 hexadecimal digit string (internally a 256bit 
45
+value). To simplify their use, a short ID of the first 12 characters can be used 
46
+on the commandline. There is a small possibility of short id collisions, so the 
47
+docker server will always return the long ID.
... ...
@@ -36,3 +36,11 @@ Base Image
36 36
 ..........
37 37
 
38 38
 An image that has no parent is a **base image**.
39
+
40
+Image IDs
41
+.........
42
+All images are identified by a 64 hexadecimal digit string (internally a 256bit 
43
+value). To simplify their use, a short ID of the first 12 characters can be used 
44
+on the command line. There is a small possibility of short id collisions, so the 
45
+docker server will always return the long ID.
46
+
... ...
@@ -22,22 +22,37 @@ specify the path to it and manually start it.
22 22
     # Run docker in daemon mode
23 23
     sudo <path to>/docker -d &
24 24
 
25
-
26
-Running an interactive shell
25
+Download a pre-built image
26
+--------------------------
27 27
 
28 28
 .. code-block:: bash
29 29
 
30 30
   # Download an ubuntu image
31 31
   sudo docker pull ubuntu
32 32
 
33
+This will find the ``ubuntu`` image by name in the :ref:`Central Index 
34
+<searching_central_index>` and download it from the top-level Central 
35
+Repository to a local image cache.
36
+
37
+.. NOTE:: When the image has successfully downloaded, you will see a 12 
38
+character hash ``539c0211cd76: Download complete`` which is the short 
39
+form of the image ID. These short image IDs are the first 12 characters 
40
+of the full image ID - which can be found using ``docker inspect`` or 
41
+``docker images -notrunc=true``
42
+
43
+.. _dockergroup:
44
+
45
+Running an interactive shell
46
+----------------------------
47
+
48
+.. code-block:: bash
49
+
33 50
   # Run an interactive shell in the ubuntu image,
34 51
   # allocate a tty, attach stdin and stdout
35 52
   # To detach the tty without exiting the shell,
36 53
   # use the escape sequence Ctrl-p + Ctrl-q
37 54
   sudo docker run -i -t ubuntu /bin/bash
38 55
 
39
-.. _dockergroup:
40 56
 
41 57
 Why ``sudo``?
42 58
 -------------
... ...
@@ -202,10 +202,6 @@ func (image *Image) Changes(rw string) ([]Change, error) {
202 202
 	return Changes(layers, rw)
203 203
 }
204 204
 
205
-func (image *Image) ShortID() string {
206
-	return utils.TruncateID(image.ID)
207
-}
208
-
209 205
 func ValidateID(id string) error {
210 206
 	if id == "" {
211 207
 		return fmt.Errorf("Image id can't be empty")
... ...
@@ -181,7 +181,7 @@ func (runtime *Runtime) ensureName(container *Container) error {
181 181
 	if container.Name == "" {
182 182
 		name, err := generateRandomName(runtime)
183 183
 		if err != nil {
184
-			name = container.ShortID()
184
+			name = utils.TruncateID(container.ID)
185 185
 		}
186 186
 		container.Name = name
187 187
 
... ...
@@ -288,7 +288,7 @@ func (runtime *Runtime) restore() error {
288 288
 		// Try to set the default name for a container if it exists prior to links
289 289
 		container.Name, err = generateRandomName(runtime)
290 290
 		if err != nil {
291
-			container.Name = container.ShortID()
291
+			container.Name = utils.TruncateID(container.ID)
292 292
 		}
293 293
 
294 294
 		if _, err := runtime.containerGraph.Set(container.Name, container.ID); err != nil {
... ...
@@ -154,7 +154,7 @@ func (srv *Server) ContainerKill(name string, sig int) error {
154 154
 			if err := container.Kill(); err != nil {
155 155
 				return fmt.Errorf("Cannot kill container %s: %s", name, err)
156 156
 			}
157
-			srv.LogEvent("kill", container.ShortID(), srv.runtime.repositories.ImageName(container.Image))
157
+			srv.LogEvent("kill", container.ID, srv.runtime.repositories.ImageName(container.Image))
158 158
 		} else {
159 159
 			// Otherwise, just send the requested signal
160 160
 			if err := container.kill(sig); err != nil {
... ...
@@ -180,7 +180,7 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error {
180 180
 		if _, err := io.Copy(out, data); err != nil {
181 181
 			return err
182 182
 		}
183
-		srv.LogEvent("export", container.ShortID(), srv.runtime.repositories.ImageName(container.Image))
183
+		srv.LogEvent("export", container.ID, srv.runtime.repositories.ImageName(container.Image))
184 184
 		return nil
185 185
 	}
186 186
 	return fmt.Errorf("No such container: %s", name)
... ...
@@ -230,7 +230,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
230 230
 		return "", err
231 231
 	}
232 232
 	out.Write(sf.FormatStatus("", img.ID))
233
-	return img.ShortID(), nil
233
+	return img.ID, nil
234 234
 }
235 235
 
236 236
 func (srv *Server) ImagesViz(out io.Writer) error {
... ...
@@ -250,9 +250,9 @@ func (srv *Server) ImagesViz(out io.Writer) error {
250 250
 			return fmt.Errorf("Error while getting parent image: %v", err)
251 251
 		}
252 252
 		if parentImage != nil {
253
-			out.Write([]byte(" \"" + parentImage.ShortID() + "\" -> \"" + image.ShortID() + "\"\n"))
253
+			out.Write([]byte(" \"" + parentImage.ID + "\" -> \"" + image.ID + "\"\n"))
254 254
 		} else {
255
-			out.Write([]byte(" base -> \"" + image.ShortID() + "\" [style=invis]\n"))
255
+			out.Write([]byte(" base -> \"" + image.ID + "\" [style=invis]\n"))
256 256
 		}
257 257
 	}
258 258
 
... ...
@@ -465,7 +465,7 @@ func (srv *Server) Containers(all, size bool, n int, since, before string) []API
465 465
 			continue
466 466
 		}
467 467
 		if before != "" {
468
-			if container.ShortID() == before {
468
+			if container.ID == before || utils.TruncateID(container.ID) == before {
469 469
 				foundBefore = true
470 470
 				continue
471 471
 			}
... ...
@@ -476,7 +476,7 @@ func (srv *Server) Containers(all, size bool, n int, since, before string) []API
476 476
 		if displayed == n {
477 477
 			break
478 478
 		}
479
-		if container.ShortID() == since {
479
+		if container.ID == since || utils.TruncateID(container.ID) == since {
480 480
 			break
481 481
 		}
482 482
 		displayed++
... ...
@@ -518,7 +518,7 @@ func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, conf
518 518
 	if err != nil {
519 519
 		return "", err
520 520
 	}
521
-	return img.ShortID(), err
521
+	return img.ID, err
522 522
 }
523 523
 
524 524
 func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
... ...
@@ -1017,7 +1017,7 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
1017 1017
 			return err
1018 1018
 		}
1019 1019
 	}
1020
-	out.Write(sf.FormatStatus("", img.ShortID()))
1020
+	out.Write(sf.FormatStatus("", img.ID))
1021 1021
 	return nil
1022 1022
 }
1023 1023
 
... ...
@@ -1046,8 +1046,8 @@ func (srv *Server) ContainerCreate(config *Config, name string) (string, []strin
1046 1046
 		}
1047 1047
 		return "", nil, err
1048 1048
 	}
1049
-	srv.LogEvent("create", container.ShortID(), srv.runtime.repositories.ImageName(container.Image))
1050
-	return container.ShortID(), buildWarnings, nil
1049
+	srv.LogEvent("create", container.ID, srv.runtime.repositories.ImageName(container.Image))
1050
+	return container.ID, buildWarnings, nil
1051 1051
 }
1052 1052
 
1053 1053
 func (srv *Server) ContainerRestart(name string, t int) error {
... ...
@@ -1055,7 +1055,7 @@ func (srv *Server) ContainerRestart(name string, t int) error {
1055 1055
 		if err := container.Restart(t); err != nil {
1056 1056
 			return fmt.Errorf("Cannot restart container %s: %s", name, err)
1057 1057
 		}
1058
-		srv.LogEvent("restart", container.ShortID(), srv.runtime.repositories.ImageName(container.Image))
1058
+		srv.LogEvent("restart", container.ID, srv.runtime.repositories.ImageName(container.Image))
1059 1059
 	} else {
1060 1060
 		return fmt.Errorf("No such container: %s", name)
1061 1061
 	}
... ...
@@ -1111,7 +1111,7 @@ func (srv *Server) ContainerDestroy(name string, removeVolume, removeLink bool)
1111 1111
 		if err := srv.runtime.Destroy(container); err != nil {
1112 1112
 			return fmt.Errorf("Cannot destroy container %s: %s", name, err)
1113 1113
 		}
1114
-		srv.LogEvent("destroy", container.ShortID(), srv.runtime.repositories.ImageName(container.Image))
1114
+		srv.LogEvent("destroy", container.ID, srv.runtime.repositories.ImageName(container.Image))
1115 1115
 
1116 1116
 		if removeVolume {
1117 1117
 			// Retrieve all volumes from all remaining containers
... ...
@@ -1228,8 +1228,8 @@ func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, erro
1228 1228
 			return nil, err
1229 1229
 		}
1230 1230
 		if tagDeleted {
1231
-			imgs = append(imgs, APIRmi{Untagged: img.ShortID()})
1232
-			srv.LogEvent("untag", img.ShortID(), "")
1231
+			imgs = append(imgs, APIRmi{Untagged: img.ID})
1232
+			srv.LogEvent("untag", img.ID, "")
1233 1233
 		}
1234 1234
 	}
1235 1235
 	if len(srv.runtime.repositories.ByID()[img.ID]) == 0 {
... ...
@@ -1364,7 +1364,7 @@ func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error {
1364 1364
 	if err := container.Start(); err != nil {
1365 1365
 		return fmt.Errorf("Cannot start container %s: %s", name, err)
1366 1366
 	}
1367
-	srv.LogEvent("start", container.ShortID(), runtime.repositories.ImageName(container.Image))
1367
+	srv.LogEvent("start", container.ID, runtime.repositories.ImageName(container.Image))
1368 1368
 
1369 1369
 	return nil
1370 1370
 }
... ...
@@ -1374,7 +1374,7 @@ func (srv *Server) ContainerStop(name string, t int) error {
1374 1374
 		if err := container.Stop(t); err != nil {
1375 1375
 			return fmt.Errorf("Cannot stop container %s: %s", name, err)
1376 1376
 		}
1377
-		srv.LogEvent("stop", container.ShortID(), srv.runtime.repositories.ImageName(container.Image))
1377
+		srv.LogEvent("stop", container.ID, srv.runtime.repositories.ImageName(container.Image))
1378 1378
 	} else {
1379 1379
 		return fmt.Errorf("No such container: %s", name)
1380 1380
 	}