diff --git a/.gitignore b/.gitignore index 5843eaf9cc..8cf66168eb 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ docs/_templates bundles/ .hg/ .git/ +vendor/pkg/ diff --git a/AUTHORS b/AUTHORS index 9a22e88de7..579ee0c48b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -151,6 +151,7 @@ Roberto Hashioka Ryan Fowler Sam Alba Sam J Sharpe +Scott Bessler Sean P. Kane Shawn Siefkas Shih-Yuan Lee @@ -185,4 +186,5 @@ Vladimir Kirillov Walter Stanish Wes Morgan Will Dietz +Yang Bai Zaiste! diff --git a/Dockerfile b/Dockerfile index 67963c836b..ff80e4ad9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,15 +33,13 @@ run apt-get update run apt-get install -y -q curl run apt-get install -y -q git run apt-get install -y -q mercurial -run apt-get install -y -q build-essential +run apt-get install -y -q build-essential libsqlite3-dev -# Install Go from source (for eventual cross-compiling) -env CGO_ENABLED 0 -run curl -s https://go.googlecode.com/files/go1.1.2.src.tar.gz | tar -v -C / -xz && mv /go /goroot -run cd /goroot/src && ./make.bash -env GOROOT /goroot -env PATH $PATH:/goroot/bin +# Install Go +run curl -s https://go.googlecode.com/files/go1.2rc2.src.tar.gz | tar -v -C /usr/local -xz +env PATH /usr/local/go/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin env GOPATH /go:/go/src/github.com/dotcloud/docker/vendor +run cd /usr/local/go/src && ./make.bash && go install -ldflags '-w -linkmode external -extldflags "-static -Wl,--unresolved-symbols=ignore-in-shared-libs"' -tags netgo -a std # Ubuntu stuff run apt-get install -y -q ruby1.9.3 rubygems libffi-dev diff --git a/NOTICE b/NOTICE index 663a4019cb..08be17f366 100644 --- a/NOTICE +++ b/NOTICE @@ -1,8 +1,7 @@ Docker -Copyright 2012-2013 dotCloud, inc. +Copyright 2012-2013 Docker, Inc. -This product includes software developed at dotCloud, -inc. (http://www.dotcloud.com). +This product includes software developed at Docker, Inc. (http://www.docker.com). This product contains software (https://github.com/kr/pty) developed by Keith Rarick, licensed under the MIT License. diff --git a/README.md b/README.md index 10291c2cd4..0d0b1cd888 100644 --- a/README.md +++ b/README.md @@ -193,10 +193,10 @@ wrong or incomplete. *Brought to you courtesy of our legal counsel. For more context, please see the Notice document.* -Transfers of Docker shall be in accordance with applicable export -controls of any country and all other applicable legal requirements. -Docker shall not be distributed or downloaded to or in Cuba, Iran, -North Korea, Sudan or Syria and shall not be distributed or downloaded -to any person on the Denied Persons List administered by the U.S. -Department of Commerce. +Transfers of Docker shall be in accordance with applicable export controls +of any country and all other applicable legal requirements. Without limiting the +foregoing, Docker shall not be distributed or downloaded to any individual or +location if such distribution or download would violate the applicable US +government export regulations. +For more information, please see http://www.bis.doc.gov diff --git a/VERSION b/VERSION index d2b13eb644..ba6a53c9ac 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.4 +0.6.4-dev diff --git a/api.go b/api.go index ad308335cc..638e78a713 100644 --- a/api.go +++ b/api.go @@ -42,6 +42,9 @@ func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) { //If we don't do this, POST method without Content-type (even with empty body) will fail func parseForm(r *http.Request) error { + if r == nil { + return nil + } if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") { return err } @@ -69,12 +72,12 @@ func httpError(w http.ResponseWriter, err error) { statusCode = http.StatusUnauthorized } else if strings.Contains(err.Error(), "hasn't been activated") { statusCode = http.StatusForbidden - } - + } + if err != nil { utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error()) - http.Error(w, err.Error(), statusCode) - } + http.Error(w, err.Error(), statusCode) + } } func writeJSON(w http.ResponseWriter, code int, v interface{}) error { @@ -135,8 +138,23 @@ func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r * if vars == nil { return fmt.Errorf("Missing parameter") } + if err := parseForm(r); err != nil { + return err + } name := vars["name"] - if err := srv.ContainerKill(name); err != nil { + + signal := 0 + if r != nil { + s := r.Form.Get("signal") + if s != "" { + if s, err := strconv.Atoi(s); err != nil { + return err + } else { + signal = s + } + } + } + if err := srv.ContainerKill(name, signal); err != nil { return err } w.WriteHeader(http.StatusNoContent) @@ -503,8 +521,12 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http } func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + if err := parseForm(r); err != nil { + return nil + } config := &Config{} out := &APIRun{} + name := r.Form.Get("name") if err := json.NewDecoder(r.Body).Decode(config); err != nil { return err @@ -515,16 +537,19 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r return err } - if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) { + if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) { out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns)) config.Dns = defaultDns } - id, err := srv.ContainerCreate(config) + id, warnings, err := srv.ContainerCreate(config, name) if err != nil { return err } out.ID = id + for _, warning := range warnings { + out.Warnings = append(out.Warnings, warning) + } if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit { log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.") @@ -570,12 +595,17 @@ func deleteContainers(srv *Server, version float64, w http.ResponseWriter, r *ht return fmt.Errorf("Missing parameter") } name := vars["name"] + removeVolume, err := getBoolParam(r.Form.Get("v")) if err != nil { return err } + removeLink, err := getBoolParam(r.Form.Get("link")) + if err != nil { + return err + } - if err := srv.ContainerDestroy(name, removeVolume); err != nil { + if err := srv.ContainerDestroy(name, removeVolume, removeLink); err != nil { return err } w.WriteHeader(http.StatusNoContent) @@ -622,6 +652,10 @@ func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r return fmt.Errorf("Missing parameter") } name := vars["name"] + // Register any links from the host config before starting the container + if err := srv.RegisterLinks(name, hostConfig); err != nil { + return err + } if err := srv.ContainerStart(name, hostConfig); err != nil { return err } @@ -655,6 +689,7 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r * return fmt.Errorf("Missing parameter") } name := vars["name"] + status, err := srv.ContainerWait(name) if err != nil { return err @@ -975,7 +1010,7 @@ func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute s if err != nil { version = APIVERSION } - if srv.enableCors { + if srv.runtime.config.EnableCors { writeCorsHeaders(w, r) } diff --git a/api_params.go b/api_params.go index 6403bc6a26..40738f9fe9 100644 --- a/api_params.go +++ b/api_params.go @@ -1,7 +1,5 @@ package docker -import "encoding/json" - type APIHistory struct { ID string `json:"Id"` Tags []string `json:",omitempty"` @@ -52,17 +50,18 @@ type APIContainers struct { Ports []APIPort SizeRw int64 SizeRootFs int64 + Names []string } func (self *APIContainers) ToLegacy() APIContainersOld { return APIContainersOld{ - ID: self.ID, - Image: self.Image, - Command: self.Command, - Created: self.Created, - Status: self.Status, - Ports: displayablePorts(self.Ports), - SizeRw: self.SizeRw, + ID: self.ID, + Image: self.Image, + Command: self.Command, + Created: self.Created, + Status: self.Status, + Ports: displayablePorts(self.Ports), + SizeRw: self.SizeRw, SizeRootFs: self.SizeRootFs, } } @@ -96,14 +95,7 @@ type APIPort struct { PrivatePort int64 PublicPort int64 Type string -} - -func (port *APIPort) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]interface{}{ - "PrivatePort": port.PrivatePort, - "PublicPort": port.PublicPort, - "Type": port.Type, - }) + IP string } type APIVersion struct { diff --git a/api_test.go b/api_test.go index fcb3bc8593..34c89f8294 100644 --- a/api_test.go +++ b/api_test.go @@ -112,6 +112,7 @@ func TestGetInfo(t *testing.T) { func TestGetEvents(t *testing.T) { runtime := mkRuntime(t) + defer nuke(runtime) srv := &Server{ runtime: runtime, events: make([]utils.JSONMessage, 0, 64), @@ -346,10 +347,12 @@ func TestGetContainersJSON(t *testing.T) { srv := &Server{runtime: runtime} - container, err := runtime.Create(&Config{ + beginLen := runtime.containers.Len() + + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "test"}, - }) + }, "") if err != nil { t.Fatal(err) } @@ -368,8 +371,8 @@ func TestGetContainersJSON(t *testing.T) { if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil { t.Fatal(err) } - if len(containers) != 1 { - t.Fatalf("Expected %d container, %d found", 1, len(containers)) + if len(containers) != beginLen+1 { + t.Fatalf("Expected %d container, %d found (started with: %d)", beginLen+1, len(containers), beginLen) } if containers[0].ID != container.ID { t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.ID, containers[0].ID) @@ -383,11 +386,12 @@ func TestGetContainersExport(t *testing.T) { srv := &Server{runtime: runtime} // Create a container and remove a file - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"touch", "/test"}, }, + "", ) if err != nil { t.Fatal(err) @@ -433,11 +437,12 @@ func TestGetContainersChanges(t *testing.T) { srv := &Server{runtime: runtime} // Create a container and remove a file - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/rm", "/etc/passwd"}, }, + "", ) if err != nil { t.Fatal(err) @@ -471,20 +476,18 @@ func TestGetContainersChanges(t *testing.T) { func TestGetContainersTop(t *testing.T) { t.Skip("Fixme. Skipping test for now. Reported error when testing using dind: 'api_test.go:527: Expected 2 processes, found 0.'") - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/sh", "-c", "cat"}, OpenStdin: true, }, + "", ) if err != nil { t.Fatal(err) @@ -561,11 +564,12 @@ func TestGetContainersByName(t *testing.T) { srv := &Server{runtime: runtime} // Create a container and remove a file - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "test"}, }, + "", ) if err != nil { t.Fatal(err) @@ -592,11 +596,12 @@ func TestPostCommit(t *testing.T) { srv := &Server{runtime: runtime} // Create a container and remove a file - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"touch", "/test"}, }, + "", ) if err != nil { t.Fatal(err) @@ -686,12 +691,13 @@ func TestPostContainersKill(t *testing.T) { srv := &Server{runtime: runtime} - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/cat"}, OpenStdin: true, }, + "", ) if err != nil { t.Fatal(err) @@ -728,12 +734,13 @@ func TestPostContainersRestart(t *testing.T) { srv := &Server{runtime: runtime} - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, - Cmd: []string{"/bin/cat"}, + Cmd: []string{"/bin/top"}, OpenStdin: true, }, + "", ) if err != nil { t.Fatal(err) @@ -782,12 +789,13 @@ func TestPostContainersStart(t *testing.T) { srv := &Server{runtime: runtime} - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/cat"}, OpenStdin: true, }, + "", ) if err != nil { t.Fatal(err) @@ -834,12 +842,13 @@ func TestPostContainersStop(t *testing.T) { srv := &Server{runtime: runtime} - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, - Cmd: []string{"/bin/cat"}, + Cmd: []string{"/bin/top"}, OpenStdin: true, }, + "", ) if err != nil { t.Fatal(err) @@ -881,12 +890,13 @@ func TestPostContainersWait(t *testing.T) { srv := &Server{runtime: runtime} - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/sleep", "1"}, OpenStdin: true, }, + "", ) if err != nil { t.Fatal(err) @@ -923,12 +933,13 @@ func TestPostContainersAttach(t *testing.T) { srv := &Server{runtime: runtime} - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/cat"}, OpenStdin: true, }, + "", ) if err != nil { t.Fatal(err) @@ -1012,12 +1023,13 @@ func TestPostContainersAttachStderr(t *testing.T) { srv := &Server{runtime: runtime} - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/sh", "-c", "/bin/cat >&2"}, OpenStdin: true, }, + "", ) if err != nil { t.Fatal(err) @@ -1104,10 +1116,10 @@ func TestDeleteContainers(t *testing.T) { srv := &Server{runtime: runtime} - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"touch", "/test"}, - }) + }, "") if err != nil { t.Fatal(err) } @@ -1142,7 +1154,8 @@ func TestOptionsRoute(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - srv := &Server{runtime: runtime, enableCors: true} + runtime.config.EnableCors = true + srv := &Server{runtime: runtime} r := httptest.NewRecorder() router, err := createRouter(srv, false) @@ -1165,7 +1178,8 @@ func TestGetEnabledCors(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - srv := &Server{runtime: runtime, enableCors: true} + runtime.config.EnableCors = true + srv := &Server{runtime: runtime} r := httptest.NewRecorder() @@ -1292,11 +1306,12 @@ func TestPostContainersCopy(t *testing.T) { srv := &Server{runtime: runtime} // Create a container and remove a file - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"touch", "/test.txt"}, }, + "", ) if err != nil { t.Fatal(err) diff --git a/auth/auth_test.go b/auth/auth_test.go index 01aecae3da..5dc634a719 100644 --- a/auth/auth_test.go +++ b/auth/auth_test.go @@ -76,7 +76,7 @@ func TestCreateAccount(t *testing.T) { } func setupTempConfigFile() (*ConfigFile, error) { - root, err := ioutil.TempDir("", "docker-test") + root, err := ioutil.TempDir("", "docker-test-auth") if err != nil { return nil, err } @@ -101,6 +101,7 @@ func TestSameAuthDataPostSave(t *testing.T) { if err != nil { t.Fatal(err) } + defer os.RemoveAll(configFile.rootPath) err = SaveConfig(configFile) if err != nil { @@ -127,6 +128,7 @@ func TestResolveAuthConfigIndexServer(t *testing.T) { if err != nil { t.Fatal(err) } + defer os.RemoveAll(configFile.rootPath) for _, registry := range []string{"", IndexServerAddress()} { resolved := configFile.ResolveAuthConfig(registry) @@ -141,6 +143,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) { if err != nil { t.Fatal(err) } + defer os.RemoveAll(configFile.rootPath) registryAuth := AuthConfig{ Username: "foo-user", diff --git a/buildfile.go b/buildfile.go index 8231287eaf..effad01ef0 100644 --- a/buildfile.go +++ b/buildfile.go @@ -187,6 +187,9 @@ func (b *buildFile) CmdCmd(args string) error { } func (b *buildFile) CmdExpose(args string) error { + if strings.Contains(args, ":") { + return fmt.Errorf("EXPOSE cannot be used to bind to a host ip or port") + } ports := strings.Split(args, " ") b.config.PortSpecs = append(ports, b.config.PortSpecs...) return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports)) @@ -332,7 +335,7 @@ func (b *buildFile) CmdAdd(args string) error { b.config.Image = b.image // Create the container and start it - container, err := b.runtime.Create(b.config) + container, _, err := b.runtime.Create(b.config, "") if err != nil { return err } @@ -367,7 +370,7 @@ func (b *buildFile) run() (string, error) { b.config.Image = b.image // Create the container and start it - c, err := b.runtime.Create(b.config) + c, _, err := b.runtime.Create(b.config, "") if err != nil { return "", err } @@ -378,15 +381,22 @@ func (b *buildFile) run() (string, error) { c.Path = b.config.Cmd[0] c.Args = b.config.Cmd[1:] + var errCh chan error + + if b.verbose { + errCh = utils.Go(func() error { + return <-c.Attach(nil, nil, b.out, b.out) + }) + } + //start the container hostConfig := &HostConfig{} if err := c.Start(hostConfig); err != nil { return "", err } - if b.verbose { - err = <-c.Attach(nil, nil, b.out, b.out) - if err != nil { + if errCh != nil { + if err := <-errCh; err != nil { return "", err } } @@ -423,7 +433,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { } } - container, err := b.runtime.Create(b.config) + container, _, err := b.runtime.Create(b.config, "") if err != nil { return err } diff --git a/buildfile_test.go b/buildfile_test.go index c3881a214f..1af457158a 100644 --- a/buildfile_test.go +++ b/buildfile_test.go @@ -229,10 +229,7 @@ func TestBuild(t *testing.T) { func buildImage(context testContextTemplate, t *testing.T, srv *Server, useCache bool) *Image { if srv == nil { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv = &Server{ @@ -370,10 +367,7 @@ func TestBuildEntrypoint(t *testing.T) { // testing #1405 - config.Cmd does not get cleaned up if // utilizing cache func TestBuildEntrypointRunCleanup(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{ @@ -402,10 +396,7 @@ func TestBuildEntrypointRunCleanup(t *testing.T) { } func TestBuildImageWithCache(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{ @@ -433,10 +424,7 @@ func TestBuildImageWithCache(t *testing.T) { } func TestBuildImageWithoutCache(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{ @@ -464,10 +452,7 @@ func TestBuildImageWithoutCache(t *testing.T) { } func TestForbiddenContextPath(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{ @@ -513,10 +498,7 @@ func TestForbiddenContextPath(t *testing.T) { } func TestBuildADDFileNotFound(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{ diff --git a/commands.go b/commands.go index 7d33b81d0a..4a7c4b8d23 100644 --- a/commands.go +++ b/commands.go @@ -23,6 +23,7 @@ import ( "os/signal" "path/filepath" "reflect" + "regexp" "runtime" "sort" "strconv" @@ -41,9 +42,13 @@ 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) { +func (cli *DockerCli) getMethod(name string) (func(...string) error, bool) { methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:]) - return reflect.TypeOf(cli).MethodByName(methodName) + method := reflect.ValueOf(cli).MethodByName(methodName) + if !method.IsValid() { + return nil, false + } + return method.Interface().(func(...string) error), true } func ParseCommands(proto, addr string, args ...string) error { @@ -55,14 +60,7 @@ func ParseCommands(proto, addr string, args ...string) error { fmt.Println("Error: Command not found:", args[0]) return cli.CmdHelp(args[1:]...) } - ret := method.Func.CallSlice([]reflect.Value{ - reflect.ValueOf(cli), - reflect.ValueOf(args[1:]), - })[0].Interface() - if ret == nil { - return nil - } - return ret.(error) + return method(args[1:]...) } return cli.CmdHelp(args...) } @@ -73,10 +71,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error { if !exists { fmt.Fprintf(cli.err, "Error: Command not found: %s\n", args[0]) } else { - method.Func.CallSlice([]reflect.Value{ - reflect.ValueOf(cli), - reflect.ValueOf([]string{"--help"}), - })[0].Interface() + method("--help") return nil } } @@ -99,7 +94,6 @@ func (cli *DockerCli) CmdHelp(args ...string) error { {"login", "Register or Login to the docker registry server"}, {"logs", "Fetch the logs of a container"}, {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"}, - {"top", "Lookup the running processes of a container"}, {"ps", "List containers"}, {"pull", "Pull an image or a repository from the docker registry server"}, {"push", "Push an image or a repository to the docker registry server"}, @@ -111,6 +105,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error { {"start", "Start a stopped container"}, {"stop", "Stop a running container"}, {"tag", "Tag an image into a repository"}, + {"top", "Lookup the running processes of a container"}, {"version", "Show the docker version information"}, {"wait", "Block until a container stops, then print its exit code"}, } { @@ -545,8 +540,23 @@ func (cli *DockerCli) CmdRestart(args ...string) error { return nil } +func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal { + sigc := make(chan os.Signal, 1) + utils.CatchAll(sigc) + go func() { + for s := range sigc { + if _, _, err := cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%d", cid, s), nil); err != nil { + utils.Debugf("Error sending signal: %s", err) + } + } + }() + return sigc +} + func (cli *DockerCli) CmdStart(args ...string) error { cmd := Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container") + attach := cmd.Bool("a", false, "Attach container's stdout/stderr and forward all signals to the process") + openStdin := cmd.Bool("i", false, "Attach container's stdin") if err := cmd.Parse(args); err != nil { return nil } @@ -555,17 +565,75 @@ func (cli *DockerCli) CmdStart(args ...string) error { return nil } + var cErr chan error + if *attach || *openStdin { + if cmd.NArg() > 1 { + return fmt.Errorf("Impossible to start and attach multiple containers at once.") + } + + body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil) + if err != nil { + return err + } + + container := &Container{} + err = json.Unmarshal(body, container) + if err != nil { + return err + } + + if !container.Config.Tty { + sigc := cli.forwardAllSignals(cmd.Arg(0)) + defer utils.StopCatch(sigc) + } + + if container.Config.Tty && cli.isTerminal { + if err := cli.monitorTtySize(cmd.Arg(0)); err != nil { + return err + } + } + + var in io.ReadCloser + + v := url.Values{} + v.Set("stream", "1") + if *openStdin && container.Config.OpenStdin { + v.Set("stdin", "1") + in = cli.in + } + v.Set("stdout", "1") + v.Set("stderr", "1") + + cErr = utils.Go(func() error { + return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, in, cli.out, cli.err, nil) + }) + } + var encounteredError error - for _, name := range args { + for _, name := range cmd.Args() { _, _, err := cli.call("POST", "/containers/"+name+"/start", nil) if err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to start one or more containers") + if !*attach || !*openStdin { + fmt.Fprintf(cli.err, "%s\n", err) + encounteredError = fmt.Errorf("Error: failed to start one or more containers") + } } else { - fmt.Fprintf(cli.out, "%s\n", name) + if !*attach || !*openStdin { + fmt.Fprintf(cli.out, "%s\n", name) + } } } - return encounteredError + if encounteredError != nil { + if *openStdin || *attach { + cli.in.Close() + <-cErr + } + return encounteredError + } + if *openStdin || *attach { + return <-cErr + } + return nil } func (cli *DockerCli) CmdInspect(args ...string) error { @@ -577,30 +645,39 @@ func (cli *DockerCli) CmdInspect(args ...string) error { cmd.Usage() return nil } - fmt.Fprintf(cli.out, "[") - for i, name := range args { - if i > 0 { - fmt.Fprintf(cli.out, ",") - } + + indented := new(bytes.Buffer) + status := 0 + + for _, name := range args { obj, _, err := cli.call("GET", "/containers/"+name+"/json", nil) if err != nil { obj, _, err = cli.call("GET", "/images/"+name+"/json", nil) if err != nil { fmt.Fprintf(cli.err, "No such image or container: %s\n", name) + status = 1 continue } } - indented := new(bytes.Buffer) if err = json.Indent(indented, obj, "", " "); err != nil { fmt.Fprintf(cli.err, "%s\n", err) + status = 1 continue } - if _, err := io.Copy(cli.out, indented); err != nil { - fmt.Fprintf(cli.err, "%s\n", err) - } + indented.WriteString(",") + } + // Remove trailling ',' + indented.Truncate(indented.Len() - 1) + + fmt.Fprintf(cli.out, "[") + if _, err := io.Copy(cli.out, indented); err != nil { + return err } fmt.Fprintf(cli.out, "]") + if status != 0 { + return &utils.StatusError{Status: status} + } return nil } @@ -647,11 +724,11 @@ func (cli *DockerCli) CmdPort(args ...string) error { } port := cmd.Arg(1) - proto := "Tcp" + proto := "tcp" parts := strings.SplitN(port, "/", 2) if len(parts) == 2 && len(parts[1]) != 0 { port = parts[0] - proto = strings.ToUpper(parts[1][:1]) + strings.ToLower(parts[1][1:]) + proto = parts[1] } body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil) if err != nil { @@ -663,8 +740,14 @@ func (cli *DockerCli) CmdPort(args ...string) error { return err } - if frontend, exists := out.NetworkSettings.PortMapping[proto][port]; exists { - fmt.Fprintf(cli.out, "%s\n", frontend) + if frontends, exists := out.NetworkSettings.Ports[Port(port+"/"+proto)]; exists { + if frontends == nil { + fmt.Fprintf(cli.out, "%s\n", port) + } else { + for _, frontend := range frontends { + fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort) + } + } } else { return fmt.Errorf("Error: No private port '%s' allocated on %s", cmd.Arg(1), cmd.Arg(0)) } @@ -740,6 +823,8 @@ func (cli *DockerCli) CmdHistory(args ...string) error { func (cli *DockerCli) CmdRm(args ...string) error { cmd := Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers") v := cmd.Bool("v", false, "Remove the volumes associated to the container") + link := cmd.Bool("link", false, "Remove the specified link and not the underlying container") + if err := cmd.Parse(args); err != nil { return nil } @@ -751,6 +836,9 @@ func (cli *DockerCli) CmdRm(args ...string) error { if *v { val.Set("v", "1") } + if *link { + val.Set("link", "1") + } for _, name := range cmd.Args() { _, _, err := cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil) if err != nil { @@ -985,25 +1073,19 @@ func (cli *DockerCli) CmdImages(args ...string) error { out.Tag = "" } + if !*noTrunc { + out.ID = utils.TruncateID(out.ID) + } + if !*quiet { - fmt.Fprintf(w, "%s\t%s\t", out.Repository, out.Tag) - if *noTrunc { - fmt.Fprintf(w, "%s\t", out.ID) - } else { - fmt.Fprintf(w, "%s\t", utils.TruncateID(out.ID)) - } - fmt.Fprintf(w, "%s ago\t", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) + fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t", out.Repository, out.Tag, out.ID, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) if out.VirtualSize > 0 { fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.Size), utils.HumanSize(out.VirtualSize)) } else { fmt.Fprintf(w, "%s\n", utils.HumanSize(out.Size)) } } else { - if *noTrunc { - fmt.Fprintln(w, out.ID) - } else { - fmt.Fprintln(w, utils.TruncateID(out.ID)) - } + fmt.Fprintln(w, out.ID) } } @@ -1017,10 +1099,10 @@ func (cli *DockerCli) CmdImages(args ...string) error { func displayablePorts(ports []APIPort) string { result := []string{} for _, port := range ports { - if port.Type == "tcp" { - result = append(result, fmt.Sprintf("%d->%d", port.PublicPort, port.PrivatePort)) + if port.IP == "" { + result = append(result, fmt.Sprintf("%d/%s", port.PublicPort, port.Type)) } else { - result = append(result, fmt.Sprintf("%d->%d/%s", port.PublicPort, port.PrivatePort, port.Type)) + result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type)) } } sort.Strings(result) @@ -1073,7 +1155,7 @@ func (cli *DockerCli) CmdPs(args ...string) error { } w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if !*quiet { - fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS") + fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES") if *size { fmt.Fprintln(w, "\tSIZE") } else { @@ -1082,12 +1164,20 @@ func (cli *DockerCli) CmdPs(args ...string) error { } for _, out := range outs { + if !*noTrunc { + out.ID = utils.TruncateID(out.ID) + } + + // Remove the leading / from the names + for i := 0; i < len(out.Names); i++ { + out.Names[i] = out.Names[i][1:] + } + if !*quiet { - if *noTrunc { - fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports)) - } else { - fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports)) + if !*noTrunc { + out.Command = utils.Trunc(out.Command, 20) } + fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports), strings.Join(out.Names, ",")) if *size { if out.SizeRootFs > 0 { fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.SizeRw), utils.HumanSize(out.SizeRootFs)) @@ -1098,11 +1188,7 @@ func (cli *DockerCli) CmdPs(args ...string) error { fmt.Fprint(w, "\n") } } else { - if *noTrunc { - fmt.Fprintln(w, out.ID) - } else { - fmt.Fprintln(w, utils.TruncateID(out.ID)) - } + fmt.Fprintln(w, out.ID) } } @@ -1229,15 +1315,18 @@ func (cli *DockerCli) CmdLogs(args ...string) error { cmd.Usage() return nil } + name := cmd.Arg(0) - if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out, cli.err); err != nil { + if err := cli.hijack("POST", "/containers/"+name+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out, cli.err, nil); err != nil { return err } return nil } func (cli *DockerCli) CmdAttach(args ...string) error { - cmd := Subcmd("attach", "CONTAINER", "Attach to a running container") + cmd := Subcmd("attach", "[OPTIONS] CONTAINER", "Attach to a running container") + noStdin := cmd.Bool("nostdin", false, "Do not attach stdin") + proxy := cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)") if err := cmd.Parse(args); err != nil { return nil } @@ -1245,8 +1334,8 @@ func (cli *DockerCli) CmdAttach(args ...string) error { cmd.Usage() return nil } - - body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil) + name := cmd.Arg(0) + body, _, err := cli.call("GET", "/containers/"+name+"/json", nil) if err != nil { return err } @@ -1261,19 +1350,29 @@ func (cli *DockerCli) CmdAttach(args ...string) error { return fmt.Errorf("Impossible to attach to a stopped container, start it first") } - if container.Config.Tty { + if container.Config.Tty && cli.isTerminal { if err := cli.monitorTtySize(cmd.Arg(0)); err != nil { - utils.Debugf("Error monitoring tty size: %s", err) + utils.Debugf("Error monitoring TTY size: %s", err) } } + var in io.ReadCloser + v := url.Values{} v.Set("stream", "1") - v.Set("stdin", "1") + if !*noStdin && container.Config.OpenStdin { + v.Set("stdin", "1") + in = cli.in + } v.Set("stdout", "1") v.Set("stderr", "1") - if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, cli.in, cli.out, cli.err); err != nil { + if *proxy && !container.Config.Tty { + sigc := cli.forwardAllSignals(cmd.Arg(0)) + defer utils.StopCatch(sigc) + } + + if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, in, cli.out, cli.err, nil); err != nil { return err } return nil @@ -1326,18 +1425,6 @@ func (cli *DockerCli) CmdSearch(args ...string) error { // Ports type - Used to parse multiple -p flags type ports []int -// ListOpts type -type ListOpts []string - -func (opts *ListOpts) String() string { - return fmt.Sprint(*opts) -} - -func (opts *ListOpts) Set(value string) error { - *opts = append(*opts, value) - return nil -} - // AttachOpts stores arguments to 'docker run -a', eg. which streams to attach to type AttachOpts map[string]bool @@ -1436,6 +1523,13 @@ func (cli *DockerCli) CmdRun(args ...string) error { flRm := cmd.Lookup("rm") autoRemove, _ := strconv.ParseBool(flRm.Value.String()) + flSigProxy := cmd.Lookup("sig-proxy") + sigProxy, _ := strconv.ParseBool(flSigProxy.Value.String()) + flName := cmd.Lookup("name") + if config.Tty { + sigProxy = false + } + var containerIDFile *os.File if len(hostConfig.ContainerIDFile) > 0 { if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil { @@ -1447,9 +1541,14 @@ func (cli *DockerCli) CmdRun(args ...string) error { } defer containerIDFile.Close() } + containerValues := url.Values{} + name := flName.Value.String() + if name != "" { + containerValues.Set("name", name) + } //create the container - body, statusCode, err := cli.call("POST", "/containers/create", config) + body, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), config) //if image not found try to pull it if statusCode == 404 { _, tag := utils.ParseRepositoryTag(config.Image) @@ -1490,7 +1589,7 @@ func (cli *DockerCli) CmdRun(args ...string) error { if err != nil { return err } - body, _, err = cli.call("POST", "/containers/create", config) + body, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config) if err != nil { return err } @@ -1514,12 +1613,15 @@ func (cli *DockerCli) CmdRun(args ...string) error { } } - //start the container - if _, _, err = cli.call("POST", "/containers/"+runResult.ID+"/start", hostConfig); err != nil { - return err + if sigProxy { + sigc := cli.forwardAllSignals(runResult.ID) + defer utils.StopCatch(sigc) } - var wait chan struct{} + var ( + wait chan struct{} + errCh chan error + ) if !config.AttachStdout && !config.AttachStderr { // Make this asynchrone in order to let the client write to stdin before having to read the ID @@ -1530,20 +1632,18 @@ func (cli *DockerCli) CmdRun(args ...string) error { }() } + hijacked := make(chan bool) + if config.AttachStdin || config.AttachStdout || config.AttachStderr { - if config.Tty { - if err := cli.monitorTtySize(runResult.ID); err != nil { - utils.Errorf("Error monitoring TTY size: %s\n", err) - } - } v := url.Values{} - v.Set("logs", "1") v.Set("stream", "1") var out, stderr io.Writer + var in io.ReadCloser if config.AttachStdin { v.Set("stdin", "1") + in = cli.in } if config.AttachStdout { v.Set("stdout", "1") @@ -1558,18 +1658,36 @@ func (cli *DockerCli) CmdRun(args ...string) error { } } - signals := make(chan os.Signal, 1) - signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) - go func() { - for sig := range signals { - fmt.Printf("\nReceived signal: %s; cleaning up\n", sig) - if err := cli.CmdStop("-t", "4", runResult.ID); err != nil { - fmt.Printf("failed to stop container: %v", err) - } - } - }() + errCh = utils.Go(func() error { + return cli.hijack("POST", "/containers/"+runResult.ID+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked) + }) + } else { + close(hijacked) + } - if err := cli.hijack("POST", "/containers/"+runResult.ID+"/attach?"+v.Encode(), config.Tty, cli.in, out, stderr); err != nil { + // Acknowledge the hijack before starting + select { + case <-hijacked: + case err := <-errCh: + if err != nil { + utils.Debugf("Error hijack: %s", err) + return err + } + } + + //start the container + if _, _, err = cli.call("POST", "/containers/"+runResult.ID+"/start", hostConfig); err != nil { + return err + } + + if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminal { + if err := cli.monitorTtySize(runResult.ID); err != nil { + utils.Errorf("Error monitoring TTY size: %s\n", err) + } + } + + if errCh != nil { + if err := <-errCh; err != nil { utils.Debugf("Error hijack: %s", err) return err } @@ -1579,13 +1697,19 @@ func (cli *DockerCli) CmdRun(args ...string) error { // Detached mode <-wait } else { - status, err := getExitCode(cli, runResult.ID) + running, status, err := getExitCode(cli, runResult.ID) if err != nil { return err } if autoRemove { - _, _, err = cli.call("DELETE", "/containers/"+runResult.ID, nil) - if err != nil { + if running { + return fmt.Errorf("Impossible to auto-remove a detached container") + } + // Wait for the process to + if _, _, err := cli.call("POST", "/containers/"+runResult.ID+"/wait", nil); err != nil { + return err + } + if _, _, err := cli.call("DELETE", "/containers/"+runResult.ID, nil); err != nil { return err } } @@ -1642,6 +1766,10 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, params = bytes.NewBuffer(buf) } + // fixme: refactor client to support redirect + re := regexp.MustCompile("/+") + path = re.ReplaceAllString(path, "/") + req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), params) if err != nil { return nil, -1, err @@ -1670,6 +1798,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, return nil, -1, err } defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, -1, err @@ -1687,6 +1816,11 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h if (method == "POST" || method == "PUT") && in == nil { in = bytes.NewReader([]byte{}) } + + // fixme: refactor client to support redirect + re := regexp.MustCompile("/+") + path = re.ReplaceAllString(path, "/") + req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), in) if err != nil { return err @@ -1742,7 +1876,10 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h return nil } -func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer) error { +func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan bool) error { + // fixme: refactor client to support redirect + re := regexp.MustCompile("/+") + path = re.ReplaceAllString(path, "/") req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil) if err != nil { @@ -1768,6 +1905,10 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea rwc, br := clientconn.Hijack() defer rwc.Close() + if started != nil { + started <- true + } + var receiveStdout chan error if stdout != nil { @@ -1854,9 +1995,6 @@ func (cli *DockerCli) resizeTty(id string) { } func (cli *DockerCli) monitorTtySize(id string) error { - if !cli.isTerminal { - return fmt.Errorf("Impossible to monitor size on non-tty") - } cli.resizeTty(id) sigchan := make(chan os.Signal, 1) @@ -1904,20 +2042,22 @@ func waitForExit(cli *DockerCli, containerId string) (int, error) { return out.StatusCode, nil } -func getExitCode(cli *DockerCli, containerId string) (int, error) { +// getExitCode perform an inspect on the container. It returns +// the running state and the exit code. +func getExitCode(cli *DockerCli, containerId string) (bool, 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 false, -1, err } - return -1, nil + return false, -1, nil } c := &Container{} if err := json.Unmarshal(body, c); err != nil { - return -1, err + return false, -1, err } - return c.State.ExitCode, nil + return c.State.Running, c.State.ExitCode, nil } func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli { diff --git a/commands_test.go b/commands_test.go index aceb7a6577..67eae04b19 100644 --- a/commands_test.go +++ b/commands_test.go @@ -84,10 +84,24 @@ func TestRunHostname(t *testing.T) { } }) - setTimeout(t, "CmdRun timed out", 5*time.Second, func() { + container := globalRuntime.List()[0] + + setTimeout(t, "CmdRun timed out", 10*time.Second, func() { <-c + + go func() { + cli.CmdWait(container.ID) + }() + + if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil { + t.Fatal(err) + } }) + // Cleanup pipes + if err := closeWrap(stdout, stdoutPipe); err != nil { + t.Fatal(err) + } } // TestRunWorkdir checks that 'docker run -w' correctly sets a custom working directory @@ -115,10 +129,24 @@ func TestRunWorkdir(t *testing.T) { } }) - setTimeout(t, "CmdRun timed out", 5*time.Second, func() { + container := globalRuntime.List()[0] + + setTimeout(t, "CmdRun timed out", 10*time.Second, func() { <-c + + go func() { + cli.CmdWait(container.ID) + }() + + if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil { + t.Fatal(err) + } }) + // Cleanup pipes + if err := closeWrap(stdout, stdoutPipe); err != nil { + t.Fatal(err) + } } // TestRunWorkdirExists checks that 'docker run -w' correctly sets a custom working directory, even if it exists @@ -146,10 +174,24 @@ func TestRunWorkdirExists(t *testing.T) { } }) + container := globalRuntime.List()[0] + setTimeout(t, "CmdRun timed out", 5*time.Second, func() { <-c + + go func() { + cli.CmdWait(container.ID) + }() + + if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil { + t.Fatal(err) + } }) + // Cleanup pipes + if err := closeWrap(stdout, stdoutPipe); err != nil { + t.Fatal(err) + } } func TestRunExit(t *testing.T) { @@ -262,7 +304,7 @@ func TestRunDisconnectTty(t *testing.T) { // We're simulating a disconnect so the return value doesn't matter. What matters is the // fact that CmdRun returns. if err := cli.CmdRun("-i", "-t", unitTestImageID, "/bin/cat"); err != nil { - utils.Debugf("Error CmdRun: %s\n", err) + utils.Debugf("Error CmdRun: %s", err) } close(c1) @@ -393,12 +435,14 @@ func TestRunDetach(t *testing.T) { container := globalRuntime.List()[0] setTimeout(t, "Escape sequence timeout", 5*time.Second, func() { - stdinPipe.Write([]byte{'', ''}) + stdinPipe.Write([]byte{16, 17}) if err := stdinPipe.Close(); err != nil { t.Fatal(err) } }) + closeWrap(stdin, stdinPipe, stdout, stdoutPipe) + // wait for CmdRun to return setTimeout(t, "Waiting for CmdRun timed out", 15*time.Second, func() { <-ch @@ -411,7 +455,6 @@ func TestRunDetach(t *testing.T) { setTimeout(t, "Waiting for container to die timed out", 20*time.Second, func() { container.Kill() - container.Wait() }) } @@ -423,39 +466,62 @@ func TestAttachDetach(t *testing.T) { 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() { + ch := make(chan struct{}) + go func() { + defer close(ch) if err := cli.CmdRun("-i", "-t", "-d", unitTestImageID, "cat"); err != nil { t.Fatal(err) } - }) + }() - container := globalRuntime.List()[0] + var container *Container + + setTimeout(t, "Reading container's id timed out", 10*time.Second, func() { + buf := make([]byte, 1024) + n, err := stdout.Read(buf) + if err != nil { + t.Fatal(err) + } + + container = globalRuntime.List()[0] + + if strings.Trim(string(buf[:n]), " \r\n") != container.ShortID() { + t.Fatalf("Wrong ID received. Expect %s, received %s", container.ShortID(), buf[:n]) + } + }) + setTimeout(t, "Starting container timed out", 10*time.Second, func() { + <-ch + }) stdin, stdinPipe = io.Pipe() stdout, stdoutPipe = io.Pipe() cli = NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr) - ch := make(chan struct{}) + ch = make(chan struct{}) go func() { defer close(ch) if err := cli.CmdAttach(container.ShortID()); err != nil { - t.Fatal(err) + if err != io.ErrClosedPipe { + 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) + if err != io.ErrClosedPipe { + t.Fatal(err) + } } }) setTimeout(t, "Escape sequence timeout", 5*time.Second, func() { - stdinPipe.Write([]byte{'', ''}) + stdinPipe.Write([]byte{16, 17}) if err := stdinPipe.Close(); err != nil { t.Fatal(err) } }) + closeWrap(stdin, stdinPipe, stdout, stdoutPipe) // wait for CmdRun to return setTimeout(t, "Waiting for CmdAttach timed out", 15*time.Second, func() { @@ -469,7 +535,6 @@ func TestAttachDetach(t *testing.T) { setTimeout(t, "Waiting for container to die timedout", 5*time.Second, func() { container.Kill() - container.Wait() }) } @@ -484,7 +549,7 @@ func TestAttachDisconnect(t *testing.T) { go func() { // Start a process in daemon mode if err := cli.CmdRun("-d", "-i", unitTestImageID, "/bin/cat"); err != nil { - utils.Debugf("Error CmdRun: %s\n", err) + utils.Debugf("Error CmdRun: %s", err) } }() @@ -570,7 +635,7 @@ func TestRunAutoRemove(t *testing.T) { } }) - setTimeout(t, "CmdRun timed out", 5*time.Second, func() { + setTimeout(t, "CmdRun timed out", 10*time.Second, func() { <-c }) diff --git a/config.go b/config.go new file mode 100644 index 0000000000..8f2d22c255 --- /dev/null +++ b/config.go @@ -0,0 +1,18 @@ +package docker + +import ( + "net" +) + +type DaemonConfig struct { + Pidfile string + GraphPath string + ProtoAddresses []string + AutoRestart bool + EnableCors bool + Dns []string + EnableIptables bool + BridgeIface string + DefaultIp net.IP + InterContainerCommunication bool +} diff --git a/container.go b/container.go index 7d85aef0a1..43792d5f00 100644 --- a/container.go +++ b/container.go @@ -1,6 +1,7 @@ package docker import ( + "bytes" "encoding/json" "errors" "flag" @@ -43,6 +44,7 @@ type Container struct { ResolvConfPath string HostnamePath string HostsPath string + Name string cmd *exec.Cmd stdout *utils.WriteBroadcaster @@ -58,6 +60,8 @@ type Container struct { // Store rw/ro in a separate structure to preserve reverse-compatibility on-disk. // Easier than migrating older container configs :) VolumesRW map[string]bool + + activeLinks map[string]*Link } type Config struct { @@ -70,7 +74,8 @@ type Config struct { AttachStdin bool AttachStdout bool AttachStderr bool - PortSpecs []string + PortSpecs []string // Deprecated - Can be in the format of 8080/tcp + ExposedPorts map[Port]struct{} Tty bool // Attach standard streams to a tty, including stdin if it is not closed. OpenStdin bool // Open stdin StdinOnce bool // If true, close stdin after the 1 attached client disconnects. @@ -90,6 +95,8 @@ type HostConfig struct { Binds []string ContainerIDFile string LxcConf []KeyValuePair + PortBindings map[Port][]PortBinding + Links []string } type BindMap struct { @@ -99,7 +106,11 @@ type BindMap struct { } var ( - ErrInvaidWorikingDirectory = errors.New("The working directory is invalid. It needs to be an absolute path.") + ErrContainerStart = errors.New("The container failed to start. Unkown error") + ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.") + ErrInvalidWorikingDirectory = errors.New("The working directory is invalid. It needs to be an absolute path.") + ErrConflictAttachDetach = errors.New("Conflicting options: -a and -d") + ErrConflictDetachAutoRemove = errors.New("Conflicting options: -rm and -d") ) type KeyValuePair struct { @@ -107,6 +118,34 @@ type KeyValuePair struct { Value string } +type PortBinding struct { + HostIp string + HostPort string +} + +// 80/tcp +type Port string + +func (p Port) Proto() string { + return strings.Split(string(p), "/")[1] +} + +func (p Port) Port() string { + return strings.Split(string(p), "/")[0] +} + +func (p Port) Int() int { + i, err := parsePort(p.Port()) + if err != nil { + panic(err) + } + return i +} + +func NewPort(proto, port string) Port { + return Port(fmt.Sprintf("%s/%s", port, proto)) +} + func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) { cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container") if os.Getenv("TEST") != "" { @@ -127,6 +166,8 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, flNetwork := cmd.Bool("n", true, "Enable networking for this container") flPrivileged := cmd.Bool("privileged", false, "Give extended privileges to this container") flAutoRemove := cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)") + cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)") + cmd.String("name", "", "Assign a name to the container") if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit { //fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n") @@ -135,35 +176,45 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, flCpuShares := cmd.Int64("c", 0, "CPU shares (relative weight)") - var flPorts ListOpts - cmd.Var(&flPorts, "p", "Expose a container's port to the host (use 'docker port' to see the actual mapping)") + var flPublish utils.ListOpts + cmd.Var(&flPublish, "p", "Publish a container's port to the host (use 'docker port' to see the actual mapping)") - var flEnv ListOpts + var flExpose utils.ListOpts + cmd.Var(&flExpose, "expose", "Expose a port from the container without publishing it to your host") + + var flEnv utils.ListOpts cmd.Var(&flEnv, "e", "Set environment variables") - var flDns ListOpts + var flDns utils.ListOpts cmd.Var(&flDns, "dns", "Set custom dns servers") flVolumes := NewPathOpts() cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)") - var flVolumesFrom ListOpts + var flVolumesFrom utils.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 + var flLxcOpts utils.ListOpts cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"") + var flLinks utils.ListOpts + cmd.Var(&flLinks, "link", "Add link to another container (name:alias)") + if err := cmd.Parse(args); err != nil { return nil, nil, cmd, err } if *flDetach && len(flAttach) > 0 { - return nil, nil, cmd, fmt.Errorf("Conflicting options: -a and -d") + return nil, nil, cmd, ErrConflictAttachDetach } if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) { - return nil, nil, cmd, ErrInvaidWorikingDirectory + return nil, nil, cmd, ErrInvalidWorikingDirectory } + if *flDetach && *flAutoRemove { + return nil, nil, cmd, ErrConflictDetachAutoRemove + } + // If neither -d or -a are set, attach to everything by default if len(flAttach) == 0 && !*flDetach { if !*flDetach { @@ -175,10 +226,6 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, } } - if *flDetach && *flAutoRemove { - return nil, nil, cmd, fmt.Errorf("Conflicting options: -rm and -d") - } - var binds []string // add any bind targets to the list of container volumes @@ -220,10 +267,28 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, hostname = parts[0] domainname = parts[1] } + + ports, portBindings, err := parsePortSpecs(flPublish) + if err != nil { + return nil, nil, cmd, err + } + + // Merge in exposed ports to the map of published ports + for _, e := range flExpose { + if strings.Contains(e, ":") { + return nil, nil, cmd, fmt.Errorf("Invalid port format for -expose: %s", e) + } + p := NewPort(splitProtoPort(e)) + if _, exists := ports[p]; !exists { + ports[p] = struct{}{} + } + } + config := &Config{ - Hostname: hostname, + Hostname: *flHostname, Domainname: domainname, - PortSpecs: flPorts, + PortSpecs: nil, // Deprecated + ExposedPorts: ports, User: *flUser, Tty: *flTty, NetworkDisabled: !*flNetwork, @@ -243,10 +308,13 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, Privileged: *flPrivileged, WorkingDir: *flWorkingDir, } + hostConfig := &HostConfig{ Binds: binds, ContainerIDFile: *flContainerIDFile, LxcConf: lxcConf, + PortBindings: portBindings, + Links: flLinks, } if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit { @@ -261,36 +329,38 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, return config, hostConfig, cmd, nil } -type PortMapping map[string]string +type PortMapping map[string]string // Deprecated type NetworkSettings struct { IPAddress string IPPrefixLen int Gateway string Bridge string - PortMapping map[string]PortMapping + PortMapping map[string]PortMapping // Deprecated + Ports map[Port][]PortBinding } -// returns a more easy to process description of the port mapping defined in the settings func (settings *NetworkSettings) PortMappingAPI() []APIPort { var mapping []APIPort - for private, public := range settings.PortMapping["Tcp"] { - pubint, _ := strconv.ParseInt(public, 0, 0) - privint, _ := strconv.ParseInt(private, 0, 0) - mapping = append(mapping, APIPort{ - PrivatePort: privint, - PublicPort: pubint, - Type: "tcp", - }) - } - for private, public := range settings.PortMapping["Udp"] { - pubint, _ := strconv.ParseInt(public, 0, 0) - privint, _ := strconv.ParseInt(private, 0, 0) - mapping = append(mapping, APIPort{ - PrivatePort: privint, - PublicPort: pubint, - Type: "udp", - }) + for port, bindings := range settings.Ports { + p, _ := parsePort(port.Port()) + if len(bindings) == 0 { + mapping = append(mapping, APIPort{ + PublicPort: int64(p), + Type: port.Proto(), + }) + continue + } + for _, binding := range bindings { + p, _ := parsePort(port.Port()) + h, _ := parsePort(binding.HostPort) + mapping = append(mapping, APIPort{ + PrivatePort: int64(p), + PublicPort: int64(h), + Type: port.Proto(), + IP: binding.HostIp, + }) + } } return mapping } @@ -460,11 +530,13 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s } else { _, err = io.Copy(cStdin, stdin) } + if err == io.ErrClosedPipe { + err = nil + } if err != nil { utils.Errorf("attach: stdin: %s", err) } - // Discard error, expecting pipe error - errors <- nil + errors <- err }() } } @@ -590,7 +662,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) { if container.runtime.networkManager.disabled { container.Config.NetworkDisabled = true } else { - if err := container.allocateNetwork(); err != nil { + if err := container.allocateNetwork(hostConfig); err != nil { return err } } @@ -780,6 +852,46 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) { "-e", "container=lxc", "-e", "HOSTNAME="+container.Config.Hostname, ) + + // Init any links between the parent and children + runtime := container.runtime + + children, err := runtime.Children(container.Name) + if err != nil { + return err + } + + if len(children) > 0 { + container.activeLinks = make(map[string]*Link, len(children)) + + // If we encounter an error make sure that we rollback any network + // config and ip table changes + rollback := func() { + for _, link := range container.activeLinks { + link.Disable() + } + container.activeLinks = nil + } + + for p, child := range children { + link, err := NewLink(container, child, p, runtime.networkManager.bridgeIface) + if err != nil { + rollback() + return err + } + + container.activeLinks[link.Alias()] = link + if err := link.Enable(); err != nil { + rollback() + return err + } + + for _, envVar := range link.ToEnv() { + params = append(params, "-e", envVar) + } + } + } + if container.Config.WorkingDir != "" { workingDir := path.Clean(container.Config.WorkingDir) utils.Debugf("[working dir] working dir is %s", workingDir) @@ -831,12 +943,43 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) { container.ToDisk() container.SaveHostConfig(hostConfig) go container.monitor(hostConfig) - return nil + + defer utils.Debugf("Container running: %v", container.State.Running) + // We wait for the container to be fully running. + // Timeout after 5 seconds. In case of broken pipe, just retry. + // Note: The container can run and finish correctly before + // the end of this loop + for now := time.Now(); time.Since(now) < 5*time.Second; { + // If the container dies while waiting for it, just return + if !container.State.Running { + return nil + } + output, err := exec.Command("lxc-info", "-s", "-n", container.ID).CombinedOutput() + if err != nil { + utils.Debugf("Error with lxc-info: %s (%s)", err, output) + + output, err = exec.Command("lxc-info", "-s", "-n", container.ID).CombinedOutput() + if err != nil { + utils.Debugf("Second Error with lxc-info: %s (%s)", err, output) + return err + } + + } + if strings.Contains(string(output), "RUNNING") { + return nil + } + utils.Debugf("Waiting for the container to start (running: %v): %s", container.State.Running, bytes.TrimSpace(output)) + time.Sleep(50 * time.Millisecond) + } + + if container.State.Running { + return ErrContainerStartTimeout + } + return ErrContainerStart } func (container *Container) Run() error { - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(&HostConfig{}); err != nil { return err } container.Wait() @@ -882,7 +1025,7 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) { return utils.NewBufReader(reader), nil } -func (container *Container) allocateNetwork() error { +func (container *Container) allocateNetwork(hostConfig *HostConfig) error { if container.Config.NetworkDisabled { return nil } @@ -909,36 +1052,59 @@ func (container *Container) allocateNetwork() error { } } - var portSpecs []string - if !container.State.Ghost { - portSpecs = container.Config.PortSpecs - } else { - for backend, frontend := range container.NetworkSettings.PortMapping["Tcp"] { - portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/tcp", frontend, backend)) + if container.Config.PortSpecs != nil { + utils.Debugf("Migrating port mappings for container: %s", strings.Join(container.Config.PortSpecs, ", ")) + if err := migratePortMappings(container.Config); err != nil { + return err } - for backend, frontend := range container.NetworkSettings.PortMapping["Udp"] { - portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/udp", frontend, backend)) + container.Config.PortSpecs = nil + } + + portSpecs := make(map[Port]struct{}) + bindings := make(map[Port][]PortBinding) + + if !container.State.Ghost { + if container.Config.ExposedPorts != nil { + portSpecs = container.Config.ExposedPorts + } + if hostConfig.PortBindings != nil { + bindings = hostConfig.PortBindings + } + } else { + if container.NetworkSettings.Ports != nil { + for port, binding := range container.NetworkSettings.Ports { + portSpecs[port] = struct{}{} + bindings[port] = binding + } } } - container.NetworkSettings.PortMapping = make(map[string]PortMapping) - container.NetworkSettings.PortMapping["Tcp"] = make(PortMapping) - container.NetworkSettings.PortMapping["Udp"] = make(PortMapping) - for _, spec := range portSpecs { - nat, err := iface.AllocatePort(spec) - if err != nil { - iface.Release() - return err + container.NetworkSettings.PortMapping = nil + + for port := range portSpecs { + binding := bindings[port] + for i := 0; i < len(binding); i++ { + b := binding[i] + nat, err := iface.AllocatePort(port, b) + if err != nil { + iface.Release() + return err + } + utils.Debugf("Allocate port: %s:%s->%s", nat.Binding.HostIp, port, nat.Binding.HostPort) + binding[i] = nat.Binding } - proto := strings.Title(nat.Proto) - backend, frontend := strconv.Itoa(nat.Backend), strconv.Itoa(nat.Frontend) - container.NetworkSettings.PortMapping[proto][backend] = frontend + bindings[port] = binding } + container.SaveHostConfig(hostConfig) + + container.NetworkSettings.Ports = bindings container.network = iface + container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface container.NetworkSettings.IPAddress = iface.IPNet.IP.String() container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size() container.NetworkSettings.Gateway = iface.Gateway.String() + return nil } @@ -951,7 +1117,7 @@ func (container *Container) releaseNetwork() { container.NetworkSettings = &NetworkSettings{} } -// FIXME: replace this with a control socket within docker-init +// FIXME: replace this with a control socket within dockerinit func (container *Container) waitLxc() error { for { output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput() @@ -1021,6 +1187,14 @@ func (container *Container) monitor(hostConfig *HostConfig) { func (container *Container) cleanup() { container.releaseNetwork() + + // Disable all active links + if container.activeLinks != nil { + for _, link := range container.activeLinks { + link.Disable() + } + } + if container.Config.OpenStdin { if err := container.stdin.Close(); err != nil { utils.Errorf("%s: Error close stdin: %s", container.ID, err) @@ -1044,54 +1218,57 @@ func (container *Container) cleanup() { } } -func (container *Container) kill() error { +func (container *Container) kill(sig int) error { + container.State.Lock() + defer container.State.Unlock() + if !container.State.Running { return nil } - // Sending SIGKILL to the process via lxc - output, err := exec.Command("lxc-kill", "-n", container.ID, "9").CombinedOutput() - if err != nil { - log.Printf("error killing container %s (%s, %s)", container.ID, output, err) + if output, err := exec.Command("lxc-kill", "-n", container.ID, strconv.Itoa(sig)).CombinedOutput(); err != nil { + log.Printf("error killing container %s (%s, %s)", container.ShortID(), output, err) + return err + } + + return nil +} + +func (container *Container) Kill() error { + if !container.State.Running { + return nil + } + + // 1. Send SIGKILL + if err := container.kill(9); err != nil { + return err } // 2. Wait for the process to die, in last resort, try to kill the process directly if err := container.WaitTimeout(10 * time.Second); err != nil { if container.cmd == nil { - return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.ID) + return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.ShortID()) } - log.Printf("Container %s failed to exit within 10 seconds of lxc SIGKILL - trying direct SIGKILL", container.ID) + log.Printf("Container %s failed to exit within 10 seconds of lxc-kill %s - trying direct SIGKILL", "SIGKILL", container.ShortID()) if err := container.cmd.Process.Kill(); err != nil { return err } } - // Wait for the container to be actually stopped container.Wait() return nil } -func (container *Container) Kill() error { - container.State.Lock() - defer container.State.Unlock() - if !container.State.Running { - return nil - } - return container.kill() -} - func (container *Container) Stop(seconds int) error { - container.State.Lock() - defer container.State.Unlock() if !container.State.Running { return nil } // 1. Send a SIGTERM - if output, err := exec.Command("lxc-kill", "-n", container.ID, "15").CombinedOutput(); err != nil { - log.Print(string(output)) + if err := container.kill(15); err != nil { + utils.Debugf("Error sending kill SIGTERM: %s", err) log.Print("Failed to send SIGTERM to the process, force killing") - if err := container.kill(); err != nil { + if err := container.kill(9); err != nil { return err } } @@ -1099,7 +1276,8 @@ func (container *Container) Stop(seconds int) error { // 2. Wait for the process to exit on its own if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil { log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds) - if err := container.kill(); err != nil { + // 3. If it doesn't, then send SIGKILL + if err := container.Kill(); err != nil { return err } } @@ -1202,6 +1380,12 @@ func (container *Container) Mounted() (bool, error) { } func (container *Container) Unmount() error { + if _, err := os.Stat(container.RootfsPath()); err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } return Unmount(container.RootfsPath()) } @@ -1292,3 +1476,9 @@ func (container *Container) Copy(resource string) (Archive, error) { } return TarFilter(basePath, Uncompressed, filter) } + +// Returns true if the container exposes a certain port +func (container *Container) Exposes(p Port) bool { + _, exists := container.Config.ExposedPorts[p] + return exists +} diff --git a/container_test.go b/container_test.go index e678c98898..262d6fcb1d 100644 --- a/container_test.go +++ b/container_test.go @@ -18,11 +18,12 @@ import ( func TestIDFormat(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container1, err := runtime.Create( + container1, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/sh", "-c", "echo hello world"}, }, + "", ) if err != nil { t.Fatal(err) @@ -388,11 +389,12 @@ func TestRun(t *testing.T) { func TestOutput(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foobar"}, }, + "", ) if err != nil { t.Fatal(err) @@ -407,16 +409,39 @@ func TestOutput(t *testing.T) { } } +func TestContainerNetwork(t *testing.T) { + runtime := mkRuntime(t) + defer nuke(runtime) + container, _, err := runtime.Create( + &Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"ping", "-c", "1", "127.0.0.1"}, + }, + "", + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container) + if err := container.Run(); err != nil { + t.Fatal(err) + } + if container.State.ExitCode != 0 { + t.Errorf("Unexpected ping 127.0.0.1 exit code %d (expected 0)", container.State.ExitCode) + } +} + func TestKillDifferentUser(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat"}, OpenStdin: true, User: "daemon", }, + "", ) if err != nil { t.Fatal(err) @@ -471,7 +496,7 @@ func TestCreateVolume(t *testing.T) { if err != nil { t.Fatal(err) } - c, err := runtime.Create(config) + c, _, err := runtime.Create(config, "") if err != nil { t.Fatal(err) } @@ -486,10 +511,11 @@ func TestCreateVolume(t *testing.T) { func TestKill(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"sleep", "2"}, }, + "", ) if err != nil { t.Fatal(err) @@ -530,10 +556,10 @@ func TestExitCode(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - trueContainer, err := runtime.Create(&Config{ + trueContainer, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/true", ""}, - }) + }, "") if err != nil { t.Fatal(err) } @@ -545,10 +571,10 @@ func TestExitCode(t *testing.T) { t.Errorf("Unexpected exit code %d (expected 0)", trueContainer.State.ExitCode) } - falseContainer, err := runtime.Create(&Config{ + falseContainer, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/false", ""}, - }) + }, "") if err != nil { t.Fatal(err) } @@ -564,10 +590,11 @@ func TestExitCode(t *testing.T) { func TestRestart(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foobar"}, }, + "", ) if err != nil { t.Fatal(err) @@ -594,12 +621,13 @@ func TestRestart(t *testing.T) { func TestRestartStdin(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat"}, OpenStdin: true, }, + "", ) if err != nil { t.Fatal(err) @@ -672,10 +700,11 @@ func TestUser(t *testing.T) { defer nuke(runtime) // Default user must be root - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, }, + "", ) if err != nil { t.Fatal(err) @@ -690,12 +719,13 @@ func TestUser(t *testing.T) { } // Set a username - container, err = runtime.Create(&Config{ + container, _, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, User: "root", }, + "", ) if err != nil { t.Fatal(err) @@ -710,12 +740,13 @@ func TestUser(t *testing.T) { } // Set a UID - container, err = runtime.Create(&Config{ + container, _, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, User: "0", }, + "", ) if err != nil || container.State.ExitCode != 0 { t.Fatal(err) @@ -730,12 +761,13 @@ func TestUser(t *testing.T) { } // Set a different user by uid - container, err = runtime.Create(&Config{ + container, _, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, User: "1", }, + "", ) if err != nil { t.Fatal(err) @@ -752,12 +784,13 @@ func TestUser(t *testing.T) { } // Set a different user by username - container, err = runtime.Create(&Config{ + container, _, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, User: "daemon", }, + "", ) if err != nil { t.Fatal(err) @@ -772,12 +805,13 @@ func TestUser(t *testing.T) { } // Test an wrong username - container, err = runtime.Create(&Config{ + container, _, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, User: "unknownuser", }, + "", ) if err != nil { t.Fatal(err) @@ -793,20 +827,22 @@ func TestMultipleContainers(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container1, err := runtime.Create(&Config{ + container1, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"sleep", "2"}, }, + "", ) if err != nil { t.Fatal(err) } defer runtime.Destroy(container1) - container2, err := runtime.Create(&Config{ + container2, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"sleep", "2"}, }, + "", ) if err != nil { t.Fatal(err) @@ -847,12 +883,13 @@ func TestMultipleContainers(t *testing.T) { func TestStdin(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat"}, OpenStdin: true, }, + "", ) if err != nil { t.Fatal(err) @@ -892,12 +929,13 @@ func TestStdin(t *testing.T) { func TestTty(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat"}, OpenStdin: true, }, + "", ) if err != nil { t.Fatal(err) @@ -937,10 +975,11 @@ func TestTty(t *testing.T) { func TestEnv(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"env"}, }, + "", ) if err != nil { t.Fatal(err) @@ -986,12 +1025,13 @@ func TestEnv(t *testing.T) { func TestEntrypoint(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Entrypoint: []string{"/bin/echo"}, Cmd: []string{"-n", "foobar"}, }, + "", ) if err != nil { t.Fatal(err) @@ -1009,11 +1049,12 @@ func TestEntrypoint(t *testing.T) { func TestEntrypointNoCmd(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Entrypoint: []string{"/bin/echo", "foobar"}, }, + "", ) if err != nil { t.Fatal(err) @@ -1060,7 +1101,7 @@ func TestLXCConfig(t *testing.T) { cpuMin := 100 cpuMax := 10000 cpu := cpuMin + rand.Intn(cpuMax-cpuMin) - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/true"}, @@ -1068,6 +1109,7 @@ func TestLXCConfig(t *testing.T) { Memory: int64(mem), CpuShares: int64(cpu), }, + "", ) if err != nil { t.Fatal(err) @@ -1084,12 +1126,13 @@ func TestLXCConfig(t *testing.T) { func TestCustomLxcConfig(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/true"}, Hostname: "foobar", }, + "", ) if err != nil { t.Fatal(err) @@ -1115,10 +1158,11 @@ func BenchmarkRunSequencial(b *testing.B) { runtime := mkRuntime(b) defer nuke(runtime) for i := 0; i < b.N; i++ { - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foo"}, }, + "", ) if err != nil { b.Fatal(err) @@ -1147,10 +1191,11 @@ func BenchmarkRunParallel(b *testing.B) { complete := make(chan error) tasks = append(tasks, complete) go func(i int, complete chan error) { - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foo"}, }, + "", ) if err != nil { complete <- err @@ -1189,7 +1234,7 @@ func BenchmarkRunParallel(b *testing.B) { } func tempDir(t *testing.T) string { - tmpDir, err := ioutil.TempDir("", "docker-test") + tmpDir, err := ioutil.TempDir("", "docker-test-container") if err != nil { t.Fatal(err) } @@ -1297,12 +1342,13 @@ func TestBindMounts(t *testing.T) { func TestVolumesFromReadonlyMount(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create( + container, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/echo", "-n", "foobar"}, Volumes: map[string]struct{}{"/test": {}}, }, + "", ) if err != nil { t.Fatal(err) @@ -1316,12 +1362,13 @@ func TestVolumesFromReadonlyMount(t *testing.T) { t.Fail() } - container2, err := runtime.Create( + container2, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/echo", "-n", "foobar"}, VolumesFrom: container.ID, }, + "", ) if err != nil { t.Fatal(err) @@ -1352,11 +1399,12 @@ func TestRestartWithVolumes(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create(&Config{ + container, _, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foobar"}, Volumes: map[string]struct{}{"/test": {}}, }, + "", ) if err != nil { t.Fatal(err) @@ -1395,11 +1443,12 @@ func TestVolumesFromWithVolumes(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create(&Config{ + 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) @@ -1422,13 +1471,14 @@ func TestVolumesFromWithVolumes(t *testing.T) { t.Fail() } - container2, err := runtime.Create( + container2, _, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat", "/test/foo"}, VolumesFrom: container.ID, Volumes: map[string]struct{}{"/test": {}}, }, + "", ) if err != nil { t.Fatal(err) @@ -1463,7 +1513,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) { if err != nil { t.Fatal(err) } - c, err := runtime.Create(config) + c, _, err := runtime.Create(config, "") if err != nil { t.Fatal(err) } @@ -1529,11 +1579,12 @@ func TestMultipleVolumesFrom(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create(&Config{ + 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) @@ -1556,12 +1607,13 @@ func TestMultipleVolumesFrom(t *testing.T) { t.Fail() } - container2, err := runtime.Create( + 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) @@ -1577,12 +1629,12 @@ func TestMultipleVolumesFrom(t *testing.T) { t.Fatal(err) } - container3, err := runtime.Create( + 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) @@ -1593,7 +1645,6 @@ func TestMultipleVolumesFrom(t *testing.T) { t.Fatal(err) } - t.Log(container3.Volumes) if container3.Volumes["/test"] != container.Volumes["/test"] { t.Fail() } diff --git a/contrib/brew/.gitignore b/contrib/brew/.gitignore deleted file mode 100644 index 0d20b6487c..0000000000 --- a/contrib/brew/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.pyc diff --git a/contrib/brew/README.md b/contrib/brew/README.md deleted file mode 100644 index 026758e876..0000000000 --- a/contrib/brew/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# docker-brew - -docker-brew is a command-line tool used to build the docker standard library. - -## Install instructions - -1. Install python if it isn't already available on your OS of choice -1. Install the easy_install tool (`sudo apt-get install python-setuptools` -for Debian) -1. Install the python package manager, `pip` (`easy_install pip`) -1. Run the following command: `sudo pip install -r requirements.txt` -1. You should now be able to use the `docker-brew` script as such. - -## Basics - - ./docker-brew -h - -Display usage and help. - - ./docker-brew - -Default build from the default repo/branch. Images will be created under the -`library/` namespace. Does not perform a remote push. - - ./docker-brew -n mycorp.com -b stable --push git://github.com/mycorp/docker - -Will fetch the library definition files in the `stable` branch of the -`git://github.com/mycorp/docker` repository and create images under the -`mycorp.com` namespace (e.g. `mycorp.com/ubuntu`). Created images will then -be pushed to the official docker repository (pending: support for private -repositories) - -## Library definition files - -The library definition files are plain text files found in the `library/` -subfolder of the docker repository. - -### File names - -The name of a definition file will determine the name of the image(s) it -creates. For example, the `library/ubuntu` file will create images in the -`/ubuntu` repository. If multiple instructions are present in -a single file, all images are expected to be created under a different tag. - -### Instruction format - -Each line represents a build instruction. -There are different formats that `docker-brew` is able to parse. - - - git://github.com/dotcloud/hipache - https://github.com/dotcloud/docker.git - -The simplest format. `docker-brew` will fetch data from the provided git -repository from the `HEAD`of its `master` branch. Generated image will be -tagged as `latest`. Use of this format is discouraged because there is no -way to ensure stability. - - - bleeding-edge git://github.com/dotcloud/docker - unstable https://github.com/dotcloud/docker-redis.git - -A more advanced format. `docker-brew` will fetch data from the provided git -repository from the `HEAD`of its `master` branch. Generated image will be -tagged as ``. Recommended if we always want to provide a snapshot -of the latest development. Again, no way to ensure stability. - - T: - 2.4.0 git://github.com/dotcloud/docker-redis T:2.4.0 - B: - zfs git://github.com/dotcloud/docker B:zfs-support - C: - 2.2.0 https://github.com/dotcloud/docker-redis.git C:a4bf8923ee4ec566d3ddc212 - -The most complete format. `docker-brew` will fetch data from the provided git -repository from the provided reference (if it's a branch, brew will fetch its -`HEAD`). Generated image will be tagged as ``. Recommended whenever -possible. \ No newline at end of file diff --git a/contrib/brew/brew/__init__.py b/contrib/brew/brew/__init__.py deleted file mode 100644 index a9476ff5a4..0000000000 --- a/contrib/brew/brew/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from brew import build_library, DEFAULT_REPOSITORY, DEFAULT_BRANCH diff --git a/contrib/brew/brew/brew.py b/contrib/brew/brew/brew.py deleted file mode 100644 index 0d9f0f3e98..0000000000 --- a/contrib/brew/brew/brew.py +++ /dev/null @@ -1,185 +0,0 @@ -import os -import logging -from shutil import rmtree - -import docker - -import git - -DEFAULT_REPOSITORY = 'git://github.com/dotcloud/docker' -DEFAULT_BRANCH = 'master' - -logger = logging.getLogger(__name__) -logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', - level='INFO') -client = docker.Client() -processed = {} -processed_folders = [] - - -def build_library(repository=None, branch=None, namespace=None, push=False, - debug=False, prefill=True, registry=None): - dst_folder = None - summary = Summary() - if repository is None: - repository = DEFAULT_REPOSITORY - if branch is None: - branch = DEFAULT_BRANCH - if debug: - logger.setLevel('DEBUG') - - if not (repository.startswith('https://') or repository.startswith('git://')): - logger.info('Repository provided assumed to be a local path') - dst_folder = repository - - try: - client.version() - except Exception as e: - logger.error('Could not reach the docker daemon. Please make sure it ' - 'is running.') - logger.warning('Also make sure you have access to the docker UNIX ' - 'socket (use sudo)') - return - - #FIXME: set destination folder and only pull latest changes instead of - # cloning the whole repo everytime - if not dst_folder: - logger.info('Cloning docker repo from {0}, branch: {1}'.format( - repository, branch)) - try: - rep, dst_folder = git.clone_branch(repository, branch) - except Exception as e: - logger.exception(e) - logger.error('Source repository could not be fetched. Check ' - 'that the address is correct and the branch exists.') - return - try: - dirlist = os.listdir(os.path.join(dst_folder, 'library')) - except OSError as e: - logger.error('The path provided ({0}) could not be found or didn\'t' - 'contain a library/ folder.'.format(dst_folder)) - return - for buildfile in dirlist: - if buildfile == 'MAINTAINERS': - continue - f = open(os.path.join(dst_folder, 'library', buildfile)) - linecnt = 0 - for line in f: - linecnt = linecnt + 1 - logger.debug('{0} ---> {1}'.format(buildfile, line)) - args = line.split() - try: - if len(args) > 3: - raise RuntimeError('Incorrect line format, ' - 'please refer to the docs') - - url = None - ref = 'refs/heads/master' - tag = None - if len(args) == 1: # Just a URL, simple mode - url = args[0] - elif len(args) == 2 or len(args) == 3: # docker-tag url - url = args[1] - tag = args[0] - - if len(args) == 3: # docker-tag url B:branch or T:tag - ref = None - if args[2].startswith('B:'): - ref = 'refs/heads/' + args[2][2:] - elif args[2].startswith('T:'): - ref = 'refs/tags/' + args[2][2:] - elif args[2].startswith('C:'): - ref = args[2][2:] - else: - raise RuntimeError('Incorrect line format, ' - 'please refer to the docs') - if prefill: - logger.debug('Pulling {0} from official repository (cache ' - 'fill)'.format(buildfile)) - client.pull(buildfile) - img = build_repo(url, ref, buildfile, tag, namespace, push, - registry) - summary.add_success(buildfile, (linecnt, line), img) - processed['{0}@{1}'.format(url, ref)] = img - except Exception as e: - logger.exception(e) - summary.add_exception(buildfile, (linecnt, line), e) - - f.close() - if dst_folder != repository: - rmtree(dst_folder, True) - for d in processed_folders: - rmtree(d, True) - summary.print_summary(logger) - - -def build_repo(repository, ref, docker_repo, docker_tag, namespace, push, registry): - docker_repo = '{0}/{1}'.format(namespace or 'library', docker_repo) - img_id = None - dst_folder = None - if '{0}@{1}'.format(repository, ref) not in processed.keys(): - logger.info('Cloning {0} (ref: {1})'.format(repository, ref)) - if repository not in processed: - rep, dst_folder = git.clone(repository, ref) - processed[repository] = rep - processed_folders.append(dst_folder) - else: - dst_folder = git.checkout(processed[repository], ref) - if not 'Dockerfile' in os.listdir(dst_folder): - raise RuntimeError('Dockerfile not found in cloned repository') - logger.info('Building using dockerfile...') - img_id, logs = client.build(path=dst_folder, quiet=True) - else: - img_id = processed['{0}@{1}'.format(repository, ref)] - logger.info('Committing to {0}:{1}'.format(docker_repo, - docker_tag or 'latest')) - client.tag(img_id, docker_repo, docker_tag) - if push: - logger.info('Pushing result to registry {0}'.format( - registry or "default")) - if registry is not None: - docker_repo = '{0}/{1}'.format(registry, docker_repo) - logger.info('Also tagging {0}'.format(docker_repo)) - client.tag(img_id, docker_repo, docker_tag) - client.push(docker_repo) - return img_id - - -class Summary(object): - def __init__(self): - self._summary = {} - self._has_exc = False - - def _add_data(self, image, linestr, data): - if image not in self._summary: - self._summary[image] = { linestr: data } - else: - self._summary[image][linestr] = data - - def add_exception(self, image, line, exc): - lineno, linestr = line - self._add_data(image, linestr, { 'line': lineno, 'exc': str(exc) }) - self._has_exc = True - - def add_success(self, image, line, img_id): - lineno, linestr = line - self._add_data(image, linestr, { 'line': lineno, 'id': img_id }) - - def print_summary(self, logger=None): - linesep = ''.center(61, '-') + '\n' - s = 'BREW BUILD SUMMARY\n' + linesep - success = 'OVERALL SUCCESS: {}\n'.format(not self._has_exc) - details = linesep - for image, lines in self._summary.iteritems(): - details = details + '{}\n{}'.format(image, linesep) - for linestr, data in lines.iteritems(): - details = details + '{0:2} | {1} | {2:50}\n'.format( - data['line'], - 'KO' if 'exc' in data else 'OK', - data['exc'] if 'exc' in data else data['id'] - ) - details = details + linesep - if logger: - logger.info(s + success + details) - else: - print s, success, details diff --git a/contrib/brew/brew/git.py b/contrib/brew/brew/git.py deleted file mode 100644 index e45e995458..0000000000 --- a/contrib/brew/brew/git.py +++ /dev/null @@ -1,63 +0,0 @@ -import tempfile -import logging - -from dulwich import index -from dulwich.client import get_transport_and_path -from dulwich.repo import Repo - -logger = logging.getLogger(__name__) - - -def clone_branch(repo_url, branch="master", folder=None): - return clone(repo_url, 'refs/heads/' + branch, folder) - - -def clone_tag(repo_url, tag, folder=None): - return clone(repo_url, 'refs/tags/' + tag, folder) - - -def checkout(rep, ref=None): - is_commit = False - if ref is None: - ref = 'refs/heads/master' - elif not ref.startswith('refs/'): - is_commit = True - if is_commit: - rep['HEAD'] = rep.commit(ref) - else: - rep['HEAD'] = rep.refs[ref] - indexfile = rep.index_path() - tree = rep["HEAD"].tree - index.build_index_from_tree(rep.path, indexfile, rep.object_store, tree) - return rep.path - -def clone(repo_url, ref=None, folder=None): - is_commit = False - if ref is None: - ref = 'refs/heads/master' - elif not ref.startswith('refs/'): - is_commit = True - logger.debug("clone repo_url={0}, ref={1}".format(repo_url, ref)) - if folder is None: - folder = tempfile.mkdtemp() - logger.debug("folder = {0}".format(folder)) - rep = Repo.init(folder) - client, relative_path = get_transport_and_path(repo_url) - logger.debug("client={0}".format(client)) - - remote_refs = client.fetch(relative_path, rep) - for k, v in remote_refs.iteritems(): - try: - rep.refs.add_if_new(k, v) - except: - pass - - if is_commit: - rep['HEAD'] = rep.commit(ref) - else: - rep['HEAD'] = remote_refs[ref] - indexfile = rep.index_path() - tree = rep["HEAD"].tree - index.build_index_from_tree(rep.path, indexfile, rep.object_store, tree) - logger.debug("done") - return rep, folder diff --git a/contrib/brew/docker-brew b/contrib/brew/docker-brew deleted file mode 100755 index c3fc147cf5..0000000000 --- a/contrib/brew/docker-brew +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python - -import argparse -import sys - -try: - import brew -except ImportError as e: - print str(e) - print 'Please install the required dependencies first' - print 'sudo pip install -r requirements.txt' - sys.exit(1) - -if __name__ == '__main__': - parser = argparse.ArgumentParser('Build the docker standard library') - parser.add_argument('--push', action='store_true', default=False, - help='Push generated repositories') - parser.add_argument('--debug', default=False, action='store_true', - help='Enable debugging output') - parser.add_argument('--noprefill', default=True, action='store_false', - dest='prefill', help='Disable cache prefill') - parser.add_argument('-n', metavar='NAMESPACE', default='library', - help='Namespace used for generated repositories.' - ' Default is library') - parser.add_argument('-b', metavar='BRANCH', default=brew.DEFAULT_BRANCH, - help='Branch in the repository where the library definition' - ' files will be fetched. Default is ' + brew.DEFAULT_BRANCH) - parser.add_argument('repository', default=brew.DEFAULT_REPOSITORY, - nargs='?', help='git repository containing the library definition' - ' files. Default is ' + brew.DEFAULT_REPOSITORY) - parser.add_argument('--reg', default=None, help='Registry address to' - ' push build results to. Also sets push to true.') - args = parser.parse_args() - brew.build_library(args.repository, args.b, args.n, - args.push or args.reg is not None, args.debug, args.prefill, args.reg) diff --git a/contrib/brew/requirements.txt b/contrib/brew/requirements.txt deleted file mode 100644 index 2f4400d20f..0000000000 --- a/contrib/brew/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -dulwich==0.9.0 --e git://github.com/dotcloud/docker-py.git#egg=docker-py diff --git a/contrib/brew/setup.py b/contrib/brew/setup.py deleted file mode 100644 index 8a099c99ff..0000000000 --- a/contrib/brew/setup.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -import os -from setuptools import setup - -ROOT_DIR = os.path.dirname(__file__) -SOURCE_DIR = os.path.join(ROOT_DIR) - -test_requirements = [] -setup( - name="docker-brew", - version='0.0.1', - description="-", - packages=['dockerbrew'], - install_requires=['dulwich', 'docker'] + test_requirements, - zip_safe=False, - classifiers=['Development Status :: 3 - Alpha', - 'Environment :: Other Environment', - 'Intended Audience :: Developers', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Topic :: Utilities'], - ) diff --git a/contrib/host-integration/Dockerfile.dev b/contrib/host-integration/Dockerfile.dev new file mode 100644 index 0000000000..161416e750 --- /dev/null +++ b/contrib/host-integration/Dockerfile.dev @@ -0,0 +1,27 @@ +# +# This Dockerfile will create an image that allows to generate upstart and +# systemd scripts (more to come) +# +# docker-version 0.6.2 +# + +FROM ubuntu:12.10 +MAINTAINER Guillaume J. Charmes + +RUN apt-get update && apt-get install -y wget git mercurial + +# Install Go +RUN wget --no-check-certificate https://go.googlecode.com/files/go1.1.2.linux-amd64.tar.gz -O go-1.1.2.tar.gz +RUN tar -xzvf go-1.1.2.tar.gz && mv /go /goroot +RUN mkdir /go + +ENV GOROOT /goroot +ENV GOPATH /go +ENV PATH $GOROOT/bin:$PATH + +RUN go get github.com/dotcloud/docker && cd /go/src/github.com/dotcloud/docker && git checkout v0.6.3 +ADD manager.go /manager/ +RUN cd /manager && go build -o /usr/bin/manager + +ENTRYPOINT ["/usr/bin/manager"] + diff --git a/contrib/host-integration/Dockerfile.min b/contrib/host-integration/Dockerfile.min new file mode 100644 index 0000000000..1a7b3a9d82 --- /dev/null +++ b/contrib/host-integration/Dockerfile.min @@ -0,0 +1,4 @@ +FROM busybox +MAINTAINER Guillaume J. Charmes +ADD manager /usr/bin/ +ENTRYPOINT ["/usr/bin/manager"] diff --git a/contrib/host-integration/manager.go b/contrib/host-integration/manager.go new file mode 100644 index 0000000000..98ff90aa1b --- /dev/null +++ b/contrib/host-integration/manager.go @@ -0,0 +1,130 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "github.com/dotcloud/docker" + "os" + "strings" + "text/template" +) + +var templates = map[string]string{ + + "upstart": `description "{{.description}}" +author "{{.author}}" +start on filesystem and started lxc-net and started docker +stop on runlevel [!2345] +respawn +exec /home/vagrant/goroot/bin/docker start -a {{.container_id}} +`, + + "systemd": `[Unit] + Description={{.description}} + Author={{.author}} + After=docker.service + +[Service] + Restart=always + ExecStart=/usr/bin/docker start -a {{.container_id}} + ExecStop=/usr/bin/docker stop -t 2 {{.container_id}} + +[Install] + WantedBy=local.target +`, +} + +func main() { + // Parse command line for custom options + kind := flag.String("t", "upstart", "Type of manager requested") + author := flag.String("a", "", "Author of the image") + description := flag.String("d", "", "Description of the image") + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "\nUsage: manager \n\n") + flag.PrintDefaults() + } + flag.Parse() + + // We require at least the container ID + if flag.NArg() != 1 { + println(flag.NArg()) + flag.Usage() + return + } + + // Check that the requested process manager is supported + if _, exists := templates[*kind]; !exists { + panic("Unkown script template") + } + + // Load the requested template + tpl, err := template.New("processManager").Parse(templates[*kind]) + if err != nil { + panic(err) + } + + // Create stdout/stderr buffers + bufOut := bytes.NewBuffer(nil) + bufErr := bytes.NewBuffer(nil) + + // Instanciate the Docker CLI + cli := docker.NewDockerCli(nil, bufOut, bufErr, "unix", "/var/run/docker.sock") + // Retrieve the container info + if err := cli.CmdInspect(flag.Arg(0)); err != nil { + // As of docker v0.6.3, CmdInspect always returns nil + panic(err) + } + + // If there is nothing in the error buffer, then the Docker daemon is there and the container has been found + if bufErr.Len() == 0 { + // Unmarshall the resulting container data + c := []*docker.Container{{}} + if err := json.Unmarshal(bufOut.Bytes(), &c); err != nil { + panic(err) + } + // Reset the buffers + bufOut.Reset() + bufErr.Reset() + // Retrieve the info of the linked image + if err := cli.CmdInspect(c[0].Image); err != nil { + panic(err) + } + // If there is nothing in the error buffer, then the image has been found. + if bufErr.Len() == 0 { + // Unmarshall the resulting image data + img := []*docker.Image{{}} + if err := json.Unmarshal(bufOut.Bytes(), &img); err != nil { + panic(err) + } + // If no author has been set, use the one from the image + if *author == "" && img[0].Author != "" { + *author = strings.Replace(img[0].Author, "\"", "", -1) + } + // If no description has been set, use the comment from the image + if *description == "" && img[0].Comment != "" { + *description = strings.Replace(img[0].Comment, "\"", "", -1) + } + } + } + + /// Old version: Wrtie the resulting script to file + // f, err := os.OpenFile(kind, os.O_CREATE|os.O_WRONLY, 0755) + // if err != nil { + // panic(err) + // } + // defer f.Close() + + // Create a map with needed data + data := map[string]string{ + "author": *author, + "description": *description, + "container_id": flag.Arg(0), + } + + // Process the template and output it on Stdout + if err := tpl.Execute(os.Stdout, data); err != nil { + panic(err) + } +} diff --git a/contrib/host-integration/manager.sh b/contrib/host-integration/manager.sh new file mode 100755 index 0000000000..fecf4bf64b --- /dev/null +++ b/contrib/host-integration/manager.sh @@ -0,0 +1,53 @@ +#!/bin/sh +set -e + +usage() { + echo >&2 "usage: $0 [-a author] [-d description] container [manager]" + echo >&2 " ie: $0 -a 'John Smith' 4ec9612a37cd systemd" + echo >&2 " ie: $0 -d 'Super Cool System' 4ec9612a37cd # defaults to upstart" + exit 1 +} + +auth='' +desc='' +have_auth= +have_desc= +while getopts a:d: opt; do + case "$opt" in + a) + auth="$OPTARG" + have_auth=1 + ;; + d) + desc="$OPTARG" + have_desc=1 + ;; + esac +done +shift $(($OPTIND - 1)) + +[ $# -ge 1 -a $# -le 2 ] || usage + +cid="$1" +script="${2:-upstart}" +if [ ! -e "manager/$script" ]; then + echo >&2 "Error: manager type '$script' is unknown (PRs always welcome!)." + echo >&2 'The currently supported types are:' + echo >&2 " $(cd manager && echo *)" + exit 1 +fi + +# TODO https://github.com/dotcloud/docker/issues/734 (docker inspect formatting) +#if command -v docker > /dev/null 2>&1; then +# image="$(docker inspect -f '{{.Image}}' "$cid")" +# if [ "$image" ]; then +# if [ -z "$have_auth" ]; then +# auth="$(docker inspect -f '{{.Author}}' "$image")" +# fi +# if [ -z "$have_desc" ]; then +# desc="$(docker inspect -f '{{.Comment}}' "$image")" +# fi +# fi +#fi + +exec "manager/$script" "$cid" "$auth" "$desc" diff --git a/contrib/host-integration/manager/systemd b/contrib/host-integration/manager/systemd new file mode 100755 index 0000000000..0431b3ced4 --- /dev/null +++ b/contrib/host-integration/manager/systemd @@ -0,0 +1,20 @@ +#!/bin/sh +set -e + +cid="$1" +auth="$2" +desc="$3" + +cat <<-EOF + [Unit] + Description=$desc + Author=$auth + After=docker.service + + [Service] + ExecStart=/usr/bin/docker start -a $cid + ExecStop=/usr/bin/docker stop -t 2 $cid + + [Install] + WantedBy=local.target +EOF diff --git a/contrib/host-integration/manager/upstart b/contrib/host-integration/manager/upstart new file mode 100755 index 0000000000..af90f1fddd --- /dev/null +++ b/contrib/host-integration/manager/upstart @@ -0,0 +1,15 @@ +#!/bin/sh +set -e + +cid="$1" +auth="$2" +desc="$3" + +cat <<-EOF + description "$(echo "$desc" | sed 's/"/\\"/g')" + author "$(echo "$auth" | sed 's/"/\\"/g')" + start on filesystem and started lxc-net and started docker + stop on runlevel [!2345] + respawn + exec /usr/bin/docker start -a "$cid" +EOF diff --git a/contrib/init/openrc/docker.confd b/contrib/init/openrc/docker.confd new file mode 100644 index 0000000000..ae247c007e --- /dev/null +++ b/contrib/init/openrc/docker.confd @@ -0,0 +1,13 @@ +# /etc/conf.d/docker: config file for /etc/init.d/docker + +# where the docker daemon output gets piped +#DOCKER_LOGFILE="/var/log/docker.log" + +# where docker's pid get stored +#DOCKER_PIDFILE="/run/docker.pid" + +# where the docker daemon itself is run from +#DOCKER_BINARY="/usr/bin/docker" + +# any other random options you want to pass to docker +DOCKER_OPTS="" diff --git a/contrib/init/openrc/docker.initd b/contrib/init/openrc/docker.initd new file mode 100755 index 0000000000..2d79a73973 --- /dev/null +++ b/contrib/init/openrc/docker.initd @@ -0,0 +1,31 @@ +#!/sbin/runscript +# Copyright 1999-2013 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +DOCKER_LOGFILE=${DOCKER_LOGFILE:-/var/log/${SVCNAME}.log} +DOCKER_PIDFILE=${DOCKER_PIDFILE:-/run/${SVCNAME}.pid} +DOCKER_BINARY=${DOCKER_BINARY:-/usr/bin/docker} +DOCKER_OPTS=${DOCKER_OPTS:-} + +start() { + checkpath -f -m 0644 -o root:docker "$DOCKER_LOGFILE" + + ebegin "Starting docker daemon" + start-stop-daemon --start --background \ + --exec "$DOCKER_BINARY" \ + --pidfile "$DOCKER_PIDFILE" \ + --stdout "$DOCKER_LOGFILE" \ + --stderr "$DOCKER_LOGFILE" \ + -- -d -p "$DOCKER_PIDFILE" \ + $DOCKER_OPTS + eend $? +} + +stop() { + ebegin "Stopping docker daemon" + start-stop-daemon --stop \ + --exec "$DOCKER_BINARY" \ + --pidfile "$DOCKER_PIDFILE" + eend $? +} diff --git a/contrib/init/systemd/docker.service b/contrib/init/systemd/docker.service new file mode 100644 index 0000000000..dacb0b74f5 --- /dev/null +++ b/contrib/init/systemd/docker.service @@ -0,0 +1,13 @@ +[Unit] +Description=Easily create lightweight, portable, self-sufficient containers from any application! +Documentation=http://docs.docker.io +Requires=network.target +After=multi-user.target + +[Service] +Type=simple +ExecStartPre=/bin/mount --make-rprivate / +ExecStart=/usr/bin/docker -d + +[Install] +WantedBy=multi-user.target diff --git a/contrib/init/sysvinit/docker b/contrib/init/sysvinit/docker new file mode 100755 index 0000000000..5a457db785 --- /dev/null +++ b/contrib/init/sysvinit/docker @@ -0,0 +1,85 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: docker +# Required-Start: $syslog $remote_fs +# Required-Stop: $syslog $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Linux container runtime +# Description: Linux container runtime +### END INIT INFO + +DOCKER=/usr/bin/docker +DOCKER_PIDFILE=/var/run/docker.pid +DOCKER_OPTS= + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin + +# Check lxc-docker is present +[ -x $DOCKER ] || (log_failure_msg "docker not present"; exit 1) + +# Get lsb functions +. /lib/lsb/init-functions + +if [ -f /etc/default/lxc ]; then + . /etc/default/lxc +fi + +if [ "$1" = start ] && which initctl >/dev/null && initctl version | grep -q upstart; then + exit 1 +fi + +check_root_id () +{ + if [ "$(id -u)" != "0" ]; then + log_failure_msg "Docker must be run as root"; exit 1 + fi +} + +case "$1" in + start) + check_root_id || exit 1 + log_begin_msg "Starting Docker" + mount | grep cgroup >/dev/null || mount -t cgroup none /sys/fs/cgroup 2>/dev/null + start-stop-daemon --start --background $NO_CLOSE \ + --exec "$DOCKER" \ + --pidfile "$DOCKER_PIDFILE" \ + -- -d -p "$DOCKER_PIDFILE" \ + $DOCKER_OPTS + log_end_msg $? + ;; + + stop) + check_root_id || exit 1 + log_begin_msg "Stopping Docker" + start-stop-daemon --stop \ + --pidfile "$DOCKER_PIDFILE" + log_end_msg $? + ;; + + restart) + check_root_id || exit 1 + docker_pid=`cat "$DOCKER_PIDFILE" 2>/dev/null` + [ -n "$docker_pid" ] \ + && ps -p $docker_pid > /dev/null 2>&1 \ + && $0 stop + $0 start + ;; + + force-reload) + check_root_id || exit 1 + $0 restart + ;; + + status) + status_of_proc -p "$DOCKER_PIDFILE" "$DOCKER" docker + ;; + + *) + echo "Usage: $0 {start|stop|restart|status}" + exit 1 + ;; +esac + +exit 0 diff --git a/contrib/init/upstart/docker.conf b/contrib/init/upstart/docker.conf new file mode 100644 index 0000000000..72c132c860 --- /dev/null +++ b/contrib/init/upstart/docker.conf @@ -0,0 +1,10 @@ +description "Docker daemon" + +start on filesystem and started lxc-net +stop on runlevel [!2345] + +respawn + +script + /usr/bin/docker -d +end script diff --git a/contrib/mkimage-arch.sh b/contrib/mkimage-arch.sh index cdc3c273d8..db14e8674e 100755 --- a/contrib/mkimage-arch.sh +++ b/contrib/mkimage-arch.sh @@ -21,18 +21,20 @@ mkdir $ROOTFS #packages to ignore for space savings PKGIGNORE=linux,jfsutils,lvm2,cryptsetup,groff,man-db,man-pages,mdadm,pciutils,pcmciautils,reiserfsprogs,s-nail,xfsprogs -expect -c " +expect <&2 "usage: $0 repo suite [mirror]" - echo >&2 - echo >&2 " ie: $0 tianon/debian squeeze" - echo >&2 " $0 tianon/debian squeeze http://ftp.uk.debian.org/debian/" - echo >&2 - echo >&2 " ie: $0 tianon/ubuntu precise" - echo >&2 " $0 tianon/ubuntu precise http://mirrors.melbourne.co.uk/ubuntu/" - echo >&2 - exit 1 -fi - -target="/tmp/docker-rootfs-debian-$suite-$$-$RANDOM" - -cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" -returnTo="$(pwd -P)" - -set -x - -# bootstrap -mkdir -p "$target" -sudo debootstrap --verbose --variant="$variant" --include="$include" "$suite" "$target" "$mirror" - -cd "$target" - -# prevent init scripts from running during install/update -# policy-rc.d (for most scripts) -echo $'#!/bin/sh\nexit 101' | sudo tee usr/sbin/policy-rc.d > /dev/null -sudo chmod +x usr/sbin/policy-rc.d -# initctl (for some pesky upstart scripts) -sudo chroot . dpkg-divert --local --rename --add /sbin/initctl -sudo ln -sf /bin/true sbin/initctl -# see https://github.com/dotcloud/docker/issues/446#issuecomment-16953173 - -# shrink the image, since apt makes us fat (wheezy: ~157.5MB vs ~120MB) -sudo chroot . apt-get clean - -# while we're at it, apt is unnecessarily slow inside containers -# this forces dpkg not to call sync() after package extraction and speeds up install -# the benefit is huge on spinning disks, and the penalty is nonexistent on SSD or decent server virtualization -echo 'force-unsafe-io' | sudo tee etc/dpkg/dpkg.cfg.d/02apt-speedup > /dev/null -# we want to effectively run "apt-get clean" after every install to keep images small -echo 'DPkg::Post-Invoke {"/bin/rm -f /var/cache/apt/archives/*.deb || true";};' | sudo tee etc/apt/apt.conf.d/no-cache > /dev/null - -# helpful undo lines for each the above tweaks (for lack of a better home to keep track of them): -# rm /usr/sbin/policy-rc.d -# rm /sbin/initctl; dpkg-divert --rename --remove /sbin/initctl -# rm /etc/dpkg/dpkg.cfg.d/02apt-speedup -# rm /etc/apt/apt.conf.d/no-cache - -# create the image (and tag $repo:$suite) -sudo tar -c . | docker import - $repo $suite - -# test the image -docker run -i -t $repo:$suite echo success - -if [ "$suite" = "$stableSuite" -o "$suite" = 'stable' ]; then - # tag latest - docker tag $repo:$suite $repo latest - - # tag the specific debian release version - ver=$(docker run $repo:$suite cat /etc/debian_version) - docker tag $repo:$suite $repo $ver -fi - -# cleanup -cd "$returnTo" -sudo rm -rf "$target" diff --git a/contrib/mkimage-debootstrap.sh b/contrib/mkimage-debootstrap.sh new file mode 100755 index 0000000000..b0a370da7d --- /dev/null +++ b/contrib/mkimage-debootstrap.sh @@ -0,0 +1,233 @@ +#!/bin/bash +set -e + +variant='minbase' +include='iproute,iputils-ping' +arch='amd64' # intentionally undocumented for now +skipDetection= +strictDebootstrap= +justTar= + +usage() { + echo >&2 + + echo >&2 "usage: $0 [options] repo suite [mirror]" + + echo >&2 + echo >&2 'options: (not recommended)' + echo >&2 " -p set an http_proxy for debootstrap" + echo >&2 " -v $variant # change default debootstrap variant" + echo >&2 " -i $include # change default package includes" + echo >&2 " -d # strict debootstrap (do not apply any docker-specific tweaks)" + echo >&2 " -s # skip version detection and tagging (ie, precise also tagged as 12.04)" + echo >&2 " # note that this will also skip adding universe and/or security/updates to sources.list" + echo >&2 " -t # just create a tarball, especially for dockerbrew (uses repo as tarball name)" + + echo >&2 + echo >&2 " ie: $0 username/debian squeeze" + echo >&2 " $0 username/debian squeeze http://ftp.uk.debian.org/debian/" + + echo >&2 + echo >&2 " ie: $0 username/ubuntu precise" + echo >&2 " $0 username/ubuntu precise http://mirrors.melbourne.co.uk/ubuntu/" + + echo >&2 + echo >&2 " ie: $0 -t precise.tar.bz2 precise" + echo >&2 " $0 -t wheezy.tgz wheezy" + echo >&2 " $0 -t wheezy-uk.tar.xz wheezy http://ftp.uk.debian.org/debian/" + + echo >&2 +} + +# these should match the names found at http://www.debian.org/releases/ +debianStable=wheezy +debianUnstable=sid +# this should match the name found at http://releases.ubuntu.com/ +ubuntuLatestLTS=precise + +while getopts v:i:a:p:dst name; do + case "$name" in + p) + http_proxy="$OPTARG" + ;; + v) + variant="$OPTARG" + ;; + i) + include="$OPTARG" + ;; + a) + arch="$OPTARG" + ;; + d) + strictDebootstrap=1 + ;; + s) + skipDetection=1 + ;; + t) + justTar=1 + ;; + ?) + usage + exit 0 + ;; + esac +done +shift $(($OPTIND - 1)) + +repo="$1" +suite="$2" +mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided + +if [ ! "$repo" ] || [ ! "$suite" ]; then + usage + exit 1 +fi + +# some rudimentary detection for whether we need to "sudo" our docker calls +docker='' +if docker version > /dev/null 2>&1; then + docker='docker' +elif sudo docker version > /dev/null 2>&1; then + docker='sudo docker' +elif command -v docker > /dev/null 2>&1; then + docker='docker' +else + echo >&2 "warning: either docker isn't installed, or your current user cannot run it;" + echo >&2 " this script is not likely to work as expected" + sleep 3 + docker='docker' # give us a command-not-found later +fi + +# make sure we have an absolute path to our final tarball so we can still reference it properly after we change directory +if [ "$justTar" ]; then + if [ ! -d "$(dirname "$repo")" ]; then + echo >&2 "error: $(dirname "$repo") does not exist" + exit 1 + fi + repo="$(cd "$(dirname "$repo")" && pwd -P)/$(basename "$repo")" +fi + +# will be filled in later, if [ -z "$skipDetection" ] +lsbDist='' + +target="/tmp/docker-rootfs-debootstrap-$suite-$$-$RANDOM" + +cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" +returnTo="$(pwd -P)" + +set -x + +# bootstrap +mkdir -p "$target" +sudo http_proxy=$http_proxy debootstrap --verbose --variant="$variant" --include="$include" --arch="$arch" "$suite" "$target" "$mirror" + +cd "$target" + +if [ -z "$strictDebootstrap" ]; then + # prevent init scripts from running during install/update + # policy-rc.d (for most scripts) + echo $'#!/bin/sh\nexit 101' | sudo tee usr/sbin/policy-rc.d > /dev/null + sudo chmod +x usr/sbin/policy-rc.d + # initctl (for some pesky upstart scripts) + sudo chroot . dpkg-divert --local --rename --add /sbin/initctl + sudo ln -sf /bin/true sbin/initctl + # see https://github.com/dotcloud/docker/issues/446#issuecomment-16953173 + + # shrink the image, since apt makes us fat (wheezy: ~157.5MB vs ~120MB) + sudo chroot . apt-get clean + + # while we're at it, apt is unnecessarily slow inside containers + # this forces dpkg not to call sync() after package extraction and speeds up install + # the benefit is huge on spinning disks, and the penalty is nonexistent on SSD or decent server virtualization + echo 'force-unsafe-io' | sudo tee etc/dpkg/dpkg.cfg.d/02apt-speedup > /dev/null + # we want to effectively run "apt-get clean" after every install to keep images small + echo 'DPkg::Post-Invoke {"/bin/rm -f /var/cache/apt/archives/*.deb || true";};' | sudo tee etc/apt/apt.conf.d/no-cache > /dev/null + + # helpful undo lines for each the above tweaks (for lack of a better home to keep track of them): + # rm /usr/sbin/policy-rc.d + # rm /sbin/initctl; dpkg-divert --rename --remove /sbin/initctl + # rm /etc/dpkg/dpkg.cfg.d/02apt-speedup + # rm /etc/apt/apt.conf.d/no-cache + + if [ -z "$skipDetection" ]; then + # see also rudimentary platform detection in hack/install.sh + lsbDist='' + if [ -r etc/lsb-release ]; then + lsbDist="$(. etc/lsb-release && echo "$DISTRIB_ID")" + fi + if [ -z "$lsbDist" ] && [ -r etc/debian_version ]; then + lsbDist='Debian' + fi + + case "$lsbDist" in + Debian) + # add the updates and security repositories + if [ "$suite" != "$debianUnstable" -a "$suite" != 'unstable' ]; then + # ${suite}-updates only applies to non-unstable + sudo sed -i "p; s/ $suite main$/ ${suite}-updates main/" etc/apt/sources.list + + # same for security updates + echo "deb http://security.debian.org/ $suite/updates main" | sudo tee -a etc/apt/sources.list > /dev/null + fi + ;; + Ubuntu) + # add the universe, updates, and security repositories + sudo sed -i " + s/ $suite main$/ $suite main universe/; p; + s/ $suite main/ ${suite}-updates main/; p; + s/ $suite-updates main/ ${suite}-security main/ + " etc/apt/sources.list + ;; + esac + fi +fi + +if [ "$justTar" ]; then + # create the tarball file so it has the right permissions (ie, not root) + touch "$repo" + + # fill the tarball + sudo tar --numeric-owner -caf "$repo" . +else + # create the image (and tag $repo:$suite) + sudo tar --numeric-owner -c . | $docker import - $repo $suite + + # test the image + $docker run -i -t $repo:$suite echo success + + if [ -z "$skipDetection" ]; then + case "$lsbDist" in + Debian) + if [ "$suite" = "$debianStable" -o "$suite" = 'stable' ] && [ -r etc/debian_version ]; then + # tag latest + $docker tag $repo:$suite $repo latest + + if [ -r etc/debian_version ]; then + # tag the specific debian release version (which is only reasonable to tag on debian stable) + ver=$(cat etc/debian_version) + $docker tag $repo:$suite $repo $ver + fi + fi + ;; + Ubuntu) + if [ "$suite" = "$ubuntuLatestLTS" ]; then + # tag latest + $docker tag $repo:$suite $repo latest + fi + if [ -r etc/lsb-release ]; then + lsbRelease="$(. etc/lsb-release && echo "$DISTRIB_RELEASE")" + if [ "$lsbRelease" ]; then + # tag specific Ubuntu version number, if available (12.04, etc.) + $docker tag $repo:$suite $repo $lsbRelease + fi + fi + ;; + esac + fi +fi + +# cleanup +cd "$returnTo" +sudo rm -rf "$target" diff --git a/contrib/mkimage-unittest.sh b/contrib/mkimage-unittest.sh index cbfeb4b72f..af6488e9b7 100755 --- a/contrib/mkimage-unittest.sh +++ b/contrib/mkimage-unittest.sh @@ -44,6 +44,6 @@ do done chmod 0755 $ROOTFS # See #486 -tar -cf- . | docker import - docker-ut +tar --numeric-owner -cf- . | docker import - docker-ut docker run -i -u root docker-ut /bin/echo Success. rm -rf $ROOTFS diff --git a/contrib/vim-syntax/syntax/dockerfile.vim b/contrib/vim-syntax/syntax/dockerfile.vim index 90e0651b66..fb5cab23cb 100644 --- a/contrib/vim-syntax/syntax/dockerfile.vim +++ b/contrib/vim-syntax/syntax/dockerfile.vim @@ -20,3 +20,5 @@ highlight link dockerfileString String syntax match dockerfileComment "\v^\s*#.*$" highlight link dockerfileComment Comment + +set commentstring=#\ %s diff --git a/docker/docker.go b/docker/docker.go index 750d63c06b..362e899ba1 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -4,9 +4,11 @@ import ( "flag" "fmt" "github.com/dotcloud/docker" + "github.com/dotcloud/docker/sysinit" "github.com/dotcloud/docker/utils" "io/ioutil" "log" + "net" "os" "os/signal" "strconv" @@ -22,7 +24,7 @@ var ( func main() { if selfPath := utils.SelfPath(); selfPath == "/sbin/init" || selfPath == "/.dockerinit" { // Running in init mode - docker.SysInit() + sysinit.SysInit() return } // FIXME: Switch d and D ? (to be more sshd like) @@ -35,9 +37,14 @@ func main() { flGraphPath := flag.String("g", "/var/lib/docker", "Path to graph storage base dir.") flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.") flDns := flag.String("dns", "", "Set custom dns servers") - flHosts := docker.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)} + flHosts := utils.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)} flag.Var(&flHosts, "H", "tcp://host:port to bind/connect to or unix://path/to/socket to use") + flEnableIptables := flag.Bool("iptables", true, "Disable iptables within docker") + flDefaultIp := flag.String("ip", "0.0.0.0", "Default ip address to use when binding a containers ports") + flInterContainerComm := flag.Bool("enable-container-comm", false, "Enable inter-container communication") + flag.Parse() + if *flVersion { showVersion() return @@ -46,13 +53,17 @@ func main() { flHosts = flHosts[1:] //trick to display a nice default value in the usage } for i, flHost := range flHosts { - flHosts[i] = utils.ParseHost(docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT, flHost) + host, err := utils.ParseHost(docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT, flHost) + if err == nil { + flHosts[i] = host + } else { + log.Fatal(err) + } } + bridge := docker.DefaultNetworkBridge if *bridgeName != "" { - docker.NetworkBridgeIface = *bridgeName - } else { - docker.NetworkBridgeIface = docker.DefaultNetworkBridge + bridge = *bridgeName } if *flDebug { os.Setenv("DEBUG", "1") @@ -64,14 +75,31 @@ func main() { flag.Usage() return } - if err := daemon(*pidfile, *flGraphPath, flHosts, *flAutoRestart, *flEnableCors, *flDns); err != nil { + var dns []string + if *flDns != "" { + dns = []string{*flDns} + } + + ip := net.ParseIP(*flDefaultIp) + + config := &docker.DaemonConfig{ + Pidfile: *pidfile, + GraphPath: *flGraphPath, + AutoRestart: *flAutoRestart, + EnableCors: *flEnableCors, + Dns: dns, + EnableIptables: *flEnableIptables, + BridgeIface: bridge, + ProtoAddresses: flHosts, + DefaultIp: ip, + InterContainerCommunication: *flInterContainerComm, + } + if err := daemon(config); err != nil { log.Fatal(err) - os.Exit(-1) } } else { if len(flHosts) > 1 { log.Fatal("Please specify only one -H") - return } protoAddrParts := strings.SplitN(flHosts[0], "://", 2) if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil { @@ -79,7 +107,6 @@ func main() { os.Exit(sterr.Status) } log.Fatal(err) - os.Exit(-1) } } } @@ -115,30 +142,30 @@ func removePidFile(pidfile string) { } } -func daemon(pidfile string, flGraphPath string, protoAddrs []string, autoRestart, enableCors bool, flDns string) error { - if err := createPidFile(pidfile); err != nil { +func daemon(config *docker.DaemonConfig) error { + if err := createPidFile(config.Pidfile); err != nil { log.Fatal(err) } - defer removePidFile(pidfile) + defer removePidFile(config.Pidfile) + + server, err := docker.NewServer(config) + if err != nil { + return err + } + defer server.Close() c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) go func() { sig := <-c log.Printf("Received signal '%v', exiting\n", sig) - removePidFile(pidfile) + server.Close() + removePidFile(config.Pidfile) os.Exit(0) }() - var dns []string - if flDns != "" { - dns = []string{flDns} - } - server, err := docker.NewServer(flGraphPath, autoRestart, enableCors, dns) - if err != nil { - return err - } - chErrors := make(chan error, len(protoAddrs)) - for _, protoAddr := range protoAddrs { + + chErrors := make(chan error, len(config.ProtoAddresses)) + for _, protoAddr := range config.ProtoAddresses { protoAddrParts := strings.SplitN(protoAddr, "://", 2) if protoAddrParts[0] == "unix" { syscall.Unlink(protoAddrParts[1]) @@ -147,14 +174,15 @@ func daemon(pidfile string, flGraphPath string, protoAddrs []string, autoRestart log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") } } else { + server.Close() + removePidFile(config.Pidfile) log.Fatal("Invalid protocol format.") - os.Exit(-1) } go func() { chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true) }() } - for i := 0; i < len(protoAddrs); i += 1 { + for i := 0; i < len(config.ProtoAddresses); i += 1 { err := <-chErrors if err != nil { return err diff --git a/dockerinit/dockerinit.go b/dockerinit/dockerinit.go new file mode 100644 index 0000000000..0c363f4ac3 --- /dev/null +++ b/dockerinit/dockerinit.go @@ -0,0 +1,16 @@ +package main + +import ( + "github.com/dotcloud/docker/sysinit" +) + +var ( + GITCOMMIT string + VERSION string +) + +func main() { + // Running in init mode + sysinit.SysInit() + return +} diff --git a/docs/Dockerfile b/docs/Dockerfile index e5fd267ebd..d3418ee968 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,11 +1,16 @@ from ubuntu:12.04 maintainer Nick Stinemates +# +# docker build -t docker:docs . && docker run -p 8000:8000 docker:docs +# run apt-get update run apt-get install -y python-setuptools make run easy_install pip +#from docs/requirements.txt, but here to increase cacheability +run pip install Sphinx==1.1.3 +run pip install sphinxcontrib-httpdomain==1.1.8 add . /docs -run pip install -r /docs/requirements.txt run cd /docs; make docs expose 8000 diff --git a/docs/README.md b/docs/README.md index 02572ee11e..63cd8a6ac5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -39,8 +39,7 @@ Getting Started To edit and test the docs, you'll need to install the Sphinx tool and its dependencies. There are two main ways to install this tool: -Native Installation -................... +###Native Installation * Install sphinx: `pip install sphinx` * Mac OS X: `[sudo] pip-2.7 install sphinx` @@ -48,8 +47,7 @@ Native Installation * Mac OS X: `[sudo] pip-2.7 install sphinxcontrib-httpdomain` * If pip is not available you can probably install it using your favorite package manager as **python-pip** -Alternative Installation: Docker Container -.......................................... +###Alternative Installation: Docker Container If you're running ``docker`` on your development machine then you may find it easier and cleaner to use the Dockerfile. This installs Sphinx @@ -59,6 +57,9 @@ docs inside the container, even starting a simple HTTP server on port build .`` and run the resulting image. This is the equivalent to ``make clean server`` since each container starts clean. +In the ``docs/`` directory, run: + ```docker build -t docker:docs . && docker run -p 8000:8000 docker:docs``` + Usage ----- * Follow the contribution guidelines (``../CONTRIBUTING.md``) diff --git a/docs/sources/api/docker_remote_api_v1.0.rst b/docs/sources/api/docker_remote_api_v1.0.rst index 1aa8035a57..dc06a27fc0 100644 --- a/docs/sources/api/docker_remote_api_v1.0.rst +++ b/docs/sources/api/docker_remote_api_v1.0.rst @@ -970,28 +970,33 @@ Create a new image from a container's changes **Example request**: - .. sourcecode:: http + .. sourcecode:: http - POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + Content-Type: application/json + + { + "Cmd": ["cat", "/world"], + "PortSpecs":["22"] + } - **Example response**: + **Example response**: - .. sourcecode:: http + .. sourcecode:: http - HTTP/1.1 201 OK - Content-Type: application/vnd.docker.raw-stream + HTTP/1.1 201 OK + Content-Type: application/vnd.docker.raw-stream - {"Id":"596069db4bf5"} + {"Id":"596069db4bf5"} - :query container: source container - :query repo: repository - :query tag: tag - :query m: commit message - :query author: author (eg. "John Hannibal Smith ") - :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) - :statuscode 201: no error - :statuscode 404: no such container - :statuscode 500: server error + :query container: source container + :query repo: repository + :query tag: tag + :query m: commit message + :query author: author (eg. "John Hannibal Smith ") + :statuscode 201: no error + :statuscode 404: no such container + :statuscode 500: server error 3. Going further diff --git a/docs/sources/api/docker_remote_api_v1.1.rst b/docs/sources/api/docker_remote_api_v1.1.rst index db423de46c..31b34caf5a 100644 --- a/docs/sources/api/docker_remote_api_v1.1.rst +++ b/docs/sources/api/docker_remote_api_v1.1.rst @@ -977,32 +977,37 @@ Create a new image from a container's changes .. http:post:: /commit - Create a new image from a container's changes + Create a new image from a container's changes - **Example request**: + **Example request**: + + .. sourcecode:: http + + POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + Content-Type: application/json + + { + "Cmd": ["cat", "/world"], + "PortSpecs":["22"] + } + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 201 OK + Content-Type: application/vnd.docker.raw-stream - .. sourcecode:: http + {"Id":"596069db4bf5"} - POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 201 OK - Content-Type: application/vnd.docker.raw-stream - - {"Id":"596069db4bf5"} - - :query container: source container - :query repo: repository - :query tag: tag - :query m: commit message - :query author: author (eg. "John Hannibal Smith ") - :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) - :statuscode 201: no error - :statuscode 404: no such container - :statuscode 500: server error + :query container: source container + :query repo: repository + :query tag: tag + :query m: commit message + :query author: author (eg. "John Hannibal Smith ") + :statuscode 201: no error + :statuscode 404: no such container + :statuscode 500: server error 3. Going further diff --git a/docs/sources/api/docker_remote_api_v1.2.rst b/docs/sources/api/docker_remote_api_v1.2.rst index a3a9ba2d15..555ec14b75 100644 --- a/docs/sources/api/docker_remote_api_v1.2.rst +++ b/docs/sources/api/docker_remote_api_v1.2.rst @@ -985,32 +985,37 @@ Create a new image from a container's changes .. http:post:: /commit - Create a new image from a container's changes + Create a new image from a container's changes - **Example request**: + **Example request**: - .. sourcecode:: http + .. sourcecode:: http - POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + Content-Type: application/json + + { + "Cmd": ["cat", "/world"], + "PortSpecs":["22"] + } - **Example response**: + **Example response**: - .. sourcecode:: http + .. sourcecode:: http - HTTP/1.1 201 OK + HTTP/1.1 201 OK Content-Type: application/vnd.docker.raw-stream - {"Id":"596069db4bf5"} + {"Id":"596069db4bf5"} - :query container: source container - :query repo: repository - :query tag: tag - :query m: commit message - :query author: author (eg. "John Hannibal Smith ") - :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) - :statuscode 201: no error - :statuscode 404: no such container - :statuscode 500: server error + :query container: source container + :query repo: repository + :query tag: tag + :query m: commit message + :query author: author (eg. "John Hannibal Smith ") + :statuscode 201: no error + :statuscode 404: no such container + :statuscode 500: server error 3. Going further diff --git a/docs/sources/api/docker_remote_api_v1.3.rst b/docs/sources/api/docker_remote_api_v1.3.rst index 40b1f7fc01..b6661bfdb6 100644 --- a/docs/sources/api/docker_remote_api_v1.3.rst +++ b/docs/sources/api/docker_remote_api_v1.3.rst @@ -1034,32 +1034,37 @@ Create a new image from a container's changes .. http:post:: /commit - Create a new image from a container's changes + Create a new image from a container's changes - **Example request**: + **Example request**: - .. sourcecode:: http + .. sourcecode:: http - POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + Content-Type: application/json + + { + "Cmd": ["cat", "/world"], + "PortSpecs":["22"] + } - **Example response**: + **Example response**: - .. sourcecode:: http + .. sourcecode:: http - HTTP/1.1 201 OK + HTTP/1.1 201 OK Content-Type: application/vnd.docker.raw-stream - {"Id":"596069db4bf5"} + {"Id":"596069db4bf5"} - :query container: source container - :query repo: repository - :query tag: tag - :query m: commit message - :query author: author (eg. "John Hannibal Smith ") - :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) - :statuscode 201: no error - :statuscode 404: no such container - :statuscode 500: server error + :query container: source container + :query repo: repository + :query tag: tag + :query m: commit message + :query author: author (eg. "John Hannibal Smith ") + :statuscode 201: no error + :statuscode 404: no such container + :statuscode 500: server error Monitor Docker's events diff --git a/docs/sources/api/docker_remote_api_v1.4.rst b/docs/sources/api/docker_remote_api_v1.4.rst index b373fe9d3a..b49bbc4f54 100644 --- a/docs/sources/api/docker_remote_api_v1.4.rst +++ b/docs/sources/api/docker_remote_api_v1.4.rst @@ -1084,23 +1084,28 @@ Create a new image from a container's changes .. sourcecode:: http - POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + Content-Type: application/json + + { + "Cmd": ["cat", "/world"], + "PortSpecs":["22"] + } **Example response**: .. sourcecode:: http - HTTP/1.1 201 OK - Content-Type: application/vnd.docker.raw-stream + HTTP/1.1 201 OK + Content-Type: application/vnd.docker.raw-stream - {"Id":"596069db4bf5"} + {"Id":"596069db4bf5"} :query container: source container :query repo: repository :query tag: tag :query m: commit message :query author: author (eg. "John Hannibal Smith ") - :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) :statuscode 201: no error :statuscode 404: no such container :statuscode 500: server error diff --git a/docs/sources/api/docker_remote_api_v1.5.rst b/docs/sources/api/docker_remote_api_v1.5.rst index 9c8fa8c2af..8cd501e2a1 100644 --- a/docs/sources/api/docker_remote_api_v1.5.rst +++ b/docs/sources/api/docker_remote_api_v1.5.rst @@ -1050,32 +1050,37 @@ Create a new image from a container's changes .. http:post:: /commit - Create a new image from a container's changes + Create a new image from a container's changes - **Example request**: + **Example request**: - .. sourcecode:: http + .. sourcecode:: http - POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + Content-Type: application/json + + { + "Cmd": ["cat", "/world"], + "PortSpecs":["22"] + } - **Example response**: + **Example response**: - .. sourcecode:: http + .. sourcecode:: http - HTTP/1.1 201 OK - Content-Type: application/vnd.docker.raw-stream + HTTP/1.1 201 OK + Content-Type: application/vnd.docker.raw-stream - {"Id":"596069db4bf5"} + {"Id":"596069db4bf5"} - :query container: source container - :query repo: repository - :query tag: tag - :query m: commit message - :query author: author (eg. "John Hannibal Smith ") - :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) - :statuscode 201: no error - :statuscode 404: no such container - :statuscode 500: server error + :query container: source container + :query repo: repository + :query tag: tag + :query m: commit message + :query author: author (eg. "John Hannibal Smith ") + :statuscode 201: no error + :statuscode 404: no such container + :statuscode 500: server error Monitor Docker's events *********************** diff --git a/docs/sources/api/docker_remote_api_v1.6.rst b/docs/sources/api/docker_remote_api_v1.6.rst index d84d5e85d0..703f7345d4 100644 --- a/docs/sources/api/docker_remote_api_v1.6.rst +++ b/docs/sources/api/docker_remote_api_v1.6.rst @@ -13,9 +13,12 @@ Docker Remote API v1.6 1. Brief introduction ===================== -- The Remote API is replacing rcli -- Default port in the docker daemon is 4243 -- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr +- The Remote API has replaced rcli +- The daemon listens on ``unix:///var/run/docker.sock``, but you can + :ref:`bind_docker`. +- The API tends to be REST, but for some complex commands, like + ``attach`` or ``pull``, the HTTP connection is hijacked to transport + ``stdout, stdin`` and ``stderr`` 2. Endpoints ============ @@ -148,6 +151,7 @@ Create a container } :jsonparam config: the container's configuration + :query name: container name to use :statuscode 201: no error :statuscode 404: no such container :statuscode 406: impossible to attach (container not running) @@ -442,7 +446,8 @@ Kill a container .. sourcecode:: http HTTP/1.1 204 OK - + + :query signal: Signal to send to the container (integer). When not set, SIGKILL is assumed and the call will waits for the container to exit. :statuscode 204: no error :statuscode 404: no such container :statuscode 500: server error @@ -1131,7 +1136,13 @@ Create a new image from a container's changes .. sourcecode:: http - POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + Content-Type: application/json + + { + "Cmd": ["cat", "/world"], + "PortSpecs":["22"] + } **Example response**: @@ -1147,7 +1158,6 @@ Create a new image from a container's changes :query tag: tag :query m: commit message :query author: author (eg. "John Hannibal Smith ") - :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) :statuscode 201: no error :statuscode 404: no such container :statuscode 500: server error diff --git a/docs/sources/api/index.rst b/docs/sources/api/index.rst index d7345e8746..017369143c 100644 --- a/docs/sources/api/index.rst +++ b/docs/sources/api/index.rst @@ -14,5 +14,5 @@ Your programs and scripts can access Docker's functionality via these interfaces registry_api index_api docker_remote_api - + remote_api_client_libraries diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index 62dd3707a1..6efa9b1ebb 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -4,11 +4,8 @@ .. _cli: -Overview -====================== - -Docker Usage -~~~~~~~~~~~~~~~~~~ +Command Line Help +----------------- To list available commands, either run ``docker`` with no parameters or execute ``docker help``:: @@ -21,71 +18,738 @@ To list available commands, either run ``docker`` with no parameters or execute ... +.. _cli_attach: + +``attach`` +---------- + +:: + + Usage: docker attach CONTAINER + + Attach to a running container. + + -nostdin=false: Do not attach stdin + -sig-proxy=true: Proxify all received signal to the process (even in non-tty mode) + +You can detach from the container again (and leave it running) with +``CTRL-c`` (for a quiet exit) or ``CTRL-\`` to get a stacktrace of +the Docker client when it quits. + +To stop a container, use ``docker stop`` + +To kill the container, use ``docker kill`` + +.. _cli_attach_examples: + +Examples: +~~~~~~~~~ + +.. code-block:: bash + + $ ID=$(sudo docker run -d ubuntu /usr/bin/top -b) + $ sudo docker attach $ID + top - 02:05:52 up 3:05, 0 users, load average: 0.01, 0.02, 0.05 + Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie + Cpu(s): 0.1%us, 0.2%sy, 0.0%ni, 99.7%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st + Mem: 373572k total, 355560k used, 18012k free, 27872k buffers + Swap: 786428k total, 0k used, 786428k free, 221740k cached + + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND + 1 root 20 0 17200 1116 912 R 0 0.3 0:00.03 top + + top - 02:05:55 up 3:05, 0 users, load average: 0.01, 0.02, 0.05 + Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie + Cpu(s): 0.0%us, 0.2%sy, 0.0%ni, 99.8%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st + Mem: 373572k total, 355244k used, 18328k free, 27872k buffers + Swap: 786428k total, 0k used, 786428k free, 221776k cached + + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND + 1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top -Available Commands -~~~~~~~~~~~~~~~~~~ + top - 02:05:58 up 3:06, 0 users, load average: 0.01, 0.02, 0.05 + Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie + Cpu(s): 0.2%us, 0.3%sy, 0.0%ni, 99.5%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st + Mem: 373572k total, 355780k used, 17792k free, 27880k buffers + Swap: 786428k total, 0k used, 786428k free, 221776k cached -.. include:: command/attach.rst + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND + 1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top + ^C$ + $ sudo docker stop $ID -.. include:: command/build.rst +.. _cli_build: -.. include:: command/commit.rst +``build`` +--------- -.. include:: command/cp.rst +:: -.. include:: command/diff.rst + Usage: docker build [OPTIONS] PATH | URL | - + Build a new container image from the source code at PATH + -t="": Repository name (and optionally a tag) to be applied to the resulting image in case of success. + -q=false: Suppress verbose build output. + -no-cache: Do not use the cache when building the image. + -rm: Remove intermediate containers after a successful build + When a single Dockerfile is given as URL, then no context is set. When a git repository is set as URL, the repository is used as context -.. include:: command/events.rst +.. _cli_build_examples: -.. include:: command/export.rst +Examples: +~~~~~~~~~ -.. include:: command/history.rst +.. code-block:: bash -.. include:: command/images.rst + sudo docker build . -.. include:: command/import.rst +This will read the ``Dockerfile`` from the current directory. It will +also send any other files and directories found in the current +directory to the ``docker`` daemon. -.. include:: command/info.rst +The contents of this directory would be used by ``ADD`` commands found +within the ``Dockerfile``. This will send a lot of data to the +``docker`` daemon if the current directory contains a lot of data. If +the absolute path is provided instead of ``.`` then only the files and +directories required by the ADD commands from the ``Dockerfile`` will be +added to the context and transferred to the ``docker`` daemon. -.. include:: command/insert.rst +.. code-block:: bash -.. include:: command/inspect.rst + sudo docker build -t vieux/apache:2.0 . + +This will build like the previous example, but it will then tag the +resulting image. The repository name will be ``vieux/apache`` and the +tag will be ``2.0`` + + +.. code-block:: bash + + sudo docker build - < Dockerfile + +This will read a ``Dockerfile`` from *stdin* without context. Due to +the lack of a context, no contents of any local directory will be sent +to the ``docker`` daemon. ``ADD`` doesn't work when running in this +mode because the absence of the context provides no source files to +copy to the container. + + +.. code-block:: bash + + sudo docker build github.com/creack/docker-firefox + +This will clone the Github repository and use it as context. The +``Dockerfile`` at the root of the repository is used as +``Dockerfile``. Note that you can specify an arbitrary git repository +by using the ``git://`` schema. + + +.. _cli_commit: + +``commit`` +---------- + +:: + + Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY [TAG]] + + Create a new image from a container's changes + + -m="": Commit message + -author="": Author (eg. "John Hannibal Smith " + -run="": Configuration to be applied when the image is launched with `docker run`. + (ex: '{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}') + +Full -run example (multiline is ok within a single quote ``'``) + +:: + + $ sudo docker commit -run=' + { + "Entrypoint" : null, + "Privileged" : false, + "User" : "", + "VolumesFrom" : "", + "Cmd" : ["cat", "-e", "/etc/resolv.conf"], + "Dns" : ["8.8.8.8", "8.8.4.4"], + "MemorySwap" : 0, + "AttachStdin" : false, + "AttachStderr" : false, + "CpuShares" : 0, + "OpenStdin" : false, + "Volumes" : null, + "Hostname" : "122612f45831", + "PortSpecs" : ["22", "80", "443"], + "Image" : "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", + "Tty" : false, + "Env" : [ + "HOME=/", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "StdinOnce" : false, + "Domainname" : "", + "WorkingDir" : "/", + "NetworkDisabled" : false, + "Memory" : 0, + "AttachStdout" : false + }' $CONTAINER_ID + +.. _cli_cp: + +``cp`` +------ + +:: + + Usage: docker cp CONTAINER:RESOURCE HOSTPATH + + Copy files/folders from the containers filesystem to the host + path. Paths are relative to the root of the filesystem. + +.. _cli_diff: + +``diff`` +-------- + +:: + + Usage: docker diff CONTAINER [OPTIONS] + + Inspect changes on a container's filesystem + +.. _cli_events: + +``events`` +---------- + +:: + + Usage: docker events + + Get real time events from the server + +.. _cli_events_example: + +Examples +~~~~~~~~ + +You'll need two shells for this example. + +Shell 1: Listening for events +............................. + +.. code-block:: bash + + $ sudo docker events + +Shell 2: Start and Stop a Container +................................... + +.. code-block:: bash + + $ sudo docker start 4386fb97867d + $ sudo docker stop 4386fb97867d + +Shell 1: (Again .. now showing events) +...................................... + +.. code-block:: bash + + [2013-09-03 15:49:26 +0200 CEST] 4386fb97867d: (from 12de384bfb10) start + [2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) die + [2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) stop + + +.. _cli_export: + +``export`` +---------- + +:: + + Usage: docker export CONTAINER + + Export the contents of a filesystem as a tar archive + +.. _cli_history: + +``history`` +----------- + +:: + + Usage: docker history [OPTIONS] IMAGE + + Show the history of an image + +.. _cli_images: + +``images`` +---------- + +:: + + Usage: docker images [OPTIONS] [NAME] + + List images + + -a=false: show all images + -q=false: only show numeric IDs + -viz=false: output in graphviz format + +Displaying images visually +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + sudo docker images -viz | dot -Tpng -o docker.png + +.. image:: docker_images.gif + :alt: Example inheritance graph of Docker images. + +.. _cli_import: + +``import`` +---------- + +:: + + Usage: docker import URL|- [REPOSITORY [TAG]] + + Create a new filesystem image from the contents of a tarball + +At this time, the URL must start with ``http`` and point to a single +file archive (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) containing a +root filesystem. If you would like to import from a local directory or +archive, you can use the ``-`` parameter to take the data from +standard in. + +Examples +~~~~~~~~ + +Import from a remote location +............................. + +``$ sudo docker import http://example.com/exampleimage.tgz exampleimagerepo`` + +Import from a local file +........................ + +Import to docker via pipe and standard in + +``$ cat exampleimage.tgz | sudo docker import - exampleimagelocal`` + +Import from a local directory +............................. + +``$ sudo tar -c . | docker import - exampleimagedir`` + +Note the ``sudo`` in this example -- you must preserve the ownership +of the files (especially root ownership) during the archiving with +tar. If you are not root (or sudo) when you tar, then the ownerships +might not get preserved. + +.. _cli_info: + +``info`` +-------- + +:: + + Usage: docker info + + Display system-wide information. + +.. _cli_insert: + +``insert`` +---------- + +:: + + Usage: docker insert IMAGE URL PATH + + Insert a file from URL in the IMAGE at PATH + +Examples +~~~~~~~~ + +Insert file from github +....................... + +.. code-block:: bash + + $ sudo docker insert 8283e18b24bc https://raw.github.com/metalivedev/django/master/postinstall /tmp/postinstall.sh + +.. _cli_inspect: + +``inspect`` +----------- + +:: + + Usage: docker inspect [OPTIONS] CONTAINER + + Return low-level information on a container + +.. _cli_kill: + +``kill`` +-------- + +:: + + Usage: docker kill CONTAINER [CONTAINER...] + + Kill a running container + +.. _cli_login: + +``login`` +--------- + +:: + + Usage: docker login [OPTIONS] [SERVER] + + Register or Login to the docker registry server + + -e="": email + -p="": password + -u="": username + + If you want to login to a private registry you can + specify this by adding the server name. + + example: + docker login localhost:8080 + + +.. _cli_logs: + +``logs`` +-------- + +:: + + Usage: docker logs [OPTIONS] CONTAINER + + Fetch the logs of a container + + +.. _cli_port: + +``port`` +-------- + +:: + + Usage: docker port [OPTIONS] CONTAINER PRIVATE_PORT + + Lookup the public-facing port which is NAT-ed to PRIVATE_PORT + + +.. _cli_ps: + +``ps`` +------ + +:: + + Usage: docker ps [OPTIONS] + + List containers + + -a=false: Show all containers. Only running containers are shown by default. + -notrunc=false: Don't truncate output + -q=false: Only display numeric IDs + +.. _cli_pull: + +``pull`` +-------- + +:: + + Usage: docker pull NAME + + Pull an image or a repository from the registry + + +.. _cli_push: + +``push`` +-------- + +:: + + Usage: docker push NAME + + Push an image or a repository to the registry + + +.. _cli_restart: + +``restart`` +----------- + +:: + + Usage: docker restart [OPTIONS] NAME + + Restart a running container + +.. _cli_rm: + +``rm`` +------ + +:: + + Usage: docker rm [OPTIONS] CONTAINER + + Remove one or more containers + -link="": Remove the link instead of the actual container + + +Examples: +~~~~~~~~~ + +.. code-block:: bash + + $ docker rm /redis + /redis + + +This will remove the container referenced under the link ``/redis``. + + +.. code-block:: bash + + $ docker rm -link /webapp/redis + /webapp/redis + + +This will remove the underlying link between ``/webapp`` and the ``/redis`` containers removing all +network communication. + +.. _cli_rmi: + +``rmi`` +------- + +:: + + Usage: docker rmi IMAGE [IMAGE...] + + Remove one or more images + +.. _cli_run: + +``run`` +------- + +:: + + Usage: docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...] + + Run a command in a new container + + -a=map[]: Attach to stdin, stdout or stderr + -c=0: CPU shares (relative weight) + -cidfile="": Write the container ID to the file + -d=false: Detached mode: Run container in the background, print new container id + -e=[]: Set environment variables + -h="": Container host name + -i=false: Keep stdin open even if not attached + -privileged=false: Give extended privileges to this container + -m=0: Memory limit (in bytes) + -n=true: Enable networking for this container + -p=[]: Map a network port to the container + -rm=false: Automatically remove the container when it exits (incompatible with -d) + -t=false: Allocate a pseudo-tty + -u="": Username or UID + -dns=[]: Set custom dns servers for the container + -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "container-dir" is missing, then docker creates a new volume. + -volumes-from="": Mount all volumes from the given container + -entrypoint="": Overwrite the default entrypoint set by the image + -w="": Working directory inside the container + -lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1" + -sig-proxy=true: Proxify all received signal to the process (even in non-tty mode) + -expose=[]: Expose a port from the container without publishing it to your host + -link="": Add link to another container (name:alias) + -name="": Assign the specified name to the container. If no name is specific docker will generate a random name + +Examples +-------- + +.. code-block:: bash + + sudo docker run -cidfile /tmp/docker_test.cid ubuntu echo "test" + +This will create a container and print "test" to the console. The +``cidfile`` flag makes docker attempt to create a new file and write the +container ID to it. If the file exists already, docker will return an +error. Docker will close this file when docker run exits. + +.. code-block:: bash + + docker run mount -t tmpfs none /var/spool/squid + +This will *not* work, because by default, most potentially dangerous +kernel capabilities are dropped; including ``cap_sys_admin`` (which is +required to mount filesystems). However, the ``-privileged`` flag will +allow it to run: + +.. code-block:: bash + + docker run -privileged mount -t tmpfs none /var/spool/squid + +The ``-privileged`` flag gives *all* capabilities to the container, +and it also lifts all the limitations enforced by the ``device`` +cgroup controller. In other words, the container can then do almost +everything that the host can do. This flag exists to allow special +use-cases, like running Docker within Docker. + +.. code-block:: bash + + docker run -w /path/to/dir/ -i -t ubuntu pwd + +The ``-w`` lets the command being executed inside directory given, +here /path/to/dir/. If the path does not exists it is created inside the +container. + +.. code-block:: bash + + docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd + +The ``-v`` flag mounts the current working directory into the container. +The ``-w`` lets the command being executed inside the current +working directory, by changing into the directory to the value +returned by ``pwd``. So this combination executes the command +using the container, but inside the current working directory. + +.. code-block:: bash + + docker run -p 127.0.0.0::80 ubuntu bash + +This the ``-p`` flag now allows you to bind a port to a specific +interface of the host machine. In this example port ``80`` of the +container will have a dynamically allocated port bound to 127.0.0.1 +of the host. + +.. code-block:: bash + + docker run -p 127.0.0.1:80:80 ubuntu bash + +This will bind port ``80`` of the container to port ``80`` on 127.0.0.1 of your +host machine. + +.. code-block:: bash + + docker run -expose 80 ubuntu bash + +This will expose port ``80`` of the container for use within a link +without publishing the port to the host system's interfaces. + +.. code-block:: bash + + docker run -name console -t -i ubuntu bash + +This will create and run a new container with the container name +being ``console``. + +.. code-block:: bash + + docker run -link /redis:redis -name console ubuntu bash + +The ``-link`` flag will link the container named ``/redis`` into the +newly created container with the alias ``redis``. The new container +can access the network and environment of the redis container via +environment variables. The ``-name`` flag will assign the name ``console`` +to the newly created container. + +.. _cli_search: + +``search`` +---------- + +:: + + Usage: docker search TERM + + Searches for the TERM parameter on the Docker index and prints out + a list of repositories that match. + +.. _cli_start: + +``start`` +--------- + +:: + + Usage: docker start [OPTIONS] NAME + + Start a stopped container + + -a=false: Attach container's stdout/stderr and forward all signals to the process + -i=false: Attach container's stdin + +.. _cli_stop: + +``stop`` +-------- + +:: + + Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...] + + Stop a running container + + -t=10: Number of seconds to wait for the container to stop before killing it. + +.. _cli_tag: + +``tag`` +------- + +:: + + Usage: docker tag [OPTIONS] IMAGE REPOSITORY [TAG] + + Tag an image into a repository + + -f=false: Force + +.. _cli_top: + +``top`` +------- + +:: + + Usage: docker top CONTAINER + + Lookup the running processes of a container + +.. _cli_version: + +``version`` +----------- + +Show the version of the docker client, daemon, and latest released version. + + +.. _cli_wait: + +``wait`` +-------- + +:: + + Usage: docker wait [OPTIONS] NAME + + Block until a container stops, then print its exit code. -.. include:: command/kill.rst - -.. include:: command/login.rst - -.. include:: command/logs.rst - -.. include:: command/port.rst - -.. include:: command/ps.rst - -.. include:: command/pull.rst - -.. include:: command/push.rst - -.. include:: command/restart.rst - -.. include:: command/rm.rst - -.. include:: command/rmi.rst - -.. include:: command/run.rst - -.. include:: command/search.rst - -.. include:: command/start.rst - -.. include:: command/stop.rst - -.. include:: command/tag.rst - -.. include:: command/top.rst - -.. include:: command/version.rst - -.. include:: command/wait.rst diff --git a/docs/sources/commandline/command/attach.rst b/docs/sources/commandline/command/attach.rst deleted file mode 100644 index 12ed802a02..0000000000 --- a/docs/sources/commandline/command/attach.rst +++ /dev/null @@ -1,59 +0,0 @@ -:title: Attach Command -:description: Attach to a running container -:keywords: attach, container, docker, documentation - -=========================================== -``attach`` -- Attach to a running container -=========================================== - -:: - - Usage: docker attach CONTAINER - - Attach to a running container. - -You can detach from the container again (and leave it running) with -``CTRL-c`` (for a quiet exit) or ``CTRL-\`` to get a stacktrace of -the Docker client when it quits. - -To stop a container, use ``docker stop`` - -To kill the container, use ``docker kill`` - -Examples: ---------- - -.. code-block:: bash - - $ ID=$(sudo docker run -d ubuntu /usr/bin/top -b) - $ sudo docker attach $ID - top - 02:05:52 up 3:05, 0 users, load average: 0.01, 0.02, 0.05 - Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie - Cpu(s): 0.1%us, 0.2%sy, 0.0%ni, 99.7%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st - Mem: 373572k total, 355560k used, 18012k free, 27872k buffers - Swap: 786428k total, 0k used, 786428k free, 221740k cached - - PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND - 1 root 20 0 17200 1116 912 R 0 0.3 0:00.03 top - - top - 02:05:55 up 3:05, 0 users, load average: 0.01, 0.02, 0.05 - Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie - Cpu(s): 0.0%us, 0.2%sy, 0.0%ni, 99.8%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st - Mem: 373572k total, 355244k used, 18328k free, 27872k buffers - Swap: 786428k total, 0k used, 786428k free, 221776k cached - - PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND - 1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top - - - top - 02:05:58 up 3:06, 0 users, load average: 0.01, 0.02, 0.05 - Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie - Cpu(s): 0.2%us, 0.3%sy, 0.0%ni, 99.5%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st - Mem: 373572k total, 355780k used, 17792k free, 27880k buffers - Swap: 786428k total, 0k used, 786428k free, 221776k cached - - PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND - 1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top - ^C$ - $ sudo docker stop $ID - diff --git a/docs/sources/commandline/command/build.rst b/docs/sources/commandline/command/build.rst deleted file mode 100644 index 5ce70d85d9..0000000000 --- a/docs/sources/commandline/command/build.rst +++ /dev/null @@ -1,65 +0,0 @@ -:title: Build Command -:description: Build a new image from the Dockerfile passed via stdin -:keywords: build, docker, container, documentation - -================================================ -``build`` -- Build a container from a Dockerfile -================================================ - -:: - - Usage: docker build [OPTIONS] PATH | URL | - - Build a new container image from the source code at PATH - -t="": Repository name (and optionally a tag) to be applied to the resulting image in case of success. - -q=false: Suppress verbose build output. - -no-cache: Do not use the cache when building the image. - -rm: Remove intermediate containers after a successful build - When a single Dockerfile is given as URL, then no context is set. When a git repository is set as URL, the repository is used as context - - -Examples --------- - -.. code-block:: bash - - sudo docker build . - -This will read the ``Dockerfile`` from the current directory. It will -also send any other files and directories found in the current -directory to the ``docker`` daemon. - -The contents of this directory would be used by ``ADD`` commands found -within the ``Dockerfile``. This will send a lot of data to the -``docker`` daemon if the current directory contains a lot of data. If -the absolute path is provided instead of ``.`` then only the files and -directories required by the ADD commands from the ``Dockerfile`` will be -added to the context and transferred to the ``docker`` daemon. - -.. code-block:: bash - - sudo docker build -t vieux/apache:2.0 . - -This will build like the previous example, but it will then tag the -resulting image. The repository name will be ``vieux/apache`` and the -tag will be ``2.0`` - - -.. code-block:: bash - - sudo docker build - < Dockerfile - -This will read a ``Dockerfile`` from *stdin* without context. Due to -the lack of a context, no contents of any local directory will be sent -to the ``docker`` daemon. ``ADD`` doesn't work when running in this -mode because the absence of the context provides no source files to -copy to the container. - - -.. code-block:: bash - - sudo docker build github.com/creack/docker-firefox - -This will clone the Github repository and use it as context. The -``Dockerfile`` at the root of the repository is used as -``Dockerfile``. Note that you can specify an arbitrary git repository -by using the ``git://`` schema. diff --git a/docs/sources/commandline/command/commit.rst b/docs/sources/commandline/command/commit.rst deleted file mode 100644 index ef0d863688..0000000000 --- a/docs/sources/commandline/command/commit.rst +++ /dev/null @@ -1,52 +0,0 @@ -:title: Commit Command -:description: Create a new image from a container's changes -:keywords: commit, docker, container, documentation - -=========================================================== -``commit`` -- Create a new image from a container's changes -=========================================================== - -:: - - Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY [TAG]] - - Create a new image from a container's changes - - -m="": Commit message - -author="": Author (eg. "John Hannibal Smith " - -run="": Configuration to be applied when the image is launched with `docker run`. - (ex: '{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}') - -Full -run example (multiline is ok within a single quote ``'``) - -:: - - $ sudo docker commit -run=' - { - "Entrypoint" : null, - "Privileged" : false, - "User" : "", - "VolumesFrom" : "", - "Cmd" : ["cat", "-e", "/etc/resolv.conf"], - "Dns" : ["8.8.8.8", "8.8.4.4"], - "MemorySwap" : 0, - "AttachStdin" : false, - "AttachStderr" : false, - "CpuShares" : 0, - "OpenStdin" : false, - "Volumes" : null, - "Hostname" : "122612f45831", - "PortSpecs" : ["22", "80", "443"], - "Image" : "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", - "Tty" : false, - "Env" : [ - "HOME=/", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" - ], - "StdinOnce" : false, - "Domainname" : "", - "WorkingDir" : "/", - "NetworkDisabled" : false, - "Memory" : 0, - "AttachStdout" : false - }' $CONTAINER_ID diff --git a/docs/sources/commandline/command/cp.rst b/docs/sources/commandline/command/cp.rst deleted file mode 100644 index ea84fa1f90..0000000000 --- a/docs/sources/commandline/command/cp.rst +++ /dev/null @@ -1,14 +0,0 @@ -:title: Cp Command -:description: Copy files/folders from the containers filesystem to the host path -:keywords: cp, docker, container, documentation, copy - -============================================================================ -``cp`` -- Copy files/folders from the containers filesystem to the host path -============================================================================ - -:: - - Usage: docker cp CONTAINER:RESOURCE HOSTPATH - - Copy files/folders from the containers filesystem to the host - path. Paths are relative to the root of the filesystem. diff --git a/docs/sources/commandline/command/diff.rst b/docs/sources/commandline/command/diff.rst deleted file mode 100644 index 2901a7f215..0000000000 --- a/docs/sources/commandline/command/diff.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Diff Command -:description: Inspect changes on a container's filesystem -:keywords: diff, docker, container, documentation - -======================================================= -``diff`` -- Inspect changes on a container's filesystem -======================================================= - -:: - - Usage: docker diff CONTAINER [OPTIONS] - - Inspect changes on a container's filesystem diff --git a/docs/sources/commandline/command/events.rst b/docs/sources/commandline/command/events.rst deleted file mode 100644 index b8dd591fb1..0000000000 --- a/docs/sources/commandline/command/events.rst +++ /dev/null @@ -1,34 +0,0 @@ -:title: Events Command -:description: Get real time events from the server -:keywords: events, docker, documentation - -================================================================= -``events`` -- Get real time events from the server -================================================================= - -:: - - Usage: docker events - - Get real time events from the server - -Examples --------- - -Starting and stopping a container -................................. - -.. code-block:: bash - - $ sudo docker start 4386fb97867d - $ sudo docker stop 4386fb97867d - -In another shell - -.. code-block:: bash - - $ sudo docker events - [2013-09-03 15:49:26 +0200 CEST] 4386fb97867d: (from 12de384bfb10) start - [2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) die - [2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) stop - diff --git a/docs/sources/commandline/command/export.rst b/docs/sources/commandline/command/export.rst deleted file mode 100644 index 9d7e6b369c..0000000000 --- a/docs/sources/commandline/command/export.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Export Command -:description: Export the contents of a filesystem as a tar archive -:keywords: export, docker, container, documentation - -================================================================= -``export`` -- Stream the contents of a container as a tar archive -================================================================= - -:: - - Usage: docker export CONTAINER - - Export the contents of a filesystem as a tar archive diff --git a/docs/sources/commandline/command/history.rst b/docs/sources/commandline/command/history.rst deleted file mode 100644 index 2f9d3f2814..0000000000 --- a/docs/sources/commandline/command/history.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: History Command -:description: Show the history of an image -:keywords: history, docker, container, documentation - -=========================================== -``history`` -- Show the history of an image -=========================================== - -:: - - Usage: docker history [OPTIONS] IMAGE - - Show the history of an image diff --git a/docs/sources/commandline/command/images.rst b/docs/sources/commandline/command/images.rst deleted file mode 100644 index 279bf240c9..0000000000 --- a/docs/sources/commandline/command/images.rst +++ /dev/null @@ -1,26 +0,0 @@ -:title: Images Command -:description: List images -:keywords: images, docker, container, documentation - -========================= -``images`` -- List images -========================= - -:: - - Usage: docker images [OPTIONS] [NAME] - - List images - - -a=false: show all images - -q=false: only show numeric IDs - -viz=false: output in graphviz format - -Displaying images visually --------------------------- - -:: - - sudo docker images -viz | dot -Tpng -o docker.png - -.. image:: https://docs.docker.io/en/latest/_static/docker_images.gif diff --git a/docs/sources/commandline/command/import.rst b/docs/sources/commandline/command/import.rst deleted file mode 100644 index e236c5bc2b..0000000000 --- a/docs/sources/commandline/command/import.rst +++ /dev/null @@ -1,44 +0,0 @@ -:title: Import Command -:description: Create a new filesystem image from the contents of a tarball -:keywords: import, tarball, docker, url, documentation - -========================================================================== -``import`` -- Create a new filesystem image from the contents of a tarball -========================================================================== - -:: - - Usage: docker import URL|- [REPOSITORY [TAG]] - - Create a new filesystem image from the contents of a tarball - -At this time, the URL must start with ``http`` and point to a single -file archive (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) containing a -root filesystem. If you would like to import from a local directory or -archive, you can use the ``-`` parameter to take the data from -standard in. - -Examples --------- - -Import from a remote location -............................. - -``$ sudo docker import http://example.com/exampleimage.tgz exampleimagerepo`` - -Import from a local file -........................ - -Import to docker via pipe and standard in - -``$ cat exampleimage.tgz | sudo docker import - exampleimagelocal`` - -Import from a local directory -............................. - -``$ sudo tar -c . | docker import - exampleimagedir`` - -Note the ``sudo`` in this example -- you must preserve the ownership -of the files (especially root ownership) during the archiving with -tar. If you are not root (or sudo) when you tar, then the ownerships -might not get preserved. diff --git a/docs/sources/commandline/command/info.rst b/docs/sources/commandline/command/info.rst deleted file mode 100644 index 6df3486c53..0000000000 --- a/docs/sources/commandline/command/info.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Info Command -:description: Display system-wide information. -:keywords: info, docker, information, documentation - -=========================================== -``info`` -- Display system-wide information -=========================================== - -:: - - Usage: docker info - - Display system-wide information. diff --git a/docs/sources/commandline/command/insert.rst b/docs/sources/commandline/command/insert.rst deleted file mode 100644 index 0f2612c9af..0000000000 --- a/docs/sources/commandline/command/insert.rst +++ /dev/null @@ -1,23 +0,0 @@ -:title: Insert Command -:description: Insert a file in an image -:keywords: insert, image, docker, documentation - -========================================================================== -``insert`` -- Insert a file in an image -========================================================================== - -:: - - Usage: docker insert IMAGE URL PATH - - Insert a file from URL in the IMAGE at PATH - -Examples --------- - -Insert file from github -....................... - -.. code-block:: bash - - $ sudo docker insert 8283e18b24bc https://raw.github.com/metalivedev/django/master/postinstall /tmp/postinstall.sh diff --git a/docs/sources/commandline/command/inspect.rst b/docs/sources/commandline/command/inspect.rst deleted file mode 100644 index 90dbe959eb..0000000000 --- a/docs/sources/commandline/command/inspect.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Inspect Command -:description: Return low-level information on a container -:keywords: inspect, container, docker, documentation - -========================================================== -``inspect`` -- Return low-level information on a container -========================================================== - -:: - - Usage: docker inspect [OPTIONS] CONTAINER - - Return low-level information on a container diff --git a/docs/sources/commandline/command/kill.rst b/docs/sources/commandline/command/kill.rst deleted file mode 100644 index f53d3883b0..0000000000 --- a/docs/sources/commandline/command/kill.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Kill Command -:description: Kill a running container -:keywords: kill, container, docker, documentation - -==================================== -``kill`` -- Kill a running container -==================================== - -:: - - Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...] - - Kill a running container diff --git a/docs/sources/commandline/command/login.rst b/docs/sources/commandline/command/login.rst deleted file mode 100644 index 46f354d6be..0000000000 --- a/docs/sources/commandline/command/login.rst +++ /dev/null @@ -1,24 +0,0 @@ -:title: Login Command -:description: Register or Login to the docker registry server -:keywords: login, docker, documentation - -============================================================ -``login`` -- Register or Login to the docker registry server -============================================================ - -:: - - Usage: docker login [OPTIONS] [SERVER] - - Register or Login to the docker registry server - - -e="": email - -p="": password - -u="": username - - If you want to login to a private registry you can - specify this by adding the server name. - - example: - docker login localhost:8080 - diff --git a/docs/sources/commandline/command/logs.rst b/docs/sources/commandline/command/logs.rst deleted file mode 100644 index a3423f6e0c..0000000000 --- a/docs/sources/commandline/command/logs.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Logs Command -:description: Fetch the logs of a container -:keywords: logs, container, docker, documentation - -========================================= -``logs`` -- Fetch the logs of a container -========================================= - -:: - - Usage: docker logs [OPTIONS] CONTAINER - - Fetch the logs of a container diff --git a/docs/sources/commandline/command/port.rst b/docs/sources/commandline/command/port.rst deleted file mode 100644 index 8d59fedab6..0000000000 --- a/docs/sources/commandline/command/port.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Port Command -:description: Lookup the public-facing port which is NAT-ed to PRIVATE_PORT -:keywords: port, docker, container, documentation - -========================================================================= -``port`` -- Lookup the public-facing port which is NAT-ed to PRIVATE_PORT -========================================================================= - -:: - - Usage: docker port [OPTIONS] CONTAINER PRIVATE_PORT - - Lookup the public-facing port which is NAT-ed to PRIVATE_PORT diff --git a/docs/sources/commandline/command/ps.rst b/docs/sources/commandline/command/ps.rst deleted file mode 100644 index 597dbd9ae7..0000000000 --- a/docs/sources/commandline/command/ps.rst +++ /dev/null @@ -1,17 +0,0 @@ -:title: Ps Command -:description: List containers -:keywords: ps, docker, documentation, container - -========================= -``ps`` -- List containers -========================= - -:: - - Usage: docker ps [OPTIONS] - - List containers - - -a=false: Show all containers. Only running containers are shown by default. - -notrunc=false: Don't truncate output - -q=false: Only display numeric IDs diff --git a/docs/sources/commandline/command/pull.rst b/docs/sources/commandline/command/pull.rst deleted file mode 100644 index 4348f28d0f..0000000000 --- a/docs/sources/commandline/command/pull.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Pull Command -:description: Pull an image or a repository from the registry -:keywords: pull, image, repo, repository, documentation, docker - -========================================================================= -``pull`` -- Pull an image or a repository from the docker registry server -========================================================================= - -:: - - Usage: docker pull NAME - - Pull an image or a repository from the registry diff --git a/docs/sources/commandline/command/push.rst b/docs/sources/commandline/command/push.rst deleted file mode 100644 index 9304f9acc5..0000000000 --- a/docs/sources/commandline/command/push.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Push Command -:description: Push an image or a repository to the registry -:keywords: push, docker, image, repository, documentation, repo - -======================================================================= -``push`` -- Push an image or a repository to the docker registry server -======================================================================= - -:: - - Usage: docker push NAME - - Push an image or a repository to the registry diff --git a/docs/sources/commandline/command/restart.rst b/docs/sources/commandline/command/restart.rst deleted file mode 100644 index dfc0dfea6e..0000000000 --- a/docs/sources/commandline/command/restart.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Restart Command -:description: Restart a running container -:keywords: restart, container, docker, documentation - -========================================== -``restart`` -- Restart a running container -========================================== - -:: - - Usage: docker restart [OPTIONS] NAME - - Restart a running container diff --git a/docs/sources/commandline/command/rm.rst b/docs/sources/commandline/command/rm.rst deleted file mode 100644 index 8a2309ce79..0000000000 --- a/docs/sources/commandline/command/rm.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Rm Command -:description: Remove a container -:keywords: remove, container, docker, documentation, rm - -============================ -``rm`` -- Remove a container -============================ - -:: - - Usage: docker rm [OPTIONS] CONTAINER - - Remove one or more containers diff --git a/docs/sources/commandline/command/rmi.rst b/docs/sources/commandline/command/rmi.rst deleted file mode 100644 index 954e5222c6..0000000000 --- a/docs/sources/commandline/command/rmi.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Rmi Command -:description: Remove an image -:keywords: rmi, remove, image, docker, documentation - -========================== -``rmi`` -- Remove an image -========================== - -:: - - Usage: docker rmi IMAGE [IMAGE...] - - Remove one or more images diff --git a/docs/sources/commandline/command/run.rst b/docs/sources/commandline/command/run.rst deleted file mode 100644 index b68e08303f..0000000000 --- a/docs/sources/commandline/command/run.rst +++ /dev/null @@ -1,85 +0,0 @@ -:title: Run Command -:description: Run a command in a new container -:keywords: run, container, docker, documentation - -=========================================== -``run`` -- Run a command in a new container -=========================================== - -:: - - Usage: docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...] - - Run a command in a new container - - -a=map[]: Attach to stdin, stdout or stderr. - -c=0: CPU shares (relative weight) - -cidfile="": Write the container ID to the file - -d=false: Detached mode: Run container in the background, print new container id - -e=[]: Set environment variables - -h="": Container host name - -i=false: Keep stdin open even if not attached - -privileged=false: Give extended privileges to this container - -m=0: Memory limit (in bytes) - -n=true: Enable networking for this container - -p=[]: Map a network port to the container - -rm=false: Automatically remove the container when it exits (incompatible with -d) - -t=false: Allocate a pseudo-tty - -u="": Username or UID - -dns=[]: Set custom dns servers for the container - -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "container-dir" is missing, then docker creates a new volume. - -volumes-from="": Mount all volumes from the given container. - -entrypoint="": Overwrite the default entrypoint set by the image. - -w="": Working directory inside the container - -lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1" - -Examples --------- - -.. code-block:: bash - - sudo docker run -cidfile /tmp/docker_test.cid ubuntu echo "test" - -This will create a container and print "test" to the console. The -``cidfile`` flag makes docker attempt to create a new file and write the -container ID to it. If the file exists already, docker will return an -error. Docker will close this file when docker run exits. - -.. code-block:: bash - - docker run mount -t tmpfs none /var/spool/squid - -This will *not* work, because by default, most potentially dangerous -kernel capabilities are dropped; including ``cap_sys_admin`` (which is -required to mount filesystems). However, the ``-privileged`` flag will -allow it to run: - -.. code-block:: bash - - docker run -privileged mount -t tmpfs none /var/spool/squid - -The ``-privileged`` flag gives *all* capabilities to the container, -and it also lifts all the limitations enforced by the ``device`` -cgroup controller. In other words, the container can then do almost -everything that the host can do. This flag exists to allow special -use-cases, like running Docker within Docker. - -.. code-block:: bash - - docker run -w /path/to/dir/ -i -t ubuntu pwd - -The ``-w`` lets the command being executed inside directory given, -here /path/to/dir/. If the path does not exists it is created inside the -container. - -.. code-block:: bash - - docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd - -The ``-v`` flag mounts the current working directory into the container. -The ``-w`` lets the command being executed inside the current -working directory, by changing into the directory to the value -returned by ``pwd``. So this combination executes the command -using the container, but inside the current working directory. - - diff --git a/docs/sources/commandline/command/search.rst b/docs/sources/commandline/command/search.rst deleted file mode 100644 index 8107016a41..0000000000 --- a/docs/sources/commandline/command/search.rst +++ /dev/null @@ -1,14 +0,0 @@ -:title: Search Command -:description: Searches for the TERM parameter on the Docker index and prints out a list of repositories that match. -:keywords: search, docker, image, documentation - -=================================================================== -``search`` -- Search for an image in the docker index -=================================================================== - -:: - - Usage: docker search TERM - - Searches for the TERM parameter on the Docker index and prints out - a list of repositories that match. diff --git a/docs/sources/commandline/command/start.rst b/docs/sources/commandline/command/start.rst deleted file mode 100644 index b70ad21cfd..0000000000 --- a/docs/sources/commandline/command/start.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Start Command -:description: Start a stopped container -:keywords: start, docker, container, documentation - -====================================== -``start`` -- Start a stopped container -====================================== - -:: - - Usage: docker start [OPTIONS] NAME - - Start a stopped container diff --git a/docs/sources/commandline/command/stop.rst b/docs/sources/commandline/command/stop.rst deleted file mode 100644 index 6a64908eae..0000000000 --- a/docs/sources/commandline/command/stop.rst +++ /dev/null @@ -1,15 +0,0 @@ -:title: Stop Command -:description: Stop a running container -:keywords: stop, container, docker, documentation - -==================================== -``stop`` -- Stop a running container -==================================== - -:: - - Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...] - - Stop a running container - - -t=10: Number of seconds to wait for the container to stop before killing it. diff --git a/docs/sources/commandline/command/tag.rst b/docs/sources/commandline/command/tag.rst deleted file mode 100644 index a9e831aae9..0000000000 --- a/docs/sources/commandline/command/tag.rst +++ /dev/null @@ -1,15 +0,0 @@ -:title: Tag Command -:description: Tag an image into a repository -:keywords: tag, docker, image, repository, documentation, repo - -========================================= -``tag`` -- Tag an image into a repository -========================================= - -:: - - Usage: docker tag [OPTIONS] IMAGE REPOSITORY [TAG] - - Tag an image into a repository - - -f=false: Force diff --git a/docs/sources/commandline/command/top.rst b/docs/sources/commandline/command/top.rst deleted file mode 100644 index bdd35adcfa..0000000000 --- a/docs/sources/commandline/command/top.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Top Command -:description: Lookup the running processes of a container -:keywords: top, docker, container, documentation - -======================================================= -``top`` -- Lookup the running processes of a container -======================================================= - -:: - - Usage: docker top CONTAINER - - Lookup the running processes of a container diff --git a/docs/sources/commandline/command/version.rst b/docs/sources/commandline/command/version.rst deleted file mode 100644 index fb3d3b450e..0000000000 --- a/docs/sources/commandline/command/version.rst +++ /dev/null @@ -1,7 +0,0 @@ -:title: Version Command -:description: -:keywords: version, docker, documentation - -================================================== -``version`` -- Show the docker version information -================================================== diff --git a/docs/sources/commandline/command/wait.rst b/docs/sources/commandline/command/wait.rst deleted file mode 100644 index 23bd54513c..0000000000 --- a/docs/sources/commandline/command/wait.rst +++ /dev/null @@ -1,13 +0,0 @@ -:title: Wait Command -:description: Block until a container stops, then print its exit code. -:keywords: wait, docker, container, documentation - -=================================================================== -``wait`` -- Block until a container stops, then print its exit code -=================================================================== - -:: - - Usage: docker wait [OPTIONS] NAME - - Block until a container stops, then print its exit code. diff --git a/docs/sources/static_files/docker_images.gif b/docs/sources/commandline/docker_images.gif similarity index 100% rename from docs/sources/static_files/docker_images.gif rename to docs/sources/commandline/docker_images.gif diff --git a/docs/sources/commandline/index.rst b/docs/sources/commandline/index.rst index 0e7c8738b3..5536e1012e 100644 --- a/docs/sources/commandline/index.rst +++ b/docs/sources/commandline/index.rst @@ -1,6 +1,6 @@ :title: Commands -:description: -- todo: change me -:keywords: todo, commands, command line, help, docker, documentation +:description: docker command line interface +:keywords: commands, command line, help, docker Commands @@ -12,34 +12,3 @@ Contents: :maxdepth: 1 cli - attach - build - commit - cp - diff - events - export - history - images - import - info - insert - inspect - kill - login - logs - port - ps - pull - push - restart - rm - rmi - run - search - start - stop - tag - top - version - wait diff --git a/docs/sources/contributing/contributing.rst b/docs/sources/contributing/contributing.rst index 301977e5e1..3cdb0b6f14 100644 --- a/docs/sources/contributing/contributing.rst +++ b/docs/sources/contributing/contributing.rst @@ -5,5 +5,18 @@ Contributing to Docker ====================== -Want to hack on Docker? Awesome! The repository includes `all the instructions you need to get started `_. +Want to hack on Docker? Awesome! +The repository includes `all the instructions you need to get +started `_. + +The developer environment `Dockerfile `_ +specifies the tools and versions used to test and build Docker. + +If you're making changes to the documentation, see the +`README.md `_. + +The documentation environment `Dockerfile `_ +specifies the tools and versions used to build the Documentation. + +Further interesting details can be found in the `Packaging hints `_. diff --git a/docs/sources/examples/hello_world.rst b/docs/sources/examples/hello_world.rst index 647a34aa1f..54ebdacc6a 100644 --- a/docs/sources/examples/hello_world.rst +++ b/docs/sources/examples/hello_world.rst @@ -178,4 +178,4 @@ you could skip to any of the other examples: * :ref:`running_ssh_service` * :ref:`running_couchdb_service` * :ref:`postgresql_service` -* :ref:`mongodb` +* :ref:`mongodb_image` diff --git a/docs/sources/examples/index.rst b/docs/sources/examples/index.rst index 01251680b6..74a7e7c406 100644 --- a/docs/sources/examples/index.rst +++ b/docs/sources/examples/index.rst @@ -1,6 +1,6 @@ :title: Docker Examples :description: Examples on how to use Docker -:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql +:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql, link .. _example_list: @@ -24,3 +24,4 @@ to more substantial services like you might find in production. postgresql_service mongodb running_riak_service + linking_into_redis diff --git a/docs/sources/examples/linking_into_redis.rst b/docs/sources/examples/linking_into_redis.rst new file mode 100644 index 0000000000..a0341ad18f --- /dev/null +++ b/docs/sources/examples/linking_into_redis.rst @@ -0,0 +1,126 @@ +:title: Linking to an Redis container +:description: Running redis linked into your web app +:keywords: docker, example, networking, redis, link + +.. _linking_redis: + +Linking Redis +============= + +.. include:: example_header.inc + +Building a redis container to link as a child of our web application. + +Building the redis container +---------------------------- + +We will use a pre-build version of redis from the index under +the name ``crosbymichael/redis``. If you are interested in the +Dockerfile that was used to build this container here it is. + +.. code-block:: bash + + # Build redis from source + # Make sure you have the redis source code checked out in + # the same directory as this Dockerfile + FROM ubuntu + + RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list + RUN apt-get update + RUN apt-get upgrade -y + + RUN apt-get install -y gcc make g++ build-essential libc6-dev tcl + + ADD . /redis + + RUN (cd /redis && make) + RUN (cd /redis && make test) + + RUN mkdir -p /redis-data + VOLUME ["/redis-data"] + EXPOSE 6379 + + ENTRYPOINT ["/redis/src/redis-server"] + CMD ["--dir", "/redis-data"] + + +We need to ``EXPOSE`` the default port of 6379 so that our link knows what ports +to connect to our redis container on. If you do not expose any ports for the +image then docker will not be able to establish the link between containers. + + +Run the redis container +----------------------- + +.. code-block:: bash + + docker run -d -e PASSWORD=docker -name redis crosbymichael/redis --requirepass=docker + +This will run our redis container using the default port of 6379 and using docker +as password to secure our service. By specifying the ``-name`` flag on run +we will assign the name ``redis`` to this container. +We can issue all the commands that you would expect; start, stop, attach, using the name. +The name also allows us to link other containers into this one. If you do not specify a +name on docker run, docker will automatically generate a name for your container. + +Linking redis as a child +------------------------ + +Next we can start a new web application that has a dependency on redis and apply a link +to connect both containers. If you noticed when running our redis service we did not use +the ``-p`` option to publish the redis port to the host system. Redis exposed port 6379 +but we did not publish the port. This allows docker to prevent all network traffic to +the redis container except when explicitly specified within a link. This is a big win +for security. + + +Now lets start our web application with a link into redis. + +.. code-block:: bash + + docker run -t -i -link /redis:db -name webapp ubuntu bash + + root@4c01db0b339c:/# env + + HOSTNAME=4c01db0b339c + DB_NAME=/webapp/db + TERM=xterm + DB_PORT=tcp://172.17.0.8:6379 + DB_PORT_6379_TCP=tcp://172.17.0.8:6379 + DB_PORT_6379_TCP_PROTO=tcp + DB_PORT_6379_TCP_ADDR=172.17.0.8 + DB_PORT_6379_TCP_PORT=6379 + PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + PWD=/ + DB_ENV_PASSWORD=dockerpass + SHLVL=1 + HOME=/ + container=lxc + _=/usr/bin/env + root@4c01db0b339c:/# + + +When we inspect the environment of the linked container we can see a few extra environment +variables have been added. When you specified ``-link /redis:db`` you are telling docker +to link the container named ``/redis`` into this new container with the alias ``db``. +Environment variables are prefixed with the alias so that the parent container can access +network and environment information from the child. + +.. code-block:: bash + + # The name of the child container + DB_NAME=/webapp/db + # The default protocol, ip, and port of the service running in the container + DB_PORT=tcp://172.17.0.8:6379 + # A specific protocol, ip, and port of various services + DB_PORT_6379_TCP=tcp://172.17.0.8:6379 + DB_PORT_6379_TCP_PROTO=tcp + DB_PORT_6379_TCP_ADDR=172.17.0.8 + DB_PORT_6379_TCP_PORT=6379 + # Get environment variables of the container + DB_ENV_PASSWORD=dockerpass + + +Accessing the network information along with the environment of the child container allows +us to easily connect to the redis service on the specific ip and port and use the password +specified in the environment. diff --git a/docs/sources/index.rst b/docs/sources/index.rst index 99d2159aa7..88752ac3bf 100644 --- a/docs/sources/index.rst +++ b/docs/sources/index.rst @@ -2,8 +2,6 @@ :description: An overview of the Docker Documentation :keywords: containers, lxc, concepts, explanation -.. image:: https://www.docker.io/static/img/linked/dockerlogo-horizontal.png - Introduction ------------ diff --git a/docs/sources/installation/kernel.rst b/docs/sources/installation/kernel.rst index 2959fa4fcd..bc8440fe2e 100644 --- a/docs/sources/installation/kernel.rst +++ b/docs/sources/installation/kernel.rst @@ -69,7 +69,7 @@ to run LXC containers. Note that 2.6.32 has some documented issues regarding network namespace setup and teardown; those issues are not a risk if you run containers in a private environment, but can lead to denial-of-service attacks if you want to run untrusted code in your containers. For more details, -see `[LP#720095 `_. +see `LP#720095 `_. Kernels 2.6.38, and every version since 3.2, have been deployed successfully to run containerized production workloads. Feature-wise, there is no huge diff --git a/docs/sources/installation/ubuntulinux.rst b/docs/sources/installation/ubuntulinux.rst index e653f704a8..fd869fbe9c 100644 --- a/docs/sources/installation/ubuntulinux.rst +++ b/docs/sources/installation/ubuntulinux.rst @@ -78,7 +78,8 @@ Docker is available as a Debian package, which makes installation easy. sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -" # Add the Docker repository to your apt sources list. - sudo sh -c "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list" + sudo sh -c "echo deb http://get.docker.io/ubuntu docker main\ + > /etc/apt/sources.list.d/docker.list" # Update your sources sudo apt-get update @@ -132,7 +133,8 @@ to follow them again.* sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -" # Add the Docker repository to your apt sources list. - sudo sh -c "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list" + sudo sh -c "echo deb http://get.docker.io/ubuntu docker main\ + > /etc/apt/sources.list.d/docker.list" # update sudo apt-get update diff --git a/docs/sources/use/baseimages.rst b/docs/sources/use/baseimages.rst index fa5c47b621..4785cc5298 100644 --- a/docs/sources/use/baseimages.rst +++ b/docs/sources/use/baseimages.rst @@ -40,4 +40,4 @@ Docker Github Repo: * `CentOS `_ * `Debian/Ubuntu - `_ + `_ diff --git a/docs/sources/use/basics.rst b/docs/sources/use/basics.rst index 8b4676f6ee..edec214a7f 100644 --- a/docs/sources/use/basics.rst +++ b/docs/sources/use/basics.rst @@ -67,6 +67,8 @@ you don't need to add ``sudo`` to all the client commands. # Restart the docker daemon sudo service docker restart +.. _bind_docker: + Bind Docker to another host/port or a Unix socket ------------------------------------------------- @@ -142,7 +144,7 @@ Expose a service on a TCP port .. code-block:: bash # Expose port 4444 of this container, and tell netcat to listen on it - JOB=$(sudo docker run -d -p 4444 ubuntu:12.10 /bin/nc -l -p 4444) + JOB=$(sudo docker run -d -p 4444 ubuntu:12.10 /bin/nc -l 4444) # Which public port is NATed to my container? PORT=$(sudo docker port $JOB 4444) diff --git a/docs/sources/use/builder.rst b/docs/sources/use/builder.rst index 7a356f8450..e40d19af95 100644 --- a/docs/sources/use/builder.rst +++ b/docs/sources/use/builder.rst @@ -220,7 +220,7 @@ The copy obeys the following rules: (``http://example.com`` will not work). * If ```` is a directory, the entire directory is copied, including filesystem metadata. -* If ````` is a tar archive in a recognized compression format +* If ```` is a tar archive in a recognized compression format (identity, gzip, bzip2 or xz), it is unpacked as a directory. When a directory is copied or unpacked, it has the same behavior as diff --git a/docs/sources/use/host_integration.rst b/docs/sources/use/host_integration.rst new file mode 100644 index 0000000000..c10a79df18 --- /dev/null +++ b/docs/sources/use/host_integration.rst @@ -0,0 +1,125 @@ +:title: Host Integration +:description: How to generate scripts for upstart, systemd, etc. +:keywords: systemd, upstart, supervisor, docker, documentation, host integration + + + +Host Integration +================ + +You can use your Docker containers with process managers like ``upstart``, +``systemd`` and ``supervisor``. + +Introduction +------------ + +When you have finished setting up your image and are happy with your +running container, you may want to use a process manager to manage +it. To help with this, we provide a simple image: ``creack/manager:min`` + +This image takes the container ID as parameter. We also can specify +the kind of process manager and metadata like *Author* and +*Description*. The output will will be text suitable for a +configuration file, echoed to stdout. It is up to you to create the +.conf file (for `upstart +`_) or +.service file (for `systemd +`_) and +put it in the right place for your system. + +Usage +----- + +.. code-block:: bash + + docker run creack/manager:min [OPTIONS] + +.. program:: docker run creack/manager:min + +.. cmdoption:: -a="" + + Author of the image + +.. cmdoption:: -d="" + + Description of the image + +.. cmdoption:: -t="upstart" + + Type of manager requested: ``upstart`` or ``systemd`` + +Example Output +.............. + +.. code-block:: bash + + docker run creack/manager:min -t="systemd" b28605f2f9a4 + [Unit] + Description= + Author= + After=docker.service + + [Service] + Restart=always + ExecStart=/usr/bin/docker start -a b28605f2f9a4 + ExecStop=/usr/bin/docker stop -t 2 b28605f2f9a4 + + [Install] + WantedBy=local.target + + + +Development +----------- + +The image ``creack/manager:min`` is a ``busybox`` base with the +compiled binary of ``manager.go`` as the :ref:`Entrypoint +`. It is meant to be light and fast to download. + +If you would like to change or add things, you can download the full +``creack/manager`` repository that contains ``creack/manager:min`` and +``creack/manager:dev``. + +The Dockerfiles and the sources are available in +`/contrib/host_integration +`_. + + +Upstart +------- + +Upstart is the default process manager. The generated script will +start the container after the ``docker`` daemon. If the container +dies, it will respawn. Start/Restart/Stop/Reload are +supported. Reload will send a SIGHUP to the container. + +Example (``upstart`` on Debian) +............................... + +.. code-block:: bash + + CID=$(docker run -d creack/firefo-vnc) + docker run creack/manager:min -a 'Guillaume J. Charmes ' -d 'Awesome Firefox in VLC' $CID > /etc/init/firefoxvnc.conf + +You can now ``start firefoxvnc`` or ``stop firefoxvnc`` and if the container +dies for some reason, upstart will restart it. + +Systemd +------- + +In order to generate a systemd script, we need to use the ``-t`` +option. The generated script will start the container after docker +daemon. If the container dies, it will respawn. +``Start/Restart/Reload/Stop`` are supported. + +Example (``systemd`` on Fedora) +............................... + +.. code-block:: bash + + CID=$(docker run -d creack/firefo-vnc) + docker run creack/manager:min -t systemd -a 'Guillaume J. Charmes ' -d 'Awesome Firefox in VLC' $CID > /usr/lib/systemd/system/firefoxvnc.service + +You can now run ``systemctl start firefoxvnc`` or ``systemctl stop +firefoxvnc`` and if the container dies for some reason, ``systemd`` +will restart it. diff --git a/docs/sources/use/index.rst b/docs/sources/use/index.rst index d0a40159e9..7d0b3c5a31 100644 --- a/docs/sources/use/index.rst +++ b/docs/sources/use/index.rst @@ -18,3 +18,4 @@ Contents: baseimages port_redirection puppet + host_integration diff --git a/docs/theme/docker/layout.html b/docs/theme/docker/layout.html index e415e97235..193df7447f 100755 --- a/docs/theme/docker/layout.html +++ b/docs/theme/docker/layout.html @@ -13,6 +13,12 @@ {%- set url_root = pathto('', 1) %} {%- if url_root == '#' %}{% set url_root = '' %}{% endif %} + {%- if current_version == 'latest' %} + {% set github_tag = 'master' %} + {% else %} + {% set github_tag = current_version %} + {% endif %} +