|
| 1 | +package ipsets |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "strings" |
| 6 | + |
| 7 | + "github.com/Azure/azure-container-networking/npm/metrics" |
| 8 | + "github.com/Azure/azure-container-networking/npm/util" |
| 9 | + "k8s.io/klog" |
| 10 | +) |
| 11 | + |
| 12 | +/* |
| 13 | + dirtyCacheInterface will maintain the dirty cache. |
| 14 | + It may maintain membersToAdd and membersToDelete. |
| 15 | + Members are either IPs, CIDRs, IP-Port pairs, or prefixed set names if the parent is a list. |
| 16 | +
|
| 17 | + Assumptions: |
| 18 | + - if the set becomes dirty via update or destroy, then the set WAS in the kernel before |
| 19 | + - if the set becomes dirty via create, then the set was NOT in the kernel before |
| 20 | +
|
| 21 | + Usage: |
| 22 | + - create, addMember, deleteMember, and destroy are idempotent |
| 23 | + - create should not be called if the set becomes dirty via add/delete or the set is removed from the deleteCache via add/update |
| 24 | + - deleteMember should not be called if the set is in the deleteCache |
| 25 | + - deleteMember is safe to call on members in the kernel and members added via addMember |
| 26 | + - deleteMember is also safe to call on members not in the kernel if the set isn't in the kernel yet (became dirty via create) |
| 27 | +
|
| 28 | + Examples of Expected Behavior: |
| 29 | + - if a set is created and then destroyed, that set will not be in the dirty cache anymore |
| 30 | + - if a set is updated and then destroyed, that set will be in the delete cache |
| 31 | + - if the only operations on a set are adding and removing the same member, the set may still be in the dirty cache, but the member will be untracked |
| 32 | +*/ |
| 33 | +type dirtyCacheInterface interface { |
| 34 | + // reset empties dirty cache |
| 35 | + reset() |
| 36 | + // resetAddOrUpdateCache empties the dirty cache of sets to be created or updated |
| 37 | + resetAddOrUpdateCache() |
| 38 | + // create will mark the new set to be created. |
| 39 | + create(set *IPSet) |
| 40 | + // addMember will mark the set to be updated and track the member to be added (if implemented). |
| 41 | + addMember(set *IPSet, member string) |
| 42 | + // deleteMember will mark the set to be updated and track the member to be deleted (if implemented). |
| 43 | + deleteMember(set *IPSet, member string) |
| 44 | + // delete will mark the set to be deleted in the cache |
| 45 | + destroy(set *IPSet) |
| 46 | + // setsToAddOrUpdate returns the set names to be added or updated |
| 47 | + setsToAddOrUpdate() map[string]struct{} |
| 48 | + // setsToDelete returns the set names to be deleted |
| 49 | + setsToDelete() map[string]struct{} |
| 50 | + // numSetsToAddOrUpdate returns the number of sets to be added or updated |
| 51 | + numSetsToAddOrUpdate() int |
| 52 | + // numSetsToDelete returns the number of sets to be deleted |
| 53 | + numSetsToDelete() int |
| 54 | + // isSetToAddOrUpdate returns true if the set is dirty and should be added or updated |
| 55 | + isSetToAddOrUpdate(setName string) bool |
| 56 | + // isSetToDelete returns true if the set is dirty and should be deleted |
| 57 | + isSetToDelete(setName string) bool |
| 58 | + // printAddOrUpdateCache returns a string representation of the add/update cache |
| 59 | + printAddOrUpdateCache() string |
| 60 | + // printDeleteCache returns a string representation of the delete cache |
| 61 | + printDeleteCache() string |
| 62 | + // memberDiff returns the member diff for the set. |
| 63 | + // Will create a new memberDiff if the setName isn't in the dirty cache. |
| 64 | + memberDiff(setName string) *memberDiff |
| 65 | +} |
| 66 | + |
| 67 | +type dirtyCache struct { |
| 68 | + // all maps have keys of set names and values of members to add/delete |
| 69 | + toCreateCache map[string]*memberDiff |
| 70 | + toUpdateCache map[string]*memberDiff |
| 71 | + toDestroyCache map[string]*memberDiff |
| 72 | +} |
| 73 | + |
| 74 | +func newDirtyCache() *dirtyCache { |
| 75 | + dc := &dirtyCache{} |
| 76 | + dc.reset() |
| 77 | + return dc |
| 78 | +} |
| 79 | + |
| 80 | +func (dc *dirtyCache) reset() { |
| 81 | + dc.toCreateCache = make(map[string]*memberDiff) |
| 82 | + dc.toUpdateCache = make(map[string]*memberDiff) |
| 83 | + dc.toDestroyCache = make(map[string]*memberDiff) |
| 84 | +} |
| 85 | + |
| 86 | +func (dc *dirtyCache) resetAddOrUpdateCache() { |
| 87 | + dc.toCreateCache = make(map[string]*memberDiff) |
| 88 | + dc.toUpdateCache = make(map[string]*memberDiff) |
| 89 | +} |
| 90 | + |
| 91 | +func (dc *dirtyCache) create(set *IPSet) { |
| 92 | + if _, ok := dc.toCreateCache[set.Name]; ok { |
| 93 | + return |
| 94 | + } |
| 95 | + // error checking |
| 96 | + if _, ok := dc.toUpdateCache[set.Name]; ok { |
| 97 | + msg := fmt.Sprintf("create should not be called for set %s since it's in the toUpdateCache", set.Name) |
| 98 | + klog.Warning(msg) |
| 99 | + metrics.SendErrorLogAndMetric(util.IpsmID, msg) |
| 100 | + return |
| 101 | + } |
| 102 | + |
| 103 | + diff, ok := dc.toDestroyCache[set.Name] |
| 104 | + if ok { |
| 105 | + // transfer from toDestroyCache to toUpdateCache and maintain member diff |
| 106 | + dc.toUpdateCache[set.Name] = diff |
| 107 | + delete(dc.toDestroyCache, set.Name) |
| 108 | + } else { |
| 109 | + // put in the toCreateCache |
| 110 | + dc.toCreateCache[set.Name] = diffOnCreate(set) |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +// could optimize Linux to remove from toUpdateCache if there were no member diff afterwards, |
| 115 | +// but leaving as is prevents difference between OS caches |
| 116 | +func (dc *dirtyCache) addMember(set *IPSet, member string) { |
| 117 | + diff, ok := dc.toCreateCache[set.Name] |
| 118 | + if !ok { |
| 119 | + diff, ok = dc.toUpdateCache[set.Name] |
| 120 | + if !ok { |
| 121 | + diff, ok = dc.toDestroyCache[set.Name] |
| 122 | + if !ok { |
| 123 | + diff = newMemberDiff() |
| 124 | + } |
| 125 | + } |
| 126 | + dc.toUpdateCache[set.Name] = diff |
| 127 | + } |
| 128 | + delete(dc.toDestroyCache, set.Name) |
| 129 | + diff.addMember(member) |
| 130 | +} |
| 131 | + |
| 132 | +// could optimize Linux to remove from toUpdateCache if there were no member diff afterwards, |
| 133 | +// but leaving as is prevents difference between OS caches |
| 134 | +func (dc *dirtyCache) deleteMember(set *IPSet, member string) { |
| 135 | + // error checking #1 |
| 136 | + if dc.isSetToDelete(set.Name) { |
| 137 | + msg := fmt.Sprintf("attempting to delete member %s for set %s in the toDestroyCache", member, set.Name) |
| 138 | + klog.Warning(msg) |
| 139 | + metrics.SendErrorLogAndMetric(util.IpsmID, msg) |
| 140 | + return |
| 141 | + } |
| 142 | + if diff, ok := dc.toCreateCache[set.Name]; ok { |
| 143 | + // don't mark a member to be deleted if it never existed in the kernel |
| 144 | + diff.removeMemberFromDiffToAdd(member) |
| 145 | + } else { |
| 146 | + diff, ok := dc.toUpdateCache[set.Name] |
| 147 | + if !ok { |
| 148 | + diff = newMemberDiff() |
| 149 | + } |
| 150 | + dc.toUpdateCache[set.Name] = diff |
| 151 | + diff.deleteMember(member) |
| 152 | + } |
| 153 | +} |
| 154 | + |
| 155 | +func (dc *dirtyCache) destroy(set *IPSet) { |
| 156 | + if dc.isSetToDelete(set.Name) { |
| 157 | + return |
| 158 | + } |
| 159 | + |
| 160 | + if _, ok := dc.toCreateCache[set.Name]; !ok { |
| 161 | + // mark all current members as membersToDelete to accommodate force delete |
| 162 | + diff, ok := dc.toUpdateCache[set.Name] |
| 163 | + if !ok { |
| 164 | + diff = newMemberDiff() |
| 165 | + } |
| 166 | + if set.Kind == HashSet { |
| 167 | + for ip := range set.IPPodKey { |
| 168 | + diff.deleteMember(ip) |
| 169 | + } |
| 170 | + } else { |
| 171 | + for _, memberSet := range set.MemberIPSets { |
| 172 | + diff.deleteMember(memberSet.HashedName) |
| 173 | + } |
| 174 | + } |
| 175 | + // must call this after deleteMember for correct member diff |
| 176 | + diff.resetMembersToAdd() |
| 177 | + |
| 178 | + // put the set/diff in the toDestroyCache |
| 179 | + dc.toDestroyCache[set.Name] = diff |
| 180 | + } |
| 181 | + // remove set from toCreateCache or toUpdateCache if necessary |
| 182 | + // if the set/diff was in the toCreateCache before, we'll forget about it |
| 183 | + delete(dc.toCreateCache, set.Name) |
| 184 | + delete(dc.toUpdateCache, set.Name) |
| 185 | +} |
| 186 | + |
| 187 | +func (dc *dirtyCache) setsToAddOrUpdate() map[string]struct{} { |
| 188 | + sets := make(map[string]struct{}, len(dc.toCreateCache)+len(dc.toUpdateCache)) |
| 189 | + for set := range dc.toCreateCache { |
| 190 | + sets[set] = struct{}{} |
| 191 | + } |
| 192 | + for set := range dc.toUpdateCache { |
| 193 | + sets[set] = struct{}{} |
| 194 | + } |
| 195 | + return sets |
| 196 | +} |
| 197 | + |
| 198 | +func (dc *dirtyCache) setsToDelete() map[string]struct{} { |
| 199 | + sets := make(map[string]struct{}, len(dc.toDestroyCache)) |
| 200 | + for setName := range dc.toDestroyCache { |
| 201 | + sets[setName] = struct{}{} |
| 202 | + } |
| 203 | + return sets |
| 204 | +} |
| 205 | + |
| 206 | +func (dc *dirtyCache) numSetsToAddOrUpdate() int { |
| 207 | + return len(dc.toCreateCache) + len(dc.toUpdateCache) |
| 208 | +} |
| 209 | + |
| 210 | +func (dc *dirtyCache) numSetsToDelete() int { |
| 211 | + return len(dc.toDestroyCache) |
| 212 | +} |
| 213 | + |
| 214 | +func (dc *dirtyCache) isSetToAddOrUpdate(setName string) bool { |
| 215 | + _, ok1 := dc.toCreateCache[setName] |
| 216 | + _, ok2 := dc.toUpdateCache[setName] |
| 217 | + return ok1 || ok2 |
| 218 | +} |
| 219 | + |
| 220 | +func (dc *dirtyCache) isSetToDelete(setName string) bool { |
| 221 | + _, ok := dc.toDestroyCache[setName] |
| 222 | + return ok |
| 223 | +} |
| 224 | + |
| 225 | +func (dc *dirtyCache) printAddOrUpdateCache() string { |
| 226 | + toCreate := make([]string, 0, len(dc.toCreateCache)) |
| 227 | + for setName, diff := range dc.toCreateCache { |
| 228 | + toCreate = append(toCreate, fmt.Sprintf("%s: %+v", setName, diff)) |
| 229 | + } |
| 230 | + toUpdate := make([]string, 0, len(dc.toUpdateCache)) |
| 231 | + for setName, diff := range dc.toUpdateCache { |
| 232 | + toUpdate = append(toUpdate, fmt.Sprintf("%s: %+v", setName, diff)) |
| 233 | + } |
| 234 | + return fmt.Sprintf("to create: [%+v], to update: [%+v]", strings.Join(toCreate, ","), strings.Join(toUpdate, ",")) |
| 235 | +} |
| 236 | + |
| 237 | +func (dc *dirtyCache) printDeleteCache() string { |
| 238 | + return fmt.Sprintf("%+v", dc.toDestroyCache) |
| 239 | +} |
| 240 | + |
| 241 | +func (dc *dirtyCache) memberDiff(setName string) *memberDiff { |
| 242 | + if diff, ok := dc.toCreateCache[setName]; ok { |
| 243 | + return diff |
| 244 | + } |
| 245 | + if diff, ok := dc.toUpdateCache[setName]; ok { |
| 246 | + return diff |
| 247 | + } |
| 248 | + if diff, ok := dc.toDestroyCache[setName]; ok { |
| 249 | + return diff |
| 250 | + } |
| 251 | + return newMemberDiff() |
| 252 | +} |
0 commit comments