npm/pkg/dataplane/ipsets/dirtycache.go (177 lines of code) (raw):

package ipsets import ( "fmt" "strings" "github.com/Azure/azure-container-networking/npm/metrics" "github.com/Azure/azure-container-networking/npm/util" "k8s.io/klog" ) /* dirtyCacheInterface will maintain the dirty cache. It may maintain membersToAdd and membersToDelete. Members are either IPs, CIDRs, IP-Port pairs, or prefixed set names if the parent is a list. Assumptions: - if the set becomes dirty via update or destroy, then the set WAS in the kernel before - if the set becomes dirty via create, then the set was NOT in the kernel before Usage: - create, addMember, deleteMember, and destroy are idempotent - create should not be called if the set becomes dirty via add/delete or the set is removed from the deleteCache via add/update - deleteMember should not be called if the set is in the deleteCache - deleteMember is safe to call on members in the kernel and members added via addMember - 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) Examples of Expected Behavior: - if a set is created and then destroyed, that set will not be in the dirty cache anymore - if a set is updated and then destroyed, that set will be in the delete cache - 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 */ type dirtyCacheInterface interface { // reset empties dirty cache reset() // resetAddOrUpdateCache empties the dirty cache of sets to be created or updated resetAddOrUpdateCache() // create will mark the new set to be created. create(set *IPSet) // addMember will mark the set to be updated and track the member to be added (if implemented). addMember(set *IPSet, member string) // deleteMember will mark the set to be updated and track the member to be deleted (if implemented). deleteMember(set *IPSet, member string) // delete will mark the set to be deleted in the cache destroy(set *IPSet) // setsToAddOrUpdate returns the set names to be added or updated setsToAddOrUpdate() map[string]struct{} // setsToDelete returns the set names to be deleted setsToDelete() map[string]struct{} // numSetsToAddOrUpdate returns the number of sets to be added or updated numSetsToAddOrUpdate() int // numSetsToDelete returns the number of sets to be deleted numSetsToDelete() int // isSetToAddOrUpdate returns true if the set is dirty and should be added or updated isSetToAddOrUpdate(setName string) bool // isSetToDelete returns true if the set is dirty and should be deleted isSetToDelete(setName string) bool // printAddOrUpdateCache returns a string representation of the add/update cache printAddOrUpdateCache() string // printDeleteCache returns a string representation of the delete cache printDeleteCache() string // memberDiff returns the member diff for the set. // Will create a new memberDiff if the setName isn't in the dirty cache. memberDiff(setName string) *memberDiff } type dirtyCache struct { // all maps have keys of set names and values of members to add/delete toCreateCache map[string]*memberDiff toUpdateCache map[string]*memberDiff toDestroyCache map[string]*memberDiff } func newDirtyCache() *dirtyCache { dc := &dirtyCache{} dc.reset() return dc } func (dc *dirtyCache) reset() { dc.toCreateCache = make(map[string]*memberDiff) dc.toUpdateCache = make(map[string]*memberDiff) dc.toDestroyCache = make(map[string]*memberDiff) } func (dc *dirtyCache) resetAddOrUpdateCache() { dc.toCreateCache = make(map[string]*memberDiff) dc.toUpdateCache = make(map[string]*memberDiff) } func (dc *dirtyCache) create(set *IPSet) { if _, ok := dc.toCreateCache[set.Name]; ok { return } // error checking if _, ok := dc.toUpdateCache[set.Name]; ok { msg := fmt.Sprintf("create should not be called for set %s since it's in the toUpdateCache", set.Name) klog.Warning(msg) metrics.SendErrorLogAndMetric(util.IpsmID, msg) return } diff, ok := dc.toDestroyCache[set.Name] if ok { // transfer from toDestroyCache to toUpdateCache and maintain member diff dc.toUpdateCache[set.Name] = diff delete(dc.toDestroyCache, set.Name) } else { // put in the toCreateCache dc.toCreateCache[set.Name] = diffOnCreate(set) } } // could optimize Linux to remove from toUpdateCache if there were no member diff afterwards, // but leaving as is prevents difference between OS caches func (dc *dirtyCache) addMember(set *IPSet, member string) { diff, ok := dc.toCreateCache[set.Name] if !ok { diff, ok = dc.toUpdateCache[set.Name] if !ok { diff, ok = dc.toDestroyCache[set.Name] if !ok { diff = newMemberDiff() } } dc.toUpdateCache[set.Name] = diff } delete(dc.toDestroyCache, set.Name) diff.addMember(member) } // could optimize Linux to remove from toUpdateCache if there were no member diff afterwards, // but leaving as is prevents difference between OS caches func (dc *dirtyCache) deleteMember(set *IPSet, member string) { // error checking #1 if dc.isSetToDelete(set.Name) { msg := fmt.Sprintf("attempting to delete member %s for set %s in the toDestroyCache", member, set.Name) klog.Warning(msg) metrics.SendErrorLogAndMetric(util.IpsmID, msg) return } if diff, ok := dc.toCreateCache[set.Name]; ok { // don't mark a member to be deleted if it never existed in the kernel diff.removeMemberFromDiffToAdd(member) } else { diff, ok := dc.toUpdateCache[set.Name] if !ok { diff = newMemberDiff() } dc.toUpdateCache[set.Name] = diff diff.deleteMember(member) } } func (dc *dirtyCache) destroy(set *IPSet) { if dc.isSetToDelete(set.Name) { return } if _, ok := dc.toCreateCache[set.Name]; !ok { // mark all current members as membersToDelete to accommodate force delete diff, ok := dc.toUpdateCache[set.Name] if !ok { diff = newMemberDiff() } if set.Kind == HashSet { for ip := range set.IPPodKey { diff.deleteMember(ip) } } else { for _, memberSet := range set.MemberIPSets { diff.deleteMember(memberSet.HashedName) } } // must call this after deleteMember for correct member diff diff.resetMembersToAdd() // put the set/diff in the toDestroyCache dc.toDestroyCache[set.Name] = diff } // remove set from toCreateCache or toUpdateCache if necessary // if the set/diff was in the toCreateCache before, we'll forget about it delete(dc.toCreateCache, set.Name) delete(dc.toUpdateCache, set.Name) } func (dc *dirtyCache) setsToAddOrUpdate() map[string]struct{} { sets := make(map[string]struct{}, len(dc.toCreateCache)+len(dc.toUpdateCache)) for set := range dc.toCreateCache { sets[set] = struct{}{} } for set := range dc.toUpdateCache { sets[set] = struct{}{} } return sets } func (dc *dirtyCache) setsToDelete() map[string]struct{} { sets := make(map[string]struct{}, len(dc.toDestroyCache)) for setName := range dc.toDestroyCache { sets[setName] = struct{}{} } return sets } func (dc *dirtyCache) numSetsToAddOrUpdate() int { return len(dc.toCreateCache) + len(dc.toUpdateCache) } func (dc *dirtyCache) numSetsToDelete() int { return len(dc.toDestroyCache) } func (dc *dirtyCache) isSetToAddOrUpdate(setName string) bool { _, ok1 := dc.toCreateCache[setName] _, ok2 := dc.toUpdateCache[setName] return ok1 || ok2 } func (dc *dirtyCache) isSetToDelete(setName string) bool { _, ok := dc.toDestroyCache[setName] return ok } func (dc *dirtyCache) printAddOrUpdateCache() string { toCreate := make([]string, 0, len(dc.toCreateCache)) for setName, diff := range dc.toCreateCache { toCreate = append(toCreate, fmt.Sprintf("%s: %+v", setName, diff)) } toUpdate := make([]string, 0, len(dc.toUpdateCache)) for setName, diff := range dc.toUpdateCache { toUpdate = append(toUpdate, fmt.Sprintf("%s: %+v", setName, diff)) } return fmt.Sprintf("to create: [%+v], to update: [%+v]", strings.Join(toCreate, ","), strings.Join(toUpdate, ",")) } func (dc *dirtyCache) printDeleteCache() string { return fmt.Sprintf("%+v", dc.toDestroyCache) } func (dc *dirtyCache) memberDiff(setName string) *memberDiff { if diff, ok := dc.toCreateCache[setName]; ok { return diff } if diff, ok := dc.toUpdateCache[setName]; ok { return diff } if diff, ok := dc.toDestroyCache[setName]; ok { return diff } return newMemberDiff() }