Compare commits

..

3 Commits

Author SHA1 Message Date
James Hunt 6928729cf9 Clear Containers for Docker Engine (v1.9.0)
Signed-off-by: James Hunt <james.o.hunt@intel.com>
2015-11-11 17:21:04 +00:00
Antonio Murdaca 13c7967b94 Dockerfile: add kvmtool and linux-container for clr execdriver
This patch adds the needed pkgs to docker/docker development Dockerfile
in order to test the clr execdriver in the development container.

Signed-off-by: Antonio Murdaca <runcom@redhat.com>
2015-11-11 11:28:21 +00:00
David Calavera 7e5506d42d Merge pull request #17000 from tiborvass/bump_v1.9.0
Bump version to v1.9.0
2015-11-03 11:56:45 -08:00
89 changed files with 456 additions and 1890 deletions
-40
View File
@@ -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
-1
View File
@@ -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
-1
View File
@@ -23,7 +23,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
e2fsprogs \
iptables \
procps \
xfsprogs \
xz-utils \
\
aufs-tools \
+1 -1
View File
@@ -1 +1 @@
1.9.1-clear-containers
1.9.0
+8 -15
View File
@@ -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
View File
@@ -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)
-10
View File
@@ -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")
+4 -11
View File
@@ -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,
}
-4
View File
@@ -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.
+11 -13
View File
@@ -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()
+2 -11
View File
@@ -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
View File
@@ -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.
+6 -7
View 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
View File
@@ -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 {
-15
View File
@@ -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
-11
View File
@@ -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
+1 -1
View File
@@ -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)'
+1 -2
View File
@@ -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"
)
+1 -1
View File
@@ -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
View File
@@ -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)
-70
View File
@@ -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
View File
@@ -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 {
+2 -5
View File
@@ -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
View File
@@ -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)
}
+1 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
+1 -1
View File
@@ -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
View File
@@ -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
+7 -72
View File
@@ -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),
+1 -1
View File
@@ -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
View File
@@ -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{}
+40
View File
@@ -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
}
-41
View File
@@ -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
}
+3 -4
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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]-->
+1 -1
View File
@@ -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.
+7 -26
View File
@@ -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:
+1 -1
View File
@@ -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`.
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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`
+5 -6
View File
@@ -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).
+1 -1
View File
@@ -37,4 +37,4 @@ Example output:
]
$ docker volume inspect --format '{{ .Mountpoint }}' 85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d
/var/lib/docker/volumes/85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d/_data
"/var/lib/docker/volumes/85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d/_data"
+2 -2
View File
@@ -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
+6 -16
View File
@@ -404,26 +404,16 @@ provision the hosts are with Docker Machine.
![Engine on each host](images/engine_on_net.png)
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
+1 -1
View File
@@ -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)
+5 -5
View File
@@ -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)
+2 -2
View File
@@ -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
View File
@@ -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)
-91
View File
@@ -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
-23
View File
@@ -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
View File
@@ -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
View File
@@ -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
-125
View File
@@ -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")
}
+4 -53
View File
@@ -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))
}
-43
View File
@@ -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")
+2 -14
View File
@@ -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) {
-10
View File
@@ -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
View File
@@ -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{}{}
}
}
-180
View File
@@ -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
View File
@@ -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
+1 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+5 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()
}
+1
View File
@@ -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")