lxd/cluster/membership.go
func Leave(state *state.State, gateway *Gateway, name string, force bool) (string, error) {
	logger.Debugf("Make node %s leave the cluster", name)

	// Check if the node can be deleted and track its address.
	var address string
	err := state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		// Get the node (if it doesn't exists an error is returned).
		node, err := tx.NodeByName(name)
		if err != nil {
			return err
		}

		// Check that the node is eligeable for leaving.
		if !force {
			err := membershipCheckClusterStateForLeave(tx, node.ID)
			if err != nil {
				return err
			}
		}

		address = node.Address
		return nil
	})
	if err != nil {
		return "", err
	}

	// If the node is a database node, leave the raft cluster too.
	var raftNodes []db.RaftNode // Current raft nodes
	raftNodeRemoveIndex := -1   // Index of the raft node to remove, if any.
	err = state.Node.Transaction(func(tx *db.NodeTx) error {
		var err error
		raftNodes, err = tx.RaftNodes()
		if err != nil {
			return errors.Wrap(err, "failed to get current database nodes")
		}
		for i, node := range raftNodes {
			if node.Address == address {
				raftNodeRemoveIndex = i
				break
			}
		}
		return nil
	})
	if err != nil {
		return "", err
	}

	if raftNodeRemoveIndex == -1 {
		// The node was not part of the raft cluster, nothing left to
		// do.
		return address, nil
	}

	id := strconv.Itoa(int(raftNodes[raftNodeRemoveIndex].ID))
	// Get the address of another database node,
	target := raftNodes[(raftNodeRemoveIndex+1)%len(raftNodes)].Address
	logger.Info(
		"Remove node from dqlite raft cluster",
		log15.Ctx{"id": id, "address": address, "target": target})
	dial, err := raftDial(gateway.cert)
	if err != nil {
		return "", err
	}
	err = rafthttp.ChangeMembership(
		raftmembership.LeaveRequest, raftEndpoint, dial,
		raft.ServerID(id), address, target, 5*time.Second)
	if err != nil {
		return "", err
	}
	return address, nil
}
func Leave(state *state.State, gateway *Gateway, name string, force bool) (string, error) {
	logger.Debugf("Make node %s leave the cluster", name)

	// Check if the node can be deleted and track its address.
	var address string
	err := state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		// Get the node (if it doesn't exists an error is returned).
		node, err := tx.NodeByName(name)
		if err != nil {
			return err
		}

		// Check that the node is eligeable for leaving.
		if !force {
			err := membershipCheckClusterStateForLeave(tx, node.ID)
			if err != nil {
				return err
			}
		}

		address = node.Address
		return nil
	})
	if err != nil {
		return "", err
	}

	// If the node is a database node, leave the raft cluster too.
	var raftNodes []db.RaftNode // Current raft nodes
	raftNodeRemoveIndex := -1   // Index of the raft node to remove, if any.
	err = state.Node.Transaction(func(tx *db.NodeTx) error {
		var err error
		raftNodes, err = tx.RaftNodes()
		if err != nil {
			return errors.Wrap(err, "failed to get current database nodes")
		}
		for i, node := range raftNodes {
			if node.Address == address {
				raftNodeRemoveIndex = i
				break
			}
		}
		return nil
	})
	if err != nil {
		return "", err
	}

	if raftNodeRemoveIndex == -1 {
		// The node was not part of the raft cluster, nothing left to
		// do.
		return address, nil
	}

	id := strconv.Itoa(int(raftNodes[raftNodeRemoveIndex].ID))
	// Get the address of another database node,
	target := raftNodes[(raftNodeRemoveIndex+1)%len(raftNodes)].Address
	logger.Info(
		"Remove node from dqlite raft cluster",
		log15.Ctx{"id": id, "address": address, "target": target})
	dial, err := raftDial(gateway.cert)
	if err != nil {
		return "", err
	}
	err = rafthttp.ChangeMembership(
		raftmembership.LeaveRequest, raftEndpoint, dial,
		raft.ServerID(id), address, target, 5*time.Second)
	if err != nil {
		return "", err
	}
	return address, nil
}
// Leave a cluster.
//
// If the force flag is true, the node will leave even if it still has
// containers and images.
//
// The node will only leave the raft cluster, and won't be removed from the
// database. That's done by Purge().
//
// Upon success, return the address of the leaving node.