In that commit, AtomicPutCreate takes previous = nil to Atomically create keys
that don't exist. We need a create operation that is atomic to prevent races
between multiple libnetworks creating the same object.
Previously, we just created new KVs with an index of 0 and wrote them to the
datastore. Consul accepts this behaviour and interprets index of 0 as
non-existing, but other data backends do no.
- Add Exists() to the KV interface. SetIndex() should also modify a KV so
that it exists.
- Call SetIndex() from within the GetObject() method on DataStore interface.
- This ensures objects have the updated values for exists and index.
- Add SetValue() to the KV interface. This allows implementers to define
their own method to marshall and unmarshall (as bitseq and allocator have).
- Update existing users of the DataStore (endpoint, network, bitseq,
allocator, ov_network) to new interfaces.
- Fix UTs.
| ... | ... |
@@ -28,6 +28,7 @@ type Handle struct {
|
| 28 | 28 |
app string |
| 29 | 29 |
id string |
| 30 | 30 |
dbIndex uint64 |
| 31 |
+ dbExists bool |
|
| 31 | 32 |
store datastore.DataStore |
| 32 | 33 |
sync.Mutex |
| 33 | 34 |
} |
| ... | ... |
@@ -54,18 +55,10 @@ func NewHandle(app string, ds datastore.DataStore, id string, numElements uint32 |
| 54 | 54 |
h.watchForChanges() |
| 55 | 55 |
|
| 56 | 56 |
// Get the initial status from the ds if present. |
| 57 |
- // We will be getting an instance without a dbIndex |
|
| 58 |
- // (GetObject() does not set it): It is ok for now, |
|
| 59 |
- // it will only cause the first allocation on this |
|
| 60 |
- // node to go through a retry. |
|
| 61 |
- var bah []byte |
|
| 62 |
- if err := h.store.GetObject(datastore.Key(h.Key()...), &bah); err != nil {
|
|
| 63 |
- if err != datastore.ErrKeyNotFound {
|
|
| 64 |
- return nil, err |
|
| 65 |
- } |
|
| 66 |
- return h, nil |
|
| 57 |
+ err := h.store.GetObject(datastore.Key(h.Key()...), h) |
|
| 58 |
+ if err != datastore.ErrKeyNotFound {
|
|
| 59 |
+ return nil, err |
|
| 67 | 60 |
} |
| 68 |
- err := h.FromByteArray(bah) |
|
| 69 | 61 |
|
| 70 | 62 |
return h, err |
| 71 | 63 |
} |
| ... | ... |
@@ -199,7 +192,14 @@ func (h *Handle) CheckIfAvailable(ordinal int) (int, int, error) {
|
| 199 | 199 |
func (h *Handle) PushReservation(bytePos, bitPos int, release bool) error {
|
| 200 | 200 |
// Create a copy of the current handler |
| 201 | 201 |
h.Lock() |
| 202 |
- nh := &Handle{app: h.app, id: h.id, store: h.store, dbIndex: h.dbIndex, head: h.head.GetCopy()}
|
|
| 202 |
+ nh := &Handle{
|
|
| 203 |
+ app: h.app, |
|
| 204 |
+ id: h.id, |
|
| 205 |
+ store: h.store, |
|
| 206 |
+ dbIndex: h.dbIndex, |
|
| 207 |
+ head: h.head.GetCopy(), |
|
| 208 |
+ dbExists: h.dbExists, |
|
| 209 |
+ } |
|
| 203 | 210 |
h.Unlock() |
| 204 | 211 |
|
| 205 | 212 |
nh.head = PushReservation(bytePos, bitPos, nh.head, release) |
| ... | ... |
@@ -214,7 +214,9 @@ func (h *Handle) PushReservation(bytePos, bitPos int, release bool) error {
|
| 214 | 214 |
} else {
|
| 215 | 215 |
h.unselected-- |
| 216 | 216 |
} |
| 217 |
- h.dbIndex = nh.dbIndex |
|
| 217 |
+ // Can't use SetIndex() since we're locked. |
|
| 218 |
+ h.dbIndex = nh.Index() |
|
| 219 |
+ h.dbExists = true |
|
| 218 | 220 |
h.Unlock() |
| 219 | 221 |
} |
| 220 | 222 |
|
| ... | ... |
@@ -276,12 +278,6 @@ func (h *Handle) Unselected() uint32 {
|
| 276 | 276 |
return h.unselected |
| 277 | 277 |
} |
| 278 | 278 |
|
| 279 |
-func (h *Handle) getDBIndex() uint64 {
|
|
| 280 |
- h.Lock() |
|
| 281 |
- defer h.Unlock() |
|
| 282 |
- return h.dbIndex |
|
| 283 |
-} |
|
| 284 |
- |
|
| 285 | 279 |
// GetFirstAvailable looks for the first unset bit in passed mask |
| 286 | 280 |
func GetFirstAvailable(head *Sequence) (int, int, error) {
|
| 287 | 281 |
byteIndex := 0 |
| ... | ... |
@@ -38,6 +38,11 @@ func (h *Handle) Value() []byte {
|
| 38 | 38 |
return jv |
| 39 | 39 |
} |
| 40 | 40 |
|
| 41 |
+// SetValue unmarshals the data from the KV store |
|
| 42 |
+func (h *Handle) SetValue(value []byte) error {
|
|
| 43 |
+ return h.FromByteArray(value) |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 41 | 46 |
// Index returns the latest DB Index as seen by this object |
| 42 | 47 |
func (h *Handle) Index() uint64 {
|
| 43 | 48 |
h.Lock() |
| ... | ... |
@@ -49,9 +54,17 @@ func (h *Handle) Index() uint64 {
|
| 49 | 49 |
func (h *Handle) SetIndex(index uint64) {
|
| 50 | 50 |
h.Lock() |
| 51 | 51 |
h.dbIndex = index |
| 52 |
+ h.dbExists = true |
|
| 52 | 53 |
h.Unlock() |
| 53 | 54 |
} |
| 54 | 55 |
|
| 56 |
+// Exists method is true if this object has been stored in the DB. |
|
| 57 |
+func (h *Handle) Exists() bool {
|
|
| 58 |
+ h.Lock() |
|
| 59 |
+ defer h.Unlock() |
|
| 60 |
+ return h.dbExists |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 55 | 63 |
func (h *Handle) watchForChanges() error {
|
| 56 | 64 |
h.Lock() |
| 57 | 65 |
store := h.store |
| ... | ... |
@@ -70,14 +83,12 @@ func (h *Handle) watchForChanges() error {
|
| 70 | 70 |
select {
|
| 71 | 71 |
case kvPair := <-kvpChan: |
| 72 | 72 |
// Only process remote update |
| 73 |
- if kvPair != nil && (kvPair.LastIndex != h.getDBIndex()) {
|
|
| 73 |
+ if kvPair != nil && (kvPair.LastIndex != h.Index()) {
|
|
| 74 | 74 |
err := h.fromDsValue(kvPair.Value) |
| 75 | 75 |
if err != nil {
|
| 76 | 76 |
log.Warnf("Failed to reconstruct bitseq handle from ds watch: %s", err.Error())
|
| 77 | 77 |
} else {
|
| 78 |
- h.Lock() |
|
| 79 |
- h.dbIndex = kvPair.LastIndex |
|
| 80 |
- h.Unlock() |
|
| 78 |
+ h.SetIndex(kvPair.LastIndex) |
|
| 81 | 79 |
} |
| 82 | 80 |
} |
| 83 | 81 |
} |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"strings" |
| 5 | 5 |
|
| 6 | 6 |
"github.com/BurntSushi/toml" |
| 7 |
+ log "github.com/Sirupsen/logrus" |
|
| 7 | 8 |
"github.com/docker/libnetwork/netlabel" |
| 8 | 9 |
) |
| 9 | 10 |
|
| ... | ... |
@@ -57,6 +58,7 @@ type Option func(c *Config) |
| 57 | 57 |
// OptionDefaultNetwork function returns an option setter for a default network |
| 58 | 58 |
func OptionDefaultNetwork(dn string) Option {
|
| 59 | 59 |
return func(c *Config) {
|
| 60 |
+ log.Infof("Option DefaultNetwork: %s", dn)
|
|
| 60 | 61 |
c.Daemon.DefaultNetwork = strings.TrimSpace(dn) |
| 61 | 62 |
} |
| 62 | 63 |
} |
| ... | ... |
@@ -64,6 +66,7 @@ func OptionDefaultNetwork(dn string) Option {
|
| 64 | 64 |
// OptionDefaultDriver function returns an option setter for default driver |
| 65 | 65 |
func OptionDefaultDriver(dd string) Option {
|
| 66 | 66 |
return func(c *Config) {
|
| 67 |
+ log.Infof("Option DefaultDriver: %s", dd)
|
|
| 67 | 68 |
c.Daemon.DefaultDriver = strings.TrimSpace(dd) |
| 68 | 69 |
} |
| 69 | 70 |
} |
| ... | ... |
@@ -82,6 +85,7 @@ func OptionLabels(labels []string) Option {
|
| 82 | 82 |
// OptionKVProvider function returns an option setter for kvstore provider |
| 83 | 83 |
func OptionKVProvider(provider string) Option {
|
| 84 | 84 |
return func(c *Config) {
|
| 85 |
+ log.Infof("Option OptionKVProvider: %s", provider)
|
|
| 85 | 86 |
c.Datastore.Client.Provider = strings.TrimSpace(provider) |
| 86 | 87 |
} |
| 87 | 88 |
} |
| ... | ... |
@@ -89,6 +93,7 @@ func OptionKVProvider(provider string) Option {
|
| 89 | 89 |
// OptionKVProviderURL function returns an option setter for kvstore url |
| 90 | 90 |
func OptionKVProviderURL(url string) Option {
|
| 91 | 91 |
return func(c *Config) {
|
| 92 |
+ log.Infof("Option OptionKVProviderURL: %s", url)
|
|
| 92 | 93 |
c.Datastore.Client.Address = strings.TrimSpace(url) |
| 93 | 94 |
} |
| 94 | 95 |
} |
| ... | ... |
@@ -262,6 +262,7 @@ func (c *controller) NewNetwork(networkType, name string, options ...NetworkOpti |
| 262 | 262 |
} |
| 263 | 263 |
|
| 264 | 264 |
if err := c.updateNetworkToStore(network); err != nil {
|
| 265 |
+ log.Warnf("couldnt create network %s: %v", network.name, err)
|
|
| 265 | 266 |
if e := network.Delete(); e != nil {
|
| 266 | 267 |
log.Warnf("couldnt cleanup network %s: %v", network.name, err)
|
| 267 | 268 |
} |
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 | 1 |
package datastore |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "encoding/json" |
|
| 5 | 4 |
"reflect" |
| 6 | 5 |
"strings" |
| 7 | 6 |
|
| ... | ... |
@@ -14,9 +13,7 @@ import ( |
| 14 | 14 |
//DataStore exported |
| 15 | 15 |
type DataStore interface {
|
| 16 | 16 |
// GetObject gets data from datastore and unmarshals to the specified object |
| 17 |
- GetObject(key string, o interface{}) error
|
|
| 18 |
- // GetUpdatedObject gets data from datastore along with its index and unmarshals to the specified object |
|
| 19 |
- GetUpdatedObject(key string, o interface{}) (uint64, error)
|
|
| 17 |
+ GetObject(key string, o KV) error |
|
| 20 | 18 |
// PutObject adds a new Record based on an object into the datastore |
| 21 | 19 |
PutObject(kvObject KV) error |
| 22 | 20 |
// PutObjectAtomic provides an atomic add and update operation for a Record |
| ... | ... |
@@ -49,10 +46,15 @@ type KV interface {
|
| 49 | 49 |
KeyPrefix() []string |
| 50 | 50 |
// Value method lets an object to marshal its content to be stored in the KV store |
| 51 | 51 |
Value() []byte |
| 52 |
+ // SetValue is used by the datastore to set the object's value when loaded from the data store. |
|
| 53 |
+ SetValue([]byte) error |
|
| 52 | 54 |
// Index method returns the latest DB Index as seen by the object |
| 53 | 55 |
Index() uint64 |
| 54 | 56 |
// SetIndex method allows the datastore to store the latest DB Index into the object |
| 55 | 57 |
SetIndex(uint64) |
| 58 |
+ // True if the object exists in the datastore, false if it hasn't been stored yet. |
|
| 59 |
+ // When SetIndex() is called, the object has been stored. |
|
| 60 |
+ Exists() bool |
|
| 56 | 61 |
} |
| 57 | 62 |
|
| 58 | 63 |
const ( |
| ... | ... |
@@ -121,7 +123,12 @@ func (ds *datastore) PutObjectAtomic(kvObject KV) error {
|
| 121 | 121 |
return types.BadRequestErrorf("invalid KV Object with a nil Value for key %s", Key(kvObject.Key()...))
|
| 122 | 122 |
} |
| 123 | 123 |
|
| 124 |
- previous := &store.KVPair{Key: Key(kvObject.Key()...), LastIndex: kvObject.Index()}
|
|
| 124 |
+ var previous *store.KVPair |
|
| 125 |
+ if kvObject.Exists() {
|
|
| 126 |
+ previous = &store.KVPair{Key: Key(kvObject.Key()...), LastIndex: kvObject.Index()}
|
|
| 127 |
+ } else {
|
|
| 128 |
+ previous = nil |
|
| 129 |
+ } |
|
| 125 | 130 |
_, pair, err := ds.store.AtomicPut(Key(kvObject.Key()...), kvObjValue, previous, nil) |
| 126 | 131 |
if err != nil {
|
| 127 | 132 |
return err |
| ... | ... |
@@ -149,24 +156,20 @@ func (ds *datastore) putObjectWithKey(kvObject KV, key ...string) error {
|
| 149 | 149 |
} |
| 150 | 150 |
|
| 151 | 151 |
// GetObject returns a record matching the key |
| 152 |
-func (ds *datastore) GetObject(key string, o interface{}) error {
|
|
| 152 |
+func (ds *datastore) GetObject(key string, o KV) error {
|
|
| 153 | 153 |
kvPair, err := ds.store.Get(key) |
| 154 | 154 |
if err != nil {
|
| 155 | 155 |
return err |
| 156 | 156 |
} |
| 157 |
- return json.Unmarshal(kvPair.Value, o) |
|
| 158 |
-} |
|
| 159 |
- |
|
| 160 |
-// GetUpdateObject returns a record matching the key |
|
| 161 |
-func (ds *datastore) GetUpdatedObject(key string, o interface{}) (uint64, error) {
|
|
| 162 |
- kvPair, err := ds.store.Get(key) |
|
| 157 |
+ err = o.SetValue(kvPair.Value) |
|
| 163 | 158 |
if err != nil {
|
| 164 |
- return 0, err |
|
| 165 |
- } |
|
| 166 |
- if err := json.Unmarshal(kvPair.Value, o); err != nil {
|
|
| 167 |
- return 0, err |
|
| 159 |
+ return err |
|
| 168 | 160 |
} |
| 169 |
- return kvPair.LastIndex, nil |
|
| 161 |
+ |
|
| 162 |
+ // Make sure the object has a correct view of the DB index in case we need to modify it |
|
| 163 |
+ // and update the DB. |
|
| 164 |
+ o.SetIndex(kvPair.LastIndex) |
|
| 165 |
+ return nil |
|
| 170 | 166 |
} |
| 171 | 167 |
|
| 172 | 168 |
// DeleteObject unconditionally deletes a record from the store |
| ... | ... |
@@ -8,6 +8,7 @@ import ( |
| 8 | 8 |
"github.com/docker/libnetwork/config" |
| 9 | 9 |
_ "github.com/docker/libnetwork/netutils" |
| 10 | 10 |
"github.com/docker/libnetwork/options" |
| 11 |
+ "github.com/stretchr/testify/assert" |
|
| 11 | 12 |
) |
| 12 | 13 |
|
| 13 | 14 |
var dummyKey = "dummy" |
| ... | ... |
@@ -69,16 +70,18 @@ func TestKVObjectFlatKey(t *testing.T) {
|
| 69 | 69 |
func TestAtomicKVObjectFlatKey(t *testing.T) {
|
| 70 | 70 |
store := NewTestDataStore() |
| 71 | 71 |
expected := dummyKVObject("1111", true)
|
| 72 |
+ assert.False(t, expected.Exists()) |
|
| 72 | 73 |
err := store.PutObjectAtomic(expected) |
| 73 | 74 |
if err != nil {
|
| 74 | 75 |
t.Fatal(err) |
| 75 | 76 |
} |
| 77 |
+ assert.True(t, expected.Exists()) |
|
| 76 | 78 |
|
| 77 | 79 |
// PutObjectAtomic automatically sets the Index again. Hence the following must pass. |
| 78 | 80 |
|
| 79 | 81 |
err = store.PutObjectAtomic(expected) |
| 80 | 82 |
if err != nil {
|
| 81 |
- t.Fatal("Atomic update with an older Index must fail")
|
|
| 83 |
+ t.Fatal("Atomic update should succeed.")
|
|
| 82 | 84 |
} |
| 83 | 85 |
|
| 84 | 86 |
// Get the latest index and try PutObjectAtomic again for the same Key |
| ... | ... |
@@ -90,12 +93,22 @@ func TestAtomicKVObjectFlatKey(t *testing.T) {
|
| 90 | 90 |
n := dummyObject{}
|
| 91 | 91 |
json.Unmarshal(data.Value, &n) |
| 92 | 92 |
n.ID = "1111" |
| 93 |
- n.DBIndex = data.LastIndex |
|
| 93 |
+ n.SetIndex(data.LastIndex) |
|
| 94 | 94 |
n.ReturnValue = true |
| 95 | 95 |
err = store.PutObjectAtomic(&n) |
| 96 | 96 |
if err != nil {
|
| 97 | 97 |
t.Fatal(err) |
| 98 | 98 |
} |
| 99 |
+ |
|
| 100 |
+ // Get the Object using GetObject, then set again. |
|
| 101 |
+ newObj := dummyObject{}
|
|
| 102 |
+ err = store.GetObject(Key(expected.Key()...), &newObj) |
|
| 103 |
+ assert.True(t, newObj.Exists()) |
|
| 104 |
+ err = store.PutObjectAtomic(&n) |
|
| 105 |
+ if err != nil {
|
|
| 106 |
+ t.Fatal(err) |
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 99 | 109 |
} |
| 100 | 110 |
|
| 101 | 111 |
// dummy data used to test the datastore |
| ... | ... |
@@ -108,6 +121,7 @@ type dummyObject struct {
|
| 108 | 108 |
Generic options.Generic `kv:"iterative"` |
| 109 | 109 |
ID string |
| 110 | 110 |
DBIndex uint64 |
| 111 |
+ DBExists bool |
|
| 111 | 112 |
ReturnValue bool |
| 112 | 113 |
} |
| 113 | 114 |
|
| ... | ... |
@@ -131,12 +145,21 @@ func (n *dummyObject) Value() []byte {
|
| 131 | 131 |
return b |
| 132 | 132 |
} |
| 133 | 133 |
|
| 134 |
+func (n *dummyObject) SetValue(value []byte) error {
|
|
| 135 |
+ return json.Unmarshal(value, n) |
|
| 136 |
+} |
|
| 137 |
+ |
|
| 134 | 138 |
func (n *dummyObject) Index() uint64 {
|
| 135 | 139 |
return n.DBIndex |
| 136 | 140 |
} |
| 137 | 141 |
|
| 138 | 142 |
func (n *dummyObject) SetIndex(index uint64) {
|
| 139 | 143 |
n.DBIndex = index |
| 144 |
+ n.DBExists = true |
|
| 145 |
+} |
|
| 146 |
+ |
|
| 147 |
+func (n *dummyObject) Exists() bool {
|
|
| 148 |
+ return n.DBExists |
|
| 140 | 149 |
} |
| 141 | 150 |
|
| 142 | 151 |
func (n *dummyObject) MarshalJSON() ([]byte, error) {
|
| ... | ... |
@@ -162,10 +185,11 @@ func (n *dummyObject) UnmarshalJSON(b []byte) (err error) {
|
| 162 | 162 |
|
| 163 | 163 |
// dummy structure to test "recursive" cases |
| 164 | 164 |
type recStruct struct {
|
| 165 |
- Name string `kv:"leaf"` |
|
| 166 |
- Field1 int `kv:"leaf"` |
|
| 167 |
- Dict map[string]string `kv:"iterative"` |
|
| 168 |
- DBIndex uint64 |
|
| 165 |
+ Name string `kv:"leaf"` |
|
| 166 |
+ Field1 int `kv:"leaf"` |
|
| 167 |
+ Dict map[string]string `kv:"iterative"` |
|
| 168 |
+ DBIndex uint64 |
|
| 169 |
+ DBExists bool |
|
| 169 | 170 |
} |
| 170 | 171 |
|
| 171 | 172 |
func (r *recStruct) Key() []string {
|
| ... | ... |
@@ -179,12 +203,21 @@ func (r *recStruct) Value() []byte {
|
| 179 | 179 |
return b |
| 180 | 180 |
} |
| 181 | 181 |
|
| 182 |
+func (r *recStruct) SetValue(value []byte) error {
|
|
| 183 |
+ return json.Unmarshal(value, r) |
|
| 184 |
+} |
|
| 185 |
+ |
|
| 182 | 186 |
func (r *recStruct) Index() uint64 {
|
| 183 | 187 |
return r.DBIndex |
| 184 | 188 |
} |
| 185 | 189 |
|
| 186 | 190 |
func (r *recStruct) SetIndex(index uint64) {
|
| 187 | 191 |
r.DBIndex = index |
| 192 |
+ r.DBExists = true |
|
| 193 |
+} |
|
| 194 |
+ |
|
| 195 |
+func (r *recStruct) Exists() bool {
|
|
| 196 |
+ return r.DBExists |
|
| 188 | 197 |
} |
| 189 | 198 |
|
| 190 | 199 |
func dummyKVObject(id string, retValue bool) *dummyObject {
|
| ... | ... |
@@ -195,12 +228,13 @@ func dummyKVObject(id string, retValue bool) *dummyObject {
|
| 195 | 195 |
Name: "testNw", |
| 196 | 196 |
NetworkType: "bridge", |
| 197 | 197 |
EnableIPv6: true, |
| 198 |
- Rec: &recStruct{"gen", 5, cDict, 0},
|
|
| 198 |
+ Rec: &recStruct{"gen", 5, cDict, 0, false},
|
|
| 199 | 199 |
ID: id, |
| 200 | 200 |
DBIndex: 0, |
| 201 |
- ReturnValue: retValue} |
|
| 201 |
+ ReturnValue: retValue, |
|
| 202 |
+ DBExists: false} |
|
| 202 | 203 |
generic := make(map[string]interface{})
|
| 203 |
- generic["label1"] = &recStruct{"value1", 1, cDict, 0}
|
|
| 204 |
+ generic["label1"] = &recStruct{"value1", 1, cDict, 0, false}
|
|
| 204 | 205 |
generic["label2"] = "subnet=10.1.1.0/16" |
| 205 | 206 |
n.Generic = generic |
| 206 | 207 |
return &n |
| ... | ... |
@@ -93,8 +93,18 @@ func (s *MockStore) NewLock(key string, options *store.LockOptions) (store.Locke |
| 93 | 93 |
// modified in the meantime, throws an error if this is the case |
| 94 | 94 |
func (s *MockStore) AtomicPut(key string, newValue []byte, previous *store.KVPair, options *store.WriteOptions) (bool, *store.KVPair, error) {
|
| 95 | 95 |
mData := s.db[key] |
| 96 |
- if mData != nil && mData.Index != previous.LastIndex {
|
|
| 97 |
- return false, nil, types.BadRequestErrorf("atomic put failed due to mismatched Index")
|
|
| 96 |
+ |
|
| 97 |
+ if previous == nil {
|
|
| 98 |
+ if mData != nil {
|
|
| 99 |
+ return false, nil, types.BadRequestErrorf("atomic put failed because key exists")
|
|
| 100 |
+ } // Else OK. |
|
| 101 |
+ } else {
|
|
| 102 |
+ if mData == nil {
|
|
| 103 |
+ return false, nil, types.BadRequestErrorf("atomic put failed because key exists")
|
|
| 104 |
+ } |
|
| 105 |
+ if mData != nil && mData.Index != previous.LastIndex {
|
|
| 106 |
+ return false, nil, types.BadRequestErrorf("atomic put failed due to mismatched Index")
|
|
| 107 |
+ } // Else OK. |
|
| 98 | 108 |
} |
| 99 | 109 |
err := s.Put(key, newValue, nil) |
| 100 | 110 |
if err != nil {
|
| ... | ... |
@@ -22,6 +22,7 @@ type network struct {
|
| 22 | 22 |
id types.UUID |
| 23 | 23 |
vni uint32 |
| 24 | 24 |
dbIndex uint64 |
| 25 |
+ dbExists bool |
|
| 25 | 26 |
sbox sandbox.Sandbox |
| 26 | 27 |
endpoints endpointTable |
| 27 | 28 |
ipAllocator *ipallocator.IPAllocator |
| ... | ... |
@@ -260,6 +261,20 @@ func (n *network) Index() uint64 {
|
| 260 | 260 |
|
| 261 | 261 |
func (n *network) SetIndex(index uint64) {
|
| 262 | 262 |
n.dbIndex = index |
| 263 |
+ n.dbExists = true |
|
| 264 |
+} |
|
| 265 |
+ |
|
| 266 |
+func (n *network) Exists() bool {
|
|
| 267 |
+ return n.dbExists |
|
| 268 |
+} |
|
| 269 |
+ |
|
| 270 |
+func (n *network) SetValue(value []byte) error {
|
|
| 271 |
+ var vni uint32 |
|
| 272 |
+ err := json.Unmarshal(value, &vni) |
|
| 273 |
+ if err == nil {
|
|
| 274 |
+ n.setVxlanID(vni) |
|
| 275 |
+ } |
|
| 276 |
+ return err |
|
| 263 | 277 |
} |
| 264 | 278 |
|
| 265 | 279 |
func (n *network) writeToStore() error {
|
| ... | ... |
@@ -297,8 +312,7 @@ func (n *network) obtainVxlanID() error {
|
| 297 | 297 |
|
| 298 | 298 |
for {
|
| 299 | 299 |
var vxlanID uint32 |
| 300 |
- if err := n.driver.store.GetObject(datastore.Key(n.Key()...), |
|
| 301 |
- &vxlanID); err != nil {
|
|
| 300 |
+ if err := n.driver.store.GetObject(datastore.Key(n.Key()...), n); err != nil {
|
|
| 302 | 301 |
if err == datastore.ErrKeyNotFound {
|
| 303 | 302 |
vxlanID, err = n.driver.vxlanIdm.GetID() |
| 304 | 303 |
if err != nil {
|
| ... | ... |
@@ -318,8 +332,6 @@ func (n *network) obtainVxlanID() error {
|
| 318 | 318 |
} |
| 319 | 319 |
return fmt.Errorf("failed to obtain vxlan id from data store: %v", err)
|
| 320 | 320 |
} |
| 321 |
- |
|
| 322 |
- n.setVxlanID(vxlanID) |
|
| 323 | 321 |
return nil |
| 324 | 322 |
} |
| 325 | 323 |
} |
| ... | ... |
@@ -124,6 +124,7 @@ type endpoint struct {
|
| 124 | 124 |
generic map[string]interface{}
|
| 125 | 125 |
joinLeaveDone chan struct{}
|
| 126 | 126 |
dbIndex uint64 |
| 127 |
+ dbExists bool |
|
| 127 | 128 |
sync.Mutex |
| 128 | 129 |
} |
| 129 | 130 |
|
| ... | ... |
@@ -258,6 +259,10 @@ func (ep *endpoint) Value() []byte {
|
| 258 | 258 |
return b |
| 259 | 259 |
} |
| 260 | 260 |
|
| 261 |
+func (ep *endpoint) SetValue(value []byte) error {
|
|
| 262 |
+ return json.Unmarshal(value, ep) |
|
| 263 |
+} |
|
| 264 |
+ |
|
| 261 | 265 |
func (ep *endpoint) Index() uint64 {
|
| 262 | 266 |
ep.Lock() |
| 263 | 267 |
defer ep.Unlock() |
| ... | ... |
@@ -268,6 +273,13 @@ func (ep *endpoint) SetIndex(index uint64) {
|
| 268 | 268 |
ep.Lock() |
| 269 | 269 |
defer ep.Unlock() |
| 270 | 270 |
ep.dbIndex = index |
| 271 |
+ ep.dbExists = true |
|
| 272 |
+} |
|
| 273 |
+ |
|
| 274 |
+func (ep *endpoint) Exists() bool {
|
|
| 275 |
+ ep.Lock() |
|
| 276 |
+ defer ep.Unlock() |
|
| 277 |
+ return ep.dbExists |
|
| 271 | 278 |
} |
| 272 | 279 |
|
| 273 | 280 |
func (ep *endpoint) processOptions(options ...EndpointOption) {
|
| ... | ... |
@@ -35,10 +35,11 @@ type Allocator struct {
|
| 35 | 35 |
// Allocated addresses in each address space's internal subnet |
| 36 | 36 |
addresses map[subnetKey]*bitseq.Handle |
| 37 | 37 |
// Datastore |
| 38 |
- store datastore.DataStore |
|
| 39 |
- App string |
|
| 40 |
- ID string |
|
| 41 |
- dbIndex uint64 |
|
| 38 |
+ store datastore.DataStore |
|
| 39 |
+ App string |
|
| 40 |
+ ID string |
|
| 41 |
+ dbIndex uint64 |
|
| 42 |
+ dbExists bool |
|
| 42 | 43 |
sync.Mutex |
| 43 | 44 |
} |
| 44 | 45 |
|
| ... | ... |
@@ -100,6 +101,7 @@ func (a *Allocator) subnetConfigFromStore(kvPair *store.KVPair) {
|
| 100 | 100 |
if a.dbIndex < kvPair.LastIndex {
|
| 101 | 101 |
a.subnets = byteArrayToSubnets(kvPair.Value) |
| 102 | 102 |
a.dbIndex = kvPair.LastIndex |
| 103 |
+ a.dbExists = true |
|
| 103 | 104 |
} |
| 104 | 105 |
a.Unlock() |
| 105 | 106 |
} |
| ... | ... |
@@ -39,6 +39,12 @@ func (a *Allocator) Value() []byte {
|
| 39 | 39 |
return b |
| 40 | 40 |
} |
| 41 | 41 |
|
| 42 |
+// SetValue unmarshalls the data from the KV store. |
|
| 43 |
+func (a *Allocator) SetValue(value []byte) error {
|
|
| 44 |
+ a.subnets = byteArrayToSubnets(value) |
|
| 45 |
+ return nil |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 42 | 48 |
func subnetsToByteArray(m map[subnetKey]*SubnetInfo) ([]byte, error) {
|
| 43 | 49 |
if m == nil {
|
| 44 | 50 |
return nil, nil |
| ... | ... |
@@ -94,9 +100,17 @@ func (a *Allocator) Index() uint64 {
|
| 94 | 94 |
func (a *Allocator) SetIndex(index uint64) {
|
| 95 | 95 |
a.Lock() |
| 96 | 96 |
a.dbIndex = index |
| 97 |
+ a.dbExists = true |
|
| 97 | 98 |
a.Unlock() |
| 98 | 99 |
} |
| 99 | 100 |
|
| 101 |
+// Exists method is true if this object has been stored in the DB. |
|
| 102 |
+func (a *Allocator) Exists() bool {
|
|
| 103 |
+ a.Lock() |
|
| 104 |
+ defer a.Unlock() |
|
| 105 |
+ return a.dbExists |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 100 | 108 |
func (a *Allocator) watchForChanges() error {
|
| 101 | 109 |
if a.store == nil {
|
| 102 | 110 |
return nil |
| ... | ... |
@@ -67,6 +67,7 @@ type network struct {
|
| 67 | 67 |
generic options.Generic |
| 68 | 68 |
dbIndex uint64 |
| 69 | 69 |
svcRecords svcMap |
| 70 |
+ dbExists bool |
|
| 70 | 71 |
stopWatchCh chan struct{}
|
| 71 | 72 |
sync.Mutex |
| 72 | 73 |
} |
| ... | ... |
@@ -116,6 +117,10 @@ func (n *network) Value() []byte {
|
| 116 | 116 |
return b |
| 117 | 117 |
} |
| 118 | 118 |
|
| 119 |
+func (n *network) SetValue(value []byte) error {
|
|
| 120 |
+ return json.Unmarshal(value, n) |
|
| 121 |
+} |
|
| 122 |
+ |
|
| 119 | 123 |
func (n *network) Index() uint64 {
|
| 120 | 124 |
n.Lock() |
| 121 | 125 |
defer n.Unlock() |
| ... | ... |
@@ -125,9 +130,16 @@ func (n *network) Index() uint64 {
|
| 125 | 125 |
func (n *network) SetIndex(index uint64) {
|
| 126 | 126 |
n.Lock() |
| 127 | 127 |
n.dbIndex = index |
| 128 |
+ n.dbExists = true |
|
| 128 | 129 |
n.Unlock() |
| 129 | 130 |
} |
| 130 | 131 |
|
| 132 |
+func (n *network) Exists() bool {
|
|
| 133 |
+ n.Lock() |
|
| 134 |
+ defer n.Unlock() |
|
| 135 |
+ return n.dbExists |
|
| 136 |
+} |
|
| 137 |
+ |
|
| 131 | 138 |
func (n *network) EndpointCnt() uint64 {
|
| 132 | 139 |
n.Lock() |
| 133 | 140 |
defer n.Unlock() |
| ... | ... |
@@ -292,7 +304,9 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi |
| 292 | 292 |
return nil, types.ForbiddenErrorf("service endpoint with name %s already exists", name)
|
| 293 | 293 |
} |
| 294 | 294 |
|
| 295 |
- ep := &endpoint{name: name, iFaces: []*endpointInterface{}, generic: make(map[string]interface{})}
|
|
| 295 |
+ ep := &endpoint{name: name,
|
|
| 296 |
+ iFaces: []*endpointInterface{},
|
|
| 297 |
+ generic: make(map[string]interface{})}
|
|
| 296 | 298 |
ep.id = types.UUID(stringid.GenerateRandomID()) |
| 297 | 299 |
ep.network = n |
| 298 | 300 |
ep.processOptions(options...) |
| ... | ... |
@@ -197,6 +197,7 @@ func (c *controller) watchNetworks() error {
|
| 197 | 197 |
} |
| 198 | 198 |
} |
| 199 | 199 |
c.processNetworkUpdate(nws, &tmpview) |
| 200 |
+ |
|
| 200 | 201 |
// Delete processing |
| 201 | 202 |
for k := range tmpview {
|
| 202 | 203 |
c.Lock() |
| ... | ... |
@@ -259,7 +260,7 @@ func (n *network) watchEndpoints() error {
|
| 259 | 259 |
continue |
| 260 | 260 |
} |
| 261 | 261 |
delete(tmpview, ep.id) |
| 262 |
- ep.dbIndex = epe.LastIndex |
|
| 262 |
+ ep.SetIndex(epe.LastIndex) |
|
| 263 | 263 |
ep.network = n |
| 264 | 264 |
if n.ctrlr.processEndpointUpdate(&ep) {
|
| 265 | 265 |
err = n.ctrlr.newEndpointFromStore(epe.Key, &ep) |
| ... | ... |
@@ -310,15 +311,17 @@ func (c *controller) processNetworkUpdate(nws []*store.KVPair, prune *networkTab |
| 310 | 310 |
if prune != nil {
|
| 311 | 311 |
delete(*prune, n.id) |
| 312 | 312 |
} |
| 313 |
- n.dbIndex = kve.LastIndex |
|
| 313 |
+ n.SetIndex(kve.LastIndex) |
|
| 314 | 314 |
c.Lock() |
| 315 | 315 |
existing, ok := c.networks[n.id] |
| 316 | 316 |
c.Unlock() |
| 317 | 317 |
if ok {
|
| 318 | 318 |
existing.Lock() |
| 319 | 319 |
// Skip existing network update |
| 320 |
- if existing.dbIndex != n.dbIndex {
|
|
| 321 |
- existing.dbIndex = n.dbIndex |
|
| 320 |
+ if existing.dbIndex != n.Index() {
|
|
| 321 |
+ // Can't use SetIndex() since existing is locked. |
|
| 322 |
+ existing.dbIndex = n.Index() |
|
| 323 |
+ existing.dbExists = true |
|
| 322 | 324 |
existing.endpointCnt = n.endpointCnt |
| 323 | 325 |
} |
| 324 | 326 |
existing.Unlock() |
| ... | ... |
@@ -353,8 +356,10 @@ func (c *controller) processEndpointUpdate(ep *endpoint) bool {
|
| 353 | 353 |
|
| 354 | 354 |
ee := existing.(*endpoint) |
| 355 | 355 |
ee.Lock() |
| 356 |
- if ee.dbIndex != ep.dbIndex {
|
|
| 357 |
- ee.dbIndex = ep.dbIndex |
|
| 356 |
+ if ee.dbIndex != ep.Index() {
|
|
| 357 |
+ // Can't use SetIndex() because ee is locked. |
|
| 358 |
+ ee.dbIndex = ep.Index() |
|
| 359 |
+ ee.dbExists = true |
|
| 358 | 360 |
if ee.container != nil && ep.container != nil {
|
| 359 | 361 |
// we care only about the container id |
| 360 | 362 |
ee.container.id = ep.container.id |