k8s源码设计模式之Iterator

迭代器模式也叫Iterator

迭代器模式是一种行为设计模式, 让你能在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// plugins/ipam/host-local/backend/allocator/allocator.go
// 对RangeIter实现Next()方法以实现对RangeSet的遍历
type RangeIter struct {
rangeset *RangeSet

// The current range id
// 记录当前访问的索引
rangeIdx int

// Our current position
// 当前的ip
cur net.IP

// The IP where we started iterating; if we hit this again, we're done.
// 开始访问的IP,如果再次访问表示已迭代完成
startIP net.IP
}

func (i *RangeIter) Next() (*net.IPNet, net.IP) {
// 获取索引为rangeIdx的元素(Range)
r := (*i.rangeset)[i.rangeIdx]

// 第一次开始迭代
if i.cur == nil {
i.cur = r.RangeStart
i.startIP = i.cur
if i.cur.Equal(r.Gateway) {
return i.Next()
}
return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway
}

// If we've reached the end of this range, we need to advance the range
// RangeEnd is inclusive as well
if i.cur.Equal(r.RangeEnd) {
i.rangeIdx++
i.rangeIdx %= len(*i.rangeset)
r = (*i.rangeset)[i.rangeIdx]

i.cur = r.RangeStart
} else {
i.cur = ip.NextIP(i.cur)
}

if i.startIP == nil {
i.startIP = i.cur
} else if i.cur.Equal(i.startIP) {
// IF we've looped back to where we started, give up
return nil, nil
}

if i.cur.Equal(r.Gateway) {
return i.Next()
}

return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway
}

func (a *IPAllocator) GetIter() (*RangeIter, error) {
iter := RangeIter{
rangeset: a.rangeset,
}

// Round-robin by trying to allocate from the last reserved IP + 1
startFromLastReservedIP := false

// We might get a last reserved IP that is wrong if the range indexes changed.
// This is not critical, we just lose round-robin this one time.
lastReservedIP, err := a.store.LastReservedIP(a.rangeID)
if err != nil && !os.IsNotExist(err) {
log.Printf("Error retrieving last reserved ip: %v", err)
} else if lastReservedIP != nil {
startFromLastReservedIP = a.rangeset.Contains(lastReservedIP)
}

// Find the range in the set with this IP
if startFromLastReservedIP {
for i, r := range *a.rangeset {
if r.Contains(lastReservedIP) {
iter.rangeIdx = i

// We advance the cursor on every Next(), so the first call
// to next() will return lastReservedIP + 1
iter.cur = lastReservedIP
break
}
}
} else {
iter.rangeIdx = 0
iter.startIP = (*a.rangeset)[0].RangeStart
}
return &iter, nil
}

// 调用Iter
func (a *IPAllocator) Get(id string, ifname string, requestedIP net.IP) (*current.IPConfig, error) {
...
iter, err := a.GetIter()
for {
reservedIP, gw := iter.Next()
...
}
...
}

// plugins/ipam/host-local/backend/allocator/config.go
type RangeSet []Range

type Range struct {
RangeStart net.IP `json:"rangeStart,omitempty"` // The first ip, inclusive
RangeEnd net.IP `json:"rangeEnd,omitempty"` // The last ip, inclusive
Subnet types.IPNet `json:"subnet"`
Gateway net.IP `json:"gateway,omitempty"`
}

REF:
1.plugins/ipam/host-local/backend/allocator/config.go
2.plugins/ipam/host-local/backend/allocator/allocator.go