2018-02-05 16:05:59 -05:00
package daemon // import "github.com/docker/docker/daemon"
2015-05-15 19:34:26 -04:00
import (
2017-09-22 09:52:41 -04:00
"context"
2015-05-15 19:34:26 -04:00
"fmt"
2019-08-13 03:10:19 -04:00
"math"
2017-02-02 14:22:12 -05:00
"path/filepath"
2019-08-13 03:10:19 -04:00
"runtime"
2015-11-18 17:20:54 -05:00
"strings"
2015-05-15 19:34:26 -04:00
2016-03-09 23:33:21 -05:00
"github.com/Microsoft/hcsshim"
2019-04-17 06:39:07 -04:00
"github.com/Microsoft/hcsshim/osversion"
2016-09-06 14:18:12 -04:00
"github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container"
2015-11-12 14:55:17 -05:00
"github.com/docker/docker/container"
2017-01-23 06:23:07 -05:00
"github.com/docker/docker/daemon/config"
2017-08-03 20:22:00 -04:00
"github.com/docker/docker/pkg/containerfs"
2016-01-07 22:43:11 -05:00
"github.com/docker/docker/pkg/idtools"
2016-03-18 14:53:27 -04:00
"github.com/docker/docker/pkg/parsers"
2016-09-07 19:08:51 -04:00
"github.com/docker/docker/pkg/platform"
2016-02-24 20:51:46 -05:00
"github.com/docker/docker/pkg/sysinfo"
2015-10-15 14:40:14 -04:00
"github.com/docker/docker/pkg/system"
2016-02-24 20:51:46 -05:00
"github.com/docker/docker/runconfig"
2015-05-15 19:34:26 -04:00
"github.com/docker/libnetwork"
2016-02-17 20:08:11 -05:00
nwconfig "github.com/docker/libnetwork/config"
2016-11-09 20:54:15 -05:00
"github.com/docker/libnetwork/datastore"
2016-03-09 23:33:21 -05:00
winlibnetwork "github.com/docker/libnetwork/drivers/windows"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/options"
2017-09-21 18:09:41 -04:00
"github.com/pkg/errors"
2017-07-26 17:42:13 -04:00
"github.com/sirupsen/logrus"
2016-11-18 15:34:25 -05:00
"golang.org/x/sys/windows"
2017-09-21 18:09:41 -04:00
"golang.org/x/sys/windows/svc/mgr"
2015-05-15 19:34:26 -04:00
)
2015-08-07 12:33:29 -04:00
const (
2019-10-12 20:29:21 -04:00
isWindows = true
2016-11-01 16:02:46 -04:00
defaultNetworkSpace = "172.16.0.0/12"
platformSupported = true
windowsMinCPUShares = 1
windowsMaxCPUShares = 10000
windowsMinCPUPercent = 1
windowsMaxCPUPercent = 100
2015-08-07 12:33:29 -04:00
)
2015-07-13 15:34:58 -04:00
2019-08-13 03:10:19 -04:00
// Windows containers are much larger than Linux containers and each of them
// have > 20 system processes which why we use much smaller parallelism value.
2018-12-04 11:44:45 -05:00
func adjustParallelLimit ( n int , limit int ) int {
2019-08-13 03:10:19 -04:00
return int ( math . Max ( 1 , math . Floor ( float64 ( runtime . NumCPU ( ) ) * .8 ) ) )
2018-12-04 11:44:45 -05:00
}
2017-02-02 14:22:12 -05:00
// Windows has no concept of an execution state directory. So use config.Root here.
func getPluginExecRoot ( root string ) string {
return filepath . Join ( root , "plugins" )
}
2017-01-08 20:22:05 -05:00
func ( daemon * Daemon ) parseSecurityOpt ( container * container . Container , hostConfig * containertypes . HostConfig ) error {
return parseSecurityOpt ( container , hostConfig )
}
2015-12-18 13:36:17 -05:00
func parseSecurityOpt ( container * container . Container , config * containertypes . HostConfig ) error {
2015-05-15 19:34:26 -04:00
return nil
}
2017-11-16 01:20:33 -05:00
func setupInitLayer ( idMapping * idtools . IdentityMapping ) func ( containerfs . ContainerFS ) error {
2016-09-21 14:45:25 -04:00
return nil
}
2015-05-15 19:34:26 -04:00
func checkKernel ( ) error {
return nil
}
2016-02-18 05:10:31 -05:00
func ( daemon * Daemon ) getCgroupDriver ( ) string {
return ""
}
2015-07-30 18:28:11 -04:00
// adaptContainerSettings is called during container creation to modify any
// settings necessary in the HostConfig structure.
2015-12-18 13:36:17 -05:00
func ( daemon * Daemon ) adaptContainerSettings ( hostConfig * containertypes . HostConfig , adjustCPUShares bool ) error {
2015-09-24 15:10:41 -04:00
if hostConfig == nil {
2015-12-01 21:53:52 -05:00
return nil
2015-09-24 15:10:41 -04:00
}
2015-12-01 21:53:52 -05:00
return nil
2015-07-13 03:17:43 -04:00
}
2018-12-18 17:41:52 -05:00
// verifyPlatformContainerResources performs platform-specific validation of the container's resource-configuration
func verifyPlatformContainerResources ( resources * containertypes . Resources , isHyperv bool ) ( warnings [ ] string , err error ) {
2017-06-30 13:34:40 -04:00
fixMemorySwappiness ( resources )
2016-11-01 16:02:46 -04:00
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" )
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" )
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" )
resources . CPUPercent = 0
}
}
2016-03-04 20:24:09 -05:00
}
2016-11-09 18:18:54 -05:00
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" )
2016-11-01 13:12:29 -04:00
}
2016-11-09 18:18:54 -05:00
if resources . NanoCPUs > 0 && resources . CPUPercent > 0 {
return warnings , fmt . Errorf ( "conflicting options: Nano CPUs and CPU Percent cannot both be set" )
}
2016-11-01 13:12:29 -04:00
if resources . NanoCPUs > 0 && resources . CPUShares > 0 {
2016-11-09 18:18:54 -05:00
return warnings , fmt . Errorf ( "conflicting options: Nano CPUs and CPU Shares cannot both be set" )
2016-11-01 13:12:29 -04:00
}
2016-11-15 18:48:46 -05:00
// 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.
2016-11-01 13:12:29 -04:00
if resources . NanoCPUs < 0 || resources . NanoCPUs > int64 ( sysinfo . NumCPU ( ) ) * 1e9 {
2016-11-15 18:48:46 -05:00
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 ( ) )
2016-11-01 13:12:29 -04:00
}
2019-04-17 06:39:07 -04:00
if resources . NanoCPUs > 0 && isHyperv && osversion . Build ( ) < osversion . RS3 {
2017-04-12 19:54:27 -04:00
leftoverNanoCPUs := resources . NanoCPUs % 1e9
if leftoverNanoCPUs != 0 && resources . NanoCPUs > 1e9 {
resources . NanoCPUs = ( ( resources . NanoCPUs + 1e9 / 2 ) / 1e9 ) * 1e9
warningString := fmt . Sprintf ( "Your current OS version does not support Hyper-V containers with NanoCPUs greater than 1000000000 but not divisible by 1000000000. NanoCPUs rounded to %d" , resources . NanoCPUs )
warnings = append ( warnings , warningString )
}
}
2016-11-09 18:18:54 -05:00
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" )
}
2016-02-24 20:51:46 -05:00
if resources . BlkioWeight > 0 {
2016-11-09 18:18:54 -05:00
return warnings , fmt . Errorf ( "invalid option: Windows does not support BlkioWeight" )
2016-02-24 20:51:46 -05:00
}
if len ( resources . BlkioWeightDevice ) > 0 {
2016-11-09 18:18:54 -05:00
return warnings , fmt . Errorf ( "invalid option: Windows does not support BlkioWeightDevice" )
2016-02-24 20:51:46 -05:00
}
2016-11-09 18:18:54 -05:00
if resources . CgroupParent != "" {
return warnings , fmt . Errorf ( "invalid option: Windows does not support CgroupParent" )
2016-02-24 20:51:46 -05:00
}
2016-11-09 18:18:54 -05:00
if resources . CPUPeriod != 0 {
return warnings , fmt . Errorf ( "invalid option: Windows does not support CPUPeriod" )
2016-02-24 20:51:46 -05:00
}
2016-11-09 18:18:54 -05:00
if resources . CpusetCpus != "" {
return warnings , fmt . Errorf ( "invalid option: Windows does not support CpusetCpus" )
2016-02-24 20:51:46 -05:00
}
2016-11-09 18:18:54 -05:00
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" )
}
2017-06-30 13:34:40 -04:00
if resources . MemorySwappiness != nil {
2016-11-09 18:18:54 -05:00
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" )
}
2017-04-11 07:28:13 -04:00
if resources . PidsLimit != nil && * resources . PidsLimit != 0 {
2016-11-09 18:18:54 -05:00
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" )
2016-02-24 20:51:46 -05:00
}
2016-03-04 20:24:09 -05:00
return warnings , nil
}
2015-07-30 18:28:11 -04:00
// verifyPlatformContainerSettings performs platform-specific validation of the
// hostconfig and config structures.
2018-12-18 17:20:17 -05:00
func verifyPlatformContainerSettings ( daemon * Daemon , hostConfig * containertypes . HostConfig , update bool ) ( warnings [ ] string , err error ) {
2018-12-18 19:28:08 -05:00
if hostConfig == nil {
return nil , nil
}
2016-11-10 17:35:19 -05:00
hyperv := daemon . runAsHyperVContainer ( hostConfig )
2018-10-09 14:58:26 -04:00
// On RS5, we allow (but don't strictly support) process isolation on Client SKUs.
// Prior to RS5, we don't allow process isolation on Client SKUs.
// @engine maintainers. This block should not be removed. It partially enforces licensing
2019-09-25 13:51:18 -04:00
// restrictions on Windows. Ping Microsoft folks if there are concerns or PRs to change this.
2019-04-17 06:39:07 -04:00
if ! hyperv && system . IsWindowsClient ( ) && osversion . Build ( ) < osversion . RS5 {
2018-10-09 14:58:26 -04:00
return warnings , fmt . Errorf ( "Windows client operating systems earlier than version 1809 can only run Hyper-V containers" )
2016-11-10 17:35:19 -05:00
}
2018-12-18 17:41:52 -05:00
w , err := verifyPlatformContainerResources ( & hostConfig . Resources , hyperv )
2016-03-04 20:24:09 -05:00
warnings = append ( warnings , w ... )
2016-12-14 15:36:58 -05:00
return warnings , err
2015-05-15 19:34:26 -04:00
}
2016-01-22 21:15:09 -05:00
// verifyDaemonSettings performs validation of daemon config struct
2017-01-23 06:23:07 -05:00
func verifyDaemonSettings ( config * config . Config ) error {
2015-05-15 19:34:26 -04:00
return nil
}
2015-07-11 15:32:08 -04:00
// checkSystem validates platform-specific requirements
2015-05-15 19:34:26 -04:00
func checkSystem ( ) error {
2019-09-11 09:16:27 -04:00
// Validate the OS version. Note that dockerd.exe must be manifested for this
2015-05-15 19:34:26 -04:00
// call to return the correct version.
2016-03-16 21:45:40 -04:00
osv := system . GetOSVersion ( )
2015-10-15 14:40:14 -04:00
if osv . MajorVersion < 10 {
2015-05-15 19:34:26 -04:00
return fmt . Errorf ( "This version of Windows does not support the docker daemon" )
}
2019-04-17 06:39:07 -04:00
if osversion . Build ( ) < osversion . RS1 {
2016-09-15 18:20:16 -04:00
return fmt . Errorf ( "The docker daemon requires build 14393 or later of Windows Server 2016 or Windows 10" )
2015-11-11 17:47:02 -05:00
}
2016-11-18 15:34:25 -05:00
vmcompute := windows . NewLazySystemDLL ( "vmcompute.dll" )
if vmcompute . Load ( ) != nil {
2017-09-25 15:39:27 -04:00
return fmt . Errorf ( "failed to load vmcompute.dll, ensure that the Containers feature is installed" )
2016-11-18 15:34:25 -05:00
}
2017-02-01 13:52:16 -05:00
2017-09-21 18:09:41 -04:00
// Ensure that the required Host Network Service and vmcompute services
// are running. Docker will fail in unexpected ways if this is not present.
var requiredServices = [ ] string { "hns" , "vmcompute" }
if err := ensureServicesInstalled ( requiredServices ) ; err != nil {
return errors . Wrap ( err , "a required service is not installed, ensure the Containers feature is installed" )
}
return nil
}
func ensureServicesInstalled ( services [ ] string ) error {
m , err := mgr . Connect ( )
if err != nil {
return err
}
defer m . Disconnect ( )
for _ , service := range services {
s , err := m . OpenService ( service )
if err != nil {
return errors . Wrapf ( err , "failed to open service %s" , service )
}
s . Close ( )
}
2017-03-27 17:32:18 -04:00
return nil
2015-05-15 19:34:26 -04:00
}
// configureKernelSecuritySupport configures and validate security support for the kernel
2017-08-24 14:48:16 -04:00
func configureKernelSecuritySupport ( config * config . Config , driverName string ) error {
2015-05-15 19:34:26 -04:00
return nil
}
2015-12-02 05:26:30 -05:00
// configureMaxThreads sets the Go runtime max threads threshold
2017-01-23 06:23:07 -05:00
func configureMaxThreads ( config * config . Config ) error {
2015-12-02 05:26:30 -05:00
return nil
}
2017-01-23 06:23:07 -05:00
func ( daemon * Daemon ) initNetworkController ( config * config . Config , activeSandboxes map [ string ] interface { } ) ( libnetwork . NetworkController , error ) {
2016-09-26 13:08:52 -04:00
netOptions , err := daemon . networkOptions ( config , nil , nil )
2016-03-09 23:33:21 -05:00
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 {
2016-11-09 20:54:15 -05:00
// 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 )
}
2016-03-09 23:33:21 -05:00
}
}
}
2016-05-08 03:33:16 -04:00
_ , err = controller . NewNetwork ( "null" , "none" , "" , libnetwork . NetworkOptionPersist ( false ) )
2016-03-09 23:33:21 -05:00
if err != nil {
return nil , err
}
2016-06-09 15:09:13 -04:00
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
}
}
}
2016-03-09 23:33:21 -05:00
// discover and add HNS networks to windows
// network that exist are removed and added again
for _ , v := range hnsresponse {
2018-09-06 05:54:23 -04:00
networkTypeNorm := strings . ToLower ( v . Type )
if networkTypeNorm == "private" || networkTypeNorm == "internal" {
2017-05-05 12:03:22 -04:00
continue // workaround for HNS reporting unsupported networks
}
2016-03-09 23:33:21 -05:00
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 )
2017-11-21 15:00:26 -05:00
drvOptions := make ( map [ string ] string )
2019-01-15 20:05:23 -05:00
nid := ""
2016-03-09 23:33:21 -05:00
if n != nil {
2019-01-15 20:05:23 -05:00
nid = n . ID ( )
2016-11-09 20:54:15 -05:00
// global networks should not be deleted by local HNS
if n . Info ( ) . Scope ( ) == datastore . GlobalScope {
continue
}
2016-03-09 23:33:21 -05:00
v . Name = n . Name ( )
2016-06-09 15:09:13 -04:00
// This will not cause network delete from HNS as the network
// is not yet populated in the libnetwork windows driver
2017-11-21 15:00:26 -05:00
// restore option if it existed before
drvOptions = n . Info ( ) . DriverOptions ( )
2016-03-09 23:33:21 -05:00
n . Delete ( )
}
netOption := map [ string ] string {
winlibnetwork . NetworkName : v . Name ,
winlibnetwork . HNSID : v . Id ,
}
2017-11-21 15:00:26 -05:00
// add persisted driver options
for k , v := range drvOptions {
if k != winlibnetwork . NetworkName && k != winlibnetwork . HNSID {
netOption [ k ] = v
}
}
2016-03-09 23:33:21 -05:00
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
2016-06-09 15:09:13 -04:00
// If there is no nat network create one from the first NAT network
2017-01-13 00:09:57 -05:00
// encountered if it doesn't already exist
if ! defaultNetworkExists &&
runconfig . DefaultDaemonNetworkMode ( ) == containertypes . NetworkMode ( strings . ToLower ( v . Type ) ) &&
n == nil {
2016-03-09 23:33:21 -05:00
name = runconfig . DefaultDaemonNetworkMode ( ) . NetworkName ( )
2016-06-09 15:09:13 -04:00
defaultNetworkExists = true
2016-03-09 23:33:21 -05:00
}
v6Conf := [ ] * libnetwork . IpamConf { }
2019-01-15 20:05:23 -05:00
_ , err := controller . NewNetwork ( strings . ToLower ( v . Type ) , name , nid ,
2016-03-09 23:33:21 -05:00
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
2015-05-15 19:34:26 -04:00
}
2017-01-23 06:23:07 -05:00
func initBridgeDriver ( controller libnetwork . NetworkController , config * config . Config ) error {
2016-03-09 23:33:21 -05:00
if _ , err := controller . NetworkByName ( runconfig . DefaultDaemonNetworkMode ( ) . NetworkName ( ) ) ; err == nil {
return nil
2015-07-13 15:34:58 -04:00
}
2016-03-09 23:33:21 -05:00
netOption := map [ string ] string {
winlibnetwork . NetworkName : runconfig . DefaultDaemonNetworkMode ( ) . NetworkName ( ) ,
}
2019-04-17 06:39:07 -04:00
subnetPrefix := defaultNetworkSpace
2017-01-23 06:23:07 -05:00
if config . BridgeConfig . FixedCIDR != "" {
subnetPrefix = config . BridgeConfig . FixedCIDR
2016-03-09 23:33:21 -05:00
}
2019-04-17 06:39:07 -04:00
ipamV4Conf := libnetwork . IpamConf { PreferredPool : subnetPrefix }
v4Conf := [ ] * libnetwork . IpamConf { & ipamV4Conf }
v6Conf := [ ] * libnetwork . IpamConf { }
ipamOption := libnetwork . NetworkOptionIpam ( "default" , "" , v4Conf , v6Conf , nil )
2016-03-09 23:33:21 -05:00
2016-05-08 03:33:16 -04:00
_ , err := controller . NewNetwork ( string ( runconfig . DefaultDaemonNetworkMode ( ) ) , runconfig . DefaultDaemonNetworkMode ( ) . NetworkName ( ) , "" ,
2016-03-09 23:33:21 -05:00
libnetwork . NetworkOptionGeneric ( options . Generic {
netlabel . GenericData : netOption ,
} ) ,
2016-06-09 15:09:13 -04:00
ipamOption ,
2016-03-09 23:33:21 -05:00
)
if err != nil {
return fmt . Errorf ( "Error creating default network: %v" , err )
}
2016-06-09 15:09:13 -04:00
2016-03-09 23:33:21 -05:00
return nil
2015-05-15 19:34:26 -04:00
}
2015-06-23 13:13:42 -04:00
2015-07-30 17:01:53 -04:00
// registerLinks sets up links between containers and writes the
2015-11-16 00:32:31 -05:00
// configuration out for persistence. As of Windows TP4, links are not supported.
2015-12-18 13:36:17 -05:00
func ( daemon * Daemon ) registerLinks ( container * container . Container , hostConfig * containertypes . HostConfig ) error {
2015-06-23 13:13:42 -04:00
return nil
}
2015-07-16 17:14:58 -04:00
2016-03-18 14:53:27 -04:00
func ( daemon * Daemon ) cleanupMountsByID ( in string ) error {
return nil
}
2015-08-03 18:05:34 -04:00
func ( daemon * Daemon ) cleanupMounts ( ) error {
return nil
}
2015-11-02 20:06:09 -05:00
2017-11-16 01:20:33 -05:00
func setupRemappedRoot ( config * config . Config ) ( * idtools . IdentityMapping , error ) {
return & idtools . IdentityMapping { } , nil
2016-01-07 22:43:11 -05:00
}
2017-11-16 01:20:33 -05:00
func setupDaemonRoot ( config * config . Config , rootDir string , rootIdentity idtools . Identity ) error {
2016-01-07 22:43:11 -05:00
config . Root = rootDir
// Create the root directory if it doesn't exists
Simplify/fix MkdirAll usage
This subtle bug keeps lurking in because error checking for `Mkdir()`
and `MkdirAll()` is slightly different wrt to `EEXIST`/`IsExist`:
- for `Mkdir()`, `IsExist` error should (usually) be ignored
(unless you want to make sure directory was not there before)
as it means "the destination directory was already there"
- for `MkdirAll()`, `IsExist` error should NEVER be ignored.
Mostly, this commit just removes ignoring the IsExist error, as it
should not be ignored.
Also, there are a couple of cases then IsExist is handled as
"directory already exist" which is wrong. As a result, some code
that never worked as intended is now removed.
NOTE that `idtools.MkdirAndChown()` behaves like `os.MkdirAll()`
rather than `os.Mkdir()` -- so its description is amended accordingly,
and its usage is handled as such (i.e. IsExist error is not ignored).
For more details, a quote from my runc commit 6f82d4b (July 2015):
TL;DR: check for IsExist(err) after a failed MkdirAll() is both
redundant and wrong -- so two reasons to remove it.
Quoting MkdirAll documentation:
> MkdirAll creates a directory named path, along with any necessary
> parents, and returns nil, or else returns an error. If path
> is already a directory, MkdirAll does nothing and returns nil.
This means two things:
1. If a directory to be created already exists, no error is
returned.
2. If the error returned is IsExist (EEXIST), it means there exists
a non-directory with the same name as MkdirAll need to use for
directory. Example: we want to MkdirAll("a/b"), but file "a"
(or "a/b") already exists, so MkdirAll fails.
The above is a theory, based on quoted documentation and my UNIX
knowledge.
3. In practice, though, current MkdirAll implementation [1] returns
ENOTDIR in most of cases described in #2, with the exception when
there is a race between MkdirAll and someone else creating the
last component of MkdirAll argument as a file. In this very case
MkdirAll() will indeed return EEXIST.
Because of #1, IsExist check after MkdirAll is not needed.
Because of #2 and #3, ignoring IsExist error is just plain wrong,
as directory we require is not created. It's cleaner to report
the error now.
Note this error is all over the tree, I guess due to copy-paste,
or trying to follow the same usage pattern as for Mkdir(),
or some not quite correct examples on the Internet.
[1] https://github.com/golang/go/blob/f9ed2f75/src/os/path.go
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2017-09-25 15:39:36 -04:00
if err := system . MkdirAllWithACL ( config . Root , 0 , system . SddlAdministratorsLocalSystem ) ; err != nil {
2016-01-07 22:43:11 -05:00
return err
}
return nil
}
2016-03-18 23:09:54 -04:00
// runasHyperVContainer returns true if we are going to run as a Hyper-V container
2016-11-01 16:02:46 -04:00
func ( daemon * Daemon ) runAsHyperVContainer ( hostConfig * containertypes . HostConfig ) bool {
if hostConfig . Isolation . IsDefault ( ) {
2016-03-18 14:53:27 -04:00
// Container is set to use the default, so take the default from the daemon configuration
2016-03-18 23:09:54 -04:00
return daemon . defaultIsolation . IsHyperV ( )
2016-03-18 14:53:27 -04:00
}
2016-03-18 23:09:54 -04:00
// Container is requesting an isolation mode. Honour it.
2016-11-01 16:02:46 -04:00
return hostConfig . Isolation . IsHyperV ( )
2016-03-18 23:09:54 -04:00
}
// conditionalMountOnStart is a platform specific helper function during the
// container start to call mount.
func ( daemon * Daemon ) conditionalMountOnStart ( container * container . Container ) error {
Windows: Experimental: Allow containerd for runtime
Signed-off-by: John Howard <jhoward@microsoft.com>
This is the first step in refactoring moby (dockerd) to use containerd on Windows.
Similar to the current model in Linux, this adds the option to enable it for runtime.
It does not switch the graphdriver to containerd snapshotters.
- Refactors libcontainerd to a series of subpackages so that either a
"local" containerd (1) or a "remote" (2) containerd can be loaded as opposed
to conditional compile as "local" for Windows and "remote" for Linux.
- Updates libcontainerd such that Windows has an option to allow the use of a
"remote" containerd. Here, it communicates over a named pipe using GRPC.
This is currently guarded behind the experimental flag, an environment variable,
and the providing of a pipename to connect to containerd.
- Infrastructure pieces such as under pkg/system to have helper functions for
determining whether containerd is being used.
(1) "local" containerd is what the daemon on Windows has used since inception.
It's not really containerd at all - it's simply local invocation of HCS APIs
directly in-process from the daemon through the Microsoft/hcsshim library.
(2) "remote" containerd is what docker on Linux uses for it's runtime. It means
that there is a separate containerd service running, and docker communicates over
GRPC to it.
To try this out, you will need to start with something like the following:
Window 1:
containerd --log-level debug
Window 2:
$env:DOCKER_WINDOWS_CONTAINERD=1
dockerd --experimental -D --containerd \\.\pipe\containerd-containerd
You will need the following binary from github.com/containerd/containerd in your path:
- containerd.exe
You will need the following binaries from github.com/Microsoft/hcsshim in your path:
- runhcs.exe
- containerd-shim-runhcs-v1.exe
For LCOW, it will require and initrd.img and kernel in `C:\Program Files\Linux Containers`.
This is no different to the current requirements. However, you may need updated binaries,
particularly initrd.img built from Microsoft/opengcs as (at the time of writing), Linuxkit
binaries are somewhat out of date.
Note that containerd and hcsshim for HCS v2 APIs do not yet support all the required
functionality needed for docker. This will come in time - this is a baby (although large)
step to migrating Docker on Windows to containerd.
Note that the HCS v2 APIs are only called on RS5+ builds. RS1..RS4 will still use
HCS v1 APIs as the v2 APIs were not fully developed enough on these builds to be usable.
This abstraction is done in HCSShim. (Referring specifically to runtime)
Note the LCOW graphdriver still uses HCS v1 APIs regardless.
Note also that this does not migrate docker to use containerd snapshotters
rather than graphdrivers. This needs to be done in conjunction with Linux also
doing the same switch.
2019-01-08 17:30:52 -05:00
2017-08-08 15:43:48 -04:00
// Bail out now for Linux containers. We cannot mount the containers filesystem on the
// host as it is a non-Windows filesystem.
if system . LCOWSupported ( ) && container . OS != "windows" {
2017-06-16 23:28:18 -04:00
return nil
}
2017-08-08 15:43:48 -04:00
// We do not mount if a Hyper-V container as it needs to be mounted inside the
// utility VM, not the host.
2016-11-01 16:02:46 -04:00
if ! daemon . runAsHyperVContainer ( container . HostConfig ) {
2016-03-18 23:09:54 -04:00
return daemon . Mount ( container )
2015-11-02 20:06:09 -05:00
}
return nil
}
// conditionalUnmountOnCleanup is a platform specific helper function called
// during the cleanup of a container to unmount.
2016-03-18 14:53:27 -04:00
func ( daemon * Daemon ) conditionalUnmountOnCleanup ( container * container . Container ) error {
Windows: Experimental: Allow containerd for runtime
Signed-off-by: John Howard <jhoward@microsoft.com>
This is the first step in refactoring moby (dockerd) to use containerd on Windows.
Similar to the current model in Linux, this adds the option to enable it for runtime.
It does not switch the graphdriver to containerd snapshotters.
- Refactors libcontainerd to a series of subpackages so that either a
"local" containerd (1) or a "remote" (2) containerd can be loaded as opposed
to conditional compile as "local" for Windows and "remote" for Linux.
- Updates libcontainerd such that Windows has an option to allow the use of a
"remote" containerd. Here, it communicates over a named pipe using GRPC.
This is currently guarded behind the experimental flag, an environment variable,
and the providing of a pipename to connect to containerd.
- Infrastructure pieces such as under pkg/system to have helper functions for
determining whether containerd is being used.
(1) "local" containerd is what the daemon on Windows has used since inception.
It's not really containerd at all - it's simply local invocation of HCS APIs
directly in-process from the daemon through the Microsoft/hcsshim library.
(2) "remote" containerd is what docker on Linux uses for it's runtime. It means
that there is a separate containerd service running, and docker communicates over
GRPC to it.
To try this out, you will need to start with something like the following:
Window 1:
containerd --log-level debug
Window 2:
$env:DOCKER_WINDOWS_CONTAINERD=1
dockerd --experimental -D --containerd \\.\pipe\containerd-containerd
You will need the following binary from github.com/containerd/containerd in your path:
- containerd.exe
You will need the following binaries from github.com/Microsoft/hcsshim in your path:
- runhcs.exe
- containerd-shim-runhcs-v1.exe
For LCOW, it will require and initrd.img and kernel in `C:\Program Files\Linux Containers`.
This is no different to the current requirements. However, you may need updated binaries,
particularly initrd.img built from Microsoft/opengcs as (at the time of writing), Linuxkit
binaries are somewhat out of date.
Note that containerd and hcsshim for HCS v2 APIs do not yet support all the required
functionality needed for docker. This will come in time - this is a baby (although large)
step to migrating Docker on Windows to containerd.
Note that the HCS v2 APIs are only called on RS5+ builds. RS1..RS4 will still use
HCS v1 APIs as the v2 APIs were not fully developed enough on these builds to be usable.
This abstraction is done in HCSShim. (Referring specifically to runtime)
Note the LCOW graphdriver still uses HCS v1 APIs regardless.
Note also that this does not migrate docker to use containerd snapshotters
rather than graphdrivers. This needs to be done in conjunction with Linux also
doing the same switch.
2019-01-08 17:30:52 -05:00
2017-06-16 23:28:18 -04:00
// Bail out now for Linux containers
2017-08-08 15:43:48 -04:00
if system . LCOWSupported ( ) && container . OS != "windows" {
2017-06-16 23:28:18 -04:00
return nil
}
2015-11-02 20:06:09 -05:00
// We do not unmount if a Hyper-V container
2016-11-01 16:02:46 -04:00
if ! daemon . runAsHyperVContainer ( container . HostConfig ) {
2016-03-18 14:53:27 -04:00
return daemon . Unmount ( container )
2015-11-18 17:20:54 -05:00
}
2016-03-18 14:53:27 -04:00
return nil
2015-11-18 17:20:54 -05:00
}
2017-01-23 06:23:07 -05:00
func driverOptions ( config * config . Config ) [ ] nwconfig . Option {
2016-03-09 23:33:21 -05:00
return [ ] nwconfig . Option { }
2016-02-17 20:08:11 -05:00
}
2016-03-18 14:53:27 -04:00
func ( daemon * Daemon ) stats ( c * container . Container ) ( * types . StatsJSON , error ) {
2016-09-07 19:08:51 -04:00
if ! c . IsRunning ( ) {
2017-07-19 10:20:13 -04:00
return nil , errNotRunning ( c . ID )
2016-09-07 19:08:51 -04:00
}
// Obtain the stats from HCS via libcontainerd
2017-09-22 09:52:41 -04:00
stats , err := daemon . containerd . Stats ( context . Background ( ) , c . ID )
2016-09-07 19:08:51 -04:00
if err != nil {
2017-07-07 03:33:45 -04:00
if strings . Contains ( err . Error ( ) , "container not found" ) {
2017-07-19 10:20:13 -04:00
return nil , containerNotFound ( c . ID )
2017-07-07 03:33:45 -04:00
}
2016-09-07 19:08:51 -04:00
return nil , err
}
// Start with an empty structure
s := & types . StatsJSON { }
2017-09-22 09:52:41 -04:00
s . Stats . Read = stats . Read
s . Stats . NumProcs = platform . NumProcs ( )
2016-09-07 19:08:51 -04:00
2017-09-22 09:52:41 -04:00
if stats . HCSStats != nil {
hcss := stats . HCSStats
// Populate the CPU/processor statistics
s . CPUStats = types . CPUStats {
CPUUsage : types . CPUUsage {
TotalUsage : hcss . Processor . TotalRuntime100ns ,
UsageInKernelmode : hcss . Processor . RuntimeKernel100ns ,
UsageInUsermode : hcss . Processor . RuntimeKernel100ns ,
} ,
2016-09-07 19:08:51 -04:00
}
2017-09-22 09:52:41 -04:00
// Populate the memory statistics
s . MemoryStats = types . MemoryStats {
Commit : hcss . Memory . UsageCommitBytes ,
CommitPeak : hcss . Memory . UsageCommitPeakBytes ,
PrivateWorkingSet : hcss . Memory . UsagePrivateWorkingSetBytes ,
}
2016-09-07 19:08:51 -04:00
2017-09-22 09:52:41 -04:00
// Populate the storage statistics
s . StorageStats = types . StorageStats {
ReadCountNormalized : hcss . Storage . ReadCountNormalized ,
ReadSizeBytes : hcss . Storage . ReadSizeBytes ,
WriteCountNormalized : hcss . Storage . WriteCountNormalized ,
WriteSizeBytes : hcss . Storage . WriteSizeBytes ,
}
// Populate the network statistics
s . Networks = make ( map [ string ] types . NetworkStats )
for _ , nstats := range hcss . 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 ,
}
}
}
2016-09-07 19:08:51 -04:00
return s , nil
2016-03-18 14:53:27 -04:00
}
// 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" )
2018-10-09 14:58:26 -04:00
// On client SKUs, default to Hyper-V. @engine maintainers. This
2019-09-25 13:51:18 -04:00
// should not be removed. Ping Microsoft folks is there are PRs to
2018-10-09 14:58:26 -04:00
// to change this.
if system . IsWindowsClient ( ) {
2016-04-14 20:12:02 -04:00
daemon . defaultIsolation = containertypes . Isolation ( "hyperv" )
}
2016-03-18 14:53:27 -04:00
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" )
}
2016-04-14 20:12:02 -04:00
if containertypes . Isolation ( val ) . IsProcess ( ) {
2019-04-17 06:39:07 -04:00
if system . IsWindowsClient ( ) && osversion . Build ( ) < osversion . RS5 {
2018-10-09 14:58:26 -04:00
// On RS5, we allow (but don't strictly support) process isolation on Client SKUs.
2016-10-25 13:30:00 -04:00
// @engine maintainers. This block should not be removed. It partially enforces licensing
2019-09-25 13:51:18 -04:00
// restrictions on Windows. Ping Microsoft folks if there are concerns or PRs to change this.
2018-10-09 14:58:26 -04:00
return fmt . Errorf ( "Windows client operating systems earlier than version 1809 can only run Hyper-V containers" )
2016-04-14 20:12:02 -04:00
}
daemon . defaultIsolation = containertypes . Isolation ( "process" )
}
2016-03-18 14:53:27 -04:00
default :
return fmt . Errorf ( "Unrecognised exec-opt '%s'\n" , key )
}
}
logrus . Infof ( "Windows default isolation mode: %s" , daemon . defaultIsolation )
return nil
}
2016-03-21 12:56:51 -04:00
2017-01-23 06:23:07 -05:00
func setupDaemonProcess ( config * config . Config ) error {
2016-07-11 18:26:23 -04:00
return nil
}
2016-09-06 09:49:10 -04:00
2016-09-02 09:20:54 -04:00
func ( daemon * Daemon ) setupSeccompProfile ( ) error {
return nil
}
2017-05-31 20:11:42 -04:00
2017-09-22 09:52:41 -04:00
func ( daemon * Daemon ) loadRuntimes ( ) error {
return nil
}
func ( daemon * Daemon ) initRuntimes ( _ map [ string ] types . Runtime ) error {
return nil
}
2018-07-17 15:11:38 -04:00
func setupResolvConf ( config * config . Config ) {
}