1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/libnetwork/endpoint_cnt.go
Madhu Venugopal 2c8670b496 endpoint_cnt store updates should not create an object
endpoint_cnt object is created during network create and destroyed when
network is deleted. But the updateToStore function creates an object
when it is not present in the store. endpoint_cnt is a mutable object
and is updated during endpoint create and delete events. If endpoint
create or delete happens after the network is deleted, it can
incorrectly create an endpoint_cnt object in the store and that can
cause problems when the same network is created again later.

The fix is to not create the endpoint_cnt object when endpoint_cnt is
incremented or decremented

Signed-off-by: Madhu Venugopal <madhu@docker.com>
2017-10-26 17:52:40 -07:00

182 lines
3.4 KiB
Go

package libnetwork
import (
"encoding/json"
"fmt"
"sync"
"github.com/docker/libnetwork/datastore"
)
type endpointCnt struct {
n *network
Count uint64
dbIndex uint64
dbExists bool
sync.Mutex
}
const epCntKeyPrefix = "endpoint_count"
func (ec *endpointCnt) Key() []string {
ec.Lock()
defer ec.Unlock()
return []string{epCntKeyPrefix, ec.n.id}
}
func (ec *endpointCnt) KeyPrefix() []string {
ec.Lock()
defer ec.Unlock()
return []string{epCntKeyPrefix, ec.n.id}
}
func (ec *endpointCnt) Value() []byte {
ec.Lock()
defer ec.Unlock()
b, err := json.Marshal(ec)
if err != nil {
return nil
}
return b
}
func (ec *endpointCnt) SetValue(value []byte) error {
ec.Lock()
defer ec.Unlock()
return json.Unmarshal(value, &ec)
}
func (ec *endpointCnt) Index() uint64 {
ec.Lock()
defer ec.Unlock()
return ec.dbIndex
}
func (ec *endpointCnt) SetIndex(index uint64) {
ec.Lock()
ec.dbIndex = index
ec.dbExists = true
ec.Unlock()
}
func (ec *endpointCnt) Exists() bool {
ec.Lock()
defer ec.Unlock()
return ec.dbExists
}
func (ec *endpointCnt) Skip() bool {
ec.Lock()
defer ec.Unlock()
return !ec.n.persist
}
func (ec *endpointCnt) New() datastore.KVObject {
ec.Lock()
defer ec.Unlock()
return &endpointCnt{
n: ec.n,
}
}
func (ec *endpointCnt) CopyTo(o datastore.KVObject) error {
ec.Lock()
defer ec.Unlock()
dstEc := o.(*endpointCnt)
dstEc.n = ec.n
dstEc.Count = ec.Count
dstEc.dbExists = ec.dbExists
dstEc.dbIndex = ec.dbIndex
return nil
}
func (ec *endpointCnt) DataScope() string {
return ec.n.DataScope()
}
func (ec *endpointCnt) EndpointCnt() uint64 {
ec.Lock()
defer ec.Unlock()
return ec.Count
}
func (ec *endpointCnt) updateStore() error {
store := ec.n.getController().getStore(ec.DataScope())
if store == nil {
return fmt.Errorf("store not found for scope %s on endpoint count update", ec.DataScope())
}
// make a copy of count and n to avoid being overwritten by store.GetObject
count := ec.EndpointCnt()
n := ec.n
for {
if err := ec.n.getController().updateToStore(ec); err == nil || err != datastore.ErrKeyModified {
return err
}
if err := store.GetObject(datastore.Key(ec.Key()...), ec); err != nil {
return fmt.Errorf("could not update the kvobject to latest on endpoint count update: %v", err)
}
ec.Lock()
ec.Count = count
ec.n = n
ec.Unlock()
}
}
func (ec *endpointCnt) setCnt(cnt uint64) error {
ec.Lock()
ec.Count = cnt
ec.Unlock()
return ec.updateStore()
}
func (ec *endpointCnt) atomicIncDecEpCnt(inc bool) error {
store := ec.n.getController().getStore(ec.DataScope())
if store == nil {
return fmt.Errorf("store not found for scope %s", ec.DataScope())
}
tmp := &endpointCnt{n: ec.n}
if err := store.GetObject(datastore.Key(ec.Key()...), tmp); err != nil {
return err
}
retry:
ec.Lock()
if inc {
ec.Count++
} else {
if ec.Count > 0 {
ec.Count--
}
}
ec.Unlock()
if err := ec.n.getController().updateToStore(ec); err != nil {
if err == datastore.ErrKeyModified {
if err := store.GetObject(datastore.Key(ec.Key()...), ec); err != nil {
return fmt.Errorf("could not update the kvobject to latest when trying to atomic add endpoint count: %v", err)
}
goto retry
}
return err
}
return nil
}
func (ec *endpointCnt) IncEndpointCnt() error {
return ec.atomicIncDecEpCnt(true)
}
func (ec *endpointCnt) DecEndpointCnt() error {
return ec.atomicIncDecEpCnt(false)
}