moby--moby/libnetwork/sandboxdata.go

260 lines
5.0 KiB
Go

package libnetwork
import (
"container/heap"
"fmt"
"sync"
"github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/sandbox"
)
type epHeap []*endpoint
type sandboxData struct {
sbox sandbox.Sandbox
refCnt int
endpoints epHeap
sync.Mutex
}
func (eh epHeap) Len() int { return len(eh) }
func (eh epHeap) Less(i, j int) bool {
eh[i].Lock()
eh[j].Lock()
defer eh[j].Unlock()
defer eh[i].Unlock()
if eh[i].container.config.prio == eh[j].container.config.prio {
return eh[i].network.Name() < eh[j].network.Name()
}
return eh[i].container.config.prio > eh[j].container.config.prio
}
func (eh epHeap) Swap(i, j int) { eh[i], eh[j] = eh[j], eh[i] }
func (eh *epHeap) Push(x interface{}) {
*eh = append(*eh, x.(*endpoint))
}
func (eh *epHeap) Pop() interface{} {
old := *eh
n := len(old)
x := old[n-1]
*eh = old[0 : n-1]
return x
}
func (s *sandboxData) updateGateway(ep *endpoint) error {
sb := s.sandbox()
sb.UnsetGateway()
sb.UnsetGatewayIPv6()
if ep == nil {
return nil
}
ep.Lock()
joinInfo := ep.joinInfo
ep.Unlock()
if err := sb.SetGateway(joinInfo.gw); err != nil {
return fmt.Errorf("failed to set gateway while updating gateway: %v", err)
}
if err := sb.SetGatewayIPv6(joinInfo.gw6); err != nil {
return fmt.Errorf("failed to set IPv6 gateway while updating gateway: %v", err)
}
return nil
}
func (s *sandboxData) addEndpoint(ep *endpoint) error {
ep.Lock()
joinInfo := ep.joinInfo
ifaces := ep.iFaces
ep.Unlock()
sb := s.sandbox()
for _, i := range ifaces {
var ifaceOptions []sandbox.IfaceOption
ifaceOptions = append(ifaceOptions, sb.InterfaceOptions().Address(&i.addr),
sb.InterfaceOptions().Routes(i.routes))
if i.addrv6.IP.To16() != nil {
ifaceOptions = append(ifaceOptions,
sb.InterfaceOptions().AddressIPv6(&i.addrv6))
}
if err := sb.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil {
return fmt.Errorf("failed to add interface %s to sandbox: %v", i.srcName, err)
}
}
if joinInfo != nil {
// Set up non-interface routes.
for _, r := range ep.joinInfo.StaticRoutes {
if err := sb.AddStaticRoute(r); err != nil {
return fmt.Errorf("failed to add static route %s: %v", r.Destination.String(), err)
}
}
}
s.Lock()
heap.Push(&s.endpoints, ep)
highEp := s.endpoints[0]
s.Unlock()
if ep == highEp {
if err := s.updateGateway(ep); err != nil {
return err
}
}
return nil
}
func (s *sandboxData) rmEndpoint(ep *endpoint) {
ep.Lock()
joinInfo := ep.joinInfo
ep.Unlock()
sb := s.sandbox()
for _, i := range sb.Info().Interfaces() {
// Only remove the interfaces owned by this endpoint from the sandbox.
if ep.hasInterface(i.SrcName()) {
if err := i.Remove(); err != nil {
logrus.Debugf("Remove interface failed: %v", err)
}
}
}
// Remove non-interface routes.
for _, r := range joinInfo.StaticRoutes {
if err := sb.RemoveStaticRoute(r); err != nil {
logrus.Debugf("Remove route failed: %v", err)
}
}
s.Lock()
if len(s.endpoints) == 0 {
// s.endpoints should never be empty and this is unexpected error condition
// We log an error message to note this down for debugging purposes.
logrus.Errorf("No endpoints in sandbox while trying to remove endpoint %s", ep.Name())
s.Unlock()
return
}
highEpBefore := s.endpoints[0]
var (
i int
e *endpoint
)
for i, e = range s.endpoints {
if e == ep {
break
}
}
heap.Remove(&s.endpoints, i)
var highEpAfter *endpoint
if len(s.endpoints) > 0 {
highEpAfter = s.endpoints[0]
}
s.Unlock()
if highEpBefore != highEpAfter {
s.updateGateway(highEpAfter)
}
}
func (s *sandboxData) sandbox() sandbox.Sandbox {
s.Lock()
defer s.Unlock()
return s.sbox
}
func (c *controller) sandboxAdd(key string, create bool, ep *endpoint) (sandbox.Sandbox, error) {
c.Lock()
sData, ok := c.sandboxes[key]
c.Unlock()
if !ok {
sb, err := sandbox.NewSandbox(key, create)
if err != nil {
return nil, fmt.Errorf("failed to create new sandbox: %v", err)
}
sData = &sandboxData{
sbox: sb,
endpoints: epHeap{},
}
heap.Init(&sData.endpoints)
c.Lock()
c.sandboxes[key] = sData
c.Unlock()
}
if err := sData.addEndpoint(ep); err != nil {
return nil, err
}
return sData.sandbox(), nil
}
func (c *controller) sandboxRm(key string, ep *endpoint) {
c.Lock()
sData := c.sandboxes[key]
c.Unlock()
sData.rmEndpoint(ep)
}
func (c *controller) sandboxGet(key string) sandbox.Sandbox {
c.Lock()
sData, ok := c.sandboxes[key]
c.Unlock()
if !ok {
return nil
}
return sData.sandbox()
}
func (c *controller) LeaveAll(id string) error {
c.Lock()
sData, ok := c.sandboxes[sandbox.GenerateKey(id)]
c.Unlock()
if !ok {
return fmt.Errorf("could not find sandbox for container id %s", id)
}
sData.Lock()
eps := make([]*endpoint, len(sData.endpoints))
for i, ep := range sData.endpoints {
eps[i] = ep
}
sData.Unlock()
for _, ep := range eps {
if err := ep.Leave(id); err != nil {
logrus.Warnf("Failed leaving endpoint id %s: %v\n", ep.ID(), err)
}
}
sData.sandbox().Destroy()
c.Lock()
delete(c.sandboxes, sandbox.GenerateKey(id))
c.Unlock()
return nil
}