| package daemon |
| |
| import ( |
| "encoding/json" |
| "errors" |
| "fmt" |
| "os" |
| "path/filepath" |
| "runtime" |
| "strings" |
| |
| "github.com/Microsoft/hcsshim" |
| "github.com/Sirupsen/logrus" |
| "github.com/docker/docker/container" |
| "github.com/docker/docker/daemon/graphdriver" |
| "github.com/docker/docker/daemon/graphdriver/windows" // register the windows graph driver |
| "github.com/docker/docker/dockerversion" |
| "github.com/docker/docker/image" |
| "github.com/docker/docker/layer" |
| "github.com/docker/docker/pkg/idtools" |
| "github.com/docker/docker/pkg/parsers" |
| "github.com/docker/docker/pkg/sysinfo" |
| "github.com/docker/docker/pkg/system" |
| "github.com/docker/docker/reference" |
| "github.com/docker/docker/runconfig" |
| "github.com/docker/engine-api/types" |
| pblkiodev "github.com/docker/engine-api/types/blkiodev" |
| containertypes "github.com/docker/engine-api/types/container" |
| "github.com/docker/libnetwork" |
| nwconfig "github.com/docker/libnetwork/config" |
| winlibnetwork "github.com/docker/libnetwork/drivers/windows" |
| "github.com/docker/libnetwork/netlabel" |
| "github.com/docker/libnetwork/options" |
| blkiodev "github.com/opencontainers/runc/libcontainer/configs" |
| ) |
| |
| const ( |
| defaultNetworkSpace = "172.16.0.0/12" |
| platformSupported = true |
| windowsMinCPUShares = 1 |
| windowsMaxCPUShares = 10000 |
| ) |
| |
| func getBlkioWeightDevices(config *containertypes.HostConfig) ([]blkiodev.WeightDevice, error) { |
| return nil, nil |
| } |
| |
| func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error { |
| return nil |
| } |
| |
| func getBlkioReadIOpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) { |
| return nil, nil |
| } |
| |
| func getBlkioWriteIOpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) { |
| return nil, nil |
| } |
| |
| func getBlkioReadBpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) { |
| return nil, nil |
| } |
| |
| func getBlkioWriteBpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) { |
| return nil, nil |
| } |
| |
| func setupInitLayer(initLayer string, rootUID, rootGID int) error { |
| return nil |
| } |
| |
| func checkKernel() error { |
| return nil |
| } |
| |
| func (daemon *Daemon) getCgroupDriver() string { |
| return "" |
| } |
| |
| // adaptContainerSettings is called during container creation to modify any |
| // settings necessary in the HostConfig structure. |
| func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error { |
| if hostConfig == nil { |
| return nil |
| } |
| |
| if hostConfig.CPUShares < 0 { |
| logrus.Warnf("Changing requested CPUShares of %d to minimum allowed of %d", hostConfig.CPUShares, windowsMinCPUShares) |
| hostConfig.CPUShares = windowsMinCPUShares |
| } else if hostConfig.CPUShares > windowsMaxCPUShares { |
| logrus.Warnf("Changing requested CPUShares of %d to maximum allowed of %d", hostConfig.CPUShares, windowsMaxCPUShares) |
| hostConfig.CPUShares = windowsMaxCPUShares |
| } |
| |
| return nil |
| } |
| |
| func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysinfo.SysInfo) ([]string, error) { |
| warnings := []string{} |
| |
| // cpu subsystem checks and adjustments |
| if resources.CPUPercent < 0 || resources.CPUPercent > 100 { |
| return warnings, fmt.Errorf("Range of CPU percent is from 1 to 100") |
| } |
| |
| if resources.CPUPercent > 0 && resources.CPUShares > 0 { |
| return warnings, fmt.Errorf("Conflicting options: CPU Shares and CPU Percent cannot both be set") |
| } |
| |
| // TODO Windows: Add more validation of resource settings not supported on Windows |
| |
| if resources.BlkioWeight > 0 { |
| warnings = append(warnings, "Windows does not support Block I/O weight. Weight discarded.") |
| logrus.Warn("Windows does not support Block I/O weight. --blkio-weight discarded.") |
| resources.BlkioWeight = 0 |
| } |
| if len(resources.BlkioWeightDevice) > 0 { |
| warnings = append(warnings, "Windows does not support Block I/O weight_device.") |
| logrus.Warn("Windows does not support Block I/O weight_device. --blkio-weight-device discarded.") |
| resources.BlkioWeightDevice = []*pblkiodev.WeightDevice{} |
| } |
| if len(resources.BlkioDeviceReadBps) > 0 { |
| warnings = append(warnings, "Windows does not support Block read limit in bytes per second.") |
| logrus.Warn("Windows does not support Block I/O read limit in bytes per second. --device-read-bps discarded.") |
| resources.BlkioDeviceReadBps = []*pblkiodev.ThrottleDevice{} |
| } |
| if len(resources.BlkioDeviceWriteBps) > 0 { |
| warnings = append(warnings, "Windows does not support Block write limit in bytes per second.") |
| logrus.Warn("Windows does not support Block I/O write limit in bytes per second. --device-write-bps discarded.") |
| resources.BlkioDeviceWriteBps = []*pblkiodev.ThrottleDevice{} |
| } |
| if len(resources.BlkioDeviceReadIOps) > 0 { |
| warnings = append(warnings, "Windows does not support Block read limit in IO per second.") |
| logrus.Warn("Windows does not support Block I/O read limit in IO per second. -device-read-iops discarded.") |
| resources.BlkioDeviceReadIOps = []*pblkiodev.ThrottleDevice{} |
| } |
| if len(resources.BlkioDeviceWriteIOps) > 0 { |
| warnings = append(warnings, "Windows does not support Block write limit in IO per second.") |
| logrus.Warn("Windows does not support Block I/O write limit in IO per second. --device-write-iops discarded.") |
| resources.BlkioDeviceWriteIOps = []*pblkiodev.ThrottleDevice{} |
| } |
| return warnings, nil |
| } |
| |
| // 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{} |
| |
| w, err := verifyContainerResources(&hostConfig.Resources, nil) |
| warnings = append(warnings, w...) |
| if err != nil { |
| return warnings, err |
| } |
| |
| return warnings, nil |
| } |
| |
| // platformReload update configuration with platform specific options |
| func (daemon *Daemon) platformReload(config *Config, attributes *map[string]string) { |
| } |
| |
| // verifyDaemonSettings performs validation of daemon config struct |
| func verifyDaemonSettings(config *Config) error { |
| return nil |
| } |
| |
| // checkSystem validates platform-specific requirements |
| func checkSystem() error { |
| // Validate the OS version. Note that docker.exe must be manifested for this |
| // call to return the correct version. |
| osv := system.GetOSVersion() |
| if osv.MajorVersion < 10 { |
| return fmt.Errorf("This version of Windows does not support the docker daemon") |
| } |
| if osv.Build < 14300 { |
| return fmt.Errorf("The Windows daemon requires Windows Server 2016 Technical Preview 5 build 14300 or later") |
| } |
| return nil |
| } |
| |
| // configureKernelSecuritySupport configures and validate security support for the kernel |
| func configureKernelSecuritySupport(config *Config, driverName string) error { |
| return nil |
| } |
| |
| // configureMaxThreads sets the Go runtime max threads threshold |
| func configureMaxThreads(config *Config) error { |
| return nil |
| } |
| |
| func (daemon *Daemon) initNetworkController(config *Config, activeSandboxes map[string]interface{}) (libnetwork.NetworkController, error) { |
| netOptions, err := daemon.networkOptions(config, nil) |
| 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 { |
| err = v.Delete() |
| if err != nil { |
| return nil, err |
| } |
| } |
| } |
| |
| _, err = controller.NewNetwork("null", "none", "", libnetwork.NetworkOptionPersist(false)) |
| if err != nil { |
| return nil, err |
| } |
| |
| // 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 { |
| v.Name = n.Name() |
| 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 |
| // There is only one nat network supported in windows. |
| // If it exists with a different name add it as the default name |
| if runconfig.DefaultDaemonNetworkMode() == containertypes.NetworkMode(strings.ToLower(v.Type)) { |
| name = runconfig.DefaultDaemonNetworkMode().NetworkName() |
| } |
| |
| v6Conf := []*libnetwork.IpamConf{} |
| _, err := controller.NewNetwork(strings.ToLower(v.Type), name, "", |
| 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 |
| } |
| |
| func initBridgeDriver(controller libnetwork.NetworkController, config *Config) error { |
| if _, err := controller.NetworkByName(runconfig.DefaultDaemonNetworkMode().NetworkName()); err == nil { |
| return nil |
| } |
| |
| netOption := map[string]string{ |
| winlibnetwork.NetworkName: runconfig.DefaultDaemonNetworkMode().NetworkName(), |
| } |
| |
| ipamV4Conf := libnetwork.IpamConf{} |
| if config.bridgeConfig.FixedCIDR == "" { |
| ipamV4Conf.PreferredPool = defaultNetworkSpace |
| } else { |
| ipamV4Conf.PreferredPool = config.bridgeConfig.FixedCIDR |
| } |
| |
| v4Conf := []*libnetwork.IpamConf{&ipamV4Conf} |
| v6Conf := []*libnetwork.IpamConf{} |
| |
| _, err := controller.NewNetwork(string(runconfig.DefaultDaemonNetworkMode()), runconfig.DefaultDaemonNetworkMode().NetworkName(), "", |
| libnetwork.NetworkOptionGeneric(options.Generic{ |
| netlabel.GenericData: netOption, |
| }), |
| libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil), |
| ) |
| |
| if err != nil { |
| return fmt.Errorf("Error creating default network: %v", err) |
| } |
| return nil |
| } |
| |
| // registerLinks sets up links between containers and writes the |
| // configuration out for persistence. As of Windows TP4, links are not supported. |
| func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error { |
| return nil |
| } |
| |
| func (daemon *Daemon) cleanupMountsByID(in string) error { |
| return nil |
| } |
| |
| func (daemon *Daemon) cleanupMounts() error { |
| return nil |
| } |
| |
| 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 |
| if err := system.MkdirAll(config.Root, 0700); err != nil && !os.IsExist(err) { |
| return err |
| } |
| return nil |
| } |
| |
| // runasHyperVContainer returns true if we are going to run as a Hyper-V container |
| func (daemon *Daemon) runAsHyperVContainer(container *container.Container) bool { |
| if container.HostConfig.Isolation.IsDefault() { |
| // Container is set to use the default, so take the default from the daemon configuration |
| return daemon.defaultIsolation.IsHyperV() |
| } |
| |
| // Container is requesting an isolation mode. Honour it. |
| return container.HostConfig.Isolation.IsHyperV() |
| |
| } |
| |
| // conditionalMountOnStart is a platform specific helper function during the |
| // container start to call mount. |
| func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error { |
| // We do not mount if a Hyper-V container |
| if !daemon.runAsHyperVContainer(container) { |
| return daemon.Mount(container) |
| } |
| return nil |
| } |
| |
| // conditionalUnmountOnCleanup is a platform specific helper function called |
| // during the cleanup of a container to unmount. |
| func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error { |
| // We do not unmount if a Hyper-V container |
| if !daemon.runAsHyperVContainer(container) { |
| return daemon.Unmount(container) |
| } |
| return nil |
| } |
| |
| func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) error { |
| type graphDriverStore interface { |
| GraphDriver() graphdriver.Driver |
| } |
| |
| gds, ok := ls.(graphDriverStore) |
| if !ok { |
| return nil |
| } |
| |
| driver := gds.GraphDriver() |
| wd, ok := driver.(*windows.Driver) |
| if !ok { |
| return nil |
| } |
| |
| imageInfos, err := wd.GetCustomImageInfos() |
| if err != nil { |
| return err |
| } |
| |
| // Convert imageData to valid image configuration |
| for _, info := range imageInfos { |
| name := strings.ToLower(info.Name) |
| |
| type registrar interface { |
| RegisterDiffID(graphID string, size int64) (layer.Layer, error) |
| } |
| r, ok := ls.(registrar) |
| if !ok { |
| return errors.New("Layerstore doesn't support RegisterDiffID") |
| } |
| if _, err := r.RegisterDiffID(info.ID, info.Size); err != nil { |
| return err |
| } |
| // layer is intentionally not released |
| |
| rootFS := image.NewRootFSWithBaseLayer(filepath.Base(info.Path)) |
| |
| // Create history for base layer |
| config, err := json.Marshal(&image.Image{ |
| V1Image: image.V1Image{ |
| DockerVersion: dockerversion.Version, |
| Architecture: runtime.GOARCH, |
| OS: runtime.GOOS, |
| Created: info.CreatedTime, |
| }, |
| RootFS: rootFS, |
| History: []image.History{}, |
| OSVersion: info.OSVersion, |
| OSFeatures: info.OSFeatures, |
| }) |
| |
| named, err := reference.ParseNamed(name) |
| if err != nil { |
| return err |
| } |
| |
| ref, err := reference.WithTag(named, info.Version) |
| if err != nil { |
| return err |
| } |
| |
| id, err := is.Create(config) |
| if err != nil { |
| logrus.Warnf("Failed to restore custom image %s with error: %s.", name, err) |
| logrus.Warnf("Skipping image %s...", name) |
| continue |
| } |
| |
| if err := rs.AddTag(ref, id, true); err != nil { |
| return err |
| } |
| |
| logrus.Debugf("Registered base layer %s as %s", ref, id) |
| } |
| 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 { |
| daemon.defaultIsolation = containertypes.Isolation("process") |
| // On client SKUs, default to Hyper-V |
| if system.IsWindowsClient() { |
| daemon.defaultIsolation = containertypes.Isolation("hyperv") |
| } |
| 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") |
| } |
| if containertypes.Isolation(val).IsProcess() { |
| if system.IsWindowsClient() { |
| return fmt.Errorf("Windows client operating systems only support Hyper-V containers") |
| } |
| daemon.defaultIsolation = containertypes.Isolation("process") |
| } |
| default: |
| return fmt.Errorf("Unrecognised exec-opt '%s'\n", key) |
| } |
| } |
| |
| logrus.Infof("Windows default isolation mode: %s", daemon.defaultIsolation) |
| return nil |
| } |
| |
| func rootFSToAPIType(rootfs *image.RootFS) types.RootFS { |
| var layers []string |
| for _, l := range rootfs.DiffIDs { |
| layers = append(layers, l.String()) |
| } |
| return types.RootFS{ |
| Type: rootfs.Type, |
| Layers: layers, |
| BaseLayer: rootfs.BaseLayer, |
| } |
| } |
| |
| func setupDaemonProcess(config *Config) error { |
| return nil |
| } |