8fb0ca2c |
package daemon
import (
"fmt" |
557c7cb8 |
"os" |
4352da78 |
"strings" |
8fb0ca2c |
|
e8026d8a |
"github.com/Microsoft/hcsshim" |
a5879bb8 |
"github.com/Sirupsen/logrus" |
91e197d6 |
"github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container" |
6bb0d181 |
"github.com/docker/docker/container" |
4352da78 |
"github.com/docker/docker/image" |
557c7cb8 |
"github.com/docker/docker/pkg/idtools" |
94d70d83 |
"github.com/docker/docker/pkg/parsers" |
340e5233 |
"github.com/docker/docker/pkg/platform" |
8df20663 |
"github.com/docker/docker/pkg/sysinfo" |
805dd0ee |
"github.com/docker/docker/pkg/system" |
8df20663 |
"github.com/docker/docker/runconfig" |
8fb0ca2c |
"github.com/docker/libnetwork" |
ed364b69 |
nwconfig "github.com/docker/libnetwork/config" |
ed8ccc30 |
"github.com/docker/libnetwork/datastore" |
e8026d8a |
winlibnetwork "github.com/docker/libnetwork/drivers/windows"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/options" |
0fbfa144 |
blkiodev "github.com/opencontainers/runc/libcontainer/configs" |
40a56f29 |
"golang.org/x/sys/windows" |
8fb0ca2c |
)
|
10d30c64 |
const ( |
4e15420b |
defaultNetworkSpace = "172.16.0.0/12"
platformSupported = true
windowsMinCPUShares = 1
windowsMaxCPUShares = 10000
windowsMinCPUPercent = 1
windowsMaxCPUPercent = 100
windowsMinCPUCount = 1 |
10d30c64 |
) |
e0ec0cc1 |
|
94d70d83 |
func getBlkioWeightDevices(config *containertypes.HostConfig) ([]blkiodev.WeightDevice, error) { |
0fbfa144 |
return nil, nil
}
|
7ac4232e |
func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error { |
8fb0ca2c |
return nil
}
|
94d70d83 |
func getBlkioReadIOpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) { |
843084b0 |
return nil, nil
}
|
94d70d83 |
func getBlkioWriteIOpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) { |
843084b0 |
return nil, nil
}
|
94d70d83 |
func getBlkioReadBpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) { |
3f15a055 |
return nil, nil
}
|
94d70d83 |
func getBlkioWriteBpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) { |
3f15a055 |
return nil, nil
}
|
442b4562 |
func setupInitLayer(initLayer string, rootUID, rootGID int) error { |
8fb0ca2c |
return nil
}
|
2508ca00 |
func (daemon *Daemon) getLayerInit() func(string) error {
return nil
}
|
8fb0ca2c |
func checkKernel() error {
return nil
}
|
ca89c329 |
func (daemon *Daemon) getCgroupDriver() string {
return ""
}
|
3fea79bf |
// adaptContainerSettings is called during container creation to modify any
// settings necessary in the HostConfig structure. |
7ac4232e |
func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error { |
608b3db5 |
if hostConfig == nil { |
4089b4e4 |
return nil |
608b3db5 |
}
|
4089b4e4 |
return nil |
7e0dfbf4 |
}
|
4e15420b |
func verifyContainerResources(resources *containertypes.Resources, isHyperv bool) ([]string, error) { |
ea8c6908 |
warnings := []string{}
|
4e15420b |
if !isHyperv {
// The processor resource controls are mutually exclusive on
// Windows Server Containers, the order of precedence is
// CPUCount first, then CPUShares, and CPUPercent last.
if resources.CPUCount > 0 {
if resources.CPUShares > 0 {
warnings = append(warnings, "Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded")
logrus.Warn("Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded")
resources.CPUShares = 0
}
if resources.CPUPercent > 0 {
warnings = append(warnings, "Conflicting options: CPU count takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
logrus.Warn("Conflicting options: CPU count takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
resources.CPUPercent = 0
}
} else if resources.CPUShares > 0 {
if resources.CPUPercent > 0 {
warnings = append(warnings, "Conflicting options: CPU shares takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
logrus.Warn("Conflicting options: CPU shares takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
resources.CPUPercent = 0
}
} |
ea8c6908 |
}
|
0ed00b36 |
if resources.CPUShares < 0 || resources.CPUShares > windowsMaxCPUShares {
return warnings, fmt.Errorf("range of CPUShares is from %d to %d", windowsMinCPUShares, windowsMaxCPUShares)
}
if resources.CPUPercent < 0 || resources.CPUPercent > windowsMaxCPUPercent {
return warnings, fmt.Errorf("range of CPUPercent is from %d to %d", windowsMinCPUPercent, windowsMaxCPUPercent)
}
if resources.CPUCount < 0 {
return warnings, fmt.Errorf("invalid CPUCount: CPUCount cannot be negative") |
846baf1f |
}
|
0ed00b36 |
if resources.NanoCPUs > 0 && resources.CPUPercent > 0 {
return warnings, fmt.Errorf("conflicting options: Nano CPUs and CPU Percent cannot both be set")
} |
846baf1f |
if resources.NanoCPUs > 0 && resources.CPUShares > 0 { |
0ed00b36 |
return warnings, fmt.Errorf("conflicting options: Nano CPUs and CPU Shares cannot both be set") |
846baf1f |
} |
e013d7c5 |
// The precision we could get is 0.01, because on Windows we have to convert to CPUPercent.
// We don't set the lower limit here and it is up to the underlying platform (e.g., Windows) to return an error. |
846baf1f |
if resources.NanoCPUs < 0 || resources.NanoCPUs > int64(sysinfo.NumCPU())*1e9 { |
e013d7c5 |
return warnings, fmt.Errorf("range of CPUs is from 0.01 to %d.00, as there are only %d CPUs available", sysinfo.NumCPU(), sysinfo.NumCPU()) |
846baf1f |
}
|
0ed00b36 |
if len(resources.BlkioDeviceReadBps) > 0 {
return warnings, fmt.Errorf("invalid option: Windows does not support BlkioDeviceReadBps")
}
if len(resources.BlkioDeviceReadIOps) > 0 {
return warnings, fmt.Errorf("invalid option: Windows does not support BlkioDeviceReadIOps")
}
if len(resources.BlkioDeviceWriteBps) > 0 {
return warnings, fmt.Errorf("invalid option: Windows does not support BlkioDeviceWriteBps")
}
if len(resources.BlkioDeviceWriteIOps) > 0 {
return warnings, fmt.Errorf("invalid option: Windows does not support BlkioDeviceWriteIOps")
} |
8df20663 |
if resources.BlkioWeight > 0 { |
0ed00b36 |
return warnings, fmt.Errorf("invalid option: Windows does not support BlkioWeight") |
8df20663 |
}
if len(resources.BlkioWeightDevice) > 0 { |
0ed00b36 |
return warnings, fmt.Errorf("invalid option: Windows does not support BlkioWeightDevice") |
8df20663 |
} |
0ed00b36 |
if resources.CgroupParent != "" {
return warnings, fmt.Errorf("invalid option: Windows does not support CgroupParent") |
8df20663 |
} |
0ed00b36 |
if resources.CPUPeriod != 0 {
return warnings, fmt.Errorf("invalid option: Windows does not support CPUPeriod") |
8df20663 |
} |
0ed00b36 |
if resources.CpusetCpus != "" {
return warnings, fmt.Errorf("invalid option: Windows does not support CpusetCpus") |
8df20663 |
} |
0ed00b36 |
if resources.CpusetMems != "" {
return warnings, fmt.Errorf("invalid option: Windows does not support CpusetMems")
}
if resources.KernelMemory != 0 {
return warnings, fmt.Errorf("invalid option: Windows does not support KernelMemory")
}
if resources.MemoryReservation != 0 {
return warnings, fmt.Errorf("invalid option: Windows does not support MemoryReservation")
}
if resources.MemorySwap != 0 {
return warnings, fmt.Errorf("invalid option: Windows does not support MemorySwap")
}
if resources.MemorySwappiness != nil && *resources.MemorySwappiness != -1 {
return warnings, fmt.Errorf("invalid option: Windows does not support MemorySwappiness")
}
if resources.OomKillDisable != nil && *resources.OomKillDisable {
return warnings, fmt.Errorf("invalid option: Windows does not support OomKillDisable")
}
if resources.PidsLimit != 0 {
return warnings, fmt.Errorf("invalid option: Windows does not support PidsLimit")
}
if len(resources.Ulimits) != 0 {
return warnings, fmt.Errorf("invalid option: Windows does not support Ulimits") |
8df20663 |
} |
ea8c6908 |
return warnings, nil
}
|
3fea79bf |
// verifyPlatformContainerSettings performs platform-specific validation of the
// hostconfig and config structures. |
8ae6f6ac |
func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { |
ea8c6908 |
warnings := []string{}
|
6b4ad8d6 |
hyperv := daemon.runAsHyperVContainer(hostConfig)
if !hyperv && system.IsWindowsClient() {
// @engine maintainers. This block should not be removed. It partially enforces licensing
// restrictions on Windows. Ping @jhowardmsft if there are concerns or PRs to change this.
return warnings, fmt.Errorf("Windows client operating systems only support Hyper-V containers")
}
w, err := verifyContainerResources(&hostConfig.Resources, hyperv) |
ea8c6908 |
warnings = append(warnings, w...)
if err != nil {
return warnings, err
}
return warnings, nil |
8fb0ca2c |
}
|
7b2e5216 |
// platformReload update configuration with platform specific options |
44c280af |
func (daemon *Daemon) platformReload(config *Config) map[string]string {
return map[string]string{} |
7b2e5216 |
}
|
5ce5a8e9 |
// verifyDaemonSettings performs validation of daemon config struct
func verifyDaemonSettings(config *Config) error { |
8fb0ca2c |
return nil
}
|
62a75fca |
// checkSystem validates platform-specific requirements |
8fb0ca2c |
func checkSystem() error {
// Validate the OS version. Note that docker.exe must be manifested for this
// call to return the correct version. |
194eaa5c |
osv := system.GetOSVersion() |
805dd0ee |
if osv.MajorVersion < 10 { |
8fb0ca2c |
return fmt.Errorf("This version of Windows does not support the docker daemon")
} |
cc4e17cb |
if osv.Build < 14393 {
return fmt.Errorf("The docker daemon requires build 14393 or later of Windows Server 2016 or Windows 10") |
122568b3 |
} |
40a56f29 |
vmcompute := windows.NewLazySystemDLL("vmcompute.dll")
if vmcompute.Load() != nil {
return fmt.Errorf("Failed to load vmcompute.dll. Ensure that the Containers role is installed.")
} |
8fb0ca2c |
return nil
}
// configureKernelSecuritySupport configures and validate security support for the kernel
func configureKernelSecuritySupport(config *Config, driverName string) error {
return nil
}
|
140a7434 |
// configureMaxThreads sets the Go runtime max threads threshold
func configureMaxThreads(config *Config) error {
return nil
}
|
ecffb6d5 |
func (daemon *Daemon) initNetworkController(config *Config, activeSandboxes map[string]interface{}) (libnetwork.NetworkController, error) { |
a00940f0 |
netOptions, err := daemon.networkOptions(config, nil, nil) |
e8026d8a |
if err != nil {
return nil, err
}
controller, err := libnetwork.New(netOptions...)
if err != nil {
return nil, fmt.Errorf("error obtaining controller instance: %v", err)
}
hnsresponse, err := hcsshim.HNSListNetworkRequest("GET", "", "")
if err != nil {
return nil, err
}
// Remove networks not present in HNS
for _, v := range controller.Networks() {
options := v.Info().DriverOptions()
hnsid := options[winlibnetwork.HNSID]
found := false
for _, v := range hnsresponse {
if v.Id == hnsid {
found = true
break
}
}
if !found { |
ed8ccc30 |
// global networks should not be deleted by local HNS
if v.Info().Scope() != datastore.GlobalScope {
err = v.Delete()
if err != nil {
logrus.Errorf("Error occurred when removing network %v", err)
} |
e8026d8a |
}
}
}
|
6eb2b903 |
_, err = controller.NewNetwork("null", "none", "", libnetwork.NetworkOptionPersist(false)) |
e8026d8a |
if err != nil {
return nil, err
}
|
aad25801 |
defaultNetworkExists := false
if network, err := controller.NetworkByName(runconfig.DefaultDaemonNetworkMode().NetworkName()); err == nil {
options := network.Info().DriverOptions()
for _, v := range hnsresponse {
if options[winlibnetwork.HNSID] == v.Id {
defaultNetworkExists = true
break
}
}
}
|
e8026d8a |
// discover and add HNS networks to windows
// network that exist are removed and added again
for _, v := range hnsresponse {
var n libnetwork.Network
s := func(current libnetwork.Network) bool {
options := current.Info().DriverOptions()
if options[winlibnetwork.HNSID] == v.Id {
n = current
return true
}
return false
}
controller.WalkNetworks(s)
if n != nil { |
ed8ccc30 |
// global networks should not be deleted by local HNS
if n.Info().Scope() == datastore.GlobalScope {
continue
} |
e8026d8a |
v.Name = n.Name() |
aad25801 |
// This will not cause network delete from HNS as the network
// is not yet populated in the libnetwork windows driver |
e8026d8a |
n.Delete()
}
netOption := map[string]string{
winlibnetwork.NetworkName: v.Name,
winlibnetwork.HNSID: v.Id,
}
v4Conf := []*libnetwork.IpamConf{}
for _, subnet := range v.Subnets {
ipamV4Conf := libnetwork.IpamConf{}
ipamV4Conf.PreferredPool = subnet.AddressPrefix
ipamV4Conf.Gateway = subnet.GatewayAddress
v4Conf = append(v4Conf, &ipamV4Conf)
}
name := v.Name |
aad25801 |
// If there is no nat network create one from the first NAT network
// encountered
if !defaultNetworkExists && runconfig.DefaultDaemonNetworkMode() == containertypes.NetworkMode(strings.ToLower(v.Type)) { |
e8026d8a |
name = runconfig.DefaultDaemonNetworkMode().NetworkName() |
aad25801 |
defaultNetworkExists = true |
e8026d8a |
}
v6Conf := []*libnetwork.IpamConf{} |
6eb2b903 |
_, err := controller.NewNetwork(strings.ToLower(v.Type), name, "", |
e8026d8a |
libnetwork.NetworkOptionGeneric(options.Generic{
netlabel.GenericData: netOption,
}),
libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil),
)
if err != nil {
logrus.Errorf("Error occurred when creating network %v", err)
}
}
if !config.DisableBridge {
// Initialize default driver "bridge"
if err := initBridgeDriver(controller, config); err != nil {
return nil, err
}
}
return controller, nil |
8fb0ca2c |
}
|
e8026d8a |
func initBridgeDriver(controller libnetwork.NetworkController, config *Config) error {
if _, err := controller.NetworkByName(runconfig.DefaultDaemonNetworkMode().NetworkName()); err == nil {
return nil |
e0ec0cc1 |
} |
e8026d8a |
netOption := map[string]string{
winlibnetwork.NetworkName: runconfig.DefaultDaemonNetworkMode().NetworkName(),
}
|
aad25801 |
var ipamOption libnetwork.NetworkOption
var subnetPrefix string
if config.bridgeConfig.FixedCIDR != "" {
subnetPrefix = config.bridgeConfig.FixedCIDR |
e8026d8a |
} else { |
aad25801 |
// TP5 doesn't support properly detecting subnet
osv := system.GetOSVersion()
if osv.Build < 14360 {
subnetPrefix = defaultNetworkSpace
} |
e8026d8a |
}
|
aad25801 |
if subnetPrefix != "" {
ipamV4Conf := libnetwork.IpamConf{}
ipamV4Conf.PreferredPool = subnetPrefix
v4Conf := []*libnetwork.IpamConf{&ipamV4Conf}
v6Conf := []*libnetwork.IpamConf{}
ipamOption = libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil)
} |
e8026d8a |
|
6eb2b903 |
_, err := controller.NewNetwork(string(runconfig.DefaultDaemonNetworkMode()), runconfig.DefaultDaemonNetworkMode().NetworkName(), "", |
e8026d8a |
libnetwork.NetworkOptionGeneric(options.Generic{
netlabel.GenericData: netOption,
}), |
aad25801 |
ipamOption, |
e8026d8a |
)
if err != nil {
return fmt.Errorf("Error creating default network: %v", err)
} |
aad25801 |
|
e8026d8a |
return nil |
8fb0ca2c |
} |
c5e6a4b3 |
|
abd72d40 |
// registerLinks sets up links between containers and writes the |
b2771b44 |
// configuration out for persistence. As of Windows TP4, links are not supported. |
7ac4232e |
func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error { |
c5e6a4b3 |
return nil
} |
47c56e43 |
|
94d70d83 |
func (daemon *Daemon) cleanupMountsByID(in string) error {
return nil
}
|
c8291f71 |
func (daemon *Daemon) cleanupMounts() error {
return nil
} |
3a497650 |
|
557c7cb8 |
func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) {
return nil, nil, nil
}
func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error {
config.Root = rootDir
// Create the root directory if it doesn't exists |
46ec4c1a |
if err := system.MkdirAllWithACL(config.Root, 0); err != nil && !os.IsExist(err) { |
557c7cb8 |
return err
}
return nil
}
|
4461bc45 |
// runasHyperVContainer returns true if we are going to run as a Hyper-V container |
4e15420b |
func (daemon *Daemon) runAsHyperVContainer(hostConfig *containertypes.HostConfig) bool {
if hostConfig.Isolation.IsDefault() { |
94d70d83 |
// Container is set to use the default, so take the default from the daemon configuration |
4461bc45 |
return daemon.defaultIsolation.IsHyperV() |
94d70d83 |
}
|
4461bc45 |
// Container is requesting an isolation mode. Honour it. |
4e15420b |
return hostConfig.Isolation.IsHyperV() |
4461bc45 |
}
// conditionalMountOnStart is a platform specific helper function during the
// container start to call mount.
func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error { |
3a497650 |
// We do not mount if a Hyper-V container |
4e15420b |
if !daemon.runAsHyperVContainer(container.HostConfig) { |
4461bc45 |
return daemon.Mount(container) |
3a497650 |
}
return nil
}
// conditionalUnmountOnCleanup is a platform specific helper function called
// during the cleanup of a container to unmount. |
94d70d83 |
func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error { |
3a497650 |
// We do not unmount if a Hyper-V container |
4e15420b |
if !daemon.runAsHyperVContainer(container.HostConfig) { |
94d70d83 |
return daemon.Unmount(container) |
4352da78 |
} |
94d70d83 |
return nil |
4352da78 |
}
|
e8026d8a |
func driverOptions(config *Config) []nwconfig.Option {
return []nwconfig.Option{} |
ed364b69 |
} |
94d70d83 |
func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { |
340e5233 |
if !c.IsRunning() {
return nil, errNotRunning{c.ID}
}
// Obtain the stats from HCS via libcontainerd
stats, err := daemon.containerd.Stats(c.ID)
if err != nil {
return nil, err
}
// Start with an empty structure
s := &types.StatsJSON{}
// Populate the CPU/processor statistics
s.CPUStats = types.CPUStats{
CPUUsage: types.CPUUsage{
TotalUsage: stats.Processor.TotalRuntime100ns,
UsageInKernelmode: stats.Processor.RuntimeKernel100ns,
UsageInUsermode: stats.Processor.RuntimeKernel100ns,
},
}
// Populate the memory statistics
s.MemoryStats = types.MemoryStats{
Commit: stats.Memory.UsageCommitBytes,
CommitPeak: stats.Memory.UsageCommitPeakBytes,
PrivateWorkingSet: stats.Memory.UsagePrivateWorkingSetBytes,
}
// Populate the storage statistics
s.StorageStats = types.StorageStats{
ReadCountNormalized: stats.Storage.ReadCountNormalized,
ReadSizeBytes: stats.Storage.ReadSizeBytes,
WriteCountNormalized: stats.Storage.WriteCountNormalized,
WriteSizeBytes: stats.Storage.WriteSizeBytes,
}
// Populate the network statistics
s.Networks = make(map[string]types.NetworkStats)
for _, nstats := range stats.Network {
s.Networks[nstats.EndpointId] = types.NetworkStats{
RxBytes: nstats.BytesReceived,
RxPackets: nstats.PacketsReceived,
RxDropped: nstats.DroppedPacketsIncoming,
TxBytes: nstats.BytesSent,
TxPackets: nstats.PacketsSent,
TxDropped: nstats.DroppedPacketsOutgoing,
}
}
// Set the timestamp
s.Stats.Read = stats.Timestamp
s.Stats.NumProcs = platform.NumProcs()
return s, nil |
94d70d83 |
}
// setDefaultIsolation determine the default isolation mode for the
// daemon to run in. This is only applicable on Windows
func (daemon *Daemon) setDefaultIsolation() error {
daemon.defaultIsolation = containertypes.Isolation("process") |
ef2db56b |
// On client SKUs, default to Hyper-V
if system.IsWindowsClient() {
daemon.defaultIsolation = containertypes.Isolation("hyperv")
} |
94d70d83 |
for _, option := range daemon.configStore.ExecOptions {
key, val, err := parsers.ParseKeyValueOpt(option)
if err != nil {
return err
}
key = strings.ToLower(key)
switch key {
case "isolation":
if !containertypes.Isolation(val).IsValid() {
return fmt.Errorf("Invalid exec-opt value for 'isolation':'%s'", val)
}
if containertypes.Isolation(val).IsHyperV() {
daemon.defaultIsolation = containertypes.Isolation("hyperv")
} |
ef2db56b |
if containertypes.Isolation(val).IsProcess() {
if system.IsWindowsClient() { |
87ab13ad |
// @engine maintainers. This block should not be removed. It partially enforces licensing
// restrictions on Windows. Ping @jhowardmsft if there are concerns or PRs to change this. |
ef2db56b |
return fmt.Errorf("Windows client operating systems only support Hyper-V containers")
}
daemon.defaultIsolation = containertypes.Isolation("process")
} |
94d70d83 |
default:
return fmt.Errorf("Unrecognised exec-opt '%s'\n", key)
}
}
logrus.Infof("Windows default isolation mode: %s", daemon.defaultIsolation)
return nil
} |
14dc4a71 |
func rootFSToAPIType(rootfs *image.RootFS) types.RootFS {
var layers []string
for _, l := range rootfs.DiffIDs {
layers = append(layers, l.String())
}
return types.RootFS{ |
f342b271 |
Type: rootfs.Type,
Layers: layers, |
14dc4a71 |
}
} |
a894aec8 |
func setupDaemonProcess(config *Config) error {
return nil
} |
dc712b92 |
// verifyVolumesInfo is a no-op on windows.
// This is called during daemon initialization to migrate volumes from pre-1.7.
// volumes were not supported on windows pre-1.7
func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error {
return nil
} |
b237189e |
func (daemon *Daemon) setupSeccompProfile() error {
return nil
} |