github.com/cilium/cilium@v1.16.2/pkg/endpoint/redirect_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package endpoint
     5  
     6  import (
     7  	"context"
     8  	"sync"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/cilium/cilium/pkg/completion"
    14  	datapath "github.com/cilium/cilium/pkg/datapath/types"
    15  	"github.com/cilium/cilium/pkg/fqdn/restore"
    16  	"github.com/cilium/cilium/pkg/identity"
    17  	"github.com/cilium/cilium/pkg/identity/cache"
    18  	"github.com/cilium/cilium/pkg/identity/identitymanager"
    19  	"github.com/cilium/cilium/pkg/kvstore"
    20  	"github.com/cilium/cilium/pkg/labels"
    21  	monitorAPI "github.com/cilium/cilium/pkg/monitor/api"
    22  	"github.com/cilium/cilium/pkg/option"
    23  	"github.com/cilium/cilium/pkg/policy"
    24  	"github.com/cilium/cilium/pkg/policy/api"
    25  	"github.com/cilium/cilium/pkg/policy/trafficdirection"
    26  	"github.com/cilium/cilium/pkg/proxy/endpoint"
    27  	"github.com/cilium/cilium/pkg/revert"
    28  	"github.com/cilium/cilium/pkg/testutils"
    29  	testidentity "github.com/cilium/cilium/pkg/testutils/identity"
    30  	testipcache "github.com/cilium/cilium/pkg/testutils/ipcache"
    31  	"github.com/cilium/cilium/pkg/u8proto"
    32  )
    33  
    34  type RedirectSuite struct {
    35  	oldPolicyEnable string
    36  	mgr             *cache.CachingIdentityAllocator
    37  	do              *DummyOwner
    38  	rsp             *RedirectSuiteProxy
    39  	stats           *regenerationStatistics
    40  }
    41  
    42  func setupRedirectSuite(tb testing.TB) *RedirectSuite {
    43  	testutils.IntegrationTest(tb)
    44  
    45  	s := &RedirectSuite{
    46  		do: &DummyOwner{},
    47  	}
    48  	s.oldPolicyEnable = policy.GetPolicyEnabled()
    49  	policy.SetPolicyEnabled(option.DefaultEnforcement)
    50  
    51  	// Setup dependencies for endpoint.
    52  	kvstore.SetupDummy(tb, "etcd")
    53  
    54  	s.mgr = cache.NewCachingIdentityAllocator(s.do)
    55  	<-s.mgr.InitIdentityAllocator(nil)
    56  
    57  	identityCache := identity.IdentityMap{
    58  		identity.NumericIdentity(identityFoo): labelsFoo,
    59  		identity.NumericIdentity(identityBar): labelsBar,
    60  	}
    61  
    62  	s.do.repo = policy.NewPolicyRepository(identityCache, nil, nil)
    63  	s.do.repo.GetSelectorCache().SetLocalIdentityNotifier(testidentity.NewDummyIdentityNotifier())
    64  
    65  	s.rsp = &RedirectSuiteProxy{
    66  		parserProxyPortMap: map[string]uint16{
    67  			policy.ParserTypeHTTP.String():  httpPort,
    68  			policy.ParserTypeDNS.String():   dnsPort,
    69  			policy.ParserTypeKafka.String(): kafkaPort,
    70  			"crd/cec1/listener1":            crd1Port,
    71  			"crd/cec2/listener2":            crd2Port,
    72  		},
    73  		redirectPortUserMap: make(map[uint16][]string),
    74  	}
    75  
    76  	s.stats = new(regenerationStatistics)
    77  
    78  	tb.Cleanup(func() {
    79  		identitymanager.RemoveAll()
    80  		s.mgr.Close()
    81  		policy.SetPolicyEnabled(s.oldPolicyEnable)
    82  	})
    83  
    84  	return s
    85  }
    86  
    87  // RedirectSuiteProxy implements EndpointProxy. It is used for testing the
    88  // functions related to generating proxy redirects for a given Endpoint.
    89  type RedirectSuiteProxy struct {
    90  	parserProxyPortMap  map[string]uint16
    91  	redirectPortUserMap map[uint16][]string
    92  }
    93  
    94  // CreateOrUpdateRedirect returns the proxy port for the given L7Parser from the
    95  // ProxyPolicy parameter.
    96  func (r *RedirectSuiteProxy) CreateOrUpdateRedirect(ctx context.Context, l4 policy.ProxyPolicy, id string, localEndpoint endpoint.EndpointUpdater, wg *completion.WaitGroup) (proxyPort uint16, err error, finalizeFunc revert.FinalizeFunc, revertFunc revert.RevertFunc) {
    97  	pp := r.parserProxyPortMap[l4.GetL7Parser().String()+l4.GetListener()]
    98  	return pp, nil, func() { log.Infof("FINALIZER CALLED") }, nil
    99  }
   100  
   101  // RemoveRedirect does nothing.
   102  func (r *RedirectSuiteProxy) RemoveRedirect(id string, wg *completion.WaitGroup) (error, revert.FinalizeFunc, revert.RevertFunc) {
   103  	return nil, nil, nil
   104  }
   105  
   106  // UpdateNetworkPolicy does nothing.
   107  func (r *RedirectSuiteProxy) UpdateNetworkPolicy(ep endpoint.EndpointUpdater, vis *policy.VisibilityPolicy, policy *policy.L4Policy, ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup) (error, func() error) {
   108  	return nil, nil
   109  }
   110  
   111  // RemoveNetworkPolicy does nothing.
   112  func (r *RedirectSuiteProxy) RemoveNetworkPolicy(ep endpoint.EndpointInfoSource) {}
   113  
   114  // DummyIdentityAllocatorOwner implements
   115  // pkg/identity/cache/IdentityAllocatorOwner. It is used for unit testing.
   116  type DummyIdentityAllocatorOwner struct{}
   117  
   118  // UpdateIdentities does nothing.
   119  func (d *DummyIdentityAllocatorOwner) UpdateIdentities(added, deleted identity.IdentityMap) {
   120  }
   121  
   122  // GetNodeSuffix does nothing.
   123  func (d *DummyIdentityAllocatorOwner) GetNodeSuffix() string {
   124  	return ""
   125  }
   126  
   127  // DummyOwner implements pkg/endpoint/regeneration/Owner. Used for unit testing.
   128  type DummyOwner struct {
   129  	repo *policy.Repository
   130  }
   131  
   132  // GetPolicyRepository returns the policy repository of the owner.
   133  func (d *DummyOwner) GetPolicyRepository() *policy.Repository {
   134  	return d.repo
   135  }
   136  
   137  // QueueEndpointBuild does nothing.
   138  func (d *DummyOwner) QueueEndpointBuild(ctx context.Context, epID uint64) (func(), error) {
   139  	return nil, nil
   140  }
   141  
   142  // GetCompilationLock does nothing.
   143  func (d *DummyOwner) GetCompilationLock() datapath.CompilationLock {
   144  	return nil
   145  }
   146  
   147  // GetCIDRPrefixLengths does nothing.
   148  func (d *DummyOwner) GetCIDRPrefixLengths() (s6, s4 []int) {
   149  	return nil, nil
   150  }
   151  
   152  // SendNotification does nothing.
   153  func (d *DummyOwner) SendNotification(msg monitorAPI.AgentNotifyMessage) error {
   154  	return nil
   155  }
   156  
   157  // Datapath returns a nil datapath.
   158  func (d *DummyOwner) Datapath() datapath.Datapath {
   159  	return nil
   160  }
   161  
   162  func (s *DummyOwner) GetDNSRules(epID uint16) restore.DNSRules {
   163  	return nil
   164  }
   165  
   166  func (s *DummyOwner) RemoveRestoredDNSRules(epID uint16) {
   167  }
   168  
   169  // GetNodeSuffix does nothing.
   170  func (d *DummyOwner) GetNodeSuffix() string {
   171  	return ""
   172  }
   173  
   174  func (d *DummyOwner) UpdateIdentities(added, deleted identity.IdentityMap) {
   175  	wg := &sync.WaitGroup{}
   176  	d.repo.GetSelectorCache().UpdateIdentities(added, deleted, wg)
   177  	wg.Wait()
   178  }
   179  
   180  const (
   181  	httpPort  = uint16(19001)
   182  	dnsPort   = uint16(19002)
   183  	kafkaPort = uint16(19003)
   184  	crd1Port  = uint16(19004)
   185  	crd2Port  = uint16(19005)
   186  )
   187  
   188  func (s *RedirectSuite) NewTestEndpoint(t *testing.T) *Endpoint {
   189  	ep := NewTestEndpointWithState(t, s.do, s.do, testipcache.NewMockIPCache(), s.rsp, s.mgr, 12345, StateRegenerating)
   190  	ep.SetPropertyValue(PropertyFakeEndpoint, false)
   191  
   192  	epIdentity, _, err := s.mgr.AllocateIdentity(context.Background(), labelsBar.Labels(), true, identity.NumericIdentity(identityBar))
   193  	require.Nil(t, err)
   194  	ep.SetIdentity(epIdentity, true)
   195  
   196  	return ep
   197  }
   198  
   199  func (s *RedirectSuite) AddRules(rules api.Rules) {
   200  	s.do.repo.MustAddList(rules)
   201  }
   202  
   203  func (s *RedirectSuite) TearDownTest(t *testing.T) {
   204  	identitymanager.RemoveAll()
   205  	s.mgr.Close()
   206  	policy.SetPolicyEnabled(s.oldPolicyEnable)
   207  }
   208  
   209  func TestAddVisibilityRedirects(t *testing.T) {
   210  	s := setupRedirectSuite(t)
   211  	ep := s.NewTestEndpoint(t)
   212  
   213  	firstAnno := "<Ingress/80/TCP/HTTP>"
   214  	ep.UpdateVisibilityPolicy(func(_, _ string) (proxyVisibility string, err error) {
   215  		return firstAnno, nil
   216  	})
   217  	res, err := ep.regeneratePolicy(s.stats)
   218  	require.Nil(t, err)
   219  	err = ep.setDesiredPolicy(res)
   220  	require.Nil(t, err)
   221  
   222  	ctx, cancel := context.WithCancel(context.Background())
   223  	defer cancel()
   224  	cmp := completion.NewWaitGroup(ctx)
   225  
   226  	_, err, _, _ = ep.addNewRedirects(cmp)
   227  	require.Nil(t, err)
   228  	v, ok := ep.desiredPolicy.GetPolicyMap().Get(policy.Key{
   229  		Identity:         0,
   230  		DestPort:         uint16(80),
   231  		Nexthdr:          uint8(u8proto.TCP),
   232  		TrafficDirection: trafficdirection.Ingress.Uint8(),
   233  	})
   234  	require.Equal(t, true, ok)
   235  	require.Equal(t, httpPort, v.ProxyPort)
   236  
   237  	secondAnno := "<Ingress/80/TCP/Kafka>"
   238  
   239  	ep.UpdateVisibilityPolicy(func(_, _ string) (proxyVisibility string, err error) {
   240  		return secondAnno, nil
   241  	})
   242  	res, err = ep.regeneratePolicy(s.stats)
   243  	require.Nil(t, err)
   244  	err = ep.setDesiredPolicy(res)
   245  	require.Nil(t, err)
   246  
   247  	d, err, _, _ := ep.addNewRedirects(cmp)
   248  	require.Nil(t, err)
   249  	v, ok = ep.desiredPolicy.GetPolicyMap().Get(policy.Key{
   250  		Identity:         0,
   251  		DestPort:         uint16(80),
   252  		Nexthdr:          uint8(u8proto.TCP),
   253  		TrafficDirection: trafficdirection.Ingress.Uint8(),
   254  	})
   255  	require.Equal(t, true, ok)
   256  	// Check that proxyport was updated accordingly.
   257  	require.Equal(t, kafkaPort, v.ProxyPort)
   258  
   259  	thirdAnno := "<Ingress/80/TCP/Kafka>,<Egress/80/TCP/HTTP>"
   260  
   261  	// Check that multiple values in annotation are handled correctly.
   262  	ep.UpdateVisibilityPolicy(func(_, _ string) (proxyVisibility string, err error) {
   263  		return thirdAnno, nil
   264  	})
   265  	res, err = ep.regeneratePolicy(s.stats)
   266  	require.Nil(t, err)
   267  	err = ep.setDesiredPolicy(res)
   268  	require.Nil(t, err)
   269  
   270  	realizedRedirects := ep.GetRealizedRedirects()
   271  	d2, err, _, _ := ep.addNewRedirects(cmp)
   272  	require.Nil(t, err)
   273  	v, ok = ep.desiredPolicy.GetPolicyMap().Get(policy.Key{
   274  		Identity:         0,
   275  		DestPort:         uint16(80),
   276  		Nexthdr:          uint8(u8proto.TCP),
   277  		TrafficDirection: trafficdirection.Ingress.Uint8(),
   278  	})
   279  	require.Equal(t, true, ok)
   280  	require.Equal(t, kafkaPort, v.ProxyPort)
   281  
   282  	v, ok = ep.desiredPolicy.GetPolicyMap().Get(policy.Key{
   283  		Identity:         0,
   284  		DestPort:         uint16(80),
   285  		Nexthdr:          uint8(u8proto.TCP),
   286  		TrafficDirection: trafficdirection.Ingress.Uint8(),
   287  	})
   288  	require.Equal(t, true, ok)
   289  	require.Equal(t, kafkaPort, v.ProxyPort)
   290  
   291  	v, ok = ep.desiredPolicy.GetPolicyMap().Get(policy.Key{
   292  		Identity:         0,
   293  		DestPort:         uint16(80),
   294  		Nexthdr:          uint8(u8proto.TCP),
   295  		TrafficDirection: trafficdirection.Egress.Uint8(),
   296  	})
   297  	require.Equal(t, true, ok)
   298  	require.Equal(t, httpPort, v.ProxyPort)
   299  	pID := policy.ProxyID(ep.ID, false, u8proto.TCP.String(), uint16(80), "")
   300  	p, ok := d2[pID]
   301  	require.Equal(t, true, ok)
   302  	require.Equal(t, httpPort, p)
   303  
   304  	// Check that the egress redirect is removed when the redirects have been
   305  	// updated.
   306  	ep.removeOldRedirects(d, realizedRedirects, cmp)
   307  	// Egress redirect should not exist in desired redirects
   308  	_, ok = d[pID]
   309  	require.Equal(t, false, ok)
   310  
   311  	// Check that all redirects are removed when no visibility policy applies.
   312  	noAnno := ""
   313  	ep.UpdateVisibilityPolicy(func(_, _ string) (proxyVisibility string, err error) {
   314  		return noAnno, nil
   315  	})
   316  	res, err = ep.regeneratePolicy(s.stats)
   317  	require.Nil(t, err)
   318  	err = ep.setDesiredPolicy(res)
   319  	require.Nil(t, err)
   320  
   321  	realizedRedirects = ep.GetRealizedRedirects()
   322  	d, err, _, _ = ep.addNewRedirects(cmp)
   323  	require.Nil(t, err)
   324  	ep.removeOldRedirects(d, realizedRedirects, cmp)
   325  	require.Equal(t, 0, len(d))
   326  }
   327  
   328  var (
   329  	// Identity, labels, selectors for an endpoint named "foo"
   330  	identityFoo = uint32(100)
   331  	labelsFoo   = labels.ParseSelectLabelArray("foo", "red")
   332  	selectFoo_  = api.NewESFromLabels(labels.ParseSelectLabel("foo"))
   333  	selectRed_  = api.NewESFromLabels(labels.ParseSelectLabel("red"))
   334  	denyFooL3__ = selectFoo_
   335  
   336  	identityBar = uint32(200)
   337  
   338  	labelsBar  = labels.ParseSelectLabelArray("bar", "blue")
   339  	selectBar_ = api.NewESFromLabels(labels.ParseSelectLabel("bar"))
   340  
   341  	denyAllL4_ []api.PortDenyRule
   342  
   343  	allowPort80 = []api.PortRule{{
   344  		Ports: []api.PortProtocol{
   345  			{Port: "80", Protocol: api.ProtoTCP},
   346  		},
   347  	}}
   348  	allowHTTPRoot = &api.L7Rules{
   349  		HTTP: []api.PortRuleHTTP{
   350  			{Method: "GET", Path: "/"},
   351  		},
   352  	}
   353  
   354  	lblsL3DenyFoo = labels.ParseLabelArray("l3-deny")
   355  	ruleL3DenyFoo = api.NewRule().
   356  			WithLabels(lblsL3DenyFoo).
   357  			WithIngressDenyRules([]api.IngressDenyRule{{
   358  			IngressCommonRule: api.IngressCommonRule{
   359  				FromEndpoints: []api.EndpointSelector{denyFooL3__},
   360  			},
   361  			ToPorts: denyAllL4_,
   362  		}})
   363  	lblsL4L7Allow = labels.ParseLabelArray("l4l7-allow")
   364  	ruleL4L7Allow = api.NewRule().
   365  			WithLabels(lblsL4L7Allow).
   366  			WithIngressRules([]api.IngressRule{{
   367  			IngressCommonRule: api.IngressCommonRule{},
   368  			ToPorts:           combineL4L7(allowPort80, allowHTTPRoot),
   369  		}})
   370  
   371  	AllowAnyEgressLabels = labels.LabelArray{labels.NewLabel(policy.LabelKeyPolicyDerivedFrom,
   372  		policy.LabelAllowAnyEgress,
   373  		labels.LabelSourceReserved)}
   374  
   375  	dirIngress      = trafficdirection.Ingress.Uint8()
   376  	dirEgress       = trafficdirection.Egress.Uint8()
   377  	mapKeyAllL7     = policy.Key{Identity: 0, DestPort: 80, Nexthdr: 6, TrafficDirection: dirIngress}
   378  	mapKeyFoo       = policy.Key{Identity: identityFoo, DestPort: 0, InvertedPortMask: 0xffff, Nexthdr: 0, TrafficDirection: dirIngress}
   379  	mapKeyFooL7     = policy.Key{Identity: identityFoo, DestPort: 80, Nexthdr: 6, TrafficDirection: dirIngress}
   380  	mapKeyAllowAllE = policy.Key{Identity: 0, DestPort: 0, Nexthdr: 0, InvertedPortMask: 0xffff, TrafficDirection: dirEgress}
   381  )
   382  
   383  // combineL4L7 returns a new PortRule that refers to the specified l4 ports and
   384  // l7 rules.
   385  func combineL4L7(l4 []api.PortRule, l7 *api.L7Rules) []api.PortRule {
   386  	result := make([]api.PortRule, 0, len(l4))
   387  	for _, pr := range l4 {
   388  		result = append(result, api.PortRule{
   389  			Ports: pr.Ports,
   390  			Rules: l7,
   391  		})
   392  	}
   393  	return result
   394  }
   395  
   396  func TestRedirectWithDeny(t *testing.T) {
   397  	s := setupRedirectSuite(t)
   398  	ep := s.NewTestEndpoint(t)
   399  
   400  	// Policy denies anything to "foo"
   401  	s.AddRules(api.Rules{
   402  		ruleL3DenyFoo.WithEndpointSelector(selectBar_),
   403  		ruleL4L7Allow.WithEndpointSelector(selectBar_),
   404  	})
   405  
   406  	res, err := ep.regeneratePolicy(s.stats)
   407  	require.Nil(t, err)
   408  	err = ep.setDesiredPolicy(res)
   409  	require.Nil(t, err)
   410  
   411  	expected := policy.NewMapState(map[policy.Key]policy.MapStateEntry{
   412  		mapKeyAllowAllE: {
   413  			DerivedFromRules: labels.LabelArrayList{AllowAnyEgressLabels},
   414  		},
   415  		mapKeyFoo: {
   416  			IsDeny:           true,
   417  			DerivedFromRules: labels.LabelArrayList{lblsL3DenyFoo},
   418  		},
   419  	})
   420  	if !ep.desiredPolicy.GetPolicyMap().Equals(expected) {
   421  		t.Fatal("desired policy map does not equal expected map:\n",
   422  			ep.desiredPolicy.GetPolicyMap().Diff(expected))
   423  	}
   424  
   425  	ctx, cancel := context.WithCancel(context.Background())
   426  	defer cancel()
   427  	cmp := completion.NewWaitGroup(ctx)
   428  
   429  	realizedRedirects := ep.GetRealizedRedirects()
   430  	desiredRedirects, err, finalizeFunc, revertFunc := ep.addNewRedirects(cmp)
   431  	require.Nil(t, err)
   432  	finalizeFunc()
   433  
   434  	// Redirect is still created, even if all MapState entries may have been overridden by a
   435  	// deny entry.  A new FQDN redirect may have no MapState entries as the associated CIDR
   436  	// identities may match no numeric IDs yet, so we can not count the number of added MapState
   437  	// entries and make any conclusions from it.
   438  	require.Equal(t, 1, len(desiredRedirects))
   439  
   440  	expected2 := policy.NewMapState(map[policy.Key]policy.MapStateEntry{
   441  		mapKeyAllowAllE: {
   442  			DerivedFromRules: labels.LabelArrayList{AllowAnyEgressLabels},
   443  		},
   444  		mapKeyAllL7: {
   445  			IsDeny:           false,
   446  			ProxyPort:        httpPort,
   447  			DerivedFromRules: labels.LabelArrayList{lblsL4L7Allow},
   448  		},
   449  		mapKeyFoo: {
   450  			IsDeny:           true,
   451  			DerivedFromRules: labels.LabelArrayList{lblsL3DenyFoo},
   452  		},
   453  		mapKeyFooL7: {
   454  			IsDeny:           true,
   455  			DerivedFromRules: labels.LabelArrayList{lblsL3DenyFoo},
   456  		},
   457  	})
   458  
   459  	// Redirect for the HTTP port should have been added, but there should be a deny for Foo on
   460  	// that port, as it is shadowed by the deny rule
   461  	if !ep.desiredPolicy.GetPolicyMap().Equals(expected2) {
   462  		t.Fatal("desired policy map does not equal expected map:\n",
   463  			ep.desiredPolicy.GetPolicyMap().Diff(expected2))
   464  	}
   465  
   466  	// Keep only desired redirects
   467  	ep.removeOldRedirects(desiredRedirects, realizedRedirects, cmp)
   468  
   469  	// Check that the redirect is still realized
   470  	require.Equal(t, 1, len(desiredRedirects))
   471  	require.Equal(t, 4, ep.desiredPolicy.GetPolicyMap().Len())
   472  
   473  	// Pretend that something failed and revert the changes
   474  	revertFunc()
   475  
   476  	// Check that the state before addRedirects is restored
   477  	if !ep.desiredPolicy.GetPolicyMap().Equals(expected) {
   478  		t.Fatal("desired policy map does not equal expected map:\n",
   479  			ep.desiredPolicy.GetPolicyMap().Diff(expected))
   480  	}
   481  	require.Equal(t, 2, ep.desiredPolicy.GetPolicyMap().Len())
   482  }
   483  
   484  var (
   485  	allowListener1Port80 = []api.PortRule{{
   486  		Ports: []api.PortProtocol{
   487  			{Port: "80", Protocol: api.ProtoTCP},
   488  		},
   489  		Listener: &api.Listener{
   490  			EnvoyConfig: &api.EnvoyConfig{
   491  				Name: "cec1",
   492  			},
   493  			Name: "listener1",
   494  		},
   495  	}}
   496  	allowListener2Port80Priority1 = []api.PortRule{{
   497  		Ports: []api.PortProtocol{
   498  			{Port: "80", Protocol: api.ProtoTCP},
   499  		},
   500  		Listener: &api.Listener{
   501  			EnvoyConfig: &api.EnvoyConfig{
   502  				Name: "cec2",
   503  			},
   504  			Name:     "listener2",
   505  			Priority: 1,
   506  		},
   507  	}}
   508  	allowListener1Port80Priority1 = []api.PortRule{{
   509  		Ports: []api.PortProtocol{
   510  			{Port: "80", Protocol: api.ProtoTCP},
   511  		},
   512  		Listener: &api.Listener{
   513  			EnvoyConfig: &api.EnvoyConfig{
   514  				Name: "cec1",
   515  			},
   516  			Name:     "listener1",
   517  			Priority: 1,
   518  		},
   519  	}}
   520  	lblsL4AllowListener1 = labels.ParseLabelArray("foo-l4l7-allow-listener1")
   521  	ruleL4AllowListener1 = api.NewRule().
   522  				WithLabels(lblsL4AllowListener1).
   523  				WithIngressRules([]api.IngressRule{{
   524  			IngressCommonRule: api.IngressCommonRule{
   525  				FromEndpoints: []api.EndpointSelector{selectFoo_},
   526  			},
   527  			ToPorts: allowListener1Port80,
   528  		}})
   529  	lblsL4L7AllowListener1Priority1 = labels.ParseLabelArray("foo-l4l7-allow-listener1-priority1")
   530  	ruleL4L7AllowListener1Priority1 = api.NewRule().
   531  					WithLabels(lblsL4L7AllowListener1Priority1).
   532  					WithIngressRules([]api.IngressRule{{
   533  			IngressCommonRule: api.IngressCommonRule{
   534  				FromEndpoints: []api.EndpointSelector{selectFoo_},
   535  			},
   536  			ToPorts: allowListener1Port80Priority1,
   537  		}})
   538  	lblsL4AllowPort80 = labels.ParseLabelArray("l4-allow-port80")
   539  	ruleL4AllowPort80 = api.NewRule().
   540  				WithLabels(lblsL4AllowPort80).
   541  				WithIngressRules([]api.IngressRule{{
   542  			IngressCommonRule: api.IngressCommonRule{},
   543  			ToPorts:           allowPort80,
   544  		}})
   545  	lblsL4L7AllowListener2Priority1 = labels.ParseLabelArray("red-l4l7-allow-listener2-priority1")
   546  	ruleL4L7AllowListener2Priority1 = api.NewRule().
   547  					WithLabels(lblsL4L7AllowListener2Priority1).
   548  					WithIngressRules([]api.IngressRule{{
   549  			IngressCommonRule: api.IngressCommonRule{
   550  				FromEndpoints: []api.EndpointSelector{selectRed_},
   551  			},
   552  			ToPorts: allowListener2Port80Priority1,
   553  		}})
   554  )
   555  
   556  func TestRedirectWithPriority(t *testing.T) {
   557  	s := setupRedirectSuite(t)
   558  
   559  	ep := s.NewTestEndpoint(t)
   560  	api.TestAllowIngressListener = true
   561  	defer func() { api.TestAllowIngressListener = false }()
   562  
   563  	s.AddRules(api.Rules{
   564  		ruleL4AllowListener1.WithEndpointSelector(selectBar_),
   565  		ruleL4AllowPort80.WithEndpointSelector(selectBar_),
   566  		ruleL4L7AllowListener2Priority1.WithEndpointSelector(selectBar_),
   567  	})
   568  
   569  	res, err := ep.regeneratePolicy(s.stats)
   570  	require.Nil(t, err)
   571  	err = ep.setDesiredPolicy(res)
   572  	require.Nil(t, err)
   573  
   574  	expected := policy.NewMapState(map[policy.Key]policy.MapStateEntry{
   575  		mapKeyAllowAllE: {
   576  			DerivedFromRules: labels.LabelArrayList{AllowAnyEgressLabels},
   577  		},
   578  		mapKeyAllL7: {
   579  			DerivedFromRules: labels.LabelArrayList{lblsL4AllowListener1, lblsL4AllowPort80, lblsL4L7AllowListener2Priority1},
   580  		},
   581  	})
   582  	if !ep.desiredPolicy.GetPolicyMap().Equals(expected) {
   583  		t.Fatal("desired policy map does not equal expected map:\n",
   584  			ep.desiredPolicy.GetPolicyMap().Diff(expected))
   585  	}
   586  
   587  	ctx, cancel := context.WithCancel(context.Background())
   588  	defer cancel()
   589  	cmp := completion.NewWaitGroup(ctx)
   590  
   591  	realizedRedirects := ep.GetRealizedRedirects()
   592  	desiredRedirects, err, finalizeFunc, revertFunc := ep.addNewRedirects(cmp)
   593  	require.Nil(t, err)
   594  	finalizeFunc()
   595  
   596  	// Check that all redirects have been created.
   597  	require.Equal(t, crd2Port, desiredRedirects["12345:ingress:TCP:80:/cec2/listener2"])
   598  	require.Equal(t, crd1Port, desiredRedirects["12345:ingress:TCP:80:/cec1/listener1"])
   599  	require.Equal(t, 2, len(desiredRedirects))
   600  
   601  	expected2 := policy.NewMapState(map[policy.Key]policy.MapStateEntry{
   602  		mapKeyAllowAllE: {
   603  			DerivedFromRules: labels.LabelArrayList{AllowAnyEgressLabels},
   604  		},
   605  		mapKeyFooL7: {
   606  			ProxyPort:        crd2Port,
   607  			Listener:         "/cec2/listener2",
   608  			DerivedFromRules: labels.LabelArrayList{lblsL4AllowListener1, lblsL4AllowPort80, lblsL4L7AllowListener2Priority1},
   609  		},
   610  		mapKeyAllL7: {
   611  			DerivedFromRules: labels.LabelArrayList{lblsL4AllowListener1, lblsL4AllowPort80, lblsL4L7AllowListener2Priority1},
   612  		},
   613  	})
   614  	if !ep.desiredPolicy.GetPolicyMap().Equals(expected2) {
   615  		t.Fatal("desired policy map does not equal expected map:\n",
   616  			ep.desiredPolicy.GetPolicyMap().Diff(expected2))
   617  	}
   618  
   619  	// Keep only desired redirects
   620  	ep.removeOldRedirects(desiredRedirects, realizedRedirects, cmp)
   621  
   622  	// Check that the redirect is still realized
   623  	require.Equal(t, 2, len(desiredRedirects))
   624  	require.Equal(t, 3, ep.desiredPolicy.GetPolicyMap().Len())
   625  
   626  	// Pretend that something failed and revert the changes
   627  	revertFunc()
   628  
   629  	// Check that the state before addRedirects is restored
   630  	if !ep.desiredPolicy.GetPolicyMap().Equals(expected) {
   631  		t.Fatal("desired policy map does not equal expected map:\n",
   632  			ep.desiredPolicy.GetPolicyMap().Diff(expected))
   633  	}
   634  	require.Equal(t, 2, ep.desiredPolicy.GetPolicyMap().Len())
   635  }
   636  
   637  func TestRedirectWithEqualPriority(t *testing.T) {
   638  	s := setupRedirectSuite(t)
   639  
   640  	ep := s.NewTestEndpoint(t)
   641  
   642  	api.TestAllowIngressListener = true
   643  	defer func() { api.TestAllowIngressListener = false }()
   644  	s.AddRules(api.Rules{
   645  		ruleL4L7AllowListener1Priority1.WithEndpointSelector(selectBar_),
   646  		ruleL4AllowPort80.WithEndpointSelector(selectBar_),
   647  		ruleL4L7AllowListener2Priority1.WithEndpointSelector(selectBar_),
   648  	})
   649  
   650  	res, err := ep.regeneratePolicy(s.stats)
   651  	require.Nil(t, err)
   652  	err = ep.setDesiredPolicy(res)
   653  	require.Nil(t, err)
   654  
   655  	expected := policy.NewMapState(map[policy.Key]policy.MapStateEntry{
   656  		mapKeyAllowAllE: {
   657  			DerivedFromRules: labels.LabelArrayList{AllowAnyEgressLabels},
   658  		},
   659  		mapKeyAllL7: {
   660  			DerivedFromRules: labels.LabelArrayList{lblsL4L7AllowListener1Priority1, lblsL4AllowPort80, lblsL4L7AllowListener2Priority1},
   661  		},
   662  	})
   663  	if !ep.desiredPolicy.GetPolicyMap().Equals(expected) {
   664  		t.Fatal("desired policy map does not equal expected map:\n",
   665  			ep.desiredPolicy.GetPolicyMap().Diff(expected))
   666  	}
   667  
   668  	ctx, cancel := context.WithCancel(context.Background())
   669  	defer cancel()
   670  	cmp := completion.NewWaitGroup(ctx)
   671  
   672  	realizedRedirects := ep.GetRealizedRedirects()
   673  	desiredRedirects, err, finalizeFunc, revertFunc := ep.addNewRedirects(cmp)
   674  	require.Nil(t, err)
   675  	finalizeFunc()
   676  
   677  	// Check that all redirects have been created.
   678  	require.Equal(t, crd2Port, desiredRedirects["12345:ingress:TCP:80:/cec2/listener2"])
   679  	require.Equal(t, crd1Port, desiredRedirects["12345:ingress:TCP:80:/cec1/listener1"])
   680  	require.Equal(t, 2, len(desiredRedirects))
   681  
   682  	expected2 := policy.NewMapState(map[policy.Key]policy.MapStateEntry{
   683  		mapKeyAllowAllE: {
   684  			DerivedFromRules: labels.LabelArrayList{AllowAnyEgressLabels},
   685  		},
   686  		mapKeyFooL7: {
   687  			ProxyPort:        crd1Port,
   688  			Listener:         "/cec1/listener1",
   689  			DerivedFromRules: labels.LabelArrayList{lblsL4L7AllowListener1Priority1, lblsL4AllowPort80, lblsL4L7AllowListener2Priority1},
   690  		},
   691  		mapKeyAllL7: {
   692  			DerivedFromRules: labels.LabelArrayList{lblsL4L7AllowListener1Priority1, lblsL4AllowPort80, lblsL4L7AllowListener2Priority1},
   693  		},
   694  	})
   695  	if !ep.desiredPolicy.GetPolicyMap().Equals(expected2) {
   696  		t.Fatal("desired policy map does not equal expected map:\n",
   697  			ep.desiredPolicy.GetPolicyMap().Diff(expected2))
   698  	}
   699  
   700  	// Keep only desired redirects
   701  	ep.removeOldRedirects(desiredRedirects, realizedRedirects, cmp)
   702  
   703  	// Check that the redirect is still realized
   704  	require.Equal(t, 2, len(desiredRedirects))
   705  	require.Equal(t, 3, ep.desiredPolicy.GetPolicyMap().Len())
   706  
   707  	// Pretend that something failed and revert the changes
   708  	revertFunc()
   709  
   710  	// Check that the state before addRedirects is restored
   711  	if !ep.desiredPolicy.GetPolicyMap().Equals(expected) {
   712  		t.Fatal("desired policy map does not equal expected map:\n",
   713  			ep.desiredPolicy.GetPolicyMap().Diff(expected))
   714  	}
   715  	require.Equal(t, 2, ep.desiredPolicy.GetPolicyMap().Len())
   716  }