Signed-off-by: Amit Krishnan <krish.amit@gmail.com>
Signed-off-by: Alexander Morozov <lk4d4@docker.com>
... | ... |
@@ -137,7 +137,8 @@ ENV DOCKER_CROSSPLATFORMS \ |
137 | 137 |
linux/386 linux/arm \ |
138 | 138 |
darwin/amd64 \ |
139 | 139 |
freebsd/amd64 freebsd/386 freebsd/arm \ |
140 |
- windows/amd64 windows/386 |
|
140 |
+ windows/amd64 windows/386 \ |
|
141 |
+ solaris/amd64 |
|
141 | 142 |
|
142 | 143 |
# Dependency for golint |
143 | 144 |
ENV GO_TOOLS_COMMIT 823804e1ae08dbb14eb807afc7db9993bc9e3cc3 |
144 | 145 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,20 @@ |
0 |
+# Defines an image that hosts a native Docker build environment for Solaris |
|
1 |
+# TODO: Improve stub |
|
2 |
+ |
|
3 |
+FROM solaris:latest |
|
4 |
+ |
|
5 |
+# compile and runtime deps |
|
6 |
+RUN pkg install --accept \ |
|
7 |
+ git \ |
|
8 |
+ gnu-coreutils \ |
|
9 |
+ gnu-make \ |
|
10 |
+ gnu-tar \ |
|
11 |
+ diagnostic/top \ |
|
12 |
+ golang \ |
|
13 |
+ library/golang/* \ |
|
14 |
+ developer/gcc-* |
|
15 |
+ |
|
16 |
+ENV GOPATH /go/:/usr/lib/gocode/1.5/ |
|
17 |
+ENV DOCKER_CROSSPLATFORMS solaris/amd64 |
|
18 |
+WORKDIR /go/src/github.com/docker/docker |
|
19 |
+COPY . /go/src/github.com/docker/docker |
... | ... |
@@ -52,6 +52,11 @@ func notifySystem() { |
52 | 52 |
|
53 | 53 |
func (cli *DaemonCli) getPlatformRemoteOptions() []libcontainerd.RemoteOption { |
54 | 54 |
opts := []libcontainerd.RemoteOption{} |
55 |
+ if cli.Config.ContainerdAddr != "" { |
|
56 |
+ opts = append(opts, libcontainerd.WithRemoteAddr(cli.Config.ContainerdAddr)) |
|
57 |
+ } else { |
|
58 |
+ opts = append(opts, libcontainerd.WithStartDaemon(true)) |
|
59 |
+ } |
|
55 | 60 |
return opts |
56 | 61 |
} |
57 | 62 |
|
... | ... |
@@ -1,13 +1,15 @@ |
1 |
-// +build !windows |
|
1 |
+// +build !windows,!solaris |
|
2 |
+ |
|
3 |
+// TODO: Create new file for Solaris which tests config parameters |
|
4 |
+// as described in daemon/config_solaris.go |
|
2 | 5 |
|
3 | 6 |
package main |
4 | 7 |
|
5 | 8 |
import ( |
6 |
- "testing" |
|
7 |
- |
|
8 | 9 |
"github.com/docker/docker/daemon" |
9 | 10 |
"github.com/docker/docker/pkg/testutil/assert" |
10 | 11 |
"github.com/docker/docker/pkg/testutil/tempfile" |
12 |
+ "testing" |
|
11 | 13 |
) |
12 | 14 |
|
13 | 15 |
func TestLoadDaemonCliConfigWithDaemonFlags(t *testing.T) { |
0 | 9 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,13 @@ |
0 |
+// +build solaris freebsd |
|
1 |
+ |
|
2 |
+package container |
|
3 |
+ |
|
4 |
+import ( |
|
5 |
+ "golang.org/x/sys/unix" |
|
6 |
+) |
|
7 |
+ |
|
8 |
+func detachMounted(path string) error { |
|
9 |
+ //Solaris and FreeBSD do not support the lazy unmount or MNT_DETACH feature. |
|
10 |
+ // Therefore there are separate definitions for this. |
|
11 |
+ return unix.Unmount(path, 0) |
|
12 |
+} |
0 | 13 |
deleted file mode 100644 |
... | ... |
@@ -1,100 +0,0 @@ |
1 |
-// +build solaris |
|
2 |
- |
|
3 |
-package container |
|
4 |
- |
|
5 |
-import ( |
|
6 |
- "os" |
|
7 |
- "path/filepath" |
|
8 |
- |
|
9 |
- "github.com/docker/docker/api/types/container" |
|
10 |
- "github.com/docker/docker/volume" |
|
11 |
-) |
|
12 |
- |
|
13 |
-// Container holds fields specific to the Solaris implementation. See |
|
14 |
-// CommonContainer for standard fields common to all containers. |
|
15 |
-type Container struct { |
|
16 |
- CommonContainer |
|
17 |
- |
|
18 |
- // fields below here are platform specific. |
|
19 |
- HostnamePath string |
|
20 |
- HostsPath string |
|
21 |
- ResolvConfPath string |
|
22 |
-} |
|
23 |
- |
|
24 |
-// ExitStatus provides exit reasons for a container. |
|
25 |
-type ExitStatus struct { |
|
26 |
- // The exit code with which the container exited. |
|
27 |
- ExitCode int |
|
28 |
-} |
|
29 |
- |
|
30 |
-// CreateDaemonEnvironment creates a new environment variable slice for this container. |
|
31 |
-func (container *Container) CreateDaemonEnvironment(_ bool, linkedEnv []string) []string { |
|
32 |
- return nil |
|
33 |
-} |
|
34 |
- |
|
35 |
-func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) { |
|
36 |
- return volumeMounts, nil |
|
37 |
-} |
|
38 |
- |
|
39 |
-// TrySetNetworkMount attempts to set the network mounts given a provided destination and |
|
40 |
-// the path to use for it; return true if the given destination was a network mount file |
|
41 |
-func (container *Container) TrySetNetworkMount(destination string, path string) bool { |
|
42 |
- return true |
|
43 |
-} |
|
44 |
- |
|
45 |
-// NetworkMounts returns the list of network mounts. |
|
46 |
-func (container *Container) NetworkMounts() []Mount { |
|
47 |
- var mount []Mount |
|
48 |
- return mount |
|
49 |
-} |
|
50 |
- |
|
51 |
-// CopyImagePathContent copies files in destination to the volume. |
|
52 |
-func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error { |
|
53 |
- return nil |
|
54 |
-} |
|
55 |
- |
|
56 |
-// UnmountIpcMounts unmount Ipc related mounts. |
|
57 |
-func (container *Container) UnmountIpcMounts(unmount func(pth string) error) { |
|
58 |
-} |
|
59 |
- |
|
60 |
-// IpcMounts returns the list of Ipc related mounts. |
|
61 |
-func (container *Container) IpcMounts() []Mount { |
|
62 |
- return nil |
|
63 |
-} |
|
64 |
- |
|
65 |
-// UpdateContainer updates configuration of a container |
|
66 |
-func (container *Container) UpdateContainer(hostConfig *container.HostConfig) error { |
|
67 |
- return nil |
|
68 |
-} |
|
69 |
- |
|
70 |
-// UnmountVolumes explicitly unmounts volumes from the container. |
|
71 |
-func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog func(name, action string, attributes map[string]string)) error { |
|
72 |
- return nil |
|
73 |
-} |
|
74 |
- |
|
75 |
-// TmpfsMounts returns the list of tmpfs mounts |
|
76 |
-func (container *Container) TmpfsMounts() []Mount { |
|
77 |
- var mounts []Mount |
|
78 |
- return mounts |
|
79 |
-} |
|
80 |
- |
|
81 |
-// cleanResourcePath cleans a resource path and prepares to combine with mnt path |
|
82 |
-func cleanResourcePath(path string) string { |
|
83 |
- return filepath.Join(string(os.PathSeparator), path) |
|
84 |
-} |
|
85 |
- |
|
86 |
-// BuildHostnameFile writes the container's hostname file. |
|
87 |
-func (container *Container) BuildHostnameFile() error { |
|
88 |
- return nil |
|
89 |
-} |
|
90 |
- |
|
91 |
-// canMountFS determines if the file system for the container |
|
92 |
-// can be mounted locally. A no-op on non-Windows platforms |
|
93 |
-func (container *Container) canMountFS() bool { |
|
94 |
- return true |
|
95 |
-} |
|
96 |
- |
|
97 |
-// EnableServiceDiscoveryOnDefaultNetwork Enable service discovery on default network |
|
98 |
-func (container *Container) EnableServiceDiscoveryOnDefaultNetwork() bool { |
|
99 |
- return false |
|
100 |
-} |
... | ... |
@@ -1,4 +1,4 @@ |
1 |
-// +build linux freebsd |
|
1 |
+// +build linux freebsd solaris |
|
2 | 2 |
|
3 | 3 |
package container |
4 | 4 |
|
... | ... |
@@ -8,7 +8,6 @@ import ( |
8 | 8 |
"os" |
9 | 9 |
"path/filepath" |
10 | 10 |
"strings" |
11 |
- "syscall" |
|
12 | 11 |
|
13 | 12 |
"github.com/Sirupsen/logrus" |
14 | 13 |
containertypes "github.com/docker/docker/api/types/container" |
... | ... |
@@ -20,6 +19,7 @@ import ( |
20 | 20 |
"github.com/docker/docker/utils" |
21 | 21 |
"github.com/docker/docker/volume" |
22 | 22 |
"github.com/opencontainers/runc/libcontainer/label" |
23 |
+ "golang.org/x/sys/unix" |
|
23 | 24 |
) |
24 | 25 |
|
25 | 26 |
// DefaultSHMSize is the default size (64MB) of the SHM which will be mounted in the container |
... | ... |
@@ -200,7 +200,7 @@ func (container *Container) CopyImagePathContent(v volume.Volume, destination st |
200 | 200 |
logrus.Warnf("error while unmounting volume %s: %v", v.Name(), err) |
201 | 201 |
} |
202 | 202 |
}() |
203 |
- if err := label.Relabel(path, container.MountLabel, true); err != nil && err != syscall.ENOTSUP { |
|
203 |
+ if err := label.Relabel(path, container.MountLabel, true); err != nil && err != unix.ENOTSUP { |
|
204 | 204 |
return err |
205 | 205 |
} |
206 | 206 |
return copyExistingContents(rootfs, path) |
... | ... |
@@ -320,10 +320,6 @@ func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfi |
320 | 320 |
return nil |
321 | 321 |
} |
322 | 322 |
|
323 |
-func detachMounted(path string) error { |
|
324 |
- return syscall.Unmount(path, syscall.MNT_DETACH) |
|
325 |
-} |
|
326 |
- |
|
327 | 323 |
// UnmountVolumes unmounts all volumes |
328 | 324 |
func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog func(name, action string, attributes map[string]string)) error { |
329 | 325 |
var ( |
... | ... |
@@ -1,6 +1,8 @@ |
1 | 1 |
package container |
2 | 2 |
|
3 |
-import "sync" |
|
3 |
+import ( |
|
4 |
+ "sync" |
|
5 |
+) |
|
4 | 6 |
|
5 | 7 |
// memoryStore implements a Store in memory. |
6 | 8 |
type memoryStore struct { |
... | ... |
@@ -25,8 +27,9 @@ func (c *memoryStore) Add(id string, cont *Container) { |
25 | 25 |
|
26 | 26 |
// Get returns a container from the store by id. |
27 | 27 |
func (c *memoryStore) Get(id string) *Container { |
28 |
+ var res *Container |
|
28 | 29 |
c.RLock() |
29 |
- res := c.s[id] |
|
30 |
+ res = c.s[id] |
|
30 | 31 |
c.RUnlock() |
31 | 32 |
return res |
32 | 33 |
} |
... | ... |
@@ -11,11 +11,22 @@ usage() { |
11 | 11 |
echo >&2 " $mkimg -t someuser/centos:5 rinse --distribution centos-5" |
12 | 12 |
echo >&2 " $mkimg -t someuser/mageia:4 mageia-urpmi --version=4" |
13 | 13 |
echo >&2 " $mkimg -t someuser/mageia:4 mageia-urpmi --version=4 --mirror=http://somemirror/" |
14 |
+ echo >&2 " $mkimg -t someuser/solaris solaris" |
|
14 | 15 |
exit 1 |
15 | 16 |
} |
16 | 17 |
|
17 | 18 |
scriptDir="$(dirname "$(readlink -f "$BASH_SOURCE")")/mkimage" |
18 | 19 |
|
20 |
+os= |
|
21 |
+os=$(uname -o) |
|
22 |
+ |
|
23 |
+# set up path to gnu tools if solaris |
|
24 |
+[[ $os == "Solaris" ]] && export PATH=/usr/gnu/bin:$PATH |
|
25 |
+# TODO check for gnu-tar, gnu-getopt |
|
26 |
+ |
|
27 |
+# TODO requires root/sudo due to some pkg operations. sigh. |
|
28 |
+[[ $os == "Solaris" && $EUID != "0" ]] && echo >&2 "image create on Solaris requires superuser privilege" |
|
29 |
+ |
|
19 | 30 |
optTemp=$(getopt --options '+d:t:c:hC' --longoptions 'dir:,tag:,compression:,no-compression,help' --name "$mkimg" -- "$@") |
20 | 31 |
eval set -- "$optTemp" |
21 | 32 |
unset optTemp |
22 | 33 |
new file mode 100755 |
... | ... |
@@ -0,0 +1,89 @@ |
0 |
+#!/usr/bin/env bash |
|
1 |
+# |
|
2 |
+# Solaris 12 base image build script. |
|
3 |
+# |
|
4 |
+set -e |
|
5 |
+ |
|
6 |
+# TODO add optional package publisher origin |
|
7 |
+ |
|
8 |
+rootfsDir="$1" |
|
9 |
+shift |
|
10 |
+ |
|
11 |
+# base install |
|
12 |
+( |
|
13 |
+ set -x |
|
14 |
+ |
|
15 |
+ pkg image-create --full --zone \ |
|
16 |
+ --facet facet.locale.*=false \ |
|
17 |
+ --facet facet.locale.POSIX=true \ |
|
18 |
+ --facet facet.doc=false \ |
|
19 |
+ --facet facet.doc.*=false \ |
|
20 |
+ "$rootfsDir" |
|
21 |
+ |
|
22 |
+ pkg -R "$rootfsDir" set-property use-system-repo true |
|
23 |
+ |
|
24 |
+ pkg -R "$rootfsDir" set-property flush-content-cache-on-success true |
|
25 |
+ |
|
26 |
+ pkg -R "$rootfsDir" install core-os |
|
27 |
+) |
|
28 |
+ |
|
29 |
+# Lay in stock configuration, set up milestone |
|
30 |
+# XXX This all may become optional in a base image |
|
31 |
+( |
|
32 |
+ # faster to build repository database on tmpfs |
|
33 |
+ REPO_DB=/system/volatile/repository.$$ |
|
34 |
+ export SVCCFG_REPOSITORY=${REPO_DB} |
|
35 |
+ export SVCCFG_DOOR_PATH=$rootfsDir/system/volatile/tmp_repo_door |
|
36 |
+ |
|
37 |
+ # Import base manifests. NOTE These are a combination of basic requirement |
|
38 |
+ # and gleaned from container milestone manifest. They may change. |
|
39 |
+ for m in $rootfsDir/lib/svc/manifest/system/environment.xml \ |
|
40 |
+ $rootfsDir/lib/svc/manifest/system/svc/global.xml \ |
|
41 |
+ $rootfsDir/lib/svc/manifest/system/svc/restarter.xml \ |
|
42 |
+ $rootfsDir/lib/svc/manifest/network/dns/client.xml \ |
|
43 |
+ $rootfsDir/lib/svc/manifest/system/name-service/switch.xml \ |
|
44 |
+ $rootfsDir/lib/svc/manifest/system/name-service/cache.xml \ |
|
45 |
+ $rootfsDir/lib/svc/manifest/milestone/container.xml ; do |
|
46 |
+ svccfg import $m |
|
47 |
+ done |
|
48 |
+ |
|
49 |
+ # Apply system layer profile, deleting unnecessary dependencies |
|
50 |
+ svccfg apply $rootfsDir/etc/svc/profile/generic_container.xml |
|
51 |
+ |
|
52 |
+ # XXX Even if we keep a repo in the base image, this is definitely optional |
|
53 |
+ svccfg apply $rootfsDir/etc/svc/profile/sysconfig/container_sc.xml |
|
54 |
+ |
|
55 |
+ for s in svc:/system/svc/restarter \ |
|
56 |
+ svc:/system/environment \ |
|
57 |
+ svc:/network/dns/client \ |
|
58 |
+ svc:/system/name-service/switch \ |
|
59 |
+ svc:/system/name-service/cache \ |
|
60 |
+ svc:/system/svc/global \ |
|
61 |
+ svc:/milestone/container ;do |
|
62 |
+ svccfg -s $s refresh |
|
63 |
+ done |
|
64 |
+ |
|
65 |
+ # now copy the built up repository into the base rootfs |
|
66 |
+ mv $REPO_DB $rootfsDir/etc/svc/repository.db |
|
67 |
+) |
|
68 |
+ |
|
69 |
+# pkg(1) needs the zoneproxy-client running in the container. |
|
70 |
+# use a simple wrapper to run it as needed. |
|
71 |
+# XXX maybe we go back to running this in SMF? |
|
72 |
+mv "$rootfsDir/usr/bin/pkg" "$rootfsDir/usr/bin/wrapped_pkg" |
|
73 |
+cat > "$rootfsDir/usr/bin/pkg" <<-'EOF' |
|
74 |
+#!/bin/sh |
|
75 |
+# |
|
76 |
+# THIS FILE CREATED DURING DOCKER BASE IMAGE CREATION |
|
77 |
+# |
|
78 |
+# The Solaris base image uses the sysrepo proxy mechanism. The |
|
79 |
+# IPS client pkg(1) requires the zoneproxy-client to reach the |
|
80 |
+# remote publisher origins through the host. This wrapper script |
|
81 |
+# enables and disables the proxy client as needed. This is a |
|
82 |
+# temporary solution. |
|
83 |
+ |
|
84 |
+/usr/lib/zones/zoneproxy-client -s localhost:1008 |
|
85 |
+PKG_SYSREPO_URL=http://localhost:1008 /usr/bin/wrapped_pkg "$@" |
|
86 |
+pkill -9 zoneproxy-client |
|
87 |
+EOF |
|
88 |
+chmod +x "$rootfsDir/usr/bin/pkg" |
5 | 5 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,57 @@ |
0 |
+package cluster |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "bufio" |
|
4 |
+ "fmt" |
|
5 |
+ "net" |
|
6 |
+ "os/exec" |
|
7 |
+ "strings" |
|
8 |
+) |
|
9 |
+ |
|
10 |
+func (c *Cluster) resolveSystemAddr() (net.IP, error) { |
|
11 |
+ defRouteCmd := "/usr/sbin/ipadm show-addr -p -o addr " + |
|
12 |
+ "`/usr/sbin/route get default | /usr/bin/grep interface | " + |
|
13 |
+ "/usr/bin/awk '{print $2}'`" |
|
14 |
+ out, err := exec.Command("/usr/bin/bash", "-c", defRouteCmd).Output() |
|
15 |
+ if err != nil { |
|
16 |
+ return nil, fmt.Errorf("cannot get default route: %v", err) |
|
17 |
+ } |
|
18 |
+ |
|
19 |
+ defInterface := strings.SplitN(string(out), "/", 2) |
|
20 |
+ defInterfaceIP := net.ParseIP(defInterface[0]) |
|
21 |
+ |
|
22 |
+ return defInterfaceIP, nil |
|
23 |
+} |
|
24 |
+ |
|
25 |
+func listSystemIPs() []net.IP { |
|
26 |
+ var systemAddrs []net.IP |
|
27 |
+ cmd := exec.Command("/usr/sbin/ipadm", "show-addr", "-p", "-o", "addr") |
|
28 |
+ cmdReader, err := cmd.StdoutPipe() |
|
29 |
+ if err != nil { |
|
30 |
+ return nil |
|
31 |
+ } |
|
32 |
+ |
|
33 |
+ if err := cmd.Start(); err != nil { |
|
34 |
+ return nil |
|
35 |
+ } |
|
36 |
+ |
|
37 |
+ scanner := bufio.NewScanner(cmdReader) |
|
38 |
+ go func() { |
|
39 |
+ for scanner.Scan() { |
|
40 |
+ text := scanner.Text() |
|
41 |
+ nameAddrPair := strings.SplitN(text, "/", 2) |
|
42 |
+ // Let go of loopback interfaces and docker interfaces |
|
43 |
+ systemAddrs = append(systemAddrs, net.ParseIP(nameAddrPair[0])) |
|
44 |
+ } |
|
45 |
+ }() |
|
46 |
+ |
|
47 |
+ if err := scanner.Err(); err != nil { |
|
48 |
+ fmt.Printf("scan underwent err: %+v\n", err) |
|
49 |
+ } |
|
50 |
+ |
|
51 |
+ if err := cmd.Wait(); err != nil { |
|
52 |
+ fmt.Printf("run command wait: %+v\n", err) |
|
53 |
+ } |
|
54 |
+ |
|
55 |
+ return systemAddrs |
|
56 |
+} |
... | ... |
@@ -126,9 +126,9 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str |
126 | 126 |
return "", err |
127 | 127 |
} |
128 | 128 |
|
129 |
- // It is not possible to commit a running container on Windows |
|
130 |
- if runtime.GOOS == "windows" && container.IsRunning() { |
|
131 |
- return "", fmt.Errorf("Windows does not support commit of a running container") |
|
129 |
+ // It is not possible to commit a running container on Windows and on Solaris. |
|
130 |
+ if (runtime.GOOS == "windows" || runtime.GOOS == "solaris") && container.IsRunning() { |
|
131 |
+ return "", fmt.Errorf("%+v does not support commit of a running container", runtime.GOOS) |
|
132 | 132 |
} |
133 | 133 |
|
134 | 134 |
if c.Pause && !container.IsPaused() { |
... | ... |
@@ -3,6 +3,7 @@ package daemon |
3 | 3 |
import ( |
4 | 4 |
"bytes" |
5 | 5 |
"encoding/json" |
6 |
+ "errors" |
|
6 | 7 |
"fmt" |
7 | 8 |
"io" |
8 | 9 |
"io/ioutil" |
... | ... |
@@ -221,6 +222,9 @@ func NewConfig() *Config { |
221 | 221 |
} |
222 | 222 |
|
223 | 223 |
func parseClusterAdvertiseSettings(clusterStore, clusterAdvertise string) (string, error) { |
224 |
+ if runtime.GOOS == "solaris" && (clusterAdvertise != "" || clusterStore != "") { |
|
225 |
+ return "", errors.New("Cluster Advertise Settings not supported on Solaris") |
|
226 |
+ } |
|
224 | 227 |
if clusterAdvertise == "" { |
225 | 228 |
return "", errDiscoveryDisabled |
226 | 229 |
} |
227 | 230 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,80 @@ |
0 |
+// +build solaris linux freebsd |
|
1 |
+ |
|
2 |
+package daemon |
|
3 |
+ |
|
4 |
+import ( |
|
5 |
+ "net" |
|
6 |
+ |
|
7 |
+ "github.com/docker/docker/api/types" |
|
8 |
+ "github.com/docker/docker/opts" |
|
9 |
+ runconfigopts "github.com/docker/docker/runconfig/opts" |
|
10 |
+ "github.com/spf13/pflag" |
|
11 |
+) |
|
12 |
+ |
|
13 |
+// CommonUnixConfig defines configuration of a docker daemon that is |
|
14 |
+// common across Unix platforms. |
|
15 |
+type CommonUnixConfig struct { |
|
16 |
+ ExecRoot string `json:"exec-root,omitempty"` |
|
17 |
+ ContainerdAddr string `json:"containerd,omitempty"` |
|
18 |
+ Runtimes map[string]types.Runtime `json:"runtimes,omitempty"` |
|
19 |
+ DefaultRuntime string `json:"default-runtime,omitempty"` |
|
20 |
+} |
|
21 |
+ |
|
22 |
+type commonUnixBridgeConfig struct { |
|
23 |
+ DefaultIP net.IP `json:"ip,omitempty"` |
|
24 |
+ IP string `json:"bip,omitempty"` |
|
25 |
+ DefaultGatewayIPv4 net.IP `json:"default-gateway,omitempty"` |
|
26 |
+ DefaultGatewayIPv6 net.IP `json:"default-gateway-v6,omitempty"` |
|
27 |
+ InterContainerCommunication bool `json:"icc,omitempty"` |
|
28 |
+} |
|
29 |
+ |
|
30 |
+// InstallCommonUnixFlags adds command-line options to the top-level flag parser for |
|
31 |
+// the current process that are common across Unix platforms. |
|
32 |
+func (config *Config) InstallCommonUnixFlags(flags *pflag.FlagSet) { |
|
33 |
+ config.Runtimes = make(map[string]types.Runtime) |
|
34 |
+ |
|
35 |
+ flags.StringVarP(&config.SocketGroup, "group", "G", "docker", "Group for the unix socket") |
|
36 |
+ flags.StringVar(&config.bridgeConfig.IP, "bip", "", "Specify network bridge IP") |
|
37 |
+ flags.StringVarP(&config.bridgeConfig.Iface, "bridge", "b", "", "Attach containers to a network bridge") |
|
38 |
+ flags.StringVar(&config.bridgeConfig.FixedCIDR, "fixed-cidr", "", "IPv4 subnet for fixed IPs") |
|
39 |
+ flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultGatewayIPv4, ""), "default-gateway", "Container default gateway IPv4 address") |
|
40 |
+ flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultGatewayIPv6, ""), "default-gateway-v6", "Container default gateway IPv6 address") |
|
41 |
+ flags.BoolVar(&config.bridgeConfig.InterContainerCommunication, "icc", true, "Enable inter-container communication") |
|
42 |
+ flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultIP, "0.0.0.0"), "ip", "Default IP when binding container ports") |
|
43 |
+ flags.Var(runconfigopts.NewNamedRuntimeOpt("runtimes", &config.Runtimes, stockRuntimeName), "add-runtime", "Register an additional OCI compatible runtime") |
|
44 |
+ flags.StringVar(&config.DefaultRuntime, "default-runtime", stockRuntimeName, "Default OCI runtime for containers") |
|
45 |
+ |
|
46 |
+} |
|
47 |
+ |
|
48 |
+// GetRuntime returns the runtime path and arguments for a given |
|
49 |
+// runtime name |
|
50 |
+func (config *Config) GetRuntime(name string) *types.Runtime { |
|
51 |
+ config.reloadLock.Lock() |
|
52 |
+ defer config.reloadLock.Unlock() |
|
53 |
+ if rt, ok := config.Runtimes[name]; ok { |
|
54 |
+ return &rt |
|
55 |
+ } |
|
56 |
+ return nil |
|
57 |
+} |
|
58 |
+ |
|
59 |
+// GetDefaultRuntimeName returns the current default runtime |
|
60 |
+func (config *Config) GetDefaultRuntimeName() string { |
|
61 |
+ config.reloadLock.Lock() |
|
62 |
+ rt := config.DefaultRuntime |
|
63 |
+ config.reloadLock.Unlock() |
|
64 |
+ |
|
65 |
+ return rt |
|
66 |
+} |
|
67 |
+ |
|
68 |
+// GetAllRuntimes returns a copy of the runtimes map |
|
69 |
+func (config *Config) GetAllRuntimes() map[string]types.Runtime { |
|
70 |
+ config.reloadLock.Lock() |
|
71 |
+ rts := config.Runtimes |
|
72 |
+ config.reloadLock.Unlock() |
|
73 |
+ return rts |
|
74 |
+} |
|
75 |
+ |
|
76 |
+// GetExecRoot returns the user configured Exec-root |
|
77 |
+func (config *Config) GetExecRoot() string { |
|
78 |
+ return config.ExecRoot |
|
79 |
+} |
... | ... |
@@ -5,7 +5,7 @@ import ( |
5 | 5 |
) |
6 | 6 |
|
7 | 7 |
var ( |
8 |
- defaultPidFile = "/var/run/docker.pid" |
|
8 |
+ defaultPidFile = "/system/volatile/docker/docker.pid" |
|
9 | 9 |
defaultGraph = "/var/lib/docker" |
10 | 10 |
defaultExec = "zones" |
11 | 11 |
) |
... | ... |
@@ -16,14 +16,17 @@ var ( |
16 | 16 |
type Config struct { |
17 | 17 |
CommonConfig |
18 | 18 |
|
19 |
- // Fields below here are platform specific. |
|
20 |
- ExecRoot string `json:"exec-root,omitempty"` |
|
19 |
+ // These fields are common to all unix platforms. |
|
20 |
+ CommonUnixConfig |
|
21 | 21 |
} |
22 | 22 |
|
23 | 23 |
// bridgeConfig stores all the bridge driver specific |
24 | 24 |
// configuration. |
25 | 25 |
type bridgeConfig struct { |
26 | 26 |
commonBridgeConfig |
27 |
+ |
|
28 |
+ // Fields below here are platform specific. |
|
29 |
+ commonUnixBridgeConfig |
|
27 | 30 |
} |
28 | 31 |
|
29 | 32 |
// InstallFlags adds command-line options to the top-level flag parser for |
... | ... |
@@ -32,14 +35,13 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) { |
32 | 32 |
// First handle install flags which are consistent cross-platform |
33 | 33 |
config.InstallCommonFlags(flags) |
34 | 34 |
|
35 |
+ // Then install flags common to unix platforms |
|
36 |
+ config.InstallCommonUnixFlags(flags) |
|
37 |
+ |
|
35 | 38 |
// Then platform-specific install flags |
36 | 39 |
config.attachExperimentalFlags(flags) |
37 | 40 |
} |
38 | 41 |
|
39 |
-// GetExecRoot returns the user configured Exec-root |
|
40 |
-func (config *Config) GetExecRoot() string { |
|
41 |
- return config.ExecRoot |
|
42 |
-} |
|
43 | 42 |
func (config *Config) isSwarmCompatible() error { |
44 | 43 |
return nil |
45 | 44 |
} |
... | ... |
@@ -3,6 +3,7 @@ package daemon |
3 | 3 |
import ( |
4 | 4 |
"io/ioutil" |
5 | 5 |
"os" |
6 |
+ "runtime" |
|
6 | 7 |
"strings" |
7 | 8 |
"testing" |
8 | 9 |
|
... | ... |
@@ -35,6 +36,9 @@ func TestDaemonBrokenConfiguration(t *testing.T) { |
35 | 35 |
} |
36 | 36 |
|
37 | 37 |
func TestParseClusterAdvertiseSettings(t *testing.T) { |
38 |
+ if runtime.GOOS == "solaris" { |
|
39 |
+ t.Skip("ClusterSettings not supported on Solaris\n") |
|
40 |
+ } |
|
38 | 41 |
_, err := parseClusterAdvertiseSettings("something", "") |
39 | 42 |
if err != errDiscoveryDisabled { |
40 | 43 |
t.Fatalf("expected discovery disabled error, got %v\n", err) |
... | ... |
@@ -4,10 +4,7 @@ package daemon |
4 | 4 |
|
5 | 5 |
import ( |
6 | 6 |
"fmt" |
7 |
- "net" |
|
8 | 7 |
|
9 |
- "github.com/docker/docker/api/types" |
|
10 |
- "github.com/docker/docker/opts" |
|
11 | 8 |
runconfigopts "github.com/docker/docker/runconfig/opts" |
12 | 9 |
units "github.com/docker/go-units" |
13 | 10 |
"github.com/spf13/pflag" |
... | ... |
@@ -25,15 +22,14 @@ var ( |
25 | 25 |
type Config struct { |
26 | 26 |
CommonConfig |
27 | 27 |
|
28 |
+ // These fields are common to all unix platforms. |
|
29 |
+ CommonUnixConfig |
|
30 |
+ |
|
28 | 31 |
// Fields below here are platform specific. |
29 | 32 |
CgroupParent string `json:"cgroup-parent,omitempty"` |
30 |
- ContainerdAddr string `json:"containerd,omitempty"` |
|
31 | 33 |
EnableSelinuxSupport bool `json:"selinux-enabled,omitempty"` |
32 |
- ExecRoot string `json:"exec-root,omitempty"` |
|
33 | 34 |
RemappedRoot string `json:"userns-remap,omitempty"` |
34 | 35 |
Ulimits map[string]*units.Ulimit `json:"default-ulimits,omitempty"` |
35 |
- Runtimes map[string]types.Runtime `json:"runtimes,omitempty"` |
|
36 |
- DefaultRuntime string `json:"default-runtime,omitempty"` |
|
37 | 36 |
CPURealtimePeriod int64 `json:"cpu-rt-period,omitempty"` |
38 | 37 |
CPURealtimeRuntime int64 `json:"cpu-rt-runtime,omitempty"` |
39 | 38 |
OOMScoreAdjust int `json:"oom-score-adjust,omitempty"` |
... | ... |
@@ -47,19 +43,17 @@ type Config struct { |
47 | 47 |
type bridgeConfig struct { |
48 | 48 |
commonBridgeConfig |
49 | 49 |
|
50 |
+ // These fields are common to all unix platforms. |
|
51 |
+ commonUnixBridgeConfig |
|
52 |
+ |
|
50 | 53 |
// Fields below here are platform specific. |
51 |
- EnableIPv6 bool `json:"ipv6,omitempty"` |
|
52 |
- EnableIPTables bool `json:"iptables,omitempty"` |
|
53 |
- EnableIPForward bool `json:"ip-forward,omitempty"` |
|
54 |
- EnableIPMasq bool `json:"ip-masq,omitempty"` |
|
55 |
- EnableUserlandProxy bool `json:"userland-proxy,omitempty"` |
|
56 |
- UserlandProxyPath string `json:"userland-proxy-path,omitempty"` |
|
57 |
- DefaultIP net.IP `json:"ip,omitempty"` |
|
58 |
- IP string `json:"bip,omitempty"` |
|
59 |
- FixedCIDRv6 string `json:"fixed-cidr-v6,omitempty"` |
|
60 |
- DefaultGatewayIPv4 net.IP `json:"default-gateway,omitempty"` |
|
61 |
- DefaultGatewayIPv6 net.IP `json:"default-gateway-v6,omitempty"` |
|
62 |
- InterContainerCommunication bool `json:"icc,omitempty"` |
|
54 |
+ EnableIPv6 bool `json:"ipv6,omitempty"` |
|
55 |
+ EnableIPTables bool `json:"iptables,omitempty"` |
|
56 |
+ EnableIPForward bool `json:"ip-forward,omitempty"` |
|
57 |
+ EnableIPMasq bool `json:"ip-masq,omitempty"` |
|
58 |
+ EnableUserlandProxy bool `json:"userland-proxy,omitempty"` |
|
59 |
+ UserlandProxyPath string `json:"userland-proxy-path,omitempty"` |
|
60 |
+ FixedCIDRv6 string `json:"fixed-cidr-v6,omitempty"` |
|
63 | 61 |
} |
64 | 62 |
|
65 | 63 |
// InstallFlags adds flags to the pflag.FlagSet to configure the daemon |
... | ... |
@@ -67,26 +61,20 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) { |
67 | 67 |
// First handle install flags which are consistent cross-platform |
68 | 68 |
config.InstallCommonFlags(flags) |
69 | 69 |
|
70 |
+ // Then install flags common to unix platforms |
|
71 |
+ config.InstallCommonUnixFlags(flags) |
|
72 |
+ |
|
70 | 73 |
config.Ulimits = make(map[string]*units.Ulimit) |
71 |
- config.Runtimes = make(map[string]types.Runtime) |
|
72 | 74 |
|
73 | 75 |
// Then platform-specific install flags |
74 | 76 |
flags.BoolVar(&config.EnableSelinuxSupport, "selinux-enabled", false, "Enable selinux support") |
75 |
- flags.StringVarP(&config.SocketGroup, "group", "G", "docker", "Group for the unix socket") |
|
76 | 77 |
flags.Var(runconfigopts.NewUlimitOpt(&config.Ulimits), "default-ulimit", "Default ulimits for containers") |
77 | 78 |
flags.BoolVar(&config.bridgeConfig.EnableIPTables, "iptables", true, "Enable addition of iptables rules") |
78 | 79 |
flags.BoolVar(&config.bridgeConfig.EnableIPForward, "ip-forward", true, "Enable net.ipv4.ip_forward") |
79 | 80 |
flags.BoolVar(&config.bridgeConfig.EnableIPMasq, "ip-masq", true, "Enable IP masquerading") |
80 | 81 |
flags.BoolVar(&config.bridgeConfig.EnableIPv6, "ipv6", false, "Enable IPv6 networking") |
81 | 82 |
flags.StringVar(&config.ExecRoot, "exec-root", defaultExecRoot, "Root directory for execution state files") |
82 |
- flags.StringVar(&config.bridgeConfig.IP, "bip", "", "Specify network bridge IP") |
|
83 |
- flags.StringVarP(&config.bridgeConfig.Iface, "bridge", "b", "", "Attach containers to a network bridge") |
|
84 |
- flags.StringVar(&config.bridgeConfig.FixedCIDR, "fixed-cidr", "", "IPv4 subnet for fixed IPs") |
|
85 | 83 |
flags.StringVar(&config.bridgeConfig.FixedCIDRv6, "fixed-cidr-v6", "", "IPv6 subnet for fixed IPs") |
86 |
- flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultGatewayIPv4, ""), "default-gateway", "Container default gateway IPv4 address") |
|
87 |
- flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultGatewayIPv6, ""), "default-gateway-v6", "Container default gateway IPv6 address") |
|
88 |
- flags.BoolVar(&config.bridgeConfig.InterContainerCommunication, "icc", true, "Enable inter-container communication") |
|
89 |
- flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultIP, "0.0.0.0"), "ip", "Default IP when binding container ports") |
|
90 | 84 |
flags.BoolVar(&config.bridgeConfig.EnableUserlandProxy, "userland-proxy", true, "Use userland proxy for loopback traffic") |
91 | 85 |
flags.StringVar(&config.bridgeConfig.UserlandProxyPath, "userland-proxy-path", "", "Path to the userland proxy binary") |
92 | 86 |
flags.BoolVar(&config.EnableCors, "api-enable-cors", false, "Enable CORS headers in the remote API, this is deprecated by --api-cors-header") |
... | ... |
@@ -95,8 +83,6 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) { |
95 | 95 |
flags.StringVar(&config.RemappedRoot, "userns-remap", "", "User/Group setting for user namespaces") |
96 | 96 |
flags.StringVar(&config.ContainerdAddr, "containerd", "", "Path to containerd socket") |
97 | 97 |
flags.BoolVar(&config.LiveRestoreEnabled, "live-restore", false, "Enable live restore of docker when containers are still running") |
98 |
- flags.Var(runconfigopts.NewNamedRuntimeOpt("runtimes", &config.Runtimes, stockRuntimeName), "add-runtime", "Register an additional OCI compatible runtime") |
|
99 |
- flags.StringVar(&config.DefaultRuntime, "default-runtime", stockRuntimeName, "Default OCI runtime for containers") |
|
100 | 98 |
flags.IntVar(&config.OOMScoreAdjust, "oom-score-adjust", -500, "Set the oom_score_adj for the daemon") |
101 | 99 |
flags.BoolVar(&config.Init, "init", false, "Run an init in the container to forward signals and reap processes") |
102 | 100 |
flags.StringVar(&config.InitPath, "init-path", "", "Path to the docker-init binary") |
... | ... |
@@ -107,39 +93,6 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) { |
107 | 107 |
config.attachExperimentalFlags(flags) |
108 | 108 |
} |
109 | 109 |
|
110 |
-// GetRuntime returns the runtime path and arguments for a given |
|
111 |
-// runtime name |
|
112 |
-func (config *Config) GetRuntime(name string) *types.Runtime { |
|
113 |
- config.reloadLock.Lock() |
|
114 |
- defer config.reloadLock.Unlock() |
|
115 |
- if rt, ok := config.Runtimes[name]; ok { |
|
116 |
- return &rt |
|
117 |
- } |
|
118 |
- return nil |
|
119 |
-} |
|
120 |
- |
|
121 |
-// GetDefaultRuntimeName returns the current default runtime |
|
122 |
-func (config *Config) GetDefaultRuntimeName() string { |
|
123 |
- config.reloadLock.Lock() |
|
124 |
- rt := config.DefaultRuntime |
|
125 |
- config.reloadLock.Unlock() |
|
126 |
- |
|
127 |
- return rt |
|
128 |
-} |
|
129 |
- |
|
130 |
-// GetAllRuntimes returns a copy of the runtimes map |
|
131 |
-func (config *Config) GetAllRuntimes() map[string]types.Runtime { |
|
132 |
- config.reloadLock.Lock() |
|
133 |
- rts := config.Runtimes |
|
134 |
- config.reloadLock.Unlock() |
|
135 |
- return rts |
|
136 |
-} |
|
137 |
- |
|
138 |
-// GetExecRoot returns the user configured Exec-root |
|
139 |
-func (config *Config) GetExecRoot() string { |
|
140 |
- return config.ExecRoot |
|
141 |
-} |
|
142 |
- |
|
143 | 110 |
func (config *Config) isSwarmCompatible() error { |
144 | 111 |
if config.ClusterStore != "" || config.ClusterAdvertise != "" { |
145 | 112 |
return fmt.Errorf("--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode") |
... | ... |
@@ -2,25 +2,20 @@ |
2 | 2 |
|
3 | 3 |
package daemon |
4 | 4 |
|
5 |
-import "github.com/docker/docker/container" |
|
5 |
+import ( |
|
6 |
+ "github.com/docker/docker/container" |
|
7 |
+ "github.com/docker/docker/runconfig" |
|
8 |
+ "github.com/docker/libnetwork" |
|
9 |
+) |
|
6 | 10 |
|
7 | 11 |
func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) { |
8 | 12 |
return nil, nil |
9 | 13 |
} |
10 | 14 |
|
11 |
-// getSize returns real size & virtual size |
|
12 |
-func (daemon *Daemon) getSize(container *container.Container) (int64, int64) { |
|
13 |
- return 0, 0 |
|
14 |
-} |
|
15 |
- |
|
16 | 15 |
func (daemon *Daemon) setupIpcDirs(container *container.Container) error { |
17 | 16 |
return nil |
18 | 17 |
} |
19 | 18 |
|
20 |
-func (daemon *Daemon) mountVolumes(container *container.Container) error { |
|
21 |
- return nil |
|
22 |
-} |
|
23 |
- |
|
24 | 19 |
func killProcessDirectly(container *container.Container) error { |
25 | 20 |
return nil |
26 | 21 |
} |
... | ... |
@@ -30,9 +25,22 @@ func detachMounted(path string) error { |
30 | 30 |
} |
31 | 31 |
|
32 | 32 |
func isLinkable(child *container.Container) bool { |
33 |
+ // A container is linkable only if it belongs to the default network |
|
34 |
+ _, ok := child.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()] |
|
35 |
+ return ok |
|
36 |
+} |
|
37 |
+ |
|
38 |
+func enableIPOnPredefinedNetwork() bool { |
|
33 | 39 |
return false |
34 | 40 |
} |
35 | 41 |
|
36 | 42 |
func (daemon *Daemon) isNetworkHotPluggable() bool { |
37 | 43 |
return false |
38 | 44 |
} |
45 |
+ |
|
46 |
+func setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error { |
|
47 |
+ return nil |
|
48 |
+} |
|
49 |
+ |
|
50 |
+func initializeNetworkingPaths(container *container.Container, nc *container.Container) { |
|
51 |
+} |
... | ... |
@@ -15,9 +15,7 @@ import ( |
15 | 15 |
containertypes "github.com/docker/docker/api/types/container" |
16 | 16 |
"github.com/docker/docker/container" |
17 | 17 |
"github.com/docker/docker/daemon/links" |
18 |
- "github.com/docker/docker/pkg/fileutils" |
|
19 | 18 |
"github.com/docker/docker/pkg/idtools" |
20 |
- "github.com/docker/docker/pkg/mount" |
|
21 | 19 |
"github.com/docker/docker/pkg/stringid" |
22 | 20 |
"github.com/docker/docker/runconfig" |
23 | 21 |
"github.com/docker/libnetwork" |
... | ... |
@@ -63,39 +61,6 @@ func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]s |
63 | 63 |
return env, nil |
64 | 64 |
} |
65 | 65 |
|
66 |
-// getSize returns the real size & virtual size of the container. |
|
67 |
-func (daemon *Daemon) getSize(container *container.Container) (int64, int64) { |
|
68 |
- var ( |
|
69 |
- sizeRw, sizeRootfs int64 |
|
70 |
- err error |
|
71 |
- ) |
|
72 |
- |
|
73 |
- if err := daemon.Mount(container); err != nil { |
|
74 |
- logrus.Errorf("Failed to compute size of container rootfs %s: %s", container.ID, err) |
|
75 |
- return sizeRw, sizeRootfs |
|
76 |
- } |
|
77 |
- defer daemon.Unmount(container) |
|
78 |
- |
|
79 |
- sizeRw, err = container.RWLayer.Size() |
|
80 |
- if err != nil { |
|
81 |
- logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", |
|
82 |
- daemon.GraphDriverName(), container.ID, err) |
|
83 |
- // FIXME: GetSize should return an error. Not changing it now in case |
|
84 |
- // there is a side-effect. |
|
85 |
- sizeRw = -1 |
|
86 |
- } |
|
87 |
- |
|
88 |
- if parent := container.RWLayer.Parent(); parent != nil { |
|
89 |
- sizeRootfs, err = parent.Size() |
|
90 |
- if err != nil { |
|
91 |
- sizeRootfs = -1 |
|
92 |
- } else if sizeRw != -1 { |
|
93 |
- sizeRootfs += sizeRw |
|
94 |
- } |
|
95 |
- } |
|
96 |
- return sizeRw, sizeRootfs |
|
97 |
-} |
|
98 |
- |
|
99 | 66 |
func (daemon *Daemon) getIpcContainer(container *container.Container) (*container.Container, error) { |
100 | 67 |
containerID := container.HostConfig.IpcMode.Container() |
101 | 68 |
c, err := daemon.GetContainer(containerID) |
... | ... |
@@ -174,54 +139,6 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error { |
174 | 174 |
|
175 | 175 |
return nil |
176 | 176 |
} |
177 |
- |
|
178 |
-func (daemon *Daemon) mountVolumes(container *container.Container) error { |
|
179 |
- mounts, err := daemon.setupMounts(container) |
|
180 |
- if err != nil { |
|
181 |
- return err |
|
182 |
- } |
|
183 |
- |
|
184 |
- for _, m := range mounts { |
|
185 |
- dest, err := container.GetResourcePath(m.Destination) |
|
186 |
- if err != nil { |
|
187 |
- return err |
|
188 |
- } |
|
189 |
- |
|
190 |
- var stat os.FileInfo |
|
191 |
- stat, err = os.Stat(m.Source) |
|
192 |
- if err != nil { |
|
193 |
- return err |
|
194 |
- } |
|
195 |
- if err = fileutils.CreateIfNotExists(dest, stat.IsDir()); err != nil { |
|
196 |
- return err |
|
197 |
- } |
|
198 |
- |
|
199 |
- opts := "rbind,ro" |
|
200 |
- if m.Writable { |
|
201 |
- opts = "rbind,rw" |
|
202 |
- } |
|
203 |
- |
|
204 |
- if err := mount.Mount(m.Source, dest, "bind", opts); err != nil { |
|
205 |
- return err |
|
206 |
- } |
|
207 |
- |
|
208 |
- // mountVolumes() seems to be called for temporary mounts |
|
209 |
- // outside the container. Soon these will be unmounted with |
|
210 |
- // lazy unmount option and given we have mounted the rbind, |
|
211 |
- // all the submounts will propagate if these are shared. If |
|
212 |
- // daemon is running in host namespace and has / as shared |
|
213 |
- // then these unmounts will propagate and unmount original |
|
214 |
- // mount as well. So make all these mounts rprivate. |
|
215 |
- // Do not use propagation property of volume as that should |
|
216 |
- // apply only when mounting happen inside the container. |
|
217 |
- if err := mount.MakeRPrivate(dest); err != nil { |
|
218 |
- return err |
|
219 |
- } |
|
220 |
- } |
|
221 |
- |
|
222 |
- return nil |
|
223 |
-} |
|
224 |
- |
|
225 | 177 |
func killProcessDirectly(container *container.Container) error { |
226 | 178 |
if _, err := container.WaitStop(10 * time.Second); err != nil { |
227 | 179 |
// Ensure that we don't kill ourselves |
... | ... |
@@ -3,11 +3,14 @@ package daemon |
3 | 3 |
import ( |
4 | 4 |
"fmt" |
5 | 5 |
"net" |
6 |
+ "runtime" |
|
6 | 7 |
"strings" |
7 | 8 |
"time" |
8 | 9 |
|
10 |
+ "github.com/pkg/errors" |
|
11 |
+ |
|
9 | 12 |
"github.com/Sirupsen/logrus" |
10 |
- "github.com/docker/docker/api/errors" |
|
13 |
+ apierrors "github.com/docker/docker/api/errors" |
|
11 | 14 |
"github.com/docker/docker/api/types" |
12 | 15 |
containertypes "github.com/docker/docker/api/types/container" |
13 | 16 |
networktypes "github.com/docker/docker/api/types/network" |
... | ... |
@@ -78,6 +81,10 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) ( |
78 | 78 |
if err != nil { |
79 | 79 |
return nil, err |
80 | 80 |
} |
81 |
+ |
|
82 |
+ if runtime.GOOS == "solaris" && img.OS != "solaris " { |
|
83 |
+ return nil, errors.New("Platform on which parent image was created is not Solaris") |
|
84 |
+ } |
|
81 | 85 |
imgID = img.ID() |
82 | 86 |
} |
83 | 87 |
|
... | ... |
@@ -260,14 +267,14 @@ func (daemon *Daemon) verifyNetworkingConfig(nwConfig *networktypes.NetworkingCo |
260 | 260 |
for _, v := range nwConfig.EndpointsConfig { |
261 | 261 |
if v != nil && v.IPAMConfig != nil { |
262 | 262 |
if v.IPAMConfig.IPv4Address != "" && net.ParseIP(v.IPAMConfig.IPv4Address).To4() == nil { |
263 |
- return errors.NewBadRequestError(fmt.Errorf("invalid IPv4 address: %s", v.IPAMConfig.IPv4Address)) |
|
263 |
+ return apierrors.NewBadRequestError(fmt.Errorf("invalid IPv4 address: %s", v.IPAMConfig.IPv4Address)) |
|
264 | 264 |
} |
265 | 265 |
if v.IPAMConfig.IPv6Address != "" { |
266 | 266 |
n := net.ParseIP(v.IPAMConfig.IPv6Address) |
267 | 267 |
// if the address is an invalid network address (ParseIP == nil) or if it is |
268 | 268 |
// an IPv4 address (To4() != nil), then it is an invalid IPv6 address |
269 | 269 |
if n == nil || n.To4() != nil { |
270 |
- return errors.NewBadRequestError(fmt.Errorf("invalid IPv6 address: %s", v.IPAMConfig.IPv6Address)) |
|
270 |
+ return apierrors.NewBadRequestError(fmt.Errorf("invalid IPv6 address: %s", v.IPAMConfig.IPv6Address)) |
|
271 | 271 |
} |
272 | 272 |
} |
273 | 273 |
} |
... | ... |
@@ -279,5 +286,5 @@ func (daemon *Daemon) verifyNetworkingConfig(nwConfig *networktypes.NetworkingCo |
279 | 279 |
l = append(l, k) |
280 | 280 |
} |
281 | 281 |
err := fmt.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", ")) |
282 |
- return errors.NewBadRequestError(err) |
|
282 |
+ return apierrors.NewBadRequestError(err) |
|
283 | 283 |
} |
... | ... |
@@ -4,7 +4,10 @@ package daemon |
4 | 4 |
|
5 | 5 |
import ( |
6 | 6 |
"fmt" |
7 |
+ "net" |
|
8 |
+ "strconv" |
|
7 | 9 |
|
10 |
+ "github.com/Sirupsen/logrus" |
|
8 | 11 |
"github.com/docker/docker/api/types" |
9 | 12 |
containertypes "github.com/docker/docker/api/types/container" |
10 | 13 |
"github.com/docker/docker/container" |
... | ... |
@@ -12,9 +15,17 @@ import ( |
12 | 12 |
"github.com/docker/docker/layer" |
13 | 13 |
"github.com/docker/docker/pkg/idtools" |
14 | 14 |
"github.com/docker/docker/pkg/parsers/kernel" |
15 |
+ "github.com/docker/docker/pkg/sysinfo" |
|
15 | 16 |
"github.com/docker/docker/reference" |
16 | 17 |
"github.com/docker/libnetwork" |
17 | 18 |
nwconfig "github.com/docker/libnetwork/config" |
19 |
+ "github.com/docker/libnetwork/drivers/solaris/bridge" |
|
20 |
+ "github.com/docker/libnetwork/netlabel" |
|
21 |
+ "github.com/docker/libnetwork/netutils" |
|
22 |
+ lntypes "github.com/docker/libnetwork/types" |
|
23 |
+ "github.com/opencontainers/runc/libcontainer/label" |
|
24 |
+ "github.com/opencontainers/runtime-spec/specs-go" |
|
25 |
+ "github.com/pkg/errors" |
|
18 | 26 |
) |
19 | 27 |
|
20 | 28 |
//#include <zone.h> |
... | ... |
@@ -27,12 +38,50 @@ const ( |
27 | 27 |
solarisMaxCPUShares = 65535 |
28 | 28 |
) |
29 | 29 |
|
30 |
+func getMemoryResources(config containertypes.Resources) specs.CappedMemory { |
|
31 |
+ memory := specs.CappedMemory{} |
|
32 |
+ |
|
33 |
+ if config.Memory > 0 { |
|
34 |
+ memory.Physical = strconv.FormatInt(config.Memory, 10) |
|
35 |
+ } |
|
36 |
+ |
|
37 |
+ if config.MemorySwap != 0 { |
|
38 |
+ memory.Swap = strconv.FormatInt(config.MemorySwap, 10) |
|
39 |
+ } |
|
40 |
+ |
|
41 |
+ return memory |
|
42 |
+} |
|
43 |
+ |
|
44 |
+func getCPUResources(config containertypes.Resources) specs.CappedCPU { |
|
45 |
+ cpu := specs.CappedCPU{} |
|
46 |
+ |
|
47 |
+ if config.CpusetCpus != "" { |
|
48 |
+ cpu.Ncpus = config.CpusetCpus |
|
49 |
+ } |
|
50 |
+ |
|
51 |
+ return cpu |
|
52 |
+} |
|
53 |
+ |
|
30 | 54 |
func (daemon *Daemon) cleanupMountsByID(id string) error { |
31 | 55 |
return nil |
32 | 56 |
} |
33 | 57 |
|
34 | 58 |
func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error { |
35 |
- return nil |
|
59 |
+ //Since config.SecurityOpt is specifically defined as a "List of string values to |
|
60 |
+ //customize labels for MLs systems, such as SELinux" |
|
61 |
+ //until we figure out how to map to Trusted Extensions |
|
62 |
+ //this is being disabled for now on Solaris |
|
63 |
+ var ( |
|
64 |
+ labelOpts []string |
|
65 |
+ err error |
|
66 |
+ ) |
|
67 |
+ |
|
68 |
+ if len(config.SecurityOpt) > 0 { |
|
69 |
+ return errors.New("Security options are not supported on Solaris") |
|
70 |
+ } |
|
71 |
+ |
|
72 |
+ container.ProcessLabel, container.MountLabel, err = label.InitLabels(labelOpts) |
|
73 |
+ return err |
|
36 | 74 |
} |
37 | 75 |
|
38 | 76 |
func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) { |
... | ... |
@@ -67,13 +116,198 @@ func (daemon *Daemon) getCgroupDriver() string { |
67 | 67 |
} |
68 | 68 |
|
69 | 69 |
func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error { |
70 |
+ if hostConfig.CPUShares < 0 { |
|
71 |
+ logrus.Warnf("Changing requested CPUShares of %d to minimum allowed of %d", hostConfig.CPUShares, solarisMinCPUShares) |
|
72 |
+ hostConfig.CPUShares = solarisMinCPUShares |
|
73 |
+ } else if hostConfig.CPUShares > solarisMaxCPUShares { |
|
74 |
+ logrus.Warnf("Changing requested CPUShares of %d to maximum allowed of %d", hostConfig.CPUShares, solarisMaxCPUShares) |
|
75 |
+ hostConfig.CPUShares = solarisMaxCPUShares |
|
76 |
+ } |
|
77 |
+ |
|
78 |
+ if hostConfig.Memory > 0 && hostConfig.MemorySwap == 0 { |
|
79 |
+ // By default, MemorySwap is set to twice the size of Memory. |
|
80 |
+ hostConfig.MemorySwap = hostConfig.Memory * 2 |
|
81 |
+ } |
|
82 |
+ |
|
83 |
+ if hostConfig.ShmSize != 0 { |
|
84 |
+ hostConfig.ShmSize = container.DefaultSHMSize |
|
85 |
+ } |
|
86 |
+ if hostConfig.OomKillDisable == nil { |
|
87 |
+ defaultOomKillDisable := false |
|
88 |
+ hostConfig.OomKillDisable = &defaultOomKillDisable |
|
89 |
+ } |
|
90 |
+ |
|
70 | 91 |
return nil |
71 | 92 |
} |
72 | 93 |
|
94 |
+// UsingSystemd returns true if cli option includes native.cgroupdriver=systemd |
|
95 |
+func UsingSystemd(config *Config) bool { |
|
96 |
+ return false |
|
97 |
+} |
|
98 |
+ |
|
73 | 99 |
// verifyPlatformContainerSettings performs platform-specific validation of the |
74 | 100 |
// hostconfig and config structures. |
75 | 101 |
func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { |
76 | 102 |
warnings := []string{} |
103 |
+ sysInfo := sysinfo.New(true) |
|
104 |
+ // NOTE: We do not enforce a minimum value for swap limits for zones on Solaris and |
|
105 |
+ // therefore we will not do that for Docker container either. |
|
106 |
+ if hostConfig.Memory > 0 && !sysInfo.MemoryLimit { |
|
107 |
+ warnings = append(warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.") |
|
108 |
+ logrus.Warnf("Your kernel does not support memory limit capabilities. Limitation discarded.") |
|
109 |
+ hostConfig.Memory = 0 |
|
110 |
+ hostConfig.MemorySwap = -1 |
|
111 |
+ } |
|
112 |
+ if hostConfig.Memory > 0 && hostConfig.MemorySwap != -1 && !sysInfo.SwapLimit { |
|
113 |
+ warnings = append(warnings, "Your kernel does not support swap limit capabilities, memory limited without swap.") |
|
114 |
+ logrus.Warnf("Your kernel does not support swap limit capabilities, memory limited without swap.") |
|
115 |
+ hostConfig.MemorySwap = -1 |
|
116 |
+ } |
|
117 |
+ if hostConfig.Memory > 0 && hostConfig.MemorySwap > 0 && hostConfig.MemorySwap < hostConfig.Memory { |
|
118 |
+ return warnings, fmt.Errorf("Minimum memoryswap limit should be larger than memory limit, see usage.") |
|
119 |
+ } |
|
120 |
+ // Solaris NOTE: We allow and encourage setting the swap without setting the memory limit. |
|
121 |
+ |
|
122 |
+ if hostConfig.MemorySwappiness != nil && *hostConfig.MemorySwappiness != -1 && !sysInfo.MemorySwappiness { |
|
123 |
+ warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, memory swappiness discarded.") |
|
124 |
+ logrus.Warnf("Your kernel does not support memory swappiness capabilities, memory swappiness discarded.") |
|
125 |
+ hostConfig.MemorySwappiness = nil |
|
126 |
+ } |
|
127 |
+ if hostConfig.MemoryReservation > 0 && !sysInfo.MemoryReservation { |
|
128 |
+ warnings = append(warnings, "Your kernel does not support memory soft limit capabilities. Limitation discarded.") |
|
129 |
+ logrus.Warnf("Your kernel does not support memory soft limit capabilities. Limitation discarded.") |
|
130 |
+ hostConfig.MemoryReservation = 0 |
|
131 |
+ } |
|
132 |
+ if hostConfig.Memory > 0 && hostConfig.MemoryReservation > 0 && hostConfig.Memory < hostConfig.MemoryReservation { |
|
133 |
+ return warnings, fmt.Errorf("Minimum memory limit should be larger than memory reservation limit, see usage.") |
|
134 |
+ } |
|
135 |
+ if hostConfig.KernelMemory > 0 && !sysInfo.KernelMemory { |
|
136 |
+ warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities. Limitation discarded.") |
|
137 |
+ logrus.Warnf("Your kernel does not support kernel memory limit capabilities. Limitation discarded.") |
|
138 |
+ hostConfig.KernelMemory = 0 |
|
139 |
+ } |
|
140 |
+ if hostConfig.CPUShares != 0 && !sysInfo.CPUShares { |
|
141 |
+ warnings = append(warnings, "Your kernel does not support CPU shares. Shares discarded.") |
|
142 |
+ logrus.Warnf("Your kernel does not support CPU shares. Shares discarded.") |
|
143 |
+ hostConfig.CPUShares = 0 |
|
144 |
+ } |
|
145 |
+ if hostConfig.CPUShares < 0 { |
|
146 |
+ warnings = append(warnings, "Invalid CPUShares value. Must be positive. Discarding.") |
|
147 |
+ logrus.Warnf("Invalid CPUShares value. Must be positive. Discarding.") |
|
148 |
+ hostConfig.CPUQuota = 0 |
|
149 |
+ } |
|
150 |
+ if hostConfig.CPUShares > 0 && !sysinfo.IsCPUSharesAvailable() { |
|
151 |
+ warnings = append(warnings, "Global zone default scheduling class not FSS. Discarding shares.") |
|
152 |
+ logrus.Warnf("Global zone default scheduling class not FSS. Discarding shares.") |
|
153 |
+ hostConfig.CPUShares = 0 |
|
154 |
+ } |
|
155 |
+ |
|
156 |
+ // Solaris NOTE: Linux does not do negative checking for CPUShares and Quota here. But it makes sense to. |
|
157 |
+ if hostConfig.CPUPeriod > 0 && !sysInfo.CPUCfsPeriod { |
|
158 |
+ warnings = append(warnings, "Your kernel does not support CPU cfs period. Period discarded.") |
|
159 |
+ logrus.Warnf("Your kernel does not support CPU cfs period. Period discarded.") |
|
160 |
+ if hostConfig.CPUQuota > 0 { |
|
161 |
+ warnings = append(warnings, "Quota will be applied on default period, not period specified.") |
|
162 |
+ logrus.Warnf("Quota will be applied on default period, not period specified.") |
|
163 |
+ } |
|
164 |
+ hostConfig.CPUPeriod = 0 |
|
165 |
+ } |
|
166 |
+ if hostConfig.CPUQuota != 0 && !sysInfo.CPUCfsQuota { |
|
167 |
+ warnings = append(warnings, "Your kernel does not support CPU cfs quota. Quota discarded.") |
|
168 |
+ logrus.Warnf("Your kernel does not support CPU cfs quota. Quota discarded.") |
|
169 |
+ hostConfig.CPUQuota = 0 |
|
170 |
+ } |
|
171 |
+ if hostConfig.CPUQuota < 0 { |
|
172 |
+ warnings = append(warnings, "Invalid CPUQuota value. Must be positive. Discarding.") |
|
173 |
+ logrus.Warnf("Invalid CPUQuota value. Must be positive. Discarding.") |
|
174 |
+ hostConfig.CPUQuota = 0 |
|
175 |
+ } |
|
176 |
+ if (hostConfig.CpusetCpus != "" || hostConfig.CpusetMems != "") && !sysInfo.Cpuset { |
|
177 |
+ warnings = append(warnings, "Your kernel does not support cpuset. Cpuset discarded.") |
|
178 |
+ logrus.Warnf("Your kernel does not support cpuset. Cpuset discarded.") |
|
179 |
+ hostConfig.CpusetCpus = "" |
|
180 |
+ hostConfig.CpusetMems = "" |
|
181 |
+ } |
|
182 |
+ cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(hostConfig.CpusetCpus) |
|
183 |
+ if err != nil { |
|
184 |
+ return warnings, fmt.Errorf("Invalid value %s for cpuset cpus.", hostConfig.CpusetCpus) |
|
185 |
+ } |
|
186 |
+ if !cpusAvailable { |
|
187 |
+ return warnings, fmt.Errorf("Requested CPUs are not available - requested %s, available: %s.", hostConfig.CpusetCpus, sysInfo.Cpus) |
|
188 |
+ } |
|
189 |
+ memsAvailable, err := sysInfo.IsCpusetMemsAvailable(hostConfig.CpusetMems) |
|
190 |
+ if err != nil { |
|
191 |
+ return warnings, fmt.Errorf("Invalid value %s for cpuset mems.", hostConfig.CpusetMems) |
|
192 |
+ } |
|
193 |
+ if !memsAvailable { |
|
194 |
+ return warnings, fmt.Errorf("Requested memory nodes are not available - requested %s, available: %s.", hostConfig.CpusetMems, sysInfo.Mems) |
|
195 |
+ } |
|
196 |
+ if hostConfig.BlkioWeight > 0 && !sysInfo.BlkioWeight { |
|
197 |
+ warnings = append(warnings, "Your kernel does not support Block I/O weight. Weight discarded.") |
|
198 |
+ logrus.Warnf("Your kernel does not support Block I/O weight. Weight discarded.") |
|
199 |
+ hostConfig.BlkioWeight = 0 |
|
200 |
+ } |
|
201 |
+ if hostConfig.OomKillDisable != nil && !sysInfo.OomKillDisable { |
|
202 |
+ *hostConfig.OomKillDisable = false |
|
203 |
+ // Don't warn; this is the default setting but only applicable to Linux |
|
204 |
+ } |
|
205 |
+ |
|
206 |
+ if sysInfo.IPv4ForwardingDisabled { |
|
207 |
+ warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.") |
|
208 |
+ logrus.Warnf("IPv4 forwarding is disabled. Networking will not work") |
|
209 |
+ } |
|
210 |
+ |
|
211 |
+ // Solaris NOTE: We do not allow setting Linux specific options, so check and warn for all of them. |
|
212 |
+ |
|
213 |
+ if hostConfig.CapAdd != nil || hostConfig.CapDrop != nil { |
|
214 |
+ warnings = append(warnings, "Adding or dropping kernel capabilities unsupported on Solaris.Discarding capabilities lists.") |
|
215 |
+ logrus.Warnf("Adding or dropping kernel capabilities unsupported on Solaris.Discarding capabilities lists.") |
|
216 |
+ hostConfig.CapAdd = nil |
|
217 |
+ hostConfig.CapDrop = nil |
|
218 |
+ } |
|
219 |
+ |
|
220 |
+ if hostConfig.GroupAdd != nil { |
|
221 |
+ warnings = append(warnings, "Additional groups unsupported on Solaris.Discarding groups lists.") |
|
222 |
+ logrus.Warnf("Additional groups unsupported on Solaris.Discarding groups lists.") |
|
223 |
+ hostConfig.GroupAdd = nil |
|
224 |
+ } |
|
225 |
+ |
|
226 |
+ if hostConfig.IpcMode != "" { |
|
227 |
+ warnings = append(warnings, "IPC namespace assignment unsupported on Solaris.Discarding IPC setting.") |
|
228 |
+ logrus.Warnf("IPC namespace assignment unsupported on Solaris.Discarding IPC setting.") |
|
229 |
+ hostConfig.IpcMode = "" |
|
230 |
+ } |
|
231 |
+ |
|
232 |
+ if hostConfig.PidMode != "" { |
|
233 |
+ warnings = append(warnings, "PID namespace setting unsupported on Solaris. Running container in host PID namespace.") |
|
234 |
+ logrus.Warnf("PID namespace setting unsupported on Solaris. Running container in host PID namespace.") |
|
235 |
+ hostConfig.PidMode = "" |
|
236 |
+ } |
|
237 |
+ |
|
238 |
+ if hostConfig.Privileged { |
|
239 |
+ warnings = append(warnings, "Privileged mode unsupported on Solaris. Discarding privileged mode setting.") |
|
240 |
+ logrus.Warnf("Privileged mode unsupported on Solaris. Discarding privileged mode setting.") |
|
241 |
+ hostConfig.Privileged = false |
|
242 |
+ } |
|
243 |
+ |
|
244 |
+ if hostConfig.UTSMode != "" { |
|
245 |
+ warnings = append(warnings, "UTS namespace assignment unsupported on Solaris.Discarding UTS setting.") |
|
246 |
+ logrus.Warnf("UTS namespace assignment unsupported on Solaris.Discarding UTS setting.") |
|
247 |
+ hostConfig.UTSMode = "" |
|
248 |
+ } |
|
249 |
+ |
|
250 |
+ if hostConfig.CgroupParent != "" { |
|
251 |
+ warnings = append(warnings, "Specifying Cgroup parent unsupported on Solaris. Discarding cgroup parent setting.") |
|
252 |
+ logrus.Warnf("Specifying Cgroup parent unsupported on Solaris. Discarding cgroup parent setting.") |
|
253 |
+ hostConfig.CgroupParent = "" |
|
254 |
+ } |
|
255 |
+ |
|
256 |
+ if hostConfig.Ulimits != nil { |
|
257 |
+ warnings = append(warnings, "Specifying ulimits unsupported on Solaris. Discarding ulimits setting.") |
|
258 |
+ logrus.Warnf("Specifying ulimits unsupported on Solaris. Discarding ulimits setting.") |
|
259 |
+ hostConfig.Ulimits = nil |
|
260 |
+ } |
|
261 |
+ |
|
77 | 262 |
return warnings, nil |
78 | 263 |
} |
79 | 264 |
|
... | ... |
@@ -84,6 +318,16 @@ func (daemon *Daemon) platformReload(config *Config) map[string]string { |
84 | 84 |
|
85 | 85 |
// verifyDaemonSettings performs validation of daemon config struct |
86 | 86 |
func verifyDaemonSettings(config *Config) error { |
87 |
+ |
|
88 |
+ if config.DefaultRuntime == "" { |
|
89 |
+ config.DefaultRuntime = stockRuntimeName |
|
90 |
+ } |
|
91 |
+ if config.Runtimes == nil { |
|
92 |
+ config.Runtimes = make(map[string]types.Runtime) |
|
93 |
+ } |
|
94 |
+ stockRuntimeOpts := []string{} |
|
95 |
+ config.Runtimes[stockRuntimeName] = types.Runtime{Path: DefaultRuntimeBinary, Args: stockRuntimeOpts} |
|
96 |
+ |
|
87 | 97 |
// checkSystem validates platform-specific requirements |
88 | 98 |
return nil |
89 | 99 |
} |
... | ... |
@@ -119,7 +363,120 @@ func configureKernelSecuritySupport(config *Config, driverName string) error { |
119 | 119 |
} |
120 | 120 |
|
121 | 121 |
func (daemon *Daemon) initNetworkController(config *Config, activeSandboxes map[string]interface{}) (libnetwork.NetworkController, error) { |
122 |
- return nil, nil |
|
122 |
+ netOptions, err := daemon.networkOptions(config, daemon.PluginStore, activeSandboxes) |
|
123 |
+ if err != nil { |
|
124 |
+ return nil, err |
|
125 |
+ } |
|
126 |
+ |
|
127 |
+ controller, err := libnetwork.New(netOptions...) |
|
128 |
+ if err != nil { |
|
129 |
+ return nil, fmt.Errorf("error obtaining controller instance: %v", err) |
|
130 |
+ } |
|
131 |
+ |
|
132 |
+ // Initialize default network on "null" |
|
133 |
+ if _, err := controller.NewNetwork("null", "none", "", libnetwork.NetworkOptionPersist(false)); err != nil { |
|
134 |
+ return nil, fmt.Errorf("Error creating default 'null' network: %v", err) |
|
135 |
+ } |
|
136 |
+ |
|
137 |
+ if !config.DisableBridge { |
|
138 |
+ // Initialize default driver "bridge" |
|
139 |
+ if err := initBridgeDriver(controller, config); err != nil { |
|
140 |
+ return nil, err |
|
141 |
+ } |
|
142 |
+ } |
|
143 |
+ |
|
144 |
+ return controller, nil |
|
145 |
+} |
|
146 |
+ |
|
147 |
+func initBridgeDriver(controller libnetwork.NetworkController, config *Config) error { |
|
148 |
+ if n, err := controller.NetworkByName("bridge"); err == nil { |
|
149 |
+ if err = n.Delete(); err != nil { |
|
150 |
+ return fmt.Errorf("could not delete the default bridge network: %v", err) |
|
151 |
+ } |
|
152 |
+ } |
|
153 |
+ |
|
154 |
+ bridgeName := bridge.DefaultBridgeName |
|
155 |
+ if config.bridgeConfig.Iface != "" { |
|
156 |
+ bridgeName = config.bridgeConfig.Iface |
|
157 |
+ } |
|
158 |
+ netOption := map[string]string{ |
|
159 |
+ bridge.BridgeName: bridgeName, |
|
160 |
+ bridge.DefaultBridge: strconv.FormatBool(true), |
|
161 |
+ netlabel.DriverMTU: strconv.Itoa(config.Mtu), |
|
162 |
+ bridge.EnableICC: strconv.FormatBool(config.bridgeConfig.InterContainerCommunication), |
|
163 |
+ } |
|
164 |
+ |
|
165 |
+ // --ip processing |
|
166 |
+ if config.bridgeConfig.DefaultIP != nil { |
|
167 |
+ netOption[bridge.DefaultBindingIP] = config.bridgeConfig.DefaultIP.String() |
|
168 |
+ } |
|
169 |
+ |
|
170 |
+ var ipamV4Conf *libnetwork.IpamConf |
|
171 |
+ |
|
172 |
+ ipamV4Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} |
|
173 |
+ |
|
174 |
+ nwList, _, err := netutils.ElectInterfaceAddresses(bridgeName) |
|
175 |
+ if err != nil { |
|
176 |
+ return errors.Wrap(err, "list bridge addresses failed") |
|
177 |
+ } |
|
178 |
+ |
|
179 |
+ nw := nwList[0] |
|
180 |
+ if len(nwList) > 1 && config.bridgeConfig.FixedCIDR != "" { |
|
181 |
+ _, fCIDR, err := net.ParseCIDR(config.bridgeConfig.FixedCIDR) |
|
182 |
+ if err != nil { |
|
183 |
+ return errors.Wrap(err, "parse CIDR failed") |
|
184 |
+ } |
|
185 |
+ // Iterate through in case there are multiple addresses for the bridge |
|
186 |
+ for _, entry := range nwList { |
|
187 |
+ if fCIDR.Contains(entry.IP) { |
|
188 |
+ nw = entry |
|
189 |
+ break |
|
190 |
+ } |
|
191 |
+ } |
|
192 |
+ } |
|
193 |
+ |
|
194 |
+ ipamV4Conf.PreferredPool = lntypes.GetIPNetCanonical(nw).String() |
|
195 |
+ hip, _ := lntypes.GetHostPartIP(nw.IP, nw.Mask) |
|
196 |
+ if hip.IsGlobalUnicast() { |
|
197 |
+ ipamV4Conf.Gateway = nw.IP.String() |
|
198 |
+ } |
|
199 |
+ |
|
200 |
+ if config.bridgeConfig.IP != "" { |
|
201 |
+ ipamV4Conf.PreferredPool = config.bridgeConfig.IP |
|
202 |
+ ip, _, err := net.ParseCIDR(config.bridgeConfig.IP) |
|
203 |
+ if err != nil { |
|
204 |
+ return err |
|
205 |
+ } |
|
206 |
+ ipamV4Conf.Gateway = ip.String() |
|
207 |
+ } else if bridgeName == bridge.DefaultBridgeName && ipamV4Conf.PreferredPool != "" { |
|
208 |
+ logrus.Infof("Default bridge (%s) is assigned with an IP address %s. Daemon option --bip can be used to set a preferred IP address", bridgeName, ipamV4Conf.PreferredPool) |
|
209 |
+ } |
|
210 |
+ |
|
211 |
+ if config.bridgeConfig.FixedCIDR != "" { |
|
212 |
+ _, fCIDR, err := net.ParseCIDR(config.bridgeConfig.FixedCIDR) |
|
213 |
+ if err != nil { |
|
214 |
+ return err |
|
215 |
+ } |
|
216 |
+ |
|
217 |
+ ipamV4Conf.SubPool = fCIDR.String() |
|
218 |
+ } |
|
219 |
+ |
|
220 |
+ if config.bridgeConfig.DefaultGatewayIPv4 != nil { |
|
221 |
+ ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.bridgeConfig.DefaultGatewayIPv4.String() |
|
222 |
+ } |
|
223 |
+ |
|
224 |
+ v4Conf := []*libnetwork.IpamConf{ipamV4Conf} |
|
225 |
+ v6Conf := []*libnetwork.IpamConf{} |
|
226 |
+ |
|
227 |
+ // Initialize default network on "bridge" with the same name |
|
228 |
+ _, err = controller.NewNetwork("bridge", "bridge", "", |
|
229 |
+ libnetwork.NetworkOptionDriverOpts(netOption), |
|
230 |
+ libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil), |
|
231 |
+ libnetwork.NetworkOptionDeferIPv6Alloc(false)) |
|
232 |
+ if err != nil { |
|
233 |
+ return fmt.Errorf("Error creating default 'bridge' network: %v", err) |
|
234 |
+ } |
|
235 |
+ return nil |
|
123 | 236 |
} |
124 | 237 |
|
125 | 238 |
// registerLinks sets up links between containers and writes the |
... | ... |
@@ -135,7 +492,7 @@ func (daemon *Daemon) cleanupMounts() error { |
135 | 135 |
// conditionalMountOnStart is a platform specific helper function during the |
136 | 136 |
// container start to call mount. |
137 | 137 |
func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error { |
138 |
- return nil |
|
138 |
+ return daemon.Mount(container) |
|
139 | 139 |
} |
140 | 140 |
|
141 | 141 |
// conditionalUnmountOnCleanup is a platform specific helper function called |
... | ... |
@@ -171,13 +528,6 @@ func setupDaemonProcess(config *Config) error { |
171 | 171 |
return nil |
172 | 172 |
} |
173 | 173 |
|
174 |
-// verifyVolumesInfo is a no-op on solaris. |
|
175 |
-// This is called during daemon initialization to migrate volumes from pre-1.7. |
|
176 |
-// Solaris was not supported on pre-1.7 daemons. |
|
177 |
-func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error { |
|
178 |
- return nil |
|
179 |
-} |
|
180 |
- |
|
181 | 174 |
func (daemon *Daemon) setupSeccompProfile() error { |
182 | 175 |
return nil |
183 | 176 |
} |
5 | 5 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,41 @@ |
0 |
+// +build linux freebsd solaris |
|
1 |
+ |
|
2 |
+package daemon |
|
3 |
+ |
|
4 |
+import ( |
|
5 |
+ "github.com/Sirupsen/logrus" |
|
6 |
+ "github.com/docker/docker/container" |
|
7 |
+) |
|
8 |
+ |
|
9 |
+// getSize returns the real size & virtual size of the container. |
|
10 |
+func (daemon *Daemon) getSize(container *container.Container) (int64, int64) { |
|
11 |
+ var ( |
|
12 |
+ sizeRw, sizeRootfs int64 |
|
13 |
+ err error |
|
14 |
+ ) |
|
15 |
+ |
|
16 |
+ if err := daemon.Mount(container); err != nil { |
|
17 |
+ logrus.Errorf("Failed to compute size of container rootfs %s: %s", container.ID, err) |
|
18 |
+ return sizeRw, sizeRootfs |
|
19 |
+ } |
|
20 |
+ defer daemon.Unmount(container) |
|
21 |
+ |
|
22 |
+ sizeRw, err = container.RWLayer.Size() |
|
23 |
+ if err != nil { |
|
24 |
+ logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", |
|
25 |
+ daemon.GraphDriverName(), container.ID, err) |
|
26 |
+ // FIXME: GetSize should return an error. Not changing it now in case |
|
27 |
+ // there is a side-effect. |
|
28 |
+ sizeRw = -1 |
|
29 |
+ } |
|
30 |
+ |
|
31 |
+ if parent := container.RWLayer.Parent(); parent != nil { |
|
32 |
+ sizeRootfs, err = parent.Size() |
|
33 |
+ if err != nil { |
|
34 |
+ sizeRootfs = -1 |
|
35 |
+ } else if sizeRw != -1 { |
|
36 |
+ sizeRootfs += sizeRw |
|
37 |
+ } |
|
38 |
+ } |
|
39 |
+ return sizeRw, sizeRootfs |
|
40 |
+} |
... | ... |
@@ -20,6 +20,7 @@ import ( |
20 | 20 |
"unsafe" |
21 | 21 |
|
22 | 22 |
"github.com/Sirupsen/logrus" |
23 |
+ "github.com/docker/docker/pkg/mount" |
|
23 | 24 |
) |
24 | 25 |
|
25 | 26 |
const ( |
... | ... |
@@ -44,6 +45,37 @@ func GetFSMagic(rootpath string) (FsMagic, error) { |
44 | 44 |
return 0, nil |
45 | 45 |
} |
46 | 46 |
|
47 |
+type fsChecker struct { |
|
48 |
+ t FsMagic |
|
49 |
+} |
|
50 |
+ |
|
51 |
+func (c *fsChecker) IsMounted(path string) bool { |
|
52 |
+ m, _ := Mounted(c.t, path) |
|
53 |
+ return m |
|
54 |
+} |
|
55 |
+ |
|
56 |
+// NewFsChecker returns a checker configured for the provied FsMagic |
|
57 |
+func NewFsChecker(t FsMagic) Checker { |
|
58 |
+ return &fsChecker{ |
|
59 |
+ t: t, |
|
60 |
+ } |
|
61 |
+} |
|
62 |
+ |
|
63 |
+// NewDefaultChecker returns a check that parses /proc/mountinfo to check |
|
64 |
+// if the specified path is mounted. |
|
65 |
+// No-op on Solaris. |
|
66 |
+func NewDefaultChecker() Checker { |
|
67 |
+ return &defaultChecker{} |
|
68 |
+} |
|
69 |
+ |
|
70 |
+type defaultChecker struct { |
|
71 |
+} |
|
72 |
+ |
|
73 |
+func (c *defaultChecker) IsMounted(path string) bool { |
|
74 |
+ m, _ := mount.Mounted(path) |
|
75 |
+ return m |
|
76 |
+} |
|
77 |
+ |
|
47 | 78 |
// Mounted checks if the given path is mounted as the fs type |
48 | 79 |
//Solaris supports only ZFS for now |
49 | 80 |
func Mounted(fsType FsMagic, mountPath string) (bool, error) { |
... | ... |
@@ -3,6 +3,7 @@ package daemon |
3 | 3 |
import ( |
4 | 4 |
"github.com/docker/docker/api/types" |
5 | 5 |
"github.com/docker/docker/api/types/backend" |
6 |
+ "github.com/docker/docker/api/types/versions/v1p19" |
|
6 | 7 |
"github.com/docker/docker/container" |
7 | 8 |
"github.com/docker/docker/daemon/exec" |
8 | 9 |
) |
... | ... |
@@ -13,8 +14,8 @@ func setPlatformSpecificContainerFields(container *container.Container, contJSON |
13 | 13 |
} |
14 | 14 |
|
15 | 15 |
// containerInspectPre120 get containers for pre 1.20 APIs. |
16 |
-func (daemon *Daemon) containerInspectPre120(name string) (*types.ContainerJSON, error) { |
|
17 |
- return daemon.containerInspectCurrent(name, false) |
|
16 |
+func (daemon *Daemon) containerInspectPre120(name string) (*v1p19.ContainerJSON, error) { |
|
17 |
+ return &v1p19.ContainerJSON{}, nil |
|
18 | 18 |
} |
19 | 19 |
|
20 | 20 |
func addMountPoints(container *container.Container) []types.MountPoint { |
... | ... |
@@ -3,17 +3,19 @@ package daemon |
3 | 3 |
import ( |
4 | 4 |
"fmt" |
5 | 5 |
"net" |
6 |
+ "runtime" |
|
6 | 7 |
"sort" |
7 | 8 |
"strings" |
8 | 9 |
|
9 | 10 |
"github.com/Sirupsen/logrus" |
10 |
- "github.com/docker/docker/api/errors" |
|
11 |
+ apierrors "github.com/docker/docker/api/errors" |
|
11 | 12 |
"github.com/docker/docker/api/types" |
12 | 13 |
"github.com/docker/docker/api/types/network" |
13 | 14 |
clustertypes "github.com/docker/docker/daemon/cluster/provider" |
14 | 15 |
"github.com/docker/docker/runconfig" |
15 | 16 |
"github.com/docker/libnetwork" |
16 | 17 |
networktypes "github.com/docker/libnetwork/types" |
18 |
+ "github.com/pkg/errors" |
|
17 | 19 |
"golang.org/x/net/context" |
18 | 20 |
) |
19 | 21 |
|
... | ... |
@@ -236,7 +238,7 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string |
236 | 236 |
|
237 | 237 |
if runconfig.IsPreDefinedNetwork(create.Name) && !agent { |
238 | 238 |
err := fmt.Errorf("%s is a pre-defined network and cannot be created", create.Name) |
239 |
- return nil, errors.NewRequestForbiddenError(err) |
|
239 |
+ return nil, apierrors.NewRequestForbiddenError(err) |
|
240 | 240 |
} |
241 | 241 |
|
242 | 242 |
var warning string |
... | ... |
@@ -336,6 +338,9 @@ func (daemon *Daemon) UpdateContainerServiceConfig(containerName string, service |
336 | 336 |
// network. If either cannot be found, an err is returned. If the |
337 | 337 |
// network cannot be set up, an err is returned. |
338 | 338 |
func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error { |
339 |
+ if runtime.GOOS == "solaris" { |
|
340 |
+ return errors.New("docker network connect is unsupported on Solaris platform") |
|
341 |
+ } |
|
339 | 342 |
container, err := daemon.GetContainer(containerName) |
340 | 343 |
if err != nil { |
341 | 344 |
return err |
... | ... |
@@ -346,6 +351,9 @@ func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName strin |
346 | 346 |
// DisconnectContainerFromNetwork disconnects the given container from |
347 | 347 |
// the given network. If either cannot be found, an err is returned. |
348 | 348 |
func (daemon *Daemon) DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error { |
349 |
+ if runtime.GOOS == "solaris" { |
|
350 |
+ return errors.New("docker network disconnect is unsupported on Solaris platform") |
|
351 |
+ } |
|
349 | 352 |
container, err := daemon.GetContainer(containerName) |
350 | 353 |
if err != nil { |
351 | 354 |
if force { |
... | ... |
@@ -401,7 +409,7 @@ func (daemon *Daemon) deleteNetwork(networkID string, dynamic bool) error { |
401 | 401 |
|
402 | 402 |
if runconfig.IsPreDefinedNetwork(nw.Name()) && !dynamic { |
403 | 403 |
err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name()) |
404 |
- return errors.NewRequestForbiddenError(err) |
|
404 |
+ return apierrors.NewRequestForbiddenError(err) |
|
405 | 405 |
} |
406 | 406 |
|
407 | 407 |
if err := nw.Delete(); err != nil { |
... | ... |
@@ -1,14 +1,183 @@ |
1 | 1 |
package daemon |
2 | 2 |
|
3 | 3 |
import ( |
4 |
+ "fmt" |
|
5 |
+ "path/filepath" |
|
6 |
+ "sort" |
|
7 |
+ "strconv" |
|
8 |
+ |
|
4 | 9 |
containertypes "github.com/docker/docker/api/types/container" |
5 | 10 |
"github.com/docker/docker/container" |
6 | 11 |
"github.com/docker/docker/oci" |
12 |
+ "github.com/docker/libnetwork" |
|
7 | 13 |
"github.com/opencontainers/runtime-spec/specs-go" |
8 | 14 |
) |
9 | 15 |
|
16 |
+func setResources(s *specs.Spec, r containertypes.Resources) error { |
|
17 |
+ mem := getMemoryResources(r) |
|
18 |
+ s.Solaris.CappedMemory = &mem |
|
19 |
+ |
|
20 |
+ capCPU := getCPUResources(r) |
|
21 |
+ s.Solaris.CappedCPU = &capCPU |
|
22 |
+ |
|
23 |
+ return nil |
|
24 |
+} |
|
25 |
+ |
|
26 |
+func setUser(s *specs.Spec, c *container.Container) error { |
|
27 |
+ uid, gid, additionalGids, err := getUser(c, c.Config.User) |
|
28 |
+ if err != nil { |
|
29 |
+ return err |
|
30 |
+ } |
|
31 |
+ s.Process.User.UID = uid |
|
32 |
+ s.Process.User.GID = gid |
|
33 |
+ s.Process.User.AdditionalGids = additionalGids |
|
34 |
+ return nil |
|
35 |
+} |
|
36 |
+ |
|
37 |
+func getUser(c *container.Container, username string) (uint32, uint32, []uint32, error) { |
|
38 |
+ return 0, 0, nil, nil |
|
39 |
+} |
|
40 |
+ |
|
41 |
+func (daemon *Daemon) getRunzAnet(ep libnetwork.Endpoint) (specs.Anet, error) { |
|
42 |
+ var ( |
|
43 |
+ linkName string |
|
44 |
+ lowerLink string |
|
45 |
+ defRouter string |
|
46 |
+ ) |
|
47 |
+ |
|
48 |
+ epInfo := ep.Info() |
|
49 |
+ if epInfo == nil { |
|
50 |
+ return specs.Anet{}, fmt.Errorf("invalid endpoint") |
|
51 |
+ } |
|
52 |
+ |
|
53 |
+ nw, err := daemon.GetNetworkByName(ep.Network()) |
|
54 |
+ if err != nil { |
|
55 |
+ return specs.Anet{}, fmt.Errorf("Failed to get network %s: %v", ep.Network(), err) |
|
56 |
+ } |
|
57 |
+ |
|
58 |
+ // Evaluate default router, linkname and lowerlink for interface endpoint |
|
59 |
+ switch nw.Type() { |
|
60 |
+ case "bridge": |
|
61 |
+ defRouter = epInfo.Gateway().String() |
|
62 |
+ linkName = "net0" // Should always be net0 for a container |
|
63 |
+ |
|
64 |
+ // TODO We construct lowerlink here exactly as done for solaris bridge |
|
65 |
+ // initialization. Need modular code to reuse. |
|
66 |
+ options := nw.Info().DriverOptions() |
|
67 |
+ nwName := options["com.docker.network.bridge.name"] |
|
68 |
+ lastChar := nwName[len(nwName)-1:] |
|
69 |
+ if _, err = strconv.Atoi(lastChar); err != nil { |
|
70 |
+ lowerLink = nwName + "_0" |
|
71 |
+ } else { |
|
72 |
+ lowerLink = nwName |
|
73 |
+ } |
|
74 |
+ |
|
75 |
+ case "overlay": |
|
76 |
+ defRouter = "" |
|
77 |
+ linkName = "net1" |
|
78 |
+ |
|
79 |
+ // TODO Follows generateVxlanName() in solaris overlay. |
|
80 |
+ id := nw.ID() |
|
81 |
+ if len(nw.ID()) > 12 { |
|
82 |
+ id = nw.ID()[:12] |
|
83 |
+ } |
|
84 |
+ lowerLink = "vx_" + id + "_0" |
|
85 |
+ } |
|
86 |
+ |
|
87 |
+ runzanet := specs.Anet{ |
|
88 |
+ Linkname: linkName, |
|
89 |
+ Lowerlink: lowerLink, |
|
90 |
+ Allowedaddr: epInfo.Iface().Address().String(), |
|
91 |
+ Configallowedaddr: "true", |
|
92 |
+ Defrouter: defRouter, |
|
93 |
+ Linkprotection: "mac-nospoof, ip-nospoof", |
|
94 |
+ Macaddress: epInfo.Iface().MacAddress().String(), |
|
95 |
+ } |
|
96 |
+ |
|
97 |
+ return runzanet, nil |
|
98 |
+} |
|
99 |
+ |
|
100 |
+func (daemon *Daemon) setNetworkInterface(s *specs.Spec, c *container.Container) error { |
|
101 |
+ var anets []specs.Anet |
|
102 |
+ |
|
103 |
+ sb, err := daemon.netController.SandboxByID(c.NetworkSettings.SandboxID) |
|
104 |
+ if err != nil { |
|
105 |
+ return fmt.Errorf("Could not obtain sandbox for container") |
|
106 |
+ } |
|
107 |
+ |
|
108 |
+ // Populate interfaces required for each endpoint |
|
109 |
+ for _, ep := range sb.Endpoints() { |
|
110 |
+ runzanet, err := daemon.getRunzAnet(ep) |
|
111 |
+ if err != nil { |
|
112 |
+ return fmt.Errorf("Failed to get interface information for endpoint %d: %v", ep.ID(), err) |
|
113 |
+ } |
|
114 |
+ anets = append(anets, runzanet) |
|
115 |
+ } |
|
116 |
+ |
|
117 |
+ s.Solaris.Anet = anets |
|
118 |
+ if anets != nil { |
|
119 |
+ s.Solaris.Milestone = "svc:/milestone/container:default" |
|
120 |
+ } |
|
121 |
+ return nil |
|
122 |
+} |
|
123 |
+ |
|
124 |
+func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container) error { |
|
125 |
+ linkedEnv, err := daemon.setupLinkedContainers(c) |
|
126 |
+ if err != nil { |
|
127 |
+ return err |
|
128 |
+ } |
|
129 |
+ s.Root = specs.Root{ |
|
130 |
+ Path: filepath.Dir(c.BaseFS), |
|
131 |
+ Readonly: c.HostConfig.ReadonlyRootfs, |
|
132 |
+ } |
|
133 |
+ rootUID, rootGID := daemon.GetRemappedUIDGID() |
|
134 |
+ if err := c.SetupWorkingDirectory(rootUID, rootGID); err != nil { |
|
135 |
+ return err |
|
136 |
+ } |
|
137 |
+ cwd := c.Config.WorkingDir |
|
138 |
+ s.Process.Args = append([]string{c.Path}, c.Args...) |
|
139 |
+ s.Process.Cwd = cwd |
|
140 |
+ s.Process.Env = c.CreateDaemonEnvironment(c.Config.Tty, linkedEnv) |
|
141 |
+ s.Process.Terminal = c.Config.Tty |
|
142 |
+ s.Hostname = c.FullHostname() |
|
143 |
+ |
|
144 |
+ return nil |
|
145 |
+} |
|
146 |
+ |
|
10 | 147 |
func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { |
11 | 148 |
s := oci.DefaultSpec() |
149 |
+ if err := daemon.populateCommonSpec(&s, c); err != nil { |
|
150 |
+ return nil, err |
|
151 |
+ } |
|
152 |
+ |
|
153 |
+ if err := setResources(&s, c.HostConfig.Resources); err != nil { |
|
154 |
+ return nil, fmt.Errorf("runtime spec resources: %v", err) |
|
155 |
+ } |
|
156 |
+ |
|
157 |
+ if err := setUser(&s, c); err != nil { |
|
158 |
+ return nil, fmt.Errorf("spec user: %v", err) |
|
159 |
+ } |
|
160 |
+ |
|
161 |
+ if err := daemon.setNetworkInterface(&s, c); err != nil { |
|
162 |
+ return nil, err |
|
163 |
+ } |
|
164 |
+ |
|
165 |
+ if err := daemon.setupIpcDirs(c); err != nil { |
|
166 |
+ return nil, err |
|
167 |
+ } |
|
168 |
+ |
|
169 |
+ ms, err := daemon.setupMounts(c) |
|
170 |
+ if err != nil { |
|
171 |
+ return nil, err |
|
172 |
+ } |
|
173 |
+ ms = append(ms, c.IpcMounts()...) |
|
174 |
+ tmpfsMounts, err := c.TmpfsMounts() |
|
175 |
+ if err != nil { |
|
176 |
+ return nil, err |
|
177 |
+ } |
|
178 |
+ ms = append(ms, tmpfsMounts...) |
|
179 |
+ sort.Sort(mounts(ms)) |
|
180 |
+ |
|
12 | 181 |
return (*specs.Spec)(&s), nil |
13 | 182 |
} |
14 | 183 |
|
... | ... |
@@ -11,7 +11,7 @@ import ( |
11 | 11 |
"google.golang.org/grpc" |
12 | 12 |
|
13 | 13 |
"github.com/Sirupsen/logrus" |
14 |
- "github.com/docker/docker/api/errors" |
|
14 |
+ apierrors "github.com/docker/docker/api/errors" |
|
15 | 15 |
"github.com/docker/docker/api/types" |
16 | 16 |
containertypes "github.com/docker/docker/api/types/container" |
17 | 17 |
"github.com/docker/docker/container" |
... | ... |
@@ -21,7 +21,7 @@ import ( |
21 | 21 |
// ContainerStart starts a container. |
22 | 22 |
func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error { |
23 | 23 |
if checkpoint != "" && !daemon.HasExperimental() { |
24 |
- return errors.NewBadRequestError(fmt.Errorf("checkpoint is only supported in experimental mode")) |
|
24 |
+ return apierrors.NewBadRequestError(fmt.Errorf("checkpoint is only supported in experimental mode")) |
|
25 | 25 |
} |
26 | 26 |
|
27 | 27 |
container, err := daemon.GetContainer(name) |
... | ... |
@@ -35,7 +35,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos |
35 | 35 |
|
36 | 36 |
if container.IsRunning() { |
37 | 37 |
err := fmt.Errorf("Container already started") |
38 |
- return errors.NewErrorWithStatusCode(err, http.StatusNotModified) |
|
38 |
+ return apierrors.NewErrorWithStatusCode(err, http.StatusNotModified) |
|
39 | 39 |
} |
40 | 40 |
|
41 | 41 |
// Windows does not have the backwards compatibility issue here. |
42 | 42 |
deleted file mode 100644 |
... | ... |
@@ -1,29 +0,0 @@ |
1 |
-package daemon |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "fmt" |
|
5 |
- |
|
6 |
- "github.com/docker/docker/container" |
|
7 |
- "github.com/docker/docker/libcontainerd" |
|
8 |
-) |
|
9 |
- |
|
10 |
-func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) ([]libcontainerd.CreateOption, error) { |
|
11 |
- createOptions := []libcontainerd.CreateOption{} |
|
12 |
- |
|
13 |
- // Ensure a runtime has been assigned to this container |
|
14 |
- if container.HostConfig.Runtime == "" { |
|
15 |
- container.HostConfig.Runtime = stockRuntimeName |
|
16 |
- container.ToDisk() |
|
17 |
- } |
|
18 |
- |
|
19 |
- rt := daemon.configStore.GetRuntime(container.HostConfig.Runtime) |
|
20 |
- if rt == nil { |
|
21 |
- return nil, fmt.Errorf("no such runtime '%s'", container.HostConfig.Runtime) |
|
22 |
- } |
|
23 |
- if UsingSystemd(daemon.configStore) { |
|
24 |
- rt.Args = append(rt.Args, "--systemd-cgroup=true") |
|
25 |
- } |
|
26 |
- createOptions = append(createOptions, libcontainerd.WithRuntime(rt.Path, rt.Args)) |
|
27 |
- |
|
28 |
- return createOptions, nil |
|
29 |
-} |
30 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,31 @@ |
0 |
+// +build !windows |
|
1 |
+ |
|
2 |
+package daemon |
|
3 |
+ |
|
4 |
+import ( |
|
5 |
+ "fmt" |
|
6 |
+ |
|
7 |
+ "github.com/docker/docker/container" |
|
8 |
+ "github.com/docker/docker/libcontainerd" |
|
9 |
+) |
|
10 |
+ |
|
11 |
+func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) ([]libcontainerd.CreateOption, error) { |
|
12 |
+ createOptions := []libcontainerd.CreateOption{} |
|
13 |
+ |
|
14 |
+ // Ensure a runtime has been assigned to this container |
|
15 |
+ if container.HostConfig.Runtime == "" { |
|
16 |
+ container.HostConfig.Runtime = stockRuntimeName |
|
17 |
+ container.ToDisk() |
|
18 |
+ } |
|
19 |
+ |
|
20 |
+ rt := daemon.configStore.GetRuntime(container.HostConfig.Runtime) |
|
21 |
+ if rt == nil { |
|
22 |
+ return nil, fmt.Errorf("no such runtime '%s'", container.HostConfig.Runtime) |
|
23 |
+ } |
|
24 |
+ if UsingSystemd(daemon.configStore) { |
|
25 |
+ rt.Args = append(rt.Args, "--systemd-cgroup=true") |
|
26 |
+ } |
|
27 |
+ createOptions = append(createOptions, libcontainerd.WithRuntime(rt.Path, rt.Args)) |
|
28 |
+ |
|
29 |
+ return createOptions, nil |
|
30 |
+} |
... | ... |
@@ -3,6 +3,7 @@ package daemon |
3 | 3 |
import ( |
4 | 4 |
"encoding/json" |
5 | 5 |
"errors" |
6 |
+ "fmt" |
|
6 | 7 |
"runtime" |
7 | 8 |
"time" |
8 | 9 |
|
... | ... |
@@ -19,6 +20,9 @@ import ( |
19 | 19 |
// ContainerStats writes information about the container to the stream |
20 | 20 |
// given in the config object. |
21 | 21 |
func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, config *backend.ContainerStatsConfig) error { |
22 |
+ if runtime.GOOS == "solaris" { |
|
23 |
+ return fmt.Errorf("%+v does not support stats", runtime.GOOS) |
|
24 |
+ } |
|
22 | 25 |
// Remote API version (used for backwards compatibility) |
23 | 26 |
apiVersion := config.Version |
24 | 27 |
|
... | ... |
@@ -1,5 +1,7 @@ |
1 | 1 |
// +build !windows |
2 | 2 |
|
3 |
+// TODO(amitkris): We need to split this file for solaris. |
|
4 |
+ |
|
3 | 5 |
package daemon |
4 | 6 |
|
5 | 7 |
import ( |
... | ... |
@@ -11,6 +13,8 @@ import ( |
11 | 11 |
"strings" |
12 | 12 |
|
13 | 13 |
"github.com/docker/docker/container" |
14 |
+ "github.com/docker/docker/pkg/fileutils" |
|
15 |
+ "github.com/docker/docker/pkg/mount" |
|
14 | 16 |
"github.com/docker/docker/volume" |
15 | 17 |
"github.com/docker/docker/volume/drivers" |
16 | 18 |
"github.com/docker/docker/volume/local" |
... | ... |
@@ -165,3 +169,50 @@ func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error { |
165 | 165 |
} |
166 | 166 |
return nil |
167 | 167 |
} |
168 |
+ |
|
169 |
+func (daemon *Daemon) mountVolumes(container *container.Container) error { |
|
170 |
+ mounts, err := daemon.setupMounts(container) |
|
171 |
+ if err != nil { |
|
172 |
+ return err |
|
173 |
+ } |
|
174 |
+ |
|
175 |
+ for _, m := range mounts { |
|
176 |
+ dest, err := container.GetResourcePath(m.Destination) |
|
177 |
+ if err != nil { |
|
178 |
+ return err |
|
179 |
+ } |
|
180 |
+ |
|
181 |
+ var stat os.FileInfo |
|
182 |
+ stat, err = os.Stat(m.Source) |
|
183 |
+ if err != nil { |
|
184 |
+ return err |
|
185 |
+ } |
|
186 |
+ if err = fileutils.CreateIfNotExists(dest, stat.IsDir()); err != nil { |
|
187 |
+ return err |
|
188 |
+ } |
|
189 |
+ |
|
190 |
+ opts := "rbind,ro" |
|
191 |
+ if m.Writable { |
|
192 |
+ opts = "rbind,rw" |
|
193 |
+ } |
|
194 |
+ |
|
195 |
+ if err := mount.Mount(m.Source, dest, bindMountType, opts); err != nil { |
|
196 |
+ return err |
|
197 |
+ } |
|
198 |
+ |
|
199 |
+ // mountVolumes() seems to be called for temporary mounts |
|
200 |
+ // outside the container. Soon these will be unmounted with |
|
201 |
+ // lazy unmount option and given we have mounted the rbind, |
|
202 |
+ // all the submounts will propagate if these are shared. If |
|
203 |
+ // daemon is running in host namespace and has / as shared |
|
204 |
+ // then these unmounts will propagate and unmount original |
|
205 |
+ // mount as well. So make all these mounts rprivate. |
|
206 |
+ // Do not use propagation property of volume as that should |
|
207 |
+ // apply only when mounting happen inside the container. |
|
208 |
+ if err := mount.MakeRPrivate(dest); err != nil { |
|
209 |
+ return err |
|
210 |
+ } |
|
211 |
+ } |
|
212 |
+ |
|
213 |
+ return nil |
|
214 |
+} |
... | ... |
@@ -29,15 +29,18 @@ for platform in $DOCKER_CROSSPLATFORMS; do |
29 | 29 |
export GOOS=${platform%/*} |
30 | 30 |
export GOARCH=${platform##*/} |
31 | 31 |
|
32 |
- if [ -z "${daemonSupporting[$platform]}" ]; then |
|
33 |
- # we just need a simple client for these platforms |
|
34 |
- export LDFLAGS_STATIC_DOCKER="" |
|
35 |
- # remove the "daemon" build tag from platforms that aren't supported |
|
36 |
- export BUILDFLAGS=( "${ORIG_BUILDFLAGS[@]/ daemon/}" ) |
|
37 |
- source "${MAKEDIR}/binary-client" |
|
38 |
- else |
|
39 |
- source "${MAKEDIR}/binary-client" |
|
40 |
- source "${MAKEDIR}/binary-daemon" |
|
32 |
+ if [ "$GOOS" != "solaris" ]; then |
|
33 |
+ # TODO. Solaris cannot be cross build because of CGO calls. |
|
34 |
+ if [ -z "${daemonSupporting[$platform]}" ]; then |
|
35 |
+ # we just need a simple client for these platforms |
|
36 |
+ export LDFLAGS_STATIC_DOCKER="" |
|
37 |
+ # remove the "daemon" build tag from platforms that aren't supported |
|
38 |
+ export BUILDFLAGS=( "${ORIG_BUILDFLAGS[@]/ daemon/}" ) |
|
39 |
+ source "${MAKEDIR}/binary-client" |
|
40 |
+ else |
|
41 |
+ source "${MAKEDIR}/binary-client" |
|
42 |
+ source "${MAKEDIR}/binary-daemon" |
|
43 |
+ fi |
|
41 | 44 |
fi |
42 | 45 |
) |
43 | 46 |
done |
... | ... |
@@ -25,15 +25,30 @@ bundle_test_unit() { |
25 | 25 |
else |
26 | 26 |
TEST_PATH=./${TESTDIRS} |
27 | 27 |
fi |
28 |
- pkg_list=$(go list -e \ |
|
29 |
- -f '{{if ne .Name "github.com/docker/docker"}} |
|
30 |
- {{.ImportPath}} |
|
31 |
- {{end}}' \ |
|
32 |
- "${BUILDFLAGS[@]}" $TEST_PATH \ |
|
33 |
- | grep github.com/docker/docker \ |
|
34 |
- | grep -v github.com/docker/docker/vendor \ |
|
35 |
- | grep -v github.com/docker/docker/man \ |
|
36 |
- | grep -v github.com/docker/docker/integration-cli) |
|
28 |
+ |
|
29 |
+ if [ "$(go env GOHOSTOS)" = 'solaris' ]; then |
|
30 |
+ pkg_list=$(go list -e \ |
|
31 |
+ -f '{{if ne .Name "github.com/docker/docker"}} |
|
32 |
+ {{.ImportPath}} |
|
33 |
+ {{end}}' \ |
|
34 |
+ "${BUILDFLAGS[@]}" $TEST_PATH \ |
|
35 |
+ | grep github.com/docker/docker \ |
|
36 |
+ | grep -v github.com/docker/docker/vendor \ |
|
37 |
+ | grep -v github.com/docker/docker/daemon/graphdriver \ |
|
38 |
+ | grep -v github.com/docker/docker/man \ |
|
39 |
+ | grep -v github.com/docker/docker/integration-cli) |
|
40 |
+ else |
|
41 |
+ pkg_list=$(go list -e \ |
|
42 |
+ -f '{{if ne .Name "github.com/docker/docker"}} |
|
43 |
+ {{.ImportPath}} |
|
44 |
+ {{end}}' \ |
|
45 |
+ "${BUILDFLAGS[@]}" $TEST_PATH \ |
|
46 |
+ | grep github.com/docker/docker \ |
|
47 |
+ | grep -v github.com/docker/docker/vendor \ |
|
48 |
+ | grep -v github.com/docker/docker/man \ |
|
49 |
+ | grep -v github.com/docker/docker/integration-cli) |
|
50 |
+ fi |
|
51 |
+ |
|
37 | 52 |
go test -cover -ldflags "$LDFLAGS" "${BUILDFLAGS[@]}" $TESTFLAGS $pkg_list |
38 | 53 |
} |
39 | 54 |
|
... | ... |
@@ -25,6 +25,9 @@ for d in "$CROSS/"*/*; do |
25 | 25 |
# if windows use a zip, not tgz |
26 | 26 |
BUNDLE_EXTENSION=".zip" |
27 | 27 |
IS_TAR="false" |
28 |
+ elif [ "$GOOS" == "solaris" ]; then |
|
29 |
+ # Solaris bypasses cross due to CGO issues. |
|
30 |
+ continue |
|
28 | 31 |
else |
29 | 32 |
BUNDLE_EXTENSION=".tgz" |
30 | 33 |
IS_TAR="true" |
... | ... |
@@ -1,10 +1,8 @@ |
1 | 1 |
package libcontainerd |
2 | 2 |
|
3 | 3 |
import ( |
4 |
- "encoding/json" |
|
5 | 4 |
"fmt" |
6 | 5 |
"os" |
7 |
- "path/filepath" |
|
8 | 6 |
"strings" |
9 | 7 |
"sync" |
10 | 8 |
"syscall" |
... | ... |
@@ -12,7 +10,6 @@ import ( |
12 | 12 |
|
13 | 13 |
"github.com/Sirupsen/logrus" |
14 | 14 |
containerd "github.com/docker/containerd/api/grpc/types" |
15 |
- "github.com/docker/docker/pkg/idtools" |
|
16 | 15 |
"github.com/docker/docker/pkg/ioutils" |
17 | 16 |
"github.com/docker/docker/pkg/mount" |
18 | 17 |
"github.com/golang/protobuf/ptypes" |
... | ... |
@@ -124,87 +121,6 @@ func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendly |
124 | 124 |
return int(resp.SystemPid), nil |
125 | 125 |
} |
126 | 126 |
|
127 |
-func (clnt *client) prepareBundleDir(uid, gid int) (string, error) { |
|
128 |
- root, err := filepath.Abs(clnt.remote.stateDir) |
|
129 |
- if err != nil { |
|
130 |
- return "", err |
|
131 |
- } |
|
132 |
- if uid == 0 && gid == 0 { |
|
133 |
- return root, nil |
|
134 |
- } |
|
135 |
- p := string(filepath.Separator) |
|
136 |
- for _, d := range strings.Split(root, string(filepath.Separator))[1:] { |
|
137 |
- p = filepath.Join(p, d) |
|
138 |
- fi, err := os.Stat(p) |
|
139 |
- if err != nil && !os.IsNotExist(err) { |
|
140 |
- return "", err |
|
141 |
- } |
|
142 |
- if os.IsNotExist(err) || fi.Mode()&1 == 0 { |
|
143 |
- p = fmt.Sprintf("%s.%d.%d", p, uid, gid) |
|
144 |
- if err := idtools.MkdirAs(p, 0700, uid, gid); err != nil && !os.IsExist(err) { |
|
145 |
- return "", err |
|
146 |
- } |
|
147 |
- } |
|
148 |
- } |
|
149 |
- return p, nil |
|
150 |
-} |
|
151 |
- |
|
152 |
-func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) (err error) { |
|
153 |
- clnt.lock(containerID) |
|
154 |
- defer clnt.unlock(containerID) |
|
155 |
- |
|
156 |
- if _, err := clnt.getContainer(containerID); err == nil { |
|
157 |
- return fmt.Errorf("Container %s is already active", containerID) |
|
158 |
- } |
|
159 |
- |
|
160 |
- uid, gid, err := getRootIDs(specs.Spec(spec)) |
|
161 |
- if err != nil { |
|
162 |
- return err |
|
163 |
- } |
|
164 |
- dir, err := clnt.prepareBundleDir(uid, gid) |
|
165 |
- if err != nil { |
|
166 |
- return err |
|
167 |
- } |
|
168 |
- |
|
169 |
- container := clnt.newContainer(filepath.Join(dir, containerID), options...) |
|
170 |
- if err := container.clean(); err != nil { |
|
171 |
- return err |
|
172 |
- } |
|
173 |
- |
|
174 |
- defer func() { |
|
175 |
- if err != nil { |
|
176 |
- container.clean() |
|
177 |
- clnt.deleteContainer(containerID) |
|
178 |
- } |
|
179 |
- }() |
|
180 |
- |
|
181 |
- if err := idtools.MkdirAllAs(container.dir, 0700, uid, gid); err != nil && !os.IsExist(err) { |
|
182 |
- return err |
|
183 |
- } |
|
184 |
- |
|
185 |
- f, err := os.Create(filepath.Join(container.dir, configFilename)) |
|
186 |
- if err != nil { |
|
187 |
- return err |
|
188 |
- } |
|
189 |
- defer f.Close() |
|
190 |
- if err := json.NewEncoder(f).Encode(spec); err != nil { |
|
191 |
- return err |
|
192 |
- } |
|
193 |
- |
|
194 |
- return container.start(checkpoint, checkpointDir, attachStdio) |
|
195 |
-} |
|
196 |
- |
|
197 |
-func (clnt *client) Signal(containerID string, sig int) error { |
|
198 |
- clnt.lock(containerID) |
|
199 |
- defer clnt.unlock(containerID) |
|
200 |
- _, err := clnt.remote.apiClient.Signal(context.Background(), &containerd.SignalRequest{ |
|
201 |
- Id: containerID, |
|
202 |
- Pid: InitFriendlyName, |
|
203 |
- Signal: uint32(sig), |
|
204 |
- }) |
|
205 |
- return err |
|
206 |
-} |
|
207 |
- |
|
208 | 127 |
func (clnt *client) SignalProcess(containerID string, pid string, sig int) error { |
209 | 128 |
clnt.lock(containerID) |
210 | 129 |
defer clnt.unlock(containerID) |
... | ... |
@@ -340,28 +256,6 @@ func (clnt *client) getContainerdContainer(containerID string) (*containerd.Cont |
340 | 340 |
return nil, fmt.Errorf("invalid state response") |
341 | 341 |
} |
342 | 342 |
|
343 |
-func (clnt *client) newContainer(dir string, options ...CreateOption) *container { |
|
344 |
- container := &container{ |
|
345 |
- containerCommon: containerCommon{ |
|
346 |
- process: process{ |
|
347 |
- dir: dir, |
|
348 |
- processCommon: processCommon{ |
|
349 |
- containerID: filepath.Base(dir), |
|
350 |
- client: clnt, |
|
351 |
- friendlyName: InitFriendlyName, |
|
352 |
- }, |
|
353 |
- }, |
|
354 |
- processes: make(map[string]*process), |
|
355 |
- }, |
|
356 |
- } |
|
357 |
- for _, option := range options { |
|
358 |
- if err := option.Apply(container); err != nil { |
|
359 |
- logrus.Errorf("libcontainerd: newContainer(): %v", err) |
|
360 |
- } |
|
361 |
- } |
|
362 |
- return container |
|
363 |
-} |
|
364 |
- |
|
365 | 343 |
func (clnt *client) UpdateResources(containerID string, resources Resources) error { |
366 | 344 |
clnt.lock(containerID) |
367 | 345 |
defer clnt.unlock(containerID) |
... | ... |
@@ -627,27 +521,6 @@ func (clnt *client) Restore(containerID string, attachStdio StdioCallback, optio |
627 | 627 |
return clnt.setExited(containerID, uint32(255)) |
628 | 628 |
} |
629 | 629 |
|
630 |
-type exitNotifier struct { |
|
631 |
- id string |
|
632 |
- client *client |
|
633 |
- c chan struct{} |
|
634 |
- once sync.Once |
|
635 |
-} |
|
636 |
- |
|
637 |
-func (en *exitNotifier) close() { |
|
638 |
- en.once.Do(func() { |
|
639 |
- close(en.c) |
|
640 |
- en.client.mapMutex.Lock() |
|
641 |
- if en == en.client.exitNotifiers[en.id] { |
|
642 |
- delete(en.client.exitNotifiers, en.id) |
|
643 |
- } |
|
644 |
- en.client.mapMutex.Unlock() |
|
645 |
- }) |
|
646 |
-} |
|
647 |
-func (en *exitNotifier) wait() <-chan struct{} { |
|
648 |
- return en.c |
|
649 |
-} |
|
650 |
- |
|
651 | 630 |
func (clnt *client) CreateCheckpoint(containerID string, checkpointID string, checkpointDir string, exit bool) error { |
652 | 631 |
clnt.lock(containerID) |
653 | 632 |
defer clnt.unlock(containerID) |
... | ... |
@@ -6,17 +6,17 @@ type client struct { |
6 | 6 |
clientCommon |
7 | 7 |
|
8 | 8 |
// Platform specific properties below here. |
9 |
+ remote *remote |
|
10 |
+ q queue |
|
11 |
+ exitNotifiers map[string]*exitNotifier |
|
12 |
+ liveRestore bool |
|
9 | 13 |
} |
10 | 14 |
|
11 |
-func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendlyName string, specp Process) error { |
|
12 |
- return nil |
|
15 |
+func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendlyName string, specp Process, attachStdio StdioCallback) (int, error) { |
|
16 |
+ return -1, nil |
|
13 | 17 |
} |
14 | 18 |
|
15 |
-func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec Spec, options ...CreateOption) (err error) { |
|
16 |
- return nil |
|
17 |
-} |
|
18 |
- |
|
19 |
-func (clnt *client) Signal(containerID string, sig int) error { |
|
19 |
+func (clnt *client) SignalProcess(containerID string, pid string, sig int) error { |
|
20 | 20 |
return nil |
21 | 21 |
} |
22 | 22 |
|
... | ... |
@@ -36,8 +36,25 @@ func (clnt *client) Stats(containerID string) (*Stats, error) { |
36 | 36 |
return nil, nil |
37 | 37 |
} |
38 | 38 |
|
39 |
+func (clnt *client) getExitNotifier(containerID string) *exitNotifier { |
|
40 |
+ clnt.mapMutex.RLock() |
|
41 |
+ defer clnt.mapMutex.RUnlock() |
|
42 |
+ return clnt.exitNotifiers[containerID] |
|
43 |
+} |
|
44 |
+ |
|
45 |
+func (clnt *client) getOrCreateExitNotifier(containerID string) *exitNotifier { |
|
46 |
+ clnt.mapMutex.Lock() |
|
47 |
+ defer clnt.mapMutex.Unlock() |
|
48 |
+ w, ok := clnt.exitNotifiers[containerID] |
|
49 |
+ if !ok { |
|
50 |
+ w = &exitNotifier{c: make(chan struct{}), client: clnt} |
|
51 |
+ clnt.exitNotifiers[containerID] = w |
|
52 |
+ } |
|
53 |
+ return w |
|
54 |
+} |
|
55 |
+ |
|
39 | 56 |
// Restore is the handler for restoring a container |
40 |
-func (clnt *client) Restore(containerID string, unusedOnWindows ...CreateOption) error { |
|
57 |
+func (clnt *client) Restore(containerID string, attachStdio StdioCallback, options ...CreateOption) error { |
|
41 | 58 |
return nil |
42 | 59 |
} |
43 | 60 |
|
... | ... |
@@ -56,3 +73,15 @@ func (clnt *client) UpdateResources(containerID string, resources Resources) err |
56 | 56 |
// but we should return nil for enabling updating container |
57 | 57 |
return nil |
58 | 58 |
} |
59 |
+ |
|
60 |
+func (clnt *client) CreateCheckpoint(containerID string, checkpointID string, checkpointDir string, exit bool) error { |
|
61 |
+ return nil |
|
62 |
+} |
|
63 |
+ |
|
64 |
+func (clnt *client) DeleteCheckpoint(containerID string, checkpointID string, checkpointDir string) error { |
|
65 |
+ return nil |
|
66 |
+} |
|
67 |
+ |
|
68 |
+func (clnt *client) ListCheckpoints(containerID string, checkpointDir string) (*Checkpoints, error) { |
|
69 |
+ return nil, nil |
|
70 |
+} |
59 | 71 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,142 @@ |
0 |
+// +build linux solaris |
|
1 |
+ |
|
2 |
+package libcontainerd |
|
3 |
+ |
|
4 |
+import ( |
|
5 |
+ "encoding/json" |
|
6 |
+ "fmt" |
|
7 |
+ "os" |
|
8 |
+ "path/filepath" |
|
9 |
+ "strings" |
|
10 |
+ "sync" |
|
11 |
+ |
|
12 |
+ "github.com/Sirupsen/logrus" |
|
13 |
+ containerd "github.com/docker/containerd/api/grpc/types" |
|
14 |
+ "github.com/docker/docker/pkg/idtools" |
|
15 |
+ specs "github.com/opencontainers/runtime-spec/specs-go" |
|
16 |
+ "golang.org/x/net/context" |
|
17 |
+) |
|
18 |
+ |
|
19 |
+func (clnt *client) prepareBundleDir(uid, gid int) (string, error) { |
|
20 |
+ root, err := filepath.Abs(clnt.remote.stateDir) |
|
21 |
+ if err != nil { |
|
22 |
+ return "", err |
|
23 |
+ } |
|
24 |
+ if uid == 0 && gid == 0 { |
|
25 |
+ return root, nil |
|
26 |
+ } |
|
27 |
+ p := string(filepath.Separator) |
|
28 |
+ for _, d := range strings.Split(root, string(filepath.Separator))[1:] { |
|
29 |
+ p = filepath.Join(p, d) |
|
30 |
+ fi, err := os.Stat(p) |
|
31 |
+ if err != nil && !os.IsNotExist(err) { |
|
32 |
+ return "", err |
|
33 |
+ } |
|
34 |
+ if os.IsNotExist(err) || fi.Mode()&1 == 0 { |
|
35 |
+ p = fmt.Sprintf("%s.%d.%d", p, uid, gid) |
|
36 |
+ if err := idtools.MkdirAs(p, 0700, uid, gid); err != nil && !os.IsExist(err) { |
|
37 |
+ return "", err |
|
38 |
+ } |
|
39 |
+ } |
|
40 |
+ } |
|
41 |
+ return p, nil |
|
42 |
+} |
|
43 |
+ |
|
44 |
+func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) (err error) { |
|
45 |
+ clnt.lock(containerID) |
|
46 |
+ defer clnt.unlock(containerID) |
|
47 |
+ |
|
48 |
+ if _, err := clnt.getContainer(containerID); err == nil { |
|
49 |
+ return fmt.Errorf("Container %s is already active", containerID) |
|
50 |
+ } |
|
51 |
+ |
|
52 |
+ uid, gid, err := getRootIDs(specs.Spec(spec)) |
|
53 |
+ if err != nil { |
|
54 |
+ return err |
|
55 |
+ } |
|
56 |
+ dir, err := clnt.prepareBundleDir(uid, gid) |
|
57 |
+ if err != nil { |
|
58 |
+ return err |
|
59 |
+ } |
|
60 |
+ |
|
61 |
+ container := clnt.newContainer(filepath.Join(dir, containerID), options...) |
|
62 |
+ if err := container.clean(); err != nil { |
|
63 |
+ return err |
|
64 |
+ } |
|
65 |
+ |
|
66 |
+ defer func() { |
|
67 |
+ if err != nil { |
|
68 |
+ container.clean() |
|
69 |
+ clnt.deleteContainer(containerID) |
|
70 |
+ } |
|
71 |
+ }() |
|
72 |
+ |
|
73 |
+ if err := idtools.MkdirAllAs(container.dir, 0700, uid, gid); err != nil && !os.IsExist(err) { |
|
74 |
+ return err |
|
75 |
+ } |
|
76 |
+ |
|
77 |
+ f, err := os.Create(filepath.Join(container.dir, configFilename)) |
|
78 |
+ if err != nil { |
|
79 |
+ return err |
|
80 |
+ } |
|
81 |
+ defer f.Close() |
|
82 |
+ if err := json.NewEncoder(f).Encode(spec); err != nil { |
|
83 |
+ return err |
|
84 |
+ } |
|
85 |
+ |
|
86 |
+ return container.start(checkpoint, checkpointDir, attachStdio) |
|
87 |
+} |
|
88 |
+ |
|
89 |
+func (clnt *client) Signal(containerID string, sig int) error { |
|
90 |
+ clnt.lock(containerID) |
|
91 |
+ defer clnt.unlock(containerID) |
|
92 |
+ _, err := clnt.remote.apiClient.Signal(context.Background(), &containerd.SignalRequest{ |
|
93 |
+ Id: containerID, |
|
94 |
+ Pid: InitFriendlyName, |
|
95 |
+ Signal: uint32(sig), |
|
96 |
+ }) |
|
97 |
+ return err |
|
98 |
+} |
|
99 |
+ |
|
100 |
+func (clnt *client) newContainer(dir string, options ...CreateOption) *container { |
|
101 |
+ container := &container{ |
|
102 |
+ containerCommon: containerCommon{ |
|
103 |
+ process: process{ |
|
104 |
+ dir: dir, |
|
105 |
+ processCommon: processCommon{ |
|
106 |
+ containerID: filepath.Base(dir), |
|
107 |
+ client: clnt, |
|
108 |
+ friendlyName: InitFriendlyName, |
|
109 |
+ }, |
|
110 |
+ }, |
|
111 |
+ processes: make(map[string]*process), |
|
112 |
+ }, |
|
113 |
+ } |
|
114 |
+ for _, option := range options { |
|
115 |
+ if err := option.Apply(container); err != nil { |
|
116 |
+ logrus.Errorf("libcontainerd: newContainer(): %v", err) |
|
117 |
+ } |
|
118 |
+ } |
|
119 |
+ return container |
|
120 |
+} |
|
121 |
+ |
|
122 |
+type exitNotifier struct { |
|
123 |
+ id string |
|
124 |
+ client *client |
|
125 |
+ c chan struct{} |
|
126 |
+ once sync.Once |
|
127 |
+} |
|
128 |
+ |
|
129 |
+func (en *exitNotifier) close() { |
|
130 |
+ en.once.Do(func() { |
|
131 |
+ close(en.c) |
|
132 |
+ en.client.mapMutex.Lock() |
|
133 |
+ if en == en.client.exitNotifiers[en.id] { |
|
134 |
+ delete(en.client.exitNotifiers, en.id) |
|
135 |
+ } |
|
136 |
+ en.client.mapMutex.Unlock() |
|
137 |
+ }) |
|
138 |
+} |
|
139 |
+func (en *exitNotifier) wait() <-chan struct{} { |
|
140 |
+ return en.c |
|
141 |
+} |
0 | 142 |
deleted file mode 100644 |
... | ... |
@@ -1,237 +0,0 @@ |
1 |
-package libcontainerd |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "encoding/json" |
|
5 |
- "io" |
|
6 |
- "io/ioutil" |
|
7 |
- "os" |
|
8 |
- "path/filepath" |
|
9 |
- "sync" |
|
10 |
- "syscall" |
|
11 |
- "time" |
|
12 |
- |
|
13 |
- "github.com/Sirupsen/logrus" |
|
14 |
- containerd "github.com/docker/containerd/api/grpc/types" |
|
15 |
- "github.com/docker/docker/pkg/ioutils" |
|
16 |
- "github.com/opencontainers/runtime-spec/specs-go" |
|
17 |
- "github.com/tonistiigi/fifo" |
|
18 |
- "golang.org/x/net/context" |
|
19 |
-) |
|
20 |
- |
|
21 |
-type container struct { |
|
22 |
- containerCommon |
|
23 |
- |
|
24 |
- // Platform specific fields are below here. |
|
25 |
- pauseMonitor |
|
26 |
- oom bool |
|
27 |
- runtime string |
|
28 |
- runtimeArgs []string |
|
29 |
-} |
|
30 |
- |
|
31 |
-type runtime struct { |
|
32 |
- path string |
|
33 |
- args []string |
|
34 |
-} |
|
35 |
- |
|
36 |
-// WithRuntime sets the runtime to be used for the created container |
|
37 |
-func WithRuntime(path string, args []string) CreateOption { |
|
38 |
- return runtime{path, args} |
|
39 |
-} |
|
40 |
- |
|
41 |
-func (rt runtime) Apply(p interface{}) error { |
|
42 |
- if pr, ok := p.(*container); ok { |
|
43 |
- pr.runtime = rt.path |
|
44 |
- pr.runtimeArgs = rt.args |
|
45 |
- } |
|
46 |
- return nil |
|
47 |
-} |
|
48 |
- |
|
49 |
-func (ctr *container) clean() error { |
|
50 |
- if os.Getenv("LIBCONTAINERD_NOCLEAN") == "1" { |
|
51 |
- return nil |
|
52 |
- } |
|
53 |
- if _, err := os.Lstat(ctr.dir); err != nil { |
|
54 |
- if os.IsNotExist(err) { |
|
55 |
- return nil |
|
56 |
- } |
|
57 |
- return err |
|
58 |
- } |
|
59 |
- |
|
60 |
- if err := os.RemoveAll(ctr.dir); err != nil { |
|
61 |
- return err |
|
62 |
- } |
|
63 |
- return nil |
|
64 |
-} |
|
65 |
- |
|
66 |
-// cleanProcess removes the fifos used by an additional process. |
|
67 |
-// Caller needs to lock container ID before calling this method. |
|
68 |
-func (ctr *container) cleanProcess(id string) { |
|
69 |
- if p, ok := ctr.processes[id]; ok { |
|
70 |
- for _, i := range []int{syscall.Stdin, syscall.Stdout, syscall.Stderr} { |
|
71 |
- if err := os.Remove(p.fifo(i)); err != nil { |
|
72 |
- logrus.Warnf("libcontainerd: failed to remove %v for process %v: %v", p.fifo(i), id, err) |
|
73 |
- } |
|
74 |
- } |
|
75 |
- } |
|
76 |
- delete(ctr.processes, id) |
|
77 |
-} |
|
78 |
- |
|
79 |
-func (ctr *container) spec() (*specs.Spec, error) { |
|
80 |
- var spec specs.Spec |
|
81 |
- dt, err := ioutil.ReadFile(filepath.Join(ctr.dir, configFilename)) |
|
82 |
- if err != nil { |
|
83 |
- return nil, err |
|
84 |
- } |
|
85 |
- if err := json.Unmarshal(dt, &spec); err != nil { |
|
86 |
- return nil, err |
|
87 |
- } |
|
88 |
- return &spec, nil |
|
89 |
-} |
|
90 |
- |
|
91 |
-func (ctr *container) start(checkpoint string, checkpointDir string, attachStdio StdioCallback) error { |
|
92 |
- spec, err := ctr.spec() |
|
93 |
- if err != nil { |
|
94 |
- return nil |
|
95 |
- } |
|
96 |
- |
|
97 |
- ctx, cancel := context.WithCancel(context.Background()) |
|
98 |
- defer cancel() |
|
99 |
- ready := make(chan struct{}) |
|
100 |
- |
|
101 |
- iopipe, err := ctr.openFifos(spec.Process.Terminal) |
|
102 |
- if err != nil { |
|
103 |
- return err |
|
104 |
- } |
|
105 |
- |
|
106 |
- var stdinOnce sync.Once |
|
107 |
- |
|
108 |
- // we need to delay stdin closure after container start or else "stdin close" |
|
109 |
- // event will be rejected by containerd. |
|
110 |
- // stdin closure happens in attachStdio |
|
111 |
- stdin := iopipe.Stdin |
|
112 |
- iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error { |
|
113 |
- var err error |
|
114 |
- stdinOnce.Do(func() { // on error from attach we don't know if stdin was already closed |
|
115 |
- err = stdin.Close() |
|
116 |
- go func() { |
|
117 |
- select { |
|
118 |
- case <-ready: |
|
119 |
- if err := ctr.sendCloseStdin(); err != nil { |
|
120 |
- logrus.Warnf("failed to close stdin: %+v", err) |
|
121 |
- } |
|
122 |
- case <-ctx.Done(): |
|
123 |
- } |
|
124 |
- }() |
|
125 |
- }) |
|
126 |
- return err |
|
127 |
- }) |
|
128 |
- |
|
129 |
- r := &containerd.CreateContainerRequest{ |
|
130 |
- Id: ctr.containerID, |
|
131 |
- BundlePath: ctr.dir, |
|
132 |
- Stdin: ctr.fifo(syscall.Stdin), |
|
133 |
- Stdout: ctr.fifo(syscall.Stdout), |
|
134 |
- Stderr: ctr.fifo(syscall.Stderr), |
|
135 |
- Checkpoint: checkpoint, |
|
136 |
- CheckpointDir: checkpointDir, |
|
137 |
- // check to see if we are running in ramdisk to disable pivot root |
|
138 |
- NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "", |
|
139 |
- Runtime: ctr.runtime, |
|
140 |
- RuntimeArgs: ctr.runtimeArgs, |
|
141 |
- } |
|
142 |
- ctr.client.appendContainer(ctr) |
|
143 |
- |
|
144 |
- if err := attachStdio(*iopipe); err != nil { |
|
145 |
- ctr.closeFifos(iopipe) |
|
146 |
- return err |
|
147 |
- } |
|
148 |
- |
|
149 |
- resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r) |
|
150 |
- if err != nil { |
|
151 |
- ctr.closeFifos(iopipe) |
|
152 |
- return err |
|
153 |
- } |
|
154 |
- ctr.systemPid = systemPid(resp.Container) |
|
155 |
- close(ready) |
|
156 |
- |
|
157 |
- return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{ |
|
158 |
- CommonStateInfo: CommonStateInfo{ |
|
159 |
- State: StateStart, |
|
160 |
- Pid: ctr.systemPid, |
|
161 |
- }}) |
|
162 |
-} |
|
163 |
- |
|
164 |
-func (ctr *container) newProcess(friendlyName string) *process { |
|
165 |
- return &process{ |
|
166 |
- dir: ctr.dir, |
|
167 |
- processCommon: processCommon{ |
|
168 |
- containerID: ctr.containerID, |
|
169 |
- friendlyName: friendlyName, |
|
170 |
- client: ctr.client, |
|
171 |
- }, |
|
172 |
- } |
|
173 |
-} |
|
174 |
- |
|
175 |
-func (ctr *container) handleEvent(e *containerd.Event) error { |
|
176 |
- ctr.client.lock(ctr.containerID) |
|
177 |
- defer ctr.client.unlock(ctr.containerID) |
|
178 |
- switch e.Type { |
|
179 |
- case StateExit, StatePause, StateResume, StateOOM: |
|
180 |
- st := StateInfo{ |
|
181 |
- CommonStateInfo: CommonStateInfo{ |
|
182 |
- State: e.Type, |
|
183 |
- ExitCode: e.Status, |
|
184 |
- }, |
|
185 |
- OOMKilled: e.Type == StateExit && ctr.oom, |
|
186 |
- } |
|
187 |
- if e.Type == StateOOM { |
|
188 |
- ctr.oom = true |
|
189 |
- } |
|
190 |
- if e.Type == StateExit && e.Pid != InitFriendlyName { |
|
191 |
- st.ProcessID = e.Pid |
|
192 |
- st.State = StateExitProcess |
|
193 |
- } |
|
194 |
- |
|
195 |
- // Remove process from list if we have exited |
|
196 |
- switch st.State { |
|
197 |
- case StateExit: |
|
198 |
- ctr.clean() |
|
199 |
- ctr.client.deleteContainer(e.Id) |
|
200 |
- case StateExitProcess: |
|
201 |
- ctr.cleanProcess(st.ProcessID) |
|
202 |
- } |
|
203 |
- ctr.client.q.append(e.Id, func() { |
|
204 |
- if err := ctr.client.backend.StateChanged(e.Id, st); err != nil { |
|
205 |
- logrus.Errorf("libcontainerd: backend.StateChanged(): %v", err) |
|
206 |
- } |
|
207 |
- if e.Type == StatePause || e.Type == StateResume { |
|
208 |
- ctr.pauseMonitor.handle(e.Type) |
|
209 |
- } |
|
210 |
- if e.Type == StateExit { |
|
211 |
- if en := ctr.client.getExitNotifier(e.Id); en != nil { |
|
212 |
- en.close() |
|
213 |
- } |
|
214 |
- } |
|
215 |
- }) |
|
216 |
- |
|
217 |
- default: |
|
218 |
- logrus.Debugf("libcontainerd: event unhandled: %+v", e) |
|
219 |
- } |
|
220 |
- return nil |
|
221 |
-} |
|
222 |
- |
|
223 |
-// discardFifos attempts to fully read the container fifos to unblock processes |
|
224 |
-// that may be blocked on the writer side. |
|
225 |
-func (ctr *container) discardFifos() { |
|
226 |
- ctx, _ := context.WithTimeout(context.Background(), 3*time.Second) |
|
227 |
- for _, i := range []int{syscall.Stdout, syscall.Stderr} { |
|
228 |
- f, err := fifo.OpenFifo(ctx, ctr.fifo(i), syscall.O_RDONLY|syscall.O_NONBLOCK, 0) |
|
229 |
- if err != nil { |
|
230 |
- logrus.Warnf("error opening fifo %v for discarding: %+v", f, err) |
|
231 |
- continue |
|
232 |
- } |
|
233 |
- go func() { |
|
234 |
- io.Copy(ioutil.Discard, f) |
|
235 |
- }() |
|
236 |
- } |
|
237 |
-} |
6 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,239 @@ |
0 |
+// +build linux solaris |
|
1 |
+ |
|
2 |
+package libcontainerd |
|
3 |
+ |
|
4 |
+import ( |
|
5 |
+ "encoding/json" |
|
6 |
+ "io" |
|
7 |
+ "io/ioutil" |
|
8 |
+ "os" |
|
9 |
+ "path/filepath" |
|
10 |
+ "sync" |
|
11 |
+ "syscall" |
|
12 |
+ "time" |
|
13 |
+ |
|
14 |
+ "github.com/Sirupsen/logrus" |
|
15 |
+ containerd "github.com/docker/containerd/api/grpc/types" |
|
16 |
+ "github.com/docker/docker/pkg/ioutils" |
|
17 |
+ "github.com/opencontainers/runtime-spec/specs-go" |
|
18 |
+ "github.com/tonistiigi/fifo" |
|
19 |
+ "golang.org/x/net/context" |
|
20 |
+) |
|
21 |
+ |
|
22 |
+type container struct { |
|
23 |
+ containerCommon |
|
24 |
+ |
|
25 |
+ // Platform specific fields are below here. |
|
26 |
+ pauseMonitor |
|
27 |
+ oom bool |
|
28 |
+ runtime string |
|
29 |
+ runtimeArgs []string |
|
30 |
+} |
|
31 |
+ |
|
32 |
+type runtime struct { |
|
33 |
+ path string |
|
34 |
+ args []string |
|
35 |
+} |
|
36 |
+ |
|
37 |
+// WithRuntime sets the runtime to be used for the created container |
|
38 |
+func WithRuntime(path string, args []string) CreateOption { |
|
39 |
+ return runtime{path, args} |
|
40 |
+} |
|
41 |
+ |
|
42 |
+func (rt runtime) Apply(p interface{}) error { |
|
43 |
+ if pr, ok := p.(*container); ok { |
|
44 |
+ pr.runtime = rt.path |
|
45 |
+ pr.runtimeArgs = rt.args |
|
46 |
+ } |
|
47 |
+ return nil |
|
48 |
+} |
|
49 |
+ |
|
50 |
+func (ctr *container) clean() error { |
|
51 |
+ if os.Getenv("LIBCONTAINERD_NOCLEAN") == "1" { |
|
52 |
+ return nil |
|
53 |
+ } |
|
54 |
+ if _, err := os.Lstat(ctr.dir); err != nil { |
|
55 |
+ if os.IsNotExist(err) { |
|
56 |
+ return nil |
|
57 |
+ } |
|
58 |
+ return err |
|
59 |
+ } |
|
60 |
+ |
|
61 |
+ if err := os.RemoveAll(ctr.dir); err != nil { |
|
62 |
+ return err |
|
63 |
+ } |
|
64 |
+ return nil |
|
65 |
+} |
|
66 |
+ |
|
67 |
+// cleanProcess removes the fifos used by an additional process. |
|
68 |
+// Caller needs to lock container ID before calling this method. |
|
69 |
+func (ctr *container) cleanProcess(id string) { |
|
70 |
+ if p, ok := ctr.processes[id]; ok { |
|
71 |
+ for _, i := range []int{syscall.Stdin, syscall.Stdout, syscall.Stderr} { |
|
72 |
+ if err := os.Remove(p.fifo(i)); err != nil { |
|
73 |
+ logrus.Warnf("libcontainerd: failed to remove %v for process %v: %v", p.fifo(i), id, err) |
|
74 |
+ } |
|
75 |
+ } |
|
76 |
+ } |
|
77 |
+ delete(ctr.processes, id) |
|
78 |
+} |
|
79 |
+ |
|
80 |
+func (ctr *container) spec() (*specs.Spec, error) { |
|
81 |
+ var spec specs.Spec |
|
82 |
+ dt, err := ioutil.ReadFile(filepath.Join(ctr.dir, configFilename)) |
|
83 |
+ if err != nil { |
|
84 |
+ return nil, err |
|
85 |
+ } |
|
86 |
+ if err := json.Unmarshal(dt, &spec); err != nil { |
|
87 |
+ return nil, err |
|
88 |
+ } |
|
89 |
+ return &spec, nil |
|
90 |
+} |
|
91 |
+ |
|
92 |
+func (ctr *container) start(checkpoint string, checkpointDir string, attachStdio StdioCallback) error { |
|
93 |
+ spec, err := ctr.spec() |
|
94 |
+ if err != nil { |
|
95 |
+ return nil |
|
96 |
+ } |
|
97 |
+ |
|
98 |
+ ctx, cancel := context.WithCancel(context.Background()) |
|
99 |
+ defer cancel() |
|
100 |
+ ready := make(chan struct{}) |
|
101 |
+ |
|
102 |
+ iopipe, err := ctr.openFifos(spec.Process.Terminal) |
|
103 |
+ if err != nil { |
|
104 |
+ return err |
|
105 |
+ } |
|
106 |
+ |
|
107 |
+ var stdinOnce sync.Once |
|
108 |
+ |
|
109 |
+ // we need to delay stdin closure after container start or else "stdin close" |
|
110 |
+ // event will be rejected by containerd. |
|
111 |
+ // stdin closure happens in attachStdio |
|
112 |
+ stdin := iopipe.Stdin |
|
113 |
+ iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error { |
|
114 |
+ var err error |
|
115 |
+ stdinOnce.Do(func() { // on error from attach we don't know if stdin was already closed |
|
116 |
+ err = stdin.Close() |
|
117 |
+ go func() { |
|
118 |
+ select { |
|
119 |
+ case <-ready: |
|
120 |
+ if err := ctr.sendCloseStdin(); err != nil { |
|
121 |
+ logrus.Warnf("failed to close stdin: %+v", err) |
|
122 |
+ } |
|
123 |
+ case <-ctx.Done(): |
|
124 |
+ } |
|
125 |
+ }() |
|
126 |
+ }) |
|
127 |
+ return err |
|
128 |
+ }) |
|
129 |
+ |
|
130 |
+ r := &containerd.CreateContainerRequest{ |
|
131 |
+ Id: ctr.containerID, |
|
132 |
+ BundlePath: ctr.dir, |
|
133 |
+ Stdin: ctr.fifo(syscall.Stdin), |
|
134 |
+ Stdout: ctr.fifo(syscall.Stdout), |
|
135 |
+ Stderr: ctr.fifo(syscall.Stderr), |
|
136 |
+ Checkpoint: checkpoint, |
|
137 |
+ CheckpointDir: checkpointDir, |
|
138 |
+ // check to see if we are running in ramdisk to disable pivot root |
|
139 |
+ NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "", |
|
140 |
+ Runtime: ctr.runtime, |
|
141 |
+ RuntimeArgs: ctr.runtimeArgs, |
|
142 |
+ } |
|
143 |
+ ctr.client.appendContainer(ctr) |
|
144 |
+ |
|
145 |
+ if err := attachStdio(*iopipe); err != nil { |
|
146 |
+ ctr.closeFifos(iopipe) |
|
147 |
+ return err |
|
148 |
+ } |
|
149 |
+ |
|
150 |
+ resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r) |
|
151 |
+ if err != nil { |
|
152 |
+ ctr.closeFifos(iopipe) |
|
153 |
+ return err |
|
154 |
+ } |
|
155 |
+ ctr.systemPid = systemPid(resp.Container) |
|
156 |
+ close(ready) |
|
157 |
+ |
|
158 |
+ return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{ |
|
159 |
+ CommonStateInfo: CommonStateInfo{ |
|
160 |
+ State: StateStart, |
|
161 |
+ Pid: ctr.systemPid, |
|
162 |
+ }}) |
|
163 |
+} |
|
164 |
+ |
|
165 |
+func (ctr *container) newProcess(friendlyName string) *process { |
|
166 |
+ return &process{ |
|
167 |
+ dir: ctr.dir, |
|
168 |
+ processCommon: processCommon{ |
|
169 |
+ containerID: ctr.containerID, |
|
170 |
+ friendlyName: friendlyName, |
|
171 |
+ client: ctr.client, |
|
172 |
+ }, |
|
173 |
+ } |
|
174 |
+} |
|
175 |
+ |
|
176 |
+func (ctr *container) handleEvent(e *containerd.Event) error { |
|
177 |
+ ctr.client.lock(ctr.containerID) |
|
178 |
+ defer ctr.client.unlock(ctr.containerID) |
|
179 |
+ switch e.Type { |
|
180 |
+ case StateExit, StatePause, StateResume, StateOOM: |
|
181 |
+ st := StateInfo{ |
|
182 |
+ CommonStateInfo: CommonStateInfo{ |
|
183 |
+ State: e.Type, |
|
184 |
+ ExitCode: e.Status, |
|
185 |
+ }, |
|
186 |
+ OOMKilled: e.Type == StateExit && ctr.oom, |
|
187 |
+ } |
|
188 |
+ if e.Type == StateOOM { |
|
189 |
+ ctr.oom = true |
|
190 |
+ } |
|
191 |
+ if e.Type == StateExit && e.Pid != InitFriendlyName { |
|
192 |
+ st.ProcessID = e.Pid |
|
193 |
+ st.State = StateExitProcess |
|
194 |
+ } |
|
195 |
+ |
|
196 |
+ // Remove process from list if we have exited |
|
197 |
+ switch st.State { |
|
198 |
+ case StateExit: |
|
199 |
+ ctr.clean() |
|
200 |
+ ctr.client.deleteContainer(e.Id) |
|
201 |
+ case StateExitProcess: |
|
202 |
+ ctr.cleanProcess(st.ProcessID) |
|
203 |
+ } |
|
204 |
+ ctr.client.q.append(e.Id, func() { |
|
205 |
+ if err := ctr.client.backend.StateChanged(e.Id, st); err != nil { |
|
206 |
+ logrus.Errorf("libcontainerd: backend.StateChanged(): %v", err) |
|
207 |
+ } |
|
208 |
+ if e.Type == StatePause || e.Type == StateResume { |
|
209 |
+ ctr.pauseMonitor.handle(e.Type) |
|
210 |
+ } |
|
211 |
+ if e.Type == StateExit { |
|
212 |
+ if en := ctr.client.getExitNotifier(e.Id); en != nil { |
|
213 |
+ en.close() |
|
214 |
+ } |
|
215 |
+ } |
|
216 |
+ }) |
|
217 |
+ |
|
218 |
+ default: |
|
219 |
+ logrus.Debugf("libcontainerd: event unhandled: %+v", e) |
|
220 |
+ } |
|
221 |
+ return nil |
|
222 |
+} |
|
223 |
+ |
|
224 |
+// discardFifos attempts to fully read the container fifos to unblock processes |
|
225 |
+// that may be blocked on the writer side. |
|
226 |
+func (ctr *container) discardFifos() { |
|
227 |
+ ctx, _ := context.WithTimeout(context.Background(), 3*time.Second) |
|
228 |
+ for _, i := range []int{syscall.Stdout, syscall.Stderr} { |
|
229 |
+ f, err := fifo.OpenFifo(ctx, ctr.fifo(i), syscall.O_RDONLY|syscall.O_NONBLOCK, 0) |
|
230 |
+ if err != nil { |
|
231 |
+ logrus.Warnf("error opening fifo %v for discarding: %+v", f, err) |
|
232 |
+ continue |
|
233 |
+ } |
|
234 |
+ go func() { |
|
235 |
+ io.Copy(ioutil.Discard, f) |
|
236 |
+ }() |
|
237 |
+ } |
|
238 |
+} |
0 | 239 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,31 @@ |
0 |
+package libcontainerd |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "fmt" |
|
4 |
+ "os" |
|
5 |
+ "strconv" |
|
6 |
+ |
|
7 |
+ "github.com/Sirupsen/logrus" |
|
8 |
+ "github.com/opencontainers/runc/libcontainer/system" |
|
9 |
+) |
|
10 |
+ |
|
11 |
+func setOOMScore(pid, score int) error { |
|
12 |
+ oomScoreAdjPath := fmt.Sprintf("/proc/%d/oom_score_adj", pid) |
|
13 |
+ f, err := os.OpenFile(oomScoreAdjPath, os.O_WRONLY, 0) |
|
14 |
+ if err != nil { |
|
15 |
+ return err |
|
16 |
+ } |
|
17 |
+ stringScore := strconv.Itoa(score) |
|
18 |
+ _, err = f.WriteString(stringScore) |
|
19 |
+ f.Close() |
|
20 |
+ if os.IsPermission(err) { |
|
21 |
+ // Setting oom_score_adj does not work in an |
|
22 |
+ // unprivileged container. Ignore the error, but log |
|
23 |
+ // it if we appear not to be in that situation. |
|
24 |
+ if !system.RunningInUserNS() { |
|
25 |
+ logrus.Debugf("Permission denied writing %q to %s", stringScore, oomScoreAdjPath) |
|
26 |
+ } |
|
27 |
+ return nil |
|
28 |
+ } |
|
29 |
+ return err |
|
30 |
+} |
0 | 5 |
deleted file mode 100644 |
... | ... |
@@ -1,40 +0,0 @@ |
1 |
-package libcontainerd |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "sync" |
|
5 |
-) |
|
6 |
- |
|
7 |
-// pauseMonitor is helper to get notifications from pause state changes. |
|
8 |
-type pauseMonitor struct { |
|
9 |
- sync.Mutex |
|
10 |
- waiters map[string][]chan struct{} |
|
11 |
-} |
|
12 |
- |
|
13 |
-func (m *pauseMonitor) handle(t string) { |
|
14 |
- m.Lock() |
|
15 |
- defer m.Unlock() |
|
16 |
- if m.waiters == nil { |
|
17 |
- return |
|
18 |
- } |
|
19 |
- q, ok := m.waiters[t] |
|
20 |
- if !ok { |
|
21 |
- return |
|
22 |
- } |
|
23 |
- if len(q) > 0 { |
|
24 |
- close(q[0]) |
|
25 |
- m.waiters[t] = q[1:] |
|
26 |
- } |
|
27 |
-} |
|
28 |
- |
|
29 |
-func (m *pauseMonitor) append(t string, waiter chan struct{}) { |
|
30 |
- m.Lock() |
|
31 |
- defer m.Unlock() |
|
32 |
- if m.waiters == nil { |
|
33 |
- m.waiters = make(map[string][]chan struct{}) |
|
34 |
- } |
|
35 |
- _, ok := m.waiters[t] |
|
36 |
- if !ok { |
|
37 |
- m.waiters[t] = make([]chan struct{}, 0) |
|
38 |
- } |
|
39 |
- m.waiters[t] = append(m.waiters[t], waiter) |
|
40 |
-} |
41 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,42 @@ |
0 |
+// +build !windows |
|
1 |
+ |
|
2 |
+package libcontainerd |
|
3 |
+ |
|
4 |
+import ( |
|
5 |
+ "sync" |
|
6 |
+) |
|
7 |
+ |
|
8 |
+// pauseMonitor is helper to get notifications from pause state changes. |
|
9 |
+type pauseMonitor struct { |
|
10 |
+ sync.Mutex |
|
11 |
+ waiters map[string][]chan struct{} |
|
12 |
+} |
|
13 |
+ |
|
14 |
+func (m *pauseMonitor) handle(t string) { |
|
15 |
+ m.Lock() |
|
16 |
+ defer m.Unlock() |
|
17 |
+ if m.waiters == nil { |
|
18 |
+ return |
|
19 |
+ } |
|
20 |
+ q, ok := m.waiters[t] |
|
21 |
+ if !ok { |
|
22 |
+ return |
|
23 |
+ } |
|
24 |
+ if len(q) > 0 { |
|
25 |
+ close(q[0]) |
|
26 |
+ m.waiters[t] = q[1:] |
|
27 |
+ } |
|
28 |
+} |
|
29 |
+ |
|
30 |
+func (m *pauseMonitor) append(t string, waiter chan struct{}) { |
|
31 |
+ m.Lock() |
|
32 |
+ defer m.Unlock() |
|
33 |
+ if m.waiters == nil { |
|
34 |
+ m.waiters = make(map[string][]chan struct{}) |
|
35 |
+ } |
|
36 |
+ _, ok := m.waiters[t] |
|
37 |
+ if !ok { |
|
38 |
+ m.waiters[t] = make([]chan struct{}, 0) |
|
39 |
+ } |
|
40 |
+ m.waiters[t] = append(m.waiters[t], waiter) |
|
41 |
+} |
0 | 42 |
deleted file mode 100644 |
... | ... |
@@ -1,101 +0,0 @@ |
1 |
-package libcontainerd |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "io" |
|
5 |
- "io/ioutil" |
|
6 |
- "os" |
|
7 |
- "path/filepath" |
|
8 |
- "syscall" |
|
9 |
- "time" |
|
10 |
- |
|
11 |
- containerd "github.com/docker/containerd/api/grpc/types" |
|
12 |
- "github.com/tonistiigi/fifo" |
|
13 |
- "golang.org/x/net/context" |
|
14 |
-) |
|
15 |
- |
|
16 |
-var fdNames = map[int]string{ |
|
17 |
- syscall.Stdin: "stdin", |
|
18 |
- syscall.Stdout: "stdout", |
|
19 |
- syscall.Stderr: "stderr", |
|
20 |
-} |
|
21 |
- |
|
22 |
-// process keeps the state for both main container process and exec process. |
|
23 |
-type process struct { |
|
24 |
- processCommon |
|
25 |
- |
|
26 |
- // Platform specific fields are below here. |
|
27 |
- dir string |
|
28 |
-} |
|
29 |
- |
|
30 |
-func (p *process) openFifos(terminal bool) (pipe *IOPipe, err error) { |
|
31 |
- if err := os.MkdirAll(p.dir, 0700); err != nil { |
|
32 |
- return nil, err |
|
33 |
- } |
|
34 |
- |
|
35 |
- ctx, _ := context.WithTimeout(context.Background(), 15*time.Second) |
|
36 |
- |
|
37 |
- io := &IOPipe{} |
|
38 |
- |
|
39 |
- io.Stdin, err = fifo.OpenFifo(ctx, p.fifo(syscall.Stdin), syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700) |
|
40 |
- if err != nil { |
|
41 |
- return nil, err |
|
42 |
- } |
|
43 |
- |
|
44 |
- defer func() { |
|
45 |
- if err != nil { |
|
46 |
- io.Stdin.Close() |
|
47 |
- } |
|
48 |
- }() |
|
49 |
- |
|
50 |
- io.Stdout, err = fifo.OpenFifo(ctx, p.fifo(syscall.Stdout), syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700) |
|
51 |
- if err != nil { |
|
52 |
- return nil, err |
|
53 |
- } |
|
54 |
- |
|
55 |
- defer func() { |
|
56 |
- if err != nil { |
|
57 |
- io.Stdout.Close() |
|
58 |
- } |
|
59 |
- }() |
|
60 |
- |
|
61 |
- if !terminal { |
|
62 |
- io.Stderr, err = fifo.OpenFifo(ctx, p.fifo(syscall.Stderr), syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700) |
|
63 |
- if err != nil { |
|
64 |
- return nil, err |
|
65 |
- } |
|
66 |
- defer func() { |
|
67 |
- if err != nil { |
|
68 |
- io.Stderr.Close() |
|
69 |
- } |
|
70 |
- }() |
|
71 |
- } else { |
|
72 |
- io.Stderr = ioutil.NopCloser(emptyReader{}) |
|
73 |
- } |
|
74 |
- |
|
75 |
- return io, nil |
|
76 |
-} |
|
77 |
- |
|
78 |
-func (p *process) sendCloseStdin() error { |
|
79 |
- _, err := p.client.remote.apiClient.UpdateProcess(context.Background(), &containerd.UpdateProcessRequest{ |
|
80 |
- Id: p.containerID, |
|
81 |
- Pid: p.friendlyName, |
|
82 |
- CloseStdin: true, |
|
83 |
- }) |
|
84 |
- return err |
|
85 |
-} |
|
86 |
- |
|
87 |
-func (p *process) closeFifos(io *IOPipe) { |
|
88 |
- io.Stdin.Close() |
|
89 |
- io.Stdout.Close() |
|
90 |
- io.Stderr.Close() |
|
91 |
-} |
|
92 |
- |
|
93 |
-type emptyReader struct{} |
|
94 |
- |
|
95 |
-func (r emptyReader) Read(b []byte) (int, error) { |
|
96 |
- return 0, io.EOF |
|
97 |
-} |
|
98 |
- |
|
99 |
-func (p *process) fifo(index int) string { |
|
100 |
- return filepath.Join(p.dir, p.friendlyName+"-"+fdNames[index]) |
|
101 |
-} |
7 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,106 @@ |
0 |
+// +build linux solaris |
|
1 |
+ |
|
2 |
+package libcontainerd |
|
3 |
+ |
|
4 |
+import ( |
|
5 |
+ "io" |
|
6 |
+ "io/ioutil" |
|
7 |
+ "os" |
|
8 |
+ "path/filepath" |
|
9 |
+ goruntime "runtime" |
|
10 |
+ "time" |
|
11 |
+ |
|
12 |
+ containerd "github.com/docker/containerd/api/grpc/types" |
|
13 |
+ "github.com/tonistiigi/fifo" |
|
14 |
+ "golang.org/x/net/context" |
|
15 |
+ "golang.org/x/sys/unix" |
|
16 |
+) |
|
17 |
+ |
|
18 |
+var fdNames = map[int]string{ |
|
19 |
+ unix.Stdin: "stdin", |
|
20 |
+ unix.Stdout: "stdout", |
|
21 |
+ unix.Stderr: "stderr", |
|
22 |
+} |
|
23 |
+ |
|
24 |
+// process keeps the state for both main container process and exec process. |
|
25 |
+type process struct { |
|
26 |
+ processCommon |
|
27 |
+ |
|
28 |
+ // Platform specific fields are below here. |
|
29 |
+ dir string |
|
30 |
+} |
|
31 |
+ |
|
32 |
+func (p *process) openFifos(terminal bool) (pipe *IOPipe, err error) { |
|
33 |
+ if err := os.MkdirAll(p.dir, 0700); err != nil { |
|
34 |
+ return nil, err |
|
35 |
+ } |
|
36 |
+ |
|
37 |
+ ctx, _ := context.WithTimeout(context.Background(), 15*time.Second) |
|
38 |
+ |
|
39 |
+ io := &IOPipe{} |
|
40 |
+ |
|
41 |
+ io.Stdin, err = fifo.OpenFifo(ctx, p.fifo(unix.Stdin), unix.O_WRONLY|unix.O_CREAT|unix.O_NONBLOCK, 0700) |
|
42 |
+ if err != nil { |
|
43 |
+ return nil, err |
|
44 |
+ } |
|
45 |
+ |
|
46 |
+ defer func() { |
|
47 |
+ if err != nil { |
|
48 |
+ io.Stdin.Close() |
|
49 |
+ } |
|
50 |
+ }() |
|
51 |
+ |
|
52 |
+ io.Stdout, err = fifo.OpenFifo(ctx, p.fifo(unix.Stdout), unix.O_RDONLY|unix.O_CREAT|unix.O_NONBLOCK, 0700) |
|
53 |
+ if err != nil { |
|
54 |
+ return nil, err |
|
55 |
+ } |
|
56 |
+ |
|
57 |
+ defer func() { |
|
58 |
+ if err != nil { |
|
59 |
+ io.Stdout.Close() |
|
60 |
+ } |
|
61 |
+ }() |
|
62 |
+ |
|
63 |
+ if goruntime.GOOS == "solaris" || !terminal { |
|
64 |
+ // For Solaris terminal handling is done exclusively by the runtime therefore we make no distinction |
|
65 |
+ // in the processing for terminal and !terminal cases. |
|
66 |
+ io.Stderr, err = fifo.OpenFifo(ctx, p.fifo(unix.Stderr), unix.O_RDONLY|unix.O_CREAT|unix.O_NONBLOCK, 0700) |
|
67 |
+ if err != nil { |
|
68 |
+ return nil, err |
|
69 |
+ } |
|
70 |
+ defer func() { |
|
71 |
+ if err != nil { |
|
72 |
+ io.Stderr.Close() |
|
73 |
+ } |
|
74 |
+ }() |
|
75 |
+ } else { |
|
76 |
+ io.Stderr = ioutil.NopCloser(emptyReader{}) |
|
77 |
+ } |
|
78 |
+ |
|
79 |
+ return io, nil |
|
80 |
+} |
|
81 |
+ |
|
82 |
+func (p *process) sendCloseStdin() error { |
|
83 |
+ _, err := p.client.remote.apiClient.UpdateProcess(context.Background(), &containerd.UpdateProcessRequest{ |
|
84 |
+ Id: p.containerID, |
|
85 |
+ Pid: p.friendlyName, |
|
86 |
+ CloseStdin: true, |
|
87 |
+ }) |
|
88 |
+ return err |
|
89 |
+} |
|
90 |
+ |
|
91 |
+func (p *process) closeFifos(io *IOPipe) { |
|
92 |
+ io.Stdin.Close() |
|
93 |
+ io.Stdout.Close() |
|
94 |
+ io.Stderr.Close() |
|
95 |
+} |
|
96 |
+ |
|
97 |
+type emptyReader struct{} |
|
98 |
+ |
|
99 |
+func (r emptyReader) Read(b []byte) (int, error) { |
|
100 |
+ return 0, io.EOF |
|
101 |
+} |
|
102 |
+ |
|
103 |
+func (p *process) fifo(index int) string { |
|
104 |
+ return filepath.Join(p.dir, p.friendlyName+"-"+fdNames[index]) |
|
105 |
+} |
0 | 106 |
deleted file mode 100644 |
... | ... |
@@ -1,29 +0,0 @@ |
1 |
-package libcontainerd |
|
2 |
- |
|
3 |
-import "sync" |
|
4 |
- |
|
5 |
-type queue struct { |
|
6 |
- sync.Mutex |
|
7 |
- fns map[string]chan struct{} |
|
8 |
-} |
|
9 |
- |
|
10 |
-func (q *queue) append(id string, f func()) { |
|
11 |
- q.Lock() |
|
12 |
- defer q.Unlock() |
|
13 |
- |
|
14 |
- if q.fns == nil { |
|
15 |
- q.fns = make(map[string]chan struct{}) |
|
16 |
- } |
|
17 |
- |
|
18 |
- done := make(chan struct{}) |
|
19 |
- |
|
20 |
- fn, ok := q.fns[id] |
|
21 |
- q.fns[id] = done |
|
22 |
- go func() { |
|
23 |
- if ok { |
|
24 |
- <-fn |
|
25 |
- } |
|
26 |
- f() |
|
27 |
- close(done) |
|
28 |
- }() |
|
29 |
-} |
30 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,31 @@ |
0 |
+// +build linux solaris |
|
1 |
+ |
|
2 |
+package libcontainerd |
|
3 |
+ |
|
4 |
+import "sync" |
|
5 |
+ |
|
6 |
+type queue struct { |
|
7 |
+ sync.Mutex |
|
8 |
+ fns map[string]chan struct{} |
|
9 |
+} |
|
10 |
+ |
|
11 |
+func (q *queue) append(id string, f func()) { |
|
12 |
+ q.Lock() |
|
13 |
+ defer q.Unlock() |
|
14 |
+ |
|
15 |
+ if q.fns == nil { |
|
16 |
+ q.fns = make(map[string]chan struct{}) |
|
17 |
+ } |
|
18 |
+ |
|
19 |
+ done := make(chan struct{}) |
|
20 |
+ |
|
21 |
+ fn, ok := q.fns[id] |
|
22 |
+ q.fns[id] = done |
|
23 |
+ go func() { |
|
24 |
+ if ok { |
|
25 |
+ <-fn |
|
26 |
+ } |
|
27 |
+ f() |
|
28 |
+ close(done) |
|
29 |
+ }() |
|
30 |
+} |
0 | 31 |
deleted file mode 100644 |
... | ... |
@@ -1,559 +0,0 @@ |
1 |
-package libcontainerd |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "fmt" |
|
5 |
- "io" |
|
6 |
- "io/ioutil" |
|
7 |
- "log" |
|
8 |
- "net" |
|
9 |
- "os" |
|
10 |
- "os/exec" |
|
11 |
- "path/filepath" |
|
12 |
- "strconv" |
|
13 |
- "strings" |
|
14 |
- "sync" |
|
15 |
- "syscall" |
|
16 |
- "time" |
|
17 |
- |
|
18 |
- "github.com/Sirupsen/logrus" |
|
19 |
- containerd "github.com/docker/containerd/api/grpc/types" |
|
20 |
- "github.com/docker/docker/pkg/locker" |
|
21 |
- sysinfo "github.com/docker/docker/pkg/system" |
|
22 |
- "github.com/docker/docker/utils" |
|
23 |
- "github.com/golang/protobuf/ptypes" |
|
24 |
- "github.com/golang/protobuf/ptypes/timestamp" |
|
25 |
- rsystem "github.com/opencontainers/runc/libcontainer/system" |
|
26 |
- "golang.org/x/net/context" |
|
27 |
- "google.golang.org/grpc" |
|
28 |
- "google.golang.org/grpc/grpclog" |
|
29 |
- "google.golang.org/grpc/health/grpc_health_v1" |
|
30 |
- "google.golang.org/grpc/transport" |
|
31 |
-) |
|
32 |
- |
|
33 |
-const ( |
|
34 |
- maxConnectionRetryCount = 3 |
|
35 |
- containerdHealthCheckTimeout = 3 * time.Second |
|
36 |
- containerdShutdownTimeout = 15 * time.Second |
|
37 |
- containerdBinary = "docker-containerd" |
|
38 |
- containerdPidFilename = "docker-containerd.pid" |
|
39 |
- containerdSockFilename = "docker-containerd.sock" |
|
40 |
- containerdStateDir = "containerd" |
|
41 |
- eventTimestampFilename = "event.ts" |
|
42 |
-) |
|
43 |
- |
|
44 |
-type remote struct { |
|
45 |
- sync.RWMutex |
|
46 |
- apiClient containerd.APIClient |
|
47 |
- daemonPid int |
|
48 |
- stateDir string |
|
49 |
- rpcAddr string |
|
50 |
- startDaemon bool |
|
51 |
- closeManually bool |
|
52 |
- debugLog bool |
|
53 |
- rpcConn *grpc.ClientConn |
|
54 |
- clients []*client |
|
55 |
- eventTsPath string |
|
56 |
- runtime string |
|
57 |
- runtimeArgs []string |
|
58 |
- daemonWaitCh chan struct{} |
|
59 |
- liveRestore bool |
|
60 |
- oomScore int |
|
61 |
- restoreFromTimestamp *timestamp.Timestamp |
|
62 |
-} |
|
63 |
- |
|
64 |
-// New creates a fresh instance of libcontainerd remote. |
|
65 |
-func New(stateDir string, options ...RemoteOption) (_ Remote, err error) { |
|
66 |
- defer func() { |
|
67 |
- if err != nil { |
|
68 |
- err = fmt.Errorf("Failed to connect to containerd. Please make sure containerd is installed in your PATH or you have specificed the correct address. Got error: %v", err) |
|
69 |
- } |
|
70 |
- }() |
|
71 |
- r := &remote{ |
|
72 |
- stateDir: stateDir, |
|
73 |
- daemonPid: -1, |
|
74 |
- eventTsPath: filepath.Join(stateDir, eventTimestampFilename), |
|
75 |
- } |
|
76 |
- for _, option := range options { |
|
77 |
- if err := option.Apply(r); err != nil { |
|
78 |
- return nil, err |
|
79 |
- } |
|
80 |
- } |
|
81 |
- |
|
82 |
- if err := sysinfo.MkdirAll(stateDir, 0700); err != nil { |
|
83 |
- return nil, err |
|
84 |
- } |
|
85 |
- |
|
86 |
- if r.rpcAddr == "" { |
|
87 |
- r.rpcAddr = filepath.Join(stateDir, containerdSockFilename) |
|
88 |
- } |
|
89 |
- |
|
90 |
- if r.startDaemon { |
|
91 |
- if err := r.runContainerdDaemon(); err != nil { |
|
92 |
- return nil, err |
|
93 |
- } |
|
94 |
- } |
|
95 |
- |
|
96 |
- // don't output the grpc reconnect logging |
|
97 |
- grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags)) |
|
98 |
- dialOpts := append([]grpc.DialOption{grpc.WithInsecure()}, |
|
99 |
- grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { |
|
100 |
- return net.DialTimeout("unix", addr, timeout) |
|
101 |
- }), |
|
102 |
- ) |
|
103 |
- conn, err := grpc.Dial(r.rpcAddr, dialOpts...) |
|
104 |
- if err != nil { |
|
105 |
- return nil, fmt.Errorf("error connecting to containerd: %v", err) |
|
106 |
- } |
|
107 |
- |
|
108 |
- r.rpcConn = conn |
|
109 |
- r.apiClient = containerd.NewAPIClient(conn) |
|
110 |
- |
|
111 |
- // Get the timestamp to restore from |
|
112 |
- t := r.getLastEventTimestamp() |
|
113 |
- tsp, err := ptypes.TimestampProto(t) |
|
114 |
- if err != nil { |
|
115 |
- logrus.Errorf("libcontainerd: failed to convert timestamp: %q", err) |
|
116 |
- } |
|
117 |
- r.restoreFromTimestamp = tsp |
|
118 |
- |
|
119 |
- go r.handleConnectionChange() |
|
120 |
- |
|
121 |
- if err := r.startEventsMonitor(); err != nil { |
|
122 |
- return nil, err |
|
123 |
- } |
|
124 |
- |
|
125 |
- return r, nil |
|
126 |
-} |
|
127 |
- |
|
128 |
-func (r *remote) UpdateOptions(options ...RemoteOption) error { |
|
129 |
- for _, option := range options { |
|
130 |
- if err := option.Apply(r); err != nil { |
|
131 |
- return err |
|
132 |
- } |
|
133 |
- } |
|
134 |
- return nil |
|
135 |
-} |
|
136 |
- |
|
137 |
-func (r *remote) handleConnectionChange() { |
|
138 |
- var transientFailureCount = 0 |
|
139 |
- |
|
140 |
- ticker := time.NewTicker(500 * time.Millisecond) |
|
141 |
- defer ticker.Stop() |
|
142 |
- healthClient := grpc_health_v1.NewHealthClient(r.rpcConn) |
|
143 |
- |
|
144 |
- for { |
|
145 |
- <-ticker.C |
|
146 |
- ctx, cancel := context.WithTimeout(context.Background(), containerdHealthCheckTimeout) |
|
147 |
- _, err := healthClient.Check(ctx, &grpc_health_v1.HealthCheckRequest{}) |
|
148 |
- cancel() |
|
149 |
- if err == nil { |
|
150 |
- continue |
|
151 |
- } |
|
152 |
- |
|
153 |
- logrus.Debugf("libcontainerd: containerd health check returned error: %v", err) |
|
154 |
- |
|
155 |
- if r.daemonPid != -1 { |
|
156 |
- if strings.Contains(err.Error(), "is closing") { |
|
157 |
- // Well, we asked for it to stop, just return |
|
158 |
- return |
|
159 |
- } |
|
160 |
- // all other errors are transient |
|
161 |
- // Reset state to be notified of next failure |
|
162 |
- transientFailureCount++ |
|
163 |
- if transientFailureCount >= maxConnectionRetryCount { |
|
164 |
- transientFailureCount = 0 |
|
165 |
- if utils.IsProcessAlive(r.daemonPid) { |
|
166 |
- utils.KillProcess(r.daemonPid) |
|
167 |
- } |
|
168 |
- <-r.daemonWaitCh |
|
169 |
- if err := r.runContainerdDaemon(); err != nil { //FIXME: Handle error |
|
170 |
- logrus.Errorf("libcontainerd: error restarting containerd: %v", err) |
|
171 |
- } |
|
172 |
- continue |
|
173 |
- } |
|
174 |
- } |
|
175 |
- } |
|
176 |
-} |
|
177 |
- |
|
178 |
-func (r *remote) Cleanup() { |
|
179 |
- if r.daemonPid == -1 { |
|
180 |
- return |
|
181 |
- } |
|
182 |
- r.closeManually = true |
|
183 |
- r.rpcConn.Close() |
|
184 |
- // Ask the daemon to quit |
|
185 |
- syscall.Kill(r.daemonPid, syscall.SIGTERM) |
|
186 |
- |
|
187 |
- // Wait up to 15secs for it to stop |
|
188 |
- for i := time.Duration(0); i < containerdShutdownTimeout; i += time.Second { |
|
189 |
- if !utils.IsProcessAlive(r.daemonPid) { |
|
190 |
- break |
|
191 |
- } |
|
192 |
- time.Sleep(time.Second) |
|
193 |
- } |
|
194 |
- |
|
195 |
- if utils.IsProcessAlive(r.daemonPid) { |
|
196 |
- logrus.Warnf("libcontainerd: containerd (%d) didn't stop within 15 secs, killing it\n", r.daemonPid) |
|
197 |
- syscall.Kill(r.daemonPid, syscall.SIGKILL) |
|
198 |
- } |
|
199 |
- |
|
200 |
- // cleanup some files |
|
201 |
- os.Remove(filepath.Join(r.stateDir, containerdPidFilename)) |
|
202 |
- os.Remove(filepath.Join(r.stateDir, containerdSockFilename)) |
|
203 |
-} |
|
204 |
- |
|
205 |
-func (r *remote) Client(b Backend) (Client, error) { |
|
206 |
- c := &client{ |
|
207 |
- clientCommon: clientCommon{ |
|
208 |
- backend: b, |
|
209 |
- containers: make(map[string]*container), |
|
210 |
- locker: locker.New(), |
|
211 |
- }, |
|
212 |
- remote: r, |
|
213 |
- exitNotifiers: make(map[string]*exitNotifier), |
|
214 |
- liveRestore: r.liveRestore, |
|
215 |
- } |
|
216 |
- |
|
217 |
- r.Lock() |
|
218 |
- r.clients = append(r.clients, c) |
|
219 |
- r.Unlock() |
|
220 |
- return c, nil |
|
221 |
-} |
|
222 |
- |
|
223 |
-func (r *remote) updateEventTimestamp(t time.Time) { |
|
224 |
- f, err := os.OpenFile(r.eventTsPath, syscall.O_CREAT|syscall.O_WRONLY|syscall.O_TRUNC, 0600) |
|
225 |
- if err != nil { |
|
226 |
- logrus.Warnf("libcontainerd: failed to open event timestamp file: %v", err) |
|
227 |
- return |
|
228 |
- } |
|
229 |
- defer f.Close() |
|
230 |
- |
|
231 |
- b, err := t.MarshalText() |
|
232 |
- if err != nil { |
|
233 |
- logrus.Warnf("libcontainerd: failed to encode timestamp: %v", err) |
|
234 |
- return |
|
235 |
- } |
|
236 |
- |
|
237 |
- n, err := f.Write(b) |
|
238 |
- if err != nil || n != len(b) { |
|
239 |
- logrus.Warnf("libcontainerd: failed to update event timestamp file: %v", err) |
|
240 |
- f.Truncate(0) |
|
241 |
- return |
|
242 |
- } |
|
243 |
-} |
|
244 |
- |
|
245 |
-func (r *remote) getLastEventTimestamp() time.Time { |
|
246 |
- t := time.Now() |
|
247 |
- |
|
248 |
- fi, err := os.Stat(r.eventTsPath) |
|
249 |
- if os.IsNotExist(err) || fi.Size() == 0 { |
|
250 |
- return t |
|
251 |
- } |
|
252 |
- |
|
253 |
- f, err := os.Open(r.eventTsPath) |
|
254 |
- if err != nil { |
|
255 |
- logrus.Warnf("libcontainerd: Unable to access last event ts: %v", err) |
|
256 |
- return t |
|
257 |
- } |
|
258 |
- defer f.Close() |
|
259 |
- |
|
260 |
- b := make([]byte, fi.Size()) |
|
261 |
- n, err := f.Read(b) |
|
262 |
- if err != nil || n != len(b) { |
|
263 |
- logrus.Warnf("libcontainerd: Unable to read last event ts: %v", err) |
|
264 |
- return t |
|
265 |
- } |
|
266 |
- |
|
267 |
- t.UnmarshalText(b) |
|
268 |
- |
|
269 |
- return t |
|
270 |
-} |
|
271 |
- |
|
272 |
-func (r *remote) startEventsMonitor() error { |
|
273 |
- // First, get past events |
|
274 |
- t := r.getLastEventTimestamp() |
|
275 |
- tsp, err := ptypes.TimestampProto(t) |
|
276 |
- if err != nil { |
|
277 |
- logrus.Errorf("libcontainerd: failed to convert timestamp: %q", err) |
|
278 |
- } |
|
279 |
- er := &containerd.EventsRequest{ |
|
280 |
- Timestamp: tsp, |
|
281 |
- } |
|
282 |
- events, err := r.apiClient.Events(context.Background(), er, grpc.FailFast(false)) |
|
283 |
- if err != nil { |
|
284 |
- return err |
|
285 |
- } |
|
286 |
- go r.handleEventStream(events) |
|
287 |
- return nil |
|
288 |
-} |
|
289 |
- |
|
290 |
-func (r *remote) handleEventStream(events containerd.API_EventsClient) { |
|
291 |
- for { |
|
292 |
- e, err := events.Recv() |
|
293 |
- if err != nil { |
|
294 |
- if grpc.ErrorDesc(err) == transport.ErrConnClosing.Desc && |
|
295 |
- r.closeManually { |
|
296 |
- // ignore error if grpc remote connection is closed manually |
|
297 |
- return |
|
298 |
- } |
|
299 |
- logrus.Errorf("libcontainerd: failed to receive event from containerd: %v", err) |
|
300 |
- go r.startEventsMonitor() |
|
301 |
- return |
|
302 |
- } |
|
303 |
- |
|
304 |
- logrus.Debugf("libcontainerd: received containerd event: %#v", e) |
|
305 |
- |
|
306 |
- var container *container |
|
307 |
- var c *client |
|
308 |
- r.RLock() |
|
309 |
- for _, c = range r.clients { |
|
310 |
- container, err = c.getContainer(e.Id) |
|
311 |
- if err == nil { |
|
312 |
- break |
|
313 |
- } |
|
314 |
- } |
|
315 |
- r.RUnlock() |
|
316 |
- if container == nil { |
|
317 |
- logrus.Warnf("libcontainerd: unknown container %s", e.Id) |
|
318 |
- continue |
|
319 |
- } |
|
320 |
- |
|
321 |
- if err := container.handleEvent(e); err != nil { |
|
322 |
- logrus.Errorf("libcontainerd: error processing state change for %s: %v", e.Id, err) |
|
323 |
- } |
|
324 |
- |
|
325 |
- tsp, err := ptypes.Timestamp(e.Timestamp) |
|
326 |
- if err != nil { |
|
327 |
- logrus.Errorf("libcontainerd: failed to convert event timestamp: %q", err) |
|
328 |
- continue |
|
329 |
- } |
|
330 |
- |
|
331 |
- r.updateEventTimestamp(tsp) |
|
332 |
- } |
|
333 |
-} |
|
334 |
- |
|
335 |
-func (r *remote) runContainerdDaemon() error { |
|
336 |
- pidFilename := filepath.Join(r.stateDir, containerdPidFilename) |
|
337 |
- f, err := os.OpenFile(pidFilename, os.O_RDWR|os.O_CREATE, 0600) |
|
338 |
- if err != nil { |
|
339 |
- return err |
|
340 |
- } |
|
341 |
- defer f.Close() |
|
342 |
- |
|
343 |
- // File exist, check if the daemon is alive |
|
344 |
- b := make([]byte, 8) |
|
345 |
- n, err := f.Read(b) |
|
346 |
- if err != nil && err != io.EOF { |
|
347 |
- return err |
|
348 |
- } |
|
349 |
- |
|
350 |
- if n > 0 { |
|
351 |
- pid, err := strconv.ParseUint(string(b[:n]), 10, 64) |
|
352 |
- if err != nil { |
|
353 |
- return err |
|
354 |
- } |
|
355 |
- if utils.IsProcessAlive(int(pid)) { |
|
356 |
- logrus.Infof("libcontainerd: previous instance of containerd still alive (%d)", pid) |
|
357 |
- r.daemonPid = int(pid) |
|
358 |
- return nil |
|
359 |
- } |
|
360 |
- } |
|
361 |
- |
|
362 |
- // rewind the file |
|
363 |
- _, err = f.Seek(0, os.SEEK_SET) |
|
364 |
- if err != nil { |
|
365 |
- return err |
|
366 |
- } |
|
367 |
- |
|
368 |
- // Truncate it |
|
369 |
- err = f.Truncate(0) |
|
370 |
- if err != nil { |
|
371 |
- return err |
|
372 |
- } |
|
373 |
- |
|
374 |
- // Start a new instance |
|
375 |
- args := []string{ |
|
376 |
- "-l", fmt.Sprintf("unix://%s", r.rpcAddr), |
|
377 |
- "--shim", "docker-containerd-shim", |
|
378 |
- "--metrics-interval=0", |
|
379 |
- "--start-timeout", "2m", |
|
380 |
- "--state-dir", filepath.Join(r.stateDir, containerdStateDir), |
|
381 |
- } |
|
382 |
- if r.runtime != "" { |
|
383 |
- args = append(args, "--runtime") |
|
384 |
- args = append(args, r.runtime) |
|
385 |
- } |
|
386 |
- if r.debugLog { |
|
387 |
- args = append(args, "--debug") |
|
388 |
- } |
|
389 |
- if len(r.runtimeArgs) > 0 { |
|
390 |
- for _, v := range r.runtimeArgs { |
|
391 |
- args = append(args, "--runtime-args") |
|
392 |
- args = append(args, v) |
|
393 |
- } |
|
394 |
- logrus.Debugf("libcontainerd: runContainerdDaemon: runtimeArgs: %s", args) |
|
395 |
- } |
|
396 |
- |
|
397 |
- cmd := exec.Command(containerdBinary, args...) |
|
398 |
- // redirect containerd logs to docker logs |
|
399 |
- cmd.Stdout = os.Stdout |
|
400 |
- cmd.Stderr = os.Stderr |
|
401 |
- cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true, Pdeathsig: syscall.SIGKILL} |
|
402 |
- cmd.Env = nil |
|
403 |
- // clear the NOTIFY_SOCKET from the env when starting containerd |
|
404 |
- for _, e := range os.Environ() { |
|
405 |
- if !strings.HasPrefix(e, "NOTIFY_SOCKET") { |
|
406 |
- cmd.Env = append(cmd.Env, e) |
|
407 |
- } |
|
408 |
- } |
|
409 |
- if err := cmd.Start(); err != nil { |
|
410 |
- return err |
|
411 |
- } |
|
412 |
- logrus.Infof("libcontainerd: new containerd process, pid: %d", cmd.Process.Pid) |
|
413 |
- if err := setOOMScore(cmd.Process.Pid, r.oomScore); err != nil { |
|
414 |
- utils.KillProcess(cmd.Process.Pid) |
|
415 |
- return err |
|
416 |
- } |
|
417 |
- if _, err := f.WriteString(fmt.Sprintf("%d", cmd.Process.Pid)); err != nil { |
|
418 |
- utils.KillProcess(cmd.Process.Pid) |
|
419 |
- return err |
|
420 |
- } |
|
421 |
- |
|
422 |
- r.daemonWaitCh = make(chan struct{}) |
|
423 |
- go func() { |
|
424 |
- cmd.Wait() |
|
425 |
- close(r.daemonWaitCh) |
|
426 |
- }() // Reap our child when needed |
|
427 |
- r.daemonPid = cmd.Process.Pid |
|
428 |
- return nil |
|
429 |
-} |
|
430 |
- |
|
431 |
-func setOOMScore(pid, score int) error { |
|
432 |
- oomScoreAdjPath := fmt.Sprintf("/proc/%d/oom_score_adj", pid) |
|
433 |
- f, err := os.OpenFile(oomScoreAdjPath, os.O_WRONLY, 0) |
|
434 |
- if err != nil { |
|
435 |
- return err |
|
436 |
- } |
|
437 |
- stringScore := strconv.Itoa(score) |
|
438 |
- _, err = f.WriteString(stringScore) |
|
439 |
- f.Close() |
|
440 |
- if os.IsPermission(err) { |
|
441 |
- // Setting oom_score_adj does not work in an |
|
442 |
- // unprivileged container. Ignore the error, but log |
|
443 |
- // it if we appear not to be in that situation. |
|
444 |
- if !rsystem.RunningInUserNS() { |
|
445 |
- logrus.Debugf("Permission denied writing %q to %s", stringScore, oomScoreAdjPath) |
|
446 |
- } |
|
447 |
- return nil |
|
448 |
- } |
|
449 |
- return err |
|
450 |
-} |
|
451 |
- |
|
452 |
-// WithRemoteAddr sets the external containerd socket to connect to. |
|
453 |
-func WithRemoteAddr(addr string) RemoteOption { |
|
454 |
- return rpcAddr(addr) |
|
455 |
-} |
|
456 |
- |
|
457 |
-type rpcAddr string |
|
458 |
- |
|
459 |
-func (a rpcAddr) Apply(r Remote) error { |
|
460 |
- if remote, ok := r.(*remote); ok { |
|
461 |
- remote.rpcAddr = string(a) |
|
462 |
- return nil |
|
463 |
- } |
|
464 |
- return fmt.Errorf("WithRemoteAddr option not supported for this remote") |
|
465 |
-} |
|
466 |
- |
|
467 |
-// WithRuntimePath sets the path of the runtime to be used as the |
|
468 |
-// default by containerd |
|
469 |
-func WithRuntimePath(rt string) RemoteOption { |
|
470 |
- return runtimePath(rt) |
|
471 |
-} |
|
472 |
- |
|
473 |
-type runtimePath string |
|
474 |
- |
|
475 |
-func (rt runtimePath) Apply(r Remote) error { |
|
476 |
- if remote, ok := r.(*remote); ok { |
|
477 |
- remote.runtime = string(rt) |
|
478 |
- return nil |
|
479 |
- } |
|
480 |
- return fmt.Errorf("WithRuntime option not supported for this remote") |
|
481 |
-} |
|
482 |
- |
|
483 |
-// WithRuntimeArgs sets the list of runtime args passed to containerd |
|
484 |
-func WithRuntimeArgs(args []string) RemoteOption { |
|
485 |
- return runtimeArgs(args) |
|
486 |
-} |
|
487 |
- |
|
488 |
-type runtimeArgs []string |
|
489 |
- |
|
490 |
-func (rt runtimeArgs) Apply(r Remote) error { |
|
491 |
- if remote, ok := r.(*remote); ok { |
|
492 |
- remote.runtimeArgs = rt |
|
493 |
- return nil |
|
494 |
- } |
|
495 |
- return fmt.Errorf("WithRuntimeArgs option not supported for this remote") |
|
496 |
-} |
|
497 |
- |
|
498 |
-// WithStartDaemon defines if libcontainerd should also run containerd daemon. |
|
499 |
-func WithStartDaemon(start bool) RemoteOption { |
|
500 |
- return startDaemon(start) |
|
501 |
-} |
|
502 |
- |
|
503 |
-type startDaemon bool |
|
504 |
- |
|
505 |
-func (s startDaemon) Apply(r Remote) error { |
|
506 |
- if remote, ok := r.(*remote); ok { |
|
507 |
- remote.startDaemon = bool(s) |
|
508 |
- return nil |
|
509 |
- } |
|
510 |
- return fmt.Errorf("WithStartDaemon option not supported for this remote") |
|
511 |
-} |
|
512 |
- |
|
513 |
-// WithDebugLog defines if containerd debug logs will be enabled for daemon. |
|
514 |
-func WithDebugLog(debug bool) RemoteOption { |
|
515 |
- return debugLog(debug) |
|
516 |
-} |
|
517 |
- |
|
518 |
-type debugLog bool |
|
519 |
- |
|
520 |
-func (d debugLog) Apply(r Remote) error { |
|
521 |
- if remote, ok := r.(*remote); ok { |
|
522 |
- remote.debugLog = bool(d) |
|
523 |
- return nil |
|
524 |
- } |
|
525 |
- return fmt.Errorf("WithDebugLog option not supported for this remote") |
|
526 |
-} |
|
527 |
- |
|
528 |
-// WithLiveRestore defines if containers are stopped on shutdown or restored. |
|
529 |
-func WithLiveRestore(v bool) RemoteOption { |
|
530 |
- return liveRestore(v) |
|
531 |
-} |
|
532 |
- |
|
533 |
-type liveRestore bool |
|
534 |
- |
|
535 |
-func (l liveRestore) Apply(r Remote) error { |
|
536 |
- if remote, ok := r.(*remote); ok { |
|
537 |
- remote.liveRestore = bool(l) |
|
538 |
- for _, c := range remote.clients { |
|
539 |
- c.liveRestore = bool(l) |
|
540 |
- } |
|
541 |
- return nil |
|
542 |
- } |
|
543 |
- return fmt.Errorf("WithLiveRestore option not supported for this remote") |
|
544 |
-} |
|
545 |
- |
|
546 |
-// WithOOMScore defines the oom_score_adj to set for the containerd process. |
|
547 |
-func WithOOMScore(score int) RemoteOption { |
|
548 |
- return oomScore(score) |
|
549 |
-} |
|
550 |
- |
|
551 |
-type oomScore int |
|
552 |
- |
|
553 |
-func (o oomScore) Apply(r Remote) error { |
|
554 |
- if remote, ok := r.(*remote); ok { |
|
555 |
- remote.oomScore = int(o) |
|
556 |
- return nil |
|
557 |
- } |
|
558 |
- return fmt.Errorf("WithOOMScore option not supported for this remote") |
|
559 |
-} |
560 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,34 +0,0 @@ |
1 |
-package libcontainerd |
|
2 |
- |
|
3 |
-import "github.com/docker/docker/pkg/locker" |
|
4 |
- |
|
5 |
-type remote struct { |
|
6 |
-} |
|
7 |
- |
|
8 |
-func (r *remote) Client(b Backend) (Client, error) { |
|
9 |
- c := &client{ |
|
10 |
- clientCommon: clientCommon{ |
|
11 |
- backend: b, |
|
12 |
- containers: make(map[string]*container), |
|
13 |
- locker: locker.New(), |
|
14 |
- }, |
|
15 |
- } |
|
16 |
- return c, nil |
|
17 |
-} |
|
18 |
- |
|
19 |
-func (r *remote) Cleanup() { |
|
20 |
-} |
|
21 |
- |
|
22 |
-func (r *remote) UpdateOptions(opts ...RemoteOption) error { |
|
23 |
- return nil |
|
24 |
-} |
|
25 |
- |
|
26 |
-// New creates a fresh instance of libcontainerd remote. |
|
27 |
-func New(_ string, _ ...RemoteOption) (Remote, error) { |
|
28 |
- return &remote{}, nil |
|
29 |
-} |
|
30 |
- |
|
31 |
-// WithLiveRestore is a noop on solaris. |
|
32 |
-func WithLiveRestore(v bool) RemoteOption { |
|
33 |
- return nil |
|
34 |
-} |
35 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,544 @@ |
0 |
+// +build linux solaris |
|
1 |
+ |
|
2 |
+package libcontainerd |
|
3 |
+ |
|
4 |
+import ( |
|
5 |
+ "fmt" |
|
6 |
+ "io" |
|
7 |
+ "io/ioutil" |
|
8 |
+ "log" |
|
9 |
+ "net" |
|
10 |
+ "os" |
|
11 |
+ "os/exec" |
|
12 |
+ "path/filepath" |
|
13 |
+ goruntime "runtime" |
|
14 |
+ "strconv" |
|
15 |
+ "strings" |
|
16 |
+ "sync" |
|
17 |
+ "syscall" |
|
18 |
+ "time" |
|
19 |
+ |
|
20 |
+ "github.com/Sirupsen/logrus" |
|
21 |
+ containerd "github.com/docker/containerd/api/grpc/types" |
|
22 |
+ "github.com/docker/docker/pkg/locker" |
|
23 |
+ sysinfo "github.com/docker/docker/pkg/system" |
|
24 |
+ "github.com/docker/docker/utils" |
|
25 |
+ "github.com/golang/protobuf/ptypes" |
|
26 |
+ "github.com/golang/protobuf/ptypes/timestamp" |
|
27 |
+ "golang.org/x/net/context" |
|
28 |
+ "google.golang.org/grpc" |
|
29 |
+ "google.golang.org/grpc/grpclog" |
|
30 |
+ "google.golang.org/grpc/health/grpc_health_v1" |
|
31 |
+ "google.golang.org/grpc/transport" |
|
32 |
+) |
|
33 |
+ |
|
34 |
+const ( |
|
35 |
+ maxConnectionRetryCount = 3 |
|
36 |
+ containerdHealthCheckTimeout = 3 * time.Second |
|
37 |
+ containerdShutdownTimeout = 15 * time.Second |
|
38 |
+ containerdBinary = "docker-containerd" |
|
39 |
+ containerdPidFilename = "docker-containerd.pid" |
|
40 |
+ containerdSockFilename = "docker-containerd.sock" |
|
41 |
+ containerdStateDir = "containerd" |
|
42 |
+ eventTimestampFilename = "event.ts" |
|
43 |
+) |
|
44 |
+ |
|
45 |
+type remote struct { |
|
46 |
+ sync.RWMutex |
|
47 |
+ apiClient containerd.APIClient |
|
48 |
+ daemonPid int |
|
49 |
+ stateDir string |
|
50 |
+ rpcAddr string |
|
51 |
+ startDaemon bool |
|
52 |
+ closeManually bool |
|
53 |
+ debugLog bool |
|
54 |
+ rpcConn *grpc.ClientConn |
|
55 |
+ clients []*client |
|
56 |
+ eventTsPath string |
|
57 |
+ runtime string |
|
58 |
+ runtimeArgs []string |
|
59 |
+ daemonWaitCh chan struct{} |
|
60 |
+ liveRestore bool |
|
61 |
+ oomScore int |
|
62 |
+ restoreFromTimestamp *timestamp.Timestamp |
|
63 |
+} |
|
64 |
+ |
|
65 |
+// New creates a fresh instance of libcontainerd remote. |
|
66 |
+func New(stateDir string, options ...RemoteOption) (_ Remote, err error) { |
|
67 |
+ defer func() { |
|
68 |
+ if err != nil { |
|
69 |
+ err = fmt.Errorf("Failed to connect to containerd. Please make sure containerd is installed in your PATH or you have specificed the correct address. Got error: %v", err) |
|
70 |
+ } |
|
71 |
+ }() |
|
72 |
+ r := &remote{ |
|
73 |
+ stateDir: stateDir, |
|
74 |
+ daemonPid: -1, |
|
75 |
+ eventTsPath: filepath.Join(stateDir, eventTimestampFilename), |
|
76 |
+ } |
|
77 |
+ for _, option := range options { |
|
78 |
+ if err := option.Apply(r); err != nil { |
|
79 |
+ return nil, err |
|
80 |
+ } |
|
81 |
+ } |
|
82 |
+ |
|
83 |
+ if err := sysinfo.MkdirAll(stateDir, 0700); err != nil { |
|
84 |
+ return nil, err |
|
85 |
+ } |
|
86 |
+ |
|
87 |
+ if r.rpcAddr == "" { |
|
88 |
+ r.rpcAddr = filepath.Join(stateDir, containerdSockFilename) |
|
89 |
+ } |
|
90 |
+ |
|
91 |
+ if r.startDaemon { |
|
92 |
+ if err := r.runContainerdDaemon(); err != nil { |
|
93 |
+ return nil, err |
|
94 |
+ } |
|
95 |
+ } |
|
96 |
+ |
|
97 |
+ // don't output the grpc reconnect logging |
|
98 |
+ grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags)) |
|
99 |
+ dialOpts := append([]grpc.DialOption{grpc.WithInsecure()}, |
|
100 |
+ grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { |
|
101 |
+ return net.DialTimeout("unix", addr, timeout) |
|
102 |
+ }), |
|
103 |
+ ) |
|
104 |
+ conn, err := grpc.Dial(r.rpcAddr, dialOpts...) |
|
105 |
+ if err != nil { |
|
106 |
+ return nil, fmt.Errorf("error connecting to containerd: %v", err) |
|
107 |
+ } |
|
108 |
+ |
|
109 |
+ r.rpcConn = conn |
|
110 |
+ r.apiClient = containerd.NewAPIClient(conn) |
|
111 |
+ |
|
112 |
+ // Get the timestamp to restore from |
|
113 |
+ t := r.getLastEventTimestamp() |
|
114 |
+ tsp, err := ptypes.TimestampProto(t) |
|
115 |
+ if err != nil { |
|
116 |
+ logrus.Errorf("libcontainerd: failed to convert timestamp: %q", err) |
|
117 |
+ } |
|
118 |
+ r.restoreFromTimestamp = tsp |
|
119 |
+ |
|
120 |
+ go r.handleConnectionChange() |
|
121 |
+ |
|
122 |
+ if err := r.startEventsMonitor(); err != nil { |
|
123 |
+ return nil, err |
|
124 |
+ } |
|
125 |
+ |
|
126 |
+ return r, nil |
|
127 |
+} |
|
128 |
+ |
|
129 |
+func (r *remote) UpdateOptions(options ...RemoteOption) error { |
|
130 |
+ for _, option := range options { |
|
131 |
+ if err := option.Apply(r); err != nil { |
|
132 |
+ return err |
|
133 |
+ } |
|
134 |
+ } |
|
135 |
+ return nil |
|
136 |
+} |
|
137 |
+ |
|
138 |
+func (r *remote) handleConnectionChange() { |
|
139 |
+ var transientFailureCount = 0 |
|
140 |
+ |
|
141 |
+ ticker := time.NewTicker(500 * time.Millisecond) |
|
142 |
+ defer ticker.Stop() |
|
143 |
+ healthClient := grpc_health_v1.NewHealthClient(r.rpcConn) |
|
144 |
+ |
|
145 |
+ for { |
|
146 |
+ <-ticker.C |
|
147 |
+ ctx, cancel := context.WithTimeout(context.Background(), containerdHealthCheckTimeout) |
|
148 |
+ _, err := healthClient.Check(ctx, &grpc_health_v1.HealthCheckRequest{}) |
|
149 |
+ cancel() |
|
150 |
+ if err == nil { |
|
151 |
+ continue |
|
152 |
+ } |
|
153 |
+ |
|
154 |
+ logrus.Debugf("libcontainerd: containerd health check returned error: %v", err) |
|
155 |
+ |
|
156 |
+ if r.daemonPid != -1 { |
|
157 |
+ if strings.Contains(err.Error(), "is closing") { |
|
158 |
+ // Well, we asked for it to stop, just return |
|
159 |
+ return |
|
160 |
+ } |
|
161 |
+ // all other errors are transient |
|
162 |
+ // Reset state to be notified of next failure |
|
163 |
+ transientFailureCount++ |
|
164 |
+ if transientFailureCount >= maxConnectionRetryCount { |
|
165 |
+ transientFailureCount = 0 |
|
166 |
+ if utils.IsProcessAlive(r.daemonPid) { |
|
167 |
+ utils.KillProcess(r.daemonPid) |
|
168 |
+ } |
|
169 |
+ <-r.daemonWaitCh |
|
170 |
+ if err := r.runContainerdDaemon(); err != nil { //FIXME: Handle error |
|
171 |
+ logrus.Errorf("libcontainerd: error restarting containerd: %v", err) |
|
172 |
+ } |
|
173 |
+ continue |
|
174 |
+ } |
|
175 |
+ } |
|
176 |
+ } |
|
177 |
+} |
|
178 |
+ |
|
179 |
+func (r *remote) Cleanup() { |
|
180 |
+ if r.daemonPid == -1 { |
|
181 |
+ return |
|
182 |
+ } |
|
183 |
+ r.closeManually = true |
|
184 |
+ r.rpcConn.Close() |
|
185 |
+ // Ask the daemon to quit |
|
186 |
+ syscall.Kill(r.daemonPid, syscall.SIGTERM) |
|
187 |
+ |
|
188 |
+ // Wait up to 15secs for it to stop |
|
189 |
+ for i := time.Duration(0); i < containerdShutdownTimeout; i += time.Second { |
|
190 |
+ if !utils.IsProcessAlive(r.daemonPid) { |
|
191 |
+ break |
|
192 |
+ } |
|
193 |
+ time.Sleep(time.Second) |
|
194 |
+ } |
|
195 |
+ |
|
196 |
+ if utils.IsProcessAlive(r.daemonPid) { |
|
197 |
+ logrus.Warnf("libcontainerd: containerd (%d) didn't stop within 15 secs, killing it\n", r.daemonPid) |
|
198 |
+ syscall.Kill(r.daemonPid, syscall.SIGKILL) |
|
199 |
+ } |
|
200 |
+ |
|
201 |
+ // cleanup some files |
|
202 |
+ os.Remove(filepath.Join(r.stateDir, containerdPidFilename)) |
|
203 |
+ os.Remove(filepath.Join(r.stateDir, containerdSockFilename)) |
|
204 |
+} |
|
205 |
+ |
|
206 |
+func (r *remote) Client(b Backend) (Client, error) { |
|
207 |
+ c := &client{ |
|
208 |
+ clientCommon: clientCommon{ |
|
209 |
+ backend: b, |
|
210 |
+ containers: make(map[string]*container), |
|
211 |
+ locker: locker.New(), |
|
212 |
+ }, |
|
213 |
+ remote: r, |
|
214 |
+ exitNotifiers: make(map[string]*exitNotifier), |
|
215 |
+ liveRestore: r.liveRestore, |
|
216 |
+ } |
|
217 |
+ |
|
218 |
+ r.Lock() |
|
219 |
+ r.clients = append(r.clients, c) |
|
220 |
+ r.Unlock() |
|
221 |
+ return c, nil |
|
222 |
+} |
|
223 |
+ |
|
224 |
+func (r *remote) updateEventTimestamp(t time.Time) { |
|
225 |
+ f, err := os.OpenFile(r.eventTsPath, syscall.O_CREAT|syscall.O_WRONLY|syscall.O_TRUNC, 0600) |
|
226 |
+ if err != nil { |
|
227 |
+ logrus.Warnf("libcontainerd: failed to open event timestamp file: %v", err) |
|
228 |
+ return |
|
229 |
+ } |
|
230 |
+ defer f.Close() |
|
231 |
+ |
|
232 |
+ b, err := t.MarshalText() |
|
233 |
+ if err != nil { |
|
234 |
+ logrus.Warnf("libcontainerd: failed to encode timestamp: %v", err) |
|
235 |
+ return |
|
236 |
+ } |
|
237 |
+ |
|
238 |
+ n, err := f.Write(b) |
|
239 |
+ if err != nil || n != len(b) { |
|
240 |
+ logrus.Warnf("libcontainerd: failed to update event timestamp file: %v", err) |
|
241 |
+ f.Truncate(0) |
|
242 |
+ return |
|
243 |
+ } |
|
244 |
+} |
|
245 |
+ |
|
246 |
+func (r *remote) getLastEventTimestamp() time.Time { |
|
247 |
+ t := time.Now() |
|
248 |
+ |
|
249 |
+ fi, err := os.Stat(r.eventTsPath) |
|
250 |
+ if os.IsNotExist(err) || fi.Size() == 0 { |
|
251 |
+ return t |
|
252 |
+ } |
|
253 |
+ |
|
254 |
+ f, err := os.Open(r.eventTsPath) |
|
255 |
+ if err != nil { |
|
256 |
+ logrus.Warnf("libcontainerd: Unable to access last event ts: %v", err) |
|
257 |
+ return t |
|
258 |
+ } |
|
259 |
+ defer f.Close() |
|
260 |
+ |
|
261 |
+ b := make([]byte, fi.Size()) |
|
262 |
+ n, err := f.Read(b) |
|
263 |
+ if err != nil || n != len(b) { |
|
264 |
+ logrus.Warnf("libcontainerd: Unable to read last event ts: %v", err) |
|
265 |
+ return t |
|
266 |
+ } |
|
267 |
+ |
|
268 |
+ t.UnmarshalText(b) |
|
269 |
+ |
|
270 |
+ return t |
|
271 |
+} |
|
272 |
+ |
|
273 |
+func (r *remote) startEventsMonitor() error { |
|
274 |
+ // First, get past events |
|
275 |
+ t := r.getLastEventTimestamp() |
|
276 |
+ tsp, err := ptypes.TimestampProto(t) |
|
277 |
+ if err != nil { |
|
278 |
+ logrus.Errorf("libcontainerd: failed to convert timestamp: %q", err) |
|
279 |
+ } |
|
280 |
+ er := &containerd.EventsRequest{ |
|
281 |
+ Timestamp: tsp, |
|
282 |
+ } |
|
283 |
+ events, err := r.apiClient.Events(context.Background(), er, grpc.FailFast(false)) |
|
284 |
+ if err != nil { |
|
285 |
+ return err |
|
286 |
+ } |
|
287 |
+ go r.handleEventStream(events) |
|
288 |
+ return nil |
|
289 |
+} |
|
290 |
+ |
|
291 |
+func (r *remote) handleEventStream(events containerd.API_EventsClient) { |
|
292 |
+ for { |
|
293 |
+ e, err := events.Recv() |
|
294 |
+ if err != nil { |
|
295 |
+ if grpc.ErrorDesc(err) == transport.ErrConnClosing.Desc && |
|
296 |
+ r.closeManually { |
|
297 |
+ // ignore error if grpc remote connection is closed manually |
|
298 |
+ return |
|
299 |
+ } |
|
300 |
+ logrus.Errorf("libcontainerd: failed to receive event from containerd: %v", err) |
|
301 |
+ go r.startEventsMonitor() |
|
302 |
+ return |
|
303 |
+ } |
|
304 |
+ |
|
305 |
+ logrus.Debugf("libcontainerd: received containerd event: %#v", e) |
|
306 |
+ |
|
307 |
+ var container *container |
|
308 |
+ var c *client |
|
309 |
+ r.RLock() |
|
310 |
+ for _, c = range r.clients { |
|
311 |
+ container, err = c.getContainer(e.Id) |
|
312 |
+ if err == nil { |
|
313 |
+ break |
|
314 |
+ } |
|
315 |
+ } |
|
316 |
+ r.RUnlock() |
|
317 |
+ if container == nil { |
|
318 |
+ logrus.Warnf("libcontainerd: unknown container %s", e.Id) |
|
319 |
+ continue |
|
320 |
+ } |
|
321 |
+ |
|
322 |
+ if err := container.handleEvent(e); err != nil { |
|
323 |
+ logrus.Errorf("libcontainerd: error processing state change for %s: %v", e.Id, err) |
|
324 |
+ } |
|
325 |
+ |
|
326 |
+ tsp, err := ptypes.Timestamp(e.Timestamp) |
|
327 |
+ if err != nil { |
|
328 |
+ logrus.Errorf("libcontainerd: failed to convert event timestamp: %q", err) |
|
329 |
+ continue |
|
330 |
+ } |
|
331 |
+ |
|
332 |
+ r.updateEventTimestamp(tsp) |
|
333 |
+ } |
|
334 |
+} |
|
335 |
+ |
|
336 |
+func (r *remote) runContainerdDaemon() error { |
|
337 |
+ pidFilename := filepath.Join(r.stateDir, containerdPidFilename) |
|
338 |
+ f, err := os.OpenFile(pidFilename, os.O_RDWR|os.O_CREATE, 0600) |
|
339 |
+ if err != nil { |
|
340 |
+ return err |
|
341 |
+ } |
|
342 |
+ defer f.Close() |
|
343 |
+ |
|
344 |
+ // File exist, check if the daemon is alive |
|
345 |
+ b := make([]byte, 8) |
|
346 |
+ n, err := f.Read(b) |
|
347 |
+ if err != nil && err != io.EOF { |
|
348 |
+ return err |
|
349 |
+ } |
|
350 |
+ |
|
351 |
+ if n > 0 { |
|
352 |
+ pid, err := strconv.ParseUint(string(b[:n]), 10, 64) |
|
353 |
+ if err != nil { |
|
354 |
+ return err |
|
355 |
+ } |
|
356 |
+ if utils.IsProcessAlive(int(pid)) { |
|
357 |
+ logrus.Infof("libcontainerd: previous instance of containerd still alive (%d)", pid) |
|
358 |
+ r.daemonPid = int(pid) |
|
359 |
+ return nil |
|
360 |
+ } |
|
361 |
+ } |
|
362 |
+ |
|
363 |
+ // rewind the file |
|
364 |
+ _, err = f.Seek(0, os.SEEK_SET) |
|
365 |
+ if err != nil { |
|
366 |
+ return err |
|
367 |
+ } |
|
368 |
+ |
|
369 |
+ // Truncate it |
|
370 |
+ err = f.Truncate(0) |
|
371 |
+ if err != nil { |
|
372 |
+ return err |
|
373 |
+ } |
|
374 |
+ |
|
375 |
+ // Start a new instance |
|
376 |
+ args := []string{ |
|
377 |
+ "-l", fmt.Sprintf("unix://%s", r.rpcAddr), |
|
378 |
+ "--metrics-interval=0", |
|
379 |
+ "--start-timeout", "2m", |
|
380 |
+ "--state-dir", filepath.Join(r.stateDir, containerdStateDir), |
|
381 |
+ } |
|
382 |
+ if goruntime.GOOS == "solaris" { |
|
383 |
+ args = append(args, "--shim", "containerd-shim", "--runtime", "runc") |
|
384 |
+ } else { |
|
385 |
+ args = append(args, "--shim", "docker-containerd-shim") |
|
386 |
+ if r.runtime != "" { |
|
387 |
+ args = append(args, "--runtime") |
|
388 |
+ args = append(args, r.runtime) |
|
389 |
+ } |
|
390 |
+ } |
|
391 |
+ if r.debugLog { |
|
392 |
+ args = append(args, "--debug") |
|
393 |
+ } |
|
394 |
+ if len(r.runtimeArgs) > 0 { |
|
395 |
+ for _, v := range r.runtimeArgs { |
|
396 |
+ args = append(args, "--runtime-args") |
|
397 |
+ args = append(args, v) |
|
398 |
+ } |
|
399 |
+ logrus.Debugf("libcontainerd: runContainerdDaemon: runtimeArgs: %s", args) |
|
400 |
+ } |
|
401 |
+ |
|
402 |
+ cmd := exec.Command(containerdBinary, args...) |
|
403 |
+ // redirect containerd logs to docker logs |
|
404 |
+ cmd.Stdout = os.Stdout |
|
405 |
+ cmd.Stderr = os.Stderr |
|
406 |
+ cmd.SysProcAttr = setSysProcAttr(true) |
|
407 |
+ cmd.Env = nil |
|
408 |
+ // clear the NOTIFY_SOCKET from the env when starting containerd |
|
409 |
+ for _, e := range os.Environ() { |
|
410 |
+ if !strings.HasPrefix(e, "NOTIFY_SOCKET") { |
|
411 |
+ cmd.Env = append(cmd.Env, e) |
|
412 |
+ } |
|
413 |
+ } |
|
414 |
+ if err := cmd.Start(); err != nil { |
|
415 |
+ return err |
|
416 |
+ } |
|
417 |
+ logrus.Infof("libcontainerd: new containerd process, pid: %d", cmd.Process.Pid) |
|
418 |
+ if err := setOOMScore(cmd.Process.Pid, r.oomScore); err != nil { |
|
419 |
+ utils.KillProcess(cmd.Process.Pid) |
|
420 |
+ return err |
|
421 |
+ } |
|
422 |
+ if _, err := f.WriteString(fmt.Sprintf("%d", cmd.Process.Pid)); err != nil { |
|
423 |
+ utils.KillProcess(cmd.Process.Pid) |
|
424 |
+ return err |
|
425 |
+ } |
|
426 |
+ |
|
427 |
+ r.daemonWaitCh = make(chan struct{}) |
|
428 |
+ go func() { |
|
429 |
+ cmd.Wait() |
|
430 |
+ close(r.daemonWaitCh) |
|
431 |
+ }() // Reap our child when needed |
|
432 |
+ r.daemonPid = cmd.Process.Pid |
|
433 |
+ return nil |
|
434 |
+} |
|
435 |
+ |
|
436 |
+// WithRemoteAddr sets the external containerd socket to connect to. |
|
437 |
+func WithRemoteAddr(addr string) RemoteOption { |
|
438 |
+ return rpcAddr(addr) |
|
439 |
+} |
|
440 |
+ |
|
441 |
+type rpcAddr string |
|
442 |
+ |
|
443 |
+func (a rpcAddr) Apply(r Remote) error { |
|
444 |
+ if remote, ok := r.(*remote); ok { |
|
445 |
+ remote.rpcAddr = string(a) |
|
446 |
+ return nil |
|
447 |
+ } |
|
448 |
+ return fmt.Errorf("WithRemoteAddr option not supported for this remote") |
|
449 |
+} |
|
450 |
+ |
|
451 |
+// WithRuntimePath sets the path of the runtime to be used as the |
|
452 |
+// default by containerd |
|
453 |
+func WithRuntimePath(rt string) RemoteOption { |
|
454 |
+ return runtimePath(rt) |
|
455 |
+} |
|
456 |
+ |
|
457 |
+type runtimePath string |
|
458 |
+ |
|
459 |
+func (rt runtimePath) Apply(r Remote) error { |
|
460 |
+ if remote, ok := r.(*remote); ok { |
|
461 |
+ remote.runtime = string(rt) |
|
462 |
+ return nil |
|
463 |
+ } |
|
464 |
+ return fmt.Errorf("WithRuntime option not supported for this remote") |
|
465 |
+} |
|
466 |
+ |
|
467 |
+// WithRuntimeArgs sets the list of runtime args passed to containerd |
|
468 |
+func WithRuntimeArgs(args []string) RemoteOption { |
|
469 |
+ return runtimeArgs(args) |
|
470 |
+} |
|
471 |
+ |
|
472 |
+type runtimeArgs []string |
|
473 |
+ |
|
474 |
+func (rt runtimeArgs) Apply(r Remote) error { |
|
475 |
+ if remote, ok := r.(*remote); ok { |
|
476 |
+ remote.runtimeArgs = rt |
|
477 |
+ return nil |
|
478 |
+ } |
|
479 |
+ return fmt.Errorf("WithRuntimeArgs option not supported for this remote") |
|
480 |
+} |
|
481 |
+ |
|
482 |
+// WithStartDaemon defines if libcontainerd should also run containerd daemon. |
|
483 |
+func WithStartDaemon(start bool) RemoteOption { |
|
484 |
+ return startDaemon(start) |
|
485 |
+} |
|
486 |
+ |
|
487 |
+type startDaemon bool |
|
488 |
+ |
|
489 |
+func (s startDaemon) Apply(r Remote) error { |
|
490 |
+ if remote, ok := r.(*remote); ok { |
|
491 |
+ remote.startDaemon = bool(s) |
|
492 |
+ return nil |
|
493 |
+ } |
|
494 |
+ return fmt.Errorf("WithStartDaemon option not supported for this remote") |
|
495 |
+} |
|
496 |
+ |
|
497 |
+// WithDebugLog defines if containerd debug logs will be enabled for daemon. |
|
498 |
+func WithDebugLog(debug bool) RemoteOption { |
|
499 |
+ return debugLog(debug) |
|
500 |
+} |
|
501 |
+ |
|
502 |
+type debugLog bool |
|
503 |
+ |
|
504 |
+func (d debugLog) Apply(r Remote) error { |
|
505 |
+ if remote, ok := r.(*remote); ok { |
|
506 |
+ remote.debugLog = bool(d) |
|
507 |
+ return nil |
|
508 |
+ } |
|
509 |
+ return fmt.Errorf("WithDebugLog option not supported for this remote") |
|
510 |
+} |
|
511 |
+ |
|
512 |
+// WithLiveRestore defines if containers are stopped on shutdown or restored. |
|
513 |
+func WithLiveRestore(v bool) RemoteOption { |
|
514 |
+ return liveRestore(v) |
|
515 |
+} |
|
516 |
+ |
|
517 |
+type liveRestore bool |
|
518 |
+ |
|
519 |
+func (l liveRestore) Apply(r Remote) error { |
|
520 |
+ if remote, ok := r.(*remote); ok { |
|
521 |
+ remote.liveRestore = bool(l) |
|
522 |
+ for _, c := range remote.clients { |
|
523 |
+ c.liveRestore = bool(l) |
|
524 |
+ } |
|
525 |
+ return nil |
|
526 |
+ } |
|
527 |
+ return fmt.Errorf("WithLiveRestore option not supported for this remote") |
|
528 |
+} |
|
529 |
+ |
|
530 |
+// WithOOMScore defines the oom_score_adj to set for the containerd process. |
|
531 |
+func WithOOMScore(score int) RemoteOption { |
|
532 |
+ return oomScore(score) |
|
533 |
+} |
|
534 |
+ |
|
535 |
+type oomScore int |
|
536 |
+ |
|
537 |
+func (o oomScore) Apply(r Remote) error { |
|
538 |
+ if remote, ok := r.(*remote); ok { |
|
539 |
+ remote.oomScore = int(o) |
|
540 |
+ return nil |
|
541 |
+ } |
|
542 |
+ return fmt.Errorf("WithOOMScore option not supported for this remote") |
|
543 |
+} |
... | ... |
@@ -1,11 +1,25 @@ |
1 | 1 |
package libcontainerd |
2 | 2 |
|
3 |
+import ( |
|
4 |
+ containerd "github.com/docker/containerd/api/grpc/types" |
|
5 |
+ "github.com/opencontainers/runtime-spec/specs-go" |
|
6 |
+) |
|
7 |
+ |
|
3 | 8 |
// Process contains information to start a specific application inside the container. |
4 | 9 |
type Process struct { |
5 | 10 |
// Terminal creates an interactive terminal for the container. |
6 | 11 |
Terminal bool `json:"terminal"` |
12 |
+ // User specifies user information for the process. |
|
13 |
+ User *specs.User `json:"user"` |
|
7 | 14 |
// Args specifies the binary and arguments for the application to execute. |
8 | 15 |
Args []string `json:"args"` |
16 |
+ // Env populates the process environment for the process. |
|
17 |
+ Env []string `json:"env,omitempty"` |
|
18 |
+ // Cwd is the current working directory for the process and must be |
|
19 |
+ // relative to the container's root. |
|
20 |
+ Cwd *string `json:"cwd"` |
|
21 |
+ // Capabilities are linux capabilities that are kept for the container. |
|
22 |
+ Capabilities []string `json:"capabilities,omitempty"` |
|
9 | 23 |
} |
10 | 24 |
|
11 | 25 |
// Stats contains a stats properties from containerd. |
... | ... |
@@ -19,7 +33,11 @@ type StateInfo struct { |
19 | 19 |
CommonStateInfo |
20 | 20 |
|
21 | 21 |
// Platform specific StateInfo |
22 |
+ OOMKilled bool |
|
22 | 23 |
} |
23 | 24 |
|
24 | 25 |
// Resources defines updatable container resource values. |
25 | 26 |
type Resources struct{} |
27 |
+ |
|
28 |
+// Checkpoints contains the details of a checkpoint |
|
29 |
+type Checkpoints containerd.ListCheckpointResponse |
... | ... |
@@ -1,6 +1,8 @@ |
1 | 1 |
package libcontainerd |
2 | 2 |
|
3 | 3 |
import ( |
4 |
+ "syscall" |
|
5 |
+ |
|
4 | 6 |
containerd "github.com/docker/containerd/api/grpc/types" |
5 | 7 |
"github.com/opencontainers/runtime-spec/specs-go" |
6 | 8 |
) |
... | ... |
@@ -50,3 +52,11 @@ func convertRlimits(sr []specs.Rlimit) (cr []*containerd.Rlimit) { |
50 | 50 |
} |
51 | 51 |
return |
52 | 52 |
} |
53 |
+ |
|
54 |
+// setPDeathSig sets the parent death signal to SIGKILL |
|
55 |
+func setSysProcAttr(sid bool) *syscall.SysProcAttr { |
|
56 |
+ return &syscall.SysProcAttr{ |
|
57 |
+ Setsid: sid, |
|
58 |
+ Pdeathsig: syscall.SIGKILL, |
|
59 |
+ } |
|
60 |
+} |
53 | 61 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,27 @@ |
0 |
+package libcontainerd |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "syscall" |
|
4 |
+ |
|
5 |
+ containerd "github.com/docker/containerd/api/grpc/types" |
|
6 |
+ "github.com/opencontainers/runtime-spec/specs-go" |
|
7 |
+) |
|
8 |
+ |
|
9 |
+func getRootIDs(s specs.Spec) (int, int, error) { |
|
10 |
+ return 0, 0, nil |
|
11 |
+} |
|
12 |
+ |
|
13 |
+func systemPid(ctr *containerd.Container) uint32 { |
|
14 |
+ var pid uint32 |
|
15 |
+ for _, p := range ctr.Processes { |
|
16 |
+ if p.Pid == InitFriendlyName { |
|
17 |
+ pid = p.SystemPid |
|
18 |
+ } |
|
19 |
+ } |
|
20 |
+ return pid |
|
21 |
+} |
|
22 |
+ |
|
23 |
+// setPDeathSig sets the parent death signal to SIGKILL |
|
24 |
+func setSysProcAttr(sid bool) *syscall.SysProcAttr { |
|
25 |
+ return nil |
|
26 |
+} |
... | ... |
@@ -1,11 +1,20 @@ |
1 | 1 |
package oci |
2 | 2 |
|
3 | 3 |
import ( |
4 |
+ "runtime" |
|
5 |
+ |
|
4 | 6 |
"github.com/opencontainers/runtime-spec/specs-go" |
5 | 7 |
) |
6 | 8 |
|
7 | 9 |
// DefaultSpec returns default oci spec used by docker. |
8 | 10 |
func DefaultSpec() specs.Spec { |
9 |
- s := specs.Spec{} |
|
11 |
+ s := specs.Spec{ |
|
12 |
+ Version: "0.6.0", |
|
13 |
+ Platform: specs.Platform{ |
|
14 |
+ OS: "SunOS", |
|
15 |
+ Arch: runtime.GOARCH, |
|
16 |
+ }, |
|
17 |
+ } |
|
18 |
+ s.Solaris = &specs.Solaris{} |
|
10 | 19 |
return s |
11 | 20 |
} |
... | ... |
@@ -67,7 +67,7 @@ func TestIsArchivePathDir(t *testing.T) { |
67 | 67 |
} |
68 | 68 |
|
69 | 69 |
func TestIsArchivePathInvalidFile(t *testing.T) { |
70 |
- cmd := exec.Command("sh", "-c", "dd if=/dev/zero bs=1K count=1 of=/tmp/archive && gzip --stdout /tmp/archive > /tmp/archive.gz") |
|
70 |
+ cmd := exec.Command("sh", "-c", "dd if=/dev/zero bs=1024 count=1 of=/tmp/archive && gzip --stdout /tmp/archive > /tmp/archive.gz") |
|
71 | 71 |
output, err := cmd.CombinedOutput() |
72 | 72 |
if err != nil { |
73 | 73 |
t.Fatalf("Fail to create an archive file for test : %s.", output) |
... | ... |
@@ -81,7 +81,14 @@ func TestIsArchivePathInvalidFile(t *testing.T) { |
81 | 81 |
} |
82 | 82 |
|
83 | 83 |
func TestIsArchivePathTar(t *testing.T) { |
84 |
- cmd := exec.Command("sh", "-c", "touch /tmp/archivedata && tar -cf /tmp/archive /tmp/archivedata && gzip --stdout /tmp/archive > /tmp/archive.gz") |
|
84 |
+ var whichTar string |
|
85 |
+ if runtime.GOOS == "solaris" { |
|
86 |
+ whichTar = "gtar" |
|
87 |
+ } else { |
|
88 |
+ whichTar = "tar" |
|
89 |
+ } |
|
90 |
+ cmdStr := fmt.Sprintf("touch /tmp/archivedata && %s -cf /tmp/archive /tmp/archivedata && gzip --stdout /tmp/archive > /tmp/archive.gz", whichTar) |
|
91 |
+ cmd := exec.Command("sh", "-c", cmdStr) |
|
85 | 92 |
output, err := cmd.CombinedOutput() |
86 | 93 |
if err != nil { |
87 | 94 |
t.Fatalf("Fail to create an archive file for test : %s.", output) |
... | ... |
@@ -8,6 +8,7 @@ import ( |
8 | 8 |
"io/ioutil" |
9 | 9 |
"os" |
10 | 10 |
"path/filepath" |
11 |
+ "runtime" |
|
11 | 12 |
"syscall" |
12 | 13 |
"testing" |
13 | 14 |
|
... | ... |
@@ -203,6 +204,9 @@ func TestTarWithBlockCharFifo(t *testing.T) { |
203 | 203 |
|
204 | 204 |
// TestTarUntarWithXattr is Unix as Lsetxattr is not supported on Windows |
205 | 205 |
func TestTarUntarWithXattr(t *testing.T) { |
206 |
+ if runtime.GOOS == "solaris" { |
|
207 |
+ t.Skip() |
|
208 |
+ } |
|
206 | 209 |
origin, err := ioutil.TempDir("", "docker-test-untar-origin") |
207 | 210 |
if err != nil { |
208 | 211 |
t.Fatal(err) |
... | ... |
@@ -7,11 +7,16 @@ import ( |
7 | 7 |
"io/ioutil" |
8 | 8 |
"os" |
9 | 9 |
"path" |
10 |
+ "runtime" |
|
10 | 11 |
"sort" |
11 | 12 |
"testing" |
12 | 13 |
) |
13 | 14 |
|
14 | 15 |
func TestHardLinkOrder(t *testing.T) { |
16 |
+ //TODO Should run for Solaris |
|
17 |
+ if runtime.GOOS == "solaris" { |
|
18 |
+ t.Skip("gcp failures on Solaris") |
|
19 |
+ } |
|
15 | 20 |
names := []string{"file1.txt", "file2.txt", "file3.txt"} |
16 | 21 |
msg := []byte("Hey y'all") |
17 | 22 |
|
... | ... |
@@ -22,6 +22,10 @@ func max(x, y int) int { |
22 | 22 |
|
23 | 23 |
func copyDir(src, dst string) error { |
24 | 24 |
cmd := exec.Command("cp", "-a", src, dst) |
25 |
+ if runtime.GOOS == "solaris" { |
|
26 |
+ cmd = exec.Command("gcp", "-a", src, dst) |
|
27 |
+ } |
|
28 |
+ |
|
25 | 29 |
if err := cmd.Run(); err != nil { |
26 | 30 |
return err |
27 | 31 |
} |
... | ... |
@@ -256,8 +260,9 @@ func TestChangesWithChangesGH13590(t *testing.T) { |
256 | 256 |
func TestChangesDirsEmpty(t *testing.T) { |
257 | 257 |
// TODO Windows. There may be a way of running this, but turning off for now |
258 | 258 |
// as createSampleDir uses symlinks. |
259 |
- if runtime.GOOS == "windows" { |
|
260 |
- t.Skip("symlinks on Windows") |
|
259 |
+ // TODO Should work for Solaris |
|
260 |
+ if runtime.GOOS == "windows" || runtime.GOOS == "solaris" { |
|
261 |
+ t.Skip("symlinks on Windows; gcp failure on Solaris") |
|
261 | 262 |
} |
262 | 263 |
src, err := ioutil.TempDir("", "docker-changes-test") |
263 | 264 |
if err != nil { |
... | ... |
@@ -364,8 +369,9 @@ func mutateSampleDir(t *testing.T, root string) { |
364 | 364 |
func TestChangesDirsMutated(t *testing.T) { |
365 | 365 |
// TODO Windows. There may be a way of running this, but turning off for now |
366 | 366 |
// as createSampleDir uses symlinks. |
367 |
- if runtime.GOOS == "windows" { |
|
368 |
- t.Skip("symlinks on Windows") |
|
367 |
+ // TODO Should work for Solaris |
|
368 |
+ if runtime.GOOS == "windows" || runtime.GOOS == "solaris" { |
|
369 |
+ t.Skip("symlinks on Windows; gcp failures on Solaris") |
|
369 | 370 |
} |
370 | 371 |
src, err := ioutil.TempDir("", "docker-changes-test") |
371 | 372 |
if err != nil { |
... | ... |
@@ -425,8 +431,9 @@ func TestChangesDirsMutated(t *testing.T) { |
425 | 425 |
func TestApplyLayer(t *testing.T) { |
426 | 426 |
// TODO Windows. There may be a way of running this, but turning off for now |
427 | 427 |
// as createSampleDir uses symlinks. |
428 |
- if runtime.GOOS == "windows" { |
|
429 |
- t.Skip("symlinks on Windows") |
|
428 |
+ // TODO Should work for Solaris |
|
429 |
+ if runtime.GOOS == "windows" || runtime.GOOS == "solaris" { |
|
430 |
+ t.Skip("symlinks on Windows; gcp failures on Solaris") |
|
430 | 431 |
} |
431 | 432 |
src, err := ioutil.TempDir("", "docker-changes-test") |
432 | 433 |
if err != nil { |
... | ... |
@@ -165,7 +165,7 @@ func TestChrootTarUntarWithSymlink(t *testing.T) { |
165 | 165 |
if err := system.MkdirAll(src, 0700); err != nil { |
166 | 166 |
t.Fatal(err) |
167 | 167 |
} |
168 |
- if _, err := prepareSourceDirectory(10, src, true); err != nil { |
|
168 |
+ if _, err := prepareSourceDirectory(10, src, false); err != nil { |
|
169 | 169 |
t.Fatal(err) |
170 | 170 |
} |
171 | 171 |
dest := filepath.Join(tmpdir, "dest") |
... | ... |
@@ -179,8 +179,8 @@ func TestChrootTarUntarWithSymlink(t *testing.T) { |
179 | 179 |
|
180 | 180 |
func TestChrootCopyWithTar(t *testing.T) { |
181 | 181 |
// TODO Windows: Figure out why this is failing |
182 |
- if runtime.GOOS == "windows" { |
|
183 |
- t.Skip("Failing on Windows") |
|
182 |
+ if runtime.GOOS == "windows" || runtime.GOOS == "solaris" { |
|
183 |
+ t.Skip("Failing on Windows and Solaris") |
|
184 | 184 |
} |
185 | 185 |
tmpdir, err := ioutil.TempDir("", "docker-TestChrootCopyWithTar") |
186 | 186 |
if err != nil { |
... | ... |
@@ -284,7 +284,7 @@ func TestChrootUntarPath(t *testing.T) { |
284 | 284 |
if err := system.MkdirAll(src, 0700); err != nil { |
285 | 285 |
t.Fatal(err) |
286 | 286 |
} |
287 |
- if _, err := prepareSourceDirectory(10, src, true); err != nil { |
|
287 |
+ if _, err := prepareSourceDirectory(10, src, false); err != nil { |
|
288 | 288 |
t.Fatal(err) |
289 | 289 |
} |
290 | 290 |
dest := filepath.Join(tmpdir, "dest") |
... | ... |
@@ -15,14 +15,20 @@ func TestRunCommand(t *testing.T) { |
15 | 15 |
t.Skip("Needs porting to Windows") |
16 | 16 |
} |
17 | 17 |
|
18 |
- result := RunCommand("ls") |
|
18 |
+ var cmd string |
|
19 |
+ if runtime.GOOS == "solaris" { |
|
20 |
+ cmd = "gls" |
|
21 |
+ } else { |
|
22 |
+ cmd = "ls" |
|
23 |
+ } |
|
24 |
+ result := RunCommand(cmd) |
|
19 | 25 |
result.Assert(t, Expected{}) |
20 | 26 |
|
21 | 27 |
result = RunCommand("doesnotexists") |
22 | 28 |
expectedError := `exec: "doesnotexists": executable file not found` |
23 | 29 |
result.Assert(t, Expected{ExitCode: 127, Error: expectedError}) |
24 | 30 |
|
25 |
- result = RunCommand("ls", "-z") |
|
31 |
+ result = RunCommand(cmd, "-z") |
|
26 | 32 |
result.Assert(t, Expected{ |
27 | 33 |
ExitCode: 2, |
28 | 34 |
Error: "exit status 2", |
... | ... |
@@ -90,11 +96,19 @@ func TestRunCommandWithStdoutStderrError(t *testing.T) { |
90 | 90 |
switch runtime.GOOS { |
91 | 91 |
case "windows": |
92 | 92 |
expected = "ls: unknown option" |
93 |
+ case "solaris": |
|
94 |
+ expected = "gls: invalid option" |
|
93 | 95 |
default: |
94 | 96 |
expected = "ls: invalid option" |
95 | 97 |
} |
96 | 98 |
|
97 |
- result = RunCommand("ls", "-z") |
|
99 |
+ var cmd string |
|
100 |
+ if runtime.GOOS == "solaris" { |
|
101 |
+ cmd = "gls" |
|
102 |
+ } else { |
|
103 |
+ cmd = "ls" |
|
104 |
+ } |
|
105 |
+ result = RunCommand(cmd, "-z") |
|
98 | 106 |
result.Assert(t, Expected{ |
99 | 107 |
Out: None, |
100 | 108 |
Err: expected, |
... | ... |
@@ -83,6 +83,10 @@ func TestRunCommandPipelineWithOutputErrors(t *testing.T) { |
83 | 83 |
} |
84 | 84 |
|
85 | 85 |
func TestRunCommandPipelineWithOutput(t *testing.T) { |
86 |
+ //TODO: Should run on Solaris |
|
87 |
+ if runtime.GOOS == "solaris" { |
|
88 |
+ t.Skip() |
|
89 |
+ } |
|
86 | 90 |
cmds := []*exec.Cmd{ |
87 | 91 |
// Print 2 characters |
88 | 92 |
exec.Command("echo", "-n", "11"), |
5 | 5 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,58 @@ |
0 |
+// +build solaris |
|
1 |
+ |
|
2 |
+package mount |
|
3 |
+ |
|
4 |
+// MakeShared ensures a mounted filesystem has the SHARED mount option enabled. |
|
5 |
+// See the supported options in flags.go for further reference. |
|
6 |
+func MakeShared(mountPoint string) error { |
|
7 |
+ return ensureMountedAs(mountPoint, "shared") |
|
8 |
+} |
|
9 |
+ |
|
10 |
+// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled. |
|
11 |
+// See the supported options in flags.go for further reference. |
|
12 |
+func MakeRShared(mountPoint string) error { |
|
13 |
+ return ensureMountedAs(mountPoint, "rshared") |
|
14 |
+} |
|
15 |
+ |
|
16 |
+// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled. |
|
17 |
+// See the supported options in flags.go for further reference. |
|
18 |
+func MakePrivate(mountPoint string) error { |
|
19 |
+ return ensureMountedAs(mountPoint, "private") |
|
20 |
+} |
|
21 |
+ |
|
22 |
+// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option |
|
23 |
+// enabled. See the supported options in flags.go for further reference. |
|
24 |
+func MakeRPrivate(mountPoint string) error { |
|
25 |
+ return ensureMountedAs(mountPoint, "rprivate") |
|
26 |
+} |
|
27 |
+ |
|
28 |
+// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled. |
|
29 |
+// See the supported options in flags.go for further reference. |
|
30 |
+func MakeSlave(mountPoint string) error { |
|
31 |
+ return ensureMountedAs(mountPoint, "slave") |
|
32 |
+} |
|
33 |
+ |
|
34 |
+// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled. |
|
35 |
+// See the supported options in flags.go for further reference. |
|
36 |
+func MakeRSlave(mountPoint string) error { |
|
37 |
+ return ensureMountedAs(mountPoint, "rslave") |
|
38 |
+} |
|
39 |
+ |
|
40 |
+// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option |
|
41 |
+// enabled. See the supported options in flags.go for further reference. |
|
42 |
+func MakeUnbindable(mountPoint string) error { |
|
43 |
+ return ensureMountedAs(mountPoint, "unbindable") |
|
44 |
+} |
|
45 |
+ |
|
46 |
+// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount |
|
47 |
+// option enabled. See the supported options in flags.go for further reference. |
|
48 |
+func MakeRUnbindable(mountPoint string) error { |
|
49 |
+ return ensureMountedAs(mountPoint, "runbindable") |
|
50 |
+} |
|
51 |
+ |
|
52 |
+func ensureMountedAs(mountPoint, options string) error { |
|
53 |
+ // TODO: Solaris does not support bind mounts. |
|
54 |
+ // Evaluate lofs and also look at the relevant |
|
55 |
+ // mount flags to be supported. |
|
56 |
+ return nil |
|
57 |
+} |
0 | 58 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,28 @@ |
0 |
+package plugin |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "fmt" |
|
4 |
+ |
|
5 |
+ "github.com/docker/docker/plugin/v2" |
|
6 |
+ specs "github.com/opencontainers/runtime-spec/specs-go" |
|
7 |
+) |
|
8 |
+ |
|
9 |
+func (pm *Manager) enable(p *v2.Plugin, force bool) error { |
|
10 |
+ return fmt.Errorf("Not implemented") |
|
11 |
+} |
|
12 |
+ |
|
13 |
+func (pm *Manager) initSpec(p *v2.Plugin) (*specs.Spec, error) { |
|
14 |
+ return nil, fmt.Errorf("Not implemented") |
|
15 |
+} |
|
16 |
+ |
|
17 |
+func (pm *Manager) disable(p *v2.Plugin) error { |
|
18 |
+ return fmt.Errorf("Not implemented") |
|
19 |
+} |
|
20 |
+ |
|
21 |
+func (pm *Manager) restore(p *v2.Plugin) error { |
|
22 |
+ return fmt.Errorf("Not implemented") |
|
23 |
+} |
|
24 |
+ |
|
25 |
+// Shutdown plugins |
|
26 |
+func (pm *Manager) Shutdown() { |
|
27 |
+} |
... | ... |
@@ -1,9 +1,6 @@ |
1 | 1 |
package runconfig |
2 | 2 |
|
3 | 3 |
import ( |
4 |
- "fmt" |
|
5 |
- "strings" |
|
6 |
- |
|
7 | 4 |
"github.com/docker/docker/api/types/container" |
8 | 5 |
"github.com/docker/docker/pkg/sysinfo" |
9 | 6 |
) |
... | ... |
@@ -11,7 +8,7 @@ import ( |
11 | 11 |
// DefaultDaemonNetworkMode returns the default network stack the daemon should |
12 | 12 |
// use. |
13 | 13 |
func DefaultDaemonNetworkMode() container.NetworkMode { |
14 |
- return container.NetworkMode("default") |
|
14 |
+ return container.NetworkMode("bridge") |
|
15 | 15 |
} |
16 | 16 |
|
17 | 17 |
// IsPreDefinedNetwork indicates if a network is predefined by the daemon |
... | ... |
@@ -23,15 +20,6 @@ func IsPreDefinedNetwork(network string) bool { |
23 | 23 |
// network settings are valid. |
24 | 24 |
func ValidateNetMode(c *container.Config, hc *container.HostConfig) error { |
25 | 25 |
// We may not be passed a host config, such as in the case of docker commit |
26 |
- if hc == nil { |
|
27 |
- return nil |
|
28 |
- } |
|
29 |
- parts := strings.Split(string(hc.NetworkMode), ":") |
|
30 |
- switch mode := parts[0]; mode { |
|
31 |
- case "default", "none": |
|
32 |
- default: |
|
33 |
- return fmt.Errorf("invalid --net: %s", hc.NetworkMode) |
|
34 |
- } |
|
35 | 26 |
return nil |
36 | 27 |
} |
37 | 28 |
|
... | ... |
@@ -164,10 +164,9 @@ func TestValidateName(t *testing.T) { |
164 | 164 |
} |
165 | 165 |
|
166 | 166 |
func TestCreateWithOpts(t *testing.T) { |
167 |
- if runtime.GOOS == "windows" { |
|
167 |
+ if runtime.GOOS == "windows" || runtime.GOOS == "solaris" { |
|
168 | 168 |
t.Skip() |
169 | 169 |
} |
170 |
- |
|
171 | 170 |
rootDir, err := ioutil.TempDir("", "local-volume-test") |
172 | 171 |
if err != nil { |
173 | 172 |
t.Fatal(err) |