func()

in pkg/provider/ip/eni/eni.go [101:193]


func (e *eniManager) CreateIPV4Address(required int, ec2APIHelper api.EC2APIHelper, log logr.Logger) ([]string, error) {
	e.lock.Lock()
	defer e.lock.Unlock()

	var assignedIPv4Address []string
	log = log.WithValues("node name", e.instance.Name())

	// Loop till we reach the last available ENI and list of assigned IPv4 addresses is less than the required IPv4 addresses
	for index := 0; index < len(e.attachedENIs) && len(assignedIPv4Address) < required; index++ {
		remainingCapacity := e.attachedENIs[index].remainingCapacity
		if remainingCapacity > 0 {
			canAssign := 0
			// Number of IPs wanted is the number of IPs required minus the number of IPs assigned till now.
			want := required - len(assignedIPv4Address)
			// Cannot fulfil the entire request using this ENI, allocate whatever the ENI can assign
			if remainingCapacity < want {
				canAssign = remainingCapacity
			} else {
				canAssign = want
			}
			// Assign the IPv4 Addresses from this ENI
			assignedIPs, err := ec2APIHelper.AssignIPv4AddressesAndWaitTillReady(e.attachedENIs[index].eniID, canAssign)
			if err != nil && len(assignedIPs) == 0 {
				// Return the list of IPs that were actually created along with the error
				return assignedIPs, err
			} else if err != nil {
				// Just log and continue processing the assigned IPs
				log.Error(err, "failed to assign all the requested IPs",
					"requested", want, "got", len(assignedIPs))
			}
			// Update the remaining capacity
			e.attachedENIs[index].remainingCapacity = remainingCapacity - canAssign
			// Append the assigned IPs on this ENI to the list of IPs created across all the ENIs
			assignedIPv4Address = append(assignedIPv4Address, assignedIPs...)
			// Add the mapping from IP to ENI, so that we can easily delete the IP and increment the remaining IP count
			// on the ENI
			for _, ip := range assignedIPs {
				e.ipToENIMap[ip] = e.attachedENIs[index]
			}

			log.Info("assigned IPv4 addresses", "ip", assignedIPs, "eni",
				e.attachedENIs[index].eniID, "want", want, "can provide upto", canAssign)
		}
	}

	// List of secondary IPs supported minus the primary IP
	ipLimit := vpc.Limits[e.instance.Type()].IPv4PerInterface - 1
	eniLimit := vpc.Limits[e.instance.Type()].Interface

	// If the existing ENIs could not assign the required IPs, loop till the new ENIs can assign the required
	// number of IPv4 Addresses
	for len(assignedIPv4Address) < required &&
		len(e.attachedENIs) < eniLimit {

		deviceIndex, err := e.instance.GetHighestUnusedDeviceIndex()
		if err != nil {
			// TODO: Refresh device index for linux nodes only
			return assignedIPv4Address, err
		}
		want := required - len(assignedIPv4Address)
		if want > ipLimit {
			want = ipLimit
		}
		nwInterface, err := ec2APIHelper.CreateAndAttachNetworkInterface(aws.String(e.instance.InstanceID()),
			aws.String(e.instance.SubnetID()), e.instance.InstanceSecurityGroup(), nil, aws.Int64(deviceIndex),
			&ENIDescription, nil, want)
		if err != nil {
			// TODO: Check if any clean up is required here for linux nodes only?
			return assignedIPv4Address, err
		}
		eni := &eni{
			remainingCapacity: ipLimit - want,
			eniID:             *nwInterface.NetworkInterfaceId,
		}
		e.attachedENIs = append(e.attachedENIs, eni)
		for _, assignedIP := range nwInterface.PrivateIpAddresses {
			if !*assignedIP.Primary {
				assignedIPv4Address = append(assignedIPv4Address, *assignedIP.PrivateIpAddress)
				// Also add the mapping from IP to ENI
				e.ipToENIMap[*assignedIP.PrivateIpAddress] = eni
			}
		}
	}

	var err error
	// This can happen if the subnet doesn't have remaining IPs
	if len(assignedIPv4Address) < required {
		err = fmt.Errorf("not able to create the desired number of IPv4 addresses, required %d, created %d",
			required, len(assignedIPv4Address))
	}

	return e.addSubnetMaskToIPSlice(assignedIPv4Address), err
}