dotcloud/docker

View on GitHub
libnetwork/networkdb/nodemgmt.go

Summary

Maintainability
A
3 hrs
Test Coverage
package networkdb

import (
    "context"
    "fmt"

    "github.com/containerd/log"
    "github.com/hashicorp/memberlist"
)

type nodeState int

const (
    nodeNotFound    nodeState = -1
    nodeActiveState nodeState = 0
    nodeLeftState   nodeState = 1
    nodeFailedState nodeState = 2
)

var nodeStateName = map[nodeState]string{
    -1: "NodeNotFound",
    0:  "NodeActive",
    1:  "NodeLeft",
    2:  "NodeFailed",
}

// findNode search the node into the 3 node lists and returns the node pointer and the list
// where it got found
func (nDB *NetworkDB) findNode(nodeName string) (*node, nodeState, map[string]*node) {
    for i, nodes := range []map[string]*node{
        nDB.nodes,
        nDB.leftNodes,
        nDB.failedNodes,
    } {
        if n, ok := nodes[nodeName]; ok {
            return n, nodeState(i), nodes
        }
    }
    return nil, nodeNotFound, nil
}

// changeNodeState changes the state of the node specified, returns true if the node was moved,
// false if there was no need to change the node state. Error will be returned if the node does not
// exists
func (nDB *NetworkDB) changeNodeState(nodeName string, newState nodeState) (bool, error) {
    n, currState, m := nDB.findNode(nodeName)
    if n == nil {
        return false, fmt.Errorf("node %s not found", nodeName)
    }

    switch newState {
    case nodeActiveState:
        if currState == nodeActiveState {
            return false, nil
        }

        delete(m, nodeName)
        // reset the node reap time
        n.reapTime = 0
        nDB.nodes[nodeName] = n
    case nodeLeftState:
        if currState == nodeLeftState {
            return false, nil
        }

        delete(m, nodeName)
        nDB.leftNodes[nodeName] = n
    case nodeFailedState:
        if currState == nodeFailedState {
            return false, nil
        }

        delete(m, nodeName)
        nDB.failedNodes[nodeName] = n
    }

    log.G(context.TODO()).Infof("Node %s change state %s --> %s", nodeName, nodeStateName[currState], nodeStateName[newState])

    if newState == nodeLeftState || newState == nodeFailedState {
        // set the node reap time, if not already set
        // It is possible that a node passes from failed to left and the reaptime was already set so keep that value
        if n.reapTime == 0 {
            n.reapTime = nodeReapInterval
        }
        // The node leave or fails, delete all the entries created by it.
        // If the node was temporary down, deleting the entries will guarantee that the CREATE events will be accepted
        // If the node instead left because was going down, then it makes sense to just delete all its state
        nDB.deleteNodeFromNetworks(n.Name)
        nDB.deleteNodeTableEntries(n.Name)
    }

    return true, nil
}

func (nDB *NetworkDB) purgeReincarnation(mn *memberlist.Node) bool {
    for name, node := range nDB.nodes {
        if node.Addr.Equal(mn.Addr) && node.Port == mn.Port && mn.Name != name {
            log.G(context.TODO()).Infof("Node %s/%s, is the new incarnation of the active node %s/%s", mn.Name, mn.Addr, name, node.Addr)
            nDB.changeNodeState(name, nodeLeftState)
            return true
        }
    }

    for name, node := range nDB.failedNodes {
        if node.Addr.Equal(mn.Addr) && node.Port == mn.Port && mn.Name != name {
            log.G(context.TODO()).Infof("Node %s/%s, is the new incarnation of the failed node %s/%s", mn.Name, mn.Addr, name, node.Addr)
            nDB.changeNodeState(name, nodeLeftState)
            return true
        }
    }

    for name, node := range nDB.leftNodes {
        if node.Addr.Equal(mn.Addr) && node.Port == mn.Port && mn.Name != name {
            log.G(context.TODO()).Infof("Node %s/%s, is the new incarnation of the shutdown node %s/%s", mn.Name, mn.Addr, name, node.Addr)
            nDB.changeNodeState(name, nodeLeftState)
            return true
        }
    }

    return false
}