mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #5640 from crosbymichael/bump_v0.11.0
Bump version to v0.11.0
This commit is contained in:
commit
57aa2f2ef4
601 changed files with 49187 additions and 38079 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -23,3 +23,6 @@ bundles/
|
|||
vendor/pkg/
|
||||
pyenv
|
||||
Vagrantfile
|
||||
docs/AWS_S3_BUCKET
|
||||
docs/GIT_BRANCH
|
||||
docs/VERSION
|
||||
|
|
16
.travis.yml
16
.travis.yml
|
@ -10,21 +10,9 @@ install: true
|
|||
|
||||
before_script:
|
||||
- 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:
|
||||
- hack/travis/dco.py
|
||||
- hack/travis/gofmt.py
|
||||
- make -sC docs SPHINXOPTS=-qW docs man
|
||||
- hack/make.sh validate-dco
|
||||
- hack/make.sh validate-gofmt
|
||||
|
||||
# vim:set sw=2 ts=2:
|
||||
|
|
2
AUTHORS
2
AUTHORS
|
@ -20,6 +20,7 @@ Andrew Munsell <andrew@wizardapps.net>
|
|||
Andrews Medina <andrewsmedina@gmail.com>
|
||||
Andy Chambers <anchambers@paypal.com>
|
||||
andy diller <dillera@gmail.com>
|
||||
Andy Goldstein <agoldste@redhat.com>
|
||||
Andy Rothfusz <github@metaliveblog.com>
|
||||
Andy Smith <github@anarkystic.com>
|
||||
Anthony Bishopric <git@anthonybishopric.com>
|
||||
|
@ -44,6 +45,7 @@ Brian Olsen <brian@maven-group.org>
|
|||
Brian Shumate <brian@couchbase.com>
|
||||
Briehan Lombaard <briehan.lombaard@gmail.com>
|
||||
Bruno Bigras <bigras.bruno@gmail.com>
|
||||
Bryan Matsuo <bryan.matsuo@gmail.com>
|
||||
Caleb Spare <cespare@gmail.com>
|
||||
Calen Pennington <cale@edx.org>
|
||||
Carl X. Su <bcbcarl@gmail.com>
|
||||
|
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,5 +1,17 @@
|
|||
# Changelog
|
||||
|
||||
## 0.11.0 (2014-05-07)
|
||||
|
||||
#### Notable features since 0.10.0
|
||||
|
||||
* SELinux support for mount and process labels
|
||||
* Linked containers can be accessed by hostname
|
||||
* Use the net `--net` flag to allow advanced network configuration such as host networking so that containers can use the host's network interfaces
|
||||
* Add a ping endpoint to the Remote API to do healthchecks of your docker daemon
|
||||
* Logs can now be returned with an optional timestamp
|
||||
* Docker now works with registries that support SHA-512
|
||||
* Multiple registry endpoints are supported to allow registry mirrors
|
||||
|
||||
## 0.10.0 (2014-04-08)
|
||||
|
||||
#### Builder
|
||||
|
|
|
@ -82,7 +82,7 @@ editors have plugins that do this automatically, and there's also a git
|
|||
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
|
||||
|
@ -90,6 +90,10 @@ reference to all the issues that they address.
|
|||
|
||||
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
|
||||
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
|
||||
|
|
|
@ -42,6 +42,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq \
|
|||
libcap-dev \
|
||||
libsqlite3-dev \
|
||||
mercurial \
|
||||
pandoc \
|
||||
reprepro \
|
||||
ruby1.9.1 \
|
||||
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
|
||||
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
|
||||
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)
|
||||
Victor Vieux <vieux@docker.com> (@vieux)
|
||||
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`
|
||||
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_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_DOCS := docker run --rm -it -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)"
|
||||
DOCKER_RUN_DOCKER := docker run --rm -it --privileged -e TESTFLAGS -e TESTDIRS -e DOCKER_GRAPHDRIVER -e DOCKER_EXECDRIVER $(DOCKER_MOUNT) "$(DOCKER_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
|
||||
|
||||
|
@ -25,13 +26,19 @@ cross: build
|
|||
$(DOCKER_RUN_DOCKER) hack/make.sh binary cross
|
||||
|
||||
docs: docs-build
|
||||
$(DOCKER_RUN_DOCS)
|
||||
$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)" mkdocs serve
|
||||
|
||||
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
|
||||
$(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
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh test-integration
|
||||
|
@ -39,6 +46,9 @@ test-integration: build
|
|||
test-integration-cli: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary test-integration-cli
|
||||
|
||||
validate: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh validate-gofmt validate-dco
|
||||
|
||||
shell: build
|
||||
$(DOCKER_RUN_DOCKER) bash
|
||||
|
||||
|
@ -46,6 +56,9 @@ build: bundles
|
|||
docker build -t "$(DOCKER_IMAGE)" .
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
applications and databases.
|
||||
|
||||

|
||||

|
||||
|
||||
## Better than VMs
|
||||
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.10.0
|
||||
0.11.0
|
||||
|
|
|
@ -65,8 +65,13 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsC
|
|||
var (
|
||||
isTerminal = false
|
||||
terminalFd uintptr
|
||||
scheme = "http"
|
||||
)
|
||||
|
||||
if tlsConfig != nil {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
if in != nil {
|
||||
if file, ok := in.(*os.File); ok {
|
||||
terminalFd = file.Fd()
|
||||
|
@ -86,6 +91,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsC
|
|||
isTerminal: isTerminal,
|
||||
terminalFd: terminalFd,
|
||||
tlsConfig: tlsConfig,
|
||||
scheme: scheme,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,4 +105,5 @@ type DockerCli struct {
|
|||
isTerminal bool
|
||||
terminalFd uintptr
|
||||
tlsConfig *tls.Config
|
||||
scheme string
|
||||
}
|
||||
|
|
|
@ -1491,7 +1491,8 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
|
|||
|
||||
func (cli *DockerCli) CmdEvents(args ...string) error {
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
|
@ -1500,22 +1501,27 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
|
|||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
if *since != "" {
|
||||
loc := time.FixedZone(time.Now().Zone())
|
||||
var (
|
||||
v = url.Values{}
|
||||
loc = time.FixedZone(time.Now().Zone())
|
||||
)
|
||||
var setTime = func(key, value string) {
|
||||
format := "2006-01-02 15:04:05 -0700 MST"
|
||||
if len(*since) < len(format) {
|
||||
format = format[:len(*since)]
|
||||
if len(value) < len(format) {
|
||||
format = format[:len(value)]
|
||||
}
|
||||
|
||||
if t, err := time.ParseInLocation(format, *since, loc); err == nil {
|
||||
v.Set("since", strconv.FormatInt(t.Unix(), 10))
|
||||
if t, err := time.ParseInLocation(format, value, loc); err == nil {
|
||||
v.Set(key, strconv.FormatInt(t.Unix(), 10))
|
||||
} 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 {
|
||||
return err
|
||||
}
|
||||
|
@ -1577,6 +1583,7 @@ func (cli *DockerCli) CmdDiff(args ...string) error {
|
|||
func (cli *DockerCli) CmdLogs(args ...string) error {
|
||||
cmd := cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
|
@ -1597,14 +1604,16 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
|||
}
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("logs", "1")
|
||||
v.Set("stdout", "1")
|
||||
v.Set("stderr", "1")
|
||||
if *times {
|
||||
v.Set("timestamps", "1")
|
||||
}
|
||||
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 nil
|
||||
|
|
133
api/client/hijack.go
Normal file
133
api/client/hijack.go
Normal file
|
@ -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 (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
@ -11,12 +10,9 @@ import (
|
|||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
gosignal "os/signal"
|
||||
"regexp"
|
||||
goruntime "runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
@ -33,11 +29,14 @@ var (
|
|||
ErrConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
||||
)
|
||||
|
||||
func (cli *DockerCli) dial() (net.Conn, error) {
|
||||
if cli.tlsConfig != nil && cli.proto != "unix" {
|
||||
return tls.Dial(cli.proto, cli.addr, cli.tlsConfig)
|
||||
func (cli *DockerCli) HTTPClient() *http.Client {
|
||||
tr := &http.Transport{
|
||||
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) {
|
||||
|
@ -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)
|
||||
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.Host = cli.addr
|
||||
req.URL.Host = cli.addr
|
||||
req.URL.Scheme = cli.scheme
|
||||
if data != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
} else if method == "POST" {
|
||||
req.Header.Set("Content-Type", "plain/text")
|
||||
}
|
||||
dial, err := cli.dial()
|
||||
resp, err := cli.HTTPClient().Do(req)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return nil, -1, ErrConnectionRefused
|
||||
}
|
||||
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 {
|
||||
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))
|
||||
}
|
||||
|
||||
wrapper := utils.NewReadCloserWrapper(resp.Body, func() error {
|
||||
if resp != nil && resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
return clientconn.Close()
|
||||
})
|
||||
return wrapper, resp.StatusCode, nil
|
||||
return resp.Body, resp.StatusCode, nil
|
||||
}
|
||||
|
||||
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 {
|
||||
in = bytes.NewReader([]byte{})
|
||||
}
|
||||
|
||||
// fixme: refactor client to support redirect
|
||||
re := regexp.MustCompile("/+")
|
||||
path = re.ReplaceAllString(path, "/")
|
||||
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("http://v%s%s", api.APIVERSION, path), in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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" {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
resp, err := cli.HTTPClient().Do(req)
|
||||
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?")
|
||||
|
@ -184,126 +156,21 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
|
|||
}
|
||||
|
||||
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 {
|
||||
if stdout != nil || stderr != nil {
|
||||
// When TTY is ON, use regular copy
|
||||
if setRawTerminal {
|
||||
_, err = io.Copy(stdout, resp.Body)
|
||||
} else {
|
||||
_, err = utils.StdCopy(stdout, stderr, resp.Body)
|
||||
}
|
||||
utils.Debugf("[stream] End of stdout")
|
||||
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 {
|
||||
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
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
func (cli *DockerCli) resizeTty(id string) {
|
||||
height, width := cli.getTtySize()
|
||||
if height == 0 && width == 0 {
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
APIVERSION version.Version = "1.10"
|
||||
APIVERSION version.Version = "1.11"
|
||||
DEFAULTHTTPHOST = "127.0.0.1"
|
||||
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
|
||||
)
|
||||
|
|
|
@ -3,7 +3,6 @@ package server
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"code.google.com/p/go.net/websocket"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
|
@ -21,6 +20,8 @@ import (
|
|||
"strings"
|
||||
"syscall"
|
||||
|
||||
"code.google.com/p/go.net/websocket"
|
||||
|
||||
"github.com/dotcloud/docker/api"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"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)
|
||||
streamJSON(job, w, true)
|
||||
job.Setenv("since", r.Form.Get("since"))
|
||||
job.Setenv("until", r.Form.Get("until"))
|
||||
return job.Run()
|
||||
}
|
||||
|
||||
|
@ -327,6 +329,48 @@ func getContainersJSON(eng *engine.Engine, version version.Version, w http.Respo
|
|||
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 {
|
||||
if err := parseForm(r); err != nil {
|
||||
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.")
|
||||
}
|
||||
var (
|
||||
authEncoded = r.Header.Get("X-Registry-Auth")
|
||||
authConfig = ®istry.AuthConfig{}
|
||||
configFileEncoded = r.Header.Get("X-Registry-Config")
|
||||
configFile = ®istry.ConfigFile{}
|
||||
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
|
||||
// ConfigFile is present, any value provided as an AuthConfig directly will
|
||||
// be overridden. See BuildFile::CmdFrom for details.
|
||||
var (
|
||||
authEncoded = r.Header.Get("X-Registry-Auth")
|
||||
authConfig = ®istry.AuthConfig{}
|
||||
)
|
||||
if version.LessThan("1.9") && authEncoded != "" {
|
||||
authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
||||
// 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
|
||||
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("nocache", r.FormValue("nocache"))
|
||||
job.Setenv("rm", r.FormValue("rm"))
|
||||
job.SetenvJson("authConfig", authConfig)
|
||||
job.SetenvJson("configFile", configFile)
|
||||
job.SetenvJson("auth", configFile)
|
||||
|
||||
if err := job.Run(); err != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
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 {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// log the request
|
||||
|
@ -998,6 +1050,7 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
|
|||
}
|
||||
m := map[string]map[string]HttpApiFunc{
|
||||
"GET": {
|
||||
"/_ping": ping,
|
||||
"/events": getEvents,
|
||||
"/info": getInfo,
|
||||
"/version": getVersion,
|
||||
|
@ -1013,6 +1066,7 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
|
|||
"/containers/{name:.*}/changes": getContainersChanges,
|
||||
"/containers/{name:.*}/json": getContainersByName,
|
||||
"/containers/{name:.*}/top": getContainersTop,
|
||||
"/containers/{name:.*}/logs": getContainersLogs,
|
||||
"/containers/{name:.*}/attach/ws": wsContainersAttach,
|
||||
},
|
||||
"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
|
||||
// off a go routine to setup a serving http.Server for each.
|
||||
func ServeApi(job *engine.Job) engine.Status {
|
||||
if len(job.Args) == 0 {
|
||||
return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
|
||||
}
|
||||
var (
|
||||
protoAddrs = job.Args
|
||||
chErrors = make(chan error, len(protoAddrs))
|
||||
|
@ -1232,6 +1289,9 @@ func ServeApi(job *engine.Job) engine.Status {
|
|||
|
||||
for _, protoAddr := range protoAddrs {
|
||||
protoAddrParts := strings.SplitN(protoAddr, "://", 2)
|
||||
if len(protoAddrParts) != 2 {
|
||||
return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
|
||||
}
|
||||
go func() {
|
||||
log.Printf("Listening for HTTP on %s (%s)\n", protoAddrParts[0], protoAddrParts[1])
|
||||
chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job)
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/api"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -57,15 +57,7 @@ func TesthttpError(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetVersion(t *testing.T) {
|
||||
tmp, err := utils.TestDirectory("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
eng, err := engine.New(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eng := engine.New()
|
||||
var called bool
|
||||
eng.Register("version", func(job *engine.Job) engine.Status {
|
||||
called = true
|
||||
|
@ -80,49 +72,21 @@ func TestGetVersion(t *testing.T) {
|
|||
}
|
||||
return engine.StatusOK
|
||||
})
|
||||
|
||||
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)
|
||||
}
|
||||
r := serveRequest("GET", "/version", nil, eng, t)
|
||||
if !called {
|
||||
t.Fatalf("handler was not called")
|
||||
}
|
||||
out := engine.NewOutput()
|
||||
v, err := out.AddEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
v := readEnv(r.Body, t)
|
||||
if v.Get("Version") != "42.1" {
|
||||
t.Fatalf("%#v\n", v)
|
||||
}
|
||||
if _, err := io.Copy(out, r.Body); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
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)
|
||||
if r.HeaderMap.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf("%#v\n", r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInfo(t *testing.T) {
|
||||
tmp, err := utils.TestDirectory("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
eng, err := engine.New(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
eng := engine.New()
|
||||
var called bool
|
||||
eng.Register("info", func(job *engine.Job) engine.Status {
|
||||
called = true
|
||||
|
@ -134,47 +98,51 @@ func TestGetInfo(t *testing.T) {
|
|||
}
|
||||
return engine.StatusOK
|
||||
})
|
||||
|
||||
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)
|
||||
}
|
||||
r := serveRequest("GET", "/info", nil, eng, t)
|
||||
if !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()
|
||||
i, err := out.AddEnv()
|
||||
func serveRequest(method, target string, body io.Reader, eng *engine.Engine, t *testing.T) *httptest.ResponseRecorder {
|
||||
r := httptest.NewRecorder()
|
||||
req, err := http.NewRequest(method, target, body)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
out.Close()
|
||||
{
|
||||
expected := 42000
|
||||
result := i.GetInt("Images")
|
||||
if expected != result {
|
||||
t.Fatalf("%#v\n", result)
|
||||
}
|
||||
}
|
||||
{
|
||||
expected := 1
|
||||
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)
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func toJson(data interface{}, t *testing.T) io.Reader {
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return &buf
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ func ApplyLayer(dest string, layer ArchiveReader) error {
|
|||
parent := filepath.Dir(hdr.Name)
|
||||
parentPath := filepath.Join(dest, parent)
|
||||
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
||||
err = os.MkdirAll(parentPath, 600)
|
||||
err = os.MkdirAll(parentPath, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -2,19 +2,25 @@ package builtins
|
|||
|
||||
import (
|
||||
api "github.com/dotcloud/docker/api/server"
|
||||
"github.com/dotcloud/docker/daemon/networkdriver/bridge"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/runtime/networkdriver/bridge"
|
||||
"github.com/dotcloud/docker/registry"
|
||||
"github.com/dotcloud/docker/server"
|
||||
)
|
||||
|
||||
func Register(eng *engine.Engine) {
|
||||
daemon(eng)
|
||||
remote(eng)
|
||||
func Register(eng *engine.Engine) error {
|
||||
if err := daemon(eng); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := remote(eng); err != nil {
|
||||
return err
|
||||
}
|
||||
return registry.NewService().Install(eng)
|
||||
}
|
||||
|
||||
// remote: a RESTful api for cross-docker communication
|
||||
func remote(eng *engine.Engine) {
|
||||
eng.Register("serveapi", api.ServeApi)
|
||||
func remote(eng *engine.Engine) error {
|
||||
return eng.Register("serveapi", api.ServeApi)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
func daemon(eng *engine.Engine) {
|
||||
eng.Register("initserver", server.InitServer)
|
||||
eng.Register("init_networkdriver", bridge.InitDriver)
|
||||
func daemon(eng *engine.Engine) error {
|
||||
if err := eng.Register("initserver", server.InitServer); err != nil {
|
||||
return err
|
||||
}
|
||||
return eng.Register("init_networkdriver", bridge.InitDriver)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,13 @@ set -e
|
|||
# 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
|
||||
|
||||
: ${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
|
||||
zgrep() {
|
||||
|
@ -74,11 +80,7 @@ check_flags() {
|
|||
|
||||
if [ ! -e "$CONFIG" ]; then
|
||||
wrap_warning "warning: $CONFIG does not exist, searching other paths for kernel config..."
|
||||
for tryConfig in \
|
||||
'/proc/config.gz' \
|
||||
"/boot/config-$(uname -r)" \
|
||||
'/usr/src/linux/.config' \
|
||||
; do
|
||||
for tryConfig in "${possibleConfigs[@]}"; do
|
||||
if [ -e "$tryConfig" ]; then
|
||||
CONFIG="$tryConfig"
|
||||
break
|
||||
|
@ -98,12 +100,16 @@ echo
|
|||
echo 'Generally Necessary:'
|
||||
|
||||
echo -n '- '
|
||||
cgroupCpuDir="$(awk '/[, ]cpu([, ]|$)/ && $8 == "cgroup" { print $5 }' /proc/$$/mountinfo | head -n1)"
|
||||
cgroupDir="$(dirname "$cgroupCpuDir")"
|
||||
if [ -d "$cgroupDir/cpu" ]; then
|
||||
cgroupSubsystemDir="$(awk '/[, ](cpu|cpuacct|cpuset|devices|freezer|memory)([, ]|$)/ && $8 == "cgroup" { print $5 }' /proc/$$/mountinfo | head -n1)"
|
||||
cgroupDir="$(dirname "$cgroupSubsystemDir")"
|
||||
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]"
|
||||
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)"
|
||||
fi
|
||||
|
||||
|
@ -112,7 +118,8 @@ flags=(
|
|||
DEVPTS_MULTIPLE_INSTANCES
|
||||
CGROUPS CGROUP_DEVICE
|
||||
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
|
||||
)
|
||||
check_flags "${flags[@]}"
|
||||
|
|
|
@ -3,7 +3,7 @@ package main
|
|||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/runtime/graphdriver/devmapper"
|
||||
"github.com/dotcloud/docker/daemon/graphdriver/devmapper"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
|
|
2
contrib/man/.gitignore
vendored
Normal file
2
contrib/man/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
# these are generated by the md/md2man-all.sh script
|
||||
man*
|
5
contrib/man/md/Dockerfile
Normal file
5
contrib/man/md/Dockerfile
Normal file
|
@ -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;
|
71
contrib/man/md/README.md
Normal file
71
contrib/man/md/README.md
Normal file
|
@ -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/).
|
57
contrib/man/md/docker-attach.1.md
Normal file
57
contrib/man/md/docker-attach.1.md
Normal file
|
@ -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.
|
82
contrib/man/md/docker-build.1.md
Normal file
82
contrib/man/md/docker-build.1.md
Normal file
|
@ -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.
|
34
contrib/man/md/docker-commit.1.md
Normal file
34
contrib/man/md/docker-commit.1.md
Normal file
|
@ -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
|
24
contrib/man/md/docker-cp.1.md
Normal file
24
contrib/man/md/docker-cp.1.md
Normal file
|
@ -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.
|
||||
|
44
contrib/man/md/docker-diff.1.md
Normal file
44
contrib/man/md/docker-diff.1.md
Normal file
|
@ -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.
|
||||
|
||||
|
46
contrib/man/md/docker-events.1.md
Normal file
46
contrib/man/md/docker-events.1.md
Normal file
|
@ -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.
|
26
contrib/man/md/docker-export.1.md
Normal file
26
contrib/man/md/docker-export.1.md
Normal file
|
@ -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.
|
32
contrib/man/md/docker-history.1.md
Normal file
32
contrib/man/md/docker-history.1.md
Normal file
|
@ -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.
|
99
contrib/man/md/docker-images.1.md
Normal file
99
contrib/man/md/docker-images.1.md
Normal file
|
@ -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.
|
39
contrib/man/md/docker-import.1.md
Normal file
39
contrib/man/md/docker-import.1.md
Normal file
|
@ -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.
|
46
contrib/man/md/docker-info.1.md
Normal file
46
contrib/man/md/docker-info.1.md
Normal file
|
@ -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.
|
229
contrib/man/md/docker-inspect.1.md
Normal file
229
contrib/man/md/docker-inspect.1.md
Normal file
|
@ -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.
|
21
contrib/man/md/docker-kill.1.md
Normal file
21
contrib/man/md/docker-kill.1.md
Normal file
|
@ -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.
|
36
contrib/man/md/docker-load.1.md
Normal file
36
contrib/man/md/docker-load.1.md
Normal file
|
@ -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.
|
35
contrib/man/md/docker-login.1.md
Normal file
35
contrib/man/md/docker-login.1.md
Normal file
|
@ -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.
|
||||
|
26
contrib/man/md/docker-logs.1.md
Normal file
26
contrib/man/md/docker-logs.1.md
Normal file
|
@ -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.
|
15
contrib/man/md/docker-port.1.md
Normal file
15
contrib/man/md/docker-port.1.md
Normal file
|
@ -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.
|
68
contrib/man/md/docker-ps.1.md
Normal file
68
contrib/man/md/docker-ps.1.md
Normal file
|
@ -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.
|
37
contrib/man/md/docker-pull.1.md
Normal file
37
contrib/man/md/docker-pull.1.md
Normal file
|
@ -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.
|
||||
|
44
contrib/man/md/docker-push.1.md
Normal file
44
contrib/man/md/docker-push.1.md
Normal file
|
@ -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.
|
21
contrib/man/md/docker-restart.1.md
Normal file
21
contrib/man/md/docker-restart.1.md
Normal file
|
@ -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.
|
||||
|
56
contrib/man/md/docker-rm.1.md
Normal file
56
contrib/man/md/docker-rm.1.md
Normal file
|
@ -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.
|
35
contrib/man/md/docker-rmi.1.md
Normal file
35
contrib/man/md/docker-rmi.1.md
Normal file
|
@ -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.
|
343
contrib/man/md/docker-run.1.md
Normal file
343
contrib/man/md/docker-run.1.md
Normal file
|
@ -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.
|
35
contrib/man/md/docker-save.1.md
Normal file
35
contrib/man/md/docker-save.1.md
Normal file
|
@ -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.
|
||||
|
55
contrib/man/md/docker-search.1.md
Normal file
55
contrib/man/md/docker-search.1.md
Normal file
|
@ -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.
|
25
contrib/man/md/docker-start.1.md
Normal file
25
contrib/man/md/docker-start.1.md
Normal file
|
@ -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.
|
22
contrib/man/md/docker-stop.1.md
Normal file
22
contrib/man/md/docker-stop.1.md
Normal file
|
@ -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.
|
48
contrib/man/md/docker-tag.1.md
Normal file
48
contrib/man/md/docker-tag.1.md
Normal file
|
@ -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.
|
27
contrib/man/md/docker-top.1.md
Normal file
27
contrib/man/md/docker-top.1.md
Normal file
|
@ -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.
|
||||
|
23
contrib/man/md/docker-wait.1.md
Normal file
23
contrib/man/md/docker-wait.1.md
Normal file
|
@ -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.
|
||||
|
187
contrib/man/md/docker.1.md
Normal file
187
contrib/man/md/docker.1.md
Normal file
|
@ -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.
|
22
contrib/man/md/md2man-all.sh
Executable file
22
contrib/man/md/md2man-all.sh
Executable file
|
@ -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
|
50
contrib/man/old-man/docker-rm.md
Normal file
50
contrib/man/old-man/docker-rm.md
Normal file
|
@ -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.
|
82
contrib/mkimage-alpine.sh
Executable file
82
contrib/mkimage-alpine.sh
Executable file
|
@ -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 600 $DEV/initctl p
|
||||
mknod -m 666 $DEV/ptmx c 5 2
|
||||
ln -sf /proc/self/fd $DEV/fd
|
||||
|
||||
tar --numeric-owner -C $ROOTFS -c . | docker import - archlinux
|
||||
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.
|
||||
|
||||
|
153
daemon/attach.go
Normal file
153
daemon/attach.go
Normal file
|
@ -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 (
|
||||
"github.com/dotcloud/docker/nat"
|
|
@ -1,27 +1,8 @@
|
|||
package runtime
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"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/ioutil"
|
||||
"log"
|
||||
|
@ -31,6 +12,28 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
"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
|
||||
|
@ -44,7 +47,7 @@ var (
|
|||
validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`)
|
||||
)
|
||||
|
||||
type Runtime struct {
|
||||
type Daemon struct {
|
||||
repository string
|
||||
sysInitPath string
|
||||
containers *list.List
|
||||
|
@ -76,17 +79,17 @@ func remountPrivate(mountPoint string) error {
|
|||
return mount.ForceMount("", mountPoint, "none", "private")
|
||||
}
|
||||
|
||||
// List returns an array of all containers registered in the runtime.
|
||||
func (runtime *Runtime) List() []*Container {
|
||||
// List returns an array of all containers registered in the daemon.
|
||||
func (daemon *Daemon) List() []*Container {
|
||||
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))
|
||||
}
|
||||
return *containers
|
||||
}
|
||||
|
||||
func (runtime *Runtime) getContainerElement(id string) *list.Element {
|
||||
for e := runtime.containers.Front(); e != nil; e = e.Next() {
|
||||
func (daemon *Daemon) getContainerElement(id string) *list.Element {
|
||||
for e := daemon.containers.Front(); e != nil; e = e.Next() {
|
||||
container := e.Value.(*Container)
|
||||
if container.ID == id {
|
||||
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.
|
||||
// If the container is not found, or if an error occurs, nil is returned.
|
||||
func (runtime *Runtime) Get(name string) *Container {
|
||||
if c, _ := runtime.GetByName(name); c != nil {
|
||||
func (daemon *Daemon) Get(name string) *Container {
|
||||
if c, _ := daemon.GetByName(name); c != nil {
|
||||
return c
|
||||
}
|
||||
|
||||
id, err := runtime.idIndex.Get(name)
|
||||
id, err := daemon.idIndex.Get(name)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e := runtime.getContainerElement(id)
|
||||
e := daemon.getContainerElement(id)
|
||||
if e == 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,
|
||||
// false otherwise.
|
||||
func (runtime *Runtime) Exists(id string) bool {
|
||||
return runtime.Get(id) != nil
|
||||
func (daemon *Daemon) Exists(id string) bool {
|
||||
return daemon.Get(id) != nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) containerRoot(id string) string {
|
||||
return path.Join(runtime.repository, id)
|
||||
func (daemon *Daemon) containerRoot(id string) string {
|
||||
return path.Join(daemon.repository, id)
|
||||
}
|
||||
|
||||
// Load reads the contents of a container from disk
|
||||
// This is typically done at startup.
|
||||
func (runtime *Runtime) load(id string) (*Container, error) {
|
||||
container := &Container{root: runtime.containerRoot(id)}
|
||||
func (daemon *Daemon) load(id string) (*Container, error) {
|
||||
container := &Container{root: daemon.containerRoot(id)}
|
||||
if err := container.FromDisk(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if 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
|
||||
}
|
||||
|
||||
// Register makes a container object usable by the runtime as <container.ID>
|
||||
func (runtime *Runtime) Register(container *Container) error {
|
||||
if container.runtime != nil || runtime.Exists(container.ID) {
|
||||
// Register makes a container object usable by the daemon as <container.ID>
|
||||
func (daemon *Daemon) Register(container *Container) error {
|
||||
if container.daemon != nil || daemon.Exists(container.ID) {
|
||||
return fmt.Errorf("Container is already loaded")
|
||||
}
|
||||
if err := validateID(container.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runtime.ensureName(container); err != nil {
|
||||
if err := daemon.ensureName(container); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
container.runtime = runtime
|
||||
container.daemon = daemon
|
||||
|
||||
// Attach to stdout and stderr
|
||||
container.stderr = utils.NewWriteBroadcaster()
|
||||
|
@ -164,55 +164,50 @@ func (runtime *Runtime) Register(container *Container) error {
|
|||
container.stdinPipe = utils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
|
||||
}
|
||||
// done
|
||||
runtime.containers.PushBack(container)
|
||||
runtime.idIndex.Add(container.ID)
|
||||
daemon.containers.PushBack(container)
|
||||
daemon.idIndex.Add(container.ID)
|
||||
|
||||
// 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 the container is supposed to be running, make sure of it
|
||||
if container.State.IsRunning() {
|
||||
if container.State.IsGhost() {
|
||||
utils.Debugf("killing ghost %s", container.ID)
|
||||
utils.Debugf("killing old running container %s", container.ID)
|
||||
|
||||
existingPid := container.State.Pid
|
||||
container.State.SetGhost(false)
|
||||
container.State.SetStopped(0)
|
||||
existingPid := container.State.Pid
|
||||
container.State.SetStopped(0)
|
||||
|
||||
// We only have to handle this for lxc because the other drivers will ensure that
|
||||
// no ghost processes are left when docker dies
|
||||
if container.ExecDriver == "" || strings.Contains(container.ExecDriver, "lxc") {
|
||||
lxc.KillLxc(container.ID, 9)
|
||||
} else {
|
||||
// use the current driver and ensure that the container is dead x.x
|
||||
cmd := &execdriver.Command{
|
||||
ID: container.ID,
|
||||
}
|
||||
var err error
|
||||
cmd.Process, err = os.FindProcess(existingPid)
|
||||
if err != nil {
|
||||
utils.Debugf("cannot find existing process for %d", existingPid)
|
||||
}
|
||||
runtime.execDriver.Terminate(cmd)
|
||||
// We only have to handle this for lxc because the other drivers will ensure that
|
||||
// no processes are left when docker dies
|
||||
if container.ExecDriver == "" || strings.Contains(container.ExecDriver, "lxc") {
|
||||
lxc.KillLxc(container.ID, 9)
|
||||
} else {
|
||||
// use the current driver and ensure that the container is dead x.x
|
||||
cmd := &execdriver.Command{
|
||||
ID: container.ID,
|
||||
}
|
||||
if err := container.Unmount(); err != nil {
|
||||
utils.Debugf("ghost unmount error %s", err)
|
||||
}
|
||||
if err := container.ToDisk(); err != nil {
|
||||
utils.Debugf("saving ghost state to disk %s", err)
|
||||
var err error
|
||||
cmd.Process, err = os.FindProcess(existingPid)
|
||||
if err != nil {
|
||||
utils.Debugf("cannot find existing process for %d", existingPid)
|
||||
}
|
||||
daemon.execDriver.Terminate(cmd)
|
||||
}
|
||||
if err := container.Unmount(); err != nil {
|
||||
utils.Debugf("unmount error %s", err)
|
||||
}
|
||||
if err := container.ToDisk(); err != nil {
|
||||
utils.Debugf("saving stopped state to disk %s", err)
|
||||
}
|
||||
|
||||
info := runtime.execDriver.Info(container.ID)
|
||||
info := daemon.execDriver.Info(container.ID)
|
||||
if !info.IsRunning() {
|
||||
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")
|
||||
if err := container.Unmount(); err != nil {
|
||||
utils.Debugf("restart unmount error %s", err)
|
||||
}
|
||||
|
||||
container.State.SetGhost(false)
|
||||
container.State.SetStopped(0)
|
||||
if err := container.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -234,9 +229,9 @@ func (runtime *Runtime) Register(container *Container) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) ensureName(container *Container) error {
|
||||
func (daemon *Daemon) ensureName(container *Container) error {
|
||||
if container.Name == "" {
|
||||
name, err := generateRandomName(runtime)
|
||||
name, err := generateRandomName(daemon)
|
||||
if err != nil {
|
||||
name = utils.TruncateID(container.ID)
|
||||
}
|
||||
|
@ -245,8 +240,8 @@ func (runtime *Runtime) ensureName(container *Container) error {
|
|||
if err := container.ToDisk(); err != nil {
|
||||
utils.Debugf("Error saving container name %s", err)
|
||||
}
|
||||
if !runtime.containerGraph.Exists(name) {
|
||||
if _, err := runtime.containerGraph.Set(name, container.ID); err != nil {
|
||||
if !daemon.containerGraph.Exists(name) {
|
||||
if _, err := daemon.containerGraph.Set(name, container.ID); err != nil {
|
||||
utils.Debugf("Setting default id - %s", err)
|
||||
}
|
||||
}
|
||||
|
@ -254,7 +249,7 @@ func (runtime *Runtime) ensureName(container *Container) error {
|
|||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -263,13 +258,13 @@ func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream strin
|
|||
return nil
|
||||
}
|
||||
|
||||
// Destroy unregisters a container from the runtime and cleanly removes its contents from the filesystem.
|
||||
func (runtime *Runtime) Destroy(container *Container) error {
|
||||
// Destroy unregisters a container from the daemon and cleanly removes its contents from the filesystem.
|
||||
func (daemon *Daemon) Destroy(container *Container) error {
|
||||
if container == nil {
|
||||
return fmt.Errorf("The given container is <nil>")
|
||||
}
|
||||
|
||||
element := runtime.getContainerElement(container.ID)
|
||||
element := daemon.getContainerElement(container.ID)
|
||||
if element == nil {
|
||||
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
|
||||
}
|
||||
|
||||
if err := runtime.driver.Remove(container.ID); err != nil {
|
||||
return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", runtime.driver, container.ID, err)
|
||||
// Deregister the container before removing its directory, to avoid race conditions
|
||||
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)
|
||||
if err := runtime.driver.Remove(initID); err != nil {
|
||||
return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", runtime.driver, initID, err)
|
||||
if err := daemon.driver.Remove(initID); err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
|
||||
}
|
||||
selinux.FreeLxcContexts(container.ProcessLabel)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) restore() error {
|
||||
func (daemon *Daemon) restore() error {
|
||||
if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
||||
fmt.Printf("Loading containers: ")
|
||||
}
|
||||
dir, err := ioutil.ReadDir(runtime.repository)
|
||||
dir, err := ioutil.ReadDir(daemon.repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containers := make(map[string]*Container)
|
||||
currentDriver := runtime.driver.String()
|
||||
currentDriver := daemon.driver.String()
|
||||
|
||||
for _, v := range dir {
|
||||
id := v.Name()
|
||||
container, err := runtime.load(id)
|
||||
container, err := daemon.load(id)
|
||||
if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
||||
fmt.Print(".")
|
||||
}
|
||||
|
@ -332,12 +330,12 @@ func (runtime *Runtime) restore() error {
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
if entities := runtime.containerGraph.List("/", -1); entities != nil {
|
||||
if entities := daemon.containerGraph.List("/", -1); entities != nil {
|
||||
for _, p := range entities.Paths() {
|
||||
if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
||||
fmt.Print(".")
|
||||
|
@ -353,12 +351,12 @@ func (runtime *Runtime) restore() error {
|
|||
// Any containers that are left over do not exist in the graph
|
||||
for _, container := range containers {
|
||||
// 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 {
|
||||
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)
|
||||
}
|
||||
register(container)
|
||||
|
@ -372,38 +370,38 @@ func (runtime *Runtime) restore() error {
|
|||
}
|
||||
|
||||
// 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 (
|
||||
container *Container
|
||||
warnings []string
|
||||
)
|
||||
|
||||
img, err := runtime.repositories.LookupImage(config.Image)
|
||||
img, err := daemon.repositories.LookupImage(config.Image)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := runtime.checkImageDepth(img); err != nil {
|
||||
if err := daemon.checkImageDepth(img); err != nil {
|
||||
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
|
||||
}
|
||||
if container, err = runtime.newContainer(name, config, img); err != nil {
|
||||
if container, err = daemon.newContainer(name, config, img); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := runtime.createRootfs(container, img); err != nil {
|
||||
if err := daemon.createRootfs(container, img); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := container.ToDisk(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := runtime.Register(container); err != nil {
|
||||
if err := daemon.Register(container); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
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
|
||||
// init layer add to the restriction
|
||||
depth, err := img.Depth()
|
||||
|
@ -416,7 +414,7 @@ func (runtime *Runtime) checkImageDepth(img *image.Image) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) checkDeprecatedExpose(config *runconfig.Config) bool {
|
||||
func (daemon *Daemon) checkDeprecatedExpose(config *runconfig.Config) bool {
|
||||
if config != nil {
|
||||
if config.PortSpecs != nil {
|
||||
for _, p := range config.PortSpecs {
|
||||
|
@ -429,9 +427,9 @@ func (runtime *Runtime) checkDeprecatedExpose(config *runconfig.Config) bool {
|
|||
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{}
|
||||
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.")
|
||||
}
|
||||
if img.Config != nil {
|
||||
|
@ -445,14 +443,14 @@ func (runtime *Runtime) mergeAndVerifyConfig(config *runconfig.Config, img *imag
|
|||
return warnings, nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) generateIdAndName(name string) (string, string, error) {
|
||||
func (daemon *Daemon) generateIdAndName(name string) (string, string, error) {
|
||||
var (
|
||||
err error
|
||||
id = utils.GenerateRandomID()
|
||||
)
|
||||
|
||||
if name == "" {
|
||||
name, err = generateRandomName(runtime)
|
||||
name, err = generateRandomName(daemon)
|
||||
if err != nil {
|
||||
name = utils.TruncateID(id)
|
||||
}
|
||||
|
@ -465,19 +463,19 @@ func (runtime *Runtime) generateIdAndName(name string) (string, string, error) {
|
|||
name = "/" + name
|
||||
}
|
||||
// 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) {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
conflictingContainer, err := runtime.GetByName(name)
|
||||
conflictingContainer, err := daemon.GetByName(name)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "Could not find entity") {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
} else {
|
||||
|
@ -490,7 +488,7 @@ func (runtime *Runtime) generateIdAndName(name string) (string, string, error) {
|
|||
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
|
||||
// FIXME: the lxc template no longer needs to set a default 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 (
|
||||
entrypoint string
|
||||
args []string
|
||||
|
@ -513,18 +511,18 @@ func (runtime *Runtime) getEntrypointAndArgs(config *runconfig.Config) (string,
|
|||
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 (
|
||||
id string
|
||||
err error
|
||||
)
|
||||
id, name, err = runtime.generateIdAndName(name)
|
||||
id, name, err = daemon.generateIdAndName(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
runtime.generateHostname(id, config)
|
||||
entrypoint, args := runtime.getEntrypointAndArgs(config)
|
||||
daemon.generateHostname(id, config)
|
||||
entrypoint, args := daemon.getEntrypointAndArgs(config)
|
||||
|
||||
container := &Container{
|
||||
// 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
|
||||
NetworkSettings: &NetworkSettings{},
|
||||
Name: name,
|
||||
Driver: runtime.driver.String(),
|
||||
ExecDriver: runtime.execDriver.Name(),
|
||||
Driver: daemon.driver.String(),
|
||||
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
|
||||
}
|
||||
|
||||
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.
|
||||
// This doubles as a barrier to avoid race conditions.
|
||||
if err := os.Mkdir(container.root, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
initPath, err := runtime.driver.Get(initID)
|
||||
initPath, err := daemon.driver.Get(initID, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer runtime.driver.Put(initID)
|
||||
defer daemon.driver.Put(initID)
|
||||
|
||||
if err := graph.SetupInitLayer(initPath); err != nil {
|
||||
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 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.
|
||||
// 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?
|
||||
if err := container.Mount(); err != nil {
|
||||
return nil, err
|
||||
|
@ -595,13 +597,13 @@ func (runtime *Runtime) Commit(container *Container, repository, tag, comment, a
|
|||
containerImage = container.Image
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
// Register the image if needed
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -618,31 +620,31 @@ func GetFullContainerName(name string) (string, error) {
|
|||
return name, nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) GetByName(name string) (*Container, error) {
|
||||
func (daemon *Daemon) GetByName(name string) (*Container, error) {
|
||||
fullName, err := GetFullContainerName(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entity := runtime.containerGraph.Get(fullName)
|
||||
entity := daemon.containerGraph.Get(fullName)
|
||||
if entity == nil {
|
||||
return nil, fmt.Errorf("Could not find entity for %s", name)
|
||||
}
|
||||
e := runtime.getContainerElement(entity.ID())
|
||||
e := daemon.getContainerElement(entity.ID())
|
||||
if e == nil {
|
||||
return nil, fmt.Errorf("Could not find container for entity id %s", entity.ID())
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
children := make(map[string]*Container)
|
||||
|
||||
err = runtime.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
|
||||
c := runtime.Get(e.ID())
|
||||
err = daemon.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
|
||||
c := daemon.Get(e.ID())
|
||||
if c == nil {
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
if !runtime.containerGraph.Exists(fullName) {
|
||||
_, err := runtime.containerGraph.Set(fullName, child.ID)
|
||||
if !daemon.containerGraph.Exists(fullName) {
|
||||
_, err := daemon.containerGraph.Set(fullName, child.ID)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FIXME: harmonize with NewGraph()
|
||||
func NewRuntime(config *daemonconfig.Config, eng *engine.Engine) (*Runtime, error) {
|
||||
runtime, err := NewRuntimeFromDirectory(config, eng)
|
||||
func NewDaemon(config *daemonconfig.Config, eng *engine.Engine) (*Daemon, error) {
|
||||
daemon, err := NewDaemonFromDirectory(config, eng)
|
||||
if err != nil {
|
||||
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 {
|
||||
selinux.SetDisabled()
|
||||
}
|
||||
|
||||
// Set the default driver
|
||||
graphdriver.DefaultDriver = config.GraphDriver
|
||||
|
||||
|
@ -693,9 +694,9 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -774,12 +775,12 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
|
|||
return nil, err
|
||||
}
|
||||
|
||||
runtime := &Runtime{
|
||||
repository: runtimeRepo,
|
||||
daemon := &Daemon{
|
||||
repository: daemonRepo,
|
||||
containers: list.New(),
|
||||
graph: g,
|
||||
repositories: repositories,
|
||||
idIndex: utils.NewTruncIndex(),
|
||||
idIndex: utils.NewTruncIndex([]string{}),
|
||||
sysInfo: sysInfo,
|
||||
volumes: volumes,
|
||||
config: config,
|
||||
|
@ -790,19 +791,19 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
|
|||
eng: eng,
|
||||
}
|
||||
|
||||
if err := runtime.checkLocaldns(); err != nil {
|
||||
if err := daemon.checkLocaldns(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := runtime.restore(); err != nil {
|
||||
if err := daemon.restore(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return runtime, nil
|
||||
return daemon, nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) shutdown() error {
|
||||
func (daemon *Daemon) shutdown() error {
|
||||
group := sync.WaitGroup{}
|
||||
utils.Debugf("starting clean shutdown of all containers...")
|
||||
for _, container := range runtime.List() {
|
||||
for _, container := range daemon.List() {
|
||||
c := container
|
||||
if c.State.IsRunning() {
|
||||
utils.Debugf("stopping %s", c.ID)
|
||||
|
@ -823,22 +824,22 @@ func (runtime *Runtime) shutdown() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) Close() error {
|
||||
func (daemon *Daemon) Close() error {
|
||||
errorsStrings := []string{}
|
||||
if err := runtime.shutdown(); err != nil {
|
||||
utils.Errorf("runtime.shutdown(): %s", err)
|
||||
if err := daemon.shutdown(); err != nil {
|
||||
utils.Errorf("daemon.shutdown(): %s", err)
|
||||
errorsStrings = append(errorsStrings, err.Error())
|
||||
}
|
||||
if err := portallocator.ReleaseAll(); err != nil {
|
||||
utils.Errorf("portallocator.ReleaseAll(): %s", err)
|
||||
errorsStrings = append(errorsStrings, err.Error())
|
||||
}
|
||||
if err := runtime.driver.Cleanup(); err != nil {
|
||||
utils.Errorf("runtime.driver.Cleanup(): %s", err.Error())
|
||||
if err := daemon.driver.Cleanup(); err != nil {
|
||||
utils.Errorf("daemon.driver.Cleanup(): %s", err.Error())
|
||||
errorsStrings = append(errorsStrings, err.Error())
|
||||
}
|
||||
if err := runtime.containerGraph.Close(); err != nil {
|
||||
utils.Errorf("runtime.containerGraph.Close(): %s", err.Error())
|
||||
if err := daemon.containerGraph.Close(); err != nil {
|
||||
utils.Errorf("daemon.containerGraph.Close(): %s", err.Error())
|
||||
errorsStrings = append(errorsStrings, err.Error())
|
||||
}
|
||||
if len(errorsStrings) > 0 {
|
||||
|
@ -847,55 +848,55 @@ func (runtime *Runtime) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) Mount(container *Container) error {
|
||||
dir, err := runtime.driver.Get(container.ID)
|
||||
func (daemon *Daemon) Mount(container *Container) error {
|
||||
dir, err := daemon.driver.Get(container.ID, container.GetMountLabel())
|
||||
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 == "" {
|
||||
container.basefs = dir
|
||||
} else if container.basefs != dir {
|
||||
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
|
||||
}
|
||||
|
||||
func (runtime *Runtime) Unmount(container *Container) error {
|
||||
runtime.driver.Put(container.ID)
|
||||
func (daemon *Daemon) Unmount(container *Container) error {
|
||||
daemon.driver.Put(container.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) Changes(container *Container) ([]archive.Change, error) {
|
||||
if differ, ok := runtime.driver.(graphdriver.Differ); ok {
|
||||
func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) {
|
||||
if differ, ok := daemon.driver.(graphdriver.Differ); ok {
|
||||
return differ.Changes(container.ID)
|
||||
}
|
||||
cDir, err := runtime.driver.Get(container.ID)
|
||||
cDir, err := daemon.driver.Get(container.ID, "")
|
||||
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)
|
||||
initDir, err := runtime.driver.Get(container.ID + "-init")
|
||||
defer daemon.driver.Put(container.ID)
|
||||
initDir, err := daemon.driver.Get(container.ID+"-init", "")
|
||||
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)
|
||||
}
|
||||
|
||||
func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
|
||||
if differ, ok := runtime.driver.(graphdriver.Differ); ok {
|
||||
func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) {
|
||||
if differ, ok := daemon.driver.(graphdriver.Differ); ok {
|
||||
return differ.Diff(container.ID)
|
||||
}
|
||||
|
||||
changes, err := runtime.Changes(container)
|
||||
changes, err := daemon.Changes(container)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cDir, err := runtime.driver.Get(container.ID)
|
||||
cDir, err := daemon.driver.Get(container.ID, "")
|
||||
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)
|
||||
|
@ -904,26 +905,26 @@ func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
|
|||
}
|
||||
return utils.NewReadCloserWrapper(archive, func() error {
|
||||
err := archive.Close()
|
||||
runtime.driver.Put(container.ID)
|
||||
daemon.driver.Put(container.ID)
|
||||
return err
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
||||
return runtime.execDriver.Run(c.command, pipes, startCallback)
|
||||
func (daemon *Daemon) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
||||
return daemon.execDriver.Run(c.command, pipes, startCallback)
|
||||
}
|
||||
|
||||
func (runtime *Runtime) Kill(c *Container, sig int) error {
|
||||
return runtime.execDriver.Kill(c.command, sig)
|
||||
func (daemon *Daemon) Kill(c *Container, sig int) error {
|
||||
return daemon.execDriver.Kill(c.command, sig)
|
||||
}
|
||||
|
||||
// Nuke kills all containers then removes all content
|
||||
// from the content root, including images, volumes and
|
||||
// container filesystems.
|
||||
// Again: this will remove your entire docker runtime!
|
||||
func (runtime *Runtime) Nuke() error {
|
||||
// Again: this will remove your entire docker daemon!
|
||||
func (daemon *Daemon) Nuke() error {
|
||||
var wg sync.WaitGroup
|
||||
for _, container := range runtime.List() {
|
||||
for _, container := range daemon.List() {
|
||||
wg.Add(1)
|
||||
go func(c *Container) {
|
||||
c.Kill()
|
||||
|
@ -931,63 +932,63 @@ func (runtime *Runtime) Nuke() error {
|
|||
}(container)
|
||||
}
|
||||
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
|
||||
// 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
|
||||
// can go away.
|
||||
func (runtime *Runtime) Graph() *graph.Graph {
|
||||
return runtime.graph
|
||||
func (daemon *Daemon) Graph() *graph.Graph {
|
||||
return daemon.graph
|
||||
}
|
||||
|
||||
func (runtime *Runtime) Repositories() *graph.TagStore {
|
||||
return runtime.repositories
|
||||
func (daemon *Daemon) Repositories() *graph.TagStore {
|
||||
return daemon.repositories
|
||||
}
|
||||
|
||||
func (runtime *Runtime) Config() *daemonconfig.Config {
|
||||
return runtime.config
|
||||
func (daemon *Daemon) Config() *daemonconfig.Config {
|
||||
return daemon.config
|
||||
}
|
||||
|
||||
func (runtime *Runtime) SystemConfig() *sysinfo.SysInfo {
|
||||
return runtime.sysInfo
|
||||
func (daemon *Daemon) SystemConfig() *sysinfo.SysInfo {
|
||||
return daemon.sysInfo
|
||||
}
|
||||
|
||||
func (runtime *Runtime) SystemInitPath() string {
|
||||
return runtime.sysInitPath
|
||||
func (daemon *Daemon) SystemInitPath() string {
|
||||
return daemon.sysInitPath
|
||||
}
|
||||
|
||||
func (runtime *Runtime) GraphDriver() graphdriver.Driver {
|
||||
return runtime.driver
|
||||
func (daemon *Daemon) GraphDriver() graphdriver.Driver {
|
||||
return daemon.driver
|
||||
}
|
||||
|
||||
func (runtime *Runtime) ExecutionDriver() execdriver.Driver {
|
||||
return runtime.execDriver
|
||||
func (daemon *Daemon) ExecutionDriver() execdriver.Driver {
|
||||
return daemon.execDriver
|
||||
}
|
||||
|
||||
func (runtime *Runtime) Volumes() *graph.Graph {
|
||||
return runtime.volumes
|
||||
func (daemon *Daemon) Volumes() *graph.Graph {
|
||||
return daemon.volumes
|
||||
}
|
||||
|
||||
func (runtime *Runtime) ContainerGraph() *graphdb.Database {
|
||||
return runtime.containerGraph
|
||||
func (daemon *Daemon) ContainerGraph() *graphdb.Database {
|
||||
return daemon.containerGraph
|
||||
}
|
||||
|
||||
func (runtime *Runtime) SetServer(server Server) {
|
||||
runtime.srv = server
|
||||
func (daemon *Daemon) SetServer(server Server) {
|
||||
daemon.srv = server
|
||||
}
|
||||
|
||||
func (runtime *Runtime) checkLocaldns() error {
|
||||
resolvConf, err := utils.GetResolvConf()
|
||||
func (daemon *Daemon) checkLocaldns() error {
|
||||
resolvConf, err := resolvconf.Get()
|
||||
if err != nil {
|
||||
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)
|
||||
runtime.config.Dns = DefaultDns
|
||||
daemon.config.Dns = DefaultDns
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
// +build !exclude_graphdriver_aufs
|
||||
|
||||
package runtime
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/daemon/graphdriver"
|
||||
"github.com/dotcloud/docker/daemon/graphdriver/aufs"
|
||||
"github.com/dotcloud/docker/graph"
|
||||
"github.com/dotcloud/docker/runtime/graphdriver"
|
||||
"github.com/dotcloud/docker/runtime/graphdriver/aufs"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
)
|
||||
|
7
daemon/daemon_btrfs.go
Normal file
7
daemon/daemon_btrfs.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build !exclude_graphdriver_btrfs
|
||||
|
||||
package daemon
|
||||
|
||||
import (
|
||||
_ "github.com/dotcloud/docker/daemon/graphdriver/btrfs"
|
||||
)
|
7
daemon/daemon_devicemapper.go
Normal file
7
daemon/daemon_devicemapper.go
Normal file
|
@ -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
|
||||
|
||||
package runtime
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/runtime/graphdriver"
|
||||
"github.com/dotcloud/docker/daemon/graphdriver"
|
||||
)
|
||||
|
||||
func migrateIfAufs(driver graphdriver.Driver, root string) error {
|
|
@ -89,8 +89,10 @@ type Driver interface {
|
|||
|
||||
// Network settings of the container
|
||||
type Network struct {
|
||||
Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled
|
||||
Mtu int `json:"mtu"`
|
||||
Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled
|
||||
Mtu int `json:"mtu"`
|
||||
ContainerID string `json:"container_id"` // id of the container to join network.
|
||||
HostNetworking bool `json:"host_networking"`
|
||||
}
|
||||
|
||||
type NetworkInterface struct {
|
|
@ -2,10 +2,10 @@ package execdrivers
|
|||
|
||||
import (
|
||||
"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/runtime/execdriver"
|
||||
"github.com/dotcloud/docker/runtime/execdriver/lxc"
|
||||
"github.com/dotcloud/docker/runtime/execdriver/native"
|
||||
"path"
|
||||
)
|
||||
|
|
@ -1,11 +1,8 @@
|
|||
package lxc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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"
|
||||
"log"
|
||||
"os"
|
||||
|
@ -16,6 +13,12 @@ import (
|
|||
"strings"
|
||||
"syscall"
|
||||
"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"
|
||||
|
@ -25,23 +28,21 @@ func init() {
|
|||
if err := setupEnv(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := setupHostname(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := setupNetworking(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := setupCapabilities(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := setupWorkingDirectory(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := system.CloseFdsFrom(3); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := changeUser(args); err != nil {
|
||||
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 {
|
||||
return -1, err
|
||||
}
|
||||
if err := d.generateEnvConfig(c); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
configPath, err := d.generateLXCConfig(c)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
|
@ -416,3 +420,14 @@ func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
|
|||
}
|
||||
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 (
|
||||
"encoding/json"
|
||||
"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"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"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
|
||||
|
@ -149,6 +150,7 @@ func setupCapabilities(args *execdriver.InitArgs) error {
|
|||
capability.CAP_MAC_OVERRIDE,
|
||||
capability.CAP_MAC_ADMIN,
|
||||
capability.CAP_NET_ADMIN,
|
||||
capability.CAP_SYSLOG,
|
||||
}
|
||||
|
||||
c, err := capability.NewPid(os.Getpid())
|
|
@ -1,10 +1,11 @@
|
|||
package lxc
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/pkg/label"
|
||||
"github.com/dotcloud/docker/runtime/execdriver"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/dotcloud/docker/daemon/execdriver"
|
||||
"github.com/dotcloud/docker/pkg/label"
|
||||
)
|
||||
|
||||
const LxcTemplate = `
|
||||
|
@ -13,12 +14,13 @@ const LxcTemplate = `
|
|||
lxc.network.type = veth
|
||||
lxc.network.link = {{.Network.Interface.Bridge}}
|
||||
lxc.network.name = eth0
|
||||
{{else}}
|
||||
lxc.network.mtu = {{.Network.Mtu}}
|
||||
{{else if not .Network.HostNetworking}}
|
||||
# network is disabled (-n=false)
|
||||
lxc.network.type = empty
|
||||
lxc.network.flags = up
|
||||
{{end}}
|
||||
lxc.network.mtu = {{.Network.Mtu}}
|
||||
{{end}}
|
||||
|
||||
# root filesystem
|
||||
{{$ROOTFS := .Rootfs}}
|
||||
|
@ -82,12 +84,11 @@ lxc.pivotdir = lxc_putold
|
|||
|
||||
# NOTICE: These mounts must be applied within the namespace
|
||||
|
||||
# WARNING: procfs is a known attack vector and should probably be disabled
|
||||
# if your userspace allows it. eg. see http://blog.zx2c4.com/749
|
||||
# WARNING: mounting procfs and/or sysfs read-write is a known attack vector.
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
{{if .Tty}}
|
||||
|
@ -109,7 +110,7 @@ lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabS
|
|||
{{if .AppArmor}}
|
||||
lxc.aa_profile = unconfined
|
||||
{{else}}
|
||||
#lxc.aa_profile = unconfined
|
||||
# Let AppArmor normal confinement take place (i.e., not unconfined)
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
@ -3,7 +3,7 @@ package lxc
|
|||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/runtime/execdriver"
|
||||
"github.com/dotcloud/docker/daemon/execdriver"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
|
@ -2,12 +2,13 @@ package configuration
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
)
|
||||
|
||||
type Action func(*libcontainer.Container, interface{}, string) error
|
||||
|
@ -21,10 +22,13 @@ var actions = map[string]Action{
|
|||
|
||||
"net.join": joinNetNamespace, // join another containers net namespace
|
||||
|
||||
"cgroups.cpu_shares": cpuShares, // set the cpu shares
|
||||
"cgroups.memory": memory, // set the memory limit
|
||||
"cgroups.memory_swap": memorySwap, // set the memory swap limit
|
||||
"cgroups.cpuset.cpus": cpusetCpus, // set the cpus used
|
||||
"cgroups.cpu_shares": cpuShares, // set the cpu shares
|
||||
"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.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
|
||||
|
||||
|
@ -40,6 +44,15 @@ func cpusetCpus(container *libcontainer.Container, context interface{}, value st
|
|||
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 {
|
||||
container.Context["apparmor_profile"] = value
|
||||
return nil
|
||||
|
@ -70,6 +83,19 @@ func memory(container *libcontainer.Container, context interface{}, value string
|
|||
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 {
|
||||
if container.Cgroups == nil {
|
||||
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 {
|
||||
c := container.CapabilitiesMask.Get(value)
|
||||
if c == nil {
|
||||
return fmt.Errorf("%s is not a valid capability", value)
|
||||
}
|
||||
c.Enabled = true
|
||||
container.CapabilitiesMask[value] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func dropCap(container *libcontainer.Container, context interface{}, value string) error {
|
||||
c := container.CapabilitiesMask.Get(value)
|
||||
if c == nil {
|
||||
return fmt.Errorf("%s is not a valid capability", value)
|
||||
}
|
||||
c.Enabled = false
|
||||
container.CapabilitiesMask[value] = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func addNamespace(container *libcontainer.Container, context interface{}, value string) error {
|
||||
ns := container.Namespaces.Get(value)
|
||||
if ns == nil {
|
||||
return fmt.Errorf("%s is not a valid namespace", value[1:])
|
||||
}
|
||||
ns.Enabled = true
|
||||
container.Namespaces[value] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func dropNamespace(container *libcontainer.Container, context interface{}, value string) error {
|
||||
ns := container.Namespaces.Get(value)
|
||||
if ns == nil {
|
||||
return fmt.Errorf("%s is not a valid namespace", value[1:])
|
||||
}
|
||||
ns.Enabled = false
|
||||
container.Namespaces[value] = false
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package configuration
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/runtime/execdriver/native/template"
|
||||
"testing"
|
||||
|
||||
"github.com/dotcloud/docker/daemon/execdriver/native/template"
|
||||
)
|
||||
|
||||
func TestSetReadonlyRootFs(t *testing.T) {
|
||||
|
@ -38,10 +39,10 @@ func TestConfigurationsDoNotConflict(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !container1.CapabilitiesMask.Get("NET_ADMIN").Enabled {
|
||||
if !container1.CapabilitiesMask["NET_ADMIN"] {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +94,7 @@ func TestCpuShares(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCgroupMemory(t *testing.T) {
|
||||
func TestMemory(t *testing.T) {
|
||||
var (
|
||||
container = template.New()
|
||||
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) {
|
||||
var (
|
||||
container = template.New()
|
||||
|
@ -121,10 +138,10 @@ func TestAddCap(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !container.CapabilitiesMask.Get("MKNOD").Enabled {
|
||||
if !container.CapabilitiesMask["MKNOD"] {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
@ -137,14 +154,14 @@ func TestDropCap(t *testing.T) {
|
|||
}
|
||||
)
|
||||
// enabled all caps like in privileged mode
|
||||
for _, c := range container.CapabilitiesMask {
|
||||
c.Enabled = true
|
||||
for key := range container.CapabilitiesMask {
|
||||
container.CapabilitiesMask[key] = true
|
||||
}
|
||||
if err := ParseConfiguration(container, nil, opts); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if container.CapabilitiesMask.Get("MKNOD").Enabled {
|
||||
if container.CapabilitiesMask["MKNOD"] {
|
||||
t.Fatal("container should not have MKNOD enabled")
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +177,7 @@ func TestDropNamespace(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if container.Namespaces.Get("NEWNET").Enabled {
|
||||
if container.Namespaces["NEWNET"] {
|
||||
t.Fatal("container should not have NEWNET enabled")
|
||||
}
|
||||
}
|
|
@ -3,12 +3,13 @@ package native
|
|||
import (
|
||||
"fmt"
|
||||
"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/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
|
||||
|
@ -24,6 +25,7 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container
|
|||
container.Cgroups.Name = c.ID
|
||||
// check to see if we are running in ramdisk to disable pivot root
|
||||
container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
|
||||
container.Context["restrictions"] = "true"
|
||||
|
||||
if err := d.createNetwork(container, c); err != nil {
|
||||
return nil, err
|
||||
|
@ -32,6 +34,8 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container
|
|||
if err := d.setPrivileged(container); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
container.Mounts = append(container.Mounts, libcontainer.Mount{Type: "devtmpfs"})
|
||||
}
|
||||
if err := d.setupCgroups(container, c); err != nil {
|
||||
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 {
|
||||
if c.Network.HostNetworking {
|
||||
container.Namespaces["NEWNET"] = false
|
||||
return nil
|
||||
}
|
||||
container.Networks = []*libcontainer.Network{
|
||||
{
|
||||
Mtu: c.Network.Mtu,
|
||||
|
@ -72,15 +80,34 @@ func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.
|
|||
}
|
||||
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
|
||||
}
|
||||
|
||||
func (d *driver) setPrivileged(container *libcontainer.Container) error {
|
||||
for _, c := range container.CapabilitiesMask {
|
||||
c.Enabled = true
|
||||
for key := range container.CapabilitiesMask {
|
||||
container.CapabilitiesMask[key] = true
|
||||
}
|
||||
container.Cgroups.DeviceAccess = true
|
||||
container.Context["apparmor_profile"] = "unconfined"
|
||||
|
||||
delete(container.Context, "restrictions")
|
||||
|
||||
if apparmor.IsEnabled() {
|
||||
container.Context["apparmor_profile"] = "unconfined"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -88,6 +115,7 @@ func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.C
|
|||
if c.Resources != nil {
|
||||
container.Cgroups.CpuShares = c.Resources.CpuShares
|
||||
container.Cgroups.Memory = c.Resources.Memory
|
||||
container.Cgroups.MemoryReservation = c.Resources.Memory
|
||||
container.Cgroups.MemorySwap = c.Resources.MemorySwap
|
||||
}
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
func (d *driver) setupLabels(container *libcontainer.Container, c *execdriver.Command) error {
|
||||
labels := c.Config["label"]
|
||||
if len(labels) > 0 {
|
||||
process, mount, err := label.GenLabels(labels[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
container.Context["mount_label"] = mount
|
||||
container.Context["process_label"] = process
|
||||
}
|
||||
container.Context["process_label"] = c.Config["process_label"][0]
|
||||
container.Context["mount_label"] = c.Config["mount_label"][0]
|
||||
return nil
|
||||
}
|
|
@ -3,35 +3,31 @@ package native
|
|||
import (
|
||||
"encoding/json"
|
||||
"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"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"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 (
|
||||
DriverName = "native"
|
||||
Version = "0.1"
|
||||
Version = "0.2"
|
||||
BackupApparmorProfilePath = "apparmor/docker.back" // relative to docker root
|
||||
)
|
||||
|
||||
func init() {
|
||||
execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
|
||||
var (
|
||||
container *libcontainer.Container
|
||||
ns = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{args.Root}, createLogger(""))
|
||||
)
|
||||
var container *libcontainer.Container
|
||||
f, err := os.Open(filepath.Join(args.Root, "container.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -42,7 +38,7 @@ func init() {
|
|||
}
|
||||
f.Close()
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
rootfs, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -50,7 +46,7 @@ func init() {
|
|||
if err != nil {
|
||||
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 nil
|
||||
|
@ -87,35 +83,49 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
|
|||
d.activeContainers[c.ID] = &c.Cmd
|
||||
|
||||
var (
|
||||
term nsinit.Terminal
|
||||
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...)
|
||||
dataPath = filepath.Join(d.root, c.ID)
|
||||
args = append([]string{c.Entrypoint}, c.Arguments...)
|
||||
)
|
||||
if err := d.createContainerRoot(c.ID); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
|
@ -228,65 +238,17 @@ func getEnv(key string, env []string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
type dockerCommandFactory struct {
|
||||
c *execdriver.Command
|
||||
driver *driver
|
||||
}
|
||||
|
||||
// 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
|
||||
func getTerminal(c *execdriver.Command, pipes *execdriver.Pipes) nsinit.Terminal {
|
||||
var term nsinit.Terminal
|
||||
if c.Tty {
|
||||
term = &dockerTtyTerm{
|
||||
pipes: pipes,
|
||||
}
|
||||
} else {
|
||||
w = ioutil.Discard
|
||||
term = &dockerStdTerm{
|
||||
pipes: pipes,
|
||||
}
|
||||
}
|
||||
return log.New(w, "[libcontainer] ", log.LstdFlags)
|
||||
c.Terminal = term
|
||||
return term
|
||||
}
|
47
daemon/execdriver/native/template/default_template.go
Normal file
47
daemon/execdriver/native/template/default_template.go
Normal file
|
@ -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
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/runtime/execdriver"
|
||||
"github.com/dotcloud/docker/daemon/execdriver"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue