Repository: cilium/cilium
Pull request #16605 (2021-06-21 14:19:29)

.github/workflows: install ginkgo for test suite build test
The test suite build check for the individual commits of a PR currently
fails due to missing ginkgo binary. Install ginkgo v1.12.1 (as per
go.mod).

Fixes: e260ba9a08cf (".github/workflows: verify that each commit builds for test suite changes")

tklauser: See e.g. https://github.com/cilium/cilium/pull/16495/checks?check_run_id=2873663278 for a recent failure.

Patch:
diff --git a/.github/workflows/lint-build-commits.yaml b/.github/workflows/lint-build-commits.yaml
index 078bb2b761..988a010dc9 100644
--- a/.github/workflows/lint-build-commits.yaml
+++ b/.github/workflows/lint-build-commits.yaml
@@ -41,6 +41,10 @@ jobs:
         run: |
           sudo apt-get install libelf-dev
 
+      - name: Install ginkgo
+        run: |
+          go get github.com/onsi/ginkgo/ginkgo@v1.12.1
+
       - name: Checkout code
         uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
         with:
diff --git a/Documentation/operations/upgrade.rst b/Documentation/operations/upgrade.rst
index 2de91ef675..16e3b5dbd0 100644
--- a/Documentation/operations/upgrade.rst
+++ b/Documentation/operations/upgrade.rst
@@ -303,6 +303,26 @@ Annotations:
 
 .. _current_release_required_changes:
 
+.. _1.11_upgrade_notes:
+
+1.11 Upgrade Notes
+------------------
+
+Removed Options
+~~~~~~~~~~~~~~~
+
+* ``bpf-compile-debug``: This option does not have any effect since 1.10
+  and is now removed.
+* ``k8s-force-json-patch``: This option does not have any effect for
+  environments running Kubernetes >= 1.13, is deprecated since 1.10, and
+  now removed.
+* ``masquerade``: This option has been deprecated in 1.10 and replaced by
+  ``enable-ipv4-masquerade``.
+* ``skip-crd-creation``: This option does not have any effect since 1.10
+  and is now removed.
+* ``hubble-flow-buffer-size``: This option was deprecated in 1.10 in favor
+  of ``hubble-event-buffer-capacity``. It is now removed.
+
 .. _1.10_upgrade_notes:
 
 1.10 Upgrade Notes
