github.com/imran-kn/cilium-fork@v1.6.9/pkg/aws/ec2/mock/mock.go (about)

     1  // Copyright 2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package mock
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"time"
    21  
    22  	"github.com/cilium/cilium/pkg/aws/types"
    23  	"github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    24  	"github.com/cilium/cilium/pkg/lock"
    25  	"github.com/cilium/cilium/pkg/uuid"
    26  
    27  	"golang.org/x/time/rate"
    28  	"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
    29  )
    30  
    31  type eniMap map[string]*v2.ENI
    32  
    33  // Operation is an EC2 API operation that this mock API supports
    34  type Operation int
    35  
    36  const (
    37  	AllOperations Operation = iota
    38  	CreateNetworkInterface
    39  	DeleteNetworkInterface
    40  	AttachNetworkInterface
    41  	ModifyNetworkInterface
    42  	AssignPrivateIpAddresses
    43  	UnassignPrivateIpAddresses
    44  	TagENI
    45  	MaxOperation
    46  )
    47  
    48  type API struct {
    49  	mutex      lock.RWMutex
    50  	unattached map[string]*v2.ENI
    51  	enis       map[string]eniMap
    52  	subnets    map[string]*types.Subnet
    53  	vpcs       map[string]*types.Vpc
    54  	errors     map[Operation]error
    55  	delays     map[Operation]time.Duration
    56  	allocator  *ipallocator.Range
    57  	limiter    *rate.Limiter
    58  }
    59  
    60  func NewAPI(subnets []*types.Subnet, vpcs []*types.Vpc) *API {
    61  	_, cidr, _ := net.ParseCIDR("10.0.0.0/8")
    62  	cidrRange, err := ipallocator.NewCIDRRange(cidr)
    63  	if err != nil {
    64  		panic(err)
    65  	}
    66  
    67  	api := &API{
    68  		unattached: map[string]*v2.ENI{},
    69  		enis:       map[string]eniMap{},
    70  		subnets:    map[string]*types.Subnet{},
    71  		vpcs:       map[string]*types.Vpc{},
    72  		allocator:  cidrRange,
    73  		errors:     map[Operation]error{},
    74  		delays:     map[Operation]time.Duration{},
    75  	}
    76  
    77  	for _, s := range subnets {
    78  		api.subnets[s.ID] = s
    79  	}
    80  
    81  	for _, v := range vpcs {
    82  		api.vpcs[v.ID] = v
    83  	}
    84  
    85  	return api
    86  }
    87  
    88  // SetMockError modifies the mock API to return an error for a particular
    89  // operation
    90  func (e *API) SetMockError(op Operation, err error) {
    91  	e.mutex.Lock()
    92  	e.errors[op] = err
    93  	e.mutex.Unlock()
    94  }
    95  
    96  func (e *API) setDelayLocked(op Operation, delay time.Duration) {
    97  	if delay == time.Duration(0) {
    98  		delete(e.delays, op)
    99  	} else {
   100  		e.delays[op] = delay
   101  	}
   102  }
   103  
   104  // SetDelay specifies the delay which should be simulated for an individual EC2
   105  // API operation
   106  func (e *API) SetDelay(op Operation, delay time.Duration) {
   107  	e.mutex.Lock()
   108  	if op == AllOperations {
   109  		for op := AllOperations + 1; op < MaxOperation; op++ {
   110  			e.setDelayLocked(op, delay)
   111  		}
   112  	} else {
   113  		e.setDelayLocked(op, delay)
   114  	}
   115  	e.mutex.Unlock()
   116  }
   117  
   118  // SetLimiter adds a rate limiter to all simulated API calls
   119  func (e *API) SetLimiter(limit float64, burst int) {
   120  	e.limiter = rate.NewLimiter(rate.Limit(limit), burst)
   121  }
   122  
   123  func (e *API) rateLimit() {
   124  	e.mutex.RLock()
   125  	if e.limiter == nil {
   126  		e.mutex.RUnlock()
   127  		return
   128  	}
   129  
   130  	r := e.limiter.Reserve()
   131  	e.mutex.RUnlock()
   132  	if delay := r.Delay(); delay != time.Duration(0) && delay != rate.InfDuration {
   133  		time.Sleep(delay)
   134  	}
   135  }
   136  
   137  func (e *API) simulateDelay(op Operation) {
   138  	e.mutex.RLock()
   139  	delay, ok := e.delays[op]
   140  	e.mutex.RUnlock()
   141  	if ok {
   142  		time.Sleep(delay)
   143  	}
   144  }
   145  
   146  func (e *API) CreateNetworkInterface(toAllocate int64, subnetID, desc string, groups []string) (string, *v2.ENI, error) {
   147  	e.rateLimit()
   148  	e.simulateDelay(CreateNetworkInterface)
   149  
   150  	e.mutex.Lock()
   151  	defer e.mutex.Unlock()
   152  
   153  	if err, ok := e.errors[CreateNetworkInterface]; ok {
   154  		return "", nil, err
   155  	}
   156  
   157  	subnet, ok := e.subnets[subnetID]
   158  	if !ok {
   159  		return "", nil, fmt.Errorf("subnet %s not found", subnetID)
   160  	}
   161  
   162  	if int(toAllocate) > subnet.AvailableAddresses {
   163  		return "", nil, fmt.Errorf("subnet %s has not enough addresses available", subnetID)
   164  	}
   165  
   166  	eniID := uuid.NewUUID().String()
   167  	eni := &v2.ENI{
   168  		ID:          eniID,
   169  		Description: desc,
   170  		Subnet: v2.AwsSubnet{
   171  			ID: subnetID,
   172  		},
   173  		SecurityGroups: groups,
   174  	}
   175  
   176  	for i := int64(0); i < toAllocate; i++ {
   177  		ip, err := e.allocator.AllocateNext()
   178  		if err != nil {
   179  			panic("Unable to allocate IP from allocator")
   180  		}
   181  		eni.Addresses = append(eni.Addresses, ip.String())
   182  	}
   183  
   184  	subnet.AvailableAddresses -= int(toAllocate)
   185  
   186  	e.unattached[eniID] = eni
   187  	return eniID, eni, nil
   188  }
   189  
   190  func (e *API) DeleteNetworkInterface(eniID string) error {
   191  	e.rateLimit()
   192  	e.simulateDelay(DeleteNetworkInterface)
   193  
   194  	e.mutex.Lock()
   195  	defer e.mutex.Unlock()
   196  
   197  	if err, ok := e.errors[DeleteNetworkInterface]; ok {
   198  		return err
   199  	}
   200  
   201  	delete(e.unattached, eniID)
   202  	for _, enis := range e.enis {
   203  		if _, ok := enis[eniID]; ok {
   204  			delete(enis, eniID)
   205  			return nil
   206  		}
   207  	}
   208  	return fmt.Errorf("ENI ID %s not found", eniID)
   209  }
   210  
   211  func (e *API) AttachNetworkInterface(index int64, instanceID, eniID string) (string, error) {
   212  	e.rateLimit()
   213  	e.simulateDelay(AttachNetworkInterface)
   214  
   215  	e.mutex.Lock()
   216  	defer e.mutex.Unlock()
   217  
   218  	if err, ok := e.errors[AttachNetworkInterface]; ok {
   219  		return "", err
   220  	}
   221  
   222  	eni, ok := e.unattached[eniID]
   223  	if !ok {
   224  		return "", fmt.Errorf("ENI ID %s does not exist", eniID)
   225  	}
   226  
   227  	delete(e.unattached, eniID)
   228  
   229  	if _, ok := e.enis[instanceID]; !ok {
   230  		e.enis[instanceID] = eniMap{}
   231  	}
   232  
   233  	eni.Number = int(index)
   234  
   235  	e.enis[instanceID][eniID] = eni
   236  
   237  	return "", nil
   238  }
   239  
   240  func (e *API) ModifyNetworkInterface(eniID, attachmentID string, deleteOnTermination bool) error {
   241  	e.rateLimit()
   242  	e.simulateDelay(ModifyNetworkInterface)
   243  
   244  	e.mutex.Lock()
   245  	defer e.mutex.Unlock()
   246  
   247  	if err, ok := e.errors[ModifyNetworkInterface]; ok {
   248  		return err
   249  	}
   250  
   251  	return nil
   252  }
   253  
   254  func (e *API) AssignPrivateIpAddresses(eniID string, addresses int64) error {
   255  	e.rateLimit()
   256  	e.simulateDelay(AssignPrivateIpAddresses)
   257  
   258  	e.mutex.Lock()
   259  	defer e.mutex.Unlock()
   260  
   261  	if err, ok := e.errors[AssignPrivateIpAddresses]; ok {
   262  		return err
   263  	}
   264  
   265  	for _, enis := range e.enis {
   266  		if eni, ok := enis[eniID]; ok {
   267  			subnet, ok := e.subnets[eni.Subnet.ID]
   268  			if !ok {
   269  				return fmt.Errorf("subnet %s not found", eni.Subnet.ID)
   270  			}
   271  
   272  			if int(addresses) > subnet.AvailableAddresses {
   273  				return fmt.Errorf("subnet %s has not enough addresses available", eni.Subnet.ID)
   274  			}
   275  
   276  			for i := int64(0); i < addresses; i++ {
   277  				ip, err := e.allocator.AllocateNext()
   278  				if err != nil {
   279  					panic("Unable to allocate IP from allocator")
   280  				}
   281  				eni.Addresses = append(eni.Addresses, ip.String())
   282  			}
   283  			subnet.AvailableAddresses -= int(addresses)
   284  			return nil
   285  		}
   286  	}
   287  	return fmt.Errorf("Unable to find ENI with ID %s", eniID)
   288  }
   289  
   290  func (e *API) UnassignPrivateIpAddresses(eniID string, addresses []string) error {
   291  	e.rateLimit()
   292  	e.simulateDelay(UnassignPrivateIpAddresses)
   293  
   294  	e.mutex.Lock()
   295  	defer e.mutex.Unlock()
   296  
   297  	if err, ok := e.errors[UnassignPrivateIpAddresses]; ok {
   298  		return err
   299  	}
   300  
   301  	releaseMap := make(map[string]int)
   302  	for _, addr := range addresses {
   303  		// Validate given addresses
   304  		ipaddr := net.ParseIP(addr)
   305  		if ipaddr == nil {
   306  			return fmt.Errorf("Invalid IP address %s", addr)
   307  		}
   308  		releaseMap[addr] = 0
   309  	}
   310  
   311  	for _, enis := range e.enis {
   312  		eni, ok := enis[eniID]
   313  		if !ok {
   314  			continue
   315  		}
   316  		subnet, ok := e.subnets[eni.Subnet.ID]
   317  		if !ok {
   318  			return fmt.Errorf("subnet %s not found", eni.Subnet.ID)
   319  		}
   320  
   321  		addressesAfterRelease := []string{}
   322  
   323  		for _, address := range eni.Addresses {
   324  			_, ok := releaseMap[address]
   325  			if !ok {
   326  				addressesAfterRelease = append(addressesAfterRelease, address)
   327  			} else {
   328  				ip := net.ParseIP(address)
   329  				e.allocator.Release(ip)
   330  				subnet.AvailableAddresses++
   331  			}
   332  		}
   333  		eni.Addresses = addressesAfterRelease
   334  		return nil
   335  	}
   336  	return fmt.Errorf("Unable to find ENI with ID %s", eniID)
   337  }
   338  
   339  func (e *API) GetInstances(vpcs types.VpcMap, subnets types.SubnetMap) (types.InstanceMap, error) {
   340  	instances := types.InstanceMap{}
   341  
   342  	e.mutex.RLock()
   343  	defer e.mutex.RUnlock()
   344  
   345  	for instanceID, enis := range e.enis {
   346  		for _, eni := range enis {
   347  			if subnets != nil {
   348  				if subnet, ok := subnets[eni.Subnet.ID]; ok {
   349  					eni.Subnet.CIDR = subnet.CIDR
   350  				}
   351  			}
   352  
   353  			if vpcs != nil {
   354  				if vpc, ok := vpcs[eni.VPC.ID]; ok {
   355  					eni.VPC.PrimaryCIDR = vpc.PrimaryCIDR
   356  				}
   357  			}
   358  
   359  			instances.Add(instanceID, eni)
   360  		}
   361  	}
   362  
   363  	return instances, nil
   364  }
   365  
   366  func (e *API) GetVpcs() (types.VpcMap, error) {
   367  	vpcs := types.VpcMap{}
   368  
   369  	e.mutex.RLock()
   370  	defer e.mutex.RUnlock()
   371  
   372  	for _, v := range e.vpcs {
   373  		vpcs[v.ID] = v
   374  	}
   375  	return vpcs, nil
   376  }
   377  
   378  func (e *API) GetSubnets() (types.SubnetMap, error) {
   379  	subnets := types.SubnetMap{}
   380  
   381  	e.mutex.RLock()
   382  	defer e.mutex.RUnlock()
   383  
   384  	for _, s := range e.subnets {
   385  		subnets[s.ID] = s
   386  	}
   387  	return subnets, nil
   388  }