diff --git a/libnetwork/network.go b/libnetwork/network.go index 60822f4104..a063d2c3ee 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net" + "runtime" "strings" "sync" "time" @@ -1059,13 +1060,6 @@ func (n *network) delete(force bool, rmLBEndpoint bool) error { goto removeFromStore } - if err = n.deleteNetwork(); err != nil { - if !force { - return err - } - logrus.Debugf("driver failed to delete stale network %s (%s): %v", n.Name(), n.ID(), err) - } - n.ipamRelease() if err = c.updateToStore(n); err != nil { logrus.Warnf("Failed to update store after ipam release for network %s (%s): %v", n.Name(), n.ID(), err) @@ -1086,8 +1080,19 @@ func (n *network) delete(force bool, rmLBEndpoint bool) error { c.cleanupServiceDiscovery(n.ID()) // Cleanup the load balancer. On Windows this call is required - // to remove remote loadbalancers in VFP. - c.cleanupServiceBindings(n.ID()) + // to remove remote loadbalancers in VFP, and must be performed before + // dataplane network deletion. + if runtime.GOOS == "windows" { + c.cleanupServiceBindings(n.ID()) + } + + // Delete the network from the dataplane + if err = n.deleteNetwork(); err != nil { + if !force { + return err + } + logrus.Debugf("driver failed to delete stale network %s (%s): %v", n.Name(), n.ID(), err) + } removeFromStore: // deleteFromStore performs an atomic delete operation and the diff --git a/libnetwork/service_common.go b/libnetwork/service_common.go index a2973d7e39..8504684690 100644 --- a/libnetwork/service_common.go +++ b/libnetwork/service_common.go @@ -375,12 +375,15 @@ func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName st // Remove loadbalancer service(if needed) and backend in all // sandboxes in the network only if the vip is valid. if entries == 0 { - // The network may well have been deleted before the last - // of the service bindings. That's ok on Linux because - // removing the network sandbox implicitly removes the - // backend service bindings. Windows VFP cleanup requires - // calling cleanupServiceBindings on the network prior to - // deleting the network, performed by network.delete. + // The network may well have been deleted from the store (and + // dataplane) before the last of the service bindings. On Linux that's + // ok because removing the network sandbox from the dataplane + // implicitly cleans up all related dataplane state. + // On the Windows dataplane, VFP policylists must be removed + // independently of the network, and they must be removed before the HNS + // network. Otherwise, policylist removal fails with "network not + // found." On Windows cleanupServiceBindings must be called prior to + // removing the network from the store or dataplane. n, err := c.NetworkByID(nID) if err == nil { n.(*network).rmLBBackend(ip, lb, rmService, fullRemove) diff --git a/libnetwork/service_windows.go b/libnetwork/service_windows.go index 6fcc35b87a..9a27546a57 100644 --- a/libnetwork/service_windows.go +++ b/libnetwork/service_windows.go @@ -131,12 +131,16 @@ func (n *network) rmLBBackend(ip net.IP, lb *loadBalancer, rmService bool, fullR if policyLists, ok := lbPolicylistMap[lb]; ok { if policyLists.ilb != nil { - policyLists.ilb.Delete() + if _, err := policyLists.ilb.Delete(); err != nil { + logrus.Errorf("Failed to remove HNS ILB policylist %s: %s", policyLists.ilb.ID, err) + } policyLists.ilb = nil } if policyLists.elb != nil { - policyLists.elb.Delete() + if _, err := policyLists.elb.Delete(); err != nil { + logrus.Errorf("Failed to remove HNS ELB policylist %s: %s", policyLists.elb.ID, err) + } policyLists.elb = nil } delete(lbPolicylistMap, lb)