Browse code

Datastore handles creating objects atomically.

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.

Spike Curtis authored on 2015/06/19 07:13:38
Showing 14 changed files
... ...
@@ -76,7 +76,7 @@
76 76
 		},
77 77
 		{
78 78
 			"ImportPath": "github.com/docker/libkv",
79
-			"Rev": "ab16c3d4a8785a9877c62d0b11ea4441cf09120c"
79
+			"Rev": "60c7c881345b3c67defc7f93a8297debf041d43c"
80 80
 		},
81 81
 		{
82 82
 			"ImportPath": "github.com/godbus/dbus",
... ...
@@ -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