github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/daemon/start_windows.go (about)

     1  package daemon
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/docker/docker/container"
    10  	"github.com/docker/docker/layer"
    11  	"github.com/docker/docker/libcontainerd"
    12  	"golang.org/x/sys/windows/registry"
    13  )
    14  
    15  const (
    16  	credentialSpecRegistryLocation = `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs`
    17  	credentialSpecFileLocation     = "CredentialSpecs"
    18  )
    19  
    20  func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) ([]libcontainerd.CreateOption, error) {
    21  	createOptions := []libcontainerd.CreateOption{}
    22  
    23  	// Are we going to run as a Hyper-V container?
    24  	hvOpts := &libcontainerd.HyperVIsolationOption{}
    25  	if container.HostConfig.Isolation.IsDefault() {
    26  		// Container is set to use the default, so take the default from the daemon configuration
    27  		hvOpts.IsHyperV = daemon.defaultIsolation.IsHyperV()
    28  	} else {
    29  		// Container is requesting an isolation mode. Honour it.
    30  		hvOpts.IsHyperV = container.HostConfig.Isolation.IsHyperV()
    31  	}
    32  
    33  	dnsSearch := daemon.getDNSSearchSettings(container)
    34  
    35  	// Generate the layer folder of the layer options
    36  	layerOpts := &libcontainerd.LayerOption{}
    37  	m, err := container.RWLayer.Metadata()
    38  	if err != nil {
    39  		return nil, fmt.Errorf("failed to get layer metadata - %s", err)
    40  	}
    41  	layerOpts.LayerFolderPath = m["dir"]
    42  
    43  	// Generate the layer paths of the layer options
    44  	img, err := daemon.imageStore.Get(container.ImageID)
    45  	if err != nil {
    46  		return nil, fmt.Errorf("failed to graph.Get on ImageID %s - %s", container.ImageID, err)
    47  	}
    48  	// Get the layer path for each layer.
    49  	max := len(img.RootFS.DiffIDs)
    50  	for i := 1; i <= max; i++ {
    51  		img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i]
    52  		layerPath, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID())
    53  		if err != nil {
    54  			return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err)
    55  		}
    56  		// Reverse order, expecting parent most first
    57  		layerOpts.LayerPaths = append([]string{layerPath}, layerOpts.LayerPaths...)
    58  	}
    59  
    60  	// Get endpoints for the libnetwork allocated networks to the container
    61  	var epList []string
    62  	AllowUnqualifiedDNSQuery := false
    63  	gwHNSID := ""
    64  	if container.NetworkSettings != nil {
    65  		for n := range container.NetworkSettings.Networks {
    66  			sn, err := daemon.FindNetwork(n)
    67  			if err != nil {
    68  				continue
    69  			}
    70  
    71  			ep, err := container.GetEndpointInNetwork(sn)
    72  			if err != nil {
    73  				continue
    74  			}
    75  
    76  			data, err := ep.DriverInfo()
    77  			if err != nil {
    78  				continue
    79  			}
    80  
    81  			if data["GW_INFO"] != nil {
    82  				gwInfo := data["GW_INFO"].(map[string]interface{})
    83  				if gwInfo["hnsid"] != nil {
    84  					gwHNSID = gwInfo["hnsid"].(string)
    85  				}
    86  			}
    87  
    88  			if data["hnsid"] != nil {
    89  				epList = append(epList, data["hnsid"].(string))
    90  			}
    91  
    92  			if data["AllowUnqualifiedDNSQuery"] != nil {
    93  				AllowUnqualifiedDNSQuery = true
    94  			}
    95  		}
    96  	}
    97  
    98  	if gwHNSID != "" {
    99  		epList = append(epList, gwHNSID)
   100  	}
   101  
   102  	// Read and add credentials from the security options if a credential spec has been provided.
   103  	if container.HostConfig.SecurityOpt != nil {
   104  		for _, sOpt := range container.HostConfig.SecurityOpt {
   105  			sOpt = strings.ToLower(sOpt)
   106  			if !strings.Contains(sOpt, "=") {
   107  				return nil, fmt.Errorf("invalid security option: no equals sign in supplied value %s", sOpt)
   108  			}
   109  			var splitsOpt []string
   110  			splitsOpt = strings.SplitN(sOpt, "=", 2)
   111  			if len(splitsOpt) != 2 {
   112  				return nil, fmt.Errorf("invalid security option: %s", sOpt)
   113  			}
   114  			if splitsOpt[0] != "credentialspec" {
   115  				return nil, fmt.Errorf("security option not supported: %s", splitsOpt[0])
   116  			}
   117  
   118  			credentialsOpts := &libcontainerd.CredentialsOption{}
   119  			var (
   120  				match   bool
   121  				csValue string
   122  				err     error
   123  			)
   124  			if match, csValue = getCredentialSpec("file://", splitsOpt[1]); match {
   125  				if csValue == "" {
   126  					return nil, fmt.Errorf("no value supplied for file:// credential spec security option")
   127  				}
   128  				if credentialsOpts.Credentials, err = readCredentialSpecFile(container.ID, daemon.root, filepath.Clean(csValue)); err != nil {
   129  					return nil, err
   130  				}
   131  			} else if match, csValue = getCredentialSpec("registry://", splitsOpt[1]); match {
   132  				if csValue == "" {
   133  					return nil, fmt.Errorf("no value supplied for registry:// credential spec security option")
   134  				}
   135  				if credentialsOpts.Credentials, err = readCredentialSpecRegistry(container.ID, csValue); err != nil {
   136  					return nil, err
   137  				}
   138  			} else {
   139  				return nil, fmt.Errorf("invalid credential spec security option - value must be prefixed file:// or registry:// followed by a value")
   140  			}
   141  			createOptions = append(createOptions, credentialsOpts)
   142  		}
   143  	}
   144  
   145  	// Now add the remaining options.
   146  	createOptions = append(createOptions, &libcontainerd.FlushOption{IgnoreFlushesDuringBoot: !container.HasBeenStartedBefore})
   147  	createOptions = append(createOptions, hvOpts)
   148  	createOptions = append(createOptions, layerOpts)
   149  
   150  	var networkSharedContainerID string
   151  	if container.HostConfig.NetworkMode.IsContainer() {
   152  		networkSharedContainerID = container.NetworkSharedContainerID
   153  	}
   154  	createOptions = append(createOptions, &libcontainerd.NetworkEndpointsOption{
   155  		Endpoints:                epList,
   156  		AllowUnqualifiedDNSQuery: AllowUnqualifiedDNSQuery,
   157  		DNSSearchList:            dnsSearch,
   158  		NetworkSharedContainerID: networkSharedContainerID,
   159  	})
   160  	return createOptions, nil
   161  }
   162  
   163  // getCredentialSpec is a helper function to get the value of a credential spec supplied
   164  // on the CLI, stripping the prefix
   165  func getCredentialSpec(prefix, value string) (bool, string) {
   166  	if strings.HasPrefix(value, prefix) {
   167  		return true, strings.TrimPrefix(value, prefix)
   168  	}
   169  	return false, ""
   170  }
   171  
   172  // readCredentialSpecRegistry is a helper function to read a credential spec from
   173  // the registry. If not found, we return an empty string and warn in the log.
   174  // This allows for staging on machines which do not have the necessary components.
   175  func readCredentialSpecRegistry(id, name string) (string, error) {
   176  	var (
   177  		k   registry.Key
   178  		err error
   179  		val string
   180  	)
   181  	if k, err = registry.OpenKey(registry.LOCAL_MACHINE, credentialSpecRegistryLocation, registry.QUERY_VALUE); err != nil {
   182  		return "", fmt.Errorf("failed handling spec %q for container %s - %s could not be opened", name, id, credentialSpecRegistryLocation)
   183  	}
   184  	if val, _, err = k.GetStringValue(name); err != nil {
   185  		if err == registry.ErrNotExist {
   186  			return "", fmt.Errorf("credential spec %q for container %s as it was not found", name, id)
   187  		}
   188  		return "", fmt.Errorf("error %v reading credential spec %q from registry for container %s", err, name, id)
   189  	}
   190  	return val, nil
   191  }
   192  
   193  // readCredentialSpecFile is a helper function to read a credential spec from
   194  // a file. If not found, we return an empty string and warn in the log.
   195  // This allows for staging on machines which do not have the necessary components.
   196  func readCredentialSpecFile(id, root, location string) (string, error) {
   197  	if filepath.IsAbs(location) {
   198  		return "", fmt.Errorf("invalid credential spec - file:// path cannot be absolute")
   199  	}
   200  	base := filepath.Join(root, credentialSpecFileLocation)
   201  	full := filepath.Join(base, location)
   202  	if !strings.HasPrefix(full, base) {
   203  		return "", fmt.Errorf("invalid credential spec - file:// path must be under %s", base)
   204  	}
   205  	bcontents, err := ioutil.ReadFile(full)
   206  	if err != nil {
   207  		return "", fmt.Errorf("credential spec '%s' for container %s as the file could not be read: %q", full, id, err)
   208  	}
   209  	return string(bcontents[:]), nil
   210  }