github.phpd.cn/cilium/cilium@v1.6.12/pkg/aws/eni/instances.go (about)

     1  // Copyright 2019 Authors of Cilium
     2  // Copyright 2017 Lyft, Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package eni
    17  
    18  import (
    19  	"time"
    20  
    21  	"github.com/cilium/cilium/pkg/aws/types"
    22  	"github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    23  	"github.com/cilium/cilium/pkg/lock"
    24  
    25  	"github.com/sirupsen/logrus"
    26  )
    27  
    28  type instanceAPI interface {
    29  	GetInstances(vpcs types.VpcMap, subnets types.SubnetMap) (types.InstanceMap, error)
    30  	GetSubnets() (types.SubnetMap, error)
    31  	GetVpcs() (types.VpcMap, error)
    32  }
    33  
    34  // instance is the minimal representation of an AWS instance as needed by the
    35  // ENI allocator
    36  type instance struct {
    37  	// enis is a map of all ENIs attached to the instance indexed by the
    38  	// ENI ID
    39  	enis map[string]*v2.ENI
    40  }
    41  
    42  // InstancesManager maintains the list of instances. It must be kept up to date
    43  // by calling resync() regularly.
    44  type InstancesManager struct {
    45  	mutex      lock.RWMutex
    46  	instances  types.InstanceMap
    47  	subnets    types.SubnetMap
    48  	vpcs       types.VpcMap
    49  	api        instanceAPI
    50  	metricsAPI metricsAPI
    51  }
    52  
    53  // NewInstancesManager returns a new instances manager
    54  func NewInstancesManager(api instanceAPI, metricsAPI metricsAPI) *InstancesManager {
    55  	return &InstancesManager{
    56  		instances:  types.InstanceMap{},
    57  		api:        api,
    58  		metricsAPI: metricsAPI,
    59  	}
    60  }
    61  
    62  // GetSubnet returns the subnet by subnet ID
    63  //
    64  // The returned subnet is immutable so it can be safely accessed
    65  func (m *InstancesManager) GetSubnet(subnetID string) *types.Subnet {
    66  	m.mutex.RLock()
    67  	defer m.mutex.RUnlock()
    68  
    69  	return m.subnets[subnetID]
    70  }
    71  
    72  // GetSubnets returns all the tracked subnets
    73  //
    74  // The returned subnetMap is immutable so it can be safely accessed
    75  func (m *InstancesManager) GetSubnets() types.SubnetMap {
    76  	m.mutex.RLock()
    77  	defer m.mutex.RUnlock()
    78  
    79  	subnetsCopy := make(types.SubnetMap)
    80  	for k, v := range m.subnets {
    81  		subnetsCopy[k] = v
    82  	}
    83  
    84  	return subnetsCopy
    85  }
    86  
    87  // FindSubnetByTags returns the subnet with the most addresses matching VPC ID,
    88  // availability zone and all required tags
    89  //
    90  // The returned subnet is immutable so it can be safely accessed
    91  func (m *InstancesManager) FindSubnetByTags(vpcID, availabilityZone string, required types.Tags) (bestSubnet *types.Subnet) {
    92  	m.mutex.RLock()
    93  	defer m.mutex.RUnlock()
    94  
    95  	for _, s := range m.subnets {
    96  		if s.VpcID == vpcID && s.AvailabilityZone == availabilityZone && s.Tags.Match(required) {
    97  			if bestSubnet == nil || bestSubnet.AvailableAddresses < s.AvailableAddresses {
    98  				bestSubnet = s
    99  			}
   100  		}
   101  	}
   102  
   103  	return
   104  }
   105  
   106  // Resync fetches the list of EC2 instances and subnets and updates the local
   107  // cache in the instanceManager. It returns the time when the resync has
   108  // started or time.Time{} if it did not complete.
   109  func (m *InstancesManager) Resync() time.Time {
   110  	m.metricsAPI.IncResyncCount()
   111  
   112  	resyncStart := time.Now()
   113  
   114  	vpcs, err := m.api.GetVpcs()
   115  	if err != nil {
   116  		log.WithError(err).Warning("Unable to synchronize EC2 VPC list")
   117  		return time.Time{}
   118  	}
   119  
   120  	subnets, err := m.api.GetSubnets()
   121  	if err != nil {
   122  		log.WithError(err).Warning("Unable to retrieve EC2 subnets list")
   123  		return time.Time{}
   124  	}
   125  
   126  	instances, err := m.api.GetInstances(vpcs, subnets)
   127  	if err != nil {
   128  		log.WithError(err).Warning("Unable to synchronize EC2 interface list")
   129  		return time.Time{}
   130  	}
   131  
   132  	log.WithFields(logrus.Fields{
   133  		"numENIs":    len(instances),
   134  		"numVPCs":    len(vpcs),
   135  		"numSubnets": len(subnets),
   136  	}).Info("Synchronized ENI information")
   137  
   138  	m.mutex.Lock()
   139  	m.instances = instances
   140  	m.subnets = subnets
   141  	m.vpcs = vpcs
   142  	m.mutex.Unlock()
   143  
   144  	return resyncStart
   145  }
   146  
   147  // GetENI returns the ENI of an instance at a particular interface index
   148  func (m *InstancesManager) GetENI(instanceID string, index int) *v2.ENI {
   149  	for _, eni := range m.getENIs(instanceID) {
   150  		if eni.Number == index {
   151  			return eni
   152  		}
   153  	}
   154  
   155  	return nil
   156  }
   157  
   158  // GetENIs returns the list of ENIs associated with a particular instance
   159  func (m *InstancesManager) GetENIs(instanceID string) []*v2.ENI {
   160  	return m.getENIs(instanceID)
   161  }
   162  
   163  // getENIs returns the list of ENIs associated with a particular instance
   164  func (m *InstancesManager) getENIs(instanceID string) []*v2.ENI {
   165  	m.mutex.RLock()
   166  	defer m.mutex.RUnlock()
   167  	return m.instances.Get(instanceID)
   168  }
   169  
   170  // UpdateENI updates the ENI definition of an ENI for a particular instance. If
   171  // the ENI is already known, the definition is updated, otherwise the ENI is
   172  // added to the instance.
   173  func (m *InstancesManager) UpdateENI(instanceID string, eni *v2.ENI) {
   174  	m.mutex.Lock()
   175  	m.instances.Update(instanceID, eni)
   176  	m.mutex.Unlock()
   177  }