mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-20 12:45:50 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6928729cf9 | |||
| 13c7967b94 | |||
| 7e5506d42d |
@@ -5,46 +5,6 @@ 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
|
||||
|
||||
@@ -58,7 +58,6 @@ RUN apt-get update && apt-get install -y \
|
||||
ruby1.9.1-dev \
|
||||
s3cmd=1.1.0* \
|
||||
ubuntu-zfs \
|
||||
xfsprogs \
|
||||
libzfs-dev \
|
||||
--no-install-recommends
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
e2fsprogs \
|
||||
iptables \
|
||||
procps \
|
||||
xfsprogs \
|
||||
xz-utils \
|
||||
\
|
||||
aufs-tools \
|
||||
|
||||
+8
-15
@@ -61,19 +61,14 @@ 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 {
|
||||
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
|
||||
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)
|
||||
}
|
||||
status = 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,9 +76,6 @@ 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)
|
||||
@@ -96,6 +88,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
||||
status = 1
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if tmpl == nil {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
@@ -34,11 +33,6 @@ 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,7 +2,6 @@ package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
@@ -12,11 +11,6 @@ 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
|
||||
@@ -42,10 +36,6 @@ 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")
|
||||
|
||||
@@ -66,9 +66,7 @@ func (s *router) getContainersStats(ctx context.Context, w http.ResponseWriter,
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
out = w
|
||||
} else {
|
||||
wf := ioutils.NewWriteFlusher(w)
|
||||
out = wf
|
||||
defer wf.Close()
|
||||
out = ioutils.NewWriteFlusher(w)
|
||||
}
|
||||
|
||||
var closeNotifier <-chan bool
|
||||
@@ -124,16 +122,11 @@ 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)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if flusher, ok := w.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
|
||||
output := ioutils.NewWriteFlusher(w)
|
||||
defer output.Close()
|
||||
outStream.Write(nil)
|
||||
|
||||
logsConfig := &daemon.ContainerLogsConfig{
|
||||
Follow: httputils.BoolValue(r, "follow"),
|
||||
@@ -142,7 +135,7 @@ func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
|
||||
Tail: r.Form.Get("tail"),
|
||||
UseStdout: stdout,
|
||||
UseStderr: stderr,
|
||||
OutStream: output,
|
||||
OutStream: outStream,
|
||||
Stop: closeNotifier,
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,6 @@ 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")
|
||||
|
||||
@@ -189,7 +188,6 @@ 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,
|
||||
@@ -220,7 +218,6 @@ 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}
|
||||
@@ -300,7 +297,6 @@ 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.
|
||||
|
||||
@@ -52,6 +52,16 @@ 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
|
||||
@@ -77,19 +87,7 @@ func (s *router) getEvents(ctx context.Context, w http.ResponseWriter, r *http.R
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
enc := buildOutputEncoder(w)
|
||||
d := s.daemon
|
||||
es := d.EventsService
|
||||
current, l := es.Subscribe()
|
||||
|
||||
@@ -199,11 +199,7 @@ func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
|
||||
|
||||
epl := nw.Endpoints()
|
||||
for _, e := range epl {
|
||||
ei := e.Info()
|
||||
if ei == nil {
|
||||
continue
|
||||
}
|
||||
sb := ei.Sandbox()
|
||||
sb := e.Info().Sandbox()
|
||||
if sb == nil {
|
||||
continue
|
||||
}
|
||||
@@ -245,12 +241,7 @@ func buildEndpointResource(e libnetwork.Endpoint) types.EndpointResource {
|
||||
}
|
||||
|
||||
er.EndpointID = e.ID()
|
||||
ei := e.Info()
|
||||
if ei == nil {
|
||||
return er
|
||||
}
|
||||
|
||||
if iface := ei.Iface(); iface != nil {
|
||||
if iface := e.Info().Iface(); iface != nil {
|
||||
if mac := iface.MacAddress(); mac != nil {
|
||||
er.MacAddress = mac.String()
|
||||
}
|
||||
|
||||
+1
-12
@@ -33,8 +33,7 @@ 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.
|
||||
// If path is a symlink it also returns the path to the target file.
|
||||
Stat(path string) (string, FileInfo, error)
|
||||
Stat(path 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.
|
||||
@@ -65,8 +64,6 @@ 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.
|
||||
@@ -74,14 +71,6 @@ 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.
|
||||
|
||||
@@ -366,7 +366,7 @@ func (b *Builder) calcCopyInfo(cmdName, origPath string, allowLocalDecompression
|
||||
|
||||
// Must be a dir or a file
|
||||
|
||||
statPath, fi, err := b.context.Stat(origPath)
|
||||
fi, err := b.context.Stat(origPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -383,9 +383,11 @@ func (b *Builder) calcCopyInfo(cmdName, origPath string, allowLocalDecompression
|
||||
hfi.SetHash("file:" + hfi.Hash())
|
||||
return copyInfos, nil
|
||||
}
|
||||
|
||||
// Must be a dir
|
||||
|
||||
var subfiles []string
|
||||
err = b.context.Walk(statPath, func(path string, info builder.FileInfo, err error) error {
|
||||
b.context.Walk(origPath, func(path string, info builder.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -393,9 +395,6 @@ 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()
|
||||
@@ -605,9 +604,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
|
||||
}
|
||||
}
|
||||
|
||||
+35
-28
@@ -5,6 +5,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
@@ -42,32 +43,26 @@ func (c *tarSumContext) Open(path string) (io.ReadCloser, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (c *tarSumContext) Stat(path string) (string, FileInfo, error) {
|
||||
func (c *tarSumContext) Stat(path string) (fi FileInfo, err 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)
|
||||
}
|
||||
|
||||
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.
|
||||
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.
|
||||
sum := path
|
||||
// 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 {
|
||||
if tsInfo := c.sums.GetFile(cleanpath); tsInfo != nil {
|
||||
sum = tsInfo.Sum()
|
||||
}
|
||||
fi := &HashedFileInfo{PathFileInfo{st, fullpath, filepath.Base(cleanpath)}, sum}
|
||||
return rel, fi, nil
|
||||
fi = &HashedFileInfo{fi, sum}
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
// MakeTarSumContext returns a build Context from a tar stream.
|
||||
@@ -119,7 +114,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.Lstat(fullpath)
|
||||
_, err = os.Stat(fullpath)
|
||||
if err != nil {
|
||||
return "", "", convertPathError(err, path)
|
||||
}
|
||||
@@ -127,26 +122,38 @@ func (c *tarSumContext) normalize(path string) (cleanpath, fullpath string, err
|
||||
}
|
||||
|
||||
func (c *tarSumContext) Walk(root string, walkFn WalkFunc) error {
|
||||
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)
|
||||
for _, tsInfo := range c.sums {
|
||||
path := tsInfo.Name()
|
||||
path, fullpath, err := c.normalize(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rel == "." {
|
||||
return nil
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
sum := rel
|
||||
if tsInfo := c.sums.GetFile(rel); tsInfo != nil {
|
||||
sum = tsInfo.Sum()
|
||||
info, err := os.Lstat(fullpath)
|
||||
if err != nil {
|
||||
return convertPathError(err, path)
|
||||
}
|
||||
fi := &HashedFileInfo{PathFileInfo{FileInfo: info, FilePath: fullpath}, sum}
|
||||
if err := walkFn(rel, fi, nil); err != nil {
|
||||
// TODO check context breakout?
|
||||
fi := &HashedFileInfo{PathFileInfo{info, fullpath}, tsInfo.Sum()}
|
||||
if err := walkFn(path, fi, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *tarSumContext) Remove(path string) error {
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@@ -42,9 +42,6 @@
|
||||
# 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 "$@"
|
||||
}
|
||||
@@ -1370,7 +1367,6 @@ _docker_run() {
|
||||
--ulimit
|
||||
--user -u
|
||||
--uts
|
||||
--volume-driver
|
||||
--volumes-from
|
||||
--volume -v
|
||||
--workdir -w
|
||||
@@ -1515,10 +1511,6 @@ _docker_run() {
|
||||
esac
|
||||
return
|
||||
;;
|
||||
--volume-driver)
|
||||
COMPREPLY=( $( compgen -W "local" -- "$cur" ) )
|
||||
return
|
||||
;;
|
||||
--volumes-from)
|
||||
__docker_containers_all
|
||||
return
|
||||
@@ -1872,7 +1864,4 @@ _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 -d 'Signal to kill a container'
|
||||
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' -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)'
|
||||
|
||||
@@ -334,7 +334,7 @@ __docker_volume_subcommand() {
|
||||
(create)
|
||||
_arguments \
|
||||
$opts_help \
|
||||
"($help -d --driver)"{-d,--driver=}"[Specify volume driver name]:Driver name:(local)" \
|
||||
"($help -d --driver)"{-d,--driver=}"[Specify volume driver name]:Driver name: " \
|
||||
"($help)--name=[Specify volume name]" \
|
||||
"($help)*"{-o,--opt=}"[Set driver specific options]:Driver option: " && ret=0
|
||||
;;
|
||||
@@ -444,7 +444,6 @@ __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"
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
var (
|
||||
defaultPidFile = "/var/run/docker.pid"
|
||||
defaultGraph = "/var/lib/docker"
|
||||
defaultExec = "clr"
|
||||
defaultExec = "native"
|
||||
)
|
||||
|
||||
// Config defines the configuration of a docker daemon.
|
||||
|
||||
+4
-23
@@ -153,28 +153,7 @@ func (container *Container) readHostConfig() error {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
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
|
||||
return json.NewDecoder(f).Decode(&container.hostConfig)
|
||||
}
|
||||
|
||||
func (container *Container) writeHostConfig() error {
|
||||
@@ -354,7 +333,9 @@ func (streamConfig *streamConfig) StderrPipe() io.ReadCloser {
|
||||
func (container *Container) cleanup() {
|
||||
container.releaseNetwork()
|
||||
|
||||
container.unmountIpcMounts(detachMounted)
|
||||
if err := container.unmountIpcMounts(); err != nil {
|
||||
logrus.Errorf("%s: Failed to umount ipc filesystems: %v", container.ID, err)
|
||||
}
|
||||
|
||||
if err := container.Unmount(); err != nil {
|
||||
logrus.Errorf("%s: Failed to umount filesystem: %v", container.ID, err)
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
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) {
|
||||
@@ -69,68 +64,3 @@ 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")
|
||||
}
|
||||
}
|
||||
|
||||
+29
-51
@@ -229,10 +229,10 @@ func populateCommand(c *Container, env []string) error {
|
||||
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")
|
||||
return fmt.Errorf("/dev/shm is not mounted, but must be for --host=ipc")
|
||||
}
|
||||
if _, err := os.Stat("/dev/mqueue"); err != nil {
|
||||
return fmt.Errorf("/dev/mqueue is not mounted, but must be for --ipc=host")
|
||||
return fmt.Errorf("/dev/mqueue is not mounted, but must be for --host=ipc")
|
||||
}
|
||||
c.ShmPath = "/dev/shm"
|
||||
c.MqueuePath = "/dev/mqueue"
|
||||
@@ -621,9 +621,7 @@ func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint, networkSett
|
||||
return networkSettings, nil
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -725,10 +723,6 @@ func (container *Container) updateNetworkSettings(n libnetwork.Network) error {
|
||||
container.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -822,17 +816,6 @@ func (container *Container) buildCreateEndpointOptions(n libnetwork.Network) ([]
|
||||
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
|
||||
}
|
||||
@@ -902,6 +885,10 @@ func (container *Container) buildCreateEndpointOptions(n libnetwork.Network) ([]
|
||||
createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption))
|
||||
}
|
||||
|
||||
if n.Name() == "bridge" || container.NetworkSettings.IsAnonymousEndpoint {
|
||||
createOptions = append(createOptions, libnetwork.CreateOptionAnonymous())
|
||||
}
|
||||
|
||||
return createOptions, nil
|
||||
}
|
||||
|
||||
@@ -940,13 +927,6 @@ func (container *Container) allocateNetwork() error {
|
||||
if mode.IsDefault() {
|
||||
networkName = controller.Config().Daemon.DefaultNetwork
|
||||
}
|
||||
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
|
||||
@@ -973,7 +953,7 @@ func (container *Container) getNetworkSandbox() libnetwork.Sandbox {
|
||||
return sb
|
||||
}
|
||||
|
||||
// ConnectToNetwork connects a container to a network
|
||||
// ConnectToNetwork connects a container to a netork
|
||||
func (container *Container) ConnectToNetwork(idOrName string) error {
|
||||
if !container.Running {
|
||||
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
|
||||
@@ -987,7 +967,9 @@ func (container *Container) ConnectToNetwork(idOrName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) connectToNetwork(idOrName string, updateSettings bool) (err error) {
|
||||
func (container *Container) connectToNetwork(idOrName string, updateSettings bool) error {
|
||||
var err error
|
||||
|
||||
if container.hostConfig.NetworkMode.IsContainer() {
|
||||
return runconfig.ErrConflictSharedNetwork
|
||||
}
|
||||
@@ -1214,10 +1196,6 @@ func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error {
|
||||
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
|
||||
}
|
||||
|
||||
if container.hostConfig.NetworkMode.IsHost() && runconfig.NetworkMode(n.Type()).IsHost() {
|
||||
return runconfig.ErrConflictHostNetwork
|
||||
}
|
||||
|
||||
if err := container.disconnectFromNetwork(n); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1235,11 +1213,7 @@ func (container *Container) disconnectFromNetwork(n libnetwork.Network) error {
|
||||
)
|
||||
|
||||
s := func(current libnetwork.Endpoint) bool {
|
||||
epInfo := current.Info()
|
||||
if epInfo == nil {
|
||||
return false
|
||||
}
|
||||
if sb := epInfo.Sandbox(); sb != nil {
|
||||
if sb := current.Info().Sandbox(); sb != nil {
|
||||
if sb.ContainerID() == container.ID {
|
||||
ep = current
|
||||
sbox = sb
|
||||
@@ -1482,21 +1456,22 @@ func (container *Container) setupIpcDirs() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) unmountIpcMounts(unmount func(pth string) error) {
|
||||
func (container *Container) unmountIpcMounts() error {
|
||||
if container.hostConfig.IpcMode.IsContainer() || container.hostConfig.IpcMode.IsHost() {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
var warnings []string
|
||||
var errors []string
|
||||
|
||||
if !container.hasMountFor("/dev/shm") {
|
||||
shmPath, err := container.shmPath()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
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))
|
||||
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())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1506,17 +1481,20 @@ func (container *Container) unmountIpcMounts(unmount func(pth string) error) {
|
||||
mqueuePath, err := container.mqueuePath()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
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))
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(warnings) > 0 {
|
||||
logrus.Warnf("failed to cleanup ipc mounts:\n%v", strings.Join(warnings, "\n"))
|
||||
if len(errors) > 0 {
|
||||
return fmt.Errorf("failed to cleanup ipc mounts:\n%v", strings.Join(errors, "\n"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) ipcMounts() []execdriver.Mount {
|
||||
|
||||
@@ -183,14 +183,11 @@ func (container *Container) removeMountPoints(_ bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) unmountIpcMounts(unmount func(pth string) error) {
|
||||
}
|
||||
|
||||
func detachMounted(path string) error {
|
||||
func (container *Container) setupIpcDirs() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) setupIpcDirs() error {
|
||||
func (container *Container) unmountIpcMounts() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -38,7 +38,6 @@ 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"
|
||||
@@ -223,8 +222,9 @@ func (daemon *Daemon) Register(container *Container) error {
|
||||
}
|
||||
daemon.execDriver.Terminate(cmd)
|
||||
|
||||
container.unmountIpcMounts(mount.Unmount)
|
||||
|
||||
if err := container.unmountIpcMounts(); err != nil {
|
||||
logrus.Errorf("%s: Failed to umount ipc filesystems: %v", container.ID, err)
|
||||
}
|
||||
if err := container.Unmount(); err != nil {
|
||||
logrus.Debugf("unmount error %s", err)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
)
|
||||
|
||||
// cleanupMounts umounts shm/mqueue mounts for old containers
|
||||
@@ -21,7 +20,7 @@ func (daemon *Daemon) cleanupMounts() error {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return daemon.cleanupMountsFromReader(f, mount.Unmount)
|
||||
return daemon.cleanupMountsFromReader(f, detachMounted)
|
||||
}
|
||||
|
||||
func (daemon *Daemon) cleanupMountsFromReader(reader io.Reader, unmount func(target string) error) error {
|
||||
|
||||
+5
-30
@@ -182,7 +182,7 @@ func TestLoadWithVolume(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
daemon, err := initDaemonWithVolumeStore(tmp)
|
||||
daemon, err := initDaemonForVolumesTest(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -270,7 +270,7 @@ func TestLoadWithBindMount(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
daemon, err := initDaemonWithVolumeStore(tmp)
|
||||
daemon, err := initDaemonForVolumesTest(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -361,7 +361,7 @@ func TestLoadWithVolume17RC(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
daemon, err := initDaemonWithVolumeStore(tmp)
|
||||
daemon, err := initDaemonForVolumesTest(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -466,7 +466,7 @@ func TestRemoveLocalVolumesFollowingSymlinks(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
daemon, err := initDaemonWithVolumeStore(tmp)
|
||||
daemon, err := initDaemonForVolumesTest(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -502,7 +502,7 @@ func TestRemoveLocalVolumesFollowingSymlinks(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
|
||||
func initDaemonForVolumesTest(tmp string) (*Daemon, error) {
|
||||
daemon := &Daemon{
|
||||
repository: tmp,
|
||||
root: tmp,
|
||||
@@ -549,28 +549,3 @@ 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")
|
||||
}
|
||||
}
|
||||
|
||||
+4
-18
@@ -332,11 +332,11 @@ 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(kv[1]))
|
||||
options = append(options, nwconfig.OptionKVProviderURL(strings.Join(kv[1:], "://")))
|
||||
}
|
||||
if len(dconfig.ClusterOpts) > 0 {
|
||||
options = append(options, nwconfig.OptionKVOpts(dconfig.ClusterOpts))
|
||||
@@ -458,25 +458,12 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
|
||||
ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.Bridge.DefaultGatewayIPv4.String()
|
||||
}
|
||||
|
||||
var (
|
||||
ipamV6Conf *libnetwork.IpamConf
|
||||
deferIPv6Alloc bool
|
||||
)
|
||||
var ipamV6Conf *libnetwork.IpamConf
|
||||
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{}
|
||||
}
|
||||
@@ -501,8 +488,7 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
|
||||
netlabel.GenericData: netOption,
|
||||
netlabel.EnableIPv6: config.Bridge.EnableIPv6,
|
||||
}),
|
||||
libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf),
|
||||
libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc))
|
||||
libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf))
|
||||
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, src.Name())
|
||||
destPath = filepath.Join(destPath, filepath.Base(srcPath))
|
||||
}
|
||||
|
||||
if err := system.MkdirAll(filepath.Dir(destPath), 0755); err != nil {
|
||||
|
||||
+60
-281
@@ -3,18 +3,14 @@
|
||||
package clr
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -36,33 +32,22 @@ const (
|
||||
|
||||
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"
|
||||
// upstream base URL
|
||||
clrURL = "https://download.clearlinux.org"
|
||||
|
||||
// upstream latest release file
|
||||
latestFile = "https://download.clearlinux.org/latest"
|
||||
|
||||
// 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
|
||||
@@ -84,58 +69,10 @@ type activeContainer struct {
|
||||
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 {
|
||||
@@ -144,56 +81,61 @@ func getClrVersion(libPath string) string {
|
||||
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)
|
||||
func fetchLatest(libPath string) error {
|
||||
out, err := os.Create(path.Join(libPath, clrFile))
|
||||
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)
|
||||
resp, err := http.Get(latestFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
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)
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
|
||||
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)
|
||||
func fetchImage(version, libPath string) error {
|
||||
// TODO: Add checksum validation
|
||||
outfile := fmt.Sprintf("clear-%s-containers.img.xz", version)
|
||||
url := fmt.Sprintf("%s/releases/%s/clear/%s", clrURL, version, outfile)
|
||||
outpath := path.Join(libPath, outfile)
|
||||
var output []byte
|
||||
|
||||
logrus.Debugf("Fetching clr version: %s, %s", version, outpath)
|
||||
out, err := os.Create(outpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Consider progress feedback ?
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// decompress the file
|
||||
cmd := exec.Command("unxz", outpath)
|
||||
cmd.Dir = libPath
|
||||
|
||||
if output, err = cmd.CombinedOutput(); err != nil {
|
||||
logrus.Debugf("Unable to extract image %s: %s", version, output)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewDriver creates a new clear linux execution driver.
|
||||
@@ -209,7 +151,6 @@ func NewDriver(root, libPath, initPath string, apparmor bool) (*driver, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &driver{
|
||||
apparmor: apparmor,
|
||||
root: root,
|
||||
@@ -223,178 +164,25 @@ func NewDriver(root, libPath, initPath string, apparmor bool) (*driver, error) {
|
||||
}, 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)
|
||||
var nversion string
|
||||
logrus.Debugf("%s preparing environment", driverName)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
nversion = getClrVersion(libPath)
|
||||
if nversion != version && version != "" {
|
||||
logrus.Debugf("Updating to clr version: %s", nversion)
|
||||
err = fetchImage(nversion, libPath)
|
||||
} 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
|
||||
err = fetchImage(nversion, libPath)
|
||||
} else {
|
||||
logrus.Debugf("Using clr version: %s", nversion)
|
||||
}
|
||||
|
||||
return nversion, nil
|
||||
@@ -882,13 +670,6 @@ func (d *driver) generateDockerInit(c *execdriver.Command) error {
|
||||
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)
|
||||
|
||||
@@ -922,16 +703,14 @@ func (d *driver) setupNetwork(c *execdriver.Command) error {
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
// Strip existing veth
|
||||
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")
|
||||
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
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package devmapper
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -149,8 +148,6 @@ 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.
|
||||
@@ -535,45 +532,6 @@ 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()
|
||||
|
||||
@@ -585,11 +543,6 @@ 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()
|
||||
@@ -891,11 +844,7 @@ func (devices *DeviceSet) getBaseDeviceSize() uint64 {
|
||||
return info.Size
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) getBaseDeviceFS() string {
|
||||
return devices.filesystem
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) verifyBaseDeviceUUIDFS(baseInfo *devInfo) error {
|
||||
func (devices *DeviceSet) verifyBaseDeviceUUID(baseInfo *devInfo) error {
|
||||
devices.Lock()
|
||||
defer devices.Unlock()
|
||||
|
||||
@@ -911,23 +860,9 @@ func (devices *DeviceSet) verifyBaseDeviceUUIDFS(baseInfo *devInfo) error {
|
||||
}
|
||||
|
||||
if devices.BaseDeviceUUID != uuid {
|
||||
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)
|
||||
return fmt.Errorf("Current Base Device UUID:%s does not match with stored UUID:%s", 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
|
||||
}
|
||||
|
||||
@@ -1028,7 +963,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) setupVerifyBaseImageUUIDFS(baseInfo *devInfo) error {
|
||||
func (devices *DeviceSet) setupVerifyBaseImageUUID(baseInfo *devInfo) error {
|
||||
// If BaseDeviceUUID is nil (upgrade case), save it and return success.
|
||||
if devices.BaseDeviceUUID == "" {
|
||||
if err := devices.saveBaseDeviceUUID(baseInfo); err != nil {
|
||||
@@ -1037,8 +972,8 @@ func (devices *DeviceSet) setupVerifyBaseImageUUIDFS(baseInfo *devInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := devices.verifyBaseDeviceUUIDFS(baseInfo); err != nil {
|
||||
return fmt.Errorf("Base Device UUID and Filesystem verification failed.%v", err)
|
||||
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)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -1053,7 +988,7 @@ func (devices *DeviceSet) setupBaseImage() error {
|
||||
|
||||
if oldInfo != nil {
|
||||
if oldInfo.Initialized && !oldInfo.Deleted {
|
||||
if err := devices.setupVerifyBaseImageUUIDFS(oldInfo); err != nil {
|
||||
if err := devices.setupVerifyBaseImageUUID(oldInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2275,7 +2210,6 @@ 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 {
|
||||
@@ -2336,6 +2270,7 @@ 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),
|
||||
|
||||
@@ -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", s.BaseDeviceFS},
|
||||
{"Backing Filesystem", backingFs},
|
||||
{"Data file", s.DataFile},
|
||||
{"Metadata file", s.MetadataFile},
|
||||
{"Data Space Used", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Used)))},
|
||||
|
||||
+8
-22
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/types"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/graphdb"
|
||||
"github.com/docker/docker/pkg/nat"
|
||||
@@ -286,24 +285,6 @@ func includeContainerInList(container *Container, ctx *listContext) iterationAct
|
||||
return includeContainer
|
||||
}
|
||||
|
||||
func getImage(s *graph.TagStore, img, imgID string) (string, error) {
|
||||
// both Image and ImageID is actually ids, nothing to guess
|
||||
if strings.HasPrefix(imgID, img) {
|
||||
return img, nil
|
||||
}
|
||||
id, err := s.GetID(img)
|
||||
if err != nil {
|
||||
if err == graph.ErrNameIsNotExist {
|
||||
return imgID, nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
if id != imgID {
|
||||
return imgID, nil
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// transformContainer generates the container type expected by the docker ps command.
|
||||
func (daemon *Daemon) transformContainer(container *Container, ctx *listContext) (*types.Container, error) {
|
||||
newC := &types.Container{
|
||||
@@ -316,11 +297,16 @@ func (daemon *Daemon) transformContainer(container *Container, ctx *listContext)
|
||||
newC.Names = []string{}
|
||||
}
|
||||
|
||||
showImg, err := getImage(daemon.repositories, container.Config.Image, container.ImageID)
|
||||
img, err := daemon.repositories.LookupImage(container.Config.Image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// If the image can no longer be found by its original reference,
|
||||
// it makes sense to show the ID instead of a stale reference.
|
||||
newC.Image = container.ImageID
|
||||
} else if container.ImageID == img.ID {
|
||||
newC.Image = container.Config.Image
|
||||
} else {
|
||||
newC.Image = container.ImageID
|
||||
}
|
||||
newC.Image = showImg
|
||||
|
||||
if len(container.Args) > 0 {
|
||||
args := []string{}
|
||||
|
||||
@@ -8,6 +8,8 @@ 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
|
||||
@@ -48,6 +50,10 @@ 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)
|
||||
@@ -127,3 +133,37 @@ 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,8 +14,6 @@ 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"
|
||||
)
|
||||
|
||||
@@ -120,11 +118,6 @@ 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)
|
||||
}
|
||||
}
|
||||
@@ -177,37 +170,3 @@ 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,10 +365,9 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc
|
||||
}
|
||||
}
|
||||
|
||||
if label.RelabelNeeded(bind.Mode) {
|
||||
if err := label.Relabel(bind.Source, container.MountLabel, label.IsShared(bind.Mode)); err != nil {
|
||||
return err
|
||||
}
|
||||
shared := label.IsShared(bind.Mode)
|
||||
if err := label.Relabel(bind.Source, container.MountLabel, shared); err != nil {
|
||||
return err
|
||||
}
|
||||
binds[bind.Destination] = true
|
||||
mountPoints[bind.Destination] = bind
|
||||
|
||||
+1
-14
@@ -12,8 +12,7 @@ weight=-1
|
||||
# Understand Docker plugins
|
||||
|
||||
You can extend the capabilities of the Docker Engine by loading third-party
|
||||
plugins. This page explains the types of plugins and provides links to several
|
||||
volume and network plugins for Docker.
|
||||
plugins.
|
||||
|
||||
## Types of plugins
|
||||
|
||||
@@ -65,18 +64,6 @@ 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
|
||||
|
||||
@@ -5,6 +5,7 @@ description = "Network driver plugins."
|
||||
keywords = ["Examples, Usage, plugins, docker, documentation, user guide"]
|
||||
[menu.main]
|
||||
parent = "mn_extend"
|
||||
weight=-1
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ from the new repository:
|
||||
|
||||
4. Add the new `gpg` key.
|
||||
|
||||
$ apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
|
||||
$ apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
|
||||
|
||||
5. Open the `/etc/apt/sources.list.d/docker.list` file in your favorite editor.
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ packages from the new repository:
|
||||
|
||||
3. Add the new `gpg` key.
|
||||
|
||||
$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
|
||||
$ sudo apt-key adv --keyserver hkp://pgp.mit.edu: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 12.04 (LTS)
|
||||
# Ubuntu Precise
|
||||
deb https://apt.dockerproject.org/repo ubuntu-precise main
|
||||
# Ubuntu Trusty 14.04 (LTS)
|
||||
# Ubuntu Trusty
|
||||
deb https://apt.dockerproject.org/repo ubuntu-trusty main
|
||||
# Ubuntu Vivid 15.04
|
||||
# Ubuntu Vivid
|
||||
deb https://apt.dockerproject.org/repo ubuntu-vivid main
|
||||
# Ubuntu Wily 15.10
|
||||
# Ubuntu Wily
|
||||
deb https://apt.dockerproject.org/repo ubuntu-wily main
|
||||
|
||||
7. Save and close the `/etc/apt/sources.list.d/docker.list` file.
|
||||
@@ -94,31 +94,12 @@ 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:
|
||||
|
||||
@@ -124,7 +124,7 @@ list of DNS options to be used in the container.
|
||||
|
||||
### v1.20 API changes
|
||||
|
||||
[Docker Remote API v1.20](docker_remote_api_v1.20.md) documentation
|
||||
[Docker Remote API v1.20](docker_remote_api_v1.20/) documentation
|
||||
|
||||
* `GET /containers/(id)/archive` get an archive of filesystem content from a container.
|
||||
* `PUT /containers/(id)/archive` upload an archive of content to be extracted to
|
||||
|
||||
@@ -15,7 +15,7 @@ weight = 7
|
||||
|
||||
- The Remote API has replaced `rcli`.
|
||||
- The daemon listens on `unix:///var/run/docker.sock` but you can
|
||||
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
|
||||
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
|
||||
- The API tends to be REST, but for some complex commands, like `attach`
|
||||
or `pull`, the HTTP connection is hijacked to transport `STDOUT`,
|
||||
`STDIN` and `STDERR`.
|
||||
|
||||
@@ -15,7 +15,7 @@ weight = 6
|
||||
|
||||
- The Remote API has replaced `rcli`.
|
||||
- The daemon listens on `unix:///var/run/docker.sock` but you can
|
||||
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
|
||||
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
|
||||
- The API tends to be REST, but for some complex commands, like `attach`
|
||||
or `pull`, the HTTP connection is hijacked to transport `STDOUT`,
|
||||
`STDIN` and `STDERR`.
|
||||
|
||||
@@ -15,7 +15,7 @@ weight = 5
|
||||
|
||||
- The Remote API has replaced `rcli`.
|
||||
- The daemon listens on `unix:///var/run/docker.sock` but you can
|
||||
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
|
||||
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
|
||||
- The API tends to be REST, but for some complex commands, like `attach`
|
||||
or `pull`, the HTTP connection is hijacked to transport `STDOUT`,
|
||||
`STDIN` and `STDERR`.
|
||||
|
||||
@@ -15,7 +15,7 @@ weight = 4
|
||||
|
||||
- The Remote API has replaced `rcli`.
|
||||
- The daemon listens on `unix:///var/run/docker.sock` but you can
|
||||
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
|
||||
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
|
||||
- The API tends to be REST, but for some complex commands, like `attach`
|
||||
or `pull`, the HTTP connection is hijacked to transport `STDOUT`,
|
||||
`STDIN` and `STDERR`.
|
||||
|
||||
@@ -15,7 +15,7 @@ weight = 3
|
||||
|
||||
- The Remote API has replaced `rcli`.
|
||||
- The daemon listens on `unix:///var/run/docker.sock` but you can
|
||||
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
|
||||
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
|
||||
- The API tends to be REST, but for some complex commands, like `attach`
|
||||
or `pull`, the HTTP connection is hijacked to transport `STDOUT`,
|
||||
`STDIN` and `STDERR`.
|
||||
|
||||
@@ -15,7 +15,7 @@ weight = 2
|
||||
|
||||
- The Remote API has replaced `rcli`.
|
||||
- The daemon listens on `unix:///var/run/docker.sock` but you can
|
||||
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
|
||||
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
|
||||
- The API tends to be REST. However, for some complex commands, like `attach`
|
||||
or `pull`, the HTTP connection is hijacked to transport `stdout`,
|
||||
`stdin` and `stderr`.
|
||||
|
||||
@@ -15,7 +15,7 @@ weight = 1
|
||||
|
||||
- The Remote API has replaced `rcli`.
|
||||
- The daemon listens on `unix:///var/run/docker.sock` but you can
|
||||
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
|
||||
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
|
||||
- The API tends to be REST. However, for some complex commands, like `attach`
|
||||
or `pull`, the HTTP connection is hijacked to transport `stdout`,
|
||||
`stdin` and `stderr`.
|
||||
|
||||
@@ -15,7 +15,7 @@ weight=-2
|
||||
|
||||
- The Remote API has replaced `rcli`.
|
||||
- The daemon listens on `unix:///var/run/docker.sock` but you can
|
||||
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
|
||||
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
|
||||
- The API tends to be REST. However, for some complex commands, like `attach`
|
||||
or `pull`, the HTTP connection is hijacked to transport `stdout`,
|
||||
`stdin` and `stderr`.
|
||||
|
||||
@@ -83,7 +83,7 @@ This is useful when you want to set up a container configuration ahead of time
|
||||
so that it is ready to start when you need it. The initial status of the
|
||||
new container is `created`.
|
||||
|
||||
Please see the [run command](run.md) section and the [Docker run reference](../run.md) for more details.
|
||||
Please see the [run command](run.md) section and the [Docker run reference](run.md) for more details.
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -261,11 +261,11 @@ options for `zfs` start with `zfs`.
|
||||
* `dm.fs`
|
||||
|
||||
Specifies the filesystem type to use for the base device. The supported
|
||||
options are "ext4" and "xfs". The default is "xfs"
|
||||
options are "ext4" and "xfs". The default is "ext4"
|
||||
|
||||
Example use:
|
||||
|
||||
$ docker daemon --storage-opt dm.fs=ext4
|
||||
$ docker daemon --storage-opt dm.fs=xfs
|
||||
|
||||
* `dm.mkfsarg`
|
||||
|
||||
|
||||
@@ -21,12 +21,11 @@ parent = "smn_cli"
|
||||
|
||||
Creates a new volume that containers can consume and store data in. If a name is not specified, Docker generates a random name. You create a volume and then configure the container to use it, for example:
|
||||
|
||||
$ docker volume create --name hello
|
||||
hello
|
||||
$ docker volume create --name hello
|
||||
hello
|
||||
$ docker run -d -v hello:/world busybox ls /world
|
||||
|
||||
$ docker run -d -v hello:/world busybox ls /world
|
||||
|
||||
The mount is created inside the container's `/world` directory. Docker does not support relative paths for mount points inside the container.
|
||||
The mount is created inside the container's `/src` directory. Docker does not support relative paths for mount points inside the container.
|
||||
|
||||
Multiple containers can use the same volume in the same time period. This is useful if two containers need access to shared data. For example, if one container writes and the other reads the data.
|
||||
|
||||
@@ -42,7 +41,7 @@ If you specify a volume name already in use on the current driver, Docker assume
|
||||
|
||||
Some volume drivers may take options to customize the volume creation. Use the `-o` or `--opt` flags to pass driver options:
|
||||
|
||||
$ docker volume create --driver fake --opt tardis=blue --opt timey=wimey
|
||||
$ docker volume create --driver fake --opt tardis=blue --opt timey=wimey
|
||||
|
||||
These options are passed directly to the volume driver. Options for
|
||||
different volume drivers may do different things (or nothing at all).
|
||||
|
||||
@@ -37,4 +37,4 @@ Example output:
|
||||
]
|
||||
|
||||
$ docker volume inspect --format '{{ .Mountpoint }}' 85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d
|
||||
/var/lib/docker/volumes/85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d/_data
|
||||
"/var/lib/docker/volumes/85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d/_data"
|
||||
|
||||
@@ -18,5 +18,5 @@ parent = "smn_cli"
|
||||
|
||||
Removes one or more volumes. You cannot remove a volume that is in use by a container.
|
||||
|
||||
$ docker volume rm hello
|
||||
hello
|
||||
$ docker volume rm hello
|
||||
hello
|
||||
|
||||
@@ -404,26 +404,16 @@ provision the hosts are with Docker Machine.
|
||||
|
||||

|
||||
|
||||
You should open the following ports between each of your hosts.
|
||||
|
||||
| Protocol | Port | Description |
|
||||
|----------|------|-----------------------|
|
||||
| udp | 4789 | Data plane (VXLAN) |
|
||||
| tcp/udp | 7946 | Control plane |
|
||||
|
||||
Your key-value store service may require additional ports.
|
||||
Check your vendor's documentation and open any required ports.
|
||||
|
||||
Once you have several machines provisioned, you can use Docker Swarm to quickly
|
||||
form them into a swarm which includes a discovery service as well.
|
||||
|
||||
To create an overlay network, you configure options on the `daemon` on each
|
||||
Docker Engine for use with `overlay` network. There are two options to set:
|
||||
|
||||
| Option | Description |
|
||||
|-----------------------------------------------|-------------------------------------------------------------|
|
||||
| `--cluster-store=PROVIDER://URL` | Describes the location of the KV service. |
|
||||
| `--cluster-advertise=HOST_IP|HOST_IFACE:PORT` | The IP address or interface of the HOST used for clustering |
|
||||
| Option | Description |
|
||||
|----------------------------------|-----------------------------------------------------------|
|
||||
| `--cluster-store=PROVIDER://URL` | Describes the location of the KV service. |
|
||||
| `--cluster-advertise=HOST_IP` | Advertises containers created by the HOST on the network. |
|
||||
|
||||
Create an `overlay` network on one of the machines in the Swarm.
|
||||
|
||||
@@ -436,7 +426,7 @@ provides complete isolation for the containers.
|
||||
|
||||
Then, on each host, launch containers making sure to specify the network name.
|
||||
|
||||
$ docker run -itd --net=my-multi-host-network busybox
|
||||
$ docker run -itd --net=mmy-multi-host-network busybox
|
||||
|
||||
Once connected, each container has access to all the containers in the network
|
||||
regardless of which Docker host the container was launched on.
|
||||
@@ -487,4 +477,4 @@ and removed in a future release.
|
||||
- [Managing Data in Containers](../dockervolumes.md)
|
||||
- [Docker Machine overview](https://docs.docker.com/machine)
|
||||
- [Docker Swarm overview](https://docs.docker.com/swarm)
|
||||
- [Investigate the LibNetwork project](https://github.com/docker/libnetwork)
|
||||
- [Investigate the LibNetwork project](https://github.com/docker/libnetwork/blob/master)
|
||||
|
||||
@@ -7,13 +7,13 @@ keywords = ["Examples, Usage, network, docker, documentation, user guide, multih
|
||||
parent = "smn_networking"
|
||||
weight=-3
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
<![end-metadata]-->
|
||||
|
||||
# Get started with multi-host networking
|
||||
|
||||
This article uses an example to explain the basics of creating a multi-host
|
||||
network. Docker Engine supports multi-host networking out-of-the-box through the
|
||||
`overlay` network driver. Unlike `bridge` networks, overlay networks require
|
||||
network. Docker Engine supports multi-host-networking out-of-the-box through the
|
||||
`overlay` network driver. Unlike `bridge` networks overlay networks require
|
||||
some pre-existing conditions before you can create one. These conditions are:
|
||||
|
||||
* A host with a 3.16 kernel version or higher.
|
||||
@@ -22,8 +22,8 @@ some pre-existing conditions before you can create one. These conditions are:
|
||||
* A properly configured Engine `daemon` on each host in the cluster.
|
||||
|
||||
Though Docker Machine and Docker Swarm are not mandatory to experience Docker
|
||||
multi-host networking, this example uses them to illustrate how they are
|
||||
integrated. You'll use Machine to create both the key-value store
|
||||
multi-host-networking, this example uses them to illustrate how they are
|
||||
integrated. You'll use Machine to create both the the key-value store
|
||||
server and the host cluster. This example creates a Swarm cluster.
|
||||
|
||||
## Prerequisites
|
||||
@@ -39,10 +39,10 @@ Machine to the latest versions.
|
||||
|
||||
## Step 1: Set up a key-value store
|
||||
|
||||
An overlay network requires a key-value store. The key-value store holds
|
||||
information about the network state which includes discovery, networks,
|
||||
endpoints, IP addresses, and more. Docker supports Consul, Etcd, and ZooKeeper
|
||||
key-value stores. This example uses Consul.
|
||||
An overlay network requires a key-value store. The key-value stores information
|
||||
about the network state which includes discovery, networks, endpoints,
|
||||
ip-addresses, and more. Docker supports Consul, Etcd, and ZooKeeper (Distributed
|
||||
store) key-value stores. This example uses Consul.
|
||||
|
||||
1. Log into a system prepared with the prerequisite Docker Engine, Docker Machine, and VirtualBox software.
|
||||
|
||||
@@ -62,10 +62,9 @@ key-value stores. This example uses Consul.
|
||||
-h "consul" \
|
||||
progrium/consul -server -bootstrap
|
||||
|
||||
A bash expansion `$(docker-machine config mh-keystore)` is used to pass the
|
||||
connection configuration to the `docker run` command. The client starts a
|
||||
`progrium/consul` image running in the `mh-keystore` machine. The server is
|
||||
called `consul` and is listening on port `8500`.
|
||||
You passed the `docker run` command the connection configuration using a bash
|
||||
expansion `$(docker-machine config mh-keystore)`. The client started a
|
||||
`progrium/consul` image running in the `mh-keystore` machine. The server is called `consul`and is listening port `8500`.
|
||||
|
||||
4. Set your local environment to the `mh-keystore` machine.
|
||||
|
||||
@@ -83,7 +82,7 @@ Keep your terminal open and move onto the next step.
|
||||
## Step 2: Create a Swarm cluster
|
||||
|
||||
In this step, you use `docker-machine` to provision the hosts for your network.
|
||||
At this point, you won't actually create the network. You'll create several
|
||||
At this point, you won't actually created the network. You'll create several
|
||||
machines in VirtualBox. One of the machines will act as the Swarm master;
|
||||
you'll create that first. As you create each host, you'll pass the Engine on
|
||||
that machine options that are needed by the `overlay` network driver.
|
||||
@@ -92,7 +91,7 @@ that machine options that are needed by the `overlay` network driver.
|
||||
|
||||
$ docker-machine create \
|
||||
-d virtualbox \
|
||||
--swarm --swarm-master \
|
||||
--swarm --swarm-image="swarm" --swarm-master \
|
||||
--swarm-discovery="consul://$(docker-machine ip mh-keystore):8500" \
|
||||
--engine-opt="cluster-store=consul://$(docker-machine ip mh-keystore):8500" \
|
||||
--engine-opt="cluster-advertise=eth1:2376" \
|
||||
@@ -103,7 +102,7 @@ that machine options that are needed by the `overlay` network driver.
|
||||
2. Create another host and add it to the Swarm cluster.
|
||||
|
||||
$ docker-machine create -d virtualbox \
|
||||
--swarm \
|
||||
--swarm --swarm-image="swarm:1.0.0-rc2" \
|
||||
--swarm-discovery="consul://$(docker-machine ip mh-keystore):8500" \
|
||||
--engine-opt="cluster-store=consul://$(docker-machine ip mh-keystore):8500" \
|
||||
--engine-opt="cluster-advertise=eth1:2376" \
|
||||
@@ -113,13 +112,14 @@ that machine options that are needed by the `overlay` network driver.
|
||||
|
||||
$ docker-machine ls
|
||||
NAME ACTIVE DRIVER STATE URL SWARM
|
||||
default - virtualbox Running tcp://192.168.99.100:2376
|
||||
mh-keystore * virtualbox Running tcp://192.168.99.103:2376
|
||||
mhs-demo0 - virtualbox Running tcp://192.168.99.104:2376 mhs-demo0 (master)
|
||||
mhs-demo1 - virtualbox Running tcp://192.168.99.105:2376 mhs-demo0
|
||||
default virtualbox Running tcp://192.168.99.100:2376
|
||||
mh-keystore virtualbox Running tcp://192.168.99.103:2376
|
||||
mhs-demo0 virtualbox Running tcp://192.168.99.104:2376 mhs-demo0 (master)
|
||||
mhs-demo1 virtualbox Running tcp://192.168.99.105:2376 mhs-demo0
|
||||
|
||||
At this point you have a set of hosts running on your network. You are ready to create a multi-host network for containers using these hosts.
|
||||
|
||||
|
||||
Leave your terminal open and go onto the next step.
|
||||
|
||||
## Step 3: Create the overlay Network
|
||||
@@ -155,7 +155,7 @@ To create an overlay network
|
||||
Total Memory: 2.043 GiB
|
||||
Name: 30438ece0915
|
||||
|
||||
From this information, you can see that you are running three containers and two images on the Master.
|
||||
From this information, you can see that you are running three containers and 2 images on the Master.
|
||||
|
||||
3. Create your `overlay` network.
|
||||
|
||||
@@ -167,51 +167,54 @@ To create an overlay network
|
||||
|
||||
$ docker network ls
|
||||
NETWORK ID NAME DRIVER
|
||||
412c2496d0eb mhs-demo1/host host
|
||||
dd51763e6dd2 mhs-demo0/bridge bridge
|
||||
6b07d0be843f my-net overlay
|
||||
b4234109bd9b mhs-demo0/none null
|
||||
1aeead6dd890 mhs-demo0/host host
|
||||
d0bb78cbe7bd mhs-demo1/bridge bridge
|
||||
1c0eb8f69ebb mhs-demo1/none null
|
||||
412c2496d0eb mhs-demo1/host host
|
||||
dd51763e6dd2 mhs-demo0/bridge bridge
|
||||
6b07d0be843f my-net overlay
|
||||
b4234109bd9b mhs-demo0/none null
|
||||
1aeead6dd890 mhs-demo0/host host
|
||||
d0bb78cbe7bd mhs-demo1/bridge bridge
|
||||
1c0eb8f69ebb mhs-demo1/none null
|
||||
|
||||
As you are in the Swarm master environment, you see all the networks on all
|
||||
the Swarm agents: the default networks on each engine and the single overlay
|
||||
network. Notice that each `NETWORK ID` is unique.
|
||||
Because you are in the Swarm master environment, you see all the networks on all Swarm agents. Notice that each `NETWORK ID` is unique. The default networks on each engine and the single overlay network.
|
||||
|
||||
5. Switch to each Swarm agent in turn and list the networks.
|
||||
5. Switch to each Swarm agent in turn and list the network.
|
||||
|
||||
$ eval $(docker-machine env mhs-demo0)
|
||||
$ docker network ls
|
||||
NETWORK ID NAME DRIVER
|
||||
6b07d0be843f my-net overlay
|
||||
dd51763e6dd2 bridge bridge
|
||||
b4234109bd9b none null
|
||||
1aeead6dd890 host host
|
||||
6b07d0be843f my-net overlay
|
||||
dd51763e6dd2 bridge bridge
|
||||
b4234109bd9b none null
|
||||
1aeead6dd890 host host
|
||||
$ eval $(docker-machine env mhs-demo1)
|
||||
$ docker network ls
|
||||
NETWORK ID NAME DRIVER
|
||||
d0bb78cbe7bd bridge bridge
|
||||
1c0eb8f69ebb none null
|
||||
412c2496d0eb host host
|
||||
6b07d0be843f my-net overlay
|
||||
d0bb78cbe7bd bridge bridge
|
||||
1c0eb8f69ebb none null
|
||||
412c2496d0eb host host
|
||||
6b07d0be843f my-net overlay
|
||||
|
||||
Both agents report they have the `my-net` network with the `6b07d0be843f` ID.
|
||||
You now have a multi-host container network running!
|
||||
Both agents reports it has the `my-net `network with the `6b07d0be843f` id. You have a multi-host container network running!
|
||||
|
||||
## Step 4: Run an application on your Network
|
||||
|
||||
Once your network is created, you can start a container on any of the hosts and it automatically is part of the network.
|
||||
|
||||
1. Point your environment to the Swarm master.
|
||||
1. Point your environment to your `mhs-demo0` instance.
|
||||
|
||||
$ eval $(docker-machine env --swarm mhs-demo0)
|
||||
$ eval $(docker-machine env mhs-demo0)
|
||||
|
||||
2. Start an Nginx web server on the `mhs-demo0` instance.
|
||||
2. Start an Nginx server on `mhs-demo0`.
|
||||
|
||||
$ docker run -itd --name=web --net=my-net --env="constraint:node==mhs-demo0" nginx
|
||||
|
||||
4. Run a BusyBox instance on the `mhs-demo1` instance and get the contents of the Nginx server's home page.
|
||||
This command starts a web server on the Swarm master.
|
||||
|
||||
3. Point your Machine environment to `mhs-demo1`
|
||||
|
||||
$ eval $(docker-machine env mhs-demo1)
|
||||
|
||||
4. Run a Busybox instance and get the contents of the Ngnix server's home page.
|
||||
|
||||
$ docker run -it --rm --net=my-net --env="constraint:node==mhs-demo1" busybox wget -O- http://web
|
||||
Unable to find image 'busybox:latest' locally
|
||||
@@ -281,7 +284,7 @@ to have external connectivity outside of their cluster.
|
||||
412c2496d0eb host host
|
||||
97102a22e8d2 docker_gwbridge bridge
|
||||
|
||||
2. Check the Nginx container's network interfaces.
|
||||
2. Check the Ngnix container's network interfaces.
|
||||
|
||||
$ docker exec web ip addr
|
||||
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
|
||||
@@ -311,11 +314,9 @@ to have external connectivity outside of their cluster.
|
||||
|
||||
You can try starting a second network on your existing Swarm cluster using Docker Compose.
|
||||
|
||||
1. If you haven't already, install Docker Compose.
|
||||
1. Log into the Swarm master.
|
||||
|
||||
2. Change your environment to the Swarm master.
|
||||
|
||||
$ eval $(docker-machine env --swarm mhs-demo0)
|
||||
2. Install Docker Compose.
|
||||
|
||||
3. Create a `docker-compose.yml` file.
|
||||
|
||||
@@ -325,7 +326,7 @@ You can try starting a second network on your existing Swarm cluster using Docke
|
||||
image: bfirsh/compose-mongodb-demo
|
||||
environment:
|
||||
- "MONGO_HOST=counter_mongo_1"
|
||||
- "constraint:node==mhs-demo0"
|
||||
- "constraint:node==swl-demo0"
|
||||
ports:
|
||||
- "80:5000"
|
||||
mongo:
|
||||
@@ -335,15 +336,7 @@ You can try starting a second network on your existing Swarm cluster using Docke
|
||||
|
||||
6. Start the application with Compose.
|
||||
|
||||
$ docker-compose --x-networking --project-name=counter up -d
|
||||
|
||||
7. Get the Swarm master's IP address.
|
||||
|
||||
$ docker-machine ip mhs-demo0
|
||||
|
||||
8. Put the IP address into your web browser.
|
||||
|
||||
Upon success, the browser should display the web application.
|
||||
$ docker-compose up --x-networking up -d
|
||||
|
||||
## Related information
|
||||
|
||||
|
||||
@@ -193,5 +193,5 @@ reason, you may want to place heavy write workloads on data volumes.
|
||||
|
||||
* [Understand images, containers, and storage drivers](imagesandcontainers.md)
|
||||
* [Select a storage driver](selectadriver.md)
|
||||
* [Btrfs storage driver in practice](btrfs-driver.md)
|
||||
* [BTRFS storage driver in practice](btrfs-driver.md)
|
||||
* [Device Mapper storage driver in practice](device-mapper-driver.md)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<!--[metadata]>
|
||||
+++
|
||||
title = "Btrfs storage in practice"
|
||||
description = "Learn how to optimize your use of Btrfs driver."
|
||||
keywords = ["container, storage, driver, Btrfs "]
|
||||
title = "BTRFS storage in practice"
|
||||
description = "Learn how to optimize your use of BTRFS driver."
|
||||
keywords = ["container, storage, driver, BTRFS "]
|
||||
[menu.main]
|
||||
parent = "mn_storage_docker"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# Docker and Btrfs in practice
|
||||
# Docker and BTRFS in practice
|
||||
|
||||
Btrfs is a next generation copy-on-write filesystem that supports many advanced
|
||||
storage technologies that make it a good fit for Docker. Btrfs is included in
|
||||
@@ -250,7 +250,7 @@ Now that you have a Btrfs filesystem mounted at `/var/lib/docker`, the daemon sh
|
||||
|
||||
Your Docker host is now configured to use the `btrfs` storage driver.
|
||||
|
||||
## Btrfs and Docker performance
|
||||
## BTRFS and Docker performance
|
||||
|
||||
There are several factors that influence Docker's performance under the `btrfs` storage driver.
|
||||
|
||||
|
||||
@@ -307,4 +307,4 @@ One final point, data volumes provide the best and most predictable performance.
|
||||
* [Understand images, containers, and storage drivers](imagesandcontainers.md)
|
||||
* [Select a storage driver](selectadriver.md)
|
||||
* [AUFS storage driver in practice](aufs-driver.md)
|
||||
* [Btrfs storage driver in practice](btrfs-driver.md)
|
||||
* [BTRFS storage driver in practice](btrfs-driver.md)
|
||||
|
||||
@@ -251,5 +251,5 @@ For detailed information about data volumes [Managing data in containers](https:
|
||||
|
||||
* [Select a storage driver](selectadriver.md)
|
||||
* [AUFS storage driver in practice](aufs-driver.md)
|
||||
* [Btrfs storage driver in practice](btrfs-driver.md)
|
||||
* [BTRFS storage driver in practice](btrfs-driver.md)
|
||||
* [Device Mapper storage driver in practice](device-mapper-driver.md)
|
||||
|
||||
@@ -18,10 +18,10 @@ Docker relies on driver technology to manage the storage and interactions associ
|
||||
* [Understand images, containers, and storage drivers](imagesandcontainers.md)
|
||||
* [Select a storage driver](selectadriver.md)
|
||||
* [AUFS storage driver in practice](aufs-driver.md)
|
||||
* [Btrfs storage driver in practice](btrfs-driver.md)
|
||||
* [BTRFS storage driver in practice](btrfs-driver.md)
|
||||
* [Device Mapper storage driver in practice](device-mapper-driver.md)
|
||||
* [OverlayFS in practice](overlayfs-driver.md)
|
||||
* [ZFS storage in practice](zfs-driver.md)
|
||||
* [FS storage in practice](zfs-driver.md)
|
||||
|
||||
If you are new to Docker containers make sure you read ["Understand images, containers, and storage drivers"](imagesandcontainers.md) first. It explains key concepts and technologies that can help you when working with storage drivers.
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ Once you decide which driver is best, you set this driver on the Docker daemon a
|
||||
|--------------|---------------------|
|
||||
|OverlayFS |`overlay` |
|
||||
|AUFS |`aufs` |
|
||||
|Btrfs |`btrfs` |
|
||||
|BTRFS |`btrfs` |
|
||||
|Device Maper |`devicemapper` |
|
||||
|VFS* |`vfs` |
|
||||
|ZFS |`zfs` |
|
||||
@@ -115,5 +115,5 @@ Whichever driver you choose, make sure it has strong community support and momen
|
||||
|
||||
* [Understand images, containers, and storage drivers](imagesandcontainers.md)
|
||||
* [AUFS storage driver in practice](aufs-driver.md)
|
||||
* [Btrfs storage driver in practice](btrfs-driver.md)
|
||||
* [BTRFS storage driver in practice](btrfs-driver.md)
|
||||
* [Device Mapper storage driver in practice](device-mapper-driver.md)
|
||||
|
||||
+4
-1
@@ -182,7 +182,10 @@ func (graph *Graph) restore() error {
|
||||
if graph.driver.Exists(id) {
|
||||
img, err := graph.loadImage(id)
|
||||
if err != nil {
|
||||
logrus.Warnf("ignoring image %s, it could not be restored: %v", id, err)
|
||||
if err != io.EOF {
|
||||
return fmt.Errorf("could not restore image %s: %v", id, err)
|
||||
}
|
||||
logrus.Warnf("could not restore image %s due to corrupted files", id)
|
||||
continue
|
||||
}
|
||||
graph.imageMutex.Lock(img.Parent)
|
||||
|
||||
@@ -3,8 +3,6 @@ package graph
|
||||
import (
|
||||
"bufio"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -201,11 +199,6 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
|
||||
p.layersPushed[dgst] = true
|
||||
}
|
||||
|
||||
// Fix parent chain if necessary
|
||||
if err = fixHistory(m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Infof("Signed manifest for %s:%s using daemon's key: %s", p.repo.Name(), tag, p.trustKey.KeyID())
|
||||
signed, err := manifest.Sign(m, p.trustKey)
|
||||
if err != nil {
|
||||
@@ -227,90 +220,6 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
|
||||
return manSvc.Put(signed)
|
||||
}
|
||||
|
||||
// fixHistory makes sure that the manifest has parent IDs that are consistent
|
||||
// with its image IDs. Because local image IDs are generated from the
|
||||
// configuration and filesystem contents, but IDs in the manifest are preserved
|
||||
// from the original pull, it's possible to have inconsistencies where parent
|
||||
// IDs don't match up with the other IDs in the manifest. This happens in the
|
||||
// case where an engine pulls images where are identical except the IDs from the
|
||||
// manifest - the local ID will be the same, and one of the v1Compatibility
|
||||
// files gets discarded.
|
||||
func fixHistory(m *manifest.Manifest) error {
|
||||
var lastID string
|
||||
|
||||
for i := len(m.History) - 1; i >= 0; i-- {
|
||||
var historyEntry map[string]*json.RawMessage
|
||||
if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), &historyEntry); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
idJSON, present := historyEntry["id"]
|
||||
if !present || idJSON == nil {
|
||||
return errors.New("missing id key in v1compatibility file")
|
||||
}
|
||||
var id string
|
||||
if err := json.Unmarshal(*idJSON, &id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parentJSON, present := historyEntry["parent"]
|
||||
|
||||
if i == len(m.History)-1 {
|
||||
// The base layer must not reference a parent layer,
|
||||
// otherwise the manifest is incomplete. There is an
|
||||
// exception for Windows to handle base layers.
|
||||
if present && parentJSON != nil {
|
||||
var parent string
|
||||
if err := json.Unmarshal(*parentJSON, &parent); err != nil {
|
||||
return err
|
||||
}
|
||||
if parent != "" {
|
||||
logrus.Debugf("parent id mismatch detected; fixing. parent reference: %s", parent)
|
||||
delete(historyEntry, "parent")
|
||||
fixedHistory, err := json.Marshal(historyEntry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.History[i].V1Compatibility = string(fixedHistory)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For all other layers, the parent ID should equal the
|
||||
// ID of the next item in the history list. If it
|
||||
// doesn't, fix it up (but preserve all other fields,
|
||||
// possibly including fields that aren't known to this
|
||||
// engine version).
|
||||
if !present || parentJSON == nil {
|
||||
return errors.New("missing parent key in v1compatibility file")
|
||||
}
|
||||
var parent string
|
||||
if err := json.Unmarshal(*parentJSON, &parent); err != nil {
|
||||
return err
|
||||
}
|
||||
if parent != lastID {
|
||||
logrus.Debugf("parent id mismatch detected; fixing. parent reference: %s actual id: %s", parent, id)
|
||||
historyEntry["parent"] = rawJSON(lastID)
|
||||
fixedHistory, err := json.Marshal(historyEntry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.History[i].V1Compatibility = string(fixedHistory)
|
||||
}
|
||||
}
|
||||
lastID = id
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func rawJSON(value interface{}) *json.RawMessage {
|
||||
jsonval, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return (*json.RawMessage)(&jsonval)
|
||||
}
|
||||
|
||||
func (p *v2Pusher) pushV2Image(bs distribution.BlobService, img *image.Image) (digest.Digest, error) {
|
||||
out := p.config.OutStream
|
||||
|
||||
|
||||
@@ -24,9 +24,6 @@ import (
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
||||
// ErrNameIsNotExist returned when there is no image with requested name.
|
||||
var ErrNameIsNotExist = errors.New("image with specified name does not exist")
|
||||
|
||||
// TagStore manages repositories. It encompasses the Graph used for versioned
|
||||
// storage, as well as various services involved in pushing and pulling
|
||||
// repositories.
|
||||
@@ -167,26 +164,6 @@ func (store *TagStore) LookupImage(name string) (*image.Image, error) {
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// GetID returns ID for image name.
|
||||
func (store *TagStore) GetID(name string) (string, error) {
|
||||
repoName, ref := parsers.ParseRepositoryTag(name)
|
||||
if ref == "" {
|
||||
ref = tags.DefaultTag
|
||||
}
|
||||
store.Lock()
|
||||
defer store.Unlock()
|
||||
repoName = registry.NormalizeLocalName(repoName)
|
||||
repo, ok := store.Repositories[repoName]
|
||||
if !ok {
|
||||
return "", ErrNameIsNotExist
|
||||
}
|
||||
id, ok := repo[ref]
|
||||
if !ok {
|
||||
return "", ErrNameIsNotExist
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// ByID returns a reverse-lookup table of all the names which refer to each
|
||||
// image - e.g. {"43b5f19b10584": {"base:latest", "base:v1"}}
|
||||
func (store *TagStore) ByID() map[string][]string {
|
||||
|
||||
+2
-62
@@ -80,16 +80,6 @@ check_forked() {
|
||||
fi
|
||||
}
|
||||
|
||||
rpm_import_repository_key() {
|
||||
local key=$1; shift
|
||||
local tmpdir=$(mktemp -d)
|
||||
chmod 600 "$tmpdir"
|
||||
gpg --homedir "$tmpdir" --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"
|
||||
gpg --homedir "$tmpdir" --export --armor "$key" > "$tmpdir"/repo.key
|
||||
rpm --import "$tmpdir"/repo.key
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
do_install() {
|
||||
case "$(uname -m)" in
|
||||
*64)
|
||||
@@ -241,60 +231,10 @@ do_install() {
|
||||
exit 0
|
||||
;;
|
||||
|
||||
'opensuse project'|opensuse)
|
||||
echo 'Going to perform the following operations:'
|
||||
if [ "$repo" != 'main' ]; then
|
||||
echo ' * add repository obs://Virtualization:containers'
|
||||
fi
|
||||
echo ' * install Docker'
|
||||
$sh_c 'echo "Press CTRL-C to abort"; sleep 3'
|
||||
|
||||
if [ "$repo" != 'main' ]; then
|
||||
# install experimental packages from OBS://Virtualization:containers
|
||||
(
|
||||
set -x
|
||||
zypper -n ar -f obs://Virtualization:containers Virtualization:containers
|
||||
rpm_import_repository_key 55A0B34D49501BB7CA474F5AA193FBB572174FC2
|
||||
)
|
||||
fi
|
||||
'opensuse project'|opensuse|'suse linux'|sle[sd])
|
||||
(
|
||||
set -x
|
||||
zypper -n install docker
|
||||
)
|
||||
echo_docker_as_nonroot
|
||||
exit 0
|
||||
;;
|
||||
'suse linux'|sle[sd])
|
||||
echo 'Going to perform the following operations:'
|
||||
if [ "$repo" != 'main' ]; then
|
||||
echo ' * add repository obs://Virtualization:containers'
|
||||
echo ' * install experimental Docker using packages NOT supported by SUSE'
|
||||
else
|
||||
echo ' * add the "Containers" module'
|
||||
echo ' * install Docker using packages supported by SUSE'
|
||||
fi
|
||||
$sh_c 'echo "Press CTRL-C to abort"; sleep 3'
|
||||
|
||||
if [ "$repo" != 'main' ]; then
|
||||
# install experimental packages from OBS://Virtualization:containers
|
||||
echo >&2 'Warning: installing experimental packages from OBS, these packages are NOT supported by SUSE'
|
||||
(
|
||||
set -x
|
||||
zypper -n ar -f obs://Virtualization:containers/SLE_12 Virtualization:containers
|
||||
rpm_import_repository_key 55A0B34D49501BB7CA474F5AA193FBB572174FC2
|
||||
)
|
||||
else
|
||||
# Add the containers module
|
||||
# Note well-1: the SLE machine must already be registered against SUSE Customer Center
|
||||
# Note well-2: the `-r ""` is required to workaround a known issue of SUSEConnect
|
||||
(
|
||||
set -x
|
||||
SUSEConnect -p sle-module-containers/12/x86_64 -r ""
|
||||
)
|
||||
fi
|
||||
(
|
||||
set -x
|
||||
zypper -n install docker
|
||||
$sh_c 'sleep 3; zypper -n install docker'
|
||||
)
|
||||
echo_docker_as_nonroot
|
||||
exit 0
|
||||
|
||||
+2
-4
@@ -20,7 +20,7 @@ clone git github.com/tchap/go-patricia v2.1.0
|
||||
clone git golang.org/x/net 3cffabab72adf04f8e3b01c5baf775361837b5fe https://github.com/golang/net.git
|
||||
|
||||
#get libnetwork packages
|
||||
clone git github.com/docker/libnetwork b4ddf18317b19d6e4bcc821145589749206a7d00
|
||||
clone git github.com/docker/libnetwork e7719596c01a83f9ef24d33e9d609a64acacd7b8
|
||||
clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
||||
clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
|
||||
clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4
|
||||
@@ -46,9 +46,7 @@ clone git github.com/endophage/gotuf 2df1c8e0a7b7e10ae2113bf37aaa1bf1c1de8cc5
|
||||
clone git github.com/jfrazelle/go 6e461eb70cb4187b41a84e9a567d7137bdbe0f16
|
||||
clone git github.com/agl/ed25519 d2b94fd789ea21d12fac1a4443dd3a3f79cda72c
|
||||
|
||||
# this runc commit from branch relabel_fix_docker_1.9.1, pls remove it when you
|
||||
# update next time
|
||||
clone git github.com/opencontainers/runc 1349b37bd56f4f5ce2690b5b2c0f53f88a261c67 # libcontainer
|
||||
clone git github.com/opencontainers/runc 6c198ae2d065c37f44316e0de3df7f3b88950923 # libcontainer
|
||||
# libcontainer deps (see src/github.com/opencontainers/runc/Godeps/Godeps.json)
|
||||
clone git github.com/coreos/go-systemd v3
|
||||
clone git github.com/godbus/dbus v2
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
|
||||
"github.com/docker/docker/builder/dockerfile/command"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
@@ -6224,127 +6223,3 @@ func (s *DockerSuite) TestBuildNoNamedVolume(c *check.C) {
|
||||
_, err := buildImage("test", dockerFile, false)
|
||||
c.Assert(err, check.NotNil, check.Commentf("image build should have failed"))
|
||||
}
|
||||
|
||||
// #17290
|
||||
func (s *DockerSuite) TestBuildCacheBrokenSymlink(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
name := "testbuildbrokensymlink"
|
||||
ctx, err := fakeContext(`
|
||||
FROM busybox
|
||||
COPY . ./`,
|
||||
map[string]string{
|
||||
"foo": "bar",
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer ctx.Close()
|
||||
|
||||
err = os.Symlink(filepath.Join(ctx.Dir, "nosuchfile"), filepath.Join(ctx.Dir, "asymlink"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// warm up cache
|
||||
_, err = buildImageFromContext(name, ctx, true)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// add new file to context, should invalidate cache
|
||||
err = ioutil.WriteFile(filepath.Join(ctx.Dir, "newfile"), []byte("foo"), 0644)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
_, out, err := buildImageFromContextWithOut(name, ctx, true)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
c.Assert(out, checker.Not(checker.Contains), "Using cache")
|
||||
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestBuildFollowSymlinkToFile(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
name := "testbuildbrokensymlink"
|
||||
ctx, err := fakeContext(`
|
||||
FROM busybox
|
||||
COPY asymlink target`,
|
||||
map[string]string{
|
||||
"foo": "bar",
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer ctx.Close()
|
||||
|
||||
err = os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
id, err := buildImageFromContext(name, ctx, true)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
out, _ := dockerCmd(c, "run", "--rm", id, "cat", "target")
|
||||
c.Assert(out, checker.Matches, "bar")
|
||||
|
||||
// change target file should invalidate cache
|
||||
err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
id, out, err = buildImageFromContextWithOut(name, ctx, true)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(out, checker.Not(checker.Contains), "Using cache")
|
||||
|
||||
out, _ = dockerCmd(c, "run", "--rm", id, "cat", "target")
|
||||
c.Assert(out, checker.Matches, "baz")
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestBuildFollowSymlinkToDir(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
name := "testbuildbrokensymlink"
|
||||
ctx, err := fakeContext(`
|
||||
FROM busybox
|
||||
COPY asymlink /`,
|
||||
map[string]string{
|
||||
"foo/abc": "bar",
|
||||
"foo/def": "baz",
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer ctx.Close()
|
||||
|
||||
err = os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
id, err := buildImageFromContext(name, ctx, true)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
out, _ := dockerCmd(c, "run", "--rm", id, "cat", "abc", "def")
|
||||
c.Assert(out, checker.Matches, "barbaz")
|
||||
|
||||
// change target file should invalidate cache
|
||||
err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo/def"), []byte("bax"), 0644)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
id, out, err = buildImageFromContextWithOut(name, ctx, true)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(out, checker.Not(checker.Contains), "Using cache")
|
||||
|
||||
out, _ = dockerCmd(c, "run", "--rm", id, "cat", "abc", "def")
|
||||
c.Assert(out, checker.Matches, "barbax")
|
||||
|
||||
}
|
||||
|
||||
// TestBuildSymlinkBasename tests that target file gets basename from symlink,
|
||||
// not from the target file.
|
||||
func (s *DockerSuite) TestBuildSymlinkBasename(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
name := "testbuildbrokensymlink"
|
||||
ctx, err := fakeContext(`
|
||||
FROM busybox
|
||||
COPY asymlink /`,
|
||||
map[string]string{
|
||||
"foo": "bar",
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer ctx.Close()
|
||||
|
||||
err = os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
id, err := buildImageFromContext(name, ctx, true)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
out, _ := dockerCmd(c, "run", "--rm", id, "cat", "asymlink")
|
||||
c.Assert(out, checker.Matches, "bar")
|
||||
|
||||
}
|
||||
|
||||
@@ -377,29 +377,6 @@ func (s *DockerSuite) TestDaemonIPv6FixedCIDR(c *check.C) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestDaemonIPv6FixedCIDRAndMac checks that when the daemon is started with ipv6 fixed CIDR
|
||||
// the running containers are given a an IPv6 address derived from the MAC address and the ipv6 fixed CIDR
|
||||
func (s *DockerSuite) TestDaemonIPv6FixedCIDRAndMac(c *check.C) {
|
||||
err := setupV6()
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
d := NewDaemon(c)
|
||||
|
||||
err = d.StartWithBusybox("--ipv6", "--fixed-cidr-v6='2001:db8:1::/64'")
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer d.Stop()
|
||||
|
||||
out, err := d.Cmd("run", "-itd", "--name=ipv6test", "--mac-address", "AA:BB:CC:DD:EE:FF", "busybox")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
out, err = d.Cmd("inspect", "--format", "'{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}'", "ipv6test")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(strings.Trim(out, " \r\n'"), checker.Equals, "2001:db8:1::aabb:ccdd:eeff")
|
||||
|
||||
err = teardownV6()
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *DockerDaemonSuite) TestDaemonLogLevelWrong(c *check.C) {
|
||||
c.Assert(s.d.Start("--log-level=bogus"), check.NotNil, check.Commentf("Daemon shouldn't start with wrong log level"))
|
||||
}
|
||||
@@ -817,33 +794,7 @@ func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *check.C) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr2(c *check.C) {
|
||||
d := s.d
|
||||
|
||||
bridgeName := "external-bridge"
|
||||
bridgeIP := "10.2.2.1/16"
|
||||
|
||||
out, err := createInterface(c, "bridge", bridgeName, bridgeIP)
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
defer deleteInterface(c, bridgeName)
|
||||
|
||||
err = d.StartWithBusybox("--bip", bridgeIP, "--fixed-cidr", "10.2.2.0/24")
|
||||
c.Assert(err, check.IsNil)
|
||||
defer s.d.Restart()
|
||||
|
||||
out, err = d.Cmd("run", "-d", "--name", "bb", "busybox", "top")
|
||||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||
defer d.Cmd("stop", "bb")
|
||||
|
||||
out, err = d.Cmd("exec", "bb", "/bin/sh", "-c", "ifconfig eth0 | awk '/inet addr/{print substr($2,6)}'")
|
||||
c.Assert(out, checker.Equals, "10.2.2.0\n")
|
||||
|
||||
out, err = d.Cmd("run", "--rm", "busybox", "/bin/sh", "-c", "ifconfig eth0 | awk '/inet addr/{print substr($2,6)}'")
|
||||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||
c.Assert(out, checker.Equals, "10.2.2.2\n")
|
||||
}
|
||||
|
||||
func (s *DockerDaemonSuite) TestDaemonBridgeFixedCIDREqualBridgeNetwork(c *check.C) {
|
||||
func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidrFixedCIDREqualBridgeNetwork(c *check.C) {
|
||||
d := s.d
|
||||
|
||||
bridgeName := "external-bridge"
|
||||
@@ -1291,10 +1242,10 @@ func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneLogsError(c *check.C) {
|
||||
}
|
||||
id := strings.TrimSpace(out)
|
||||
out, err = s.d.Cmd("logs", id)
|
||||
if err == nil {
|
||||
c.Fatalf("Logs should fail with 'none' driver")
|
||||
if err != nil {
|
||||
c.Fatalf("Logs request should be sent and then fail with \"none\" driver")
|
||||
}
|
||||
if !strings.Contains(out, `"logs" command is supported only for "json-file" and "journald" logging drivers (got: none)`) {
|
||||
if !strings.Contains(out, `Error running logs job: Failed to get logging factory: logger: no log driver named 'none' is registered`) {
|
||||
c.Fatalf("There should be an error about none not being a recognized log driver, got: %s", out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,11 +389,3 @@ func (s *DockerSuite) TestInspectTempateError(c *check.C) {
|
||||
c.Assert(err, check.Not(check.IsNil))
|
||||
c.Assert(out, checker.Contains, "Template parsing error")
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestInspectJSONFields(c *check.C) {
|
||||
dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "top")
|
||||
out, _, err := dockerCmdWithError("inspect", "--type=container", "--format='{{.HostConfig.Dns}}'", "busybox")
|
||||
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(out, checker.Equals, "[]\n")
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/versions/v1p20"
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
remoteapi "github.com/docker/libnetwork/drivers/remote/api"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
@@ -517,7 +516,6 @@ func (s *DockerDaemonSuite) TestDockerNetworkNoDiscoveryDefaultBridgeNetwork(c *
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *check.C) {
|
||||
hostsFile := "/etc/hosts"
|
||||
cstmBridgeNw := "custom-bridge-nw"
|
||||
cstmBridgeNw1 := "custom-bridge-nw1"
|
||||
|
||||
dockerCmd(c, "network", "create", "-d", "bridge", cstmBridgeNw)
|
||||
assertNwIsAvailable(c, cstmBridgeNw)
|
||||
@@ -541,18 +539,6 @@ func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *check.C) {
|
||||
c.Assert(string(hosts1), checker.Equals, string(hosts1post),
|
||||
check.Commentf("Unexpected %s change on anonymous container creation", hostsFile))
|
||||
|
||||
// Connect the 2nd container to a new network and verify the
|
||||
// first container /etc/hosts file still hasn't changed.
|
||||
dockerCmd(c, "network", "create", "-d", "bridge", cstmBridgeNw1)
|
||||
assertNwIsAvailable(c, cstmBridgeNw1)
|
||||
|
||||
dockerCmd(c, "network", "connect", cstmBridgeNw1, cid2)
|
||||
|
||||
hosts1post, err = readContainerFileWithExec(cid1, hostsFile)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(string(hosts1), checker.Equals, string(hosts1post),
|
||||
check.Commentf("Unexpected %s change on container connect", hostsFile))
|
||||
|
||||
// start a named container
|
||||
cName := "AnyName"
|
||||
out, _ = dockerCmd(c, "run", "-d", "--net", cstmBridgeNw, "--name", cName, "busybox", "top")
|
||||
@@ -739,46 +725,3 @@ func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksUngracefulDaemonRe
|
||||
|
||||
verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList)
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkRunNetByID(c *check.C) {
|
||||
out, _ := dockerCmd(c, "network", "create", "one")
|
||||
dockerCmd(c, "run", "-d", "--net", strings.TrimSpace(out), "busybox", "top")
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkConnectToHostFromOtherNetwork(c *check.C) {
|
||||
dockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top")
|
||||
c.Assert(waitRun("container1"), check.IsNil)
|
||||
dockerCmd(c, "network", "disconnect", "bridge", "container1")
|
||||
out, _, err := dockerCmdWithError("network", "connect", "host", "container1")
|
||||
c.Assert(err, checker.NotNil, check.Commentf(out))
|
||||
c.Assert(out, checker.Contains, runconfig.ErrConflictHostNetwork.Error())
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkDisconnectFromHost(c *check.C) {
|
||||
dockerCmd(c, "run", "-d", "--name", "container1", "--net=host", "busybox", "top")
|
||||
c.Assert(waitRun("container1"), check.IsNil)
|
||||
out, _, err := dockerCmdWithError("network", "disconnect", "host", "container1")
|
||||
c.Assert(err, checker.NotNil, check.Commentf("Should err out disconnect from host"))
|
||||
c.Assert(out, checker.Contains, runconfig.ErrConflictHostNetwork.Error())
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkConnectWithPortMapping(c *check.C) {
|
||||
dockerCmd(c, "network", "create", "test1")
|
||||
dockerCmd(c, "run", "-d", "--name", "c1", "-p", "5000:5000", "busybox", "top")
|
||||
c.Assert(waitRun("c1"), check.IsNil)
|
||||
dockerCmd(c, "network", "connect", "test1", "c1")
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkConnectWithMac(c *check.C) {
|
||||
macAddress := "02:42:ac:11:00:02"
|
||||
dockerCmd(c, "network", "create", "mynetwork")
|
||||
dockerCmd(c, "run", "--name=test", "-d", "--mac-address", macAddress, "busybox", "top")
|
||||
c.Assert(waitRun("test"), check.IsNil)
|
||||
mac1, err := inspectField("test", "NetworkSettings.Networks.bridge.MacAddress")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(strings.TrimSpace(mac1), checker.Equals, macAddress)
|
||||
dockerCmd(c, "network", "connect", "mynetwork", "test")
|
||||
mac2, err := inspectField("test", "NetworkSettings.Networks.mynetwork.MacAddress")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(strings.TrimSpace(mac2), checker.Not(checker.Equals), strings.TrimSpace(mac1))
|
||||
}
|
||||
|
||||
@@ -2,16 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
@@ -100,46 +97,6 @@ func (s *DockerRegistrySuite) TestPushMultipleTags(c *check.C) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestPushBadParentChain tries to push an image with a corrupted parent chain
|
||||
// in the v1compatibility files, and makes sure the push process fixes it.
|
||||
func (s *DockerRegistrySuite) TestPushBadParentChain(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockercli/badparent", privateRegistryURL)
|
||||
|
||||
id, err := buildImage(repoName, `
|
||||
FROM busybox
|
||||
CMD echo "adding another layer"
|
||||
`, true)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// Push to create v1compatibility file
|
||||
dockerCmd(c, "push", repoName)
|
||||
|
||||
// Corrupt the parent in the v1compatibility file from the top layer
|
||||
filename := filepath.Join(dockerBasePath, "graph", id, "v1Compatibility")
|
||||
|
||||
jsonBytes, err := ioutil.ReadFile(filename)
|
||||
c.Assert(err, check.IsNil, check.Commentf("Could not read v1Compatibility file: %s", err))
|
||||
|
||||
var img image.Image
|
||||
err = json.Unmarshal(jsonBytes, &img)
|
||||
c.Assert(err, check.IsNil, check.Commentf("Could not unmarshal json: %s", err))
|
||||
|
||||
img.Parent = "1234123412341234123412341234123412341234123412341234123412341234"
|
||||
|
||||
jsonBytes, err = json.Marshal(&img)
|
||||
c.Assert(err, check.IsNil, check.Commentf("Could not marshal json: %s", err))
|
||||
|
||||
err = ioutil.WriteFile(filename, jsonBytes, 0600)
|
||||
c.Assert(err, check.IsNil, check.Commentf("Could not write v1Compatibility file: %s", err))
|
||||
|
||||
dockerCmd(c, "push", repoName)
|
||||
|
||||
// pull should succeed
|
||||
dockerCmd(c, "pull", repoName)
|
||||
}
|
||||
|
||||
func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockercli/emptylayer", privateRegistryURL)
|
||||
emptyTarball, err := ioutil.TempFile("", "empty_tarball")
|
||||
|
||||
@@ -1214,14 +1214,6 @@ func buildImage(name, dockerfile string, useCache bool, buildFlags ...string) (s
|
||||
}
|
||||
|
||||
func buildImageFromContext(name string, ctx *FakeContext, useCache bool, buildFlags ...string) (string, error) {
|
||||
id, _, err := buildImageFromContextWithOut(name, ctx, useCache, buildFlags...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func buildImageFromContextWithOut(name string, ctx *FakeContext, useCache bool, buildFlags ...string) (string, string, error) {
|
||||
args := []string{"build", "-t", name}
|
||||
if !useCache {
|
||||
args = append(args, "--no-cache")
|
||||
@@ -1232,13 +1224,9 @@ func buildImageFromContextWithOut(name string, ctx *FakeContext, useCache bool,
|
||||
buildCmd.Dir = ctx.Dir
|
||||
out, exitCode, err := runCommandWithOutput(buildCmd)
|
||||
if err != nil || exitCode != 0 {
|
||||
return "", "", fmt.Errorf("failed to build the image: %s", out)
|
||||
return "", fmt.Errorf("failed to build the image: %s", out)
|
||||
}
|
||||
id, err := getIDByName(name)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return id, out, nil
|
||||
return getIDByName(name)
|
||||
}
|
||||
|
||||
func buildImageFromPath(name, path string, useCache bool, buildFlags ...string) (string, error) {
|
||||
|
||||
@@ -101,16 +101,6 @@ func (opts *ListOpts) GetAll() []string {
|
||||
return (*opts.values)
|
||||
}
|
||||
|
||||
// GetAllOrEmpty returns the values of the slice
|
||||
// or an empty slice when there are no values.
|
||||
func (opts *ListOpts) GetAllOrEmpty() []string {
|
||||
v := *opts.values
|
||||
if v == nil {
|
||||
return make([]string, 0)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Get checks the existence of the specified key.
|
||||
func (opts *ListOpts) Get(key string) bool {
|
||||
for _, k := range *opts.values {
|
||||
|
||||
+6
-21
@@ -25,7 +25,6 @@ func UnpackLayer(dest string, layer Reader, options *TarOptions) (size int64, er
|
||||
defer pools.BufioReader32KPool.Put(trBuf)
|
||||
|
||||
var dirs []*tar.Header
|
||||
unpackedPaths := make(map[string]struct{})
|
||||
|
||||
if options == nil {
|
||||
options = &TarOptions{}
|
||||
@@ -135,27 +134,14 @@ func UnpackLayer(dest string, layer Reader, options *TarOptions) (size int64, er
|
||||
if strings.HasPrefix(base, WhiteoutPrefix) {
|
||||
dir := filepath.Dir(path)
|
||||
if base == WhiteoutOpaqueDir {
|
||||
_, err := os.Lstat(dir)
|
||||
if err != nil {
|
||||
fi, err := os.Lstat(dir)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return 0, err
|
||||
}
|
||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = nil // parent was deleted
|
||||
}
|
||||
return err
|
||||
}
|
||||
if path == dir {
|
||||
return nil
|
||||
}
|
||||
if _, exists := unpackedPaths[path]; !exists {
|
||||
err := os.RemoveAll(path)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
if err := os.RemoveAll(dir); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := os.Mkdir(dir, fi.Mode()&os.ModePerm); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
@@ -228,7 +214,6 @@ func UnpackLayer(dest string, layer Reader, options *TarOptions) (size int64, er
|
||||
if hdr.Typeflag == tar.TypeDir {
|
||||
dirs = append(dirs, hdr)
|
||||
}
|
||||
unpackedPaths[path] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,7 @@ package archive
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
)
|
||||
|
||||
func TestApplyLayerInvalidFilenames(t *testing.T) {
|
||||
@@ -195,176 +188,3 @@ func TestApplyLayerInvalidSymlink(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyLayerWhiteouts(t *testing.T) {
|
||||
wd, err := ioutil.TempDir("", "graphdriver-test-whiteouts")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(wd)
|
||||
|
||||
base := []string{
|
||||
".baz",
|
||||
"bar/",
|
||||
"bar/bax",
|
||||
"bar/bay/",
|
||||
"baz",
|
||||
"foo/",
|
||||
"foo/.abc",
|
||||
"foo/.bcd/",
|
||||
"foo/.bcd/a",
|
||||
"foo/cde/",
|
||||
"foo/cde/def",
|
||||
"foo/cde/efg",
|
||||
"foo/fgh",
|
||||
"foobar",
|
||||
}
|
||||
|
||||
type tcase struct {
|
||||
change, expected []string
|
||||
}
|
||||
|
||||
tcases := []tcase{
|
||||
{
|
||||
base,
|
||||
base,
|
||||
},
|
||||
{
|
||||
[]string{
|
||||
".bay",
|
||||
".wh.baz",
|
||||
"foo/",
|
||||
"foo/.bce",
|
||||
"foo/.wh..wh..opq",
|
||||
"foo/cde/",
|
||||
"foo/cde/efg",
|
||||
},
|
||||
[]string{
|
||||
".bay",
|
||||
".baz",
|
||||
"bar/",
|
||||
"bar/bax",
|
||||
"bar/bay/",
|
||||
"foo/",
|
||||
"foo/.bce",
|
||||
"foo/cde/",
|
||||
"foo/cde/efg",
|
||||
"foobar",
|
||||
},
|
||||
},
|
||||
{
|
||||
[]string{
|
||||
".bay",
|
||||
".wh..baz",
|
||||
".wh.foobar",
|
||||
"foo/",
|
||||
"foo/.abc",
|
||||
"foo/.wh.cde",
|
||||
"bar/",
|
||||
},
|
||||
[]string{
|
||||
".bay",
|
||||
"bar/",
|
||||
"bar/bax",
|
||||
"bar/bay/",
|
||||
"foo/",
|
||||
"foo/.abc",
|
||||
"foo/.bce",
|
||||
},
|
||||
},
|
||||
{
|
||||
[]string{
|
||||
".abc",
|
||||
".wh..wh..opq",
|
||||
"foobar",
|
||||
},
|
||||
[]string{
|
||||
".abc",
|
||||
"foobar",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tcases {
|
||||
l, err := makeTestLayer(tc.change)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = UnpackLayer(wd, l, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = l.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
paths, err := readDirContents(wd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tc.expected, paths) {
|
||||
t.Fatalf("invalid files for layer %d: expected %q, got %q", i, tc.expected, paths)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func makeTestLayer(paths []string) (rc io.ReadCloser, err error) {
|
||||
tmpDir, err := ioutil.TempDir("", "graphdriver-test-mklayer")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
os.RemoveAll(tmpDir)
|
||||
}
|
||||
}()
|
||||
for _, p := range paths {
|
||||
if p[len(p)-1] == filepath.Separator {
|
||||
if err = os.MkdirAll(filepath.Join(tmpDir, p), 0700); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err = ioutil.WriteFile(filepath.Join(tmpDir, p), nil, 0600); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
archive, err := Tar(tmpDir, Uncompressed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return ioutils.NewReadCloserWrapper(archive, func() error {
|
||||
err := archive.Close()
|
||||
os.RemoveAll(tmpDir)
|
||||
return err
|
||||
}), nil
|
||||
}
|
||||
|
||||
func readDirContents(root string) ([]string, error) {
|
||||
var files []string
|
||||
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if path == root {
|
||||
return nil
|
||||
}
|
||||
rel, err := filepath.Rel(root, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
rel = rel + "/"
|
||||
}
|
||||
files = append(files, rel)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
+10
-51
@@ -1,54 +1,32 @@
|
||||
package ioutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// WriteFlusher wraps the Write and Flush operation ensuring that every write
|
||||
// is a flush. In addition, the Close method can be called to intercept
|
||||
// Read/Write calls if the targets lifecycle has already ended.
|
||||
// WriteFlusher wraps the Write and Flush operation.
|
||||
type WriteFlusher struct {
|
||||
mu sync.Mutex
|
||||
sync.Mutex
|
||||
w io.Writer
|
||||
flusher http.Flusher
|
||||
flushed bool
|
||||
closed error
|
||||
|
||||
// TODO(stevvooe): Use channel for closed instead, remove mutex. Using a
|
||||
// channel will allow one to properly order the operations.
|
||||
}
|
||||
|
||||
var errWriteFlusherClosed = errors.New("writeflusher: closed")
|
||||
|
||||
func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
|
||||
wf.mu.Lock()
|
||||
defer wf.mu.Unlock()
|
||||
if wf.closed != nil {
|
||||
return 0, wf.closed
|
||||
}
|
||||
|
||||
wf.Lock()
|
||||
defer wf.Unlock()
|
||||
n, err = wf.w.Write(b)
|
||||
wf.flush() // every write is a flush.
|
||||
wf.flushed = true
|
||||
wf.flusher.Flush()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Flush the stream immediately.
|
||||
func (wf *WriteFlusher) Flush() {
|
||||
wf.mu.Lock()
|
||||
defer wf.mu.Unlock()
|
||||
|
||||
wf.flush()
|
||||
}
|
||||
|
||||
// flush the stream immediately without taking a lock. Used internally.
|
||||
func (wf *WriteFlusher) flush() {
|
||||
if wf.closed != nil {
|
||||
return
|
||||
}
|
||||
|
||||
wf.Lock()
|
||||
defer wf.Unlock()
|
||||
wf.flushed = true
|
||||
wf.flusher.Flush()
|
||||
}
|
||||
@@ -56,30 +34,11 @@ func (wf *WriteFlusher) flush() {
|
||||
// Flushed returns the state of flushed.
|
||||
// If it's flushed, return true, or else it return false.
|
||||
func (wf *WriteFlusher) Flushed() bool {
|
||||
// BUG(stevvooe): Remove this method. Its use is inherently racy. Seems to
|
||||
// be used to detect whether or a response code has been issued or not.
|
||||
// Another hook should be used instead.
|
||||
wf.mu.Lock()
|
||||
defer wf.mu.Unlock()
|
||||
|
||||
wf.Lock()
|
||||
defer wf.Unlock()
|
||||
return wf.flushed
|
||||
}
|
||||
|
||||
// Close closes the write flusher, disallowing any further writes to the
|
||||
// target. After the flusher is closed, all calls to write or flush will
|
||||
// result in an error.
|
||||
func (wf *WriteFlusher) Close() error {
|
||||
wf.mu.Lock()
|
||||
defer wf.mu.Unlock()
|
||||
|
||||
if wf.closed != nil {
|
||||
return wf.closed
|
||||
}
|
||||
|
||||
wf.closed = errWriteFlusherClosed
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewWriteFlusher returns a new WriteFlusher.
|
||||
func NewWriteFlusher(w io.Writer) *WriteFlusher {
|
||||
var flusher http.Flusher
|
||||
|
||||
@@ -266,8 +266,7 @@ installed and available at runtime:
|
||||
|
||||
* iptables version 1.4 or later
|
||||
* procps (or similar provider of a "ps" executable)
|
||||
* e2fsprogs version 1.4.12 or later (in use: mkfs.ext4, tune2fs)
|
||||
* xfsprogs (in use: mkfs.xfs)
|
||||
* e2fsprogs version 1.4.12 or later (in use: mkfs.ext4, mkfs.xfs, tune2fs)
|
||||
* XZ Utils version 4.9 or later
|
||||
* a [properly
|
||||
mounted](https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount)
|
||||
|
||||
+24
-53
@@ -239,28 +239,15 @@ func validateNoSchema(reposName string) error {
|
||||
|
||||
// ValidateRepositoryName validates a repository name
|
||||
func ValidateRepositoryName(reposName string) error {
|
||||
_, _, err := loadRepositoryName(reposName, true)
|
||||
return err
|
||||
}
|
||||
|
||||
// loadRepositoryName returns the repo name splitted into index name
|
||||
// and remote repo name. It returns an error if the name is not valid.
|
||||
func loadRepositoryName(reposName string, checkRemoteName bool) (string, string, error) {
|
||||
if err := validateNoSchema(reposName); err != nil {
|
||||
return "", "", err
|
||||
var err error
|
||||
if err = validateNoSchema(reposName); err != nil {
|
||||
return err
|
||||
}
|
||||
indexName, remoteName := splitReposName(reposName)
|
||||
|
||||
var err error
|
||||
if indexName, err = ValidateIndexName(indexName); err != nil {
|
||||
return "", "", err
|
||||
if _, err = ValidateIndexName(indexName); err != nil {
|
||||
return err
|
||||
}
|
||||
if checkRemoteName {
|
||||
if err = validateRemoteName(remoteName); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
}
|
||||
return indexName, remoteName, nil
|
||||
return validateRemoteName(remoteName)
|
||||
}
|
||||
|
||||
// NewIndexInfo returns IndexInfo configuration from indexName
|
||||
@@ -314,22 +301,34 @@ func splitReposName(reposName string) (string, string) {
|
||||
|
||||
// NewRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
|
||||
func (config *ServiceConfig) NewRepositoryInfo(reposName string, bySearch bool) (*RepositoryInfo, error) {
|
||||
indexName, remoteName, err := loadRepositoryName(reposName, !bySearch)
|
||||
if err != nil {
|
||||
if err := validateNoSchema(reposName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexName, remoteName := splitReposName(reposName)
|
||||
|
||||
if !bySearch {
|
||||
if err := validateRemoteName(remoteName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
repoInfo := &RepositoryInfo{
|
||||
RemoteName: remoteName,
|
||||
}
|
||||
|
||||
var err error
|
||||
repoInfo.Index, err = config.NewIndexInfo(indexName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if repoInfo.Index.Official {
|
||||
normalizedName := normalizeLibraryRepoName(repoInfo.RemoteName)
|
||||
normalizedName := repoInfo.RemoteName
|
||||
if strings.HasPrefix(normalizedName, "library/") {
|
||||
// If pull "library/foo", it's stored locally under "foo"
|
||||
normalizedName = strings.SplitN(normalizedName, "/", 2)[1]
|
||||
}
|
||||
|
||||
repoInfo.LocalName = normalizedName
|
||||
repoInfo.RemoteName = normalizedName
|
||||
@@ -343,7 +342,7 @@ func (config *ServiceConfig) NewRepositoryInfo(reposName string, bySearch bool)
|
||||
|
||||
repoInfo.CanonicalName = "docker.io/" + repoInfo.RemoteName
|
||||
} else {
|
||||
repoInfo.LocalName = localNameFromRemote(repoInfo.Index.Name, repoInfo.RemoteName)
|
||||
repoInfo.LocalName = repoInfo.Index.Name + "/" + repoInfo.RemoteName
|
||||
repoInfo.CanonicalName = repoInfo.LocalName
|
||||
|
||||
}
|
||||
@@ -379,38 +378,10 @@ func ParseIndexInfo(reposName string) (*IndexInfo, error) {
|
||||
|
||||
// NormalizeLocalName transforms a repository name into a normalize LocalName
|
||||
// Passes through the name without transformation on error (image id, etc)
|
||||
// It does not use the repository info because we don't want to load
|
||||
// the repository index and do request over the network.
|
||||
func NormalizeLocalName(name string) string {
|
||||
indexName, remoteName, err := loadRepositoryName(name, true)
|
||||
repoInfo, err := ParseRepositoryInfo(name)
|
||||
if err != nil {
|
||||
return name
|
||||
}
|
||||
|
||||
var officialIndex bool
|
||||
// Return any configured index info, first.
|
||||
if index, ok := emptyServiceConfig.IndexConfigs[indexName]; ok {
|
||||
officialIndex = index.Official
|
||||
}
|
||||
|
||||
if officialIndex {
|
||||
return normalizeLibraryRepoName(remoteName)
|
||||
}
|
||||
return localNameFromRemote(indexName, remoteName)
|
||||
}
|
||||
|
||||
// normalizeLibraryRepoName removes the library prefix from
|
||||
// the repository name for official repos.
|
||||
func normalizeLibraryRepoName(name string) string {
|
||||
if strings.HasPrefix(name, "library/") {
|
||||
// If pull "library/foo", it's stored locally under "foo"
|
||||
name = strings.SplitN(name, "/", 2)[1]
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// localNameFromRemote combines the index name and the repo remote name
|
||||
// to generate a repo local name.
|
||||
func localNameFromRemote(indexName, remoteName string) string {
|
||||
return indexName + "/" + remoteName
|
||||
return repoInfo.LocalName
|
||||
}
|
||||
|
||||
+20
-27
@@ -21,8 +21,6 @@ var (
|
||||
ErrConflictUserDefinedNetworkAndLinks = fmt.Errorf("Conflicting options: --net=<NETWORK> can't be used with links. This would result in undefined behavior")
|
||||
// ErrConflictSharedNetwork conflict between private and other networks
|
||||
ErrConflictSharedNetwork = fmt.Errorf("Container sharing network namespace with another container or host cannot be connected to any other network")
|
||||
// ErrConflictHostNetwork conflict from being disconnected from host network or connected to host network.
|
||||
ErrConflictHostNetwork = fmt.Errorf("Container cannot be disconnected from host network or connected to host network")
|
||||
// ErrConflictNoNetwork conflict between private and other networks
|
||||
ErrConflictNoNetwork = fmt.Errorf("Container cannot be connected to multiple networks with one of the networks in --none mode")
|
||||
// ErrConflictNetworkAndDNS conflict between --dns and the network mode
|
||||
@@ -363,31 +361,26 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
||||
PortBindings: portBindings,
|
||||
Links: flLinks.GetAll(),
|
||||
PublishAllPorts: *flPublishAll,
|
||||
// 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.
|
||||
DNS: flDNS.GetAllOrEmpty(),
|
||||
DNSSearch: flDNSSearch.GetAllOrEmpty(),
|
||||
DNSOptions: flDNSOptions.GetAllOrEmpty(),
|
||||
ExtraHosts: flExtraHosts.GetAll(),
|
||||
VolumesFrom: flVolumesFrom.GetAll(),
|
||||
NetworkMode: NetworkMode(*flNetMode),
|
||||
IpcMode: ipcMode,
|
||||
PidMode: pidMode,
|
||||
UTSMode: utsMode,
|
||||
Devices: deviceMappings,
|
||||
CapAdd: stringutils.NewStrSlice(flCapAdd.GetAll()...),
|
||||
CapDrop: stringutils.NewStrSlice(flCapDrop.GetAll()...),
|
||||
GroupAdd: flGroupAdd.GetAll(),
|
||||
RestartPolicy: restartPolicy,
|
||||
SecurityOpt: flSecurityOpt.GetAll(),
|
||||
ReadonlyRootfs: *flReadonlyRootfs,
|
||||
Ulimits: flUlimits.GetList(),
|
||||
LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
|
||||
CgroupParent: *flCgroupParent,
|
||||
VolumeDriver: *flVolumeDriver,
|
||||
DNS: flDNS.GetAll(),
|
||||
DNSSearch: flDNSSearch.GetAll(),
|
||||
DNSOptions: flDNSOptions.GetAll(),
|
||||
ExtraHosts: flExtraHosts.GetAll(),
|
||||
VolumesFrom: flVolumesFrom.GetAll(),
|
||||
NetworkMode: NetworkMode(*flNetMode),
|
||||
IpcMode: ipcMode,
|
||||
PidMode: pidMode,
|
||||
UTSMode: utsMode,
|
||||
Devices: deviceMappings,
|
||||
CapAdd: stringutils.NewStrSlice(flCapAdd.GetAll()...),
|
||||
CapDrop: stringutils.NewStrSlice(flCapDrop.GetAll()...),
|
||||
GroupAdd: flGroupAdd.GetAll(),
|
||||
RestartPolicy: restartPolicy,
|
||||
SecurityOpt: flSecurityOpt.GetAll(),
|
||||
ReadonlyRootfs: *flReadonlyRootfs,
|
||||
Ulimits: flUlimits.GetList(),
|
||||
LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
|
||||
CgroupParent: *flCgroupParent,
|
||||
VolumeDriver: *flVolumeDriver,
|
||||
}
|
||||
|
||||
// When allocating stdin in attached mode, close stdin at client disconnect
|
||||
|
||||
+4
-1
@@ -128,11 +128,14 @@ type ipamData struct {
|
||||
|
||||
type driverTable map[string]*driverData
|
||||
|
||||
//type networkTable map[string]*network
|
||||
//type endpointTable map[string]*endpoint
|
||||
type ipamTable map[string]*ipamData
|
||||
type sandboxTable map[string]*sandbox
|
||||
|
||||
type controller struct {
|
||||
id string
|
||||
id string
|
||||
//networks networkTable
|
||||
drivers driverTable
|
||||
ipamDrivers ipamTable
|
||||
sandboxes sandboxTable
|
||||
|
||||
@@ -451,8 +451,6 @@ func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []d
|
||||
}
|
||||
|
||||
if len(ipamV6Data) > 0 {
|
||||
c.AddressIPv6 = ipamV6Data[0].Pool
|
||||
|
||||
if ipamV6Data[0].Gateway != nil {
|
||||
c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway)
|
||||
}
|
||||
@@ -741,9 +739,7 @@ func (d *driver) DeleteNetwork(nid string) error {
|
||||
|
||||
// We only delete the bridge when it's not the default bridge. This is keep the backward compatible behavior.
|
||||
if !config.DefaultBridge {
|
||||
if err := netlink.LinkDel(n.bridge.Link); err != nil {
|
||||
logrus.Warnf("Failed to remove bridge interface %s on network %s delete: %v", config.BridgeName, nid, err)
|
||||
}
|
||||
err = netlink.LinkDel(n.bridge.Link)
|
||||
}
|
||||
|
||||
return d.storeDelete(config)
|
||||
@@ -966,20 +962,13 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
|
||||
if endpoint.addrv6 == nil && config.EnableIPv6 {
|
||||
var ip6 net.IP
|
||||
network := n.bridge.bridgeIPv6
|
||||
if config.AddressIPv6 != nil {
|
||||
network = config.AddressIPv6
|
||||
}
|
||||
|
||||
ones, _ := network.Mask.Size()
|
||||
if ones > 80 {
|
||||
err = types.ForbiddenErrorf("Cannot self generate an IPv6 address on network %v: At least 48 host bits are needed.", network)
|
||||
return err
|
||||
}
|
||||
|
||||
ip6 = make(net.IP, len(network.IP))
|
||||
copy(ip6, network.IP)
|
||||
for i, h := range endpoint.macAddress {
|
||||
ip6[i+10] = h
|
||||
if ones <= 80 {
|
||||
ip6 = make(net.IP, len(network.IP))
|
||||
copy(ip6, network.IP)
|
||||
for i, h := range endpoint.macAddress {
|
||||
ip6[i+10] = h
|
||||
}
|
||||
}
|
||||
|
||||
endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask}
|
||||
@@ -1051,8 +1040,9 @@ func (d *driver) DeleteEndpoint(nid, eid string) error {
|
||||
// Remove port mappings. Do not stop endpoint delete on unmap failure
|
||||
n.releasePorts(ep)
|
||||
|
||||
// Try removal of link. Discard error: it is a best effort.
|
||||
// Also make sure defer does not see this error either.
|
||||
// Try removal of link. Discard error: link pair might have
|
||||
// already been deleted by sandbox delete. Make sure defer
|
||||
// does not see this error either.
|
||||
if link, err := netlink.LinkByName(ep.srcName); err == nil {
|
||||
netlink.LinkDel(link)
|
||||
}
|
||||
|
||||
+7
-19
@@ -584,8 +584,7 @@ func (ep *endpoint) Delete() error {
|
||||
ep.Lock()
|
||||
epid := ep.id
|
||||
name := ep.name
|
||||
sb, _ := n.getController().SandboxByID(ep.sandboxID)
|
||||
if sb != nil {
|
||||
if ep.sandboxID != "" {
|
||||
ep.Unlock()
|
||||
return &ActiveContainerError{name: name, id: epid}
|
||||
}
|
||||
@@ -738,7 +737,7 @@ func (ep *endpoint) DataScope() string {
|
||||
return ep.getNetwork().DataScope()
|
||||
}
|
||||
|
||||
func (ep *endpoint) assignAddress(assignIPv4, assignIPv6 bool) error {
|
||||
func (ep *endpoint) assignAddress() error {
|
||||
var (
|
||||
ipam ipamapi.Ipam
|
||||
err error
|
||||
@@ -755,18 +754,11 @@ func (ep *endpoint) assignAddress(assignIPv4, assignIPv6 bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if assignIPv4 {
|
||||
if err = ep.assignAddressVersion(4, ipam); err != nil {
|
||||
return err
|
||||
}
|
||||
err = ep.assignAddressVersion(4, ipam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if assignIPv6 {
|
||||
err = ep.assignAddressVersion(6, ipam)
|
||||
}
|
||||
|
||||
return err
|
||||
return ep.assignAddressVersion(6, ipam)
|
||||
}
|
||||
|
||||
func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
|
||||
@@ -795,11 +787,7 @@ func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
|
||||
}
|
||||
|
||||
for _, d := range ipInfo {
|
||||
var prefIP net.IP
|
||||
if *address != nil {
|
||||
prefIP = (*address).IP
|
||||
}
|
||||
addr, _, err := ipam.RequestAddress(d.PoolID, prefIP, nil)
|
||||
addr, _, err := ipam.RequestAddress(d.PoolID, nil, nil)
|
||||
if err == nil {
|
||||
ep.Lock()
|
||||
*address = addr
|
||||
|
||||
@@ -220,7 +220,7 @@ func (a *Allocator) parsePoolRequest(addressSpace, pool, subPool string, v6 bool
|
||||
return nil, nil, nil, ipamapi.ErrInvalidPool
|
||||
}
|
||||
if subPool != "" {
|
||||
if ipr, err = getAddressRange(subPool, nw); err != nil {
|
||||
if ipr, err = getAddressRange(subPool); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
@@ -431,6 +431,9 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
|
||||
aSpace.Unlock()
|
||||
|
||||
mask := p.Pool.Mask
|
||||
if p.Range != nil {
|
||||
mask = p.Range.Sub.Mask
|
||||
}
|
||||
|
||||
h, err := types.GetHostPartIP(address, mask)
|
||||
if err != nil {
|
||||
@@ -468,6 +471,7 @@ func (a *Allocator) getAddress(nw *net.IPNet, bitmask *bitseq.Handle, prefAddres
|
||||
ordinal = ipToUint64(types.GetMinimalIP(hostPart))
|
||||
err = bitmask.Set(ordinal)
|
||||
} else {
|
||||
base.IP = ipr.Sub.IP
|
||||
ordinal, err = bitmask.SetAnyInRange(ipr.Start, ipr.End)
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
+3
-3
@@ -15,12 +15,12 @@ const (
|
||||
v6 = 6
|
||||
)
|
||||
|
||||
func getAddressRange(pool string, masterNw *net.IPNet) (*AddressRange, error) {
|
||||
func getAddressRange(pool string) (*AddressRange, error) {
|
||||
ip, nw, err := net.ParseCIDR(pool)
|
||||
if err != nil {
|
||||
return nil, ipamapi.ErrInvalidSubPool
|
||||
}
|
||||
lIP, e := types.GetHostPartIP(nw.IP, masterNw.Mask)
|
||||
lIP, e := types.GetHostPartIP(nw.IP, nw.Mask)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to compute range's lowest ip address: %v", e)
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func getAddressRange(pool string, masterNw *net.IPNet) (*AddressRange, error) {
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to compute range's broadcast ip address: %v", e)
|
||||
}
|
||||
hIP, e := types.GetHostPartIP(bIP, masterNw.Mask)
|
||||
hIP, e := types.GetHostPartIP(bIP, nw.Mask)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to compute range's highest ip address: %v", e)
|
||||
}
|
||||
|
||||
+1
-21
@@ -152,7 +152,6 @@ type network struct {
|
||||
ipamV4Info []*IpamInfo
|
||||
ipamV6Info []*IpamInfo
|
||||
enableIPv6 bool
|
||||
postIPv6 bool
|
||||
epCnt *endpointCnt
|
||||
generic options.Generic
|
||||
dbIndex uint64
|
||||
@@ -299,7 +298,6 @@ func (n *network) CopyTo(o datastore.KVObject) error {
|
||||
dstN.ipamType = n.ipamType
|
||||
dstN.enableIPv6 = n.enableIPv6
|
||||
dstN.persist = n.persist
|
||||
dstN.postIPv6 = n.postIPv6
|
||||
dstN.dbIndex = n.dbIndex
|
||||
dstN.dbExists = n.dbExists
|
||||
dstN.drvOnce = n.drvOnce
|
||||
@@ -360,7 +358,6 @@ func (n *network) MarshalJSON() ([]byte, error) {
|
||||
netMap["generic"] = n.generic
|
||||
}
|
||||
netMap["persist"] = n.persist
|
||||
netMap["postIPv6"] = n.postIPv6
|
||||
if len(n.ipamV4Config) > 0 {
|
||||
ics, err := json.Marshal(n.ipamV4Config)
|
||||
if err != nil {
|
||||
@@ -421,9 +418,6 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
|
||||
if v, ok := netMap["persist"]; ok {
|
||||
n.persist = v.(bool)
|
||||
}
|
||||
if v, ok := netMap["postIPv6"]; ok {
|
||||
n.postIPv6 = v.(bool)
|
||||
}
|
||||
if v, ok := netMap["ipamType"]; ok {
|
||||
n.ipamType = v.(string)
|
||||
} else {
|
||||
@@ -511,16 +505,6 @@ func NetworkOptionDriverOpts(opts map[string]string) NetworkOption {
|
||||
}
|
||||
}
|
||||
|
||||
// NetworkOptionDeferIPv6Alloc instructs the network to defer the IPV6 address allocation until after the endpoint has been created
|
||||
// It is being provided to support the specific docker daemon flags where user can deterministically assign an IPv6 address
|
||||
// to a container as combination of fixed-cidr-v6 + mac-address
|
||||
// TODO: Remove this option setter once we support endpoint ipam options
|
||||
func NetworkOptionDeferIPv6Alloc(enable bool) NetworkOption {
|
||||
return func(n *network) {
|
||||
n.postIPv6 = enable
|
||||
}
|
||||
}
|
||||
|
||||
func (n *network) processOptions(options ...NetworkOption) {
|
||||
for _, opt := range options {
|
||||
if opt != nil {
|
||||
@@ -671,7 +655,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
|
||||
|
||||
ep.processOptions(options...)
|
||||
|
||||
if err = ep.assignAddress(true, !n.postIPv6); err != nil {
|
||||
if err = ep.assignAddress(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
@@ -691,10 +675,6 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
|
||||
}
|
||||
}()
|
||||
|
||||
if err = ep.assignAddress(false, n.postIPv6); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = n.getController().updateToStore(ep); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+9
-25
@@ -177,18 +177,13 @@ func (sb *sandbox) Delete() error {
|
||||
continue
|
||||
}
|
||||
|
||||
// Retain the sanbdox if we can't obtain the network from store.
|
||||
if _, err := c.getNetworkFromStore(ep.getNetwork().ID()); err != nil {
|
||||
retain = true
|
||||
log.Warnf("Failed getting network for ep %s during sandbox %s delete: %v", ep.ID(), sb.ID(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := ep.Leave(sb); err != nil {
|
||||
retain = true
|
||||
log.Warnf("Failed detaching sandbox %s from endpoint %s: %v\n", sb.ID(), ep.ID(), err)
|
||||
}
|
||||
|
||||
if err := ep.Delete(); err != nil {
|
||||
retain = true
|
||||
log.Warnf("Failed deleting endpoint %s: %v\n", ep.ID(), err)
|
||||
}
|
||||
}
|
||||
@@ -460,7 +455,7 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
|
||||
i := ep.iface
|
||||
ep.Unlock()
|
||||
|
||||
if i != nil && i.srcName != "" {
|
||||
if i.srcName != "" {
|
||||
var ifaceOptions []osl.IfaceOption
|
||||
|
||||
ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes))
|
||||
@@ -956,11 +951,6 @@ func OptionGeneric(generic map[string]interface{}) SandboxOption {
|
||||
func (eh epHeap) Len() int { return len(eh) }
|
||||
|
||||
func (eh epHeap) Less(i, j int) bool {
|
||||
var (
|
||||
cip, cjp int
|
||||
ok bool
|
||||
)
|
||||
|
||||
ci, _ := eh[i].getSandbox()
|
||||
cj, _ := eh[j].getSandbox()
|
||||
|
||||
@@ -975,20 +965,14 @@ func (eh epHeap) Less(i, j int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if ci != nil {
|
||||
cip, ok = ci.epPriority[eh[i].ID()]
|
||||
if !ok {
|
||||
cip = 0
|
||||
}
|
||||
cip, ok := ci.epPriority[eh[i].ID()]
|
||||
if !ok {
|
||||
cip = 0
|
||||
}
|
||||
|
||||
if cj != nil {
|
||||
cjp, ok = cj.epPriority[eh[j].ID()]
|
||||
if !ok {
|
||||
cjp = 0
|
||||
}
|
||||
cjp, ok := cj.epPriority[eh[j].ID()]
|
||||
if !ok {
|
||||
cjp = 0
|
||||
}
|
||||
|
||||
if cip == cjp {
|
||||
return eh[i].network.Name() < eh[j].network.Name()
|
||||
}
|
||||
|
||||
@@ -176,6 +176,7 @@ func (n *network) getEndpointsFromStore() ([]*endpoint, error) {
|
||||
|
||||
for _, kvo := range kvol {
|
||||
ep := kvo.(*endpoint)
|
||||
ep.network = n
|
||||
epl = append(epl, ep)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,11 +65,6 @@ func Validate(label string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RelabelNeeded checks whether the user requested a relabel
|
||||
func RelabelNeeded(label string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsShared checks that the label includes a "shared" mark
|
||||
func IsShared(label string) bool {
|
||||
return false
|
||||
|
||||
@@ -181,11 +181,6 @@ func Validate(label string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RelabelNeeded checks whether the user requested a relabel
|
||||
func RelabelNeeded(label string) bool {
|
||||
return strings.Contains(label, "z") || strings.Contains(label, "Z")
|
||||
}
|
||||
|
||||
// IsShared checks that the label includes a "shared" mark
|
||||
func IsShared(label string) bool {
|
||||
return strings.Contains(label, "z")
|
||||
|
||||
Reference in New Issue
Block a user