github.com/fafucoder/cilium@v1.6.11/pkg/endpoint/endpoint_status_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 endpoint 18 19 import ( 20 "context" 21 "fmt" 22 "reflect" 23 "time" 24 25 "github.com/cilium/cilium/api/v1/models" 26 "github.com/cilium/cilium/pkg/checker" 27 "github.com/cilium/cilium/pkg/controller" 28 "github.com/cilium/cilium/pkg/identity" 29 "github.com/cilium/cilium/pkg/identity/cache" 30 cilium_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 31 "github.com/cilium/cilium/pkg/labels" 32 "github.com/cilium/cilium/pkg/policy" 33 "github.com/cilium/cilium/pkg/policy/trafficdirection" 34 35 "gopkg.in/check.v1" 36 ) 37 38 var ( 39 allowAllIdentityList = cilium_v2.AllowedIdentityList{{}} 40 denyAllIdentityList = cilium_v2.AllowedIdentityList(nil) 41 ) 42 43 type endpointGeneratorSpec struct { 44 failingControllers int 45 logErrors int 46 allowedIngressIdentities int 47 allowedEgressIdentities int 48 numPortsPerIdentity int 49 fakeControllerManager bool 50 } 51 52 func (s *EndpointSuite) newEndpoint(c *check.C, spec endpointGeneratorSpec) *Endpoint { 53 e, err := NewEndpointFromChangeModel(s, &models.EndpointChangeRequest{ 54 Addressing: &models.AddressPair{}, 55 ID: 200, 56 Labels: models.Labels{ 57 "k8s:io.cilium.k8s.policy.cluster=default", 58 "k8s:io.cilium.k8s.policy.serviceaccount=default", 59 "k8s:io.kubernetes.pod.namespace=default", 60 "k8s:name=probe", 61 }, 62 State: models.EndpointState("waiting-for-identity"), 63 }) 64 c.Assert(err, check.IsNil) 65 66 e.SecurityIdentity = &identity.Identity{ 67 ID: 100, 68 Labels: labels.NewLabelsFromModel([]string{ 69 "k8s:io.cilium.k8s.policy.cluster=default", 70 "k8s:io.cilium.k8s.policy.serviceaccount=default", 71 "k8s:io.kubernetes.pod.namespace=default", 72 "k8s:name=probe", 73 }), 74 } 75 76 if spec.fakeControllerManager { 77 e.controllers = controller.FakeManager(spec.failingControllers) 78 } 79 80 for i := 0; i < spec.logErrors; i++ { 81 e.Status.addStatusLog(&statusLogMsg{ 82 Status: Status{Code: Failure, Msg: "Failure", Type: BPF}, 83 }) 84 } 85 86 e.desiredPolicy.PolicyMapState = policy.MapState{} 87 88 if spec.numPortsPerIdentity == 0 { 89 spec.numPortsPerIdentity = 1 90 } 91 92 for i := 0; i < spec.allowedIngressIdentities; i++ { 93 for n := 0; n < spec.numPortsPerIdentity; n++ { 94 key := policy.Key{ 95 Identity: uint32(i), 96 DestPort: uint16(80 + n), 97 TrafficDirection: trafficdirection.Ingress.Uint8(), 98 } 99 e.desiredPolicy.PolicyMapState[key] = policy.MapStateEntry{} 100 } 101 } 102 103 for i := 0; i < spec.allowedIngressIdentities; i++ { 104 for n := 0; n < spec.numPortsPerIdentity; n++ { 105 key := policy.Key{ 106 Identity: uint32(i + 30000), 107 DestPort: uint16(80 + n), 108 TrafficDirection: trafficdirection.Egress.Uint8(), 109 } 110 e.desiredPolicy.PolicyMapState[key] = policy.MapStateEntry{} 111 } 112 } 113 114 return e 115 } 116 117 func (s *EndpointSuite) TestGetCiliumEndpointStatusSuccessfulControllers(c *check.C) { 118 e := s.newEndpoint(c, endpointGeneratorSpec{}) 119 cepA := e.GetCiliumEndpointStatus() 120 121 // Run successful controllers in the background 122 for i := 0; i < 50; i++ { 123 e.controllers.UpdateController(fmt.Sprintf("controller-%d", i), 124 controller.ControllerParams{ 125 DoFunc: func(ctx context.Context) error { 126 return nil 127 }, 128 RunInterval: 10 * time.Millisecond, 129 }, 130 ) 131 } 132 defer e.controllers.RemoveAll() 133 134 // Generate EndpointStatus in quick interval while controllers are 135 // succeeding in the background 136 timeout := time.After(1 * time.Second) 137 tick := time.Tick(10 * time.Millisecond) 138 for { 139 select { 140 case <-timeout: 141 return 142 case <-tick: 143 cepB := e.GetCiliumEndpointStatus() 144 c.Assert(cepA, checker.DeepEquals, cepB) 145 } 146 } 147 } 148 149 func (s *EndpointSuite) TestGetCiliumEndpointStatusSuccessfulLog(c *check.C) { 150 e := s.newEndpoint(c, endpointGeneratorSpec{}) 151 cepA := e.GetCiliumEndpointStatus() 152 153 go func() { 154 for i := 0; i < 1000; i++ { 155 e.Status.addStatusLog(&statusLogMsg{ 156 Status: Status{Code: OK, Msg: "Success", Type: BPF}, 157 }) 158 time.Sleep(time.Millisecond) 159 } 160 }() 161 162 // Generate EndpointStatus in quick interval while state transitions 163 // are succeeding in the background 164 timeout := time.After(1 * time.Second) 165 tick := time.Tick(10 * time.Millisecond) 166 for { 167 select { 168 case <-timeout: 169 return 170 case <-tick: 171 cepB := e.GetCiliumEndpointStatus() 172 c.Assert(cepA, checker.DeepEquals, cepB) 173 } 174 } 175 } 176 177 func (s *EndpointSuite) TestGetCiliumEndpointStatusDeepEqual(c *check.C) { 178 a := s.newEndpoint(c, endpointGeneratorSpec{ 179 fakeControllerManager: true, 180 failingControllers: 10, 181 logErrors: maxLogs, 182 allowedIngressIdentities: 100, 183 allowedEgressIdentities: 100, 184 numPortsPerIdentity: 10, 185 }) 186 187 b := s.newEndpoint(c, endpointGeneratorSpec{ 188 fakeControllerManager: true, 189 failingControllers: 10, 190 logErrors: maxLogs, 191 allowedIngressIdentities: 100, 192 allowedEgressIdentities: 100, 193 numPortsPerIdentity: 10, 194 }) 195 196 cepA := a.GetCiliumEndpointStatus() 197 cepB := b.GetCiliumEndpointStatus() 198 199 c.Assert(cepA, checker.DeepEquals, cepB) 200 } 201 202 func (s *EndpointSuite) TestGetCiliumEndpointStatusCorrectnes(c *check.C) { 203 e := s.newEndpoint(c, endpointGeneratorSpec{ 204 fakeControllerManager: true, 205 failingControllers: 10, 206 logErrors: maxLogs, 207 allowedIngressIdentities: 100, 208 allowedEgressIdentities: 100, 209 numPortsPerIdentity: 10, 210 }) 211 212 cep := e.GetCiliumEndpointStatus() 213 214 c.Assert(len(cep.Status.Log), check.Equals, cilium_v2.EndpointStatusLogEntries) 215 } 216 217 // apiResult is an individual desired AllowedIdentityEntry test result entry. 218 type apiResult struct { 219 labels string 220 identity uint64 221 dport uint16 222 proto uint8 223 } 224 225 func prepareExpectedList(want []apiResult) cilium_v2.AllowedIdentityList { 226 expectedList := denyAllIdentityList 227 if want != nil { 228 expectedList = cilium_v2.AllowedIdentityList{} 229 for _, w := range want { 230 entry := cilium_v2.AllowedIdentityTuple{ 231 Identity: w.identity, 232 DestPort: w.dport, 233 Protocol: w.proto, 234 } 235 if w.labels != "" { 236 entry.IdentityLabels = map[string]string{ 237 w.labels: "", 238 } 239 } 240 expectedList = append(expectedList, entry) 241 } 242 expectedList.Sort() 243 } 244 245 return expectedList 246 } 247 248 func (s *EndpointSuite) TestgetEndpointPolicyMapState(c *check.C) { 249 e := s.newEndpoint(c, endpointGeneratorSpec{ 250 fakeControllerManager: true, 251 failingControllers: 10, 252 logErrors: maxLogs, 253 allowedIngressIdentities: 100, 254 allowedEgressIdentities: 100, 255 numPortsPerIdentity: 10, 256 }) 257 // Policy not enabled; allow all. 258 apiPolicy := e.getEndpointPolicy() 259 c.Assert(apiPolicy.Ingress.Allowed, checker.DeepEquals, allowAllIdentityList) 260 c.Assert(apiPolicy.Egress.Allowed, checker.DeepEquals, allowAllIdentityList) 261 262 fooLbls := labels.Labels{"": labels.ParseLabel("foo")} 263 fooIdentity, _, err := cache.AllocateIdentity(context.Background(), nil, fooLbls) 264 c.Assert(err, check.Equals, nil) 265 defer cache.Release(context.Background(), nil, fooIdentity) 266 267 e.desiredPolicy = policy.NewEndpointPolicy(s.repo) 268 e.desiredPolicy.IngressPolicyEnabled = true 269 e.desiredPolicy.EgressPolicyEnabled = true 270 271 type args struct { 272 identity uint32 273 destPort uint16 274 nexthdr uint8 275 direction trafficdirection.TrafficDirection 276 } 277 278 tests := []struct { 279 name string 280 args []args 281 egressResult []apiResult 282 ingressResult []apiResult 283 }{ 284 { 285 name: "Deny all", 286 }, 287 { 288 name: "Allow all ingress", 289 args: []args{ 290 {0, 0, 0, trafficdirection.Ingress}, 291 }, 292 ingressResult: []apiResult{{}}, 293 egressResult: nil, 294 }, 295 { 296 name: "Allow all egress", 297 args: []args{ 298 {0, 0, 0, trafficdirection.Egress}, 299 }, 300 ingressResult: nil, 301 egressResult: []apiResult{{}}, 302 }, 303 { 304 name: "Allow all both directions", 305 args: []args{ 306 {0, 0, 0, trafficdirection.Ingress}, 307 {0, 0, 0, trafficdirection.Egress}, 308 }, 309 ingressResult: []apiResult{{}}, 310 egressResult: []apiResult{{}}, 311 }, 312 { 313 name: "Allow world ingress", 314 args: []args{ 315 {uint32(identity.ReservedIdentityWorld), 0, 0, trafficdirection.Ingress}, 316 }, 317 ingressResult: []apiResult{ 318 {"reserved:world", uint64(identity.ReservedIdentityWorld), 0, 0}, 319 }, 320 egressResult: nil, 321 }, 322 { 323 name: "Allow world egress", 324 args: []args{ 325 {uint32(identity.ReservedIdentityWorld), 0, 0, trafficdirection.Egress}, 326 }, 327 ingressResult: nil, 328 egressResult: []apiResult{ 329 {"reserved:world", uint64(identity.ReservedIdentityWorld), 0, 0}, 330 }, 331 }, 332 { 333 name: "Allow world both directions", 334 args: []args{ 335 {uint32(identity.ReservedIdentityWorld), 0, 0, trafficdirection.Ingress}, 336 {uint32(identity.ReservedIdentityWorld), 0, 0, trafficdirection.Egress}, 337 }, 338 ingressResult: []apiResult{ 339 {"reserved:world", uint64(identity.ReservedIdentityWorld), 0, 0}, 340 }, 341 egressResult: []apiResult{ 342 {"reserved:world", uint64(identity.ReservedIdentityWorld), 0, 0}, 343 }, 344 }, 345 { 346 name: "Ingress mix of L3, L4, L3-dependent L4", 347 args: []args{ 348 {uint32(fooIdentity.ID), 0, 0, trafficdirection.Ingress}, // L3-only map state 349 {0, 80, 6, trafficdirection.Ingress}, // L4-only map state 350 {uint32(fooIdentity.ID), 80, 6, trafficdirection.Ingress}, // L3-dependent L4 map state 351 }, 352 ingressResult: []apiResult{ 353 {"unspec:foo", uint64(fooIdentity.ID), 0, 0}, 354 {"", 0, 80, 6}, 355 {"unspec:foo", uint64(fooIdentity.ID), 80, 6}, 356 }, 357 egressResult: nil, 358 }, 359 { 360 name: "Egress mix of L3, L4, L3-dependent L4", 361 args: []args{ 362 {uint32(fooIdentity.ID), 0, 0, trafficdirection.Egress}, // L3-only map state 363 {0, 80, 6, trafficdirection.Egress}, // L4-only map state 364 {uint32(fooIdentity.ID), 80, 6, trafficdirection.Egress}, // L3-dependent L4 map state 365 }, 366 ingressResult: nil, 367 egressResult: []apiResult{ 368 {"unspec:foo", uint64(fooIdentity.ID), 0, 0}, 369 {"", 0, 80, 6}, 370 {"unspec:foo", uint64(fooIdentity.ID), 80, 6}, 371 }, 372 }, 373 { 374 name: "World shadows CIDR ingress", 375 args: []args{ 376 {uint32(identity.ReservedIdentityWorld), 0, 0, trafficdirection.Ingress}, 377 {uint32(identity.LocalIdentityFlag), 0, 0, trafficdirection.Ingress}, 378 }, 379 ingressResult: []apiResult{ 380 {"reserved:world", uint64(identity.ReservedIdentityWorld), 0, 0}, 381 }, 382 egressResult: nil, 383 }, 384 { 385 name: "World shadows CIDR egress", 386 args: []args{ 387 {uint32(identity.ReservedIdentityWorld), 0, 0, trafficdirection.Egress}, 388 {uint32(identity.LocalIdentityFlag), 0, 0, trafficdirection.Egress}, 389 }, 390 ingressResult: nil, 391 egressResult: []apiResult{ 392 {"reserved:world", uint64(identity.ReservedIdentityWorld), 0, 0}, 393 }, 394 }, 395 } 396 397 for _, tt := range tests { 398 e.desiredPolicy.PolicyMapState = policy.MapState{} 399 for _, arg := range tt.args { 400 t := policy.Key{ 401 Identity: arg.identity, 402 DestPort: arg.destPort, 403 Nexthdr: arg.nexthdr, 404 TrafficDirection: arg.direction.Uint8(), 405 } 406 e.desiredPolicy.PolicyMapState[t] = policy.MapStateEntry{} 407 } 408 expectedIngressList := prepareExpectedList(tt.ingressResult) 409 expectedEgressList := prepareExpectedList(tt.egressResult) 410 411 apiPolicy = e.getEndpointPolicy() 412 c.Assert(apiPolicy.Ingress.Allowed, checker.DeepEquals, expectedIngressList) 413 c.Assert(apiPolicy.Egress.Allowed, checker.DeepEquals, expectedEgressList) 414 } 415 } 416 417 func (s *EndpointSuite) BenchmarkGetCiliumEndpointStatusDeepEqual(c *check.C) { 418 a := s.newEndpoint(c, endpointGeneratorSpec{ 419 fakeControllerManager: true, 420 failingControllers: 10, 421 logErrors: maxLogs, 422 allowedIngressIdentities: 100, 423 allowedEgressIdentities: 100, 424 numPortsPerIdentity: 10, 425 }) 426 427 b := s.newEndpoint(c, endpointGeneratorSpec{ 428 fakeControllerManager: true, 429 failingControllers: 10, 430 logErrors: maxLogs, 431 allowedIngressIdentities: 100, 432 allowedEgressIdentities: 100, 433 numPortsPerIdentity: 10, 434 }) 435 436 c.ResetTimer() 437 for i := 0; i < c.N; i++ { 438 if !reflect.DeepEqual(a, b) { 439 c.Errorf("DeepEqual failed") 440 } 441 } 442 c.StopTimer() 443 } 444 445 func (s *EndpointSuite) BenchmarkGetCiliumEndpointStatus(c *check.C) { 446 e := s.newEndpoint(c, endpointGeneratorSpec{ 447 failingControllers: 10, 448 logErrors: maxLogs, 449 allowedIngressIdentities: 100, 450 allowedEgressIdentities: 100, 451 numPortsPerIdentity: 10, 452 }) 453 454 c.ResetTimer() 455 for i := 0; i < c.N; i++ { 456 status := e.GetCiliumEndpointStatus() 457 c.Assert(status, check.Not(check.IsNil)) 458 } 459 c.StopTimer() 460 }