node_tree.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /*
  2. Copyright 2018 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cache
  14. import (
  15. "fmt"
  16. "sync"
  17. "k8s.io/api/core/v1"
  18. utilnode "k8s.io/kubernetes/pkg/util/node"
  19. "k8s.io/klog"
  20. )
  21. // NodeTree is a tree-like data structure that holds node names in each zone. Zone names are
  22. // keys to "NodeTree.tree" and values of "NodeTree.tree" are arrays of node names.
  23. type NodeTree struct {
  24. tree map[string]*nodeArray // a map from zone (region-zone) to an array of nodes in the zone.
  25. zones []string // a list of all the zones in the tree (keys)
  26. zoneIndex int
  27. numNodes int
  28. mu sync.RWMutex
  29. }
  30. // nodeArray is a struct that has nodes that are in a zone.
  31. // We use a slice (as opposed to a set/map) to store the nodes because iterating over the nodes is
  32. // a lot more frequent than searching them by name.
  33. type nodeArray struct {
  34. nodes []string
  35. lastIndex int
  36. }
  37. func (na *nodeArray) next() (nodeName string, exhausted bool) {
  38. if len(na.nodes) == 0 {
  39. klog.Error("The nodeArray is empty. It should have been deleted from NodeTree.")
  40. return "", false
  41. }
  42. if na.lastIndex >= len(na.nodes) {
  43. return "", true
  44. }
  45. nodeName = na.nodes[na.lastIndex]
  46. na.lastIndex++
  47. return nodeName, false
  48. }
  49. // newNodeTree creates a NodeTree from nodes.
  50. func newNodeTree(nodes []*v1.Node) *NodeTree {
  51. nt := &NodeTree{
  52. tree: make(map[string]*nodeArray),
  53. }
  54. for _, n := range nodes {
  55. nt.AddNode(n)
  56. }
  57. return nt
  58. }
  59. // AddNode adds a node and its corresponding zone to the tree. If the zone already exists, the node
  60. // is added to the array of nodes in that zone.
  61. func (nt *NodeTree) AddNode(n *v1.Node) {
  62. nt.mu.Lock()
  63. defer nt.mu.Unlock()
  64. nt.addNode(n)
  65. }
  66. func (nt *NodeTree) addNode(n *v1.Node) {
  67. zone := utilnode.GetZoneKey(n)
  68. if na, ok := nt.tree[zone]; ok {
  69. for _, nodeName := range na.nodes {
  70. if nodeName == n.Name {
  71. klog.Warningf("node %v already exist in the NodeTree", n.Name)
  72. return
  73. }
  74. }
  75. na.nodes = append(na.nodes, n.Name)
  76. } else {
  77. nt.zones = append(nt.zones, zone)
  78. nt.tree[zone] = &nodeArray{nodes: []string{n.Name}, lastIndex: 0}
  79. }
  80. klog.V(5).Infof("Added node %v in group %v to NodeTree", n.Name, zone)
  81. nt.numNodes++
  82. }
  83. // RemoveNode removes a node from the NodeTree.
  84. func (nt *NodeTree) RemoveNode(n *v1.Node) error {
  85. nt.mu.Lock()
  86. defer nt.mu.Unlock()
  87. return nt.removeNode(n)
  88. }
  89. func (nt *NodeTree) removeNode(n *v1.Node) error {
  90. zone := utilnode.GetZoneKey(n)
  91. if na, ok := nt.tree[zone]; ok {
  92. for i, nodeName := range na.nodes {
  93. if nodeName == n.Name {
  94. na.nodes = append(na.nodes[:i], na.nodes[i+1:]...)
  95. if len(na.nodes) == 0 {
  96. nt.removeZone(zone)
  97. }
  98. klog.V(5).Infof("Removed node %v in group %v from NodeTree", n.Name, zone)
  99. nt.numNodes--
  100. return nil
  101. }
  102. }
  103. }
  104. klog.Errorf("Node %v in group %v was not found", n.Name, zone)
  105. return fmt.Errorf("node %v in group %v was not found", n.Name, zone)
  106. }
  107. // removeZone removes a zone from tree.
  108. // This function must be called while writer locks are hold.
  109. func (nt *NodeTree) removeZone(zone string) {
  110. delete(nt.tree, zone)
  111. for i, z := range nt.zones {
  112. if z == zone {
  113. nt.zones = append(nt.zones[:i], nt.zones[i+1:]...)
  114. return
  115. }
  116. }
  117. }
  118. // UpdateNode updates a node in the NodeTree.
  119. func (nt *NodeTree) UpdateNode(old, new *v1.Node) {
  120. var oldZone string
  121. if old != nil {
  122. oldZone = utilnode.GetZoneKey(old)
  123. }
  124. newZone := utilnode.GetZoneKey(new)
  125. // If the zone ID of the node has not changed, we don't need to do anything. Name of the node
  126. // cannot be changed in an update.
  127. if oldZone == newZone {
  128. return
  129. }
  130. nt.mu.Lock()
  131. defer nt.mu.Unlock()
  132. nt.removeNode(old) // No error checking. We ignore whether the old node exists or not.
  133. nt.addNode(new)
  134. }
  135. func (nt *NodeTree) resetExhausted() {
  136. for _, na := range nt.tree {
  137. na.lastIndex = 0
  138. }
  139. nt.zoneIndex = 0
  140. }
  141. // Next returns the name of the next node. NodeTree iterates over zones and in each zone iterates
  142. // over nodes in a round robin fashion.
  143. func (nt *NodeTree) Next() string {
  144. nt.mu.Lock()
  145. defer nt.mu.Unlock()
  146. if len(nt.zones) == 0 {
  147. return ""
  148. }
  149. numExhaustedZones := 0
  150. for {
  151. if nt.zoneIndex >= len(nt.zones) {
  152. nt.zoneIndex = 0
  153. }
  154. zone := nt.zones[nt.zoneIndex]
  155. nt.zoneIndex++
  156. // We do not check the exhausted zones before calling next() on the zone. This ensures
  157. // that if more nodes are added to a zone after it is exhausted, we iterate over the new nodes.
  158. nodeName, exhausted := nt.tree[zone].next()
  159. if exhausted {
  160. numExhaustedZones++
  161. if numExhaustedZones >= len(nt.zones) { // all zones are exhausted. we should reset.
  162. nt.resetExhausted()
  163. }
  164. } else {
  165. return nodeName
  166. }
  167. }
  168. }
  169. // NumNodes returns the number of nodes.
  170. func (nt *NodeTree) NumNodes() int {
  171. nt.mu.RLock()
  172. defer nt.mu.RUnlock()
  173. return nt.numNodes
  174. }