in pkg/provider/branch/trunk/trunk.go [201:348]
func (t *trunkENI) InitTrunk(instance ec2.EC2Instance, podList []v1.Pod) error {
instanceID := t.instance.InstanceID()
log := t.log.WithValues("request", "initialize", "instance ID", instanceID)
nwInterfaces, err := t.ec2ApiHelper.GetInstanceNetworkInterface(&instanceID)
if err != nil {
trunkENIOperationsErrCount.WithLabelValues("describe_instance_nw_interface").Inc()
return err
}
var trunk ec2types.InstanceNetworkInterface
// Get trunk network interface
for _, nwInterface := range nwInterfaces {
// It's possible to get an empty network interface response if the instance is being deleted.
if nwInterface.InterfaceType == nil {
return fmt.Errorf("received an empty network interface response "+
"from EC2 %+v", nwInterface)
}
if *nwInterface.InterfaceType == "trunk" {
// Check that the trunkENI is in attached state before adding to cache
if err = t.ec2ApiHelper.WaitForNetworkInterfaceStatusChange(nwInterface.NetworkInterfaceId, string(ec2types.AttachmentStatusAttached)); err == nil {
t.trunkENIId = *nwInterface.NetworkInterfaceId
} else {
return fmt.Errorf("failed to verify network interface status attached for %v", *nwInterface.NetworkInterfaceId)
}
trunk = nwInterface
}
}
// Trunk interface doesn't exists, try to create a new trunk interface
if t.trunkENIId == "" {
freeIndex, err := instance.GetHighestUnusedDeviceIndex()
if err != nil {
trunkENIOperationsErrCount.WithLabelValues("find_free_index").Inc()
log.Error(err, "failed to find free device index")
return err
}
trunk, err := t.ec2ApiHelper.CreateAndAttachNetworkInterface(&instanceID, aws.String(t.instance.SubnetID()),
t.instance.CurrentInstanceSecurityGroups(), t.nodeIDTag, &freeIndex, &TrunkEniDescription, &InterfaceTypeTrunk, nil)
if err != nil {
trunkENIOperationsErrCount.WithLabelValues("create_trunk_eni").Inc()
return err
}
t.trunkENIId = *trunk.NetworkInterfaceId
log.Info("created a new trunk interface", "trunk id", t.trunkENIId)
return nil
}
// the node already have trunk, let's check if its SGs and Subnets match with expected
expectedSubnetID, expectedSecurityGroups := t.instance.GetCustomNetworkingSpec()
if len(expectedSecurityGroups) > 0 || expectedSubnetID != "" {
slices.Sort(expectedSecurityGroups)
trunkSGs := lo.Map(trunk.Groups, func(g ec2types.GroupIdentifier, _ int) string {
return lo.FromPtr(g.GroupId)
})
slices.Sort(trunkSGs)
mismatchedSubnets := expectedSubnetID != lo.FromPtr(trunk.SubnetId)
mismatchedSGs := !slices.Equal(expectedSecurityGroups, trunkSGs)
extraSGsInTrunk, missingSGsInTrunk := lo.Difference(trunkSGs, expectedSecurityGroups)
t.log.Info("Observed trunk ENI config",
"instanceID", t.instance.InstanceID(),
"trunkENIID", lo.FromPtr(trunk.NetworkInterfaceId),
"configuredTrunkSGs", trunkSGs,
"configuredTrunkSubnet", lo.FromPtr(trunk.SubnetId),
"desiredTrunkSGs", expectedSecurityGroups,
"desiredTrunkSubnet", expectedSubnetID,
"mismatchedSGs", mismatchedSGs,
"mismatchedSubnets", mismatchedSubnets,
"missingSGs", missingSGsInTrunk,
"extraSGs", extraSGsInTrunk,
)
if mismatchedSGs {
unreconciledTrunkENICount.WithLabelValues("security_groups").Inc()
}
if mismatchedSubnets {
unreconciledTrunkENICount.WithLabelValues("subnet").Inc()
}
}
// Get the list of branch ENIs
branchInterfaces, err := t.ec2ApiHelper.GetBranchNetworkInterface(&t.trunkENIId, aws.String(t.instance.SubnetID()))
if err != nil {
return err
}
// Convert the list of interfaces to a set
associatedBranchInterfaces := make(map[string]*ec2types.NetworkInterface)
for _, branchInterface := range branchInterfaces {
associatedBranchInterfaces[*branchInterface.NetworkInterfaceId] = branchInterface
}
// From the list of pods on the given node, and the branch ENIs from EC2 API call rebuild the internal cache
for _, pod := range podList {
pod := pod // Fix gosec G601, so we can use &node
eniListFromPod := t.getBranchInterfacesUsedByPod(&pod)
if len(eniListFromPod) == 0 {
continue
}
var branchENIs []*ENIDetails
for _, eni := range eniListFromPod {
_, isPresent := associatedBranchInterfaces[eni.ID]
if !isPresent {
t.log.Error(fmt.Errorf("eni allocated to pod not found in ec2"), "eni not found", "eni", eni)
trunkENIOperationsErrCount.WithLabelValues("get_branch_eni_from_ec2").Inc()
continue
}
// Mark the Vlan ID from the pod's annotation
t.markVlanAssigned(eni.VlanID)
branchENIs = append(branchENIs, eni)
delete(associatedBranchInterfaces, eni.ID)
}
t.uidToBranchENIMap[string(pod.UID)] = branchENIs
}
// Delete the branch ENI that don't belong to any pod.
for _, branchInterface := range associatedBranchInterfaces {
t.log.Info("pushing eni to delete queue as no pod owns it", "eni",
*branchInterface.NetworkInterfaceId)
vlanId, err := t.getVlanIdFromTag(branchInterface.TagSet)
if err != nil {
trunkENIOperationsErrCount.WithLabelValues("get_vlan_from_tag").Inc()
log.Error(err, "failed to find vlan id", "interface", *branchInterface.NetworkInterfaceId)
continue
}
// Even thought the ENI is going to be deleted still mark Vlan ID assigned as ENI will sit in cool down queue for a while
t.markVlanAssigned(vlanId)
t.pushENIToDeleteQueue(&ENIDetails{
ID: *branchInterface.NetworkInterfaceId,
VlanID: vlanId,
deletionTimeStamp: time.Now(),
})
}
log.V(1).Info("successfully initialized trunk with all associated branch interfaces",
"trunk", t.trunkENIId, "branch interfaces", t.uidToBranchENIMap)
return nil
}