github.com/imran-kn/cilium-fork@v1.6.9/pkg/policy/distillery_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 "bytes" 21 "fmt" 22 "io" 23 "testing" 24 25 "github.com/cilium/cilium/pkg/checker" 26 "github.com/cilium/cilium/pkg/identity" 27 "github.com/cilium/cilium/pkg/identity/cache" 28 "github.com/cilium/cilium/pkg/labels" 29 "github.com/cilium/cilium/pkg/policy/api" 30 "github.com/cilium/cilium/pkg/policy/trafficdirection" 31 "github.com/cilium/cilium/pkg/testutils" 32 33 logging "github.com/op/go-logging" 34 . "gopkg.in/check.v1" 35 ) 36 37 // 38 // Distillery unit tests 39 // 40 41 type DistilleryTestSuite struct{} 42 43 var ( 44 _ = Suite(&DistilleryTestSuite{}) 45 46 ep1 = newTestEP() 47 ep2 = newTestEP() 48 ) 49 50 // testEP wraps the testutils endpoint implementation to provide 51 // LookupRedirectPort() until tproxy support makes this redundant. 52 // This avoids import cycles when adding policy to the imports in testutils. 53 type testEP struct { 54 testutils.TestEndpoint 55 } 56 57 func newTestEP() *testEP { 58 return &testEP{ 59 testutils.NewTestEndpoint(), 60 } 61 } 62 63 func (ep *testEP) WithIdentity(id int64) *testEP { 64 ep.SetIdentity(id, true) 65 return ep 66 } 67 68 func (ep *testEP) LookupRedirectPort(l4 *L4Filter) uint16 { 69 return 42 70 } 71 72 func (s *DistilleryTestSuite) TestCacheManagement(c *C) { 73 repo := NewPolicyRepository() 74 cache := repo.policyCache 75 identity := ep1.GetSecurityIdentity() 76 c.Assert(ep2.GetSecurityIdentity(), Equals, identity) 77 78 // Nonsense delete of entry that isn't yet inserted 79 deleted := cache.delete(identity) 80 c.Assert(deleted, Equals, false) 81 82 // Insert identity twice. Should be the same policy. 83 policy1, _ := cache.insert(identity) 84 policy2, _ := cache.insert(identity) 85 c.Assert(policy1, Equals, policy2) 86 87 // Despite two insert calls, there is no reference tracking; any delete 88 // will clear the cache. 89 cacheCleared := cache.delete(identity) 90 c.Assert(cacheCleared, Equals, true) 91 cacheCleared = cache.delete(identity) 92 c.Assert(cacheCleared, Equals, false) 93 94 // Insert two distinct identities, then delete one. Other should still 95 // be there. 96 ep3 := newTestEP().WithIdentity(1234) 97 identity3 := ep3.GetSecurityIdentity() 98 c.Assert(identity3, Not(Equals), identity) 99 policy1, _ = cache.insert(identity) 100 policy3, _ := cache.insert(identity3) 101 c.Assert(policy1, Not(Equals), policy3) 102 _ = cache.delete(identity) 103 policy3, _ = cache.lookupOrCreate(identity3, false) 104 c.Assert(policy3, NotNil) 105 } 106 107 func (s *DistilleryTestSuite) TestCachePopulation(c *C) { 108 repo := NewPolicyRepository() 109 repo.revision = 42 110 cache := repo.policyCache 111 112 identity1 := ep1.GetSecurityIdentity() 113 c.Assert(ep2.GetSecurityIdentity(), Equals, identity1) 114 policy1, computed := cache.insert(identity1) 115 c.Assert(computed, Equals, false) 116 117 // Calculate the policy and observe that it's cached 118 updated, err := cache.updateSelectorPolicy(identity1) 119 c.Assert(err, IsNil) 120 c.Assert(updated, Equals, true) 121 updated, err = cache.updateSelectorPolicy(identity1) 122 c.Assert(err, IsNil) 123 c.Assert(updated, Equals, false) 124 policy2, computed := cache.insert(identity1) 125 c.Assert(computed, Equals, true) 126 idp1 := policy1.(*cachedSelectorPolicy).getPolicy() 127 idp2 := policy2.(*cachedSelectorPolicy).getPolicy() 128 c.Assert(idp1, Equals, idp2) 129 130 // Remove the identity and observe that it is no longer available 131 cacheCleared := cache.delete(identity1) 132 c.Assert(cacheCleared, Equals, true) 133 updated, err = cache.updateSelectorPolicy(identity1) 134 c.Assert(err, NotNil) 135 136 // Attempt to update policy for non-cached endpoint and observe failure 137 ep3 := newTestEP().WithIdentity(1234) 138 _, err = cache.updateSelectorPolicy(ep3.GetSecurityIdentity()) 139 c.Assert(err, NotNil) 140 c.Assert(updated, Equals, false) 141 142 // Insert endpoint with different identity and observe that the cache 143 // is different from ep1, ep2 144 policy1, computed = cache.insert(identity1) 145 c.Assert(computed, Equals, false) 146 idp1 = policy1.(*cachedSelectorPolicy).getPolicy() 147 c.Assert(idp1, NotNil) 148 identity3 := ep3.GetSecurityIdentity() 149 policy3, computed := cache.insert(identity3) 150 c.Assert(policy3, Not(Equals), policy1) 151 c.Assert(computed, Equals, false) 152 updated, err = cache.updateSelectorPolicy(identity3) 153 c.Assert(err, IsNil) 154 c.Assert(updated, Equals, true) 155 idp3 := policy3.(*cachedSelectorPolicy).getPolicy() 156 c.Assert(idp3, Not(Equals), idp1) 157 158 // If there's an error during policy resolution, update should fail 159 //repo.err = fmt.Errorf("not implemented!") 160 //repo.revision++ 161 //_, err = cache.updateSelectorPolicy(identity3) 162 //c.Assert(err, NotNil) 163 } 164 165 // 166 // Distillery integration tests 167 // 168 169 var ( 170 // Identity, labels, selectors for an endpoint named "foo" 171 identityFoo = uint32(100) 172 labelsFoo = labels.ParseSelectLabelArray("foo", "red") 173 selectFoo_ = api.NewESFromLabels(labels.ParseSelectLabel("foo")) 174 allowFooL3_ = selectFoo_ 175 176 // Identity, labels, selectors for an endpoint named "bar" 177 identityBar = uint32(200) 178 labelsBar = labels.ParseSelectLabelArray("bar", "blue") 179 selectBar_ = api.NewESFromLabels(labels.ParseSelectLabel("bar")) 180 allowBarL3_ = selectBar_ 181 182 // API rule sections for composability 183 // L4 rule sections 184 allowAllL4_ []api.PortRule 185 allowPort80 = []api.PortRule{{ 186 Ports: []api.PortProtocol{ 187 {Port: "80", Protocol: api.ProtoTCP}, 188 }, 189 }} 190 // L7 rule sections 191 allowHTTPRoot = &api.L7Rules{ 192 HTTP: []api.PortRuleHTTP{ 193 {Method: "GET", Path: "/"}, 194 }, 195 L7Proto: ParserTypeHTTP.String(), 196 } 197 // API rule definitions for default-deny, L3, L3L4, L3L4L7, L4, L4L7 198 rule____NoAllow = api.NewRule(). 199 WithIngressRules([]api.IngressRule{{}}) 200 ruleL3____Allow = api.NewRule(). 201 WithIngressRules([]api.IngressRule{{ 202 FromEndpoints: []api.EndpointSelector{allowFooL3_}, 203 ToPorts: allowAllL4_, 204 }}) 205 ruleL3L4__Allow = api.NewRule(). 206 WithIngressRules([]api.IngressRule{{ 207 FromEndpoints: []api.EndpointSelector{allowFooL3_}, 208 ToPorts: allowPort80, 209 }}) 210 ruleL3L4L7Allow = api.NewRule(). 211 WithIngressRules([]api.IngressRule{{ 212 FromEndpoints: []api.EndpointSelector{allowFooL3_}, 213 ToPorts: combineL4L7(allowPort80, allowHTTPRoot), 214 }}) 215 rule__L4__Allow = api.NewRule(). 216 WithIngressRules([]api.IngressRule{{ 217 ToPorts: allowPort80, 218 }}) 219 rule__L4L7Allow = api.NewRule(). 220 WithIngressRules([]api.IngressRule{{ 221 ToPorts: combineL4L7(allowPort80, allowHTTPRoot), 222 }}) 223 224 rule__L3AllowFoo = api.NewRule(). 225 WithIngressRules([]api.IngressRule{{ 226 FromEndpoints: []api.EndpointSelector{allowFooL3_}, 227 }}) 228 229 rule__L3AllowBar = api.NewRule(). 230 WithIngressRules([]api.IngressRule{{ 231 FromEndpoints: []api.EndpointSelector{allowBarL3_}, 232 }}) 233 rule____AllowAll = api.NewRule(). 234 WithIngressRules([]api.IngressRule{{ 235 FromEndpoints: []api.EndpointSelector{api.WildcardEndpointSelector}, 236 }}) 237 238 // Misc other bpf key fields for convenience / readability. 239 l7RedirectNone_ = uint16(0) 240 l7RedirectProxy = uint16(1) 241 dirIngress = trafficdirection.Ingress.Uint8() 242 // Desired map keys for L3, L3-dependent L4, L4 243 mapKeyAllowFoo__ = Key{identityFoo, 0, 0, dirIngress} 244 mapKeyAllowBar__ = Key{identityBar, 0, 0, dirIngress} 245 mapKeyAllowFooL4 = Key{identityFoo, 80, 6, dirIngress} 246 mapKeyAllow___L4 = Key{0, 80, 6, dirIngress} 247 mapKeyAllowAll__ = Key{0, 0, 0, dirIngress} 248 // Desired map entries for no L7 redirect / redirect to Proxy 249 mapEntryL7None_ = MapStateEntry{l7RedirectNone_} 250 mapEntryL7Proxy = MapStateEntry{l7RedirectProxy} 251 ) 252 253 // combineL4L7 returns a new PortRule that refers to the specified l4 ports and 254 // l7 rules. 255 func combineL4L7(l4 []api.PortRule, l7 *api.L7Rules) []api.PortRule { 256 result := make([]api.PortRule, len(l4)) 257 for _, pr := range l4 { 258 result = append(result, api.PortRule{ 259 Ports: pr.Ports, 260 Rules: l7, 261 }) 262 } 263 return result 264 } 265 266 // policyDistillery is a convenience wrapper around the existing policy engine, 267 // allowing simple direct evaluation of L3 and L4 state into "MapState". 268 type policyDistillery struct { 269 *Repository 270 log io.Writer 271 } 272 273 func newPolicyDistillery(selectorCache *SelectorCache) *policyDistillery { 274 ret := &policyDistillery{ 275 Repository: NewPolicyRepository(), 276 } 277 ret.selectorCache = selectorCache 278 return ret 279 } 280 281 func (d *policyDistillery) WithLogBuffer(w io.Writer) *policyDistillery { 282 return &policyDistillery{ 283 Repository: d.Repository, 284 log: w, 285 } 286 } 287 288 // distillPolicy distills the policy repository into a set of bpf map state 289 // entries for an endpoint with the specified labels. 290 func (d *policyDistillery) distillPolicy(epLabels labels.LabelArray) (MapState, error) { 291 result := make(MapState) 292 293 endpointSelected, _ := d.Repository.GetRulesMatching(epLabels) 294 io.WriteString(d.log, fmt.Sprintf("[distill] Endpoint selected by policy: %t\n", endpointSelected)) 295 if !endpointSelected { 296 allowAllIngress := true 297 allowAllEgress := false // Skip egress 298 result.AllowAllIdentities(allowAllIngress, allowAllEgress) 299 return result, nil 300 } 301 302 // Prepare the L4 policy so we know whether L4 policy may apply 303 ingressL4 := SearchContext{ 304 To: epLabels, 305 Trace: TRACE_VERBOSE, 306 } 307 ingressL4.Logging = logging.NewLogBackend(d.log, "", 0) 308 io.WriteString(d.log, fmt.Sprintf("[distill] Evaluating L4 -> %s", epLabels)) 309 l4IngressPolicy, err := d.Repository.ResolveL4IngressPolicy(&ingressL4) 310 if err != nil { 311 return nil, err 312 } 313 314 // Handle L4 ingress from each identity in the cache to the endpoint. 315 io.WriteString(d.log, "[distill] Producing L4 filter keys\n") 316 for _, l4 := range l4IngressPolicy { 317 io.WriteString(d.log, fmt.Sprintf("[distill] Processing L4Filter (l3: %+v), (l4: %d/%s), (l7: %+v)\n", l4.CachedSelectors, l4.Port, l4.Protocol, l4.L7RulesPerEp)) 318 for _, key := range l4.ToKeys(0) { 319 io.WriteString(d.log, fmt.Sprintf("[distill] L4 ingress allow %+v (parser=%s, redirect=%t)\n", key, l4.L7Parser, l4.IsRedirect())) 320 if l4.IsRedirect() { 321 result[key] = MapStateEntry{l7RedirectProxy} 322 } else { 323 result[key] = MapStateEntry{l7RedirectNone_} 324 } 325 } 326 } 327 l4IngressPolicy.Detach(d.Repository.GetSelectorCache()) 328 329 // Handle L3-wildcard of L7 destinations 330 // Eg, when you have L4+L7 "allow /public on 80" with L3 "allow all from foo" 331 // Initially, three keys would be generated: L4+L7, L3+L4+L7, and L3. 332 // Here, we remove the L3+L4+L7 key if it overlaps with the L4+L7, 333 // but only if they have the same L7 redirect. 334 // 335 // For these the BPF policy order of attempting L3+L4 lookup, L4 lookup, 336 // then L3 lookup means that three keys are not strictly necessary; the 337 // correct behaviour can be encoded with two keys - an L4 key and L3 key. 338 nKeys := 0 339 l3l4keys := make([]Key, len(result)) 340 for k := range result { 341 if k.Identity > 0 && k.DestPort > 0 { 342 l3l4keys[nKeys] = k 343 nKeys++ 344 } 345 } 346 for i := 0; i < nKeys; i++ { 347 k := l3l4keys[i] 348 io.WriteString(d.log, fmt.Sprintf("[distill] Squashing L3-dependent L4 key %+v\n", k)) 349 wildcardL3 := Key{DestPort: k.DestPort, Nexthdr: k.Nexthdr, TrafficDirection: k.TrafficDirection} 350 wildcardL4 := Key{Identity: k.Identity, TrafficDirection: k.TrafficDirection} 351 if _, ok := result[wildcardL4]; ok { 352 io.WriteString(d.log, fmt.Sprintf("[distill] -> Found L3 overlap %+v\n", wildcardL4)) 353 if entry, ok := result[wildcardL3]; ok { 354 io.WriteString(d.log, fmt.Sprintf("[distill] -> Found L4 overlap %+v:%+v\n", wildcardL3, entry)) 355 if entry.ProxyPort == result[k].ProxyPort { 356 io.WriteString(d.log, fmt.Sprintf("[distill] -> Removing L3-dependent L4 %+v\n", k)) 357 delete(result, k) 358 } 359 } 360 } 361 } 362 363 return result, nil 364 } 365 366 func Test_MergeL3(t *testing.T) { 367 identityCache := cache.IdentityCache{ 368 identity.NumericIdentity(identityFoo): labelsFoo, 369 identity.NumericIdentity(identityBar): labelsBar, 370 } 371 selectorCache := testNewSelectorCache(identityCache) 372 373 tests := []struct { 374 test int 375 rules api.Rules 376 result MapState 377 }{ 378 {0, api.Rules{rule__L3AllowFoo, rule__L3AllowBar}, MapState{mapKeyAllowFoo__: mapEntryL7None_, mapKeyAllowBar__: mapEntryL7None_}}, 379 {1, api.Rules{rule__L3AllowFoo, ruleL3L4__Allow}, MapState{mapKeyAllowFoo__: mapEntryL7None_, mapKeyAllowFooL4: mapEntryL7None_}}, 380 } 381 382 for _, tt := range tests { 383 repo := newPolicyDistillery(selectorCache) 384 for _, r := range tt.rules { 385 if r != nil { 386 rule := r.WithEndpointSelector(selectFoo_) 387 _, _ = repo.AddList(api.Rules{rule}) 388 } 389 } 390 t.Run(fmt.Sprintf("permutation_%d", tt.test), func(t *testing.T) { 391 logBuffer := new(bytes.Buffer) 392 repo = repo.WithLogBuffer(logBuffer) 393 mapstate, err := repo.distillPolicy(labelsFoo) 394 if err != nil { 395 t.Errorf("Policy resolution failure: %s", err) 396 } 397 if equal, err := checker.DeepEqual(mapstate, tt.result); !equal { 398 t.Logf("Rules:\n%s\n\n", tt.rules.String()) 399 t.Logf("Policy Trace: \n%s\n", logBuffer.String()) 400 t.Errorf("Policy obtained didn't match expected for endpoint %s:\n%s", labelsFoo, err) 401 } 402 }) 403 } 404 } 405 406 func Test_MergeRules(t *testing.T) { 407 identityCache := cache.IdentityCache{ 408 identity.NumericIdentity(identityFoo): labelsFoo, 409 } 410 selectorCache := testNewSelectorCache(identityCache) 411 412 tests := []struct { 413 test int 414 rules api.Rules 415 result MapState 416 }{ 417 // The following table is derived from the Google Doc here: 418 // https://docs.google.com/spreadsheets/d/1WANIoZGB48nryylQjjOw6lKjI80eVgPShrdMTMalLEw/edit?usp=sharing 419 // 420 // Rule 0 | Rule 1 | Rule 2 | Rule 3 | Rule 4 | Desired BPF map state 421 {0, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{}}, 422 {1, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFoo__: mapEntryL7None_}}, 423 {2, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7None_}}, 424 {3, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}}, 425 {4, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7None_}}, 426 {5, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}}, 427 {6, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7None_}}, // Differs from spreadsheet(!) 428 {7, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!) 429 {8, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy}}, 430 {9, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, 431 {10, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy}}, 432 {11, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, 433 {12, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7Proxy}}, 434 {13, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!) 435 {14, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7Proxy}}, 436 {15, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!) 437 {16, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy}}, 438 {17, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, 439 // TODO: Tests 22-23 reveal a bug in the redirect logic (GH-7438). 440 //{18, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7None_}}, 441 //{19, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}}, 442 {20, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy}}, 443 {21, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, 444 // TODO: Tests 22-23 reveal a bug in the redirect logic (GH-7438). 445 //{22, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7None_}}, 446 //{23, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}}, 447 {24, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7Proxy}}, 448 {25, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!) 449 {26, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7Proxy}}, 450 {27, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!) 451 {28, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7Proxy}}, 452 {29, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!) 453 {30, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7Proxy}}, 454 {31, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!) 455 } 456 for _, tt := range tests { 457 repo := newPolicyDistillery(selectorCache) 458 for _, r := range tt.rules { 459 if r != nil { 460 rule := r.WithEndpointSelector(selectFoo_) 461 _, _ = repo.AddList(api.Rules{rule}) 462 } 463 } 464 t.Run(fmt.Sprintf("permutation_%d", tt.test), func(t *testing.T) { 465 logBuffer := new(bytes.Buffer) 466 repo = repo.WithLogBuffer(logBuffer) 467 mapstate, err := repo.distillPolicy(labelsFoo) 468 if err != nil { 469 t.Errorf("Policy resolution failure: %s", err) 470 } 471 if equal, err := checker.DeepEqual(mapstate, tt.result); !equal { 472 t.Logf("Rules:\n%s\n\n", tt.rules.String()) 473 t.Logf("Policy Trace: \n%s\n", logBuffer.String()) 474 t.Errorf("Policy obtained didn't match expected for endpoint %s:\n%s", labelsFoo, err) 475 } 476 }) 477 } 478 } 479 480 func Test_AllowAll(t *testing.T) { 481 identityCache := cache.IdentityCache{ 482 identity.NumericIdentity(identityFoo): labelsFoo, 483 identity.NumericIdentity(identityBar): labelsBar, 484 } 485 selectorCache := testNewSelectorCache(identityCache) 486 487 tests := []struct { 488 test int 489 selector api.EndpointSelector 490 rules api.Rules 491 result MapState 492 }{ 493 {0, api.EndpointSelectorNone, api.Rules{rule____AllowAll}, MapState{mapKeyAllowAll__: mapEntryL7None_}}, 494 {1, api.WildcardEndpointSelector, api.Rules{rule____AllowAll}, MapState{mapKeyAllowAll__: mapEntryL7None_}}, 495 } 496 497 for _, tt := range tests { 498 repo := newPolicyDistillery(selectorCache) 499 for _, r := range tt.rules { 500 if r != nil { 501 rule := r.WithEndpointSelector(tt.selector) 502 _, _ = repo.AddList(api.Rules{rule}) 503 } 504 } 505 t.Run(fmt.Sprintf("permutation_%d", tt.test), func(t *testing.T) { 506 logBuffer := new(bytes.Buffer) 507 repo = repo.WithLogBuffer(logBuffer) 508 mapstate, err := repo.distillPolicy(labelsFoo) 509 if err != nil { 510 t.Errorf("Policy resolution failure: %s", err) 511 } 512 if equal, err := checker.DeepEqual(mapstate, tt.result); !equal { 513 t.Logf("Rules:\n%s\n\n", tt.rules.String()) 514 t.Logf("Policy Trace: \n%s\n", logBuffer.String()) 515 t.Errorf("Policy obtained didn't match expected for endpoint %s:\n%s", labelsFoo, err) 516 } 517 }) 518 } 519 }