Skip to content

Commit c395cf2

Browse files
mavenugoaboch
authored andcommitted
Datastore additions to bitmask management
Signed-off-by: Madhu Venugopal <madhu@docker.com>
1 parent 4c4f71e commit c395cf2

File tree

6 files changed

+136
-22
lines changed

6 files changed

+136
-22
lines changed

libnetwork/bitseq/sequence.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"sync"
99

10+
"github.com/docker/libnetwork/datastore"
1011
"github.com/docker/libnetwork/netutils"
1112
)
1213

@@ -21,21 +22,28 @@ const (
2122

2223
// Handle contains the sequece representing the bitmask and its identifier
2324
type Handle struct {
24-
ID string
25-
Head *Sequence
25+
App string
26+
ID string
27+
Head *Sequence
28+
store datastore.DataStore
29+
dbIndex uint64
2630
sync.Mutex
2731
}
2832

2933
// NewHandle returns a thread-safe instance of the bitmask handler
30-
func NewHandle(id string, numElements uint32) *Handle {
31-
return &Handle{
32-
ID: id,
34+
func NewHandle(app string, ds datastore.DataStore, id string, numElements uint32) *Handle {
35+
h := &Handle{
36+
App: app,
37+
ID: id,
38+
store: ds,
3339
Head: &Sequence{
3440
Block: 0x0,
3541
Count: getNumBlocks(numElements),
3642
Next: nil,
3743
},
3844
}
45+
h.watchForChanges()
46+
return h
3947
}
4048

4149
// Sequence reresents a recurring sequence of 32 bits long bitmasks
@@ -151,10 +159,11 @@ func (h *Handle) CheckIfAvailable(ordinal int) (int, int, error) {
151159
}
152160

153161
// PushReservation pushes the bit reservation inside the bitmask.
154-
func (h *Handle) PushReservation(bytePos, bitPos int, release bool) {
162+
func (h *Handle) PushReservation(bytePos, bitPos int, release bool) error {
155163
h.Lock()
156-
defer h.Unlock()
157164
h.Head = PushReservation(bytePos, bitPos, h.Head, release)
165+
h.Unlock()
166+
return h.writeToStore()
158167
}
159168

160169
// GetFirstAvailable looks for the first unset bit in passed mask

libnetwork/bitseq/store.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package bitseq
2+
3+
import (
4+
"github.com/docker/libnetwork/datastore"
5+
"github.com/docker/libnetwork/types"
6+
)
7+
8+
// Key provides the Key to be used in KV Store
9+
func (h *Handle) Key() []string {
10+
h.Lock()
11+
defer h.Unlock()
12+
return []string{h.App, h.ID}
13+
}
14+
15+
// KeyPrefix returns the immediate parent key that can be used for tree walk
16+
func (h *Handle) KeyPrefix() []string {
17+
h.Lock()
18+
defer h.Unlock()
19+
return []string{h.App}
20+
}
21+
22+
// Value marshala the data to be stored in the KV store
23+
func (h *Handle) Value() []byte {
24+
h.Lock()
25+
defer h.Unlock()
26+
head := h.Head
27+
if head == nil {
28+
return []byte{}
29+
}
30+
b, err := head.ToByteArray()
31+
if err != nil {
32+
return []byte{}
33+
}
34+
return b
35+
}
36+
37+
// Index returns the latest DB Index as seen by this object
38+
func (h *Handle) Index() uint64 {
39+
h.Lock()
40+
defer h.Unlock()
41+
return h.dbIndex
42+
}
43+
44+
// SetIndex method allows the datastore to store the latest DB Index into this object
45+
func (h *Handle) SetIndex(index uint64) {
46+
h.Lock()
47+
h.dbIndex = index
48+
h.Unlock()
49+
}
50+
51+
func (h *Handle) watchForChanges() error {
52+
h.Lock()
53+
store := h.store
54+
h.Unlock()
55+
56+
if store == nil {
57+
return nil
58+
}
59+
60+
kvpChan, err := store.KVStore().Watch(datastore.Key(h.Key()...), nil)
61+
if err != nil {
62+
return err
63+
}
64+
go func() {
65+
for {
66+
select {
67+
case kvPair := <-kvpChan:
68+
h.Lock()
69+
h.dbIndex = kvPair.LastIndex
70+
h.Head.FromByteArray(kvPair.Value)
71+
h.Unlock()
72+
}
73+
}
74+
}()
75+
return nil
76+
}
77+
78+
func (h *Handle) writeToStore() error {
79+
h.Lock()
80+
store := h.store
81+
h.Unlock()
82+
if store == nil {
83+
return nil
84+
}
85+
err := store.PutObjectAtomic(h)
86+
if err == datastore.ErrKeyModified {
87+
return types.RetryErrorf("failed to perform atomic write (%v). retry might fix the error", err)
88+
}
89+
return err
90+
}
91+
92+
func (h *Handle) deleteFromStore() error {
93+
h.Lock()
94+
store := h.store
95+
h.Unlock()
96+
if store == nil {
97+
return nil
98+
}
99+
return store.DeleteObjectAtomic(h)
100+
}

libnetwork/idm/idm.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66

77
"github.com/docker/libnetwork/bitseq"
8+
"github.com/docker/libnetwork/datastore"
89
)
910

1011
// Idm manages the reservation/release of numerical ids from a contiguos set
@@ -15,14 +16,14 @@ type Idm struct {
1516
}
1617

1718
// New returns an instance of id manager for a set of [start-end] numerical ids
18-
func New(id string, start, end uint32) (*Idm, error) {
19+
func New(ds datastore.DataStore, id string, start, end uint32) (*Idm, error) {
1920
if id == "" {
2021
return nil, fmt.Errorf("Invalid id")
2122
}
2223
if end <= start {
2324
return nil, fmt.Errorf("Invalid set range: [%d, %d]", start, end)
2425
}
25-
return &Idm{start: start, end: end, handle: bitseq.NewHandle(id, 1+end-start)}, nil
26+
return &Idm{start: start, end: end, handle: bitseq.NewHandle("idm", ds, id, uint32(1+end-start))}, nil
2627
}
2728

2829
// GetID returns the first available id in the set

libnetwork/idm/idm_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ import (
55
)
66

77
func TestNew(t *testing.T) {
8-
_, err := New("", 0, 1)
8+
_, err := New(nil, "", 0, 1)
99
if err == nil {
1010
t.Fatalf("Expected failure, but succeeded")
1111
}
1212

13-
_, err = New("myset", 1<<10, 0)
13+
_, err = New(nil, "myset", 1<<10, 0)
1414
if err == nil {
1515
t.Fatalf("Expected failure, but succeeded")
1616
}
1717

18-
i, err := New("myset", 0, 10)
18+
i, err := New(nil, "myset", 0, 10)
1919
if err != nil {
2020
t.Fatalf("Unexpected failure: %v", err)
2121
}
@@ -31,7 +31,7 @@ func TestNew(t *testing.T) {
3131
}
3232

3333
func TestAllocate(t *testing.T) {
34-
i, err := New("myids", 50, 52)
34+
i, err := New(nil, "myids", 50, 52)
3535
if err != nil {
3636
t.Fatal(err)
3737
}

libnetwork/ipam/allocator.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"sync"
77

88
"github.com/docker/libnetwork/bitseq"
9+
"github.com/docker/libnetwork/datastore"
910
)
1011

1112
const (
@@ -26,15 +27,18 @@ type Allocator struct {
2627
subnetsInfo map[subnetKey]*SubnetInfo
2728
// Allocated addresses in each address space's internal subnet
2829
addresses map[subnetKey]*bitmask
30+
// Datastore
31+
store datastore.DataStore
2932
sync.Mutex
3033
}
3134

3235
// NewAllocator returns an instance of libnetwork ipam
33-
func NewAllocator() *Allocator {
36+
func NewAllocator(ds datastore.DataStore) *Allocator {
3437
a := &Allocator{}
3538
a.subnetsInfo = make(map[subnetKey]*SubnetInfo)
3639
a.addresses = make(map[subnetKey]*bitmask)
3740
a.internalHostSize = defaultInternalHostSize
41+
a.store = ds
3842
return a
3943
}
4044

@@ -102,7 +106,7 @@ func (a *Allocator) AddSubnet(addrSpace AddressSpace, subnetInfo *SubnetInfo) er
102106
a.Lock()
103107
a.addresses[smallKey] = &bitmask{
104108
subnet: sub,
105-
addressMask: bitseq.NewHandle(smallKey.String(), uint32(numAddresses)),
109+
addressMask: bitseq.NewHandle("ipam", a.store, smallKey.String(), uint32(numAddresses)),
106110
freeAddresses: numAddresses,
107111
}
108112
a.Unlock()

libnetwork/ipam/allocator_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
)
1111

1212
func getAllocator(subnet *net.IPNet) *Allocator {
13-
a := NewAllocator()
13+
a := NewAllocator(nil)
1414
a.AddSubnet("default", &SubnetInfo{Subnet: subnet})
1515
return a
1616
}
@@ -58,7 +58,7 @@ func TestGetAddressVersion(t *testing.T) {
5858
}
5959

6060
func TestAddSubnets(t *testing.T) {
61-
a := NewAllocator()
61+
a := NewAllocator(nil)
6262

6363
_, sub0, _ := net.ParseCIDR("10.0.0.0/8")
6464
err := a.AddSubnet("default", &SubnetInfo{Subnet: sub0})
@@ -133,7 +133,7 @@ func TestAdjustAndCheckSubnet(t *testing.T) {
133133
}
134134

135135
func TestRemoveSubnet(t *testing.T) {
136-
a := NewAllocator()
136+
a := NewAllocator(nil)
137137

138138
input := []struct {
139139
addrSpace AddressSpace
@@ -247,7 +247,7 @@ func TestGetAddress(t *testing.T) {
247247
}
248248

249249
func TestGetSubnetList(t *testing.T) {
250-
a := NewAllocator()
250+
a := NewAllocator(nil)
251251
input := []struct {
252252
addrSpace AddressSpace
253253
subnet string
@@ -295,7 +295,7 @@ func TestGetSubnetList(t *testing.T) {
295295

296296
func TestRequestSyntaxCheck(t *testing.T) {
297297
var (
298-
a = NewAllocator()
298+
a = NewAllocator(nil)
299299
subnet = "192.168.0.0/16"
300300
addSpace = AddressSpace("green")
301301
)
@@ -462,7 +462,7 @@ func assertGetAddress(t *testing.T, subnet string) {
462462

463463
bm := &bitmask{
464464
subnet: sub,
465-
addressMask: bitseq.NewHandle("default/192.168.0.0/24", uint32(numAddresses)),
465+
addressMask: bitseq.NewHandle("ipam_test", nil, "default/192.168.0.0/24", uint32(numAddresses)),
466466
freeAddresses: numAddresses,
467467
}
468468
numBlocks := bm.addressMask.Head.Count
@@ -513,7 +513,7 @@ func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP str
513513
func benchmarkRequest(subnet *net.IPNet) {
514514
var err error
515515

516-
a := NewAllocator()
516+
a := NewAllocator(nil)
517517
a.internalHostSize = 20
518518
a.AddSubnet("default", &SubnetInfo{Subnet: subnet})
519519

0 commit comments

Comments
 (0)