From 934328d8ea650bf8a9c3c719999ce2a1f5dd5df6 Mon Sep 17 00:00:00 2001 From: Amit Krishnan Date: Tue, 7 Jun 2016 00:45:21 -0700 Subject: [PATCH] Add functional support for Docker sub commands on Solaris Signed-off-by: Amit Krishnan Signed-off-by: Alexander Morozov --- Dockerfile | 3 +- Dockerfile.solaris | 20 + cmd/dockerd/daemon_solaris.go | 5 + cmd/dockerd/daemon_unix_test.go | 8 +- container/container_linux.go | 9 + container/container_notlinux.go | 13 + container/container_solaris.go | 100 ----- container/container_unix.go | 10 +- container/memory_store.go | 7 +- contrib/docker-device-tool/device_tool.go | 2 +- contrib/httpserver/Dockerfile.solaris | 4 + contrib/mkimage.sh | 11 + contrib/mkimage/solaris | 89 +++++ daemon/bindmount_solaris.go | 5 + daemon/bindmount_unix.go | 5 + daemon/cluster/listen_addr_others.go | 2 +- daemon/cluster/listen_addr_solaris.go | 57 +++ daemon/commit.go | 6 +- daemon/config.go | 4 + daemon/config_common_unix.go | 80 ++++ daemon/config_solaris.go | 16 +- daemon/config_test.go | 4 + daemon/config_unix.go | 79 +--- daemon/container_operations_solaris.go | 28 +- daemon/container_operations_unix.go | 83 ---- daemon/create.go | 15 +- daemon/daemon_solaris.go | 370 +++++++++++++++++- daemon/daemon_test.go | 2 + daemon/daemon_unix_test.go | 2 +- daemon/getsize_unix.go | 41 ++ daemon/graphdriver/driver_solaris.go | 32 ++ .../graphdriver/graphtest/graphtest_unix.go | 2 +- daemon/inspect_solaris.go | 5 +- daemon/network.go | 14 +- daemon/oci_solaris.go | 169 ++++++++ daemon/start.go | 6 +- daemon/{start_linux.go => start_unix.go} | 2 + daemon/stats.go | 4 + daemon/volumes_unix.go | 51 +++ hack/make/.detect-daemon-osarch | 3 + hack/make/cross | 21 +- hack/make/test-unit | 33 +- hack/make/tgz | 3 + libcontainerd/client_linux.go | 127 ------ libcontainerd/client_solaris.go | 45 ++- libcontainerd/client_unix.go | 142 +++++++ libcontainerd/container_solaris.go | 5 - .../{container_linux.go => container_unix.go} | 2 + libcontainerd/oom_linux.go | 31 ++ libcontainerd/oom_solaris.go | 5 + ...emonitor_linux.go => pausemonitor_unix.go} | 2 + libcontainerd/process_solaris.go | 6 - .../{process_linux.go => process_unix.go} | 21 +- .../{queue_linux.go => queue_unix.go} | 2 + libcontainerd/remote_solaris.go | 34 -- .../{remote_linux.go => remote_unix.go} | 39 +- libcontainerd/types_solaris.go | 18 + libcontainerd/utils_linux.go | 10 + libcontainerd/utils_solaris.go | 27 ++ oci/defaults_solaris.go | 11 +- pkg/archive/archive_test.go | 11 +- pkg/archive/archive_unix_test.go | 4 + pkg/archive/changes_posix_test.go | 5 + pkg/archive/changes_test.go | 19 +- pkg/chrootarchive/archive_test.go | 8 +- pkg/integration/cmd/command_test.go | 20 +- pkg/integration/utils_test.go | 4 + pkg/mount/mount_unix_test.go | 2 +- pkg/mount/sharedsubtree_solaris.go | 58 +++ plugin/manager_solaris.go | 28 ++ registry/auth_test.go | 4 + registry/registry_mock_test.go | 2 + registry/registry_test.go | 2 + runconfig/config_test.go | 5 + runconfig/hostconfig_solaris.go | 14 +- utils/process_unix.go | 2 +- volume/local/local_test.go | 3 +- 77 files changed, 1572 insertions(+), 571 deletions(-) create mode 100644 Dockerfile.solaris create mode 100644 container/container_linux.go create mode 100644 container/container_notlinux.go delete mode 100644 container/container_solaris.go create mode 100644 contrib/httpserver/Dockerfile.solaris create mode 100755 contrib/mkimage/solaris create mode 100644 daemon/bindmount_solaris.go create mode 100644 daemon/bindmount_unix.go create mode 100644 daemon/cluster/listen_addr_solaris.go create mode 100644 daemon/config_common_unix.go create mode 100644 daemon/getsize_unix.go rename daemon/{start_linux.go => start_unix.go} (97%) create mode 100644 libcontainerd/client_unix.go delete mode 100644 libcontainerd/container_solaris.go rename libcontainerd/{container_linux.go => container_unix.go} (99%) create mode 100644 libcontainerd/oom_linux.go create mode 100644 libcontainerd/oom_solaris.go rename libcontainerd/{pausemonitor_linux.go => pausemonitor_unix.go} (97%) delete mode 100644 libcontainerd/process_solaris.go rename libcontainerd/{process_linux.go => process_unix.go} (70%) rename libcontainerd/{queue_linux.go => queue_unix.go} (93%) delete mode 100644 libcontainerd/remote_solaris.go rename libcontainerd/{remote_linux.go => remote_unix.go} (93%) create mode 100644 libcontainerd/utils_solaris.go create mode 100644 pkg/mount/sharedsubtree_solaris.go create mode 100644 plugin/manager_solaris.go diff --git a/Dockerfile b/Dockerfile index 5b6bbc318..6efabdfc5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -137,7 +137,8 @@ ENV DOCKER_CROSSPLATFORMS \ linux/386 linux/arm \ darwin/amd64 \ freebsd/amd64 freebsd/386 freebsd/arm \ - windows/amd64 windows/386 + windows/amd64 windows/386 \ + solaris/amd64 # Dependency for golint ENV GO_TOOLS_COMMIT 823804e1ae08dbb14eb807afc7db9993bc9e3cc3 diff --git a/Dockerfile.solaris b/Dockerfile.solaris new file mode 100644 index 000000000..bb342e5e6 --- /dev/null +++ b/Dockerfile.solaris @@ -0,0 +1,20 @@ +# Defines an image that hosts a native Docker build environment for Solaris +# TODO: Improve stub + +FROM solaris:latest + +# compile and runtime deps +RUN pkg install --accept \ + git \ + gnu-coreutils \ + gnu-make \ + gnu-tar \ + diagnostic/top \ + golang \ + library/golang/* \ + developer/gcc-* + +ENV GOPATH /go/:/usr/lib/gocode/1.5/ +ENV DOCKER_CROSSPLATFORMS solaris/amd64 +WORKDIR /go/src/github.com/docker/docker +COPY . /go/src/github.com/docker/docker diff --git a/cmd/dockerd/daemon_solaris.go b/cmd/dockerd/daemon_solaris.go index 70f3ca519..74dac5dd2 100644 --- a/cmd/dockerd/daemon_solaris.go +++ b/cmd/dockerd/daemon_solaris.go @@ -52,6 +52,11 @@ func notifySystem() { func (cli *DaemonCli) getPlatformRemoteOptions() []libcontainerd.RemoteOption { opts := []libcontainerd.RemoteOption{} + if cli.Config.ContainerdAddr != "" { + opts = append(opts, libcontainerd.WithRemoteAddr(cli.Config.ContainerdAddr)) + } else { + opts = append(opts, libcontainerd.WithStartDaemon(true)) + } return opts } diff --git a/cmd/dockerd/daemon_unix_test.go b/cmd/dockerd/daemon_unix_test.go index 28624a6e5..d66dba77e 100644 --- a/cmd/dockerd/daemon_unix_test.go +++ b/cmd/dockerd/daemon_unix_test.go @@ -1,13 +1,15 @@ -// +build !windows +// +build !windows,!solaris + +// TODO: Create new file for Solaris which tests config parameters +// as described in daemon/config_solaris.go package main import ( - "testing" - "github.com/docker/docker/daemon" "github.com/docker/docker/pkg/testutil/assert" "github.com/docker/docker/pkg/testutil/tempfile" + "testing" ) func TestLoadDaemonCliConfigWithDaemonFlags(t *testing.T) { diff --git a/container/container_linux.go b/container/container_linux.go new file mode 100644 index 000000000..4d4c16b56 --- /dev/null +++ b/container/container_linux.go @@ -0,0 +1,9 @@ +package container + +import ( + "golang.org/x/sys/unix" +) + +func detachMounted(path string) error { + return unix.Unmount(path, unix.MNT_DETACH) +} diff --git a/container/container_notlinux.go b/container/container_notlinux.go new file mode 100644 index 000000000..c9774fd5b --- /dev/null +++ b/container/container_notlinux.go @@ -0,0 +1,13 @@ +// +build solaris freebsd + +package container + +import ( + "golang.org/x/sys/unix" +) + +func detachMounted(path string) error { + //Solaris and FreeBSD do not support the lazy unmount or MNT_DETACH feature. + // Therefore there are separate definitions for this. + return unix.Unmount(path, 0) +} diff --git a/container/container_solaris.go b/container/container_solaris.go deleted file mode 100644 index 2abb9c2c8..000000000 --- a/container/container_solaris.go +++ /dev/null @@ -1,100 +0,0 @@ -// +build solaris - -package container - -import ( - "os" - "path/filepath" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/volume" -) - -// Container holds fields specific to the Solaris implementation. See -// CommonContainer for standard fields common to all containers. -type Container struct { - CommonContainer - - // fields below here are platform specific. - HostnamePath string - HostsPath string - ResolvConfPath string -} - -// ExitStatus provides exit reasons for a container. -type ExitStatus struct { - // The exit code with which the container exited. - ExitCode int -} - -// CreateDaemonEnvironment creates a new environment variable slice for this container. -func (container *Container) CreateDaemonEnvironment(_ bool, linkedEnv []string) []string { - return nil -} - -func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) { - return volumeMounts, nil -} - -// TrySetNetworkMount attempts to set the network mounts given a provided destination and -// the path to use for it; return true if the given destination was a network mount file -func (container *Container) TrySetNetworkMount(destination string, path string) bool { - return true -} - -// NetworkMounts returns the list of network mounts. -func (container *Container) NetworkMounts() []Mount { - var mount []Mount - return mount -} - -// CopyImagePathContent copies files in destination to the volume. -func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error { - return nil -} - -// UnmountIpcMounts unmount Ipc related mounts. -func (container *Container) UnmountIpcMounts(unmount func(pth string) error) { -} - -// IpcMounts returns the list of Ipc related mounts. -func (container *Container) IpcMounts() []Mount { - return nil -} - -// UpdateContainer updates configuration of a container -func (container *Container) UpdateContainer(hostConfig *container.HostConfig) error { - return nil -} - -// UnmountVolumes explicitly unmounts volumes from the container. -func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog func(name, action string, attributes map[string]string)) error { - return nil -} - -// TmpfsMounts returns the list of tmpfs mounts -func (container *Container) TmpfsMounts() []Mount { - var mounts []Mount - return mounts -} - -// cleanResourcePath cleans a resource path and prepares to combine with mnt path -func cleanResourcePath(path string) string { - return filepath.Join(string(os.PathSeparator), path) -} - -// BuildHostnameFile writes the container's hostname file. -func (container *Container) BuildHostnameFile() error { - return nil -} - -// canMountFS determines if the file system for the container -// can be mounted locally. A no-op on non-Windows platforms -func (container *Container) canMountFS() bool { - return true -} - -// EnableServiceDiscoveryOnDefaultNetwork Enable service discovery on default network -func (container *Container) EnableServiceDiscoveryOnDefaultNetwork() bool { - return false -} diff --git a/container/container_unix.go b/container/container_unix.go index 4bc38aec8..b62dce8be 100644 --- a/container/container_unix.go +++ b/container/container_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd +// +build linux freebsd solaris package container @@ -8,7 +8,6 @@ import ( "os" "path/filepath" "strings" - "syscall" "github.com/Sirupsen/logrus" containertypes "github.com/docker/docker/api/types/container" @@ -20,6 +19,7 @@ import ( "github.com/docker/docker/utils" "github.com/docker/docker/volume" "github.com/opencontainers/runc/libcontainer/label" + "golang.org/x/sys/unix" ) // 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 logrus.Warnf("error while unmounting volume %s: %v", v.Name(), err) } }() - if err := label.Relabel(path, container.MountLabel, true); err != nil && err != syscall.ENOTSUP { + if err := label.Relabel(path, container.MountLabel, true); err != nil && err != unix.ENOTSUP { return err } return copyExistingContents(rootfs, path) @@ -320,10 +320,6 @@ func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfi return nil } -func detachMounted(path string) error { - return syscall.Unmount(path, syscall.MNT_DETACH) -} - // UnmountVolumes unmounts all volumes func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog func(name, action string, attributes map[string]string)) error { var ( diff --git a/container/memory_store.go b/container/memory_store.go index c218f6340..706407a71 100644 --- a/container/memory_store.go +++ b/container/memory_store.go @@ -1,6 +1,8 @@ package container -import "sync" +import ( + "sync" +) // memoryStore implements a Store in memory. type memoryStore struct { @@ -25,8 +27,9 @@ func (c *memoryStore) Add(id string, cont *Container) { // Get returns a container from the store by id. func (c *memoryStore) Get(id string) *Container { + var res *Container c.RLock() - res := c.s[id] + res = c.s[id] c.RUnlock() return res } diff --git a/contrib/docker-device-tool/device_tool.go b/contrib/docker-device-tool/device_tool.go index 73e1bfdf5..906d064df 100644 --- a/contrib/docker-device-tool/device_tool.go +++ b/contrib/docker-device-tool/device_tool.go @@ -1,4 +1,4 @@ -// +build !windows +// +build !windows,!solaris package main diff --git a/contrib/httpserver/Dockerfile.solaris b/contrib/httpserver/Dockerfile.solaris new file mode 100644 index 000000000..3d0d691c1 --- /dev/null +++ b/contrib/httpserver/Dockerfile.solaris @@ -0,0 +1,4 @@ +FROM solaris +EXPOSE 80/tcp +COPY httpserver . +CMD ["./httpserver"] diff --git a/contrib/mkimage.sh b/contrib/mkimage.sh index 3976d72d9..13298c803 100755 --- a/contrib/mkimage.sh +++ b/contrib/mkimage.sh @@ -11,11 +11,22 @@ usage() { echo >&2 " $mkimg -t someuser/centos:5 rinse --distribution centos-5" echo >&2 " $mkimg -t someuser/mageia:4 mageia-urpmi --version=4" echo >&2 " $mkimg -t someuser/mageia:4 mageia-urpmi --version=4 --mirror=http://somemirror/" + echo >&2 " $mkimg -t someuser/solaris solaris" exit 1 } scriptDir="$(dirname "$(readlink -f "$BASH_SOURCE")")/mkimage" +os= +os=$(uname -o) + +# set up path to gnu tools if solaris +[[ $os == "Solaris" ]] && export PATH=/usr/gnu/bin:$PATH +# TODO check for gnu-tar, gnu-getopt + +# TODO requires root/sudo due to some pkg operations. sigh. +[[ $os == "Solaris" && $EUID != "0" ]] && echo >&2 "image create on Solaris requires superuser privilege" + optTemp=$(getopt --options '+d:t:c:hC' --longoptions 'dir:,tag:,compression:,no-compression,help' --name "$mkimg" -- "$@") eval set -- "$optTemp" unset optTemp diff --git a/contrib/mkimage/solaris b/contrib/mkimage/solaris new file mode 100755 index 000000000..158970e69 --- /dev/null +++ b/contrib/mkimage/solaris @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# +# Solaris 12 base image build script. +# +set -e + +# TODO add optional package publisher origin + +rootfsDir="$1" +shift + +# base install +( + set -x + + pkg image-create --full --zone \ + --facet facet.locale.*=false \ + --facet facet.locale.POSIX=true \ + --facet facet.doc=false \ + --facet facet.doc.*=false \ + "$rootfsDir" + + pkg -R "$rootfsDir" set-property use-system-repo true + + pkg -R "$rootfsDir" set-property flush-content-cache-on-success true + + pkg -R "$rootfsDir" install core-os +) + +# Lay in stock configuration, set up milestone +# XXX This all may become optional in a base image +( + # faster to build repository database on tmpfs + REPO_DB=/system/volatile/repository.$$ + export SVCCFG_REPOSITORY=${REPO_DB} + export SVCCFG_DOOR_PATH=$rootfsDir/system/volatile/tmp_repo_door + + # Import base manifests. NOTE These are a combination of basic requirement + # and gleaned from container milestone manifest. They may change. + for m in $rootfsDir/lib/svc/manifest/system/environment.xml \ + $rootfsDir/lib/svc/manifest/system/svc/global.xml \ + $rootfsDir/lib/svc/manifest/system/svc/restarter.xml \ + $rootfsDir/lib/svc/manifest/network/dns/client.xml \ + $rootfsDir/lib/svc/manifest/system/name-service/switch.xml \ + $rootfsDir/lib/svc/manifest/system/name-service/cache.xml \ + $rootfsDir/lib/svc/manifest/milestone/container.xml ; do + svccfg import $m + done + + # Apply system layer profile, deleting unnecessary dependencies + svccfg apply $rootfsDir/etc/svc/profile/generic_container.xml + + # XXX Even if we keep a repo in the base image, this is definitely optional + svccfg apply $rootfsDir/etc/svc/profile/sysconfig/container_sc.xml + + for s in svc:/system/svc/restarter \ + svc:/system/environment \ + svc:/network/dns/client \ + svc:/system/name-service/switch \ + svc:/system/name-service/cache \ + svc:/system/svc/global \ + svc:/milestone/container ;do + svccfg -s $s refresh + done + + # now copy the built up repository into the base rootfs + mv $REPO_DB $rootfsDir/etc/svc/repository.db +) + +# pkg(1) needs the zoneproxy-client running in the container. +# use a simple wrapper to run it as needed. +# XXX maybe we go back to running this in SMF? +mv "$rootfsDir/usr/bin/pkg" "$rootfsDir/usr/bin/wrapped_pkg" +cat > "$rootfsDir/usr/bin/pkg" <<-'EOF' +#!/bin/sh +# +# THIS FILE CREATED DURING DOCKER BASE IMAGE CREATION +# +# The Solaris base image uses the sysrepo proxy mechanism. The +# IPS client pkg(1) requires the zoneproxy-client to reach the +# remote publisher origins through the host. This wrapper script +# enables and disables the proxy client as needed. This is a +# temporary solution. + +/usr/lib/zones/zoneproxy-client -s localhost:1008 +PKG_SYSREPO_URL=http://localhost:1008 /usr/bin/wrapped_pkg "$@" +pkill -9 zoneproxy-client +EOF +chmod +x "$rootfsDir/usr/bin/pkg" diff --git a/daemon/bindmount_solaris.go b/daemon/bindmount_solaris.go new file mode 100644 index 000000000..87bf3ef72 --- /dev/null +++ b/daemon/bindmount_solaris.go @@ -0,0 +1,5 @@ +// +build solaris + +package daemon + +const bindMountType = "lofs" diff --git a/daemon/bindmount_unix.go b/daemon/bindmount_unix.go new file mode 100644 index 000000000..3966babb4 --- /dev/null +++ b/daemon/bindmount_unix.go @@ -0,0 +1,5 @@ +// +build linux freebsd + +package daemon + +const bindMountType = "bind" diff --git a/daemon/cluster/listen_addr_others.go b/daemon/cluster/listen_addr_others.go index ebf7daea2..4e845f5c8 100644 --- a/daemon/cluster/listen_addr_others.go +++ b/daemon/cluster/listen_addr_others.go @@ -1,4 +1,4 @@ -// +build !linux +// +build !linux,!solaris package cluster diff --git a/daemon/cluster/listen_addr_solaris.go b/daemon/cluster/listen_addr_solaris.go new file mode 100644 index 000000000..57a894b25 --- /dev/null +++ b/daemon/cluster/listen_addr_solaris.go @@ -0,0 +1,57 @@ +package cluster + +import ( + "bufio" + "fmt" + "net" + "os/exec" + "strings" +) + +func (c *Cluster) resolveSystemAddr() (net.IP, error) { + defRouteCmd := "/usr/sbin/ipadm show-addr -p -o addr " + + "`/usr/sbin/route get default | /usr/bin/grep interface | " + + "/usr/bin/awk '{print $2}'`" + out, err := exec.Command("/usr/bin/bash", "-c", defRouteCmd).Output() + if err != nil { + return nil, fmt.Errorf("cannot get default route: %v", err) + } + + defInterface := strings.SplitN(string(out), "/", 2) + defInterfaceIP := net.ParseIP(defInterface[0]) + + return defInterfaceIP, nil +} + +func listSystemIPs() []net.IP { + var systemAddrs []net.IP + cmd := exec.Command("/usr/sbin/ipadm", "show-addr", "-p", "-o", "addr") + cmdReader, err := cmd.StdoutPipe() + if err != nil { + return nil + } + + if err := cmd.Start(); err != nil { + return nil + } + + scanner := bufio.NewScanner(cmdReader) + go func() { + for scanner.Scan() { + text := scanner.Text() + nameAddrPair := strings.SplitN(text, "/", 2) + // Let go of loopback interfaces and docker interfaces + systemAddrs = append(systemAddrs, net.ParseIP(nameAddrPair[0])) + } + }() + + if err := scanner.Err(); err != nil { + fmt.Printf("scan underwent err: %+v\n", err) + } + + if err := cmd.Wait(); err != nil { + fmt.Printf("run command wait: %+v\n", err) + } + + return systemAddrs +} diff --git a/daemon/commit.go b/daemon/commit.go index 1e27ccc88..d11f011fb 100644 --- a/daemon/commit.go +++ b/daemon/commit.go @@ -126,9 +126,9 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str return "", err } - // It is not possible to commit a running container on Windows - if runtime.GOOS == "windows" && container.IsRunning() { - return "", fmt.Errorf("Windows does not support commit of a running container") + // It is not possible to commit a running container on Windows and on Solaris. + if (runtime.GOOS == "windows" || runtime.GOOS == "solaris") && container.IsRunning() { + return "", fmt.Errorf("%+v does not support commit of a running container", runtime.GOOS) } if c.Pause && !container.IsPaused() { diff --git a/daemon/config.go b/daemon/config.go index 114fc3edf..e686abdb0 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -3,6 +3,7 @@ package daemon import ( "bytes" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -221,6 +222,9 @@ func NewConfig() *Config { } func parseClusterAdvertiseSettings(clusterStore, clusterAdvertise string) (string, error) { + if runtime.GOOS == "solaris" && (clusterAdvertise != "" || clusterStore != "") { + return "", errors.New("Cluster Advertise Settings not supported on Solaris") + } if clusterAdvertise == "" { return "", errDiscoveryDisabled } diff --git a/daemon/config_common_unix.go b/daemon/config_common_unix.go new file mode 100644 index 000000000..e03a4f7e2 --- /dev/null +++ b/daemon/config_common_unix.go @@ -0,0 +1,80 @@ +// +build solaris linux freebsd + +package daemon + +import ( + "net" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/opts" + runconfigopts "github.com/docker/docker/runconfig/opts" + "github.com/spf13/pflag" +) + +// CommonUnixConfig defines configuration of a docker daemon that is +// common across Unix platforms. +type CommonUnixConfig struct { + ExecRoot string `json:"exec-root,omitempty"` + ContainerdAddr string `json:"containerd,omitempty"` + Runtimes map[string]types.Runtime `json:"runtimes,omitempty"` + DefaultRuntime string `json:"default-runtime,omitempty"` +} + +type commonUnixBridgeConfig struct { + DefaultIP net.IP `json:"ip,omitempty"` + IP string `json:"bip,omitempty"` + DefaultGatewayIPv4 net.IP `json:"default-gateway,omitempty"` + DefaultGatewayIPv6 net.IP `json:"default-gateway-v6,omitempty"` + InterContainerCommunication bool `json:"icc,omitempty"` +} + +// InstallCommonUnixFlags adds command-line options to the top-level flag parser for +// the current process that are common across Unix platforms. +func (config *Config) InstallCommonUnixFlags(flags *pflag.FlagSet) { + config.Runtimes = make(map[string]types.Runtime) + + flags.StringVarP(&config.SocketGroup, "group", "G", "docker", "Group for the unix socket") + flags.StringVar(&config.bridgeConfig.IP, "bip", "", "Specify network bridge IP") + flags.StringVarP(&config.bridgeConfig.Iface, "bridge", "b", "", "Attach containers to a network bridge") + flags.StringVar(&config.bridgeConfig.FixedCIDR, "fixed-cidr", "", "IPv4 subnet for fixed IPs") + flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultGatewayIPv4, ""), "default-gateway", "Container default gateway IPv4 address") + flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultGatewayIPv6, ""), "default-gateway-v6", "Container default gateway IPv6 address") + flags.BoolVar(&config.bridgeConfig.InterContainerCommunication, "icc", true, "Enable inter-container communication") + flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultIP, "0.0.0.0"), "ip", "Default IP when binding container ports") + flags.Var(runconfigopts.NewNamedRuntimeOpt("runtimes", &config.Runtimes, stockRuntimeName), "add-runtime", "Register an additional OCI compatible runtime") + flags.StringVar(&config.DefaultRuntime, "default-runtime", stockRuntimeName, "Default OCI runtime for containers") + +} + +// GetRuntime returns the runtime path and arguments for a given +// runtime name +func (config *Config) GetRuntime(name string) *types.Runtime { + config.reloadLock.Lock() + defer config.reloadLock.Unlock() + if rt, ok := config.Runtimes[name]; ok { + return &rt + } + return nil +} + +// GetDefaultRuntimeName returns the current default runtime +func (config *Config) GetDefaultRuntimeName() string { + config.reloadLock.Lock() + rt := config.DefaultRuntime + config.reloadLock.Unlock() + + return rt +} + +// GetAllRuntimes returns a copy of the runtimes map +func (config *Config) GetAllRuntimes() map[string]types.Runtime { + config.reloadLock.Lock() + rts := config.Runtimes + config.reloadLock.Unlock() + return rts +} + +// GetExecRoot returns the user configured Exec-root +func (config *Config) GetExecRoot() string { + return config.ExecRoot +} diff --git a/daemon/config_solaris.go b/daemon/config_solaris.go index e99d1bbf1..bc18ccd7e 100644 --- a/daemon/config_solaris.go +++ b/daemon/config_solaris.go @@ -5,7 +5,7 @@ import ( ) var ( - defaultPidFile = "/var/run/docker.pid" + defaultPidFile = "/system/volatile/docker/docker.pid" defaultGraph = "/var/lib/docker" defaultExec = "zones" ) @@ -16,14 +16,17 @@ var ( type Config struct { CommonConfig - // Fields below here are platform specific. - ExecRoot string `json:"exec-root,omitempty"` + // These fields are common to all unix platforms. + CommonUnixConfig } // bridgeConfig stores all the bridge driver specific // configuration. type bridgeConfig struct { commonBridgeConfig + + // Fields below here are platform specific. + commonUnixBridgeConfig } // InstallFlags adds command-line options to the top-level flag parser for @@ -32,14 +35,13 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) { // First handle install flags which are consistent cross-platform config.InstallCommonFlags(flags) + // Then install flags common to unix platforms + config.InstallCommonUnixFlags(flags) + // Then platform-specific install flags config.attachExperimentalFlags(flags) } -// GetExecRoot returns the user configured Exec-root -func (config *Config) GetExecRoot() string { - return config.ExecRoot -} func (config *Config) isSwarmCompatible() error { return nil } diff --git a/daemon/config_test.go b/daemon/config_test.go index beb797d47..90f6a1277 100644 --- a/daemon/config_test.go +++ b/daemon/config_test.go @@ -3,6 +3,7 @@ package daemon import ( "io/ioutil" "os" + "runtime" "strings" "testing" @@ -35,6 +36,9 @@ func TestDaemonBrokenConfiguration(t *testing.T) { } func TestParseClusterAdvertiseSettings(t *testing.T) { + if runtime.GOOS == "solaris" { + t.Skip("ClusterSettings not supported on Solaris\n") + } _, err := parseClusterAdvertiseSettings("something", "") if err != errDiscoveryDisabled { t.Fatalf("expected discovery disabled error, got %v\n", err) diff --git a/daemon/config_unix.go b/daemon/config_unix.go index 277cb66c2..25081ddbe 100644 --- a/daemon/config_unix.go +++ b/daemon/config_unix.go @@ -4,10 +4,7 @@ package daemon import ( "fmt" - "net" - "github.com/docker/docker/api/types" - "github.com/docker/docker/opts" runconfigopts "github.com/docker/docker/runconfig/opts" units "github.com/docker/go-units" "github.com/spf13/pflag" @@ -25,15 +22,14 @@ var ( type Config struct { CommonConfig + // These fields are common to all unix platforms. + CommonUnixConfig + // Fields below here are platform specific. CgroupParent string `json:"cgroup-parent,omitempty"` - ContainerdAddr string `json:"containerd,omitempty"` EnableSelinuxSupport bool `json:"selinux-enabled,omitempty"` - ExecRoot string `json:"exec-root,omitempty"` RemappedRoot string `json:"userns-remap,omitempty"` Ulimits map[string]*units.Ulimit `json:"default-ulimits,omitempty"` - Runtimes map[string]types.Runtime `json:"runtimes,omitempty"` - DefaultRuntime string `json:"default-runtime,omitempty"` CPURealtimePeriod int64 `json:"cpu-rt-period,omitempty"` CPURealtimeRuntime int64 `json:"cpu-rt-runtime,omitempty"` OOMScoreAdjust int `json:"oom-score-adjust,omitempty"` @@ -47,19 +43,17 @@ type Config struct { type bridgeConfig struct { commonBridgeConfig + // These fields are common to all unix platforms. + commonUnixBridgeConfig + // Fields below here are platform specific. - EnableIPv6 bool `json:"ipv6,omitempty"` - EnableIPTables bool `json:"iptables,omitempty"` - EnableIPForward bool `json:"ip-forward,omitempty"` - EnableIPMasq bool `json:"ip-masq,omitempty"` - EnableUserlandProxy bool `json:"userland-proxy,omitempty"` - UserlandProxyPath string `json:"userland-proxy-path,omitempty"` - DefaultIP net.IP `json:"ip,omitempty"` - IP string `json:"bip,omitempty"` - FixedCIDRv6 string `json:"fixed-cidr-v6,omitempty"` - DefaultGatewayIPv4 net.IP `json:"default-gateway,omitempty"` - DefaultGatewayIPv6 net.IP `json:"default-gateway-v6,omitempty"` - InterContainerCommunication bool `json:"icc,omitempty"` + EnableIPv6 bool `json:"ipv6,omitempty"` + EnableIPTables bool `json:"iptables,omitempty"` + EnableIPForward bool `json:"ip-forward,omitempty"` + EnableIPMasq bool `json:"ip-masq,omitempty"` + EnableUserlandProxy bool `json:"userland-proxy,omitempty"` + UserlandProxyPath string `json:"userland-proxy-path,omitempty"` + FixedCIDRv6 string `json:"fixed-cidr-v6,omitempty"` } // InstallFlags adds flags to the pflag.FlagSet to configure the daemon @@ -67,26 +61,20 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) { // First handle install flags which are consistent cross-platform config.InstallCommonFlags(flags) + // Then install flags common to unix platforms + config.InstallCommonUnixFlags(flags) + config.Ulimits = make(map[string]*units.Ulimit) - config.Runtimes = make(map[string]types.Runtime) // Then platform-specific install flags flags.BoolVar(&config.EnableSelinuxSupport, "selinux-enabled", false, "Enable selinux support") - flags.StringVarP(&config.SocketGroup, "group", "G", "docker", "Group for the unix socket") flags.Var(runconfigopts.NewUlimitOpt(&config.Ulimits), "default-ulimit", "Default ulimits for containers") flags.BoolVar(&config.bridgeConfig.EnableIPTables, "iptables", true, "Enable addition of iptables rules") flags.BoolVar(&config.bridgeConfig.EnableIPForward, "ip-forward", true, "Enable net.ipv4.ip_forward") flags.BoolVar(&config.bridgeConfig.EnableIPMasq, "ip-masq", true, "Enable IP masquerading") flags.BoolVar(&config.bridgeConfig.EnableIPv6, "ipv6", false, "Enable IPv6 networking") flags.StringVar(&config.ExecRoot, "exec-root", defaultExecRoot, "Root directory for execution state files") - flags.StringVar(&config.bridgeConfig.IP, "bip", "", "Specify network bridge IP") - flags.StringVarP(&config.bridgeConfig.Iface, "bridge", "b", "", "Attach containers to a network bridge") - flags.StringVar(&config.bridgeConfig.FixedCIDR, "fixed-cidr", "", "IPv4 subnet for fixed IPs") flags.StringVar(&config.bridgeConfig.FixedCIDRv6, "fixed-cidr-v6", "", "IPv6 subnet for fixed IPs") - flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultGatewayIPv4, ""), "default-gateway", "Container default gateway IPv4 address") - flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultGatewayIPv6, ""), "default-gateway-v6", "Container default gateway IPv6 address") - flags.BoolVar(&config.bridgeConfig.InterContainerCommunication, "icc", true, "Enable inter-container communication") - flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultIP, "0.0.0.0"), "ip", "Default IP when binding container ports") flags.BoolVar(&config.bridgeConfig.EnableUserlandProxy, "userland-proxy", true, "Use userland proxy for loopback traffic") flags.StringVar(&config.bridgeConfig.UserlandProxyPath, "userland-proxy-path", "", "Path to the userland proxy binary") 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) { flags.StringVar(&config.RemappedRoot, "userns-remap", "", "User/Group setting for user namespaces") flags.StringVar(&config.ContainerdAddr, "containerd", "", "Path to containerd socket") flags.BoolVar(&config.LiveRestoreEnabled, "live-restore", false, "Enable live restore of docker when containers are still running") - flags.Var(runconfigopts.NewNamedRuntimeOpt("runtimes", &config.Runtimes, stockRuntimeName), "add-runtime", "Register an additional OCI compatible runtime") - flags.StringVar(&config.DefaultRuntime, "default-runtime", stockRuntimeName, "Default OCI runtime for containers") flags.IntVar(&config.OOMScoreAdjust, "oom-score-adjust", -500, "Set the oom_score_adj for the daemon") flags.BoolVar(&config.Init, "init", false, "Run an init in the container to forward signals and reap processes") flags.StringVar(&config.InitPath, "init-path", "", "Path to the docker-init binary") @@ -107,39 +93,6 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) { config.attachExperimentalFlags(flags) } -// GetRuntime returns the runtime path and arguments for a given -// runtime name -func (config *Config) GetRuntime(name string) *types.Runtime { - config.reloadLock.Lock() - defer config.reloadLock.Unlock() - if rt, ok := config.Runtimes[name]; ok { - return &rt - } - return nil -} - -// GetDefaultRuntimeName returns the current default runtime -func (config *Config) GetDefaultRuntimeName() string { - config.reloadLock.Lock() - rt := config.DefaultRuntime - config.reloadLock.Unlock() - - return rt -} - -// GetAllRuntimes returns a copy of the runtimes map -func (config *Config) GetAllRuntimes() map[string]types.Runtime { - config.reloadLock.Lock() - rts := config.Runtimes - config.reloadLock.Unlock() - return rts -} - -// GetExecRoot returns the user configured Exec-root -func (config *Config) GetExecRoot() string { - return config.ExecRoot -} - func (config *Config) isSwarmCompatible() error { if config.ClusterStore != "" || config.ClusterAdvertise != "" { return fmt.Errorf("--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode") diff --git a/daemon/container_operations_solaris.go b/daemon/container_operations_solaris.go index dbc531fec..1653948de 100644 --- a/daemon/container_operations_solaris.go +++ b/daemon/container_operations_solaris.go @@ -2,25 +2,20 @@ package daemon -import "github.com/docker/docker/container" +import ( + "github.com/docker/docker/container" + "github.com/docker/docker/runconfig" + "github.com/docker/libnetwork" +) func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) { return nil, nil } -// getSize returns real size & virtual size -func (daemon *Daemon) getSize(container *container.Container) (int64, int64) { - return 0, 0 -} - func (daemon *Daemon) setupIpcDirs(container *container.Container) error { return nil } -func (daemon *Daemon) mountVolumes(container *container.Container) error { - return nil -} - func killProcessDirectly(container *container.Container) error { return nil } @@ -30,9 +25,22 @@ func detachMounted(path string) error { } func isLinkable(child *container.Container) bool { + // A container is linkable only if it belongs to the default network + _, ok := child.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()] + return ok +} + +func enableIPOnPredefinedNetwork() bool { return false } func (daemon *Daemon) isNetworkHotPluggable() bool { return false } + +func setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error { + return nil +} + +func initializeNetworkingPaths(container *container.Container, nc *container.Container) { +} diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go index b095828e0..66b11bb28 100644 --- a/daemon/container_operations_unix.go +++ b/daemon/container_operations_unix.go @@ -15,9 +15,7 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/daemon/links" - "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/runconfig" "github.com/docker/libnetwork" @@ -63,39 +61,6 @@ func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]s return env, nil } -// getSize returns the real size & virtual size of the container. -func (daemon *Daemon) getSize(container *container.Container) (int64, int64) { - var ( - sizeRw, sizeRootfs int64 - err error - ) - - if err := daemon.Mount(container); err != nil { - logrus.Errorf("Failed to compute size of container rootfs %s: %s", container.ID, err) - return sizeRw, sizeRootfs - } - defer daemon.Unmount(container) - - sizeRw, err = container.RWLayer.Size() - if err != nil { - logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", - daemon.GraphDriverName(), container.ID, err) - // FIXME: GetSize should return an error. Not changing it now in case - // there is a side-effect. - sizeRw = -1 - } - - if parent := container.RWLayer.Parent(); parent != nil { - sizeRootfs, err = parent.Size() - if err != nil { - sizeRootfs = -1 - } else if sizeRw != -1 { - sizeRootfs += sizeRw - } - } - return sizeRw, sizeRootfs -} - func (daemon *Daemon) getIpcContainer(container *container.Container) (*container.Container, error) { containerID := container.HostConfig.IpcMode.Container() c, err := daemon.GetContainer(containerID) @@ -174,54 +139,6 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error { return nil } - -func (daemon *Daemon) mountVolumes(container *container.Container) error { - mounts, err := daemon.setupMounts(container) - if err != nil { - return err - } - - for _, m := range mounts { - dest, err := container.GetResourcePath(m.Destination) - if err != nil { - return err - } - - var stat os.FileInfo - stat, err = os.Stat(m.Source) - if err != nil { - return err - } - if err = fileutils.CreateIfNotExists(dest, stat.IsDir()); err != nil { - return err - } - - opts := "rbind,ro" - if m.Writable { - opts = "rbind,rw" - } - - if err := mount.Mount(m.Source, dest, "bind", opts); err != nil { - return err - } - - // mountVolumes() seems to be called for temporary mounts - // outside the container. Soon these will be unmounted with - // lazy unmount option and given we have mounted the rbind, - // all the submounts will propagate if these are shared. If - // daemon is running in host namespace and has / as shared - // then these unmounts will propagate and unmount original - // mount as well. So make all these mounts rprivate. - // Do not use propagation property of volume as that should - // apply only when mounting happen inside the container. - if err := mount.MakeRPrivate(dest); err != nil { - return err - } - } - - return nil -} - func killProcessDirectly(container *container.Container) error { if _, err := container.WaitStop(10 * time.Second); err != nil { // Ensure that we don't kill ourselves diff --git a/daemon/create.go b/daemon/create.go index 4ec17c486..4483ebf04 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -3,11 +3,14 @@ package daemon import ( "fmt" "net" + "runtime" "strings" "time" + "github.com/pkg/errors" + "github.com/Sirupsen/logrus" - "github.com/docker/docker/api/errors" + apierrors "github.com/docker/docker/api/errors" "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" networktypes "github.com/docker/docker/api/types/network" @@ -78,6 +81,10 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) ( if err != nil { return nil, err } + + if runtime.GOOS == "solaris" && img.OS != "solaris " { + return nil, errors.New("Platform on which parent image was created is not Solaris") + } imgID = img.ID() } @@ -260,14 +267,14 @@ func (daemon *Daemon) verifyNetworkingConfig(nwConfig *networktypes.NetworkingCo for _, v := range nwConfig.EndpointsConfig { if v != nil && v.IPAMConfig != nil { if v.IPAMConfig.IPv4Address != "" && net.ParseIP(v.IPAMConfig.IPv4Address).To4() == nil { - return errors.NewBadRequestError(fmt.Errorf("invalid IPv4 address: %s", v.IPAMConfig.IPv4Address)) + return apierrors.NewBadRequestError(fmt.Errorf("invalid IPv4 address: %s", v.IPAMConfig.IPv4Address)) } if v.IPAMConfig.IPv6Address != "" { n := net.ParseIP(v.IPAMConfig.IPv6Address) // if the address is an invalid network address (ParseIP == nil) or if it is // an IPv4 address (To4() != nil), then it is an invalid IPv6 address if n == nil || n.To4() != nil { - return errors.NewBadRequestError(fmt.Errorf("invalid IPv6 address: %s", v.IPAMConfig.IPv6Address)) + return apierrors.NewBadRequestError(fmt.Errorf("invalid IPv6 address: %s", v.IPAMConfig.IPv6Address)) } } } @@ -279,5 +286,5 @@ func (daemon *Daemon) verifyNetworkingConfig(nwConfig *networktypes.NetworkingCo l = append(l, k) } err := fmt.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", ")) - return errors.NewBadRequestError(err) + return apierrors.NewBadRequestError(err) } diff --git a/daemon/daemon_solaris.go b/daemon/daemon_solaris.go index 9e3dd5314..21af812d4 100644 --- a/daemon/daemon_solaris.go +++ b/daemon/daemon_solaris.go @@ -4,7 +4,10 @@ package daemon import ( "fmt" + "net" + "strconv" + "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" @@ -12,9 +15,17 @@ import ( "github.com/docker/docker/layer" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/parsers/kernel" + "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/reference" "github.com/docker/libnetwork" nwconfig "github.com/docker/libnetwork/config" + "github.com/docker/libnetwork/drivers/solaris/bridge" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" + lntypes "github.com/docker/libnetwork/types" + "github.com/opencontainers/runc/libcontainer/label" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" ) //#include @@ -27,12 +38,50 @@ const ( solarisMaxCPUShares = 65535 ) +func getMemoryResources(config containertypes.Resources) specs.CappedMemory { + memory := specs.CappedMemory{} + + if config.Memory > 0 { + memory.Physical = strconv.FormatInt(config.Memory, 10) + } + + if config.MemorySwap != 0 { + memory.Swap = strconv.FormatInt(config.MemorySwap, 10) + } + + return memory +} + +func getCPUResources(config containertypes.Resources) specs.CappedCPU { + cpu := specs.CappedCPU{} + + if config.CpusetCpus != "" { + cpu.Ncpus = config.CpusetCpus + } + + return cpu +} + func (daemon *Daemon) cleanupMountsByID(id string) error { return nil } func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error { - return nil + //Since config.SecurityOpt is specifically defined as a "List of string values to + //customize labels for MLs systems, such as SELinux" + //until we figure out how to map to Trusted Extensions + //this is being disabled for now on Solaris + var ( + labelOpts []string + err error + ) + + if len(config.SecurityOpt) > 0 { + return errors.New("Security options are not supported on Solaris") + } + + container.ProcessLabel, container.MountLabel, err = label.InitLabels(labelOpts) + return err } func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) { @@ -67,13 +116,198 @@ func (daemon *Daemon) getCgroupDriver() string { } func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error { + if hostConfig.CPUShares < 0 { + logrus.Warnf("Changing requested CPUShares of %d to minimum allowed of %d", hostConfig.CPUShares, solarisMinCPUShares) + hostConfig.CPUShares = solarisMinCPUShares + } else if hostConfig.CPUShares > solarisMaxCPUShares { + logrus.Warnf("Changing requested CPUShares of %d to maximum allowed of %d", hostConfig.CPUShares, solarisMaxCPUShares) + hostConfig.CPUShares = solarisMaxCPUShares + } + + if hostConfig.Memory > 0 && hostConfig.MemorySwap == 0 { + // By default, MemorySwap is set to twice the size of Memory. + hostConfig.MemorySwap = hostConfig.Memory * 2 + } + + if hostConfig.ShmSize != 0 { + hostConfig.ShmSize = container.DefaultSHMSize + } + if hostConfig.OomKillDisable == nil { + defaultOomKillDisable := false + hostConfig.OomKillDisable = &defaultOomKillDisable + } + return nil } +// UsingSystemd returns true if cli option includes native.cgroupdriver=systemd +func UsingSystemd(config *Config) bool { + return false +} + // verifyPlatformContainerSettings performs platform-specific validation of the // hostconfig and config structures. func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { warnings := []string{} + sysInfo := sysinfo.New(true) + // NOTE: We do not enforce a minimum value for swap limits for zones on Solaris and + // therefore we will not do that for Docker container either. + if hostConfig.Memory > 0 && !sysInfo.MemoryLimit { + warnings = append(warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.") + logrus.Warnf("Your kernel does not support memory limit capabilities. Limitation discarded.") + hostConfig.Memory = 0 + hostConfig.MemorySwap = -1 + } + if hostConfig.Memory > 0 && hostConfig.MemorySwap != -1 && !sysInfo.SwapLimit { + warnings = append(warnings, "Your kernel does not support swap limit capabilities, memory limited without swap.") + logrus.Warnf("Your kernel does not support swap limit capabilities, memory limited without swap.") + hostConfig.MemorySwap = -1 + } + if hostConfig.Memory > 0 && hostConfig.MemorySwap > 0 && hostConfig.MemorySwap < hostConfig.Memory { + return warnings, fmt.Errorf("Minimum memoryswap limit should be larger than memory limit, see usage.") + } + // Solaris NOTE: We allow and encourage setting the swap without setting the memory limit. + + if hostConfig.MemorySwappiness != nil && *hostConfig.MemorySwappiness != -1 && !sysInfo.MemorySwappiness { + warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, memory swappiness discarded.") + logrus.Warnf("Your kernel does not support memory swappiness capabilities, memory swappiness discarded.") + hostConfig.MemorySwappiness = nil + } + if hostConfig.MemoryReservation > 0 && !sysInfo.MemoryReservation { + warnings = append(warnings, "Your kernel does not support memory soft limit capabilities. Limitation discarded.") + logrus.Warnf("Your kernel does not support memory soft limit capabilities. Limitation discarded.") + hostConfig.MemoryReservation = 0 + } + if hostConfig.Memory > 0 && hostConfig.MemoryReservation > 0 && hostConfig.Memory < hostConfig.MemoryReservation { + return warnings, fmt.Errorf("Minimum memory limit should be larger than memory reservation limit, see usage.") + } + if hostConfig.KernelMemory > 0 && !sysInfo.KernelMemory { + warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities. Limitation discarded.") + logrus.Warnf("Your kernel does not support kernel memory limit capabilities. Limitation discarded.") + hostConfig.KernelMemory = 0 + } + if hostConfig.CPUShares != 0 && !sysInfo.CPUShares { + warnings = append(warnings, "Your kernel does not support CPU shares. Shares discarded.") + logrus.Warnf("Your kernel does not support CPU shares. Shares discarded.") + hostConfig.CPUShares = 0 + } + if hostConfig.CPUShares < 0 { + warnings = append(warnings, "Invalid CPUShares value. Must be positive. Discarding.") + logrus.Warnf("Invalid CPUShares value. Must be positive. Discarding.") + hostConfig.CPUQuota = 0 + } + if hostConfig.CPUShares > 0 && !sysinfo.IsCPUSharesAvailable() { + warnings = append(warnings, "Global zone default scheduling class not FSS. Discarding shares.") + logrus.Warnf("Global zone default scheduling class not FSS. Discarding shares.") + hostConfig.CPUShares = 0 + } + + // Solaris NOTE: Linux does not do negative checking for CPUShares and Quota here. But it makes sense to. + if hostConfig.CPUPeriod > 0 && !sysInfo.CPUCfsPeriod { + warnings = append(warnings, "Your kernel does not support CPU cfs period. Period discarded.") + logrus.Warnf("Your kernel does not support CPU cfs period. Period discarded.") + if hostConfig.CPUQuota > 0 { + warnings = append(warnings, "Quota will be applied on default period, not period specified.") + logrus.Warnf("Quota will be applied on default period, not period specified.") + } + hostConfig.CPUPeriod = 0 + } + if hostConfig.CPUQuota != 0 && !sysInfo.CPUCfsQuota { + warnings = append(warnings, "Your kernel does not support CPU cfs quota. Quota discarded.") + logrus.Warnf("Your kernel does not support CPU cfs quota. Quota discarded.") + hostConfig.CPUQuota = 0 + } + if hostConfig.CPUQuota < 0 { + warnings = append(warnings, "Invalid CPUQuota value. Must be positive. Discarding.") + logrus.Warnf("Invalid CPUQuota value. Must be positive. Discarding.") + hostConfig.CPUQuota = 0 + } + if (hostConfig.CpusetCpus != "" || hostConfig.CpusetMems != "") && !sysInfo.Cpuset { + warnings = append(warnings, "Your kernel does not support cpuset. Cpuset discarded.") + logrus.Warnf("Your kernel does not support cpuset. Cpuset discarded.") + hostConfig.CpusetCpus = "" + hostConfig.CpusetMems = "" + } + cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(hostConfig.CpusetCpus) + if err != nil { + return warnings, fmt.Errorf("Invalid value %s for cpuset cpus.", hostConfig.CpusetCpus) + } + if !cpusAvailable { + return warnings, fmt.Errorf("Requested CPUs are not available - requested %s, available: %s.", hostConfig.CpusetCpus, sysInfo.Cpus) + } + memsAvailable, err := sysInfo.IsCpusetMemsAvailable(hostConfig.CpusetMems) + if err != nil { + return warnings, fmt.Errorf("Invalid value %s for cpuset mems.", hostConfig.CpusetMems) + } + if !memsAvailable { + return warnings, fmt.Errorf("Requested memory nodes are not available - requested %s, available: %s.", hostConfig.CpusetMems, sysInfo.Mems) + } + if hostConfig.BlkioWeight > 0 && !sysInfo.BlkioWeight { + warnings = append(warnings, "Your kernel does not support Block I/O weight. Weight discarded.") + logrus.Warnf("Your kernel does not support Block I/O weight. Weight discarded.") + hostConfig.BlkioWeight = 0 + } + if hostConfig.OomKillDisable != nil && !sysInfo.OomKillDisable { + *hostConfig.OomKillDisable = false + // Don't warn; this is the default setting but only applicable to Linux + } + + if sysInfo.IPv4ForwardingDisabled { + warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.") + logrus.Warnf("IPv4 forwarding is disabled. Networking will not work") + } + + // Solaris NOTE: We do not allow setting Linux specific options, so check and warn for all of them. + + if hostConfig.CapAdd != nil || hostConfig.CapDrop != nil { + warnings = append(warnings, "Adding or dropping kernel capabilities unsupported on Solaris.Discarding capabilities lists.") + logrus.Warnf("Adding or dropping kernel capabilities unsupported on Solaris.Discarding capabilities lists.") + hostConfig.CapAdd = nil + hostConfig.CapDrop = nil + } + + if hostConfig.GroupAdd != nil { + warnings = append(warnings, "Additional groups unsupported on Solaris.Discarding groups lists.") + logrus.Warnf("Additional groups unsupported on Solaris.Discarding groups lists.") + hostConfig.GroupAdd = nil + } + + if hostConfig.IpcMode != "" { + warnings = append(warnings, "IPC namespace assignment unsupported on Solaris.Discarding IPC setting.") + logrus.Warnf("IPC namespace assignment unsupported on Solaris.Discarding IPC setting.") + hostConfig.IpcMode = "" + } + + if hostConfig.PidMode != "" { + warnings = append(warnings, "PID namespace setting unsupported on Solaris. Running container in host PID namespace.") + logrus.Warnf("PID namespace setting unsupported on Solaris. Running container in host PID namespace.") + hostConfig.PidMode = "" + } + + if hostConfig.Privileged { + warnings = append(warnings, "Privileged mode unsupported on Solaris. Discarding privileged mode setting.") + logrus.Warnf("Privileged mode unsupported on Solaris. Discarding privileged mode setting.") + hostConfig.Privileged = false + } + + if hostConfig.UTSMode != "" { + warnings = append(warnings, "UTS namespace assignment unsupported on Solaris.Discarding UTS setting.") + logrus.Warnf("UTS namespace assignment unsupported on Solaris.Discarding UTS setting.") + hostConfig.UTSMode = "" + } + + if hostConfig.CgroupParent != "" { + warnings = append(warnings, "Specifying Cgroup parent unsupported on Solaris. Discarding cgroup parent setting.") + logrus.Warnf("Specifying Cgroup parent unsupported on Solaris. Discarding cgroup parent setting.") + hostConfig.CgroupParent = "" + } + + if hostConfig.Ulimits != nil { + warnings = append(warnings, "Specifying ulimits unsupported on Solaris. Discarding ulimits setting.") + logrus.Warnf("Specifying ulimits unsupported on Solaris. Discarding ulimits setting.") + hostConfig.Ulimits = nil + } + return warnings, nil } @@ -84,6 +318,16 @@ func (daemon *Daemon) platformReload(config *Config) map[string]string { // verifyDaemonSettings performs validation of daemon config struct func verifyDaemonSettings(config *Config) error { + + if config.DefaultRuntime == "" { + config.DefaultRuntime = stockRuntimeName + } + if config.Runtimes == nil { + config.Runtimes = make(map[string]types.Runtime) + } + stockRuntimeOpts := []string{} + config.Runtimes[stockRuntimeName] = types.Runtime{Path: DefaultRuntimeBinary, Args: stockRuntimeOpts} + // checkSystem validates platform-specific requirements return nil } @@ -119,7 +363,120 @@ func configureKernelSecuritySupport(config *Config, driverName string) error { } func (daemon *Daemon) initNetworkController(config *Config, activeSandboxes map[string]interface{}) (libnetwork.NetworkController, error) { - return nil, nil + netOptions, err := daemon.networkOptions(config, daemon.PluginStore, activeSandboxes) + if err != nil { + return nil, err + } + + controller, err := libnetwork.New(netOptions...) + if err != nil { + return nil, fmt.Errorf("error obtaining controller instance: %v", err) + } + + // Initialize default network on "null" + if _, err := controller.NewNetwork("null", "none", "", libnetwork.NetworkOptionPersist(false)); err != nil { + return nil, fmt.Errorf("Error creating default 'null' network: %v", err) + } + + if !config.DisableBridge { + // Initialize default driver "bridge" + if err := initBridgeDriver(controller, config); err != nil { + return nil, err + } + } + + return controller, nil +} + +func initBridgeDriver(controller libnetwork.NetworkController, config *Config) error { + if n, err := controller.NetworkByName("bridge"); err == nil { + if err = n.Delete(); err != nil { + return fmt.Errorf("could not delete the default bridge network: %v", err) + } + } + + bridgeName := bridge.DefaultBridgeName + if config.bridgeConfig.Iface != "" { + bridgeName = config.bridgeConfig.Iface + } + netOption := map[string]string{ + bridge.BridgeName: bridgeName, + bridge.DefaultBridge: strconv.FormatBool(true), + netlabel.DriverMTU: strconv.Itoa(config.Mtu), + bridge.EnableICC: strconv.FormatBool(config.bridgeConfig.InterContainerCommunication), + } + + // --ip processing + if config.bridgeConfig.DefaultIP != nil { + netOption[bridge.DefaultBindingIP] = config.bridgeConfig.DefaultIP.String() + } + + var ipamV4Conf *libnetwork.IpamConf + + ipamV4Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} + + nwList, _, err := netutils.ElectInterfaceAddresses(bridgeName) + if err != nil { + return errors.Wrap(err, "list bridge addresses failed") + } + + nw := nwList[0] + if len(nwList) > 1 && config.bridgeConfig.FixedCIDR != "" { + _, fCIDR, err := net.ParseCIDR(config.bridgeConfig.FixedCIDR) + if err != nil { + return errors.Wrap(err, "parse CIDR failed") + } + // Iterate through in case there are multiple addresses for the bridge + for _, entry := range nwList { + if fCIDR.Contains(entry.IP) { + nw = entry + break + } + } + } + + ipamV4Conf.PreferredPool = lntypes.GetIPNetCanonical(nw).String() + hip, _ := lntypes.GetHostPartIP(nw.IP, nw.Mask) + if hip.IsGlobalUnicast() { + ipamV4Conf.Gateway = nw.IP.String() + } + + if config.bridgeConfig.IP != "" { + ipamV4Conf.PreferredPool = config.bridgeConfig.IP + ip, _, err := net.ParseCIDR(config.bridgeConfig.IP) + if err != nil { + return err + } + ipamV4Conf.Gateway = ip.String() + } else if bridgeName == bridge.DefaultBridgeName && ipamV4Conf.PreferredPool != "" { + logrus.Infof("Default bridge (%s) is assigned with an IP address %s. Daemon option --bip can be used to set a preferred IP address", bridgeName, ipamV4Conf.PreferredPool) + } + + if config.bridgeConfig.FixedCIDR != "" { + _, fCIDR, err := net.ParseCIDR(config.bridgeConfig.FixedCIDR) + if err != nil { + return err + } + + ipamV4Conf.SubPool = fCIDR.String() + } + + if config.bridgeConfig.DefaultGatewayIPv4 != nil { + ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.bridgeConfig.DefaultGatewayIPv4.String() + } + + v4Conf := []*libnetwork.IpamConf{ipamV4Conf} + v6Conf := []*libnetwork.IpamConf{} + + // Initialize default network on "bridge" with the same name + _, err = controller.NewNetwork("bridge", "bridge", "", + libnetwork.NetworkOptionDriverOpts(netOption), + libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil), + libnetwork.NetworkOptionDeferIPv6Alloc(false)) + if err != nil { + return fmt.Errorf("Error creating default 'bridge' network: %v", err) + } + return nil } // registerLinks sets up links between containers and writes the @@ -135,7 +492,7 @@ func (daemon *Daemon) cleanupMounts() error { // conditionalMountOnStart is a platform specific helper function during the // container start to call mount. func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error { - return nil + return daemon.Mount(container) } // conditionalUnmountOnCleanup is a platform specific helper function called @@ -171,13 +528,6 @@ func setupDaemonProcess(config *Config) error { return nil } -// verifyVolumesInfo is a no-op on solaris. -// This is called during daemon initialization to migrate volumes from pre-1.7. -// Solaris was not supported on pre-1.7 daemons. -func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error { - return nil -} - func (daemon *Daemon) setupSeccompProfile() error { return nil } diff --git a/daemon/daemon_test.go b/daemon/daemon_test.go index 20cc784d6..00817bd1b 100644 --- a/daemon/daemon_test.go +++ b/daemon/daemon_test.go @@ -1,3 +1,5 @@ +// +build !solaris + package daemon import ( diff --git a/daemon/daemon_unix_test.go b/daemon/daemon_unix_test.go index 9766584b8..6250d359e 100644 --- a/daemon/daemon_unix_test.go +++ b/daemon/daemon_unix_test.go @@ -1,4 +1,4 @@ -// +build !windows +// +build !windows,!solaris package daemon diff --git a/daemon/getsize_unix.go b/daemon/getsize_unix.go new file mode 100644 index 000000000..707323a4b --- /dev/null +++ b/daemon/getsize_unix.go @@ -0,0 +1,41 @@ +// +build linux freebsd solaris + +package daemon + +import ( + "github.com/Sirupsen/logrus" + "github.com/docker/docker/container" +) + +// getSize returns the real size & virtual size of the container. +func (daemon *Daemon) getSize(container *container.Container) (int64, int64) { + var ( + sizeRw, sizeRootfs int64 + err error + ) + + if err := daemon.Mount(container); err != nil { + logrus.Errorf("Failed to compute size of container rootfs %s: %s", container.ID, err) + return sizeRw, sizeRootfs + } + defer daemon.Unmount(container) + + sizeRw, err = container.RWLayer.Size() + if err != nil { + logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", + daemon.GraphDriverName(), container.ID, err) + // FIXME: GetSize should return an error. Not changing it now in case + // there is a side-effect. + sizeRw = -1 + } + + if parent := container.RWLayer.Parent(); parent != nil { + sizeRootfs, err = parent.Size() + if err != nil { + sizeRootfs = -1 + } else if sizeRw != -1 { + sizeRootfs += sizeRw + } + } + return sizeRw, sizeRootfs +} diff --git a/daemon/graphdriver/driver_solaris.go b/daemon/graphdriver/driver_solaris.go index 61c4e2712..7daf01c32 100644 --- a/daemon/graphdriver/driver_solaris.go +++ b/daemon/graphdriver/driver_solaris.go @@ -20,6 +20,7 @@ import ( "unsafe" "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/mount" ) const ( @@ -44,6 +45,37 @@ func GetFSMagic(rootpath string) (FsMagic, error) { return 0, nil } +type fsChecker struct { + t FsMagic +} + +func (c *fsChecker) IsMounted(path string) bool { + m, _ := Mounted(c.t, path) + return m +} + +// NewFsChecker returns a checker configured for the provied FsMagic +func NewFsChecker(t FsMagic) Checker { + return &fsChecker{ + t: t, + } +} + +// NewDefaultChecker returns a check that parses /proc/mountinfo to check +// if the specified path is mounted. +// No-op on Solaris. +func NewDefaultChecker() Checker { + return &defaultChecker{} +} + +type defaultChecker struct { +} + +func (c *defaultChecker) IsMounted(path string) bool { + m, _ := mount.Mounted(path) + return m +} + // Mounted checks if the given path is mounted as the fs type //Solaris supports only ZFS for now func Mounted(fsType FsMagic, mountPath string) (bool, error) { diff --git a/daemon/graphdriver/graphtest/graphtest_unix.go b/daemon/graphdriver/graphtest/graphtest_unix.go index 70fba957f..0d44ae5a5 100644 --- a/daemon/graphdriver/graphtest/graphtest_unix.go +++ b/daemon/graphdriver/graphtest/graphtest_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd +// +build linux freebsd solaris package graphtest diff --git a/daemon/inspect_solaris.go b/daemon/inspect_solaris.go index 72e61d92c..0e3dcc111 100644 --- a/daemon/inspect_solaris.go +++ b/daemon/inspect_solaris.go @@ -3,6 +3,7 @@ package daemon import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/backend" + "github.com/docker/docker/api/types/versions/v1p19" "github.com/docker/docker/container" "github.com/docker/docker/daemon/exec" ) @@ -13,8 +14,8 @@ func setPlatformSpecificContainerFields(container *container.Container, contJSON } // containerInspectPre120 get containers for pre 1.20 APIs. -func (daemon *Daemon) containerInspectPre120(name string) (*types.ContainerJSON, error) { - return daemon.containerInspectCurrent(name, false) +func (daemon *Daemon) containerInspectPre120(name string) (*v1p19.ContainerJSON, error) { + return &v1p19.ContainerJSON{}, nil } func addMountPoints(container *container.Container) []types.MountPoint { diff --git a/daemon/network.go b/daemon/network.go index bde0bcdbf..3f9930e7b 100644 --- a/daemon/network.go +++ b/daemon/network.go @@ -3,17 +3,19 @@ package daemon import ( "fmt" "net" + "runtime" "sort" "strings" "github.com/Sirupsen/logrus" - "github.com/docker/docker/api/errors" + apierrors "github.com/docker/docker/api/errors" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/network" clustertypes "github.com/docker/docker/daemon/cluster/provider" "github.com/docker/docker/runconfig" "github.com/docker/libnetwork" networktypes "github.com/docker/libnetwork/types" + "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -236,7 +238,7 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string if runconfig.IsPreDefinedNetwork(create.Name) && !agent { err := fmt.Errorf("%s is a pre-defined network and cannot be created", create.Name) - return nil, errors.NewRequestForbiddenError(err) + return nil, apierrors.NewRequestForbiddenError(err) } var warning string @@ -336,6 +338,9 @@ func (daemon *Daemon) UpdateContainerServiceConfig(containerName string, service // network. If either cannot be found, an err is returned. If the // network cannot be set up, an err is returned. func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error { + if runtime.GOOS == "solaris" { + return errors.New("docker network connect is unsupported on Solaris platform") + } container, err := daemon.GetContainer(containerName) if err != nil { return err @@ -346,6 +351,9 @@ func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName strin // DisconnectContainerFromNetwork disconnects the given container from // the given network. If either cannot be found, an err is returned. func (daemon *Daemon) DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error { + if runtime.GOOS == "solaris" { + return errors.New("docker network disconnect is unsupported on Solaris platform") + } container, err := daemon.GetContainer(containerName) if err != nil { if force { @@ -401,7 +409,7 @@ func (daemon *Daemon) deleteNetwork(networkID string, dynamic bool) error { if runconfig.IsPreDefinedNetwork(nw.Name()) && !dynamic { err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name()) - return errors.NewRequestForbiddenError(err) + return apierrors.NewRequestForbiddenError(err) } if err := nw.Delete(); err != nil { diff --git a/daemon/oci_solaris.go b/daemon/oci_solaris.go index 76094e3da..0c757f919 100644 --- a/daemon/oci_solaris.go +++ b/daemon/oci_solaris.go @@ -1,14 +1,183 @@ package daemon import ( + "fmt" + "path/filepath" + "sort" + "strconv" + containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/oci" + "github.com/docker/libnetwork" "github.com/opencontainers/runtime-spec/specs-go" ) +func setResources(s *specs.Spec, r containertypes.Resources) error { + mem := getMemoryResources(r) + s.Solaris.CappedMemory = &mem + + capCPU := getCPUResources(r) + s.Solaris.CappedCPU = &capCPU + + return nil +} + +func setUser(s *specs.Spec, c *container.Container) error { + uid, gid, additionalGids, err := getUser(c, c.Config.User) + if err != nil { + return err + } + s.Process.User.UID = uid + s.Process.User.GID = gid + s.Process.User.AdditionalGids = additionalGids + return nil +} + +func getUser(c *container.Container, username string) (uint32, uint32, []uint32, error) { + return 0, 0, nil, nil +} + +func (daemon *Daemon) getRunzAnet(ep libnetwork.Endpoint) (specs.Anet, error) { + var ( + linkName string + lowerLink string + defRouter string + ) + + epInfo := ep.Info() + if epInfo == nil { + return specs.Anet{}, fmt.Errorf("invalid endpoint") + } + + nw, err := daemon.GetNetworkByName(ep.Network()) + if err != nil { + return specs.Anet{}, fmt.Errorf("Failed to get network %s: %v", ep.Network(), err) + } + + // Evaluate default router, linkname and lowerlink for interface endpoint + switch nw.Type() { + case "bridge": + defRouter = epInfo.Gateway().String() + linkName = "net0" // Should always be net0 for a container + + // TODO We construct lowerlink here exactly as done for solaris bridge + // initialization. Need modular code to reuse. + options := nw.Info().DriverOptions() + nwName := options["com.docker.network.bridge.name"] + lastChar := nwName[len(nwName)-1:] + if _, err = strconv.Atoi(lastChar); err != nil { + lowerLink = nwName + "_0" + } else { + lowerLink = nwName + } + + case "overlay": + defRouter = "" + linkName = "net1" + + // TODO Follows generateVxlanName() in solaris overlay. + id := nw.ID() + if len(nw.ID()) > 12 { + id = nw.ID()[:12] + } + lowerLink = "vx_" + id + "_0" + } + + runzanet := specs.Anet{ + Linkname: linkName, + Lowerlink: lowerLink, + Allowedaddr: epInfo.Iface().Address().String(), + Configallowedaddr: "true", + Defrouter: defRouter, + Linkprotection: "mac-nospoof, ip-nospoof", + Macaddress: epInfo.Iface().MacAddress().String(), + } + + return runzanet, nil +} + +func (daemon *Daemon) setNetworkInterface(s *specs.Spec, c *container.Container) error { + var anets []specs.Anet + + sb, err := daemon.netController.SandboxByID(c.NetworkSettings.SandboxID) + if err != nil { + return fmt.Errorf("Could not obtain sandbox for container") + } + + // Populate interfaces required for each endpoint + for _, ep := range sb.Endpoints() { + runzanet, err := daemon.getRunzAnet(ep) + if err != nil { + return fmt.Errorf("Failed to get interface information for endpoint %d: %v", ep.ID(), err) + } + anets = append(anets, runzanet) + } + + s.Solaris.Anet = anets + if anets != nil { + s.Solaris.Milestone = "svc:/milestone/container:default" + } + return nil +} + +func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container) error { + linkedEnv, err := daemon.setupLinkedContainers(c) + if err != nil { + return err + } + s.Root = specs.Root{ + Path: filepath.Dir(c.BaseFS), + Readonly: c.HostConfig.ReadonlyRootfs, + } + rootUID, rootGID := daemon.GetRemappedUIDGID() + if err := c.SetupWorkingDirectory(rootUID, rootGID); err != nil { + return err + } + cwd := c.Config.WorkingDir + s.Process.Args = append([]string{c.Path}, c.Args...) + s.Process.Cwd = cwd + s.Process.Env = c.CreateDaemonEnvironment(c.Config.Tty, linkedEnv) + s.Process.Terminal = c.Config.Tty + s.Hostname = c.FullHostname() + + return nil +} + func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { s := oci.DefaultSpec() + if err := daemon.populateCommonSpec(&s, c); err != nil { + return nil, err + } + + if err := setResources(&s, c.HostConfig.Resources); err != nil { + return nil, fmt.Errorf("runtime spec resources: %v", err) + } + + if err := setUser(&s, c); err != nil { + return nil, fmt.Errorf("spec user: %v", err) + } + + if err := daemon.setNetworkInterface(&s, c); err != nil { + return nil, err + } + + if err := daemon.setupIpcDirs(c); err != nil { + return nil, err + } + + ms, err := daemon.setupMounts(c) + if err != nil { + return nil, err + } + ms = append(ms, c.IpcMounts()...) + tmpfsMounts, err := c.TmpfsMounts() + if err != nil { + return nil, err + } + ms = append(ms, tmpfsMounts...) + sort.Sort(mounts(ms)) + return (*specs.Spec)(&s), nil } diff --git a/daemon/start.go b/daemon/start.go index 607609c8e..1b5f87604 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -11,7 +11,7 @@ import ( "google.golang.org/grpc" "github.com/Sirupsen/logrus" - "github.com/docker/docker/api/errors" + apierrors "github.com/docker/docker/api/errors" "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" @@ -21,7 +21,7 @@ import ( // ContainerStart starts a container. func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error { if checkpoint != "" && !daemon.HasExperimental() { - return errors.NewBadRequestError(fmt.Errorf("checkpoint is only supported in experimental mode")) + return apierrors.NewBadRequestError(fmt.Errorf("checkpoint is only supported in experimental mode")) } container, err := daemon.GetContainer(name) @@ -35,7 +35,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos if container.IsRunning() { err := fmt.Errorf("Container already started") - return errors.NewErrorWithStatusCode(err, http.StatusNotModified) + return apierrors.NewErrorWithStatusCode(err, http.StatusNotModified) } // Windows does not have the backwards compatibility issue here. diff --git a/daemon/start_linux.go b/daemon/start_unix.go similarity index 97% rename from daemon/start_linux.go rename to daemon/start_unix.go index e354d4003..6bbe48507 100644 --- a/daemon/start_linux.go +++ b/daemon/start_unix.go @@ -1,3 +1,5 @@ +// +build !windows + package daemon import ( diff --git a/daemon/stats.go b/daemon/stats.go index 2db685256..d1ce430aa 100644 --- a/daemon/stats.go +++ b/daemon/stats.go @@ -3,6 +3,7 @@ package daemon import ( "encoding/json" "errors" + "fmt" "runtime" "time" @@ -19,6 +20,9 @@ import ( // ContainerStats writes information about the container to the stream // given in the config object. func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, config *backend.ContainerStatsConfig) error { + if runtime.GOOS == "solaris" { + return fmt.Errorf("%+v does not support stats", runtime.GOOS) + } // Remote API version (used for backwards compatibility) apiVersion := config.Version diff --git a/daemon/volumes_unix.go b/daemon/volumes_unix.go index 8cecce2c0..61665e1e4 100644 --- a/daemon/volumes_unix.go +++ b/daemon/volumes_unix.go @@ -1,5 +1,7 @@ // +build !windows +// TODO(amitkris): We need to split this file for solaris. + package daemon import ( @@ -11,6 +13,8 @@ import ( "strings" "github.com/docker/docker/container" + "github.com/docker/docker/pkg/fileutils" + "github.com/docker/docker/pkg/mount" "github.com/docker/docker/volume" "github.com/docker/docker/volume/drivers" "github.com/docker/docker/volume/local" @@ -165,3 +169,50 @@ func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error { } return nil } + +func (daemon *Daemon) mountVolumes(container *container.Container) error { + mounts, err := daemon.setupMounts(container) + if err != nil { + return err + } + + for _, m := range mounts { + dest, err := container.GetResourcePath(m.Destination) + if err != nil { + return err + } + + var stat os.FileInfo + stat, err = os.Stat(m.Source) + if err != nil { + return err + } + if err = fileutils.CreateIfNotExists(dest, stat.IsDir()); err != nil { + return err + } + + opts := "rbind,ro" + if m.Writable { + opts = "rbind,rw" + } + + if err := mount.Mount(m.Source, dest, bindMountType, opts); err != nil { + return err + } + + // mountVolumes() seems to be called for temporary mounts + // outside the container. Soon these will be unmounted with + // lazy unmount option and given we have mounted the rbind, + // all the submounts will propagate if these are shared. If + // daemon is running in host namespace and has / as shared + // then these unmounts will propagate and unmount original + // mount as well. So make all these mounts rprivate. + // Do not use propagation property of volume as that should + // apply only when mounting happen inside the container. + if err := mount.MakeRPrivate(dest); err != nil { + return err + } + } + + return nil +} diff --git a/hack/make/.detect-daemon-osarch b/hack/make/.detect-daemon-osarch index 420b49968..73955392d 100644 --- a/hack/make/.detect-daemon-osarch +++ b/hack/make/.detect-daemon-osarch @@ -56,6 +56,9 @@ case "$PACKAGE_ARCH" in windows) DOCKERFILE='Dockerfile.windows' ;; + solaris) + DOCKERFILE='Dockerfile.solaris' + ;; esac ;; *) diff --git a/hack/make/cross b/hack/make/cross index a9118393f..6d672b17c 100644 --- a/hack/make/cross +++ b/hack/make/cross @@ -29,15 +29,18 @@ for platform in $DOCKER_CROSSPLATFORMS; do export GOOS=${platform%/*} export GOARCH=${platform##*/} - if [ -z "${daemonSupporting[$platform]}" ]; then - # we just need a simple client for these platforms - export LDFLAGS_STATIC_DOCKER="" - # remove the "daemon" build tag from platforms that aren't supported - export BUILDFLAGS=( "${ORIG_BUILDFLAGS[@]/ daemon/}" ) - source "${MAKEDIR}/binary-client" - else - source "${MAKEDIR}/binary-client" - source "${MAKEDIR}/binary-daemon" + if [ "$GOOS" != "solaris" ]; then + # TODO. Solaris cannot be cross build because of CGO calls. + if [ -z "${daemonSupporting[$platform]}" ]; then + # we just need a simple client for these platforms + export LDFLAGS_STATIC_DOCKER="" + # remove the "daemon" build tag from platforms that aren't supported + export BUILDFLAGS=( "${ORIG_BUILDFLAGS[@]/ daemon/}" ) + source "${MAKEDIR}/binary-client" + else + source "${MAKEDIR}/binary-client" + source "${MAKEDIR}/binary-daemon" + fi fi ) done diff --git a/hack/make/test-unit b/hack/make/test-unit index 482edaf42..f263345ce 100644 --- a/hack/make/test-unit +++ b/hack/make/test-unit @@ -25,15 +25,30 @@ bundle_test_unit() { else TEST_PATH=./${TESTDIRS} fi - pkg_list=$(go list -e \ - -f '{{if ne .Name "github.com/docker/docker"}} - {{.ImportPath}} - {{end}}' \ - "${BUILDFLAGS[@]}" $TEST_PATH \ - | grep github.com/docker/docker \ - | grep -v github.com/docker/docker/vendor \ - | grep -v github.com/docker/docker/man \ - | grep -v github.com/docker/docker/integration-cli) + + if [ "$(go env GOHOSTOS)" = 'solaris' ]; then + pkg_list=$(go list -e \ + -f '{{if ne .Name "github.com/docker/docker"}} + {{.ImportPath}} + {{end}}' \ + "${BUILDFLAGS[@]}" $TEST_PATH \ + | grep github.com/docker/docker \ + | grep -v github.com/docker/docker/vendor \ + | grep -v github.com/docker/docker/daemon/graphdriver \ + | grep -v github.com/docker/docker/man \ + | grep -v github.com/docker/docker/integration-cli) + else + pkg_list=$(go list -e \ + -f '{{if ne .Name "github.com/docker/docker"}} + {{.ImportPath}} + {{end}}' \ + "${BUILDFLAGS[@]}" $TEST_PATH \ + | grep github.com/docker/docker \ + | grep -v github.com/docker/docker/vendor \ + | grep -v github.com/docker/docker/man \ + | grep -v github.com/docker/docker/integration-cli) + fi + go test -cover -ldflags "$LDFLAGS" "${BUILDFLAGS[@]}" $TESTFLAGS $pkg_list } diff --git a/hack/make/tgz b/hack/make/tgz index 0b32aa589..3ccd93fa0 100644 --- a/hack/make/tgz +++ b/hack/make/tgz @@ -25,6 +25,9 @@ for d in "$CROSS/"*/*; do # if windows use a zip, not tgz BUNDLE_EXTENSION=".zip" IS_TAR="false" + elif [ "$GOOS" == "solaris" ]; then + # Solaris bypasses cross due to CGO issues. + continue else BUNDLE_EXTENSION=".tgz" IS_TAR="true" diff --git a/libcontainerd/client_linux.go b/libcontainerd/client_linux.go index 90e6d4cc2..9875d15f4 100644 --- a/libcontainerd/client_linux.go +++ b/libcontainerd/client_linux.go @@ -1,10 +1,8 @@ package libcontainerd import ( - "encoding/json" "fmt" "os" - "path/filepath" "strings" "sync" "syscall" @@ -12,7 +10,6 @@ import ( "github.com/Sirupsen/logrus" containerd "github.com/docker/containerd/api/grpc/types" - "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/mount" "github.com/golang/protobuf/ptypes" @@ -124,87 +121,6 @@ func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendly return int(resp.SystemPid), nil } -func (clnt *client) prepareBundleDir(uid, gid int) (string, error) { - root, err := filepath.Abs(clnt.remote.stateDir) - if err != nil { - return "", err - } - if uid == 0 && gid == 0 { - return root, nil - } - p := string(filepath.Separator) - for _, d := range strings.Split(root, string(filepath.Separator))[1:] { - p = filepath.Join(p, d) - fi, err := os.Stat(p) - if err != nil && !os.IsNotExist(err) { - return "", err - } - if os.IsNotExist(err) || fi.Mode()&1 == 0 { - p = fmt.Sprintf("%s.%d.%d", p, uid, gid) - if err := idtools.MkdirAs(p, 0700, uid, gid); err != nil && !os.IsExist(err) { - return "", err - } - } - } - return p, nil -} - -func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) (err error) { - clnt.lock(containerID) - defer clnt.unlock(containerID) - - if _, err := clnt.getContainer(containerID); err == nil { - return fmt.Errorf("Container %s is already active", containerID) - } - - uid, gid, err := getRootIDs(specs.Spec(spec)) - if err != nil { - return err - } - dir, err := clnt.prepareBundleDir(uid, gid) - if err != nil { - return err - } - - container := clnt.newContainer(filepath.Join(dir, containerID), options...) - if err := container.clean(); err != nil { - return err - } - - defer func() { - if err != nil { - container.clean() - clnt.deleteContainer(containerID) - } - }() - - if err := idtools.MkdirAllAs(container.dir, 0700, uid, gid); err != nil && !os.IsExist(err) { - return err - } - - f, err := os.Create(filepath.Join(container.dir, configFilename)) - if err != nil { - return err - } - defer f.Close() - if err := json.NewEncoder(f).Encode(spec); err != nil { - return err - } - - return container.start(checkpoint, checkpointDir, attachStdio) -} - -func (clnt *client) Signal(containerID string, sig int) error { - clnt.lock(containerID) - defer clnt.unlock(containerID) - _, err := clnt.remote.apiClient.Signal(context.Background(), &containerd.SignalRequest{ - Id: containerID, - Pid: InitFriendlyName, - Signal: uint32(sig), - }) - return err -} - func (clnt *client) SignalProcess(containerID string, pid string, sig int) error { clnt.lock(containerID) defer clnt.unlock(containerID) @@ -340,28 +256,6 @@ func (clnt *client) getContainerdContainer(containerID string) (*containerd.Cont return nil, fmt.Errorf("invalid state response") } -func (clnt *client) newContainer(dir string, options ...CreateOption) *container { - container := &container{ - containerCommon: containerCommon{ - process: process{ - dir: dir, - processCommon: processCommon{ - containerID: filepath.Base(dir), - client: clnt, - friendlyName: InitFriendlyName, - }, - }, - processes: make(map[string]*process), - }, - } - for _, option := range options { - if err := option.Apply(container); err != nil { - logrus.Errorf("libcontainerd: newContainer(): %v", err) - } - } - return container -} - func (clnt *client) UpdateResources(containerID string, resources Resources) error { clnt.lock(containerID) defer clnt.unlock(containerID) @@ -627,27 +521,6 @@ func (clnt *client) Restore(containerID string, attachStdio StdioCallback, optio return clnt.setExited(containerID, uint32(255)) } -type exitNotifier struct { - id string - client *client - c chan struct{} - once sync.Once -} - -func (en *exitNotifier) close() { - en.once.Do(func() { - close(en.c) - en.client.mapMutex.Lock() - if en == en.client.exitNotifiers[en.id] { - delete(en.client.exitNotifiers, en.id) - } - en.client.mapMutex.Unlock() - }) -} -func (en *exitNotifier) wait() <-chan struct{} { - return en.c -} - func (clnt *client) CreateCheckpoint(containerID string, checkpointID string, checkpointDir string, exit bool) error { clnt.lock(containerID) defer clnt.unlock(containerID) diff --git a/libcontainerd/client_solaris.go b/libcontainerd/client_solaris.go index df9106fab..8bfb04873 100644 --- a/libcontainerd/client_solaris.go +++ b/libcontainerd/client_solaris.go @@ -6,17 +6,17 @@ type client struct { clientCommon // Platform specific properties below here. + remote *remote + q queue + exitNotifiers map[string]*exitNotifier + liveRestore bool } -func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendlyName string, specp Process) error { - return nil +func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendlyName string, specp Process, attachStdio StdioCallback) (int, error) { + return -1, nil } -func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec Spec, options ...CreateOption) (err error) { - return nil -} - -func (clnt *client) Signal(containerID string, sig int) error { +func (clnt *client) SignalProcess(containerID string, pid string, sig int) error { return nil } @@ -36,8 +36,25 @@ func (clnt *client) Stats(containerID string) (*Stats, error) { return nil, nil } +func (clnt *client) getExitNotifier(containerID string) *exitNotifier { + clnt.mapMutex.RLock() + defer clnt.mapMutex.RUnlock() + return clnt.exitNotifiers[containerID] +} + +func (clnt *client) getOrCreateExitNotifier(containerID string) *exitNotifier { + clnt.mapMutex.Lock() + defer clnt.mapMutex.Unlock() + w, ok := clnt.exitNotifiers[containerID] + if !ok { + w = &exitNotifier{c: make(chan struct{}), client: clnt} + clnt.exitNotifiers[containerID] = w + } + return w +} + // Restore is the handler for restoring a container -func (clnt *client) Restore(containerID string, unusedOnWindows ...CreateOption) error { +func (clnt *client) Restore(containerID string, attachStdio StdioCallback, options ...CreateOption) error { return nil } @@ -56,3 +73,15 @@ func (clnt *client) UpdateResources(containerID string, resources Resources) err // but we should return nil for enabling updating container return nil } + +func (clnt *client) CreateCheckpoint(containerID string, checkpointID string, checkpointDir string, exit bool) error { + return nil +} + +func (clnt *client) DeleteCheckpoint(containerID string, checkpointID string, checkpointDir string) error { + return nil +} + +func (clnt *client) ListCheckpoints(containerID string, checkpointDir string) (*Checkpoints, error) { + return nil, nil +} diff --git a/libcontainerd/client_unix.go b/libcontainerd/client_unix.go new file mode 100644 index 000000000..21e8fea66 --- /dev/null +++ b/libcontainerd/client_unix.go @@ -0,0 +1,142 @@ +// +build linux solaris + +package libcontainerd + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + "sync" + + "github.com/Sirupsen/logrus" + containerd "github.com/docker/containerd/api/grpc/types" + "github.com/docker/docker/pkg/idtools" + specs "github.com/opencontainers/runtime-spec/specs-go" + "golang.org/x/net/context" +) + +func (clnt *client) prepareBundleDir(uid, gid int) (string, error) { + root, err := filepath.Abs(clnt.remote.stateDir) + if err != nil { + return "", err + } + if uid == 0 && gid == 0 { + return root, nil + } + p := string(filepath.Separator) + for _, d := range strings.Split(root, string(filepath.Separator))[1:] { + p = filepath.Join(p, d) + fi, err := os.Stat(p) + if err != nil && !os.IsNotExist(err) { + return "", err + } + if os.IsNotExist(err) || fi.Mode()&1 == 0 { + p = fmt.Sprintf("%s.%d.%d", p, uid, gid) + if err := idtools.MkdirAs(p, 0700, uid, gid); err != nil && !os.IsExist(err) { + return "", err + } + } + } + return p, nil +} + +func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) (err error) { + clnt.lock(containerID) + defer clnt.unlock(containerID) + + if _, err := clnt.getContainer(containerID); err == nil { + return fmt.Errorf("Container %s is already active", containerID) + } + + uid, gid, err := getRootIDs(specs.Spec(spec)) + if err != nil { + return err + } + dir, err := clnt.prepareBundleDir(uid, gid) + if err != nil { + return err + } + + container := clnt.newContainer(filepath.Join(dir, containerID), options...) + if err := container.clean(); err != nil { + return err + } + + defer func() { + if err != nil { + container.clean() + clnt.deleteContainer(containerID) + } + }() + + if err := idtools.MkdirAllAs(container.dir, 0700, uid, gid); err != nil && !os.IsExist(err) { + return err + } + + f, err := os.Create(filepath.Join(container.dir, configFilename)) + if err != nil { + return err + } + defer f.Close() + if err := json.NewEncoder(f).Encode(spec); err != nil { + return err + } + + return container.start(checkpoint, checkpointDir, attachStdio) +} + +func (clnt *client) Signal(containerID string, sig int) error { + clnt.lock(containerID) + defer clnt.unlock(containerID) + _, err := clnt.remote.apiClient.Signal(context.Background(), &containerd.SignalRequest{ + Id: containerID, + Pid: InitFriendlyName, + Signal: uint32(sig), + }) + return err +} + +func (clnt *client) newContainer(dir string, options ...CreateOption) *container { + container := &container{ + containerCommon: containerCommon{ + process: process{ + dir: dir, + processCommon: processCommon{ + containerID: filepath.Base(dir), + client: clnt, + friendlyName: InitFriendlyName, + }, + }, + processes: make(map[string]*process), + }, + } + for _, option := range options { + if err := option.Apply(container); err != nil { + logrus.Errorf("libcontainerd: newContainer(): %v", err) + } + } + return container +} + +type exitNotifier struct { + id string + client *client + c chan struct{} + once sync.Once +} + +func (en *exitNotifier) close() { + en.once.Do(func() { + close(en.c) + en.client.mapMutex.Lock() + if en == en.client.exitNotifiers[en.id] { + delete(en.client.exitNotifiers, en.id) + } + en.client.mapMutex.Unlock() + }) +} +func (en *exitNotifier) wait() <-chan struct{} { + return en.c +} diff --git a/libcontainerd/container_solaris.go b/libcontainerd/container_solaris.go deleted file mode 100644 index 24ab1de03..000000000 --- a/libcontainerd/container_solaris.go +++ /dev/null @@ -1,5 +0,0 @@ -package libcontainerd - -type container struct { - containerCommon -} diff --git a/libcontainerd/container_linux.go b/libcontainerd/container_unix.go similarity index 99% rename from libcontainerd/container_linux.go rename to libcontainerd/container_unix.go index 9610d3caa..6bb307d6a 100644 --- a/libcontainerd/container_linux.go +++ b/libcontainerd/container_unix.go @@ -1,3 +1,5 @@ +// +build linux solaris + package libcontainerd import ( diff --git a/libcontainerd/oom_linux.go b/libcontainerd/oom_linux.go new file mode 100644 index 000000000..e126b7a55 --- /dev/null +++ b/libcontainerd/oom_linux.go @@ -0,0 +1,31 @@ +package libcontainerd + +import ( + "fmt" + "os" + "strconv" + + "github.com/Sirupsen/logrus" + "github.com/opencontainers/runc/libcontainer/system" +) + +func setOOMScore(pid, score int) error { + oomScoreAdjPath := fmt.Sprintf("/proc/%d/oom_score_adj", pid) + f, err := os.OpenFile(oomScoreAdjPath, os.O_WRONLY, 0) + if err != nil { + return err + } + stringScore := strconv.Itoa(score) + _, err = f.WriteString(stringScore) + f.Close() + if os.IsPermission(err) { + // Setting oom_score_adj does not work in an + // unprivileged container. Ignore the error, but log + // it if we appear not to be in that situation. + if !system.RunningInUserNS() { + logrus.Debugf("Permission denied writing %q to %s", stringScore, oomScoreAdjPath) + } + return nil + } + return err +} diff --git a/libcontainerd/oom_solaris.go b/libcontainerd/oom_solaris.go new file mode 100644 index 000000000..2ebe5e87c --- /dev/null +++ b/libcontainerd/oom_solaris.go @@ -0,0 +1,5 @@ +package libcontainerd + +func setOOMScore(pid, score int) error { + return nil +} diff --git a/libcontainerd/pausemonitor_linux.go b/libcontainerd/pausemonitor_unix.go similarity index 97% rename from libcontainerd/pausemonitor_linux.go rename to libcontainerd/pausemonitor_unix.go index 80f5d025d..4f3766d95 100644 --- a/libcontainerd/pausemonitor_linux.go +++ b/libcontainerd/pausemonitor_unix.go @@ -1,3 +1,5 @@ +// +build !windows + package libcontainerd import ( diff --git a/libcontainerd/process_solaris.go b/libcontainerd/process_solaris.go deleted file mode 100644 index 2ee9b2566..000000000 --- a/libcontainerd/process_solaris.go +++ /dev/null @@ -1,6 +0,0 @@ -package libcontainerd - -// process keeps the state for both main container process and exec process. -type process struct { - processCommon -} diff --git a/libcontainerd/process_linux.go b/libcontainerd/process_unix.go similarity index 70% rename from libcontainerd/process_linux.go rename to libcontainerd/process_unix.go index 135a11e75..e3d8c5f34 100644 --- a/libcontainerd/process_linux.go +++ b/libcontainerd/process_unix.go @@ -1,3 +1,5 @@ +// +build linux solaris + package libcontainerd import ( @@ -5,18 +7,19 @@ import ( "io/ioutil" "os" "path/filepath" - "syscall" + goruntime "runtime" "time" containerd "github.com/docker/containerd/api/grpc/types" "github.com/tonistiigi/fifo" "golang.org/x/net/context" + "golang.org/x/sys/unix" ) var fdNames = map[int]string{ - syscall.Stdin: "stdin", - syscall.Stdout: "stdout", - syscall.Stderr: "stderr", + unix.Stdin: "stdin", + unix.Stdout: "stdout", + unix.Stderr: "stderr", } // process keeps the state for both main container process and exec process. @@ -36,7 +39,7 @@ func (p *process) openFifos(terminal bool) (pipe *IOPipe, err error) { io := &IOPipe{} - io.Stdin, err = fifo.OpenFifo(ctx, p.fifo(syscall.Stdin), syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700) + io.Stdin, err = fifo.OpenFifo(ctx, p.fifo(unix.Stdin), unix.O_WRONLY|unix.O_CREAT|unix.O_NONBLOCK, 0700) if err != nil { return nil, err } @@ -47,7 +50,7 @@ func (p *process) openFifos(terminal bool) (pipe *IOPipe, err error) { } }() - io.Stdout, err = fifo.OpenFifo(ctx, p.fifo(syscall.Stdout), syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700) + io.Stdout, err = fifo.OpenFifo(ctx, p.fifo(unix.Stdout), unix.O_RDONLY|unix.O_CREAT|unix.O_NONBLOCK, 0700) if err != nil { return nil, err } @@ -58,8 +61,10 @@ func (p *process) openFifos(terminal bool) (pipe *IOPipe, err error) { } }() - if !terminal { - io.Stderr, err = fifo.OpenFifo(ctx, p.fifo(syscall.Stderr), syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700) + if goruntime.GOOS == "solaris" || !terminal { + // For Solaris terminal handling is done exclusively by the runtime therefore we make no distinction + // in the processing for terminal and !terminal cases. + io.Stderr, err = fifo.OpenFifo(ctx, p.fifo(unix.Stderr), unix.O_RDONLY|unix.O_CREAT|unix.O_NONBLOCK, 0700) if err != nil { return nil, err } diff --git a/libcontainerd/queue_linux.go b/libcontainerd/queue_unix.go similarity index 93% rename from libcontainerd/queue_linux.go rename to libcontainerd/queue_unix.go index 34bc81d24..b848b9872 100644 --- a/libcontainerd/queue_linux.go +++ b/libcontainerd/queue_unix.go @@ -1,3 +1,5 @@ +// +build linux solaris + package libcontainerd import "sync" diff --git a/libcontainerd/remote_solaris.go b/libcontainerd/remote_solaris.go deleted file mode 100644 index e04f19288..000000000 --- a/libcontainerd/remote_solaris.go +++ /dev/null @@ -1,34 +0,0 @@ -package libcontainerd - -import "github.com/docker/docker/pkg/locker" - -type remote struct { -} - -func (r *remote) Client(b Backend) (Client, error) { - c := &client{ - clientCommon: clientCommon{ - backend: b, - containers: make(map[string]*container), - locker: locker.New(), - }, - } - return c, nil -} - -func (r *remote) Cleanup() { -} - -func (r *remote) UpdateOptions(opts ...RemoteOption) error { - return nil -} - -// New creates a fresh instance of libcontainerd remote. -func New(_ string, _ ...RemoteOption) (Remote, error) { - return &remote{}, nil -} - -// WithLiveRestore is a noop on solaris. -func WithLiveRestore(v bool) RemoteOption { - return nil -} diff --git a/libcontainerd/remote_linux.go b/libcontainerd/remote_unix.go similarity index 93% rename from libcontainerd/remote_linux.go rename to libcontainerd/remote_unix.go index e10e813ab..d064026d3 100644 --- a/libcontainerd/remote_linux.go +++ b/libcontainerd/remote_unix.go @@ -1,3 +1,5 @@ +// +build linux solaris + package libcontainerd import ( @@ -9,6 +11,7 @@ import ( "os" "os/exec" "path/filepath" + goruntime "runtime" "strconv" "strings" "sync" @@ -22,7 +25,6 @@ import ( "github.com/docker/docker/utils" "github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes/timestamp" - rsystem "github.com/opencontainers/runc/libcontainer/system" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/grpclog" @@ -374,14 +376,18 @@ func (r *remote) runContainerdDaemon() error { // Start a new instance args := []string{ "-l", fmt.Sprintf("unix://%s", r.rpcAddr), - "--shim", "docker-containerd-shim", "--metrics-interval=0", "--start-timeout", "2m", "--state-dir", filepath.Join(r.stateDir, containerdStateDir), } - if r.runtime != "" { - args = append(args, "--runtime") - args = append(args, r.runtime) + if goruntime.GOOS == "solaris" { + args = append(args, "--shim", "containerd-shim", "--runtime", "runc") + } else { + args = append(args, "--shim", "docker-containerd-shim") + if r.runtime != "" { + args = append(args, "--runtime") + args = append(args, r.runtime) + } } if r.debugLog { args = append(args, "--debug") @@ -398,7 +404,7 @@ func (r *remote) runContainerdDaemon() error { // redirect containerd logs to docker logs cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true, Pdeathsig: syscall.SIGKILL} + cmd.SysProcAttr = setSysProcAttr(true) cmd.Env = nil // clear the NOTIFY_SOCKET from the env when starting containerd for _, e := range os.Environ() { @@ -428,27 +434,6 @@ func (r *remote) runContainerdDaemon() error { return nil } -func setOOMScore(pid, score int) error { - oomScoreAdjPath := fmt.Sprintf("/proc/%d/oom_score_adj", pid) - f, err := os.OpenFile(oomScoreAdjPath, os.O_WRONLY, 0) - if err != nil { - return err - } - stringScore := strconv.Itoa(score) - _, err = f.WriteString(stringScore) - f.Close() - if os.IsPermission(err) { - // Setting oom_score_adj does not work in an - // unprivileged container. Ignore the error, but log - // it if we appear not to be in that situation. - if !rsystem.RunningInUserNS() { - logrus.Debugf("Permission denied writing %q to %s", stringScore, oomScoreAdjPath) - } - return nil - } - return err -} - // WithRemoteAddr sets the external containerd socket to connect to. func WithRemoteAddr(addr string) RemoteOption { return rpcAddr(addr) diff --git a/libcontainerd/types_solaris.go b/libcontainerd/types_solaris.go index b5954a8e7..dbafef669 100644 --- a/libcontainerd/types_solaris.go +++ b/libcontainerd/types_solaris.go @@ -1,11 +1,25 @@ package libcontainerd +import ( + containerd "github.com/docker/containerd/api/grpc/types" + "github.com/opencontainers/runtime-spec/specs-go" +) + // Process contains information to start a specific application inside the container. type Process struct { // Terminal creates an interactive terminal for the container. Terminal bool `json:"terminal"` + // User specifies user information for the process. + User *specs.User `json:"user"` // Args specifies the binary and arguments for the application to execute. Args []string `json:"args"` + // Env populates the process environment for the process. + Env []string `json:"env,omitempty"` + // Cwd is the current working directory for the process and must be + // relative to the container's root. + Cwd *string `json:"cwd"` + // Capabilities are linux capabilities that are kept for the container. + Capabilities []string `json:"capabilities,omitempty"` } // Stats contains a stats properties from containerd. @@ -19,7 +33,11 @@ type StateInfo struct { CommonStateInfo // Platform specific StateInfo + OOMKilled bool } // Resources defines updatable container resource values. type Resources struct{} + +// Checkpoints contains the details of a checkpoint +type Checkpoints containerd.ListCheckpointResponse diff --git a/libcontainerd/utils_linux.go b/libcontainerd/utils_linux.go index 1c1ced04a..78828bcda 100644 --- a/libcontainerd/utils_linux.go +++ b/libcontainerd/utils_linux.go @@ -1,6 +1,8 @@ package libcontainerd import ( + "syscall" + containerd "github.com/docker/containerd/api/grpc/types" "github.com/opencontainers/runtime-spec/specs-go" ) @@ -50,3 +52,11 @@ func convertRlimits(sr []specs.Rlimit) (cr []*containerd.Rlimit) { } return } + +// setPDeathSig sets the parent death signal to SIGKILL +func setSysProcAttr(sid bool) *syscall.SysProcAttr { + return &syscall.SysProcAttr{ + Setsid: sid, + Pdeathsig: syscall.SIGKILL, + } +} diff --git a/libcontainerd/utils_solaris.go b/libcontainerd/utils_solaris.go new file mode 100644 index 000000000..49632b45e --- /dev/null +++ b/libcontainerd/utils_solaris.go @@ -0,0 +1,27 @@ +package libcontainerd + +import ( + "syscall" + + containerd "github.com/docker/containerd/api/grpc/types" + "github.com/opencontainers/runtime-spec/specs-go" +) + +func getRootIDs(s specs.Spec) (int, int, error) { + return 0, 0, nil +} + +func systemPid(ctr *containerd.Container) uint32 { + var pid uint32 + for _, p := range ctr.Processes { + if p.Pid == InitFriendlyName { + pid = p.SystemPid + } + } + return pid +} + +// setPDeathSig sets the parent death signal to SIGKILL +func setSysProcAttr(sid bool) *syscall.SysProcAttr { + return nil +} diff --git a/oci/defaults_solaris.go b/oci/defaults_solaris.go index 24139585f..85c8b68e1 100644 --- a/oci/defaults_solaris.go +++ b/oci/defaults_solaris.go @@ -1,11 +1,20 @@ package oci import ( + "runtime" + "github.com/opencontainers/runtime-spec/specs-go" ) // DefaultSpec returns default oci spec used by docker. func DefaultSpec() specs.Spec { - s := specs.Spec{} + s := specs.Spec{ + Version: "0.6.0", + Platform: specs.Platform{ + OS: "SunOS", + Arch: runtime.GOARCH, + }, + } + s.Solaris = &specs.Solaris{} return s } diff --git a/pkg/archive/archive_test.go b/pkg/archive/archive_test.go index bd0d01e33..b883be33e 100644 --- a/pkg/archive/archive_test.go +++ b/pkg/archive/archive_test.go @@ -67,7 +67,7 @@ func TestIsArchivePathDir(t *testing.T) { } func TestIsArchivePathInvalidFile(t *testing.T) { - cmd := exec.Command("sh", "-c", "dd if=/dev/zero bs=1K count=1 of=/tmp/archive && gzip --stdout /tmp/archive > /tmp/archive.gz") + cmd := exec.Command("sh", "-c", "dd if=/dev/zero bs=1024 count=1 of=/tmp/archive && gzip --stdout /tmp/archive > /tmp/archive.gz") output, err := cmd.CombinedOutput() if err != nil { t.Fatalf("Fail to create an archive file for test : %s.", output) @@ -81,7 +81,14 @@ func TestIsArchivePathInvalidFile(t *testing.T) { } func TestIsArchivePathTar(t *testing.T) { - cmd := exec.Command("sh", "-c", "touch /tmp/archivedata && tar -cf /tmp/archive /tmp/archivedata && gzip --stdout /tmp/archive > /tmp/archive.gz") + var whichTar string + if runtime.GOOS == "solaris" { + whichTar = "gtar" + } else { + whichTar = "tar" + } + cmdStr := fmt.Sprintf("touch /tmp/archivedata && %s -cf /tmp/archive /tmp/archivedata && gzip --stdout /tmp/archive > /tmp/archive.gz", whichTar) + cmd := exec.Command("sh", "-c", cmdStr) output, err := cmd.CombinedOutput() if err != nil { t.Fatalf("Fail to create an archive file for test : %s.", output) diff --git a/pkg/archive/archive_unix_test.go b/pkg/archive/archive_unix_test.go index 548391b35..4eeafdd12 100644 --- a/pkg/archive/archive_unix_test.go +++ b/pkg/archive/archive_unix_test.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "path/filepath" + "runtime" "syscall" "testing" @@ -203,6 +204,9 @@ func TestTarWithBlockCharFifo(t *testing.T) { // TestTarUntarWithXattr is Unix as Lsetxattr is not supported on Windows func TestTarUntarWithXattr(t *testing.T) { + if runtime.GOOS == "solaris" { + t.Skip() + } origin, err := ioutil.TempDir("", "docker-test-untar-origin") if err != nil { t.Fatal(err) diff --git a/pkg/archive/changes_posix_test.go b/pkg/archive/changes_posix_test.go index 1a1837037..095102e57 100644 --- a/pkg/archive/changes_posix_test.go +++ b/pkg/archive/changes_posix_test.go @@ -7,11 +7,16 @@ import ( "io/ioutil" "os" "path" + "runtime" "sort" "testing" ) func TestHardLinkOrder(t *testing.T) { + //TODO Should run for Solaris + if runtime.GOOS == "solaris" { + t.Skip("gcp failures on Solaris") + } names := []string{"file1.txt", "file2.txt", "file3.txt"} msg := []byte("Hey y'all") diff --git a/pkg/archive/changes_test.go b/pkg/archive/changes_test.go index 8a2d0e8b1..eae1d022c 100644 --- a/pkg/archive/changes_test.go +++ b/pkg/archive/changes_test.go @@ -22,6 +22,10 @@ func max(x, y int) int { func copyDir(src, dst string) error { cmd := exec.Command("cp", "-a", src, dst) + if runtime.GOOS == "solaris" { + cmd = exec.Command("gcp", "-a", src, dst) + } + if err := cmd.Run(); err != nil { return err } @@ -256,8 +260,9 @@ func TestChangesWithChangesGH13590(t *testing.T) { func TestChangesDirsEmpty(t *testing.T) { // TODO Windows. There may be a way of running this, but turning off for now // as createSampleDir uses symlinks. - if runtime.GOOS == "windows" { - t.Skip("symlinks on Windows") + // TODO Should work for Solaris + if runtime.GOOS == "windows" || runtime.GOOS == "solaris" { + t.Skip("symlinks on Windows; gcp failure on Solaris") } src, err := ioutil.TempDir("", "docker-changes-test") if err != nil { @@ -364,8 +369,9 @@ func mutateSampleDir(t *testing.T, root string) { func TestChangesDirsMutated(t *testing.T) { // TODO Windows. There may be a way of running this, but turning off for now // as createSampleDir uses symlinks. - if runtime.GOOS == "windows" { - t.Skip("symlinks on Windows") + // TODO Should work for Solaris + if runtime.GOOS == "windows" || runtime.GOOS == "solaris" { + t.Skip("symlinks on Windows; gcp failures on Solaris") } src, err := ioutil.TempDir("", "docker-changes-test") if err != nil { @@ -425,8 +431,9 @@ func TestChangesDirsMutated(t *testing.T) { func TestApplyLayer(t *testing.T) { // TODO Windows. There may be a way of running this, but turning off for now // as createSampleDir uses symlinks. - if runtime.GOOS == "windows" { - t.Skip("symlinks on Windows") + // TODO Should work for Solaris + if runtime.GOOS == "windows" || runtime.GOOS == "solaris" { + t.Skip("symlinks on Windows; gcp failures on Solaris") } src, err := ioutil.TempDir("", "docker-changes-test") if err != nil { diff --git a/pkg/chrootarchive/archive_test.go b/pkg/chrootarchive/archive_test.go index 5fbe20843..d2d7e621f 100644 --- a/pkg/chrootarchive/archive_test.go +++ b/pkg/chrootarchive/archive_test.go @@ -165,7 +165,7 @@ func TestChrootTarUntarWithSymlink(t *testing.T) { if err := system.MkdirAll(src, 0700); err != nil { t.Fatal(err) } - if _, err := prepareSourceDirectory(10, src, true); err != nil { + if _, err := prepareSourceDirectory(10, src, false); err != nil { t.Fatal(err) } dest := filepath.Join(tmpdir, "dest") @@ -179,8 +179,8 @@ func TestChrootTarUntarWithSymlink(t *testing.T) { func TestChrootCopyWithTar(t *testing.T) { // TODO Windows: Figure out why this is failing - if runtime.GOOS == "windows" { - t.Skip("Failing on Windows") + if runtime.GOOS == "windows" || runtime.GOOS == "solaris" { + t.Skip("Failing on Windows and Solaris") } tmpdir, err := ioutil.TempDir("", "docker-TestChrootCopyWithTar") if err != nil { @@ -284,7 +284,7 @@ func TestChrootUntarPath(t *testing.T) { if err := system.MkdirAll(src, 0700); err != nil { t.Fatal(err) } - if _, err := prepareSourceDirectory(10, src, true); err != nil { + if _, err := prepareSourceDirectory(10, src, false); err != nil { t.Fatal(err) } dest := filepath.Join(tmpdir, "dest") diff --git a/pkg/integration/cmd/command_test.go b/pkg/integration/cmd/command_test.go index f28f5de7b..df2344207 100644 --- a/pkg/integration/cmd/command_test.go +++ b/pkg/integration/cmd/command_test.go @@ -15,14 +15,20 @@ func TestRunCommand(t *testing.T) { t.Skip("Needs porting to Windows") } - result := RunCommand("ls") + var cmd string + if runtime.GOOS == "solaris" { + cmd = "gls" + } else { + cmd = "ls" + } + result := RunCommand(cmd) result.Assert(t, Expected{}) result = RunCommand("doesnotexists") expectedError := `exec: "doesnotexists": executable file not found` result.Assert(t, Expected{ExitCode: 127, Error: expectedError}) - result = RunCommand("ls", "-z") + result = RunCommand(cmd, "-z") result.Assert(t, Expected{ ExitCode: 2, Error: "exit status 2", @@ -90,11 +96,19 @@ func TestRunCommandWithStdoutStderrError(t *testing.T) { switch runtime.GOOS { case "windows": expected = "ls: unknown option" + case "solaris": + expected = "gls: invalid option" default: expected = "ls: invalid option" } - result = RunCommand("ls", "-z") + var cmd string + if runtime.GOOS == "solaris" { + cmd = "gls" + } else { + cmd = "ls" + } + result = RunCommand(cmd, "-z") result.Assert(t, Expected{ Out: None, Err: expected, diff --git a/pkg/integration/utils_test.go b/pkg/integration/utils_test.go index 5afa03a9c..0b2ef4aff 100644 --- a/pkg/integration/utils_test.go +++ b/pkg/integration/utils_test.go @@ -83,6 +83,10 @@ func TestRunCommandPipelineWithOutputErrors(t *testing.T) { } func TestRunCommandPipelineWithOutput(t *testing.T) { + //TODO: Should run on Solaris + if runtime.GOOS == "solaris" { + t.Skip() + } cmds := []*exec.Cmd{ // Print 2 characters exec.Command("echo", "-n", "11"), diff --git a/pkg/mount/mount_unix_test.go b/pkg/mount/mount_unix_test.go index 90fa348b2..253aff3b8 100644 --- a/pkg/mount/mount_unix_test.go +++ b/pkg/mount/mount_unix_test.go @@ -1,4 +1,4 @@ -// +build !windows +// +build !windows,!solaris package mount diff --git a/pkg/mount/sharedsubtree_solaris.go b/pkg/mount/sharedsubtree_solaris.go new file mode 100644 index 000000000..09f6b03cb --- /dev/null +++ b/pkg/mount/sharedsubtree_solaris.go @@ -0,0 +1,58 @@ +// +build solaris + +package mount + +// MakeShared ensures a mounted filesystem has the SHARED mount option enabled. +// See the supported options in flags.go for further reference. +func MakeShared(mountPoint string) error { + return ensureMountedAs(mountPoint, "shared") +} + +// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled. +// See the supported options in flags.go for further reference. +func MakeRShared(mountPoint string) error { + return ensureMountedAs(mountPoint, "rshared") +} + +// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled. +// See the supported options in flags.go for further reference. +func MakePrivate(mountPoint string) error { + return ensureMountedAs(mountPoint, "private") +} + +// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option +// enabled. See the supported options in flags.go for further reference. +func MakeRPrivate(mountPoint string) error { + return ensureMountedAs(mountPoint, "rprivate") +} + +// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled. +// See the supported options in flags.go for further reference. +func MakeSlave(mountPoint string) error { + return ensureMountedAs(mountPoint, "slave") +} + +// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled. +// See the supported options in flags.go for further reference. +func MakeRSlave(mountPoint string) error { + return ensureMountedAs(mountPoint, "rslave") +} + +// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option +// enabled. See the supported options in flags.go for further reference. +func MakeUnbindable(mountPoint string) error { + return ensureMountedAs(mountPoint, "unbindable") +} + +// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount +// option enabled. See the supported options in flags.go for further reference. +func MakeRUnbindable(mountPoint string) error { + return ensureMountedAs(mountPoint, "runbindable") +} + +func ensureMountedAs(mountPoint, options string) error { + // TODO: Solaris does not support bind mounts. + // Evaluate lofs and also look at the relevant + // mount flags to be supported. + return nil +} diff --git a/plugin/manager_solaris.go b/plugin/manager_solaris.go new file mode 100644 index 000000000..7656a59ad --- /dev/null +++ b/plugin/manager_solaris.go @@ -0,0 +1,28 @@ +package plugin + +import ( + "fmt" + + "github.com/docker/docker/plugin/v2" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +func (pm *Manager) enable(p *v2.Plugin, force bool) error { + return fmt.Errorf("Not implemented") +} + +func (pm *Manager) initSpec(p *v2.Plugin) (*specs.Spec, error) { + return nil, fmt.Errorf("Not implemented") +} + +func (pm *Manager) disable(p *v2.Plugin) error { + return fmt.Errorf("Not implemented") +} + +func (pm *Manager) restore(p *v2.Plugin) error { + return fmt.Errorf("Not implemented") +} + +// Shutdown plugins +func (pm *Manager) Shutdown() { +} diff --git a/registry/auth_test.go b/registry/auth_test.go index f5f213bf9..9ab71aa4f 100644 --- a/registry/auth_test.go +++ b/registry/auth_test.go @@ -1,3 +1,7 @@ +// +build !solaris + +// TODO: Support Solaris + package registry import ( diff --git a/registry/registry_mock_test.go b/registry/registry_mock_test.go index ea2719fe5..21fc1fdcc 100644 --- a/registry/registry_mock_test.go +++ b/registry/registry_mock_test.go @@ -1,3 +1,5 @@ +// +build !solaris + package registry import ( diff --git a/registry/registry_test.go b/registry/registry_test.go index 4eb312a99..786dfbed4 100644 --- a/registry/registry_test.go +++ b/registry/registry_test.go @@ -1,3 +1,5 @@ +// +build !solaris + package registry import ( diff --git a/runconfig/config_test.go b/runconfig/config_test.go index 47a58f4f3..f1f9de595 100644 --- a/runconfig/config_test.go +++ b/runconfig/config_test.go @@ -26,6 +26,11 @@ func TestDecodeContainerConfig(t *testing.T) { image string ) + //TODO: Should run for Solaris + if runtime.GOOS == "solaris" { + t.Skip() + } + if runtime.GOOS != "windows" { image = "ubuntu" fixtures = []f{ diff --git a/runconfig/hostconfig_solaris.go b/runconfig/hostconfig_solaris.go index 399593a89..83ad32ecc 100644 --- a/runconfig/hostconfig_solaris.go +++ b/runconfig/hostconfig_solaris.go @@ -1,9 +1,6 @@ package runconfig import ( - "fmt" - "strings" - "github.com/docker/docker/api/types/container" "github.com/docker/docker/pkg/sysinfo" ) @@ -11,7 +8,7 @@ import ( // DefaultDaemonNetworkMode returns the default network stack the daemon should // use. func DefaultDaemonNetworkMode() container.NetworkMode { - return container.NetworkMode("default") + return container.NetworkMode("bridge") } // IsPreDefinedNetwork indicates if a network is predefined by the daemon @@ -23,15 +20,6 @@ func IsPreDefinedNetwork(network string) bool { // network settings are valid. func ValidateNetMode(c *container.Config, hc *container.HostConfig) error { // We may not be passed a host config, such as in the case of docker commit - if hc == nil { - return nil - } - parts := strings.Split(string(hc.NetworkMode), ":") - switch mode := parts[0]; mode { - case "default", "none": - default: - return fmt.Errorf("invalid --net: %s", hc.NetworkMode) - } return nil } diff --git a/utils/process_unix.go b/utils/process_unix.go index bdb1b46b3..fc0b1c8b7 100644 --- a/utils/process_unix.go +++ b/utils/process_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd +// +build linux freebsd solaris package utils diff --git a/volume/local/local_test.go b/volume/local/local_test.go index 2ba42fe67..d0d5d7280 100644 --- a/volume/local/local_test.go +++ b/volume/local/local_test.go @@ -164,10 +164,9 @@ func TestValidateName(t *testing.T) { } func TestCreateWithOpts(t *testing.T) { - if runtime.GOOS == "windows" { + if runtime.GOOS == "windows" || runtime.GOOS == "solaris" { t.Skip() } - rootDir, err := ioutil.TempDir("", "local-volume-test") if err != nil { t.Fatal(err)