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 }