Browse code

Add functional support for Docker sub commands on Solaris

Signed-off-by: Amit Krishnan <krish.amit@gmail.com>

Signed-off-by: Alexander Morozov <lk4d4@docker.com>

Amit Krishnan authored on 2016/06/07 16:45:21
Showing 83 changed files
... ...
@@ -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) {
14 16
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+package container
1
+
2
+import (
3
+	"golang.org/x/sys/unix"
4
+)
5
+
6
+func detachMounted(path string) error {
7
+	return unix.Unmount(path, unix.MNT_DETACH)
8
+}
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
 }
... ...
@@ -1,4 +1,4 @@
1
-// +build !windows
1
+// +build !windows,!solaris
2 2
 
3 3
 package main
4 4
 
5 5
new file mode 100644
... ...
@@ -0,0 +1,4 @@
0
+FROM solaris
1
+EXPOSE 80/tcp
2
+COPY httpserver .
3
+CMD ["./httpserver"]
... ...
@@ -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"
0 89
new file mode 100644
... ...
@@ -0,0 +1,5 @@
0
+// +build solaris
1
+
2
+package daemon
3
+
4
+const bindMountType = "lofs"
0 5
new file mode 100644
... ...
@@ -0,0 +1,5 @@
0
+// +build linux freebsd
1
+
2
+package daemon
3
+
4
+const bindMountType = "bind"
... ...
@@ -1,4 +1,4 @@
1
-// +build !linux
1
+// +build !linux,!solaris
2 2
 
3 3
 package cluster
4 4
 
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
 }
... ...
@@ -1,3 +1,5 @@
1
+// +build !solaris
2
+
1 3
 package daemon
2 4
 
3 5
 import (
... ...
@@ -1,4 +1,4 @@
1
-// +build !windows
1
+// +build !windows,!solaris
2 2
 
3 3
 package daemon
4 4
 
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) {
... ...
@@ -1,4 +1,4 @@
1
-// +build linux freebsd
1
+// +build linux freebsd solaris
2 2
 
3 3
 package graphtest
4 4
 
... ...
@@ -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
+}
... ...
@@ -56,6 +56,9 @@ case "$PACKAGE_ARCH" in
56 56
 			windows)
57 57
 				DOCKERFILE='Dockerfile.windows'
58 58
 				;;
59
+			solaris)
60
+				DOCKERFILE='Dockerfile.solaris'
61
+				;;
59 62
 		esac
60 63
 		;;
61 64
 	*)
... ...
@@ -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
-}
238 1
deleted file mode 100644
... ...
@@ -1,5 +0,0 @@
1
-package libcontainerd
2
-
3
-type container struct {
4
-	containerCommon
5
-}
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 31
new file mode 100644
... ...
@@ -0,0 +1,5 @@
0
+package libcontainerd
1
+
2
+func setOOMScore(pid, score int) error {
3
+	return nil
4
+}
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
-}
102 1
deleted file mode 100644
... ...
@@ -1,6 +0,0 @@
1
-package libcontainerd
2
-
3
-// process keeps the state for both main container process and exec process.
4
-type process struct {
5
-	processCommon
6
-}
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"),
... ...
@@ -1,4 +1,4 @@
1
-// +build !windows
1
+// +build !windows,!solaris
2 2
 
3 3
 package mount
4 4
 
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,3 +1,7 @@
1
+// +build !solaris
2
+
3
+// TODO: Support Solaris
4
+
1 5
 package registry
2 6
 
3 7
 import (
... ...
@@ -1,3 +1,5 @@
1
+// +build !solaris
2
+
1 3
 package registry
2 4
 
3 5
 import (
... ...
@@ -1,3 +1,5 @@
1
+// +build !solaris
2
+
1 3
 package registry
2 4
 
3 5
 import (
... ...
@@ -26,6 +26,11 @@ func TestDecodeContainerConfig(t *testing.T) {
26 26
 		image    string
27 27
 	)
28 28
 
29
+	//TODO: Should run for Solaris
30
+	if runtime.GOOS == "solaris" {
31
+		t.Skip()
32
+	}
33
+
29 34
 	if runtime.GOOS != "windows" {
30 35
 		image = "ubuntu"
31 36
 		fixtures = []f{
... ...
@@ -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
 
... ...
@@ -1,4 +1,4 @@
1
-// +build linux freebsd
1
+// +build linux freebsd solaris
2 2
 
3 3
 package utils
4 4
 
... ...
@@ -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)