moby/daemon/daemon_windows.go
John Starks 194eaa5c0f Add os_version and os_features to Image
These fields are needed to specify the exact version of Windows that an
image can run on. They may be useful for other platforms in the future.

This also changes image.store.Create to validate that the loaded image is
supported on the current machine. This change affects Linux as well, since
it now validates the architecture and OS fields.

Signed-off-by: John Starks <jostarks@microsoft.com>
2016-04-04 13:14:57 -07:00

463 lines
13 KiB
Go

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/dockerversion"
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
"github.com/docker/docker/reference"
"github.com/docker/docker/runconfig"
// register the windows graph driver
"github.com/docker/docker/daemon/graphdriver/windows"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/pkg/system"
"github.com/docker/engine-api/types"
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 (
defaultVirtualSwitch = "Virtual Switch"
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
}
// 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) {
return nil, nil
}
// 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 < 10586 {
return fmt.Errorf("The Windows daemon requires Windows Server 2016 Technical Preview 4, build 10586 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) (libnetwork.NetworkController, error) {
// TODO Windows: Remove this check once TP4 is no longer supported
osv := system.GetOSVersion()
if osv.Build < 14260 {
// Set the name of the virtual switch if not specified by -b on daemon start
if config.bridgeConfig.Iface == "" {
config.bridgeConfig.Iface = defaultVirtualSwitch
}
logrus.Warnf("Network controller is not supported by the current platform build version")
return nil, nil
}
netOptions, err := daemon.networkOptions(config)
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.NewRootFS()
rootFS.BaseLayer = 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 {
return err
}
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")
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")
}
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,
}
}