diff --git a/bpf/lib/nat.h b/bpf/lib/nat.h
index b847dc9ea0..9ed3603e1e 100644
--- a/bpf/lib/nat.h
+++ b/bpf/lib/nat.h
@@ -324,9 +324,9 @@ static __always_inline int snat_v4_rewrite_egress(struct __ctx_buff *ctx,
 						  struct ipv4_nat_entry *state,
 						  __u32 off, bool has_l4_header)
 {
+	int ret, flags = BPF_F_PSEUDO_HDR;
 	struct csum_offset csum = {};
 	__be32 sum_l4 = 0, sum;
-	int ret;
 
 	if (state->to_saddr == tuple->saddr &&
 	    state->to_sport == tuple->sport)
@@ -356,6 +356,7 @@ static __always_inline int snat_v4_rewrite_egress(struct __ctx_buff *ctx,
 					return DROP_WRITE_ERROR;
 				from = tuple->sport;
 				to = state->to_sport;
+				flags = 0; /* ICMPv4 has no pseudo-header */
 				sum_l4 = csum_diff(&from, 4, &to, 4, 0);
 				csum.offset = offsetof(struct icmphdr, checksum);
 				break;
@@ -371,7 +372,7 @@ static __always_inline int snat_v4_rewrite_egress(struct __ctx_buff *ctx,
 	if (tuple->nexthdr == IPPROTO_ICMP)
 		sum = sum_l4;
 	if (csum.offset &&
-	    csum_l4_replace(ctx, off, &csum, 0, sum, BPF_F_PSEUDO_HDR) < 0)
+	    csum_l4_replace(ctx, off, &csum, 0, sum, flags) < 0)
 		return DROP_CSUM_L4;
 	return 0;
 }
@@ -381,9 +382,9 @@ static __always_inline int snat_v4_rewrite_ingress(struct __ctx_buff *ctx,
 						   struct ipv4_nat_entry *state,
 						   __u32 off)
 {
+	int ret, flags = BPF_F_PSEUDO_HDR;
 	struct csum_offset csum = {};
 	__be32 sum_l4 = 0, sum;
-	int ret;
 
 	if (state->to_daddr == tuple->daddr &&
 	    state->to_dport == tuple->dport)
@@ -411,6 +412,7 @@ static __always_inline int snat_v4_rewrite_ingress(struct __ctx_buff *ctx,
 				return DROP_WRITE_ERROR;
 			from = tuple->dport;
 			to = state->to_dport;
+			flags = 0; /* ICMPv4 has no pseudo-header */
 			sum_l4 = csum_diff(&from, 4, &to, 4, 0);
 			csum.offset = offsetof(struct icmphdr, checksum);
 			break;
@@ -425,7 +427,7 @@ static __always_inline int snat_v4_rewrite_ingress(struct __ctx_buff *ctx,
 	if (tuple->nexthdr == IPPROTO_ICMP)
 		sum = sum_l4;
 	if (csum.offset &&
-	    csum_l4_replace(ctx, off, &csum, 0, sum, BPF_F_PSEUDO_HDR) < 0)
+	    csum_l4_replace(ctx, off, &csum, 0, sum, flags) < 0)
 		return DROP_CSUM_L4;
 	return 0;
 }
diff --git a/daemon/cmd/daemon.go b/daemon/cmd/daemon.go
index d713052f90..8575e33ad5 100644
--- a/daemon/cmd/daemon.go
+++ b/daemon/cmd/daemon.go
@@ -635,9 +635,9 @@ func NewDaemon(ctx context.Context, cancel context.CancelFunc, epMgr *endpointma
 			log.WithError(err).Fatal("Failed to determine BPF masquerade IPv4 addrs")
 		}
 	} else if option.Config.EnableIPMasqAgent {
-		log.Fatalf("BPF ip-masq-agent requires --%s=\"true\" and --%s=\"true\"", option.Masquerade, option.EnableBPFMasquerade)
+		log.Fatalf("BPF ip-masq-agent requires --%s=\"true\" and --%s=\"true\"", option.EnableIPv4Masquerade, option.EnableBPFMasquerade)
 	} else if option.Config.EnableEgressGateway {
-		log.Fatalf("Egress Gateway requires --%s=\"true\" and --%s=\"true\"", option.Masquerade, option.EnableBPFMasquerade)
+		log.Fatalf("Egress Gateway requires --%s=\"true\" and --%s=\"true\"", option.EnableIPv4Masquerade, option.EnableBPFMasquerade)
 	} else if !option.Config.EnableIPv4Masquerade && option.Config.EnableBPFMasquerade {
 		// There is not yet support for option.Config.EnableIPv6Masquerade
 		log.Infof("Auto-disabling %q feature since IPv4 masquerading was generally disabled",
diff --git a/daemon/cmd/daemon_main.go b/daemon/cmd/daemon_main.go
index 271b1eae3b..897821b548 100644
--- a/daemon/cmd/daemon_main.go
+++ b/daemon/cmd/daemon_main.go
@@ -245,10 +245,6 @@ func init() {
 	flags.String(option.CGroupRoot, "", "Path to Cgroup2 filesystem")
 	option.BindEnv(option.CGroupRoot)
 
-	flags.Bool(option.BPFCompileDebugName, true, "Enable debugging of the BPF compilation process. ")
-	flags.MarkDeprecated(option.BPFCompileDebugName, "This flag is no longer available and will be removed in v1.11")
-	option.BindEnv(option.BPFCompileDebugName)
-
 	flags.Bool(option.SockopsEnableName, defaults.SockopsEnable, "Enable sockops when kernel supported")
 	option.BindEnv(option.SockopsEnableName)
 
@@ -496,12 +492,6 @@ func init() {
 	option.BindEnv(option.K8sServiceCacheSize)
 	flags.MarkHidden(option.K8sServiceCacheSize)
 
-	// Remove in 1.11
-	flags.Bool(option.K8sForceJSONPatch, false, "When set uses JSON Patch to update CNP and CEP status in kube-apiserver")
-	option.BindEnv(option.K8sForceJSONPatch)
-	flags.MarkHidden(option.K8sForceJSONPatch)
-	flags.MarkDeprecated(option.K8sForceJSONPatch, "Marked for removal in Cilium 1.11 as this functionality is enabled by default since Kubernetes >= 1.13")
-
 	flags.String(option.K8sWatcherEndpointSelector, defaults.K8sWatcherEndpointSelector, "K8s endpoint watcher will watch for these k8s endpoints")
 	option.BindEnv(option.K8sWatcherEndpointSelector)
 
@@ -657,10 +647,6 @@ func init() {
 	flags.String(option.NAT46Range, defaults.DefaultNAT46Prefix, "IPv6 prefix to map IPv4 addresses to")
 	option.BindEnv(option.NAT46Range)
 
-	flags.Bool(option.Masquerade, true, "Masquerade packets from endpoints leaving the host")
-	option.BindEnv(option.Masquerade)
-	flags.MarkDeprecated(option.Masquerade, fmt.Sprintf("This option will be removed in v1.11 in favour of %s", option.EnableIPv4Masquerade))
-
 	flags.Bool(option.EnableIPv4Masquerade, true, "Masquerade IPv4 traffic from endpoints leaving the host")
 	option.BindEnv(option.EnableIPv4Masquerade)
 
@@ -868,10 +854,6 @@ func init() {
 	flags.MarkHidden(option.SelectiveRegeneration)
 	option.BindEnv(option.SelectiveRegeneration)
 
-	flags.Bool(option.SkipCRDCreation, false, "Skip Kubernetes Custom Resource Definitions creations")
-	flags.MarkDeprecated(option.SkipCRDCreation, "This will be deprecated in 1.10, and the flag will be removed in 1.11")
-	option.BindEnv(option.SkipCRDCreation)
-
 	flags.String(option.WriteCNIConfigurationWhenReady, "", fmt.Sprintf("Write the CNI configuration as specified via --%s to path when agent is ready", option.ReadCNIConfiguration))
 	option.BindEnv(option.WriteCNIConfigurationWhenReady)
 
@@ -906,10 +888,6 @@ func init() {
 	flags.StringSlice(option.HubbleTLSClientCAFiles, []string{}, "Paths to one or more public key files of client CA certificates to use for TLS with mutual authentication (mTLS). The files must contain PEM encoded data. When provided, this option effectively enables mTLS.")
 	option.BindEnv(option.HubbleTLSClientCAFiles)
 
-	flags.Int(option.HubbleFlowBufferSize, 0, "Maximum number of flows in Hubble's buffer.")
-	flags.MarkDeprecated(option.HubbleFlowBufferSize, fmt.Sprintf("Use %s instead.", option.HubbleEventBufferCapacity))
-	option.BindEnv(option.HubbleFlowBufferSize)
-
 	flags.Int(option.HubbleEventBufferCapacity, observeroption.Default.MaxFlows.AsInt(), "Capacity of Hubble events buffer. The provided value must be one less than an integer power of two and no larger than 65535 (ie: 1, 3, ..., 2047, 4095, ..., 65535)")
 	option.BindEnv(option.HubbleEventBufferCapacity)
 
diff --git a/daemon/cmd/datapath.go b/daemon/cmd/datapath.go
index 6caa89ce07..96d515e616 100644
--- a/daemon/cmd/datapath.go
+++ b/daemon/cmd/datapath.go
@@ -21,7 +21,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/cilium/cilium/pkg/bpf"
 	"github.com/cilium/cilium/pkg/common"
 	"github.com/cilium/cilium/pkg/controller"
 	"github.com/cilium/cilium/pkg/datapath"
@@ -286,18 +285,6 @@ func (d *Daemon) initMaps() error {
 		return nil
 	}
 
-	// Rename old policy call map to avoid packet drops during upgrade.
-	// TODO: Remove this renaming step once Cilium 1.8 is the oldest supported
-	// release.
-	policyMapPath := bpf.MapPath("cilium_policy")
-	if _, err := os.Stat(policyMapPath); err == nil {
-		newPolicyMapPath := bpf.MapPath(policymap.PolicyCallMapName)
-		if err = os.Rename(policyMapPath, newPolicyMapPath); err != nil {
-			log.WithError(err).Fatalf("Failed to rename policy call map from %s to %s",
-				policyMapPath, newPolicyMapPath)
-		}
-	}
-
 	if _, err := lxcmap.LXCMap.OpenOrCreate(); err != nil {
 		return err
 	}
diff --git a/daemon/cmd/hubble.go b/daemon/cmd/hubble.go
index 4e880b71d9..40c691c3a8 100644
--- a/daemon/cmd/hubble.go
+++ b/daemon/cmd/hubble.go
@@ -29,7 +29,6 @@ import (
 	"github.com/cilium/cilium/pkg/hubble/container"
 	"github.com/cilium/cilium/pkg/hubble/exporter"
 	"github.com/cilium/cilium/pkg/hubble/exporter/exporteroption"
-	"github.com/cilium/cilium/pkg/hubble/math"
 	"github.com/cilium/cilium/pkg/hubble/metrics"
 	"github.com/cilium/cilium/pkg/hubble/monitor"
 	"github.com/cilium/cilium/pkg/hubble/observer"
@@ -409,22 +408,7 @@ func (d *Daemon) GetK8sStore(name string) k8scache.Store {
 }
 
 // getHubbleEventBufferCapacity returns the user configured capacity for
-// Hubble's events buffer. The deprecated flag hubble-flow-buffer-size is
-// evaluated if greater than 0, otherwise the new flag
-// hubble-event-buffer-capacity is used instead.
+// Hubble's events buffer.
 func getHubbleEventBufferCapacity(logger logrus.FieldLogger) (container.Capacity, error) {
-	// check deprecated old flag for compatibility
-	// TODO: remove support for HubbleFlowBufferSize once 1.11 is out
-	if option.Config.HubbleFlowBufferSize > 0 {
-		logger.Warningf("Option '%s' is deprecated and will be removed in Cilium 1.11", option.HubbleFlowBufferSize)
-		c, err := container.NewCapacity(option.Config.HubbleFlowBufferSize)
-		if err == nil {
-			return c, nil
-		}
-		// old flag behavior was to silently round up the buffer capacity to a
-		// valid value (eg: 1500 -> 2047, 5000 -> 8191, etc) so adjust provided
-		// value to the nearest valid one for compatibility purpose
-		return container.NewCapacity((1<<math.MSB(uint64(option.Config.HubbleFlowBufferSize)) - 1))
-	}
 	return container.NewCapacity(option.Config.HubbleEventBufferCapacity)
 }
diff --git a/install/kubernetes/cilium/templates/hubble-generate-certs-clusterrole.yaml b/install/kubernetes/cilium/templates/hubble-generate-certs-clusterrole.yaml
index 845f52e299..fcd253060e 100644
--- a/install/kubernetes/cilium/templates/hubble-generate-certs-clusterrole.yaml
+++ b/install/kubernetes/cilium/templates/hubble-generate-certs-clusterrole.yaml
@@ -37,4 +37,5 @@ rules:
       - hubble-ca-secret
     verbs:
       - get
+      - update
 {{- end }}
diff --git a/pkg/datapath/linux/ipsec/ipsec_linux.go b/pkg/datapath/linux/ipsec/ipsec_linux.go
index 507c8cea96..2182b2352b 100644
--- a/pkg/datapath/linux/ipsec/ipsec_linux.go
+++ b/pkg/datapath/linux/ipsec/ipsec_linux.go
@@ -486,9 +486,7 @@ func LoadIPSecKeysFile(path string) (int, uint8, error) {
 func loadIPSecKeys(r io.Reader) (int, uint8, error) {
 	var spi uint8
 	var keyLen int
-	scopedLog := log.WithFields(logrus.Fields{
-		"spi": spi,
-	})
+	scopedLog := log
 
 	if err := encrypt.MapCreate(); err != nil {
 		return 0, 0, fmt.Errorf("Encrypt map create failed: %v", err)
@@ -581,6 +579,11 @@ func loadIPSecKeys(r io.Reader) (int, uint8, error) {
 			ipSecKeysGlobal[""] = ipSecKey
 		}
 
+		scopedLog := log.WithFields(logrus.Fields{
+			"oldSPI": oldSpi,
+			"SPI":    spi,
+		})
+
 		// Detect a version change and call cleanup routine to remove old
 		// keys after a timeout period. We also want to ensure on restart
 		// we remove any stale keys for example when a restart changes keys.
diff --git a/pkg/datapath/linux/node.go b/pkg/datapath/linux/node.go
index eb0053ddaa..aaf40e546a 100644
--- a/pkg/datapath/linux/node.go
+++ b/pkg/datapath/linux/node.go
@@ -788,7 +788,7 @@ func (n *linuxNodeHandler) insertNeighbor(ctx context.Context, newNode *nodeType
 			// issued arping after us, as it might have a more recent hwAddr value.
 			return
 		}
-		n.neighLastPingByNextHop[nextHopStr] = time.Now()
+		n.neighLastPingByNextHop[nextHopStr] = now
 		if prevHwAddr, found := n.neighByNextHop[nextHopStr]; found && prevHwAddr.String() == hwAddr.String() {
 			// Nothing to update, return early to avoid calling to netlink. This
 			// is based on the assumption that n.neighByNextHop gets populated
diff --git a/pkg/endpoint/bpf.go b/pkg/endpoint/bpf.go
index 94d9dbdf0d..eeef9369fb 100644
--- a/pkg/endpoint/bpf.go
+++ b/pkg/endpoint/bpf.go
@@ -58,7 +58,7 @@ const (
 	EndpointGenerationTimeout = 330 * time.Second
 
 	// OldCHeaderFileName is the previous name of the C header file for BPF
-	// programs for a particular endpoint. It can be removed once Cilium v1.8
+	// programs for a particular endpoint. It can be removed once Cilium v1.11
 	// is the oldest supported version.
 	oldCHeaderFileName = "lxc_config.h"
 
@@ -193,10 +193,10 @@ func (e *Endpoint) writeHeaderfile(prefix string) error {
 
 	err = f.CloseAtomicallyReplace()
 
-	// Create symlink with old header filename, to allow downgrade to pre-1.8
-	// Cilium. Can be removed once v1.8 is the oldest supported release.
-	// We don't add the symlink for the host endpoint so that it is not
-	// restored when downgrading to <1.8. To avoid linking to a
+	// Create symlink with old header filename, to allow downgrade to pre-1.11
+	// Cilium. Can be removed once v1.11 is the oldest supported release.
+	// The symlink is not needed for the host endpoint because we check the new
+	// header filename for that special endpoint. To avoid linking to a
 	// nonexistent file, only create the symlink if the header file
 	// creation/replacement file succeeded above.
 	if !e.IsHost() && err == nil {
diff --git a/pkg/endpoint/restore.go b/pkg/endpoint/restore.go
index d0bd882f20..a503363eba 100644
--- a/pkg/endpoint/restore.go
+++ b/pkg/endpoint/restore.go
@@ -99,18 +99,7 @@ func ReadEPsFromDirNames(ctx context.Context, owner regeneration.Owner, basePath
 		epDir := filepath.Join(basePath, epDirName)
 		isHost := hasHostObjectFile(epDir)
 
-		// We only check for the old header file. On v1.8+, if the old header
-		// file is present, the new header file will be too. When upgrading
-		// from pre-1.8, only the old header file is present and we will create
-		// the new.
-		// We can switch this to use the new header file once v1.8 is the
-		// oldest supported version.
-		cHeaderFile := filepath.Join(epDir, oldCHeaderFileName)
-		if isHost {
-			// Host endpoint doesn't have an old header file so that it's not
-			// restored on downgrades.
-			cHeaderFile = filepath.Join(epDir, common.CHeaderFileName)
-		}
+		cHeaderFile := filepath.Join(epDir, common.CHeaderFileName)
 		scopedLog := log.WithFields(logrus.Fields{
 			logfields.EndpointID: epDirName,
 			logfields.Path:       cHeaderFile,
@@ -136,25 +125,21 @@ func ReadEPsFromDirNames(ctx context.Context, owner regeneration.Owner, basePath
 			continue
 		}
 
-		// This copy is only needed when upgrading from a pre-1.8 Cilium and we
-		// can thus remove it once Cilium v1.8 is the oldest supported version.
-		newCHeaderFile := filepath.Join(epDir, common.CHeaderFileName)
-		if _, err := os.Stat(newCHeaderFile); err != nil {
+		// This symlink is only needed when upgrading from a pre-1.11 Cilium
+		// and we can thus remove it once Cilium v1.11 is the oldest supported
+		// version.
+		oldCHeaderFile := filepath.Join(epDir, oldCHeaderFileName)
+		if _, err := os.Stat(oldCHeaderFile); err != nil {
 			if !os.IsNotExist(err) {
-				scopedLog.WithError(err).Warn("Failed to check if C header exists. Ignoring endpoint")
-				continue
-			}
-			if err := os.Rename(cHeaderFile, newCHeaderFile); err != nil {
-				scopedLog.WithError(err).Warn("Failed to rename C header. Ignoring endpoint")
+				scopedLog.WithError(err).Warn("Failed to check if old C header exists. Ignoring endpoint")
 				continue
 			}
-			if err := os.Symlink(common.CHeaderFileName, cHeaderFile); err != nil {
+			if err := os.Symlink(common.CHeaderFileName, oldCHeaderFile); err != nil {
 				scopedLog.WithError(err).Warn("Failed to create symlink for C header. Ignoring endpoint")
 				continue
 			}
 			scopedLog.Debug("Created symlink for endpoint C header file")
 		}
-		cHeaderFile = newCHeaderFile
 
 		scopedLog.Debug("Found endpoint C header file")
 
diff --git a/pkg/nodediscovery/nodediscovery.go b/pkg/nodediscovery/nodediscovery.go
index d21a4c1b79..859dcc3fb0 100644
--- a/pkg/nodediscovery/nodediscovery.go
+++ b/pkg/nodediscovery/nodediscovery.go
@@ -466,22 +466,14 @@ func (n *NodeDiscovery) mutateNodeResource(nodeResource *ciliumv2.CiliumNode) er
 		if c := n.NetConf; c != nil {
 			if c.IPAM.MinAllocate != 0 {
 				nodeResource.Spec.IPAM.MinAllocate = c.IPAM.MinAllocate
-				// OBSOLETE: Left for backwards compatibility with <=1.7. Remove in >=1.11
-				nodeResource.Spec.ENI.MinAllocate = c.IPAM.MinAllocate
 			} else if c.ENI.MinAllocate != 0 {
 				nodeResource.Spec.IPAM.MinAllocate = c.ENI.MinAllocate
-				// OBSOLETE: Left for backwards compatibility with <=1.7. Remove in >=1.11
-				nodeResource.Spec.ENI.MinAllocate = c.ENI.MinAllocate
 			}
 
 			if c.IPAM.PreAllocate != 0 {
 				nodeResource.Spec.IPAM.PreAllocate = c.IPAM.PreAllocate
-				// OBSOLETE: Left for backwards compatibility with <=1.7. Remove in >=1.11
-				nodeResource.Spec.ENI.PreAllocate = c.IPAM.PreAllocate
 			} else if c.ENI.PreAllocate != 0 {
 				nodeResource.Spec.IPAM.PreAllocate = c.ENI.PreAllocate
-				// OBSOLETE: Left for backwards compatibility with <=1.7. Remove in >=1.11
-				nodeResource.Spec.ENI.PreAllocate = c.ENI.PreAllocate
 			}
 
 			if c.ENI.FirstInterfaceIndex != nil {
@@ -508,8 +500,6 @@ func (n *NodeDiscovery) mutateNodeResource(nodeResource *ciliumv2.CiliumNode) er
 		}
 
 		nodeResource.Spec.InstanceID = instanceID
-		// OBSOLETE: Left for backwards compatibility with <=1.7. Remove in >=1.11
-		nodeResource.Spec.ENI.InstanceID = instanceID
 		nodeResource.Spec.ENI.InstanceType = instanceType
 		nodeResource.Spec.ENI.AvailabilityZone = availabilityZone
 
diff --git a/pkg/option/config.go b/pkg/option/config.go
index 4465ee76b2..32b4b99b48 100644
--- a/pkg/option/config.go
+++ b/pkg/option/config.go
@@ -193,10 +193,6 @@ const (
 	// K8sRequireIPv6PodCIDRName is the name of the K8sRequireIPv6PodCIDR option
 	K8sRequireIPv6PodCIDRName = "k8s-require-ipv6-pod-cidr"
 
-	// K8sForceJSONPatch when set, uses JSON Patch to update CNP and CEP
-	// status in kube-apiserver.
-	K8sForceJSONPatch = "k8s-force-json-patch"
-
 	// K8sWatcherEndpointSelector specifies the k8s endpoints that Cilium
 	// should watch for.
 	K8sWatcherEndpointSelector = "k8s-watcher-endpoint-selector"
@@ -332,9 +328,6 @@ const (
 	// NAT46Range is the IPv6 prefix to map IPv4 addresses to
 	NAT46Range = "nat46-range"
 
-	// Masquerade are the packets from endpoints leaving the host
-	Masquerade = "masquerade"
-
 	// EnableIPv4Masquerade masquerades IPv4 packets from endpoints leaving the host.
 	EnableIPv4Masquerade = "enable-ipv4-masquerade"
 
@@ -495,9 +488,6 @@ const (
 	// ClusterMeshConfigName is the name of the ClusterMeshConfig option
 	ClusterMeshConfigName = "clustermesh-config"
 
-	// BPFCompileDebugName is the name of the option to enable BPF compiliation debugging
-	BPFCompileDebugName = "bpf-compile-debug"
-
 	// CTMapEntriesGlobalTCPDefault is the default maximum number of entries
 	// in the TCP CT table.
 	CTMapEntriesGlobalTCPDefault = 2 << 18 // 512Ki
@@ -848,10 +838,6 @@ const (
 	// must contain PEM encoded data.
 	HubbleTLSClientCAFiles = "hubble-tls-client-ca-files"
 
-	// HubbleFlowBufferSize specifies the maximum number of flows in Hubble's buffer.
-	// Deprecated: please, use HubbleEventBufferCapacity instead.
-	HubbleFlowBufferSize = "hubble-flow-buffer-size"
-
 	// HubbleEventBufferCapacity specifies the capacity of Hubble events buffer.
 	HubbleEventBufferCapacity = "hubble-event-buffer-capacity"
 
@@ -1244,10 +1230,6 @@ type DaemonConfig struct {
 	// K8sServiceCacheSize is the service cache size for cilium k8s package.
 	K8sServiceCacheSize uint
 
-	// K8sForceJSONPatch when set, uses JSON Patch to update CNP and CEP
-	// status in kube-apiserver.
-	K8sForceJSONPatch bool
-
 	// MTU is the maximum transmission unit of the underlying network
 	MTU int
 
@@ -1622,11 +1604,6 @@ type DaemonConfig struct {
 	// endpoint for all local communication
 	ForceLocalPolicyEvalAtSource bool
 
-	// SkipCRDCreation disables creation of the CustomResourceDefinition
-	// on daemon startup
-	// Deprecated: this option is not used by the cilium-agents anymore.
-	SkipCRDCreation bool
-
 	// EnableEndpointRoutes enables use of per endpoint routes
 	EnableEndpointRoutes bool
 
@@ -1843,10 +1820,6 @@ type DaemonConfig struct {
 	// must contain PEM encoded data.
 	HubbleTLSClientCAFiles []string
 
-	// HubbleFlowBufferSize specifies the maximum number of flows in Hubble's buffer.
-	// Deprecated: please, use HubbleEventBufferCapacity instead.
-	HubbleFlowBufferSize int
-
 	// HubbleEventBufferCapacity specifies the capacity of Hubble events buffer.
 	HubbleEventBufferCapacity int
 
@@ -2454,7 +2427,6 @@ func (c *DaemonConfig) Populate() {
 	c.K8sRequireIPv4PodCIDR = viper.GetBool(K8sRequireIPv4PodCIDRName)
 	c.K8sRequireIPv6PodCIDR = viper.GetBool(K8sRequireIPv6PodCIDRName)
 	c.K8sServiceCacheSize = uint(viper.GetInt(K8sServiceCacheSize))
-	c.K8sForceJSONPatch = viper.GetBool(K8sForceJSONPatch)
 	c.K8sEventHandover = viper.GetBool(K8sEventHandover)
 	c.K8sSyncTimeout = viper.GetDuration(K8sSyncTimeoutName)
 	c.AllocatorListTimeout = viper.GetDuration(AllocatorListTimeoutName)
@@ -2531,10 +2503,10 @@ func (c *DaemonConfig) Populate() {
 	c.BGPConfigPath = viper.GetString(BGPConfigPath)
 	c.ExternalClusterIP = viper.GetBool(ExternalClusterIPName)
 
-	err = c.populateMasqueradingSettings()
-	if err != nil {
-		log.WithError(err).Fatal("Failed to populate masquerading settings")
-	}
+	c.EnableIPv4Masquerade = viper.GetBool(EnableIPv4Masquerade) && c.EnableIPv4
+	c.EnableIPv6Masquerade = viper.GetBool(EnableIPv6Masquerade) && c.EnableIPv6
+	c.EnableBPFMasquerade = viper.GetBool(EnableBPFMasquerade)
+
 	c.populateLoadBalancerSettings()
 	c.populateDevices()
 	c.EgressMultiHomeIPRuleCompat = viper.GetBool(EgressMultiHomeIPRuleCompat)
@@ -2708,7 +2680,6 @@ func (c *DaemonConfig) Populate() {
 	c.HubbleTLSCertFile = viper.GetString(HubbleTLSCertFile)
 	c.HubbleTLSKeyFile = viper.GetString(HubbleTLSKeyFile)
 	c.HubbleTLSClientCAFiles = viper.GetStringSlice(HubbleTLSClientCAFiles)
-	c.HubbleFlowBufferSize = viper.GetInt(HubbleFlowBufferSize)
 	c.HubbleEventBufferCapacity = viper.GetInt(HubbleEventBufferCapacity)
 	c.HubbleEventQueueSize = viper.GetInt(HubbleEventQueueSize)
 	if c.HubbleEventQueueSize == 0 {
@@ -2736,26 +2707,9 @@ func (c *DaemonConfig) Populate() {
 	c.EndpointQueueSize = sanitizeIntParam(EndpointQueueSize, defaults.EndpointQueueSize)
 	c.EndpointGCInterval = viper.GetDuration(EndpointGCInterval)
 	c.SelectiveRegeneration = viper.GetBool(SelectiveRegeneration)
-	c.SkipCRDCreation = viper.GetBool(SkipCRDCreation)
 	c.DisableCNPStatusUpdates = viper.GetBool(DisableCNPStatusUpdates)
 }
 
-func (c *DaemonConfig) populateMasqueradingSettings() error {
-	switch {
-	case viper.IsSet(Masquerade) && viper.IsSet(EnableIPv4Masquerade):
-		return fmt.Errorf("--%s and --%s (deprecated) are mutually exclusive", EnableIPv4Masquerade, Masquerade)
-	case viper.IsSet(Masquerade):
-		c.EnableIPv4Masquerade = viper.GetBool(Masquerade) && c.EnableIPv4
-	default:
-		c.EnableIPv4Masquerade = viper.GetBool(EnableIPv4Masquerade) && c.EnableIPv4
-	}
-
-	c.EnableIPv6Masquerade = viper.GetBool(EnableIPv6Masquerade) && c.EnableIPv6
-	c.EnableBPFMasquerade = viper.GetBool(EnableBPFMasquerade)
-
-	return nil
-}
-
 func (c *DaemonConfig) populateDevices() {
 	c.Devices = viper.GetStringSlice(Devices)
 
@@ -2932,7 +2886,7 @@ func (c *DaemonConfig) checkIPv4NativeRoutingCIDR() error {
 		return fmt.Errorf(
 			"native routing cidr must be configured with option --%s "+
 				"in combination with --%s --%s=%s --%s=%s --%s=true",
-			IPv4NativeRoutingCIDR, Masquerade, TunnelName, c.Tunnel,
+			IPv4NativeRoutingCIDR, EnableIPv4Masquerade, TunnelName, c.Tunnel,
 			IPAM, c.IPAMMode(), EnableIPv4Name)
 	}
 
diff --git a/pkg/policy/l4.go b/pkg/policy/l4.go
index 2feb1d1eaa..99634d7a5c 100644
--- a/pkg/policy/l4.go
+++ b/pkg/policy/l4.go
@@ -34,7 +34,7 @@ import (
 	"github.com/cilium/cilium/pkg/policy/api"
 	"github.com/cilium/cilium/pkg/policy/trafficdirection"
 	"github.com/cilium/cilium/pkg/u8proto"
-	"github.com/cilium/proxy/go/cilium/api"
+	cilium "github.com/cilium/proxy/go/cilium/api"
 
 	"github.com/sirupsen/logrus"
 )
@@ -426,8 +426,8 @@ func (l4Filter *L4Filter) ToMapState(policyOwner PolicyOwner, direction trafficd
 }
 
 // IdentitySelectionUpdated implements CachedSelectionUser interface
-// This call is made while holding name manager and selector cache
-// locks, must beware of deadlocking!
+// This call is made from a single goroutine in FIFO order to keep add
+// and delete events ordered properly. No locks are held.
 //
 // The caller is responsible for making sure the same identity is not
 // present in both 'added' and 'deleted'.
diff --git a/pkg/policy/selectorcache.go b/pkg/policy/selectorcache.go
index f413948eea..6808887c85 100644
--- a/pkg/policy/selectorcache.go
+++ b/pkg/policy/selectorcache.go
@@ -19,6 +19,7 @@ import (
 	"encoding/json"
 	"sort"
 	"strings"
+	"sync"
 	"sync/atomic"
 	"unsafe"
 
@@ -152,7 +153,7 @@ type identitySelector interface {
 	removeUser(CachedSelectionUser, identityNotifier) (last bool)
 
 	// This may be called while the NameManager lock is held
-	notifyUsers(added, deleted []identity.NumericIdentity)
+	notifyUsers(sc *SelectorCache, added, deleted []identity.NumericIdentity)
 
 	numUsers() int
 }
@@ -183,6 +184,17 @@ func getIdentityCache(ids cache.IdentityCache) scIdentityCache {
 	return idCache
 }
 
+// userNotification stores the information needed to call
+// IdentitySelectionUpdated callbacks to notify users of selector's
+// identity changes. These are queued to be able to call the callbacks
+// in FIFO order while not holding any locks.
+type userNotification struct {
+	user     CachedSelectionUser
+	selector CachedSelector
+	added    []identity.NumericIdentity
+	deleted  []identity.NumericIdentity
+}
+
 // SelectorCache caches identities, identity selectors, and the
 // subsets of identities each selector selects.
 type SelectorCache struct {
@@ -197,6 +209,14 @@ type SelectorCache struct {
 	selectors map[string]identitySelector
 
 	localIdentityNotifier identityNotifier
+
+	// userCond is a condition variable for receiving signals
+	// about addition of new elements in userNotes
+	userCond *sync.Cond
+	// userMutex protects userNotes and is linked to userCond
+	userMutex lock.Mutex
+	// userNotes holds a FIFO list of user notifications to be made
+	userNotes []userNotification
 }
 
 // GetModel returns the API model of the SelectorCache.
@@ -223,12 +243,46 @@ func (sc *SelectorCache) GetModel() models.SelectorCache {
 	return selCacheMdl
 }
 
+func (sc *SelectorCache) handleUserNotifications() {
+	for {
+		sc.userMutex.Lock()
+		for len(sc.userNotes) == 0 {
+			sc.userCond.Wait()
+		}
+		// get the current batch of notifications and release the lock so that SelectorCache
+		// can't block on userMutex while we call IdentitySelectionUpdated callbacks below.
+		notifications := sc.userNotes
+		sc.userNotes = nil
+		sc.userMutex.Unlock()
+
+		for _, n := range notifications {
+			n.user.IdentitySelectionUpdated(n.selector, n.added, n.deleted)
+		}
+	}
+}
+
+func (sc *SelectorCache) queueUserNotification(user CachedSelectionUser, selector CachedSelector, added, deleted []identity.NumericIdentity) {
+	sc.userMutex.Lock()
+	sc.userNotes = append(sc.userNotes, userNotification{
+		user:     user,
+		selector: selector,
+		added:    added,
+		deleted:  deleted,
+	})
+	sc.userMutex.Unlock()
+	sc.userCond.Signal()
+}
+
 // NewSelectorCache creates a new SelectorCache with the given identities.
 func NewSelectorCache(ids cache.IdentityCache) *SelectorCache {
-	return &SelectorCache{
+	sc := &SelectorCache{
 		idCache:   getIdentityCache(ids),
 		selectors: make(map[string]identitySelector),
 	}
+	sc.userCond = sync.NewCond(&sc.userMutex)
+	go sc.handleUserNotifications()
+
+	return sc
 }
 
 // SetLocalIdentityNotifier injects the provided identityNotifier into the
@@ -375,10 +429,10 @@ type fqdnSelector struct {
 //
 // The caller is responsible for making sure the same identity is not
 // present in both 'added' and 'deleted'.
-func (f *fqdnSelector) notifyUsers(added, deleted []identity.NumericIdentity) {
+func (f *fqdnSelector) notifyUsers(sc *SelectorCache, added, deleted []identity.NumericIdentity) {
 	for user := range f.users {
 		// pass 'f' to the user as '*fqdnSelector'
-		user.IdentitySelectionUpdated(f, added, deleted)
+		sc.queueUserNotification(user, f, added, deleted)
 	}
 }
 
@@ -434,10 +488,10 @@ type labelIdentitySelector struct {
 //
 // The caller is responsible for making sure the same identity is not
 // present in both 'added' and 'deleted'.
-func (l *labelIdentitySelector) notifyUsers(added, deleted []identity.NumericIdentity) {
+func (l *labelIdentitySelector) notifyUsers(sc *SelectorCache, added, deleted []identity.NumericIdentity) {
 	for user := range l.users {
 		// pass 'l' to the user as '*labelIdentitySelector'
-		user.IdentitySelectionUpdated(l, added, deleted)
+		sc.queueUserNotification(user, l, added, deleted)
 	}
 }
 
@@ -567,7 +621,7 @@ func (sc *SelectorCache) updateFQDNSelector(fqdnSelec api.FQDNSelector, identiti
 	// getting the CIDR identities which correspond to this FQDNSelector. This
 	// is the primary difference here between FQDNSelector and IdentitySelector.
 	fqdnSel.updateSelections()
-	fqdnSel.notifyUsers(added, deleted) // disjoint sets, see the comment above
+	fqdnSel.notifyUsers(sc, added, deleted) // disjoint sets, see the comment above
 }
 
 // AddFQDNSelector adds the given api.FQDNSelector in to the selector cache. If
@@ -826,7 +880,7 @@ func (sc *SelectorCache) UpdateIdentities(added, deleted cache.IdentityCache) {
 				}
 				if len(dels)+len(adds) > 0 {
 					idSel.updateSelections()
-					idSel.notifyUsers(adds, dels)
+					idSel.notifyUsers(sc, adds, dels)
 				}
 			case *fqdnSelector:
 				// This is a no-op right now. We don't encode in the identities
diff --git a/pkg/redirectpolicy/manager.go b/pkg/redirectpolicy/manager.go
index 0e00894c23..f8983489d9 100644
--- a/pkg/redirectpolicy/manager.go
+++ b/pkg/redirectpolicy/manager.go
@@ -193,7 +193,7 @@ func (rpm *Manager) DeleteRedirectPolicy(config LRPConfig) error {
 
 	switch storedConfig.lrpType {
 	case lrpConfigTypeSvc:
-		rpm.deletePolicyService(*storedConfig.serviceID)
+		rpm.deletePolicyService(storedConfig)
 	case lrpConfigTypeAddr:
 		for _, feM := range storedConfig.frontendMappings {
 			rpm.deletePolicyFrontend(storedConfig, feM.feAddr)
@@ -246,7 +246,7 @@ func (rpm *Manager) OnDeleteService(svcID k8s.ServiceID) {
 		return
 	}
 
-	rpm.deletePolicyService(svcID)
+	rpm.deleteService(svcID)
 }
 
 func (rpm *Manager) OnAddPod(pod *slimcorev1.Pod) {
@@ -476,29 +476,51 @@ func (rpm *Manager) notifyPolicyBackendDelete(config *LRPConfig, frontendMapping
 }
 
 // deletePolicyService deletes internal state associated with the specified service.
-func (rpm *Manager) deletePolicyService(svcID k8s.ServiceID) {
-	if rp, ok := rpm.policyServices[svcID]; ok {
-		// Get the policy config that selects this service.
-		config := rpm.policyConfigs[rp]
-		for _, m := range config.frontendMappings {
-			rpm.deletePolicyFrontend(config, m.feAddr)
-		}
-		switch config.frontendType {
-		case svcFrontendAll:
-			config.frontendMappings = nil
-		case svcFrontendSinglePort:
-			fallthrough
-		case svcFrontendNamedPorts:
-			for _, feM := range config.frontendMappings {
-				feM.feAddr.IP = net.IP{}
-			}
+func (rpm *Manager) deletePolicyService(config *LRPConfig) {
+	for _, m := range config.frontendMappings {
+		rpm.deletePolicyFrontend(config, m.feAddr)
+	}
+	switch config.frontendType {
+	case svcFrontendAll:
+		config.frontendMappings = nil
+	case svcFrontendSinglePort:
+		fallthrough
+	case svcFrontendNamedPorts:
+		for _, feM := range config.frontendMappings {
+			feM.feAddr.IP = net.IP{}
 		}
-		// Retores the svc backends if there's still such a k8s svc.
-		swg := lock.NewStoppableWaitGroup()
-		if restored := rpm.svcCache.EnsureService(svcID, swg); restored {
-			log.WithFields(logrus.Fields{
-				logfields.K8sSvcID: svcID,
-			}).Debug("Restored service")
+	}
+	// Retores the svc backends if there's still such a k8s svc.
+	swg := lock.NewStoppableWaitGroup()
+	svcID := *config.serviceID
+	if restored := rpm.svcCache.EnsureService(svcID, swg); restored {
+		log.WithFields(logrus.Fields{
+			logfields.K8sSvcID: svcID,
+		}).Debug("Restored service")
+	}
+}
+
+func (rpm *Manager) deleteService(svcID k8s.ServiceID) {
+	var (
+		rp policyID
+		ok bool
+	)
+	if rp, ok = rpm.policyServices[svcID]; !ok {
+		return
+	}
+	// Get the policy config that selects this service.
+	config := rpm.policyConfigs[rp]
+	for _, m := range config.frontendMappings {
+		rpm.deletePolicyFrontend(config, m.feAddr)
+	}
+	switch config.frontendType {
+	case svcFrontendAll:
+		config.frontendMappings = nil
+	case svcFrontendSinglePort:
+		fallthrough
+	case svcFrontendNamedPorts:
+		for _, feM := range config.frontendMappings {
+			feM.feAddr.IP = net.IP{}
 		}
 	}
 }
diff --git a/pkg/service/service.go b/pkg/service/service.go
index 0b22f75c21..de3e29766f 100644
--- a/pkg/service/service.go
+++ b/pkg/service/service.go
@@ -194,14 +194,6 @@ func (s *Service) InitMaps(ipv6, ipv4, sockMaps, restore bool) error {
 	s.Lock()
 	defer s.Unlock()
 
-	// The following two calls can be removed in v1.8+.
-	if err := bpf.UnpinMapIfExists("cilium_lb6_rr_seq_v2"); err != nil {
-		return nil
-	}
-	if err := bpf.UnpinMapIfExists("cilium_lb4_rr_seq_v2"); err != nil {
-		return nil
-	}
-
 	toOpen := []*bpf.Map{}
 	toDelete := []*bpf.Map{}
 	if ipv6 {


Test patch:
diff --git a/pkg/datapath/linux/node_linux_test.go b/pkg/datapath/linux/node_linux_test.go
index 1b02cde816..b5bb3ef06f 100644
--- a/pkg/datapath/linux/node_linux_test.go
+++ b/pkg/datapath/linux/node_linux_test.go
@@ -1188,23 +1188,28 @@ func (s *linuxPrivilegedIPv4OnlyTestSuite) TestArpPingHandling(c *check.C) {
 			c.Assert(err, check.IsNil)
 			return nil
 		})
+
 		// Check that MAC has been changed in the neigh table
-		time.Sleep(500 * time.Millisecond)
-		neighs, err = netlink.NeighList(veth0.Attrs().Index, netlink.FAMILY_V4)
-		c.Assert(err, check.IsNil)
-		found = false
-		for _, n := range neighs {
-			if n.IP.Equal(ip1) && n.State == netlink.NUD_PERMANENT {
-				c.Assert(n.HardwareAddr.String(), check.Equals, mac.String())
-				c.Assert(neighHwAddr(ip1.String()), check.Equals, mac.String())
-				c.Assert(neighRefCount(ip1.String()), check.Equals, 1)
-				found = true
-				break
+		var found bool
+		err := testutils.WaitUntilWithSleep(func() bool {
+			neighs, err = netlink.NeighList(veth0.Attrs().Index, netlink.FAMILY_V4)
+			c.Assert(err, check.IsNil)
+			found = false
+			for _, n := range neighs {
+				if n.IP.Equal(ip1) && n.State == netlink.NUD_PERMANENT &&
+					n.HardwareAddr.String() == mac.String() &&
+					neighHwAddr(ip1.String()) == mac.String() &&
+					neighRefCount(ip1.String()) == 1 {
+					found = true
+					return true
+				}
 			}
-		}
+			return false
+		}, 5*time.Second, 200*time.Millisecond)
+		c.Assert(err, check.IsNil)
 		c.Assert(found, check.Equals, true)
-
 	}
+
 	// Cleanup
 	close(done)
 	wg.Wait()
diff --git a/pkg/policy/resolve_deny_test.go b/pkg/policy/resolve_deny_test.go
index 299c5f2ba9..e26be5674d 100644
--- a/pkg/policy/resolve_deny_test.go
+++ b/pkg/policy/resolve_deny_test.go
@@ -17,6 +17,8 @@
 package policy
 
 import (
+	"time"
+
 	"github.com/cilium/cilium/pkg/checker"
 	"github.com/cilium/cilium/pkg/identity"
 	"github.com/cilium/cilium/pkg/identity/cache"
@@ -356,6 +358,7 @@ func (ds *PolicyTestSuite) TestMapStateWithIngressDenyWildcard(c *C) {
 		identity.NumericIdentity(192): labels.ParseSelectLabelArray("id=resolve_test_1"),
 	}
 	testSelectorCache.UpdateIdentities(added1, nil)
+	time.Sleep(100 * time.Millisecond)
 	c.Assert(policy.policyMapChanges.changes, HasLen, 0)
 
 	// Have to remove circular reference before testing to avoid an infinite loop
@@ -433,12 +436,14 @@ func (ds *PolicyTestSuite) TestMapStateWithIngressDeny(c *C) {
 	testSelectorCache.UpdateIdentities(added1, nil)
 	// Cleanup the identities from the testSelectorCache
 	defer testSelectorCache.UpdateIdentities(nil, added1)
+	time.Sleep(100 * time.Millisecond)
 	c.Assert(policy.policyMapChanges.changes, HasLen, 3)
 
 	deleted1 := cache.IdentityCache{
 		identity.NumericIdentity(193): labels.ParseSelectLabelArray("id=resolve_test_1", "num=2"),
 	}
 	testSelectorCache.UpdateIdentities(nil, deleted1)
+	time.Sleep(100 * time.Millisecond)
 	c.Assert(policy.policyMapChanges.changes, HasLen, 4)
 
 	cachedSelectorWorld := testSelectorCache.FindCachedIdentitySelector(api.ReservedEndpointSelectors[labels.IDNameWorld])
diff --git a/pkg/policy/resolve_test.go b/pkg/policy/resolve_test.go
index a2d5ed3d33..2c33b33550 100644
--- a/pkg/policy/resolve_test.go
+++ b/pkg/policy/resolve_test.go
@@ -20,6 +20,7 @@ import (
 	"fmt"
 	"sync"
 	"testing"
+	"time"
 
 	"github.com/cilium/cilium/pkg/checker"
 	"github.com/cilium/cilium/pkg/identity"
@@ -519,6 +520,7 @@ func (ds *PolicyTestSuite) TestMapStateWithIngressWildcard(c *C) {
 		identity.NumericIdentity(192): labels.ParseSelectLabelArray("id=resolve_test_1"),
 	}
 	testSelectorCache.UpdateIdentities(added1, nil)
+	time.Sleep(100 * time.Millisecond)
 	c.Assert(policy.policyMapChanges.changes, HasLen, 0)
 
 	// Have to remove circular reference before testing to avoid an infinite loop
@@ -598,12 +600,14 @@ func (ds *PolicyTestSuite) TestMapStateWithIngress(c *C) {
 	testSelectorCache.UpdateIdentities(added1, nil)
 	// Cleanup the identities from the testSelectorCache
 	defer testSelectorCache.UpdateIdentities(nil, added1)
+	time.Sleep(100 * time.Millisecond)
 	c.Assert(policy.policyMapChanges.changes, HasLen, 3)
 
 	deleted1 := cache.IdentityCache{
 		identity.NumericIdentity(193): labels.ParseSelectLabelArray("id=resolve_test_1", "num=2"),
 	}
 	testSelectorCache.UpdateIdentities(nil, deleted1)
+	time.Sleep(100 * time.Millisecond)
 	c.Assert(policy.policyMapChanges.changes, HasLen, 4)
 
 	cachedSelectorWorld := testSelectorCache.FindCachedIdentitySelector(api.ReservedEndpointSelectors[labels.IDNameWorld])
diff --git a/pkg/policy/selectorcache_test.go b/pkg/policy/selectorcache_test.go
index 94f61ebe39..fe114d7207 100644
--- a/pkg/policy/selectorcache_test.go
+++ b/pkg/policy/selectorcache_test.go
@@ -17,11 +17,14 @@
 package policy
 
 import (
+	"sync"
+
 	"github.com/cilium/cilium/pkg/checker"
 	"github.com/cilium/cilium/pkg/identity"
 	"github.com/cilium/cilium/pkg/identity/cache"
 	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
 	"github.com/cilium/cilium/pkg/labels"
+	"github.com/cilium/cilium/pkg/lock"
 	"github.com/cilium/cilium/pkg/policy/api"
 	"github.com/cilium/cilium/pkg/testutils"
 
@@ -38,22 +41,33 @@ func (d *DummySelectorCacheUser) IdentitySelectionUpdated(selector CachedSelecto
 }
 
 type cachedSelectionUser struct {
-	c             *C
-	sc            *SelectorCache
-	name          string
+	c    *C
+	sc   *SelectorCache
+	name string
+
+	updateMutex   lock.Mutex
+	updateCond    *sync.Cond
 	selections    map[CachedSelector][]identity.NumericIdentity
 	notifications int
 	adds          int
 	deletes       int
 }
 
+func (sc *SelectorCache) haveUserNotifications() bool {
+	sc.userMutex.Lock()
+	defer sc.userMutex.Unlock()
+	return len(sc.userNotes) > 0
+}
+
 func newUser(c *C, name string, sc *SelectorCache) *cachedSelectionUser {
-	return &cachedSelectionUser{
+	csu := &cachedSelectionUser{
 		c:          c,
 		sc:         sc,
 		name:       name,
 		selections: make(map[CachedSelector][]identity.NumericIdentity),
 	}
+	csu.updateCond = sync.NewCond(&csu.updateMutex)
+	return csu
 }
 
 func haveNid(nid identity.NumericIdentity, selections []identity.NumericIdentity) bool {
@@ -66,7 +80,9 @@ func haveNid(nid identity.NumericIdentity, selections []identity.NumericIdentity
 }
 
 func (csu *cachedSelectionUser) AddIdentitySelector(sel api.EndpointSelector) CachedSelector {
-	notifications := csu.notifications
+	csu.updateMutex.Lock()
+	defer csu.updateMutex.Unlock()
+
 	cached, added := csu.sc.AddIdentitySelector(csu, sel)
 	csu.c.Assert(cached, Not(Equals), nil)
 
@@ -76,13 +92,15 @@ func (csu *cachedSelectionUser) AddIdentitySelector(sel api.EndpointSelector) Ca
 	csu.selections[cached] = cached.GetSelections()
 
 	// Pre-existing selections are not notified as updates
-	csu.c.Assert(csu.notifications, Equals, notifications)
+	csu.c.Assert(csu.sc.haveUserNotifications(), Equals, false)
 
 	return cached
 }
 
 func (csu *cachedSelectionUser) AddFQDNSelector(sel api.FQDNSelector) CachedSelector {
-	notifications := csu.notifications
+	csu.updateMutex.Lock()
+	defer csu.updateMutex.Unlock()
+
 	cached, added := csu.sc.AddFQDNSelector(csu, sel)
 	csu.c.Assert(cached, Not(Equals), nil)
 
@@ -92,21 +110,41 @@ func (csu *cachedSelectionUser) AddFQDNSelector(sel api.FQDNSelector) CachedSele
 	csu.selections[cached] = cached.GetSelections()
 
 	// Pre-existing selections are not notified as updates
-	csu.c.Assert(csu.notifications, Equals, notifications)
+	csu.c.Assert(csu.sc.haveUserNotifications(), Equals, false)
 
 	return cached
 }
 
 func (csu *cachedSelectionUser) RemoveSelector(sel CachedSelector) {
-	notifications := csu.notifications
+	csu.updateMutex.Lock()
+	defer csu.updateMutex.Unlock()
+
 	csu.sc.RemoveSelector(sel, csu)
 	delete(csu.selections, sel)
 
 	// No notifications for a removed selector
-	csu.c.Assert(csu.notifications, Equals, notifications)
+	csu.c.Assert(csu.sc.haveUserNotifications(), Equals, false)
+}
+
+func (csu *cachedSelectionUser) Reset() {
+	csu.updateMutex.Lock()
+	defer csu.updateMutex.Unlock()
+	csu.notifications = 0
+}
+
+func (csu *cachedSelectionUser) WaitForUpdate() (adds, deletes int) {
+	csu.updateMutex.Lock()
+	defer csu.updateMutex.Unlock()
+	for csu.notifications == 0 {
+		csu.updateCond.Wait()
+	}
+	return csu.adds, csu.deletes
 }
 
 func (csu *cachedSelectionUser) IdentitySelectionUpdated(selector CachedSelector, added, deleted []identity.NumericIdentity) {
+	csu.updateMutex.Lock()
+	defer csu.updateMutex.Unlock()
+
 	csu.notifications++
 	csu.adds += len(added)
 	csu.deletes += len(deleted)
@@ -123,6 +161,8 @@ func (csu *cachedSelectionUser) IdentitySelectionUpdated(selector CachedSelector
 
 	// update selections
 	csu.selections[selector] = selections
+
+	csu.updateCond.Signal()
 }
 
 // Mock CachedSelector for unit testing.
@@ -323,12 +363,14 @@ func (ds *SelectorCacheTestSuite) TestIdentityUpdates(c *C) {
 	c.Assert(len(selections2), Equals, 1)
 	c.Assert(selections2[0], Equals, identity.NumericIdentity(2345))
 
+	user1.Reset()
 	// Add some identities to the identity cache
 	sc.UpdateIdentities(cache.IdentityCache{
 		12345: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s)}.LabelArray(),
 	}, nil)
-	c.Assert(user1.adds, Equals, 1)
-	c.Assert(user1.deletes, Equals, 0)
+	adds, deletes := user1.WaitForUpdate()
+	c.Assert(adds, Equals, 1)
+	c.Assert(deletes, Equals, 0)
 
 	// Current selections contain the numeric identities of existing identities that match
 	selections = cached.GetSelections()
@@ -336,12 +378,14 @@ func (ds *SelectorCacheTestSuite) TestIdentityUpdates(c *C) {
 	c.Assert(selections[0], Equals, identity.NumericIdentity(1234))
 	c.Assert(selections[1], Equals, identity.NumericIdentity(12345))
 
+	user1.Reset()
 	// Remove some identities from the identity cache
 	sc.UpdateIdentities(nil, cache.IdentityCache{
 		12345: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s)}.LabelArray(),
 	})
-	c.Assert(user1.adds, Equals, 1)
-	c.Assert(user1.deletes, Equals, 1)
+	adds, deletes = user1.WaitForUpdate()
+	c.Assert(adds, Equals, 1)
+	c.Assert(deletes, Equals, 1)
 
 	// Current selections contain the numeric identities of existing identities that match
 	selections = cached.GetSelections()
@@ -392,19 +436,26 @@ func (ds *SelectorCacheTestSuite) TestFQDNSelectorUpdates(c *C) {
 	}
 
 	// Add some identities to the identity cache
+	user1.Reset()
 	ciliumIdentities = append(ciliumIdentities, identity.NumericIdentity(123456))
 	sc.UpdateFQDNSelector(ciliumSel, ciliumIdentities)
-	c.Assert(user1.adds, Equals, 1)
-	c.Assert(user1.deletes, Equals, 0)
+	adds, deletes := user1.WaitForUpdate()
+	c.Assert(adds, Equals, 1)
+	c.Assert(deletes, Equals, 0)
 
+	user1.Reset()
 	ciliumIdentities = ciliumIdentities[:1]
 	sc.UpdateFQDNSelector(ciliumSel, ciliumIdentities)
-	c.Assert(user1.adds, Equals, 1)
-	c.Assert(user1.deletes, Equals, 3)
+	adds, deletes = user1.WaitForUpdate()
+	c.Assert(adds, Equals, 1)
+	c.Assert(deletes, Equals, 3)
 
+	user1.Reset()
 	ciliumIdentities = []identity.NumericIdentity{}
 	sc.UpdateFQDNSelector(ciliumSel, ciliumIdentities)
-	c.Assert(user1.deletes, Equals, 4)
+	adds, deletes = user1.WaitForUpdate()
+	c.Assert(adds, Equals, 1)
+	c.Assert(deletes, Equals, 4)
 
 	user1.RemoveSelector(cached)
 	user1.RemoveSelector(cached2)
@@ -484,16 +535,20 @@ func (ds *SelectorCacheTestSuite) TestIdentityUpdatesMultipleUsers(c *C) {
 	cached2 := user2.AddIdentitySelector(testSelector)
 	c.Assert(cached2, Equals, cached)
 
+	user1.Reset()
+	user2.Reset()
 	// Add some identities to the identity cache
 	sc.UpdateIdentities(cache.IdentityCache{
 		123: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s)}.LabelArray(),
 		234: labels.Labels{"app": labels.NewLabel("app", "test2", labels.LabelSourceK8s)}.LabelArray(),
 		345: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s)}.LabelArray(),
 	}, nil)
-	c.Assert(user1.adds, Equals, 2)
-	c.Assert(user1.deletes, Equals, 0)
-	c.Assert(user2.adds, Equals, 2)
-	c.Assert(user2.deletes, Equals, 0)
+	adds, deletes := user1.WaitForUpdate()
+	c.Assert(adds, Equals, 2)
+	c.Assert(deletes, Equals, 0)
+	adds, deletes = user2.WaitForUpdate()
+	c.Assert(adds, Equals, 2)
+	c.Assert(deletes, Equals, 0)
 
 	// Current selections contain the numeric identities of existing identities that match
 	selections := cached.GetSelections()
@@ -504,15 +559,19 @@ func (ds *SelectorCacheTestSuite) TestIdentityUpdatesMultipleUsers(c *C) {
 
 	c.Assert(cached.GetSelections(), checker.DeepEquals, cached2.GetSelections())
 
+	user1.Reset()
+	user2.Reset()
 	// Remove some identities from the identity cache
 	sc.UpdateIdentities(nil, cache.IdentityCache{
 		123: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s)}.LabelArray(),
 		234: labels.Labels{"app": labels.NewLabel("app", "test2", labels.LabelSourceK8s)}.LabelArray(),
 	})
-	c.Assert(user1.adds, Equals, 2)
-	c.Assert(user1.deletes, Equals, 1)
-	c.Assert(user2.adds, Equals, 2)
-	c.Assert(user2.deletes, Equals, 1)
+	adds, deletes = user1.WaitForUpdate()
+	c.Assert(adds, Equals, 2)
+	c.Assert(deletes, Equals, 1)
+	adds, deletes = user2.WaitForUpdate()
+	c.Assert(adds, Equals, 2)
+	c.Assert(deletes, Equals, 1)
 
 	// Current selections contain the numeric identities of existing identities that match
 	selections = cached.GetSelections()
diff --git a/pkg/testutils/condition.go b/pkg/testutils/condition.go
index 2aa258a9cb..8c53936610 100644
--- a/pkg/testutils/condition.go
+++ b/pkg/testutils/condition.go
@@ -26,7 +26,14 @@ type ConditionFunc func() bool
 // WaitUntil evaluates the condition every 10 milliseconds and waits for the
 // condition to be met. The function will time out and return an error after
 // timeout
+
 func WaitUntil(condition ConditionFunc, timeout time.Duration) error {
+	return WaitUntilWithSleep(condition, timeout, 10*time.Millisecond)
+}
+
+// WaitUntilWithSleep does the same as WaitUntil except that the sleep time
+// between the condition checks is given.
+func WaitUntilWithSleep(condition ConditionFunc, timeout, sleep time.Duration) error {
 	now := time.Now()
 	for {
 		if time.Since(now) > timeout {
@@ -37,6 +44,6 @@ func WaitUntil(condition ConditionFunc, timeout time.Duration) error {
 			return nil
 		}
 
-		time.Sleep(10 * time.Millisecond)
+		time.Sleep(sleep)
 	}
 }
diff --git a/test/helpers/cons.go b/test/helpers/cons.go
index c62539e01f..7622c3100d 100644
--- a/test/helpers/cons.go
+++ b/test/helpers/cons.go
@@ -279,16 +279,11 @@ const (
 // NightlyStableUpgradesFrom maps the cilium image versions to the helm charts
 // that will be used to run update tests in the Nightly test.
 var NightlyStableUpgradesFrom = map[string]string{
-	"v1.6": "1.6-dev",
-	"v1.7": "1.7-dev",
 	"v1.8": "1.8-dev",
 	"v1.9": "1.9-dev",
 }
 
 var (
-	IsCiliumV1_5  = versioncheck.MustCompile(">=1.4.90 <1.6.0")
-	IsCiliumV1_6  = versioncheck.MustCompile(">=1.5.90 <1.7.0")
-	IsCiliumV1_7  = versioncheck.MustCompile(">=1.6.90 <1.8.0")
 	IsCiliumV1_8  = versioncheck.MustCompile(">=1.7.90 <1.9.0")
 	IsCiliumV1_9  = versioncheck.MustCompile(">=1.8.90 <1.10.0")
 	IsCiliumV1_10 = versioncheck.MustCompile(">=1.9.90 <1.11.0")
diff --git a/test/helpers/utils.go b/test/helpers/utils.go
index 197e6f7736..5a60e8ebe0 100644
--- a/test/helpers/utils.go
+++ b/test/helpers/utils.go
@@ -399,7 +399,7 @@ func DNSDeployment(base string) string {
 	var DNSEngine = "coredns"
 	k8sVersion := GetCurrentK8SEnv()
 	switch k8sVersion {
-	case "1.7", "1.8", "1.9", "1.10":
+	case "1.8", "1.9", "1.10":
 		DNSEngine = "kubedns"
 	}
 
@@ -427,12 +427,6 @@ func getK8sSupportedConstraints(ciliumVersion string) (semver.Range, error) {
 		return nil, err
 	}
 	switch {
-	case IsCiliumV1_5(cst):
-		return versioncheck.MustCompile(">=1.8.0 <1.16.0"), nil
-	case IsCiliumV1_6(cst):
-		return versioncheck.MustCompile(">=1.8.0 <1.18.0"), nil
-	case IsCiliumV1_7(cst):
-		return versioncheck.MustCompile(">=1.10.0 <1.18.0"), nil
 	case IsCiliumV1_8(cst):
 		return versioncheck.MustCompile(">=1.10.0 <1.19.0"), nil
 	case IsCiliumV1_9(cst):