@@ -16,6 +16,7 @@ import (
1616 "github.com/Sirupsen/logrus"
1717 apitypes "github.com/docker/docker/api/types"
1818 "github.com/docker/docker/api/types/filters"
19+ "github.com/docker/docker/api/types/network"
1920 types "github.com/docker/docker/api/types/swarm"
2021 "github.com/docker/docker/daemon/cluster/convert"
2122 executorpkg "github.com/docker/docker/daemon/cluster/executor"
@@ -126,6 +127,18 @@ type Cluster struct {
126127 stop bool
127128 err error
128129 cancelDelay func ()
130+ attachers map [string ]* attacher
131+ }
132+
133+ // attacher manages the in-memory attachment state of a container
134+ // attachment to a global scope network managed by swarm manager. It
135+ // helps in identifying the attachment ID via the taskID and the
136+ // corresponding attachment configuration obtained from the manager.
137+ type attacher struct {
138+ taskID string
139+ config * network.NetworkingConfig
140+ attachWaitCh chan * network.NetworkingConfig
141+ detachWaitCh chan struct {}
129142}
130143
131144type node struct {
@@ -154,6 +167,7 @@ func New(config Config) (*Cluster, error) {
154167 config : config ,
155168 configEvent : make (chan struct {}, 10 ),
156169 runtimeRoot : config .RuntimeRoot ,
170+ attachers : make (map [string ]* attacher ),
157171 }
158172
159173 st , err := c .loadState ()
@@ -1212,6 +1226,120 @@ func (c *Cluster) GetNetworks() ([]apitypes.NetworkResource, error) {
12121226 return networks , nil
12131227}
12141228
1229+ func attacherKey (target , containerID string ) string {
1230+ return containerID + ":" + target
1231+ }
1232+
1233+ // UpdateAttachment signals the attachment config to the attachment
1234+ // waiter who is trying to start or attach the container to the
1235+ // network.
1236+ func (c * Cluster ) UpdateAttachment (target , containerID string , config * network.NetworkingConfig ) error {
1237+ c .RLock ()
1238+ attacher , ok := c .attachers [attacherKey (target , containerID )]
1239+ c .RUnlock ()
1240+ if ! ok || attacher == nil {
1241+ return fmt .Errorf ("could not find attacher for container %s to network %s" , containerID , target )
1242+ }
1243+
1244+ attacher .attachWaitCh <- config
1245+ close (attacher .attachWaitCh )
1246+ return nil
1247+ }
1248+
1249+ // WaitForDetachment waits for the container to stop or detach from
1250+ // the network.
1251+ func (c * Cluster ) WaitForDetachment (ctx context.Context , networkName , networkID , taskID , containerID string ) error {
1252+ c .RLock ()
1253+ attacher , ok := c .attachers [attacherKey (networkName , containerID )]
1254+ if ! ok {
1255+ attacher , ok = c .attachers [attacherKey (networkID , containerID )]
1256+ }
1257+ if c .node == nil || c .node .Agent () == nil {
1258+ c .RUnlock ()
1259+ return fmt .Errorf ("invalid cluster node while waiting for detachment" )
1260+ }
1261+
1262+ agent := c .node .Agent ()
1263+ c .RUnlock ()
1264+
1265+ if ok && attacher != nil && attacher .detachWaitCh != nil {
1266+ select {
1267+ case <- attacher .detachWaitCh :
1268+ case <- ctx .Done ():
1269+ return ctx .Err ()
1270+ }
1271+ }
1272+
1273+ return agent .ResourceAllocator ().DetachNetwork (ctx , taskID )
1274+ }
1275+
1276+ // AttachNetwork generates an attachment request towards the manager.
1277+ func (c * Cluster ) AttachNetwork (target string , containerID string , addresses []string ) (* network.NetworkingConfig , error ) {
1278+ aKey := attacherKey (target , containerID )
1279+ c .Lock ()
1280+ if c .node == nil || c .node .Agent () == nil {
1281+ c .Unlock ()
1282+ return nil , fmt .Errorf ("invalid cluster node while attaching to network" )
1283+ }
1284+ if attacher , ok := c .attachers [aKey ]; ok {
1285+ c .Unlock ()
1286+ return attacher .config , nil
1287+ }
1288+
1289+ agent := c .node .Agent ()
1290+ attachWaitCh := make (chan * network.NetworkingConfig )
1291+ detachWaitCh := make (chan struct {})
1292+ c .attachers [aKey ] = & attacher {
1293+ attachWaitCh : attachWaitCh ,
1294+ detachWaitCh : detachWaitCh ,
1295+ }
1296+ c .Unlock ()
1297+
1298+ ctx , cancel := c .getRequestContext ()
1299+ defer cancel ()
1300+
1301+ taskID , err := agent .ResourceAllocator ().AttachNetwork (ctx , containerID , target , addresses )
1302+ if err != nil {
1303+ c .Lock ()
1304+ delete (c .attachers , aKey )
1305+ c .Unlock ()
1306+ return nil , fmt .Errorf ("Could not attach to network %s: %v" , target , err )
1307+ }
1308+
1309+ logrus .Debugf ("Successfully attached to network %s with tid %s" , target , taskID )
1310+
1311+ var config * network.NetworkingConfig
1312+ select {
1313+ case config = <- attachWaitCh :
1314+ case <- ctx .Done ():
1315+ return nil , fmt .Errorf ("attaching to network failed, make sure your network options are correct and check manager logs: %v" , ctx .Err ())
1316+ }
1317+
1318+ c .Lock ()
1319+ c .attachers [aKey ].taskID = taskID
1320+ c .attachers [aKey ].config = config
1321+ c .Unlock ()
1322+ return config , nil
1323+ }
1324+
1325+ // DetachNetwork unblocks the waiters waiting on WaitForDetachment so
1326+ // that a request to detach can be generated towards the manager.
1327+ func (c * Cluster ) DetachNetwork (target string , containerID string ) error {
1328+ aKey := attacherKey (target , containerID )
1329+
1330+ c .Lock ()
1331+ attacher , ok := c .attachers [aKey ]
1332+ delete (c .attachers , aKey )
1333+ c .Unlock ()
1334+
1335+ if ! ok {
1336+ return fmt .Errorf ("could not find network attachment for container %s to network %s" , containerID , target )
1337+ }
1338+
1339+ close (attacher .detachWaitCh )
1340+ return nil
1341+ }
1342+
12151343// CreateNetwork creates a new cluster managed network.
12161344func (c * Cluster ) CreateNetwork (s apitypes.NetworkCreateRequest ) (string , error ) {
12171345 c .RLock ()
@@ -1262,7 +1390,14 @@ func (c *Cluster) RemoveNetwork(input string) error {
12621390}
12631391
12641392func (c * Cluster ) populateNetworkID (ctx context.Context , client swarmapi.ControlClient , s * types.ServiceSpec ) error {
1265- for i , n := range s .Networks {
1393+ // Always prefer NetworkAttachmentConfigs from TaskTemplate
1394+ // but fallback to service spec for backward compatibility
1395+ networks := s .TaskTemplate .Networks
1396+ if len (networks ) == 0 {
1397+ networks = s .Networks
1398+ }
1399+
1400+ for i , n := range networks {
12661401 apiNetwork , err := getNetwork (ctx , client , n .Target )
12671402 if err != nil {
12681403 if ln , _ := c .config .Backend .FindNetwork (n .Target ); ln != nil && ! ln .Info ().Dynamic () {
@@ -1271,7 +1406,7 @@ func (c *Cluster) populateNetworkID(ctx context.Context, client swarmapi.Control
12711406 }
12721407 return err
12731408 }
1274- s . Networks [i ].Target = apiNetwork .ID
1409+ networks [i ].Target = apiNetwork .ID
12751410 }
12761411 return nil
12771412}
0 commit comments