diff --git a/components/engine/CHANGELOG.md b/components/engine/CHANGELOG.md index acd46e87f9..0dfc3a88b4 100644 --- a/components/engine/CHANGELOG.md +++ b/components/engine/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 0.6.3 (2013-09-23) +* Packaging: Update tar vendor dependency +- Client: Fix detach issue +- Runtime: Only copy and change permissions on non-bindmount volumes +- Registry: Update regular expression to match index +* Runtime: Allow multiple volumes-from +* Packaging: Download apt key over HTTPS +* Documentation: Update section on extracting the docker binary after build +* Documentation: Update development environment docs for new build process +* Documentation: Remove 'base' image from documentation +* Packaging: Add 'docker' group on install for ubuntu package +- Runtime: Fix HTTP imports from STDIN + ## 0.6.2 (2013-09-17) + Hack: Vendor all dependencies + Builder: Add -rm option in order to remove intermediate containers diff --git a/components/engine/VERSION b/components/engine/VERSION index b616048743..844f6a91ac 100644 --- a/components/engine/VERSION +++ b/components/engine/VERSION @@ -1 +1 @@ -0.6.2 +0.6.3 diff --git a/components/engine/commands.go b/components/engine/commands.go index 62d42b9874..556debdbbc 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -6,6 +6,7 @@ import ( "bytes" "encoding/base64" "encoding/json" + "errors" "flag" "fmt" "github.com/dotcloud/docker/auth" @@ -36,6 +37,10 @@ var ( VERSION string ) +var ( + ErrConnectionRefused = errors.New("Can't connect to docker daemon. Is 'docker -d' running on this host?") +) + func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) { methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:]) return reflect.TypeOf(cli).MethodByName(methodName) @@ -795,11 +800,13 @@ func (cli *DockerCli) CmdImport(args ...string) error { v.Set("tag", tag) v.Set("fromSrc", src) - err := cli.stream("POST", "/images/create?"+v.Encode(), cli.in, cli.out, nil) - if err != nil { - return err + var in io.Reader + + if src == "-" { + in = cli.in } - return nil + + return cli.stream("POST", "/images/create?"+v.Encode(), in, cli.out, nil) } func (cli *DockerCli) CmdPush(args ...string) error { @@ -1256,7 +1263,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error { if container.Config.Tty { if err := cli.monitorTtySize(cmd.Arg(0)); err != nil { - return err + utils.Debugf("Error monitoring tty size: %s", err) } } @@ -1565,12 +1572,12 @@ func (cli *DockerCli) CmdRun(args ...string) error { // Detached mode <-wait } else { - status, err := waitForExit(cli, runResult.ID) + status, err := getExitCode(cli, runResult.ID) if err != nil { return err } if status != 0 { - return &utils.StatusError{status} + return &utils.StatusError{Status: status} } } @@ -1636,7 +1643,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, dial, err := net.Dial(cli.proto, cli.addr) if err != nil { if strings.Contains(err.Error(), "connection refused") { - return nil, -1, fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?") + return nil, -1, ErrConnectionRefused } return nil, -1, err } @@ -1645,7 +1652,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, defer clientconn.Close() if err != nil { if strings.Contains(err.Error(), "connection refused") { - return nil, -1, fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?") + return nil, -1, ErrConnectionRefused } return nil, -1, err } @@ -1864,7 +1871,11 @@ func (cli *DockerCli) LoadConfigFile() (err error) { func waitForExit(cli *DockerCli, containerId string) (int, error) { body, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil) if err != nil { - return -1, err + // If we can't connect, then the daemon probably died. + if err != ErrConnectionRefused { + return -1, err + } + return -1, nil } var out APIWait @@ -1874,6 +1885,22 @@ func waitForExit(cli *DockerCli, containerId string) (int, error) { return out.StatusCode, nil } +func getExitCode(cli *DockerCli, containerId string) (int, error) { + body, _, err := cli.call("GET", "/containers/"+containerId+"/json", nil) + if err != nil { + // If we can't connect, then the daemon probably died. + if err != ErrConnectionRefused { + return -1, err + } + return -1, nil + } + c := &Container{} + if err := json.Unmarshal(body, c); err != nil { + return -1, err + } + return c.State.ExitCode, nil +} + func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli { var ( isTerminal = false diff --git a/components/engine/commands_test.go b/components/engine/commands_test.go index 2946da8792..dbd85be0c7 100644 --- a/components/engine/commands_test.go +++ b/components/engine/commands_test.go @@ -369,6 +369,110 @@ func TestRunAttachStdin(t *testing.T) { } } +// TestRunDetach checks attaching and detaching with the escape sequence. +func TestRunDetach(t *testing.T) { + + stdin, stdinPipe := io.Pipe() + stdout, stdoutPipe := io.Pipe() + + cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr) + defer cleanup(globalRuntime) + + ch := make(chan struct{}) + go func() { + defer close(ch) + cli.CmdRun("-i", "-t", unitTestImageID, "cat") + }() + + setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() { + if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil { + t.Fatal(err) + } + }) + + container := globalRuntime.List()[0] + + setTimeout(t, "Escape sequence timeout", 5*time.Second, func() { + stdinPipe.Write([]byte{'', ''}) + if err := stdinPipe.Close(); err != nil { + t.Fatal(err) + } + }) + + // wait for CmdRun to return + setTimeout(t, "Waiting for CmdRun timed out", 5*time.Second, func() { + <-ch + }) + + time.Sleep(500 * time.Millisecond) + if !container.State.Running { + t.Fatal("The detached container should be still running") + } + + setTimeout(t, "Waiting for container to die timed out", 20*time.Second, func() { + container.Kill() + container.Wait() + }) +} + +// TestAttachDetach checks that attach in tty mode can be detached +func TestAttachDetach(t *testing.T) { + stdin, stdinPipe := io.Pipe() + stdout, stdoutPipe := io.Pipe() + + cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr) + defer cleanup(globalRuntime) + + go stdout.Read(make([]byte, 1024)) + setTimeout(t, "Starting container timed out", 2*time.Second, func() { + if err := cli.CmdRun("-i", "-t", "-d", unitTestImageID, "cat"); err != nil { + t.Fatal(err) + } + }) + + container := globalRuntime.List()[0] + + stdin, stdinPipe = io.Pipe() + stdout, stdoutPipe = io.Pipe() + cli = NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr) + + ch := make(chan struct{}) + go func() { + defer close(ch) + if err := cli.CmdAttach(container.ShortID()); err != nil { + t.Fatal(err) + } + }() + + setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() { + if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil { + t.Fatal(err) + } + }) + + setTimeout(t, "Escape sequence timeout", 5*time.Second, func() { + stdinPipe.Write([]byte{'', ''}) + if err := stdinPipe.Close(); err != nil { + t.Fatal(err) + } + }) + + // wait for CmdRun to return + setTimeout(t, "Waiting for CmdAttach timed out", 5*time.Second, func() { + <-ch + }) + + time.Sleep(500 * time.Millisecond) + if !container.State.Running { + t.Fatal("The detached container should be still running") + } + + setTimeout(t, "Waiting for container to die timedout", 5*time.Second, func() { + container.Kill() + container.Wait() + }) +} + // Expected behaviour, the process stays alive when the client disconnects func TestAttachDisconnect(t *testing.T) { stdin, stdinPipe := io.Pipe() diff --git a/components/engine/container.go b/components/engine/container.go index 4ea44f2705..6bca3dfbf2 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -146,7 +146,9 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, flVolumes := NewPathOpts() cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)") - flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container") + var flVolumesFrom ListOpts + cmd.Var(&flVolumesFrom, "volumes-from", "Mount volumes from the specified container") + flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image") var flLxcOpts ListOpts @@ -231,7 +233,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, Dns: flDns, Image: image, Volumes: flVolumes, - VolumesFrom: *flVolumesFrom, + VolumesFrom: strings.Join(flVolumesFrom, ","), Entrypoint: entrypoint, Privileged: *flPrivileged, WorkingDir: *flWorkingDir, @@ -639,21 +641,25 @@ func (container *Container) Start(hostConfig *HostConfig) error { // Apply volumes from another container if requested if container.Config.VolumesFrom != "" { - c := container.runtime.Get(container.Config.VolumesFrom) - if c == nil { - return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID) - } - for volPath, id := range c.Volumes { - if _, exists := container.Volumes[volPath]; exists { - continue + volumes := strings.Split(container.Config.VolumesFrom, ",") + for _, v := range volumes { + c := container.runtime.Get(v) + if c == nil { + return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID) } - if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { - return err - } - container.Volumes[volPath] = id - if isRW, exists := c.VolumesRW[volPath]; exists { - container.VolumesRW[volPath] = isRW + for volPath, id := range c.Volumes { + if _, exists := container.Volumes[volPath]; exists { + continue + } + if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { + return err + } + container.Volumes[volPath] = id + if isRW, exists := c.VolumesRW[volPath]; exists { + container.VolumesRW[volPath] = isRW + } } + } } @@ -665,9 +671,11 @@ func (container *Container) Start(hostConfig *HostConfig) error { continue } var srcPath string + var isBindMount bool srcRW := false // If an external bind is defined for this volume, use that as a source if bindMap, exists := binds[volPath]; exists { + isBindMount = true srcPath = bindMap.SrcPath if strings.ToLower(bindMap.Mode) == "rw" { srcRW = true @@ -691,7 +699,9 @@ func (container *Container) Start(hostConfig *HostConfig) error { if err := os.MkdirAll(rootVolPath, 0755); err != nil { return nil } - if srcRW { + + // Do not copy or change permissions if we are mounting from the host + if srcRW && !isBindMount { volList, err := ioutil.ReadDir(rootVolPath) if err != nil { return err @@ -702,22 +712,26 @@ func (container *Container) Start(hostConfig *HostConfig) error { return err } if len(srcList) == 0 { + // If the source volume is empty copy files from the root into the volume if err := CopyWithTar(rootVolPath, srcPath); err != nil { return err } - } - } - var stat syscall.Stat_t - if err := syscall.Stat(rootVolPath, &stat); err != nil { - return err - } - var srcStat syscall.Stat_t - if err := syscall.Stat(srcPath, &srcStat); err != nil { - return err - } - if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid { - if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil { - return err + + var stat syscall.Stat_t + if err := syscall.Stat(rootVolPath, &stat); err != nil { + return err + } + var srcStat syscall.Stat_t + if err := syscall.Stat(srcPath, &srcStat); err != nil { + return err + } + // Change the source volume's ownership if it differs from the root + // files that where just copied + if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid { + if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil { + return err + } + } } } } @@ -950,14 +964,19 @@ func (container *Container) monitor() { } } utils.Debugf("Process finished") - if container.runtime != nil && container.runtime.srv != nil { - container.runtime.srv.LogEvent("die", container.ShortID(), container.runtime.repositories.ImageName(container.Image)) - } + exitCode := -1 if container.cmd != nil { exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() } + // Report status back + container.State.setStopped(exitCode) + + if container.runtime != nil && container.runtime.srv != nil { + container.runtime.srv.LogEvent("die", container.ShortID(), container.runtime.repositories.ImageName(container.Image)) + } + // Cleanup container.releaseNetwork() if container.Config.OpenStdin { @@ -987,9 +1006,6 @@ func (container *Container) monitor() { container.stdin, container.stdinPipe = io.Pipe() } - // Report status back - container.State.setStopped(exitCode) - // Release the lock close(container.waitLock) diff --git a/components/engine/container_test.go b/components/engine/container_test.go index 41940242f9..e678c98898 100644 --- a/components/engine/container_test.go +++ b/components/engine/container_test.go @@ -1202,7 +1202,7 @@ func TestCopyVolumeUidGid(t *testing.T) { defer nuke(r) // Add directory not owned by root - container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello && chown daemon.daemon /hello"}, t) + container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello && touch /hello/test.txt && chown daemon.daemon /hello"}, t) defer r.Destroy(container1) if container1.State.Running { @@ -1227,18 +1227,10 @@ func TestCopyVolumeUidGid(t *testing.T) { // Test that the uid and gid is copied from the image to the volume tmpDir1 := tempDir(t) defer os.RemoveAll(tmpDir1) - stdout1, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello", tmpDir1), img.ID, "stat", "-c", "%U %G", "/hello"}, t) + stdout1, _ := runContainer(r, []string{"-v", "/hello", img.ID, "stat", "-c", "%U %G", "/hello"}, t) if !strings.Contains(stdout1, "daemon daemon") { t.Fatal("Container failed to transfer uid and gid to volume") } - - // Test that the uid and gid is not copied from the image when the volume is read only - tmpDir2 := tempDir(t) - defer os.RemoveAll(tmpDir1) - stdout2, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:ro", tmpDir2), img.ID, "stat", "-c", "%U %G", "/hello"}, t) - if strings.Contains(stdout2, "daemon daemon") { - t.Fatal("Container transfered uid and gid to volume") - } } // Test for #1582 @@ -1272,27 +1264,10 @@ func TestCopyVolumeContent(t *testing.T) { // Test that the content is copied from the image to the volume tmpDir1 := tempDir(t) defer os.RemoveAll(tmpDir1) - stdout1, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello", tmpDir1), img.ID, "find", "/hello"}, t) + stdout1, _ := runContainer(r, []string{"-v", "/hello", img.ID, "find", "/hello"}, t) if !(strings.Contains(stdout1, "/hello/local/world") && strings.Contains(stdout1, "/hello/local")) { t.Fatal("Container failed to transfer content to volume") } - - // Test that the content is not copied when the volume is readonly - tmpDir2 := tempDir(t) - defer os.RemoveAll(tmpDir2) - stdout2, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:ro", tmpDir2), img.ID, "find", "/hello"}, t) - if strings.Contains(stdout2, "/hello/local/world") || strings.Contains(stdout2, "/hello/local") { - t.Fatal("Container transfered content to readonly volume") - } - - // Test that the content is not copied when the volume is non-empty - tmpDir3 := tempDir(t) - defer os.RemoveAll(tmpDir3) - writeFile(path.Join(tmpDir3, "touch-me"), "", t) - stdout3, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:rw", tmpDir3), img.ID, "find", "/hello"}, t) - if strings.Contains(stdout3, "/hello/local/world") || strings.Contains(stdout3, "/hello/local") || !strings.Contains(stdout3, "/hello/touch-me") { - t.Fatal("Container transfered content to non-empty volume") - } } func TestBindMounts(t *testing.T) { @@ -1549,3 +1524,80 @@ func TestPrivilegedCannotMount(t *testing.T) { t.Fatal("Could mount into secure container") } } + +func TestMultipleVolumesFrom(t *testing.T) { + runtime := mkRuntime(t) + defer nuke(runtime) + + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"}, + Volumes: map[string]struct{}{"/test": {}}, + }, + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container) + + for key := range container.Config.Volumes { + if key != "/test" { + t.Fail() + } + } + + _, err = container.Output() + if err != nil { + t.Fatal(err) + } + + expected := container.Volumes["/test"] + if expected == "" { + t.Fail() + } + + container2, err := runtime.Create( + &Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"sh", "-c", "echo -n bar > /other/foo"}, + Volumes: map[string]struct{}{"/other": {}}, + }, + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container2) + + for key := range container2.Config.Volumes { + if key != "/other" { + t.FailNow() + } + } + if _, err := container2.Output(); err != nil { + t.Fatal(err) + } + + container3, err := runtime.Create( + &Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"/bin/echo", "-n", "foobar"}, + VolumesFrom: strings.Join([]string{container.ID, container2.ID}, ","), + }) + + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container3) + + if _, err := container3.Output(); err != nil { + t.Fatal(err) + } + + t.Log(container3.Volumes) + if container3.Volumes["/test"] != container.Volumes["/test"] { + t.Fail() + } + if container3.Volumes["/other"] != container2.Volumes["/other"] { + t.Fail() + } +} diff --git a/components/engine/contrib/MAINTAINERS b/components/engine/contrib/MAINTAINERS index 2531745dc6..e6aeeeb67d 100644 --- a/components/engine/contrib/MAINTAINERS +++ b/components/engine/contrib/MAINTAINERS @@ -1 +1,2 @@ -Kawsar Saiyeed +Kawsar Saiyeed (@KSid) +Tianon Gravi (@tianon) diff --git a/components/engine/contrib/crashTest.go b/components/engine/contrib/crashTest.go index d3ba80698c..6da89bf887 100644 --- a/components/engine/contrib/crashTest.go +++ b/components/engine/contrib/crashTest.go @@ -77,7 +77,7 @@ func crashTest() error { stop = false for i := 0; i < 100 && !stop; { func() error { - cmd := exec.Command(DOCKERPATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount)) + cmd := exec.Command(DOCKERPATH, "run", "ubuntu", "echo", fmt.Sprintf("%d", totalTestCount)) i++ totalTestCount++ outPipe, err := cmd.StdoutPipe() diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.0.rst b/components/engine/docs/sources/api/docker_remote_api_v1.0.rst index 3ee3245137..1aa8035a57 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.0.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.0.rst @@ -49,28 +49,28 @@ List containers [ { "Id": "8dfafdbc3a40", - "Image": "base:latest", + "Image": "ubuntu:latest", "Command": "echo 1", "Created": 1367854155, "Status": "Exit 0" }, { "Id": "9cd87474be90", - "Image": "base:latest", + "Image": "ubuntu:latest", "Command": "echo 222222", "Created": 1367854155, "Status": "Exit 0" }, { "Id": "3176a2479c92", - "Image": "base:latest", + "Image": "centos:latest", "Command": "echo 3333333333333333", "Created": 1367854154, "Status": "Exit 0" }, { "Id": "4cb07b47f9fb", - "Image": "base:latest", + "Image": "fedora:latest", "Command": "echo 444444444444444444444444444444444", "Created": 1367854152, "Status": "Exit 0" @@ -117,7 +117,7 @@ Create a container "date" ], "Dns":null, - "Image":"base", + "Image":"ubuntu", "Volumes":{}, "VolumesFrom":"" } @@ -183,7 +183,7 @@ Inspect a container "date" ], "Dns": null, - "Image": "base", + "Image": "ubuntu", "Volumes": {}, "VolumesFrom": "" }, @@ -490,14 +490,14 @@ List Images [ { - "Repository":"base", - "Tag":"ubuntu-12.10", + "Repository":"ubuntu", + "Tag":"precise", "Id":"b750fe79269d", "Created":1364102658 }, { - "Repository":"base", - "Tag":"ubuntu-quantal", + "Repository":"ubuntu", + "Tag":"12.04", "Id":"b750fe79269d", "Created":1364102658 } @@ -529,9 +529,9 @@ List Images "d6434d954665" -> "d82cbacda43a" base -> "e9aa60c60128" [style=invis] "074be284591f" -> "f71189fff3de" - "b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; - "e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; - "9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; base [style=invisible] } @@ -552,7 +552,7 @@ Create an image .. sourcecode:: http - POST /images/create?fromImage=base HTTP/1.1 + POST /images/create?fromImage=ubuntu HTTP/1.1 **Example response**: @@ -572,8 +572,8 @@ Create an image :statuscode 500: server error -Insert a file in a image -************************ +Insert a file in an image +************************* .. http:post:: /images/(name)/insert @@ -608,7 +608,7 @@ Inspect an image .. sourcecode:: http - GET /images/base/json HTTP/1.1 + GET /images/centos/json HTTP/1.1 **Example response**: @@ -638,7 +638,7 @@ Inspect an image "Env":null, "Cmd": ["/bin/bash"] ,"Dns":null, - "Image":"base", + "Image":"centos", "Volumes":null, "VolumesFrom":"" } @@ -660,7 +660,7 @@ Get the history of an image .. sourcecode:: http - GET /images/base/history HTTP/1.1 + GET /images/fedora/history HTTP/1.1 **Example response**: diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.1.rst b/components/engine/docs/sources/api/docker_remote_api_v1.1.rst index 7fecdbfddd..db423de46c 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.1.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.1.rst @@ -49,28 +49,28 @@ List containers [ { "Id": "8dfafdbc3a40", - "Image": "base:latest", + "Image": "ubuntu:latest", "Command": "echo 1", "Created": 1367854155, "Status": "Exit 0" }, { "Id": "9cd87474be90", - "Image": "base:latest", + "Image": "ubuntu:latest", "Command": "echo 222222", "Created": 1367854155, "Status": "Exit 0" }, { "Id": "3176a2479c92", - "Image": "base:latest", + "Image": "centos:latest", "Command": "echo 3333333333333333", "Created": 1367854154, "Status": "Exit 0" }, { "Id": "4cb07b47f9fb", - "Image": "base:latest", + "Image": "fedora:latest", "Command": "echo 444444444444444444444444444444444", "Created": 1367854152, "Status": "Exit 0" @@ -117,7 +117,7 @@ Create a container "date" ], "Dns":null, - "Image":"base", + "Image":"ubuntu", "Volumes":{}, "VolumesFrom":"" } @@ -183,7 +183,7 @@ Inspect a container "date" ], "Dns": null, - "Image": "base", + "Image": "ubuntu", "Volumes": {}, "VolumesFrom": "" }, @@ -490,14 +490,14 @@ List Images [ { - "Repository":"base", - "Tag":"ubuntu-12.10", + "Repository":"ubuntu", + "Tag":"precise", "Id":"b750fe79269d", "Created":1364102658 }, { - "Repository":"base", - "Tag":"ubuntu-quantal", + "Repository":"ubuntu", + "Tag":"12.04", "Id":"b750fe79269d", "Created":1364102658 } @@ -529,9 +529,9 @@ List Images "d6434d954665" -> "d82cbacda43a" base -> "e9aa60c60128" [style=invis] "074be284591f" -> "f71189fff3de" - "b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; - "e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; - "9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; base [style=invisible] } @@ -552,7 +552,7 @@ Create an image .. sourcecode:: http - POST /images/create?fromImage=base HTTP/1.1 + POST /images/create?fromImage=ubuntu HTTP/1.1 **Example response**: @@ -575,8 +575,8 @@ Create an image :statuscode 500: server error -Insert a file in a image -************************ +Insert a file in an image +************************* .. http:post:: /images/(name)/insert @@ -615,7 +615,7 @@ Inspect an image .. sourcecode:: http - GET /images/base/json HTTP/1.1 + GET /images/centos/json HTTP/1.1 **Example response**: @@ -645,7 +645,7 @@ Inspect an image "Env":null, "Cmd": ["/bin/bash"] ,"Dns":null, - "Image":"base", + "Image":"centos", "Volumes":null, "VolumesFrom":"" } @@ -667,7 +667,7 @@ Get the history of an image .. sourcecode:: http - GET /images/base/history HTTP/1.1 + GET /images/fedora/history HTTP/1.1 **Example response**: diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.2.rst b/components/engine/docs/sources/api/docker_remote_api_v1.2.rst index 387b5a5dda..a3a9ba2d15 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.2.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.2.rst @@ -49,7 +49,7 @@ List containers [ { "Id": "8dfafdbc3a40", - "Image": "base:latest", + "Image": "ubuntu:latest", "Command": "echo 1", "Created": 1367854155, "Status": "Exit 0", @@ -59,7 +59,7 @@ List containers }, { "Id": "9cd87474be90", - "Image": "base:latest", + "Image": "ubuntu:latest", "Command": "echo 222222", "Created": 1367854155, "Status": "Exit 0", @@ -69,7 +69,7 @@ List containers }, { "Id": "3176a2479c92", - "Image": "base:latest", + "Image": "centos:latest", "Command": "echo 3333333333333333", "Created": 1367854154, "Status": "Exit 0", @@ -79,7 +79,7 @@ List containers }, { "Id": "4cb07b47f9fb", - "Image": "base:latest", + "Image": "fedora:latest", "Command": "echo 444444444444444444444444444444444", "Created": 1367854152, "Status": "Exit 0", @@ -129,7 +129,7 @@ Create a container "date" ], "Dns":null, - "Image":"base", + "Image":"ubuntu", "Volumes":{}, "VolumesFrom":"" } @@ -195,7 +195,7 @@ Inspect a container "date" ], "Dns": null, - "Image": "base", + "Image": "ubuntu", "Volumes": {}, "VolumesFrom": "" }, @@ -502,16 +502,16 @@ List Images [ { - "Repository":"base", - "Tag":"ubuntu-12.10", + "Repository":"ubuntu", + "Tag":"precise", "Id":"b750fe79269d", "Created":1364102658, "Size":24653, "VirtualSize":180116135 }, { - "Repository":"base", - "Tag":"ubuntu-quantal", + "Repository":"ubuntu", + "Tag":"12.04", "Id":"b750fe79269d", "Created":1364102658, "Size":24653, @@ -545,9 +545,9 @@ List Images "d6434d954665" -> "d82cbacda43a" base -> "e9aa60c60128" [style=invis] "074be284591f" -> "f71189fff3de" - "b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; - "e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; - "9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; base [style=invisible] } @@ -568,7 +568,7 @@ Create an image .. sourcecode:: http - POST /images/create?fromImage=base HTTP/1.1 + POST /images/create?fromImage=ubuntu HTTP/1.1 **Example response**: @@ -591,8 +591,8 @@ Create an image :statuscode 500: server error -Insert a file in a image -************************ +Insert a file in an image +************************* .. http:post:: /images/(name)/insert @@ -631,7 +631,7 @@ Inspect an image .. sourcecode:: http - GET /images/base/json HTTP/1.1 + GET /images/centos/json HTTP/1.1 **Example response**: @@ -661,7 +661,7 @@ Inspect an image "Env":null, "Cmd": ["/bin/bash"] ,"Dns":null, - "Image":"base", + "Image":"centos", "Volumes":null, "VolumesFrom":"" }, @@ -684,7 +684,7 @@ Get the history of an image .. sourcecode:: http - GET /images/base/history HTTP/1.1 + GET /images/fedora/history HTTP/1.1 **Example response**: @@ -696,7 +696,7 @@ Get the history of an image [ { "Id":"b750fe79269d", - "Tag":["base:latest"], + "Tag":["ubuntu:latest"], "Created":1364102658, "CreatedBy":"/bin/bash" }, diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst index f1d743dd63..40b1f7fc01 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst @@ -49,7 +49,7 @@ List containers [ { "Id": "8dfafdbc3a40", - "Image": "base:latest", + "Image": "ubuntu:latest", "Command": "echo 1", "Created": 1367854155, "Status": "Exit 0", @@ -59,7 +59,7 @@ List containers }, { "Id": "9cd87474be90", - "Image": "base:latest", + "Image": "ubuntu:latest", "Command": "echo 222222", "Created": 1367854155, "Status": "Exit 0", @@ -69,7 +69,7 @@ List containers }, { "Id": "3176a2479c92", - "Image": "base:latest", + "Image": "centos:latest", "Command": "echo 3333333333333333", "Created": 1367854154, "Status": "Exit 0", @@ -79,7 +79,7 @@ List containers }, { "Id": "4cb07b47f9fb", - "Image": "base:latest", + "Image": "fedora:latest", "Command": "echo 444444444444444444444444444444444", "Created": 1367854152, "Status": "Exit 0", @@ -130,7 +130,7 @@ Create a container "date" ], "Dns":null, - "Image":"base", + "Image":"ubuntu", "Volumes":{}, "VolumesFrom":"" } @@ -196,7 +196,7 @@ Inspect a container "date" ], "Dns": null, - "Image": "base", + "Image": "ubuntu", "Volumes": {}, "VolumesFrom": "" }, @@ -550,16 +550,16 @@ List Images [ { - "Repository":"base", - "Tag":"ubuntu-12.10", + "Repository":"ubuntu", + "Tag":"precise", "Id":"b750fe79269d", "Created":1364102658, "Size":24653, "VirtualSize":180116135 }, { - "Repository":"base", - "Tag":"ubuntu-quantal", + "Repository":"ubuntu", + "Tag":"12.04", "Id":"b750fe79269d", "Created":1364102658, "Size":24653, @@ -593,9 +593,9 @@ List Images "d6434d954665" -> "d82cbacda43a" base -> "e9aa60c60128" [style=invis] "074be284591f" -> "f71189fff3de" - "b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; - "e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; - "9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; base [style=invisible] } @@ -616,7 +616,7 @@ Create an image .. sourcecode:: http - POST /images/create?fromImage=base HTTP/1.1 + POST /images/create?fromImage=ubuntu HTTP/1.1 **Example response**: @@ -639,8 +639,8 @@ Create an image :statuscode 500: server error -Insert a file in a image -************************ +Insert a file in an image +************************* .. http:post:: /images/(name)/insert @@ -679,7 +679,7 @@ Inspect an image .. sourcecode:: http - GET /images/base/json HTTP/1.1 + GET /images/centos/json HTTP/1.1 **Example response**: @@ -709,7 +709,7 @@ Inspect an image "Env":null, "Cmd": ["/bin/bash"] ,"Dns":null, - "Image":"base", + "Image":"centos", "Volumes":null, "VolumesFrom":"" }, @@ -732,7 +732,7 @@ Get the history of an image .. sourcecode:: http - GET /images/base/history HTTP/1.1 + GET /images/fedora/history HTTP/1.1 **Example response**: diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.4.rst b/components/engine/docs/sources/api/docker_remote_api_v1.4.rst index b3628f0252..b373fe9d3a 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.4.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.4.rst @@ -46,7 +46,7 @@ List containers [ { "Id": "8dfafdbc3a40", - "Image": "base:latest", + "Image": "ubuntu:latest", "Command": "echo 1", "Created": 1367854155, "Status": "Exit 0", @@ -56,7 +56,7 @@ List containers }, { "Id": "9cd87474be90", - "Image": "base:latest", + "Image": "ubuntu:latest", "Command": "echo 222222", "Created": 1367854155, "Status": "Exit 0", @@ -66,7 +66,7 @@ List containers }, { "Id": "3176a2479c92", - "Image": "base:latest", + "Image": "centos:latest", "Command": "echo 3333333333333333", "Created": 1367854154, "Status": "Exit 0", @@ -76,7 +76,7 @@ List containers }, { "Id": "4cb07b47f9fb", - "Image": "base:latest", + "Image": "fedora:latest", "Command": "echo 444444444444444444444444444444444", "Created": 1367854152, "Status": "Exit 0", @@ -128,7 +128,7 @@ Create a container "date" ], "Dns":null, - "Image":"base", + "Image":"ubuntu", "Volumes":{}, "VolumesFrom":"", "WorkingDir":"" @@ -196,7 +196,7 @@ Inspect a container "date" ], "Dns": null, - "Image": "base", + "Image": "ubuntu", "Volumes": {}, "VolumesFrom": "", "WorkingDir":"" @@ -592,16 +592,16 @@ List Images [ { - "Repository":"base", - "Tag":"ubuntu-12.10", + "Repository":"ubuntu", + "Tag":"precise", "Id":"b750fe79269d", "Created":1364102658, "Size":24653, "VirtualSize":180116135 }, { - "Repository":"base", - "Tag":"ubuntu-quantal", + "Repository":"ubuntu", + "Tag":"12.04", "Id":"b750fe79269d", "Created":1364102658, "Size":24653, @@ -635,9 +635,9 @@ List Images "d6434d954665" -> "d82cbacda43a" base -> "e9aa60c60128" [style=invis] "074be284591f" -> "f71189fff3de" - "b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; - "e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; - "9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; base [style=invisible] } @@ -658,7 +658,7 @@ Create an image .. sourcecode:: http - POST /images/create?fromImage=base HTTP/1.1 + POST /images/create?fromImage=ubuntu HTTP/1.1 **Example response**: @@ -721,7 +721,7 @@ Inspect an image .. sourcecode:: http - GET /images/base/json HTTP/1.1 + GET /images/centos/json HTTP/1.1 **Example response**: @@ -751,7 +751,7 @@ Inspect an image "Env":null, "Cmd": ["/bin/bash"] ,"Dns":null, - "Image":"base", + "Image":"centos", "Volumes":null, "VolumesFrom":"", "WorkingDir":"" @@ -776,7 +776,7 @@ Get the history of an image .. sourcecode:: http - GET /images/base/history HTTP/1.1 + GET /images/fedora/history HTTP/1.1 **Example response**: @@ -1126,10 +1126,10 @@ Monitor Docker's events HTTP/1.1 200 OK Content-Type: application/json - {"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924} - {"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924} - {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} - {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} + {"status":"create","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067924} + {"status":"start","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067924} + {"status":"stop","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067966} + {"status":"destroy","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067970} :query since: timestamp used for polling :statuscode 200: no error diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.5.rst b/components/engine/docs/sources/api/docker_remote_api_v1.5.rst index 522885ff86..2581059b59 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.5.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.5.rst @@ -46,7 +46,7 @@ List containers [ { "Id": "8dfafdbc3a40", - "Image": "base:latest", + "Image": "ubuntu:latest", "Command": "echo 1", "Created": 1367854155, "Status": "Exit 0", @@ -56,7 +56,7 @@ List containers }, { "Id": "9cd87474be90", - "Image": "base:latest", + "Image": "ubuntu:latest", "Command": "echo 222222", "Created": 1367854155, "Status": "Exit 0", @@ -66,7 +66,7 @@ List containers }, { "Id": "3176a2479c92", - "Image": "base:latest", + "Image": "centos:latest", "Command": "echo 3333333333333333", "Created": 1367854154, "Status": "Exit 0", @@ -76,7 +76,7 @@ List containers }, { "Id": "4cb07b47f9fb", - "Image": "base:latest", + "Image": "fedora:latest", "Command": "echo 444444444444444444444444444444444", "Created": 1367854152, "Status": "Exit 0", @@ -128,7 +128,7 @@ Create a container "date" ], "Dns":null, - "Image":"base", + "Image":"ubuntu", "Volumes":{}, "VolumesFrom":"", "WorkingDir":"" @@ -196,7 +196,7 @@ Inspect a container "date" ], "Dns": null, - "Image": "base", + "Image": "ubuntu", "Volumes": {}, "VolumesFrom": "", "WorkingDir":"" @@ -591,16 +591,16 @@ List Images [ { - "Repository":"base", - "Tag":"ubuntu-12.10", + "Repository":"ubuntu", + "Tag":"precise", "Id":"b750fe79269d", "Created":1364102658, "Size":24653, "VirtualSize":180116135 }, { - "Repository":"base", - "Tag":"ubuntu-quantal", + "Repository":"ubuntu", + "Tag":"12.04", "Id":"b750fe79269d", "Created":1364102658, "Size":24653, @@ -634,9 +634,9 @@ List Images "d6434d954665" -> "d82cbacda43a" base -> "e9aa60c60128" [style=invis] "074be284591f" -> "f71189fff3de" - "b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; - "e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; - "9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; base [style=invisible] } @@ -657,7 +657,7 @@ Create an image .. sourcecode:: http - POST /images/create?fromImage=base HTTP/1.1 + POST /images/create?fromImage=ubuntu HTTP/1.1 **Example response**: @@ -724,7 +724,7 @@ Inspect an image .. sourcecode:: http - GET /images/base/json HTTP/1.1 + GET /images/centos/json HTTP/1.1 **Example response**: @@ -754,7 +754,7 @@ Inspect an image "Env":null, "Cmd": ["/bin/bash"] ,"Dns":null, - "Image":"base", + "Image":"centos", "Volumes":null, "VolumesFrom":"", "WorkingDir":"" @@ -778,7 +778,7 @@ Get the history of an image .. sourcecode:: http - GET /images/base/history HTTP/1.1 + GET /images/fedora/history HTTP/1.1 **Example response**: @@ -1131,10 +1131,10 @@ Monitor Docker's events HTTP/1.1 200 OK Content-Type: application/json - {"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924} - {"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924} - {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} - {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} + {"status":"create","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067924} + {"status":"start","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067924} + {"status":"stop","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067966} + {"status":"destroy","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067970} :query since: timestamp used for polling :statuscode 200: no error diff --git a/components/engine/docs/sources/api/index_api.rst b/components/engine/docs/sources/api/index_api.rst index 1d4f475caf..c4b961aff2 100644 --- a/components/engine/docs/sources/api/index_api.rst +++ b/components/engine/docs/sources/api/index_api.rst @@ -541,10 +541,11 @@ Search Content-Type: application/json {"query":"search_term", - "num_results": 2, + "num_results": 3, "results" : [ - {"name": "dotcloud/base", "description": "A base ubuntu64 image..."}, - {"name": "base2", "description": "A base ubuntu64 image..."}, + {"name": "ubuntu", "description": "An ubuntu image..."}, + {"name": "centos", "description": "A centos image..."}, + {"name": "fedora", "description": "A fedora image..."} ] } diff --git a/components/engine/docs/sources/examples/index.rst b/components/engine/docs/sources/examples/index.rst index 904080bb24..01251680b6 100644 --- a/components/engine/docs/sources/examples/index.rst +++ b/components/engine/docs/sources/examples/index.rst @@ -3,6 +3,7 @@ :keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql +.. _example_list: Examples ======== diff --git a/components/engine/docs/sources/examples/running_ssh_service.rst b/components/engine/docs/sources/examples/running_ssh_service.rst index 8effafc82c..db3a71c961 100644 --- a/components/engine/docs/sources/examples/running_ssh_service.rst +++ b/components/engine/docs/sources/examples/running_ssh_service.rst @@ -47,7 +47,7 @@ The password is 'screencast' # I had it so it was quick # now let's connect using -i for interactive and with -t for terminal # we execute /bin/bash to get a prompt. - $ docker run -i -t base /bin/bash + $ docker run -i -t ubuntu /bin/bash # yes! we are in! # now lets install openssh $ apt-get update diff --git a/components/engine/docs/sources/use/baseimages.rst b/components/engine/docs/sources/use/baseimages.rst index 6c8d4fbe2c..d7aef9c062 100644 --- a/components/engine/docs/sources/use/baseimages.rst +++ b/components/engine/docs/sources/use/baseimages.rst @@ -37,7 +37,5 @@ There are more example scripts for creating base images in the Docker Github Repo: * `BusyBox `_ -* `CentOS - `_ * `Debian `_ diff --git a/components/engine/docs/sources/use/workingwithrepository.rst b/components/engine/docs/sources/use/workingwithrepository.rst index cbb0e073bb..a0d46011e0 100644 --- a/components/engine/docs/sources/use/workingwithrepository.rst +++ b/components/engine/docs/sources/use/workingwithrepository.rst @@ -52,31 +52,58 @@ repositories in these examples. * User images are not checked, it is therefore up to you whether or not you trust the creator of this image. -Find public images available on the Central Index -------------------------------------------------- +.. _searching_central_index: -Search by name, namespace or description +Find Public Images on the Central Index +--------------------------------------- + +You can search the Central Index `online `_ +or by the CLI. Searching can find images by name, user name or +description: .. code-block:: bash - sudo docker search + $ sudo docker help search + Usage: docker search NAME + Search the docker index for images -Download them simply by their name + -notrunc=false: Don't truncate output + $ sudo docker search centos + Found 25 results matching your query ("centos") + NAME DESCRIPTION + centos + slantview/centos-chef-solo CentOS 6.4 with chef-solo. + ... + +There you can see two example results: ``centos`` and +``slantview/centos-chef-solo``. The second result shows that it comes +from the public repository of a user, ``slantview/``, while the first +result (``centos``) doesn't explicitly list a repository so it comes +from the trusted Central Repository. The ``/`` character separates a +user's repository and the image name. + +Once you have found the image name, you can download it: .. code-block:: bash - sudo docker pull + # sudo docker pull + $ sudo docker pull centos + Pulling repository centos + 539c0211cd76: Download complete +What can you do with that image? Check out the :ref:`example_list` +and, when you're ready with your own image, come back here to learn +how to share it. -Very similarly you can search for and browse the index online on -https://index.docker.io +Contributing to the Central Registry +------------------------------------ - -Connecting to the Central Registry ----------------------------------- - -You can create a user on the central Docker Index online, or by running +Anyone can pull public images from the Central Registry, but if you +would like to share one of your own images, then you must register a +unique user name first. You can create your username and login on the +`central Docker Index online +`_, or by running .. code-block:: bash @@ -85,22 +112,27 @@ You can create a user on the central Docker Index online, or by running This will prompt you for a username, which will become a public namespace for your public repositories. -If your username does not exist it will prompt you to also enter a -password and your e-mail address. It will then automatically log you -in. +If your username is available then ``docker`` will also prompt you to +enter a password and your e-mail address. It will then automatically +log you in. Now you're ready to commit and push your own images! .. _container_commit: -Committing a container to a named image +Committing a Container to a Named Image --------------------------------------- -In order to commit to the repository it is required to have committed -your container to an image within your username namespace. +When you make changes to an existing image, those changes get saved to +a container's file system. You can then promote that container to +become an image by making a ``commit``. In addition to converting the +container to an image, this is also your opportunity to name the +image, specifically a name that includes your user name from the +Central Docker Index (as you did a ``login`` above) and a meaningful +name for the image. .. code-block:: bash - # for example docker commit $CONTAINER_ID dhrp/kickassapp - sudo docker commit / + # format is "sudo docker commit /" + $ sudo docker commit $CONTAINER_ID myname/kickassapp .. _image_push: @@ -115,15 +147,15 @@ or tag. .. code-block:: bash - # for example docker push dhrp/kickassapp - sudo docker push / + # format is "docker push /" + $ sudo docker push myname/kickassapp .. _using_private_repositories: Private Repositories -------------------- -Right now (version 0.5), private repositories are only possible by +Right now (version 0.6), private repositories are only possible by hosting `your own registry `_. To push or pull to a repository on your own registry, you must prefix the tag with the diff --git a/components/engine/hack/make/ubuntu b/components/engine/hack/make/ubuntu index dde11ae81e..772fa91385 100644 --- a/components/engine/hack/make/ubuntu +++ b/components/engine/hack/make/ubuntu @@ -51,11 +51,13 @@ bundle_ubuntu() { cat >/tmp/postinstall </tmp/prerm <= 0x80 { return false } } return true } -func stripTo7Bits(s string) string { - var buffer bytes.Buffer - for _, character := range s { - if (character & 0x7f) == character { - buffer.WriteRune(character) +func toASCII(s string) string { + if isASCII(s) { + return s + } + var buf bytes.Buffer + for _, c := range s { + if c < 0x80 { + buf.WriteByte(byte(c)) } } - return buffer.String() -} - -func stripTo7BitsAndShorten(s string, maxLen int) string { - var buffer bytes.Buffer - count := 0 - for _, character := range s { - if count == maxLen { - break - } - if (character & 0x7f) == character { - buffer.WriteRune(character) - count++ - } - } - return buffer.String() + return buf.String() } diff --git a/components/engine/vendor/src/github.com/dotcloud/tar/reader.go b/components/engine/vendor/src/github.com/dotcloud/tar/reader.go index 3f026f393c..b2d62f3c51 100644 --- a/components/engine/vendor/src/github.com/dotcloud/tar/reader.go +++ b/components/engine/vendor/src/github.com/dotcloud/tar/reader.go @@ -95,45 +95,45 @@ func (tr *Reader) Next() (*Header, error) { func mergePAX(hdr *Header, headers map[string]string) error { for k, v := range headers { switch k { - case PAX_PATH: + case paxPath: hdr.Name = v - case PAX_LINKPATH: + case paxLinkpath: hdr.Linkname = v - case PAX_GNAME: + case paxGname: hdr.Gname = v - case PAX_UNAME: + case paxUname: hdr.Uname = v - case PAX_UID: + case paxUid: uid, err := strconv.ParseInt(v, 10, 0) if err != nil { return err } hdr.Uid = int(uid) - case PAX_GID: + case paxGid: gid, err := strconv.ParseInt(v, 10, 0) if err != nil { return err } hdr.Gid = int(gid) - case PAX_ATIME: + case paxAtime: t, err := parsePAXTime(v) if err != nil { return err } hdr.AccessTime = t - case PAX_MTIME: + case paxMtime: t, err := parsePAXTime(v) if err != nil { return err } hdr.ModTime = t - case PAX_CTIME: + case paxCtime: t, err := parsePAXTime(v) if err != nil { return err } hdr.ChangeTime = t - case PAX_SIZE: + case paxSize: size, err := strconv.ParseInt(v, 10, 0) if err != nil { return err @@ -243,13 +243,15 @@ func (tr *Reader) octal(b []byte) int64 { return x } - // Removing leading spaces. - for len(b) > 0 && b[0] == ' ' { - b = b[1:] - } - // Removing trailing NULs and spaces. - for len(b) > 0 && (b[len(b)-1] == ' ' || b[len(b)-1] == '\x00') { - b = b[0 : len(b)-1] + // Because unused fields are filled with NULs, we need + // to skip leading NULs. Fields may also be padded with + // spaces or NULs. + // So we remove leading and trailing NULs and spaces to + // be sure. + b = bytes.Trim(b, " \x00") + + if len(b) == 0 { + return 0 } x, err := strconv.ParseUint(cString(b), 8, 64) if err != nil { diff --git a/components/engine/vendor/src/github.com/dotcloud/tar/reader_test.go b/components/engine/vendor/src/github.com/dotcloud/tar/reader_test.go index 9a19682371..1285616565 100644 --- a/components/engine/vendor/src/github.com/dotcloud/tar/reader_test.go +++ b/components/engine/vendor/src/github.com/dotcloud/tar/reader_test.go @@ -142,6 +142,25 @@ var untarTests = []*untarTest{ }, }, }, + { + file: "testdata/nil-uid.tar", // golang.org/issue/5290 + headers: []*Header{ + { + Name: "P1050238.JPG.log", + Mode: 0664, + Uid: 0, + Gid: 0, + Size: 14, + ModTime: time.Unix(1365454838, 0), + Typeflag: TypeReg, + Linkname: "", + Uname: "eyefi", + Gname: "eyefi", + Devmajor: 0, + Devminor: 0, + }, + }, + }, } func TestReader(t *testing.T) { @@ -152,6 +171,7 @@ testLoop: t.Errorf("test %d: Unexpected error: %v", i, err) continue } + defer f.Close() tr := NewReader(f) for j, header := range test.headers { hdr, err := tr.Next() @@ -172,7 +192,6 @@ testLoop: if hdr != nil || err != nil { t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err) } - f.Close() } } diff --git a/components/engine/vendor/src/github.com/dotcloud/tar/testdata/nil-uid.tar b/components/engine/vendor/src/github.com/dotcloud/tar/testdata/nil-uid.tar new file mode 100644 index 0000000000..cc9cfaa33c Binary files /dev/null and b/components/engine/vendor/src/github.com/dotcloud/tar/testdata/nil-uid.tar differ diff --git a/components/engine/vendor/src/github.com/dotcloud/tar/writer.go b/components/engine/vendor/src/github.com/dotcloud/tar/writer.go index 05dcaf04e8..549f1464c3 100644 --- a/components/engine/vendor/src/github.com/dotcloud/tar/writer.go +++ b/components/engine/vendor/src/github.com/dotcloud/tar/writer.go @@ -20,11 +20,11 @@ import ( ) var ( - ErrWriteTooLong = errors.New("archive/tar: write too long") - ErrFieldTooLong = errors.New("archive/tar: header field too long") - ErrWriteAfterClose = errors.New("archive/tar: write after close") - errNameTooLong = errors.New("archive/tar: name too long") - errFieldTooLongNoAscii = errors.New("archive/tar: header field too long or contains invalid values") + ErrWriteTooLong = errors.New("archive/tar: write too long") + ErrFieldTooLong = errors.New("archive/tar: header field too long") + ErrWriteAfterClose = errors.New("archive/tar: write after close") + errNameTooLong = errors.New("archive/tar: name too long") + errInvalidHeader = errors.New("archive/tar: header field too long or contains invalid values") ) // A Writer provides sequential writing of a tar archive in POSIX.1 format. @@ -67,29 +67,23 @@ func (tw *Writer) Flush() error { } // Write s into b, terminating it with a NUL if there is room. -func (tw *Writer) cString(b []byte, s string) { +// If the value is too long for the field and allowPax is true add a paxheader record instead +func (tw *Writer) cString(b []byte, s string, allowPax bool, paxKeyword string, paxHeaders map[string]string) { + needsPaxHeader := allowPax && len(s) > len(b) || !isASCII(s) + if needsPaxHeader { + paxHeaders[paxKeyword] = s + return + } if len(s) > len(b) { if tw.err == nil { tw.err = ErrFieldTooLong } return } - copy(b, s) - if len(s) < len(b) { - b[len(s)] = 0 - } -} - -// Write s into b, terminating it with a NUL if there is room. If the value is too long for the field add a paxheader record instead -func (tw *Writer) fillHeaderField(b []byte, paxHeader map[string]string, paxKeyword string, s string) { - needsPaxHeader := len(s) > len(b) || !isASCII7Bit(s) - if needsPaxHeader { - paxHeader[paxKeyword] = s - return - } - copy(b, stripTo7BitsAndShorten(s, len(b))) - if len(s) < len(b) { - b[len(s)] = 0 + ascii := toASCII(s) + copy(b, ascii) + if len(ascii) < len(b) { + b[len(ascii)] = 0 } } @@ -100,17 +94,27 @@ func (tw *Writer) octal(b []byte, x int64) { for len(s)+1 < len(b) { s = "0" + s } - tw.cString(b, s) + tw.cString(b, s, false, paxNone, nil) } // Write x into b, either as octal or as binary (GNUtar/star extension). -func (tw *Writer) numeric(b []byte, x int64) { +// If the value is too long for the field and writingPax is enabled both for the field and the add a paxheader record instead +func (tw *Writer) numeric(b []byte, x int64, allowPax bool, paxKeyword string, paxHeaders map[string]string) { // Try octal first. s := strconv.FormatInt(x, 8) if len(s) < len(b) { tw.octal(b, x) return } + + // If it is too long for octal, and pax is preferred, use a pax header + if allowPax && tw.preferPax { + tw.octal(b, 0) + s := strconv.FormatInt(x, 10) + paxHeaders[paxKeyword] = s + return + } + // Too big: use binary (big-endian). tw.usedBinary = true for i := len(b) - 1; x > 0 && i >= 0; i-- { @@ -120,28 +124,6 @@ func (tw *Writer) numeric(b []byte, x int64) { b[0] |= 0x80 // highest bit indicates binary format } -// Write x into b, if it is smaller than 2097151. If the value is too long for the field add a paxheader record instead -func (tw *Writer) fillNumericHeaderField(b []byte, paxHeader map[string]string, paxKeyword string, x int64) { - if tw.preferPax && x > 2097151 { - s := strconv.FormatInt(x, 10) - paxHeader[paxKeyword] = s - tw.numeric(b, 0) - } else { - tw.numeric(b, x) - } -} - -// Write x into b, if it is smaller than 2097151. If the value is too long for the field add a paxheader record instead -func (tw *Writer) fillNumericLongHeaderField(b []byte, paxHeader map[string]string, paxKeyword string, x int64) { - if tw.preferPax && x > 8589934591 { - s := strconv.FormatInt(x, 10) - paxHeader[paxKeyword] = s - tw.numeric(b, 0) - } else { - tw.numeric(b, x) - } -} - var ( minTime = time.Unix(0, 0) // There is room for 11 octal digits (33 bits) of mtime. @@ -158,7 +140,7 @@ func (tw *Writer) WriteHeader(hdr *Header) error { // WriteHeader writes hdr and prepares to accept the file's contents. // WriteHeader calls Flush if it is not the first header. // Calling after a Close will return ErrWriteAfterClose. -// As this method is called internally by writePax header it allows to +// As this method is called internally by writePax header to allow it to // suppress writing the pax header. func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { if tw.closed { @@ -172,7 +154,7 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { } // a map to hold pax header records, if any are needed - paxHeaderRecords := make(map[string]string) + paxHeaders := make(map[string]string) // TODO(shanemhansen): we might want to use PAX headers for // subsecond time resolution, but for now let's just capture @@ -184,7 +166,7 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { // keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax pathHeaderBytes := s.next(fileNameSize) - tw.fillHeaderField(pathHeaderBytes, paxHeaderRecords, PAX_PATH, hdr.Name) + tw.cString(pathHeaderBytes, hdr.Name, true, paxPath, paxHeaders) // Handle out of range ModTime carefully. var modTime int64 @@ -192,48 +174,48 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { modTime = hdr.ModTime.Unix() } - tw.octal(s.next(8), hdr.Mode) // 100:108 - tw.fillNumericHeaderField(s.next(8), paxHeaderRecords, PAX_UID, int64(hdr.Uid)) // 108:116 - tw.fillNumericHeaderField(s.next(8), paxHeaderRecords, PAX_GID, int64(hdr.Gid)) // 116:124 - tw.fillNumericLongHeaderField(s.next(12), paxHeaderRecords, PAX_SIZE, hdr.Size) // 124:136 - tw.numeric(s.next(12), modTime) // 136:148 --- consider using pax for finer granularity - s.next(8) // chksum (148:156) - s.next(1)[0] = hdr.Typeflag // 156:157 + tw.octal(s.next(8), hdr.Mode) // 100:108 + tw.numeric(s.next(8), int64(hdr.Uid), true, paxUid, paxHeaders) // 108:116 + tw.numeric(s.next(8), int64(hdr.Gid), true, paxGid, paxHeaders) // 116:124 + tw.numeric(s.next(12), hdr.Size, true, paxSize, paxHeaders) // 124:136 + tw.numeric(s.next(12), modTime, false, paxNone, nil) // 136:148 --- consider using pax for finer granularity + s.next(8) // chksum (148:156) + s.next(1)[0] = hdr.Typeflag // 156:157 - tw.fillHeaderField(s.next(100), paxHeaderRecords, PAX_LINKPATH, hdr.Linkname) + tw.cString(s.next(100), hdr.Linkname, true, paxLinkpath, paxHeaders) - copy(s.next(8), []byte("ustar\x0000")) // 257:265 - tw.fillHeaderField(s.next(32), paxHeaderRecords, PAX_UNAME, hdr.Uname) // 265:297 - tw.fillHeaderField(s.next(32), paxHeaderRecords, PAX_GNAME, hdr.Gname) // 297:329 - tw.numeric(s.next(8), hdr.Devmajor) // 329:337 - tw.numeric(s.next(8), hdr.Devminor) // 337:345 + copy(s.next(8), []byte("ustar\x0000")) // 257:265 + tw.cString(s.next(32), hdr.Uname, true, paxUname, paxHeaders) // 265:297 + tw.cString(s.next(32), hdr.Gname, true, paxGname, paxHeaders) // 297:329 + tw.numeric(s.next(8), hdr.Devmajor, false, paxNone, nil) // 329:337 + tw.numeric(s.next(8), hdr.Devminor, false, paxNone, nil) // 337:345 // keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax prefixHeaderBytes := s.next(155) - tw.cString(prefixHeaderBytes, "") // 345:500 prefix + tw.cString(prefixHeaderBytes, "", false, paxNone, nil) // 345:500 prefix // Use the GNU magic instead of POSIX magic if we used any GNU extensions. if tw.usedBinary { copy(header[257:265], []byte("ustar \x00")) } - _, paxPathUsed := paxHeaderRecords[PAX_PATH] + _, paxPathUsed := paxHeaders[paxPath] // try to use a ustar header when only the name is too long - if !tw.preferPax && len(paxHeaderRecords) == 1 && paxPathUsed { + if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed { suffix := hdr.Name prefix := "" - if len(hdr.Name) > fileNameSize && isASCII7Bit(hdr.Name) { + if len(hdr.Name) > fileNameSize && isASCII(hdr.Name) { var err error prefix, suffix, err = tw.splitUSTARLongName(hdr.Name) if err == nil { // ok we can use a ustar long name instead of pax, now correct the fields // remove the path field from the pax header. this will suppress the pax header - delete(paxHeaderRecords, PAX_PATH) + delete(paxHeaders, paxPath) // update the path fields - tw.cString(pathHeaderBytes, suffix) - tw.cString(prefixHeaderBytes, prefix) + tw.cString(pathHeaderBytes, suffix, false, paxNone, nil) + tw.cString(prefixHeaderBytes, prefix, false, paxNone, nil) // Use the ustar magic if we used ustar long names. if len(prefix) > 0 { @@ -254,17 +236,16 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { return tw.err } - if len(paxHeaderRecords) > 0 { - if allowPax { - if err := tw.writePAXHeader(hdr, paxHeaderRecords); err != nil { - return err - } - } else { - return errFieldTooLongNoAscii + if len(paxHeaders) > 0 { + if !allowPax { + return errInvalidHeader + } + if err := tw.writePAXHeader(hdr, paxHeaders); err != nil { + return err } } tw.nb = int64(hdr.Size) - tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two + tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize _, tw.err = tw.w.Write(header) return tw.err @@ -282,8 +263,11 @@ func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err er length-- } i := strings.LastIndex(name[:length], "/") - nlen := length - i - 1 - if i <= 0 || nlen > fileNameSize || nlen == 0 { + // nlen contains the resulting length in the name field. + // plen contains the resulting length in the prefix field. + nlen := len(name) - i - 1 + plen := i + if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize { err = errNameTooLong return } @@ -293,7 +277,7 @@ func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err er // writePaxHeader writes an extended pax header to the // archive. -func (tw *Writer) writePAXHeader(hdr *Header, paxHeaderRecords map[string]string) error { +func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) error { // Prepare extended header ext := new(Header) ext.Typeflag = TypeXHeader @@ -307,12 +291,16 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHeaderRecords map[string]string fullName := path.Join(dir, fmt.Sprintf("PaxHeaders.%d", pid), file) - ext.Name = stripTo7BitsAndShorten(fullName, 100) + ascii := toASCII(fullName) + if len(ascii) > 100 { + ascii = ascii[:100] + } + ext.Name = ascii // Construct the body var buf bytes.Buffer - for k, v := range paxHeaderRecords { - fmt.Fprint(&buf, paxHeader(k, v)) + for k, v := range paxHeaders { + fmt.Fprint(&buf, paxHeader(k+"="+v)) } ext.Size = int64(len(buf.Bytes())) @@ -329,17 +317,16 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHeaderRecords map[string]string } // paxHeader formats a single pax record, prefixing it with the appropriate length -func paxHeader(keyword string, value string) string { - - const padding = 3 // Extra padding for space and newline - size := len(keyword) + len(value) + padding +func paxHeader(msg string) string { + const padding = 2 // Extra padding for space and newline + size := len(msg) + padding size += len(strconv.Itoa(size)) - record := fmt.Sprintf("%d %s=%s\n", size, keyword, value) + record := fmt.Sprintf("%d %s\n", size, msg) if len(record) != size { // Final adjustment if adding size increased // the number of digits in size size = len(record) - record = fmt.Sprintf("%d %s=%s\n", size, keyword, value) + record = fmt.Sprintf("%d %s\n", size, msg) } return record } diff --git a/components/engine/vendor/src/github.com/dotcloud/tar/writer_test.go b/components/engine/vendor/src/github.com/dotcloud/tar/writer_test.go index bd477a91b1..30ebf977ac 100644 --- a/components/engine/vendor/src/github.com/dotcloud/tar/writer_test.go +++ b/components/engine/vendor/src/github.com/dotcloud/tar/writer_test.go @@ -341,17 +341,53 @@ func TestPaxNonAscii(t *testing.T) { func TestPAXHeader(t *testing.T) { medName := strings.Repeat("CD", 50) longName := strings.Repeat("AB", 100) - paxTests := [][3]string{ - {PAX_PATH, "/etc/hosts", "19 path=/etc/hosts\n"}, - {"a", "b", "6 a=b\n"}, // Single digit length - {"a", "names", "11 a=names\n"}, // Test case involving carries - {PAX_PATH, longName, fmt.Sprintf("210 path=%s\n", longName)}, - {PAX_PATH, medName, fmt.Sprintf("110 path=%s\n", medName)}} + paxTests := [][2]string{ + {paxPath + "=/etc/hosts", "19 path=/etc/hosts\n"}, + {"a=b", "6 a=b\n"}, // Single digit length + {"a=names", "11 a=names\n"}, // Test case involving carries + {paxPath + "=" + longName, fmt.Sprintf("210 path=%s\n", longName)}, + {paxPath + "=" + medName, fmt.Sprintf("110 path=%s\n", medName)}} for _, test := range paxTests { - field, key, expected := test[0], test[1], test[2] - if result := paxHeader(field, key); result != expected { + key, expected := test[0], test[1] + if result := paxHeader(key); result != expected { t.Fatalf("paxHeader: got %s, expected %s", result, expected) } } } + +func TestUSTARLongName(t *testing.T) { + // Create an archive with a path that failed to split with USTAR extension in previous versions. + fileinfo, err := os.Stat("testdata/small.txt") + if err != nil { + t.Fatal(err) + } + hdr, err := FileInfoHeader(fileinfo, "") + hdr.Typeflag = TypeDir + if err != nil { + t.Fatalf("os.Stat:1 %v", err) + } + // Force a PAX long name to be written. The name was taken from a practical example + // that fails and replaced ever char through numbers to anonymize the sample. + longName := "/0000_0000000/00000-000000000/0000_0000000/00000-0000000000000/0000_0000000/00000-0000000-00000000/0000_0000000/00000000/0000_0000000/000/0000_0000000/00000000v00/0000_0000000/000000/0000_0000000/0000000/0000_0000000/00000y-00/0000/0000/00000000/0x000000/" + hdr.Name = longName + + hdr.Size = 0 + var buf bytes.Buffer + writer := NewWriter(&buf) + if err := writer.WriteHeader(hdr); err != nil { + t.Fatal(err) + } + if err := writer.Close(); err != nil { + t.Fatal(err) + } + // Test that we can get a long name back out of the archive. + reader := NewReader(&buf) + hdr, err = reader.Next() + if err != nil { + t.Fatal(err) + } + if hdr.Name != longName { + t.Fatal("Couldn't recover long name") + } +}