Skip to content

Commit 380ded6

Browse files
committed
Store an endpoint count for networks, for downgrade
Since commit 51d7f95 ("libnet: remove struct endpointCnt") an endpoint count for networks has not been persisted. But, on downgrade to a version older than that commit, the missing field caused daemon startup to fail. So, create the count in the store - it only needs to exist, it's no longer maintained as a count of endpoints. On downgrade, the count is probably zero anyway (the daemon is stopped), but the older daemon fixes it up on startup if necessary. Signed-off-by: Rob Murray <[email protected]>
1 parent b2d06ba commit 380ded6

3 files changed

Lines changed: 138 additions & 0 deletions

File tree

libnetwork/controller.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,28 @@ func (c *Controller) NewNetwork(ctx context.Context, networkType, name string, i
688688
}
689689

690690
addToStore:
691+
// First store the endpoint count, then the network. To avoid to
692+
// end up with a datastore containing a network and not an epCnt,
693+
// in case of an ungraceful shutdown during this function call.
694+
//
695+
// TODO(robmry) - remove this once downgrade past 28.1.0 is no longer supported.
696+
// The endpoint count is no longer used, it's created in the store to make
697+
// downgrade work, versions older than 28.1.0 expect to read it and error if they
698+
// can't. The stored count is not maintained, so the downgraded version will
699+
// always find it's zero (which is usually correct because the daemon had
700+
// stopped), but older daemons fix it on startup anyway.
701+
epCnt := &endpointCnt{n: nw}
702+
if err := c.updateToStore(ctx, epCnt); err != nil {
703+
return nil, err
704+
}
705+
defer func() {
706+
if retErr != nil {
707+
if err := c.deleteFromStore(epCnt); err != nil {
708+
log.G(ctx).Warnf("could not rollback from store, epCnt %v on failure (%v): %v", epCnt, retErr, err)
709+
}
710+
}
711+
}()
712+
691713
if err := c.storeNetwork(ctx, nw); err != nil {
692714
return nil, err
693715
}

libnetwork/endpoint_cnt.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package libnetwork
2+
3+
import (
4+
"encoding/json"
5+
"sync"
6+
7+
"github.com/docker/docker/libnetwork/datastore"
8+
)
9+
10+
// endpointCnt was used to refcount network-endpoint relationships. It's
11+
// unused since v28.1, and kept around only to ensure that users can properly
12+
// downgrade.
13+
//
14+
// TODO(aker): remove this struct in v30.
15+
type endpointCnt struct {
16+
n *Network
17+
Count uint64
18+
dbIndex uint64
19+
dbExists bool
20+
sync.Mutex
21+
}
22+
23+
const epCntKeyPrefix = "endpoint_count"
24+
25+
func (ec *endpointCnt) Key() []string {
26+
ec.Lock()
27+
defer ec.Unlock()
28+
29+
return []string{epCntKeyPrefix, ec.n.id}
30+
}
31+
32+
func (ec *endpointCnt) KeyPrefix() []string {
33+
ec.Lock()
34+
defer ec.Unlock()
35+
36+
return []string{epCntKeyPrefix, ec.n.id}
37+
}
38+
39+
func (ec *endpointCnt) Value() []byte {
40+
ec.Lock()
41+
defer ec.Unlock()
42+
43+
b, err := json.Marshal(ec)
44+
if err != nil {
45+
return nil
46+
}
47+
return b
48+
}
49+
50+
func (ec *endpointCnt) SetValue(value []byte) error {
51+
ec.Lock()
52+
defer ec.Unlock()
53+
54+
return json.Unmarshal(value, &ec)
55+
}
56+
57+
func (ec *endpointCnt) Index() uint64 {
58+
ec.Lock()
59+
defer ec.Unlock()
60+
return ec.dbIndex
61+
}
62+
63+
func (ec *endpointCnt) SetIndex(index uint64) {
64+
ec.Lock()
65+
ec.dbIndex = index
66+
ec.dbExists = true
67+
ec.Unlock()
68+
}
69+
70+
func (ec *endpointCnt) Exists() bool {
71+
ec.Lock()
72+
defer ec.Unlock()
73+
return ec.dbExists
74+
}
75+
76+
func (ec *endpointCnt) Skip() bool {
77+
ec.Lock()
78+
defer ec.Unlock()
79+
return !ec.n.persist
80+
}
81+
82+
func (ec *endpointCnt) New() datastore.KVObject {
83+
ec.Lock()
84+
defer ec.Unlock()
85+
86+
return &endpointCnt{
87+
n: ec.n,
88+
}
89+
}
90+
91+
func (ec *endpointCnt) CopyTo(o datastore.KVObject) error {
92+
ec.Lock()
93+
defer ec.Unlock()
94+
95+
dstEc := o.(*endpointCnt)
96+
dstEc.n = ec.n
97+
dstEc.Count = ec.Count
98+
dstEc.dbExists = ec.dbExists
99+
dstEc.dbIndex = ec.dbIndex
100+
101+
return nil
102+
}

libnetwork/network.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,20 @@ func (n *Network) delete(force bool, rmLBEndpoint bool) error {
11131113
}
11141114

11151115
removeFromStore:
1116+
// deleteFromStore performs an atomic delete operation and the
1117+
// Network.epCnt will help prevent any possible
1118+
// race between endpoint join and network delete
1119+
//
1120+
// TODO(robmry) - remove this once downgrade past 28.1.0 is no longer supported.
1121+
// The endpoint count is no longer used, it's created in the store to make
1122+
// downgrade work, versions older than 28.1.0 expect to read it and error if they
1123+
// can't. The stored count is not maintained, so the downgraded version will
1124+
// always find it's zero (which is usually correct because the daemon had
1125+
// stopped), but older daemons fix it on startup anyway.
1126+
if err = c.deleteFromStore(&endpointCnt{n: n}); err != nil {
1127+
log.G(context.TODO()).Debugf("Error deleting endpoint count from store for stale network %s (%s) for deletion: %v", n.Name(), n.ID(), err)
1128+
}
1129+
11161130
if err = c.deleteStoredNetwork(n); err != nil {
11171131
return fmt.Errorf("error deleting network from store: %v", err)
11181132
}

0 commit comments

Comments
 (0)