moby/daemon/daemon_solaris.go
Tonis Tiigi 38d914cc96 Implement content addressability for plugins
Move plugins to shared distribution stack with images.

Create immutable plugin config that matches schema2 requirements.

Ensure data being pushed is same as pulled/created.

Store distribution artifacts in a blobstore.

Run init layer setup for every plugin start.

Fix breakouts from unsafe file accesses.

Add support for `docker plugin install --alias`

Uses normalized references for default names to avoid collisions when using default hosts/tags.

Some refactoring of the plugin manager to support the change, like removing the singleton manager and adding manager config struct.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
Signed-off-by: Derek McGowan <derek@mcgstyle.net>
(cherry picked from commit 3d86b0c79b)
2016-12-27 13:31:14 -08:00

523 lines
19 KiB
Go

// +build solaris,cgo
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"
"github.com/docker/docker/image"
"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 <zone.h>
import "C"
const (
defaultVirtualSwitch = "Virtual Switch"
platformSupported = true
solarisMinCPUShares = 1
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 {
//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) {
return nil, nil, nil
}
func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error {
return nil
}
func (daemon *Daemon) getLayerInit() func(string) error {
return nil
}
func checkKernel() error {
// solaris can rely upon checkSystem() below, we don't skew kernel versions
return nil
}
func (daemon *Daemon) getCgroupDriver() string {
return ""
}
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
}
// platformReload update configuration with platform specific options
func (daemon *Daemon) platformReload(config *Config) map[string]string {
return 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
}
func checkSystem() error {
// check OS version for compatibility, ensure running in global zone
var err error
var id C.zoneid_t
if id, err = C.getzoneid(); err != nil {
return fmt.Errorf("Exiting. Error getting zone id: %+v", err)
}
if int(id) != 0 {
return fmt.Errorf("Exiting because the Docker daemon is not running in the global zone")
}
v, err := kernel.GetKernelVersion()
if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 5, Major: 12, Minor: 0}) < 0 {
return fmt.Errorf("Your Solaris kernel version: %s doesn't support Docker. Please upgrade to 5.12.0", v.String())
}
return err
}
// configureMaxThreads sets the Go runtime max threads threshold
// which is 90% of the kernel setting from /proc/sys/kernel/threads-max
func configureMaxThreads(config *Config) error {
return nil
}
// configureKernelSecuritySupport configures and validate security support for the kernel
func configureKernelSecuritySupport(config *Config, driverName string) error {
return nil
}
func (daemon *Daemon) initNetworkController(config *Config, activeSandboxes map[string]interface{}) (libnetwork.NetworkController, error) {
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
// configuration out for persistence.
func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error {
return nil
}
func (daemon *Daemon) cleanupMounts() error {
return nil
}
// conditionalMountOnStart is a platform specific helper function during the
// container start to call mount.
func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
return daemon.Mount(container)
}
// conditionalUnmountOnCleanup is a platform specific helper function called
// during the cleanup of a container to unmount.
func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error {
return daemon.Unmount(container)
}
func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) error {
// Solaris has no custom images to register
return nil
}
func driverOptions(config *Config) []nwconfig.Option {
return []nwconfig.Option{}
}
func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
return nil, nil
}
// setDefaultIsolation determine the default isolation mode for the
// daemon to run in. This is only applicable on Windows
func (daemon *Daemon) setDefaultIsolation() error {
return nil
}
func rootFSToAPIType(rootfs *image.RootFS) types.RootFS {
return types.RootFS{}
}
func setupDaemonProcess(config *Config) error {
return nil
}
func (daemon *Daemon) setupSeccompProfile() error {
return nil
}