mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #675 from mrjana/model
Make sandbox cleanup robust for ungraceful exits
This commit is contained in:
commit
9145f18132
4 changed files with 75 additions and 7 deletions
|
@ -66,6 +66,7 @@ type sandbox struct {
|
||||||
joinLeaveDone chan struct{}
|
joinLeaveDone chan struct{}
|
||||||
dbIndex uint64
|
dbIndex uint64
|
||||||
dbExists bool
|
dbExists bool
|
||||||
|
inDelete bool
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,6 +147,22 @@ func (sb *sandbox) Statistics() (map[string]*types.InterfaceStatistics, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *sandbox) Delete() error {
|
func (sb *sandbox) Delete() error {
|
||||||
|
sb.Lock()
|
||||||
|
if sb.inDelete {
|
||||||
|
sb.Unlock()
|
||||||
|
return types.ForbiddenErrorf("another sandbox delete in progress")
|
||||||
|
}
|
||||||
|
// Set the inDelete flag. This will ensure that we don't
|
||||||
|
// update the store until we have completed all the endpoint
|
||||||
|
// leaves and deletes. And when endpoint leaves and deletes
|
||||||
|
// are completed then we can finally delete the sandbox object
|
||||||
|
// altogether from the data store. If the daemon exits
|
||||||
|
// ungracefully in the middle of a sandbox delete this way we
|
||||||
|
// will have all the references to the endpoints in the
|
||||||
|
// sandbox so that we can clean them up when we restart
|
||||||
|
sb.inDelete = true
|
||||||
|
sb.Unlock()
|
||||||
|
|
||||||
c := sb.controller
|
c := sb.controller
|
||||||
|
|
||||||
// Detach from all endpoints
|
// Detach from all endpoints
|
||||||
|
@ -355,6 +372,10 @@ func releaseOSSboxResources(osSbox osl.Sandbox, ep *endpoint) {
|
||||||
joinInfo := ep.joinInfo
|
joinInfo := ep.joinInfo
|
||||||
ep.Unlock()
|
ep.Unlock()
|
||||||
|
|
||||||
|
if joinInfo == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Remove non-interface routes.
|
// Remove non-interface routes.
|
||||||
for _, r := range joinInfo.StaticRoutes {
|
for _, r := range joinInfo.StaticRoutes {
|
||||||
if err := osSbox.RemoveStaticRoute(r); err != nil {
|
if err := osSbox.RemoveStaticRoute(r); err != nil {
|
||||||
|
@ -386,6 +407,7 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
|
||||||
sb.Unlock()
|
sb.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
inDelete := sb.inDelete
|
||||||
sb.Unlock()
|
sb.Unlock()
|
||||||
|
|
||||||
ep.Lock()
|
ep.Lock()
|
||||||
|
@ -425,7 +447,16 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sb.storeUpdate()
|
|
||||||
|
// Only update the store if we did not come here as part of
|
||||||
|
// sandbox delete. If we came here as part of delete then do
|
||||||
|
// not bother updating the store. The sandbox object will be
|
||||||
|
// deleted anyway
|
||||||
|
if !inDelete {
|
||||||
|
return sb.storeUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *sandbox) clearNetworkResources(origEp *endpoint) error {
|
func (sb *sandbox) clearNetworkResources(origEp *endpoint) error {
|
||||||
|
@ -437,6 +468,7 @@ func (sb *sandbox) clearNetworkResources(origEp *endpoint) error {
|
||||||
|
|
||||||
sb.Lock()
|
sb.Lock()
|
||||||
osSbox := sb.osSbox
|
osSbox := sb.osSbox
|
||||||
|
inDelete := sb.inDelete
|
||||||
sb.Unlock()
|
sb.Unlock()
|
||||||
if osSbox != nil {
|
if osSbox != nil {
|
||||||
releaseOSSboxResources(osSbox, ep)
|
releaseOSSboxResources(osSbox, ep)
|
||||||
|
@ -480,7 +512,15 @@ func (sb *sandbox) clearNetworkResources(origEp *endpoint) error {
|
||||||
sb.updateGateway(gwepAfter)
|
sb.updateGateway(gwepAfter)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.storeUpdate()
|
// Only update the store if we did not come here as part of
|
||||||
|
// sandbox delete. If we came here as part of delete then do
|
||||||
|
// not bother updating the store. The sandbox object will be
|
||||||
|
// deleted anyway
|
||||||
|
if !inDelete {
|
||||||
|
return sb.storeUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -123,6 +123,8 @@ func (sb *sandbox) storeUpdate() error {
|
||||||
ID: sb.id,
|
ID: sb.id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
retry:
|
||||||
|
sbs.Eps = nil
|
||||||
for _, ep := range sb.getConnectedEndpoints() {
|
for _, ep := range sb.getConnectedEndpoints() {
|
||||||
eps := epState{
|
eps := epState{
|
||||||
Nid: ep.getNetwork().ID(),
|
Nid: ep.getNetwork().ID(),
|
||||||
|
@ -132,7 +134,16 @@ func (sb *sandbox) storeUpdate() error {
|
||||||
sbs.Eps = append(sbs.Eps, eps)
|
sbs.Eps = append(sbs.Eps, eps)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.controller.updateToStore(sbs)
|
err := sb.controller.updateToStore(sbs)
|
||||||
|
if err == datastore.ErrKeyModified {
|
||||||
|
// When we get ErrKeyModified it is sufficient to just
|
||||||
|
// go back and retry. No need to get the object from
|
||||||
|
// the store because we always regenerate the store
|
||||||
|
// state from in memory sandbox state
|
||||||
|
goto retry
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *sandbox) storeDelete() error {
|
func (sb *sandbox) storeDelete() error {
|
||||||
|
|
|
@ -64,6 +64,23 @@ function test_single_network_connectivity() {
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "Test default network dnet ungraceful restart" {
|
||||||
|
skip_for_circleci
|
||||||
|
|
||||||
|
echo $(docker ps)
|
||||||
|
|
||||||
|
for iter in `seq 1 2`;
|
||||||
|
do
|
||||||
|
if [ "$iter" -eq 1 ]; then
|
||||||
|
test_single_network_connectivity bridge 3 skip
|
||||||
|
docker restart dnet-1-bridge
|
||||||
|
wait_for_dnet $(inst_id2port 1) dnet-1-bridge
|
||||||
|
else
|
||||||
|
test_single_network_connectivity bridge 3
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
@test "Test bridge network" {
|
@test "Test bridge network" {
|
||||||
skip_for_circleci
|
skip_for_circleci
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ function run_dnet_tests() {
|
||||||
./integration-tmp/bin/bats ./test/integration/dnet/dnet.bats
|
./integration-tmp/bin/bats ./test/integration/dnet/dnet.bats
|
||||||
}
|
}
|
||||||
|
|
||||||
function run_simple_tests() {
|
function run_simple_consul_tests() {
|
||||||
# Test a single node configuration with a global scope test driver
|
# Test a single node configuration with a global scope test driver
|
||||||
## Setup
|
## Setup
|
||||||
start_dnet 1 simple 1>>${INTEGRATION_ROOT}/test.log 2>&1
|
start_dnet 1 simple 1>>${INTEGRATION_ROOT}/test.log 2>&1
|
||||||
|
@ -205,15 +205,15 @@ if [ -z "$SUITES" ]; then
|
||||||
then
|
then
|
||||||
# We can only run a limited list of suites in circleci because of the
|
# We can only run a limited list of suites in circleci because of the
|
||||||
# old kernel and limited docker environment.
|
# old kernel and limited docker environment.
|
||||||
suites="dnet simple multi_consul multi_zk multi_etcd"
|
suites="dnet simple_consul multi_consul multi_zk multi_etcd"
|
||||||
else
|
else
|
||||||
suites="dnet simple multi_consul multi_zk multi_etcd bridge overlay_consul overlay_zk overlay_etcd"
|
suites="dnet simple_consul multi_consul multi_zk multi_etcd bridge overlay_consul overlay_zk overlay_etcd"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
suites="$SUITES"
|
suites="$SUITES"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$suites" =~ .*consul.* ]]; then
|
if [[ ( "$suites" =~ .*consul.* ) || ( "$suites" =~ .*bridge.* ) ]]; then
|
||||||
echo "Starting consul ..."
|
echo "Starting consul ..."
|
||||||
start_consul 1>>${INTEGRATION_ROOT}/test.log 2>&1
|
start_consul 1>>${INTEGRATION_ROOT}/test.log 2>&1
|
||||||
cmap[pr_consul]=pr_consul
|
cmap[pr_consul]=pr_consul
|
||||||
|
|
Loading…
Reference in a new issue