2015-11-12 14:55:17 -05:00
package daemon
2016-03-09 23:33:21 -05:00
import (
"errors"
"fmt"
"net"
"os"
"path"
"strings"
"github.com/Sirupsen/logrus"
2016-09-15 13:15:57 -04:00
derr "github.com/docker/docker/api/errors"
2016-09-06 14:18:12 -04:00
containertypes "github.com/docker/docker/api/types/container"
networktypes "github.com/docker/docker/api/types/network"
2016-03-09 23:33:21 -05:00
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/network"
2016-04-08 18:25:07 -04:00
"github.com/docker/docker/pkg/stringid"
2016-03-09 23:33:21 -05:00
"github.com/docker/docker/runconfig"
"github.com/docker/go-connections/nat"
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/options"
"github.com/docker/libnetwork/types"
)
2015-11-12 14:55:17 -05:00
var (
// ErrRootFSReadOnly is returned when a container
// rootfs is marked readonly.
ErrRootFSReadOnly = errors . New ( "container rootfs is marked read-only" )
2016-05-25 16:47:38 -04:00
getPortMapInfo = container . GetSandboxPortMapInfo
2015-11-12 14:55:17 -05:00
)
2016-03-09 23:33:21 -05:00
2016-06-14 12:13:53 -04:00
func ( daemon * Daemon ) buildSandboxOptions ( container * container . Container ) ( [ ] libnetwork . SandboxOption , error ) {
2016-03-09 23:33:21 -05:00
var (
sboxOptions [ ] libnetwork . SandboxOption
err error
dns [ ] string
dnsSearch [ ] string
dnsOptions [ ] string
bindings = make ( nat . PortMap )
pbList [ ] types . PortBinding
exposeList [ ] types . TransportPort
)
defaultNetName := runconfig . DefaultDaemonNetworkMode ( ) . NetworkName ( )
sboxOptions = append ( sboxOptions , libnetwork . OptionHostname ( container . Config . Hostname ) ,
libnetwork . OptionDomainname ( container . Config . Domainname ) )
if container . HostConfig . NetworkMode . IsHost ( ) {
sboxOptions = append ( sboxOptions , libnetwork . OptionUseDefaultSandbox ( ) )
2016-05-24 21:49:11 -04:00
if len ( container . HostConfig . ExtraHosts ) == 0 {
sboxOptions = append ( sboxOptions , libnetwork . OptionOriginHostsPath ( "/etc/hosts" ) )
}
2016-04-29 01:46:57 -04:00
if len ( container . HostConfig . DNS ) == 0 && len ( daemon . configStore . DNS ) == 0 &&
len ( container . HostConfig . DNSSearch ) == 0 && len ( daemon . configStore . DNSSearch ) == 0 &&
len ( container . HostConfig . DNSOptions ) == 0 && len ( daemon . configStore . DNSOptions ) == 0 {
sboxOptions = append ( sboxOptions , libnetwork . OptionOriginResolvConfPath ( "/etc/resolv.conf" ) )
}
2016-04-06 16:45:43 -04:00
} else {
// OptionUseExternalKey is mandatory for userns support.
// But optional for non-userns support
sboxOptions = append ( sboxOptions , libnetwork . OptionUseExternalKey ( ) )
2016-03-09 23:33:21 -05:00
}
container . HostsPath , err = container . GetRootResourcePath ( "hosts" )
if err != nil {
return nil , err
}
sboxOptions = append ( sboxOptions , libnetwork . OptionHostsPath ( container . HostsPath ) )
container . ResolvConfPath , err = container . GetRootResourcePath ( "resolv.conf" )
if err != nil {
return nil , err
}
sboxOptions = append ( sboxOptions , libnetwork . OptionResolvConfPath ( container . ResolvConfPath ) )
if len ( container . HostConfig . DNS ) > 0 {
dns = container . HostConfig . DNS
} else if len ( daemon . configStore . DNS ) > 0 {
dns = daemon . configStore . DNS
}
for _ , d := range dns {
sboxOptions = append ( sboxOptions , libnetwork . OptionDNS ( d ) )
}
if len ( container . HostConfig . DNSSearch ) > 0 {
dnsSearch = container . HostConfig . DNSSearch
} else if len ( daemon . configStore . DNSSearch ) > 0 {
dnsSearch = daemon . configStore . DNSSearch
}
for _ , ds := range dnsSearch {
sboxOptions = append ( sboxOptions , libnetwork . OptionDNSSearch ( ds ) )
}
if len ( container . HostConfig . DNSOptions ) > 0 {
dnsOptions = container . HostConfig . DNSOptions
} else if len ( daemon . configStore . DNSOptions ) > 0 {
dnsOptions = daemon . configStore . DNSOptions
}
for _ , ds := range dnsOptions {
sboxOptions = append ( sboxOptions , libnetwork . OptionDNSOptions ( ds ) )
}
if container . NetworkSettings . SecondaryIPAddresses != nil {
name := container . Config . Hostname
if container . Config . Domainname != "" {
name = name + "." + container . Config . Domainname
}
for _ , a := range container . NetworkSettings . SecondaryIPAddresses {
sboxOptions = append ( sboxOptions , libnetwork . OptionExtraHost ( name , a . Addr ) )
}
}
for _ , extraHost := range container . HostConfig . ExtraHosts {
// allow IPv6 addresses in extra hosts; only split on first ":"
parts := strings . SplitN ( extraHost , ":" , 2 )
sboxOptions = append ( sboxOptions , libnetwork . OptionExtraHost ( parts [ 0 ] , parts [ 1 ] ) )
}
if container . HostConfig . PortBindings != nil {
for p , b := range container . HostConfig . PortBindings {
bindings [ p ] = [ ] nat . PortBinding { }
for _ , bb := range b {
bindings [ p ] = append ( bindings [ p ] , nat . PortBinding {
HostIP : bb . HostIP ,
HostPort : bb . HostPort ,
} )
}
}
}
portSpecs := container . Config . ExposedPorts
ports := make ( [ ] nat . Port , len ( portSpecs ) )
var i int
for p := range portSpecs {
ports [ i ] = p
i ++
}
nat . SortPortMap ( ports , bindings )
for _ , port := range ports {
expose := types . TransportPort { }
expose . Proto = types . ParseProtocol ( port . Proto ( ) )
expose . Port = uint16 ( port . Int ( ) )
exposeList = append ( exposeList , expose )
pb := types . PortBinding { Port : expose . Port , Proto : expose . Proto }
binding := bindings [ port ]
for i := 0 ; i < len ( binding ) ; i ++ {
pbCopy := pb . GetCopy ( )
newP , err := nat . NewPort ( nat . SplitProtoPort ( binding [ i ] . HostPort ) )
var portStart , portEnd int
if err == nil {
portStart , portEnd , err = newP . Range ( )
}
if err != nil {
return nil , fmt . Errorf ( "Error parsing HostPort value(%s):%v" , binding [ i ] . HostPort , err )
}
pbCopy . HostPort = uint16 ( portStart )
pbCopy . HostPortEnd = uint16 ( portEnd )
pbCopy . HostIP = net . ParseIP ( binding [ i ] . HostIP )
pbList = append ( pbList , pbCopy )
}
if container . HostConfig . PublishAllPorts && len ( binding ) == 0 {
pbList = append ( pbList , pb )
}
}
sboxOptions = append ( sboxOptions ,
libnetwork . OptionPortMapping ( pbList ) ,
libnetwork . OptionExposedPorts ( exposeList ) )
2016-03-11 05:13:37 -05:00
// Legacy Link feature is supported only for the default bridge network.
2016-03-09 23:33:21 -05:00
// return if this call to build join options is not for default bridge network
2016-06-14 12:13:53 -04:00
// Legacy Link is only supported by docker run --link
2016-07-22 18:42:26 -04:00
bridgeSettings , ok := container . NetworkSettings . Networks [ defaultNetName ]
2016-08-23 19:50:15 -04:00
if ! ok || bridgeSettings . EndpointSettings == nil {
2016-03-09 23:33:21 -05:00
return sboxOptions , nil
}
2016-07-22 18:42:26 -04:00
if bridgeSettings . EndpointID == "" {
2016-03-09 23:33:21 -05:00
return sboxOptions , nil
}
2016-06-14 12:13:53 -04:00
var (
childEndpoints , parentEndpoints [ ] string
cEndpointID string
)
2016-03-09 23:33:21 -05:00
children := daemon . children ( container )
for linkAlias , child := range children {
if ! isLinkable ( child ) {
return nil , fmt . Errorf ( "Cannot link to %s, as it does not belong to the default network" , child . Name )
}
_ , alias := path . Split ( linkAlias )
// allow access to the linked container via the alias, real name, and container hostname
aliasList := alias + " " + child . Config . Hostname
// only add the name if alias isn't equal to the name
if alias != child . Name [ 1 : ] {
aliasList = aliasList + " " + child . Name [ 1 : ]
}
sboxOptions = append ( sboxOptions , libnetwork . OptionExtraHost ( aliasList , child . NetworkSettings . Networks [ defaultNetName ] . IPAddress ) )
2016-06-14 12:13:53 -04:00
cEndpointID = child . NetworkSettings . Networks [ defaultNetName ] . EndpointID
if cEndpointID != "" {
childEndpoints = append ( childEndpoints , cEndpointID )
2016-03-09 23:33:21 -05:00
}
}
for alias , parent := range daemon . parents ( container ) {
if daemon . configStore . DisableBridge || ! container . HostConfig . NetworkMode . IsPrivate ( ) {
continue
}
_ , alias = path . Split ( alias )
logrus . Debugf ( "Update /etc/hosts of %s for alias %s with ip %s" , parent . ID , alias , bridgeSettings . IPAddress )
sboxOptions = append ( sboxOptions , libnetwork . OptionParentUpdate (
parent . ID ,
alias ,
bridgeSettings . IPAddress ,
) )
2016-06-14 12:13:53 -04:00
if cEndpointID != "" {
parentEndpoints = append ( parentEndpoints , cEndpointID )
2016-03-09 23:33:21 -05:00
}
}
linkOptions := options . Generic {
netlabel . GenericData : options . Generic {
"ParentEndpoints" : parentEndpoints ,
"ChildEndpoints" : childEndpoints ,
} ,
}
sboxOptions = append ( sboxOptions , libnetwork . OptionGeneric ( linkOptions ) )
return sboxOptions , nil
}
2016-08-23 19:50:15 -04:00
func ( daemon * Daemon ) updateNetworkSettings ( container * container . Container , n libnetwork . Network , endpointConfig * networktypes . EndpointSettings ) error {
2016-03-09 23:33:21 -05:00
if container . NetworkSettings == nil {
2016-08-23 19:50:15 -04:00
container . NetworkSettings = & network . Settings { Networks : make ( map [ string ] * network . EndpointSettings ) }
2016-03-09 23:33:21 -05:00
}
if ! container . HostConfig . NetworkMode . IsHost ( ) && containertypes . NetworkMode ( n . Type ( ) ) . IsHost ( ) {
return runconfig . ErrConflictHostNetwork
}
for s := range container . NetworkSettings . Networks {
sn , err := daemon . FindNetwork ( s )
if err != nil {
continue
}
if sn . Name ( ) == n . Name ( ) {
// Avoid duplicate config
return nil
}
if ! containertypes . NetworkMode ( sn . Type ( ) ) . IsPrivate ( ) ||
! containertypes . NetworkMode ( n . Type ( ) ) . IsPrivate ( ) {
return runconfig . ErrConflictSharedNetwork
}
if containertypes . NetworkMode ( sn . Name ( ) ) . IsNone ( ) ||
containertypes . NetworkMode ( n . Name ( ) ) . IsNone ( ) {
return runconfig . ErrConflictNoNetwork
}
}
if _ , ok := container . NetworkSettings . Networks [ n . Name ( ) ] ; ! ok {
2016-08-23 19:50:15 -04:00
container . NetworkSettings . Networks [ n . Name ( ) ] = & network . EndpointSettings {
EndpointSettings : endpointConfig ,
}
2016-03-09 23:33:21 -05:00
}
return nil
}
func ( daemon * Daemon ) updateEndpointNetworkSettings ( container * container . Container , n libnetwork . Network , ep libnetwork . Endpoint ) error {
if err := container . BuildEndpointInfo ( n , ep ) ; err != nil {
return err
}
if container . HostConfig . NetworkMode == runconfig . DefaultDaemonNetworkMode ( ) {
container . NetworkSettings . Bridge = daemon . configStore . bridgeConfig . Iface
}
return nil
}
// UpdateNetwork is used to update the container's network (e.g. when linked containers
// get removed/unlinked).
func ( daemon * Daemon ) updateNetwork ( container * container . Container ) error {
ctrl := daemon . netController
sid := container . NetworkSettings . SandboxID
sb , err := ctrl . SandboxByID ( sid )
if err != nil {
return fmt . Errorf ( "error locating sandbox id %s: %v" , sid , err )
}
// Find if container is connected to the default bridge network
var n libnetwork . Network
for name := range container . NetworkSettings . Networks {
sn , err := daemon . FindNetwork ( name )
if err != nil {
continue
}
if sn . Name ( ) == runconfig . DefaultDaemonNetworkMode ( ) . NetworkName ( ) {
n = sn
break
}
}
if n == nil {
// Not connected to the default bridge network; Nothing to do
return nil
}
2016-06-14 12:13:53 -04:00
options , err := daemon . buildSandboxOptions ( container )
2016-03-09 23:33:21 -05:00
if err != nil {
return fmt . Errorf ( "Update network failed: %v" , err )
}
if err := sb . Refresh ( options ... ) ; err != nil {
return fmt . Errorf ( "Update network failed: Failure in refresh sandbox %s: %v" , sid , err )
}
return nil
}
2016-06-13 22:52:49 -04:00
func errClusterNetworkOnRun ( n string ) error {
2016-07-05 02:18:06 -04:00
return fmt . Errorf ( "swarm-scoped network (%s) is not compatible with `docker create` or `docker run`. This network can only be used by a docker service" , n )
2016-06-13 22:52:49 -04:00
}
2016-08-23 19:50:15 -04:00
func ( daemon * Daemon ) findAndAttachNetwork ( container * container . Container , idOrName string , epConfig * networktypes . EndpointSettings ) ( libnetwork . Network , * networktypes . NetworkingConfig , error ) {
n , err := daemon . FindNetwork ( idOrName )
if err != nil {
// We should always be able to find the network for a
// managed container.
if container . Managed {
return nil , nil , err
}
}
// If we found a network and if it is not dynamically created
// we should never attempt to attach to that network here.
if n != nil {
if container . Managed || ! n . Info ( ) . Dynamic ( ) {
return n , nil , nil
}
}
var addresses [ ] string
if epConfig != nil && epConfig . IPAMConfig != nil {
if epConfig . IPAMConfig . IPv4Address != "" {
addresses = append ( addresses , epConfig . IPAMConfig . IPv4Address )
}
if epConfig . IPAMConfig . IPv6Address != "" {
addresses = append ( addresses , epConfig . IPAMConfig . IPv6Address )
}
}
// In all other cases, attempt to attach to the network to
// trigger attachment in the swarm cluster manager.
var config * networktypes . NetworkingConfig
if daemon . clusterProvider != nil {
var err error
config , err = daemon . clusterProvider . AttachNetwork ( idOrName , container . ID , addresses )
if err != nil {
return nil , nil , err
}
}
n , err = daemon . FindNetwork ( idOrName )
if err != nil {
if daemon . clusterProvider != nil {
if err := daemon . clusterProvider . DetachNetwork ( idOrName , container . ID ) ; err != nil {
logrus . Warnf ( "Could not rollback attachment for container %s to network %s: %v" , container . ID , idOrName , err )
}
}
return nil , nil , err
}
2016-09-09 12:55:57 -04:00
// This container has attachment to a swarm scope
// network. Update the container network settings accordingly.
container . NetworkSettings . HasSwarmEndpoint = true
2016-08-23 19:50:15 -04:00
return n , config , nil
}
2016-03-09 23:33:21 -05:00
// updateContainerNetworkSettings update the network settings
func ( daemon * Daemon ) updateContainerNetworkSettings ( container * container . Container , endpointsConfig map [ string ] * networktypes . EndpointSettings ) error {
2016-08-23 19:50:15 -04:00
var n libnetwork . Network
2016-03-09 23:33:21 -05:00
mode := container . HostConfig . NetworkMode
if container . Config . NetworkDisabled || mode . IsContainer ( ) {
return nil
}
networkName := mode . NetworkName ( )
if mode . IsDefault ( ) {
networkName = daemon . netController . Config ( ) . Daemon . DefaultNetwork
}
2016-08-23 19:50:15 -04:00
2016-03-09 23:33:21 -05:00
if mode . IsUserDefined ( ) {
2016-08-23 19:50:15 -04:00
var err error
2016-03-09 23:33:21 -05:00
n , err = daemon . FindNetwork ( networkName )
2016-08-23 19:50:15 -04:00
if err == nil {
networkName = n . Name ( )
2016-06-13 22:52:49 -04:00
}
2016-03-09 23:33:21 -05:00
}
2016-08-23 19:50:15 -04:00
2016-03-09 23:33:21 -05:00
if container . NetworkSettings == nil {
container . NetworkSettings = & network . Settings { }
}
2016-08-23 19:50:15 -04:00
2016-03-09 23:33:21 -05:00
if len ( endpointsConfig ) > 0 {
2016-08-23 19:50:15 -04:00
if container . NetworkSettings . Networks == nil {
container . NetworkSettings . Networks = make ( map [ string ] * network . EndpointSettings )
}
for name , epConfig := range endpointsConfig {
container . NetworkSettings . Networks [ name ] = & network . EndpointSettings {
EndpointSettings : epConfig ,
}
}
2016-03-09 23:33:21 -05:00
}
2016-08-23 19:50:15 -04:00
2016-03-09 23:33:21 -05:00
if container . NetworkSettings . Networks == nil {
2016-08-23 19:50:15 -04:00
container . NetworkSettings . Networks = make ( map [ string ] * network . EndpointSettings )
container . NetworkSettings . Networks [ networkName ] = & network . EndpointSettings {
EndpointSettings : & networktypes . EndpointSettings { } ,
}
2016-03-09 23:33:21 -05:00
}
2016-08-23 19:50:15 -04:00
// Convert any settings added by client in default name to
// engine's default network name key
if mode . IsDefault ( ) {
if nConf , ok := container . NetworkSettings . Networks [ mode . NetworkName ( ) ] ; ok {
container . NetworkSettings . Networks [ networkName ] = nConf
delete ( container . NetworkSettings . Networks , mode . NetworkName ( ) )
}
}
2016-03-09 23:33:21 -05:00
if ! mode . IsUserDefined ( ) {
return nil
}
// Make sure to internally store the per network endpoint config by network name
if _ , ok := container . NetworkSettings . Networks [ networkName ] ; ok {
return nil
}
2016-08-23 19:50:15 -04:00
if n != nil {
if nwConfig , ok := container . NetworkSettings . Networks [ n . ID ( ) ] ; ok {
container . NetworkSettings . Networks [ networkName ] = nwConfig
delete ( container . NetworkSettings . Networks , n . ID ( ) )
return nil
}
2016-03-09 23:33:21 -05:00
}
return nil
}
func ( daemon * Daemon ) allocateNetwork ( container * container . Container ) error {
controller := daemon . netController
if daemon . netController == nil {
return nil
}
// Cleanup any stale sandbox left over due to ungraceful daemon shutdown
if err := controller . SandboxDestroy ( container . ID ) ; err != nil {
logrus . Errorf ( "failed to cleanup up stale network sandbox for container %s" , container . ID )
}
updateSettings := false
if len ( container . NetworkSettings . Networks ) == 0 {
if container . Config . NetworkDisabled || container . HostConfig . NetworkMode . IsContainer ( ) {
return nil
}
err := daemon . updateContainerNetworkSettings ( container , nil )
if err != nil {
return err
}
updateSettings = true
}
2016-05-26 03:20:53 -04:00
// always connect default network first since only default
// network mode support link and we need do some setting
2016-06-24 15:04:26 -04:00
// on sandbox initialize for link, but the sandbox only be initialized
2016-05-26 03:20:53 -04:00
// on first network connecting.
defaultNetName := runconfig . DefaultDaemonNetworkMode ( ) . NetworkName ( )
if nConf , ok := container . NetworkSettings . Networks [ defaultNetName ] ; ok {
2016-09-09 12:55:57 -04:00
cleanOperationalData ( nConf )
2016-08-23 19:50:15 -04:00
if err := daemon . connectToNetwork ( container , defaultNetName , nConf . EndpointSettings , updateSettings ) ; err != nil {
2016-05-26 03:20:53 -04:00
return err
}
}
2016-08-23 19:50:15 -04:00
var (
networks [ ] string
epConfigs [ ] * network . EndpointSettings
)
for n , epConf := range container . NetworkSettings . Networks {
2016-05-26 03:20:53 -04:00
if n == defaultNetName {
continue
}
2016-08-23 19:50:15 -04:00
networks = append ( networks , n )
epConfigs = append ( epConfigs , epConf )
}
for i , epConf := range epConfigs {
2016-09-09 12:55:57 -04:00
cleanOperationalData ( epConf )
2016-08-23 19:50:15 -04:00
if err := daemon . connectToNetwork ( container , networks [ i ] , epConf . EndpointSettings , updateSettings ) ; err != nil {
2016-03-09 23:33:21 -05:00
return err
}
}
return container . WriteHostConfig ( )
}
func ( daemon * Daemon ) getNetworkSandbox ( container * container . Container ) libnetwork . Sandbox {
var sb libnetwork . Sandbox
daemon . netController . WalkSandboxes ( func ( s libnetwork . Sandbox ) bool {
if s . ContainerID ( ) == container . ID {
sb = s
return true
}
return false
} )
return sb
}
// hasUserDefinedIPAddress returns whether the passed endpoint configuration contains IP address configuration
func hasUserDefinedIPAddress ( epConfig * networktypes . EndpointSettings ) bool {
return epConfig != nil && epConfig . IPAMConfig != nil && ( len ( epConfig . IPAMConfig . IPv4Address ) > 0 || len ( epConfig . IPAMConfig . IPv6Address ) > 0 )
}
// User specified ip address is acceptable only for networks with user specified subnets.
func validateNetworkingConfig ( n libnetwork . Network , epConfig * networktypes . EndpointSettings ) error {
if n == nil || epConfig == nil {
return nil
}
if ! hasUserDefinedIPAddress ( epConfig ) {
return nil
}
_ , _ , nwIPv4Configs , nwIPv6Configs := n . Info ( ) . IpamConfig ( )
for _ , s := range [ ] struct {
ipConfigured bool
subnetConfigs [ ] * libnetwork . IpamConf
} {
{
ipConfigured : len ( epConfig . IPAMConfig . IPv4Address ) > 0 ,
subnetConfigs : nwIPv4Configs ,
} ,
{
ipConfigured : len ( epConfig . IPAMConfig . IPv6Address ) > 0 ,
subnetConfigs : nwIPv6Configs ,
} ,
} {
if s . ipConfigured {
foundSubnet := false
for _ , cfg := range s . subnetConfigs {
if len ( cfg . PreferredPool ) > 0 {
foundSubnet = true
break
}
}
if ! foundSubnet {
return runconfig . ErrUnsupportedNetworkNoSubnetAndIP
}
}
}
return nil
}
// cleanOperationalData resets the operational data from the passed endpoint settings
2016-08-23 19:50:15 -04:00
func cleanOperationalData ( es * network . EndpointSettings ) {
2016-03-09 23:33:21 -05:00
es . EndpointID = ""
es . Gateway = ""
es . IPAddress = ""
es . IPPrefixLen = 0
es . IPv6Gateway = ""
es . GlobalIPv6Address = ""
es . GlobalIPv6PrefixLen = 0
es . MacAddress = ""
2016-08-23 19:50:15 -04:00
if es . IPAMOperational {
es . IPAMConfig = nil
2016-03-09 23:33:21 -05:00
}
2016-08-23 19:50:15 -04:00
}
2016-03-09 23:33:21 -05:00
2016-08-23 19:50:15 -04:00
func ( daemon * Daemon ) updateNetworkConfig ( container * container . Container , n libnetwork . Network , endpointConfig * networktypes . EndpointSettings , updateSettings bool ) error {
if ! containertypes . NetworkMode ( n . Name ( ) ) . IsUserDefined ( ) {
2016-04-20 18:35:11 -04:00
if hasUserDefinedIPAddress ( endpointConfig ) && ! enableIPOnPredefinedNetwork ( ) {
2016-08-23 19:50:15 -04:00
return runconfig . ErrUnsupportedNetworkAndIP
2016-03-09 23:33:21 -05:00
}
if endpointConfig != nil && len ( endpointConfig . Aliases ) > 0 {
2016-08-23 19:50:15 -04:00
return runconfig . ErrUnsupportedNetworkAndAlias
2016-03-09 23:33:21 -05:00
}
2016-04-08 18:25:07 -04:00
} else {
addShortID := true
shortID := stringid . TruncateID ( container . ID )
for _ , alias := range endpointConfig . Aliases {
if alias == shortID {
addShortID = false
break
}
}
if addShortID {
endpointConfig . Aliases = append ( endpointConfig . Aliases , shortID )
}
2016-03-09 23:33:21 -05:00
}
if err := validateNetworkingConfig ( n , endpointConfig ) ; err != nil {
2016-08-23 19:50:15 -04:00
return err
2016-03-09 23:33:21 -05:00
}
if updateSettings {
2016-08-23 19:50:15 -04:00
if err := daemon . updateNetworkSettings ( container , n , endpointConfig ) ; err != nil {
return err
2016-03-09 23:33:21 -05:00
}
}
2016-08-23 19:50:15 -04:00
return nil
2016-03-09 23:33:21 -05:00
}
func ( daemon * Daemon ) connectToNetwork ( container * container . Container , idOrName string , endpointConfig * networktypes . EndpointSettings , updateSettings bool ) ( err error ) {
2016-08-23 19:50:15 -04:00
if container . HostConfig . NetworkMode . IsContainer ( ) {
return runconfig . ErrConflictSharedNetwork
}
if containertypes . NetworkMode ( idOrName ) . IsBridge ( ) &&
daemon . configStore . DisableBridge {
container . Config . NetworkDisabled = true
return nil
}
2016-04-08 18:25:07 -04:00
if endpointConfig == nil {
endpointConfig = & networktypes . EndpointSettings { }
}
2016-08-23 19:50:15 -04:00
n , config , err := daemon . findAndAttachNetwork ( container , idOrName , endpointConfig )
2016-03-09 23:33:21 -05:00
if err != nil {
return err
}
if n == nil {
return nil
}
2016-08-23 19:50:15 -04:00
var operIPAM bool
if config != nil {
if epConfig , ok := config . EndpointsConfig [ n . Name ( ) ] ; ok {
if endpointConfig . IPAMConfig == nil ||
( endpointConfig . IPAMConfig . IPv4Address == "" &&
endpointConfig . IPAMConfig . IPv6Address == "" &&
len ( endpointConfig . IPAMConfig . LinkLocalIPs ) == 0 ) {
operIPAM = true
}
endpointConfig = epConfig
}
}
err = daemon . updateNetworkConfig ( container , n , endpointConfig , updateSettings )
if err != nil {
return err
}
2016-03-09 23:33:21 -05:00
controller := daemon . netController
sb := daemon . getNetworkSandbox ( container )
createOptions , err := container . BuildCreateEndpointOptions ( n , endpointConfig , sb )
if err != nil {
return err
}
endpointName := strings . TrimPrefix ( container . Name , "/" )
ep , err := n . CreateEndpoint ( endpointName , createOptions ... )
if err != nil {
return err
}
defer func ( ) {
if err != nil {
if e := ep . Delete ( false ) ; e != nil {
logrus . Warnf ( "Could not rollback container connection to network %s" , idOrName )
}
}
} ( )
2016-08-23 19:50:15 -04:00
container . NetworkSettings . Networks [ n . Name ( ) ] = & network . EndpointSettings {
EndpointSettings : endpointConfig ,
IPAMOperational : operIPAM ,
}
if _ , ok := container . NetworkSettings . Networks [ n . ID ( ) ] ; ok {
delete ( container . NetworkSettings . Networks , n . ID ( ) )
}
2016-03-09 23:33:21 -05:00
if err := daemon . updateEndpointNetworkSettings ( container , n , ep ) ; err != nil {
return err
}
if sb == nil {
2016-06-14 12:13:53 -04:00
options , err := daemon . buildSandboxOptions ( container )
2016-03-09 23:33:21 -05:00
if err != nil {
return err
}
sb , err = controller . NewSandbox ( container . ID , options ... )
if err != nil {
return err
}
container . UpdateSandboxNetworkSettings ( sb )
}
joinOptions , err := container . BuildJoinOptions ( n )
if err != nil {
return err
}
if err := ep . Join ( sb , joinOptions ... ) ; err != nil {
return err
}
if err := container . UpdateJoinInfo ( n , ep ) ; err != nil {
return fmt . Errorf ( "Updating join info failed: %v" , err )
}
2016-05-25 16:47:38 -04:00
container . NetworkSettings . Ports = getPortMapInfo ( sb )
2016-03-09 23:33:21 -05:00
daemon . LogNetworkEventWithAttributes ( n , "connect" , map [ string ] string { "container" : container . ID } )
return nil
}
2016-08-26 16:08:28 -04:00
// ForceEndpointDelete deletes an endpoint from a network forcefully
func ( daemon * Daemon ) ForceEndpointDelete ( name string , networkName string ) error {
n , err := daemon . FindNetwork ( networkName )
if err != nil {
return err
}
2016-03-09 23:33:21 -05:00
ep , err := n . EndpointByName ( name )
if err != nil {
return err
}
return ep . Delete ( true )
}
2016-08-23 19:50:15 -04:00
func ( daemon * Daemon ) disconnectFromNetwork ( container * container . Container , n libnetwork . Network , force bool ) error {
2016-03-09 23:33:21 -05:00
var (
ep libnetwork . Endpoint
sbox libnetwork . Sandbox
)
s := func ( current libnetwork . Endpoint ) bool {
epInfo := current . Info ( )
if epInfo == nil {
return false
}
if sb := epInfo . Sandbox ( ) ; sb != nil {
if sb . ContainerID ( ) == container . ID {
ep = current
sbox = sb
return true
}
}
return false
}
n . WalkEndpoints ( s )
if ep == nil && force {
epName := strings . TrimPrefix ( container . Name , "/" )
ep , err := n . EndpointByName ( epName )
if err != nil {
return err
}
return ep . Delete ( force )
}
if ep == nil {
return fmt . Errorf ( "container %s is not connected to the network" , container . ID )
}
if err := ep . Leave ( sbox ) ; err != nil {
return fmt . Errorf ( "container %s failed to leave network %s: %v" , container . ID , n . Name ( ) , err )
}
2016-05-25 16:47:38 -04:00
container . NetworkSettings . Ports = getPortMapInfo ( sbox )
2016-03-09 23:33:21 -05:00
if err := ep . Delete ( false ) ; err != nil {
return fmt . Errorf ( "endpoint delete failed for container %s on network %s: %v" , container . ID , n . Name ( ) , err )
}
delete ( container . NetworkSettings . Networks , n . Name ( ) )
2016-08-23 19:50:15 -04:00
if daemon . clusterProvider != nil && n . Info ( ) . Dynamic ( ) && ! container . Managed {
if err := daemon . clusterProvider . DetachNetwork ( n . Name ( ) , container . ID ) ; err != nil {
logrus . Warnf ( "error detaching from network %s: %v" , n , err )
}
}
2016-03-09 23:33:21 -05:00
return nil
}
func ( daemon * Daemon ) initializeNetworking ( container * container . Container ) error {
var err error
if container . HostConfig . NetworkMode . IsContainer ( ) {
// we need to get the hosts files from the container to join
nc , err := daemon . getNetworkedContainer ( container . ID , container . HostConfig . NetworkMode . ConnectedContainer ( ) )
if err != nil {
return err
}
container . HostnamePath = nc . HostnamePath
container . HostsPath = nc . HostsPath
container . ResolvConfPath = nc . ResolvConfPath
container . Config . Hostname = nc . Config . Hostname
container . Config . Domainname = nc . Config . Domainname
return nil
}
if container . HostConfig . NetworkMode . IsHost ( ) {
container . Config . Hostname , err = os . Hostname ( )
if err != nil {
return err
}
}
if err := daemon . allocateNetwork ( container ) ; err != nil {
return err
}
return container . BuildHostnameFile ( )
}
func ( daemon * Daemon ) getNetworkedContainer ( containerID , connectedContainerID string ) ( * container . Container , error ) {
nc , err := daemon . GetContainer ( connectedContainerID )
if err != nil {
return nil , err
}
if containerID == nc . ID {
return nil , fmt . Errorf ( "cannot join own network" )
}
if ! nc . IsRunning ( ) {
err := fmt . Errorf ( "cannot join network of a non running container: %s" , connectedContainerID )
return nil , derr . NewRequestConflictError ( err )
}
if nc . IsRestarting ( ) {
return nil , errContainerIsRestarting ( connectedContainerID )
}
return nc , nil
}
func ( daemon * Daemon ) releaseNetwork ( container * container . Container ) {
2016-06-14 12:13:53 -04:00
if daemon . netController == nil {
return
}
2016-03-09 23:33:21 -05:00
if container . HostConfig . NetworkMode . IsContainer ( ) || container . Config . NetworkDisabled {
return
}
sid := container . NetworkSettings . SandboxID
settings := container . NetworkSettings . Networks
container . NetworkSettings . Ports = nil
if sid == "" || len ( settings ) == 0 {
return
}
var networks [ ] libnetwork . Network
for n , epSettings := range settings {
if nw , err := daemon . FindNetwork ( n ) ; err == nil {
networks = append ( networks , nw )
}
2016-08-23 19:50:15 -04:00
if epSettings . EndpointSettings == nil {
continue
}
2016-03-09 23:33:21 -05:00
cleanOperationalData ( epSettings )
}
sb , err := daemon . netController . SandboxByID ( sid )
if err != nil {
2016-03-29 18:27:04 -04:00
logrus . Warnf ( "error locating sandbox id %s: %v" , sid , err )
2016-03-09 23:33:21 -05:00
return
}
if err := sb . Delete ( ) ; err != nil {
logrus . Errorf ( "Error deleting sandbox id %s for container %s: %v" , sid , container . ID , err )
}
for _ , nw := range networks {
2016-08-23 19:50:15 -04:00
if daemon . clusterProvider != nil && nw . Info ( ) . Dynamic ( ) && ! container . Managed {
if err := daemon . clusterProvider . DetachNetwork ( nw . Name ( ) , container . ID ) ; err != nil {
logrus . Warnf ( "error detaching from network %s: %v" , nw . Name ( ) , err )
}
}
2016-06-08 10:36:42 -04:00
attributes := map [ string ] string {
"container" : container . ID ,
}
2016-03-09 23:33:21 -05:00
daemon . LogNetworkEventWithAttributes ( nw , "disconnect" , attributes )
}
}