@@ -11,6 +11,7 @@ import (
1111 "k8s.io/apimachinery/pkg/util/cache"
1212 "k8s.io/apimachinery/pkg/util/sets"
1313 "net"
14+ "sigs.k8s.io/aws-load-balancer-controller/pkg/algorithm"
1415 "sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
1516 "sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
1617 "sigs.k8s.io/controller-runtime/pkg/client"
@@ -20,6 +21,10 @@ import (
2021
2122const (
2223 defaultPodENIInfoCacheTTL = 10 * time .Minute
24+ // EC2:DescribeNetworkInterface supports up to 200 filters per call.
25+ describeNetworkInterfacesFiltersLimit = 200
26+
27+ labelEKSComputeType = "eks.amazonaws.com/compute-type"
2328)
2429
2530// PodENIInfoResolver is responsible for resolve the AWS VPC ENI that supports pod network.
@@ -29,15 +34,17 @@ type PodENIInfoResolver interface {
2934}
3035
3136// NewDefaultPodENIInfoResolver constructs new defaultPodENIInfoResolver.
32- func NewDefaultPodENIInfoResolver (k8sClient client.Client , ec2Client services.EC2 , nodeInfoProvider NodeInfoProvider , logger logr.Logger ) * defaultPodENIInfoResolver {
37+ func NewDefaultPodENIInfoResolver (k8sClient client.Client , ec2Client services.EC2 , nodeInfoProvider NodeInfoProvider , vpcID string , logger logr.Logger ) * defaultPodENIInfoResolver {
3338 return & defaultPodENIInfoResolver {
34- k8sClient : k8sClient ,
35- ec2Client : ec2Client ,
36- nodeInfoProvider : nodeInfoProvider ,
37- logger : logger ,
38- podENIInfoCache : cache .NewExpiring (),
39- podENIInfoCacheMutex : sync.RWMutex {},
40- podENIInfoCacheTTL : defaultPodENIInfoCacheTTL ,
39+ k8sClient : k8sClient ,
40+ ec2Client : ec2Client ,
41+ nodeInfoProvider : nodeInfoProvider ,
42+ vpcID : vpcID ,
43+ logger : logger ,
44+ podENIInfoCache : cache .NewExpiring (),
45+ podENIInfoCacheMutex : sync.RWMutex {},
46+ podENIInfoCacheTTL : defaultPodENIInfoCacheTTL ,
47+ describeNetworkInterfacesIPChunkSize : describeNetworkInterfacesFiltersLimit - 1 , // we used 1 filter for VPC.
4148 }
4249}
4350
@@ -51,6 +58,8 @@ type defaultPodENIInfoResolver struct {
5158 ec2Client services.EC2
5259 // nodeInfoProvider
5360 nodeInfoProvider NodeInfoProvider
61+ // vpcID
62+ vpcID string
5463 // logger
5564 logger logr.Logger
5665
@@ -62,6 +71,9 @@ type defaultPodENIInfoResolver struct {
6271 // TTL for each cache entries.
6372 // Note: we assume pod's ENI information(e.g. securityGroups) haven't changed per podENICacheTTL.
6473 podENIInfoCacheTTL time.Duration
74+
75+ // chunkSize when describe network interface with IPAddress filter.
76+ describeNetworkInterfacesIPChunkSize int
6577}
6678
6779func (r * defaultPodENIInfoResolver ) Resolve (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
@@ -129,7 +141,8 @@ func (r *defaultPodENIInfoResolver) saveENIInfosToCache(pods []k8s.PodInfo, eniI
129141func (r * defaultPodENIInfoResolver ) resolveViaCascadedLookup (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
130142 resolveFuncs := []func (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ){
131143 r .resolveViaPodENIAnnotation ,
132- r .resolveViaVPCIPAddress ,
144+ r .resolveViaNodeENIs ,
145+ r .resolveViaVPCENIs ,
133146 // TODO, add support for kubenet CNI plugin(kops) by resolve via routeTable.
134147 }
135148
@@ -151,7 +164,8 @@ func (r *defaultPodENIInfoResolver) resolveViaCascadedLookup(ctx context.Context
151164 return eniInfoByPodKey , nil
152165}
153166
154- // resolveViaPodENIAnnotation tries to resolve a pod ENI via the branch ENI annotation.
167+ // resolveViaPodENIAnnotation tries to resolve pod ENI by lookup pod's ENIInfo annotation.
168+ // with aws-vpc-cni CNI plugin's SecurityGroups for pods feature, podIP is supported by branchENI, whose information is exposed as pod annotation.
155169func (r * defaultPodENIInfoResolver ) resolveViaPodENIAnnotation (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
156170 podKeysByENIID := make (map [string ][]types.NamespacedName )
157171 for _ , pod := range pods {
@@ -191,8 +205,9 @@ func (r *defaultPodENIInfoResolver) resolveViaPodENIAnnotation(ctx context.Conte
191205 return eniInfoByPodKey , nil
192206}
193207
194- // resolveViaVPCIPAddress tries to resolve Pod ENI through the Pod IPAddress within VPC.
195- func (r * defaultPodENIInfoResolver ) resolveViaVPCIPAddress (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
208+ // resolveViaNodeENIs tries to resolve Pod ENI by matching podIP against ENIs on EC2 node's ENIs.
209+ // with aws-vpc-cni CNI plugin, podIP can be supported by either IPv4Addresses or IPv4Prefixes on ENI.
210+ func (r * defaultPodENIInfoResolver ) resolveViaNodeENIs (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
196211 nodeKeysSet := make (map [types.NamespacedName ]sets.Empty )
197212 for _ , pod := range pods {
198213 nodeKey := types.NamespacedName {Name : pod .NodeName }
@@ -204,13 +219,20 @@ func (r *defaultPodENIInfoResolver) resolveViaVPCIPAddress(ctx context.Context,
204219 if err := r .k8sClient .Get (ctx , nodeKey , node ); err != nil {
205220 return nil , err
206221 }
222+ // Fargate based nodes are not EC2 instances
223+ if node .Labels [labelEKSComputeType ] == "fargate" {
224+ continue
225+ }
207226 nodes = append (nodes , node )
208227 }
228+ if len (nodes ) == 0 {
229+ return nil , nil
230+ }
231+
209232 nodeInstanceByNodeKey , err := r .nodeInfoProvider .FetchNodeInstances (ctx , nodes )
210233 if err != nil {
211234 return nil , err
212235 }
213-
214236 eniInfoByPodKey := make (map [types.NamespacedName ]ENIInfo , len (pods ))
215237 for _ , pod := range pods {
216238 nodeKey := types.NamespacedName {Name : pod .NodeName }
@@ -226,6 +248,56 @@ func (r *defaultPodENIInfoResolver) resolveViaVPCIPAddress(ctx context.Context,
226248 return eniInfoByPodKey , nil
227249}
228250
251+ // resolveViaVPCENIs tries to resolve pod ENI by matching podIP against ENIs in vpc.
252+ // with EKS fargate pods, podIP is supported by an ENI in vpc.
253+ func (r * defaultPodENIInfoResolver ) resolveViaVPCENIs (ctx context.Context , pods []k8s.PodInfo ) (map [types.NamespacedName ]ENIInfo , error ) {
254+ podKeysByIP := make (map [string ][]types.NamespacedName , len (pods ))
255+ for _ , pod := range pods {
256+ podKeysByIP [pod .PodIP ] = append (podKeysByIP [pod .PodIP ], pod .Key )
257+ }
258+ if len (podKeysByIP ) == 0 {
259+ return nil , nil
260+ }
261+
262+ podIPs := sets .StringKeySet (podKeysByIP ).List ()
263+ podIPChunks := algorithm .ChunkStrings (podIPs , r .describeNetworkInterfacesIPChunkSize )
264+ eniByID := make (map [string ]* ec2sdk.NetworkInterface )
265+ for _ , podIPChunk := range podIPChunks {
266+ req := & ec2sdk.DescribeNetworkInterfacesInput {
267+ Filters : []* ec2sdk.Filter {
268+ {
269+ Name : awssdk .String ("vpc-id" ),
270+ Values : awssdk .StringSlice ([]string {r .vpcID }),
271+ },
272+ {
273+ Name : awssdk .String ("addresses.private-ip-address" ),
274+ Values : awssdk .StringSlice (podIPChunk ),
275+ },
276+ },
277+ }
278+ enis , err := r .ec2Client .DescribeNetworkInterfacesAsList (ctx , req )
279+ if err != nil {
280+ return nil , err
281+ }
282+ for _ , eni := range enis {
283+ eniID := awssdk .StringValue (eni .NetworkInterfaceId )
284+ eniByID [eniID ] = eni
285+ }
286+ }
287+
288+ eniInfoByPodKey := make (map [types.NamespacedName ]ENIInfo )
289+ for _ , eni := range eniByID {
290+ eniInfo := buildENIInfoViaENI (eni )
291+ for _ , addr := range eni .PrivateIpAddresses {
292+ eniIP := awssdk .StringValue (addr .PrivateIpAddress )
293+ for _ , podKey := range podKeysByIP [eniIP ] {
294+ eniInfoByPodKey [podKey ] = eniInfo
295+ }
296+ }
297+ }
298+ return eniInfoByPodKey , nil
299+ }
300+
229301// isPodSupportedByNodeENI checks whether pod is supported by specific nodeENI.
230302func (r * defaultPodENIInfoResolver ) isPodSupportedByNodeENI (pod k8s.PodInfo , nodeENI * ec2sdk.InstanceNetworkInterface ) bool {
231303 for _ , ipv4Address := range nodeENI .PrivateIpAddresses {
0 commit comments