// +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 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 }