in npm/pkg/controlplane/controllers/v1/networkPolicyController.go [325:422]
func (c *NetworkPolicyController) syncAddAndUpdateNetPol(netPolObj *networkingv1.NetworkPolicy) (metrics.OperationKind, error) {
var err error
netpolKey, err := cache.MetaNamespaceKeyFunc(netPolObj)
if err != nil {
// consider a no-op since we can't determine add vs. update. The exec time here isn't important either.
return metrics.NoOp, fmt.Errorf("[syncAddAndUpdateNetPol] Error: while running MetaNamespaceKeyFunc err: %w", err)
}
// Start reconciling loop to eventually meet cached states against the desired states from network policy.
// #1 If a new network policy is created, the network policy is not in RawNPMap.
// start translating policy and install translated ipset and iptables rules into kernel
// #2 If a network policy with <ns>-<netpol namespace>-<netpol name> is applied before and two network policy are the same object (same UID),
// first delete the applied network policy, then start translating policy and install translated ipset and iptables rules into kernel
// #3 If a network policy with <ns>-<netpol namespace>-<netpol name> is applied before and two network policy are the different object (different UID) due to missing some events for the old object
// first delete the applied network policy, then start translating policy and install translated ipset and iptables rules into kernel
// To deal with all three cases, we first delete network policy if possible, then install translated rules into kernel.
// (TODO): can optimize logic more to reduce computations. For example, apply only difference if possible like podController
// Do not need to clean up default Azure NPM chain in deleteNetworkPolicy function, if network policy object is applied soon.
// So, avoid extra overhead to install default Azure NPM chain in initializeDefaultAzureNpmChain function.
// To achieve it, use flag unSafeToCleanUpAzureNpmChain to indicate that the default Azure NPM chain cannot be deleted.
// delete existing network policy
var operationKind metrics.OperationKind
if _, ok := c.rawNpMap[netpolKey]; ok {
operationKind = metrics.UpdateOp
} else {
operationKind = metrics.CreateOp
}
err = c.cleanUpNetworkPolicy(netpolKey, unSafeToCleanUpAzureNpmChain)
if err != nil {
return operationKind, fmt.Errorf("[syncAddAndUpdateNetPol] Error: failed to deleteNetworkPolicy due to %w", err)
}
// Install this default rules for kube-system and azure-npm chains if they are not initilized.
// Execute initializeDefaultAzureNpmChain function first before actually starting processing network policy object.
if err = c.initializeDefaultAzureNpmChain(); err != nil {
return operationKind, fmt.Errorf("[syncNetPol] Error: due to %w", err)
}
// Cache network object first before applying ipsets and iptables.
// If error happens while applying ipsets and iptables,
// the key is re-queued in workqueue and process this function again, which eventually meets desired states of network policy
c.rawNpMap[netpolKey] = netPolObj
metrics.IncNumPolicies()
c.ipsMgr.Lock()
defer c.ipsMgr.Unlock()
sets, namedPorts, lists, ingressIPCidrs, egressIPCidrs, iptEntries := translatePolicy(netPolObj)
for _, set := range sets {
klog.Infof("Creating set: %v, hashedSet: %v", set, util.GetHashedName(set))
if err = c.ipsMgr.CreateSetNoLock(set, []string{util.IpsetNetHashFlag}); err != nil {
return operationKind, fmt.Errorf("[syncAddAndUpdateNetPol] Error: creating ipset %s with err: %w", set, err)
}
}
for _, set := range namedPorts {
klog.Infof("Creating set: %v, hashedSet: %v", set, util.GetHashedName(set))
if err = c.ipsMgr.CreateSetNoLock(set, []string{util.IpsetIPPortHashFlag}); err != nil {
return operationKind, fmt.Errorf("[syncAddAndUpdateNetPol] Error: creating ipset named port %s with err: %w", set, err)
}
}
// lists is a map with list name and members as value
// NPM will create the list first and increments the refer count
for listKey := range lists {
if err = c.ipsMgr.CreateListNoLock(listKey); err != nil {
return operationKind, fmt.Errorf("[syncAddAndUpdateNetPol] Error: creating ipset list %s with err: %w", listKey, err)
}
c.ipsMgr.IpSetReferIncOrDec(listKey, util.IpsetSetListFlag, ipsm.IncrementOp)
}
// Then NPM will add members to the above list, this is to avoid members being added
// to lists before they are created.
for listKey, listLabelsMembers := range lists {
for _, listMember := range listLabelsMembers {
if err = c.ipsMgr.AddToListNoLock(listKey, listMember); err != nil {
return operationKind, fmt.Errorf("[syncAddAndUpdateNetPol] Error: Adding ipset member %s to ipset list %s with err: %w", listMember, listKey, err)
}
}
c.ipsMgr.IpSetReferIncOrDec(listKey, util.IpsetSetListFlag, ipsm.IncrementOp)
}
if err = c.createCidrsRule("in", netPolObj.Name, netPolObj.Namespace, ingressIPCidrs); err != nil {
return operationKind, fmt.Errorf("[syncAddAndUpdateNetPol] Error: createCidrsRule in due to %w", err)
}
if err = c.createCidrsRule("out", netPolObj.Name, netPolObj.Namespace, egressIPCidrs); err != nil {
return operationKind, fmt.Errorf("[syncAddAndUpdateNetPol] Error: createCidrsRule out due to %w", err)
}
for _, iptEntry := range iptEntries {
if err = c.iptMgr.Add(iptEntry); err != nil {
return operationKind, fmt.Errorf("[syncAddAndUpdateNetPol] Error: failed to apply iptables rule. Rule: %+v with err: %w", iptEntry, err)
}
}
return operationKind, nil
}