2017-02-11 13:49:10 -05:00
package cluster
import (
"fmt"
apitypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/network"
types "github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/daemon/cluster/convert"
"github.com/docker/docker/runconfig"
swarmapi "github.com/docker/swarmkit/api"
"github.com/pkg/errors"
2017-07-26 17:42:13 -04:00
"github.com/sirupsen/logrus"
2017-02-11 13:49:10 -05:00
"golang.org/x/net/context"
)
// GetNetworks returns all current cluster managed networks.
func ( c * Cluster ) GetNetworks ( ) ( [ ] apitypes . NetworkResource , error ) {
2017-05-01 19:44:04 -04:00
list , err := c . getNetworks ( nil )
if err != nil {
return nil , err
}
removePredefinedNetworks ( & list )
return list , nil
}
func removePredefinedNetworks ( networks * [ ] apitypes . NetworkResource ) {
if networks == nil {
return
}
var idxs [ ] int
for i , n := range * networks {
if v , ok := n . Labels [ "com.docker.swarm.predefined" ] ; ok && v == "true" {
idxs = append ( idxs , i )
}
}
for i , idx := range idxs {
idx -= i
* networks = append ( ( * networks ) [ : idx ] , ( * networks ) [ idx + 1 : ] ... )
}
2017-02-11 13:49:10 -05:00
}
func ( c * Cluster ) getNetworks ( filters * swarmapi . ListNetworksRequest_Filters ) ( [ ] apitypes . NetworkResource , error ) {
c . mu . RLock ( )
defer c . mu . RUnlock ( )
state := c . currentNodeState ( )
if ! state . IsActiveManager ( ) {
return nil , c . errNoManager ( state )
}
ctx , cancel := c . getRequestContext ( )
defer cancel ( )
r , err := state . controlClient . ListNetworks ( ctx , & swarmapi . ListNetworksRequest { Filters : filters } )
if err != nil {
return nil , err
}
2017-04-13 23:02:28 -04:00
networks := make ( [ ] apitypes . NetworkResource , 0 , len ( r . Networks ) )
2017-02-11 13:49:10 -05:00
for _ , network := range r . Networks {
networks = append ( networks , convert . BasicNetworkFromGRPC ( * network ) )
}
return networks , nil
}
// GetNetwork returns a cluster network by an ID.
func ( c * Cluster ) GetNetwork ( input string ) ( apitypes . NetworkResource , error ) {
2017-02-28 05:12:11 -05:00
var network * swarmapi . Network
2017-02-11 13:49:10 -05:00
2017-02-28 05:12:11 -05:00
if err := c . lockedManagerAction ( func ( ctx context . Context , state nodeState ) error {
n , err := getNetwork ( ctx , state . controlClient , input )
if err != nil {
return err
}
network = n
return nil
} ) ; err != nil {
2017-02-11 13:49:10 -05:00
return apitypes . NetworkResource { } , err
}
return convert . BasicNetworkFromGRPC ( * network ) , nil
}
// GetNetworksByName returns cluster managed networks by name.
// It is ok to have multiple networks here. #18864
func ( c * Cluster ) GetNetworksByName ( name string ) ( [ ] apitypes . NetworkResource , error ) {
// Note that swarmapi.GetNetworkRequest.Name is not functional.
// So we cannot just use that with c.GetNetwork.
return c . getNetworks ( & swarmapi . ListNetworksRequest_Filters {
Names : [ ] string { name } ,
} )
}
func attacherKey ( target , containerID string ) string {
return containerID + ":" + target
}
// UpdateAttachment signals the attachment config to the attachment
// waiter who is trying to start or attach the container to the
// network.
func ( c * Cluster ) UpdateAttachment ( target , containerID string , config * network . NetworkingConfig ) error {
2017-03-24 09:43:23 -04:00
c . mu . Lock ( )
2017-02-11 13:49:10 -05:00
attacher , ok := c . attachers [ attacherKey ( target , containerID ) ]
if ! ok || attacher == nil {
2017-03-24 09:43:23 -04:00
c . mu . Unlock ( )
2017-02-11 13:49:10 -05:00
return fmt . Errorf ( "could not find attacher for container %s to network %s" , containerID , target )
}
2017-03-24 09:43:23 -04:00
if attacher . inProgress {
logrus . Debugf ( "Discarding redundant notice of resource allocation on network %s for task id %s" , target , attacher . taskID )
c . mu . Unlock ( )
return nil
}
attacher . inProgress = true
c . mu . Unlock ( )
2017-02-11 13:49:10 -05:00
attacher . attachWaitCh <- config
2017-03-24 09:43:23 -04:00
2017-02-11 13:49:10 -05:00
return nil
}
// WaitForDetachment waits for the container to stop or detach from
// the network.
func ( c * Cluster ) WaitForDetachment ( ctx context . Context , networkName , networkID , taskID , containerID string ) error {
c . mu . RLock ( )
attacher , ok := c . attachers [ attacherKey ( networkName , containerID ) ]
if ! ok {
attacher , ok = c . attachers [ attacherKey ( networkID , containerID ) ]
}
state := c . currentNodeState ( )
if state . swarmNode == nil || state . swarmNode . Agent ( ) == nil {
c . mu . RUnlock ( )
return errors . New ( "invalid cluster node while waiting for detachment" )
}
c . mu . RUnlock ( )
agent := state . swarmNode . Agent ( )
if ok && attacher != nil &&
attacher . detachWaitCh != nil &&
attacher . attachCompleteCh != nil {
// Attachment may be in progress still so wait for
// attachment to complete.
select {
case <- attacher . attachCompleteCh :
case <- ctx . Done ( ) :
return ctx . Err ( )
}
if attacher . taskID == taskID {
select {
case <- attacher . detachWaitCh :
case <- ctx . Done ( ) :
return ctx . Err ( )
}
}
}
return agent . ResourceAllocator ( ) . DetachNetwork ( ctx , taskID )
}
// AttachNetwork generates an attachment request towards the manager.
func ( c * Cluster ) AttachNetwork ( target string , containerID string , addresses [ ] string ) ( * network . NetworkingConfig , error ) {
aKey := attacherKey ( target , containerID )
c . mu . Lock ( )
state := c . currentNodeState ( )
if state . swarmNode == nil || state . swarmNode . Agent ( ) == nil {
c . mu . Unlock ( )
return nil , errors . New ( "invalid cluster node while attaching to network" )
}
if attacher , ok := c . attachers [ aKey ] ; ok {
c . mu . Unlock ( )
return attacher . config , nil
}
agent := state . swarmNode . Agent ( )
attachWaitCh := make ( chan * network . NetworkingConfig )
detachWaitCh := make ( chan struct { } )
attachCompleteCh := make ( chan struct { } )
c . attachers [ aKey ] = & attacher {
attachWaitCh : attachWaitCh ,
attachCompleteCh : attachCompleteCh ,
detachWaitCh : detachWaitCh ,
}
c . mu . Unlock ( )
ctx , cancel := c . getRequestContext ( )
defer cancel ( )
taskID , err := agent . ResourceAllocator ( ) . AttachNetwork ( ctx , containerID , target , addresses )
if err != nil {
c . mu . Lock ( )
delete ( c . attachers , aKey )
c . mu . Unlock ( )
return nil , fmt . Errorf ( "Could not attach to network %s: %v" , target , err )
}
c . mu . Lock ( )
c . attachers [ aKey ] . taskID = taskID
close ( attachCompleteCh )
c . mu . Unlock ( )
2017-02-15 22:53:51 -05:00
logrus . Debugf ( "Successfully attached to network %s with task id %s" , target , taskID )
release := func ( ) {
ctx , cancel := c . getRequestContext ( )
defer cancel ( )
if err := agent . ResourceAllocator ( ) . DetachNetwork ( ctx , taskID ) ; err != nil {
logrus . Errorf ( "Failed remove network attachment %s to network %s on allocation failure: %v" ,
taskID , target , err )
}
}
2017-02-11 13:49:10 -05:00
var config * network . NetworkingConfig
select {
case config = <- attachWaitCh :
case <- ctx . Done ( ) :
2017-02-15 22:53:51 -05:00
release ( )
2017-02-11 13:49:10 -05:00
return nil , fmt . Errorf ( "attaching to network failed, make sure your network options are correct and check manager logs: %v" , ctx . Err ( ) )
}
c . mu . Lock ( )
c . attachers [ aKey ] . config = config
c . mu . Unlock ( )
2017-02-15 22:53:51 -05:00
logrus . Debugf ( "Successfully allocated resources on network %s for task id %s" , target , taskID )
2017-02-11 13:49:10 -05:00
return config , nil
}
// DetachNetwork unblocks the waiters waiting on WaitForDetachment so
// that a request to detach can be generated towards the manager.
func ( c * Cluster ) DetachNetwork ( target string , containerID string ) error {
aKey := attacherKey ( target , containerID )
c . mu . Lock ( )
attacher , ok := c . attachers [ aKey ]
delete ( c . attachers , aKey )
c . mu . Unlock ( )
if ! ok {
return fmt . Errorf ( "could not find network attachment for container %s to network %s" , containerID , target )
}
close ( attacher . detachWaitCh )
return nil
}
// CreateNetwork creates a new cluster managed network.
func ( c * Cluster ) CreateNetwork ( s apitypes . NetworkCreateRequest ) ( string , error ) {
if runconfig . IsPreDefinedNetwork ( s . Name ) {
2017-07-19 10:20:13 -04:00
err := notAllowedError ( fmt . Sprintf ( "%s is a pre-defined network and cannot be created" , s . Name ) )
return "" , errors . WithStack ( err )
2017-02-11 13:49:10 -05:00
}
2017-02-28 05:12:11 -05:00
var resp * swarmapi . CreateNetworkResponse
if err := c . lockedManagerAction ( func ( ctx context . Context , state nodeState ) error {
networkSpec := convert . BasicNetworkCreateToGRPC ( s )
r , err := state . controlClient . CreateNetwork ( ctx , & swarmapi . CreateNetworkRequest { Spec : & networkSpec } )
if err != nil {
return err
}
resp = r
return nil
} ) ; err != nil {
2017-02-11 13:49:10 -05:00
return "" , err
}
2017-02-28 05:12:11 -05:00
return resp . Network . ID , nil
2017-02-11 13:49:10 -05:00
}
// RemoveNetwork removes a cluster network.
func ( c * Cluster ) RemoveNetwork ( input string ) error {
2017-02-28 05:12:11 -05:00
return c . lockedManagerAction ( func ( ctx context . Context , state nodeState ) error {
network , err := getNetwork ( ctx , state . controlClient , input )
if err != nil {
return err
}
2017-02-11 13:49:10 -05:00
2017-02-28 05:12:11 -05:00
_ , err = state . controlClient . RemoveNetwork ( ctx , & swarmapi . RemoveNetworkRequest { NetworkID : network . ID } )
2017-02-11 13:49:10 -05:00
return err
2017-02-28 05:12:11 -05:00
} )
2017-02-11 13:49:10 -05:00
}
func ( c * Cluster ) populateNetworkID ( ctx context . Context , client swarmapi . ControlClient , s * types . ServiceSpec ) error {
// Always prefer NetworkAttachmentConfigs from TaskTemplate
// but fallback to service spec for backward compatibility
networks := s . TaskTemplate . Networks
if len ( networks ) == 0 {
networks = s . Networks
}
for i , n := range networks {
apiNetwork , err := getNetwork ( ctx , client , n . Target )
if err != nil {
2017-05-01 19:44:04 -04:00
ln , _ := c . config . Backend . FindNetwork ( n . Target )
if ln != nil && runconfig . IsPreDefinedNetwork ( ln . Name ( ) ) {
// Need to retrieve the corresponding predefined swarm network
// and use its id for the request.
apiNetwork , err = getNetwork ( ctx , client , ln . Name ( ) )
if err != nil {
2017-07-19 10:20:13 -04:00
return errors . Wrap ( notFoundError { err } , "could not find the corresponding predefined swarm network" )
2017-05-01 19:44:04 -04:00
}
goto setid
}
if ln != nil && ! ln . Info ( ) . Dynamic ( ) {
2017-07-19 10:20:13 -04:00
errMsg := fmt . Sprintf ( "The network %s cannot be used with services. Only networks scoped to the swarm can be used, such as those created with the overlay driver." , ln . Name ( ) )
return errors . WithStack ( notAllowedError ( errMsg ) )
2017-02-11 13:49:10 -05:00
}
return err
}
2017-05-01 19:44:04 -04:00
setid :
2017-02-11 13:49:10 -05:00
networks [ i ] . Target = apiNetwork . ID
}
return nil
}