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 }