github.phpd.cn/cilium/cilium@v1.6.12/pkg/aws/eni/node_manager_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 eni 18 19 import ( 20 "errors" 21 "fmt" 22 "time" 23 24 ec2mock "github.com/cilium/cilium/pkg/aws/ec2/mock" 25 metricsmock "github.com/cilium/cilium/pkg/aws/eni/metrics/mock" 26 "github.com/cilium/cilium/pkg/aws/types" 27 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 28 "github.com/cilium/cilium/pkg/option" 29 "github.com/cilium/cilium/pkg/testutils" 30 31 "gopkg.in/check.v1" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 ) 34 35 var ( 36 testSubnet = &types.Subnet{ 37 ID: "s-1", 38 AvailabilityZone: "us-west-1", 39 VpcID: "vpc-1", 40 AvailableAddresses: 200, 41 Tags: types.Tags{"k": "v"}, 42 } 43 testVpc = &types.Vpc{ 44 ID: "v-1", 45 PrimaryCIDR: "10.10.0.0/16", 46 } 47 k8sapi = &k8sMock{} 48 metricsapi = metricsmock.NewMockMetrics() 49 ) 50 51 func (e *ENISuite) TestGetNodeNames(c *check.C) { 52 ec2api := ec2mock.NewAPI([]*types.Subnet{testSubnet}, []*types.Vpc{testVpc}) 53 instances := NewInstancesManager(ec2api, metricsapi) 54 c.Assert(instances, check.Not(check.IsNil)) 55 mngr, err := NewNodeManager(instances, ec2api, k8sapi, metricsapi, 10) 56 c.Assert(err, check.IsNil) 57 c.Assert(mngr, check.Not(check.IsNil)) 58 59 mngr.Update(newCiliumNode("node1", "i-1", "m4.large", "us-west-1", "vpc-1", 0, 0, 0, 0)) 60 61 names := mngr.GetNames() 62 c.Assert(len(names), check.Equals, 1) 63 c.Assert(names[0], check.Equals, "node1") 64 65 mngr.Update(newCiliumNode("node2", "i-2", "m4.large", "us-west-1", "vpc-1", 0, 0, 0, 0)) 66 67 names = mngr.GetNames() 68 c.Assert(len(names), check.Equals, 2) 69 70 mngr.Delete("node1") 71 72 names = mngr.GetNames() 73 c.Assert(len(names), check.Equals, 1) 74 c.Assert(names[0], check.Equals, "node2") 75 } 76 77 func (e *ENISuite) TestNodeManagerGet(c *check.C) { 78 ec2api := ec2mock.NewAPI([]*types.Subnet{testSubnet}, []*types.Vpc{testVpc}) 79 instances := NewInstancesManager(ec2api, metricsapi) 80 c.Assert(instances, check.Not(check.IsNil)) 81 mngr, err := NewNodeManager(instances, ec2api, k8sapi, metricsapi, 10) 82 c.Assert(err, check.IsNil) 83 c.Assert(mngr, check.Not(check.IsNil)) 84 85 mngr.Update(newCiliumNode("node1", "i-1", "m4.large", "us-west-1", "vpc-1", 0, 0, 0, 0)) 86 87 c.Assert(mngr.Get("node1"), check.Not(check.IsNil)) 88 c.Assert(mngr.Get("node2"), check.IsNil) 89 90 mngr.Delete("node1") 91 c.Assert(mngr.Get("node1"), check.IsNil) 92 c.Assert(mngr.Get("node2"), check.IsNil) 93 } 94 95 type k8sMock struct{} 96 97 func (k *k8sMock) Update(node, origNode *v2.CiliumNode) (*v2.CiliumNode, error) { 98 return nil, nil 99 } 100 101 func (k *k8sMock) UpdateStatus(node, origNode *v2.CiliumNode) (*v2.CiliumNode, error) { 102 return nil, nil 103 } 104 105 func (k *k8sMock) Get(node string) (*v2.CiliumNode, error) { 106 return &v2.CiliumNode{}, nil 107 } 108 109 func newCiliumNode(node, instanceID, instanceType, az, vpcID string, preAllocate, minAllocate, available, used int) *v2.CiliumNode { 110 cn := &v2.CiliumNode{ 111 ObjectMeta: metav1.ObjectMeta{Name: node, Namespace: "default"}, 112 Spec: v2.NodeSpec{ 113 ENI: v2.ENISpec{ 114 InstanceID: instanceID, 115 InstanceType: instanceType, 116 PreAllocate: preAllocate, 117 MinAllocate: minAllocate, 118 AvailabilityZone: az, 119 VpcID: vpcID, 120 }, 121 IPAM: v2.IPAMSpec{ 122 Pool: map[string]v2.AllocationIP{}, 123 }, 124 }, 125 Status: v2.NodeStatus{ 126 IPAM: v2.IPAMStatus{ 127 Used: map[string]v2.AllocationIP{}, 128 }, 129 }, 130 } 131 132 updateCiliumNode(cn, available, used) 133 134 return cn 135 } 136 137 func updateCiliumNode(cn *v2.CiliumNode, available, used int) *v2.CiliumNode { 138 cn.Spec.IPAM.Pool = map[string]v2.AllocationIP{} 139 for i := 0; i < used; i++ { 140 cn.Spec.IPAM.Pool[fmt.Sprintf("1.1.1.%d", i)] = v2.AllocationIP{Resource: "foo"} 141 } 142 143 cn.Status.IPAM.Used = map[string]v2.AllocationIP{} 144 for ip, ipAllocation := range cn.Spec.IPAM.Pool { 145 if used > 0 { 146 delete(cn.Spec.IPAM.Pool, ip) 147 cn.Status.IPAM.Used[ip] = ipAllocation 148 used-- 149 } 150 } 151 152 return cn 153 } 154 155 func reachedAddressesNeeded(mngr *NodeManager, nodeName string, needed int) (success bool) { 156 if node := mngr.Get(nodeName); node != nil { 157 success = node.getNeededAddresses() == needed 158 } 159 return 160 } 161 162 // TestNodeManagerDefaultAllocation tests allocation with default parameters 163 // 164 // - m4.large (2x ENIs, 2x10 IPs) 165 // - MinAllocate 0 166 // - PreAllocate 0 (default: 8) 167 func (e *ENISuite) TestNodeManagerDefaultAllocation(c *check.C) { 168 ec2api := ec2mock.NewAPI([]*types.Subnet{testSubnet}, []*types.Vpc{testVpc}) 169 instances := NewInstancesManager(ec2api, metricsapi) 170 c.Assert(instances, check.Not(check.IsNil)) 171 mngr, err := NewNodeManager(instances, ec2api, k8sapi, metricsapi, 10) 172 c.Assert(err, check.IsNil) 173 c.Assert(mngr, check.Not(check.IsNil)) 174 175 // Announce node wait for IPs to become available 176 cn := newCiliumNode("node1", "i-0", "m4.large", "us-west-1", "vpc-1", 0, 0, 0, 0) 177 mngr.Update(cn) 178 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node1", 0) }, 5*time.Second), check.IsNil) 179 180 node := mngr.Get("node1") 181 c.Assert(node, check.Not(check.IsNil)) 182 c.Assert(node.stats.availableIPs, check.Equals, 8) 183 c.Assert(node.stats.usedIPs, check.Equals, 0) 184 185 // Use 7 out of 8 IPs 186 mngr.Update(updateCiliumNode(cn, 8, 7)) 187 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node1", 0) }, 5*time.Second), check.IsNil) 188 189 node = mngr.Get("node1") 190 c.Assert(node, check.Not(check.IsNil)) 191 c.Assert(node.stats.availableIPs, check.Equals, 15) 192 c.Assert(node.stats.usedIPs, check.Equals, 7) 193 } 194 195 // TestNodeManagerMinAllocate20 tests MinAllocate without PreAllocate 196 // 197 // - m4.large (2x ENIs, 2x10 IPs) 198 // - MinAllocate 10 199 // - PreAllocate -1 200 func (e *ENISuite) TestNodeManagerMinAllocate20(c *check.C) { 201 ec2api := ec2mock.NewAPI([]*types.Subnet{testSubnet}, []*types.Vpc{testVpc}) 202 instances := NewInstancesManager(ec2api, metricsapi) 203 c.Assert(instances, check.Not(check.IsNil)) 204 mngr, err := NewNodeManager(instances, ec2api, k8sapi, metricsapi, 10) 205 c.Assert(err, check.IsNil) 206 c.Assert(mngr, check.Not(check.IsNil)) 207 208 // Announce node wait for IPs to become available 209 cn := newCiliumNode("node2", "i-1", "m5.4xlarge", "us-west-1", "vpc-1", -1, 10, 0, 0) 210 mngr.Update(cn) 211 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node2", 0) }, 5*time.Second), check.IsNil) 212 213 node := mngr.Get("node2") 214 c.Assert(node, check.Not(check.IsNil)) 215 c.Assert(node.stats.availableIPs, check.Equals, 10) 216 c.Assert(node.stats.usedIPs, check.Equals, 0) 217 218 mngr.Update(updateCiliumNode(cn, 10, 8)) 219 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node2", 0) }, 5*time.Second), check.IsNil) 220 221 node = mngr.Get("node2") 222 c.Assert(node, check.Not(check.IsNil)) 223 c.Assert(node.stats.availableIPs, check.Equals, 10) 224 c.Assert(node.stats.usedIPs, check.Equals, 8) 225 226 // Change MinAllocate to 20 227 cn = newCiliumNode("node2", "i-1", "m5.4xlarge", "us-west-1", "vpc-1", 0, 20, 10, 8) 228 mngr.Update(cn) 229 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node2", 0) }, 5*time.Second), check.IsNil) 230 231 node = mngr.Get("node2") 232 c.Assert(node, check.Not(check.IsNil)) 233 c.Assert(node.stats.availableIPs, check.Equals, 20) 234 c.Assert(node.stats.usedIPs, check.Equals, 8) 235 } 236 237 // TestNodeManagerMinAllocateAndPreallocate tests MinAllocate in combination with PreAllocate 238 // 239 // - m4.large (2x ENIs, 2x10 IPs) 240 // - MinAllocate 10 241 // - PreAllocate 1 242 func (e *ENISuite) TestNodeManagerMinAllocateAndPreallocate(c *check.C) { 243 ec2api := ec2mock.NewAPI([]*types.Subnet{testSubnet}, []*types.Vpc{testVpc}) 244 instances := NewInstancesManager(ec2api, metricsapi) 245 c.Assert(instances, check.Not(check.IsNil)) 246 mngr, err := NewNodeManager(instances, ec2api, k8sapi, metricsapi, 10) 247 c.Assert(err, check.IsNil) 248 c.Assert(mngr, check.Not(check.IsNil)) 249 250 // Announce node, wait for IPs to become available 251 cn := newCiliumNode("node2", "i-1", "m4.large", "us-west-1", "vpc-1", 1, 10, 0, 0) 252 mngr.Update(cn) 253 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node2", 0) }, 5*time.Second), check.IsNil) 254 255 node := mngr.Get("node2") 256 c.Assert(node, check.Not(check.IsNil)) 257 c.Assert(node.stats.availableIPs, check.Equals, 10) 258 c.Assert(node.stats.usedIPs, check.Equals, 0) 259 260 // Use 9 out of 10 IPs, no additional IPs should be allocated 261 mngr.Update(updateCiliumNode(cn, 10, 9)) 262 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node2", 0) }, 5*time.Second), check.IsNil) 263 node = mngr.Get("node2") 264 c.Assert(node, check.Not(check.IsNil)) 265 c.Assert(node.stats.availableIPs, check.Equals, 10) 266 c.Assert(node.stats.usedIPs, check.Equals, 9) 267 268 // Use 10 out of 10 IPs, PreAllocate 1 must kick in and allocate an additional IP 269 mngr.Update(updateCiliumNode(cn, 10, 10)) 270 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node2", 0) }, 5*time.Second), check.IsNil) 271 node = mngr.Get("node2") 272 c.Assert(node, check.Not(check.IsNil)) 273 c.Assert(node.stats.availableIPs, check.Equals, 11) 274 c.Assert(node.stats.usedIPs, check.Equals, 10) 275 276 // Release some IPs, no additional IPs should be allocated 277 mngr.Update(updateCiliumNode(cn, 10, 8)) 278 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node2", 0) }, 5*time.Second), check.IsNil) 279 node = mngr.Get("node2") 280 c.Assert(node, check.Not(check.IsNil)) 281 c.Assert(node.stats.availableIPs, check.Equals, 11) 282 c.Assert(node.stats.usedIPs, check.Equals, 8) 283 } 284 285 // TestNodeManagerReleaseAddress tests PreAllocate, MinAllocate and MaxAboveWatermark 286 // when release excess IP is enabled 287 // 288 // - m4.large (4x ENIs, 4x15 IPs) 289 // - MinAllocate 10 290 // - PreAllocate 4 291 // - MaxAboveWatermark 4 292 // - FirstInterfaceIndex 1 293 func (e *ENISuite) TestNodeManagerReleaseAddress(c *check.C) { 294 ec2api := ec2mock.NewAPI([]*types.Subnet{testSubnet}, []*types.Vpc{testVpc}) 295 instances := NewInstancesManager(ec2api, metricsapi) 296 c.Assert(instances, check.Not(check.IsNil)) 297 mngr, err := NewNodeManager(instances, ec2api, k8sapi, metricsapi, 10) 298 c.Assert(err, check.IsNil) 299 c.Assert(mngr, check.Not(check.IsNil)) 300 301 option.Config.AwsReleaseExcessIps = true 302 303 // Announce node, wait for IPs to become available 304 cn := newCiliumNode("node3", "i-3", "m4.xlarge", "us-west-1", "vpc-1", 4, 10, 0, 0) 305 cn.Spec.ENI.MaxAboveWatermark = 4 306 cn.Spec.ENI.FirstInterfaceIndex = 1 307 mngr.Update(cn) 308 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node3", 0) }, 5*time.Second), check.IsNil) 309 310 // 10 min-allocate + 4 max-above-watermark => 14 IPs must become 311 // available as 14 < 15 (interface limit) 312 node := mngr.Get("node3") 313 c.Assert(node, check.Not(check.IsNil)) 314 c.Assert(node.stats.availableIPs, check.Equals, 14) 315 c.Assert(node.stats.usedIPs, check.Equals, 0) 316 317 // Use 11 out of 14 IPs, no additional IPs should be allocated 318 mngr.Update(updateCiliumNode(cn, 14, 11)) 319 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node3", 0) }, 5*time.Second), check.IsNil) 320 node = mngr.Get("node3") 321 c.Assert(node, check.Not(check.IsNil)) 322 c.Assert(node.stats.availableIPs, check.Equals, 15) 323 c.Assert(node.stats.usedIPs, check.Equals, 11) 324 325 // Use 14 out of 15 IPs, PreAllocate 4 + MaxAboveWatermark must kick in 326 // and allocate 8 additional IPs 327 mngr.Update(updateCiliumNode(cn, 15, 14)) 328 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node3", 0) }, 5*time.Second), check.IsNil) 329 node = mngr.Get("node3") 330 c.Assert(node, check.Not(check.IsNil)) 331 c.Assert(node.stats.availableIPs, check.Equals, 22) 332 c.Assert(node.stats.usedIPs, check.Equals, 14) 333 334 // Reduce used IPs to 10, this leads to 15 excess IPs but release 335 // occurs at interval based resync, so expect timeout at first 336 mngr.Update(updateCiliumNode(cn, 22, 10)) 337 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node3", 0) }, 2*time.Second), check.Not(check.IsNil)) 338 node = mngr.Get("node3") 339 c.Assert(node, check.Not(check.IsNil)) 340 c.Assert(node.stats.availableIPs, check.Equals, 22) 341 c.Assert(node.stats.usedIPs, check.Equals, 10) 342 343 // Trigger resync manually, excess IPs should be released 344 // 10 used + 4 pre-allocate + 4 max-above-watermark => 18 345 node = mngr.Get("node3") 346 node.resource.Status.ENI.ENIs = node.enis 347 syncTime := instances.Resync() 348 mngr.resyncNode(node, &resyncStats{}, syncTime) 349 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node3", 0) }, 5*time.Second), check.IsNil) 350 node = mngr.Get("node3") 351 c.Assert(node, check.Not(check.IsNil)) 352 c.Assert(node.stats.availableIPs, check.Equals, 18) 353 c.Assert(node.stats.usedIPs, check.Equals, 10) 354 355 option.Config.AwsReleaseExcessIps = false 356 } 357 358 // TestNodeManagerExceedENICapacity tests exceeding ENI capacity 359 // 360 // - m4.large (2x ENIs, 2x10 IPs) 361 // - MinAllocate 20 362 // - PreAllocate 8 363 func (e *ENISuite) TestNodeManagerExceedENICapacity(c *check.C) { 364 ec2api := ec2mock.NewAPI([]*types.Subnet{testSubnet}, []*types.Vpc{testVpc}) 365 instances := NewInstancesManager(ec2api, metricsapi) 366 c.Assert(instances, check.Not(check.IsNil)) 367 mngr, err := NewNodeManager(instances, ec2api, k8sapi, metricsapi, 10) 368 c.Assert(err, check.IsNil) 369 c.Assert(mngr, check.Not(check.IsNil)) 370 371 // Announce node, wait for IPs to become available 372 cn := newCiliumNode("node2", "i-1", "m4.large", "us-west-1", "vpc-1", 8, 20, 0, 0) 373 mngr.Update(cn) 374 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node2", 0) }, 5*time.Second), check.IsNil) 375 376 node := mngr.Get("node2") 377 c.Assert(node, check.Not(check.IsNil)) 378 c.Assert(node.stats.availableIPs, check.Equals, 20) 379 c.Assert(node.stats.usedIPs, check.Equals, 0) 380 381 // Use 16 out of 20 IPs, we should reach 4 addresses needed but never 0 addresses needed 382 mngr.Update(updateCiliumNode(cn, 20, 16)) 383 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node2", 4) }, 5*time.Second), check.IsNil) 384 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, "node2", 0) }, 5*time.Second), check.Not(check.IsNil)) 385 386 node = mngr.Get("node2") 387 c.Assert(node, check.Not(check.IsNil)) 388 c.Assert(node.stats.availableIPs, check.Equals, 20) 389 c.Assert(node.stats.usedIPs, check.Equals, 16) 390 } 391 392 type nodeState struct { 393 cn *v2.CiliumNode 394 name string 395 instanceName string 396 } 397 398 // TestNodeManagerManyNodes tests IP allocation of 100 nodes across 3 subnets 399 // 400 // - m4.large (2x ENIs, 2x10 IPs) 401 // - MinAllocate 10 402 // - PreAllocate 1 403 func (e *ENISuite) TestNodeManagerManyNodes(c *check.C) { 404 const ( 405 numNodes = 100 406 minAllocate = 10 407 ) 408 409 subnets := []*types.Subnet{ 410 {ID: "s-1", AvailabilityZone: "us-west-1", VpcID: "vpc-1", AvailableAddresses: 400}, 411 {ID: "s-2", AvailabilityZone: "us-west-1", VpcID: "vpc-1", AvailableAddresses: 400}, 412 {ID: "s-3", AvailabilityZone: "us-west-1", VpcID: "vpc-1", AvailableAddresses: 400}, 413 } 414 415 ec2api := ec2mock.NewAPI(subnets, []*types.Vpc{testVpc}) 416 metricsapi := metricsmock.NewMockMetrics() 417 instancesManager := NewInstancesManager(ec2api, metricsapi) 418 instancesManager.Resync() 419 mngr, err := NewNodeManager(instancesManager, ec2api, k8sapi, metricsapi, 10) 420 c.Assert(err, check.IsNil) 421 c.Assert(mngr, check.Not(check.IsNil)) 422 423 state := make([]*nodeState, numNodes) 424 425 for i := range state { 426 s := &nodeState{name: fmt.Sprintf("node%d", i), instanceName: fmt.Sprintf("i-%d", i)} 427 s.cn = newCiliumNode(s.name, s.instanceName, "m4.large", "us-west-1", "vpc-1", 1, minAllocate, 0, 0) 428 state[i] = s 429 mngr.Update(s.cn) 430 } 431 432 for _, s := range state { 433 c.Assert(testutils.WaitUntil(func() bool { return reachedAddressesNeeded(mngr, s.name, 0) }, 5*time.Second), check.IsNil) 434 435 node := mngr.Get(s.name) 436 c.Assert(node, check.Not(check.IsNil)) 437 if node.stats.availableIPs != minAllocate { 438 c.Errorf("Node %s allocation mismatch. expected: %d allocated: %d", s.name, minAllocate, node.stats.availableIPs) 439 c.Fail() 440 } 441 c.Assert(node.stats.usedIPs, check.Equals, 0) 442 } 443 444 // The above check returns as soon as the address requirements are met. 445 // The metrics may still be oudated, resync all nodes to update 446 // metrics. 447 mngr.Resync(time.Now()) 448 449 c.Assert(metricsapi.Nodes("total"), check.Equals, numNodes) 450 c.Assert(metricsapi.Nodes("in-deficit"), check.Equals, 0) 451 c.Assert(metricsapi.Nodes("at-capacity"), check.Equals, 0) 452 453 c.Assert(metricsapi.AllocatedIPs("available"), check.Equals, numNodes*minAllocate) 454 c.Assert(metricsapi.AllocatedIPs("needed"), check.Equals, 0) 455 c.Assert(metricsapi.AllocatedIPs("used"), check.Equals, 0) 456 457 // All subnets must have been used for allocation 458 for _, subnet := range subnets { 459 c.Assert(metricsapi.ENIAllocationAttempts("success", subnet.ID), check.Not(check.Equals), 0) 460 c.Assert(metricsapi.IPAllocations(subnet.ID), check.Not(check.Equals), 0) 461 } 462 463 c.Assert(metricsapi.ResyncCount(), check.Not(check.Equals), 0) 464 c.Assert(metricsapi.AvailableENIs(), check.Not(check.Equals), 0) 465 } 466 467 // TestNodeManagerInstanceNotRunning verifies that allocation correctly detects 468 // instances which are no longer running 469 func (e *ENISuite) TestNodeManagerInstanceNotRunning(c *check.C) { 470 ec2api := ec2mock.NewAPI([]*types.Subnet{testSubnet}, []*types.Vpc{testVpc}) 471 ec2api.SetMockError(ec2mock.AttachNetworkInterface, errors.New("foo is not 'running' foo")) 472 metricsMock := metricsmock.NewMockMetrics() 473 instances := NewInstancesManager(ec2api, metricsapi) 474 c.Assert(instances, check.Not(check.IsNil)) 475 mngr, err := NewNodeManager(instances, ec2api, k8sapi, metricsMock, 10) 476 c.Assert(err, check.IsNil) 477 c.Assert(mngr, check.Not(check.IsNil)) 478 479 // Announce node, ENI attachement will fail 480 cn := newCiliumNode("node1", "i-0", "m4.large", "us-west-1", "vpc-1", 0, 0, 0, 0) 481 mngr.Update(cn) 482 483 // Wait for node to be declared notRunning 484 c.Assert(testutils.WaitUntil(func() bool { 485 if n := mngr.Get("node1"); n != nil { 486 return n.instanceNotRunning 487 } 488 return false 489 }, 5*time.Second), check.IsNil) 490 491 // Metric should not indicate failure 492 c.Assert(metricsMock.ENIAllocationAttempts("ENI attachment failed", testSubnet.ID), check.Equals, int64(0)) 493 494 node := mngr.Get("node1") 495 c.Assert(node, check.Not(check.IsNil)) 496 c.Assert(node.stats.availableIPs, check.Equals, 0) 497 c.Assert(node.stats.usedIPs, check.Equals, 0) 498 } 499 500 func benchmarkAllocWorker(c *check.C, workers int64, delay time.Duration, rateLimit float64, burst int) { 501 testSubnet1 := &types.Subnet{ID: "s-1", AvailabilityZone: "us-west-1", VpcID: "vpc-1", AvailableAddresses: 1000000} 502 testSubnet2 := &types.Subnet{ID: "s-2", AvailabilityZone: "us-west-1", VpcID: "vpc-1", AvailableAddresses: 1000000} 503 testSubnet3 := &types.Subnet{ID: "s-3", AvailabilityZone: "us-west-1", VpcID: "vpc-1", AvailableAddresses: 1000000} 504 505 ec2api := ec2mock.NewAPI([]*types.Subnet{testSubnet1, testSubnet2, testSubnet3}, []*types.Vpc{testVpc}) 506 ec2api.SetDelay(ec2mock.AllOperations, delay) 507 ec2api.SetLimiter(rateLimit, burst) 508 instances := NewInstancesManager(ec2api, metricsapi) 509 c.Assert(instances, check.Not(check.IsNil)) 510 mngr, err := NewNodeManager(instances, ec2api, k8sapi, metricsapi, workers) 511 c.Assert(err, check.IsNil) 512 c.Assert(mngr, check.Not(check.IsNil)) 513 514 state := make([]*nodeState, c.N) 515 516 c.ResetTimer() 517 for i := range state { 518 s := &nodeState{name: fmt.Sprintf("node%d", i), instanceName: fmt.Sprintf("i-%d", i)} 519 s.cn = newCiliumNode(s.name, s.instanceName, "m4.large", "us-west-1", "vpc-1", 1, 10, 0, 0) 520 state[i] = s 521 mngr.Update(s.cn) 522 } 523 524 restart: 525 for _, s := range state { 526 if !reachedAddressesNeeded(mngr, s.name, 0) { 527 time.Sleep(5 * time.Millisecond) 528 goto restart 529 } 530 } 531 c.StopTimer() 532 533 } 534 535 func (e *ENISuite) BenchmarkAllocDelay20Worker1(c *check.C) { 536 benchmarkAllocWorker(c, 1, 20*time.Millisecond, 100.0, 4) 537 } 538 func (e *ENISuite) BenchmarkAllocDelay20Worker10(c *check.C) { 539 benchmarkAllocWorker(c, 10, 20*time.Millisecond, 100.0, 4) 540 } 541 func (e *ENISuite) BenchmarkAllocDelay20Worker50(c *check.C) { 542 benchmarkAllocWorker(c, 50, 20*time.Millisecond, 100.0, 4) 543 } 544 func (e *ENISuite) BenchmarkAllocDelay50Worker1(c *check.C) { 545 benchmarkAllocWorker(c, 1, 50*time.Millisecond, 100.0, 4) 546 } 547 func (e *ENISuite) BenchmarkAllocDelay50Worker10(c *check.C) { 548 benchmarkAllocWorker(c, 10, 50*time.Millisecond, 100.0, 4) 549 } 550 func (e *ENISuite) BenchmarkAllocDelay50Worker50(c *check.C) { 551 benchmarkAllocWorker(c, 50, 50*time.Millisecond, 100.0, 4) 552 }