github.phpd.cn/cilium/cilium@v1.6.12/pkg/aws/ec2/ec2.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 ec2 16 17 import ( 18 "context" 19 "fmt" 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/spanstat" 25 26 "github.com/aws/aws-sdk-go-v2/aws" 27 "github.com/aws/aws-sdk-go-v2/service/ec2" 28 "golang.org/x/time/rate" 29 ) 30 31 // Client represents an EC2 API client 32 type Client struct { 33 ec2Client *ec2.EC2 34 limiter *rate.Limiter 35 metricsAPI metricsAPI 36 } 37 38 type metricsAPI interface { 39 ObserveEC2APICall(call, status string, duration float64) 40 ObserveEC2RateLimit(operation string, duration time.Duration) 41 } 42 43 // NewClient returns a new EC2 client 44 func NewClient(ec2Client *ec2.EC2, metrics metricsAPI, rateLimit float64, burst int) *Client { 45 return &Client{ 46 ec2Client: ec2Client, 47 metricsAPI: metrics, 48 limiter: rate.NewLimiter(rate.Limit(rateLimit), burst), 49 } 50 } 51 52 // deriveStatus returns a status string based on the HTTP response provided by 53 // the AWS API server. If no specific status is provided, either "OK" or 54 // "Failed" is returned based on the error variable. 55 func deriveStatus(req *aws.Request, err error) string { 56 if req.HTTPResponse != nil { 57 return req.HTTPResponse.Status 58 } 59 60 if err != nil { 61 return "Failed" 62 } 63 64 return "OK" 65 } 66 67 func (c *Client) rateLimit(operation string) { 68 r := c.limiter.Reserve() 69 if delay := r.Delay(); delay != time.Duration(0) && delay != rate.InfDuration { 70 c.metricsAPI.ObserveEC2RateLimit(operation, delay) 71 c.limiter.Wait(context.TODO()) 72 } 73 } 74 75 // describeNetworkInterfaces lists all ENIs 76 func (c *Client) describeNetworkInterfaces() ([]ec2.NetworkInterface, error) { 77 var ( 78 networkInterfaces []ec2.NetworkInterface 79 nextToken string 80 ) 81 82 for { 83 c.rateLimit("DescribeNetworkInterfaces") 84 req := &ec2.DescribeNetworkInterfacesInput{} 85 if nextToken != "" { 86 req.NextToken = &nextToken 87 } 88 89 sinceStart := spanstat.Start() 90 listReq := c.ec2Client.DescribeNetworkInterfacesRequest(req) 91 response, err := listReq.Send() 92 c.metricsAPI.ObserveEC2APICall("DescribeNetworkInterfaces", deriveStatus(listReq.Request, err), sinceStart.Seconds()) 93 if err != nil { 94 return nil, err 95 } 96 97 networkInterfaces = append(networkInterfaces, response.NetworkInterfaces...) 98 99 if response.NextToken == nil || *response.NextToken == "" { 100 break 101 } else { 102 nextToken = *response.NextToken 103 } 104 } 105 106 return networkInterfaces, nil 107 } 108 109 // parseENI parses a ec2.NetworkInterface as returned by the EC2 service API, 110 // converts it into a v2. ENI object 111 func parseENI(iface *ec2.NetworkInterface, vpcs types.VpcMap, subnets types.SubnetMap) (instanceID string, eni *v2.ENI, err error) { 112 if iface.PrivateIpAddress == nil { 113 err = fmt.Errorf("ENI has no IP address") 114 return 115 } 116 117 eni = &v2.ENI{ 118 IP: *iface.PrivateIpAddress, 119 SecurityGroups: []string{}, 120 Addresses: []string{}, 121 } 122 123 if iface.MacAddress != nil { 124 eni.MAC = *iface.MacAddress 125 } 126 127 if iface.NetworkInterfaceId != nil { 128 eni.ID = *iface.NetworkInterfaceId 129 } 130 131 if iface.Description != nil { 132 eni.Description = *iface.Description 133 } 134 135 if iface.Attachment != nil { 136 if iface.Attachment.DeviceIndex != nil { 137 eni.Number = int(*iface.Attachment.DeviceIndex) 138 } 139 140 if iface.Attachment.InstanceId != nil { 141 instanceID = *iface.Attachment.InstanceId 142 } 143 } 144 145 if iface.SubnetId != nil { 146 eni.Subnet.ID = *iface.SubnetId 147 148 if subnets != nil { 149 if subnet, ok := subnets[eni.Subnet.ID]; ok { 150 eni.Subnet.CIDR = subnet.CIDR 151 } 152 } 153 } 154 155 if iface.VpcId != nil { 156 eni.VPC.ID = *iface.VpcId 157 158 if vpcs != nil { 159 if vpc, ok := vpcs[eni.VPC.ID]; ok { 160 eni.VPC.PrimaryCIDR = vpc.PrimaryCIDR 161 } 162 } 163 } 164 165 for _, ip := range iface.PrivateIpAddresses { 166 if ip.PrivateIpAddress != nil { 167 eni.Addresses = append(eni.Addresses, *ip.PrivateIpAddress) 168 } 169 } 170 171 for _, g := range iface.Groups { 172 if g.GroupId != nil { 173 eni.SecurityGroups = append(eni.SecurityGroups, *g.GroupId) 174 } 175 } 176 177 return 178 } 179 180 // GetInstances returns the list of all instances including their ENIs as 181 // instanceMap 182 func (c *Client) GetInstances(vpcs types.VpcMap, subnets types.SubnetMap) (types.InstanceMap, error) { 183 instances := types.InstanceMap{} 184 185 networkInterfaces, err := c.describeNetworkInterfaces() 186 if err != nil { 187 return nil, err 188 } 189 190 for _, iface := range networkInterfaces { 191 id, eni, err := parseENI(&iface, vpcs, subnets) 192 if err != nil { 193 return nil, err 194 } 195 196 if id != "" { 197 instances.Add(id, eni) 198 } 199 } 200 201 return instances, nil 202 } 203 204 // describeVpcs lists all VPCs 205 func (c *Client) describeVpcs() ([]ec2.Vpc, error) { 206 var vpcs []ec2.Vpc 207 208 c.rateLimit("DescribeVpcs") 209 req := &ec2.DescribeVpcsInput{} 210 211 sinceStart := spanstat.Start() 212 listReq := c.ec2Client.DescribeVpcsRequest(req) 213 response, err := listReq.Send() 214 c.metricsAPI.ObserveEC2APICall("DescribeVpcs", deriveStatus(listReq.Request, err), sinceStart.Seconds()) 215 if err != nil { 216 return nil, err 217 } 218 219 vpcs = append(vpcs, response.Vpcs...) 220 221 return vpcs, nil 222 } 223 224 // GetVpcs retrieves and returns all Vpcs 225 func (c *Client) GetVpcs() (types.VpcMap, error) { 226 vpcs := types.VpcMap{} 227 228 vpcList, err := c.describeVpcs() 229 if err != nil { 230 return nil, err 231 } 232 233 for _, v := range vpcList { 234 vpc := &types.Vpc{ID: *v.VpcId} 235 236 if v.CidrBlock != nil { 237 vpc.PrimaryCIDR = *v.CidrBlock 238 } 239 240 vpcs[vpc.ID] = vpc 241 } 242 243 return vpcs, nil 244 } 245 246 // describeSubnets lists all subnets 247 func (c *Client) describeSubnets() ([]ec2.Subnet, error) { 248 sinceStart := spanstat.Start() 249 listReq := c.ec2Client.DescribeSubnetsRequest(&ec2.DescribeSubnetsInput{}) 250 result, err := listReq.Send() 251 c.metricsAPI.ObserveEC2APICall("DescribeSubnets", deriveStatus(listReq.Request, err), sinceStart.Seconds()) 252 if err != nil { 253 return nil, err 254 } 255 256 return result.Subnets, nil 257 } 258 259 // GetSubnets returns all EC2 subnets as a subnetMap 260 func (c *Client) GetSubnets() (types.SubnetMap, error) { 261 subnets := types.SubnetMap{} 262 263 subnetList, err := c.describeSubnets() 264 if err != nil { 265 return nil, err 266 } 267 268 for _, s := range subnetList { 269 subnet := &types.Subnet{ 270 ID: *s.SubnetId, 271 CIDR: *s.CidrBlock, 272 AvailableAddresses: int(*s.AvailableIpAddressCount), 273 Tags: map[string]string{}, 274 } 275 276 if s.AvailabilityZone != nil { 277 subnet.AvailabilityZone = *s.AvailabilityZone 278 } 279 280 if s.VpcId != nil { 281 subnet.VpcID = *s.VpcId 282 } 283 284 for _, tag := range s.Tags { 285 if *tag.Key == "Name" { 286 subnet.Name = *tag.Value 287 } 288 subnet.Tags[*tag.Key] = *tag.Value 289 } 290 291 subnets[subnet.ID] = subnet 292 } 293 294 return subnets, nil 295 } 296 297 // CreateNetworkInterface creates an ENI with the given parameters 298 func (c *Client) CreateNetworkInterface(toAllocate int64, subnetID, desc string, groups []string) (string, *v2.ENI, error) { 299 createReq := &ec2.CreateNetworkInterfaceInput{ 300 Description: &desc, 301 SecondaryPrivateIpAddressCount: &toAllocate, 302 SubnetId: &subnetID, 303 } 304 for _, grp := range groups { 305 createReq.Groups = append(createReq.Groups, grp) 306 } 307 308 c.rateLimit("CreateNetworkInterface") 309 sinceStart := spanstat.Start() 310 create := c.ec2Client.CreateNetworkInterfaceRequest(createReq) 311 resp, err := create.Send() 312 c.metricsAPI.ObserveEC2APICall("CreateNetworkInterfaceRequest", deriveStatus(create.Request, err), sinceStart.Seconds()) 313 if err != nil { 314 return "", nil, err 315 } 316 317 _, eni, err := parseENI(resp.NetworkInterface, nil, nil) 318 if err != nil { 319 // The error is ignored on purpose. The allocation itself has 320 // succeeded. The ability to parse and return the ENI 321 // information is optional. Returning the ENI ID is sufficient 322 // to allow for the caller to retrieve the ENI information via 323 // the API or wait for a regular sync to fetch the information. 324 return *resp.NetworkInterface.NetworkInterfaceId, nil, nil 325 } 326 327 return eni.ID, eni, nil 328 329 } 330 331 // DeleteNetworkInterface deletes an ENI with the specified ID 332 func (c *Client) DeleteNetworkInterface(eniID string) error { 333 delReq := &ec2.DeleteNetworkInterfaceInput{} 334 delReq.NetworkInterfaceId = &eniID 335 336 c.rateLimit("DeleteNetworkInterface") 337 sinceStart := spanstat.Start() 338 req := c.ec2Client.DeleteNetworkInterfaceRequest(delReq) 339 _, err := req.Send() 340 c.metricsAPI.ObserveEC2APICall("DeleteNetworkInterface", deriveStatus(req.Request, err), sinceStart.Seconds()) 341 return err 342 } 343 344 // AttachNetworkInterface attaches a previously created ENI to an instance 345 func (c *Client) AttachNetworkInterface(index int64, instanceID, eniID string) (string, error) { 346 attachReq := &ec2.AttachNetworkInterfaceInput{ 347 DeviceIndex: &index, 348 InstanceId: &instanceID, 349 NetworkInterfaceId: &eniID, 350 } 351 352 c.rateLimit("AttachNetworkInterface") 353 sinceStart := spanstat.Start() 354 attach := c.ec2Client.AttachNetworkInterfaceRequest(attachReq) 355 attachResp, err := attach.Send() 356 c.metricsAPI.ObserveEC2APICall("AttachNetworkInterface", deriveStatus(attach.Request, err), sinceStart.Seconds()) 357 if err != nil { 358 return "", err 359 } 360 361 return *attachResp.AttachmentId, nil 362 } 363 364 // ModifyNetworkInterface modifies the attributes of an ENI 365 func (c *Client) ModifyNetworkInterface(eniID, attachmentID string, deleteOnTermination bool) error { 366 changes := &ec2.NetworkInterfaceAttachmentChanges{ 367 AttachmentId: &attachmentID, 368 DeleteOnTermination: &deleteOnTermination, 369 } 370 371 modifyReq := &ec2.ModifyNetworkInterfaceAttributeInput{ 372 Attachment: changes, 373 NetworkInterfaceId: &eniID, 374 } 375 376 c.rateLimit("ModifyNetworkInterfaceAttribute") 377 sinceStart := spanstat.Start() 378 modify := c.ec2Client.ModifyNetworkInterfaceAttributeRequest(modifyReq) 379 _, err := modify.Send() 380 c.metricsAPI.ObserveEC2APICall("ModifyNetworkInterface", deriveStatus(modify.Request, err), sinceStart.Seconds()) 381 return err 382 } 383 384 // AssignPrivateIpAddresses assigns the specified number of secondary IP 385 // addresses 386 func (c *Client) AssignPrivateIpAddresses(eniID string, addresses int64) error { 387 request := ec2.AssignPrivateIpAddressesInput{ 388 NetworkInterfaceId: &eniID, 389 SecondaryPrivateIpAddressCount: &addresses, 390 } 391 392 c.rateLimit("AssignPrivateIpAddresses") 393 sinceStart := spanstat.Start() 394 req := c.ec2Client.AssignPrivateIpAddressesRequest(&request) 395 _, err := req.Send() 396 c.metricsAPI.ObserveEC2APICall("AssignPrivateIpAddresses", deriveStatus(req.Request, err), sinceStart.Seconds()) 397 return err 398 } 399 400 // UnassignPrivateIpAddresses unassigns specified IP addresses from ENI 401 func (c *Client) UnassignPrivateIpAddresses(eniID string, addresses []string) error { 402 request := ec2.UnassignPrivateIpAddressesInput{ 403 NetworkInterfaceId: &eniID, 404 PrivateIpAddresses: addresses, 405 } 406 407 c.rateLimit("UnassignPrivateIpAddresses") 408 sinceStart := spanstat.Start() 409 req := c.ec2Client.UnassignPrivateIpAddressesRequest(&request) 410 _, err := req.Send() 411 c.metricsAPI.ObserveEC2APICall("UnassignPrivateIpAddresses", deriveStatus(req.Request, err), sinceStart.Seconds()) 412 return err 413 } 414 415 // TagENI creates the specified tags on the ENI 416 func (c *Client) TagENI(ctx context.Context, eniID string, eniTags map[string]string) error { 417 request := ec2.CreateTagsInput{ 418 Resources: []string{eniID}, 419 Tags: createAWSTagSlice(eniTags), 420 } 421 c.rateLimit("CreateTags") 422 sinceStart := spanstat.Start() 423 req := c.ec2Client.CreateTagsRequest(&request) 424 _, err := req.Send() 425 c.metricsAPI.ObserveEC2APICall("CreateTags", deriveStatus(req.Request, err), sinceStart.Seconds()) 426 return err 427 } 428 429 func createAWSTagSlice(tags map[string]string) []ec2.Tag { 430 awsTags := make([]ec2.Tag, 0, len(tags)) 431 for k, v := range tags { 432 awsTag := ec2.Tag{ 433 Key: aws.String(k), 434 Value: aws.String(v), 435 } 436 awsTags = append(awsTags, awsTag) 437 } 438 439 return awsTags 440 }