github.com/cilium/cilium@v1.16.2/pkg/alibabacloud/api/mock/mock.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package mock
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"net"
    10  
    11  	"github.com/google/uuid"
    12  
    13  	eniTypes "github.com/cilium/cilium/pkg/alibabacloud/eni/types"
    14  	"github.com/cilium/cilium/pkg/alibabacloud/types"
    15  	"github.com/cilium/cilium/pkg/ipam/service/ipallocator"
    16  	ipamTypes "github.com/cilium/cilium/pkg/ipam/types"
    17  	"github.com/cilium/cilium/pkg/lock"
    18  )
    19  
    20  // ENIMap is a map of ENI interfaced indexed by ENI ID
    21  type ENIMap map[string]*eniTypes.ENI
    22  
    23  type API struct {
    24  	mutex          lock.RWMutex
    25  	unattached     map[string]*eniTypes.ENI
    26  	enis           map[string]ENIMap
    27  	subnets        map[string]*ipamTypes.Subnet
    28  	vpcs           map[string]*ipamTypes.VirtualNetwork
    29  	securityGroups map[string]*types.SecurityGroup
    30  	allocator      *ipallocator.Range
    31  }
    32  
    33  // NewAPI returns a new mocked ECS API
    34  func NewAPI(subnets []*ipamTypes.Subnet, vpcs []*ipamTypes.VirtualNetwork, securityGroups []*types.SecurityGroup) *API {
    35  	_, cidr, _ := net.ParseCIDR("10.0.0.0/8")
    36  
    37  	api := &API{
    38  		unattached:     map[string]*eniTypes.ENI{},
    39  		enis:           map[string]ENIMap{},
    40  		subnets:        map[string]*ipamTypes.Subnet{},
    41  		vpcs:           map[string]*ipamTypes.VirtualNetwork{},
    42  		securityGroups: map[string]*types.SecurityGroup{},
    43  		allocator:      ipallocator.NewCIDRRange(cidr),
    44  	}
    45  
    46  	api.UpdateSubnets(subnets)
    47  	api.UpdateSecurityGroups(securityGroups)
    48  
    49  	for _, v := range vpcs {
    50  		api.vpcs[v.ID] = v
    51  	}
    52  
    53  	return api
    54  }
    55  
    56  // UpdateSubnets replaces the subents which the mock API will return
    57  func (a *API) UpdateSubnets(subnets []*ipamTypes.Subnet) {
    58  	a.mutex.Lock()
    59  	defer a.mutex.Unlock()
    60  	a.subnets = map[string]*ipamTypes.Subnet{}
    61  	for _, s := range subnets {
    62  		a.subnets[s.ID] = s.DeepCopy()
    63  	}
    64  }
    65  
    66  // UpdateSecurityGroups replaces the security groups which the mock API will return
    67  func (a *API) UpdateSecurityGroups(securityGroups []*types.SecurityGroup) {
    68  	a.mutex.Lock()
    69  	defer a.mutex.Unlock()
    70  	a.securityGroups = map[string]*types.SecurityGroup{}
    71  	for _, sg := range securityGroups {
    72  		a.securityGroups[sg.ID] = sg.DeepCopy()
    73  	}
    74  }
    75  
    76  // UpdateENIs replaces the ENIs which the mock API will return
    77  func (a *API) UpdateENIs(enis map[string]ENIMap) {
    78  	a.mutex.Lock()
    79  	a.enis = map[string]ENIMap{}
    80  	for instanceID, m := range enis {
    81  		a.enis[instanceID] = ENIMap{}
    82  		for eniID, eni := range m {
    83  			a.enis[instanceID][eniID] = eni.DeepCopy()
    84  		}
    85  	}
    86  	a.mutex.Unlock()
    87  }
    88  
    89  func (a *API) GetInstance(ctx context.Context, vpcs ipamTypes.VirtualNetworkMap, subnets ipamTypes.SubnetMap, instanceID string) (*ipamTypes.Instance, error) {
    90  	instance := ipamTypes.Instance{}
    91  	instance.Interfaces = map[string]ipamTypes.InterfaceRevision{}
    92  
    93  	a.mutex.RLock()
    94  	defer a.mutex.RUnlock()
    95  
    96  	for id, enis := range a.enis {
    97  		if id != instanceID {
    98  			continue
    99  		}
   100  		for ifaceID, eni := range enis {
   101  			if subnets != nil {
   102  				if subnet, ok := subnets[eni.VSwitch.VSwitchID]; ok && subnet.CIDR != nil {
   103  					eni.VSwitch.CIDRBlock = subnet.CIDR.String()
   104  					eni.ZoneID = subnet.AvailabilityZone
   105  				}
   106  			}
   107  
   108  			if vpcs != nil {
   109  				if vpc, ok := vpcs[eni.VPC.VPCID]; ok {
   110  					eni.VPC.CIDRBlock = vpc.PrimaryCIDR
   111  					eni.VPC.SecondaryCIDRs = vpc.CIDRs
   112  				}
   113  			}
   114  
   115  			eniRevision := ipamTypes.InterfaceRevision{Resource: eni.DeepCopy()}
   116  			instance.Interfaces[ifaceID] = eniRevision
   117  		}
   118  	}
   119  
   120  	return &instance, nil
   121  }
   122  
   123  func (a *API) GetInstances(ctx context.Context, vpcs ipamTypes.VirtualNetworkMap, subnets ipamTypes.SubnetMap) (*ipamTypes.InstanceMap, error) {
   124  	instances := ipamTypes.NewInstanceMap()
   125  
   126  	a.mutex.RLock()
   127  	defer a.mutex.RUnlock()
   128  
   129  	for instanceID, enis := range a.enis {
   130  		for _, eni := range enis {
   131  			if subnets != nil {
   132  				if subnet, ok := subnets[eni.VSwitch.VSwitchID]; ok && subnet.CIDR != nil {
   133  					eni.VSwitch.CIDRBlock = subnet.CIDR.String()
   134  					eni.ZoneID = subnet.AvailabilityZone
   135  				}
   136  			}
   137  
   138  			if vpcs != nil {
   139  				if vpc, ok := vpcs[eni.VPC.VPCID]; ok {
   140  					eni.VPC.CIDRBlock = vpc.PrimaryCIDR
   141  					eni.VPC.SecondaryCIDRs = vpc.CIDRs
   142  				}
   143  			}
   144  
   145  			eniRevision := ipamTypes.InterfaceRevision{Resource: eni.DeepCopy()}
   146  			instances.Update(instanceID, eniRevision)
   147  		}
   148  	}
   149  
   150  	return instances, nil
   151  }
   152  
   153  func (a *API) GetVSwitches(ctx context.Context) (ipamTypes.SubnetMap, error) {
   154  	subnets := ipamTypes.SubnetMap{}
   155  
   156  	a.mutex.RLock()
   157  	defer a.mutex.RUnlock()
   158  
   159  	for _, s := range a.subnets {
   160  		subnets[s.ID] = s.DeepCopy()
   161  	}
   162  	return subnets, nil
   163  }
   164  
   165  func (a *API) GetVPC(ctx context.Context, vpcID string) (*ipamTypes.VirtualNetwork, error) {
   166  	a.mutex.RLock()
   167  	defer a.mutex.RUnlock()
   168  	vpc, ok := a.vpcs[vpcID]
   169  	if !ok {
   170  		return nil, fmt.Errorf("can't found vpc by id %s", vpcID)
   171  	}
   172  	return vpc.DeepCopy(), nil
   173  }
   174  
   175  func (a *API) GetVPCs(ctx context.Context) (ipamTypes.VirtualNetworkMap, error) {
   176  	vpcs := ipamTypes.VirtualNetworkMap{}
   177  
   178  	a.mutex.RLock()
   179  	defer a.mutex.RUnlock()
   180  
   181  	for _, v := range a.vpcs {
   182  		vpcs[v.ID] = v.DeepCopy()
   183  	}
   184  	return vpcs, nil
   185  }
   186  
   187  func (a *API) GetSecurityGroups(ctx context.Context) (types.SecurityGroupMap, error) {
   188  	securityGroups := types.SecurityGroupMap{}
   189  
   190  	a.mutex.RLock()
   191  	defer a.mutex.RUnlock()
   192  
   193  	for _, sg := range a.securityGroups {
   194  		securityGroups[sg.ID] = sg.DeepCopy()
   195  	}
   196  	return securityGroups, nil
   197  }
   198  
   199  func (a *API) CreateNetworkInterface(ctx context.Context, secondaryPrivateIPCount int, vSwitchID string, groups []string, tags map[string]string) (string, *eniTypes.ENI, error) {
   200  	a.mutex.Lock()
   201  	defer a.mutex.Unlock()
   202  
   203  	vsw, ok := a.subnets[vSwitchID]
   204  	if !ok {
   205  		return "", nil, fmt.Errorf("can not found vSwitch by id %s", vSwitchID)
   206  	}
   207  	if secondaryPrivateIPCount+1 > vsw.AvailableAddresses {
   208  		return "", nil, fmt.Errorf("vSwitch %s has not enough addresses available", vsw.ID)
   209  	}
   210  
   211  	eniID := uuid.New().String()
   212  	eni := &eniTypes.ENI{
   213  		NetworkInterfaceID: eniID,
   214  		VSwitch: eniTypes.VSwitch{
   215  			VSwitchID: vSwitchID,
   216  			CIDRBlock: vsw.CIDR.String(),
   217  		},
   218  		Type:             eniTypes.ENITypeSecondary,
   219  		SecurityGroupIDs: groups,
   220  		Tags:             tags,
   221  	}
   222  	for i := 0; i < secondaryPrivateIPCount+1; i++ {
   223  		ip, err := a.allocator.AllocateNext()
   224  		if err != nil {
   225  			panic("Unable to allocate IP from allocator")
   226  		}
   227  		primary := false
   228  		if eni.PrimaryIPAddress == "" {
   229  			eni.PrimaryIPAddress = ip.String()
   230  			primary = true
   231  		}
   232  		eni.PrivateIPSets = append(eni.PrivateIPSets, eniTypes.PrivateIPSet{
   233  			PrivateIpAddress: ip.String(),
   234  			Primary:          primary,
   235  		})
   236  	}
   237  
   238  	vsw.AvailableAddresses -= secondaryPrivateIPCount + 1
   239  
   240  	a.unattached[eniID] = eni
   241  	return eniID, eni.DeepCopy(), nil
   242  }
   243  
   244  func (a *API) AttachNetworkInterface(ctx context.Context, instanceID, eniID string) error {
   245  	a.mutex.Lock()
   246  	defer a.mutex.Unlock()
   247  
   248  	eni, ok := a.unattached[eniID]
   249  	if !ok {
   250  		return fmt.Errorf("ENI ID %s does not exist", eniID)
   251  	}
   252  
   253  	delete(a.unattached, eniID)
   254  
   255  	if _, ok := a.enis[instanceID]; !ok {
   256  		a.enis[instanceID] = ENIMap{}
   257  	}
   258  
   259  	a.enis[instanceID][eniID] = eni
   260  	return nil
   261  }
   262  
   263  func (a *API) WaitENIAttached(ctx context.Context, eniID string) (string, error) {
   264  	return "", nil
   265  }
   266  
   267  func (a *API) DeleteNetworkInterface(ctx context.Context, eniID string) error {
   268  	a.mutex.Lock()
   269  	defer a.mutex.Unlock()
   270  
   271  	delete(a.unattached, eniID)
   272  	for _, enis := range a.enis {
   273  		if _, ok := enis[eniID]; ok {
   274  			delete(enis, eniID)
   275  			return nil
   276  		}
   277  	}
   278  	return fmt.Errorf("ENI ID %s not found", eniID)
   279  }
   280  
   281  func (a *API) AssignPrivateIPAddresses(ctx context.Context, eniID string, toAllocate int) ([]string, error) {
   282  	a.mutex.Lock()
   283  	defer a.mutex.Unlock()
   284  
   285  	for _, enis := range a.enis {
   286  		if eni, ok := enis[eniID]; ok {
   287  			subnet, ok := a.subnets[eni.VSwitch.VSwitchID]
   288  			if !ok {
   289  				return nil, fmt.Errorf("vSwitch %s not found", eni.VSwitch.VSwitchID)
   290  			}
   291  
   292  			if toAllocate > subnet.AvailableAddresses {
   293  				return nil, fmt.Errorf("vSwitch %s don't have enough addresses available", eni.VSwitch.VSwitchID)
   294  			}
   295  
   296  			for i := 0; i < toAllocate; i++ {
   297  				ip, err := a.allocator.AllocateNext()
   298  				if err != nil {
   299  					panic("Unable to allocate IP from allocator")
   300  				}
   301  				primary := false
   302  				if eni.PrimaryIPAddress == "" {
   303  					eni.PrimaryIPAddress = ip.String()
   304  					primary = true
   305  				}
   306  				eni.PrivateIPSets = append(eni.PrivateIPSets, eniTypes.PrivateIPSet{
   307  					PrivateIpAddress: ip.String(),
   308  					Primary:          primary,
   309  				})
   310  			}
   311  			subnet.AvailableAddresses -= toAllocate
   312  			return nil, nil
   313  		}
   314  	}
   315  	return nil, fmt.Errorf("unable to find ENI with ID %s", eniID)
   316  }
   317  
   318  func (a *API) UnassignPrivateIPAddresses(ctx context.Context, eniID string, addresses []string) error {
   319  	a.mutex.Lock()
   320  	defer a.mutex.Unlock()
   321  
   322  	releaseMap := make(map[string]int)
   323  	for _, addr := range addresses {
   324  		// Validate given addresses
   325  		ipaddr := net.ParseIP(addr)
   326  		if ipaddr == nil {
   327  			return fmt.Errorf("invalid IP address %s", addr)
   328  		}
   329  		releaseMap[addr] = 0
   330  	}
   331  
   332  	for _, enis := range a.enis {
   333  		eni, ok := enis[eniID]
   334  		if !ok {
   335  			continue
   336  		}
   337  		subnet, ok := a.subnets[eni.VSwitch.VSwitchID]
   338  		if !ok {
   339  			return fmt.Errorf("vSwitch %s not found", eni.VSwitch.VSwitchID)
   340  		}
   341  
   342  		addressesAfterRelease := []eniTypes.PrivateIPSet{}
   343  
   344  		for _, address := range eni.PrivateIPSets {
   345  			if address.Primary {
   346  				continue
   347  			}
   348  			_, ok := releaseMap[address.PrivateIpAddress]
   349  			if !ok {
   350  				addressesAfterRelease = append(addressesAfterRelease, address)
   351  			} else {
   352  				ip := net.ParseIP(address.PrivateIpAddress)
   353  				a.allocator.Release(ip)
   354  				subnet.AvailableAddresses++
   355  			}
   356  		}
   357  		eni.PrivateIPSets = addressesAfterRelease
   358  		return nil
   359  	}
   360  	return fmt.Errorf("unable to find ENI with ID %s", eniID)
   361  }