| 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) |
| } |