github.com/imran-kn/cilium-fork@v1.6.9/pkg/policy/selectorcache_test.go (about)

     1  // Copyright 2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build !privileged_tests
    16  
    17  package policy
    18  
    19  import (
    20  	"github.com/cilium/cilium/pkg/checker"
    21  	"github.com/cilium/cilium/pkg/identity"
    22  	"github.com/cilium/cilium/pkg/identity/cache"
    23  	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
    24  	"github.com/cilium/cilium/pkg/labels"
    25  	"github.com/cilium/cilium/pkg/policy/api"
    26  	"github.com/cilium/cilium/pkg/testutils"
    27  
    28  	. "gopkg.in/check.v1"
    29  )
    30  
    31  type SelectorCacheTestSuite struct{}
    32  
    33  var _ = Suite(&SelectorCacheTestSuite{})
    34  
    35  type DummySelectorCacheUser struct{}
    36  
    37  func (d *DummySelectorCacheUser) IdentitySelectionUpdated(selector CachedSelector, selections, added, deleted []identity.NumericIdentity) {
    38  }
    39  
    40  type cachedSelectionUser struct {
    41  	c             *C
    42  	sc            *SelectorCache
    43  	name          string
    44  	selections    map[CachedSelector][]identity.NumericIdentity
    45  	notifications int
    46  	adds          int
    47  	deletes       int
    48  }
    49  
    50  func newUser(c *C, name string, sc *SelectorCache) *cachedSelectionUser {
    51  	return &cachedSelectionUser{
    52  		c:          c,
    53  		sc:         sc,
    54  		name:       name,
    55  		selections: make(map[CachedSelector][]identity.NumericIdentity),
    56  	}
    57  }
    58  
    59  func haveNid(nid identity.NumericIdentity, selections []identity.NumericIdentity) bool {
    60  	for i := range selections {
    61  		if selections[i] == nid {
    62  			return true
    63  		}
    64  	}
    65  	return false
    66  }
    67  
    68  func (csu *cachedSelectionUser) AddIdentitySelector(sel api.EndpointSelector) CachedSelector {
    69  	notifications := csu.notifications
    70  	cached, added := csu.sc.AddIdentitySelector(csu, sel)
    71  	csu.c.Assert(cached, Not(Equals), nil)
    72  
    73  	_, exists := csu.selections[cached]
    74  	// Not added if already exists for this user
    75  	csu.c.Assert(added, Equals, !exists)
    76  	csu.selections[cached] = cached.GetSelections()
    77  
    78  	// Pre-existing selections are not notified as updates
    79  	csu.c.Assert(csu.notifications, Equals, notifications)
    80  
    81  	return cached
    82  }
    83  
    84  func (csu *cachedSelectionUser) AddFQDNSelector(sel api.FQDNSelector) CachedSelector {
    85  	notifications := csu.notifications
    86  	cached, added := csu.sc.AddFQDNSelector(csu, sel)
    87  	csu.c.Assert(cached, Not(Equals), nil)
    88  
    89  	_, exists := csu.selections[cached]
    90  	// Not added if already exists for this user
    91  	csu.c.Assert(added, Equals, !exists)
    92  	csu.selections[cached] = cached.GetSelections()
    93  
    94  	// Pre-existing selections are not notified as updates
    95  	csu.c.Assert(csu.notifications, Equals, notifications)
    96  
    97  	return cached
    98  }
    99  
   100  func (csu *cachedSelectionUser) RemoveSelector(sel CachedSelector) {
   101  	notifications := csu.notifications
   102  	csu.sc.RemoveSelector(sel, csu)
   103  	delete(csu.selections, sel)
   104  
   105  	// No notifications for a removed selector
   106  	csu.c.Assert(csu.notifications, Equals, notifications)
   107  }
   108  
   109  func (csu *cachedSelectionUser) IdentitySelectionUpdated(selector CachedSelector, selections, added, deleted []identity.NumericIdentity) {
   110  	csu.notifications++
   111  	csu.adds += len(added)
   112  	csu.deletes += len(deleted)
   113  
   114  	// Validate added & deleted against the selections
   115  	for _, add := range added {
   116  		csu.c.Assert(haveNid(add, selections), Equals, true)
   117  	}
   118  	for _, del := range deleted {
   119  		csu.c.Assert(haveNid(del, selections), Equals, false)
   120  	}
   121  
   122  	// update selections
   123  	csu.selections[selector] = selections
   124  }
   125  
   126  func (ds *SelectorCacheTestSuite) SetUpTest(c *C) {
   127  }
   128  
   129  func (ds *SelectorCacheTestSuite) TearDownTest(c *C) {
   130  }
   131  
   132  func (ds *SelectorCacheTestSuite) TestAddRemoveSelector(c *C) {
   133  	sc := testNewSelectorCache(cache.IdentityCache{})
   134  
   135  	// Add some identities to the identity cache
   136  	sc.UpdateIdentities(cache.IdentityCache{
   137  		1234: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s),
   138  			k8sConst.PodNamespaceLabel: labels.NewLabel(k8sConst.PodNamespaceLabel, "default", labels.LabelSourceK8s)}.LabelArray(),
   139  		2345: labels.Labels{"app": labels.NewLabel("app", "test2", labels.LabelSourceK8s)}.LabelArray(),
   140  	}, nil)
   141  
   142  	testSelector := api.NewESFromLabels(labels.NewLabel("app", "test", labels.LabelSourceK8s),
   143  		labels.NewLabel(k8sConst.PodNamespaceLabel, "default", labels.LabelSourceK8s))
   144  
   145  	user1 := newUser(c, "user1", sc)
   146  	cached := user1.AddIdentitySelector(testSelector)
   147  
   148  	// Current selections contain the numeric identities of existing identities that match
   149  	selections := cached.GetSelections()
   150  	c.Assert(len(selections), Equals, 1)
   151  	c.Assert(selections[0], Equals, identity.NumericIdentity(1234))
   152  
   153  	// Try add the same selector from the same user the second time
   154  	testSelector = api.NewESFromLabels(labels.NewLabel("app", "test", labels.LabelSourceK8s),
   155  		labels.NewLabel(k8sConst.PodNamespaceLabel, "default", labels.LabelSourceK8s))
   156  	cached2 := user1.AddIdentitySelector(testSelector)
   157  	c.Assert(cached2, Equals, cached)
   158  
   159  	// Add the same selector from a different user
   160  	testSelector = api.NewESFromLabels(labels.NewLabel("app", "test", labels.LabelSourceK8s),
   161  		labels.NewLabel(k8sConst.PodNamespaceLabel, "default", labels.LabelSourceK8s))
   162  	user2 := newUser(c, "user2", sc)
   163  	cached3 := user2.AddIdentitySelector(testSelector)
   164  
   165  	// Same old CachedSelector is returned, nothing new is cached
   166  	c.Assert(cached3, Equals, cached)
   167  
   168  	// Removing the first user does not remove the cached selector
   169  	user1.RemoveSelector(cached)
   170  	// Remove is idempotent
   171  	user1.RemoveSelector(cached)
   172  
   173  	// Removing the last user removes the cached selector
   174  	user2.RemoveSelector(cached3)
   175  	// Remove is idempotent
   176  	user2.RemoveSelector(cached3)
   177  
   178  	// All identities removed
   179  	c.Assert(len(sc.selectors), Equals, 0)
   180  }
   181  
   182  func (ds *SelectorCacheTestSuite) TestMultipleIdentitySelectors(c *C) {
   183  	sc := testNewSelectorCache(cache.IdentityCache{})
   184  
   185  	// Add some identities to the identity cache
   186  	sc.UpdateIdentities(cache.IdentityCache{
   187  		1234: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s)}.LabelArray(),
   188  		2345: labels.Labels{"app": labels.NewLabel("app", "test2", labels.LabelSourceK8s)}.LabelArray(),
   189  	}, nil)
   190  
   191  	testSelector := api.NewESFromLabels(labels.NewLabel("app", "test", labels.LabelSourceK8s))
   192  	test2Selector := api.NewESFromLabels(labels.NewLabel("app", "test2", labels.LabelSourceK8s))
   193  
   194  	user1 := newUser(c, "user1", sc)
   195  	cached := user1.AddIdentitySelector(testSelector)
   196  
   197  	// Current selections contain the numeric identities of existing identities that match
   198  	selections := cached.GetSelections()
   199  	c.Assert(len(selections), Equals, 1)
   200  	c.Assert(selections[0], Equals, identity.NumericIdentity(1234))
   201  
   202  	// Add another selector from the same user
   203  	cached2 := user1.AddIdentitySelector(test2Selector)
   204  	c.Assert(cached2, Not(Equals), cached)
   205  
   206  	// Current selections contain the numeric identities of existing identities that match
   207  	selections2 := cached2.GetSelections()
   208  	c.Assert(len(selections2), Equals, 1)
   209  	c.Assert(selections2[0], Equals, identity.NumericIdentity(2345))
   210  
   211  	user1.RemoveSelector(cached)
   212  	user1.RemoveSelector(cached2)
   213  
   214  	// All identities removed
   215  	c.Assert(len(sc.selectors), Equals, 0)
   216  }
   217  
   218  func (ds *SelectorCacheTestSuite) TestIdentityUpdates(c *C) {
   219  	sc := testNewSelectorCache(cache.IdentityCache{})
   220  
   221  	// Add some identities to the identity cache
   222  	sc.UpdateIdentities(cache.IdentityCache{
   223  		1234: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s)}.LabelArray(),
   224  		2345: labels.Labels{"app": labels.NewLabel("app", "test2", labels.LabelSourceK8s)}.LabelArray(),
   225  	}, nil)
   226  
   227  	testSelector := api.NewESFromLabels(labels.NewLabel("app", "test", labels.LabelSourceK8s))
   228  	test2Selector := api.NewESFromLabels(labels.NewLabel("app", "test2", labels.LabelSourceK8s))
   229  
   230  	user1 := newUser(c, "user1", sc)
   231  	cached := user1.AddIdentitySelector(testSelector)
   232  
   233  	// Current selections contain the numeric identities of existing identities that match
   234  	selections := cached.GetSelections()
   235  	c.Assert(len(selections), Equals, 1)
   236  	c.Assert(selections[0], Equals, identity.NumericIdentity(1234))
   237  
   238  	// Add another selector from the same user
   239  	cached2 := user1.AddIdentitySelector(test2Selector)
   240  	c.Assert(cached2, Not(Equals), cached)
   241  
   242  	// Current selections contain the numeric identities of existing identities that match
   243  	selections2 := cached2.GetSelections()
   244  	c.Assert(len(selections2), Equals, 1)
   245  	c.Assert(selections2[0], Equals, identity.NumericIdentity(2345))
   246  
   247  	// Add some identities to the identity cache
   248  	sc.UpdateIdentities(cache.IdentityCache{
   249  		12345: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s)}.LabelArray(),
   250  	}, nil)
   251  	c.Assert(user1.adds, Equals, 1)
   252  	c.Assert(user1.deletes, Equals, 0)
   253  
   254  	// Current selections contain the numeric identities of existing identities that match
   255  	selections = cached.GetSelections()
   256  	c.Assert(len(selections), Equals, 2)
   257  	c.Assert(selections[0], Equals, identity.NumericIdentity(1234))
   258  	c.Assert(selections[1], Equals, identity.NumericIdentity(12345))
   259  
   260  	// Remove some identities from the identity cache
   261  	sc.UpdateIdentities(nil, cache.IdentityCache{
   262  		12345: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s)}.LabelArray(),
   263  	})
   264  	c.Assert(user1.adds, Equals, 1)
   265  	c.Assert(user1.deletes, Equals, 1)
   266  
   267  	// Current selections contain the numeric identities of existing identities that match
   268  	selections = cached.GetSelections()
   269  	c.Assert(len(selections), Equals, 1)
   270  	c.Assert(selections[0], Equals, identity.NumericIdentity(1234))
   271  
   272  	user1.RemoveSelector(cached)
   273  	user1.RemoveSelector(cached2)
   274  
   275  	// All identities removed
   276  	c.Assert(len(sc.selectors), Equals, 0)
   277  }
   278  
   279  func (ds *SelectorCacheTestSuite) TestFQDNSelectorUpdates(c *C) {
   280  	sc := testNewSelectorCache(cache.IdentityCache{})
   281  
   282  	// Add some identities to the identity cache
   283  	googleSel := api.FQDNSelector{MatchName: "google.com"}
   284  	ciliumSel := api.FQDNSelector{MatchName: "cilium.io"}
   285  
   286  	googleIdentities := []identity.NumericIdentity{321, 456, 987}
   287  	ciliumIdentities := []identity.NumericIdentity{123, 456, 789}
   288  
   289  	sc.UpdateFQDNSelector(ciliumSel, ciliumIdentities)
   290  	sc.UpdateFQDNSelector(googleSel, googleIdentities)
   291  
   292  	_, exists := sc.selectors[ciliumSel.String()]
   293  	c.Assert(exists, Equals, true)
   294  
   295  	user1 := newUser(c, "user1", sc)
   296  	cached := user1.AddFQDNSelector(ciliumSel)
   297  
   298  	selections := cached.GetSelections()
   299  	c.Assert(len(selections), Equals, 3)
   300  	for i, selection := range selections {
   301  		c.Assert(selection, Equals, ciliumIdentities[i])
   302  	}
   303  
   304  	// Add another selector from the same user
   305  	cached2 := user1.AddFQDNSelector(googleSel)
   306  	c.Assert(cached2, Not(Equals), cached)
   307  
   308  	// Current selections contain the numeric identities of existing identities that match
   309  	selections2 := cached2.GetSelections()
   310  	c.Assert(len(selections2), Equals, 3)
   311  	for i, selection := range selections2 {
   312  		c.Assert(selection, Equals, googleIdentities[i])
   313  	}
   314  
   315  	// Add some identities to the identity cache
   316  	ciliumIdentities = append(ciliumIdentities, identity.NumericIdentity(123456))
   317  	sc.UpdateFQDNSelector(ciliumSel, ciliumIdentities)
   318  	c.Assert(user1.adds, Equals, 1)
   319  	c.Assert(user1.deletes, Equals, 0)
   320  
   321  	ciliumIdentities = ciliumIdentities[:1]
   322  	sc.UpdateFQDNSelector(ciliumSel, ciliumIdentities)
   323  	c.Assert(user1.adds, Equals, 1)
   324  	c.Assert(user1.deletes, Equals, 3)
   325  
   326  	ciliumIdentities = []identity.NumericIdentity{}
   327  	sc.UpdateFQDNSelector(ciliumSel, ciliumIdentities)
   328  	c.Assert(user1.deletes, Equals, 4)
   329  
   330  	user1.RemoveSelector(cached)
   331  	user1.RemoveSelector(cached2)
   332  
   333  	// All identities removed
   334  	c.Assert(len(sc.selectors), Equals, 0)
   335  
   336  	yahooSel := api.FQDNSelector{MatchName: "yahoo.com"}
   337  	_, added := sc.AddFQDNSelector(user1, yahooSel)
   338  	c.Assert(added, Equals, true)
   339  }
   340  
   341  func (ds *SelectorCacheTestSuite) TestRemoveIdentitiesFQDNSelectors(c *C) {
   342  	sc := testNewSelectorCache(cache.IdentityCache{})
   343  
   344  	// Add some identities to the identity cache
   345  	googleSel := api.FQDNSelector{MatchName: "google.com"}
   346  	ciliumSel := api.FQDNSelector{MatchName: "cilium.io"}
   347  
   348  	googleIdentities := []identity.NumericIdentity{321, 456, 987}
   349  	ciliumIdentities := []identity.NumericIdentity{123, 456, 789}
   350  
   351  	sc.UpdateFQDNSelector(ciliumSel, ciliumIdentities)
   352  	sc.UpdateFQDNSelector(googleSel, googleIdentities)
   353  
   354  	_, exists := sc.selectors[ciliumSel.String()]
   355  	c.Assert(exists, Equals, true)
   356  
   357  	user1 := newUser(c, "user1", sc)
   358  	cached := user1.AddFQDNSelector(ciliumSel)
   359  
   360  	selections := cached.GetSelections()
   361  	c.Assert(len(selections), Equals, 3)
   362  	for i, selection := range selections {
   363  		c.Assert(selection, Equals, ciliumIdentities[i])
   364  	}
   365  
   366  	// Add another selector from the same user
   367  	cached2 := user1.AddFQDNSelector(googleSel)
   368  	c.Assert(cached2, Not(Equals), cached)
   369  
   370  	// Current selections contain the numeric identities of existing identities that match
   371  	selections2 := cached2.GetSelections()
   372  	c.Assert(len(selections2), Equals, 3)
   373  	for i, selection := range selections2 {
   374  		c.Assert(selection, Equals, googleIdentities[i])
   375  	}
   376  
   377  	sc.RemoveIdentitiesFQDNSelectors([]api.FQDNSelector{
   378  		googleSel,
   379  		ciliumSel,
   380  	})
   381  
   382  	selections = cached.GetSelections()
   383  	c.Assert(len(selections), Equals, 0)
   384  
   385  	selections2 = cached2.GetSelections()
   386  	c.Assert(len(selections2), Equals, 0)
   387  }
   388  
   389  func (ds *SelectorCacheTestSuite) TestIdentityUpdatesMultipleUsers(c *C) {
   390  	sc := testNewSelectorCache(cache.IdentityCache{})
   391  
   392  	// Add some identities to the identity cache
   393  	sc.UpdateIdentities(cache.IdentityCache{
   394  		1234: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s)}.LabelArray(),
   395  		2345: labels.Labels{"app": labels.NewLabel("app", "test2", labels.LabelSourceK8s)}.LabelArray(),
   396  	}, nil)
   397  
   398  	testSelector := api.NewESFromLabels(labels.NewLabel("app", "test", labels.LabelSourceK8s))
   399  
   400  	user1 := newUser(c, "user1", sc)
   401  	cached := user1.AddIdentitySelector(testSelector)
   402  
   403  	// Add same selector from a different user
   404  	user2 := newUser(c, "user2", sc)
   405  	cached2 := user2.AddIdentitySelector(testSelector)
   406  	c.Assert(cached2, Equals, cached)
   407  
   408  	// Add some identities to the identity cache
   409  	sc.UpdateIdentities(cache.IdentityCache{
   410  		123: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s)}.LabelArray(),
   411  		234: labels.Labels{"app": labels.NewLabel("app", "test2", labels.LabelSourceK8s)}.LabelArray(),
   412  		345: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s)}.LabelArray(),
   413  	}, nil)
   414  	c.Assert(user1.adds, Equals, 2)
   415  	c.Assert(user1.deletes, Equals, 0)
   416  	c.Assert(user2.adds, Equals, 2)
   417  	c.Assert(user2.deletes, Equals, 0)
   418  
   419  	// Current selections contain the numeric identities of existing identities that match
   420  	selections := cached.GetSelections()
   421  	c.Assert(len(selections), Equals, 3)
   422  	c.Assert(selections[0], Equals, identity.NumericIdentity(123))
   423  	c.Assert(selections[1], Equals, identity.NumericIdentity(345))
   424  	c.Assert(selections[2], Equals, identity.NumericIdentity(1234))
   425  
   426  	c.Assert(cached.GetSelections(), checker.DeepEquals, cached2.GetSelections())
   427  
   428  	// Remove some identities from the identity cache
   429  	sc.UpdateIdentities(nil, cache.IdentityCache{
   430  		123: labels.Labels{"app": labels.NewLabel("app", "test", labels.LabelSourceK8s)}.LabelArray(),
   431  		234: labels.Labels{"app": labels.NewLabel("app", "test2", labels.LabelSourceK8s)}.LabelArray(),
   432  	})
   433  	c.Assert(user1.adds, Equals, 2)
   434  	c.Assert(user1.deletes, Equals, 1)
   435  	c.Assert(user2.adds, Equals, 2)
   436  	c.Assert(user2.deletes, Equals, 1)
   437  
   438  	// Current selections contain the numeric identities of existing identities that match
   439  	selections = cached.GetSelections()
   440  	c.Assert(len(selections), Equals, 2)
   441  	c.Assert(selections[0], Equals, identity.NumericIdentity(345))
   442  	c.Assert(selections[1], Equals, identity.NumericIdentity(1234))
   443  
   444  	c.Assert(cached.GetSelections(), checker.DeepEquals, cached2.GetSelections())
   445  
   446  	user1.RemoveSelector(cached)
   447  	user2.RemoveSelector(cached2)
   448  
   449  	// All identities removed
   450  	c.Assert(len(sc.selectors), Equals, 0)
   451  }
   452  
   453  func (ds *SelectorCacheTestSuite) TestIdentityNotifier(c *C) {
   454  	sc := testNewSelectorCache(cache.IdentityCache{})
   455  	idNotifier, ok := sc.localIdentityNotifier.(*testutils.DummyIdentityNotifier)
   456  	c.Assert(ok, Equals, true)
   457  	c.Assert(idNotifier, Not(IsNil))
   458  
   459  	// Add some identities to the identity cache
   460  	googleSel := api.FQDNSelector{MatchName: "google.com"}
   461  	ciliumSel := api.FQDNSelector{MatchName: "cilium.io"}
   462  
   463  	// Nothing should be registered yet.
   464  	c.Assert(idNotifier.IsRegistered(ciliumSel), Equals, false)
   465  	c.Assert(idNotifier.IsRegistered(googleSel), Equals, false)
   466  
   467  	injectedIDs := []identity.NumericIdentity{1000, 1001, 1002}
   468  	idNotifier.InjectIdentitiesForSelector(ciliumSel, injectedIDs)
   469  
   470  	// Add a user without adding identities explicitly. The identityNotifier
   471  	// should have populated them for us.
   472  	user1 := newUser(c, "user1", sc)
   473  	cached := user1.AddFQDNSelector(ciliumSel)
   474  	_, exists := sc.selectors[ciliumSel.String()]
   475  	c.Assert(exists, Equals, true)
   476  
   477  	selections := cached.GetSelections()
   478  	c.Assert(len(selections), Equals, 3)
   479  	for i, selection := range selections {
   480  		c.Assert(selection, Equals, injectedIDs[i])
   481  	}
   482  
   483  	// Add another selector from the same user
   484  	cached2 := user1.AddFQDNSelector(googleSel)
   485  	c.Assert(cached2, Not(Equals), cached)
   486  
   487  	selections2 := cached2.GetSelections()
   488  	c.Assert(len(selections2), Equals, 0)
   489  
   490  	sc.RemoveIdentitiesFQDNSelectors([]api.FQDNSelector{
   491  		googleSel,
   492  		ciliumSel,
   493  	})
   494  
   495  	selections = cached.GetSelections()
   496  	c.Assert(len(selections), Equals, 0)
   497  
   498  	selections2 = cached2.GetSelections()
   499  	c.Assert(len(selections2), Equals, 0)
   500  
   501  	sc.RemoveSelector(cached, user1)
   502  	c.Assert(idNotifier.IsRegistered(ciliumSel), Equals, false)
   503  	c.Assert(idNotifier.IsRegistered(googleSel), Equals, true)
   504  
   505  	sc.RemoveSelector(cached2, user1)
   506  	c.Assert(idNotifier.IsRegistered(googleSel), Equals, false)
   507  
   508  }
   509  
   510  func testNewSelectorCache(ids cache.IdentityCache) *SelectorCache {
   511  	sc := NewSelectorCache(ids)
   512  	sc.SetLocalIdentityNotifier(testutils.NewDummyIdentityNotifier())
   513  	return sc
   514  }