2016-05-23 17:49:50 -04:00
package daemon
import (
2016-09-19 17:47:48 -04:00
"fmt"
2016-06-07 15:15:50 -04:00
"io/ioutil"
2016-09-19 17:47:48 -04:00
"path/filepath"
2016-06-07 15:15:50 -04:00
"strings"
2016-09-19 17:47:48 -04:00
2017-08-08 17:21:56 -04:00
"github.com/Microsoft/opengcs/client"
2016-05-23 17:49:50 -04:00
"github.com/docker/docker/container"
2016-09-19 17:47:48 -04:00
"github.com/docker/docker/layer"
2016-05-23 17:49:50 -04:00
"github.com/docker/docker/libcontainerd"
2016-06-07 15:15:50 -04:00
"golang.org/x/sys/windows/registry"
)
const (
credentialSpecRegistryLocation = ` SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs `
credentialSpecFileLocation = "CredentialSpecs"
2016-05-23 17:49:50 -04:00
)
2016-10-05 16:29:56 -04:00
func ( daemon * Daemon ) getLibcontainerdCreateOptions ( container * container . Container ) ( [ ] libcontainerd . CreateOption , error ) {
2016-09-16 13:41:47 -04:00
createOptions := [ ] libcontainerd . CreateOption { }
2016-09-19 17:47:48 -04:00
// Are we going to run as a Hyper-V container?
hvOpts := & libcontainerd . HyperVIsolationOption { }
if container . HostConfig . Isolation . IsDefault ( ) {
// Container is set to use the default, so take the default from the daemon configuration
hvOpts . IsHyperV = daemon . defaultIsolation . IsHyperV ( )
} else {
// Container is requesting an isolation mode. Honour it.
hvOpts . IsHyperV = container . HostConfig . Isolation . IsHyperV ( )
}
2017-01-13 00:09:57 -05:00
dnsSearch := daemon . getDNSSearchSettings ( container )
2016-09-19 17:47:48 -04:00
// Generate the layer folder of the layer options
layerOpts := & libcontainerd . LayerOption { }
m , err := container . RWLayer . Metadata ( )
if err != nil {
return nil , fmt . Errorf ( "failed to get layer metadata - %s" , err )
}
2016-10-04 15:30:07 -04:00
layerOpts . LayerFolderPath = m [ "dir" ]
2016-09-19 17:47:48 -04:00
// Generate the layer paths of the layer options
2017-05-16 19:56:56 -04:00
img , err := daemon . stores [ container . Platform ] . imageStore . Get ( container . ImageID )
2016-09-19 17:47:48 -04:00
if err != nil {
return nil , fmt . Errorf ( "failed to graph.Get on ImageID %s - %s" , container . ImageID , err )
}
// Get the layer path for each layer.
max := len ( img . RootFS . DiffIDs )
for i := 1 ; i <= max ; i ++ {
img . RootFS . DiffIDs = img . RootFS . DiffIDs [ : i ]
2017-05-16 19:56:56 -04:00
layerPath , err := layer . GetLayerPath ( daemon . stores [ container . Platform ] . layerStore , img . RootFS . ChainID ( ) )
2016-09-19 17:47:48 -04:00
if err != nil {
2017-05-16 19:56:56 -04:00
return nil , fmt . Errorf ( "failed to get layer path from graphdriver %s for ImageID %s - %s" , daemon . stores [ container . Platform ] . layerStore , img . RootFS . ChainID ( ) , err )
2016-09-19 17:47:48 -04:00
}
// Reverse order, expecting parent most first
layerOpts . LayerPaths = append ( [ ] string { layerPath } , layerOpts . LayerPaths ... )
}
2016-09-17 21:17:51 -04:00
// Get endpoints for the libnetwork allocated networks to the container
var epList [ ] string
2016-09-12 20:17:09 -04:00
AllowUnqualifiedDNSQuery := false
2016-11-09 20:54:15 -05:00
gwHNSID := ""
2016-09-17 21:17:51 -04:00
if container . NetworkSettings != nil {
for n := range container . NetworkSettings . Networks {
sn , err := daemon . FindNetwork ( n )
if err != nil {
continue
}
ep , err := container . GetEndpointInNetwork ( sn )
if err != nil {
continue
}
data , err := ep . DriverInfo ( )
if err != nil {
continue
}
2016-11-09 20:54:15 -05:00
if data [ "GW_INFO" ] != nil {
gwInfo := data [ "GW_INFO" ] . ( map [ string ] interface { } )
if gwInfo [ "hnsid" ] != nil {
gwHNSID = gwInfo [ "hnsid" ] . ( string )
}
}
2016-09-17 21:17:51 -04:00
if data [ "hnsid" ] != nil {
epList = append ( epList , data [ "hnsid" ] . ( string ) )
}
2016-09-12 20:17:09 -04:00
if data [ "AllowUnqualifiedDNSQuery" ] != nil {
AllowUnqualifiedDNSQuery = true
}
2016-09-17 21:17:51 -04:00
}
}
2016-11-09 20:54:15 -05:00
if gwHNSID != "" {
epList = append ( epList , gwHNSID )
}
2016-06-07 15:15:50 -04:00
// Read and add credentials from the security options if a credential spec has been provided.
if container . HostConfig . SecurityOpt != nil {
for _ , sOpt := range container . HostConfig . SecurityOpt {
sOpt = strings . ToLower ( sOpt )
if ! strings . Contains ( sOpt , "=" ) {
return nil , fmt . Errorf ( "invalid security option: no equals sign in supplied value %s" , sOpt )
}
var splitsOpt [ ] string
splitsOpt = strings . SplitN ( sOpt , "=" , 2 )
if len ( splitsOpt ) != 2 {
return nil , fmt . Errorf ( "invalid security option: %s" , sOpt )
}
if splitsOpt [ 0 ] != "credentialspec" {
return nil , fmt . Errorf ( "security option not supported: %s" , splitsOpt [ 0 ] )
}
credentialsOpts := & libcontainerd . CredentialsOption { }
var (
match bool
csValue string
err error
)
if match , csValue = getCredentialSpec ( "file://" , splitsOpt [ 1 ] ) ; match {
if csValue == "" {
return nil , fmt . Errorf ( "no value supplied for file:// credential spec security option" )
}
if credentialsOpts . Credentials , err = readCredentialSpecFile ( container . ID , daemon . root , filepath . Clean ( csValue ) ) ; err != nil {
return nil , err
}
} else if match , csValue = getCredentialSpec ( "registry://" , splitsOpt [ 1 ] ) ; match {
if csValue == "" {
return nil , fmt . Errorf ( "no value supplied for registry:// credential spec security option" )
}
if credentialsOpts . Credentials , err = readCredentialSpecRegistry ( container . ID , csValue ) ; err != nil {
return nil , err
}
} else {
return nil , fmt . Errorf ( "invalid credential spec security option - value must be prefixed file:// or registry:// followed by a value" )
}
createOptions = append ( createOptions , credentialsOpts )
}
}
2017-08-01 14:57:50 -04:00
// LCOW options.
if container . Platform == "linux" {
config := & client . Config { }
if err := config . GenerateDefault ( daemon . configStore . GraphOptions ) ; err != nil {
return nil , err
}
// Override from user-supplied options.
for k , v := range container . HostConfig . StorageOpt {
switch k {
case "lcow.kirdpath" :
config . KirdPath = v
case "lcow.kernel" :
config . KernelFile = v
case "lcow.initrd" :
config . InitrdFile = v
case "lcow.vhdx" :
config . Vhdx = v
case "lcow.bootparameters" :
config . BootParameters = v
}
}
if err := config . Validate ( ) ; err != nil {
return nil , err
}
lcowOpts := & libcontainerd . LCOWOption {
Config : config ,
}
createOptions = append ( createOptions , lcowOpts )
}
2016-06-07 15:15:50 -04:00
// Now add the remaining options.
2016-09-16 13:41:47 -04:00
createOptions = append ( createOptions , & libcontainerd . FlushOption { IgnoreFlushesDuringBoot : ! container . HasBeenStartedBefore } )
2016-09-19 17:47:48 -04:00
createOptions = append ( createOptions , hvOpts )
createOptions = append ( createOptions , layerOpts )
2017-02-28 23:03:43 -05:00
var networkSharedContainerID string
if container . HostConfig . NetworkMode . IsContainer ( ) {
networkSharedContainerID = container . NetworkSharedContainerID
2017-06-12 18:20:23 -04:00
for _ , ep := range container . SharedEndpointList {
epList = append ( epList , ep )
}
2017-02-28 23:03:43 -05:00
}
2017-06-12 18:20:23 -04:00
2017-02-28 23:03:43 -05:00
createOptions = append ( createOptions , & libcontainerd . NetworkEndpointsOption {
Endpoints : epList ,
AllowUnqualifiedDNSQuery : AllowUnqualifiedDNSQuery ,
DNSSearchList : dnsSearch ,
NetworkSharedContainerID : networkSharedContainerID ,
} )
2016-10-05 16:29:56 -04:00
return createOptions , nil
2016-05-23 17:49:50 -04:00
}
2016-06-07 15:15:50 -04:00
// getCredentialSpec is a helper function to get the value of a credential spec supplied
// on the CLI, stripping the prefix
func getCredentialSpec ( prefix , value string ) ( bool , string ) {
if strings . HasPrefix ( value , prefix ) {
return true , strings . TrimPrefix ( value , prefix )
}
return false , ""
}
// readCredentialSpecRegistry is a helper function to read a credential spec from
// the registry. If not found, we return an empty string and warn in the log.
// This allows for staging on machines which do not have the necessary components.
func readCredentialSpecRegistry ( id , name string ) ( string , error ) {
var (
k registry . Key
err error
val string
)
if k , err = registry . OpenKey ( registry . LOCAL_MACHINE , credentialSpecRegistryLocation , registry . QUERY_VALUE ) ; err != nil {
return "" , fmt . Errorf ( "failed handling spec %q for container %s - %s could not be opened" , name , id , credentialSpecRegistryLocation )
}
if val , _ , err = k . GetStringValue ( name ) ; err != nil {
if err == registry . ErrNotExist {
return "" , fmt . Errorf ( "credential spec %q for container %s as it was not found" , name , id )
}
return "" , fmt . Errorf ( "error %v reading credential spec %q from registry for container %s" , err , name , id )
}
return val , nil
}
// readCredentialSpecFile is a helper function to read a credential spec from
// a file. If not found, we return an empty string and warn in the log.
// This allows for staging on machines which do not have the necessary components.
func readCredentialSpecFile ( id , root , location string ) ( string , error ) {
if filepath . IsAbs ( location ) {
return "" , fmt . Errorf ( "invalid credential spec - file:// path cannot be absolute" )
}
base := filepath . Join ( root , credentialSpecFileLocation )
full := filepath . Join ( base , location )
if ! strings . HasPrefix ( full , base ) {
return "" , fmt . Errorf ( "invalid credential spec - file:// path must be under %s" , base )
}
bcontents , err := ioutil . ReadFile ( full )
if err != nil {
return "" , fmt . Errorf ( "credential spec '%s' for container %s as the file could not be read: %q" , full , id , err )
}
return string ( bcontents [ : ] ) , nil
}