github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/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  	// Generate the layer folder of the layer options
    34  	layerOpts := &libcontainerd.LayerOption{}
    35  	m, err := container.RWLayer.Metadata()
    36  	if err != nil {
    37  		return nil, fmt.Errorf("failed to get layer metadata - %s", err)
    38  	}
    39  	if hvOpts.IsHyperV {
    40  		hvOpts.SandboxPath = filepath.Dir(m["dir"])
    41  	}
    42  
    43  	layerOpts.LayerFolderPath = m["dir"]
    44  
    45  	// Generate the layer paths of the layer options
    46  	img, err := daemon.imageStore.Get(container.ImageID)
    47  	if err != nil {
    48  		return nil, fmt.Errorf("failed to graph.Get on ImageID %s - %s", container.ImageID, err)
    49  	}
    50  	// Get the layer path for each layer.
    51  	max := len(img.RootFS.DiffIDs)
    52  	for i := 1; i <= max; i++ {
    53  		img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i]
    54  		layerPath, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID())
    55  		if err != nil {
    56  			return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err)
    57  		}
    58  		// Reverse order, expecting parent most first
    59  		layerOpts.LayerPaths = append([]string{layerPath}, layerOpts.LayerPaths...)
    60  	}
    61  
    62  	// Get endpoints for the libnetwork allocated networks to the container
    63  	var epList []string
    64  	AllowUnqualifiedDNSQuery := false
    65  	if container.NetworkSettings != nil {
    66  		for n := range container.NetworkSettings.Networks {
    67  			sn, err := daemon.FindNetwork(n)
    68  			if err != nil {
    69  				continue
    70  			}
    71  
    72  			ep, err := container.GetEndpointInNetwork(sn)
    73  			if err != nil {
    74  				continue
    75  			}
    76  
    77  			data, err := ep.DriverInfo()
    78  			if err != nil {
    79  				continue
    80  			}
    81  			if data["hnsid"] != nil {
    82  				epList = append(epList, data["hnsid"].(string))
    83  			}
    84  
    85  			if data["AllowUnqualifiedDNSQuery"] != nil {
    86  				AllowUnqualifiedDNSQuery = true
    87  			}
    88  		}
    89  	}
    90  
    91  	// Read and add credentials from the security options if a credential spec has been provided.
    92  	if container.HostConfig.SecurityOpt != nil {
    93  		for _, sOpt := range container.HostConfig.SecurityOpt {
    94  			sOpt = strings.ToLower(sOpt)
    95  			if !strings.Contains(sOpt, "=") {
    96  				return nil, fmt.Errorf("invalid security option: no equals sign in supplied value %s", sOpt)
    97  			}
    98  			var splitsOpt []string
    99  			splitsOpt = strings.SplitN(sOpt, "=", 2)
   100  			if len(splitsOpt) != 2 {
   101  				return nil, fmt.Errorf("invalid security option: %s", sOpt)
   102  			}
   103  			if splitsOpt[0] != "credentialspec" {
   104  				return nil, fmt.Errorf("security option not supported: %s", splitsOpt[0])
   105  			}
   106  
   107  			credentialsOpts := &libcontainerd.CredentialsOption{}
   108  			var (
   109  				match   bool
   110  				csValue string
   111  				err     error
   112  			)
   113  			if match, csValue = getCredentialSpec("file://", splitsOpt[1]); match {
   114  				if csValue == "" {
   115  					return nil, fmt.Errorf("no value supplied for file:// credential spec security option")
   116  				}
   117  				if credentialsOpts.Credentials, err = readCredentialSpecFile(container.ID, daemon.root, filepath.Clean(csValue)); err != nil {
   118  					return nil, err
   119  				}
   120  			} else if match, csValue = getCredentialSpec("registry://", splitsOpt[1]); match {
   121  				if csValue == "" {
   122  					return nil, fmt.Errorf("no value supplied for registry:// credential spec security option")
   123  				}
   124  				if credentialsOpts.Credentials, err = readCredentialSpecRegistry(container.ID, csValue); err != nil {
   125  					return nil, err
   126  				}
   127  			} else {
   128  				return nil, fmt.Errorf("invalid credential spec security option - value must be prefixed file:// or registry:// followed by a value")
   129  			}
   130  			createOptions = append(createOptions, credentialsOpts)
   131  		}
   132  	}
   133  
   134  	// Now add the remaining options.
   135  	createOptions = append(createOptions, &libcontainerd.FlushOption{IgnoreFlushesDuringBoot: !container.HasBeenStartedBefore})
   136  	createOptions = append(createOptions, hvOpts)
   137  	createOptions = append(createOptions, layerOpts)
   138  	if epList != nil {
   139  		createOptions = append(createOptions, &libcontainerd.NetworkEndpointsOption{Endpoints: epList, AllowUnqualifiedDNSQuery: AllowUnqualifiedDNSQuery})
   140  	}
   141  
   142  	return createOptions, nil
   143  }
   144  
   145  // getCredentialSpec is a helper function to get the value of a credential spec supplied
   146  // on the CLI, stripping the prefix
   147  func getCredentialSpec(prefix, value string) (bool, string) {
   148  	if strings.HasPrefix(value, prefix) {
   149  		return true, strings.TrimPrefix(value, prefix)
   150  	}
   151  	return false, ""
   152  }
   153  
   154  // readCredentialSpecRegistry is a helper function to read a credential spec from
   155  // the registry. If not found, we return an empty string and warn in the log.
   156  // This allows for staging on machines which do not have the necessary components.
   157  func readCredentialSpecRegistry(id, name string) (string, error) {
   158  	var (
   159  		k   registry.Key
   160  		err error
   161  		val string
   162  	)
   163  	if k, err = registry.OpenKey(registry.LOCAL_MACHINE, credentialSpecRegistryLocation, registry.QUERY_VALUE); err != nil {
   164  		return "", fmt.Errorf("failed handling spec %q for container %s - %s could not be opened", name, id, credentialSpecRegistryLocation)
   165  	}
   166  	if val, _, err = k.GetStringValue(name); err != nil {
   167  		if err == registry.ErrNotExist {
   168  			return "", fmt.Errorf("credential spec %q for container %s as it was not found", name, id)
   169  		}
   170  		return "", fmt.Errorf("error %v reading credential spec %q from registry for container %s", err, name, id)
   171  	}
   172  	return val, nil
   173  }
   174  
   175  // readCredentialSpecFile is a helper function to read a credential spec from
   176  // a file. If not found, we return an empty string and warn in the log.
   177  // This allows for staging on machines which do not have the necessary components.
   178  func readCredentialSpecFile(id, root, location string) (string, error) {
   179  	if filepath.IsAbs(location) {
   180  		return "", fmt.Errorf("invalid credential spec - file:// path cannot be absolute")
   181  	}
   182  	base := filepath.Join(root, credentialSpecFileLocation)
   183  	full := filepath.Join(base, location)
   184  	if !strings.HasPrefix(full, base) {
   185  		return "", fmt.Errorf("invalid credential spec - file:// path must be under %s", base)
   186  	}
   187  	bcontents, err := ioutil.ReadFile(full)
   188  	if err != nil {
   189  		return "", fmt.Errorf("credential spec '%s' for container %s as the file could not be read: %q", full, id, err)
   190  	}
   191  	return string(bcontents[:]), nil
   192  }