| // +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" |
| refstore "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 (daemon *Daemon) parseSecurityOpt(container *container.Container, hostConfig *containertypes.HostConfig) error { |
| return parseSecurityOpt(container, hostConfig) |
| } |
| |
| 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 |
| } |
| |
| // reloadPlatform updates configuration with platform specific options |
| // and updates the passed attributes |
| func (daemon *Daemon) reloadPlatform(config *Config, attributes 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 refstore.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 |
| } |