mirror of https://github.com/docker/docs.git
Merge branch 'master' into bump_v0.11.0
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
This commit is contained in:
commit
263d134fff
|
@ -23,3 +23,6 @@ bundles/
|
||||||
vendor/pkg/
|
vendor/pkg/
|
||||||
pyenv
|
pyenv
|
||||||
Vagrantfile
|
Vagrantfile
|
||||||
|
docs/AWS_S3_BUCKET
|
||||||
|
docs/GIT_BRANCH
|
||||||
|
docs/VERSION
|
||||||
|
|
16
.travis.yml
16
.travis.yml
|
@ -10,21 +10,9 @@ install: true
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- env | sort
|
- env | sort
|
||||||
- sudo apt-get update -qq
|
|
||||||
- sudo apt-get install -qq python-yaml
|
|
||||||
- git remote add upstream git://github.com/dotcloud/docker.git
|
|
||||||
- upstream=master;
|
|
||||||
if [ "$TRAVIS_PULL_REQUEST" != false ]; then
|
|
||||||
upstream=$TRAVIS_BRANCH;
|
|
||||||
fi;
|
|
||||||
git fetch --append --no-tags upstream refs/heads/$upstream:refs/remotes/upstream/$upstream
|
|
||||||
# sometimes we have upstream master already as origin/master (PRs), but other times we don't, so let's just make sure we have a completely unambiguous way to specify "upstream master" from here out
|
|
||||||
# but if it's a PR against non-master, we need that upstream branch instead :)
|
|
||||||
- sudo pip install -r docs/requirements.txt
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- hack/travis/dco.py
|
- hack/make.sh validate-dco
|
||||||
- hack/travis/gofmt.py
|
- hack/make.sh validate-gofmt
|
||||||
- make -sC docs SPHINXOPTS=-qW docs man
|
|
||||||
|
|
||||||
# vim:set sw=2 ts=2:
|
# vim:set sw=2 ts=2:
|
||||||
|
|
2
AUTHORS
2
AUTHORS
|
@ -20,6 +20,7 @@ Andrew Munsell <andrew@wizardapps.net>
|
||||||
Andrews Medina <andrewsmedina@gmail.com>
|
Andrews Medina <andrewsmedina@gmail.com>
|
||||||
Andy Chambers <anchambers@paypal.com>
|
Andy Chambers <anchambers@paypal.com>
|
||||||
andy diller <dillera@gmail.com>
|
andy diller <dillera@gmail.com>
|
||||||
|
Andy Goldstein <agoldste@redhat.com>
|
||||||
Andy Rothfusz <github@metaliveblog.com>
|
Andy Rothfusz <github@metaliveblog.com>
|
||||||
Andy Smith <github@anarkystic.com>
|
Andy Smith <github@anarkystic.com>
|
||||||
Anthony Bishopric <git@anthonybishopric.com>
|
Anthony Bishopric <git@anthonybishopric.com>
|
||||||
|
@ -44,6 +45,7 @@ Brian Olsen <brian@maven-group.org>
|
||||||
Brian Shumate <brian@couchbase.com>
|
Brian Shumate <brian@couchbase.com>
|
||||||
Briehan Lombaard <briehan.lombaard@gmail.com>
|
Briehan Lombaard <briehan.lombaard@gmail.com>
|
||||||
Bruno Bigras <bigras.bruno@gmail.com>
|
Bruno Bigras <bigras.bruno@gmail.com>
|
||||||
|
Bryan Matsuo <bryan.matsuo@gmail.com>
|
||||||
Caleb Spare <cespare@gmail.com>
|
Caleb Spare <cespare@gmail.com>
|
||||||
Calen Pennington <cale@edx.org>
|
Calen Pennington <cale@edx.org>
|
||||||
Carl X. Su <bcbcarl@gmail.com>
|
Carl X. Su <bcbcarl@gmail.com>
|
||||||
|
|
|
@ -82,7 +82,7 @@ editors have plugins that do this automatically, and there's also a git
|
||||||
pre-commit hook:
|
pre-commit hook:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -o .git/hooks/pre-commit https://raw.github.com/edsrzf/gofmt-git-hook/master/fmt-check && chmod +x .git/hooks/pre-commit
|
curl -o .git/hooks/pre-commit https://raw.githubusercontent.com/edsrzf/gofmt-git-hook/master/fmt-check && chmod +x .git/hooks/pre-commit
|
||||||
```
|
```
|
||||||
|
|
||||||
Pull requests descriptions should be as clear as possible and include a
|
Pull requests descriptions should be as clear as possible and include a
|
||||||
|
@ -90,6 +90,10 @@ reference to all the issues that they address.
|
||||||
|
|
||||||
Pull requests must not contain commits from other users or branches.
|
Pull requests must not contain commits from other users or branches.
|
||||||
|
|
||||||
|
Commit messages must start with a capitalized and short summary (max. 50
|
||||||
|
chars) written in the imperative, followed by an optional, more detailed
|
||||||
|
explanatory text which is separated from the summary by an empty line.
|
||||||
|
|
||||||
Code review comments may be added to your pull request. Discuss, then make the
|
Code review comments may be added to your pull request. Discuss, then make the
|
||||||
suggested modifications and push additional commits to your feature branch. Be
|
suggested modifications and push additional commits to your feature branch. Be
|
||||||
sure to post a comment after pushing. The new commits will show up in the pull
|
sure to post a comment after pushing. The new commits will show up in the pull
|
||||||
|
|
|
@ -42,6 +42,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq \
|
||||||
libcap-dev \
|
libcap-dev \
|
||||||
libsqlite3-dev \
|
libsqlite3-dev \
|
||||||
mercurial \
|
mercurial \
|
||||||
|
pandoc \
|
||||||
reprepro \
|
reprepro \
|
||||||
ruby1.9.1 \
|
ruby1.9.1 \
|
||||||
ruby1.9.1-dev \
|
ruby1.9.1-dev \
|
||||||
|
@ -82,6 +83,9 @@ RUN go get code.google.com/p/go.tools/cmd/cover
|
||||||
# TODO replace FPM with some very minimal debhelper stuff
|
# TODO replace FPM with some very minimal debhelper stuff
|
||||||
RUN gem install --no-rdoc --no-ri fpm --version 1.0.2
|
RUN gem install --no-rdoc --no-ri fpm --version 1.0.2
|
||||||
|
|
||||||
|
# Get the "busybox" image source so we can build locally instead of pulling
|
||||||
|
RUN git clone https://github.com/jpetazzo/docker-busybox.git /docker-busybox
|
||||||
|
|
||||||
# Setup s3cmd config
|
# Setup s3cmd config
|
||||||
RUN /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY' > /.s3cfg
|
RUN /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY' > /.s3cfg
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Solomon Hykes <solomon@dotcloud.com> (@shykes)
|
Solomon Hykes <solomon@docker.com> (@shykes)
|
||||||
Guillaume J. Charmes <guillaume@docker.com> (@creack)
|
Guillaume J. Charmes <guillaume@docker.com> (@creack)
|
||||||
Victor Vieux <vieux@docker.com> (@vieux)
|
Victor Vieux <vieux@docker.com> (@vieux)
|
||||||
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
|
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
|
||||||
|
|
25
Makefile
25
Makefile
|
@ -1,4 +1,4 @@
|
||||||
.PHONY: all binary build cross default docs docs-build docs-shell shell test test-integration test-integration-cli
|
.PHONY: all binary build cross default docs docs-build docs-shell shell test test-unit test-integration test-integration-cli validate
|
||||||
|
|
||||||
# to allow `make BINDDIR=. shell` or `make BINDDIR= test`
|
# to allow `make BINDDIR=. shell` or `make BINDDIR= test`
|
||||||
BINDDIR := bundles
|
BINDDIR := bundles
|
||||||
|
@ -10,8 +10,9 @@ DOCKER_IMAGE := docker$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
||||||
DOCKER_DOCS_IMAGE := docker-docs$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
DOCKER_DOCS_IMAGE := docker-docs$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
||||||
DOCKER_MOUNT := $(if $(BINDDIR),-v "$(CURDIR)/$(BINDDIR):/go/src/github.com/dotcloud/docker/$(BINDDIR)")
|
DOCKER_MOUNT := $(if $(BINDDIR),-v "$(CURDIR)/$(BINDDIR):/go/src/github.com/dotcloud/docker/$(BINDDIR)")
|
||||||
|
|
||||||
DOCKER_RUN_DOCKER := docker run --rm -it --privileged -e TESTFLAGS -e DOCKER_GRAPHDRIVER -e DOCKER_EXECDRIVER $(DOCKER_MOUNT) "$(DOCKER_IMAGE)"
|
DOCKER_RUN_DOCKER := docker run --rm -it --privileged -e TESTFLAGS -e TESTDIRS -e DOCKER_GRAPHDRIVER -e DOCKER_EXECDRIVER $(DOCKER_MOUNT) "$(DOCKER_IMAGE)"
|
||||||
DOCKER_RUN_DOCS := docker run --rm -it -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)"
|
# to allow `make DOCSDIR=docs docs-shell`
|
||||||
|
DOCKER_RUN_DOCS := docker run --rm -it $(if $(DOCSDIR),-v $(CURDIR)/$(DOCSDIR):/$(DOCSDIR)) -e AWS_S3_BUCKET
|
||||||
|
|
||||||
default: binary
|
default: binary
|
||||||
|
|
||||||
|
@ -25,13 +26,19 @@ cross: build
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary cross
|
$(DOCKER_RUN_DOCKER) hack/make.sh binary cross
|
||||||
|
|
||||||
docs: docs-build
|
docs: docs-build
|
||||||
$(DOCKER_RUN_DOCS)
|
$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)" mkdocs serve
|
||||||
|
|
||||||
docs-shell: docs-build
|
docs-shell: docs-build
|
||||||
$(DOCKER_RUN_DOCS) bash
|
$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)" bash
|
||||||
|
|
||||||
|
docs-release: docs-build
|
||||||
|
$(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)" ./release.sh
|
||||||
|
|
||||||
test: build
|
test: build
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary test test-integration test-integration-cli
|
$(DOCKER_RUN_DOCKER) hack/make.sh binary test-unit test-integration test-integration-cli
|
||||||
|
|
||||||
|
test-unit: build
|
||||||
|
$(DOCKER_RUN_DOCKER) hack/make.sh test-unit
|
||||||
|
|
||||||
test-integration: build
|
test-integration: build
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh test-integration
|
$(DOCKER_RUN_DOCKER) hack/make.sh test-integration
|
||||||
|
@ -39,6 +46,9 @@ test-integration: build
|
||||||
test-integration-cli: build
|
test-integration-cli: build
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary test-integration-cli
|
$(DOCKER_RUN_DOCKER) hack/make.sh binary test-integration-cli
|
||||||
|
|
||||||
|
validate: build
|
||||||
|
$(DOCKER_RUN_DOCKER) hack/make.sh validate-gofmt validate-dco
|
||||||
|
|
||||||
shell: build
|
shell: build
|
||||||
$(DOCKER_RUN_DOCKER) bash
|
$(DOCKER_RUN_DOCKER) bash
|
||||||
|
|
||||||
|
@ -46,6 +56,9 @@ build: bundles
|
||||||
docker build -t "$(DOCKER_IMAGE)" .
|
docker build -t "$(DOCKER_IMAGE)" .
|
||||||
|
|
||||||
docs-build:
|
docs-build:
|
||||||
|
cp ./VERSION docs/VERSION
|
||||||
|
echo "$(GIT_BRANCH)" > docs/GIT_BRANCH
|
||||||
|
echo "$(AWS_S3_BUCKET)" > docs/AWS_S3_BUCKET
|
||||||
docker build -t "$(DOCKER_DOCS_IMAGE)" docs
|
docker build -t "$(DOCKER_DOCS_IMAGE)" docs
|
||||||
|
|
||||||
bundles:
|
bundles:
|
||||||
|
|
|
@ -18,7 +18,7 @@ It benefits directly from the experience accumulated over several years
|
||||||
of large-scale operation and support of hundreds of thousands of
|
of large-scale operation and support of hundreds of thousands of
|
||||||
applications and databases.
|
applications and databases.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Better than VMs
|
## Better than VMs
|
||||||
|
|
||||||
|
|
|
@ -65,8 +65,13 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsC
|
||||||
var (
|
var (
|
||||||
isTerminal = false
|
isTerminal = false
|
||||||
terminalFd uintptr
|
terminalFd uintptr
|
||||||
|
scheme = "http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if tlsConfig != nil {
|
||||||
|
scheme = "https"
|
||||||
|
}
|
||||||
|
|
||||||
if in != nil {
|
if in != nil {
|
||||||
if file, ok := in.(*os.File); ok {
|
if file, ok := in.(*os.File); ok {
|
||||||
terminalFd = file.Fd()
|
terminalFd = file.Fd()
|
||||||
|
@ -86,6 +91,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsC
|
||||||
isTerminal: isTerminal,
|
isTerminal: isTerminal,
|
||||||
terminalFd: terminalFd,
|
terminalFd: terminalFd,
|
||||||
tlsConfig: tlsConfig,
|
tlsConfig: tlsConfig,
|
||||||
|
scheme: scheme,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,4 +105,5 @@ type DockerCli struct {
|
||||||
isTerminal bool
|
isTerminal bool
|
||||||
terminalFd uintptr
|
terminalFd uintptr
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
|
scheme string
|
||||||
}
|
}
|
||||||
|
|
|
@ -1491,7 +1491,8 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
|
||||||
|
|
||||||
func (cli *DockerCli) CmdEvents(args ...string) error {
|
func (cli *DockerCli) CmdEvents(args ...string) error {
|
||||||
cmd := cli.Subcmd("events", "[OPTIONS]", "Get real time events from the server")
|
cmd := cli.Subcmd("events", "[OPTIONS]", "Get real time events from the server")
|
||||||
since := cmd.String([]string{"#since", "-since"}, "", "Show previously created events and then stream.")
|
since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp")
|
||||||
|
until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1500,22 +1501,27 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
var (
|
||||||
v := url.Values{}
|
v = url.Values{}
|
||||||
if *since != "" {
|
loc = time.FixedZone(time.Now().Zone())
|
||||||
loc := time.FixedZone(time.Now().Zone())
|
)
|
||||||
|
var setTime = func(key, value string) {
|
||||||
format := "2006-01-02 15:04:05 -0700 MST"
|
format := "2006-01-02 15:04:05 -0700 MST"
|
||||||
if len(*since) < len(format) {
|
if len(value) < len(format) {
|
||||||
format = format[:len(*since)]
|
format = format[:len(value)]
|
||||||
}
|
}
|
||||||
|
if t, err := time.ParseInLocation(format, value, loc); err == nil {
|
||||||
if t, err := time.ParseInLocation(format, *since, loc); err == nil {
|
v.Set(key, strconv.FormatInt(t.Unix(), 10))
|
||||||
v.Set("since", strconv.FormatInt(t.Unix(), 10))
|
|
||||||
} else {
|
} else {
|
||||||
v.Set("since", *since)
|
v.Set(key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if *since != "" {
|
||||||
|
setTime("since", *since)
|
||||||
|
}
|
||||||
|
if *until != "" {
|
||||||
|
setTime("until", *until)
|
||||||
|
}
|
||||||
if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil {
|
if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1577,6 +1583,7 @@ func (cli *DockerCli) CmdDiff(args ...string) error {
|
||||||
func (cli *DockerCli) CmdLogs(args ...string) error {
|
func (cli *DockerCli) CmdLogs(args ...string) error {
|
||||||
cmd := cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
|
cmd := cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
|
||||||
follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
|
follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
|
||||||
|
times := cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1597,14 +1604,16 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("logs", "1")
|
|
||||||
v.Set("stdout", "1")
|
v.Set("stdout", "1")
|
||||||
v.Set("stderr", "1")
|
v.Set("stderr", "1")
|
||||||
|
if *times {
|
||||||
|
v.Set("timestamps", "1")
|
||||||
|
}
|
||||||
if *follow && container.State.Running {
|
if *follow && container.State.Running {
|
||||||
v.Set("stream", "1")
|
v.Set("follow", "1")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cli.hijack("POST", "/containers/"+name+"/attach?"+v.Encode(), container.Config.Tty, nil, cli.out, cli.err, nil); err != nil {
|
if err := cli.streamHelper("GET", "/containers/"+name+"/logs?"+v.Encode(), container.Config.Tty, nil, cli.out, cli.err, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/api"
|
||||||
|
"github.com/dotcloud/docker/dockerversion"
|
||||||
|
"github.com/dotcloud/docker/pkg/term"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (cli *DockerCli) dial() (net.Conn, error) {
|
||||||
|
if cli.tlsConfig != nil && cli.proto != "unix" {
|
||||||
|
return tls.Dial(cli.proto, cli.addr, cli.tlsConfig)
|
||||||
|
}
|
||||||
|
return net.Dial(cli.proto, cli.addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer) error {
|
||||||
|
defer func() {
|
||||||
|
if started != nil {
|
||||||
|
close(started)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
|
||||||
|
req.Header.Set("Content-Type", "plain/text")
|
||||||
|
req.Host = cli.addr
|
||||||
|
|
||||||
|
dial, err := cli.dial()
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "connection refused") {
|
||||||
|
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
clientconn := httputil.NewClientConn(dial, nil)
|
||||||
|
defer clientconn.Close()
|
||||||
|
|
||||||
|
// Server hijacks the connection, error 'connection closed' expected
|
||||||
|
clientconn.Do(req)
|
||||||
|
|
||||||
|
rwc, br := clientconn.Hijack()
|
||||||
|
defer rwc.Close()
|
||||||
|
|
||||||
|
if started != nil {
|
||||||
|
started <- rwc
|
||||||
|
}
|
||||||
|
|
||||||
|
var receiveStdout chan error
|
||||||
|
|
||||||
|
var oldState *term.State
|
||||||
|
|
||||||
|
if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" {
|
||||||
|
oldState, err = term.SetRawTerminal(cli.terminalFd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer term.RestoreTerminal(cli.terminalFd, oldState)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stdout != nil || stderr != nil {
|
||||||
|
receiveStdout = utils.Go(func() (err error) {
|
||||||
|
defer func() {
|
||||||
|
if in != nil {
|
||||||
|
if setRawTerminal && cli.isTerminal {
|
||||||
|
term.RestoreTerminal(cli.terminalFd, oldState)
|
||||||
|
}
|
||||||
|
// For some reason this Close call blocks on darwin..
|
||||||
|
// As the client exists right after, simply discard the close
|
||||||
|
// until we find a better solution.
|
||||||
|
if runtime.GOOS != "darwin" {
|
||||||
|
in.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// When TTY is ON, use regular copy
|
||||||
|
if setRawTerminal {
|
||||||
|
_, err = io.Copy(stdout, br)
|
||||||
|
} else {
|
||||||
|
_, err = utils.StdCopy(stdout, stderr, br)
|
||||||
|
}
|
||||||
|
utils.Debugf("[hijack] End of stdout")
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
sendStdin := utils.Go(func() error {
|
||||||
|
if in != nil {
|
||||||
|
io.Copy(rwc, in)
|
||||||
|
utils.Debugf("[hijack] End of stdin")
|
||||||
|
}
|
||||||
|
if tcpc, ok := rwc.(*net.TCPConn); ok {
|
||||||
|
if err := tcpc.CloseWrite(); err != nil {
|
||||||
|
utils.Debugf("Couldn't send EOF: %s\n", err)
|
||||||
|
}
|
||||||
|
} else if unixc, ok := rwc.(*net.UnixConn); ok {
|
||||||
|
if err := unixc.CloseWrite(); err != nil {
|
||||||
|
utils.Debugf("Couldn't send EOF: %s\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Discard errors due to pipe interruption
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if stdout != nil || stderr != nil {
|
||||||
|
if err := <-receiveStdout; err != nil {
|
||||||
|
utils.Debugf("Error receiveStdout: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cli.isTerminal {
|
||||||
|
if err := <-sendStdin; err != nil {
|
||||||
|
utils.Debugf("Error sendStdin: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -11,12 +10,9 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
gosignal "os/signal"
|
gosignal "os/signal"
|
||||||
"regexp"
|
|
||||||
goruntime "runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -33,11 +29,14 @@ var (
|
||||||
ErrConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
ErrConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (cli *DockerCli) dial() (net.Conn, error) {
|
func (cli *DockerCli) HTTPClient() *http.Client {
|
||||||
if cli.tlsConfig != nil && cli.proto != "unix" {
|
tr := &http.Transport{
|
||||||
return tls.Dial(cli.proto, cli.addr, cli.tlsConfig)
|
TLSClientConfig: cli.tlsConfig,
|
||||||
}
|
Dial: func(network, addr string) (net.Conn, error) {
|
||||||
return net.Dial(cli.proto, cli.addr)
|
return net.Dial(cli.proto, cli.addr)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return &http.Client{Transport: tr}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) {
|
func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) {
|
||||||
|
@ -57,9 +56,6 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// fixme: refactor client to support redirect
|
|
||||||
re := regexp.MustCompile("/+")
|
|
||||||
path = re.ReplaceAllString(path, "/")
|
|
||||||
|
|
||||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params)
|
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -86,28 +82,20 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
|
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
|
||||||
req.Host = cli.addr
|
req.URL.Host = cli.addr
|
||||||
|
req.URL.Scheme = cli.scheme
|
||||||
if data != nil {
|
if data != nil {
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
} else if method == "POST" {
|
} else if method == "POST" {
|
||||||
req.Header.Set("Content-Type", "plain/text")
|
req.Header.Set("Content-Type", "plain/text")
|
||||||
}
|
}
|
||||||
dial, err := cli.dial()
|
resp, err := cli.HTTPClient().Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
if strings.Contains(err.Error(), "connection refused") {
|
||||||
return nil, -1, ErrConnectionRefused
|
return nil, -1, ErrConnectionRefused
|
||||||
}
|
}
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
clientconn := httputil.NewClientConn(dial, nil)
|
|
||||||
resp, err := clientconn.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
clientconn.Close()
|
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
|
||||||
return nil, -1, ErrConnectionRefused
|
|
||||||
}
|
|
||||||
return nil, -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
|
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
@ -119,31 +107,25 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
|
||||||
}
|
}
|
||||||
return nil, resp.StatusCode, fmt.Errorf("Error: %s", bytes.TrimSpace(body))
|
return nil, resp.StatusCode, fmt.Errorf("Error: %s", bytes.TrimSpace(body))
|
||||||
}
|
}
|
||||||
|
return resp.Body, resp.StatusCode, nil
|
||||||
wrapper := utils.NewReadCloserWrapper(resp.Body, func() error {
|
|
||||||
if resp != nil && resp.Body != nil {
|
|
||||||
resp.Body.Close()
|
|
||||||
}
|
|
||||||
return clientconn.Close()
|
|
||||||
})
|
|
||||||
return wrapper, resp.StatusCode, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
|
func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
|
||||||
|
return cli.streamHelper(method, path, true, in, out, nil, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in io.Reader, stdout, stderr io.Writer, headers map[string][]string) error {
|
||||||
if (method == "POST" || method == "PUT") && in == nil {
|
if (method == "POST" || method == "PUT") && in == nil {
|
||||||
in = bytes.NewReader([]byte{})
|
in = bytes.NewReader([]byte{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// fixme: refactor client to support redirect
|
req, err := http.NewRequest(method, fmt.Sprintf("http://v%s%s", api.APIVERSION, path), in)
|
||||||
re := regexp.MustCompile("/+")
|
|
||||||
path = re.ReplaceAllString(path, "/")
|
|
||||||
|
|
||||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
|
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
|
||||||
req.Host = cli.addr
|
req.URL.Host = cli.addr
|
||||||
|
req.URL.Scheme = cli.scheme
|
||||||
if method == "POST" {
|
if method == "POST" {
|
||||||
req.Header.Set("Content-Type", "plain/text")
|
req.Header.Set("Content-Type", "plain/text")
|
||||||
}
|
}
|
||||||
|
@ -153,17 +135,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
|
||||||
req.Header[k] = v
|
req.Header[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
resp, err := cli.HTTPClient().Do(req)
|
||||||
dial, err := cli.dial()
|
|
||||||
if err != nil {
|
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
|
||||||
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
clientconn := httputil.NewClientConn(dial, nil)
|
|
||||||
resp, err := clientconn.Do(req)
|
|
||||||
defer clientconn.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
if strings.Contains(err.Error(), "connection refused") {
|
||||||
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
||||||
|
@ -184,124 +156,19 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
|
||||||
}
|
}
|
||||||
|
|
||||||
if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
|
if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
|
||||||
return utils.DisplayJSONMessagesStream(resp.Body, out, cli.terminalFd, cli.isTerminal)
|
return utils.DisplayJSONMessagesStream(resp.Body, stdout, cli.terminalFd, cli.isTerminal)
|
||||||
}
|
}
|
||||||
if _, err := io.Copy(out, resp.Body); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer) error {
|
|
||||||
defer func() {
|
|
||||||
if started != nil {
|
|
||||||
close(started)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// fixme: refactor client to support redirect
|
|
||||||
re := regexp.MustCompile("/+")
|
|
||||||
path = re.ReplaceAllString(path, "/")
|
|
||||||
|
|
||||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
|
|
||||||
req.Header.Set("Content-Type", "plain/text")
|
|
||||||
req.Host = cli.addr
|
|
||||||
|
|
||||||
dial, err := cli.dial()
|
|
||||||
if err != nil {
|
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
|
||||||
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
clientconn := httputil.NewClientConn(dial, nil)
|
|
||||||
defer clientconn.Close()
|
|
||||||
|
|
||||||
// Server hijacks the connection, error 'connection closed' expected
|
|
||||||
clientconn.Do(req)
|
|
||||||
|
|
||||||
rwc, br := clientconn.Hijack()
|
|
||||||
defer rwc.Close()
|
|
||||||
|
|
||||||
if started != nil {
|
|
||||||
started <- rwc
|
|
||||||
}
|
|
||||||
|
|
||||||
var receiveStdout chan error
|
|
||||||
|
|
||||||
var oldState *term.State
|
|
||||||
|
|
||||||
if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" {
|
|
||||||
oldState, err = term.SetRawTerminal(cli.terminalFd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer term.RestoreTerminal(cli.terminalFd, oldState)
|
|
||||||
}
|
|
||||||
|
|
||||||
if stdout != nil || stderr != nil {
|
if stdout != nil || stderr != nil {
|
||||||
receiveStdout = utils.Go(func() (err error) {
|
|
||||||
defer func() {
|
|
||||||
if in != nil {
|
|
||||||
if setRawTerminal && cli.isTerminal {
|
|
||||||
term.RestoreTerminal(cli.terminalFd, oldState)
|
|
||||||
}
|
|
||||||
// For some reason this Close call blocks on darwin..
|
|
||||||
// As the client exists right after, simply discard the close
|
|
||||||
// until we find a better solution.
|
|
||||||
if goruntime.GOOS != "darwin" {
|
|
||||||
in.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// When TTY is ON, use regular copy
|
// When TTY is ON, use regular copy
|
||||||
if setRawTerminal {
|
if setRawTerminal {
|
||||||
_, err = io.Copy(stdout, br)
|
_, err = io.Copy(stdout, resp.Body)
|
||||||
} else {
|
} else {
|
||||||
_, err = utils.StdCopy(stdout, stderr, br)
|
_, err = utils.StdCopy(stdout, stderr, resp.Body)
|
||||||
}
|
}
|
||||||
utils.Debugf("[hijack] End of stdout")
|
utils.Debugf("[stream] End of stdout")
|
||||||
return err
|
return err
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
sendStdin := utils.Go(func() error {
|
|
||||||
if in != nil {
|
|
||||||
io.Copy(rwc, in)
|
|
||||||
utils.Debugf("[hijack] End of stdin")
|
|
||||||
}
|
|
||||||
if tcpc, ok := rwc.(*net.TCPConn); ok {
|
|
||||||
if err := tcpc.CloseWrite(); err != nil {
|
|
||||||
utils.Debugf("Couldn't send EOF: %s\n", err)
|
|
||||||
}
|
|
||||||
} else if unixc, ok := rwc.(*net.UnixConn); ok {
|
|
||||||
if err := unixc.CloseWrite(); err != nil {
|
|
||||||
utils.Debugf("Couldn't send EOF: %s\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Discard errors due to pipe interruption
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if stdout != nil || stderr != nil {
|
|
||||||
if err := <-receiveStdout; err != nil {
|
|
||||||
utils.Debugf("Error receiveStdout: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !cli.isTerminal {
|
|
||||||
if err := <-sendStdin; err != nil {
|
|
||||||
utils.Debugf("Error sendStdin: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) resizeTty(id string) {
|
func (cli *DockerCli) resizeTty(id string) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
APIVERSION version.Version = "1.10"
|
APIVERSION version.Version = "1.11"
|
||||||
DEFAULTHTTPHOST = "127.0.0.1"
|
DEFAULTHTTPHOST = "127.0.0.1"
|
||||||
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
|
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,6 @@ package server
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"code.google.com/p/go.net/websocket"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
@ -21,6 +20,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"code.google.com/p/go.net/websocket"
|
||||||
|
|
||||||
"github.com/dotcloud/docker/api"
|
"github.com/dotcloud/docker/api"
|
||||||
"github.com/dotcloud/docker/engine"
|
"github.com/dotcloud/docker/engine"
|
||||||
"github.com/dotcloud/docker/pkg/listenbuffer"
|
"github.com/dotcloud/docker/pkg/listenbuffer"
|
||||||
|
@ -246,6 +247,7 @@ func getEvents(eng *engine.Engine, version version.Version, w http.ResponseWrite
|
||||||
var job = eng.Job("events", r.RemoteAddr)
|
var job = eng.Job("events", r.RemoteAddr)
|
||||||
streamJSON(job, w, true)
|
streamJSON(job, w, true)
|
||||||
job.Setenv("since", r.Form.Get("since"))
|
job.Setenv("since", r.Form.Get("since"))
|
||||||
|
job.Setenv("until", r.Form.Get("until"))
|
||||||
return job.Run()
|
return job.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,6 +329,48 @@ func getContainersJSON(eng *engine.Engine, version version.Version, w http.Respo
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getContainersLogs(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
job = eng.Job("inspect", vars["name"], "container")
|
||||||
|
c, err = job.Stdout.AddEnv()
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = job.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var outStream, errStream io.Writer
|
||||||
|
outStream = utils.NewWriteFlusher(w)
|
||||||
|
|
||||||
|
if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
|
||||||
|
errStream = utils.NewStdWriter(outStream, utils.Stderr)
|
||||||
|
outStream = utils.NewStdWriter(outStream, utils.Stdout)
|
||||||
|
} else {
|
||||||
|
errStream = outStream
|
||||||
|
}
|
||||||
|
|
||||||
|
job = eng.Job("logs", vars["name"])
|
||||||
|
job.Setenv("follow", r.Form.Get("follow"))
|
||||||
|
job.Setenv("stdout", r.Form.Get("stdout"))
|
||||||
|
job.Setenv("stderr", r.Form.Get("stderr"))
|
||||||
|
job.Setenv("timestamps", r.Form.Get("timestamps"))
|
||||||
|
job.Stdout.Add(outStream)
|
||||||
|
job.Stderr.Set(errStream)
|
||||||
|
if err := job.Run(); err != nil {
|
||||||
|
fmt.Fprintf(outStream, "Error: %s\n", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := parseForm(r); err != nil {
|
if err := parseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -828,8 +872,6 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
|
||||||
return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
|
return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
authEncoded = r.Header.Get("X-Registry-Auth")
|
|
||||||
authConfig = ®istry.AuthConfig{}
|
|
||||||
configFileEncoded = r.Header.Get("X-Registry-Config")
|
configFileEncoded = r.Header.Get("X-Registry-Config")
|
||||||
configFile = ®istry.ConfigFile{}
|
configFile = ®istry.ConfigFile{}
|
||||||
job = eng.Job("build")
|
job = eng.Job("build")
|
||||||
|
@ -839,12 +881,18 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
|
||||||
// Both headers will be parsed and sent along to the daemon, but if a non-empty
|
// Both headers will be parsed and sent along to the daemon, but if a non-empty
|
||||||
// ConfigFile is present, any value provided as an AuthConfig directly will
|
// ConfigFile is present, any value provided as an AuthConfig directly will
|
||||||
// be overridden. See BuildFile::CmdFrom for details.
|
// be overridden. See BuildFile::CmdFrom for details.
|
||||||
|
var (
|
||||||
|
authEncoded = r.Header.Get("X-Registry-Auth")
|
||||||
|
authConfig = ®istry.AuthConfig{}
|
||||||
|
)
|
||||||
if version.LessThan("1.9") && authEncoded != "" {
|
if version.LessThan("1.9") && authEncoded != "" {
|
||||||
authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||||
if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
||||||
// for a pull it is not an error if no auth was given
|
// for a pull it is not an error if no auth was given
|
||||||
// to increase compatibility with the existing api it is defaulting to be empty
|
// to increase compatibility with the existing api it is defaulting to be empty
|
||||||
authConfig = ®istry.AuthConfig{}
|
authConfig = ®istry.AuthConfig{}
|
||||||
|
} else {
|
||||||
|
configFile.Configs[authConfig.ServerAddress] = *authConfig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -869,8 +917,7 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
|
||||||
job.Setenv("q", r.FormValue("q"))
|
job.Setenv("q", r.FormValue("q"))
|
||||||
job.Setenv("nocache", r.FormValue("nocache"))
|
job.Setenv("nocache", r.FormValue("nocache"))
|
||||||
job.Setenv("rm", r.FormValue("rm"))
|
job.Setenv("rm", r.FormValue("rm"))
|
||||||
job.SetenvJson("authConfig", authConfig)
|
job.SetenvJson("auth", configFile)
|
||||||
job.SetenvJson("configFile", configFile)
|
|
||||||
|
|
||||||
if err := job.Run(); err != nil {
|
if err := job.Run(); err != nil {
|
||||||
if !job.Stdout.Used() {
|
if !job.Stdout.Used() {
|
||||||
|
@ -930,6 +977,11 @@ func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
|
w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ping(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
_, err := w.Write([]byte{'O', 'K'})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc {
|
func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
// log the request
|
// log the request
|
||||||
|
@ -998,6 +1050,7 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
|
||||||
}
|
}
|
||||||
m := map[string]map[string]HttpApiFunc{
|
m := map[string]map[string]HttpApiFunc{
|
||||||
"GET": {
|
"GET": {
|
||||||
|
"/_ping": ping,
|
||||||
"/events": getEvents,
|
"/events": getEvents,
|
||||||
"/info": getInfo,
|
"/info": getInfo,
|
||||||
"/version": getVersion,
|
"/version": getVersion,
|
||||||
|
@ -1013,6 +1066,7 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
|
||||||
"/containers/{name:.*}/changes": getContainersChanges,
|
"/containers/{name:.*}/changes": getContainersChanges,
|
||||||
"/containers/{name:.*}/json": getContainersByName,
|
"/containers/{name:.*}/json": getContainersByName,
|
||||||
"/containers/{name:.*}/top": getContainersTop,
|
"/containers/{name:.*}/top": getContainersTop,
|
||||||
|
"/containers/{name:.*}/logs": getContainersLogs,
|
||||||
"/containers/{name:.*}/attach/ws": wsContainersAttach,
|
"/containers/{name:.*}/attach/ws": wsContainersAttach,
|
||||||
},
|
},
|
||||||
"POST": {
|
"POST": {
|
||||||
|
@ -1220,6 +1274,9 @@ func ListenAndServe(proto, addr string, job *engine.Job) error {
|
||||||
// ServeApi loops through all of the protocols sent in to docker and spawns
|
// ServeApi loops through all of the protocols sent in to docker and spawns
|
||||||
// off a go routine to setup a serving http.Server for each.
|
// off a go routine to setup a serving http.Server for each.
|
||||||
func ServeApi(job *engine.Job) engine.Status {
|
func ServeApi(job *engine.Job) engine.Status {
|
||||||
|
if len(job.Args) == 0 {
|
||||||
|
return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
protoAddrs = job.Args
|
protoAddrs = job.Args
|
||||||
chErrors = make(chan error, len(protoAddrs))
|
chErrors = make(chan error, len(protoAddrs))
|
||||||
|
@ -1232,6 +1289,9 @@ func ServeApi(job *engine.Job) engine.Status {
|
||||||
|
|
||||||
for _, protoAddr := range protoAddrs {
|
for _, protoAddr := range protoAddrs {
|
||||||
protoAddrParts := strings.SplitN(protoAddr, "://", 2)
|
protoAddrParts := strings.SplitN(protoAddr, "://", 2)
|
||||||
|
if len(protoAddrParts) != 2 {
|
||||||
|
return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
log.Printf("Listening for HTTP on %s (%s)\n", protoAddrParts[0], protoAddrParts[1])
|
log.Printf("Listening for HTTP on %s (%s)\n", protoAddrParts[0], protoAddrParts[1])
|
||||||
chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job)
|
chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job)
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/api"
|
"github.com/dotcloud/docker/api"
|
||||||
"github.com/dotcloud/docker/engine"
|
"github.com/dotcloud/docker/engine"
|
||||||
"github.com/dotcloud/docker/utils"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -57,15 +57,7 @@ func TesthttpError(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetVersion(t *testing.T) {
|
func TestGetVersion(t *testing.T) {
|
||||||
tmp, err := utils.TestDirectory("")
|
eng := engine.New()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmp)
|
|
||||||
eng, err := engine.New(tmp)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
var called bool
|
var called bool
|
||||||
eng.Register("version", func(job *engine.Job) engine.Status {
|
eng.Register("version", func(job *engine.Job) engine.Status {
|
||||||
called = true
|
called = true
|
||||||
|
@ -80,49 +72,21 @@ func TestGetVersion(t *testing.T) {
|
||||||
}
|
}
|
||||||
return engine.StatusOK
|
return engine.StatusOK
|
||||||
})
|
})
|
||||||
|
r := serveRequest("GET", "/version", nil, eng, t)
|
||||||
r := httptest.NewRecorder()
|
|
||||||
req, err := http.NewRequest("GET", "/version", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// FIXME getting the version should require an actual running Server
|
|
||||||
if err := ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !called {
|
if !called {
|
||||||
t.Fatalf("handler was not called")
|
t.Fatalf("handler was not called")
|
||||||
}
|
}
|
||||||
out := engine.NewOutput()
|
v := readEnv(r.Body, t)
|
||||||
v, err := out.AddEnv()
|
if v.Get("Version") != "42.1" {
|
||||||
if err != nil {
|
t.Fatalf("%#v\n", v)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
}
|
||||||
if _, err := io.Copy(out, r.Body); err != nil {
|
if r.HeaderMap.Get("Content-Type") != "application/json" {
|
||||||
t.Fatal(err)
|
t.Fatalf("%#v\n", r)
|
||||||
}
|
|
||||||
out.Close()
|
|
||||||
expected := "42.1"
|
|
||||||
if result := v.Get("Version"); result != expected {
|
|
||||||
t.Errorf("Expected version %s, %s found", expected, result)
|
|
||||||
}
|
|
||||||
expected = "application/json"
|
|
||||||
if result := r.HeaderMap.Get("Content-Type"); result != expected {
|
|
||||||
t.Errorf("Expected Content-Type %s, %s found", expected, result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetInfo(t *testing.T) {
|
func TestGetInfo(t *testing.T) {
|
||||||
tmp, err := utils.TestDirectory("")
|
eng := engine.New()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmp)
|
|
||||||
eng, err := engine.New(tmp)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var called bool
|
var called bool
|
||||||
eng.Register("info", func(job *engine.Job) engine.Status {
|
eng.Register("info", func(job *engine.Job) engine.Status {
|
||||||
called = true
|
called = true
|
||||||
|
@ -134,47 +98,51 @@ func TestGetInfo(t *testing.T) {
|
||||||
}
|
}
|
||||||
return engine.StatusOK
|
return engine.StatusOK
|
||||||
})
|
})
|
||||||
|
r := serveRequest("GET", "/info", nil, eng, t)
|
||||||
r := httptest.NewRecorder()
|
|
||||||
req, err := http.NewRequest("GET", "/info", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// FIXME getting the version should require an actual running Server
|
|
||||||
if err := ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !called {
|
if !called {
|
||||||
t.Fatalf("handler was not called")
|
t.Fatalf("handler was not called")
|
||||||
}
|
}
|
||||||
|
v := readEnv(r.Body, t)
|
||||||
|
if v.GetInt("Images") != 42000 {
|
||||||
|
t.Fatalf("%#v\n", v)
|
||||||
|
}
|
||||||
|
if v.GetInt("Containers") != 1 {
|
||||||
|
t.Fatalf("%#v\n", v)
|
||||||
|
}
|
||||||
|
if r.HeaderMap.Get("Content-Type") != "application/json" {
|
||||||
|
t.Fatalf("%#v\n", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
out := engine.NewOutput()
|
func serveRequest(method, target string, body io.Reader, eng *engine.Engine, t *testing.T) *httptest.ResponseRecorder {
|
||||||
i, err := out.AddEnv()
|
r := httptest.NewRecorder()
|
||||||
|
req, err := http.NewRequest(method, target, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if _, err := io.Copy(out, r.Body); err != nil {
|
if err := ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func readEnv(src io.Reader, t *testing.T) *engine.Env {
|
||||||
|
out := engine.NewOutput()
|
||||||
|
v, err := out.AddEnv()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(out, src); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
out.Close()
|
out.Close()
|
||||||
{
|
return v
|
||||||
expected := 42000
|
}
|
||||||
result := i.GetInt("Images")
|
|
||||||
if expected != result {
|
func toJson(data interface{}, t *testing.T) io.Reader {
|
||||||
t.Fatalf("%#v\n", result)
|
var buf bytes.Buffer
|
||||||
}
|
if err := json.NewEncoder(&buf).Encode(data); err != nil {
|
||||||
}
|
t.Fatal(err)
|
||||||
{
|
}
|
||||||
expected := 1
|
return &buf
|
||||||
result := i.GetInt("Containers")
|
|
||||||
if expected != result {
|
|
||||||
t.Fatalf("%#v\n", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
expected := "application/json"
|
|
||||||
if result := r.HeaderMap.Get("Content-Type"); result != expected {
|
|
||||||
t.Fatalf("%#v\n", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ func ApplyLayer(dest string, layer ArchiveReader) error {
|
||||||
parent := filepath.Dir(hdr.Name)
|
parent := filepath.Dir(hdr.Name)
|
||||||
parentPath := filepath.Join(dest, parent)
|
parentPath := filepath.Join(dest, parent)
|
||||||
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
||||||
err = os.MkdirAll(parentPath, 600)
|
err = os.MkdirAll(parentPath, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,19 +2,25 @@ package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
api "github.com/dotcloud/docker/api/server"
|
api "github.com/dotcloud/docker/api/server"
|
||||||
|
"github.com/dotcloud/docker/daemon/networkdriver/bridge"
|
||||||
"github.com/dotcloud/docker/engine"
|
"github.com/dotcloud/docker/engine"
|
||||||
"github.com/dotcloud/docker/runtime/networkdriver/bridge"
|
"github.com/dotcloud/docker/registry"
|
||||||
"github.com/dotcloud/docker/server"
|
"github.com/dotcloud/docker/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Register(eng *engine.Engine) {
|
func Register(eng *engine.Engine) error {
|
||||||
daemon(eng)
|
if err := daemon(eng); err != nil {
|
||||||
remote(eng)
|
return err
|
||||||
|
}
|
||||||
|
if err := remote(eng); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return registry.NewService().Install(eng)
|
||||||
}
|
}
|
||||||
|
|
||||||
// remote: a RESTful api for cross-docker communication
|
// remote: a RESTful api for cross-docker communication
|
||||||
func remote(eng *engine.Engine) {
|
func remote(eng *engine.Engine) error {
|
||||||
eng.Register("serveapi", api.ServeApi)
|
return eng.Register("serveapi", api.ServeApi)
|
||||||
}
|
}
|
||||||
|
|
||||||
// daemon: a default execution and storage backend for Docker on Linux,
|
// daemon: a default execution and storage backend for Docker on Linux,
|
||||||
|
@ -32,7 +38,9 @@ func remote(eng *engine.Engine) {
|
||||||
//
|
//
|
||||||
// These components should be broken off into plugins of their own.
|
// These components should be broken off into plugins of their own.
|
||||||
//
|
//
|
||||||
func daemon(eng *engine.Engine) {
|
func daemon(eng *engine.Engine) error {
|
||||||
eng.Register("initserver", server.InitServer)
|
if err := eng.Register("initserver", server.InitServer); err != nil {
|
||||||
eng.Register("init_networkdriver", bridge.InitDriver)
|
return err
|
||||||
|
}
|
||||||
|
return eng.Register("init_networkdriver", bridge.InitDriver)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,13 @@ set -e
|
||||||
# bits of this were adapted from lxc-checkconfig
|
# bits of this were adapted from lxc-checkconfig
|
||||||
# see also https://github.com/lxc/lxc/blob/lxc-1.0.2/src/lxc/lxc-checkconfig.in
|
# see also https://github.com/lxc/lxc/blob/lxc-1.0.2/src/lxc/lxc-checkconfig.in
|
||||||
|
|
||||||
: ${CONFIG:=/proc/config.gz}
|
possibleConfigs=(
|
||||||
|
'/proc/config.gz'
|
||||||
|
"/boot/config-$(uname -r)"
|
||||||
|
"/usr/src/linux-$(uname -r)/.config"
|
||||||
|
'/usr/src/linux/.config'
|
||||||
|
)
|
||||||
|
: ${CONFIG:="${possibleConfigs[0]}"}
|
||||||
|
|
||||||
if ! command -v zgrep &> /dev/null; then
|
if ! command -v zgrep &> /dev/null; then
|
||||||
zgrep() {
|
zgrep() {
|
||||||
|
@ -74,11 +80,7 @@ check_flags() {
|
||||||
|
|
||||||
if [ ! -e "$CONFIG" ]; then
|
if [ ! -e "$CONFIG" ]; then
|
||||||
wrap_warning "warning: $CONFIG does not exist, searching other paths for kernel config..."
|
wrap_warning "warning: $CONFIG does not exist, searching other paths for kernel config..."
|
||||||
for tryConfig in \
|
for tryConfig in "${possibleConfigs[@]}"; do
|
||||||
'/proc/config.gz' \
|
|
||||||
"/boot/config-$(uname -r)" \
|
|
||||||
'/usr/src/linux/.config' \
|
|
||||||
; do
|
|
||||||
if [ -e "$tryConfig" ]; then
|
if [ -e "$tryConfig" ]; then
|
||||||
CONFIG="$tryConfig"
|
CONFIG="$tryConfig"
|
||||||
break
|
break
|
||||||
|
@ -98,12 +100,16 @@ echo
|
||||||
echo 'Generally Necessary:'
|
echo 'Generally Necessary:'
|
||||||
|
|
||||||
echo -n '- '
|
echo -n '- '
|
||||||
cgroupCpuDir="$(awk '/[, ]cpu([, ]|$)/ && $8 == "cgroup" { print $5 }' /proc/$$/mountinfo | head -n1)"
|
cgroupSubsystemDir="$(awk '/[, ](cpu|cpuacct|cpuset|devices|freezer|memory)([, ]|$)/ && $8 == "cgroup" { print $5 }' /proc/$$/mountinfo | head -n1)"
|
||||||
cgroupDir="$(dirname "$cgroupCpuDir")"
|
cgroupDir="$(dirname "$cgroupSubsystemDir")"
|
||||||
if [ -d "$cgroupDir/cpu" ]; then
|
if [ -d "$cgroupDir/cpu" -o -d "$cgroupDir/cpuacct" -o -d "$cgroupDir/cpuset" -o -d "$cgroupDir/devices" -o -d "$cgroupDir/freezer" -o -d "$cgroupDir/memory" ]; then
|
||||||
echo "$(wrap_good 'cgroup hierarchy' 'properly mounted') [$cgroupDir]"
|
echo "$(wrap_good 'cgroup hierarchy' 'properly mounted') [$cgroupDir]"
|
||||||
else
|
else
|
||||||
echo "$(wrap_bad 'cgroup hierarchy' 'single mountpoint!') [$cgroupCpuDir]"
|
if [ "$cgroupSubsystemDir" ]; then
|
||||||
|
echo "$(wrap_bad 'cgroup hierarchy' 'single mountpoint!') [$cgroupSubsystemDir]"
|
||||||
|
else
|
||||||
|
echo "$(wrap_bad 'cgroup hierarchy' 'nonexistent??')"
|
||||||
|
fi
|
||||||
echo " $(wrap_color '(see https://github.com/tianon/cgroupfs-mount)' yellow)"
|
echo " $(wrap_color '(see https://github.com/tianon/cgroupfs-mount)' yellow)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -112,7 +118,8 @@ flags=(
|
||||||
DEVPTS_MULTIPLE_INSTANCES
|
DEVPTS_MULTIPLE_INSTANCES
|
||||||
CGROUPS CGROUP_DEVICE
|
CGROUPS CGROUP_DEVICE
|
||||||
MACVLAN VETH BRIDGE
|
MACVLAN VETH BRIDGE
|
||||||
IP_NF_TARGET_MASQUERADE NETFILTER_XT_MATCH_{ADDRTYPE,CONNTRACK}
|
NF_NAT_IPV4 IP_NF_TARGET_MASQUERADE
|
||||||
|
NETFILTER_XT_MATCH_{ADDRTYPE,CONNTRACK}
|
||||||
NF_NAT NF_NAT_NEEDED
|
NF_NAT NF_NAT_NEEDED
|
||||||
)
|
)
|
||||||
check_flags "${flags[@]}"
|
check_flags "${flags[@]}"
|
||||||
|
|
|
@ -3,7 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/runtime/graphdriver/devmapper"
|
"github.com/dotcloud/docker/daemon/graphdriver/devmapper"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
# these are generated by the md/md2man-all.sh script
|
||||||
|
man*
|
|
@ -0,0 +1,5 @@
|
||||||
|
FROM fedora:20
|
||||||
|
MAINTAINER ipbabble <emailwhenry@redhat.com>
|
||||||
|
# Update and install pandoc
|
||||||
|
RUN yum -y update; yum clean all;
|
||||||
|
RUN yum -y install pandoc;
|
|
@ -0,0 +1,71 @@
|
||||||
|
Docker Documentation
|
||||||
|
====================
|
||||||
|
|
||||||
|
This directory contains the Docker user manual in the Markdown format.
|
||||||
|
Do *not* edit the man pages in the man1 directory. Instead, amend the
|
||||||
|
Markdown (*.md) files.
|
||||||
|
|
||||||
|
# File List
|
||||||
|
|
||||||
|
docker.md
|
||||||
|
docker-attach.md
|
||||||
|
docker-build.md
|
||||||
|
docker-commit.md
|
||||||
|
docker-cp.md
|
||||||
|
docker-diff.md
|
||||||
|
docker-events.md
|
||||||
|
docker-export.md
|
||||||
|
docker-history.md
|
||||||
|
docker-images.md
|
||||||
|
docker-import.md
|
||||||
|
docker-info.md
|
||||||
|
docker-inspect.md
|
||||||
|
docker-kill.md
|
||||||
|
docker-load.md
|
||||||
|
docker-login.md
|
||||||
|
docker-logs.md
|
||||||
|
docker-port.md
|
||||||
|
docker-ps.md
|
||||||
|
docker-pull.md
|
||||||
|
docker-push.md
|
||||||
|
docker-restart.md
|
||||||
|
docker-rmi.md
|
||||||
|
docker-rm.md
|
||||||
|
docker-run.md
|
||||||
|
docker-save.md
|
||||||
|
docker-search.md
|
||||||
|
docker-start.md
|
||||||
|
docker-stop.md
|
||||||
|
docker-tag.md
|
||||||
|
docker-top.md
|
||||||
|
docker-wait.md
|
||||||
|
Dockerfile
|
||||||
|
md2man-all.sh
|
||||||
|
|
||||||
|
# Generating man pages from the Markdown files
|
||||||
|
|
||||||
|
The recommended approach for generating the man pages is via a Docker
|
||||||
|
container. Using the supplied Dockerfile, Docker will create a Fedora based
|
||||||
|
container and isolate the Pandoc installation. This is a seamless process,
|
||||||
|
saving you from dealing with Pandoc and dependencies on your own computer.
|
||||||
|
|
||||||
|
## Building the Fedora / Pandoc image
|
||||||
|
|
||||||
|
There is a Dockerfile provided in the `docker/contrib/man/md` directory.
|
||||||
|
|
||||||
|
Using this Dockerfile, create a Docker image tagged `fedora/pandoc`:
|
||||||
|
|
||||||
|
docker build -t fedora/pandoc .
|
||||||
|
|
||||||
|
## Utilizing the Fedora / Pandoc image
|
||||||
|
|
||||||
|
Once the image is built, run a container using the image with *volumes*:
|
||||||
|
|
||||||
|
docker run -v /<path-to-git-dir>/docker/contrib/man:/pandoc:rw \
|
||||||
|
-w /pandoc -i fedora/pandoc /pandoc/md/md2man-all.sh
|
||||||
|
|
||||||
|
The Pandoc Docker container will process the Markdown files and generate
|
||||||
|
the man pages inside the `docker/contrib/man/man1` directory using
|
||||||
|
Docker volumes. For more information on Docker volumes see the man page for
|
||||||
|
`docker run` and also look at the article [Sharing Directories via Volumes]
|
||||||
|
(http://docs.docker.io/use/working_with_volumes/).
|
|
@ -0,0 +1,57 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-attach - Attach to a running container
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker attach** **--no-stdin**[=*false*] **--sig-proxy**[=*true*] CONTAINER
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
If you **docker run** a container in detached mode (**-d**), you can reattach to
|
||||||
|
the detached container with **docker attach** using the container's ID or name.
|
||||||
|
|
||||||
|
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.
|
||||||
|
When you detach from a container the exit code will be returned to
|
||||||
|
the client.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
**--no-stdin**=*true*|*false*
|
||||||
|
When set to true, do not attach to stdin. The default is *false*.
|
||||||
|
|
||||||
|
**--sig-proxy**=*true*|*false*:
|
||||||
|
When set to true, proxify all received signal to the process (even in non-tty
|
||||||
|
mode). The default is *true*.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Attaching to a container
|
||||||
|
|
||||||
|
In this example the top command is run inside a container, from an image called
|
||||||
|
fedora, in detached mode. The ID from the container is passed into the **docker
|
||||||
|
attach** command:
|
||||||
|
|
||||||
|
# ID=$(sudo docker run -d fedora /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
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,82 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-build - Build a container image from a Dockerfile source at PATH
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker build** [**--no-cache**[=*false*]] [**-q**|**--quiet**[=*false*]]
|
||||||
|
[**--rm**] [**-t**|**--tag**=TAG] PATH | URL | -
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
This will read the Dockerfile from the directory specified in **PATH**.
|
||||||
|
It also sends 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.
|
||||||
|
|
||||||
|
Warning, this will send a lot of data to the Docker daemon depending
|
||||||
|
on the contents of the current directory. The build is run by the Docker
|
||||||
|
daemon, not by the CLI, so the whole context must be transferred to the daemon.
|
||||||
|
The Docker CLI reports "Uploading context" when the context is sent to
|
||||||
|
the daemon.
|
||||||
|
|
||||||
|
When a single Dockerfile is given as the URL, then no context is set.
|
||||||
|
When a Git repository is set as the **URL**, the repository is used
|
||||||
|
as context.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
|
||||||
|
**-q**, **--quiet**=*true*|*false*
|
||||||
|
When set to true, suppress verbose build output. Default is *false*.
|
||||||
|
|
||||||
|
**--rm**=*true*|*false*
|
||||||
|
When true, remove intermediate containers that are created during the
|
||||||
|
build process. The default is true.
|
||||||
|
|
||||||
|
**-t**, **--tag**=*tag*
|
||||||
|
Tag to be applied to the resulting image on successful completion of
|
||||||
|
the build.
|
||||||
|
|
||||||
|
**--no-cache**=*true*|*false*
|
||||||
|
When set to true, do not use a cache when building the image. The
|
||||||
|
default is *false*.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Building an image using a Dockefile located inside the current directory
|
||||||
|
|
||||||
|
Docker images can be built using the build command and a Dockerfile:
|
||||||
|
|
||||||
|
docker build .
|
||||||
|
|
||||||
|
During the build process Docker creates intermediate images. In order to
|
||||||
|
keep them, you must explicitly set `--rm=false`.
|
||||||
|
|
||||||
|
docker build --rm=false .
|
||||||
|
|
||||||
|
A good practice is to make a sub-directory with a related name and create
|
||||||
|
the Dockerfile in that directory. For example, a directory called mongo may
|
||||||
|
contain a Dockerfile to create a Docker MongoDB image. Likewise, another
|
||||||
|
directory called httpd may be used to store Dockerfiles for Apache web
|
||||||
|
server images.
|
||||||
|
|
||||||
|
It is also a good practice to add the files required for the image to the
|
||||||
|
sub-directory. These files will then be specified with the `ADD` instruction
|
||||||
|
in the Dockerfile. Note: If you include a tar file (a good practice!), then
|
||||||
|
Docker will automatically extract the contents of the tar file
|
||||||
|
specified within the `ADD` instruction into the specified target.
|
||||||
|
|
||||||
|
## Building an image using a URL
|
||||||
|
|
||||||
|
This will clone the specified Github repository from the URL and use it
|
||||||
|
as context. The Dockerfile at the root of the repository is used as
|
||||||
|
Dockerfile. This only works if the Github repository is a dedicated
|
||||||
|
repository.
|
||||||
|
|
||||||
|
docker build github.com/scollier/Fedora-Dockerfiles/tree/master/apache
|
||||||
|
|
||||||
|
Note: You can set an arbitrary Git repository via the `git://` schema.
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
March 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,34 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-commit - Create a new image from the changes to an existing
|
||||||
|
container
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker commit** **-a**|**--author**[=""] **-m**|**--message**[=""]
|
||||||
|
CONTAINER [REPOSITORY[:TAG]]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Using an existing container's name or ID you can create a new image.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
**-a, --author**=""
|
||||||
|
Author name. (eg. "John Hannibal Smith <hannibal@a-team.com>"
|
||||||
|
|
||||||
|
**-m, --message**=""
|
||||||
|
Commit message
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Creating a new image from an existing container
|
||||||
|
An existing Fedora based container has had Apache installed while running
|
||||||
|
in interactive mode with the bash shell. Apache is also running. To
|
||||||
|
create a new image run docker ps to find the container's ID and then run:
|
||||||
|
|
||||||
|
# docker commit -me= "Added Apache to Fedora base image" \
|
||||||
|
--a="A D Ministrator" 98bd7fc99854 fedora/fedora_httpd:20
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and in
|
|
@ -0,0 +1,24 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-cp - Copy files/folders from the PATH to the HOSTPATH
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker cp** CONTAINER:PATH HOSTPATH
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Copy files/folders from the containers filesystem to the host
|
||||||
|
path. Paths are relative to the root of the filesystem. Files
|
||||||
|
can be copied from a running or stopped container.
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
An important shell script file, created in a bash shell, is copied from
|
||||||
|
the exited container to the current dir on the host:
|
||||||
|
|
||||||
|
# docker cp c071f3c3ee81:setup.sh .
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-diff - Inspect changes on a container's filesystem
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker diff** CONTAINER
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Inspect changes on a container's filesystem. You can use the full or
|
||||||
|
shortened container ID or the container name set using
|
||||||
|
**docker run --name** option.
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
Inspect the changes to on a nginx container:
|
||||||
|
|
||||||
|
# docker diff 1fdfd1f54c1b
|
||||||
|
C /dev
|
||||||
|
C /dev/console
|
||||||
|
C /dev/core
|
||||||
|
C /dev/stdout
|
||||||
|
C /dev/fd
|
||||||
|
C /dev/ptmx
|
||||||
|
C /dev/stderr
|
||||||
|
C /dev/stdin
|
||||||
|
C /run
|
||||||
|
A /run/nginx.pid
|
||||||
|
C /var/lib/nginx/tmp
|
||||||
|
A /var/lib/nginx/tmp/client_body
|
||||||
|
A /var/lib/nginx/tmp/fastcgi
|
||||||
|
A /var/lib/nginx/tmp/proxy
|
||||||
|
A /var/lib/nginx/tmp/scgi
|
||||||
|
A /var/lib/nginx/tmp/uwsgi
|
||||||
|
C /var/log/nginx
|
||||||
|
A /var/log/nginx/access.log
|
||||||
|
A /var/log/nginx/error.log
|
||||||
|
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-events - Get real time events from the server
|
||||||
|
|
||||||
|
**docker events** **--since**=""|*epoch-time*
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Get event information from the Docker daemon. Information can include historical
|
||||||
|
information and real-time information.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
**--since**=""
|
||||||
|
Show previously created events and then stream. This can be in either
|
||||||
|
seconds since epoch, or date string.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Listening for Docker events
|
||||||
|
|
||||||
|
After running docker events a container 786d698004576 is started and stopped
|
||||||
|
(The container name has been shortened in the ouput below):
|
||||||
|
|
||||||
|
# docker events
|
||||||
|
[2014-04-12 18:23:04 -0400 EDT] 786d69800457: (from whenry/testimage:latest) start
|
||||||
|
[2014-04-12 18:23:13 -0400 EDT] 786d69800457: (from whenry/testimage:latest) die
|
||||||
|
[2014-04-12 18:23:13 -0400 EDT] 786d69800457: (from whenry/testimage:latest) stop
|
||||||
|
|
||||||
|
## Listening for events since a given date
|
||||||
|
Again the output container IDs have been shortened for the purposes of this document:
|
||||||
|
|
||||||
|
# docker events --since '2014-04-12'
|
||||||
|
[2014-04-12 18:11:28 -0400 EDT] c655dbf640dc: (from whenry/testimage:latest) create
|
||||||
|
[2014-04-12 18:11:28 -0400 EDT] c655dbf640dc: (from whenry/testimage:latest) start
|
||||||
|
[2014-04-12 18:14:13 -0400 EDT] 786d69800457: (from whenry/testimage:latest) create
|
||||||
|
[2014-04-12 18:14:13 -0400 EDT] 786d69800457: (from whenry/testimage:latest) start
|
||||||
|
[2014-04-12 18:22:44 -0400 EDT] 786d69800457: (from whenry/testimage:latest) die
|
||||||
|
[2014-04-12 18:22:44 -0400 EDT] 786d69800457: (from whenry/testimage:latest) stop
|
||||||
|
[2014-04-12 18:23:04 -0400 EDT] 786d69800457: (from whenry/testimage:latest) start
|
||||||
|
[2014-04-12 18:23:13 -0400 EDT] 786d69800457: (from whenry/testimage:latest) die
|
||||||
|
[2014-04-12 18:23:13 -0400 EDT] 786d69800457: (from whenry/testimage:latest) stop
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,26 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-export - Export the contents of a filesystem as a tar archive to
|
||||||
|
STDOUT.
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker export** CONTAINER
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Export the contents of a container's filesystem using the full or shortened
|
||||||
|
container ID or container name. The output is exported to STDOUT and can be
|
||||||
|
redirected to a tar file.
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
Export the contents of the container called angry_bell to a tar file
|
||||||
|
called test.tar:
|
||||||
|
|
||||||
|
# docker export angry_bell > test.tar
|
||||||
|
# ls *.tar
|
||||||
|
test.tar
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,32 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-history - Show the history of an image
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker history** **--no-trunc**[=*false*] [**-q**|**--quiet**[=*false*]]
|
||||||
|
IMAGE
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
Show the history of when and how an image was created.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
|
||||||
|
**--no-trunc**=*true*|*false*
|
||||||
|
When true don't truncate output. Default is false
|
||||||
|
|
||||||
|
**-q**, **--quiet=*true*|*false*
|
||||||
|
When true only show numeric IDs. Default is false.
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
$ sudo docker history fedora
|
||||||
|
IMAGE CREATED CREATED BY SIZE
|
||||||
|
105182bb5e8b 5 days ago /bin/sh -c #(nop) ADD file:71356d2ad59aa3119d 372.7 MB
|
||||||
|
73bd853d2ea5 13 days ago /bin/sh -c #(nop) MAINTAINER Lokesh Mandvekar 0 B
|
||||||
|
511136ea3c5a 10 months ago 0 B
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,99 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-images - List the images in the local repository
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker images**
|
||||||
|
[**-a**|**--all**=*false*]
|
||||||
|
[**--no-trunc**[=*false*]
|
||||||
|
[**-q**|**--quiet**[=*false*]
|
||||||
|
[**-t**|**--tree**=*false*]
|
||||||
|
[**-v**|**--viz**=*false*]
|
||||||
|
[NAME]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
This command lists the images stored in the local Docker repository.
|
||||||
|
|
||||||
|
By default, intermediate images, used during builds, are not listed. Some of the
|
||||||
|
output, e.g. image ID, is truncated, for space reasons. However the truncated
|
||||||
|
image ID, and often the first few characters, are enough to be used in other
|
||||||
|
Docker commands that use the image ID. The output includes repository, tag, image
|
||||||
|
ID, date created and the virtual size.
|
||||||
|
|
||||||
|
The title REPOSITORY for the first title may seem confusing. It is essentially
|
||||||
|
the image name. However, because you can tag a specific image, and multiple tags
|
||||||
|
(image instances) can be associated with a single name, the name is really a
|
||||||
|
repository for all tagged images of the same name. For example consider an image
|
||||||
|
called fedora. It may be tagged with 18, 19, or 20, etc. to manage different
|
||||||
|
versions.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
|
||||||
|
**-a**, **--all**=*true*|*false*
|
||||||
|
When set to true, also include all intermediate images in the list. The
|
||||||
|
default is false.
|
||||||
|
|
||||||
|
**--no-trunc**=*true*|*false*
|
||||||
|
When set to true, list the full image ID and not the truncated ID. The
|
||||||
|
default is false.
|
||||||
|
|
||||||
|
**-q**, **--quiet**=*true*|*false*
|
||||||
|
When set to true, list the complete image ID as part of the output. The
|
||||||
|
default is false.
|
||||||
|
|
||||||
|
**-t**, **--tree**=*true*|*false*
|
||||||
|
When set to true, list the images in a tree dependency tree (hierarchy)
|
||||||
|
format. The default is false.
|
||||||
|
|
||||||
|
**-v**, **--viz**=*true*|*false*
|
||||||
|
When set to true, list the graph in graphviz format. The default is
|
||||||
|
*false*.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Listing the images
|
||||||
|
|
||||||
|
To list the images in a local repository (not the registry) run:
|
||||||
|
|
||||||
|
docker images
|
||||||
|
|
||||||
|
The list will contain the image repository name, a tag for the image, and an
|
||||||
|
image ID, when it was created and its virtual size. Columns: REPOSITORY, TAG,
|
||||||
|
IMAGE ID, CREATED, and VIRTUAL SIZE.
|
||||||
|
|
||||||
|
To get a verbose list of images which contains all the intermediate images
|
||||||
|
used in builds use **-a**:
|
||||||
|
|
||||||
|
docker images -a
|
||||||
|
|
||||||
|
## List images dependency tree hierarchy
|
||||||
|
|
||||||
|
To list the images in the local repository (not the registry) in a dependency
|
||||||
|
tree format, use the **-t** option.
|
||||||
|
|
||||||
|
docker images -t
|
||||||
|
|
||||||
|
This displays a staggered hierarchy tree where the less indented image is
|
||||||
|
the oldest with dependent image layers branching inward (to the right) on
|
||||||
|
subsequent lines. The newest or top level image layer is listed last in
|
||||||
|
any tree branch.
|
||||||
|
|
||||||
|
## List images in GraphViz format
|
||||||
|
|
||||||
|
To display the list in a format consumable by a GraphViz tools run with
|
||||||
|
**-v**. For example to produce a .png graph file of the hierarchy use:
|
||||||
|
|
||||||
|
docker images --viz | dot -Tpng -o docker.png
|
||||||
|
|
||||||
|
## Listing only the shortened image IDs
|
||||||
|
|
||||||
|
Listing just the shortened image IDs. This can be useful for some automated
|
||||||
|
tools.
|
||||||
|
|
||||||
|
docker images -q
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,39 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-import - Create an empty filesystem image and import the contents
|
||||||
|
of the tarball into it.
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker import** URL|- [REPOSITORY[:TAG]]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Create a new filesystem image from the contents of a tarball (.tar,
|
||||||
|
.tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then optionally tag it.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Import from a remote location
|
||||||
|
|
||||||
|
# docker import http://example.com/exampleimage.tgz example/imagerepo
|
||||||
|
|
||||||
|
## Import from a local file
|
||||||
|
|
||||||
|
Import to docker via pipe and stdin:
|
||||||
|
|
||||||
|
# cat exampleimage.tgz | docker import - example/imagelocal
|
||||||
|
|
||||||
|
## Import from a local file and tag
|
||||||
|
|
||||||
|
Import to docker via pipe and stdin:
|
||||||
|
|
||||||
|
# cat exampleimageV2.tgz | docker import - example/imagelocal:V-2.0
|
||||||
|
|
||||||
|
## Import from a local directory
|
||||||
|
|
||||||
|
# tar -c . | docker import - exampleimagedir
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,46 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-info - Display system wide information
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker info**
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
This command displays system wide information regarding the Docker installation.
|
||||||
|
Information displayed includes the number of containers and images, pool name,
|
||||||
|
data file, metadata file, data space used, total data space, metadata space used
|
||||||
|
, total metadata space, execution driver, and the kernel version.
|
||||||
|
|
||||||
|
The data file is where the images are stored and the metadata file is where the
|
||||||
|
meta data regarding those images are stored. When run for the first time Docker
|
||||||
|
allocates a certain amount of data space and meta data space from the space
|
||||||
|
available on the volume where `/var/lib/docker` is mounted.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
There are no available options.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Display Docker system information
|
||||||
|
|
||||||
|
Here is a sample output:
|
||||||
|
|
||||||
|
# docker info
|
||||||
|
Containers: 18
|
||||||
|
Images: 95
|
||||||
|
Storage Driver: devicemapper
|
||||||
|
Pool Name: docker-8:1-170408448-pool
|
||||||
|
Data file: /var/lib/docker/devicemapper/devicemapper/data
|
||||||
|
Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata
|
||||||
|
Data Space Used: 9946.3 Mb
|
||||||
|
Data Space Total: 102400.0 Mb
|
||||||
|
Metadata Space Used: 9.9 Mb
|
||||||
|
Metadata Space Total: 2048.0 Mb
|
||||||
|
Execution Driver: native-0.1
|
||||||
|
Kernel Version: 3.10.0-116.el7.x86_64
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,229 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-inspect - Return low-level information on a container/image
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker inspect** [**-f**|**--format**="" CONTAINER|IMAGE
|
||||||
|
[CONTAINER|IMAGE...]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This displays all the information available in Docker for a given
|
||||||
|
container or image. By default, this will render all results in a JSON
|
||||||
|
array. If a format is specified, the given template will be executed for
|
||||||
|
each result.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
**-f**, **--format**=""
|
||||||
|
The text/template package of Go describes all the details of the
|
||||||
|
format. See examples section
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Getting information on a container
|
||||||
|
|
||||||
|
To get information on a container use it's ID or instance name:
|
||||||
|
|
||||||
|
#docker inspect 1eb5fabf5a03
|
||||||
|
[{
|
||||||
|
"ID": "1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b",
|
||||||
|
"Created": "2014-04-04T21:33:52.02361335Z",
|
||||||
|
"Path": "/usr/sbin/nginx",
|
||||||
|
"Args": [],
|
||||||
|
"Config": {
|
||||||
|
"Hostname": "1eb5fabf5a03",
|
||||||
|
"Domainname": "",
|
||||||
|
"User": "",
|
||||||
|
"Memory": 0,
|
||||||
|
"MemorySwap": 0,
|
||||||
|
"CpuShares": 0,
|
||||||
|
"AttachStdin": false,
|
||||||
|
"AttachStdout": false,
|
||||||
|
"AttachStderr": false,
|
||||||
|
"PortSpecs": null,
|
||||||
|
"ExposedPorts": {
|
||||||
|
"80/tcp": {}
|
||||||
|
},
|
||||||
|
"Tty": true,
|
||||||
|
"OpenStdin": false,
|
||||||
|
"StdinOnce": false,
|
||||||
|
"Env": [
|
||||||
|
"HOME=/",
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||||
|
],
|
||||||
|
"Cmd": [
|
||||||
|
"/usr/sbin/nginx"
|
||||||
|
],
|
||||||
|
"Dns": null,
|
||||||
|
"DnsSearch": null,
|
||||||
|
"Image": "summit/nginx",
|
||||||
|
"Volumes": null,
|
||||||
|
"VolumesFrom": "",
|
||||||
|
"WorkingDir": "",
|
||||||
|
"Entrypoint": null,
|
||||||
|
"NetworkDisabled": false,
|
||||||
|
"OnBuild": null,
|
||||||
|
"Context": {
|
||||||
|
"mount_label": "system_u:object_r:svirt_sandbox_file_t:s0:c0,c650",
|
||||||
|
"process_label": "system_u:system_r:svirt_lxc_net_t:s0:c0,c650"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"State": {
|
||||||
|
"Running": true,
|
||||||
|
"Pid": 858,
|
||||||
|
"ExitCode": 0,
|
||||||
|
"StartedAt": "2014-04-04T21:33:54.16259207Z",
|
||||||
|
"FinishedAt": "0001-01-01T00:00:00Z",
|
||||||
|
"Ghost": false
|
||||||
|
},
|
||||||
|
"Image": "df53773a4390e25936f9fd3739e0c0e60a62d024ea7b669282b27e65ae8458e6",
|
||||||
|
"NetworkSettings": {
|
||||||
|
"IPAddress": "172.17.0.2",
|
||||||
|
"IPPrefixLen": 16,
|
||||||
|
"Gateway": "172.17.42.1",
|
||||||
|
"Bridge": "docker0",
|
||||||
|
"PortMapping": null,
|
||||||
|
"Ports": {
|
||||||
|
"80/tcp": [
|
||||||
|
{
|
||||||
|
"HostIp": "0.0.0.0",
|
||||||
|
"HostPort": "80"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ResolvConfPath": "/etc/resolv.conf",
|
||||||
|
"HostnamePath": "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/hostname",
|
||||||
|
"HostsPath": "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/hosts",
|
||||||
|
"Name": "/ecstatic_ptolemy",
|
||||||
|
"Driver": "devicemapper",
|
||||||
|
"ExecDriver": "native-0.1",
|
||||||
|
"Volumes": {},
|
||||||
|
"VolumesRW": {},
|
||||||
|
"HostConfig": {
|
||||||
|
"Binds": null,
|
||||||
|
"ContainerIDFile": "",
|
||||||
|
"LxcConf": [],
|
||||||
|
"Privileged": false,
|
||||||
|
"PortBindings": {
|
||||||
|
"80/tcp": [
|
||||||
|
{
|
||||||
|
"HostIp": "0.0.0.0",
|
||||||
|
"HostPort": "80"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Links": null,
|
||||||
|
"PublishAllPorts": false,
|
||||||
|
"DriverOptions": {
|
||||||
|
"lxc": null
|
||||||
|
},
|
||||||
|
"CliAddress": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
## Getting the IP address of a container instance
|
||||||
|
|
||||||
|
To get the IP address of a container use:
|
||||||
|
|
||||||
|
# docker inspect --format='{{.NetworkSettings.IPAddress}}' 1eb5fabf5a03
|
||||||
|
172.17.0.2
|
||||||
|
|
||||||
|
## Listing all port bindings
|
||||||
|
|
||||||
|
One can loop over arrays and maps in the results to produce simple text
|
||||||
|
output:
|
||||||
|
|
||||||
|
# docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}} \
|
||||||
|
{{$p}} -> {{(index $conf 0).HostPort}} {{end}}' 1eb5fabf5a03
|
||||||
|
|
||||||
|
80/tcp -> 80
|
||||||
|
|
||||||
|
## Getting information on an image
|
||||||
|
|
||||||
|
Use an image's ID or name (e.g. repository/name[:tag]) to get information
|
||||||
|
on it.
|
||||||
|
|
||||||
|
# docker inspect 58394af37342
|
||||||
|
[{
|
||||||
|
"id": "58394af373423902a1b97f209a31e3777932d9321ef10e64feaaa7b4df609cf9",
|
||||||
|
"parent": "8abc22bad04266308ff408ca61cb8f6f4244a59308f7efc64e54b08b496c58db",
|
||||||
|
"created": "2014-02-03T16:10:40.500814677Z",
|
||||||
|
"container": "f718f19a28a5147da49313c54620306243734bafa63c76942ef6f8c4b4113bc5",
|
||||||
|
"container_config": {
|
||||||
|
"Hostname": "88807319f25e",
|
||||||
|
"Domainname": "",
|
||||||
|
"User": "",
|
||||||
|
"Memory": 0,
|
||||||
|
"MemorySwap": 0,
|
||||||
|
"CpuShares": 0,
|
||||||
|
"AttachStdin": false,
|
||||||
|
"AttachStdout": false,
|
||||||
|
"AttachStderr": false,
|
||||||
|
"PortSpecs": null,
|
||||||
|
"ExposedPorts": null,
|
||||||
|
"Tty": false,
|
||||||
|
"OpenStdin": false,
|
||||||
|
"StdinOnce": false,
|
||||||
|
"Env": [
|
||||||
|
"HOME=/",
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||||
|
],
|
||||||
|
"Cmd": [
|
||||||
|
"/bin/sh",
|
||||||
|
"-c",
|
||||||
|
"#(nop) ADD fedora-20-dummy.tar.xz in /"
|
||||||
|
],
|
||||||
|
"Dns": null,
|
||||||
|
"DnsSearch": null,
|
||||||
|
"Image": "8abc22bad04266308ff408ca61cb8f6f4244a59308f7efc64e54b08b496c58db",
|
||||||
|
"Volumes": null,
|
||||||
|
"VolumesFrom": "",
|
||||||
|
"WorkingDir": "",
|
||||||
|
"Entrypoint": null,
|
||||||
|
"NetworkDisabled": false,
|
||||||
|
"OnBuild": null,
|
||||||
|
"Context": null
|
||||||
|
},
|
||||||
|
"docker_version": "0.6.3",
|
||||||
|
"author": "I P Babble \u003clsm5@ipbabble.com\u003e - ./buildcontainers.sh",
|
||||||
|
"config": {
|
||||||
|
"Hostname": "88807319f25e",
|
||||||
|
"Domainname": "",
|
||||||
|
"User": "",
|
||||||
|
"Memory": 0,
|
||||||
|
"MemorySwap": 0,
|
||||||
|
"CpuShares": 0,
|
||||||
|
"AttachStdin": false,
|
||||||
|
"AttachStdout": false,
|
||||||
|
"AttachStderr": false,
|
||||||
|
"PortSpecs": null,
|
||||||
|
"ExposedPorts": null,
|
||||||
|
"Tty": false,
|
||||||
|
"OpenStdin": false,
|
||||||
|
"StdinOnce": false,
|
||||||
|
"Env": [
|
||||||
|
"HOME=/",
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||||
|
],
|
||||||
|
"Cmd": null,
|
||||||
|
"Dns": null,
|
||||||
|
"DnsSearch": null,
|
||||||
|
"Image": "8abc22bad04266308ff408ca61cb8f6f4244a59308f7efc64e54b08b496c58db",
|
||||||
|
"Volumes": null,
|
||||||
|
"VolumesFrom": "",
|
||||||
|
"WorkingDir": "",
|
||||||
|
"Entrypoint": null,
|
||||||
|
"NetworkDisabled": false,
|
||||||
|
"OnBuild": null,
|
||||||
|
"Context": null
|
||||||
|
},
|
||||||
|
"architecture": "x86_64",
|
||||||
|
"Size": 385520098
|
||||||
|
}]
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,21 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-kill - Kill a running container (send SIGKILL, or specified signal)
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker kill** **--signal**[=*"KILL"*] CONTAINER [CONTAINER...]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The main process inside each container specified will be sent SIGKILL,
|
||||||
|
or any signal specified with option --signal.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
**-s**, **--signal**=*"KILL"*
|
||||||
|
Signal to send to the container
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,36 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-load - Load an image from a tar archive on STDIN
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker load** **--input**=""
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
Loads a tarred repository from a file or the standard input stream.
|
||||||
|
Restores both images and tags.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
|
||||||
|
**-i**, **--input**=""
|
||||||
|
Read from a tar archive file, instead of STDIN
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
|
||||||
|
$ sudo docker images
|
||||||
|
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
|
||||||
|
busybox latest 769b9341d937 7 weeks ago 2.489 MB
|
||||||
|
$ sudo docker load --input fedora.tar
|
||||||
|
$ sudo docker images
|
||||||
|
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
|
||||||
|
busybox latest 769b9341d937 7 weeks ago 2.489 MB
|
||||||
|
fedora rawhide 0d20aec6529d 7 weeks ago 387 MB
|
||||||
|
fedora 20 58394af37342 7 weeks ago 385.5 MB
|
||||||
|
fedora heisenbug 58394af37342 7 weeks ago 385.5 MB
|
||||||
|
fedora latest 58394af37342 7 weeks ago 385.5 MB
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,35 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-login - Register or Login to a docker registry server.
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker login** [**-e**|**-email**=""] [**-p**|**--password**=""]
|
||||||
|
[**-u**|**--username**=""] [SERVER]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Register or Login to a docker registry server, if no server is
|
||||||
|
specified "https://index.docker.io/v1/" is the default. If you want to
|
||||||
|
login to a private registry you can specify this by adding the server name.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
**-e**, **--email**=""
|
||||||
|
Email address
|
||||||
|
|
||||||
|
**-p**, **--password**=""
|
||||||
|
Password
|
||||||
|
|
||||||
|
**-u**, **--username**=""
|
||||||
|
Username
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
|
||||||
|
## Login to a local registry
|
||||||
|
|
||||||
|
# docker login localhost:8080
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-logs - Fetch the logs of a container
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker logs** **--follow**[=*false*] CONTAINER
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
The **docker logs** command batch-retrieves whatever logs are present for
|
||||||
|
a container at the time of execution. This does not guarantee execution
|
||||||
|
order when combined with a docker run (i.e. your run may not have generated
|
||||||
|
any logs at the time you execute docker logs).
|
||||||
|
|
||||||
|
The **docker logs --follow** command combines commands **docker logs** and
|
||||||
|
**docker attach**. It will first return all logs from the beginning and
|
||||||
|
then continue streaming new output from the container’s stdout and stderr.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
**-f, --follow**=*true*|*false*
|
||||||
|
When *true*, follow log output. The default is false.
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,15 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-port - Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker port** CONTAINER PRIVATE_PORT
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,68 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-ps - List containers
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker ps** [**-a**|**--all**=*false*] [**--before**=""]
|
||||||
|
[**-l**|**--latest**=*false*] [**-n**=*-1*] [**--no-trunc**=*false*]
|
||||||
|
[**-q**|**--quiet**=*false*] [**-s**|**--size**=*false*]
|
||||||
|
[**--since**=""]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
List the containers in the local repository. By default this show only
|
||||||
|
the running containers.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
|
||||||
|
**-a**, **--all**=*true*|*false*
|
||||||
|
When true show all containers. Only running containers are shown by
|
||||||
|
default. Default is false.
|
||||||
|
|
||||||
|
**--before**=""
|
||||||
|
Show only container created before Id or Name, include non-running
|
||||||
|
ones.
|
||||||
|
|
||||||
|
**-l**, **--latest**=*true*|*false*
|
||||||
|
When true show only the latest created container, include non-running
|
||||||
|
ones. The default is false.
|
||||||
|
|
||||||
|
**-n**=NUM
|
||||||
|
Show NUM (integer) last created containers, include non-running ones.
|
||||||
|
The default is -1 (none)
|
||||||
|
|
||||||
|
**--no-trunc**=*true*|*false*
|
||||||
|
When true truncate output. Default is false.
|
||||||
|
|
||||||
|
**-q**, **--quiet**=*true*|*false*
|
||||||
|
When false only display numeric IDs. Default is false.
|
||||||
|
|
||||||
|
**-s**, **--size**=*true*|*false*
|
||||||
|
When true display container sizes. Default is false.
|
||||||
|
|
||||||
|
**--since**=""
|
||||||
|
Show only containers created since Id or Name, include non-running ones.
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
# Display all containers, including non-running
|
||||||
|
|
||||||
|
# docker ps -a
|
||||||
|
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
|
a87ecb4f327c fedora:20 /bin/sh -c #(nop) MA 20 minutes ago Exit 0 desperate_brattain
|
||||||
|
01946d9d34d8 vpavlin/rhel7:latest /bin/sh -c #(nop) MA 33 minutes ago Exit 0 thirsty_bell
|
||||||
|
c1d3b0166030 acffc0358b9e /bin/sh -c yum -y up 2 weeks ago Exit 1 determined_torvalds
|
||||||
|
41d50ecd2f57 fedora:20 /bin/sh -c #(nop) MA 2 weeks ago Exit 0 drunk_pike
|
||||||
|
|
||||||
|
# Display only IDs of all containers, including non-running
|
||||||
|
|
||||||
|
# docker ps -a -q
|
||||||
|
a87ecb4f327c
|
||||||
|
01946d9d34d8
|
||||||
|
c1d3b0166030
|
||||||
|
41d50ecd2f57
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,37 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-pull - Pull an image or a repository from the registry
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker pull** NAME[:TAG]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This command pulls down an image or a repository from the registry. If
|
||||||
|
there is more than one image for a repository (e.g. fedora) then all
|
||||||
|
images for that repository name are pulled down including any tags.
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
|
||||||
|
# Pull a reposiotry with multiple images
|
||||||
|
|
||||||
|
$ sudo docker pull fedora
|
||||||
|
Pulling repository fedora
|
||||||
|
ad57ef8d78d7: Download complete
|
||||||
|
105182bb5e8b: Download complete
|
||||||
|
511136ea3c5a: Download complete
|
||||||
|
73bd853d2ea5: Download complete
|
||||||
|
|
||||||
|
$ sudo docker images
|
||||||
|
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
|
||||||
|
fedora rawhide ad57ef8d78d7 5 days ago 359.3 MB
|
||||||
|
fedora 20 105182bb5e8b 5 days ago 372.7 MB
|
||||||
|
fedora heisenbug 105182bb5e8b 5 days ago 372.7 MB
|
||||||
|
fedora latest 105182bb5e8b 5 days ago 372.7 MB
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-push - Push an image or a repository to the registry
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker push** NAME[:TAG]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Push an image or a repository to a registry. The default registry is the Docker
|
||||||
|
Index located at [index.docker.io](https://index.docker.io/v1/). However the
|
||||||
|
image can be pushed to another, perhaps private, registry as demonstrated in
|
||||||
|
the example below.
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
|
||||||
|
# Pushing a new image to a registry
|
||||||
|
|
||||||
|
First save the new image by finding the container ID (using **docker ps**)
|
||||||
|
and then committing it to a new image name:
|
||||||
|
|
||||||
|
# docker commit c16378f943fe rhel-httpd
|
||||||
|
|
||||||
|
Now push the image to the registry using the image ID. In this example
|
||||||
|
the registry is on host named registry-host and listening on port 5000.
|
||||||
|
Default Docker commands will push to the default `index.docker.io`
|
||||||
|
registry. Instead, push to the local registry, which is on a host called
|
||||||
|
registry-host*. To do this, tag the image with the host name or IP
|
||||||
|
address, and the port of the registry:
|
||||||
|
|
||||||
|
# docker tag rhel-httpd registry-host:5000/myadmin/rhel-httpd
|
||||||
|
# docker push registry-host:5000/myadmin/rhel-httpd
|
||||||
|
|
||||||
|
Check that this worked by running:
|
||||||
|
|
||||||
|
# docker images
|
||||||
|
|
||||||
|
You should see both `rhel-httpd` and `registry-host:5000/myadmin/rhel-httpd`
|
||||||
|
listed.
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,21 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-restart - Restart a running container
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker restart** [**-t**|**--time**[=*10*]] CONTAINER [CONTAINER...]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Restart each container listed.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
**-t**, **--time**=NUM
|
||||||
|
Number of seconds to try to stop for before killing the container. Once
|
||||||
|
killed it will then be restarted. Default=10
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
docker-rm - Remove one or more containers.
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
|
||||||
|
**docker rm** [**-f**|**--force**[=*false*] [**-l**|**--link**[=*false*] [**-v**|
|
||||||
|
**--volumes**[=*false*]
|
||||||
|
CONTAINER [CONTAINER...]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
**docker rm** will remove one or more containers from the host node. The
|
||||||
|
container name or ID can be used. This does not remove images. You cannot
|
||||||
|
remove a running container unless you use the \fB-f\fR option. To see all
|
||||||
|
containers on a host use the **docker ps -a** command.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
|
||||||
|
**-f**, **--force**=*true*|*false*
|
||||||
|
When set to true, force the removal of the container. The default is
|
||||||
|
*false*.
|
||||||
|
|
||||||
|
**-l**, **--link**=*true*|*false*
|
||||||
|
When set to true, remove the specified link and not the underlying
|
||||||
|
container. The default is *false*.
|
||||||
|
|
||||||
|
**-v**, **--volumes**=*true*|*false*
|
||||||
|
When set to true, remove the volumes associated to the container. The
|
||||||
|
default is *false*.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
##Removing a container using its ID##
|
||||||
|
|
||||||
|
To remove a container using its ID, find either from a **docker ps -a**
|
||||||
|
command, or use the ID returned from the **docker run** command, or retrieve
|
||||||
|
it from a file used to store it using the **docker run --cidfile**:
|
||||||
|
|
||||||
|
docker rm abebf7571666
|
||||||
|
|
||||||
|
##Removing a container using the container name##
|
||||||
|
|
||||||
|
The name of the container can be found using the **docker ps -a**
|
||||||
|
command. The use that name as follows:
|
||||||
|
|
||||||
|
docker rm hopeful_morse
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,35 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-rmi \- Remove one or more images.
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
|
||||||
|
**docker rmi** [**-f**|**--force**[=*false*] IMAGE [IMAGE...]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This will remove one or more images from the host node. This does not
|
||||||
|
remove images from a registry. You cannot remove an image of a running
|
||||||
|
container unless you use the **-f** option. To see all images on a host
|
||||||
|
use the **docker images** command.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
|
||||||
|
**-f**, **--force**=*true*|*false*
|
||||||
|
When set to true, force the removal of the image. The default is
|
||||||
|
*false*.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Removing an image
|
||||||
|
|
||||||
|
Here is an example of removing and image:
|
||||||
|
|
||||||
|
docker rmi fedora/httpd
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,343 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-run - Run a process in an isolated container
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker run**
|
||||||
|
[**-a**|**--attach**[=]] [**-c**|**--cpu-shares**[=0]
|
||||||
|
[**-m**|**--memory**=*memory-limit*]
|
||||||
|
[**--cidfile**=*file*] [**-d**|**--detach**[=*false*]] [**--dns**=*IP-address*]
|
||||||
|
[**--name**=*name*] [**-u**|**--user**=*username*|*uid*]
|
||||||
|
[**--link**=*name*:*alias*]
|
||||||
|
[**-e**|**--env**=*environment*] [**--entrypoint**=*command*]
|
||||||
|
[**--expose**=*port*] [**-P**|**--publish-all**[=*false*]]
|
||||||
|
[**-p**|**--publish**=*port-mappping*] [**-h**|**--hostname**=*hostname*]
|
||||||
|
[**--rm**[=*false*]] [**--priviledged**[=*false*]
|
||||||
|
[**-i**|**--interactive**[=*false*]
|
||||||
|
[**-t**|**--tty**[=*false*]] [**--lxc-conf**=*options*]
|
||||||
|
[**-n**|**--networking**[=*true*]]
|
||||||
|
[**-v**|**--volume**=*volume*] [**--volumes-from**=*container-id*]
|
||||||
|
[**-w**|**--workdir**=*directory*] [**--sig-proxy**[=*true*]]
|
||||||
|
IMAGE [COMMAND] [ARG...]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
Run a process in a new container. **docker run** starts a process with its own
|
||||||
|
file system, its own networking, and its own isolated process tree. The IMAGE
|
||||||
|
which starts the process may define defaults related to the process that will be
|
||||||
|
run in the container, the networking to expose, and more, but **docker run**
|
||||||
|
gives final control to the operator or administrator who starts the container
|
||||||
|
from the image. For that reason **docker run** has more options than any other
|
||||||
|
Docker command.
|
||||||
|
|
||||||
|
If the IMAGE is not already loaded then **docker run** will pull the IMAGE, and
|
||||||
|
all image dependencies, from the repository in the same way running **docker
|
||||||
|
pull** IMAGE, before it starts the container from that image.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
|
||||||
|
**-a**, **--attach**=*stdin*|*stdout*|*stderr*
|
||||||
|
Attach to stdin, stdout or stderr. In foreground mode (the default when
|
||||||
|
**-d** is not specified), **docker run** can start the process in the container
|
||||||
|
and attach the console to the process’s standard input, output, and standard
|
||||||
|
error. It can even pretend to be a TTY (this is what most commandline
|
||||||
|
executables expect) and pass along signals. The **-a** option can be set for
|
||||||
|
each of stdin, stdout, and stderr.
|
||||||
|
|
||||||
|
**-c**, **--cpu-shares**=0
|
||||||
|
CPU shares in relative weight. You can increase the priority of a container
|
||||||
|
with the -c option. By default, all containers run at the same priority and get
|
||||||
|
the same proportion of CPU cycles, but you can tell the kernel to give more
|
||||||
|
shares of CPU time to one or more containers when you start them via **docker
|
||||||
|
run**.
|
||||||
|
|
||||||
|
**--cidfile**=*file*
|
||||||
|
Write the container ID to the file specified.
|
||||||
|
|
||||||
|
|
||||||
|
**-d**, **-detach**=*true*|*false*
|
||||||
|
Detached mode. This runs the container in the background. It outputs the new
|
||||||
|
container's ID and any error messages. At any time you can run **docker ps** in
|
||||||
|
the other shell to view a list of the running containers. You can reattach to a
|
||||||
|
detached container with **docker attach**. If you choose to run a container in
|
||||||
|
the detached mode, then you cannot use the **-rm** option.
|
||||||
|
|
||||||
|
|
||||||
|
**--dns**=*IP-address*
|
||||||
|
Set custom DNS servers. This option can be used to override the DNS
|
||||||
|
configuration passed to the container. Typically this is necessary when the
|
||||||
|
host DNS configuration is invalid for the container (eg. 127.0.0.1). When this
|
||||||
|
is the case the **-dns** flags is necessary for every run.
|
||||||
|
|
||||||
|
|
||||||
|
**-e**, **-env**=*environment*
|
||||||
|
Set environment variables. This option allows you to specify arbitrary
|
||||||
|
environment variables that are available for the process that will be launched
|
||||||
|
inside of the container.
|
||||||
|
|
||||||
|
|
||||||
|
**--entrypoint**=*command*
|
||||||
|
This option allows you to overwrite the default entrypoint of the image that
|
||||||
|
is set in the Dockerfile. The ENTRYPOINT of an image is similar to a COMMAND
|
||||||
|
because it specifies what executable to run when the container starts, but it is
|
||||||
|
(purposely) more difficult to override. The ENTRYPOINT gives a container its
|
||||||
|
default nature or behavior, so that when you set an ENTRYPOINT you can run the
|
||||||
|
container as if it were that binary, complete with default options, and you can
|
||||||
|
pass in more options via the COMMAND. But, sometimes an operator may want to run
|
||||||
|
something else inside the container, so you can override the default ENTRYPOINT
|
||||||
|
at runtime by using a **--entrypoint** and a string to specify the new
|
||||||
|
ENTRYPOINT.
|
||||||
|
|
||||||
|
**--expose**=*port*
|
||||||
|
Expose a port from the container without publishing it to your host. A
|
||||||
|
containers port can be exposed to other containers in three ways: 1) The
|
||||||
|
developer can expose the port using the EXPOSE parameter of the Dockerfile, 2)
|
||||||
|
the operator can use the **--expose** option with **docker run**, or 3) the
|
||||||
|
container can be started with the **--link**.
|
||||||
|
|
||||||
|
**-m**, **-memory**=*memory-limit*
|
||||||
|
Allows you to constrain the memory available to a container. If the host
|
||||||
|
supports swap memory, then the -m memory setting can be larger than physical
|
||||||
|
RAM. The memory limit format: <number><optional unit>, where unit = b, k, m or
|
||||||
|
g.
|
||||||
|
|
||||||
|
**-P**, **-publish-all**=*true*|*false*
|
||||||
|
When set to true publish all exposed ports to the host interfaces. The
|
||||||
|
default is false. If the operator uses -P (or -p) then Docker will make the
|
||||||
|
exposed port accessible on the host and the ports will be available to any
|
||||||
|
client that can reach the host. To find the map between the host ports and the
|
||||||
|
exposed ports, use **docker port**.
|
||||||
|
|
||||||
|
|
||||||
|
**-p**, **-publish**=[]
|
||||||
|
Publish a container's port to the host (format: ip:hostPort:containerPort |
|
||||||
|
ip::containerPort | hostPort:containerPort) (use **docker port** to see the
|
||||||
|
actual mapping)
|
||||||
|
|
||||||
|
|
||||||
|
**-h**, **-hostname**=*hostname*
|
||||||
|
Sets the container host name that is available inside the container.
|
||||||
|
|
||||||
|
|
||||||
|
**-i**, **-interactive**=*true*|*false*
|
||||||
|
When set to true, keep stdin open even if not attached. The default is false.
|
||||||
|
|
||||||
|
|
||||||
|
**--link**=*name*:*alias*
|
||||||
|
Add link to another container. The format is name:alias. If the operator
|
||||||
|
uses **--link** when starting the new client container, then the client
|
||||||
|
container can access the exposed port via a private networking interface. Docker
|
||||||
|
will set some environment variables in the client container to help indicate
|
||||||
|
which interface and port to use.
|
||||||
|
|
||||||
|
|
||||||
|
**-n**, **-networking**=*true*|*false*
|
||||||
|
By default, all containers have networking enabled (true) and can make
|
||||||
|
outgoing connections. The operator can disable networking with **--networking**
|
||||||
|
to false. This disables all incoming and outgoing networking. In cases like this
|
||||||
|
, I/O can only be performed through files or by using STDIN/STDOUT.
|
||||||
|
|
||||||
|
Also by default, the container will use the same DNS servers as the host. The
|
||||||
|
operator may override this with **-dns**.
|
||||||
|
|
||||||
|
|
||||||
|
**--name**=*name*
|
||||||
|
Assign a name to the container. The operator can identify a container in
|
||||||
|
three ways:
|
||||||
|
|
||||||
|
UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”)
|
||||||
|
UUID short identifier (“f78375b1c487”)
|
||||||
|
Name (“jonah”)
|
||||||
|
|
||||||
|
The UUID identifiers come from the Docker daemon, and if a name is not assigned
|
||||||
|
to the container with **--name** then the daemon will also generate a random
|
||||||
|
string name. The name is useful when defining links (see **--link**) (or any
|
||||||
|
other place you need to identify a container). This works for both background
|
||||||
|
and foreground Docker containers.
|
||||||
|
|
||||||
|
|
||||||
|
**--privileged**=*true*|*false*
|
||||||
|
Give extended privileges to this container. By default, Docker containers are
|
||||||
|
“unprivileged” (=false) and cannot, for example, run a Docker daemon inside the
|
||||||
|
Docker container. This is because by default a container is not allowed to
|
||||||
|
access any devices. A “privileged” container is given access to all devices.
|
||||||
|
|
||||||
|
When the operator executes **docker run -privileged**, Docker will enable access
|
||||||
|
to all devices on the host as well as set some configuration in AppArmor to
|
||||||
|
allow the container nearly all the same access to the host as processes running
|
||||||
|
outside of a container on the host.
|
||||||
|
|
||||||
|
|
||||||
|
**--rm**=*true*|*false*
|
||||||
|
If set to *true* the container is automatically removed when it exits. The
|
||||||
|
default is *false*. This option is incompatible with **-d**.
|
||||||
|
|
||||||
|
|
||||||
|
**--sig-proxy**=*true*|*false*
|
||||||
|
When set to true, proxify all received signals to the process (even in
|
||||||
|
non-tty mode). The default is true.
|
||||||
|
|
||||||
|
|
||||||
|
**-t**, **-tty**=*true*|*false*
|
||||||
|
When set to true Docker can allocate a pseudo-tty and attach to the standard
|
||||||
|
input of any container. This can be used, for example, to run a throwaway
|
||||||
|
interactive shell. The default is value is false.
|
||||||
|
|
||||||
|
|
||||||
|
**-u**, **-user**=*username*,*uid*
|
||||||
|
Set a username or UID for the container.
|
||||||
|
|
||||||
|
|
||||||
|
**-v**, **-volume**=*volume*
|
||||||
|
Bind mount a volume to the container. The **-v** option can be used one or
|
||||||
|
more times to add one or more mounts to a container. These mounts can then be
|
||||||
|
used in other containers using the **--volumes-from** option. See examples.
|
||||||
|
|
||||||
|
|
||||||
|
**--volumes-from**=*container-id*
|
||||||
|
Will mount volumes from the specified container identified by container-id.
|
||||||
|
Once a volume is mounted in a one container it can be shared with other
|
||||||
|
containers using the **--volumes-from** option when running those other
|
||||||
|
containers. The volumes can be shared even if the original container with the
|
||||||
|
mount is not running.
|
||||||
|
|
||||||
|
|
||||||
|
**-w**, **-workdir**=*directory*
|
||||||
|
Working directory inside the container. The default working directory for
|
||||||
|
running binaries within a container is the root directory (/). The developer can
|
||||||
|
set a different default with the Dockerfile WORKDIR instruction. The operator
|
||||||
|
can override the working directory by using the **-w** option.
|
||||||
|
|
||||||
|
|
||||||
|
**IMAGE**
|
||||||
|
The image name or ID.
|
||||||
|
|
||||||
|
|
||||||
|
**COMMAND**
|
||||||
|
The command or program to run inside the image.
|
||||||
|
|
||||||
|
|
||||||
|
**ARG**
|
||||||
|
The arguments for the command to be run in the container.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Exposing log messages from the container to the host's log
|
||||||
|
|
||||||
|
If you want messages that are logged in your container to show up in the host's
|
||||||
|
syslog/journal then you should bind mount the /var/log directory as follows.
|
||||||
|
|
||||||
|
# docker run -v /dev/log:/dev/log -i -t fedora /bin/bash
|
||||||
|
|
||||||
|
From inside the container you can test this by sending a message to the log.
|
||||||
|
|
||||||
|
(bash)# logger "Hello from my container"
|
||||||
|
|
||||||
|
Then exit and check the journal.
|
||||||
|
|
||||||
|
# exit
|
||||||
|
|
||||||
|
# journalctl -b | grep Hello
|
||||||
|
|
||||||
|
This should list the message sent to logger.
|
||||||
|
|
||||||
|
## Attaching to one or more from STDIN, STDOUT, STDERR
|
||||||
|
|
||||||
|
If you do not specify -a then Docker will attach everything (stdin,stdout,stderr)
|
||||||
|
. You can specify to which of the three standard streams (stdin, stdout, stderr)
|
||||||
|
you’d like to connect instead, as in:
|
||||||
|
|
||||||
|
# docker run -a stdin -a stdout -i -t fedora /bin/bash
|
||||||
|
|
||||||
|
## Linking Containers
|
||||||
|
|
||||||
|
The link feature allows multiple containers to communicate with each other. For
|
||||||
|
example, a container whose Dockerfile has exposed port 80 can be run and named
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
# docker run --name=link-test -d -i -t fedora/httpd
|
||||||
|
|
||||||
|
A second container, in this case called linker, can communicate with the httpd
|
||||||
|
container, named link-test, by running with the **--link=<name>:<alias>**
|
||||||
|
|
||||||
|
# docker run -t -i --link=link-test:lt --name=linker fedora /bin/bash
|
||||||
|
|
||||||
|
Now the container linker is linked to container link-test with the alias lt.
|
||||||
|
Running the **env** command in the linker container shows environment variables
|
||||||
|
with the LT (alias) context (**LT_**)
|
||||||
|
|
||||||
|
# env
|
||||||
|
HOSTNAME=668231cb0978
|
||||||
|
TERM=xterm
|
||||||
|
LT_PORT_80_TCP=tcp://172.17.0.3:80
|
||||||
|
LT_PORT_80_TCP_PORT=80
|
||||||
|
LT_PORT_80_TCP_PROTO=tcp
|
||||||
|
LT_PORT=tcp://172.17.0.3:80
|
||||||
|
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
|
PWD=/
|
||||||
|
LT_NAME=/linker/lt
|
||||||
|
SHLVL=1
|
||||||
|
HOME=/
|
||||||
|
LT_PORT_80_TCP_ADDR=172.17.0.3
|
||||||
|
_=/usr/bin/env
|
||||||
|
|
||||||
|
When linking two containers Docker will use the exposed ports of the container
|
||||||
|
to create a secure tunnel for the parent to access.
|
||||||
|
|
||||||
|
|
||||||
|
## Mapping Ports for External Usage
|
||||||
|
|
||||||
|
The exposed port of an application can be mapped to a host port using the **-p**
|
||||||
|
flag. For example a httpd port 80 can be mapped to the host port 8080 using the
|
||||||
|
following:
|
||||||
|
|
||||||
|
# docker run -p 8080:80 -d -i -t fedora/httpd
|
||||||
|
|
||||||
|
## Creating and Mounting a Data Volume Container
|
||||||
|
|
||||||
|
Many applications require the sharing of persistent data across several
|
||||||
|
containers. Docker allows you to create a Data Volume Container that other
|
||||||
|
containers can mount from. For example, create a named container that contains
|
||||||
|
directories /var/volume1 and /tmp/volume2. The image will need to contain these
|
||||||
|
directories so a couple of RUN mkdir instructions might be required for you
|
||||||
|
fedora-data image:
|
||||||
|
|
||||||
|
# docker run --name=data -v /var/volume1 -v /tmp/volume2 -i -t fedora-data true
|
||||||
|
# docker run --volumes-from=data --name=fedora-container1 -i -t fedora bash
|
||||||
|
|
||||||
|
Multiple -volumes-from parameters will bring together multiple data volumes from
|
||||||
|
multiple containers. And it's possible to mount the volumes that came from the
|
||||||
|
DATA container in yet another container via the fedora-container1 intermidiery
|
||||||
|
container, allowing to abstract the actual data source from users of that data:
|
||||||
|
|
||||||
|
# docker run --volumes-from=fedora-container1 --name=fedora-container2 -i -t fedora bash
|
||||||
|
|
||||||
|
## Mounting External Volumes
|
||||||
|
|
||||||
|
To mount a host directory as a container volume, specify the absolute path to
|
||||||
|
the directory and the absolute path for the container directory separated by a
|
||||||
|
colon:
|
||||||
|
|
||||||
|
# docker run -v /var/db:/data1 -i -t fedora bash
|
||||||
|
|
||||||
|
When using SELinux, be aware that the host has no knowledge of container SELinux
|
||||||
|
policy. Therefore, in the above example, if SELinux policy is enforced, the
|
||||||
|
`/var/db` directory is not writable to the container. A "Permission Denied"
|
||||||
|
message will occur and an avc: message in the host's syslog.
|
||||||
|
|
||||||
|
|
||||||
|
To work around this, at time of writing this man page, the following command
|
||||||
|
needs to be run in order for the proper SELinux policy type label to be attached
|
||||||
|
to the host directory:
|
||||||
|
|
||||||
|
# chcon -Rt svirt_sandbox_file_t /var/db
|
||||||
|
|
||||||
|
|
||||||
|
Now, writing to the /data1 volume in the container will be allowed and the
|
||||||
|
changes will also be reflected on the host in /var/db.
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,35 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-save - Save an image to a tar archive (streamed to STDOUT by default)
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker save** [**-o**|**--output**=""] IMAGE
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Produces a tarred repository to the standard output stream. Contains all
|
||||||
|
parent layers, and all tags + versions, or specified repo:tag.
|
||||||
|
|
||||||
|
Stream to a file instead of STDOUT by using **-o**.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
**-o**, **--output**=""
|
||||||
|
Write to an file, instead of STDOUT
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
|
||||||
|
Save all fedora repository images to a fedora-all.tar and save the latest
|
||||||
|
fedora image to a fedora-latest.tar:
|
||||||
|
|
||||||
|
$ sudo docker save fedora > fedora-all.tar
|
||||||
|
$ sudo docker save --output=fedora-latest.tar fedora:latest
|
||||||
|
$ ls -sh fedora-all.tar
|
||||||
|
721M fedora-all.tar
|
||||||
|
$ ls -sh fedora-latest.tar
|
||||||
|
367M fedora-latest.tar
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-search - Search the docker index for images
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker search** **--no-trunc**[=*false*] **-t**|**--trusted**[=*false*]
|
||||||
|
**-s**|**--stars**[=*0*] TERM
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
Search an index for an image with that matches the term TERM. The table
|
||||||
|
of images returned displays the name, description (truncated by default),
|
||||||
|
number of stars awarded, whether the image is official, and whether it
|
||||||
|
is trusted.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
**--no-trunc**=*true*|*false*
|
||||||
|
When true display the complete description. The default is false.
|
||||||
|
|
||||||
|
**-s**, **--stars**=NUM
|
||||||
|
Only displays with at least NUM (integer) stars. I.e. only those images
|
||||||
|
ranked >=NUM.
|
||||||
|
|
||||||
|
**-t**, **--trusted**=*true*|*false*
|
||||||
|
When true only show trusted builds. The default is false.
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
|
||||||
|
## Search the registry for ranked images
|
||||||
|
|
||||||
|
Search the registry for the term 'fedora' and only display those images
|
||||||
|
ranked 3 or higher:
|
||||||
|
|
||||||
|
$ sudo docker search -s 3 fedora
|
||||||
|
NAME DESCRIPTION STARS OFFICIAL TRUSTED
|
||||||
|
mattdm/fedora A basic Fedora image corresponding roughly... 50
|
||||||
|
fedora (Semi) Official Fedora base image. 38
|
||||||
|
mattdm/fedora-small A small Fedora image on which to build. Co... 8
|
||||||
|
goldmann/wildfly A WildFly application server running on a ... 3 [OK]
|
||||||
|
|
||||||
|
## Search the registry for trusted images
|
||||||
|
|
||||||
|
Search the registry for the term 'fedora' and only display trusted images
|
||||||
|
ranked 1 or higher:
|
||||||
|
|
||||||
|
$ sudo docker search -s 1 -t fedora
|
||||||
|
NAME DESCRIPTION STARS OFFICIAL TRUSTED
|
||||||
|
goldmann/wildfly A WildFly application server running on a ... 3 [OK]
|
||||||
|
tutum/fedora-20 Fedora 20 image with SSH access. For the r... 1 [OK]
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,25 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-start - Restart a stopped container
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker start** [**a**|**--attach**[=*false*]] [**-i**|**--interactive**
|
||||||
|
[=*true*] CONTAINER [CONTAINER...]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
Start a stopped container.
|
||||||
|
|
||||||
|
# OPTION
|
||||||
|
**-a**, **--attach**=*true*|*false*
|
||||||
|
When true attach to container's stdout/stderr and forward all signals to
|
||||||
|
the process
|
||||||
|
|
||||||
|
**-i**, **--interactive**=*true*|*false*
|
||||||
|
When true attach to container's stdin
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,22 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-stop - Stop a running container
|
||||||
|
grace period)
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker stop** [**-t**|**--time**[=*10*]] CONTAINER [CONTAINER...]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Stop a running container (Send SIGTERM, and then SIGKILL after
|
||||||
|
grace period)
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
**-t**, **--time**=NUM
|
||||||
|
Wait NUM number of seconds for the container to stop before killing it.
|
||||||
|
The default is 10 seconds.
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,48 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-tag - Tag an image in the repository
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker tag** [**-f**|**--force**[=*false*]
|
||||||
|
IMAGE [REGISTRYHOST/][USERNAME/]NAME[:TAG]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
This will tag an image in the repository.
|
||||||
|
|
||||||
|
# "OPTIONS"
|
||||||
|
**-f**, **--force**=*true*|*false*
|
||||||
|
When set to true, force the tag name. The default is *false*.
|
||||||
|
|
||||||
|
**REGISTRYHOST**
|
||||||
|
The hostname of the registry if required. This may also include the port
|
||||||
|
separated by a ':'
|
||||||
|
|
||||||
|
**USERNAME**
|
||||||
|
The username or other qualifying identifier for the image.
|
||||||
|
|
||||||
|
**NAME**
|
||||||
|
The image name.
|
||||||
|
|
||||||
|
**TAG**
|
||||||
|
The tag you are assigning to the image.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Tagging an image
|
||||||
|
|
||||||
|
Here is an example of tagging an image with the tag version1.0 :
|
||||||
|
|
||||||
|
docker tag 0e5574283393 fedora/httpd:version1.0
|
||||||
|
|
||||||
|
## Tagging an image for a private repository
|
||||||
|
|
||||||
|
To push an image to an private registry and not the central Docker
|
||||||
|
registry you must tag it with the registry hostname and port (if needed).
|
||||||
|
|
||||||
|
docker tag 0e5574283393 myregistryhost:5000/fedora/httpd:version1.0
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
|
@ -0,0 +1,27 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-top - Lookup the running processes of a container
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker top** CONTAINER [ps-OPTION]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
Look up the running process of the container. ps-OPTION can be any of the
|
||||||
|
options you would pass to a Linux ps command.
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
|
||||||
|
Run **docker top** with the ps option of -x:
|
||||||
|
|
||||||
|
$ sudo docker top 8601afda2b -x
|
||||||
|
PID TTY STAT TIME COMMAND
|
||||||
|
16623 ? Ss 0:00 sleep 99999
|
||||||
|
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker-wait - Block until a container stops, then print its exit code.
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker wait** CONTAINER [CONTAINER...]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
Block until a container stops, then print its exit code.
|
||||||
|
|
||||||
|
#EXAMPLE
|
||||||
|
|
||||||
|
$ sudo docker run -d fedora sleep 99
|
||||||
|
079b83f558a2bc52ecad6b2a5de13622d584e6bb1aea058c11b36511e85e7622
|
||||||
|
$ sudo docker wait 079b83f558a2bc
|
||||||
|
0
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||||
|
based on docker.io source material and internal work.
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
% DOCKER(1) Docker User Manuals
|
||||||
|
% William Henry
|
||||||
|
% APRIL 2014
|
||||||
|
# NAME
|
||||||
|
docker \- Docker image and container command line interface
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**docker** [OPTIONS] COMMAND [arg...]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
**docker** has two distinct functions. It is used for starting the Docker
|
||||||
|
daemon and to run the CLI (i.e., to command the daemon to manage images,
|
||||||
|
containers etc.) So **docker** is both a server, as a deamon, and a client
|
||||||
|
to the daemon, through the CLI.
|
||||||
|
|
||||||
|
To run the Docker deamon you do not specify any of the commands listed below but
|
||||||
|
must specify the **-d** option. The other options listed below are for the
|
||||||
|
daemon only.
|
||||||
|
|
||||||
|
The Docker CLI has over 30 commands. The commands are listed below and each has
|
||||||
|
its own man page which explain usage and arguements.
|
||||||
|
|
||||||
|
To see the man page for a command run **man docker <command>**.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
**-D**=*true*|*false*
|
||||||
|
Enable debug mode. Default is false.
|
||||||
|
|
||||||
|
**-H**, **--host**=[unix:///var/run/docker.sock]: tcp://[host[:port]] to bind or
|
||||||
|
unix://[/path/to/socket] to use.
|
||||||
|
Enable both the socket support and TCP on localhost. When host=[0.0.0.0],
|
||||||
|
port=[4243] or path =[/var/run/docker.sock] is omitted, default values are used.
|
||||||
|
|
||||||
|
**--api-enable-cors**=*true*|*false*
|
||||||
|
Enable CORS headers in the remote API. Default is false.
|
||||||
|
|
||||||
|
**-b**=""
|
||||||
|
Attach containers to a pre\-existing network bridge; use 'none' to disable container networking
|
||||||
|
|
||||||
|
**--bip**=""
|
||||||
|
Use the provided CIDR notation address for the dynamically created bridge (docker0); Mutually exclusive of \-b
|
||||||
|
|
||||||
|
**-d**=*true*|*false*
|
||||||
|
Enable daemon mode. Default is false.
|
||||||
|
|
||||||
|
**--dns**=""
|
||||||
|
Force Docker to use specific DNS servers
|
||||||
|
|
||||||
|
**-g**=""
|
||||||
|
Path to use as the root of the Docker runtime. Default is `/var/lib/docker`.
|
||||||
|
|
||||||
|
**--icc**=*true*|*false*
|
||||||
|
Enable inter\-container communication. Default is true.
|
||||||
|
|
||||||
|
**--ip**=""
|
||||||
|
Default IP address to use when binding container ports. Default is `0.0.0.0`.
|
||||||
|
|
||||||
|
**--iptables**=*true*|*false*
|
||||||
|
Disable Docker's addition of iptables rules. Default is true.
|
||||||
|
|
||||||
|
**--mtu**=VALUE
|
||||||
|
Set the containers network mtu. Default is `1500`.
|
||||||
|
|
||||||
|
**-p**=""
|
||||||
|
Path to use for daemon PID file. Default is `/var/run/docker.pid`
|
||||||
|
|
||||||
|
**-r**=*true*|*false*
|
||||||
|
Restart previously running containers. Default is true.
|
||||||
|
|
||||||
|
**-s**=""
|
||||||
|
Force the Docker runtime to use a specific storage driver.
|
||||||
|
|
||||||
|
**-v**=*true*|*false*
|
||||||
|
Print version information and quit. Default is false.
|
||||||
|
|
||||||
|
**--selinux-enabled=*true*|*false*
|
||||||
|
Enable selinux support. Default is false.
|
||||||
|
|
||||||
|
# COMMANDS
|
||||||
|
**docker-attach(1)**
|
||||||
|
Attach to a running container
|
||||||
|
|
||||||
|
**docker-build(1)**
|
||||||
|
Build a container from a Dockerfile
|
||||||
|
|
||||||
|
**docker-commit(1)**
|
||||||
|
Create a new image from a container's changes
|
||||||
|
|
||||||
|
**docker-cp(1)**
|
||||||
|
Copy files/folders from the containers filesystem to the host at path
|
||||||
|
|
||||||
|
**docker-diff(1)**
|
||||||
|
Inspect changes on a container's filesystem
|
||||||
|
|
||||||
|
|
||||||
|
**docker-events(1)**
|
||||||
|
Get real time events from the server
|
||||||
|
|
||||||
|
**docker-export(1)**
|
||||||
|
Stream the contents of a container as a tar archive
|
||||||
|
|
||||||
|
**docker-history(1)**
|
||||||
|
Show the history of an image
|
||||||
|
|
||||||
|
**docker-images(1)**
|
||||||
|
List images
|
||||||
|
|
||||||
|
**docker-import(1)**
|
||||||
|
Create a new filesystem image from the contents of a tarball
|
||||||
|
|
||||||
|
**docker-info(1)**
|
||||||
|
Display system-wide information
|
||||||
|
|
||||||
|
**docker-inspect(1)**
|
||||||
|
Return low-level information on a container
|
||||||
|
|
||||||
|
**docker-kill(1)**
|
||||||
|
Kill a running container (which includes the wrapper process and everything
|
||||||
|
inside it)
|
||||||
|
|
||||||
|
**docker-load(1)**
|
||||||
|
Load an image from a tar archive
|
||||||
|
|
||||||
|
**docker-login(1)**
|
||||||
|
Register or Login to a Docker registry server
|
||||||
|
|
||||||
|
**docker-logs(1)**
|
||||||
|
Fetch the logs of a container
|
||||||
|
|
||||||
|
**docker-port(1)**
|
||||||
|
Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
|
||||||
|
|
||||||
|
**docker-ps(1)**
|
||||||
|
List containers
|
||||||
|
|
||||||
|
**docker-pull(1)**
|
||||||
|
Pull an image or a repository from a Docker registry server
|
||||||
|
|
||||||
|
**docker-push(1)**
|
||||||
|
Push an image or a repository to a Docker registry server
|
||||||
|
|
||||||
|
**docker-restart(1)**
|
||||||
|
Restart a running container
|
||||||
|
|
||||||
|
**docker-rm(1)**
|
||||||
|
Remove one or more containers
|
||||||
|
|
||||||
|
**docker-rmi(1)**
|
||||||
|
Remove one or more images
|
||||||
|
|
||||||
|
**docker-run(1)**
|
||||||
|
Run a command in a new container
|
||||||
|
|
||||||
|
**docker-save(1)**
|
||||||
|
Save an image to a tar archive
|
||||||
|
|
||||||
|
**docker-search(1)**
|
||||||
|
Search for an image in the Docker index
|
||||||
|
|
||||||
|
**docker-start(1)**
|
||||||
|
Start a stopped container
|
||||||
|
|
||||||
|
**docker-stop(1)**
|
||||||
|
Stop a running container
|
||||||
|
|
||||||
|
**docker-tag(1)**
|
||||||
|
Tag an image into a repository
|
||||||
|
|
||||||
|
**docker-top(1)**
|
||||||
|
Lookup the running processes of a container
|
||||||
|
|
||||||
|
**version**
|
||||||
|
Show the Docker version information
|
||||||
|
|
||||||
|
**docker-wait(1)**
|
||||||
|
Block until a container stops, then print its exit code
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
For specific examples please see the man page for the specific Docker command.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
man docker run
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com) based
|
||||||
|
on docker.io source material and internal work.
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# get into this script's directory
|
||||||
|
cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
|
||||||
|
|
||||||
|
[ "$1" = '-q' ] || {
|
||||||
|
set -x
|
||||||
|
pwd
|
||||||
|
}
|
||||||
|
|
||||||
|
for FILE in *.md; do
|
||||||
|
base="$(basename "$FILE")"
|
||||||
|
name="${base%.md}"
|
||||||
|
num="${name##*.}"
|
||||||
|
if [ -z "$num" -o "$base" = "$num" ]; then
|
||||||
|
# skip files that aren't of the format xxxx.N.md (like README.md)
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
mkdir -p "../man${num}"
|
||||||
|
pandoc -s -t man "$FILE" -o "../man${num}/${name}"
|
||||||
|
done
|
|
@ -0,0 +1,50 @@
|
||||||
|
DOCKER "1" "APRIL 2014" "0.1" "Docker"
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
NAME
|
||||||
|
----
|
||||||
|
|
||||||
|
docker-rm - Remove one or more containers.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
--------
|
||||||
|
|
||||||
|
`docker rm` [`-f`|`--force`[=*false*] [`-l`|`--link`[=*false*] [`-v`|`--volumes`[=*false*]
|
||||||
|
CONTAINER [CONTAINER...]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
-----------
|
||||||
|
|
||||||
|
`docker rm` will remove one or more containers from the host node. The container name or ID can be used. This does not remove images. You cannot remove a running container unless you use the \fB-f\fR option. To see all containers on a host use the `docker ps -a` command.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-------
|
||||||
|
|
||||||
|
`-f`, `--force`=*true*|*false*:
|
||||||
|
When set to true, force the removal of the container. The default is *false*.
|
||||||
|
|
||||||
|
`-l`, `--link`=*true*|*false*:
|
||||||
|
When set to true, remove the specified link and not the underlying container. The default is *false*.
|
||||||
|
|
||||||
|
`-v`, `--volumes`=*true*|*false*:
|
||||||
|
When set to true, remove the volumes associated to the container. The default is *false*.
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
--------
|
||||||
|
|
||||||
|
##Removing a container using its ID##
|
||||||
|
|
||||||
|
To remove a container using its ID, find either from a `docker ps -a` command, or use the ID returned from the `docker run` command, or retrieve it from a file used to store it using the `docker run --cidfile`:
|
||||||
|
|
||||||
|
docker rm abebf7571666
|
||||||
|
|
||||||
|
##Removing a container using the container name##
|
||||||
|
|
||||||
|
The name of the container can be found using the \fBdocker ps -a\fR command. The use that name as follows:
|
||||||
|
|
||||||
|
docker rm hopeful_morse
|
||||||
|
|
||||||
|
HISTORY
|
||||||
|
-------
|
||||||
|
|
||||||
|
April 2014, Originally compiled by William Henry (whenry at redhat dot com) based on dockier.io source material and internal work.
|
|
@ -0,0 +1,82 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
[ $(id -u) -eq 0 ] || {
|
||||||
|
printf >&2 '%s requires root\n' "$0"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
printf >&2 '%s: [-r release] [-m mirror] [-s]\n' "$0"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp() {
|
||||||
|
TMP=$(mktemp -d /tmp/alpine-docker-XXXXXXXXXX)
|
||||||
|
ROOTFS=$(mktemp -d /tmp/alpine-docker-rootfs-XXXXXXXXXX)
|
||||||
|
trap "rm -rf $TMP $ROOTFS" EXIT TERM INT
|
||||||
|
}
|
||||||
|
|
||||||
|
apkv() {
|
||||||
|
curl -s $REPO/$ARCH/APKINDEX.tar.gz | tar -Oxz |
|
||||||
|
grep '^P:apk-tools-static$' -A1 | tail -n1 | cut -d: -f2
|
||||||
|
}
|
||||||
|
|
||||||
|
getapk() {
|
||||||
|
curl -s $REPO/$ARCH/apk-tools-static-$(apkv).apk |
|
||||||
|
tar -xz -C $TMP sbin/apk.static
|
||||||
|
}
|
||||||
|
|
||||||
|
mkbase() {
|
||||||
|
$TMP/sbin/apk.static --repository $REPO --update-cache --allow-untrusted \
|
||||||
|
--root $ROOTFS --initdb add alpine-base
|
||||||
|
}
|
||||||
|
|
||||||
|
conf() {
|
||||||
|
printf '%s\n' $REPO > $ROOTFS/etc/apk/repositories
|
||||||
|
}
|
||||||
|
|
||||||
|
pack() {
|
||||||
|
local id
|
||||||
|
id=$(tar --numeric-owner -C $ROOTFS -c . | docker import - alpine:$REL)
|
||||||
|
|
||||||
|
docker tag $id alpine:latest
|
||||||
|
docker run -i -t alpine printf 'alpine:%s with id=%s created!\n' $REL $id
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
[ $SAVE -eq 1 ] || return
|
||||||
|
|
||||||
|
tar --numeric-owner -C $ROOTFS -c . | xz > rootfs.tar.xz
|
||||||
|
}
|
||||||
|
|
||||||
|
while getopts "hr:m:s" opt; do
|
||||||
|
case $opt in
|
||||||
|
r)
|
||||||
|
REL=$OPTARG
|
||||||
|
;;
|
||||||
|
m)
|
||||||
|
MIRROR=$OPTARG
|
||||||
|
;;
|
||||||
|
s)
|
||||||
|
SAVE=1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
REL=${REL:-edge}
|
||||||
|
MIRROR=${MIRROR:-http://nl.alpinelinux.org/alpine}
|
||||||
|
SAVE=${SAVE:-0}
|
||||||
|
REPO=$MIRROR/$REL/main
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
|
||||||
|
tmp
|
||||||
|
getapk
|
||||||
|
mkbase
|
||||||
|
conf
|
||||||
|
pack
|
||||||
|
save
|
|
@ -57,6 +57,7 @@ mknod -m 666 $DEV/tty0 c 4 0
|
||||||
mknod -m 666 $DEV/full c 1 7
|
mknod -m 666 $DEV/full c 1 7
|
||||||
mknod -m 600 $DEV/initctl p
|
mknod -m 600 $DEV/initctl p
|
||||||
mknod -m 666 $DEV/ptmx c 5 2
|
mknod -m 666 $DEV/ptmx c 5 2
|
||||||
|
ln -sf /proc/self/fd $DEV/fd
|
||||||
|
|
||||||
tar --numeric-owner -C $ROOTFS -c . | docker import - archlinux
|
tar --numeric-owner -C $ROOTFS -c . | docker import - archlinux
|
||||||
docker run -i -t archlinux echo Success.
|
docker run -i -t archlinux echo Success.
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Gurjeet Singh <gurjeet@singh.im> (gurjeet.singh.im)
|
|
|
@ -1,23 +0,0 @@
|
||||||
# ZFS Storage Driver
|
|
||||||
|
|
||||||
This is a placeholder to declare the presence and status of ZFS storage driver
|
|
||||||
for containers.
|
|
||||||
|
|
||||||
The current development is done in Gurjeet Singh's fork of Docker, under the
|
|
||||||
branch named [zfs_driver].
|
|
||||||
|
|
||||||
[zfs_driver]: https://github.com/gurjeet/docker/tree/zfs_driver
|
|
||||||
|
|
||||||
|
|
||||||
# Status
|
|
||||||
|
|
||||||
Alpha: The code is now capable of creating, running and destroying containers
|
|
||||||
and images.
|
|
||||||
|
|
||||||
The code is under development. Contributions in the form of suggestions,
|
|
||||||
code-reviews, and patches are welcome.
|
|
||||||
|
|
||||||
Please send the communication to gurjeet@singh.im and CC at least one Docker
|
|
||||||
mailing list.
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
|
||||||
|
var (
|
||||||
|
cStdout, cStderr io.ReadCloser
|
||||||
|
nJobs int
|
||||||
|
errors = make(chan error, 3)
|
||||||
|
)
|
||||||
|
|
||||||
|
if stdin != nil && container.Config.OpenStdin {
|
||||||
|
nJobs += 1
|
||||||
|
if cStdin, err := container.StdinPipe(); err != nil {
|
||||||
|
errors <- err
|
||||||
|
} else {
|
||||||
|
go func() {
|
||||||
|
utils.Debugf("attach: stdin: begin")
|
||||||
|
defer utils.Debugf("attach: stdin: end")
|
||||||
|
// No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
|
||||||
|
if container.Config.StdinOnce && !container.Config.Tty {
|
||||||
|
defer cStdin.Close()
|
||||||
|
} else {
|
||||||
|
defer func() {
|
||||||
|
if cStdout != nil {
|
||||||
|
cStdout.Close()
|
||||||
|
}
|
||||||
|
if cStderr != nil {
|
||||||
|
cStderr.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if container.Config.Tty {
|
||||||
|
_, err = utils.CopyEscapable(cStdin, stdin)
|
||||||
|
} else {
|
||||||
|
_, err = io.Copy(cStdin, stdin)
|
||||||
|
}
|
||||||
|
if err == io.ErrClosedPipe {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
utils.Errorf("attach: stdin: %s", err)
|
||||||
|
}
|
||||||
|
errors <- err
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if stdout != nil {
|
||||||
|
nJobs += 1
|
||||||
|
if p, err := container.StdoutPipe(); err != nil {
|
||||||
|
errors <- err
|
||||||
|
} else {
|
||||||
|
cStdout = p
|
||||||
|
go func() {
|
||||||
|
utils.Debugf("attach: stdout: begin")
|
||||||
|
defer utils.Debugf("attach: stdout: end")
|
||||||
|
// If we are in StdinOnce mode, then close stdin
|
||||||
|
if container.Config.StdinOnce && stdin != nil {
|
||||||
|
defer stdin.Close()
|
||||||
|
}
|
||||||
|
if stdinCloser != nil {
|
||||||
|
defer stdinCloser.Close()
|
||||||
|
}
|
||||||
|
_, err := io.Copy(stdout, cStdout)
|
||||||
|
if err == io.ErrClosedPipe {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
utils.Errorf("attach: stdout: %s", err)
|
||||||
|
}
|
||||||
|
errors <- err
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
go func() {
|
||||||
|
if stdinCloser != nil {
|
||||||
|
defer stdinCloser.Close()
|
||||||
|
}
|
||||||
|
if cStdout, err := container.StdoutPipe(); err != nil {
|
||||||
|
utils.Errorf("attach: stdout pipe: %s", err)
|
||||||
|
} else {
|
||||||
|
io.Copy(&utils.NopWriter{}, cStdout)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if stderr != nil {
|
||||||
|
nJobs += 1
|
||||||
|
if p, err := container.StderrPipe(); err != nil {
|
||||||
|
errors <- err
|
||||||
|
} else {
|
||||||
|
cStderr = p
|
||||||
|
go func() {
|
||||||
|
utils.Debugf("attach: stderr: begin")
|
||||||
|
defer utils.Debugf("attach: stderr: end")
|
||||||
|
// If we are in StdinOnce mode, then close stdin
|
||||||
|
if container.Config.StdinOnce && stdin != nil {
|
||||||
|
defer stdin.Close()
|
||||||
|
}
|
||||||
|
if stdinCloser != nil {
|
||||||
|
defer stdinCloser.Close()
|
||||||
|
}
|
||||||
|
_, err := io.Copy(stderr, cStderr)
|
||||||
|
if err == io.ErrClosedPipe {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
utils.Errorf("attach: stderr: %s", err)
|
||||||
|
}
|
||||||
|
errors <- err
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
go func() {
|
||||||
|
if stdinCloser != nil {
|
||||||
|
defer stdinCloser.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if cStderr, err := container.StderrPipe(); err != nil {
|
||||||
|
utils.Errorf("attach: stdout pipe: %s", err)
|
||||||
|
} else {
|
||||||
|
io.Copy(&utils.NopWriter{}, cStderr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils.Go(func() error {
|
||||||
|
defer func() {
|
||||||
|
if cStdout != nil {
|
||||||
|
cStdout.Close()
|
||||||
|
}
|
||||||
|
if cStderr != nil {
|
||||||
|
cStderr.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// FIXME: how to clean up the stdin goroutine without the unwanted side effect
|
||||||
|
// of closing the passed stdin? Add an intermediary io.Pipe?
|
||||||
|
for i := 0; i < nJobs; i += 1 {
|
||||||
|
utils.Debugf("attach: waiting for job %d/%d", i+1, nJobs)
|
||||||
|
if err := <-errors; err != nil {
|
||||||
|
utils.Errorf("attach: job %d returned error %s, aborting all jobs", i+1, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
utils.Debugf("attach: job %d completed successfully", i+1)
|
||||||
|
}
|
||||||
|
utils.Debugf("attach: all jobs completed successfully")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
||||||
package runtime
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/dotcloud/docker/nat"
|
"github.com/dotcloud/docker/nat"
|
|
@ -1,27 +1,8 @@
|
||||||
package runtime
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/archive"
|
|
||||||
"github.com/dotcloud/docker/daemonconfig"
|
|
||||||
"github.com/dotcloud/docker/dockerversion"
|
|
||||||
"github.com/dotcloud/docker/engine"
|
|
||||||
"github.com/dotcloud/docker/graph"
|
|
||||||
"github.com/dotcloud/docker/image"
|
|
||||||
"github.com/dotcloud/docker/pkg/graphdb"
|
|
||||||
"github.com/dotcloud/docker/pkg/mount"
|
|
||||||
"github.com/dotcloud/docker/pkg/selinux"
|
|
||||||
"github.com/dotcloud/docker/pkg/sysinfo"
|
|
||||||
"github.com/dotcloud/docker/runconfig"
|
|
||||||
"github.com/dotcloud/docker/runtime/execdriver"
|
|
||||||
"github.com/dotcloud/docker/runtime/execdriver/execdrivers"
|
|
||||||
"github.com/dotcloud/docker/runtime/execdriver/lxc"
|
|
||||||
"github.com/dotcloud/docker/runtime/graphdriver"
|
|
||||||
_ "github.com/dotcloud/docker/runtime/graphdriver/vfs"
|
|
||||||
_ "github.com/dotcloud/docker/runtime/networkdriver/bridge"
|
|
||||||
"github.com/dotcloud/docker/runtime/networkdriver/portallocator"
|
|
||||||
"github.com/dotcloud/docker/utils"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
@ -31,6 +12,28 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/archive"
|
||||||
|
"github.com/dotcloud/docker/daemon/execdriver"
|
||||||
|
"github.com/dotcloud/docker/daemon/execdriver/execdrivers"
|
||||||
|
"github.com/dotcloud/docker/daemon/execdriver/lxc"
|
||||||
|
"github.com/dotcloud/docker/daemon/graphdriver"
|
||||||
|
_ "github.com/dotcloud/docker/daemon/graphdriver/vfs"
|
||||||
|
_ "github.com/dotcloud/docker/daemon/networkdriver/bridge"
|
||||||
|
"github.com/dotcloud/docker/daemon/networkdriver/portallocator"
|
||||||
|
"github.com/dotcloud/docker/daemonconfig"
|
||||||
|
"github.com/dotcloud/docker/dockerversion"
|
||||||
|
"github.com/dotcloud/docker/engine"
|
||||||
|
"github.com/dotcloud/docker/graph"
|
||||||
|
"github.com/dotcloud/docker/image"
|
||||||
|
"github.com/dotcloud/docker/pkg/graphdb"
|
||||||
|
"github.com/dotcloud/docker/pkg/label"
|
||||||
|
"github.com/dotcloud/docker/pkg/mount"
|
||||||
|
"github.com/dotcloud/docker/pkg/networkfs/resolvconf"
|
||||||
|
"github.com/dotcloud/docker/pkg/selinux"
|
||||||
|
"github.com/dotcloud/docker/pkg/sysinfo"
|
||||||
|
"github.com/dotcloud/docker/runconfig"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Set the max depth to the aufs default that most
|
// Set the max depth to the aufs default that most
|
||||||
|
@ -44,7 +47,7 @@ var (
|
||||||
validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`)
|
validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
type Runtime struct {
|
type Daemon struct {
|
||||||
repository string
|
repository string
|
||||||
sysInitPath string
|
sysInitPath string
|
||||||
containers *list.List
|
containers *list.List
|
||||||
|
@ -76,17 +79,17 @@ func remountPrivate(mountPoint string) error {
|
||||||
return mount.ForceMount("", mountPoint, "none", "private")
|
return mount.ForceMount("", mountPoint, "none", "private")
|
||||||
}
|
}
|
||||||
|
|
||||||
// List returns an array of all containers registered in the runtime.
|
// List returns an array of all containers registered in the daemon.
|
||||||
func (runtime *Runtime) List() []*Container {
|
func (daemon *Daemon) List() []*Container {
|
||||||
containers := new(History)
|
containers := new(History)
|
||||||
for e := runtime.containers.Front(); e != nil; e = e.Next() {
|
for e := daemon.containers.Front(); e != nil; e = e.Next() {
|
||||||
containers.Add(e.Value.(*Container))
|
containers.Add(e.Value.(*Container))
|
||||||
}
|
}
|
||||||
return *containers
|
return *containers
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) getContainerElement(id string) *list.Element {
|
func (daemon *Daemon) getContainerElement(id string) *list.Element {
|
||||||
for e := runtime.containers.Front(); e != nil; e = e.Next() {
|
for e := daemon.containers.Front(); e != nil; e = e.Next() {
|
||||||
container := e.Value.(*Container)
|
container := e.Value.(*Container)
|
||||||
if container.ID == id {
|
if container.ID == id {
|
||||||
return e
|
return e
|
||||||
|
@ -97,17 +100,17 @@ func (runtime *Runtime) getContainerElement(id string) *list.Element {
|
||||||
|
|
||||||
// Get looks for a container by the specified ID or name, and returns it.
|
// Get looks for a container by the specified ID or name, and returns it.
|
||||||
// If the container is not found, or if an error occurs, nil is returned.
|
// If the container is not found, or if an error occurs, nil is returned.
|
||||||
func (runtime *Runtime) Get(name string) *Container {
|
func (daemon *Daemon) Get(name string) *Container {
|
||||||
if c, _ := runtime.GetByName(name); c != nil {
|
if c, _ := daemon.GetByName(name); c != nil {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := runtime.idIndex.Get(name)
|
id, err := daemon.idIndex.Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
e := runtime.getContainerElement(id)
|
e := daemon.getContainerElement(id)
|
||||||
if e == nil {
|
if e == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -116,43 +119,40 @@ func (runtime *Runtime) Get(name string) *Container {
|
||||||
|
|
||||||
// Exists returns a true if a container of the specified ID or name exists,
|
// Exists returns a true if a container of the specified ID or name exists,
|
||||||
// false otherwise.
|
// false otherwise.
|
||||||
func (runtime *Runtime) Exists(id string) bool {
|
func (daemon *Daemon) Exists(id string) bool {
|
||||||
return runtime.Get(id) != nil
|
return daemon.Get(id) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) containerRoot(id string) string {
|
func (daemon *Daemon) containerRoot(id string) string {
|
||||||
return path.Join(runtime.repository, id)
|
return path.Join(daemon.repository, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load reads the contents of a container from disk
|
// Load reads the contents of a container from disk
|
||||||
// This is typically done at startup.
|
// This is typically done at startup.
|
||||||
func (runtime *Runtime) load(id string) (*Container, error) {
|
func (daemon *Daemon) load(id string) (*Container, error) {
|
||||||
container := &Container{root: runtime.containerRoot(id)}
|
container := &Container{root: daemon.containerRoot(id)}
|
||||||
if err := container.FromDisk(); err != nil {
|
if err := container.FromDisk(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if container.ID != id {
|
if container.ID != id {
|
||||||
return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
|
return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
|
||||||
}
|
}
|
||||||
if container.State.IsRunning() {
|
|
||||||
container.State.SetGhost(true)
|
|
||||||
}
|
|
||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register makes a container object usable by the runtime as <container.ID>
|
// Register makes a container object usable by the daemon as <container.ID>
|
||||||
func (runtime *Runtime) Register(container *Container) error {
|
func (daemon *Daemon) Register(container *Container) error {
|
||||||
if container.runtime != nil || runtime.Exists(container.ID) {
|
if container.daemon != nil || daemon.Exists(container.ID) {
|
||||||
return fmt.Errorf("Container is already loaded")
|
return fmt.Errorf("Container is already loaded")
|
||||||
}
|
}
|
||||||
if err := validateID(container.ID); err != nil {
|
if err := validateID(container.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := runtime.ensureName(container); err != nil {
|
if err := daemon.ensureName(container); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
container.runtime = runtime
|
container.daemon = daemon
|
||||||
|
|
||||||
// Attach to stdout and stderr
|
// Attach to stdout and stderr
|
||||||
container.stderr = utils.NewWriteBroadcaster()
|
container.stderr = utils.NewWriteBroadcaster()
|
||||||
|
@ -164,22 +164,20 @@ func (runtime *Runtime) Register(container *Container) error {
|
||||||
container.stdinPipe = utils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
|
container.stdinPipe = utils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
|
||||||
}
|
}
|
||||||
// done
|
// done
|
||||||
runtime.containers.PushBack(container)
|
daemon.containers.PushBack(container)
|
||||||
runtime.idIndex.Add(container.ID)
|
daemon.idIndex.Add(container.ID)
|
||||||
|
|
||||||
// FIXME: if the container is supposed to be running but is not, auto restart it?
|
// FIXME: if the container is supposed to be running but is not, auto restart it?
|
||||||
// if so, then we need to restart monitor and init a new lock
|
// if so, then we need to restart monitor and init a new lock
|
||||||
// If the container is supposed to be running, make sure of it
|
// If the container is supposed to be running, make sure of it
|
||||||
if container.State.IsRunning() {
|
if container.State.IsRunning() {
|
||||||
if container.State.IsGhost() {
|
utils.Debugf("killing old running container %s", container.ID)
|
||||||
utils.Debugf("killing ghost %s", container.ID)
|
|
||||||
|
|
||||||
existingPid := container.State.Pid
|
existingPid := container.State.Pid
|
||||||
container.State.SetGhost(false)
|
|
||||||
container.State.SetStopped(0)
|
container.State.SetStopped(0)
|
||||||
|
|
||||||
// We only have to handle this for lxc because the other drivers will ensure that
|
// We only have to handle this for lxc because the other drivers will ensure that
|
||||||
// no ghost processes are left when docker dies
|
// no processes are left when docker dies
|
||||||
if container.ExecDriver == "" || strings.Contains(container.ExecDriver, "lxc") {
|
if container.ExecDriver == "" || strings.Contains(container.ExecDriver, "lxc") {
|
||||||
lxc.KillLxc(container.ID, 9)
|
lxc.KillLxc(container.ID, 9)
|
||||||
} else {
|
} else {
|
||||||
|
@ -192,27 +190,24 @@ func (runtime *Runtime) Register(container *Container) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Debugf("cannot find existing process for %d", existingPid)
|
utils.Debugf("cannot find existing process for %d", existingPid)
|
||||||
}
|
}
|
||||||
runtime.execDriver.Terminate(cmd)
|
daemon.execDriver.Terminate(cmd)
|
||||||
}
|
}
|
||||||
if err := container.Unmount(); err != nil {
|
if err := container.Unmount(); err != nil {
|
||||||
utils.Debugf("ghost unmount error %s", err)
|
utils.Debugf("unmount error %s", err)
|
||||||
}
|
}
|
||||||
if err := container.ToDisk(); err != nil {
|
if err := container.ToDisk(); err != nil {
|
||||||
utils.Debugf("saving ghost state to disk %s", err)
|
utils.Debugf("saving stopped state to disk %s", err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info := runtime.execDriver.Info(container.ID)
|
info := daemon.execDriver.Info(container.ID)
|
||||||
if !info.IsRunning() {
|
if !info.IsRunning() {
|
||||||
utils.Debugf("Container %s was supposed to be running but is not.", container.ID)
|
utils.Debugf("Container %s was supposed to be running but is not.", container.ID)
|
||||||
if runtime.config.AutoRestart {
|
if daemon.config.AutoRestart {
|
||||||
utils.Debugf("Restarting")
|
utils.Debugf("Restarting")
|
||||||
if err := container.Unmount(); err != nil {
|
if err := container.Unmount(); err != nil {
|
||||||
utils.Debugf("restart unmount error %s", err)
|
utils.Debugf("restart unmount error %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
container.State.SetGhost(false)
|
|
||||||
container.State.SetStopped(0)
|
|
||||||
if err := container.Start(); err != nil {
|
if err := container.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -234,9 +229,9 @@ func (runtime *Runtime) Register(container *Container) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) ensureName(container *Container) error {
|
func (daemon *Daemon) ensureName(container *Container) error {
|
||||||
if container.Name == "" {
|
if container.Name == "" {
|
||||||
name, err := generateRandomName(runtime)
|
name, err := generateRandomName(daemon)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
name = utils.TruncateID(container.ID)
|
name = utils.TruncateID(container.ID)
|
||||||
}
|
}
|
||||||
|
@ -245,8 +240,8 @@ func (runtime *Runtime) ensureName(container *Container) error {
|
||||||
if err := container.ToDisk(); err != nil {
|
if err := container.ToDisk(); err != nil {
|
||||||
utils.Debugf("Error saving container name %s", err)
|
utils.Debugf("Error saving container name %s", err)
|
||||||
}
|
}
|
||||||
if !runtime.containerGraph.Exists(name) {
|
if !daemon.containerGraph.Exists(name) {
|
||||||
if _, err := runtime.containerGraph.Set(name, container.ID); err != nil {
|
if _, err := daemon.containerGraph.Set(name, container.ID); err != nil {
|
||||||
utils.Debugf("Setting default id - %s", err)
|
utils.Debugf("Setting default id - %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,7 +249,7 @@ func (runtime *Runtime) ensureName(container *Container) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error {
|
func (daemon *Daemon) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error {
|
||||||
log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
|
log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -263,13 +258,13 @@ func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream strin
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy unregisters a container from the runtime and cleanly removes its contents from the filesystem.
|
// Destroy unregisters a container from the daemon and cleanly removes its contents from the filesystem.
|
||||||
func (runtime *Runtime) Destroy(container *Container) error {
|
func (daemon *Daemon) Destroy(container *Container) error {
|
||||||
if container == nil {
|
if container == nil {
|
||||||
return fmt.Errorf("The given container is <nil>")
|
return fmt.Errorf("The given container is <nil>")
|
||||||
}
|
}
|
||||||
|
|
||||||
element := runtime.getContainerElement(container.ID)
|
element := daemon.getContainerElement(container.ID)
|
||||||
if element == nil {
|
if element == nil {
|
||||||
return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
|
return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
|
||||||
}
|
}
|
||||||
|
@ -278,42 +273,45 @@ func (runtime *Runtime) Destroy(container *Container) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := runtime.driver.Remove(container.ID); err != nil {
|
// Deregister the container before removing its directory, to avoid race conditions
|
||||||
return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", runtime.driver, container.ID, err)
|
daemon.idIndex.Delete(container.ID)
|
||||||
|
daemon.containers.Remove(element)
|
||||||
|
|
||||||
|
if err := daemon.driver.Remove(container.ID); err != nil {
|
||||||
|
return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", daemon.driver, container.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
initID := fmt.Sprintf("%s-init", container.ID)
|
initID := fmt.Sprintf("%s-init", container.ID)
|
||||||
if err := runtime.driver.Remove(initID); err != nil {
|
if err := daemon.driver.Remove(initID); err != nil {
|
||||||
return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", runtime.driver, initID, err)
|
return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", daemon.driver, initID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := runtime.containerGraph.Purge(container.ID); err != nil {
|
if _, err := daemon.containerGraph.Purge(container.ID); err != nil {
|
||||||
utils.Debugf("Unable to remove container from link graph: %s", err)
|
utils.Debugf("Unable to remove container from link graph: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deregister the container before removing its directory, to avoid race conditions
|
|
||||||
runtime.idIndex.Delete(container.ID)
|
|
||||||
runtime.containers.Remove(element)
|
|
||||||
if err := os.RemoveAll(container.root); err != nil {
|
if err := os.RemoveAll(container.root); err != nil {
|
||||||
return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
|
return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
|
||||||
}
|
}
|
||||||
|
selinux.FreeLxcContexts(container.ProcessLabel)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) restore() error {
|
func (daemon *Daemon) restore() error {
|
||||||
if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
||||||
fmt.Printf("Loading containers: ")
|
fmt.Printf("Loading containers: ")
|
||||||
}
|
}
|
||||||
dir, err := ioutil.ReadDir(runtime.repository)
|
dir, err := ioutil.ReadDir(daemon.repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
containers := make(map[string]*Container)
|
containers := make(map[string]*Container)
|
||||||
currentDriver := runtime.driver.String()
|
currentDriver := daemon.driver.String()
|
||||||
|
|
||||||
for _, v := range dir {
|
for _, v := range dir {
|
||||||
id := v.Name()
|
id := v.Name()
|
||||||
container, err := runtime.load(id)
|
container, err := daemon.load(id)
|
||||||
if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
||||||
fmt.Print(".")
|
fmt.Print(".")
|
||||||
}
|
}
|
||||||
|
@ -332,12 +330,12 @@ func (runtime *Runtime) restore() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
register := func(container *Container) {
|
register := func(container *Container) {
|
||||||
if err := runtime.Register(container); err != nil {
|
if err := daemon.Register(container); err != nil {
|
||||||
utils.Debugf("Failed to register container %s: %s", container.ID, err)
|
utils.Debugf("Failed to register container %s: %s", container.ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if entities := runtime.containerGraph.List("/", -1); entities != nil {
|
if entities := daemon.containerGraph.List("/", -1); entities != nil {
|
||||||
for _, p := range entities.Paths() {
|
for _, p := range entities.Paths() {
|
||||||
if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
||||||
fmt.Print(".")
|
fmt.Print(".")
|
||||||
|
@ -353,12 +351,12 @@ func (runtime *Runtime) restore() error {
|
||||||
// Any containers that are left over do not exist in the graph
|
// Any containers that are left over do not exist in the graph
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
// Try to set the default name for a container if it exists prior to links
|
// Try to set the default name for a container if it exists prior to links
|
||||||
container.Name, err = generateRandomName(runtime)
|
container.Name, err = generateRandomName(daemon)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
container.Name = utils.TruncateID(container.ID)
|
container.Name = utils.TruncateID(container.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := runtime.containerGraph.Set(container.Name, container.ID); err != nil {
|
if _, err := daemon.containerGraph.Set(container.Name, container.ID); err != nil {
|
||||||
utils.Debugf("Setting default id - %s", err)
|
utils.Debugf("Setting default id - %s", err)
|
||||||
}
|
}
|
||||||
register(container)
|
register(container)
|
||||||
|
@ -372,38 +370,38 @@ func (runtime *Runtime) restore() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a new container from the given configuration with a given name.
|
// Create creates a new container from the given configuration with a given name.
|
||||||
func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Container, []string, error) {
|
func (daemon *Daemon) Create(config *runconfig.Config, name string) (*Container, []string, error) {
|
||||||
var (
|
var (
|
||||||
container *Container
|
container *Container
|
||||||
warnings []string
|
warnings []string
|
||||||
)
|
)
|
||||||
|
|
||||||
img, err := runtime.repositories.LookupImage(config.Image)
|
img, err := daemon.repositories.LookupImage(config.Image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if err := runtime.checkImageDepth(img); err != nil {
|
if err := daemon.checkImageDepth(img); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if warnings, err = runtime.mergeAndVerifyConfig(config, img); err != nil {
|
if warnings, err = daemon.mergeAndVerifyConfig(config, img); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if container, err = runtime.newContainer(name, config, img); err != nil {
|
if container, err = daemon.newContainer(name, config, img); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if err := runtime.createRootfs(container, img); err != nil {
|
if err := daemon.createRootfs(container, img); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if err := container.ToDisk(); err != nil {
|
if err := container.ToDisk(); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if err := runtime.Register(container); err != nil {
|
if err := daemon.Register(container); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return container, warnings, nil
|
return container, warnings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) checkImageDepth(img *image.Image) error {
|
func (daemon *Daemon) checkImageDepth(img *image.Image) error {
|
||||||
// We add 2 layers to the depth because the container's rw and
|
// We add 2 layers to the depth because the container's rw and
|
||||||
// init layer add to the restriction
|
// init layer add to the restriction
|
||||||
depth, err := img.Depth()
|
depth, err := img.Depth()
|
||||||
|
@ -416,7 +414,7 @@ func (runtime *Runtime) checkImageDepth(img *image.Image) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) checkDeprecatedExpose(config *runconfig.Config) bool {
|
func (daemon *Daemon) checkDeprecatedExpose(config *runconfig.Config) bool {
|
||||||
if config != nil {
|
if config != nil {
|
||||||
if config.PortSpecs != nil {
|
if config.PortSpecs != nil {
|
||||||
for _, p := range config.PortSpecs {
|
for _, p := range config.PortSpecs {
|
||||||
|
@ -429,9 +427,9 @@ func (runtime *Runtime) checkDeprecatedExpose(config *runconfig.Config) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) mergeAndVerifyConfig(config *runconfig.Config, img *image.Image) ([]string, error) {
|
func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *image.Image) ([]string, error) {
|
||||||
warnings := []string{}
|
warnings := []string{}
|
||||||
if runtime.checkDeprecatedExpose(img.Config) || runtime.checkDeprecatedExpose(config) {
|
if daemon.checkDeprecatedExpose(img.Config) || daemon.checkDeprecatedExpose(config) {
|
||||||
warnings = append(warnings, "The mapping to public ports on your host via Dockerfile EXPOSE (host:port:port) has been deprecated. Use -p to publish the ports.")
|
warnings = append(warnings, "The mapping to public ports on your host via Dockerfile EXPOSE (host:port:port) has been deprecated. Use -p to publish the ports.")
|
||||||
}
|
}
|
||||||
if img.Config != nil {
|
if img.Config != nil {
|
||||||
|
@ -445,14 +443,14 @@ func (runtime *Runtime) mergeAndVerifyConfig(config *runconfig.Config, img *imag
|
||||||
return warnings, nil
|
return warnings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) generateIdAndName(name string) (string, string, error) {
|
func (daemon *Daemon) generateIdAndName(name string) (string, string, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
id = utils.GenerateRandomID()
|
id = utils.GenerateRandomID()
|
||||||
)
|
)
|
||||||
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name, err = generateRandomName(runtime)
|
name, err = generateRandomName(daemon)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
name = utils.TruncateID(id)
|
name = utils.TruncateID(id)
|
||||||
}
|
}
|
||||||
|
@ -465,19 +463,19 @@ func (runtime *Runtime) generateIdAndName(name string) (string, string, error) {
|
||||||
name = "/" + name
|
name = "/" + name
|
||||||
}
|
}
|
||||||
// Set the enitity in the graph using the default name specified
|
// Set the enitity in the graph using the default name specified
|
||||||
if _, err := runtime.containerGraph.Set(name, id); err != nil {
|
if _, err := daemon.containerGraph.Set(name, id); err != nil {
|
||||||
if !graphdb.IsNonUniqueNameError(err) {
|
if !graphdb.IsNonUniqueNameError(err) {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
conflictingContainer, err := runtime.GetByName(name)
|
conflictingContainer, err := daemon.GetByName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "Could not find entity") {
|
if strings.Contains(err.Error(), "Could not find entity") {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove name and continue starting the container
|
// Remove name and continue starting the container
|
||||||
if err := runtime.containerGraph.Delete(name); err != nil {
|
if err := daemon.containerGraph.Delete(name); err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -490,7 +488,7 @@ func (runtime *Runtime) generateIdAndName(name string) (string, string, error) {
|
||||||
return id, name, nil
|
return id, name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) generateHostname(id string, config *runconfig.Config) {
|
func (daemon *Daemon) generateHostname(id string, config *runconfig.Config) {
|
||||||
// Generate default hostname
|
// Generate default hostname
|
||||||
// FIXME: the lxc template no longer needs to set a default hostname
|
// FIXME: the lxc template no longer needs to set a default hostname
|
||||||
if config.Hostname == "" {
|
if config.Hostname == "" {
|
||||||
|
@ -498,7 +496,7 @@ func (runtime *Runtime) generateHostname(id string, config *runconfig.Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) getEntrypointAndArgs(config *runconfig.Config) (string, []string) {
|
func (daemon *Daemon) getEntrypointAndArgs(config *runconfig.Config) (string, []string) {
|
||||||
var (
|
var (
|
||||||
entrypoint string
|
entrypoint string
|
||||||
args []string
|
args []string
|
||||||
|
@ -513,18 +511,18 @@ func (runtime *Runtime) getEntrypointAndArgs(config *runconfig.Config) (string,
|
||||||
return entrypoint, args
|
return entrypoint, args
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) newContainer(name string, config *runconfig.Config, img *image.Image) (*Container, error) {
|
func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *image.Image) (*Container, error) {
|
||||||
var (
|
var (
|
||||||
id string
|
id string
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
id, name, err = runtime.generateIdAndName(name)
|
id, name, err = daemon.generateIdAndName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.generateHostname(id, config)
|
daemon.generateHostname(id, config)
|
||||||
entrypoint, args := runtime.getEntrypointAndArgs(config)
|
entrypoint, args := daemon.getEntrypointAndArgs(config)
|
||||||
|
|
||||||
container := &Container{
|
container := &Container{
|
||||||
// FIXME: we should generate the ID here instead of receiving it as an argument
|
// FIXME: we should generate the ID here instead of receiving it as an argument
|
||||||
|
@ -537,34 +535,38 @@ func (runtime *Runtime) newContainer(name string, config *runconfig.Config, img
|
||||||
Image: img.ID, // Always use the resolved image id
|
Image: img.ID, // Always use the resolved image id
|
||||||
NetworkSettings: &NetworkSettings{},
|
NetworkSettings: &NetworkSettings{},
|
||||||
Name: name,
|
Name: name,
|
||||||
Driver: runtime.driver.String(),
|
Driver: daemon.driver.String(),
|
||||||
ExecDriver: runtime.execDriver.Name(),
|
ExecDriver: daemon.execDriver.Name(),
|
||||||
|
}
|
||||||
|
container.root = daemon.containerRoot(container.ID)
|
||||||
|
|
||||||
|
if container.ProcessLabel, container.MountLabel, err = label.GenLabels(""); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
container.root = runtime.containerRoot(container.ID)
|
|
||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) createRootfs(container *Container, img *image.Image) error {
|
func (daemon *Daemon) createRootfs(container *Container, img *image.Image) error {
|
||||||
// Step 1: create the container directory.
|
// Step 1: create the container directory.
|
||||||
// This doubles as a barrier to avoid race conditions.
|
// This doubles as a barrier to avoid race conditions.
|
||||||
if err := os.Mkdir(container.root, 0700); err != nil {
|
if err := os.Mkdir(container.root, 0700); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
initID := fmt.Sprintf("%s-init", container.ID)
|
initID := fmt.Sprintf("%s-init", container.ID)
|
||||||
if err := runtime.driver.Create(initID, img.ID, ""); err != nil {
|
if err := daemon.driver.Create(initID, img.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
initPath, err := runtime.driver.Get(initID)
|
initPath, err := daemon.driver.Get(initID, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer runtime.driver.Put(initID)
|
defer daemon.driver.Put(initID)
|
||||||
|
|
||||||
if err := graph.SetupInitLayer(initPath); err != nil {
|
if err := graph.SetupInitLayer(initPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := runtime.driver.Create(container.ID, initID, ""); err != nil {
|
if err := daemon.driver.Create(container.ID, initID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -572,7 +574,7 @@ func (runtime *Runtime) createRootfs(container *Container, img *image.Image) err
|
||||||
|
|
||||||
// Commit creates a new filesystem image from the current state of a container.
|
// Commit creates a new filesystem image from the current state of a container.
|
||||||
// The image can optionally be tagged into a repository
|
// The image can optionally be tagged into a repository
|
||||||
func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *runconfig.Config) (*image.Image, error) {
|
func (daemon *Daemon) Commit(container *Container, repository, tag, comment, author string, config *runconfig.Config) (*image.Image, error) {
|
||||||
// FIXME: freeze the container before copying it to avoid data corruption?
|
// FIXME: freeze the container before copying it to avoid data corruption?
|
||||||
if err := container.Mount(); err != nil {
|
if err := container.Mount(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -595,13 +597,13 @@ func (runtime *Runtime) Commit(container *Container, repository, tag, comment, a
|
||||||
containerImage = container.Image
|
containerImage = container.Image
|
||||||
containerConfig = container.Config
|
containerConfig = container.Config
|
||||||
}
|
}
|
||||||
img, err := runtime.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config)
|
img, err := daemon.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Register the image if needed
|
// Register the image if needed
|
||||||
if repository != "" {
|
if repository != "" {
|
||||||
if err := runtime.repositories.Set(repository, tag, img.ID, true); err != nil {
|
if err := daemon.repositories.Set(repository, tag, img.ID, true); err != nil {
|
||||||
return img, err
|
return img, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -618,31 +620,31 @@ func GetFullContainerName(name string) (string, error) {
|
||||||
return name, nil
|
return name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) GetByName(name string) (*Container, error) {
|
func (daemon *Daemon) GetByName(name string) (*Container, error) {
|
||||||
fullName, err := GetFullContainerName(name)
|
fullName, err := GetFullContainerName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
entity := runtime.containerGraph.Get(fullName)
|
entity := daemon.containerGraph.Get(fullName)
|
||||||
if entity == nil {
|
if entity == nil {
|
||||||
return nil, fmt.Errorf("Could not find entity for %s", name)
|
return nil, fmt.Errorf("Could not find entity for %s", name)
|
||||||
}
|
}
|
||||||
e := runtime.getContainerElement(entity.ID())
|
e := daemon.getContainerElement(entity.ID())
|
||||||
if e == nil {
|
if e == nil {
|
||||||
return nil, fmt.Errorf("Could not find container for entity id %s", entity.ID())
|
return nil, fmt.Errorf("Could not find container for entity id %s", entity.ID())
|
||||||
}
|
}
|
||||||
return e.Value.(*Container), nil
|
return e.Value.(*Container), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
|
func (daemon *Daemon) Children(name string) (map[string]*Container, error) {
|
||||||
name, err := GetFullContainerName(name)
|
name, err := GetFullContainerName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
children := make(map[string]*Container)
|
children := make(map[string]*Container)
|
||||||
|
|
||||||
err = runtime.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
|
err = daemon.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
|
||||||
c := runtime.Get(e.ID())
|
c := daemon.Get(e.ID())
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
|
return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
|
||||||
}
|
}
|
||||||
|
@ -656,29 +658,28 @@ func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
|
||||||
return children, nil
|
return children, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) RegisterLink(parent, child *Container, alias string) error {
|
func (daemon *Daemon) RegisterLink(parent, child *Container, alias string) error {
|
||||||
fullName := path.Join(parent.Name, alias)
|
fullName := path.Join(parent.Name, alias)
|
||||||
if !runtime.containerGraph.Exists(fullName) {
|
if !daemon.containerGraph.Exists(fullName) {
|
||||||
_, err := runtime.containerGraph.Set(fullName, child.ID)
|
_, err := daemon.containerGraph.Set(fullName, child.ID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: harmonize with NewGraph()
|
// FIXME: harmonize with NewGraph()
|
||||||
func NewRuntime(config *daemonconfig.Config, eng *engine.Engine) (*Runtime, error) {
|
func NewDaemon(config *daemonconfig.Config, eng *engine.Engine) (*Daemon, error) {
|
||||||
runtime, err := NewRuntimeFromDirectory(config, eng)
|
daemon, err := NewDaemonFromDirectory(config, eng)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return runtime, nil
|
return daemon, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*Runtime, error) {
|
func NewDaemonFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*Daemon, error) {
|
||||||
if !config.EnableSelinuxSupport {
|
if !config.EnableSelinuxSupport {
|
||||||
selinux.SetDisabled()
|
selinux.SetDisabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the default driver
|
// Set the default driver
|
||||||
graphdriver.DefaultDriver = config.GraphDriver
|
graphdriver.DefaultDriver = config.GraphDriver
|
||||||
|
|
||||||
|
@ -693,9 +694,9 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
runtimeRepo := path.Join(config.Root, "containers")
|
daemonRepo := path.Join(config.Root, "containers")
|
||||||
|
|
||||||
if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
|
if err := os.MkdirAll(daemonRepo, 0700); err != nil && !os.IsExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,12 +775,12 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime := &Runtime{
|
daemon := &Daemon{
|
||||||
repository: runtimeRepo,
|
repository: daemonRepo,
|
||||||
containers: list.New(),
|
containers: list.New(),
|
||||||
graph: g,
|
graph: g,
|
||||||
repositories: repositories,
|
repositories: repositories,
|
||||||
idIndex: utils.NewTruncIndex(),
|
idIndex: utils.NewTruncIndex([]string{}),
|
||||||
sysInfo: sysInfo,
|
sysInfo: sysInfo,
|
||||||
volumes: volumes,
|
volumes: volumes,
|
||||||
config: config,
|
config: config,
|
||||||
|
@ -790,19 +791,19 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
|
||||||
eng: eng,
|
eng: eng,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := runtime.checkLocaldns(); err != nil {
|
if err := daemon.checkLocaldns(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := runtime.restore(); err != nil {
|
if err := daemon.restore(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return runtime, nil
|
return daemon, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) shutdown() error {
|
func (daemon *Daemon) shutdown() error {
|
||||||
group := sync.WaitGroup{}
|
group := sync.WaitGroup{}
|
||||||
utils.Debugf("starting clean shutdown of all containers...")
|
utils.Debugf("starting clean shutdown of all containers...")
|
||||||
for _, container := range runtime.List() {
|
for _, container := range daemon.List() {
|
||||||
c := container
|
c := container
|
||||||
if c.State.IsRunning() {
|
if c.State.IsRunning() {
|
||||||
utils.Debugf("stopping %s", c.ID)
|
utils.Debugf("stopping %s", c.ID)
|
||||||
|
@ -823,22 +824,22 @@ func (runtime *Runtime) shutdown() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) Close() error {
|
func (daemon *Daemon) Close() error {
|
||||||
errorsStrings := []string{}
|
errorsStrings := []string{}
|
||||||
if err := runtime.shutdown(); err != nil {
|
if err := daemon.shutdown(); err != nil {
|
||||||
utils.Errorf("runtime.shutdown(): %s", err)
|
utils.Errorf("daemon.shutdown(): %s", err)
|
||||||
errorsStrings = append(errorsStrings, err.Error())
|
errorsStrings = append(errorsStrings, err.Error())
|
||||||
}
|
}
|
||||||
if err := portallocator.ReleaseAll(); err != nil {
|
if err := portallocator.ReleaseAll(); err != nil {
|
||||||
utils.Errorf("portallocator.ReleaseAll(): %s", err)
|
utils.Errorf("portallocator.ReleaseAll(): %s", err)
|
||||||
errorsStrings = append(errorsStrings, err.Error())
|
errorsStrings = append(errorsStrings, err.Error())
|
||||||
}
|
}
|
||||||
if err := runtime.driver.Cleanup(); err != nil {
|
if err := daemon.driver.Cleanup(); err != nil {
|
||||||
utils.Errorf("runtime.driver.Cleanup(): %s", err.Error())
|
utils.Errorf("daemon.driver.Cleanup(): %s", err.Error())
|
||||||
errorsStrings = append(errorsStrings, err.Error())
|
errorsStrings = append(errorsStrings, err.Error())
|
||||||
}
|
}
|
||||||
if err := runtime.containerGraph.Close(); err != nil {
|
if err := daemon.containerGraph.Close(); err != nil {
|
||||||
utils.Errorf("runtime.containerGraph.Close(): %s", err.Error())
|
utils.Errorf("daemon.containerGraph.Close(): %s", err.Error())
|
||||||
errorsStrings = append(errorsStrings, err.Error())
|
errorsStrings = append(errorsStrings, err.Error())
|
||||||
}
|
}
|
||||||
if len(errorsStrings) > 0 {
|
if len(errorsStrings) > 0 {
|
||||||
|
@ -847,55 +848,55 @@ func (runtime *Runtime) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) Mount(container *Container) error {
|
func (daemon *Daemon) Mount(container *Container) error {
|
||||||
dir, err := runtime.driver.Get(container.ID)
|
dir, err := daemon.driver.Get(container.ID, container.GetMountLabel())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, runtime.driver, err)
|
return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, daemon.driver, err)
|
||||||
}
|
}
|
||||||
if container.basefs == "" {
|
if container.basefs == "" {
|
||||||
container.basefs = dir
|
container.basefs = dir
|
||||||
} else if container.basefs != dir {
|
} else if container.basefs != dir {
|
||||||
return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
|
return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
|
||||||
runtime.driver, container.ID, container.basefs, dir)
|
daemon.driver, container.ID, container.basefs, dir)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) Unmount(container *Container) error {
|
func (daemon *Daemon) Unmount(container *Container) error {
|
||||||
runtime.driver.Put(container.ID)
|
daemon.driver.Put(container.ID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) Changes(container *Container) ([]archive.Change, error) {
|
func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) {
|
||||||
if differ, ok := runtime.driver.(graphdriver.Differ); ok {
|
if differ, ok := daemon.driver.(graphdriver.Differ); ok {
|
||||||
return differ.Changes(container.ID)
|
return differ.Changes(container.ID)
|
||||||
}
|
}
|
||||||
cDir, err := runtime.driver.Get(container.ID)
|
cDir, err := daemon.driver.Get(container.ID, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
|
return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.daemon.driver, err)
|
||||||
}
|
}
|
||||||
defer runtime.driver.Put(container.ID)
|
defer daemon.driver.Put(container.ID)
|
||||||
initDir, err := runtime.driver.Get(container.ID + "-init")
|
initDir, err := daemon.driver.Get(container.ID+"-init", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error getting container init rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
|
return nil, fmt.Errorf("Error getting container init rootfs %s from driver %s: %s", container.ID, container.daemon.driver, err)
|
||||||
}
|
}
|
||||||
defer runtime.driver.Put(container.ID + "-init")
|
defer daemon.driver.Put(container.ID + "-init")
|
||||||
return archive.ChangesDirs(cDir, initDir)
|
return archive.ChangesDirs(cDir, initDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
|
func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) {
|
||||||
if differ, ok := runtime.driver.(graphdriver.Differ); ok {
|
if differ, ok := daemon.driver.(graphdriver.Differ); ok {
|
||||||
return differ.Diff(container.ID)
|
return differ.Diff(container.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
changes, err := runtime.Changes(container)
|
changes, err := daemon.Changes(container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cDir, err := runtime.driver.Get(container.ID)
|
cDir, err := daemon.driver.Get(container.ID, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
|
return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.daemon.driver, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
archive, err := archive.ExportChanges(cDir, changes)
|
archive, err := archive.ExportChanges(cDir, changes)
|
||||||
|
@ -904,26 +905,26 @@ func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
|
||||||
}
|
}
|
||||||
return utils.NewReadCloserWrapper(archive, func() error {
|
return utils.NewReadCloserWrapper(archive, func() error {
|
||||||
err := archive.Close()
|
err := archive.Close()
|
||||||
runtime.driver.Put(container.ID)
|
daemon.driver.Put(container.ID)
|
||||||
return err
|
return err
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
func (daemon *Daemon) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
||||||
return runtime.execDriver.Run(c.command, pipes, startCallback)
|
return daemon.execDriver.Run(c.command, pipes, startCallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) Kill(c *Container, sig int) error {
|
func (daemon *Daemon) Kill(c *Container, sig int) error {
|
||||||
return runtime.execDriver.Kill(c.command, sig)
|
return daemon.execDriver.Kill(c.command, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nuke kills all containers then removes all content
|
// Nuke kills all containers then removes all content
|
||||||
// from the content root, including images, volumes and
|
// from the content root, including images, volumes and
|
||||||
// container filesystems.
|
// container filesystems.
|
||||||
// Again: this will remove your entire docker runtime!
|
// Again: this will remove your entire docker daemon!
|
||||||
func (runtime *Runtime) Nuke() error {
|
func (daemon *Daemon) Nuke() error {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for _, container := range runtime.List() {
|
for _, container := range daemon.List() {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(c *Container) {
|
go func(c *Container) {
|
||||||
c.Kill()
|
c.Kill()
|
||||||
|
@ -931,63 +932,63 @@ func (runtime *Runtime) Nuke() error {
|
||||||
}(container)
|
}(container)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
runtime.Close()
|
daemon.Close()
|
||||||
|
|
||||||
return os.RemoveAll(runtime.config.Root)
|
return os.RemoveAll(daemon.config.Root)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: this is a convenience function for integration tests
|
// FIXME: this is a convenience function for integration tests
|
||||||
// which need direct access to runtime.graph.
|
// which need direct access to daemon.graph.
|
||||||
// Once the tests switch to using engine and jobs, this method
|
// Once the tests switch to using engine and jobs, this method
|
||||||
// can go away.
|
// can go away.
|
||||||
func (runtime *Runtime) Graph() *graph.Graph {
|
func (daemon *Daemon) Graph() *graph.Graph {
|
||||||
return runtime.graph
|
return daemon.graph
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) Repositories() *graph.TagStore {
|
func (daemon *Daemon) Repositories() *graph.TagStore {
|
||||||
return runtime.repositories
|
return daemon.repositories
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) Config() *daemonconfig.Config {
|
func (daemon *Daemon) Config() *daemonconfig.Config {
|
||||||
return runtime.config
|
return daemon.config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) SystemConfig() *sysinfo.SysInfo {
|
func (daemon *Daemon) SystemConfig() *sysinfo.SysInfo {
|
||||||
return runtime.sysInfo
|
return daemon.sysInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) SystemInitPath() string {
|
func (daemon *Daemon) SystemInitPath() string {
|
||||||
return runtime.sysInitPath
|
return daemon.sysInitPath
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) GraphDriver() graphdriver.Driver {
|
func (daemon *Daemon) GraphDriver() graphdriver.Driver {
|
||||||
return runtime.driver
|
return daemon.driver
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) ExecutionDriver() execdriver.Driver {
|
func (daemon *Daemon) ExecutionDriver() execdriver.Driver {
|
||||||
return runtime.execDriver
|
return daemon.execDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) Volumes() *graph.Graph {
|
func (daemon *Daemon) Volumes() *graph.Graph {
|
||||||
return runtime.volumes
|
return daemon.volumes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) ContainerGraph() *graphdb.Database {
|
func (daemon *Daemon) ContainerGraph() *graphdb.Database {
|
||||||
return runtime.containerGraph
|
return daemon.containerGraph
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) SetServer(server Server) {
|
func (daemon *Daemon) SetServer(server Server) {
|
||||||
runtime.srv = server
|
daemon.srv = server
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) checkLocaldns() error {
|
func (daemon *Daemon) checkLocaldns() error {
|
||||||
resolvConf, err := utils.GetResolvConf()
|
resolvConf, err := resolvconf.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
|
if len(daemon.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
|
||||||
log.Printf("Local (127.0.0.1) DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v\n", DefaultDns)
|
log.Printf("Local (127.0.0.1) DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v\n", DefaultDns)
|
||||||
runtime.config.Dns = DefaultDns
|
daemon.config.Dns = DefaultDns
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
// +build !exclude_graphdriver_aufs
|
// +build !exclude_graphdriver_aufs
|
||||||
|
|
||||||
package runtime
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/dotcloud/docker/daemon/graphdriver"
|
||||||
|
"github.com/dotcloud/docker/daemon/graphdriver/aufs"
|
||||||
"github.com/dotcloud/docker/graph"
|
"github.com/dotcloud/docker/graph"
|
||||||
"github.com/dotcloud/docker/runtime/graphdriver"
|
|
||||||
"github.com/dotcloud/docker/runtime/graphdriver/aufs"
|
|
||||||
"github.com/dotcloud/docker/utils"
|
"github.com/dotcloud/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// +build !exclude_graphdriver_btrfs
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/dotcloud/docker/daemon/graphdriver/btrfs"
|
||||||
|
)
|
|
@ -0,0 +1,7 @@
|
||||||
|
// +build !exclude_graphdriver_devicemapper
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/dotcloud/docker/daemon/graphdriver/devmapper"
|
||||||
|
)
|
|
@ -1,9 +1,9 @@
|
||||||
// +build exclude_graphdriver_aufs
|
// +build exclude_graphdriver_aufs
|
||||||
|
|
||||||
package runtime
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/dotcloud/docker/runtime/graphdriver"
|
"github.com/dotcloud/docker/daemon/graphdriver"
|
||||||
)
|
)
|
||||||
|
|
||||||
func migrateIfAufs(driver graphdriver.Driver, root string) error {
|
func migrateIfAufs(driver graphdriver.Driver, root string) error {
|
|
@ -91,6 +91,8 @@ type Driver interface {
|
||||||
type Network struct {
|
type Network struct {
|
||||||
Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled
|
Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled
|
||||||
Mtu int `json:"mtu"`
|
Mtu int `json:"mtu"`
|
||||||
|
ContainerID string `json:"container_id"` // id of the container to join network.
|
||||||
|
HostNetworking bool `json:"host_networking"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NetworkInterface struct {
|
type NetworkInterface struct {
|
|
@ -2,10 +2,10 @@ package execdrivers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/daemon/execdriver"
|
||||||
|
"github.com/dotcloud/docker/daemon/execdriver/lxc"
|
||||||
|
"github.com/dotcloud/docker/daemon/execdriver/native"
|
||||||
"github.com/dotcloud/docker/pkg/sysinfo"
|
"github.com/dotcloud/docker/pkg/sysinfo"
|
||||||
"github.com/dotcloud/docker/runtime/execdriver"
|
|
||||||
"github.com/dotcloud/docker/runtime/execdriver/lxc"
|
|
||||||
"github.com/dotcloud/docker/runtime/execdriver/native"
|
|
||||||
"path"
|
"path"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
package lxc
|
package lxc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/pkg/cgroups"
|
|
||||||
"github.com/dotcloud/docker/pkg/label"
|
|
||||||
"github.com/dotcloud/docker/runtime/execdriver"
|
|
||||||
"github.com/dotcloud/docker/utils"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -16,6 +13,12 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/daemon/execdriver"
|
||||||
|
"github.com/dotcloud/docker/pkg/cgroups"
|
||||||
|
"github.com/dotcloud/docker/pkg/label"
|
||||||
|
"github.com/dotcloud/docker/pkg/system"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DriverName = "lxc"
|
const DriverName = "lxc"
|
||||||
|
@ -25,23 +28,21 @@ func init() {
|
||||||
if err := setupEnv(args); err != nil {
|
if err := setupEnv(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := setupHostname(args); err != nil {
|
if err := setupHostname(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := setupNetworking(args); err != nil {
|
if err := setupNetworking(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := setupCapabilities(args); err != nil {
|
if err := setupCapabilities(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := setupWorkingDirectory(args); err != nil {
|
if err := setupWorkingDirectory(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := system.CloseFdsFrom(3); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := changeUser(args); err != nil {
|
if err := changeUser(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -85,6 +86,9 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
|
||||||
if err := execdriver.SetTerminal(c, pipes); err != nil {
|
if err := execdriver.SetTerminal(c, pipes); err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
if err := d.generateEnvConfig(c); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
configPath, err := d.generateLXCConfig(c)
|
configPath, err := d.generateLXCConfig(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
|
@ -416,3 +420,14 @@ func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
|
||||||
}
|
}
|
||||||
return root, nil
|
return root, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *driver) generateEnvConfig(c *execdriver.Command) error {
|
||||||
|
data, err := json.Marshal(c.Env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p := path.Join(d.root, "containers", c.ID, "config.env")
|
||||||
|
c.Mounts = append(c.Mounts, execdriver.Mount{p, "/.dockerenv", false, true})
|
||||||
|
|
||||||
|
return ioutil.WriteFile(p, data, 0600)
|
||||||
|
}
|
|
@ -3,15 +3,16 @@ package lxc
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/pkg/netlink"
|
|
||||||
"github.com/dotcloud/docker/pkg/user"
|
|
||||||
"github.com/dotcloud/docker/runtime/execdriver"
|
|
||||||
"github.com/syndtr/gocapability/capability"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/daemon/execdriver"
|
||||||
|
"github.com/dotcloud/docker/pkg/netlink"
|
||||||
|
"github.com/dotcloud/docker/pkg/user"
|
||||||
|
"github.com/syndtr/gocapability/capability"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Clear environment pollution introduced by lxc-start
|
// Clear environment pollution introduced by lxc-start
|
||||||
|
@ -149,6 +150,7 @@ func setupCapabilities(args *execdriver.InitArgs) error {
|
||||||
capability.CAP_MAC_OVERRIDE,
|
capability.CAP_MAC_OVERRIDE,
|
||||||
capability.CAP_MAC_ADMIN,
|
capability.CAP_MAC_ADMIN,
|
||||||
capability.CAP_NET_ADMIN,
|
capability.CAP_NET_ADMIN,
|
||||||
|
capability.CAP_SYSLOG,
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := capability.NewPid(os.Getpid())
|
c, err := capability.NewPid(os.Getpid())
|
|
@ -1,10 +1,11 @@
|
||||||
package lxc
|
package lxc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/dotcloud/docker/pkg/label"
|
|
||||||
"github.com/dotcloud/docker/runtime/execdriver"
|
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/daemon/execdriver"
|
||||||
|
"github.com/dotcloud/docker/pkg/label"
|
||||||
)
|
)
|
||||||
|
|
||||||
const LxcTemplate = `
|
const LxcTemplate = `
|
||||||
|
@ -13,12 +14,13 @@ const LxcTemplate = `
|
||||||
lxc.network.type = veth
|
lxc.network.type = veth
|
||||||
lxc.network.link = {{.Network.Interface.Bridge}}
|
lxc.network.link = {{.Network.Interface.Bridge}}
|
||||||
lxc.network.name = eth0
|
lxc.network.name = eth0
|
||||||
{{else}}
|
lxc.network.mtu = {{.Network.Mtu}}
|
||||||
|
{{else if not .Network.HostNetworking}}
|
||||||
# network is disabled (-n=false)
|
# network is disabled (-n=false)
|
||||||
lxc.network.type = empty
|
lxc.network.type = empty
|
||||||
lxc.network.flags = up
|
lxc.network.flags = up
|
||||||
{{end}}
|
|
||||||
lxc.network.mtu = {{.Network.Mtu}}
|
lxc.network.mtu = {{.Network.Mtu}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
# root filesystem
|
# root filesystem
|
||||||
{{$ROOTFS := .Rootfs}}
|
{{$ROOTFS := .Rootfs}}
|
||||||
|
@ -82,12 +84,11 @@ lxc.pivotdir = lxc_putold
|
||||||
|
|
||||||
# NOTICE: These mounts must be applied within the namespace
|
# NOTICE: These mounts must be applied within the namespace
|
||||||
|
|
||||||
# WARNING: procfs is a known attack vector and should probably be disabled
|
# WARNING: mounting procfs and/or sysfs read-write is a known attack vector.
|
||||||
# if your userspace allows it. eg. see http://blog.zx2c4.com/749
|
# See e.g. http://blog.zx2c4.com/749 and http://bit.ly/T9CkqJ
|
||||||
|
# We mount them read-write here, but later, dockerinit will call the Restrict() function to remount them read-only.
|
||||||
|
# We cannot mount them directly read-only, because that would prevent loading AppArmor profiles.
|
||||||
lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
|
lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
|
||||||
|
|
||||||
# WARNING: sysfs is a known attack vector and should probably be disabled
|
|
||||||
# if your userspace allows it. eg. see http://bit.ly/T9CkqJ
|
|
||||||
lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
|
lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
|
||||||
|
|
||||||
{{if .Tty}}
|
{{if .Tty}}
|
||||||
|
@ -109,7 +110,7 @@ lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabS
|
||||||
{{if .AppArmor}}
|
{{if .AppArmor}}
|
||||||
lxc.aa_profile = unconfined
|
lxc.aa_profile = unconfined
|
||||||
{{else}}
|
{{else}}
|
||||||
#lxc.aa_profile = unconfined
|
# Let AppArmor normal confinement take place (i.e., not unconfined)
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package lxc
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/runtime/execdriver"
|
"github.com/dotcloud/docker/daemon/execdriver"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
|
@ -2,12 +2,13 @@ package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer"
|
|
||||||
"github.com/dotcloud/docker/utils"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Action func(*libcontainer.Container, interface{}, string) error
|
type Action func(*libcontainer.Container, interface{}, string) error
|
||||||
|
@ -23,9 +24,12 @@ var actions = map[string]Action{
|
||||||
|
|
||||||
"cgroups.cpu_shares": cpuShares, // set the cpu shares
|
"cgroups.cpu_shares": cpuShares, // set the cpu shares
|
||||||
"cgroups.memory": memory, // set the memory limit
|
"cgroups.memory": memory, // set the memory limit
|
||||||
|
"cgroups.memory_reservation": memoryReservation, // set the memory reservation
|
||||||
"cgroups.memory_swap": memorySwap, // set the memory swap limit
|
"cgroups.memory_swap": memorySwap, // set the memory swap limit
|
||||||
"cgroups.cpuset.cpus": cpusetCpus, // set the cpus used
|
"cgroups.cpuset.cpus": cpusetCpus, // set the cpus used
|
||||||
|
|
||||||
|
"systemd.slice": systemdSlice, // set parent Slice used for systemd unit
|
||||||
|
|
||||||
"apparmor_profile": apparmorProfile, // set the apparmor profile to apply
|
"apparmor_profile": apparmorProfile, // set the apparmor profile to apply
|
||||||
|
|
||||||
"fs.readonly": readonlyFs, // make the rootfs of the container read only
|
"fs.readonly": readonlyFs, // make the rootfs of the container read only
|
||||||
|
@ -40,6 +44,15 @@ func cpusetCpus(container *libcontainer.Container, context interface{}, value st
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func systemdSlice(container *libcontainer.Container, context interface{}, value string) error {
|
||||||
|
if container.Cgroups == nil {
|
||||||
|
return fmt.Errorf("cannot set slice when cgroups are disabled")
|
||||||
|
}
|
||||||
|
container.Cgroups.Slice = value
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func apparmorProfile(container *libcontainer.Container, context interface{}, value string) error {
|
func apparmorProfile(container *libcontainer.Container, context interface{}, value string) error {
|
||||||
container.Context["apparmor_profile"] = value
|
container.Context["apparmor_profile"] = value
|
||||||
return nil
|
return nil
|
||||||
|
@ -70,6 +83,19 @@ func memory(container *libcontainer.Container, context interface{}, value string
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func memoryReservation(container *libcontainer.Container, context interface{}, value string) error {
|
||||||
|
if container.Cgroups == nil {
|
||||||
|
return fmt.Errorf("cannot set cgroups when they are disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := utils.RAMInBytes(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
container.Cgroups.MemoryReservation = v
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func memorySwap(container *libcontainer.Container, context interface{}, value string) error {
|
func memorySwap(container *libcontainer.Container, context interface{}, value string) error {
|
||||||
if container.Cgroups == nil {
|
if container.Cgroups == nil {
|
||||||
return fmt.Errorf("cannot set cgroups when they are disabled")
|
return fmt.Errorf("cannot set cgroups when they are disabled")
|
||||||
|
@ -83,38 +109,22 @@ func memorySwap(container *libcontainer.Container, context interface{}, value st
|
||||||
}
|
}
|
||||||
|
|
||||||
func addCap(container *libcontainer.Container, context interface{}, value string) error {
|
func addCap(container *libcontainer.Container, context interface{}, value string) error {
|
||||||
c := container.CapabilitiesMask.Get(value)
|
container.CapabilitiesMask[value] = true
|
||||||
if c == nil {
|
|
||||||
return fmt.Errorf("%s is not a valid capability", value)
|
|
||||||
}
|
|
||||||
c.Enabled = true
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func dropCap(container *libcontainer.Container, context interface{}, value string) error {
|
func dropCap(container *libcontainer.Container, context interface{}, value string) error {
|
||||||
c := container.CapabilitiesMask.Get(value)
|
container.CapabilitiesMask[value] = false
|
||||||
if c == nil {
|
|
||||||
return fmt.Errorf("%s is not a valid capability", value)
|
|
||||||
}
|
|
||||||
c.Enabled = false
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addNamespace(container *libcontainer.Container, context interface{}, value string) error {
|
func addNamespace(container *libcontainer.Container, context interface{}, value string) error {
|
||||||
ns := container.Namespaces.Get(value)
|
container.Namespaces[value] = true
|
||||||
if ns == nil {
|
|
||||||
return fmt.Errorf("%s is not a valid namespace", value[1:])
|
|
||||||
}
|
|
||||||
ns.Enabled = true
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func dropNamespace(container *libcontainer.Container, context interface{}, value string) error {
|
func dropNamespace(container *libcontainer.Container, context interface{}, value string) error {
|
||||||
ns := container.Namespaces.Get(value)
|
container.Namespaces[value] = false
|
||||||
if ns == nil {
|
|
||||||
return fmt.Errorf("%s is not a valid namespace", value[1:])
|
|
||||||
}
|
|
||||||
ns.Enabled = false
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/dotcloud/docker/runtime/execdriver/native/template"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/daemon/execdriver/native/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSetReadonlyRootFs(t *testing.T) {
|
func TestSetReadonlyRootFs(t *testing.T) {
|
||||||
|
@ -38,10 +39,10 @@ func TestConfigurationsDoNotConflict(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !container1.CapabilitiesMask.Get("NET_ADMIN").Enabled {
|
if !container1.CapabilitiesMask["NET_ADMIN"] {
|
||||||
t.Fatal("container one should have NET_ADMIN enabled")
|
t.Fatal("container one should have NET_ADMIN enabled")
|
||||||
}
|
}
|
||||||
if container2.CapabilitiesMask.Get("NET_ADMIN").Enabled {
|
if container2.CapabilitiesMask["NET_ADMIN"] {
|
||||||
t.Fatal("container two should not have NET_ADMIN enabled")
|
t.Fatal("container two should not have NET_ADMIN enabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +94,7 @@ func TestCpuShares(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCgroupMemory(t *testing.T) {
|
func TestMemory(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
container = template.New()
|
container = template.New()
|
||||||
opts = []string{
|
opts = []string{
|
||||||
|
@ -109,6 +110,22 @@ func TestCgroupMemory(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMemoryReservation(t *testing.T) {
|
||||||
|
var (
|
||||||
|
container = template.New()
|
||||||
|
opts = []string{
|
||||||
|
"cgroups.memory_reservation=500m",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if err := ParseConfiguration(container, nil, opts); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected := int64(500 * 1024 * 1024); container.Cgroups.MemoryReservation != expected {
|
||||||
|
t.Fatalf("expected memory reservation %d got %d", expected, container.Cgroups.MemoryReservation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAddCap(t *testing.T) {
|
func TestAddCap(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
container = template.New()
|
container = template.New()
|
||||||
|
@ -121,10 +138,10 @@ func TestAddCap(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !container.CapabilitiesMask.Get("MKNOD").Enabled {
|
if !container.CapabilitiesMask["MKNOD"] {
|
||||||
t.Fatal("container should have MKNOD enabled")
|
t.Fatal("container should have MKNOD enabled")
|
||||||
}
|
}
|
||||||
if !container.CapabilitiesMask.Get("SYS_ADMIN").Enabled {
|
if !container.CapabilitiesMask["SYS_ADMIN"] {
|
||||||
t.Fatal("container should have SYS_ADMIN enabled")
|
t.Fatal("container should have SYS_ADMIN enabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,14 +154,14 @@ func TestDropCap(t *testing.T) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// enabled all caps like in privileged mode
|
// enabled all caps like in privileged mode
|
||||||
for _, c := range container.CapabilitiesMask {
|
for key := range container.CapabilitiesMask {
|
||||||
c.Enabled = true
|
container.CapabilitiesMask[key] = true
|
||||||
}
|
}
|
||||||
if err := ParseConfiguration(container, nil, opts); err != nil {
|
if err := ParseConfiguration(container, nil, opts); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if container.CapabilitiesMask.Get("MKNOD").Enabled {
|
if container.CapabilitiesMask["MKNOD"] {
|
||||||
t.Fatal("container should not have MKNOD enabled")
|
t.Fatal("container should not have MKNOD enabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +177,7 @@ func TestDropNamespace(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if container.Namespaces.Get("NEWNET").Enabled {
|
if container.Namespaces["NEWNET"] {
|
||||||
t.Fatal("container should not have NEWNET enabled")
|
t.Fatal("container should not have NEWNET enabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,12 +3,13 @@ package native
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/dotcloud/docker/pkg/label"
|
"github.com/dotcloud/docker/daemon/execdriver"
|
||||||
|
"github.com/dotcloud/docker/daemon/execdriver/native/configuration"
|
||||||
|
"github.com/dotcloud/docker/daemon/execdriver/native/template"
|
||||||
|
"github.com/dotcloud/docker/pkg/apparmor"
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer"
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
"github.com/dotcloud/docker/runtime/execdriver"
|
|
||||||
"github.com/dotcloud/docker/runtime/execdriver/native/configuration"
|
|
||||||
"github.com/dotcloud/docker/runtime/execdriver/native/template"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// createContainer populates and configures the container type with the
|
// createContainer populates and configures the container type with the
|
||||||
|
@ -24,6 +25,7 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container
|
||||||
container.Cgroups.Name = c.ID
|
container.Cgroups.Name = c.ID
|
||||||
// check to see if we are running in ramdisk to disable pivot root
|
// check to see if we are running in ramdisk to disable pivot root
|
||||||
container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
|
container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
|
||||||
|
container.Context["restrictions"] = "true"
|
||||||
|
|
||||||
if err := d.createNetwork(container, c); err != nil {
|
if err := d.createNetwork(container, c); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -32,6 +34,8 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container
|
||||||
if err := d.setPrivileged(container); err != nil {
|
if err := d.setPrivileged(container); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
container.Mounts = append(container.Mounts, libcontainer.Mount{Type: "devtmpfs"})
|
||||||
}
|
}
|
||||||
if err := d.setupCgroups(container, c); err != nil {
|
if err := d.setupCgroups(container, c); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -49,6 +53,10 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.Command) error {
|
func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.Command) error {
|
||||||
|
if c.Network.HostNetworking {
|
||||||
|
container.Namespaces["NEWNET"] = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
container.Networks = []*libcontainer.Network{
|
container.Networks = []*libcontainer.Network{
|
||||||
{
|
{
|
||||||
Mtu: c.Network.Mtu,
|
Mtu: c.Network.Mtu,
|
||||||
|
@ -72,15 +80,34 @@ func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.
|
||||||
}
|
}
|
||||||
container.Networks = append(container.Networks, &vethNetwork)
|
container.Networks = append(container.Networks, &vethNetwork)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Network.ContainerID != "" {
|
||||||
|
cmd := d.activeContainers[c.Network.ContainerID]
|
||||||
|
if cmd == nil || cmd.Process == nil {
|
||||||
|
return fmt.Errorf("%s is not a valid running container to join", c.Network.ContainerID)
|
||||||
|
}
|
||||||
|
nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net")
|
||||||
|
container.Networks = append(container.Networks, &libcontainer.Network{
|
||||||
|
Type: "netns",
|
||||||
|
Context: libcontainer.Context{
|
||||||
|
"nspath": nspath,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *driver) setPrivileged(container *libcontainer.Container) error {
|
func (d *driver) setPrivileged(container *libcontainer.Container) error {
|
||||||
for _, c := range container.CapabilitiesMask {
|
for key := range container.CapabilitiesMask {
|
||||||
c.Enabled = true
|
container.CapabilitiesMask[key] = true
|
||||||
}
|
}
|
||||||
container.Cgroups.DeviceAccess = true
|
container.Cgroups.DeviceAccess = true
|
||||||
|
|
||||||
|
delete(container.Context, "restrictions")
|
||||||
|
|
||||||
|
if apparmor.IsEnabled() {
|
||||||
container.Context["apparmor_profile"] = "unconfined"
|
container.Context["apparmor_profile"] = "unconfined"
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +115,7 @@ func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.C
|
||||||
if c.Resources != nil {
|
if c.Resources != nil {
|
||||||
container.Cgroups.CpuShares = c.Resources.CpuShares
|
container.Cgroups.CpuShares = c.Resources.CpuShares
|
||||||
container.Cgroups.Memory = c.Resources.Memory
|
container.Cgroups.Memory = c.Resources.Memory
|
||||||
|
container.Cgroups.MemoryReservation = c.Resources.Memory
|
||||||
container.Cgroups.MemorySwap = c.Resources.MemorySwap
|
container.Cgroups.MemorySwap = c.Resources.MemorySwap
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -95,20 +123,19 @@ func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.C
|
||||||
|
|
||||||
func (d *driver) setupMounts(container *libcontainer.Container, c *execdriver.Command) error {
|
func (d *driver) setupMounts(container *libcontainer.Container, c *execdriver.Command) error {
|
||||||
for _, m := range c.Mounts {
|
for _, m := range c.Mounts {
|
||||||
container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private})
|
container.Mounts = append(container.Mounts, libcontainer.Mount{
|
||||||
|
Type: "bind",
|
||||||
|
Source: m.Source,
|
||||||
|
Destination: m.Destination,
|
||||||
|
Writable: m.Writable,
|
||||||
|
Private: m.Private,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *driver) setupLabels(container *libcontainer.Container, c *execdriver.Command) error {
|
func (d *driver) setupLabels(container *libcontainer.Container, c *execdriver.Command) error {
|
||||||
labels := c.Config["label"]
|
container.Context["process_label"] = c.Config["process_label"][0]
|
||||||
if len(labels) > 0 {
|
container.Context["mount_label"] = c.Config["mount_label"][0]
|
||||||
process, mount, err := label.GenLabels(labels[0])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
container.Context["mount_label"] = mount
|
|
||||||
container.Context["process_label"] = process
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
|
@ -3,35 +3,31 @@ package native
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/pkg/cgroups"
|
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer"
|
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/apparmor"
|
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
|
|
||||||
"github.com/dotcloud/docker/pkg/system"
|
|
||||||
"github.com/dotcloud/docker/runtime/execdriver"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/daemon/execdriver"
|
||||||
|
"github.com/dotcloud/docker/pkg/apparmor"
|
||||||
|
"github.com/dotcloud/docker/pkg/cgroups"
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
|
||||||
|
"github.com/dotcloud/docker/pkg/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DriverName = "native"
|
DriverName = "native"
|
||||||
Version = "0.1"
|
Version = "0.2"
|
||||||
BackupApparmorProfilePath = "apparmor/docker.back" // relative to docker root
|
BackupApparmorProfilePath = "apparmor/docker.back" // relative to docker root
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
|
execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
|
||||||
var (
|
var container *libcontainer.Container
|
||||||
container *libcontainer.Container
|
|
||||||
ns = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{args.Root}, createLogger(""))
|
|
||||||
)
|
|
||||||
f, err := os.Open(filepath.Join(args.Root, "container.json"))
|
f, err := os.Open(filepath.Join(args.Root, "container.json"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -42,7 +38,7 @@ func init() {
|
||||||
}
|
}
|
||||||
f.Close()
|
f.Close()
|
||||||
|
|
||||||
cwd, err := os.Getwd()
|
rootfs, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -50,7 +46,7 @@ func init() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ns.Init(container, cwd, args.Console, syncPipe, args.Args); err != nil {
|
if err := nsinit.Init(container, rootfs, args.Console, syncPipe, args.Args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -87,14 +83,7 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
|
||||||
d.activeContainers[c.ID] = &c.Cmd
|
d.activeContainers[c.ID] = &c.Cmd
|
||||||
|
|
||||||
var (
|
var (
|
||||||
term nsinit.Terminal
|
dataPath = filepath.Join(d.root, c.ID)
|
||||||
factory = &dockerCommandFactory{c: c, driver: d}
|
|
||||||
stateWriter = &dockerStateWriter{
|
|
||||||
callback: startCallback,
|
|
||||||
c: c,
|
|
||||||
dsw: &nsinit.DefaultStateWriter{filepath.Join(d.root, c.ID)},
|
|
||||||
}
|
|
||||||
ns = nsinit.NewNsInit(factory, stateWriter, createLogger(os.Getenv("DEBUG")))
|
|
||||||
args = append([]string{c.Entrypoint}, c.Arguments...)
|
args = append([]string{c.Entrypoint}, c.Arguments...)
|
||||||
)
|
)
|
||||||
if err := d.createContainerRoot(c.ID); err != nil {
|
if err := d.createContainerRoot(c.ID); err != nil {
|
||||||
|
@ -102,20 +91,41 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
|
||||||
}
|
}
|
||||||
defer d.removeContainerRoot(c.ID)
|
defer d.removeContainerRoot(c.ID)
|
||||||
|
|
||||||
if c.Tty {
|
|
||||||
term = &dockerTtyTerm{
|
|
||||||
pipes: pipes,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
term = &dockerStdTerm{
|
|
||||||
pipes: pipes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.Terminal = term
|
|
||||||
if err := d.writeContainerFile(container, c.ID); err != nil {
|
if err := d.writeContainerFile(container, c.ID); err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
return ns.Exec(container, term, args)
|
|
||||||
|
term := getTerminal(c, pipes)
|
||||||
|
|
||||||
|
return nsinit.Exec(container, term, c.Rootfs, dataPath, args, func(container *libcontainer.Container, console, rootfs, dataPath, init string, child *os.File, args []string) *exec.Cmd {
|
||||||
|
// we need to join the rootfs because nsinit will setup the rootfs and chroot
|
||||||
|
initPath := filepath.Join(c.Rootfs, c.InitPath)
|
||||||
|
|
||||||
|
c.Path = d.initPath
|
||||||
|
c.Args = append([]string{
|
||||||
|
initPath,
|
||||||
|
"-driver", DriverName,
|
||||||
|
"-console", console,
|
||||||
|
"-pipe", "3",
|
||||||
|
"-root", filepath.Join(d.root, c.ID),
|
||||||
|
"--",
|
||||||
|
}, args...)
|
||||||
|
|
||||||
|
// set this to nil so that when we set the clone flags anything else is reset
|
||||||
|
c.SysProcAttr = nil
|
||||||
|
system.SetCloneFlags(&c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces)))
|
||||||
|
c.ExtraFiles = []*os.File{child}
|
||||||
|
|
||||||
|
c.Env = container.Env
|
||||||
|
c.Dir = c.Rootfs
|
||||||
|
|
||||||
|
return &c.Cmd
|
||||||
|
}, func() {
|
||||||
|
if startCallback != nil {
|
||||||
|
c.ContainerPid = c.Process.Pid
|
||||||
|
startCallback(c)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *driver) Kill(p *execdriver.Command, sig int) error {
|
func (d *driver) Kill(p *execdriver.Command, sig int) error {
|
||||||
|
@ -228,65 +238,17 @@ func getEnv(key string, env []string) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
type dockerCommandFactory struct {
|
func getTerminal(c *execdriver.Command, pipes *execdriver.Pipes) nsinit.Terminal {
|
||||||
c *execdriver.Command
|
var term nsinit.Terminal
|
||||||
driver *driver
|
if c.Tty {
|
||||||
}
|
term = &dockerTtyTerm{
|
||||||
|
pipes: pipes,
|
||||||
// createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
|
|
||||||
// defined on the container's configuration and use the current binary as the init with the
|
|
||||||
// args provided
|
|
||||||
func (d *dockerCommandFactory) Create(container *libcontainer.Container, console string, syncFile *os.File, args []string) *exec.Cmd {
|
|
||||||
// we need to join the rootfs because nsinit will setup the rootfs and chroot
|
|
||||||
initPath := filepath.Join(d.c.Rootfs, d.c.InitPath)
|
|
||||||
|
|
||||||
d.c.Path = d.driver.initPath
|
|
||||||
d.c.Args = append([]string{
|
|
||||||
initPath,
|
|
||||||
"-driver", DriverName,
|
|
||||||
"-console", console,
|
|
||||||
"-pipe", "3",
|
|
||||||
"-root", filepath.Join(d.driver.root, d.c.ID),
|
|
||||||
"--",
|
|
||||||
}, args...)
|
|
||||||
|
|
||||||
// set this to nil so that when we set the clone flags anything else is reset
|
|
||||||
d.c.SysProcAttr = nil
|
|
||||||
system.SetCloneFlags(&d.c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces)))
|
|
||||||
d.c.ExtraFiles = []*os.File{syncFile}
|
|
||||||
|
|
||||||
d.c.Env = container.Env
|
|
||||||
d.c.Dir = d.c.Rootfs
|
|
||||||
|
|
||||||
return &d.c.Cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
type dockerStateWriter struct {
|
|
||||||
dsw nsinit.StateWriter
|
|
||||||
c *execdriver.Command
|
|
||||||
callback execdriver.StartCallback
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dockerStateWriter) WritePid(pid int, started string) error {
|
|
||||||
d.c.ContainerPid = pid
|
|
||||||
err := d.dsw.WritePid(pid, started)
|
|
||||||
if d.callback != nil {
|
|
||||||
d.callback(d.c)
|
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dockerStateWriter) DeletePid() error {
|
|
||||||
return d.dsw.DeletePid()
|
|
||||||
}
|
|
||||||
|
|
||||||
func createLogger(debug string) *log.Logger {
|
|
||||||
var w io.Writer
|
|
||||||
// if we are in debug mode set the logger to stderr
|
|
||||||
if debug != "" {
|
|
||||||
w = os.Stderr
|
|
||||||
} else {
|
} else {
|
||||||
w = ioutil.Discard
|
term = &dockerStdTerm{
|
||||||
|
pipes: pipes,
|
||||||
}
|
}
|
||||||
return log.New(w, "[libcontainer] ", log.LstdFlags)
|
}
|
||||||
|
c.Terminal = term
|
||||||
|
return term
|
||||||
}
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package template
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/dotcloud/docker/pkg/apparmor"
|
||||||
|
"github.com/dotcloud/docker/pkg/cgroups"
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns the docker default configuration for libcontainer
|
||||||
|
func New() *libcontainer.Container {
|
||||||
|
container := &libcontainer.Container{
|
||||||
|
CapabilitiesMask: map[string]bool{
|
||||||
|
"SETPCAP": false,
|
||||||
|
"SYS_MODULE": false,
|
||||||
|
"SYS_RAWIO": false,
|
||||||
|
"SYS_PACCT": false,
|
||||||
|
"SYS_ADMIN": false,
|
||||||
|
"SYS_NICE": false,
|
||||||
|
"SYS_RESOURCE": false,
|
||||||
|
"SYS_TIME": false,
|
||||||
|
"SYS_TTY_CONFIG": false,
|
||||||
|
"AUDIT_WRITE": false,
|
||||||
|
"AUDIT_CONTROL": false,
|
||||||
|
"MAC_OVERRIDE": false,
|
||||||
|
"MAC_ADMIN": false,
|
||||||
|
"NET_ADMIN": false,
|
||||||
|
"MKNOD": true,
|
||||||
|
"SYSLOG": false,
|
||||||
|
},
|
||||||
|
Namespaces: map[string]bool{
|
||||||
|
"NEWNS": true,
|
||||||
|
"NEWUTS": true,
|
||||||
|
"NEWIPC": true,
|
||||||
|
"NEWPID": true,
|
||||||
|
"NEWNET": true,
|
||||||
|
},
|
||||||
|
Cgroups: &cgroups.Cgroup{
|
||||||
|
Parent: "docker",
|
||||||
|
DeviceAccess: false,
|
||||||
|
},
|
||||||
|
Context: libcontainer.Context{},
|
||||||
|
}
|
||||||
|
if apparmor.IsEnabled() {
|
||||||
|
container.Context["apparmor_profile"] = "docker-default"
|
||||||
|
}
|
||||||
|
return container
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
package native
|
package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/dotcloud/docker/runtime/execdriver"
|
"github.com/dotcloud/docker/daemon/execdriver"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
|
@ -24,8 +24,9 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/archive"
|
"github.com/dotcloud/docker/archive"
|
||||||
|
"github.com/dotcloud/docker/daemon/graphdriver"
|
||||||
|
"github.com/dotcloud/docker/pkg/label"
|
||||||
mountpk "github.com/dotcloud/docker/pkg/mount"
|
mountpk "github.com/dotcloud/docker/pkg/mount"
|
||||||
"github.com/dotcloud/docker/runtime/graphdriver"
|
|
||||||
"github.com/dotcloud/docker/utils"
|
"github.com/dotcloud/docker/utils"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -134,7 +135,7 @@ func (a Driver) Exists(id string) bool {
|
||||||
|
|
||||||
// Three folders are created for each id
|
// Three folders are created for each id
|
||||||
// mnt, layers, and diff
|
// mnt, layers, and diff
|
||||||
func (a *Driver) Create(id, parent string, mountLabel string) error {
|
func (a *Driver) Create(id, parent string) error {
|
||||||
if err := a.createDirsFor(id); err != nil {
|
if err := a.createDirsFor(id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -218,7 +219,7 @@ func (a *Driver) Remove(id string) error {
|
||||||
|
|
||||||
// Return the rootfs path for the id
|
// Return the rootfs path for the id
|
||||||
// This will mount the dir at it's given path
|
// This will mount the dir at it's given path
|
||||||
func (a *Driver) Get(id string) (string, error) {
|
func (a *Driver) Get(id, mountLabel string) (string, error) {
|
||||||
ids, err := getParentIds(a.rootPath(), id)
|
ids, err := getParentIds(a.rootPath(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(err) {
|
||||||
|
@ -240,7 +241,7 @@ func (a *Driver) Get(id string) (string, error) {
|
||||||
out = path.Join(a.rootPath(), "mnt", id)
|
out = path.Join(a.rootPath(), "mnt", id)
|
||||||
|
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
if err := a.mount(id); err != nil {
|
if err := a.mount(id, mountLabel); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -309,7 +310,7 @@ func (a *Driver) getParentLayerPaths(id string) ([]string, error) {
|
||||||
return layers, nil
|
return layers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Driver) mount(id string) error {
|
func (a *Driver) mount(id, mountLabel string) error {
|
||||||
// If the id is mounted or we get an error return
|
// If the id is mounted or we get an error return
|
||||||
if mounted, err := a.mounted(id); err != nil || mounted {
|
if mounted, err := a.mounted(id); err != nil || mounted {
|
||||||
return err
|
return err
|
||||||
|
@ -325,7 +326,7 @@ func (a *Driver) mount(id string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := a.aufsMount(layers, rw, target); err != nil {
|
if err := a.aufsMount(layers, rw, target, mountLabel); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -358,21 +359,21 @@ func (a *Driver) Cleanup() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Driver) aufsMount(ro []string, rw, target string) (err error) {
|
func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Unmount(target)
|
Unmount(target)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err = a.tryMount(ro, rw, target); err != nil {
|
if err = a.tryMount(ro, rw, target, mountLabel); err != nil {
|
||||||
if err = a.mountRw(rw, target); err != nil {
|
if err = a.mountRw(rw, target, mountLabel); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, layer := range ro {
|
for _, layer := range ro {
|
||||||
branch := fmt.Sprintf("append:%s=ro+wh", layer)
|
data := label.FormatMountLabel(fmt.Sprintf("append:%s=ro+wh", layer), mountLabel)
|
||||||
if err = mount("none", target, "aufs", MsRemount, branch); err != nil {
|
if err = mount("none", target, "aufs", MsRemount, data); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -382,16 +383,18 @@ func (a *Driver) aufsMount(ro []string, rw, target string) (err error) {
|
||||||
|
|
||||||
// Try to mount using the aufs fast path, if this fails then
|
// Try to mount using the aufs fast path, if this fails then
|
||||||
// append ro layers.
|
// append ro layers.
|
||||||
func (a *Driver) tryMount(ro []string, rw, target string) (err error) {
|
func (a *Driver) tryMount(ro []string, rw, target, mountLabel string) (err error) {
|
||||||
var (
|
var (
|
||||||
rwBranch = fmt.Sprintf("%s=rw", rw)
|
rwBranch = fmt.Sprintf("%s=rw", rw)
|
||||||
roBranches = fmt.Sprintf("%s=ro+wh:", strings.Join(ro, "=ro+wh:"))
|
roBranches = fmt.Sprintf("%s=ro+wh:", strings.Join(ro, "=ro+wh:"))
|
||||||
|
data = label.FormatMountLabel(fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches), mountLabel)
|
||||||
)
|
)
|
||||||
return mount("none", target, "aufs", 0, fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches))
|
return mount("none", target, "aufs", 0, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Driver) mountRw(rw, target string) error {
|
func (a *Driver) mountRw(rw, target, mountLabel string) error {
|
||||||
return mount("none", target, "aufs", 0, fmt.Sprintf("br:%s,xino=/dev/shm/aufs.xino", rw))
|
data := label.FormatMountLabel(fmt.Sprintf("br:%s,xino=/dev/shm/aufs.xino", rw), mountLabel)
|
||||||
|
return mount("none", target, "aufs", 0, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func rollbackMount(target string, err error) {
|
func rollbackMount(target string, err error) {
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue