mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-19 12:16:30 +00:00
Compare commits
191 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e87d4b5af0 | |||
| 0a19f03d99 | |||
| 556ea285db | |||
| a34a1d598c | |||
| b6bb8c26fb | |||
| a3004aea15 | |||
| 8d7d152b87 | |||
| 04c702f617 | |||
| 842733a582 | |||
| f1d9a79214 | |||
| f97b8f403f | |||
| b5cc212e75 | |||
| 45dbc03636 | |||
| a22e1dd557 | |||
| ad0298c5a1 | |||
| 039cf28484 | |||
| 6702fac9af | |||
| 94ef56b8b3 | |||
| 5b0ca7c58a | |||
| 5d417ee6e8 | |||
| 9a1d077d9c | |||
| a2f92e92b5 | |||
| 3950ab5741 | |||
| 200258c2a0 | |||
| e0d0582d97 | |||
| da9b925066 | |||
| 736ba2937e | |||
| fe5baaf228 | |||
| 5fd7bc0d74 | |||
| 2d6c4af15e | |||
| 3f089980e4 | |||
| 25493c674b | |||
| fe7768f54d | |||
| c7055ebf84 | |||
| ed95e3d325 | |||
| 4f23c44a68 | |||
| e35ac39418 | |||
| f4dbb093eb | |||
| 6aa14d58ff | |||
| f3827116a3 | |||
| be83aacf80 | |||
| a58b402fc8 | |||
| 56cc398564 | |||
| 7aa5d784d7 | |||
| 5f285e4df4 | |||
| 67d34cebf7 | |||
| b633af5875 | |||
| 2c3d4a7f15 | |||
| 2c20ae1fef | |||
| 8a30a35b11 | |||
| 7e485e614c | |||
| e1f59b5987 | |||
| 04c79bb941 | |||
| 4158c70f58 | |||
| a9450f35cc | |||
| 08091ba9fb | |||
| 19f3bc00ee | |||
| 76d6bc9a9f | |||
| b9c3ba79a6 | |||
| 17fb8c3c5a | |||
| 0849a71036 | |||
| b53c39f606 | |||
| 5d72b7a582 | |||
| c50bd847a7 | |||
| fa2a6b3522 | |||
| f2d0d2e516 | |||
| 7be73fe0cb | |||
| 75847fb840 | |||
| dfad0c3da7 | |||
| 263604f570 | |||
| 6cbcdc7a6d | |||
| 0d762ef9d2 | |||
| 9f7231816f | |||
| 0430024bad | |||
| 6fa9da0363 | |||
| 2ec7433aa4 | |||
| 51e5073111 | |||
| c4f0a2aa00 | |||
| 581f297644 | |||
| cff248b35f | |||
| 12da51944a | |||
| 87417387c6 | |||
| b693a04658 | |||
| e50176876c | |||
| 22c2f04847 | |||
| 75f6674b11 | |||
| ea1862d346 | |||
| 9335cf9d29 | |||
| 22cf3df4c0 | |||
| c57d1e026d | |||
| ce6e6b18ad | |||
| a09d521221 | |||
| 3648ffe722 | |||
| 37e04b864e | |||
| 160f4e4a3b | |||
| a76b5fd324 | |||
| 04cc66b08e | |||
| 93f966cdc0 | |||
| 6b779ead93 | |||
| 7ac3502444 | |||
| 963b4d0b22 | |||
| 2801a373a8 | |||
| c2c88fec85 | |||
| 7825b5a5cd | |||
| 3040ad3581 | |||
| 84eaf42839 | |||
| 581df6aaa1 | |||
| b8410cd2ae | |||
| cc1782b318 | |||
| f8c24afcb4 | |||
| 8b5c7d6a60 | |||
| 05e3f2f62f | |||
| 957fcb10bf | |||
| 9c5885135f | |||
| 03d7cf1aef | |||
| 7ff56c563d | |||
| e6ff99629b | |||
| 50a8ce2b28 | |||
| dc5a0118a1 | |||
| f2671e07b3 | |||
| 033ce66707 | |||
| 6a01c901d3 | |||
| c75fd9b5da | |||
| b83bbbe864 | |||
| f110597a78 | |||
| adfbdc361c | |||
| 587a0ffa2d | |||
| 7176e68625 | |||
| 98af0d7f3c | |||
| fd2633a451 | |||
| b576f54b73 | |||
| d2e25ed81c | |||
| 44c74f2109 | |||
| 434221d0eb | |||
| 620817efba | |||
| f4dc974b2a | |||
| 42abfab3a7 | |||
| b63c2ab3c1 | |||
| 595fd5226b | |||
| 46b742dbb3 | |||
| cd8357e5a6 | |||
| d6a407f9b2 | |||
| 392fe9e507 | |||
| 2b4c34e756 | |||
| 130f92a67f | |||
| d676ebc156 | |||
| 7e6a483578 | |||
| 304a08ee91 | |||
| 38fb64bba3 | |||
| 69cfb521bb | |||
| 9d8bf95811 | |||
| 10b594924a | |||
| 1b4f705a2b | |||
| 737cddc85d | |||
| 1f209fffd0 | |||
| fb6ef9086d | |||
| f2bbcab6c9 | |||
| 292d3b2052 | |||
| f04a7bd388 | |||
| 08cec812cb | |||
| 34fdfc8f67 | |||
| 1d5b8348ce | |||
| c0705bc51d | |||
| d88f6c33a3 | |||
| 1361eaef7c | |||
| cbec9c9a6b | |||
| 4a66841730 | |||
| f679c1a5ed | |||
| 69c0989dfb | |||
| 10f946d312 | |||
| 45cfeed39d | |||
| 417386caa6 | |||
| 71f5c74a04 | |||
| daa20c724a | |||
| b4207be09d | |||
| 02a8baf069 | |||
| 447c28b165 | |||
| 78afc5876a | |||
| 3748494073 | |||
| 90d352e765 | |||
| e51ffc7dd3 | |||
| bbd690e2e2 | |||
| 14897a0b47 | |||
| ac4349bd30 | |||
| fb525a33e1 | |||
| 17def9a2c6 | |||
| d1e3a75934 | |||
| 38cd4eb1b2 | |||
| fbe20aa393 | |||
| 9b313adb51 | |||
| 9419eade34 |
+148
@@ -1,5 +1,153 @@
|
||||
# Changelog
|
||||
|
||||
Items starting with `DEPRECATE` are important deprecation notices. For more
|
||||
information on the list of deprecated flags and APIs please have a look at
|
||||
https://docs.docker.com/misc/deprecated/ where target removal dates can also
|
||||
be found.
|
||||
|
||||
## 1.9.1
|
||||
|
||||
## Runtime
|
||||
|
||||
- Do not prevent daemon from booting if images could not be restored (#17695)
|
||||
- Force IPC mount to unmount on daemon shutdown/init (#17539)
|
||||
- Turn IPC unmount errors into warnings (#17554)
|
||||
- Fix `docker stats` performance regression (#17638)
|
||||
- Clarify cryptic error message upon `docker logs` if `--log-driver=none` (#17767)
|
||||
- Fix seldom panics (#17639, #17634, #17703)
|
||||
- Fix opq whiteouts problems for files with dot prefix (#17819)
|
||||
- devicemapper: try defaulting to xfs instead of ext4 for performance reasons (#17903, #17918)
|
||||
- devicemapper: fix displayed fs in docker info (#17974)
|
||||
- selinux: only relabel if user requested so with the `z` option (#17450, #17834)
|
||||
- Do not make network calls when normalizing names (#18014)
|
||||
|
||||
## Client
|
||||
|
||||
- Fix `docker login` on windows (#17738)
|
||||
- Fix bug with `docker inspect` output when not connected to daemon (#17715)
|
||||
- Fix `docker inspect -f {{.HostConfig.Dns}} somecontainer` (#17680)
|
||||
|
||||
## Builder
|
||||
|
||||
- Fix regression with symlink behavior in ADD/COPY (#17710)
|
||||
|
||||
## Networking
|
||||
|
||||
- Allow passing a network ID as an argument for `--net` (#17558)
|
||||
- Fix connect to host and prevent disconnect from host for `host` network (#17476)
|
||||
- Fix `--fixed-cidr` issue when gateway ip falls in ip-range and ip-range is
|
||||
not the first block in the network (#17853)
|
||||
- Restore deterministic `IPv6` generation from `MAC` address on default `bridge` network (#17890)
|
||||
- Allow port-mapping only for endpoints created on docker run (#17858)
|
||||
- Fixed an endpoint delete issue with a possible stale sbox (#18102)
|
||||
|
||||
## Distribution
|
||||
|
||||
- Correct parent chain in v2 push when v1Compatibility files on the disk are inconsistent (#18047)
|
||||
|
||||
## 1.9.0 (2015-11-03)
|
||||
|
||||
## Runtime
|
||||
|
||||
+ `docker stats` now returns block IO metrics (#15005)
|
||||
+ `docker stats` now details network stats per interface (#15786)
|
||||
+ Add `ancestor=<image>` filter to `docker ps --filter` flag to filter
|
||||
containers based on their ancestor images (#14570)
|
||||
+ Add `label=<somelabel>` filter to `docker ps --filter` to filter containers
|
||||
based on label (#16530)
|
||||
+ Add `--kernel-memory` flag to `docker run` (#14006)
|
||||
+ Add `--message` flag to `docker import` allowing to specify an optional
|
||||
message (#15711)
|
||||
+ Add `--privileged` flag to `docker exec` (#14113)
|
||||
+ Add `--stop-signal` flag to `docker run` allowing to replace the container
|
||||
process stopping signal (#15307)
|
||||
+ Add a new `unless-stopped` restart policy (#15348)
|
||||
+ Inspecting an image now returns tags (#13185)
|
||||
+ Add container size information to `docker inspect` (#15796)
|
||||
+ Add `RepoTags` and `RepoDigests` field to `/images/{name:.*}/json` (#17275)
|
||||
- Remove the deprecated `/container/ps` endpoint from the API (#15972)
|
||||
- Send and document correct HTTP codes for `/exec/<name>/start` (#16250)
|
||||
- Share shm and mqueue between containers sharing IPC namespace (#15862)
|
||||
- Event stream now shows OOM status when `--oom-kill-disable` is set (#16235)
|
||||
- Ensure special network files (/etc/hosts etc.) are read-only if bind-mounted
|
||||
with `ro` option (#14965)
|
||||
- Improve `rmi` performance (#16890)
|
||||
- Do not update /etc/hosts for the default bridge network, except for links (#17325)
|
||||
- Fix conflict with duplicate container names (#17389)
|
||||
- Fix an issue with incorrect template execution in `docker inspect` (#17284)
|
||||
- DEPRECATE `-c` short flag variant for `--cpu-shares` in docker run (#16271)
|
||||
|
||||
## Client
|
||||
|
||||
+ Allow `docker import` to import from local files (#11907)
|
||||
|
||||
## Builder
|
||||
|
||||
+ Add a `STOPSIGNAL` Dockerfile instruction allowing to set a different
|
||||
stop-signal for the container process (#15307)
|
||||
+ Add an `ARG` Dockerfile instruction and a `--build-arg` flag to `docker build`
|
||||
that allows to add build-time environment variables (#15182)
|
||||
- Improve cache miss performance (#16890)
|
||||
|
||||
## Storage
|
||||
|
||||
- devicemapper: Implement deferred deletion capability (#16381)
|
||||
|
||||
## Networking
|
||||
|
||||
+ `docker network` exits experimental and is part of standard release (#16645)
|
||||
+ New network top-level concept, with associated subcommands and API (#16645)
|
||||
WARNING: the API is different from the experimental API
|
||||
+ Support for multiple isolated/micro-segmented networks (#16645)
|
||||
+ Built-in multihost networking using VXLAN based overlay driver (#14071)
|
||||
+ Support for third-party network plugins (#13424)
|
||||
+ Ability to dynamically connect containers to multiple networks (#16645)
|
||||
+ Support for user-defined IP address management via pluggable IPAM drivers (#16910)
|
||||
+ Add daemon flags `--cluster-store` and `--cluster-advertise` for built-in nodes discovery (#16229)
|
||||
+ Add `--cluster-store-opt` for setting up TLS settings (#16644)
|
||||
+ Add `--dns-opt` to the daemon (#16031)
|
||||
- DEPRECATE following container `NetworkSettings` fields in API v1.21: `EndpointID`, `Gateway`,
|
||||
`GlobalIPv6Address`, `GlobalIPv6PrefixLen`, `IPAddress`, `IPPrefixLen`, `IPv6Gateway` and `MacAddress`.
|
||||
Those are now specific to the `bridge` network. Use `NetworkSettings.Networks` to inspect
|
||||
the networking settings of a container per network.
|
||||
|
||||
## Volumes
|
||||
|
||||
+ New top-level `volume` subcommand and API (#14242)
|
||||
- Move API volume driver settings to host-specific config (#15798)
|
||||
- Print an error message if volume name is not unique (#16009)
|
||||
- Ensure volumes created from Dockerfiles always use the local volume driver
|
||||
(#15507)
|
||||
- DEPRECATE auto-creating missing host paths for bind mounts (#16349)
|
||||
|
||||
## Logging
|
||||
|
||||
+ Add `awslogs` logging driver for Amazon CloudWatch (#15495)
|
||||
+ Add generic `tag` log option to allow customizing container/image
|
||||
information passed to driver (e.g. show container names) (#15384)
|
||||
- Implement the `docker logs` endpoint for the journald driver (#13707)
|
||||
- DEPRECATE driver-specific log tags (e.g. `syslog-tag`, etc.) (#15384)
|
||||
|
||||
## Distribution
|
||||
|
||||
+ `docker search` now works with partial names (#16509)
|
||||
- Push optimization: avoid buffering to file (#15493)
|
||||
- The daemon will display progress for images that were already being pulled
|
||||
by another client (#15489)
|
||||
- Only permissions required for the current action being performed are requested (#)
|
||||
+ Renaming trust keys (and respective environment variables) from `offline` to
|
||||
`root` and `tagging` to `repository` (#16894)
|
||||
- DEPRECATE trust key environment variables
|
||||
`DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE` and
|
||||
`DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE` (#16894)
|
||||
|
||||
## Security
|
||||
|
||||
+ Add SELinux profiles to the rpm package (#15832)
|
||||
- Fix various issues with AppArmor profiles provided in the deb package
|
||||
(#14609)
|
||||
- Add AppArmor policy that prevents writing to /proc (#15571)
|
||||
|
||||
## 1.8.3 (2015-10-12)
|
||||
|
||||
### Distribution
|
||||
|
||||
+10
-2
@@ -58,9 +58,16 @@ RUN apt-get update && apt-get install -y \
|
||||
ruby1.9.1-dev \
|
||||
s3cmd=1.1.0* \
|
||||
ubuntu-zfs \
|
||||
xfsprogs \
|
||||
libzfs-dev \
|
||||
--no-install-recommends
|
||||
|
||||
# clr
|
||||
RUN echo deb http://download.opensuse.org/repositories/home:/clearlinux:/preview/xUbuntu_15.04/ / > /etc/apt/sources.list.d/clear-containers-docker.list
|
||||
RUN apt-get --allow-unauthenticated update && apt-get install -y --force-yes \
|
||||
kvmtool\
|
||||
linux-container
|
||||
|
||||
# Get lvm2 source for compiling statically
|
||||
RUN git clone -b v2_02_103 https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
|
||||
@@ -150,10 +157,11 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT 139850f3f3b17357bab5ba3edfb745fb14043764
|
||||
ENV DOCKER_PY_COMMIT 47ab89ec2bd3bddf1221b856ffbaff333edeabb4
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT
|
||||
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||
&& pip install -r test-requirements.txt
|
||||
|
||||
# Setup s3cmd config
|
||||
RUN { \
|
||||
|
||||
@@ -23,6 +23,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
e2fsprogs \
|
||||
iptables \
|
||||
procps \
|
||||
xfsprogs \
|
||||
xz-utils \
|
||||
\
|
||||
aufs-tools \
|
||||
|
||||
+6
-1
@@ -112,8 +112,13 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
|
||||
return errors.New("Please specify only one -H")
|
||||
}
|
||||
|
||||
defaultHost := opts.DefaultTCPHost
|
||||
if clientFlags.Common.TLSOptions != nil {
|
||||
defaultHost = opts.DefaultTLSHost
|
||||
}
|
||||
|
||||
var e error
|
||||
if hosts[0], e = opts.ParseHost(hosts[0]); e != nil {
|
||||
if hosts[0], e = opts.ParseHost(defaultHost, hosts[0]); e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
|
||||
@@ -107,5 +107,8 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
fmt.Fprintf(cli.out, "Cluster store: %s\n", info.ClusterStore)
|
||||
}
|
||||
|
||||
if info.ClusterAdvertise != "" {
|
||||
fmt.Fprintf(cli.out, "Cluster advertise: %s\n", info.ClusterAdvertise)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+66
-27
@@ -59,17 +59,21 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
||||
}
|
||||
|
||||
for _, name := range cmd.Args() {
|
||||
|
||||
if *inspectType == "" || *inspectType == "container" {
|
||||
obj, _, err = readBody(cli.call("GET", "/containers/"+name+"/json?"+v.Encode(), nil, nil))
|
||||
if err != nil && *inspectType == "container" {
|
||||
if strings.Contains(err.Error(), "No such") {
|
||||
fmt.Fprintf(cli.err, "Error: No such container: %s\n", name)
|
||||
} else {
|
||||
fmt.Fprintf(cli.err, "%s", err)
|
||||
if err != nil {
|
||||
if err == errConnectionFailed {
|
||||
return err
|
||||
}
|
||||
if *inspectType == "container" {
|
||||
if strings.Contains(err.Error(), "No such") {
|
||||
fmt.Fprintf(cli.err, "Error: No such container: %s\n", name)
|
||||
} else {
|
||||
fmt.Fprintf(cli.err, "%s", err)
|
||||
}
|
||||
status = 1
|
||||
continue
|
||||
}
|
||||
status = 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +81,9 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
||||
obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil))
|
||||
isImage = true
|
||||
if err != nil {
|
||||
if err == errConnectionFailed {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(err.Error(), "No such") {
|
||||
if *inspectType == "" {
|
||||
fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
|
||||
@@ -89,7 +96,6 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
||||
status = 1
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if tmpl == nil {
|
||||
@@ -101,42 +107,45 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
||||
} else {
|
||||
rdr := bytes.NewReader(obj)
|
||||
dec := json.NewDecoder(rdr)
|
||||
buf := bytes.NewBufferString("")
|
||||
|
||||
if isImage {
|
||||
inspPtr := types.ImageInspect{}
|
||||
if err := dec.Decode(&inspPtr); err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", err)
|
||||
status = 1
|
||||
continue
|
||||
break
|
||||
}
|
||||
if err := tmpl.Execute(cli.out, inspPtr); err != nil {
|
||||
if err := tmpl.Execute(buf, inspPtr); err != nil {
|
||||
rdr.Seek(0, 0)
|
||||
var raw interface{}
|
||||
if err := dec.Decode(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tmpl.Execute(cli.out, raw); err != nil {
|
||||
return err
|
||||
var ok bool
|
||||
|
||||
if buf, ok = cli.decodeRawInspect(tmpl, dec); !ok {
|
||||
fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
|
||||
status = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
inspPtr := types.ContainerJSON{}
|
||||
if err := dec.Decode(&inspPtr); err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", err)
|
||||
status = 1
|
||||
continue
|
||||
break
|
||||
}
|
||||
if err := tmpl.Execute(cli.out, inspPtr); err != nil {
|
||||
if err := tmpl.Execute(buf, inspPtr); err != nil {
|
||||
rdr.Seek(0, 0)
|
||||
var raw interface{}
|
||||
if err := dec.Decode(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tmpl.Execute(cli.out, raw); err != nil {
|
||||
return err
|
||||
var ok bool
|
||||
|
||||
if buf, ok = cli.decodeRawInspect(tmpl, dec); !ok {
|
||||
fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
|
||||
status = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cli.out.Write(buf.Bytes())
|
||||
cli.out.Write([]byte{'\n'})
|
||||
}
|
||||
indented.WriteString(",")
|
||||
@@ -162,3 +171,33 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeRawInspect executes the inspect template with a raw interface.
|
||||
// This allows docker cli to parse inspect structs injected with Swarm fields.
|
||||
// Unfortunately, go 1.4 doesn't fail executing invalid templates when the input is an interface.
|
||||
// It doesn't allow to modify this behavior either, sending <no value> messages to the output.
|
||||
// We assume that the template is invalid when there is a <no value>, if the template was valid
|
||||
// we'd get <nil> or "" values. In that case we fail with the original error raised executing the
|
||||
// template with the typed input.
|
||||
//
|
||||
// TODO: Go 1.5 allows to customize the error behavior, we can probably get rid of this as soon as
|
||||
// we build Docker with that version:
|
||||
// https://golang.org/pkg/text/template/#Template.Option
|
||||
func (cli *DockerCli) decodeRawInspect(tmpl *template.Template, dec *json.Decoder) (*bytes.Buffer, bool) {
|
||||
var raw interface{}
|
||||
buf := bytes.NewBufferString("")
|
||||
|
||||
if rawErr := dec.Decode(&raw); rawErr != nil {
|
||||
fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", rawErr)
|
||||
return buf, false
|
||||
}
|
||||
|
||||
if rawErr := tmpl.Execute(buf, raw); rawErr != nil {
|
||||
return buf, false
|
||||
}
|
||||
|
||||
if strings.Contains(buf.String(), "<no value>") {
|
||||
return buf, false
|
||||
}
|
||||
return buf, true
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
@@ -33,6 +34,11 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
// On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210
|
||||
if runtime.GOOS == "windows" {
|
||||
cli.in = os.Stdin
|
||||
}
|
||||
|
||||
serverAddress := registry.IndexServer
|
||||
if len(cmd.Args()) > 0 {
|
||||
serverAddress = cmd.Arg(0)
|
||||
|
||||
@@ -2,6 +2,7 @@ package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
@@ -11,6 +12,11 @@ import (
|
||||
"github.com/docker/docker/pkg/timeutils"
|
||||
)
|
||||
|
||||
var validDrivers = map[string]bool{
|
||||
"json-file": true,
|
||||
"journald": true,
|
||||
}
|
||||
|
||||
// CmdLogs fetches the logs of a given container.
|
||||
//
|
||||
// docker logs [OPTIONS] CONTAINER
|
||||
@@ -36,6 +42,10 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !validDrivers[c.HostConfig.LogConfig.Type] {
|
||||
return fmt.Errorf("\"logs\" command is supported only for \"json-file\" and \"journald\" logging drivers (got: %s)", c.HostConfig.LogConfig.Type)
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("stdout", "1")
|
||||
v.Set("stderr", "1")
|
||||
|
||||
+38
-14
@@ -54,6 +54,13 @@ func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the default driver to "" if the user didn't set the value.
|
||||
// That way we can know whether it was user input or not.
|
||||
driver := *flDriver
|
||||
if !cmd.IsSet("-driver") && !cmd.IsSet("d") {
|
||||
driver = ""
|
||||
}
|
||||
|
||||
ipamCfg, err := consolidateIpam(flIpamSubnet.GetAll(), flIpamIPRange.GetAll(), flIpamGateway.GetAll(), flIpamAux.GetAll())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -62,7 +69,7 @@ func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
|
||||
// Construct network create request body
|
||||
nc := types.NetworkCreate{
|
||||
Name: cmd.Arg(0),
|
||||
Driver: *flDriver,
|
||||
Driver: driver,
|
||||
IPAM: network.IPAM{Driver: *flIpamDriver, Config: ipamCfg},
|
||||
Options: flOpts.GetAll(),
|
||||
CheckDuplicate: true,
|
||||
@@ -184,31 +191,48 @@ func (cli *DockerCli) CmdNetworkLs(args ...string) error {
|
||||
|
||||
// CmdNetworkInspect inspects the network object for more details
|
||||
//
|
||||
// Usage: docker network inspect <NETWORK>
|
||||
// CmdNetworkInspect handles Network inspect UI
|
||||
// Usage: docker network inspect [OPTIONS] <NETWORK> [NETWORK...]
|
||||
func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
|
||||
cmd := Cli.Subcmd("network inspect", []string{"NETWORK"}, "Displays detailed information on a network", false)
|
||||
cmd.Require(flag.Exact, 1)
|
||||
cmd := Cli.Subcmd("network inspect", []string{"NETWORK [NETWORK...]"}, "Displays detailed information on a network", false)
|
||||
cmd.Require(flag.Min, 1)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj, _, err := readBody(cli.call("GET", "/networks/"+cmd.Arg(0), nil, nil))
|
||||
status := 0
|
||||
var networks []*types.NetworkResource
|
||||
for _, name := range cmd.Args() {
|
||||
obj, _, err := readBody(cli.call("GET", "/networks/"+name, nil, nil))
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
fmt.Fprintf(cli.err, "Error: No such network: %s\n", name)
|
||||
} else {
|
||||
fmt.Fprintf(cli.err, "%s", err)
|
||||
}
|
||||
status = 1
|
||||
continue
|
||||
}
|
||||
networkResource := types.NetworkResource{}
|
||||
if err := json.NewDecoder(bytes.NewReader(obj)).Decode(&networkResource); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
networks = append(networks, &networkResource)
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(networks, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
networkResource := &types.NetworkResource{}
|
||||
if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
indented := new(bytes.Buffer)
|
||||
if err := json.Indent(indented, obj, "", " "); err != nil {
|
||||
if _, err := io.Copy(cli.out, bytes.NewReader(b)); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(cli.out, indented); err != nil {
|
||||
return err
|
||||
io.WriteString(cli.out, "\n")
|
||||
|
||||
if status != 0 {
|
||||
return Cli.StatusError{StatusCode: status}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+9
-7
@@ -198,15 +198,17 @@ func (cli *DockerCli) getPassphraseRetriever() passphrase.Retriever {
|
||||
|
||||
// Backwards compatibility with old env names. We should remove this in 1.10
|
||||
if env["root"] == "" {
|
||||
env["root"] = os.Getenv("DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE")
|
||||
fmt.Fprintf(cli.err, "[DEPRECATED] The environment variable DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE has been deprecated and will be removed in v1.10. Please use DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE\n")
|
||||
|
||||
if passphrase := os.Getenv("DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE"); passphrase != "" {
|
||||
env["root"] = passphrase
|
||||
fmt.Fprintf(cli.err, "[DEPRECATED] The environment variable DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE has been deprecated and will be removed in v1.10. Please use DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE\n")
|
||||
}
|
||||
}
|
||||
if env["snapshot"] == "" || env["targets"] == "" {
|
||||
env["snapshot"] = os.Getenv("DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE")
|
||||
env["targets"] = os.Getenv("DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE")
|
||||
fmt.Fprintf(cli.err, "[DEPRECATED] The environment variable DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE has been deprecated and will be removed in v1.10. Please use DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE\n")
|
||||
|
||||
if passphrase := os.Getenv("DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE"); passphrase != "" {
|
||||
env["snapshot"] = passphrase
|
||||
env["targets"] = passphrase
|
||||
fmt.Fprintf(cli.err, "[DEPRECATED] The environment variable DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE has been deprecated and will be removed in v1.10. Please use DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE\n")
|
||||
}
|
||||
}
|
||||
|
||||
return func(keyName string, alias string, createNew bool, numAttempts int) (string, bool, error) {
|
||||
|
||||
@@ -195,7 +195,7 @@ func (cli *DockerCli) CmdVolumeCreate(args ...string) error {
|
||||
volReq.Name = *flName
|
||||
}
|
||||
|
||||
resp, err := cli.call("POST", "/volumes", volReq, nil)
|
||||
resp, err := cli.call("POST", "/volumes/create", volReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ import (
|
||||
// Common constants for daemon and client.
|
||||
const (
|
||||
// Version of Current REST API
|
||||
Version version.Version = "1.22"
|
||||
Version version.Version = "1.21"
|
||||
|
||||
// MinVersion represents Minimun REST API version supported
|
||||
MinVersion version.Version = "1.12"
|
||||
|
||||
@@ -55,6 +55,9 @@ type ArchiveOptions struct {
|
||||
// ArchiveFormValues parses form values and turns them into ArchiveOptions.
|
||||
// It fails if the archive name and path are not in the request.
|
||||
func ArchiveFormValues(r *http.Request, vars map[string]string) (ArchiveOptions, error) {
|
||||
if vars == nil {
|
||||
return ArchiveOptions{}, fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if err := ParseForm(r); err != nil {
|
||||
return ArchiveOptions{}, err
|
||||
}
|
||||
|
||||
@@ -56,6 +56,9 @@ func (s *router) getContainersStats(ctx context.Context, w http.ResponseWriter,
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
stream := httputils.BoolValueOrDefault(r, "stream", true)
|
||||
var out io.Writer
|
||||
@@ -63,7 +66,9 @@ func (s *router) getContainersStats(ctx context.Context, w http.ResponseWriter,
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
out = w
|
||||
} else {
|
||||
out = ioutils.NewWriteFlusher(w)
|
||||
wf := ioutils.NewWriteFlusher(w)
|
||||
out = wf
|
||||
defer wf.Close()
|
||||
}
|
||||
|
||||
var closeNotifier <-chan bool
|
||||
@@ -85,6 +90,9 @@ func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
// Args are validated before the stream starts because when it starts we're
|
||||
// sending HTTP 200 by writing an empty chunk of data to tell the client that
|
||||
@@ -116,11 +124,16 @@ func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
|
||||
return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
|
||||
}
|
||||
|
||||
outStream := ioutils.NewWriteFlusher(w)
|
||||
// write an empty chunk of data (this is to ensure that the
|
||||
// HTTP Response is sent immediately, even if the container has
|
||||
// not yet produced any data)
|
||||
outStream.Write(nil)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if flusher, ok := w.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
|
||||
output := ioutils.NewWriteFlusher(w)
|
||||
defer output.Close()
|
||||
|
||||
logsConfig := &daemon.ContainerLogsConfig{
|
||||
Follow: httputils.BoolValue(r, "follow"),
|
||||
@@ -129,7 +142,7 @@ func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
|
||||
Tail: r.Form.Get("tail"),
|
||||
UseStdout: stdout,
|
||||
UseStderr: stderr,
|
||||
OutStream: outStream,
|
||||
OutStream: output,
|
||||
Stop: closeNotifier,
|
||||
}
|
||||
|
||||
@@ -144,10 +157,18 @@ func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
|
||||
}
|
||||
|
||||
func (s *router) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
return s.daemon.ContainerExport(vars["name"], w)
|
||||
}
|
||||
|
||||
func (s *router) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
// If contentLength is -1, we can assumed chunked encoding
|
||||
// or more technically that the length is unknown
|
||||
// https://golang.org/src/pkg/net/http/request.go#L139
|
||||
@@ -179,6 +200,9 @@ func (s *router) postContainersStop(ctx context.Context, w http.ResponseWriter,
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
seconds, _ := strconv.Atoi(r.Form.Get("t"))
|
||||
|
||||
@@ -191,6 +215,9 @@ func (s *router) postContainersStop(ctx context.Context, w http.ResponseWriter,
|
||||
}
|
||||
|
||||
func (s *router) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -227,6 +254,9 @@ func (s *router) postContainersRestart(ctx context.Context, w http.ResponseWrite
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
timeout, _ := strconv.Atoi(r.Form.Get("t"))
|
||||
|
||||
@@ -240,6 +270,9 @@ func (s *router) postContainersRestart(ctx context.Context, w http.ResponseWrite
|
||||
}
|
||||
|
||||
func (s *router) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -254,6 +287,9 @@ func (s *router) postContainersPause(ctx context.Context, w http.ResponseWriter,
|
||||
}
|
||||
|
||||
func (s *router) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -268,6 +304,10 @@ func (s *router) postContainersUnpause(ctx context.Context, w http.ResponseWrite
|
||||
}
|
||||
|
||||
func (s *router) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
status, err := s.daemon.ContainerWait(vars["name"], -1*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -279,6 +319,10 @@ func (s *router) postContainersWait(ctx context.Context, w http.ResponseWriter,
|
||||
}
|
||||
|
||||
func (s *router) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
changes, err := s.daemon.ContainerChanges(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -288,6 +332,10 @@ func (s *router) getContainersChanges(ctx context.Context, w http.ResponseWriter
|
||||
}
|
||||
|
||||
func (s *router) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -304,6 +352,9 @@ func (s *router) postContainerRename(ctx context.Context, w http.ResponseWriter,
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
name := vars["name"]
|
||||
newName := r.Form.Get("name")
|
||||
@@ -343,6 +394,9 @@ func (s *router) deleteContainers(ctx context.Context, w http.ResponseWriter, r
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
name := vars["name"]
|
||||
config := &daemon.ContainerRmConfig{
|
||||
@@ -368,6 +422,9 @@ func (s *router) postContainersResize(ctx context.Context, w http.ResponseWriter
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
height, err := strconv.Atoi(r.Form.Get("h"))
|
||||
if err != nil {
|
||||
@@ -385,6 +442,9 @@ func (s *router) postContainersAttach(ctx context.Context, w http.ResponseWriter
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
containerName := vars["name"]
|
||||
|
||||
if !s.daemon.Exists(containerName) {
|
||||
@@ -424,6 +484,9 @@ func (s *router) wsContainersAttach(ctx context.Context, w http.ResponseWriter,
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
containerName := vars["name"]
|
||||
|
||||
if !s.daemon.Exists(containerName) {
|
||||
|
||||
@@ -16,6 +16,10 @@ import (
|
||||
|
||||
// postContainersCopy is deprecated in favor of getContainersArchive.
|
||||
func (s *router) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@ import (
|
||||
)
|
||||
|
||||
func (s *router) getExecByID(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter 'id'")
|
||||
}
|
||||
|
||||
eConfig, err := s.daemon.ContainerExecInspect(vars["id"])
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -114,6 +118,10 @@ func (s *router) postContainerExecResize(ctx context.Context, w http.ResponseWri
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
height, err := strconv.Atoi(r.Form.Get("h"))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/docker/docker/builder/dockerfile"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/daemon/daemonbuilder"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/graph/tags"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
@@ -64,11 +63,12 @@ func (s *router) postCommit(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
Config: c,
|
||||
}
|
||||
|
||||
if !s.daemon.Exists(cname) {
|
||||
return derr.ErrorCodeNoSuchContainer.WithArgs(cname)
|
||||
container, err := s.daemon.Get(cname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
imgID, err := dockerfile.Commit(cname, s.daemon, commitCfg)
|
||||
imgID, err := dockerfile.Commit(container, s.daemon, commitCfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -105,6 +105,7 @@ func (s *router) postImagesCreate(ctx context.Context, w http.ResponseWriter, r
|
||||
err error
|
||||
output = ioutils.NewWriteFlusher(w)
|
||||
)
|
||||
defer output.Close()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
@@ -156,6 +157,10 @@ func (s *router) postImagesCreate(ctx context.Context, w http.ResponseWriter, r
|
||||
}
|
||||
|
||||
func (s *router) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
metaHeaders := map[string][]string{}
|
||||
for k, v := range r.Header {
|
||||
if strings.HasPrefix(k, "X-Meta-") {
|
||||
@@ -184,6 +189,7 @@ func (s *router) postImagesPush(ctx context.Context, w http.ResponseWriter, r *h
|
||||
|
||||
name := vars["name"]
|
||||
output := ioutils.NewWriteFlusher(w)
|
||||
defer output.Close()
|
||||
imagePushConfig := &graph.ImagePushConfig{
|
||||
MetaHeaders: metaHeaders,
|
||||
AuthConfig: authConfig,
|
||||
@@ -204,6 +210,9 @@ func (s *router) postImagesPush(ctx context.Context, w http.ResponseWriter, r *h
|
||||
}
|
||||
|
||||
func (s *router) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -211,6 +220,7 @@ func (s *router) getImagesGet(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
w.Header().Set("Content-Type", "application/x-tar")
|
||||
|
||||
output := ioutils.NewWriteFlusher(w)
|
||||
defer output.Close()
|
||||
var names []string
|
||||
if name, ok := vars["name"]; ok {
|
||||
names = []string{name}
|
||||
@@ -236,6 +246,9 @@ func (s *router) deleteImages(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
name := vars["name"]
|
||||
|
||||
@@ -255,6 +268,10 @@ func (s *router) deleteImages(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
}
|
||||
|
||||
func (s *router) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
imageInspect, err := s.daemon.LookupImage(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -283,6 +300,7 @@ func (s *router) postBuild(ctx context.Context, w http.ResponseWriter, r *http.R
|
||||
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
output := ioutils.NewWriteFlusher(w)
|
||||
defer output.Close()
|
||||
sf := streamformatter.NewJSONStreamFormatter()
|
||||
errf := func(err error) error {
|
||||
// Do not write the error in the http output if it's still empty.
|
||||
@@ -442,6 +460,10 @@ func (s *router) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *ht
|
||||
}
|
||||
|
||||
func (s *router) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
name := vars["name"]
|
||||
history, err := s.daemon.ImageHistory(name)
|
||||
if err != nil {
|
||||
@@ -455,6 +477,10 @@ func (s *router) postImagesTag(ctx context.Context, w http.ResponseWriter, r *ht
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
repo := r.Form.Get("repo")
|
||||
tag := r.Form.Get("tag")
|
||||
name := vars["name"]
|
||||
|
||||
@@ -52,16 +52,6 @@ func (s *router) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Req
|
||||
return httputils.WriteJSON(w, http.StatusOK, info)
|
||||
}
|
||||
|
||||
func buildOutputEncoder(w http.ResponseWriter) *json.Encoder {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
outStream := ioutils.NewWriteFlusher(w)
|
||||
// Write an empty chunk of data.
|
||||
// This is to ensure that the HTTP status code is sent immediately,
|
||||
// so that it will not block the receiver.
|
||||
outStream.Write(nil)
|
||||
return json.NewEncoder(outStream)
|
||||
}
|
||||
|
||||
func (s *router) getEvents(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
@@ -87,7 +77,19 @@ func (s *router) getEvents(ctx context.Context, w http.ResponseWriter, r *http.R
|
||||
return err
|
||||
}
|
||||
|
||||
enc := buildOutputEncoder(w)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// This is to ensure that the HTTP status code is sent immediately,
|
||||
// so that it will not block the receiver.
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if flusher, ok := w.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
|
||||
output := ioutils.NewWriteFlusher(w)
|
||||
defer output.Close()
|
||||
|
||||
enc := json.NewEncoder(output)
|
||||
d := s.daemon
|
||||
es := d.EventsService
|
||||
current, l := es.Subscribe()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
@@ -10,6 +11,9 @@ import (
|
||||
// getContainersByName inspects containers configuration and serializes it as json.
|
||||
func (s *router) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
displaySize := httputils.BoolValue(r, "size")
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
var json interface{}
|
||||
var err error
|
||||
|
||||
@@ -141,7 +141,7 @@ func (r *router) initRoutes() {
|
||||
NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
|
||||
NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
|
||||
NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
|
||||
NewPostRoute("/volumes", r.postVolumesCreate),
|
||||
NewPostRoute("/volumes/create", r.postVolumesCreate),
|
||||
// PUT
|
||||
NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
|
||||
// DELETE
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/server/router"
|
||||
"github.com/docker/docker/api/server/router/local"
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/errors"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// networkRouter is a router to talk with the network controller
|
||||
@@ -29,13 +34,24 @@ func (r *networkRouter) Routes() []router.Route {
|
||||
func (r *networkRouter) initRoutes() {
|
||||
r.routes = []router.Route{
|
||||
// GET
|
||||
local.NewGetRoute("/networks", r.getNetworksList),
|
||||
local.NewGetRoute("/networks/{id:.*}", r.getNetwork),
|
||||
local.NewGetRoute("/networks", r.controllerEnabledMiddleware(r.getNetworksList)),
|
||||
local.NewGetRoute("/networks/{id:.*}", r.controllerEnabledMiddleware(r.getNetwork)),
|
||||
// POST
|
||||
local.NewPostRoute("/networks/create", r.postNetworkCreate),
|
||||
local.NewPostRoute("/networks/{id:.*}/connect", r.postNetworkConnect),
|
||||
local.NewPostRoute("/networks/{id:.*}/disconnect", r.postNetworkDisconnect),
|
||||
local.NewPostRoute("/networks/create", r.controllerEnabledMiddleware(r.postNetworkCreate)),
|
||||
local.NewPostRoute("/networks/{id:.*}/connect", r.controllerEnabledMiddleware(r.postNetworkConnect)),
|
||||
local.NewPostRoute("/networks/{id:.*}/disconnect", r.controllerEnabledMiddleware(r.postNetworkDisconnect)),
|
||||
// DELETE
|
||||
local.NewDeleteRoute("/networks/{id:.*}", r.deleteNetwork),
|
||||
local.NewDeleteRoute("/networks/{id:.*}", r.controllerEnabledMiddleware(r.deleteNetwork)),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *networkRouter) controllerEnabledMiddleware(handler httputils.APIFunc) httputils.APIFunc {
|
||||
if r.daemon.NetworkControllerEnabled() {
|
||||
return handler
|
||||
}
|
||||
return networkControllerDisabled
|
||||
}
|
||||
|
||||
func networkControllerDisabled(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
return errors.ErrorNetworkControllerNotEnabled.WithArgs()
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/daemon/network"
|
||||
"github.com/docker/docker/pkg/parsers/filters"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/libnetwork"
|
||||
)
|
||||
|
||||
@@ -85,6 +86,11 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr
|
||||
return err
|
||||
}
|
||||
|
||||
if runconfig.IsPreDefinedNetwork(create.Name) {
|
||||
return httputils.WriteJSON(w, http.StatusForbidden,
|
||||
fmt.Sprintf("%s is a pre-defined network and cannot be created", create.Name))
|
||||
}
|
||||
|
||||
nw, err := n.daemon.GetNetwork(create.Name, daemon.NetworkByName)
|
||||
if _, ok := err.(libnetwork.ErrNoSuchNetwork); err != nil && !ok {
|
||||
return err
|
||||
@@ -126,7 +132,11 @@ func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseW
|
||||
return err
|
||||
}
|
||||
|
||||
return n.daemon.ConnectContainerToNetwork(connect.Container, nw.Name())
|
||||
container, err := n.daemon.Get(connect.Container)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid container %s : %v", container, err)
|
||||
}
|
||||
return container.ConnectToNetwork(nw.Name())
|
||||
}
|
||||
|
||||
func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -148,7 +158,11 @@ func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.Respon
|
||||
return err
|
||||
}
|
||||
|
||||
return n.daemon.DisconnectContainerFromNetwork(disconnect.Container, nw)
|
||||
container, err := n.daemon.Get(disconnect.Container)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid container %s : %v", container, err)
|
||||
}
|
||||
return container.DisconnectFromNetwork(nw)
|
||||
}
|
||||
|
||||
func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -161,6 +175,11 @@ func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter
|
||||
return err
|
||||
}
|
||||
|
||||
if runconfig.IsPreDefinedNetwork(nw.Name()) {
|
||||
return httputils.WriteJSON(w, http.StatusForbidden,
|
||||
fmt.Sprintf("%s is a pre-defined network and cannot be removed", nw.Name()))
|
||||
}
|
||||
|
||||
return nw.Delete()
|
||||
}
|
||||
|
||||
@@ -180,7 +199,11 @@ func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
|
||||
|
||||
epl := nw.Endpoints()
|
||||
for _, e := range epl {
|
||||
sb := e.Info().Sandbox()
|
||||
ei := e.Info()
|
||||
if ei == nil {
|
||||
continue
|
||||
}
|
||||
sb := ei.Sandbox()
|
||||
if sb == nil {
|
||||
continue
|
||||
}
|
||||
@@ -222,7 +245,12 @@ func buildEndpointResource(e libnetwork.Endpoint) types.EndpointResource {
|
||||
}
|
||||
|
||||
er.EndpointID = e.ID()
|
||||
if iface := e.Info().Iface(); iface != nil {
|
||||
ei := e.Info()
|
||||
if ei == nil {
|
||||
return er
|
||||
}
|
||||
|
||||
if iface := ei.Iface(); iface != nil {
|
||||
if mac := iface.MacAddress(); mac != nil {
|
||||
er.MacAddress = mac.String()
|
||||
}
|
||||
|
||||
@@ -153,12 +153,7 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
|
||||
ctx := context.Background()
|
||||
handlerFunc := s.handleWithGlobalMiddlewares(handler)
|
||||
|
||||
vars := mux.Vars(r)
|
||||
if vars == nil {
|
||||
vars = make(map[string]string)
|
||||
}
|
||||
|
||||
if err := handlerFunc(ctx, w, r, vars); err != nil {
|
||||
if err := handlerFunc(ctx, w, r, mux.Vars(r)); err != nil {
|
||||
logrus.Errorf("Handler for %s %s returned error: %s", r.Method, r.URL.Path, utils.GetErrorMessage(err))
|
||||
httputils.WriteError(w, err)
|
||||
}
|
||||
@@ -170,6 +165,7 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
|
||||
func (s *Server) InitRouters(d *daemon.Daemon) {
|
||||
s.addRouter(local.NewRouter(d))
|
||||
s.addRouter(network.NewRouter(d))
|
||||
|
||||
for _, srv := range s.servers {
|
||||
srv.srv.Handler = s.CreateMux()
|
||||
}
|
||||
|
||||
+62
-25
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/daemon/network"
|
||||
"github.com/docker/docker/pkg/nat"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/docker/runconfig"
|
||||
@@ -96,7 +97,8 @@ type GraphDriverData struct {
|
||||
// GET "/images/{name:.*}/json"
|
||||
type ImageInspect struct {
|
||||
ID string `json:"Id"`
|
||||
Tags []string
|
||||
RepoTags []string
|
||||
RepoDigests []string
|
||||
Parent string
|
||||
Comment string
|
||||
Created string
|
||||
@@ -218,6 +220,7 @@ type Info struct {
|
||||
ExperimentalBuild bool
|
||||
ServerVersion string
|
||||
ClusterStore string
|
||||
ClusterAdvertise string
|
||||
}
|
||||
|
||||
// ExecStartCheck is a temp struct used by execStart
|
||||
@@ -254,7 +257,6 @@ type ContainerJSONBase struct {
|
||||
Args []string
|
||||
State *ContainerState
|
||||
Image string
|
||||
NetworkSettings *network.Settings
|
||||
ResolvConfPath string
|
||||
HostnamePath string
|
||||
HostsPath string
|
||||
@@ -276,8 +278,43 @@ type ContainerJSONBase struct {
|
||||
// ContainerJSON is newly used struct along with MountPoint
|
||||
type ContainerJSON struct {
|
||||
*ContainerJSONBase
|
||||
Mounts []MountPoint
|
||||
Config *runconfig.Config
|
||||
Mounts []MountPoint
|
||||
Config *runconfig.Config
|
||||
NetworkSettings *NetworkSettings
|
||||
}
|
||||
|
||||
// NetworkSettings exposes the network settings in the api
|
||||
type NetworkSettings struct {
|
||||
NetworkSettingsBase
|
||||
DefaultNetworkSettings
|
||||
Networks map[string]*network.EndpointSettings
|
||||
}
|
||||
|
||||
// NetworkSettingsBase holds basic information about networks
|
||||
type NetworkSettingsBase struct {
|
||||
Bridge string
|
||||
SandboxID string
|
||||
HairpinMode bool
|
||||
LinkLocalIPv6Address string
|
||||
LinkLocalIPv6PrefixLen int
|
||||
Ports nat.PortMap
|
||||
SandboxKey string
|
||||
SecondaryIPAddresses []network.Address
|
||||
SecondaryIPv6Addresses []network.Address
|
||||
}
|
||||
|
||||
// DefaultNetworkSettings holds network information
|
||||
// during the 2 release deprecation period.
|
||||
// It will be removed in Docker 1.11.
|
||||
type DefaultNetworkSettings struct {
|
||||
EndpointID string
|
||||
Gateway string
|
||||
GlobalIPv6Address string
|
||||
GlobalIPv6PrefixLen int
|
||||
IPAddress string
|
||||
IPPrefixLen int
|
||||
IPv6Gateway string
|
||||
MacAddress string
|
||||
}
|
||||
|
||||
// MountPoint represents a mount point configuration inside the container.
|
||||
@@ -304,7 +341,7 @@ type VolumesListResponse struct {
|
||||
}
|
||||
|
||||
// VolumeCreateRequest contains the response for the remote API:
|
||||
// POST "/volumes"
|
||||
// POST "/volumes/create"
|
||||
type VolumeCreateRequest struct {
|
||||
Name string // Name is the requested name of the volume
|
||||
Driver string // Driver is the name of the driver that should be used to create the volume
|
||||
@@ -313,44 +350,44 @@ type VolumeCreateRequest struct {
|
||||
|
||||
// NetworkResource is the body of the "get network" http response message
|
||||
type NetworkResource struct {
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
Scope string `json:"scope"`
|
||||
Driver string `json:"driver"`
|
||||
IPAM network.IPAM `json:"ipam"`
|
||||
Containers map[string]EndpointResource `json:"containers"`
|
||||
Options map[string]string `json:"options"`
|
||||
Name string
|
||||
ID string `json:"Id"`
|
||||
Scope string
|
||||
Driver string
|
||||
IPAM network.IPAM
|
||||
Containers map[string]EndpointResource
|
||||
Options map[string]string
|
||||
}
|
||||
|
||||
//EndpointResource contains network resources allocated and usd for a container in a network
|
||||
type EndpointResource struct {
|
||||
EndpointID string `json:"endpoint"`
|
||||
MacAddress string `json:"mac_address"`
|
||||
IPv4Address string `json:"ipv4_address"`
|
||||
IPv6Address string `json:"ipv6_address"`
|
||||
EndpointID string
|
||||
MacAddress string
|
||||
IPv4Address string
|
||||
IPv6Address string
|
||||
}
|
||||
|
||||
// NetworkCreate is the expected body of the "create network" http request message
|
||||
type NetworkCreate struct {
|
||||
Name string `json:"name"`
|
||||
CheckDuplicate bool `json:"check_duplicate"`
|
||||
Driver string `json:"driver"`
|
||||
IPAM network.IPAM `json:"ipam"`
|
||||
Options map[string]string `json:"options"`
|
||||
Name string
|
||||
CheckDuplicate bool
|
||||
Driver string
|
||||
IPAM network.IPAM
|
||||
Options map[string]string
|
||||
}
|
||||
|
||||
// NetworkCreateResponse is the response message sent by the server for network create call
|
||||
type NetworkCreateResponse struct {
|
||||
ID string `json:"id"`
|
||||
Warning string `json:"warning"`
|
||||
ID string `json:"Id"`
|
||||
Warning string
|
||||
}
|
||||
|
||||
// NetworkConnect represents the data to be used to connect a container to the network
|
||||
type NetworkConnect struct {
|
||||
Container string `json:"container"`
|
||||
Container string
|
||||
}
|
||||
|
||||
// NetworkDisconnect represents the data to be used to disconnect a container from the network
|
||||
type NetworkDisconnect struct {
|
||||
Container string `json:"container"`
|
||||
Container string
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package v1p19
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/versions/v1p20"
|
||||
"github.com/docker/docker/pkg/nat"
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
@@ -10,15 +12,20 @@ import (
|
||||
// Note this is not used by the Windows daemon.
|
||||
type ContainerJSON struct {
|
||||
*types.ContainerJSONBase
|
||||
Volumes map[string]string
|
||||
VolumesRW map[string]bool
|
||||
Config *ContainerConfig
|
||||
Volumes map[string]string
|
||||
VolumesRW map[string]bool
|
||||
Config *ContainerConfig
|
||||
NetworkSettings *v1p20.NetworkSettings
|
||||
}
|
||||
|
||||
// ContainerConfig is a backcompatibility struct for APIs prior to 1.20.
|
||||
type ContainerConfig struct {
|
||||
*runconfig.Config
|
||||
|
||||
MacAddress string
|
||||
NetworkDisabled bool
|
||||
ExposedPorts map[nat.Port]struct{}
|
||||
|
||||
// backward compatibility, they now live in HostConfig
|
||||
VolumeDriver string
|
||||
Memory int64
|
||||
|
||||
@@ -3,20 +3,26 @@ package v1p20
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/nat"
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
// ContainerJSON is a backcompatibility struct for the API 1.20
|
||||
type ContainerJSON struct {
|
||||
*types.ContainerJSONBase
|
||||
Mounts []types.MountPoint
|
||||
Config *ContainerConfig
|
||||
Mounts []types.MountPoint
|
||||
Config *ContainerConfig
|
||||
NetworkSettings *NetworkSettings
|
||||
}
|
||||
|
||||
// ContainerConfig is a backcompatibility struct used in ContainerJSON for the API 1.20
|
||||
type ContainerConfig struct {
|
||||
*runconfig.Config
|
||||
|
||||
MacAddress string
|
||||
NetworkDisabled bool
|
||||
ExposedPorts map[nat.Port]struct{}
|
||||
|
||||
// backward compatibility, they now live in HostConfig
|
||||
VolumeDriver string
|
||||
}
|
||||
@@ -26,3 +32,9 @@ type StatsJSON struct {
|
||||
types.Stats
|
||||
Network types.NetworkStats `json:"network,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkSettings is a backward compatible struct for APIs prior to 1.21
|
||||
type NetworkSettings struct {
|
||||
types.NetworkSettingsBase
|
||||
types.DefaultNetworkSettings
|
||||
}
|
||||
|
||||
+12
-1
@@ -33,7 +33,8 @@ type Context interface {
|
||||
Close() error
|
||||
// Stat returns an entry corresponding to path if any.
|
||||
// It is recommended to return an error if path was not found.
|
||||
Stat(path string) (FileInfo, error)
|
||||
// If path is a symlink it also returns the path to the target file.
|
||||
Stat(path string) (string, FileInfo, error)
|
||||
// Open opens path from the context and returns a readable stream of it.
|
||||
Open(path string) (io.ReadCloser, error)
|
||||
// Walk walks the tree of the context with the function passed to it.
|
||||
@@ -64,6 +65,8 @@ type PathFileInfo struct {
|
||||
os.FileInfo
|
||||
// FilePath holds the absolute path to the file.
|
||||
FilePath string
|
||||
// Name holds the basename for the file.
|
||||
FileName string
|
||||
}
|
||||
|
||||
// Path returns the absolute path to the file.
|
||||
@@ -71,6 +74,14 @@ func (fi PathFileInfo) Path() string {
|
||||
return fi.FilePath
|
||||
}
|
||||
|
||||
// Name returns the basename of the file.
|
||||
func (fi PathFileInfo) Name() string {
|
||||
if fi.FileName != "" {
|
||||
return fi.FileName
|
||||
}
|
||||
return fi.FileInfo.Name()
|
||||
}
|
||||
|
||||
// Hashed defines an extra method intended for implementations of os.FileInfo.
|
||||
type Hashed interface {
|
||||
// Hash returns the hash of a file.
|
||||
|
||||
@@ -256,12 +256,7 @@ func BuildFromConfig(config *runconfig.Config, changes []string) (*runconfig.Con
|
||||
|
||||
// Commit will create a new image from a container's changes
|
||||
// TODO: remove daemon, make Commit a method on *Builder ?
|
||||
func Commit(containerName string, d *daemon.Daemon, c *CommitConfig) (string, error) {
|
||||
container, err := d.Get(containerName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
func Commit(container *daemon.Container, d *daemon.Daemon, c *CommitConfig) (string, error) {
|
||||
// It is not possible to commit a running container on Windows
|
||||
if runtime.GOOS == "windows" && container.IsRunning() {
|
||||
return "", fmt.Errorf("Windows does not support commit of a running container")
|
||||
|
||||
@@ -366,7 +366,7 @@ func (b *Builder) calcCopyInfo(cmdName, origPath string, allowLocalDecompression
|
||||
|
||||
// Must be a dir or a file
|
||||
|
||||
fi, err := b.context.Stat(origPath)
|
||||
statPath, fi, err := b.context.Stat(origPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -383,11 +383,9 @@ func (b *Builder) calcCopyInfo(cmdName, origPath string, allowLocalDecompression
|
||||
hfi.SetHash("file:" + hfi.Hash())
|
||||
return copyInfos, nil
|
||||
}
|
||||
|
||||
// Must be a dir
|
||||
|
||||
var subfiles []string
|
||||
b.context.Walk(origPath, func(path string, info builder.FileInfo, err error) error {
|
||||
err = b.context.Walk(statPath, func(path string, info builder.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -395,6 +393,9 @@ func (b *Builder) calcCopyInfo(cmdName, origPath string, allowLocalDecompression
|
||||
subfiles = append(subfiles, info.(builder.Hashed).Hash())
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Strings(subfiles)
|
||||
hasher := sha256.New()
|
||||
@@ -557,9 +558,8 @@ func (b *Builder) run(c *daemon.Container) error {
|
||||
go func() {
|
||||
select {
|
||||
case <-b.cancelled:
|
||||
logrus.Debugln("Build cancelled, killing and removing container:", c.ID)
|
||||
logrus.Debugln("Build cancelled, killing container:", c.ID)
|
||||
c.Kill()
|
||||
b.removeContainer(c.ID)
|
||||
case <-finished:
|
||||
}
|
||||
}()
|
||||
@@ -583,21 +583,14 @@ func (b *Builder) run(c *daemon.Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Builder) removeContainer(c string) error {
|
||||
rmConfig := &daemon.ContainerRmConfig{
|
||||
ForceRemove: true,
|
||||
RemoveVolume: true,
|
||||
}
|
||||
if err := b.docker.Remove(c, rmConfig); err != nil {
|
||||
fmt.Fprintf(b.Stdout, "Error removing intermediate container %s: %v\n", stringid.TruncateID(c), err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Builder) clearTmp() {
|
||||
for c := range b.tmpContainers {
|
||||
if err := b.removeContainer(c); err != nil {
|
||||
rmConfig := &daemon.ContainerRmConfig{
|
||||
ForceRemove: true,
|
||||
RemoveVolume: true,
|
||||
}
|
||||
if err := b.docker.Remove(c, rmConfig); err != nil {
|
||||
fmt.Fprintf(b.Stdout, "Error removing intermediate container %s: %v\n", stringid.TruncateID(c), err)
|
||||
return
|
||||
}
|
||||
delete(b.tmpContainers, c)
|
||||
@@ -612,9 +605,9 @@ func (b *Builder) readDockerfile() error {
|
||||
// back to 'Dockerfile' and use that in the error message.
|
||||
if b.DockerfileName == "" {
|
||||
b.DockerfileName = api.DefaultDockerfileName
|
||||
if _, err := b.context.Stat(b.DockerfileName); os.IsNotExist(err) {
|
||||
if _, _, err := b.context.Stat(b.DockerfileName); os.IsNotExist(err) {
|
||||
lowercase := strings.ToLower(b.DockerfileName)
|
||||
if _, err := b.context.Stat(lowercase); err == nil {
|
||||
if _, _, err := b.context.Stat(lowercase); err == nil {
|
||||
b.DockerfileName = lowercase
|
||||
}
|
||||
}
|
||||
|
||||
+28
-35
@@ -5,7 +5,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
@@ -43,26 +42,32 @@ func (c *tarSumContext) Open(path string) (io.ReadCloser, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (c *tarSumContext) Stat(path string) (fi FileInfo, err error) {
|
||||
func (c *tarSumContext) Stat(path string) (string, FileInfo, error) {
|
||||
cleanpath, fullpath, err := c.normalize(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
st, err := os.Lstat(fullpath)
|
||||
if err != nil {
|
||||
return nil, convertPathError(err, cleanpath)
|
||||
return "", nil, convertPathError(err, cleanpath)
|
||||
}
|
||||
|
||||
fi = PathFileInfo{st, fullpath}
|
||||
// we set sum to path by default for the case where GetFile returns nil.
|
||||
// The usual case is if cleanpath is empty.
|
||||
rel, err := filepath.Rel(c.root, fullpath)
|
||||
if err != nil {
|
||||
return "", nil, convertPathError(err, cleanpath)
|
||||
}
|
||||
|
||||
// We set sum to path by default for the case where GetFile returns nil.
|
||||
// The usual case is if relative path is empty.
|
||||
sum := path
|
||||
if tsInfo := c.sums.GetFile(cleanpath); tsInfo != nil {
|
||||
// Use the checksum of the followed path(not the possible symlink) because
|
||||
// this is the file that is actually copied.
|
||||
if tsInfo := c.sums.GetFile(rel); tsInfo != nil {
|
||||
sum = tsInfo.Sum()
|
||||
}
|
||||
fi = &HashedFileInfo{fi, sum}
|
||||
return fi, nil
|
||||
fi := &HashedFileInfo{PathFileInfo{st, fullpath, filepath.Base(cleanpath)}, sum}
|
||||
return rel, fi, nil
|
||||
}
|
||||
|
||||
// MakeTarSumContext returns a build Context from a tar stream.
|
||||
@@ -114,7 +119,7 @@ func (c *tarSumContext) normalize(path string) (cleanpath, fullpath string, err
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("Forbidden path outside the build context: %s (%s)", path, fullpath)
|
||||
}
|
||||
_, err = os.Stat(fullpath)
|
||||
_, err = os.Lstat(fullpath)
|
||||
if err != nil {
|
||||
return "", "", convertPathError(err, path)
|
||||
}
|
||||
@@ -122,38 +127,26 @@ func (c *tarSumContext) normalize(path string) (cleanpath, fullpath string, err
|
||||
}
|
||||
|
||||
func (c *tarSumContext) Walk(root string, walkFn WalkFunc) error {
|
||||
for _, tsInfo := range c.sums {
|
||||
path := tsInfo.Name()
|
||||
path, fullpath, err := c.normalize(path)
|
||||
root = filepath.Join(c.root, filepath.Join(string(filepath.Separator), root))
|
||||
return filepath.Walk(root, func(fullpath string, info os.FileInfo, err error) error {
|
||||
rel, err := filepath.Rel(c.root, fullpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Any file in the context that starts with the given path will be
|
||||
// picked up and its hashcode used. However, we'll exclude the
|
||||
// root dir itself. We do this for a coupel of reasons:
|
||||
// 1 - ADD/COPY will not copy the dir itself, just its children
|
||||
// so there's no reason to include it in the hash calc
|
||||
// 2 - the metadata on the dir will change when any child file
|
||||
// changes. This will lead to a miss in the cache check if that
|
||||
// child file is in the .dockerignore list.
|
||||
if rel, err := filepath.Rel(root, path); err != nil {
|
||||
return err
|
||||
} else if rel == "." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
|
||||
continue
|
||||
if rel == "." {
|
||||
return nil
|
||||
}
|
||||
|
||||
info, err := os.Lstat(fullpath)
|
||||
if err != nil {
|
||||
return convertPathError(err, path)
|
||||
sum := rel
|
||||
if tsInfo := c.sums.GetFile(rel); tsInfo != nil {
|
||||
sum = tsInfo.Sum()
|
||||
}
|
||||
// TODO check context breakout?
|
||||
fi := &HashedFileInfo{PathFileInfo{info, fullpath}, tsInfo.Sum()}
|
||||
if err := walkFn(path, fi, nil); err != nil {
|
||||
fi := &HashedFileInfo{PathFileInfo{FileInfo: info, FilePath: fullpath}, sum}
|
||||
if err := walkFn(rel, fi, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *tarSumContext) Remove(path string) error {
|
||||
|
||||
@@ -6,7 +6,7 @@ FROM debian:jessie
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.3
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ FROM debian:stretch
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.3
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ FROM debian:wheezy-backports
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools/wheezy-backports build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.3
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ FROM ubuntu:precise
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion build-essential curl ca-certificates debhelper git libapparmor-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.3
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ FROM ubuntu:trusty
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.3
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ FROM ubuntu:vivid
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.3
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ FROM ubuntu:wily
|
||||
|
||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GO_VERSION 1.4.3
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ RUN yum groupinstall -y "Development Tools"
|
||||
RUN yum -y swap -- remove systemd-container systemd-container-libs -- install systemd systemd-libs
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.4.3
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ FROM fedora:21
|
||||
RUN yum install -y @development-tools fedora-packager
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.4.3
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ FROM fedora:22
|
||||
RUN yum install -y @development-tools fedora-packager
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.4.3
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/rpm/generate.sh"!
|
||||
#
|
||||
|
||||
FROM fedora:23
|
||||
|
||||
RUN dnf install -y @development-tools fedora-packager
|
||||
RUN dnf install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.5.1
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
ENV AUTO_GOPATH 1
|
||||
ENV DOCKER_BUILDTAGS selinux
|
||||
@@ -7,7 +7,7 @@ FROM opensuse:13.2
|
||||
RUN zypper --non-interactive install ca-certificates* curl gzip rpm-build
|
||||
RUN zypper --non-interactive install libbtrfs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.4.3
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ FROM oraclelinux:6
|
||||
RUN yum groupinstall -y "Development Tools"
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.4.3
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ FROM oraclelinux:7
|
||||
RUN yum groupinstall -y "Development Tools"
|
||||
RUN yum install -y --enablerepo=ol7_optional_latest btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.4.3
|
||||
ENV GO_VERSION 1.4.2
|
||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||
ENV PATH $PATH:/usr/local/go/bin
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
# options immediately following their corresponding long form.
|
||||
# This order should be applied to lists, alternatives and code blocks.
|
||||
|
||||
__docker_previous_extglob_setting=$(shopt -p extglob)
|
||||
shopt -s extglob
|
||||
|
||||
__docker_q() {
|
||||
docker ${host:+-H "$host"} ${config:+--config "$config"} 2>/dev/null "$@"
|
||||
}
|
||||
@@ -303,6 +306,7 @@ __docker_capabilities() {
|
||||
|
||||
__docker_log_drivers() {
|
||||
COMPREPLY=( $( compgen -W "
|
||||
awslogs
|
||||
fluentd
|
||||
gelf
|
||||
journald
|
||||
@@ -314,15 +318,21 @@ __docker_log_drivers() {
|
||||
|
||||
__docker_log_driver_options() {
|
||||
# see docs/reference/logging/index.md
|
||||
local fluentd_options="fluentd-address tag"
|
||||
local gelf_options="gelf-address tag"
|
||||
local json_file_options="max-file max-size"
|
||||
local syslog_options="syslog-address syslog-facility tag"
|
||||
local awslogs_options="awslogs-region awslogs-group awslogs-stream"
|
||||
local fluentd_options="env fluentd-address labels tag"
|
||||
local gelf_options="env gelf-address labels tag"
|
||||
local journald_options="env labels"
|
||||
local json_file_options="env labels max-file max-size"
|
||||
local syslog_options="syslog-address syslog-facility tag"
|
||||
|
||||
local all_options="$fluentd_options $gelf_options $journald_options $json_file_options $syslog_options"
|
||||
|
||||
case $(__docker_value_of_option --log-driver) in
|
||||
'')
|
||||
COMPREPLY=( $( compgen -W "$fluentd_options $gelf_options $json_file_options $syslog_options" -S = -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "$all_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
awslogs)
|
||||
COMPREPLY=( $( compgen -W "$awslogs_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
fluentd)
|
||||
COMPREPLY=( $( compgen -W "$fluentd_options" -S = -- "$cur" ) )
|
||||
@@ -330,15 +340,15 @@ __docker_log_driver_options() {
|
||||
gelf)
|
||||
COMPREPLY=( $( compgen -W "$gelf_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
journald)
|
||||
COMPREPLY=( $( compgen -W "$journald_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
json-file)
|
||||
COMPREPLY=( $( compgen -W "$json_file_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
syslog)
|
||||
COMPREPLY=( $( compgen -W "$syslog_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
awslogs)
|
||||
COMPREPLY=( $( compgen -W "$awslogs_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
return
|
||||
;;
|
||||
@@ -561,9 +571,18 @@ _docker_cp() {
|
||||
return
|
||||
;;
|
||||
*)
|
||||
# combined container and filename completion
|
||||
_filedir
|
||||
local files=( ${COMPREPLY[@]} )
|
||||
|
||||
__docker_containers_all
|
||||
COMPREPLY=( $( compgen -W "${COMPREPLY[*]}" -S ':' ) )
|
||||
__docker_nospace
|
||||
local containers=( ${COMPREPLY[@]} )
|
||||
|
||||
COMPREPLY=( $( compgen -W "${files[*]} ${containers[*]}" -- "$cur" ) )
|
||||
if [[ "$COMPREPLY" == *: ]]; then
|
||||
__docker_nospace
|
||||
fi
|
||||
return
|
||||
;;
|
||||
esac
|
||||
@@ -571,7 +590,13 @@ _docker_cp() {
|
||||
(( counter++ ))
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
_filedir -d
|
||||
if [ -e "$prev" ]; then
|
||||
__docker_containers_all
|
||||
COMPREPLY=( $( compgen -W "${COMPREPLY[*]}" -S ':' ) )
|
||||
__docker_nospace
|
||||
else
|
||||
_filedir
|
||||
fi
|
||||
return
|
||||
fi
|
||||
;;
|
||||
@@ -667,6 +692,8 @@ _docker_daemon() {
|
||||
dm.mountopt
|
||||
dm.override_udev_sync_check
|
||||
dm.thinpooldev
|
||||
dm.use_deferred_deletion
|
||||
dm.use_deferred_removal
|
||||
"
|
||||
local zfs_options="zfs.fsname"
|
||||
|
||||
@@ -704,7 +731,7 @@ _docker_daemon() {
|
||||
|
||||
case "${words[$cword-2]}$prev=" in
|
||||
# completions for --storage-opt
|
||||
*dm.blkdiscard=*)
|
||||
*dm.@(blkdiscard|override_udev_sync_check|use_deferred_@(removal|deletion))=*)
|
||||
COMPREPLY=( $( compgen -W "false true" -- "${cur#=}" ) )
|
||||
return
|
||||
;;
|
||||
@@ -712,10 +739,6 @@ _docker_daemon() {
|
||||
COMPREPLY=( $( compgen -W "ext4 xfs" -- "${cur#=}" ) )
|
||||
return
|
||||
;;
|
||||
*dm.override_udev_sync_check=*)
|
||||
COMPREPLY=( $( compgen -W "false true" -- "${cur#=}" ) )
|
||||
return
|
||||
;;
|
||||
*dm.thinpooldev=*)
|
||||
_filedir
|
||||
return
|
||||
@@ -1054,6 +1077,13 @@ _docker_network_connect() {
|
||||
|
||||
_docker_network_create() {
|
||||
case "$prev" in
|
||||
--aux-address|--gateway|--ip-range|--opt|-o|--subnet)
|
||||
return
|
||||
;;
|
||||
--ipam-driver)
|
||||
COMPREPLY=( $( compgen -W "default" -- "$cur" ) )
|
||||
return
|
||||
;;
|
||||
--driver|-d)
|
||||
# no need to suggest drivers that allow one instance only
|
||||
# (host, null)
|
||||
@@ -1064,7 +1094,7 @@ _docker_network_create() {
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--driver -d --help" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "--aux-address --driver -d --gateway --help --ip-range --ipam-driver --opt -o --subnet" -- "$cur" ) )
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@@ -1081,11 +1111,7 @@ _docker_network_inspect() {
|
||||
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_networks
|
||||
fi
|
||||
;;
|
||||
__docker_networks
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -1344,6 +1370,7 @@ _docker_run() {
|
||||
--ulimit
|
||||
--user -u
|
||||
--uts
|
||||
--volume-driver
|
||||
--volumes-from
|
||||
--volume -v
|
||||
--workdir -w
|
||||
@@ -1465,7 +1492,7 @@ _docker_run() {
|
||||
on-failure:*)
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=( $( compgen -W "no on-failure on-failure: always" -- "$cur") )
|
||||
COMPREPLY=( $( compgen -W "always no on-failure on-failure: unless-stopped" -- "$cur") )
|
||||
;;
|
||||
esac
|
||||
return
|
||||
@@ -1488,6 +1515,10 @@ _docker_run() {
|
||||
esac
|
||||
return
|
||||
;;
|
||||
--volume-driver)
|
||||
COMPREPLY=( $( compgen -W "local" -- "$cur" ) )
|
||||
return
|
||||
;;
|
||||
--volumes-from)
|
||||
__docker_containers_all
|
||||
return
|
||||
@@ -1841,4 +1872,7 @@ _docker() {
|
||||
return 0
|
||||
}
|
||||
|
||||
eval "$__docker_previous_extglob_setting"
|
||||
unset __docker_previous_extglob_setting
|
||||
|
||||
complete -F _docker docker
|
||||
|
||||
@@ -339,7 +339,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l restart -d 'Res
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l rm -d 'Automatically remove the container when it exits (incompatible with -d)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l security-opt -d 'Security Options'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l sig-proxy -d 'Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and SIGKILL are not proxied.'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l stop-signal 'Signal to kill a container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l stop-signal -d 'Signal to kill a container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s t -l tty -d 'Allocate a pseudo-TTY'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s u -l user -d 'Username or UID'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s v -l volume -d 'Bind mount a volume (e.g., from the host: -v /host:/container, from Docker: -v /container)'
|
||||
|
||||
@@ -258,12 +258,13 @@ __docker_network_subcommand() {
|
||||
"($help)*--ip-range=[Allocate container ip from a sub-range]:IP/mask: " \
|
||||
"($help)*--gateway=[ipv4 or ipv6 Gateway for the master subnet]:IP: " \
|
||||
"($help)*--aux-address[Auxiliary ipv4 or ipv6 addresses used by network driver]:key=IP: " \
|
||||
"($help)*"{-o=,--opt=}"[Set driver specific options]:key=value: " \
|
||||
"($help -)1:Network Name: " && ret=0
|
||||
;;
|
||||
(inspect|rm)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
"($help -):network:__docker_networks" && ret=0
|
||||
"($help -)*:network:__docker_networks" && ret=0
|
||||
;;
|
||||
(ls)
|
||||
_arguments \
|
||||
@@ -333,9 +334,9 @@ __docker_volume_subcommand() {
|
||||
(create)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
"($help -d --driver)"{-d,--driver=}"[Specify volume driver name]:Driver name: " \
|
||||
"($help -d --driver)"{-d,--driver=}"[Specify volume driver name]:Driver name:(local)" \
|
||||
"($help)--name=[Specify volume name]" \
|
||||
"($help -o --opt)*"{-o,--opt=}"[Set driver specific options]:Driver option: " && ret=0
|
||||
"($help)*"{-o,--opt=}"[Set driver specific options]:Driver option: " && ret=0
|
||||
;;
|
||||
(inspect)
|
||||
_arguments \
|
||||
@@ -346,7 +347,7 @@ __docker_volume_subcommand() {
|
||||
(ls)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
"($help -f --filter)*"{-f,--filter=}"[Provide filter values (i.e. 'dangling=true')]:filter: " \
|
||||
"($help)*"{-f,--filter=}"[Provide filter values (i.e. 'dangling=true')]:filter: " \
|
||||
"($help -q --quiet)"{-q,--quiet}"[Only display volume names]" && ret=0
|
||||
;;
|
||||
(rm)
|
||||
@@ -443,6 +444,7 @@ __docker_subcommand() {
|
||||
"($help -t --tty)"{-t,--tty}"[Allocate a pseudo-tty]"
|
||||
"($help -u --user)"{-u,--user=}"[Username or UID]:user:_users"
|
||||
"($help)*-v[Bind mount a volume]:volume: "
|
||||
"($help)--volume-driver=[Optional volume driver for the container]:volume driver:(local)"
|
||||
"($help)*--volumes-from=[Mount volumes from the specified container]:volume: "
|
||||
"($help -w --workdir)"{-w,--workdir=}"[Working directory inside the container]:directory:_directories"
|
||||
)
|
||||
@@ -473,7 +475,7 @@ __docker_subcommand() {
|
||||
_arguments \
|
||||
$opts_help \
|
||||
"($help -a --author)"{-a,--author=}"[Author]:author: " \
|
||||
"($help -c --change)*"{-c,--change=}"[Apply Dockerfile instruction to the created image]:Dockerfile:_files" \
|
||||
"($help)*"{-c,--change=}"[Apply Dockerfile instruction to the created image]:Dockerfile:_files" \
|
||||
"($help -m --message)"{-m,--message=}"[Commit message]:message: " \
|
||||
"($help -p --pause)"{-p,--pause}"[Pause container during commit]" \
|
||||
"($help -):container:__docker_containers" \
|
||||
@@ -641,7 +643,7 @@ __docker_subcommand() {
|
||||
(import)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
"($help -c --change)*"{-c,--change=}"[Apply Dockerfile instruction to the created image]:Dockerfile:_files" \
|
||||
"($help)*"{-c,--change=}"[Apply Dockerfile instruction to the created image]:Dockerfile:_files" \
|
||||
"($help -m --message)"{-m,--message=}"[Set commit message for imported image]:message: " \
|
||||
"($help -):URL:(- http:// file://)" \
|
||||
"($help -): :__docker_repositories_with_tags" && ret=0
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string)
|
||||
cmd.Var(opts.NewListOptsRef(&config.Labels, opts.ValidateLabel), []string{"-label"}, usageFn("Set key=value labels to the daemon"))
|
||||
cmd.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", usageFn("Default driver for container logs"))
|
||||
cmd.Var(opts.NewMapOpts(config.LogConfig.Config, nil), []string{"-log-opt"}, usageFn("Set log driver options"))
|
||||
cmd.StringVar(&config.ClusterAdvertise, []string{"-cluster-advertise"}, "", usageFn("Address of the daemon instance to advertise"))
|
||||
cmd.StringVar(&config.ClusterAdvertise, []string{"-cluster-advertise"}, "", usageFn("Address or interface name to advertise"))
|
||||
cmd.StringVar(&config.ClusterStore, []string{"-cluster-store"}, "", usageFn("Set the cluster store"))
|
||||
cmd.Var(opts.NewMapOpts(config.ClusterOpts, nil), []string{"-cluster-store-opt"}, usageFn("Set cluster store options"))
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
var (
|
||||
defaultPidFile = "/var/run/docker.pid"
|
||||
defaultGraph = "/var/lib/docker"
|
||||
defaultExec = "native"
|
||||
defaultExec = "clr"
|
||||
)
|
||||
|
||||
// Config defines the configuration of a docker daemon.
|
||||
|
||||
+23
-8
@@ -153,7 +153,28 @@ func (container *Container) readHostConfig() error {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return json.NewDecoder(f).Decode(&container.hostConfig)
|
||||
if err := json.NewDecoder(f).Decode(&container.hostConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure the dns fields are never nil.
|
||||
// New containers don't ever have those fields nil,
|
||||
// but pre created containers can still have those nil values.
|
||||
// See https://github.com/docker/docker/pull/17779
|
||||
// for a more detailed explanation on why we don't want that.
|
||||
if container.hostConfig.DNS == nil {
|
||||
container.hostConfig.DNS = make([]string, 0)
|
||||
}
|
||||
|
||||
if container.hostConfig.DNSSearch == nil {
|
||||
container.hostConfig.DNSSearch = make([]string, 0)
|
||||
}
|
||||
|
||||
if container.hostConfig.DNSOptions == nil {
|
||||
container.hostConfig.DNSOptions = make([]string, 0)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) writeHostConfig() error {
|
||||
@@ -328,18 +349,12 @@ func (streamConfig *streamConfig) StderrPipe() io.ReadCloser {
|
||||
return ioutils.NewBufReader(reader)
|
||||
}
|
||||
|
||||
func (container *Container) isNetworkAllocated() bool {
|
||||
return container.NetworkSettings.IPAddress != ""
|
||||
}
|
||||
|
||||
// cleanup releases any network resources allocated to the container along with any rules
|
||||
// around how containers are linked together. It also unmounts the container's root filesystem.
|
||||
func (container *Container) cleanup() {
|
||||
container.releaseNetwork()
|
||||
|
||||
if err := container.unmountIpcMounts(); err != nil {
|
||||
logrus.Errorf("%s: Failed to umount ipc filesystems: %v", container.ID, err)
|
||||
}
|
||||
container.unmountIpcMounts(detachMounted)
|
||||
|
||||
if err := container.Unmount(); err != nil {
|
||||
logrus.Errorf("%s: Failed to umount filesystem: %v", container.ID, err)
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/volume"
|
||||
"github.com/docker/docker/volume/drivers"
|
||||
)
|
||||
|
||||
func TestGetFullName(t *testing.T) {
|
||||
@@ -64,3 +69,68 @@ func TestContainerStopSignal(t *testing.T) {
|
||||
t.Fatalf("Expected 9, got %v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerInitDNS(t *testing.T) {
|
||||
tmp, err := ioutil.TempDir("", "docker-container-test-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
containerID := "d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e"
|
||||
containerPath := filepath.Join(tmp, containerID)
|
||||
if err := os.MkdirAll(containerPath, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config := `{"State":{"Running":true,"Paused":false,"Restarting":false,"OOMKilled":false,"Dead":false,"Pid":2464,"ExitCode":0,
|
||||
"Error":"","StartedAt":"2015-05-26T16:48:53.869308965Z","FinishedAt":"0001-01-01T00:00:00Z"},
|
||||
"ID":"d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e","Created":"2015-05-26T16:48:53.7987917Z","Path":"top",
|
||||
"Args":[],"Config":{"Hostname":"d59df5276e7b","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"Cpuset":"",
|
||||
"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":true,"OpenStdin":true,
|
||||
"StdinOnce":false,"Env":null,"Cmd":["top"],"Image":"ubuntu:latest","Volumes":null,"WorkingDir":"","Entrypoint":null,
|
||||
"NetworkDisabled":false,"MacAddress":"","OnBuild":null,"Labels":{}},"Image":"07f8e8c5e66084bef8f848877857537ffe1c47edd01a93af27e7161672ad0e95",
|
||||
"NetworkSettings":{"IPAddress":"172.17.0.1","IPPrefixLen":16,"MacAddress":"02:42:ac:11:00:01","LinkLocalIPv6Address":"fe80::42:acff:fe11:1",
|
||||
"LinkLocalIPv6PrefixLen":64,"GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"Gateway":"172.17.42.1","IPv6Gateway":"","Bridge":"docker0","Ports":{}},
|
||||
"ResolvConfPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/resolv.conf",
|
||||
"HostnamePath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/hostname",
|
||||
"HostsPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/hosts",
|
||||
"LogPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e-json.log",
|
||||
"Name":"/ubuntu","Driver":"aufs","MountLabel":"","ProcessLabel":"","AppArmorProfile":"","RestartCount":0,
|
||||
"UpdateDns":false,"Volumes":{},"VolumesRW":{},"AppliedVolumesFrom":null}`
|
||||
|
||||
if err = ioutil.WriteFile(filepath.Join(containerPath, "config.json"), []byte(config), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hostConfig := `{"Binds":[],"ContainerIDFile":"","Memory":0,"MemorySwap":0,"CpuShares":0,"CpusetCpus":"",
|
||||
"Privileged":false,"PortBindings":{},"Links":null,"PublishAllPorts":false,"Dns":null,"DnsOptions":null,"DnsSearch":null,"ExtraHosts":null,"VolumesFrom":null,
|
||||
"Devices":[],"NetworkMode":"bridge","IpcMode":"","PidMode":"","CapAdd":null,"CapDrop":null,"RestartPolicy":{"Name":"no","MaximumRetryCount":0},
|
||||
"SecurityOpt":null,"ReadonlyRootfs":false,"Ulimits":null,"LogConfig":{"Type":"","Config":null},"CgroupParent":""}`
|
||||
if err = ioutil.WriteFile(filepath.Join(containerPath, "hostconfig.json"), []byte(hostConfig), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
daemon, err := initDaemonWithVolumeStore(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer volumedrivers.Unregister(volume.DefaultDriverName)
|
||||
|
||||
c, err := daemon.load(containerID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if c.hostConfig.DNS == nil {
|
||||
t.Fatal("Expected container DNS to not be nil")
|
||||
}
|
||||
|
||||
if c.hostConfig.DNSSearch == nil {
|
||||
t.Fatal("Expected container DNSSearch to not be nil")
|
||||
}
|
||||
|
||||
if c.hostConfig.DNSOptions == nil {
|
||||
t.Fatal("Expected container DNSOptions to not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
+180
-111
@@ -89,15 +89,25 @@ func (container *Container) setupLinkedContainers() ([]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bridgeSettings := container.NetworkSettings.Networks["bridge"]
|
||||
if bridgeSettings == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if len(children) > 0 {
|
||||
for linkAlias, child := range children {
|
||||
if !child.IsRunning() {
|
||||
return nil, derr.ErrorCodeLinkNotRunning.WithArgs(child.Name, linkAlias)
|
||||
}
|
||||
|
||||
childBridgeSettings := child.NetworkSettings.Networks["bridge"]
|
||||
if childBridgeSettings == nil {
|
||||
return nil, fmt.Errorf("container %d not attached to default bridge network", child.ID)
|
||||
}
|
||||
|
||||
link := links.NewLink(
|
||||
container.NetworkSettings.IPAddress,
|
||||
child.NetworkSettings.IPAddress,
|
||||
bridgeSettings.IPAddress,
|
||||
childBridgeSettings.IPAddress,
|
||||
linkAlias,
|
||||
child.Config.Env,
|
||||
child.Config.ExposedPorts,
|
||||
@@ -218,6 +228,12 @@ func populateCommand(c *Container, env []string) error {
|
||||
} else {
|
||||
ipc.HostIpc = c.hostConfig.IpcMode.IsHost()
|
||||
if ipc.HostIpc {
|
||||
if _, err := os.Stat("/dev/shm"); err != nil {
|
||||
return fmt.Errorf("/dev/shm is not mounted, but must be for --ipc=host")
|
||||
}
|
||||
if _, err := os.Stat("/dev/mqueue"); err != nil {
|
||||
return fmt.Errorf("/dev/mqueue is not mounted, but must be for --ipc=host")
|
||||
}
|
||||
c.ShmPath = "/dev/shm"
|
||||
c.MqueuePath = "/dev/mqueue"
|
||||
}
|
||||
@@ -525,6 +541,9 @@ func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetw
|
||||
}
|
||||
|
||||
for linkAlias, child := range children {
|
||||
if !isLinkable(child) {
|
||||
return nil, fmt.Errorf("Cannot link to %s, as it does not belong to the default network", child.Name)
|
||||
}
|
||||
_, alias := path.Split(linkAlias)
|
||||
// allow access to the linked container via the alias, real name, and container hostname
|
||||
aliasList := alias + " " + child.Config.Hostname
|
||||
@@ -532,13 +551,14 @@ func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetw
|
||||
if alias != child.Name[1:] {
|
||||
aliasList = aliasList + " " + child.Name[1:]
|
||||
}
|
||||
sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, child.NetworkSettings.IPAddress))
|
||||
sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, child.NetworkSettings.Networks["bridge"].IPAddress))
|
||||
cEndpoint, _ := child.getEndpointInNetwork(n)
|
||||
if cEndpoint != nil && cEndpoint.ID() != "" {
|
||||
childEndpoints = append(childEndpoints, cEndpoint.ID())
|
||||
}
|
||||
}
|
||||
|
||||
bridgeSettings := container.NetworkSettings.Networks["bridge"]
|
||||
refs := container.daemon.containerGraph().RefPaths(container.ID)
|
||||
for _, ref := range refs {
|
||||
if ref.ParentID == "0" {
|
||||
@@ -551,8 +571,8 @@ func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetw
|
||||
}
|
||||
|
||||
if c != nil && !container.daemon.configStore.DisableBridge && container.hostConfig.NetworkMode.IsPrivate() {
|
||||
logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress)
|
||||
sboxOptions = append(sboxOptions, libnetwork.OptionParentUpdate(c.ID, ref.Name, container.NetworkSettings.IPAddress))
|
||||
logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, bridgeSettings.IPAddress)
|
||||
sboxOptions = append(sboxOptions, libnetwork.OptionParentUpdate(c.ID, ref.Name, bridgeSettings.IPAddress))
|
||||
if ep.ID() != "" {
|
||||
parentEndpoints = append(parentEndpoints, ep.ID())
|
||||
}
|
||||
@@ -571,6 +591,12 @@ func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetw
|
||||
return sboxOptions, nil
|
||||
}
|
||||
|
||||
func isLinkable(child *Container) bool {
|
||||
// A container is linkable only if it belongs to the default network
|
||||
_, ok := child.NetworkSettings.Networks["bridge"]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (container *Container) getEndpointInNetwork(n libnetwork.Network) (libnetwork.Endpoint, error) {
|
||||
endpointName := strings.TrimPrefix(container.Name, "/")
|
||||
return n.EndpointByName(endpointName)
|
||||
@@ -595,12 +621,10 @@ func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint, networkSett
|
||||
return networkSettings, nil
|
||||
}
|
||||
|
||||
if mac, ok := driverInfo[netlabel.MacAddress]; ok {
|
||||
networkSettings.MacAddress = mac.(net.HardwareAddr).String()
|
||||
if networkSettings.Ports == nil {
|
||||
networkSettings.Ports = nat.PortMap{}
|
||||
}
|
||||
|
||||
networkSettings.Ports = nat.PortMap{}
|
||||
|
||||
if expData, ok := driverInfo[netlabel.ExposedPorts]; ok {
|
||||
if exposedPorts, ok := expData.([]types.TransportPort); ok {
|
||||
for _, tp := range exposedPorts {
|
||||
@@ -632,7 +656,7 @@ func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint, networkSett
|
||||
return networkSettings, nil
|
||||
}
|
||||
|
||||
func (container *Container) buildEndpointInfo(ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) {
|
||||
func (container *Container) buildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) {
|
||||
if ep == nil {
|
||||
return nil, derr.ErrorCodeEmptyEndpoint
|
||||
}
|
||||
@@ -647,36 +671,50 @@ func (container *Container) buildEndpointInfo(ep libnetwork.Endpoint, networkSet
|
||||
return networkSettings, nil
|
||||
}
|
||||
|
||||
if _, ok := networkSettings.Networks[n.Name()]; !ok {
|
||||
networkSettings.Networks[n.Name()] = new(network.EndpointSettings)
|
||||
}
|
||||
networkSettings.Networks[n.Name()].EndpointID = ep.ID()
|
||||
|
||||
iface := epInfo.Iface()
|
||||
if iface == nil {
|
||||
return networkSettings, nil
|
||||
}
|
||||
|
||||
if iface.MacAddress() != nil {
|
||||
networkSettings.Networks[n.Name()].MacAddress = iface.MacAddress().String()
|
||||
}
|
||||
|
||||
if iface.Address() != nil {
|
||||
ones, _ := iface.Address().Mask.Size()
|
||||
networkSettings.IPAddress = iface.Address().IP.String()
|
||||
networkSettings.IPPrefixLen = ones
|
||||
networkSettings.Networks[n.Name()].IPAddress = iface.Address().IP.String()
|
||||
networkSettings.Networks[n.Name()].IPPrefixLen = ones
|
||||
}
|
||||
|
||||
if iface.AddressIPv6() != nil && iface.AddressIPv6().IP.To16() != nil {
|
||||
onesv6, _ := iface.AddressIPv6().Mask.Size()
|
||||
networkSettings.GlobalIPv6Address = iface.AddressIPv6().IP.String()
|
||||
networkSettings.GlobalIPv6PrefixLen = onesv6
|
||||
networkSettings.Networks[n.Name()].GlobalIPv6Address = iface.AddressIPv6().IP.String()
|
||||
networkSettings.Networks[n.Name()].GlobalIPv6PrefixLen = onesv6
|
||||
}
|
||||
|
||||
return networkSettings, nil
|
||||
}
|
||||
|
||||
func (container *Container) updateJoinInfo(ep libnetwork.Endpoint) error {
|
||||
func (container *Container) updateJoinInfo(n libnetwork.Network, ep libnetwork.Endpoint) error {
|
||||
if _, err := container.buildPortMapInfo(ep, container.NetworkSettings); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
epInfo := ep.Info()
|
||||
if epInfo == nil {
|
||||
// It is not an error to get an empty endpoint info
|
||||
return nil
|
||||
}
|
||||
|
||||
container.NetworkSettings.Gateway = epInfo.Gateway().String()
|
||||
if epInfo.Gateway() != nil {
|
||||
container.NetworkSettings.Networks[n.Name()].Gateway = epInfo.Gateway().String()
|
||||
}
|
||||
if epInfo.GatewayIPv6().To16() != nil {
|
||||
container.NetworkSettings.IPv6Gateway = epInfo.GatewayIPv6().String()
|
||||
container.NetworkSettings.Networks[n.Name()].IPv6Gateway = epInfo.GatewayIPv6().String()
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -684,11 +722,14 @@ func (container *Container) updateJoinInfo(ep libnetwork.Endpoint) error {
|
||||
|
||||
func (container *Container) updateNetworkSettings(n libnetwork.Network) error {
|
||||
if container.NetworkSettings == nil {
|
||||
container.NetworkSettings = &network.Settings{Networks: []string{}}
|
||||
container.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)}
|
||||
}
|
||||
settings := container.NetworkSettings
|
||||
|
||||
for _, s := range settings.Networks {
|
||||
if !container.hostConfig.NetworkMode.IsHost() && runconfig.NetworkMode(n.Type()).IsHost() {
|
||||
return runconfig.ErrConflictHostNetwork
|
||||
}
|
||||
|
||||
for s := range container.NetworkSettings.Networks {
|
||||
sn, err := container.daemon.FindNetwork(s)
|
||||
if err != nil {
|
||||
continue
|
||||
@@ -707,18 +748,13 @@ func (container *Container) updateNetworkSettings(n libnetwork.Network) error {
|
||||
return runconfig.ErrConflictNoNetwork
|
||||
}
|
||||
}
|
||||
settings.Networks = append(settings.Networks, n.Name())
|
||||
container.NetworkSettings.Networks[n.Name()] = new(network.EndpointSettings)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) updateEndpointNetworkSettings(n libnetwork.Network, ep libnetwork.Endpoint) error {
|
||||
networkSettings, err := container.buildPortMapInfo(ep, container.NetworkSettings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
networkSettings, err = container.buildEndpointInfo(ep, networkSettings)
|
||||
networkSettings, err := container.buildEndpointInfo(n, ep, container.NetworkSettings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -749,7 +785,7 @@ func (container *Container) updateNetwork() error {
|
||||
|
||||
// Find if container is connected to the default bridge network
|
||||
var n libnetwork.Network
|
||||
for _, name := range container.NetworkSettings.Networks {
|
||||
for name := range container.NetworkSettings.Networks {
|
||||
sn, err := container.daemon.FindNetwork(name)
|
||||
if err != nil {
|
||||
continue
|
||||
@@ -777,7 +813,7 @@ func (container *Container) updateNetwork() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) buildCreateEndpointOptions() ([]libnetwork.EndpointOption, error) {
|
||||
func (container *Container) buildCreateEndpointOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) {
|
||||
var (
|
||||
portSpecs = make(nat.PortSet)
|
||||
bindings = make(nat.PortMap)
|
||||
@@ -786,6 +822,17 @@ func (container *Container) buildCreateEndpointOptions() ([]libnetwork.EndpointO
|
||||
createOptions []libnetwork.EndpointOption
|
||||
)
|
||||
|
||||
if n.Name() == "bridge" || container.NetworkSettings.IsAnonymousEndpoint {
|
||||
createOptions = append(createOptions, libnetwork.CreateOptionAnonymous())
|
||||
}
|
||||
|
||||
// Other configs are applicable only for the endpoint in the network
|
||||
// to which container was connected to on docker run.
|
||||
if n.Name() != container.hostConfig.NetworkMode.NetworkName() &&
|
||||
!(n.Name() == "bridge" && container.hostConfig.NetworkMode.IsDefault()) {
|
||||
return createOptions, nil
|
||||
}
|
||||
|
||||
if container.Config.ExposedPorts != nil {
|
||||
portSpecs = container.Config.ExposedPorts
|
||||
}
|
||||
@@ -875,11 +922,16 @@ func createNetwork(controller libnetwork.NetworkController, dnet string, driver
|
||||
}
|
||||
|
||||
func (container *Container) allocateNetwork() error {
|
||||
settings := container.NetworkSettings.Networks
|
||||
controller := container.daemon.netController
|
||||
|
||||
// Cleanup any stale sandbox left over due to ungraceful daemon shutdown
|
||||
if err := controller.SandboxDestroy(container.ID); err != nil {
|
||||
logrus.Errorf("failed to cleanup up stale network sandbox for container %s", container.ID)
|
||||
}
|
||||
|
||||
updateSettings := false
|
||||
if settings == nil {
|
||||
if len(container.NetworkSettings.Networks) == 0 {
|
||||
mode := container.hostConfig.NetworkMode
|
||||
controller := container.daemon.netController
|
||||
if container.Config.NetworkDisabled || mode.IsContainer() {
|
||||
return nil
|
||||
}
|
||||
@@ -888,32 +940,54 @@ func (container *Container) allocateNetwork() error {
|
||||
if mode.IsDefault() {
|
||||
networkName = controller.Config().Daemon.DefaultNetwork
|
||||
}
|
||||
settings = []string{networkName}
|
||||
if mode.IsUserDefined() {
|
||||
n, err := container.daemon.FindNetwork(networkName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
networkName = n.Name()
|
||||
}
|
||||
container.NetworkSettings.Networks = make(map[string]*network.EndpointSettings)
|
||||
container.NetworkSettings.Networks[networkName] = new(network.EndpointSettings)
|
||||
updateSettings = true
|
||||
}
|
||||
|
||||
for _, n := range settings {
|
||||
for n := range container.NetworkSettings.Networks {
|
||||
if err := container.connectToNetwork(n, updateSettings); err != nil {
|
||||
if updateSettings {
|
||||
return err
|
||||
}
|
||||
// dont fail a container restart case if the user removed the network
|
||||
logrus.Warnf("Could not connect container %s : %v", container.ID, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return container.writeHostConfig()
|
||||
}
|
||||
|
||||
// ConnectToNetwork connects a container to a netork
|
||||
func (container *Container) getNetworkSandbox() libnetwork.Sandbox {
|
||||
var sb libnetwork.Sandbox
|
||||
container.daemon.netController.WalkSandboxes(func(s libnetwork.Sandbox) bool {
|
||||
if s.ContainerID() == container.ID {
|
||||
sb = s
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
return sb
|
||||
}
|
||||
|
||||
// ConnectToNetwork connects a container to a network
|
||||
func (container *Container) ConnectToNetwork(idOrName string) error {
|
||||
if !container.Running {
|
||||
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
|
||||
}
|
||||
return container.connectToNetwork(idOrName, true)
|
||||
if err := container.connectToNetwork(idOrName, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.toDiskLocking(); err != nil {
|
||||
return fmt.Errorf("Error saving container to disk: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) connectToNetwork(idOrName string, updateSettings bool) error {
|
||||
func (container *Container) connectToNetwork(idOrName string, updateSettings bool) (err error) {
|
||||
if container.hostConfig.NetworkMode.IsContainer() {
|
||||
return runconfig.ErrConflictSharedNetwork
|
||||
}
|
||||
@@ -938,35 +1012,37 @@ func (container *Container) connectToNetwork(idOrName string, updateSettings boo
|
||||
}
|
||||
|
||||
ep, err := container.getEndpointInNetwork(n)
|
||||
if err != nil {
|
||||
if _, ok := err.(libnetwork.ErrNoSuchEndpoint); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
createOptions, err := container.buildCreateEndpointOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endpointName := strings.TrimPrefix(container.Name, "/")
|
||||
ep, err = n.CreateEndpoint(endpointName, createOptions...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
return fmt.Errorf("container already connected to network %s", idOrName)
|
||||
}
|
||||
|
||||
if _, ok := err.(libnetwork.ErrNoSuchEndpoint); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
createOptions, err := container.buildCreateEndpointOptions(n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endpointName := strings.TrimPrefix(container.Name, "/")
|
||||
ep, err = n.CreateEndpoint(endpointName, createOptions...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if e := ep.Delete(); e != nil {
|
||||
logrus.Warnf("Could not rollback container connection to network %s", idOrName)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := container.updateEndpointNetworkSettings(n, ep); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var sb libnetwork.Sandbox
|
||||
controller.WalkSandboxes(func(s libnetwork.Sandbox) bool {
|
||||
if s.ContainerID() == container.ID {
|
||||
sb = s
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
sb := container.getNetworkSandbox()
|
||||
if sb == nil {
|
||||
options, err := container.buildSandboxOptions(n)
|
||||
if err != nil {
|
||||
@@ -976,15 +1052,15 @@ func (container *Container) connectToNetwork(idOrName string, updateSettings boo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
container.updateSandboxNetworkSettings(sb)
|
||||
container.updateSandboxNetworkSettings(sb)
|
||||
}
|
||||
|
||||
if err := ep.Join(sb); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := container.updateJoinInfo(ep); err != nil {
|
||||
if err := container.updateJoinInfo(n, ep); err != nil {
|
||||
return derr.ErrorCodeJoinInfo.WithArgs(err)
|
||||
}
|
||||
|
||||
@@ -1111,6 +1187,9 @@ func (container *Container) releaseNetwork() {
|
||||
|
||||
sid := container.NetworkSettings.SandboxID
|
||||
networks := container.NetworkSettings.Networks
|
||||
for n := range networks {
|
||||
networks[n] = &network.EndpointSettings{}
|
||||
}
|
||||
|
||||
container.NetworkSettings = &network.Settings{Networks: networks}
|
||||
|
||||
@@ -1124,14 +1203,6 @@ func (container *Container) releaseNetwork() {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ns := range networks {
|
||||
n, err := container.daemon.FindNetwork(ns)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
container.disconnectFromNetwork(n, false)
|
||||
}
|
||||
|
||||
if err := sb.Delete(); err != nil {
|
||||
logrus.Errorf("Error deleting sandbox id %s for container %s: %v", sid, container.ID, err)
|
||||
}
|
||||
@@ -1143,17 +1214,32 @@ func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error {
|
||||
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
|
||||
}
|
||||
|
||||
return container.disconnectFromNetwork(n, true)
|
||||
if container.hostConfig.NetworkMode.IsHost() && runconfig.NetworkMode(n.Type()).IsHost() {
|
||||
return runconfig.ErrConflictHostNetwork
|
||||
}
|
||||
|
||||
if err := container.disconnectFromNetwork(n); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := container.toDiskLocking(); err != nil {
|
||||
return fmt.Errorf("Error saving container to disk: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) disconnectFromNetwork(n libnetwork.Network, updateSettings bool) error {
|
||||
func (container *Container) disconnectFromNetwork(n libnetwork.Network) error {
|
||||
var (
|
||||
ep libnetwork.Endpoint
|
||||
sbox libnetwork.Sandbox
|
||||
)
|
||||
|
||||
s := func(current libnetwork.Endpoint) bool {
|
||||
if sb := current.Info().Sandbox(); sb != nil {
|
||||
epInfo := current.Info()
|
||||
if epInfo == nil {
|
||||
return false
|
||||
}
|
||||
if sb := epInfo.Sandbox(); sb != nil {
|
||||
if sb.ContainerID() == container.ID {
|
||||
ep = current
|
||||
sbox = sb
|
||||
@@ -1176,20 +1262,7 @@ func (container *Container) disconnectFromNetwork(n libnetwork.Network, updateSe
|
||||
return fmt.Errorf("endpoint delete failed for container %s on network %s: %v", container.ID, n.Name(), err)
|
||||
}
|
||||
|
||||
if updateSettings {
|
||||
networks := container.NetworkSettings.Networks
|
||||
for i, s := range networks {
|
||||
sn, err := container.daemon.FindNetwork(s)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if sn.Name() == n.Name() {
|
||||
networks = append(networks[:i], networks[i+1:]...)
|
||||
container.NetworkSettings.Networks = networks
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
delete(container.NetworkSettings.Networks, n.Name())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1409,22 +1482,21 @@ func (container *Container) setupIpcDirs() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) unmountIpcMounts() error {
|
||||
func (container *Container) unmountIpcMounts(unmount func(pth string) error) {
|
||||
if container.hostConfig.IpcMode.IsContainer() || container.hostConfig.IpcMode.IsHost() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
var errors []string
|
||||
var warnings []string
|
||||
|
||||
if !container.hasMountFor("/dev/shm") {
|
||||
shmPath, err := container.shmPath()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
errors = append(errors, err.Error())
|
||||
} else {
|
||||
if err := detachMounted(shmPath); err != nil {
|
||||
logrus.Errorf("failed to umount %s: %v", shmPath, err)
|
||||
errors = append(errors, err.Error())
|
||||
warnings = append(warnings, err.Error())
|
||||
} else if shmPath != "" {
|
||||
if err := unmount(shmPath); err != nil {
|
||||
warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", shmPath, err))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1434,20 +1506,17 @@ func (container *Container) unmountIpcMounts() error {
|
||||
mqueuePath, err := container.mqueuePath()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
errors = append(errors, err.Error())
|
||||
} else {
|
||||
if err := detachMounted(mqueuePath); err != nil {
|
||||
logrus.Errorf("failed to umount %s: %v", mqueuePath, err)
|
||||
errors = append(errors, err.Error())
|
||||
warnings = append(warnings, err.Error())
|
||||
} else if mqueuePath != "" {
|
||||
if err := unmount(mqueuePath); err != nil {
|
||||
warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", mqueuePath, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return fmt.Errorf("failed to cleanup ipc mounts:\n%v", strings.Join(errors, "\n"))
|
||||
if len(warnings) > 0 {
|
||||
logrus.Warnf("failed to cleanup ipc mounts:\n%v", strings.Join(warnings, "\n"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) ipcMounts() []execdriver.Mount {
|
||||
|
||||
@@ -183,11 +183,14 @@ func (container *Container) removeMountPoints(_ bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) setupIpcDirs() error {
|
||||
func (container *Container) unmountIpcMounts(unmount func(pth string) error) {
|
||||
}
|
||||
|
||||
func detachMounted(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) unmountIpcMounts() error {
|
||||
func (container *Container) setupIpcDirs() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+32
-24
@@ -12,7 +12,6 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -39,6 +38,7 @@ import (
|
||||
"github.com/docker/docker/pkg/graphdb"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
"github.com/docker/docker/pkg/namesgenerator"
|
||||
"github.com/docker/docker/pkg/nat"
|
||||
"github.com/docker/docker/pkg/parsers/filters"
|
||||
@@ -50,6 +50,7 @@ import (
|
||||
"github.com/docker/docker/pkg/truncindex"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/utils"
|
||||
volumedrivers "github.com/docker/docker/volume/drivers"
|
||||
"github.com/docker/docker/volume/local"
|
||||
"github.com/docker/docker/volume/store"
|
||||
@@ -57,8 +58,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
validContainerNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.-]`
|
||||
validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`)
|
||||
validContainerNameChars = utils.RestrictedNameChars
|
||||
validContainerNamePattern = utils.RestrictedNamePattern
|
||||
|
||||
errSystemNotSupported = errors.New("The Docker daemon is not supported on this platform.")
|
||||
)
|
||||
@@ -222,9 +223,8 @@ func (daemon *Daemon) Register(container *Container) error {
|
||||
}
|
||||
daemon.execDriver.Terminate(cmd)
|
||||
|
||||
if err := container.unmountIpcMounts(); err != nil {
|
||||
logrus.Errorf("%s: Failed to umount ipc filesystems: %v", container.ID, err)
|
||||
}
|
||||
container.unmountIpcMounts(mount.Unmount)
|
||||
|
||||
if err := container.Unmount(); err != nil {
|
||||
logrus.Debugf("unmount error %s", err)
|
||||
}
|
||||
@@ -406,20 +406,12 @@ func (daemon *Daemon) reserveName(id, name string) (string, error) {
|
||||
|
||||
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 := daemon.containerGraphDB.Delete(name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
nameAsKnownByUser := strings.TrimPrefix(name, "/")
|
||||
return "", fmt.Errorf(
|
||||
"Conflict. The name %q is already in use by container %s. You have to remove (or rename) that container to be able to reuse that name.", nameAsKnownByUser,
|
||||
stringid.TruncateID(conflictingContainer.ID))
|
||||
return "", err
|
||||
}
|
||||
return "", fmt.Errorf(
|
||||
"Conflict. The name %q is already in use by container %s. You have to remove (or rename) that container to be able to reuse that name.", strings.TrimPrefix(name, "/"),
|
||||
stringid.TruncateID(conflictingContainer.ID))
|
||||
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
@@ -476,8 +468,9 @@ func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint *stringutils.StrSlic
|
||||
|
||||
func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID string) (*Container, error) {
|
||||
var (
|
||||
id string
|
||||
err error
|
||||
id string
|
||||
err error
|
||||
noExplicitName = name == ""
|
||||
)
|
||||
id, name, err = daemon.generateIDAndName(name)
|
||||
if err != nil {
|
||||
@@ -494,7 +487,7 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID
|
||||
base.Config = config
|
||||
base.hostConfig = &runconfig.HostConfig{}
|
||||
base.ImageID = imgID
|
||||
base.NetworkSettings = &network.Settings{}
|
||||
base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName}
|
||||
base.Name = name
|
||||
base.Driver = daemon.driver.String()
|
||||
base.ExecDriver = daemon.execDriver.Name()
|
||||
@@ -761,10 +754,17 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
|
||||
// initialized, the daemon is registered and we can store the discovery backend as its read-only
|
||||
// DiscoveryWatcher version.
|
||||
if config.ClusterStore != "" && config.ClusterAdvertise != "" {
|
||||
var err error
|
||||
if d.discoveryWatcher, err = initDiscovery(config.ClusterStore, config.ClusterAdvertise, config.ClusterOpts); err != nil {
|
||||
advertise, err := discovery.ParseAdvertise(config.ClusterStore, config.ClusterAdvertise)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("discovery advertise parsing failed (%v)", err)
|
||||
}
|
||||
config.ClusterAdvertise = advertise
|
||||
d.discoveryWatcher, err = initDiscovery(config.ClusterStore, config.ClusterAdvertise, config.ClusterOpts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("discovery initialization failed (%v)", err)
|
||||
}
|
||||
} else if config.ClusterAdvertise != "" {
|
||||
return nil, fmt.Errorf("invalid cluster configuration. --cluster-advertise must be accompanied by --cluster-store configuration")
|
||||
}
|
||||
|
||||
d.netController, err = d.initNetworkController(config)
|
||||
@@ -1136,6 +1136,14 @@ func (daemon *Daemon) GetRemappedUIDGID() (int, int) {
|
||||
// created. nil is returned if a child cannot be found. An error is
|
||||
// returned if the parent image cannot be found.
|
||||
func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*image.Image, error) {
|
||||
// for now just exit if imgID has no children.
|
||||
// maybe parentRefs in graph could be used to store
|
||||
// the Image obj children for faster lookup below but this can
|
||||
// be quite memory hungry.
|
||||
if !daemon.Graph().HasChildren(imgID) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Retrieve all images
|
||||
images := daemon.Graph().Map()
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
)
|
||||
|
||||
// cleanupMounts umounts shm/mqueue mounts for old containers
|
||||
@@ -20,7 +21,7 @@ func (daemon *Daemon) cleanupMounts() error {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return daemon.cleanupMountsFromReader(f, detachMounted)
|
||||
return daemon.cleanupMountsFromReader(f, mount.Unmount)
|
||||
}
|
||||
|
||||
func (daemon *Daemon) cleanupMountsFromReader(reader io.Reader, unmount func(target string) error) error {
|
||||
|
||||
+30
-5
@@ -182,7 +182,7 @@ func TestLoadWithVolume(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
daemon, err := initDaemonForVolumesTest(tmp)
|
||||
daemon, err := initDaemonWithVolumeStore(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -270,7 +270,7 @@ func TestLoadWithBindMount(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
daemon, err := initDaemonForVolumesTest(tmp)
|
||||
daemon, err := initDaemonWithVolumeStore(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -361,7 +361,7 @@ func TestLoadWithVolume17RC(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
daemon, err := initDaemonForVolumesTest(tmp)
|
||||
daemon, err := initDaemonWithVolumeStore(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -466,7 +466,7 @@ func TestRemoveLocalVolumesFollowingSymlinks(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
daemon, err := initDaemonForVolumesTest(tmp)
|
||||
daemon, err := initDaemonWithVolumeStore(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -502,7 +502,7 @@ func TestRemoveLocalVolumesFollowingSymlinks(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func initDaemonForVolumesTest(tmp string) (*Daemon, error) {
|
||||
func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
|
||||
daemon := &Daemon{
|
||||
repository: tmp,
|
||||
root: tmp,
|
||||
@@ -549,3 +549,28 @@ func TestParseSecurityOpt(t *testing.T) {
|
||||
t.Fatal("Expected parseSecurityOpt error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkOptions(t *testing.T) {
|
||||
daemon := &Daemon{}
|
||||
dconfigCorrect := &Config{
|
||||
CommonConfig: CommonConfig{
|
||||
DefaultNetwork: "netPlugin:mynet:dev",
|
||||
ClusterStore: "consul://localhost:8500",
|
||||
ClusterAdvertise: "192.168.0.1:8000",
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := daemon.networkOptions(dconfigCorrect); err != nil {
|
||||
t.Fatalf("Expect networkOptions sucess, got error: %v", err)
|
||||
}
|
||||
|
||||
dconfigWrong := &Config{
|
||||
CommonConfig: CommonConfig{
|
||||
ClusterStore: "consul://localhost:8500://test://bbb",
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := daemon.networkOptions(dconfigWrong); err == nil {
|
||||
t.Fatalf("Expected networkOptions error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
+23
-7
@@ -114,9 +114,6 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *runconfig.HostConfig, a
|
||||
// By default, MemorySwap is set to twice the size of Memory.
|
||||
hostConfig.MemorySwap = hostConfig.Memory * 2
|
||||
}
|
||||
if hostConfig.MemoryReservation == 0 && hostConfig.Memory > 0 {
|
||||
hostConfig.MemoryReservation = hostConfig.Memory
|
||||
}
|
||||
}
|
||||
|
||||
// verifyPlatformContainerSettings performs platform-specific validation of the
|
||||
@@ -335,11 +332,14 @@ func (daemon *Daemon) networkOptions(dconfig *Config) ([]nwconfig.Option, error)
|
||||
|
||||
if strings.TrimSpace(dconfig.ClusterStore) != "" {
|
||||
kv := strings.Split(dconfig.ClusterStore, "://")
|
||||
if len(kv) < 2 {
|
||||
if len(kv) != 2 {
|
||||
return nil, fmt.Errorf("kv store daemon config must be of the form KV-PROVIDER://KV-URL")
|
||||
}
|
||||
options = append(options, nwconfig.OptionKVProvider(kv[0]))
|
||||
options = append(options, nwconfig.OptionKVProviderURL(strings.Join(kv[1:], "://")))
|
||||
options = append(options, nwconfig.OptionKVProviderURL(kv[1]))
|
||||
}
|
||||
if len(dconfig.ClusterOpts) > 0 {
|
||||
options = append(options, nwconfig.OptionKVOpts(dconfig.ClusterOpts))
|
||||
}
|
||||
|
||||
if daemon.discoveryWatcher != nil {
|
||||
@@ -441,6 +441,8 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
|
||||
return err
|
||||
}
|
||||
ipamV4Conf.Gateway = ip.String()
|
||||
} else if bridgeName == bridge.DefaultBridgeName && ipamV4Conf.PreferredPool != "" {
|
||||
logrus.Infof("Default bridge (%s) is assigned with an IP address %s. Daemon option --bip can be used to set a preferred IP address", bridgeName, ipamV4Conf.PreferredPool)
|
||||
}
|
||||
|
||||
if config.Bridge.FixedCIDR != "" {
|
||||
@@ -456,12 +458,25 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
|
||||
ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.Bridge.DefaultGatewayIPv4.String()
|
||||
}
|
||||
|
||||
var ipamV6Conf *libnetwork.IpamConf
|
||||
var (
|
||||
ipamV6Conf *libnetwork.IpamConf
|
||||
deferIPv6Alloc bool
|
||||
)
|
||||
if config.Bridge.FixedCIDRv6 != "" {
|
||||
_, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// In case user has specified the daemon flag --fixed-cidr-v6 and the passed network has
|
||||
// at least 48 host bits, we need to guarantee the current behavior where the containers'
|
||||
// IPv6 addresses will be constructed based on the containers' interface MAC address.
|
||||
// We do so by telling libnetwork to defer the IPv6 address allocation for the endpoints
|
||||
// on this network until after the driver has created the endpoint and returned the
|
||||
// constructed address. Libnetwork will then reserve this address with the ipam driver.
|
||||
ones, _ := fCIDRv6.Mask.Size()
|
||||
deferIPv6Alloc = ones <= 80
|
||||
|
||||
if ipamV6Conf == nil {
|
||||
ipamV6Conf = &libnetwork.IpamConf{}
|
||||
}
|
||||
@@ -486,7 +501,8 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
|
||||
netlabel.GenericData: netOption,
|
||||
netlabel.EnableIPv6: config.Bridge.EnableIPv6,
|
||||
}),
|
||||
libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf))
|
||||
libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf),
|
||||
libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating default \"bridge\" network: %v", err)
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ func (d Docker) Copy(c *daemon.Container, destPath string, src builder.FileInfo,
|
||||
|
||||
// only needed for fixPermissions, but might as well put it before CopyFileWithTar
|
||||
if destExists && destStat.IsDir() {
|
||||
destPath = filepath.Join(destPath, filepath.Base(srcPath))
|
||||
destPath = filepath.Join(destPath, src.Name())
|
||||
}
|
||||
|
||||
if err := system.MkdirAll(filepath.Dir(destPath), 0755); err != nil {
|
||||
@@ -193,7 +193,7 @@ func (d Docker) Copy(c *daemon.Container, destPath string, src builder.FileInfo,
|
||||
// GetCachedImage returns a reference to a cached image whose parent equals `parent`
|
||||
// and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
|
||||
func (d Docker) GetCachedImage(imgID string, cfg *runconfig.Config) (string, error) {
|
||||
cache, err := d.Daemon.ImageGetCached(string(imgID), cfg)
|
||||
cache, err := d.Daemon.ImageGetCached(imgID, cfg)
|
||||
if cache == nil || err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
+10
-12
@@ -76,22 +76,20 @@ func (daemon *Daemon) rm(container *Container, forceRemove bool) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Container state RemovalInProgress should be used to avoid races.
|
||||
if err = container.setRemovalInProgress(); err != nil {
|
||||
if err == derr.ErrorCodeAlreadyRemoving {
|
||||
// do not fail when the removal is in progress started by other request.
|
||||
return nil
|
||||
}
|
||||
return derr.ErrorCodeRmState.WithArgs(err)
|
||||
}
|
||||
defer container.resetRemovalInProgress()
|
||||
|
||||
// stop collection of stats for the container regardless
|
||||
// if stats are currently getting collected.
|
||||
daemon.statsCollector.stopCollection(container)
|
||||
|
||||
element := daemon.containers.Get(container.ID)
|
||||
if element == nil {
|
||||
return derr.ErrorCodeRmNotFound.WithArgs(container.ID)
|
||||
}
|
||||
|
||||
// Container state RemovalInProgress should be used to avoid races.
|
||||
if err = container.setRemovalInProgress(); err != nil {
|
||||
return derr.ErrorCodeRmState.WithArgs(err)
|
||||
}
|
||||
|
||||
defer container.resetRemovalInProgress()
|
||||
|
||||
if err = container.Stop(3); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
func TestContainerDoubleDelete(t *testing.T) {
|
||||
tmp, err := ioutil.TempDir("", "docker-daemon-unix-test-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
daemon := &Daemon{
|
||||
repository: tmp,
|
||||
root: tmp,
|
||||
}
|
||||
|
||||
container := &Container{
|
||||
CommonContainer: CommonContainer{
|
||||
State: NewState(),
|
||||
Config: &runconfig.Config{},
|
||||
},
|
||||
}
|
||||
|
||||
// Mark the container as having a delete in progress
|
||||
if err := container.setRemovalInProgress(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Try to remove the container when it's start is removalInProgress.
|
||||
// It should ignore the container and not return an error.
|
||||
if err := daemon.rm(container, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,964 @@
|
||||
// +build linux
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
sysinfo "github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/kr/pty"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
)
|
||||
|
||||
const (
|
||||
// Clear Linux for Intel(R) Architecture
|
||||
driverName = "clr"
|
||||
|
||||
envVarPrefix = "CLR_"
|
||||
|
||||
// variable that allows clrURL to be modified
|
||||
clrEnvURL = envVarPrefix + "DOWNLOAD_URL"
|
||||
|
||||
// Command used for lkvm control
|
||||
lkvmName = "lkvm"
|
||||
|
||||
// default value for clrURL
|
||||
defaultClrURL = "https://download.clearlinux.org"
|
||||
|
||||
// upstream latest release file (found below clrURL)
|
||||
latestFile = "latest"
|
||||
|
||||
// local "latest" information
|
||||
clrFile = "latest"
|
||||
|
||||
// ASCII file containing a checksums for the downloaded image.
|
||||
clrChecksumFile = "SHA512SUMS"
|
||||
|
||||
// clr kernel (not bzimage)
|
||||
clrKernel = "/usr/lib/kernel/vmlinux.container"
|
||||
)
|
||||
|
||||
var (
|
||||
// upstream base URL
|
||||
clrURL = defaultClrURL
|
||||
)
|
||||
|
||||
type driver struct {
|
||||
root string // root path for the driver to use
|
||||
libPath string
|
||||
initPath string
|
||||
version string
|
||||
apparmor bool
|
||||
sharedRoot bool
|
||||
activeContainers map[string]*activeContainer
|
||||
machineMemory int64
|
||||
containerPid int
|
||||
sync.Mutex
|
||||
ipaddr string
|
||||
gateway string
|
||||
macaddr string
|
||||
}
|
||||
|
||||
type activeContainer struct {
|
||||
container *configs.Config
|
||||
cmd *exec.Cmd
|
||||
}
|
||||
|
||||
type ProgressReader struct {
|
||||
// The real reader
|
||||
io.Reader
|
||||
|
||||
// Total size of download
|
||||
total uint64
|
||||
|
||||
// Bytes received
|
||||
bytes uint64
|
||||
|
||||
// Rate-limiting counter
|
||||
lastShown int
|
||||
|
||||
rawURL string
|
||||
}
|
||||
|
||||
// Read implements the Reader interface by calling the _real_ reader
|
||||
// and periodically displaying progress information.
|
||||
func (r *ProgressReader) Read(p []byte) (n int, err error) {
|
||||
n, err = r.Reader.Read(p)
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
// A true error occured
|
||||
return n, err
|
||||
}
|
||||
|
||||
r.bytes += uint64(n)
|
||||
|
||||
if r.total == 0 {
|
||||
// http header did not provide size
|
||||
logrus.Debugf("Read %v bytes from %v", r.bytes, r.rawURL)
|
||||
} else {
|
||||
percent := (float64(r.bytes) / float64(r.total)) * 100
|
||||
|
||||
// rate-limit to only display messages for every 10% downloaded
|
||||
next := int(percent / 10)
|
||||
|
||||
if r.lastShown == -1 || next > r.lastShown {
|
||||
logrus.Debugf("Read %v of %v bytes from %v (%2.2f%%)", r.bytes, r.total, r.rawURL, percent)
|
||||
r.lastShown = next
|
||||
}
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
func getTapIf(c *execdriver.Command) string {
|
||||
return fmt.Sprintf("tb-%s", c.ID[:12])
|
||||
}
|
||||
|
||||
// getClrVersion reads the latest image version from the
|
||||
// locally-downloaded "latest" file.
|
||||
func getClrVersion(libPath string) string {
|
||||
txt, err := ioutil.ReadFile(path.Join(libPath, clrFile))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.Split(string(txt), "\n")[0]
|
||||
}
|
||||
|
||||
func getURL(rawURL, outfile string) error {
|
||||
|
||||
_url, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := os.Create(outfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
transport := &http.Transport{Proxy: http.ProxyFromEnvironment}
|
||||
client := &http.Client{Transport: transport}
|
||||
|
||||
// Determine size of download by consulting the headers
|
||||
response, err := client.Head(rawURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lenStr := response.Header.Get("Content-Length")
|
||||
dataLen, _ := strconv.Atoi(lenStr)
|
||||
|
||||
request, err := http.NewRequest("GET", _url.String(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response, err = client.Do(request)
|
||||
defer response.Body.Close()
|
||||
|
||||
reader := &ProgressReader{
|
||||
Reader: response.Body,
|
||||
total: uint64(dataLen),
|
||||
lastShown: -1,
|
||||
rawURL: rawURL,
|
||||
}
|
||||
|
||||
_, err = io.Copy(out, reader)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// fetchLatest downloads the "latest" file which contains the version
|
||||
// number of the latest image.
|
||||
func fetchLatest(libPath string) error {
|
||||
file := path.Join(libPath, clrFile)
|
||||
return getURL(clrURL+"/"+latestFile, file)
|
||||
}
|
||||
|
||||
// NewDriver creates a new clear linux execution driver.
|
||||
func NewDriver(root, libPath, initPath string, apparmor bool) (*driver, error) {
|
||||
if err := os.MkdirAll(root, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
meminfo, err := sysinfo.ReadMemInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version, err := prepareClr(libPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &driver{
|
||||
apparmor: apparmor,
|
||||
root: root,
|
||||
libPath: libPath,
|
||||
initPath: initPath,
|
||||
version: version,
|
||||
sharedRoot: false,
|
||||
activeContainers: make(map[string]*activeContainer),
|
||||
// FIXME:
|
||||
machineMemory: meminfo.MemTotal,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// fetchImage downloads the latest image and verifies its checksum.
|
||||
func fetchImage(version, outfile, outpath string) error {
|
||||
|
||||
url := fmt.Sprintf("%s/releases/%s/clear/%s", clrURL, version, outfile)
|
||||
|
||||
logrus.Debugf("Fetching clr version: %s, %s", version, outpath)
|
||||
|
||||
return getURL(url, outpath)
|
||||
}
|
||||
|
||||
// uncompressImage handles decompressing the image specified by path.
|
||||
func uncompressImage(path string) error {
|
||||
// decompress the file
|
||||
cmd := exec.Command("unxz", path)
|
||||
cmd.Dir = filepath.Dir(path)
|
||||
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("Unable to extract image %s: %s", path, output)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getChecksumLine reads the file specified and returns the checksum for
|
||||
// the filename specified.
|
||||
func getChecksumLine(file string, filename string) string {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
lines := strings.Split(string(data), "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
if strings.HasSuffix(line, filename) {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 2 {
|
||||
return fields[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// getChecksum downloads the checksum file for the image version specified,
|
||||
// storing it under libPath, then extracts the checksum corresponding to
|
||||
// imageFile and returns it.
|
||||
func getChecksum(version string, libPath string, imageFile string) (string, error) {
|
||||
outfile := path.Join(libPath, clrChecksumFile)
|
||||
checkurl := fmt.Sprintf("%s/releases/%s/clear/%s", clrURL, version, clrChecksumFile)
|
||||
|
||||
err := getURL(checkurl, outfile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Now, extract the checksum from the downloaded file.
|
||||
return getChecksumLine(outfile, imageFile), nil
|
||||
}
|
||||
|
||||
func verifyChecksum(fullPath, expectedChecksum string) error {
|
||||
|
||||
if expectedChecksum == "" {
|
||||
return errors.New("blank expectedChecksum")
|
||||
}
|
||||
if fullPath == "" {
|
||||
return errors.New("blank path")
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
calculatedChecksum := fmt.Sprintf("%x", sha512.Sum512(data))
|
||||
|
||||
if calculatedChecksum != expectedChecksum {
|
||||
return fmt.Errorf("Checksum mismatch: %v %v", calculatedChecksum, expectedChecksum)
|
||||
}
|
||||
|
||||
logrus.Debugf("Checksum for %v correct: %v", fullPath, expectedChecksum)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// alreadyDownloaded determines if the uncompressed version of the file
|
||||
// specified by outpath already exists and seems sane.
|
||||
func alreadyDownloaded(outpath string) bool {
|
||||
// strip off the (first) extension
|
||||
base := path.Base(outpath)
|
||||
extension := filepath.Ext(base)
|
||||
|
||||
uncompressed := strings.TrimSuffix(outpath, extension)
|
||||
|
||||
st, err := os.Stat(uncompressed)
|
||||
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if st.IsDir() {
|
||||
return false
|
||||
}
|
||||
|
||||
if st.Size() == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// prepareClr handles downloading and checking clr images, returning the
|
||||
// version of clr that will be used.
|
||||
func prepareClr(libPath string) (string, error) {
|
||||
logrus.Debugf("%s preparing environment", driverName)
|
||||
|
||||
tmp := os.Getenv(clrEnvURL)
|
||||
if tmp != "" {
|
||||
clrURL = tmp
|
||||
logrus.Debugf("%s using alternate download URL: %s", driverName, clrURL)
|
||||
}
|
||||
|
||||
var version = getClrVersion(libPath)
|
||||
|
||||
err := fetchLatest(libPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
nversion := getClrVersion(libPath)
|
||||
if nversion == "" {
|
||||
return "", errors.New("unable to determine latest clr version")
|
||||
}
|
||||
|
||||
outfile := fmt.Sprintf("clear-%s-containers.img.xz", nversion)
|
||||
outpath := path.Join(libPath, outfile)
|
||||
|
||||
// If the download originally failed, there may be a partial .xz
|
||||
// file. However, this will be ignored and a new download triggered
|
||||
// since we only only check for the uncompressed file (which would
|
||||
// only exist if the download was successful).
|
||||
if alreadyDownloaded(outpath) && nversion == version {
|
||||
logrus.Debugf("Using clr version: %s", nversion)
|
||||
return nversion, nil
|
||||
}
|
||||
|
||||
// Determine the expected checksum for the file about to be
|
||||
// downloaded.
|
||||
checksum, err := getChecksum(nversion, libPath, outfile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if nversion != version && version != "" {
|
||||
logrus.Debugf("Updating to clr version: %s", nversion)
|
||||
} else if version == "" {
|
||||
logrus.Debugf("Installing clr version: %s", nversion)
|
||||
}
|
||||
|
||||
err = fetchImage(nversion, outfile, outpath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err = verifyChecksum(outpath, checksum); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = uncompressImage(outpath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return nversion, nil
|
||||
}
|
||||
|
||||
func (d *driver) Name() string {
|
||||
return fmt.Sprintf("%s-%s", driverName, d.version)
|
||||
}
|
||||
|
||||
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execdriver.Hooks) (execdriver.ExitStatus, error) {
|
||||
var (
|
||||
term execdriver.Terminal
|
||||
err error
|
||||
)
|
||||
|
||||
container, err := d.createContainer(c)
|
||||
if err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
memoryMiB := c.HostConfig.Memory
|
||||
if memoryMiB == 0 {
|
||||
memoryMiB = 1024
|
||||
} else {
|
||||
// docker passes the value as bytes
|
||||
memoryMiB = memoryMiB / int64(math.Pow(2, 20))
|
||||
}
|
||||
|
||||
workingDirVar := fmt.Sprintf("%s%s=%q", envVarPrefix, "WORKINGDIR", c.WorkingDir)
|
||||
c.ProcessConfig.Cmd.Env = append(c.ProcessConfig.Cmd.Env, workingDirVar)
|
||||
|
||||
userVar := fmt.Sprintf("%s%s=%q", envVarPrefix, "USER", c.ProcessConfig.User)
|
||||
c.ProcessConfig.Cmd.Env = append(c.ProcessConfig.Cmd.Env, userVar)
|
||||
|
||||
if err := d.setupNetwork(c); err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
if c.ProcessConfig.Tty {
|
||||
term, err = NewTtyConsole(&c.ProcessConfig, pipes)
|
||||
} else {
|
||||
term, err = execdriver.NewStdConsole(&c.ProcessConfig, pipes)
|
||||
}
|
||||
if err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
c.ProcessConfig.Terminal = term
|
||||
|
||||
d.Lock()
|
||||
d.activeContainers[c.ID] = &activeContainer{
|
||||
container: container,
|
||||
cmd: &c.ProcessConfig.Cmd,
|
||||
}
|
||||
d.Unlock()
|
||||
|
||||
if err := d.generateEnvConfig(c); err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
if err := d.generateDockerInit(c); err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
for _, m := range c.Mounts {
|
||||
dest := path.Join(c.Rootfs, m.Destination)
|
||||
|
||||
if m.Destination == "/etc/hostname" {
|
||||
continue
|
||||
}
|
||||
|
||||
if !pathExists(m.Source) {
|
||||
continue
|
||||
}
|
||||
|
||||
opts := "bind"
|
||||
|
||||
if m.Private {
|
||||
opts = opts + ",rprivate"
|
||||
}
|
||||
if m.Slave {
|
||||
opts = opts + ",rslave"
|
||||
}
|
||||
|
||||
// This may look racy, but it isn't since the VM isn't
|
||||
// running yet.
|
||||
//
|
||||
// The check is necessary to handle bind mounting of
|
||||
// regular files correctly since without it we may be
|
||||
// attempting to create a directory where there already
|
||||
// exists a normal file.
|
||||
if !pathExists(dest) {
|
||||
if err := os.MkdirAll(dest, 0750); err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := mount.Mount(m.Source, dest, "", opts); err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
if !m.Writable {
|
||||
if err := mount.Mount("", dest, "", "bind,remount,ro"); err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
}
|
||||
defer mount.Unmount(dest)
|
||||
}
|
||||
|
||||
var args []string
|
||||
// various things for lkvm
|
||||
ifname := getTapIf(c)
|
||||
// FIXME: Should be real hostname from like process/container struct
|
||||
hostname := c.ID[0:12]
|
||||
img := fmt.Sprintf("%s/clear-%s-containers.img", d.libPath, d.version)
|
||||
memory := fmt.Sprintf("%d", memoryMiB)
|
||||
// FIXME: Locked cores to 6 ?
|
||||
cores := fmt.Sprintf("%d", 6)
|
||||
|
||||
args = append(args, c.ProcessConfig.Entrypoint)
|
||||
args = append(args, c.ProcessConfig.Arguments...)
|
||||
|
||||
rootParams := fmt.Sprintf("root=/dev/plkvm0p1 rootfstype=ext4 rootflags=dax,data=ordered "+
|
||||
"init=/usr/lib/systemd/systemd systemd.unit=container.target rw tsc=reliable "+
|
||||
"systemd.show_status=false "+
|
||||
"no_timer_check rcupdate.rcu_expedited=1 console=hvc0 quiet ip=%s::%s::%s::off",
|
||||
d.ipaddr, d.gateway, hostname)
|
||||
|
||||
params := []string{
|
||||
lkvmName, "run", "-c", cores, "-m", memory,
|
||||
"--name", c.ID, "--console", "virtio",
|
||||
"--kernel", clrKernel,
|
||||
"--params", rootParams,
|
||||
"--shmem", fmt.Sprintf("0x200000000:0:file=%s:private", img),
|
||||
"--network", fmt.Sprintf("mode=tap,script=none,tapif=%s,guest_mac=%s", ifname, d.macaddr),
|
||||
"--9p", fmt.Sprintf("%s,rootfs", c.Rootfs),
|
||||
}
|
||||
|
||||
logrus.Debugf("%s params %s", driverName, params)
|
||||
var (
|
||||
name = params[0]
|
||||
arg = params[1:]
|
||||
)
|
||||
aname, err := exec.LookPath(name)
|
||||
if err != nil {
|
||||
aname = name
|
||||
}
|
||||
c.ProcessConfig.Path = aname
|
||||
c.ProcessConfig.Args = append([]string{name}, arg...)
|
||||
c.ProcessConfig.Env = []string{fmt.Sprintf("HOME=%s", d.root)}
|
||||
|
||||
// Start the container. Since it runs synchronously, we don't Wait()
|
||||
// for it since we need to check the status to determine if it did
|
||||
// actually start successfully.
|
||||
if err := c.ProcessConfig.Start(); err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
var (
|
||||
waitErr error
|
||||
waitLock = make(chan struct{})
|
||||
)
|
||||
go func() {
|
||||
if err := c.ProcessConfig.Wait(); err != nil {
|
||||
if _, ok := err.(*exec.ExitError); !ok { // Do not propagate the error if it's simply a status code != 0
|
||||
waitErr = err
|
||||
}
|
||||
}
|
||||
close(waitLock)
|
||||
}()
|
||||
|
||||
// FIXME: need to create state.json for Stats() to work.
|
||||
pid := c.ProcessConfig.Process.Pid
|
||||
c.ContainerPid = pid
|
||||
d.containerPid = pid
|
||||
|
||||
if hooks.Start != nil {
|
||||
logrus.Debugf("Invoking startCallback")
|
||||
chOOM := make(chan struct{})
|
||||
close(chOOM)
|
||||
hooks.Start(&c.ProcessConfig, pid, chOOM)
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
oomKill := false
|
||||
|
||||
// Wait for the VM to shutdown
|
||||
<-waitLock
|
||||
exitCode := getExitCode(c)
|
||||
|
||||
cExitStatus, cerr := d.cleanupVM(c)
|
||||
|
||||
if cerr != nil {
|
||||
waitErr = cerr
|
||||
exitCode = cExitStatus
|
||||
}
|
||||
|
||||
// check oom error
|
||||
if oomKill {
|
||||
exitCode = 137
|
||||
}
|
||||
|
||||
return execdriver.ExitStatus{ExitCode: exitCode, OOMKilled: false}, waitErr
|
||||
}
|
||||
|
||||
func pathExists(path string) bool {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func pathExecutable(path string) bool {
|
||||
s, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
mode := s.Mode()
|
||||
if mode&0111 != 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *driver) cleanupVM(c *execdriver.Command) (exitStatus int, err error) {
|
||||
cmd := exec.Command("ip", "tuntap", "del", "dev", getTapIf(c), "mode", "tap")
|
||||
var output []byte
|
||||
|
||||
if output, err = cmd.CombinedOutput(); err != nil {
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
waitStatus := exitError.Sys().(syscall.WaitStatus)
|
||||
exitStatus = waitStatus.ExitStatus()
|
||||
}
|
||||
logrus.Debugf("teardown failed for vm %s: %s (%s)", c.ID, string(output), err.Error())
|
||||
}
|
||||
|
||||
// doesn't matter if this fails
|
||||
// lkvm could have removed it, and stale sockets are not fatal
|
||||
_ = os.Remove(fmt.Sprintf("%s/.lkvm/%s.sock", d.root, c.ID))
|
||||
|
||||
return exitStatus, err
|
||||
}
|
||||
|
||||
// createContainer populates and configures the container type with the
|
||||
// data provided by the execdriver.Command
|
||||
func (d *driver) createContainer(c *execdriver.Command) (*configs.Config, error) {
|
||||
return execdriver.InitContainer(c), nil
|
||||
}
|
||||
|
||||
/// Return the exit code of the process
|
||||
// if the process has not exited -1 will be returned
|
||||
func getExitCode(c *execdriver.Command) int {
|
||||
if c.ProcessConfig.ProcessState == nil {
|
||||
return -1
|
||||
}
|
||||
return c.ProcessConfig.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
||||
}
|
||||
|
||||
func (d *driver) lkvmCommand(c *execdriver.Command, arg string) ([]byte, error) {
|
||||
args := append([]string{lkvmName}, arg)
|
||||
if c != nil {
|
||||
args = append(args, "--name", c.ID)
|
||||
}
|
||||
cmd := exec.Command(lkvmName, args...)
|
||||
cmd.Env = []string{fmt.Sprintf("HOME=%s", d.root)}
|
||||
return cmd.Output()
|
||||
}
|
||||
|
||||
// Kill sends a signal to workload
|
||||
func (d *driver) Kill(c *execdriver.Command, sig int) error {
|
||||
// Not supported
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) Pause(c *execdriver.Command) error {
|
||||
_, err := d.lkvmCommand(c, "pause")
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *driver) Unpause(c *execdriver.Command) error {
|
||||
_, err := d.lkvmCommand(c, "resume")
|
||||
return err
|
||||
}
|
||||
|
||||
// Terminate forcibly stops a container
|
||||
func (d *driver) Terminate(c *execdriver.Command) error {
|
||||
_, err := d.lkvmCommand(c, "stop")
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *driver) containerDir(containerID string) string {
|
||||
return path.Join(d.libPath, "containers", containerID)
|
||||
}
|
||||
|
||||
// isDigit returns true if s can be represented as an integer
|
||||
func isDigit(s string) bool {
|
||||
if _, err := strconv.Atoi(s); err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *driver) getInfo(id string) ([]byte, error) {
|
||||
output, err := d.lkvmCommand(nil, "list")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, line := range strings.Split(string(output), "\n") {
|
||||
if i < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
fields := strings.Fields(strings.TrimSpace(line))
|
||||
|
||||
if len(fields) != 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
if !isDigit(fields[0]) {
|
||||
continue
|
||||
}
|
||||
|
||||
if fields[1] != id {
|
||||
continue
|
||||
}
|
||||
|
||||
return []byte(line), nil
|
||||
}
|
||||
|
||||
return []byte(fmt.Sprintf("-1 %s stopped", id)), nil
|
||||
}
|
||||
|
||||
type info struct {
|
||||
ID string
|
||||
driver *driver
|
||||
}
|
||||
|
||||
func (i *info) IsRunning() bool {
|
||||
output, err := i.driver.getInfo(i.ID)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error getting info for %s container %s: %s (%s)",
|
||||
driverName, i.ID, err, output)
|
||||
return false
|
||||
}
|
||||
|
||||
clrInfo, err := parseClrInfo(i.ID, string(output))
|
||||
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return clrInfo.Running
|
||||
}
|
||||
|
||||
func (d *driver) Info(id string) execdriver.Info {
|
||||
return &info{
|
||||
ID: id,
|
||||
driver: d,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *driver) GetPidsForContainer(id string) ([]int, error) {
|
||||
// The VM doesn't expose the worload pid(s), so the only meaningful
|
||||
// pid is that of the VM
|
||||
return []int{d.containerPid}, nil
|
||||
}
|
||||
|
||||
// TtyConsole is a type to represent a pseud-oterminal (see pty(7))
|
||||
type TtyConsole struct {
|
||||
MasterPty *os.File
|
||||
SlavePty *os.File
|
||||
}
|
||||
|
||||
// NewTtyConsole returns a new TtyConsole object.
|
||||
func NewTtyConsole(processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes) (*TtyConsole, error) {
|
||||
// lxc is special in that we cannot create the master outside of the container without
|
||||
// opening the slave because we have nothing to provide to the cmd. We have to open both then do
|
||||
// the crazy setup on command right now instead of passing the console path to lxc and telling it
|
||||
// to open up that console. we save a couple of openfiles in the native driver because we can do
|
||||
// this.
|
||||
ptyMaster, ptySlave, err := pty.Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tty := &TtyConsole{
|
||||
MasterPty: ptyMaster,
|
||||
SlavePty: ptySlave,
|
||||
}
|
||||
|
||||
if err := tty.AttachPipes(&processConfig.Cmd, pipes); err != nil {
|
||||
tty.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
processConfig.Console = tty.SlavePty.Name()
|
||||
|
||||
return tty, nil
|
||||
}
|
||||
|
||||
// Master returns the master end of the pty
|
||||
func (t *TtyConsole) Master() *os.File {
|
||||
return t.MasterPty
|
||||
}
|
||||
|
||||
// Resize modifies the size of the pty terminal being used.
|
||||
func (t *TtyConsole) Resize(h, w int) error {
|
||||
return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
||||
}
|
||||
|
||||
// AttachPipes associates the specified pipes with the pty master.
|
||||
func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
|
||||
command.Stdout = t.SlavePty
|
||||
command.Stderr = t.SlavePty
|
||||
|
||||
go func() {
|
||||
if wb, ok := pipes.Stdout.(interface {
|
||||
CloseWriters() error
|
||||
}); ok {
|
||||
defer wb.CloseWriters()
|
||||
}
|
||||
|
||||
io.Copy(pipes.Stdout, t.MasterPty)
|
||||
}()
|
||||
|
||||
if pipes.Stdin != nil {
|
||||
command.Stdin = t.SlavePty
|
||||
command.SysProcAttr.Setctty = true
|
||||
|
||||
go func() {
|
||||
io.Copy(t.MasterPty, pipes.Stdin)
|
||||
|
||||
pipes.Stdin.Close()
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes both ends of the pty.
|
||||
func (t *TtyConsole) Close() error {
|
||||
t.SlavePty.Close()
|
||||
return t.MasterPty.Close()
|
||||
}
|
||||
|
||||
func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, hooks execdriver.Hooks) (int, error) {
|
||||
return -1, fmt.Errorf("Unsupported: Exec is not supported by the %q driver", driverName)
|
||||
|
||||
}
|
||||
|
||||
// Clean up after an Exec
|
||||
func (d *driver) Clean(id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) generateEnvConfig(c *execdriver.Command) error {
|
||||
data := []byte(strings.Join(c.ProcessConfig.Env, "\n"))
|
||||
|
||||
p := path.Join(d.libPath, "containers", c.ID, "config.env")
|
||||
c.Mounts = append(c.Mounts, execdriver.Mount{
|
||||
Source: p,
|
||||
Destination: "/.dockerenv",
|
||||
Writable: false,
|
||||
Private: true,
|
||||
})
|
||||
|
||||
return ioutil.WriteFile(p, data, 0600)
|
||||
}
|
||||
|
||||
func (d *driver) generateDockerInit(c *execdriver.Command) error {
|
||||
p := fmt.Sprintf("%s/.containerexec", c.Rootfs)
|
||||
var args []string
|
||||
|
||||
if pathExecutable(p) {
|
||||
return nil
|
||||
}
|
||||
|
||||
args = append(args, c.ProcessConfig.Entrypoint)
|
||||
args = append(args, c.ProcessConfig.Arguments...)
|
||||
|
||||
data := []byte(fmt.Sprintf("#!/bin/sh\n%s\n", strings.Join(args, " ")))
|
||||
|
||||
return ioutil.WriteFile(p, data, 0755)
|
||||
}
|
||||
|
||||
func (d *driver) linkExists(name string) bool {
|
||||
cmd := exec.Command("ip", "link", "show", name)
|
||||
err := cmd.Run()
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (d *driver) setupNetwork(c *execdriver.Command) error {
|
||||
ifname := getTapIf(c)
|
||||
|
||||
var bridgeName string
|
||||
var bridgeLinkName string
|
||||
var output []byte
|
||||
var err error
|
||||
var ok bool
|
||||
|
||||
bridge := c.NetworkSettings.Networks["bridge"]
|
||||
if bridge == nil {
|
||||
return fmt.Errorf("no bridge network available")
|
||||
}
|
||||
|
||||
d.ipaddr = bridge.IPAddress
|
||||
d.macaddr = bridge.MacAddress
|
||||
d.gateway = bridge.Gateway
|
||||
|
||||
// Extract the bridge details from the relevant endpoint.
|
||||
for _, info := range c.EndpointInfo {
|
||||
if _, ok = info[netlabel.BridgeEID].(string); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if bridgeName, ok = info[netlabel.BridgeName].(string); !ok {
|
||||
return fmt.Errorf("unable to determine bridge name")
|
||||
}
|
||||
|
||||
if bridgeLinkName, ok = info[netlabel.BridgeLinkName].(string); !ok {
|
||||
return fmt.Errorf("unable to determine bridge link name")
|
||||
}
|
||||
}
|
||||
|
||||
// Strip existing veth if it exists
|
||||
if bridgeLinkName != "" && d.linkExists(bridgeLinkName) {
|
||||
cmd := exec.Command("ip", "link", "del", bridgeLinkName)
|
||||
if output, err = cmd.CombinedOutput(); err != nil {
|
||||
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.Command("ip", "tuntap", "add", "dev", ifname, "mode", "tap", "vnet_hdr")
|
||||
if output, err = cmd.CombinedOutput(); err != nil {
|
||||
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
|
||||
return err
|
||||
}
|
||||
cmd = exec.Command("ip", "link", "set", "dev", ifname, "master", bridgeName)
|
||||
if output, err = cmd.CombinedOutput(); err != nil {
|
||||
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
|
||||
return err
|
||||
}
|
||||
|
||||
cmd = exec.Command("ip", "link", "set", "dev", ifname, "up")
|
||||
if output, err = cmd.CombinedOutput(); err != nil {
|
||||
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *driver) Stats(id string) (*execdriver.ResourceStats, error) {
|
||||
if _, ok := d.activeContainers[id]; !ok {
|
||||
return nil, fmt.Errorf("%s is not a key in active containers", id)
|
||||
}
|
||||
// FIXME:
|
||||
return execdriver.Stats(d.containerDir(id), d.activeContainers[id].container.Cgroups.Memory, d.machineMemory)
|
||||
}
|
||||
|
||||
func (d *driver) SupportsHooks() bool {
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// +build linux
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCannotParse = errors.New("cannot parse raw input")
|
||||
)
|
||||
|
||||
type clrInfo struct {
|
||||
Running bool
|
||||
Pid int
|
||||
}
|
||||
|
||||
func parseClrInfo(name, raw string) (*clrInfo, error) {
|
||||
if raw == "" {
|
||||
return nil, ErrCannotParse
|
||||
}
|
||||
var (
|
||||
err error
|
||||
info = &clrInfo{}
|
||||
)
|
||||
|
||||
fields := strings.Fields(strings.TrimSpace(raw))
|
||||
|
||||
// The format is expected to be:
|
||||
//
|
||||
// <pid> <name> <state>
|
||||
//
|
||||
if len(fields) != 3 {
|
||||
return nil, ErrCannotParse
|
||||
}
|
||||
|
||||
info.Pid, err = strconv.Atoi(fields[0])
|
||||
if err != nil {
|
||||
return nil, ErrCannotParse
|
||||
}
|
||||
|
||||
if fields[1] != name {
|
||||
return nil, ErrCannotParse
|
||||
}
|
||||
|
||||
info.Running = fields[2] == "running"
|
||||
|
||||
return info, nil
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// +build linux
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Args provided to the init function for a driver
|
||||
type InitArgs struct {
|
||||
User string
|
||||
Gateway string
|
||||
Ip string
|
||||
WorkDir string
|
||||
Privileged bool
|
||||
Env []string
|
||||
Args []string
|
||||
Mtu int
|
||||
Console string
|
||||
Pipe int
|
||||
Root string
|
||||
CapAdd string
|
||||
CapDrop string
|
||||
}
|
||||
|
||||
func getArgs() *InitArgs {
|
||||
var (
|
||||
// Get cmdline arguments
|
||||
user = flag.String("u", "", "username or uid")
|
||||
gateway = flag.String("g", "", "gateway address")
|
||||
ip = flag.String("i", "", "ip address")
|
||||
workDir = flag.String("w", "", "workdir")
|
||||
privileged = flag.Bool("privileged", false, "privileged mode")
|
||||
mtu = flag.Int("mtu", 1500, "interface mtu")
|
||||
capAdd = flag.String("cap-add", "", "capabilities to add")
|
||||
capDrop = flag.String("cap-drop", "", "capabilities to drop")
|
||||
)
|
||||
|
||||
flag.Parse()
|
||||
|
||||
return &InitArgs{
|
||||
User: *user,
|
||||
Gateway: *gateway,
|
||||
Ip: *ip,
|
||||
WorkDir: *workDir,
|
||||
Privileged: *privileged,
|
||||
Args: flag.Args(),
|
||||
Mtu: *mtu,
|
||||
CapAdd: *capAdd,
|
||||
CapDrop: *capDrop,
|
||||
}
|
||||
}
|
||||
|
||||
// Clear environment pollution introduced by lxc-start
|
||||
func setupEnv(args *InitArgs) error {
|
||||
// Get env
|
||||
var env []string
|
||||
dockerenv, err := os.Open(".dockerenv")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to load environment variables: %v", err)
|
||||
}
|
||||
defer dockerenv.Close()
|
||||
if err := json.NewDecoder(dockerenv).Decode(&env); err != nil {
|
||||
return fmt.Errorf("Unable to decode environment variables: %v", err)
|
||||
}
|
||||
// Propagate the plugin-specific container env variable
|
||||
env = append(env, "container="+os.Getenv("container"))
|
||||
|
||||
args.Env = env
|
||||
|
||||
os.Clearenv()
|
||||
for _, kv := range args.Env {
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
if len(parts) == 1 {
|
||||
parts = append(parts, "")
|
||||
}
|
||||
os.Setenv(parts[0], parts[1])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Setup working directory
|
||||
func setupWorkingDirectory(args *InitArgs) error {
|
||||
if args.WorkDir == "" {
|
||||
return nil
|
||||
}
|
||||
if err := syscall.Chdir(args.WorkDir); err != nil {
|
||||
return fmt.Errorf("Unable to change dir to %v: %v", args.WorkDir, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -8,7 +8,9 @@ import (
|
||||
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
// TODO Windows: Factor out ulimit
|
||||
"github.com/docker/docker/daemon/network"
|
||||
"github.com/docker/docker/pkg/ulimit"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
)
|
||||
@@ -230,4 +232,8 @@ type Command struct {
|
||||
LayerPaths []string `json:"layer_paths"` // Windows needs to know the layer paths and folder for a command
|
||||
LayerFolder string `json:"layer_folder"`
|
||||
Hostname string `json:"hostname"` // Windows sets the hostname in the execdriver
|
||||
NetworkSettings *network.Settings `json:"network_settings"`
|
||||
EndpointInfo []map[string]interface{} `json:"endpoint_info"`
|
||||
HostConfig *runconfig.HostConfig `json:"hostconfig"`
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/docker/daemon/execdriver/clr"
|
||||
"github.com/docker/docker/daemon/execdriver/lxc"
|
||||
"github.com/docker/docker/daemon/execdriver/native"
|
||||
"github.com/docker/docker/pkg/sysinfo"
|
||||
@@ -15,7 +16,10 @@ import (
|
||||
|
||||
// NewDriver returns a new execdriver.Driver from the given name configured with the provided options.
|
||||
func NewDriver(name string, options []string, root, libPath, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
|
||||
rootPath := path.Join(root, "execdriver", name)
|
||||
switch name {
|
||||
case "clr":
|
||||
return clr.NewDriver(rootPath, libPath, initPath, sysInfo.AppArmor)
|
||||
case "lxc":
|
||||
// we want to give the lxc driver the full docker root because it needs
|
||||
// to access and write config and template files in /var/lib/docker/containers/*
|
||||
@@ -23,7 +27,7 @@ func NewDriver(name string, options []string, root, libPath, initPath string, sy
|
||||
logrus.Warn("LXC built-in support is deprecated.")
|
||||
return lxc.NewDriver(root, libPath, initPath, sysInfo.AppArmor)
|
||||
case "native":
|
||||
return native.NewDriver(path.Join(root, "execdriver", "native"), initPath, options)
|
||||
return native.NewDriver(rootPath, initPath, options)
|
||||
}
|
||||
return nil, fmt.Errorf("unknown exec driver %s", name)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo
|
||||
term execdriver.Terminal
|
||||
err error
|
||||
exitCode int32
|
||||
errno uint32
|
||||
)
|
||||
|
||||
active := d.activeContainers[c.ID]
|
||||
@@ -78,15 +77,12 @@ func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo
|
||||
hooks.Start(&c.ProcessConfig, int(pid), chOOM)
|
||||
}
|
||||
|
||||
if exitCode, errno, err = hcsshim.WaitForProcessInComputeSystem(c.ID, pid, hcsshim.TimeoutInfinite); err != nil {
|
||||
if errno == hcsshim.Win32PipeHasBeenEnded {
|
||||
logrus.Debugf("Exiting Run() after WaitForProcessInComputeSystem failed with recognised error 0x%X", errno)
|
||||
return hcsshim.WaitErrExecFailed, nil
|
||||
}
|
||||
logrus.Warnf("WaitForProcessInComputeSystem failed (container may have been killed): 0x%X %s", errno, err)
|
||||
if exitCode, err = hcsshim.WaitForProcessInComputeSystem(c.ID, pid); err != nil {
|
||||
logrus.Errorf("Failed to WaitForProcessInComputeSystem %s", err)
|
||||
return -1, err
|
||||
}
|
||||
|
||||
logrus.Debugln("Exiting Run()", c.ID)
|
||||
// TODO Windows - Do something with this exit code
|
||||
logrus.Debugln("Exiting Run() with ExitCode 0", c.ID)
|
||||
return int(exitCode), nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
@@ -23,11 +22,7 @@ func startStdinCopy(dst io.WriteCloser, src io.Reader) {
|
||||
go func() {
|
||||
defer dst.Close()
|
||||
bytes, err := io.Copy(dst, src)
|
||||
log := fmt.Sprintf("Copied %d bytes from stdin.", bytes)
|
||||
if err != nil {
|
||||
log = log + " err=" + err.Error()
|
||||
}
|
||||
logrus.Debugf(log)
|
||||
logrus.Debugf("Copied %d bytes from stdin err=%s", bytes, err)
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -40,11 +35,7 @@ func startStdouterrCopy(dst io.Writer, src io.ReadCloser, name string) {
|
||||
go func() {
|
||||
defer src.Close()
|
||||
bytes, err := io.Copy(dst, src)
|
||||
log := fmt.Sprintf("Copied %d bytes from %s.", bytes, name)
|
||||
if err != nil {
|
||||
log = log + " err=" + err.Error()
|
||||
}
|
||||
logrus.Debugf(log)
|
||||
logrus.Debugf("Copied %d bytes from %s err=%s", bytes, name, err)
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
@@ -220,19 +220,22 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execd
|
||||
}
|
||||
defer func() {
|
||||
// Stop the container
|
||||
if forceKill {
|
||||
logrus.Debugf("Forcibly terminating container %s", c.ID)
|
||||
if errno, err := hcsshim.TerminateComputeSystem(c.ID, hcsshim.TimeoutInfinite, "exec-run-defer"); err != nil {
|
||||
logrus.Warnf("Ignoring error from TerminateComputeSystem 0x%X %s", errno, err)
|
||||
|
||||
if terminateMode {
|
||||
logrus.Debugf("Terminating container %s", c.ID)
|
||||
if err := hcsshim.TerminateComputeSystem(c.ID); err != nil {
|
||||
// IMPORTANT: Don't fail if fails to change state. It could already
|
||||
// have been stopped through kill().
|
||||
// Otherwise, the docker daemon will hang in job wait()
|
||||
logrus.Warnf("Ignoring error from TerminateComputeSystem %s", err)
|
||||
}
|
||||
} else {
|
||||
logrus.Debugf("Shutting down container %s", c.ID)
|
||||
if errno, err := hcsshim.ShutdownComputeSystem(c.ID, hcsshim.TimeoutInfinite, "exec-run-defer"); err != nil {
|
||||
if errno != hcsshim.Win32SystemShutdownIsInProgress &&
|
||||
errno != hcsshim.Win32SpecifiedPathInvalid &&
|
||||
errno != hcsshim.Win32SystemCannotFindThePathSpecified {
|
||||
logrus.Warnf("Ignoring error from ShutdownComputeSystem 0x%X %s", errno, err)
|
||||
}
|
||||
if err := hcsshim.ShutdownComputeSystem(c.ID); err != nil {
|
||||
// IMPORTANT: Don't fail if fails to change state. It could already
|
||||
// have been stopped through kill().
|
||||
// Otherwise, the docker daemon will hang in job wait()
|
||||
logrus.Warnf("Ignoring error from ShutdownComputeSystem %s", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -300,20 +303,11 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execd
|
||||
hooks.Start(&c.ProcessConfig, int(pid), chOOM)
|
||||
}
|
||||
|
||||
var (
|
||||
exitCode int32
|
||||
errno uint32
|
||||
)
|
||||
exitCode, errno, err = hcsshim.WaitForProcessInComputeSystem(c.ID, pid, hcsshim.TimeoutInfinite)
|
||||
var exitCode int32
|
||||
exitCode, err = hcsshim.WaitForProcessInComputeSystem(c.ID, pid)
|
||||
if err != nil {
|
||||
if errno != hcsshim.Win32PipeHasBeenEnded {
|
||||
logrus.Warnf("WaitForProcessInComputeSystem failed (container may have been killed): %s", err)
|
||||
}
|
||||
// Do NOT return err here as the container would have
|
||||
// started, otherwise docker will deadlock. It's perfectly legitimate
|
||||
// for WaitForProcessInComputeSystem to fail in situations such
|
||||
// as the container being killed on another thread.
|
||||
return execdriver.ExitStatus{ExitCode: hcsshim.WaitErrExecFailed}, nil
|
||||
logrus.Errorf("Failed to WaitForProcessInComputeSystem %s", err)
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
logrus.Debugf("Exiting Run() exitCode %d id=%s", exitCode, c.ID)
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/microsoft/hcsshim"
|
||||
@@ -13,36 +10,37 @@ import (
|
||||
|
||||
// Terminate implements the exec driver Driver interface.
|
||||
func (d *Driver) Terminate(p *execdriver.Command) error {
|
||||
return kill(p.ID, p.ContainerPid, syscall.SIGTERM)
|
||||
logrus.Debugf("WindowsExec: Terminate() id=%s", p.ID)
|
||||
return kill(p.ID, p.ContainerPid)
|
||||
}
|
||||
|
||||
// Kill implements the exec driver Driver interface.
|
||||
func (d *Driver) Kill(p *execdriver.Command, sig int) error {
|
||||
return kill(p.ID, p.ContainerPid, syscall.Signal(sig))
|
||||
logrus.Debugf("WindowsExec: Kill() id=%s sig=%d", p.ID, sig)
|
||||
return kill(p.ID, p.ContainerPid)
|
||||
}
|
||||
|
||||
func kill(id string, pid int, sig syscall.Signal) error {
|
||||
logrus.Debugf("WindowsExec: kill() id=%s pid=%d sig=%d", id, pid, sig)
|
||||
func kill(id string, pid int) error {
|
||||
logrus.Debugln("kill() ", id, pid)
|
||||
var err error
|
||||
context := fmt.Sprintf("kill: sig=%d pid=%d", sig, pid)
|
||||
|
||||
if sig == syscall.SIGKILL || forceKill {
|
||||
// Terminate Process
|
||||
if err = hcsshim.TerminateProcessInComputeSystem(id, uint32(pid)); err != nil {
|
||||
logrus.Warnf("Failed to terminate pid %d in %s: %q", pid, id, err)
|
||||
// Ignore errors
|
||||
err = nil
|
||||
}
|
||||
|
||||
if terminateMode {
|
||||
// Terminate the compute system
|
||||
if errno, err := hcsshim.TerminateComputeSystem(id, hcsshim.TimeoutInfinite, context); err != nil {
|
||||
logrus.Errorf("Failed to terminate %s - 0x%X %q", id, errno, err)
|
||||
if err = hcsshim.TerminateComputeSystem(id); err != nil {
|
||||
logrus.Errorf("Failed to terminate %s - %q", id, err)
|
||||
}
|
||||
|
||||
} else {
|
||||
// Terminate Process
|
||||
if err = hcsshim.TerminateProcessInComputeSystem(id, uint32(pid)); err != nil {
|
||||
logrus.Warnf("Failed to terminate pid %d in %s: %q", pid, id, err)
|
||||
// Ignore errors
|
||||
err = nil
|
||||
}
|
||||
|
||||
// Shutdown the compute system
|
||||
if errno, err := hcsshim.ShutdownComputeSystem(id, hcsshim.TimeoutInfinite, context); err != nil {
|
||||
logrus.Errorf("Failed to shutdown %s - 0x%X %q", id, errno, err)
|
||||
if err = hcsshim.ShutdownComputeSystem(id); err != nil {
|
||||
logrus.Errorf("Failed to shutdown %s - %q", id, err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
|
||||
@@ -18,8 +18,7 @@ import (
|
||||
var dummyMode bool
|
||||
|
||||
// This allows the daemon to terminate containers rather than shutdown
|
||||
// This allows the daemon to force kill (HCS terminate) rather than shutdown
|
||||
var forceKill bool
|
||||
var terminateMode bool
|
||||
|
||||
// Define name and version for windows
|
||||
var (
|
||||
@@ -63,11 +62,11 @@ func NewDriver(root, initPath string, options []string) (*Driver, error) {
|
||||
logrus.Warn("Using dummy mode in Windows exec driver. This is for development use only!")
|
||||
}
|
||||
|
||||
case "forcekill":
|
||||
case "terminate":
|
||||
switch val {
|
||||
case "1":
|
||||
forceKill = true
|
||||
logrus.Warn("Using force kill mode in Windows exec driver. This is for testing purposes only.")
|
||||
terminateMode = true
|
||||
logrus.Warn("Using terminate mode in Windows exec driver. This is for testing purposes only.")
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package devmapper
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -148,6 +149,8 @@ type Status struct {
|
||||
Metadata DiskUsage
|
||||
// BaseDeviceSize is base size of container and image
|
||||
BaseDeviceSize uint64
|
||||
// BaseDeviceFS is backing filesystem.
|
||||
BaseDeviceFS string
|
||||
// SectorSize size of the vector.
|
||||
SectorSize uint64
|
||||
// UdevSyncSupported is true if sync is supported.
|
||||
@@ -532,6 +535,45 @@ func (devices *DeviceSet) activateDeviceIfNeeded(info *devInfo, ignoreDeleted bo
|
||||
return devicemapper.ActivateDevice(devices.getPoolDevName(), info.Name(), info.DeviceID, info.Size)
|
||||
}
|
||||
|
||||
// Return true only if kernel supports xfs and mkfs.xfs is available
|
||||
func xfsSupported() bool {
|
||||
// Make sure mkfs.xfs is available
|
||||
if _, err := exec.LookPath("mkfs.xfs"); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if kernel supports xfs filesystem or not.
|
||||
exec.Command("modprobe", "xfs").Run()
|
||||
|
||||
f, err := os.Open("/proc/filesystems")
|
||||
if err != nil {
|
||||
logrus.Warnf("Could not check if xfs is supported: %v", err)
|
||||
return false
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
if strings.HasSuffix(s.Text(), "\txfs") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.Err(); err != nil {
|
||||
logrus.Warnf("Could not check if xfs is supported: %v", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func determineDefaultFS() string {
|
||||
if xfsSupported() {
|
||||
return "xfs"
|
||||
}
|
||||
|
||||
logrus.Warn("XFS is not supported in your system. Either the kernel doesnt support it or mkfs.xfs is not in your PATH. Defaulting to ext4 filesystem")
|
||||
return "ext4"
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) createFilesystem(info *devInfo) error {
|
||||
devname := info.DevName()
|
||||
|
||||
@@ -543,6 +585,11 @@ func (devices *DeviceSet) createFilesystem(info *devInfo) error {
|
||||
args = append(args, devname)
|
||||
|
||||
var err error
|
||||
|
||||
if devices.filesystem == "" {
|
||||
devices.filesystem = determineDefaultFS()
|
||||
}
|
||||
|
||||
switch devices.filesystem {
|
||||
case "xfs":
|
||||
err = exec.Command("mkfs.xfs", args...).Run()
|
||||
@@ -599,6 +646,7 @@ func (devices *DeviceSet) cleanupDeletedDevices() error {
|
||||
|
||||
// If there are no deleted devices, there is nothing to do.
|
||||
if devices.nrDeletedDevices == 0 {
|
||||
devices.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -843,7 +891,11 @@ func (devices *DeviceSet) getBaseDeviceSize() uint64 {
|
||||
return info.Size
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) verifyBaseDeviceUUID(baseInfo *devInfo) error {
|
||||
func (devices *DeviceSet) getBaseDeviceFS() string {
|
||||
return devices.filesystem
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) verifyBaseDeviceUUIDFS(baseInfo *devInfo) error {
|
||||
devices.Lock()
|
||||
defer devices.Unlock()
|
||||
|
||||
@@ -859,9 +911,23 @@ func (devices *DeviceSet) verifyBaseDeviceUUID(baseInfo *devInfo) error {
|
||||
}
|
||||
|
||||
if devices.BaseDeviceUUID != uuid {
|
||||
return fmt.Errorf("Current Base Device UUID:%s does not match with stored UUID:%s", uuid, devices.BaseDeviceUUID)
|
||||
return fmt.Errorf("Current Base Device UUID:%s does not match with stored UUID:%s. Possibly using a different thin pool than last invocation", uuid, devices.BaseDeviceUUID)
|
||||
}
|
||||
|
||||
// If user specified a filesystem using dm.fs option and current
|
||||
// file system of base image is not same, warn user that dm.fs
|
||||
// will be ignored.
|
||||
if devices.filesystem != "" {
|
||||
fs, err := ProbeFsType(baseInfo.DevName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fs != devices.filesystem {
|
||||
logrus.Warnf("Base device already exists and has filesystem %s on it. User specified filesystem %s will be ignored.", fs, devices.filesystem)
|
||||
devices.filesystem = fs
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -962,7 +1028,7 @@ func (devices *DeviceSet) checkThinPool() error {
|
||||
|
||||
// Base image is initialized properly. Either save UUID for first time (for
|
||||
// upgrade case or verify UUID.
|
||||
func (devices *DeviceSet) setupVerifyBaseImageUUID(baseInfo *devInfo) error {
|
||||
func (devices *DeviceSet) setupVerifyBaseImageUUIDFS(baseInfo *devInfo) error {
|
||||
// If BaseDeviceUUID is nil (upgrade case), save it and return success.
|
||||
if devices.BaseDeviceUUID == "" {
|
||||
if err := devices.saveBaseDeviceUUID(baseInfo); err != nil {
|
||||
@@ -971,8 +1037,8 @@ func (devices *DeviceSet) setupVerifyBaseImageUUID(baseInfo *devInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := devices.verifyBaseDeviceUUID(baseInfo); err != nil {
|
||||
return fmt.Errorf("Base Device UUID verification failed. Possibly using a different thin pool than last invocation:%v", err)
|
||||
if err := devices.verifyBaseDeviceUUIDFS(baseInfo); err != nil {
|
||||
return fmt.Errorf("Base Device UUID and Filesystem verification failed.%v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -987,7 +1053,7 @@ func (devices *DeviceSet) setupBaseImage() error {
|
||||
|
||||
if oldInfo != nil {
|
||||
if oldInfo.Initialized && !oldInfo.Deleted {
|
||||
if err := devices.setupVerifyBaseImageUUID(oldInfo); err != nil {
|
||||
if err := devices.setupVerifyBaseImageUUIDFS(oldInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2209,6 +2275,7 @@ func (devices *DeviceSet) Status() *Status {
|
||||
status.DeferredDeleteEnabled = devices.deferredDelete
|
||||
status.DeferredDeletedDeviceCount = devices.nrDeletedDevices
|
||||
status.BaseDeviceSize = devices.getBaseDeviceSize()
|
||||
status.BaseDeviceFS = devices.getBaseDeviceFS()
|
||||
|
||||
totalSizeInSectors, _, dataUsed, dataTotal, metadataUsed, metadataTotal, err := devices.poolStatus()
|
||||
if err == nil {
|
||||
@@ -2269,7 +2336,6 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [
|
||||
metaDataLoopbackSize: defaultMetaDataLoopbackSize,
|
||||
baseFsSize: defaultBaseFsSize,
|
||||
overrideUdevSyncCheck: defaultUdevSyncOverride,
|
||||
filesystem: "ext4",
|
||||
doBlkDiscard: true,
|
||||
thinpBlockSize: defaultThinpBlockSize,
|
||||
deviceIDMap: make([]byte, deviceIDMapSz),
|
||||
|
||||
@@ -5,6 +5,7 @@ package devmapper
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/daemon/graphdriver"
|
||||
"github.com/docker/docker/daemon/graphdriver/graphtest"
|
||||
@@ -79,3 +80,31 @@ func testChangeLoopBackSize(t *testing.T, delta, expectDataSize, expectMetaDataS
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure devices.Lock() has been release upon return from cleanupDeletedDevices() function
|
||||
func TestDevmapperLockReleasedDeviceDeletion(t *testing.T) {
|
||||
driver := graphtest.GetDriver(t, "devicemapper").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver)
|
||||
defer graphtest.PutDriver(t)
|
||||
|
||||
// Call cleanupDeletedDevices() and after the call take and release
|
||||
// DeviceSet Lock. If lock has not been released, this will hang.
|
||||
driver.DeviceSet.cleanupDeletedDevices()
|
||||
|
||||
doneChan := make(chan bool)
|
||||
|
||||
go func() {
|
||||
driver.DeviceSet.Lock()
|
||||
defer driver.DeviceSet.Unlock()
|
||||
doneChan <- true
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(time.Second * 5):
|
||||
// Timer expired. That means lock was not released upon
|
||||
// function return and we are deadlocked. Release lock
|
||||
// here so that cleanup could succeed and fail the test.
|
||||
driver.DeviceSet.Unlock()
|
||||
t.Fatalf("Could not acquire devices lock after call to cleanupDeletedDevices()")
|
||||
case <-doneChan:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func (d *Driver) Status() [][2]string {
|
||||
{"Pool Name", s.PoolName},
|
||||
{"Pool Blocksize", fmt.Sprintf("%s", units.HumanSize(float64(s.SectorSize)))},
|
||||
{"Base Device Size", fmt.Sprintf("%s", units.HumanSize(float64(s.BaseDeviceSize)))},
|
||||
{"Backing Filesystem", backingFs},
|
||||
{"Backing Filesystem", s.BaseDeviceFS},
|
||||
{"Data file", s.DataFile},
|
||||
{"Metadata file", s.MetadataFile},
|
||||
{"Data Space Used", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Used)))},
|
||||
|
||||
@@ -84,6 +84,11 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
|
||||
daemon.EventsService.Log("untag", img.ID, "")
|
||||
records = append(records, untaggedRecord)
|
||||
|
||||
// If has remaining references then untag finishes the remove
|
||||
if daemon.repositories.HasReferences(img) {
|
||||
return records, nil
|
||||
}
|
||||
|
||||
removedRepositoryRef = true
|
||||
} else {
|
||||
// If an ID reference was given AND there is exactly one
|
||||
@@ -279,7 +284,7 @@ func (daemon *Daemon) checkImageDeleteHardConflict(img *image.Image) *imageDelet
|
||||
}
|
||||
|
||||
// Check if the image has any descendent images.
|
||||
if daemon.Graph().HasChildren(img) {
|
||||
if daemon.Graph().HasChildren(img.ID) {
|
||||
return &imageDeleteConflict{
|
||||
hard: true,
|
||||
imgID: img.ID,
|
||||
@@ -337,5 +342,5 @@ func (daemon *Daemon) checkImageDeleteSoftConflict(img *image.Image) *imageDelet
|
||||
// that there are no repository references to the given image and it has no
|
||||
// child images.
|
||||
func (daemon *Daemon) imageIsDangling(img *image.Image) bool {
|
||||
return !(daemon.repositories.HasReferences(img) || daemon.Graph().HasChildren(img))
|
||||
return !(daemon.repositories.HasReferences(img) || daemon.Graph().HasChildren(img.ID))
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
|
||||
ExperimentalBuild: utils.ExperimentalBuild(),
|
||||
ServerVersion: dockerversion.VERSION,
|
||||
ClusterStore: daemon.config().ClusterStore,
|
||||
ClusterAdvertise: daemon.config().ClusterAdvertise,
|
||||
}
|
||||
|
||||
// TODO Windows. Refactor this more once sysinfo is refactored into
|
||||
|
||||
+74
-18
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/versions/v1p20"
|
||||
"github.com/docker/docker/daemon/network"
|
||||
)
|
||||
|
||||
// ContainerInspect returns low-level information about a
|
||||
@@ -26,8 +27,23 @@ func (daemon *Daemon) ContainerInspect(name string, size bool) (*types.Container
|
||||
}
|
||||
|
||||
mountPoints := addMountPoints(container)
|
||||
networkSettings := &types.NetworkSettings{
|
||||
NetworkSettingsBase: types.NetworkSettingsBase{
|
||||
Bridge: container.NetworkSettings.Bridge,
|
||||
SandboxID: container.NetworkSettings.SandboxID,
|
||||
HairpinMode: container.NetworkSettings.HairpinMode,
|
||||
LinkLocalIPv6Address: container.NetworkSettings.LinkLocalIPv6Address,
|
||||
LinkLocalIPv6PrefixLen: container.NetworkSettings.LinkLocalIPv6PrefixLen,
|
||||
Ports: container.NetworkSettings.Ports,
|
||||
SandboxKey: container.NetworkSettings.SandboxKey,
|
||||
SecondaryIPAddresses: container.NetworkSettings.SecondaryIPAddresses,
|
||||
SecondaryIPv6Addresses: container.NetworkSettings.SecondaryIPv6Addresses,
|
||||
},
|
||||
DefaultNetworkSettings: daemon.getDefaultNetworkSettings(container.NetworkSettings.Networks),
|
||||
Networks: container.NetworkSettings.Networks,
|
||||
}
|
||||
|
||||
return &types.ContainerJSON{base, mountPoints, container.Config}, nil
|
||||
return &types.ContainerJSON{base, mountPoints, container.Config, networkSettings}, nil
|
||||
}
|
||||
|
||||
// ContainerInspect120 serializes the master version of a container into a json type.
|
||||
@@ -48,10 +64,14 @@ func (daemon *Daemon) ContainerInspect120(name string) (*v1p20.ContainerJSON, er
|
||||
mountPoints := addMountPoints(container)
|
||||
config := &v1p20.ContainerConfig{
|
||||
container.Config,
|
||||
container.Config.MacAddress,
|
||||
container.Config.NetworkDisabled,
|
||||
container.Config.ExposedPorts,
|
||||
container.hostConfig.VolumeDriver,
|
||||
}
|
||||
networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings)
|
||||
|
||||
return &v1p20.ContainerJSON{base, mountPoints, config}, nil
|
||||
return &v1p20.ContainerJSON{base, mountPoints, config, networkSettings}, nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) getInspectData(container *Container, size bool) (*types.ContainerJSONBase, error) {
|
||||
@@ -88,22 +108,21 @@ func (daemon *Daemon) getInspectData(container *Container, size bool) (*types.Co
|
||||
}
|
||||
|
||||
contJSONBase := &types.ContainerJSONBase{
|
||||
ID: container.ID,
|
||||
Created: container.Created.Format(time.RFC3339Nano),
|
||||
Path: container.Path,
|
||||
Args: container.Args,
|
||||
State: containerState,
|
||||
Image: container.ImageID,
|
||||
NetworkSettings: container.NetworkSettings,
|
||||
LogPath: container.LogPath,
|
||||
Name: container.Name,
|
||||
RestartCount: container.RestartCount,
|
||||
Driver: container.Driver,
|
||||
ExecDriver: container.ExecDriver,
|
||||
MountLabel: container.MountLabel,
|
||||
ProcessLabel: container.ProcessLabel,
|
||||
ExecIDs: container.getExecIDs(),
|
||||
HostConfig: &hostConfig,
|
||||
ID: container.ID,
|
||||
Created: container.Created.Format(time.RFC3339Nano),
|
||||
Path: container.Path,
|
||||
Args: container.Args,
|
||||
State: containerState,
|
||||
Image: container.ImageID,
|
||||
LogPath: container.LogPath,
|
||||
Name: container.Name,
|
||||
RestartCount: container.RestartCount,
|
||||
Driver: container.Driver,
|
||||
ExecDriver: container.ExecDriver,
|
||||
MountLabel: container.MountLabel,
|
||||
ProcessLabel: container.ProcessLabel,
|
||||
ExecIDs: container.getExecIDs(),
|
||||
HostConfig: &hostConfig,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -148,3 +167,40 @@ func (daemon *Daemon) VolumeInspect(name string) (*types.Volume, error) {
|
||||
}
|
||||
return volumeToAPIType(v), nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) getBackwardsCompatibleNetworkSettings(settings *network.Settings) *v1p20.NetworkSettings {
|
||||
result := &v1p20.NetworkSettings{
|
||||
NetworkSettingsBase: types.NetworkSettingsBase{
|
||||
Bridge: settings.Bridge,
|
||||
SandboxID: settings.SandboxID,
|
||||
HairpinMode: settings.HairpinMode,
|
||||
LinkLocalIPv6Address: settings.LinkLocalIPv6Address,
|
||||
LinkLocalIPv6PrefixLen: settings.LinkLocalIPv6PrefixLen,
|
||||
Ports: settings.Ports,
|
||||
SandboxKey: settings.SandboxKey,
|
||||
SecondaryIPAddresses: settings.SecondaryIPAddresses,
|
||||
SecondaryIPv6Addresses: settings.SecondaryIPv6Addresses,
|
||||
},
|
||||
DefaultNetworkSettings: daemon.getDefaultNetworkSettings(settings.Networks),
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// getDefaultNetworkSettings creates the deprecated structure that holds the information
|
||||
// about the bridge network for a container.
|
||||
func (daemon *Daemon) getDefaultNetworkSettings(networks map[string]*network.EndpointSettings) types.DefaultNetworkSettings {
|
||||
var settings types.DefaultNetworkSettings
|
||||
|
||||
if defaultNetwork, ok := networks["bridge"]; ok {
|
||||
settings.EndpointID = defaultNetwork.EndpointID
|
||||
settings.Gateway = defaultNetwork.Gateway
|
||||
settings.GlobalIPv6Address = defaultNetwork.GlobalIPv6Address
|
||||
settings.GlobalIPv6PrefixLen = defaultNetwork.GlobalIPv6PrefixLen
|
||||
settings.IPAddress = defaultNetwork.IPAddress
|
||||
settings.IPPrefixLen = defaultNetwork.IPPrefixLen
|
||||
settings.IPv6Gateway = defaultNetwork.IPv6Gateway
|
||||
settings.MacAddress = defaultNetwork.MacAddress
|
||||
}
|
||||
return settings
|
||||
}
|
||||
|
||||
@@ -41,14 +41,18 @@ func (daemon *Daemon) ContainerInspectPre120(name string) (*v1p19.ContainerJSON,
|
||||
|
||||
config := &v1p19.ContainerConfig{
|
||||
container.Config,
|
||||
container.Config.MacAddress,
|
||||
container.Config.NetworkDisabled,
|
||||
container.Config.ExposedPorts,
|
||||
container.hostConfig.VolumeDriver,
|
||||
container.hostConfig.Memory,
|
||||
container.hostConfig.MemorySwap,
|
||||
container.hostConfig.CPUShares,
|
||||
container.hostConfig.CpusetCpus,
|
||||
}
|
||||
networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings)
|
||||
|
||||
return &v1p19.ContainerJSON{base, volumes, volumesRW, config}, nil
|
||||
return &v1p19.ContainerJSON{base, volumes, volumesRW, config, networkSettings}, nil
|
||||
}
|
||||
|
||||
func addMountPoints(container *Container) []types.MountPoint {
|
||||
|
||||
+1
-11
@@ -1,12 +1,6 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
)
|
||||
import "syscall"
|
||||
|
||||
// ContainerKill send signal to the container
|
||||
// If no signal is given (sig 0), then Kill with SIGKILL and wait
|
||||
@@ -18,10 +12,6 @@ func (daemon *Daemon) ContainerKill(name string, sig uint64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if sig != 0 && !signal.ValidSignalForPlatform(syscall.Signal(sig)) {
|
||||
return fmt.Errorf("The %s daemon does not support signal %d", runtime.GOOS, sig)
|
||||
}
|
||||
|
||||
// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
|
||||
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
|
||||
if err := container.Kill(); err != nil {
|
||||
|
||||
@@ -142,6 +142,32 @@ func (m *containerMonitor) Start() error {
|
||||
|
||||
m.lastStartTime = time.Now()
|
||||
|
||||
// Make the network settings available to the execution
|
||||
// driver to allow for integration with libnetwork networking.
|
||||
m.container.command.NetworkSettings = m.container.NetworkSettings
|
||||
|
||||
// Allow the execution driver to query memory limits
|
||||
m.container.command.HostConfig = m.container.hostConfig
|
||||
|
||||
// Make the network endpoint details available to the execution
|
||||
// driver, to allow it to determine the bridge name.
|
||||
for name, _ := range m.container.NetworkSettings.Networks {
|
||||
n, _err := m.container.daemon.netController.NetworkByName(name)
|
||||
if _err == nil {
|
||||
var eps []map[string]interface{}
|
||||
|
||||
for _, ep := range n.Endpoints() {
|
||||
info, err := ep.DriverInfo()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
eps = append(eps, info)
|
||||
}
|
||||
|
||||
m.container.command.EndpointInfo = eps
|
||||
}
|
||||
}
|
||||
|
||||
if exitStatus, err = m.container.daemon.run(m.container, pipes, m.callback); err != nil {
|
||||
// if we receive an internal error from the initial start of a container then lets
|
||||
// return it instead of entering the restart loop
|
||||
|
||||
+7
-24
@@ -17,6 +17,12 @@ const (
|
||||
NetworkByName
|
||||
)
|
||||
|
||||
// NetworkControllerEnabled checks if the networking stack is enabled.
|
||||
// This feature depends on OS primitives and it's dissabled in systems like Windows.
|
||||
func (daemon *Daemon) NetworkControllerEnabled() bool {
|
||||
return daemon.netController != nil
|
||||
}
|
||||
|
||||
// FindNetwork function finds a network for a given string that can represent network name or id
|
||||
func (daemon *Daemon) FindNetwork(idName string) (libnetwork.Network, error) {
|
||||
// Find by Name
|
||||
@@ -93,9 +99,7 @@ func (daemon *Daemon) CreateNetwork(name, driver string, ipam network.IPAM, opti
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ipam.Config) > 0 {
|
||||
nwOptions = append(nwOptions, libnetwork.NetworkOptionIpam(ipam.Driver, "", v4Conf, v6Conf))
|
||||
}
|
||||
nwOptions = append(nwOptions, libnetwork.NetworkOptionIpam(ipam.Driver, "", v4Conf, v6Conf))
|
||||
nwOptions = append(nwOptions, libnetwork.NetworkOptionDriverOpts(options))
|
||||
return c.NewNetwork(driver, name, nwOptions...)
|
||||
}
|
||||
@@ -121,24 +125,3 @@ func getIpamConfig(data []network.IPAMConfig) ([]*libnetwork.IpamConf, []*libnet
|
||||
}
|
||||
return ipamV4Cfg, ipamV6Cfg, nil
|
||||
}
|
||||
|
||||
// ConnectContainerToNetwork connects the given container to the given
|
||||
// network. If either cannot be found, an err is returned. If the
|
||||
// network cannot be set up, an err is returned.
|
||||
func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName string) error {
|
||||
container, err := daemon.Get(containerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return container.ConnectToNetwork(networkName)
|
||||
}
|
||||
|
||||
// DisconnectContainerFromNetwork disconnects the given container from
|
||||
// the given network. If either cannot be found, an err is returned.
|
||||
func (daemon *Daemon) DisconnectContainerFromNetwork(containerName string, network libnetwork.Network) error {
|
||||
container, err := daemon.Get(containerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return container.DisconnectFromNetwork(network)
|
||||
}
|
||||
|
||||
+20
-15
@@ -10,37 +10,42 @@ type Address struct {
|
||||
|
||||
// IPAM represents IP Address Management
|
||||
type IPAM struct {
|
||||
Driver string `json:"driver"`
|
||||
Config []IPAMConfig `json:"config"`
|
||||
Driver string
|
||||
Config []IPAMConfig
|
||||
}
|
||||
|
||||
// IPAMConfig represents IPAM configurations
|
||||
type IPAMConfig struct {
|
||||
Subnet string `json:"subnet,omitempty"`
|
||||
IPRange string `json:"ip_range,omitempty"`
|
||||
Gateway string `json:"gateway,omitempty"`
|
||||
AuxAddress map[string]string `json:"auxiliary_address,omitempty"`
|
||||
Subnet string `json:",omitempty"`
|
||||
IPRange string `json:",omitempty"`
|
||||
Gateway string `json:",omitempty"`
|
||||
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
|
||||
}
|
||||
|
||||
// Settings stores configuration details about the daemon network config
|
||||
// TODO Windows. Many of these fields can be factored out.,
|
||||
type Settings struct {
|
||||
Bridge string
|
||||
EndpointID string
|
||||
SandboxID string
|
||||
Gateway string
|
||||
GlobalIPv6Address string
|
||||
GlobalIPv6PrefixLen int
|
||||
HairpinMode bool
|
||||
IPAddress string
|
||||
IPPrefixLen int
|
||||
IPv6Gateway string
|
||||
LinkLocalIPv6Address string
|
||||
LinkLocalIPv6PrefixLen int
|
||||
MacAddress string
|
||||
Networks []string
|
||||
Networks map[string]*EndpointSettings
|
||||
Ports nat.PortMap
|
||||
SandboxKey string
|
||||
SecondaryIPAddresses []Address
|
||||
SecondaryIPv6Addresses []Address
|
||||
IsAnonymousEndpoint bool
|
||||
}
|
||||
|
||||
// EndpointSettings stores the network endpoint details
|
||||
type EndpointSettings struct {
|
||||
EndpointID string
|
||||
Gateway string
|
||||
IPAddress string
|
||||
IPPrefixLen int
|
||||
IPv6Gateway string
|
||||
GlobalIPv6Address string
|
||||
GlobalIPv6PrefixLen int
|
||||
MacAddress string
|
||||
}
|
||||
|
||||
+45
-10
@@ -1,18 +1,28 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/libnetwork"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ContainerRename changes the name of a container, using the oldName
|
||||
// to find the container. An error is returned if newName is already
|
||||
// reserved.
|
||||
func (daemon *Daemon) ContainerRename(oldName, newName string) error {
|
||||
var (
|
||||
err error
|
||||
sid string
|
||||
sb libnetwork.Sandbox
|
||||
container *Container
|
||||
)
|
||||
|
||||
if oldName == "" || newName == "" {
|
||||
return derr.ErrorCodeEmptyRename
|
||||
}
|
||||
|
||||
container, err := daemon.Get(oldName)
|
||||
container, err = daemon.Get(oldName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -27,19 +37,44 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
|
||||
|
||||
container.Name = newName
|
||||
|
||||
undo := func() {
|
||||
container.Name = oldName
|
||||
daemon.reserveName(container.ID, oldName)
|
||||
daemon.containerGraphDB.Delete(newName)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
container.Name = oldName
|
||||
daemon.reserveName(container.ID, oldName)
|
||||
daemon.containerGraphDB.Delete(newName)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := daemon.containerGraphDB.Delete(oldName); err != nil {
|
||||
undo()
|
||||
if err = daemon.containerGraphDB.Delete(oldName); err != nil {
|
||||
return derr.ErrorCodeRenameDelete.WithArgs(oldName, err)
|
||||
}
|
||||
|
||||
if err := container.toDisk(); err != nil {
|
||||
undo()
|
||||
if err = container.toDisk(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !container.Running {
|
||||
container.logEvent("rename")
|
||||
return nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
container.Name = oldName
|
||||
if e := container.toDisk(); e != nil {
|
||||
logrus.Errorf("%s: Failed in writing to Disk on rename failure: %v", container.ID, e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
sid = container.NetworkSettings.SandboxID
|
||||
sb, err = daemon.netController.SandboxByID(sid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = sb.Rename(strings.TrimPrefix(container.Name, "/"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+7
-58
@@ -8,8 +8,6 @@ import (
|
||||
"github.com/docker/docker/api/types/versions/v1p20"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
lntypes "github.com/docker/libnetwork/types"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
)
|
||||
|
||||
// ContainerStatsConfig holds information for configuring the runtime
|
||||
@@ -50,10 +48,6 @@ func (daemon *Daemon) ContainerStats(prefixOrName string, config *ContainerStats
|
||||
var preCPUStats types.CPUStats
|
||||
getStatJSON := func(v interface{}) *types.StatsJSON {
|
||||
update := v.(*execdriver.ResourceStats)
|
||||
// Retrieve the nw statistics from libnetwork and inject them in the Stats
|
||||
if nwStats, err := daemon.getNetworkStats(container); err == nil {
|
||||
update.Stats.Interfaces = nwStats
|
||||
}
|
||||
ss := convertStatsToAPITypes(update.Stats)
|
||||
ss.PreCPUStats = preCPUStats
|
||||
ss.MemoryStats.Limit = uint64(update.MemoryLimit)
|
||||
@@ -75,7 +69,8 @@ func (daemon *Daemon) ContainerStats(prefixOrName string, config *ContainerStats
|
||||
return nil
|
||||
}
|
||||
|
||||
statsJSON := getStatJSON(v)
|
||||
var statsJSON interface{}
|
||||
statsJSONPost120 := getStatJSON(v)
|
||||
if config.Version.LessThan("1.21") {
|
||||
var (
|
||||
rxBytes uint64
|
||||
@@ -87,7 +82,7 @@ func (daemon *Daemon) ContainerStats(prefixOrName string, config *ContainerStats
|
||||
txErrors uint64
|
||||
txDropped uint64
|
||||
)
|
||||
for _, v := range statsJSON.Networks {
|
||||
for _, v := range statsJSONPost120.Networks {
|
||||
rxBytes += v.RxBytes
|
||||
rxPackets += v.RxPackets
|
||||
rxErrors += v.RxErrors
|
||||
@@ -97,8 +92,8 @@ func (daemon *Daemon) ContainerStats(prefixOrName string, config *ContainerStats
|
||||
txErrors += v.TxErrors
|
||||
txDropped += v.TxDropped
|
||||
}
|
||||
statsJSONPre121 := &v1p20.StatsJSON{
|
||||
Stats: statsJSON.Stats,
|
||||
statsJSON = &v1p20.StatsJSON{
|
||||
Stats: statsJSONPost120.Stats,
|
||||
Network: types.NetworkStats{
|
||||
RxBytes: rxBytes,
|
||||
RxPackets: rxPackets,
|
||||
@@ -110,20 +105,8 @@ func (daemon *Daemon) ContainerStats(prefixOrName string, config *ContainerStats
|
||||
TxDropped: txDropped,
|
||||
},
|
||||
}
|
||||
|
||||
if !config.Stream && noStreamFirstFrame {
|
||||
// prime the cpu stats so they aren't 0 in the final output
|
||||
noStreamFirstFrame = false
|
||||
continue
|
||||
}
|
||||
|
||||
if err := enc.Encode(statsJSONPre121); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !config.Stream {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
statsJSON = statsJSONPost120
|
||||
}
|
||||
|
||||
if !config.Stream && noStreamFirstFrame {
|
||||
@@ -144,37 +127,3 @@ func (daemon *Daemon) ContainerStats(prefixOrName string, config *ContainerStats
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (daemon *Daemon) getNetworkStats(c *Container) ([]*libcontainer.NetworkInterface, error) {
|
||||
var list []*libcontainer.NetworkInterface
|
||||
|
||||
sb, err := daemon.netController.SandboxByID(c.NetworkSettings.SandboxID)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
stats, err := sb.Statistics()
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Convert libnetwork nw stats into libcontainer nw stats
|
||||
for ifName, ifStats := range stats {
|
||||
list = append(list, convertLnNetworkStats(ifName, ifStats))
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *libcontainer.NetworkInterface {
|
||||
n := &libcontainer.NetworkInterface{Name: name}
|
||||
n.RxBytes = stats.RxBytes
|
||||
n.RxPackets = stats.RxPackets
|
||||
n.RxErrors = stats.RxErrors
|
||||
n.RxDropped = stats.RxDropped
|
||||
n.TxBytes = stats.TxBytes
|
||||
n.TxPackets = stats.TxPackets
|
||||
n.TxErrors = stats.TxErrors
|
||||
n.TxDropped = stats.TxDropped
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/pubsub"
|
||||
lntypes "github.com/docker/libnetwork/types"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/system"
|
||||
)
|
||||
|
||||
@@ -118,6 +120,11 @@ func (s *statsCollector) run() {
|
||||
continue
|
||||
}
|
||||
stats.SystemUsage = systemUsage
|
||||
|
||||
// Retrieve the nw statistics from libnetwork and inject them in the Stats
|
||||
if nwStats, err := s.getNetworkStats(pair.container); err == nil {
|
||||
stats.Interfaces = nwStats
|
||||
}
|
||||
pair.publisher.Publish(stats)
|
||||
}
|
||||
}
|
||||
@@ -170,3 +177,37 @@ func (s *statsCollector) getSystemCPUUsage() (uint64, error) {
|
||||
}
|
||||
return 0, derr.ErrorCodeBadStatFormat
|
||||
}
|
||||
|
||||
func (s *statsCollector) getNetworkStats(c *Container) ([]*libcontainer.NetworkInterface, error) {
|
||||
var list []*libcontainer.NetworkInterface
|
||||
|
||||
sb, err := c.daemon.netController.SandboxByID(c.NetworkSettings.SandboxID)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
stats, err := sb.Statistics()
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Convert libnetwork nw stats into libcontainer nw stats
|
||||
for ifName, ifStats := range stats {
|
||||
list = append(list, convertLnNetworkStats(ifName, ifStats))
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *libcontainer.NetworkInterface {
|
||||
n := &libcontainer.NetworkInterface{Name: name}
|
||||
n.RxBytes = stats.RxBytes
|
||||
n.RxPackets = stats.RxPackets
|
||||
n.RxErrors = stats.RxErrors
|
||||
n.RxDropped = stats.RxDropped
|
||||
n.TxBytes = stats.TxBytes
|
||||
n.TxPackets = stats.TxPackets
|
||||
n.TxErrors = stats.TxErrors
|
||||
n.TxDropped = stats.TxDropped
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -365,9 +365,10 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc
|
||||
}
|
||||
}
|
||||
|
||||
shared := label.IsShared(bind.Mode)
|
||||
if err := label.Relabel(bind.Source, container.MountLabel, shared); err != nil {
|
||||
return err
|
||||
if label.RelabelNeeded(bind.Mode) {
|
||||
if err := label.Relabel(bind.Source, container.MountLabel, label.IsShared(bind.Mode)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
binds[bind.Destination] = true
|
||||
mountPoints[bind.Destination] = bind
|
||||
|
||||
+3
-1
@@ -210,6 +210,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
|
||||
}
|
||||
serverConfig = setPlatformServerConfig(serverConfig, cli.Config)
|
||||
|
||||
defaultHost := opts.DefaultHost
|
||||
if commonFlags.TLSOptions != nil {
|
||||
if !commonFlags.TLSOptions.InsecureSkipVerify {
|
||||
// server requires and verifies client's certificate
|
||||
@@ -220,6 +221,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
serverConfig.TLSConfig = tlsConfig
|
||||
defaultHost = opts.DefaultTLSHost
|
||||
}
|
||||
|
||||
if len(commonFlags.Hosts) == 0 {
|
||||
@@ -227,7 +229,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
|
||||
}
|
||||
for i := 0; i < len(commonFlags.Hosts); i++ {
|
||||
var err error
|
||||
if commonFlags.Hosts[i], err = opts.ParseHost(commonFlags.Hosts[i]); err != nil {
|
||||
if commonFlags.Hosts[i], err = opts.ParseHost(defaultHost, commonFlags.Hosts[i]); err != nil {
|
||||
logrus.Fatalf("error parsing -H %s : %v", commonFlags.Hosts[i], err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,42 +81,43 @@ On the Docker host (192.168.1.52) that Redis will run on:
|
||||
^D
|
||||
|
||||
# add redis ambassador
|
||||
$ docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 busybox sh
|
||||
$ docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 alpine:3.2 sh
|
||||
|
||||
In the `redis_ambassador` container, you can see the linked Redis
|
||||
containers `env`:
|
||||
|
||||
$ env
|
||||
/ # env
|
||||
REDIS_PORT=tcp://172.17.0.136:6379
|
||||
REDIS_PORT_6379_TCP_ADDR=172.17.0.136
|
||||
REDIS_NAME=/redis_ambassador/redis
|
||||
HOSTNAME=19d7adf4705e
|
||||
SHLVL=1
|
||||
HOME=/root
|
||||
REDIS_PORT_6379_TCP_PORT=6379
|
||||
HOME=/
|
||||
REDIS_PORT_6379_TCP_PROTO=tcp
|
||||
container=lxc
|
||||
REDIS_PORT_6379_TCP=tcp://172.17.0.136:6379
|
||||
TERM=xterm
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
PWD=/
|
||||
/ # exit
|
||||
|
||||
This environment is used by the ambassador `socat` script to expose Redis
|
||||
to the world (via the `-p 6379:6379` port mapping):
|
||||
|
||||
$ docker rm redis_ambassador
|
||||
$ sudo ./contrib/mkimage-unittest.sh
|
||||
$ docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 docker-ut sh
|
||||
|
||||
$ socat TCP4-LISTEN:6379,fork,reuseaddr TCP4:172.17.0.136:6379
|
||||
$ CMD="apk update && apk add socat && sh"
|
||||
$ docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 alpine:3.2 sh -c "$CMD"
|
||||
[...]
|
||||
/ # socat -t 100000000 TCP4-LISTEN:6379,fork,reuseaddr TCP4:172.17.0.136:6379
|
||||
|
||||
Now ping the Redis server via the ambassador:
|
||||
|
||||
Now go to a different server:
|
||||
|
||||
$ sudo ./contrib/mkimage-unittest.sh
|
||||
$ docker run -t -i --expose 6379 --name redis_ambassador docker-ut sh
|
||||
|
||||
$ socat TCP4-LISTEN:6379,fork,reuseaddr TCP4:192.168.1.52:6379
|
||||
$ CMD="apk update && apk add socat && sh"
|
||||
$ docker run -t -i --expose 6379 --name redis_ambassador alpine:3.2 sh -c "$CMD"
|
||||
[...]
|
||||
/ # socat -t 100000000 TCP4-LISTEN:6379,fork,reuseaddr TCP4:192.168.1.52:6379
|
||||
|
||||
And get the `redis-cli` image so we can talk over the ambassador bridge.
|
||||
|
||||
@@ -127,8 +128,8 @@ And get the `redis-cli` image so we can talk over the ambassador bridge.
|
||||
|
||||
## The svendowideit/ambassador Dockerfile
|
||||
|
||||
The `svendowideit/ambassador` image is a small `busybox` image with
|
||||
`socat` built in. When you start the container, it uses a small `sed`
|
||||
The `svendowideit/ambassador` image is based on the `alpine:3.2` image with
|
||||
`socat` installed. When you start the container, it uses a small `sed`
|
||||
script to parse out the (possibly multiple) link environment variables
|
||||
to set up the port forwarding. On the remote host, you need to set the
|
||||
variable using the `-e` command line option.
|
||||
@@ -139,19 +140,21 @@ Will forward the local `1234` port to the remote IP and port, in this
|
||||
case `192.168.1.52:6379`.
|
||||
|
||||
#
|
||||
#
|
||||
# first you need to build the docker-ut image
|
||||
# using ./contrib/mkimage-unittest.sh
|
||||
# then
|
||||
# docker build -t SvenDowideit/ambassador .
|
||||
# docker tag SvenDowideit/ambassador ambassador
|
||||
# do
|
||||
# docker build -t svendowideit/ambassador .
|
||||
# then to run it (on the host that has the real backend on it)
|
||||
# docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 ambassador
|
||||
# docker run -t -i -link redis:redis -name redis_ambassador -p 6379:6379 svendowideit/ambassador
|
||||
# on the remote host, you can set up another ambassador
|
||||
# docker run -t -i --name redis_ambassador --expose 6379 sh
|
||||
# docker run -t -i -name redis_ambassador -expose 6379 -e REDIS_PORT_6379_TCP=tcp://192.168.1.52:6379 svendowideit/ambassador sh
|
||||
# you can read more about this process at https://docs.docker.com/articles/ambassador_pattern_linking/
|
||||
|
||||
FROM docker-ut
|
||||
MAINTAINER SvenDowideit@home.org.au
|
||||
# use alpine because its a minimal image with a package manager.
|
||||
# prettymuch all that is needed is a container that has a functioning env and socat (or equivalent)
|
||||
FROM alpine:3.2
|
||||
MAINTAINER SvenDowideit@home.org.au
|
||||
|
||||
RUN apk update && \
|
||||
apk add socat && \
|
||||
rm -r /var/cache/
|
||||
|
||||
CMD env | grep _TCP= | sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/' | sh && top
|
||||
CMD env | grep _TCP= | sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \& wait/' | sh
|
||||
|
||||
@@ -59,7 +59,7 @@ in a database image.
|
||||
In almost all cases, you should only run a single process in a single
|
||||
container. Decoupling applications into multiple containers makes it much
|
||||
easier to scale horizontally and reuse containers. If that service depends on
|
||||
another service, make use of [container linking](../userguide/dockerlinks.md).
|
||||
another service, make use of [container linking](../userguide/networking/default_network/dockerlinks.md).
|
||||
|
||||
### Minimize the number of layers
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ title = "Automatically start containers"
|
||||
description = "How to generate scripts for upstart, systemd, etc."
|
||||
keywords = ["systemd, upstart, supervisor, docker, documentation, host integration"]
|
||||
[menu.main]
|
||||
parent = "smn_containers"
|
||||
weight = 99
|
||||
parent = "smn_administrate"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -39,7 +39,7 @@ of another container. Of course, if the host system is setup
|
||||
accordingly, containers can interact with each other through their
|
||||
respective network interfaces — just like they can interact with
|
||||
external hosts. When you specify public ports for your containers or use
|
||||
[*links*](../userguide/dockerlinks.md)
|
||||
[*links*](../userguide/networking/default_network/dockerlinks.md)
|
||||
then IP traffic is allowed between containers. They can ping each other,
|
||||
send/receive UDP packets, and establish TCP connections, but that can be
|
||||
restricted if necessary. From a network architecture point of view, all
|
||||
@@ -129,7 +129,7 @@ privilege separation.
|
||||
|
||||
Eventually, it is expected that the Docker daemon will run restricted
|
||||
privileges, delegating operations well-audited sub-processes,
|
||||
each with its own (very limited) scope of Linux capabilities,
|
||||
each with its own (very limited) scope of Linux capabilities,
|
||||
virtual network setup, filesystem management, etc. That is, most likely,
|
||||
pieces of the Docker engine itself will run inside of containers.
|
||||
|
||||
|
||||
@@ -172,6 +172,6 @@ the exposed port to two different ports on the host
|
||||
$ mongo --port 28001
|
||||
$ mongo --port 28002
|
||||
|
||||
- [Linking containers](../userguide/dockerlinks.md)
|
||||
- [Linking containers](../userguide/networking/default_network/dockerlinks.md)
|
||||
- [Cross-host linking containers](../articles/ambassador_pattern_linking.md)
|
||||
- [Creating an Automated Build](https://docs.docker.com/docker-hub/builds/)
|
||||
|
||||
@@ -10,7 +10,7 @@ parent = "smn_applied"
|
||||
|
||||
# Dockerizing PostgreSQL
|
||||
|
||||
> **Note**:
|
||||
> **Note**:
|
||||
> - **If you don't like sudo** then see [*Giving non-root
|
||||
> access*](../installation/binaries.md#giving-non-root-access)
|
||||
|
||||
@@ -85,7 +85,7 @@ And run the PostgreSQL server container (in the foreground):
|
||||
$ docker run --rm -P --name pg_test eg_postgresql
|
||||
|
||||
There are 2 ways to connect to the PostgreSQL server. We can use [*Link
|
||||
Containers*](../userguide/dockerlinks.md), or we can access it from our host
|
||||
Containers*](../userguide/networking/default_network/dockerlinks.md), or we can access it from our host
|
||||
(or the network).
|
||||
|
||||
> **Note**:
|
||||
|
||||
+16
-4
@@ -12,15 +12,15 @@ weight=-1
|
||||
# Understand Docker plugins
|
||||
|
||||
You can extend the capabilities of the Docker Engine by loading third-party
|
||||
plugins.
|
||||
plugins. This page explains the types of plugins and provides links to several
|
||||
volume and network plugins for Docker.
|
||||
|
||||
## Types of plugins
|
||||
|
||||
Plugins extend Docker's functionality. They come in specific types. For
|
||||
example, a [volume plugin](plugins_volume.md) might enable Docker
|
||||
volumes to persist across multiple Docker hosts and a
|
||||
[network plugin](plugins_network.md) might provide network plumbing
|
||||
using a favorite networking technology, such as vxlan overlay, ipvlan, EVPN, etc.
|
||||
volumes to persist across multiple Docker hosts and a
|
||||
[network plugin](plugins_network.md) might provide network plumbing.
|
||||
|
||||
Currently Docker supports volume and network driver plugins. In the future it
|
||||
will support additional plugin types.
|
||||
@@ -65,6 +65,18 @@ The following plugins exist:
|
||||
which is written in Go and provides advanced storage functionality for many
|
||||
platforms including EC2, OpenStack, XtremIO, and ScaleIO.
|
||||
|
||||
* The [Contiv Volume Plugin](https://github.com/contiv/volplugin) is an open
|
||||
source volume plugin that provides multi-tenant, persistent, distributed storage
|
||||
with intent based consumption using ceph underneath.
|
||||
|
||||
* The [Contiv Networking](https://github.com/contiv/netplugin) is an open source
|
||||
libnetwork plugin to provide infrastructure and security policies for a
|
||||
multi-tenant micro services deployment, while providing an integration to
|
||||
physical network for non-container workload. Contiv Networking implements the
|
||||
remote driver and IPAM APIs available in Docker 1.9 onwards.
|
||||
|
||||
* The [Weave Network Plugin](https://github.com/weaveworks/docker-plugin) creates a virtual network that connects your Docker containers - across multiple hosts or clouds and enables automatic discovery of applications. Weave networks are resilient, partition tolerant, secure and work in partially connected networks, and other adverse environments - all configured with delightful simplicity.
|
||||
|
||||
## Troubleshooting a plugin
|
||||
|
||||
If you are having problems with Docker after loading a plugin, ask the authors
|
||||
|
||||
@@ -1,51 +1,57 @@
|
||||
<!--[metadata]>
|
||||
+++
|
||||
title = "Docker network driver plugins"
|
||||
description = "Network drive plugins."
|
||||
description = "Network driver plugins."
|
||||
keywords = ["Examples, Usage, plugins, docker, documentation, user guide"]
|
||||
[menu.main]
|
||||
parent = "mn_extend"
|
||||
weight=-1
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Docker network driver plugins
|
||||
|
||||
Docker supports network driver plugins via
|
||||
[LibNetwork](https://github.com/docker/libnetwork). Network driver plugins are
|
||||
implemented as "remote drivers" for LibNetwork, which shares plugin
|
||||
infrastructure with Docker. In effect this means that network driver plugins
|
||||
are activated in the same way as other plugins, and use the same kind of
|
||||
protocol.
|
||||
Docker network plugins enable Docker deployments to be extended to support a
|
||||
wide range of networking technologies, such as VXLAN, IPVLAN, MACVLAN or
|
||||
something completely different. Network driver plugins are supported via the
|
||||
LibNetwork project. Each plugin is implemented asa "remote driver" for
|
||||
LibNetwork, which shares plugin infrastructure with Docker. Effectively,
|
||||
network driver plugins are activated in the same way as other plugins, and use
|
||||
the same kind of protocol.
|
||||
|
||||
## Using network driver plugins
|
||||
|
||||
The means of installing and running a network driver plugin will depend on the
|
||||
particular plugin.
|
||||
The means of installing and running a network driver plugin depend on the
|
||||
particular plugin. So, be sure to install your plugin according to the
|
||||
instructions obtained from the plugin developer.
|
||||
|
||||
Once running however, network driver plugins are used just like the built-in
|
||||
network drivers: by being mentioned as a driver in network-oriented Docker
|
||||
commands. For example,
|
||||
|
||||
docker network create -d weave mynet
|
||||
$ docker network create --driver weave mynet
|
||||
|
||||
Some network driver plugins are listed in [plugins](plugins.md)
|
||||
|
||||
The network thus created is owned by the plugin, so subsequent commands
|
||||
referring to that network will also be run through the plugin such as,
|
||||
The `mynet` network is now owned by `weave`, so subsequent commands
|
||||
referring to that network will be sent to the plugin,
|
||||
|
||||
docker run --net=mynet busybox top
|
||||
$ docker run --net=mynet busybox top
|
||||
|
||||
## Network driver plugin protocol
|
||||
|
||||
The network driver protocol, additional to the plugin activation call, is
|
||||
documented as part of LibNetwork:
|
||||
## Write a network plugin
|
||||
|
||||
Network plugins implement the [Docker plugin
|
||||
API](https://docs.docker.com/extend/plugin_api/) and the network plugin protocol
|
||||
|
||||
## Network plugin protocol
|
||||
|
||||
The network driver protocol, in addition to the plugin activation call, is
|
||||
documented as part of libnetwork:
|
||||
[https://github.com/docker/libnetwork/blob/master/docs/remote.md](https://github.com/docker/libnetwork/blob/master/docs/remote.md).
|
||||
|
||||
# Related GitHub PRs and issues
|
||||
# Related Information
|
||||
|
||||
Please record your feedback in the following issue, on the usual
|
||||
Google Groups, or the IRC channel #docker-network.
|
||||
To interact with the Docker maintainers and other interested users, se the IRC channel `#docker-network`.
|
||||
|
||||
- [#14083](https://github.com/docker/docker/issues/14083) Feedback on
|
||||
experimental networking features
|
||||
- [Docker networks feature overview](../userguide/networking/index.md)
|
||||
- The [LibNetwork](https://github.com/docker/libnetwork) project
|
||||
|
||||
@@ -109,8 +109,8 @@ You must delete the user created configuration files manually.
|
||||
|
||||
## Where to go from here
|
||||
|
||||
You can find more details about Docker on openSUSE or SUSE Linux Enterprise in
|
||||
the [Docker quick start guide](https://www.suse.com/documentation/sles-12/dockerquick/data/dockerquick.
|
||||
html) on the SUSE website. The document targets SUSE Linux Enterprise, but its contents apply also to openSUSE.
|
||||
You can find more details about Docker on openSUSE or SUSE Linux Enterprise in the
|
||||
[Docker quick start guide](https://www.suse.com/documentation/sles-12/dockerquick/data/dockerquick.html)
|
||||
on the SUSE website. The document targets SUSE Linux Enterprise, but its contents apply also to openSUSE.
|
||||
|
||||
Continue to the [User Guide](../userguide/).
|
||||
|
||||
@@ -51,7 +51,7 @@ from the new repository:
|
||||
|
||||
4. Add the new `gpg` key.
|
||||
|
||||
$ apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
|
||||
$ apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
|
||||
|
||||
5. Open the `/etc/apt/sources.list.d/docker.list` file in your favorite editor.
|
||||
|
||||
|
||||
@@ -49,13 +49,13 @@ your `apt` sources to the new Docker repository.
|
||||
Docker's `apt` repository contains Docker 1.7.1 and higher. To set `apt` to use
|
||||
packages from the new repository:
|
||||
|
||||
1. If you haven't already done so, log into your Ubuntu instance.
|
||||
1. If you haven't already done so, log into your Ubuntu instance as a privileged user.
|
||||
|
||||
2. Open a terminal window.
|
||||
|
||||
3. Add the new `gpg` key.
|
||||
|
||||
$ apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
|
||||
$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
|
||||
|
||||
4. Open the `/etc/apt/sources.list.d/docker.list` file in your favorite editor.
|
||||
|
||||
@@ -67,13 +67,13 @@ packages from the new repository:
|
||||
|
||||
The possible entries are:
|
||||
|
||||
# Ubuntu Precise
|
||||
# Ubuntu Precise 12.04 (LTS)
|
||||
deb https://apt.dockerproject.org/repo ubuntu-precise main
|
||||
# Ubuntu Trusty
|
||||
# Ubuntu Trusty 14.04 (LTS)
|
||||
deb https://apt.dockerproject.org/repo ubuntu-trusty main
|
||||
# Ubuntu Vivid
|
||||
# Ubuntu Vivid 15.04
|
||||
deb https://apt.dockerproject.org/repo ubuntu-vivid main
|
||||
# Ubuntu Wily
|
||||
# Ubuntu Wily 15.10
|
||||
deb https://apt.dockerproject.org/repo ubuntu-wily main
|
||||
|
||||
7. Save and close the `/etc/apt/sources.list.d/docker.list` file.
|
||||
@@ -94,12 +94,31 @@ packages from the new repository:
|
||||
|
||||
### Prerequisites by Ubuntu Version
|
||||
|
||||
The following Ubuntu versions have no additional prerequisites:
|
||||
|
||||
- Ubuntu Wily 15.10
|
||||
- Ubuntu Vivid 15.04
|
||||
- Ubuntu Trusty 14.04 (LTS)
|
||||
|
||||
For Ubuntu Trusty, Vivid, and Wily, it's recommended to install the
|
||||
`linux-image-extra` kernel package. The `linux-image-extra` package
|
||||
allows you use the `aufs` storage driver.
|
||||
|
||||
To install the `linux-image-extra` package for your kernel version:
|
||||
|
||||
1. Open a terminal on your Ubuntu host.
|
||||
|
||||
2. Update your package manager.
|
||||
|
||||
$ sudo apt-get update
|
||||
|
||||
3. Install the recommended package.
|
||||
|
||||
$ sudo apt-get install linux-image-extra-$(uname -r)
|
||||
|
||||
4. Go ahead and install Docker.
|
||||
|
||||
|
||||
#### Ubuntu Precise 12.04 (LTS)
|
||||
|
||||
For Ubuntu Precise, Docker requires the 3.13 kernel version. If your kernel
|
||||
version is older than 3.13, you must upgrade it. Refer to this table to see
|
||||
which packages are required for your environment:
|
||||
|
||||
@@ -44,7 +44,7 @@ The built-in LXC execution driver is deprecated for an external implementation.
|
||||
The lxc-conf flag and API fields will also be removed.
|
||||
|
||||
### Old Command Line Options
|
||||
**Deprecated In Release: [v1.8.0](../release-notes.md#docker-engine-1-8-0)**
|
||||
**Deprecated In Release: [v1.8.0](https://github.com/docker/docker/releases/tag/v1.8.0)**
|
||||
|
||||
**Target For Removal In Release: v1.10**
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user