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 }