1use crate::aws::aws_request_utils::{AwsRequestBuilderExt, AwsSignConfig};
22use crate::aws::credential_provider::AwsCredentialProvider;
23use alien_client_core::{ErrorData, Result};
24use base64::{engine::general_purpose::STANDARD, Engine as _};
25
26use alien_error::ContextError;
27use async_trait::async_trait;
28use bon::Builder;
29use form_urlencoded;
30use reqwest::{Client, Method, StatusCode};
31use serde::de::DeserializeOwned;
32use serde::{Deserialize, Serialize};
33use std::collections::HashMap;
34
35#[cfg(feature = "test-utils")]
36use mockall::automock;
37
38#[derive(Debug, Deserialize)]
43#[serde(rename_all = "PascalCase")]
44struct Ec2ErrorResponse {
45 pub errors: Ec2ErrorsWrapper,
46}
47
48#[derive(Debug, Deserialize)]
49#[serde(rename_all = "PascalCase")]
50struct Ec2ErrorsWrapper {
51 #[serde(rename = "Error")]
52 pub error: Ec2ErrorDetails,
53}
54
55#[derive(Debug, Deserialize)]
56#[serde(rename_all = "PascalCase")]
57struct Ec2ErrorDetails {
58 pub code: String,
59 pub message: String,
60}
61
62#[cfg_attr(feature = "test-utils", automock)]
67#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
68#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
69pub trait Ec2Api: Send + Sync + std::fmt::Debug {
70 async fn describe_vpcs(&self, request: DescribeVpcsRequest) -> Result<DescribeVpcsResponse>;
72 async fn describe_vpc_attribute(
73 &self,
74 request: DescribeVpcAttributeRequest,
75 ) -> Result<DescribeVpcAttributeResponse>;
76 async fn create_vpc(&self, request: CreateVpcRequest) -> Result<CreateVpcResponse>;
77 async fn delete_vpc(&self, vpc_id: &str) -> Result<()>;
78 async fn modify_vpc_attribute(&self, request: ModifyVpcAttributeRequest) -> Result<()>;
79
80 async fn describe_subnets(
82 &self,
83 request: DescribeSubnetsRequest,
84 ) -> Result<DescribeSubnetsResponse>;
85 async fn create_subnet(&self, request: CreateSubnetRequest) -> Result<CreateSubnetResponse>;
86 async fn delete_subnet(&self, subnet_id: &str) -> Result<()>;
87
88 async fn create_internet_gateway(
90 &self,
91 request: CreateInternetGatewayRequest,
92 ) -> Result<CreateInternetGatewayResponse>;
93 async fn delete_internet_gateway(&self, internet_gateway_id: &str) -> Result<()>;
94 async fn attach_internet_gateway(&self, request: AttachInternetGatewayRequest) -> Result<()>;
95 async fn detach_internet_gateway(&self, request: DetachInternetGatewayRequest) -> Result<()>;
96 async fn describe_internet_gateways(
97 &self,
98 request: DescribeInternetGatewaysRequest,
99 ) -> Result<DescribeInternetGatewaysResponse>;
100
101 async fn create_nat_gateway(
103 &self,
104 request: CreateNatGatewayRequest,
105 ) -> Result<CreateNatGatewayResponse>;
106 async fn delete_nat_gateway(&self, nat_gateway_id: &str) -> Result<DeleteNatGatewayResponse>;
107 async fn describe_nat_gateways(
108 &self,
109 request: DescribeNatGatewaysRequest,
110 ) -> Result<DescribeNatGatewaysResponse>;
111
112 async fn allocate_address(
114 &self,
115 request: AllocateAddressRequest,
116 ) -> Result<AllocateAddressResponse>;
117 async fn release_address(&self, allocation_id: &str) -> Result<()>;
118
119 async fn describe_route_tables(
121 &self,
122 request: DescribeRouteTablesRequest,
123 ) -> Result<DescribeRouteTablesResponse>;
124 async fn create_route_table(
125 &self,
126 request: CreateRouteTableRequest,
127 ) -> Result<CreateRouteTableResponse>;
128 async fn delete_route_table(&self, route_table_id: &str) -> Result<()>;
129 async fn create_route(&self, request: CreateRouteRequest) -> Result<()>;
130 async fn delete_route(&self, request: DeleteRouteRequest) -> Result<()>;
131 async fn associate_route_table(
132 &self,
133 request: AssociateRouteTableRequest,
134 ) -> Result<AssociateRouteTableResponse>;
135 async fn disassociate_route_table(&self, association_id: &str) -> Result<()>;
136
137 async fn describe_security_groups(
139 &self,
140 request: DescribeSecurityGroupsRequest,
141 ) -> Result<DescribeSecurityGroupsResponse>;
142 async fn describe_network_interfaces(
143 &self,
144 request: DescribeNetworkInterfacesRequest,
145 ) -> Result<DescribeNetworkInterfacesResponse>;
146 async fn create_security_group(
147 &self,
148 request: CreateSecurityGroupRequest,
149 ) -> Result<CreateSecurityGroupResponse>;
150 async fn delete_security_group(&self, group_id: &str) -> Result<()>;
151 async fn authorize_security_group_ingress(
152 &self,
153 request: AuthorizeSecurityGroupIngressRequest,
154 ) -> Result<()>;
155 async fn authorize_security_group_egress(
156 &self,
157 request: AuthorizeSecurityGroupEgressRequest,
158 ) -> Result<()>;
159 async fn revoke_security_group_ingress(
160 &self,
161 request: RevokeSecurityGroupIngressRequest,
162 ) -> Result<()>;
163 async fn revoke_security_group_egress(
164 &self,
165 request: RevokeSecurityGroupEgressRequest,
166 ) -> Result<()>;
167
168 async fn describe_availability_zones(
170 &self,
171 request: DescribeAvailabilityZonesRequest,
172 ) -> Result<DescribeAvailabilityZonesResponse>;
173
174 async fn describe_images(
176 &self,
177 request: DescribeImagesRequest,
178 ) -> Result<DescribeImagesResponse>;
179
180 async fn terminate_instances(
182 &self,
183 instance_ids: Vec<String>,
184 ) -> Result<TerminateInstancesResponse>;
185 async fn describe_instances(
186 &self,
187 request: DescribeInstancesRequest,
188 ) -> Result<DescribeInstancesResponse>;
189
190 async fn create_volume(&self, request: CreateVolumeRequest) -> Result<CreateVolumeResponse>;
192 async fn modify_volume(&self, request: ModifyVolumeRequest) -> Result<ModifyVolumeResponse>;
193 async fn describe_volumes_modifications(
194 &self,
195 request: DescribeVolumesModificationsRequest,
196 ) -> Result<DescribeVolumesModificationsResponse>;
197 async fn delete_volume(&self, volume_id: &str) -> Result<()>;
198 async fn describe_volumes(
199 &self,
200 request: DescribeVolumesRequest,
201 ) -> Result<DescribeVolumesResponse>;
202 async fn attach_volume(&self, request: AttachVolumeRequest) -> Result<AttachVolumeResponse>;
203 async fn detach_volume(&self, request: DetachVolumeRequest) -> Result<DetachVolumeResponse>;
204
205 async fn create_launch_template(
207 &self,
208 request: CreateLaunchTemplateRequest,
209 ) -> Result<CreateLaunchTemplateResponse>;
210 async fn create_launch_template_version(
214 &self,
215 request: CreateLaunchTemplateVersionRequest,
216 ) -> Result<CreateLaunchTemplateVersionResponse>;
217 async fn delete_launch_template(
218 &self,
219 request: DeleteLaunchTemplateRequest,
220 ) -> Result<DeleteLaunchTemplateResponse>;
221 async fn describe_launch_templates(
222 &self,
223 request: DescribeLaunchTemplatesRequest,
224 ) -> Result<DescribeLaunchTemplatesResponse>;
225
226 async fn get_console_output(&self, instance_id: String) -> Result<GetConsoleOutputResponse>;
230}
231
232#[derive(Debug, Clone)]
237pub struct Ec2Client {
238 client: Client,
239 credentials: AwsCredentialProvider,
240}
241
242impl Ec2Client {
243 pub fn new(client: Client, credentials: AwsCredentialProvider) -> Self {
245 Self {
246 client,
247 credentials,
248 }
249 }
250
251 fn sign_config(&self) -> AwsSignConfig {
252 AwsSignConfig {
253 service_name: "ec2".into(),
254 region: self.credentials.region().to_string(),
255 credentials: self.credentials.get_credentials(),
256 signing_region: None,
257 }
258 }
259
260 fn get_base_url(&self) -> String {
261 if let Some(override_url) = self.credentials.get_service_endpoint_option("ec2") {
262 override_url.to_string()
263 } else {
264 format!("https://ec2.{}.amazonaws.com", self.credentials.region())
265 }
266 }
267
268 fn get_host(&self) -> String {
269 format!("ec2.{}.amazonaws.com", self.credentials.region())
270 }
271
272 async fn send_form<T: DeserializeOwned + Send + 'static>(
275 &self,
276 form_data: HashMap<String, String>,
277 operation: &str,
278 resource: &str,
279 ) -> Result<T> {
280 self.credentials.ensure_fresh().await?;
281 let url = self.get_base_url();
282
283 let form_body = form_urlencoded::Serializer::new(String::new())
284 .extend_pairs(form_data.iter())
285 .finish();
286
287 let builder = self
288 .client
289 .request(Method::POST, &url)
290 .host(&self.get_host())
291 .content_type_form()
292 .content_sha256(&form_body)
293 .body(form_body.clone());
294
295 let result =
296 crate::aws::aws_request_utils::sign_send_xml(builder, &self.sign_config()).await;
297
298 Self::map_result(result, operation, resource, Some(&form_body))
299 }
300
301 async fn send_form_no_body(
302 &self,
303 form_data: HashMap<String, String>,
304 operation: &str,
305 resource: &str,
306 ) -> Result<()> {
307 self.credentials.ensure_fresh().await?;
308 let url = self.get_base_url();
309
310 let form_body = form_urlencoded::Serializer::new(String::new())
311 .extend_pairs(form_data.iter())
312 .finish();
313
314 let builder = self
315 .client
316 .request(Method::POST, &url)
317 .host(&self.get_host())
318 .content_type_form()
319 .content_sha256(&form_body)
320 .body(form_body.clone());
321
322 let result =
323 crate::aws::aws_request_utils::sign_send_no_response(builder, &self.sign_config())
324 .await;
325
326 Self::map_result(result, operation, resource, Some(&form_body))
327 }
328
329 fn map_result<T>(
330 result: Result<T>,
331 operation: &str,
332 resource: &str,
333 request_body: Option<&str>,
334 ) -> Result<T> {
335 match result {
336 Ok(v) => Ok(v),
337 Err(e) => {
338 if let Some(ErrorData::HttpResponseError {
339 http_status,
340 http_response_text: Some(ref text),
341 ..
342 }) = &e.error
343 {
344 let status = StatusCode::from_u16(*http_status)
345 .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
346 if let Some(mapped) =
347 Self::map_ec2_error(status, text, operation, resource, request_body)
348 {
349 Err(e.context(mapped))
350 } else {
351 Err(e)
352 }
353 } else {
354 Err(e)
355 }
356 }
357 }
358 }
359
360 fn map_ec2_error(
361 status: StatusCode,
362 body: &str,
363 _operation: &str,
364 resource: &str,
365 request_body: Option<&str>,
366 ) -> Option<ErrorData> {
367 if body.trim().is_empty() {
369 return match status {
370 StatusCode::NOT_FOUND => Some(ErrorData::RemoteResourceNotFound {
371 resource_type: "EC2 Resource".into(),
372 resource_name: resource.into(),
373 }),
374 StatusCode::CONFLICT => Some(ErrorData::RemoteResourceConflict {
375 message: "Resource conflict".into(),
376 resource_type: "EC2 Resource".into(),
377 resource_name: resource.into(),
378 }),
379 StatusCode::FORBIDDEN | StatusCode::UNAUTHORIZED => {
380 Some(ErrorData::RemoteAccessDenied {
381 resource_type: "EC2 Resource".into(),
382 resource_name: resource.into(),
383 })
384 }
385 StatusCode::TOO_MANY_REQUESTS => Some(ErrorData::RateLimitExceeded {
386 message: "Too many requests".into(),
387 }),
388 StatusCode::SERVICE_UNAVAILABLE
389 | StatusCode::BAD_GATEWAY
390 | StatusCode::GATEWAY_TIMEOUT => Some(ErrorData::RemoteServiceUnavailable {
391 message: "Service unavailable".into(),
392 }),
393 _ => None,
394 };
395 }
396
397 let parsed: std::result::Result<Ec2ErrorResponse, _> = quick_xml::de::from_str(body);
399 let (code, message) = match parsed {
400 Ok(e) => (e.errors.error.code, e.errors.error.message),
401 Err(_) => {
402 let default_message = "Unknown error".to_string();
404 return match status {
405 StatusCode::NOT_FOUND => Some(ErrorData::RemoteResourceNotFound {
406 resource_type: "EC2 Resource".into(),
407 resource_name: resource.into(),
408 }),
409 StatusCode::CONFLICT => Some(ErrorData::RemoteResourceConflict {
410 message: default_message,
411 resource_type: "EC2 Resource".into(),
412 resource_name: resource.into(),
413 }),
414 StatusCode::FORBIDDEN | StatusCode::UNAUTHORIZED => {
415 Some(ErrorData::RemoteAccessDenied {
416 resource_type: "EC2 Resource".into(),
417 resource_name: resource.into(),
418 })
419 }
420 StatusCode::TOO_MANY_REQUESTS => Some(ErrorData::RateLimitExceeded {
421 message: default_message,
422 }),
423 StatusCode::SERVICE_UNAVAILABLE
424 | StatusCode::BAD_GATEWAY
425 | StatusCode::GATEWAY_TIMEOUT => Some(ErrorData::RemoteServiceUnavailable {
426 message: default_message,
427 }),
428 _ => None,
429 };
430 }
431 };
432
433 Some(match code.as_str() {
436 "AuthFailure" | "UnauthorizedOperation" | "Blocked" => ErrorData::RemoteAccessDenied {
438 resource_type: "EC2 Resource".into(),
439 resource_name: resource.into(),
440 },
441 "RequestLimitExceeded" | "ResourceLimitExceeded" => {
443 ErrorData::RateLimitExceeded { message }
444 }
445 "ServiceUnavailable" | "Unavailable" | "InternalError" | "InternalFailure" => {
447 ErrorData::RemoteServiceUnavailable { message }
448 }
449 "InvalidVpcID.NotFound" | "InvalidVpc.NotFound" => ErrorData::RemoteResourceNotFound {
451 resource_type: "VPC".into(),
452 resource_name: resource.into(),
453 },
454 "InvalidSubnetID.NotFound" | "InvalidSubnet.NotFound" => {
455 ErrorData::RemoteResourceNotFound {
456 resource_type: "Subnet".into(),
457 resource_name: resource.into(),
458 }
459 }
460 "InvalidInternetGatewayID.NotFound" | "InvalidInternetGateway.NotFound" => {
461 ErrorData::RemoteResourceNotFound {
462 resource_type: "InternetGateway".into(),
463 resource_name: resource.into(),
464 }
465 }
466 "InvalidNatGatewayID.NotFound" | "NatGatewayNotFound" => {
467 ErrorData::RemoteResourceNotFound {
468 resource_type: "NatGateway".into(),
469 resource_name: resource.into(),
470 }
471 }
472 "InvalidRouteTableID.NotFound" | "InvalidRouteTableId.NotFound" => {
473 ErrorData::RemoteResourceNotFound {
474 resource_type: "RouteTable".into(),
475 resource_name: resource.into(),
476 }
477 }
478 "InvalidGroup.NotFound" | "InvalidSecurityGroupID.NotFound" => {
479 ErrorData::RemoteResourceNotFound {
480 resource_type: "SecurityGroup".into(),
481 resource_name: resource.into(),
482 }
483 }
484 "InvalidAllocationID.NotFound" | "InvalidAddress.NotFound" => {
485 ErrorData::RemoteResourceNotFound {
486 resource_type: "ElasticIP".into(),
487 resource_name: resource.into(),
488 }
489 }
490 "InvalidAssociationID.NotFound" => ErrorData::RemoteResourceNotFound {
491 resource_type: "RouteTableAssociation".into(),
492 resource_name: resource.into(),
493 },
494 "InvalidVolume.NotFound" | "InvalidVolumeID.NotFound" => {
495 ErrorData::RemoteResourceNotFound {
496 resource_type: "Volume".into(),
497 resource_name: resource.into(),
498 }
499 }
500 "VpcLimitExceeded"
502 | "SubnetLimitExceeded"
503 | "SecurityGroupLimitExceeded"
504 | "AddressLimitExceeded" => ErrorData::QuotaExceeded { message },
505 "InvalidVpcState"
506 | "InvalidSubnetID.DuplicateSubnet"
507 | "InvalidGroup.Duplicate"
508 | "InvalidPermission.Duplicate"
509 | "InvalidLaunchTemplateName.AlreadyExistsException" => {
510 ErrorData::RemoteResourceConflict {
511 message,
512 resource_type: "EC2 Resource".into(),
513 resource_name: resource.into(),
514 }
515 }
516 "DependencyViolation" | "ResourceInUse" => ErrorData::RemoteResourceConflict {
517 message,
518 resource_type: "EC2 Resource".into(),
519 resource_name: resource.into(),
520 },
521 "Gateway.NotAttached" => ErrorData::RemoteResourceConflict {
522 message,
523 resource_type: "InternetGateway".into(),
524 resource_name: resource.into(),
525 },
526 "RouteAlreadyExists" => ErrorData::RemoteResourceConflict {
527 message,
528 resource_type: "Route".into(),
529 resource_name: resource.into(),
530 },
531 "InvalidParameterValue" | "InvalidParameter" | "MissingParameter" | "InvalidInput" => {
533 ErrorData::InvalidInput {
534 message,
535 field_name: None,
536 }
537 }
538 "InvalidVpcRange" | "InvalidSubnetRange.Conflict" | "InvalidCIDRBlock" => {
539 ErrorData::InvalidInput {
540 message,
541 field_name: Some("cidr_block".into()),
542 }
543 }
544 _ => match status {
546 StatusCode::NOT_FOUND => ErrorData::RemoteResourceNotFound {
547 resource_type: "EC2 Resource".into(),
548 resource_name: resource.into(),
549 },
550 StatusCode::CONFLICT => ErrorData::RemoteResourceConflict {
551 message,
552 resource_type: "EC2 Resource".into(),
553 resource_name: resource.into(),
554 },
555 StatusCode::FORBIDDEN | StatusCode::UNAUTHORIZED => ErrorData::RemoteAccessDenied {
556 resource_type: "EC2 Resource".into(),
557 resource_name: resource.into(),
558 },
559 StatusCode::TOO_MANY_REQUESTS => ErrorData::RateLimitExceeded { message },
560 StatusCode::SERVICE_UNAVAILABLE
561 | StatusCode::BAD_GATEWAY
562 | StatusCode::GATEWAY_TIMEOUT => ErrorData::RemoteServiceUnavailable { message },
563 _ => ErrorData::HttpResponseError {
564 message: format!("EC2 operation failed: {}", message),
565 url: "ec2.amazonaws.com".into(),
566 http_status: status.as_u16(),
567 http_response_text: Some(body.into()),
568 http_request_text: request_body.map(|s| s.to_string()),
569 },
570 },
571 })
572 }
573
574 fn add_filters(form_data: &mut HashMap<String, String>, filters: &[Filter]) {
576 for (i, filter) in filters.iter().enumerate() {
577 let idx = i + 1;
578 form_data.insert(format!("Filter.{}.Name", idx), filter.name.clone());
579 for (j, value) in filter.values.iter().enumerate() {
580 form_data.insert(format!("Filter.{}.Value.{}", idx, j + 1), value.clone());
581 }
582 }
583 }
584
585 fn add_tag_specifications(
587 form_data: &mut HashMap<String, String>,
588 tag_specs: &[TagSpecification],
589 ) {
590 Self::add_tag_specifications_with_prefix(form_data, "TagSpecification", tag_specs);
591 }
592
593 fn add_tag_specifications_with_prefix(
595 form_data: &mut HashMap<String, String>,
596 prefix: &str,
597 tag_specs: &[TagSpecification],
598 ) {
599 for (i, spec) in tag_specs.iter().enumerate() {
600 let idx = i + 1;
601 form_data.insert(
602 format!("{prefix}.{idx}.ResourceType"),
603 spec.resource_type.clone(),
604 );
605 for (j, tag) in spec.tags.iter().enumerate() {
606 form_data.insert(format!("{prefix}.{idx}.Tag.{}.Key", j + 1), tag.key.clone());
607 form_data.insert(
608 format!("{prefix}.{idx}.Tag.{}.Value", j + 1),
609 tag.value.clone(),
610 );
611 }
612 }
613 }
614
615 fn create_volume_form_data(request: &CreateVolumeRequest) -> HashMap<String, String> {
616 let mut form_data = HashMap::new();
617 form_data.insert("Action".to_string(), "CreateVolume".to_string());
618 form_data.insert("Version".to_string(), "2016-11-15".to_string());
619 form_data.insert(
620 "AvailabilityZone".to_string(),
621 request.availability_zone.clone(),
622 );
623
624 if let Some(client_token) = &request.client_token {
625 form_data.insert("ClientToken".to_string(), client_token.clone());
626 }
627 if let Some(size) = request.size {
628 form_data.insert("Size".to_string(), size.to_string());
629 }
630 if let Some(snapshot_id) = &request.snapshot_id {
631 form_data.insert("SnapshotId".to_string(), snapshot_id.clone());
632 }
633 if let Some(volume_type) = &request.volume_type {
634 form_data.insert("VolumeType".to_string(), volume_type.clone());
635 }
636 if let Some(iops) = request.iops {
637 form_data.insert("Iops".to_string(), iops.to_string());
638 }
639 if let Some(throughput) = request.throughput {
640 form_data.insert("Throughput".to_string(), throughput.to_string());
641 }
642 if let Some(encrypted) = request.encrypted {
643 form_data.insert("Encrypted".to_string(), encrypted.to_string());
644 }
645 if let Some(kms_key_id) = &request.kms_key_id {
646 form_data.insert("KmsKeyId".to_string(), kms_key_id.clone());
647 }
648 if let Some(tag_specs) = &request.tag_specifications {
649 Self::add_tag_specifications(&mut form_data, tag_specs);
650 }
651
652 form_data
653 }
654
655 fn modify_volume_form_data(request: &ModifyVolumeRequest) -> HashMap<String, String> {
656 let mut form_data = HashMap::new();
657 form_data.insert("Action".to_string(), "ModifyVolume".to_string());
658 form_data.insert("Version".to_string(), "2016-11-15".to_string());
659 form_data.insert("VolumeId".to_string(), request.volume_id.clone());
660 form_data.insert("Size".to_string(), request.size.to_string());
661 form_data
662 }
663
664 fn describe_volumes_modifications_form_data(
665 request: &DescribeVolumesModificationsRequest,
666 ) -> HashMap<String, String> {
667 let mut form_data = HashMap::new();
668 form_data.insert(
669 "Action".to_string(),
670 "DescribeVolumesModifications".to_string(),
671 );
672 form_data.insert("Version".to_string(), "2016-11-15".to_string());
673
674 if let Some(volume_ids) = &request.volume_ids {
675 for (i, volume_id) in volume_ids.iter().enumerate() {
676 form_data.insert(format!("VolumeId.{}", i + 1), volume_id.clone());
677 }
678 }
679 if let Some(filters) = &request.filters {
680 Self::add_filters(&mut form_data, filters);
681 }
682 if let Some(max_results) = request.max_results {
683 form_data.insert("MaxResults".to_string(), max_results.to_string());
684 }
685 if let Some(next_token) = &request.next_token {
686 form_data.insert("NextToken".to_string(), next_token.clone());
687 }
688
689 form_data
690 }
691}
692
693#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
694#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
695impl Ec2Api for Ec2Client {
696 async fn describe_vpcs(&self, request: DescribeVpcsRequest) -> Result<DescribeVpcsResponse> {
701 let mut form_data = HashMap::new();
702 form_data.insert("Action".to_string(), "DescribeVpcs".to_string());
703 form_data.insert("Version".to_string(), "2016-11-15".to_string());
704
705 if let Some(vpc_ids) = &request.vpc_ids {
706 for (i, vpc_id) in vpc_ids.iter().enumerate() {
707 form_data.insert(format!("VpcId.{}", i + 1), vpc_id.clone());
708 }
709 }
710
711 if let Some(filters) = &request.filters {
712 Self::add_filters(&mut form_data, filters);
713 }
714
715 if let Some(max_results) = request.max_results {
716 form_data.insert("MaxResults".to_string(), max_results.to_string());
717 }
718
719 if let Some(next_token) = &request.next_token {
720 form_data.insert("NextToken".to_string(), next_token.clone());
721 }
722
723 self.send_form(form_data, "DescribeVpcs", "VPC").await
724 }
725
726 async fn describe_vpc_attribute(
727 &self,
728 request: DescribeVpcAttributeRequest,
729 ) -> Result<DescribeVpcAttributeResponse> {
730 let mut form_data = HashMap::new();
731 form_data.insert("Action".to_string(), "DescribeVpcAttribute".to_string());
732 form_data.insert("Version".to_string(), "2016-11-15".to_string());
733 form_data.insert("VpcId".to_string(), request.vpc_id.clone());
734 form_data.insert("Attribute".to_string(), request.attribute.clone());
735
736 self.send_form(form_data, "DescribeVpcAttribute", &request.vpc_id)
737 .await
738 }
739
740 async fn create_vpc(&self, request: CreateVpcRequest) -> Result<CreateVpcResponse> {
741 let mut form_data = HashMap::new();
742 form_data.insert("Action".to_string(), "CreateVpc".to_string());
743 form_data.insert("Version".to_string(), "2016-11-15".to_string());
744 form_data.insert("CidrBlock".to_string(), request.cidr_block.clone());
745
746 if let Some(instance_tenancy) = &request.instance_tenancy {
747 form_data.insert("InstanceTenancy".to_string(), instance_tenancy.clone());
748 }
749
750 if let Some(amazon_provided_ipv6_cidr_block) = request.amazon_provided_ipv6_cidr_block {
751 form_data.insert(
752 "AmazonProvidedIpv6CidrBlock".to_string(),
753 amazon_provided_ipv6_cidr_block.to_string(),
754 );
755 }
756
757 if let Some(tag_specs) = &request.tag_specifications {
758 Self::add_tag_specifications(&mut form_data, tag_specs);
759 }
760
761 self.send_form(form_data, "CreateVpc", &request.cidr_block)
762 .await
763 }
764
765 async fn delete_vpc(&self, vpc_id: &str) -> Result<()> {
766 let mut form_data = HashMap::new();
767 form_data.insert("Action".to_string(), "DeleteVpc".to_string());
768 form_data.insert("Version".to_string(), "2016-11-15".to_string());
769 form_data.insert("VpcId".to_string(), vpc_id.to_string());
770
771 self.send_form_no_body(form_data, "DeleteVpc", vpc_id).await
772 }
773
774 async fn modify_vpc_attribute(&self, request: ModifyVpcAttributeRequest) -> Result<()> {
775 let mut form_data = HashMap::new();
776 form_data.insert("Action".to_string(), "ModifyVpcAttribute".to_string());
777 form_data.insert("Version".to_string(), "2016-11-15".to_string());
778 form_data.insert("VpcId".to_string(), request.vpc_id.clone());
779
780 if let Some(enable_dns_support) = request.enable_dns_support {
781 form_data.insert(
782 "EnableDnsSupport.Value".to_string(),
783 enable_dns_support.to_string(),
784 );
785 }
786
787 if let Some(enable_dns_hostnames) = request.enable_dns_hostnames {
788 form_data.insert(
789 "EnableDnsHostnames.Value".to_string(),
790 enable_dns_hostnames.to_string(),
791 );
792 }
793
794 self.send_form_no_body(form_data, "ModifyVpcAttribute", &request.vpc_id)
795 .await
796 }
797
798 async fn describe_subnets(
803 &self,
804 request: DescribeSubnetsRequest,
805 ) -> Result<DescribeSubnetsResponse> {
806 let mut form_data = HashMap::new();
807 form_data.insert("Action".to_string(), "DescribeSubnets".to_string());
808 form_data.insert("Version".to_string(), "2016-11-15".to_string());
809
810 if let Some(subnet_ids) = &request.subnet_ids {
811 for (i, subnet_id) in subnet_ids.iter().enumerate() {
812 form_data.insert(format!("SubnetId.{}", i + 1), subnet_id.clone());
813 }
814 }
815
816 if let Some(filters) = &request.filters {
817 Self::add_filters(&mut form_data, filters);
818 }
819
820 if let Some(max_results) = request.max_results {
821 form_data.insert("MaxResults".to_string(), max_results.to_string());
822 }
823
824 if let Some(next_token) = &request.next_token {
825 form_data.insert("NextToken".to_string(), next_token.clone());
826 }
827
828 self.send_form(form_data, "DescribeSubnets", "Subnet").await
829 }
830
831 async fn create_subnet(&self, request: CreateSubnetRequest) -> Result<CreateSubnetResponse> {
832 let mut form_data = HashMap::new();
833 form_data.insert("Action".to_string(), "CreateSubnet".to_string());
834 form_data.insert("Version".to_string(), "2016-11-15".to_string());
835 form_data.insert("VpcId".to_string(), request.vpc_id.clone());
836 form_data.insert("CidrBlock".to_string(), request.cidr_block.clone());
837
838 if let Some(availability_zone) = &request.availability_zone {
839 form_data.insert("AvailabilityZone".to_string(), availability_zone.clone());
840 }
841
842 if let Some(availability_zone_id) = &request.availability_zone_id {
843 form_data.insert(
844 "AvailabilityZoneId".to_string(),
845 availability_zone_id.clone(),
846 );
847 }
848
849 if let Some(tag_specs) = &request.tag_specifications {
850 Self::add_tag_specifications(&mut form_data, tag_specs);
851 }
852
853 self.send_form(form_data, "CreateSubnet", &request.cidr_block)
854 .await
855 }
856
857 async fn delete_subnet(&self, subnet_id: &str) -> Result<()> {
858 let mut form_data = HashMap::new();
859 form_data.insert("Action".to_string(), "DeleteSubnet".to_string());
860 form_data.insert("Version".to_string(), "2016-11-15".to_string());
861 form_data.insert("SubnetId".to_string(), subnet_id.to_string());
862
863 self.send_form_no_body(form_data, "DeleteSubnet", subnet_id)
864 .await
865 }
866
867 async fn create_internet_gateway(
872 &self,
873 request: CreateInternetGatewayRequest,
874 ) -> Result<CreateInternetGatewayResponse> {
875 let mut form_data = HashMap::new();
876 form_data.insert("Action".to_string(), "CreateInternetGateway".to_string());
877 form_data.insert("Version".to_string(), "2016-11-15".to_string());
878
879 if let Some(tag_specs) = &request.tag_specifications {
880 Self::add_tag_specifications(&mut form_data, tag_specs);
881 }
882
883 self.send_form(form_data, "CreateInternetGateway", "InternetGateway")
884 .await
885 }
886
887 async fn delete_internet_gateway(&self, internet_gateway_id: &str) -> Result<()> {
888 let mut form_data = HashMap::new();
889 form_data.insert("Action".to_string(), "DeleteInternetGateway".to_string());
890 form_data.insert("Version".to_string(), "2016-11-15".to_string());
891 form_data.insert(
892 "InternetGatewayId".to_string(),
893 internet_gateway_id.to_string(),
894 );
895
896 self.send_form_no_body(form_data, "DeleteInternetGateway", internet_gateway_id)
897 .await
898 }
899
900 async fn attach_internet_gateway(&self, request: AttachInternetGatewayRequest) -> Result<()> {
901 let mut form_data = HashMap::new();
902 form_data.insert("Action".to_string(), "AttachInternetGateway".to_string());
903 form_data.insert("Version".to_string(), "2016-11-15".to_string());
904 form_data.insert(
905 "InternetGatewayId".to_string(),
906 request.internet_gateway_id.clone(),
907 );
908 form_data.insert("VpcId".to_string(), request.vpc_id.clone());
909
910 self.send_form_no_body(
911 form_data,
912 "AttachInternetGateway",
913 &request.internet_gateway_id,
914 )
915 .await
916 }
917
918 async fn detach_internet_gateway(&self, request: DetachInternetGatewayRequest) -> Result<()> {
919 let mut form_data = HashMap::new();
920 form_data.insert("Action".to_string(), "DetachInternetGateway".to_string());
921 form_data.insert("Version".to_string(), "2016-11-15".to_string());
922 form_data.insert(
923 "InternetGatewayId".to_string(),
924 request.internet_gateway_id.clone(),
925 );
926 form_data.insert("VpcId".to_string(), request.vpc_id.clone());
927
928 self.send_form_no_body(
929 form_data,
930 "DetachInternetGateway",
931 &request.internet_gateway_id,
932 )
933 .await
934 }
935
936 async fn describe_internet_gateways(
937 &self,
938 request: DescribeInternetGatewaysRequest,
939 ) -> Result<DescribeInternetGatewaysResponse> {
940 let mut form_data = HashMap::new();
941 form_data.insert("Action".to_string(), "DescribeInternetGateways".to_string());
942 form_data.insert("Version".to_string(), "2016-11-15".to_string());
943
944 if let Some(igw_ids) = &request.internet_gateway_ids {
945 for (i, igw_id) in igw_ids.iter().enumerate() {
946 form_data.insert(format!("InternetGatewayId.{}", i + 1), igw_id.clone());
947 }
948 }
949
950 if let Some(filters) = &request.filters {
951 Self::add_filters(&mut form_data, filters);
952 }
953
954 if let Some(max_results) = request.max_results {
955 form_data.insert("MaxResults".to_string(), max_results.to_string());
956 }
957
958 if let Some(next_token) = &request.next_token {
959 form_data.insert("NextToken".to_string(), next_token.clone());
960 }
961
962 self.send_form(form_data, "DescribeInternetGateways", "InternetGateway")
963 .await
964 }
965
966 async fn create_nat_gateway(
971 &self,
972 request: CreateNatGatewayRequest,
973 ) -> Result<CreateNatGatewayResponse> {
974 let mut form_data = HashMap::new();
975 form_data.insert("Action".to_string(), "CreateNatGateway".to_string());
976 form_data.insert("Version".to_string(), "2016-11-15".to_string());
977 form_data.insert("SubnetId".to_string(), request.subnet_id.clone());
978
979 if let Some(allocation_id) = &request.allocation_id {
980 form_data.insert("AllocationId".to_string(), allocation_id.clone());
981 }
982
983 if let Some(connectivity_type) = &request.connectivity_type {
984 form_data.insert("ConnectivityType".to_string(), connectivity_type.clone());
985 }
986
987 if let Some(private_ip_address) = &request.private_ip_address {
988 form_data.insert("PrivateIpAddress".to_string(), private_ip_address.clone());
989 }
990
991 if let Some(tag_specs) = &request.tag_specifications {
992 Self::add_tag_specifications(&mut form_data, tag_specs);
993 }
994
995 self.send_form(form_data, "CreateNatGateway", &request.subnet_id)
996 .await
997 }
998
999 async fn delete_nat_gateway(&self, nat_gateway_id: &str) -> Result<DeleteNatGatewayResponse> {
1000 let mut form_data = HashMap::new();
1001 form_data.insert("Action".to_string(), "DeleteNatGateway".to_string());
1002 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1003 form_data.insert("NatGatewayId".to_string(), nat_gateway_id.to_string());
1004
1005 self.send_form(form_data, "DeleteNatGateway", nat_gateway_id)
1006 .await
1007 }
1008
1009 async fn describe_nat_gateways(
1010 &self,
1011 request: DescribeNatGatewaysRequest,
1012 ) -> Result<DescribeNatGatewaysResponse> {
1013 let mut form_data = HashMap::new();
1014 form_data.insert("Action".to_string(), "DescribeNatGateways".to_string());
1015 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1016
1017 if let Some(nat_gateway_ids) = &request.nat_gateway_ids {
1018 for (i, nat_id) in nat_gateway_ids.iter().enumerate() {
1019 form_data.insert(format!("NatGatewayId.{}", i + 1), nat_id.clone());
1020 }
1021 }
1022
1023 if let Some(filters) = &request.filters {
1024 Self::add_filters(&mut form_data, filters);
1025 }
1026
1027 if let Some(max_results) = request.max_results {
1028 form_data.insert("MaxResults".to_string(), max_results.to_string());
1029 }
1030
1031 if let Some(next_token) = &request.next_token {
1032 form_data.insert("NextToken".to_string(), next_token.clone());
1033 }
1034
1035 self.send_form(form_data, "DescribeNatGateways", "NatGateway")
1036 .await
1037 }
1038
1039 async fn allocate_address(
1044 &self,
1045 request: AllocateAddressRequest,
1046 ) -> Result<AllocateAddressResponse> {
1047 let mut form_data = HashMap::new();
1048 form_data.insert("Action".to_string(), "AllocateAddress".to_string());
1049 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1050
1051 let domain = request.domain.as_deref().unwrap_or("vpc");
1053 form_data.insert("Domain".to_string(), domain.to_string());
1054
1055 if let Some(tag_specs) = &request.tag_specifications {
1056 Self::add_tag_specifications(&mut form_data, tag_specs);
1057 }
1058
1059 self.send_form(form_data, "AllocateAddress", "ElasticIP")
1060 .await
1061 }
1062
1063 async fn release_address(&self, allocation_id: &str) -> Result<()> {
1064 let mut form_data = HashMap::new();
1065 form_data.insert("Action".to_string(), "ReleaseAddress".to_string());
1066 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1067 form_data.insert("AllocationId".to_string(), allocation_id.to_string());
1068
1069 self.send_form_no_body(form_data, "ReleaseAddress", allocation_id)
1070 .await
1071 }
1072
1073 async fn describe_route_tables(
1078 &self,
1079 request: DescribeRouteTablesRequest,
1080 ) -> Result<DescribeRouteTablesResponse> {
1081 let mut form_data = HashMap::new();
1082 form_data.insert("Action".to_string(), "DescribeRouteTables".to_string());
1083 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1084
1085 if let Some(rt_ids) = &request.route_table_ids {
1086 for (i, rt_id) in rt_ids.iter().enumerate() {
1087 form_data.insert(format!("RouteTableId.{}", i + 1), rt_id.clone());
1088 }
1089 }
1090
1091 if let Some(filters) = &request.filters {
1092 Self::add_filters(&mut form_data, filters);
1093 }
1094
1095 if let Some(max_results) = request.max_results {
1096 form_data.insert("MaxResults".to_string(), max_results.to_string());
1097 }
1098
1099 if let Some(next_token) = &request.next_token {
1100 form_data.insert("NextToken".to_string(), next_token.clone());
1101 }
1102
1103 self.send_form(form_data, "DescribeRouteTables", "RouteTable")
1104 .await
1105 }
1106
1107 async fn create_route_table(
1108 &self,
1109 request: CreateRouteTableRequest,
1110 ) -> Result<CreateRouteTableResponse> {
1111 let mut form_data = HashMap::new();
1112 form_data.insert("Action".to_string(), "CreateRouteTable".to_string());
1113 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1114 form_data.insert("VpcId".to_string(), request.vpc_id.clone());
1115
1116 if let Some(tag_specs) = &request.tag_specifications {
1117 Self::add_tag_specifications(&mut form_data, tag_specs);
1118 }
1119
1120 self.send_form(form_data, "CreateRouteTable", &request.vpc_id)
1121 .await
1122 }
1123
1124 async fn delete_route_table(&self, route_table_id: &str) -> Result<()> {
1125 let mut form_data = HashMap::new();
1126 form_data.insert("Action".to_string(), "DeleteRouteTable".to_string());
1127 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1128 form_data.insert("RouteTableId".to_string(), route_table_id.to_string());
1129
1130 self.send_form_no_body(form_data, "DeleteRouteTable", route_table_id)
1131 .await
1132 }
1133
1134 async fn create_route(&self, request: CreateRouteRequest) -> Result<()> {
1135 let mut form_data = HashMap::new();
1136 form_data.insert("Action".to_string(), "CreateRoute".to_string());
1137 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1138 form_data.insert("RouteTableId".to_string(), request.route_table_id.clone());
1139 form_data.insert(
1140 "DestinationCidrBlock".to_string(),
1141 request.destination_cidr_block.clone(),
1142 );
1143
1144 if let Some(gateway_id) = &request.gateway_id {
1145 form_data.insert("GatewayId".to_string(), gateway_id.clone());
1146 }
1147
1148 if let Some(nat_gateway_id) = &request.nat_gateway_id {
1149 form_data.insert("NatGatewayId".to_string(), nat_gateway_id.clone());
1150 }
1151
1152 if let Some(instance_id) = &request.instance_id {
1153 form_data.insert("InstanceId".to_string(), instance_id.clone());
1154 }
1155
1156 if let Some(network_interface_id) = &request.network_interface_id {
1157 form_data.insert(
1158 "NetworkInterfaceId".to_string(),
1159 network_interface_id.clone(),
1160 );
1161 }
1162
1163 if let Some(vpc_peering_connection_id) = &request.vpc_peering_connection_id {
1164 form_data.insert(
1165 "VpcPeeringConnectionId".to_string(),
1166 vpc_peering_connection_id.clone(),
1167 );
1168 }
1169
1170 if let Some(transit_gateway_id) = &request.transit_gateway_id {
1171 form_data.insert("TransitGatewayId".to_string(), transit_gateway_id.clone());
1172 }
1173
1174 self.send_form_no_body(form_data, "CreateRoute", &request.route_table_id)
1175 .await
1176 }
1177
1178 async fn delete_route(&self, request: DeleteRouteRequest) -> Result<()> {
1179 let mut form_data = HashMap::new();
1180 form_data.insert("Action".to_string(), "DeleteRoute".to_string());
1181 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1182 form_data.insert("RouteTableId".to_string(), request.route_table_id.clone());
1183 form_data.insert(
1184 "DestinationCidrBlock".to_string(),
1185 request.destination_cidr_block.clone(),
1186 );
1187
1188 self.send_form_no_body(form_data, "DeleteRoute", &request.route_table_id)
1189 .await
1190 }
1191
1192 async fn associate_route_table(
1193 &self,
1194 request: AssociateRouteTableRequest,
1195 ) -> Result<AssociateRouteTableResponse> {
1196 let mut form_data = HashMap::new();
1197 form_data.insert("Action".to_string(), "AssociateRouteTable".to_string());
1198 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1199 form_data.insert("RouteTableId".to_string(), request.route_table_id.clone());
1200 form_data.insert("SubnetId".to_string(), request.subnet_id.clone());
1201
1202 self.send_form(form_data, "AssociateRouteTable", &request.route_table_id)
1203 .await
1204 }
1205
1206 async fn disassociate_route_table(&self, association_id: &str) -> Result<()> {
1207 let mut form_data = HashMap::new();
1208 form_data.insert("Action".to_string(), "DisassociateRouteTable".to_string());
1209 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1210 form_data.insert("AssociationId".to_string(), association_id.to_string());
1211
1212 self.send_form_no_body(form_data, "DisassociateRouteTable", association_id)
1213 .await
1214 }
1215
1216 async fn describe_security_groups(
1221 &self,
1222 request: DescribeSecurityGroupsRequest,
1223 ) -> Result<DescribeSecurityGroupsResponse> {
1224 let mut form_data = HashMap::new();
1225 form_data.insert("Action".to_string(), "DescribeSecurityGroups".to_string());
1226 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1227
1228 if let Some(group_ids) = &request.group_ids {
1229 for (i, group_id) in group_ids.iter().enumerate() {
1230 form_data.insert(format!("GroupId.{}", i + 1), group_id.clone());
1231 }
1232 }
1233
1234 if let Some(group_names) = &request.group_names {
1235 for (i, group_name) in group_names.iter().enumerate() {
1236 form_data.insert(format!("GroupName.{}", i + 1), group_name.clone());
1237 }
1238 }
1239
1240 if let Some(filters) = &request.filters {
1241 Self::add_filters(&mut form_data, filters);
1242 }
1243
1244 if let Some(max_results) = request.max_results {
1245 form_data.insert("MaxResults".to_string(), max_results.to_string());
1246 }
1247
1248 if let Some(next_token) = &request.next_token {
1249 form_data.insert("NextToken".to_string(), next_token.clone());
1250 }
1251
1252 self.send_form(form_data, "DescribeSecurityGroups", "SecurityGroup")
1253 .await
1254 }
1255
1256 async fn describe_network_interfaces(
1257 &self,
1258 request: DescribeNetworkInterfacesRequest,
1259 ) -> Result<DescribeNetworkInterfacesResponse> {
1260 let mut form_data = HashMap::new();
1261 form_data.insert(
1262 "Action".to_string(),
1263 "DescribeNetworkInterfaces".to_string(),
1264 );
1265 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1266
1267 if let Some(network_interface_ids) = &request.network_interface_ids {
1268 for (i, network_interface_id) in network_interface_ids.iter().enumerate() {
1269 form_data.insert(
1270 format!("NetworkInterfaceId.{}", i + 1),
1271 network_interface_id.clone(),
1272 );
1273 }
1274 }
1275
1276 if let Some(filters) = &request.filters {
1277 Self::add_filters(&mut form_data, filters);
1278 }
1279
1280 if let Some(max_results) = request.max_results {
1281 form_data.insert("MaxResults".to_string(), max_results.to_string());
1282 }
1283
1284 if let Some(next_token) = &request.next_token {
1285 form_data.insert("NextToken".to_string(), next_token.clone());
1286 }
1287
1288 self.send_form(form_data, "DescribeNetworkInterfaces", "NetworkInterface")
1289 .await
1290 }
1291
1292 async fn create_security_group(
1293 &self,
1294 request: CreateSecurityGroupRequest,
1295 ) -> Result<CreateSecurityGroupResponse> {
1296 let mut form_data = HashMap::new();
1297 form_data.insert("Action".to_string(), "CreateSecurityGroup".to_string());
1298 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1299 form_data.insert("GroupName".to_string(), request.group_name.clone());
1300 form_data.insert("GroupDescription".to_string(), request.description.clone());
1301 form_data.insert("VpcId".to_string(), request.vpc_id.clone());
1302
1303 if let Some(tag_specs) = &request.tag_specifications {
1304 Self::add_tag_specifications(&mut form_data, tag_specs);
1305 }
1306
1307 self.send_form(form_data, "CreateSecurityGroup", &request.group_name)
1308 .await
1309 }
1310
1311 async fn delete_security_group(&self, group_id: &str) -> Result<()> {
1312 let mut form_data = HashMap::new();
1313 form_data.insert("Action".to_string(), "DeleteSecurityGroup".to_string());
1314 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1315 form_data.insert("GroupId".to_string(), group_id.to_string());
1316
1317 self.send_form_no_body(form_data, "DeleteSecurityGroup", group_id)
1318 .await
1319 }
1320
1321 async fn authorize_security_group_ingress(
1322 &self,
1323 request: AuthorizeSecurityGroupIngressRequest,
1324 ) -> Result<()> {
1325 let mut form_data = HashMap::new();
1326 form_data.insert(
1327 "Action".to_string(),
1328 "AuthorizeSecurityGroupIngress".to_string(),
1329 );
1330 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1331 form_data.insert("GroupId".to_string(), request.group_id.clone());
1332
1333 Self::add_ip_permissions(&mut form_data, &request.ip_permissions);
1334
1335 self.send_form_no_body(
1336 form_data,
1337 "AuthorizeSecurityGroupIngress",
1338 &request.group_id,
1339 )
1340 .await
1341 }
1342
1343 async fn authorize_security_group_egress(
1344 &self,
1345 request: AuthorizeSecurityGroupEgressRequest,
1346 ) -> Result<()> {
1347 let mut form_data = HashMap::new();
1348 form_data.insert(
1349 "Action".to_string(),
1350 "AuthorizeSecurityGroupEgress".to_string(),
1351 );
1352 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1353 form_data.insert("GroupId".to_string(), request.group_id.clone());
1354
1355 Self::add_ip_permissions(&mut form_data, &request.ip_permissions);
1356
1357 self.send_form_no_body(form_data, "AuthorizeSecurityGroupEgress", &request.group_id)
1358 .await
1359 }
1360
1361 async fn revoke_security_group_ingress(
1362 &self,
1363 request: RevokeSecurityGroupIngressRequest,
1364 ) -> Result<()> {
1365 let mut form_data = HashMap::new();
1366 form_data.insert(
1367 "Action".to_string(),
1368 "RevokeSecurityGroupIngress".to_string(),
1369 );
1370 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1371 form_data.insert("GroupId".to_string(), request.group_id.clone());
1372
1373 Self::add_ip_permissions(&mut form_data, &request.ip_permissions);
1374
1375 self.send_form_no_body(form_data, "RevokeSecurityGroupIngress", &request.group_id)
1376 .await
1377 }
1378
1379 async fn revoke_security_group_egress(
1380 &self,
1381 request: RevokeSecurityGroupEgressRequest,
1382 ) -> Result<()> {
1383 let mut form_data = HashMap::new();
1384 form_data.insert(
1385 "Action".to_string(),
1386 "RevokeSecurityGroupEgress".to_string(),
1387 );
1388 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1389 form_data.insert("GroupId".to_string(), request.group_id.clone());
1390
1391 Self::add_ip_permissions(&mut form_data, &request.ip_permissions);
1392
1393 self.send_form_no_body(form_data, "RevokeSecurityGroupEgress", &request.group_id)
1394 .await
1395 }
1396
1397 async fn describe_availability_zones(
1402 &self,
1403 request: DescribeAvailabilityZonesRequest,
1404 ) -> Result<DescribeAvailabilityZonesResponse> {
1405 let mut form_data = HashMap::new();
1406 form_data.insert(
1407 "Action".to_string(),
1408 "DescribeAvailabilityZones".to_string(),
1409 );
1410 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1411
1412 if let Some(zone_names) = &request.zone_names {
1413 for (i, zone_name) in zone_names.iter().enumerate() {
1414 form_data.insert(format!("ZoneName.{}", i + 1), zone_name.clone());
1415 }
1416 }
1417
1418 if let Some(zone_ids) = &request.zone_ids {
1419 for (i, zone_id) in zone_ids.iter().enumerate() {
1420 form_data.insert(format!("ZoneId.{}", i + 1), zone_id.clone());
1421 }
1422 }
1423
1424 if let Some(filters) = &request.filters {
1425 Self::add_filters(&mut form_data, filters);
1426 }
1427
1428 if let Some(all_availability_zones) = request.all_availability_zones {
1429 form_data.insert(
1430 "AllAvailabilityZones".to_string(),
1431 all_availability_zones.to_string(),
1432 );
1433 }
1434
1435 self.send_form(form_data, "DescribeAvailabilityZones", "AvailabilityZone")
1436 .await
1437 }
1438
1439 async fn describe_images(
1444 &self,
1445 request: DescribeImagesRequest,
1446 ) -> Result<DescribeImagesResponse> {
1447 let mut form_data = HashMap::new();
1448 form_data.insert("Action".to_string(), "DescribeImages".to_string());
1449 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1450
1451 if let Some(image_ids) = &request.image_ids {
1452 for (i, image_id) in image_ids.iter().enumerate() {
1453 form_data.insert(format!("ImageId.{}", i + 1), image_id.clone());
1454 }
1455 }
1456
1457 if let Some(owners) = &request.owners {
1458 for (i, owner) in owners.iter().enumerate() {
1459 form_data.insert(format!("Owner.{}", i + 1), owner.clone());
1460 }
1461 }
1462
1463 if let Some(executable_users) = &request.executable_users {
1464 for (i, user) in executable_users.iter().enumerate() {
1465 form_data.insert(format!("ExecutableBy.{}", i + 1), user.clone());
1466 }
1467 }
1468
1469 if let Some(filters) = &request.filters {
1470 Self::add_filters(&mut form_data, filters);
1471 }
1472
1473 if let Some(include_deprecated) = request.include_deprecated {
1474 form_data.insert(
1475 "IncludeDeprecated".to_string(),
1476 include_deprecated.to_string(),
1477 );
1478 }
1479
1480 if let Some(max_results) = request.max_results {
1481 form_data.insert("MaxResults".to_string(), max_results.to_string());
1482 }
1483
1484 if let Some(next_token) = &request.next_token {
1485 form_data.insert("NextToken".to_string(), next_token.clone());
1486 }
1487
1488 self.send_form(form_data, "DescribeImages", "AMI").await
1489 }
1490
1491 async fn terminate_instances(
1496 &self,
1497 instance_ids: Vec<String>,
1498 ) -> Result<TerminateInstancesResponse> {
1499 let mut form_data = HashMap::new();
1500 form_data.insert("Action".to_string(), "TerminateInstances".to_string());
1501 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1502
1503 for (i, instance_id) in instance_ids.iter().enumerate() {
1504 form_data.insert(format!("InstanceId.{}", i + 1), instance_id.clone());
1505 }
1506
1507 let resource = instance_ids.join(",");
1508 self.send_form(form_data, "TerminateInstances", &resource)
1509 .await
1510 }
1511
1512 async fn describe_instances(
1513 &self,
1514 request: DescribeInstancesRequest,
1515 ) -> Result<DescribeInstancesResponse> {
1516 let mut form_data = HashMap::new();
1517 form_data.insert("Action".to_string(), "DescribeInstances".to_string());
1518 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1519
1520 if let Some(instance_ids) = &request.instance_ids {
1521 for (i, instance_id) in instance_ids.iter().enumerate() {
1522 form_data.insert(format!("InstanceId.{}", i + 1), instance_id.clone());
1523 }
1524 }
1525
1526 if let Some(filters) = &request.filters {
1527 Self::add_filters(&mut form_data, filters);
1528 }
1529
1530 if let Some(max_results) = request.max_results {
1531 form_data.insert("MaxResults".to_string(), max_results.to_string());
1532 }
1533
1534 if let Some(next_token) = &request.next_token {
1535 form_data.insert("NextToken".to_string(), next_token.clone());
1536 }
1537
1538 self.send_form(form_data, "DescribeInstances", "Instance")
1539 .await
1540 }
1541
1542 async fn create_volume(&self, request: CreateVolumeRequest) -> Result<CreateVolumeResponse> {
1547 let form_data = Self::create_volume_form_data(&request);
1548
1549 self.send_form(form_data, "CreateVolume", &request.availability_zone)
1550 .await
1551 }
1552
1553 async fn modify_volume(&self, request: ModifyVolumeRequest) -> Result<ModifyVolumeResponse> {
1554 let form_data = Self::modify_volume_form_data(&request);
1555 self.send_form(form_data, "ModifyVolume", &request.volume_id)
1556 .await
1557 }
1558
1559 async fn describe_volumes_modifications(
1560 &self,
1561 request: DescribeVolumesModificationsRequest,
1562 ) -> Result<DescribeVolumesModificationsResponse> {
1563 let form_data = Self::describe_volumes_modifications_form_data(&request);
1564 self.send_form(
1565 form_data,
1566 "DescribeVolumesModifications",
1567 "VolumeModification",
1568 )
1569 .await
1570 }
1571
1572 async fn delete_volume(&self, volume_id: &str) -> Result<()> {
1573 let mut form_data = HashMap::new();
1574 form_data.insert("Action".to_string(), "DeleteVolume".to_string());
1575 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1576 form_data.insert("VolumeId".to_string(), volume_id.to_string());
1577
1578 self.send_form_no_body(form_data, "DeleteVolume", volume_id)
1579 .await
1580 }
1581
1582 async fn describe_volumes(
1583 &self,
1584 request: DescribeVolumesRequest,
1585 ) -> Result<DescribeVolumesResponse> {
1586 let mut form_data = HashMap::new();
1587 form_data.insert("Action".to_string(), "DescribeVolumes".to_string());
1588 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1589
1590 if let Some(volume_ids) = &request.volume_ids {
1591 for (i, volume_id) in volume_ids.iter().enumerate() {
1592 form_data.insert(format!("VolumeId.{}", i + 1), volume_id.clone());
1593 }
1594 }
1595
1596 if let Some(filters) = &request.filters {
1597 Self::add_filters(&mut form_data, filters);
1598 }
1599
1600 if let Some(max_results) = request.max_results {
1601 form_data.insert("MaxResults".to_string(), max_results.to_string());
1602 }
1603
1604 if let Some(next_token) = &request.next_token {
1605 form_data.insert("NextToken".to_string(), next_token.clone());
1606 }
1607
1608 self.send_form(form_data, "DescribeVolumes", "Volume").await
1609 }
1610
1611 async fn attach_volume(&self, request: AttachVolumeRequest) -> Result<AttachVolumeResponse> {
1612 let mut form_data = HashMap::new();
1613 form_data.insert("Action".to_string(), "AttachVolume".to_string());
1614 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1615 form_data.insert("VolumeId".to_string(), request.volume_id.clone());
1616 form_data.insert("InstanceId".to_string(), request.instance_id.clone());
1617 form_data.insert("Device".to_string(), request.device.clone());
1618
1619 let resource = format!("{}:{}", request.volume_id, request.instance_id);
1620 self.send_form(form_data, "AttachVolume", &resource).await
1621 }
1622
1623 async fn detach_volume(&self, request: DetachVolumeRequest) -> Result<DetachVolumeResponse> {
1624 let mut form_data = HashMap::new();
1625 form_data.insert("Action".to_string(), "DetachVolume".to_string());
1626 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1627 form_data.insert("VolumeId".to_string(), request.volume_id.clone());
1628
1629 if let Some(instance_id) = &request.instance_id {
1630 form_data.insert("InstanceId".to_string(), instance_id.clone());
1631 }
1632
1633 if let Some(device) = &request.device {
1634 form_data.insert("Device".to_string(), device.clone());
1635 }
1636
1637 if let Some(force) = request.force {
1638 form_data.insert("Force".to_string(), force.to_string());
1639 }
1640
1641 self.send_form(form_data, "DetachVolume", &request.volume_id)
1642 .await
1643 }
1644
1645 async fn create_launch_template(
1650 &self,
1651 request: CreateLaunchTemplateRequest,
1652 ) -> Result<CreateLaunchTemplateResponse> {
1653 let mut form_data = HashMap::new();
1654 form_data.insert("Action".to_string(), "CreateLaunchTemplate".to_string());
1655 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1656 form_data.insert(
1657 "LaunchTemplateName".to_string(),
1658 request.launch_template_name.clone(),
1659 );
1660
1661 if let Some(version_description) = &request.version_description {
1662 form_data.insert(
1663 "VersionDescription".to_string(),
1664 version_description.clone(),
1665 );
1666 }
1667
1668 let data = &request.launch_template_data;
1670
1671 if let Some(image_id) = &data.image_id {
1672 form_data.insert("LaunchTemplateData.ImageId".to_string(), image_id.clone());
1673 }
1674
1675 if let Some(instance_type) = &data.instance_type {
1676 form_data.insert(
1677 "LaunchTemplateData.InstanceType".to_string(),
1678 instance_type.clone(),
1679 );
1680 }
1681
1682 if let Some(key_name) = &data.key_name {
1683 form_data.insert("LaunchTemplateData.KeyName".to_string(), key_name.clone());
1684 }
1685
1686 if let Some(user_data) = &data.user_data {
1687 form_data.insert("LaunchTemplateData.UserData".to_string(), user_data.clone());
1688 }
1689
1690 if let Some(security_group_ids) = &data.security_group_ids {
1691 for (i, sg_id) in security_group_ids.iter().enumerate() {
1692 form_data.insert(
1693 format!("LaunchTemplateData.SecurityGroupId.{}", i + 1),
1694 sg_id.clone(),
1695 );
1696 }
1697 }
1698
1699 if let Some(iam_instance_profile) = &data.iam_instance_profile {
1700 if let Some(arn) = &iam_instance_profile.arn {
1701 form_data.insert(
1702 "LaunchTemplateData.IamInstanceProfile.Arn".to_string(),
1703 arn.clone(),
1704 );
1705 }
1706 if let Some(name) = &iam_instance_profile.name {
1707 form_data.insert(
1708 "LaunchTemplateData.IamInstanceProfile.Name".to_string(),
1709 name.clone(),
1710 );
1711 }
1712 }
1713
1714 if let Some(block_device_mappings) = &data.block_device_mappings {
1715 for (i, bdm) in block_device_mappings.iter().enumerate() {
1716 let idx = i + 1;
1717 if let Some(device_name) = &bdm.device_name {
1718 form_data.insert(
1719 format!("LaunchTemplateData.BlockDeviceMapping.{}.DeviceName", idx),
1720 device_name.clone(),
1721 );
1722 }
1723 if let Some(ebs) = &bdm.ebs {
1724 if let Some(volume_size) = ebs.volume_size {
1725 form_data.insert(
1726 format!(
1727 "LaunchTemplateData.BlockDeviceMapping.{}.Ebs.VolumeSize",
1728 idx
1729 ),
1730 volume_size.to_string(),
1731 );
1732 }
1733 if let Some(volume_type) = &ebs.volume_type {
1734 form_data.insert(
1735 format!(
1736 "LaunchTemplateData.BlockDeviceMapping.{}.Ebs.VolumeType",
1737 idx
1738 ),
1739 volume_type.clone(),
1740 );
1741 }
1742 if let Some(delete_on_termination) = ebs.delete_on_termination {
1743 form_data.insert(
1744 format!(
1745 "LaunchTemplateData.BlockDeviceMapping.{}.Ebs.DeleteOnTermination",
1746 idx
1747 ),
1748 delete_on_termination.to_string(),
1749 );
1750 }
1751 if let Some(encrypted) = ebs.encrypted {
1752 form_data.insert(
1753 format!(
1754 "LaunchTemplateData.BlockDeviceMapping.{}.Ebs.Encrypted",
1755 idx
1756 ),
1757 encrypted.to_string(),
1758 );
1759 }
1760 if let Some(iops) = ebs.iops {
1761 form_data.insert(
1762 format!("LaunchTemplateData.BlockDeviceMapping.{}.Ebs.Iops", idx),
1763 iops.to_string(),
1764 );
1765 }
1766 if let Some(throughput) = ebs.throughput {
1767 form_data.insert(
1768 format!(
1769 "LaunchTemplateData.BlockDeviceMapping.{}.Ebs.Throughput",
1770 idx
1771 ),
1772 throughput.to_string(),
1773 );
1774 }
1775 }
1776 }
1777 }
1778
1779 if let Some(network_interfaces) = &data.network_interfaces {
1780 for (i, ni) in network_interfaces.iter().enumerate() {
1781 let idx = i + 1;
1782 if let Some(device_index) = ni.device_index {
1783 form_data.insert(
1784 format!("LaunchTemplateData.NetworkInterface.{}.DeviceIndex", idx),
1785 device_index.to_string(),
1786 );
1787 }
1788 if let Some(associate_public_ip) = ni.associate_public_ip_address {
1789 form_data.insert(
1790 format!(
1791 "LaunchTemplateData.NetworkInterface.{}.AssociatePublicIpAddress",
1792 idx
1793 ),
1794 associate_public_ip.to_string(),
1795 );
1796 }
1797 if let Some(subnet_id) = &ni.subnet_id {
1798 form_data.insert(
1799 format!("LaunchTemplateData.NetworkInterface.{}.SubnetId", idx),
1800 subnet_id.clone(),
1801 );
1802 }
1803 if let Some(groups) = &ni.groups {
1804 for (j, group) in groups.iter().enumerate() {
1805 form_data.insert(
1806 format!(
1807 "LaunchTemplateData.NetworkInterface.{}.SecurityGroupId.{}",
1808 idx,
1809 j + 1
1810 ),
1811 group.clone(),
1812 );
1813 }
1814 }
1815 }
1816 }
1817
1818 if let Some(metadata_options) = &data.metadata_options {
1819 if let Some(http_tokens) = &metadata_options.http_tokens {
1820 form_data.insert(
1821 "LaunchTemplateData.MetadataOptions.HttpTokens".to_string(),
1822 http_tokens.clone(),
1823 );
1824 }
1825 if let Some(http_endpoint) = &metadata_options.http_endpoint {
1826 form_data.insert(
1827 "LaunchTemplateData.MetadataOptions.HttpEndpoint".to_string(),
1828 http_endpoint.clone(),
1829 );
1830 }
1831 if let Some(http_put_response_hop_limit) = metadata_options.http_put_response_hop_limit
1832 {
1833 form_data.insert(
1834 "LaunchTemplateData.MetadataOptions.HttpPutResponseHopLimit".to_string(),
1835 http_put_response_hop_limit.to_string(),
1836 );
1837 }
1838 if let Some(instance_metadata_tags) = &metadata_options.instance_metadata_tags {
1839 form_data.insert(
1840 "LaunchTemplateData.MetadataOptions.InstanceMetadataTags".to_string(),
1841 instance_metadata_tags.clone(),
1842 );
1843 }
1844 }
1845
1846 Self::add_cpu_options(&mut form_data, data.cpu_options.as_ref());
1847
1848 if let Some(tag_specs) = &data.tag_specifications {
1849 Self::add_tag_specifications_with_prefix(
1850 &mut form_data,
1851 "LaunchTemplateData.TagSpecification",
1852 tag_specs,
1853 );
1854 }
1855
1856 if let Some(tag_specs) = &request.tag_specifications {
1857 Self::add_tag_specifications(&mut form_data, tag_specs);
1858 }
1859
1860 self.send_form(
1861 form_data,
1862 "CreateLaunchTemplate",
1863 &request.launch_template_name,
1864 )
1865 .await
1866 }
1867
1868 async fn create_launch_template_version(
1869 &self,
1870 request: CreateLaunchTemplateVersionRequest,
1871 ) -> Result<CreateLaunchTemplateVersionResponse> {
1872 let mut form_data = HashMap::new();
1873 form_data.insert(
1874 "Action".to_string(),
1875 "CreateLaunchTemplateVersion".to_string(),
1876 );
1877 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1878
1879 let resource_name;
1880 if let Some(ref id) = request.launch_template_id {
1881 form_data.insert("LaunchTemplateId".to_string(), id.clone());
1882 resource_name = id.clone();
1883 } else if let Some(ref name) = request.launch_template_name {
1884 form_data.insert("LaunchTemplateName".to_string(), name.clone());
1885 resource_name = name.clone();
1886 } else {
1887 return Err(alien_error::AlienError::new(ErrorData::InvalidInput {
1888 message: "Either launch_template_id or launch_template_name must be provided"
1889 .to_string(),
1890 field_name: Some("launch_template_id".to_string()),
1891 }));
1892 }
1893
1894 if let Some(ref source_version) = request.source_version {
1895 form_data.insert("SourceVersion".to_string(), source_version.clone());
1896 }
1897 if let Some(ref description) = request.version_description {
1898 form_data.insert("VersionDescription".to_string(), description.clone());
1899 }
1900
1901 let data = &request.launch_template_data;
1902 if let Some(ref user_data) = data.user_data {
1903 form_data.insert("LaunchTemplateData.UserData".to_string(), user_data.clone());
1904 }
1905 if let Some(ref image_id) = data.image_id {
1906 form_data.insert("LaunchTemplateData.ImageId".to_string(), image_id.clone());
1907 }
1908 if let Some(ref instance_type) = data.instance_type {
1909 form_data.insert(
1910 "LaunchTemplateData.InstanceType".to_string(),
1911 instance_type.clone(),
1912 );
1913 }
1914 if let Some(metadata_options) = &data.metadata_options {
1915 if let Some(http_tokens) = &metadata_options.http_tokens {
1916 form_data.insert(
1917 "LaunchTemplateData.MetadataOptions.HttpTokens".to_string(),
1918 http_tokens.clone(),
1919 );
1920 }
1921 if let Some(http_endpoint) = &metadata_options.http_endpoint {
1922 form_data.insert(
1923 "LaunchTemplateData.MetadataOptions.HttpEndpoint".to_string(),
1924 http_endpoint.clone(),
1925 );
1926 }
1927 if let Some(http_put_response_hop_limit) = metadata_options.http_put_response_hop_limit
1928 {
1929 form_data.insert(
1930 "LaunchTemplateData.MetadataOptions.HttpPutResponseHopLimit".to_string(),
1931 http_put_response_hop_limit.to_string(),
1932 );
1933 }
1934 if let Some(instance_metadata_tags) = &metadata_options.instance_metadata_tags {
1935 form_data.insert(
1936 "LaunchTemplateData.MetadataOptions.InstanceMetadataTags".to_string(),
1937 instance_metadata_tags.clone(),
1938 );
1939 }
1940 }
1941 Self::add_cpu_options(&mut form_data, data.cpu_options.as_ref());
1942 if let Some(tag_specs) = &data.tag_specifications {
1943 Self::add_tag_specifications_with_prefix(
1944 &mut form_data,
1945 "LaunchTemplateData.TagSpecification",
1946 tag_specs,
1947 );
1948 }
1949
1950 self.send_form(form_data, "CreateLaunchTemplateVersion", &resource_name)
1951 .await
1952 }
1953
1954 async fn delete_launch_template(
1955 &self,
1956 request: DeleteLaunchTemplateRequest,
1957 ) -> Result<DeleteLaunchTemplateResponse> {
1958 let mut form_data = HashMap::new();
1959 form_data.insert("Action".to_string(), "DeleteLaunchTemplate".to_string());
1960 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1961
1962 let resource: String;
1963 if let Some(launch_template_id) = &request.launch_template_id {
1964 form_data.insert("LaunchTemplateId".to_string(), launch_template_id.clone());
1965 resource = launch_template_id.clone();
1966 } else if let Some(launch_template_name) = &request.launch_template_name {
1967 form_data.insert(
1968 "LaunchTemplateName".to_string(),
1969 launch_template_name.clone(),
1970 );
1971 resource = launch_template_name.clone();
1972 } else {
1973 resource = "unknown".to_string();
1974 }
1975
1976 self.send_form(form_data, "DeleteLaunchTemplate", &resource)
1977 .await
1978 }
1979
1980 async fn describe_launch_templates(
1981 &self,
1982 request: DescribeLaunchTemplatesRequest,
1983 ) -> Result<DescribeLaunchTemplatesResponse> {
1984 let mut form_data = HashMap::new();
1985 form_data.insert("Action".to_string(), "DescribeLaunchTemplates".to_string());
1986 form_data.insert("Version".to_string(), "2016-11-15".to_string());
1987
1988 if let Some(launch_template_ids) = &request.launch_template_ids {
1989 for (i, lt_id) in launch_template_ids.iter().enumerate() {
1990 form_data.insert(format!("LaunchTemplateId.{}", i + 1), lt_id.clone());
1991 }
1992 }
1993
1994 if let Some(launch_template_names) = &request.launch_template_names {
1995 for (i, lt_name) in launch_template_names.iter().enumerate() {
1996 form_data.insert(format!("LaunchTemplateName.{}", i + 1), lt_name.clone());
1997 }
1998 }
1999
2000 if let Some(filters) = &request.filters {
2001 Self::add_filters(&mut form_data, filters);
2002 }
2003
2004 if let Some(max_results) = request.max_results {
2005 form_data.insert("MaxResults".to_string(), max_results.to_string());
2006 }
2007
2008 if let Some(next_token) = &request.next_token {
2009 form_data.insert("NextToken".to_string(), next_token.clone());
2010 }
2011
2012 self.send_form(form_data, "DescribeLaunchTemplates", "LaunchTemplate")
2013 .await
2014 }
2015
2016 async fn get_console_output(&self, instance_id: String) -> Result<GetConsoleOutputResponse> {
2017 let mut form_data = HashMap::new();
2018 form_data.insert("Action".to_string(), "GetConsoleOutput".to_string());
2019 form_data.insert("Version".to_string(), "2016-11-15".to_string());
2020 form_data.insert("InstanceId".to_string(), instance_id.clone());
2021
2022 self.send_form(form_data, "GetConsoleOutput", &instance_id)
2023 .await
2024 }
2025}
2026
2027impl Ec2Client {
2028 fn add_cpu_options(
2035 form_data: &mut HashMap<String, String>,
2036 cpu_options: Option<&LaunchTemplateCpuOptions>,
2037 ) {
2038 let Some(cpu_options) = cpu_options else {
2039 return;
2040 };
2041 if let Some(nested) = &cpu_options.nested_virtualization {
2042 form_data.insert(
2043 "LaunchTemplateData.CpuOptions.NestedVirtualization".to_string(),
2044 nested.clone(),
2045 );
2046 }
2047 }
2048
2049 fn add_ip_permissions(form_data: &mut HashMap<String, String>, permissions: &[IpPermission]) {
2051 for (i, perm) in permissions.iter().enumerate() {
2052 let idx = i + 1;
2053 form_data.insert(
2054 format!("IpPermissions.{}.IpProtocol", idx),
2055 perm.ip_protocol.clone(),
2056 );
2057
2058 if let Some(from_port) = perm.from_port {
2059 form_data.insert(
2060 format!("IpPermissions.{}.FromPort", idx),
2061 from_port.to_string(),
2062 );
2063 }
2064
2065 if let Some(to_port) = perm.to_port {
2066 form_data.insert(format!("IpPermissions.{}.ToPort", idx), to_port.to_string());
2067 }
2068
2069 if let Some(ip_ranges) = &perm.ip_ranges {
2070 for (j, range) in ip_ranges.iter().enumerate() {
2071 form_data.insert(
2072 format!("IpPermissions.{}.IpRanges.{}.CidrIp", idx, j + 1),
2073 range.cidr_ip.clone(),
2074 );
2075 if let Some(description) = &range.description {
2076 form_data.insert(
2077 format!("IpPermissions.{}.IpRanges.{}.Description", idx, j + 1),
2078 description.clone(),
2079 );
2080 }
2081 }
2082 }
2083
2084 if let Some(ipv6_ranges) = &perm.ipv6_ranges {
2085 for (j, range) in ipv6_ranges.iter().enumerate() {
2086 form_data.insert(
2087 format!("IpPermissions.{}.Ipv6Ranges.{}.CidrIpv6", idx, j + 1),
2088 range.cidr_ipv6.clone(),
2089 );
2090 if let Some(description) = &range.description {
2091 form_data.insert(
2092 format!("IpPermissions.{}.Ipv6Ranges.{}.Description", idx, j + 1),
2093 description.clone(),
2094 );
2095 }
2096 }
2097 }
2098
2099 if let Some(user_id_group_pairs) = &perm.user_id_group_pairs {
2100 for (j, pair) in user_id_group_pairs.iter().enumerate() {
2101 if let Some(group_id) = &pair.group_id {
2102 form_data.insert(
2103 format!("IpPermissions.{}.Groups.{}.GroupId", idx, j + 1),
2104 group_id.clone(),
2105 );
2106 }
2107 if let Some(user_id) = &pair.user_id {
2108 form_data.insert(
2109 format!("IpPermissions.{}.Groups.{}.UserId", idx, j + 1),
2110 user_id.clone(),
2111 );
2112 }
2113 if let Some(description) = &pair.description {
2114 form_data.insert(
2115 format!("IpPermissions.{}.Groups.{}.Description", idx, j + 1),
2116 description.clone(),
2117 );
2118 }
2119 }
2120 }
2121 }
2122 }
2123}
2124
2125#[derive(Debug, Clone, Serialize, Builder)]
2131pub struct Filter {
2132 pub name: String,
2134 pub values: Vec<String>,
2136}
2137
2138#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
2141pub struct Tag {
2142 pub key: String,
2144 pub value: String,
2146}
2147
2148#[derive(Debug, Clone, Serialize, Builder)]
2150pub struct TagSpecification {
2151 pub resource_type: String,
2153 pub tags: Vec<Tag>,
2155}
2156
2157#[derive(Debug, Clone, Serialize, Builder, Default)]
2163pub struct DescribeVpcsRequest {
2164 #[serde(skip_serializing_if = "Option::is_none")]
2165 pub vpc_ids: Option<Vec<String>>,
2166 #[serde(skip_serializing_if = "Option::is_none")]
2167 pub filters: Option<Vec<Filter>>,
2168 #[serde(skip_serializing_if = "Option::is_none")]
2169 pub max_results: Option<i32>,
2170 #[serde(skip_serializing_if = "Option::is_none")]
2171 pub next_token: Option<String>,
2172}
2173
2174#[derive(Debug, Deserialize)]
2176#[serde(rename_all = "camelCase")]
2177pub struct DescribeVpcsResponse {
2178 #[serde(rename = "vpcSet")]
2179 pub vpc_set: Option<VpcSet>,
2180 #[serde(rename = "nextToken")]
2181 pub next_token: Option<String>,
2182}
2183
2184#[derive(Debug, Deserialize)]
2185#[serde(rename_all = "camelCase")]
2186pub struct VpcSet {
2187 #[serde(rename = "item", default)]
2188 pub items: Vec<Vpc>,
2189}
2190
2191#[derive(Debug, Clone, Deserialize)]
2193#[serde(rename_all = "camelCase")]
2194pub struct Vpc {
2195 pub vpc_id: Option<String>,
2196 pub state: Option<String>,
2197 pub cidr_block: Option<String>,
2198 pub owner_id: Option<String>,
2199 pub instance_tenancy: Option<String>,
2200 pub is_default: Option<bool>,
2201 #[serde(rename = "tagSet")]
2202 pub tag_set: Option<TagSet>,
2203}
2204
2205#[derive(Debug, Clone, Deserialize)]
2206#[serde(rename_all = "camelCase")]
2207pub struct TagSet {
2208 #[serde(rename = "item", default)]
2209 pub items: Vec<Tag>,
2210}
2211
2212#[derive(Debug, Clone, Serialize, Builder)]
2214pub struct DescribeVpcAttributeRequest {
2215 pub vpc_id: String,
2216 pub attribute: String,
2218}
2219
2220#[derive(Debug, Deserialize)]
2222#[serde(rename_all = "camelCase")]
2223pub struct DescribeVpcAttributeResponse {
2224 pub vpc_id: Option<String>,
2225 #[serde(rename = "enableDnsSupport")]
2226 pub enable_dns_support: Option<AttributeBooleanValue>,
2227 #[serde(rename = "enableDnsHostnames")]
2228 pub enable_dns_hostnames: Option<AttributeBooleanValue>,
2229}
2230
2231#[derive(Debug, Deserialize)]
2232#[serde(rename_all = "camelCase")]
2233pub struct AttributeBooleanValue {
2234 pub value: Option<bool>,
2235}
2236
2237#[derive(Debug, Clone, Serialize, Builder)]
2239pub struct CreateVpcRequest {
2240 pub cidr_block: String,
2241 #[serde(skip_serializing_if = "Option::is_none")]
2242 pub instance_tenancy: Option<String>,
2243 #[serde(skip_serializing_if = "Option::is_none")]
2244 pub amazon_provided_ipv6_cidr_block: Option<bool>,
2245 #[serde(skip_serializing_if = "Option::is_none")]
2246 pub tag_specifications: Option<Vec<TagSpecification>>,
2247}
2248
2249#[derive(Debug, Deserialize)]
2251#[serde(rename_all = "camelCase")]
2252pub struct CreateVpcResponse {
2253 pub vpc: Option<Vpc>,
2254}
2255
2256#[derive(Debug, Clone, Serialize, Builder)]
2258pub struct ModifyVpcAttributeRequest {
2259 pub vpc_id: String,
2260 #[serde(skip_serializing_if = "Option::is_none")]
2261 pub enable_dns_support: Option<bool>,
2262 #[serde(skip_serializing_if = "Option::is_none")]
2263 pub enable_dns_hostnames: Option<bool>,
2264}
2265
2266#[derive(Debug, Clone, Serialize, Builder, Default)]
2272pub struct DescribeSubnetsRequest {
2273 #[serde(skip_serializing_if = "Option::is_none")]
2274 pub subnet_ids: Option<Vec<String>>,
2275 #[serde(skip_serializing_if = "Option::is_none")]
2276 pub filters: Option<Vec<Filter>>,
2277 #[serde(skip_serializing_if = "Option::is_none")]
2278 pub max_results: Option<i32>,
2279 #[serde(skip_serializing_if = "Option::is_none")]
2280 pub next_token: Option<String>,
2281}
2282
2283#[derive(Debug, Deserialize)]
2285#[serde(rename_all = "camelCase")]
2286pub struct DescribeSubnetsResponse {
2287 #[serde(rename = "subnetSet")]
2288 pub subnet_set: Option<SubnetSet>,
2289 #[serde(rename = "nextToken")]
2290 pub next_token: Option<String>,
2291}
2292
2293#[derive(Debug, Deserialize)]
2294#[serde(rename_all = "camelCase")]
2295pub struct SubnetSet {
2296 #[serde(rename = "item", default)]
2297 pub items: Vec<Subnet>,
2298}
2299
2300#[derive(Debug, Clone, Deserialize)]
2302#[serde(rename_all = "camelCase")]
2303pub struct Subnet {
2304 pub subnet_id: Option<String>,
2305 pub vpc_id: Option<String>,
2306 pub state: Option<String>,
2307 pub cidr_block: Option<String>,
2308 pub availability_zone: Option<String>,
2309 pub availability_zone_id: Option<String>,
2310 pub available_ip_address_count: Option<i32>,
2311 pub default_for_az: Option<bool>,
2312 pub map_public_ip_on_launch: Option<bool>,
2313 #[serde(rename = "tagSet")]
2314 pub tag_set: Option<TagSet>,
2315}
2316
2317#[derive(Debug, Clone, Serialize, Builder)]
2319pub struct CreateSubnetRequest {
2320 pub vpc_id: String,
2321 pub cidr_block: String,
2322 #[serde(skip_serializing_if = "Option::is_none")]
2323 pub availability_zone: Option<String>,
2324 #[serde(skip_serializing_if = "Option::is_none")]
2325 pub availability_zone_id: Option<String>,
2326 #[serde(skip_serializing_if = "Option::is_none")]
2327 pub tag_specifications: Option<Vec<TagSpecification>>,
2328}
2329
2330#[derive(Debug, Deserialize)]
2332#[serde(rename_all = "camelCase")]
2333pub struct CreateSubnetResponse {
2334 pub subnet: Option<Subnet>,
2335}
2336
2337#[derive(Debug, Clone, Serialize, Builder, Default)]
2343pub struct CreateInternetGatewayRequest {
2344 #[serde(skip_serializing_if = "Option::is_none")]
2345 pub tag_specifications: Option<Vec<TagSpecification>>,
2346}
2347
2348#[derive(Debug, Deserialize)]
2350#[serde(rename_all = "camelCase")]
2351pub struct CreateInternetGatewayResponse {
2352 #[serde(rename = "internetGateway")]
2353 pub internet_gateway: Option<InternetGateway>,
2354}
2355
2356#[derive(Debug, Clone, Deserialize)]
2358#[serde(rename_all = "camelCase")]
2359pub struct InternetGateway {
2360 pub internet_gateway_id: Option<String>,
2361 #[serde(rename = "attachmentSet")]
2362 pub attachment_set: Option<InternetGatewayAttachmentSet>,
2363 #[serde(rename = "tagSet")]
2364 pub tag_set: Option<TagSet>,
2365}
2366
2367#[derive(Debug, Clone, Deserialize)]
2368#[serde(rename_all = "camelCase")]
2369pub struct InternetGatewayAttachmentSet {
2370 #[serde(rename = "item", default)]
2371 pub items: Vec<InternetGatewayAttachment>,
2372}
2373
2374#[derive(Debug, Clone, Deserialize)]
2375#[serde(rename_all = "camelCase")]
2376pub struct InternetGatewayAttachment {
2377 pub vpc_id: Option<String>,
2378 pub state: Option<String>,
2379}
2380
2381#[derive(Debug, Clone, Serialize, Builder)]
2383pub struct AttachInternetGatewayRequest {
2384 pub internet_gateway_id: String,
2385 pub vpc_id: String,
2386}
2387
2388#[derive(Debug, Clone, Serialize, Builder)]
2390pub struct DetachInternetGatewayRequest {
2391 pub internet_gateway_id: String,
2392 pub vpc_id: String,
2393}
2394
2395#[derive(Debug, Clone, Serialize, Builder, Default)]
2397pub struct DescribeInternetGatewaysRequest {
2398 #[serde(skip_serializing_if = "Option::is_none")]
2399 pub internet_gateway_ids: Option<Vec<String>>,
2400 #[serde(skip_serializing_if = "Option::is_none")]
2401 pub filters: Option<Vec<Filter>>,
2402 #[serde(skip_serializing_if = "Option::is_none")]
2403 pub max_results: Option<i32>,
2404 #[serde(skip_serializing_if = "Option::is_none")]
2405 pub next_token: Option<String>,
2406}
2407
2408#[derive(Debug, Deserialize)]
2410#[serde(rename_all = "camelCase")]
2411pub struct DescribeInternetGatewaysResponse {
2412 #[serde(rename = "internetGatewaySet")]
2413 pub internet_gateway_set: Option<InternetGatewaySet>,
2414 #[serde(rename = "nextToken")]
2415 pub next_token: Option<String>,
2416}
2417
2418#[derive(Debug, Deserialize)]
2419#[serde(rename_all = "camelCase")]
2420pub struct InternetGatewaySet {
2421 #[serde(rename = "item", default)]
2422 pub items: Vec<InternetGateway>,
2423}
2424
2425#[derive(Debug, Clone, Serialize, Builder)]
2431pub struct CreateNatGatewayRequest {
2432 pub subnet_id: String,
2434 #[serde(skip_serializing_if = "Option::is_none")]
2436 pub allocation_id: Option<String>,
2437 #[serde(skip_serializing_if = "Option::is_none")]
2439 pub connectivity_type: Option<String>,
2440 #[serde(skip_serializing_if = "Option::is_none")]
2442 pub private_ip_address: Option<String>,
2443 #[serde(skip_serializing_if = "Option::is_none")]
2444 pub tag_specifications: Option<Vec<TagSpecification>>,
2445}
2446
2447#[derive(Debug, Deserialize)]
2449#[serde(rename_all = "camelCase")]
2450pub struct CreateNatGatewayResponse {
2451 #[serde(rename = "natGateway")]
2452 pub nat_gateway: Option<NatGateway>,
2453}
2454
2455#[derive(Debug, Clone, Deserialize)]
2457#[serde(rename_all = "camelCase")]
2458pub struct NatGateway {
2459 pub nat_gateway_id: Option<String>,
2460 pub subnet_id: Option<String>,
2461 pub vpc_id: Option<String>,
2462 pub state: Option<String>,
2463 pub connectivity_type: Option<String>,
2464 #[serde(rename = "natGatewayAddressSet")]
2465 pub nat_gateway_address_set: Option<NatGatewayAddressSet>,
2466 #[serde(rename = "tagSet")]
2467 pub tag_set: Option<TagSet>,
2468}
2469
2470#[derive(Debug, Clone, Deserialize)]
2471#[serde(rename_all = "camelCase")]
2472pub struct NatGatewayAddressSet {
2473 #[serde(rename = "item", default)]
2474 pub items: Vec<NatGatewayAddress>,
2475}
2476
2477#[derive(Debug, Clone, Deserialize)]
2478#[serde(rename_all = "camelCase")]
2479pub struct NatGatewayAddress {
2480 pub allocation_id: Option<String>,
2481 pub network_interface_id: Option<String>,
2482 pub private_ip: Option<String>,
2483 pub public_ip: Option<String>,
2484}
2485
2486#[derive(Debug, Deserialize)]
2488#[serde(rename_all = "camelCase")]
2489pub struct DeleteNatGatewayResponse {
2490 pub nat_gateway_id: Option<String>,
2491}
2492
2493#[derive(Debug, Clone, Serialize, Builder, Default)]
2495pub struct DescribeNatGatewaysRequest {
2496 #[serde(skip_serializing_if = "Option::is_none")]
2497 pub nat_gateway_ids: Option<Vec<String>>,
2498 #[serde(skip_serializing_if = "Option::is_none")]
2499 pub filters: Option<Vec<Filter>>,
2500 #[serde(skip_serializing_if = "Option::is_none")]
2501 pub max_results: Option<i32>,
2502 #[serde(skip_serializing_if = "Option::is_none")]
2503 pub next_token: Option<String>,
2504}
2505
2506#[derive(Debug, Deserialize)]
2508#[serde(rename_all = "camelCase")]
2509pub struct DescribeNatGatewaysResponse {
2510 #[serde(rename = "natGatewaySet")]
2511 pub nat_gateway_set: Option<NatGatewaySet>,
2512 #[serde(rename = "nextToken")]
2513 pub next_token: Option<String>,
2514}
2515
2516#[derive(Debug, Deserialize)]
2517#[serde(rename_all = "camelCase")]
2518pub struct NatGatewaySet {
2519 #[serde(rename = "item", default)]
2520 pub items: Vec<NatGateway>,
2521}
2522
2523#[derive(Debug, Clone, Serialize, Builder, Default)]
2529pub struct AllocateAddressRequest {
2530 #[serde(skip_serializing_if = "Option::is_none")]
2532 pub domain: Option<String>,
2533 #[serde(skip_serializing_if = "Option::is_none")]
2534 pub tag_specifications: Option<Vec<TagSpecification>>,
2535}
2536
2537#[derive(Debug, Deserialize)]
2539#[serde(rename_all = "camelCase")]
2540pub struct AllocateAddressResponse {
2541 pub public_ip: Option<String>,
2542 pub allocation_id: Option<String>,
2543 pub domain: Option<String>,
2544}
2545
2546#[derive(Debug, Clone, Serialize, Builder, Default)]
2552pub struct DescribeRouteTablesRequest {
2553 #[serde(skip_serializing_if = "Option::is_none")]
2554 pub route_table_ids: Option<Vec<String>>,
2555 #[serde(skip_serializing_if = "Option::is_none")]
2556 pub filters: Option<Vec<Filter>>,
2557 #[serde(skip_serializing_if = "Option::is_none")]
2558 pub max_results: Option<i32>,
2559 #[serde(skip_serializing_if = "Option::is_none")]
2560 pub next_token: Option<String>,
2561}
2562
2563#[derive(Debug, Deserialize)]
2565#[serde(rename_all = "camelCase")]
2566pub struct DescribeRouteTablesResponse {
2567 #[serde(rename = "routeTableSet")]
2568 pub route_table_set: Option<RouteTableSet>,
2569 #[serde(rename = "nextToken")]
2570 pub next_token: Option<String>,
2571}
2572
2573#[derive(Debug, Deserialize)]
2574#[serde(rename_all = "camelCase")]
2575pub struct RouteTableSet {
2576 #[serde(rename = "item", default)]
2577 pub items: Vec<RouteTable>,
2578}
2579
2580#[derive(Debug, Clone, Deserialize)]
2582#[serde(rename_all = "camelCase")]
2583pub struct RouteTable {
2584 pub route_table_id: Option<String>,
2585 pub vpc_id: Option<String>,
2586 #[serde(rename = "routeSet")]
2587 pub route_set: Option<RouteSet>,
2588 #[serde(rename = "associationSet")]
2589 pub association_set: Option<RouteTableAssociationSet>,
2590 #[serde(rename = "tagSet")]
2591 pub tag_set: Option<TagSet>,
2592}
2593
2594#[derive(Debug, Clone, Deserialize)]
2595#[serde(rename_all = "camelCase")]
2596pub struct RouteSet {
2597 #[serde(rename = "item", default)]
2598 pub items: Vec<Route>,
2599}
2600
2601#[derive(Debug, Clone, Deserialize)]
2603#[serde(rename_all = "camelCase")]
2604pub struct Route {
2605 pub destination_cidr_block: Option<String>,
2606 pub gateway_id: Option<String>,
2607 pub nat_gateway_id: Option<String>,
2608 pub instance_id: Option<String>,
2609 pub network_interface_id: Option<String>,
2610 pub vpc_peering_connection_id: Option<String>,
2611 pub transit_gateway_id: Option<String>,
2612 pub state: Option<String>,
2613 pub origin: Option<String>,
2614}
2615
2616#[derive(Debug, Clone, Deserialize)]
2617#[serde(rename_all = "camelCase")]
2618pub struct RouteTableAssociationSet {
2619 #[serde(rename = "item", default)]
2620 pub items: Vec<RouteTableAssociation>,
2621}
2622
2623#[derive(Debug, Clone, Deserialize)]
2625#[serde(rename_all = "camelCase")]
2626pub struct RouteTableAssociation {
2627 pub route_table_association_id: Option<String>,
2628 pub route_table_id: Option<String>,
2629 pub subnet_id: Option<String>,
2630 pub main: Option<bool>,
2631}
2632
2633#[derive(Debug, Clone, Serialize, Builder)]
2635pub struct CreateRouteTableRequest {
2636 pub vpc_id: String,
2637 #[serde(skip_serializing_if = "Option::is_none")]
2638 pub tag_specifications: Option<Vec<TagSpecification>>,
2639}
2640
2641#[derive(Debug, Deserialize)]
2643#[serde(rename_all = "camelCase")]
2644pub struct CreateRouteTableResponse {
2645 #[serde(rename = "routeTable")]
2646 pub route_table: Option<RouteTable>,
2647}
2648
2649#[derive(Debug, Clone, Serialize, Builder)]
2651pub struct CreateRouteRequest {
2652 pub route_table_id: String,
2653 pub destination_cidr_block: String,
2654 #[serde(skip_serializing_if = "Option::is_none")]
2655 pub gateway_id: Option<String>,
2656 #[serde(skip_serializing_if = "Option::is_none")]
2657 pub nat_gateway_id: Option<String>,
2658 #[serde(skip_serializing_if = "Option::is_none")]
2659 pub instance_id: Option<String>,
2660 #[serde(skip_serializing_if = "Option::is_none")]
2661 pub network_interface_id: Option<String>,
2662 #[serde(skip_serializing_if = "Option::is_none")]
2663 pub vpc_peering_connection_id: Option<String>,
2664 #[serde(skip_serializing_if = "Option::is_none")]
2665 pub transit_gateway_id: Option<String>,
2666}
2667
2668#[derive(Debug, Clone, Serialize, Builder)]
2670pub struct DeleteRouteRequest {
2671 pub route_table_id: String,
2672 pub destination_cidr_block: String,
2673}
2674
2675#[derive(Debug, Clone, Serialize, Builder)]
2677pub struct AssociateRouteTableRequest {
2678 pub route_table_id: String,
2679 pub subnet_id: String,
2680}
2681
2682#[derive(Debug, Deserialize)]
2684#[serde(rename_all = "camelCase")]
2685pub struct AssociateRouteTableResponse {
2686 pub association_id: Option<String>,
2687}
2688
2689#[derive(Debug, Clone, Serialize, Builder, Default)]
2695pub struct DescribeSecurityGroupsRequest {
2696 #[serde(skip_serializing_if = "Option::is_none")]
2697 pub group_ids: Option<Vec<String>>,
2698 #[serde(skip_serializing_if = "Option::is_none")]
2699 pub group_names: Option<Vec<String>>,
2700 #[serde(skip_serializing_if = "Option::is_none")]
2701 pub filters: Option<Vec<Filter>>,
2702 #[serde(skip_serializing_if = "Option::is_none")]
2703 pub max_results: Option<i32>,
2704 #[serde(skip_serializing_if = "Option::is_none")]
2705 pub next_token: Option<String>,
2706}
2707
2708#[derive(Debug, Deserialize)]
2710#[serde(rename_all = "camelCase")]
2711pub struct DescribeSecurityGroupsResponse {
2712 #[serde(rename = "securityGroupInfo")]
2713 pub security_group_info: Option<SecurityGroupSet>,
2714 #[serde(rename = "nextToken")]
2715 pub next_token: Option<String>,
2716}
2717
2718#[derive(Debug, Deserialize)]
2719#[serde(rename_all = "camelCase")]
2720pub struct SecurityGroupSet {
2721 #[serde(rename = "item", default)]
2722 pub items: Vec<SecurityGroup>,
2723}
2724
2725#[derive(Debug, Clone, Deserialize)]
2727#[serde(rename_all = "camelCase")]
2728pub struct SecurityGroup {
2729 pub group_id: Option<String>,
2730 pub group_name: Option<String>,
2731 pub vpc_id: Option<String>,
2732 pub owner_id: Option<String>,
2733 pub group_description: Option<String>,
2734 #[serde(rename = "ipPermissions")]
2735 pub ip_permissions: Option<IpPermissionSet>,
2736 #[serde(rename = "ipPermissionsEgress")]
2737 pub ip_permissions_egress: Option<IpPermissionSet>,
2738 #[serde(rename = "tagSet")]
2739 pub tag_set: Option<TagSet>,
2740}
2741
2742#[derive(Debug, Clone, Serialize, Builder, Default)]
2744pub struct DescribeNetworkInterfacesRequest {
2745 #[serde(skip_serializing_if = "Option::is_none")]
2746 pub network_interface_ids: Option<Vec<String>>,
2747 #[serde(skip_serializing_if = "Option::is_none")]
2748 pub filters: Option<Vec<Filter>>,
2749 #[serde(skip_serializing_if = "Option::is_none")]
2750 pub max_results: Option<i32>,
2751 #[serde(skip_serializing_if = "Option::is_none")]
2752 pub next_token: Option<String>,
2753}
2754
2755#[derive(Debug, Deserialize)]
2757#[serde(rename_all = "camelCase")]
2758pub struct DescribeNetworkInterfacesResponse {
2759 #[serde(rename = "networkInterfaceSet")]
2760 pub network_interface_set: Option<NetworkInterfaceSet>,
2761 #[serde(rename = "nextToken")]
2762 pub next_token: Option<String>,
2763}
2764
2765#[derive(Debug, Deserialize)]
2766#[serde(rename_all = "camelCase")]
2767pub struct NetworkInterfaceSet {
2768 #[serde(rename = "item", default)]
2769 pub items: Vec<NetworkInterface>,
2770}
2771
2772#[derive(Debug, Clone, Deserialize)]
2774#[serde(rename_all = "camelCase")]
2775pub struct NetworkInterface {
2776 pub network_interface_id: Option<String>,
2777 pub status: Option<String>,
2778 pub description: Option<String>,
2779 pub subnet_id: Option<String>,
2780 pub vpc_id: Option<String>,
2781 #[serde(rename = "groupSet")]
2782 pub group_set: Option<GroupIdentifierSet>,
2783}
2784
2785#[derive(Debug, Clone, Deserialize)]
2786#[serde(rename_all = "camelCase")]
2787pub struct GroupIdentifierSet {
2788 #[serde(rename = "item", default)]
2789 pub items: Vec<GroupIdentifier>,
2790}
2791
2792#[derive(Debug, Clone, Deserialize)]
2793#[serde(rename_all = "camelCase")]
2794pub struct GroupIdentifier {
2795 pub group_id: Option<String>,
2796 pub group_name: Option<String>,
2797}
2798
2799#[derive(Debug, Clone, Deserialize)]
2800#[serde(rename_all = "camelCase")]
2801pub struct IpPermissionSet {
2802 #[serde(rename = "item", default)]
2803 pub items: Vec<IpPermissionResponse>,
2804}
2805
2806#[derive(Debug, Clone, Deserialize)]
2808#[serde(rename_all = "camelCase")]
2809pub struct IpPermissionResponse {
2810 pub ip_protocol: Option<String>,
2811 pub from_port: Option<i32>,
2812 pub to_port: Option<i32>,
2813 #[serde(rename = "ipRanges")]
2814 pub ip_ranges: Option<IpRangeSet>,
2815 #[serde(rename = "ipv6Ranges")]
2816 pub ipv6_ranges: Option<Ipv6RangeSet>,
2817 #[serde(rename = "groups")]
2818 pub groups: Option<UserIdGroupPairSet>,
2819}
2820
2821#[derive(Debug, Clone, Deserialize)]
2822#[serde(rename_all = "camelCase")]
2823pub struct IpRangeSet {
2824 #[serde(rename = "item", default)]
2825 pub items: Vec<IpRangeResponse>,
2826}
2827
2828#[derive(Debug, Clone, Deserialize)]
2829#[serde(rename_all = "camelCase")]
2830pub struct IpRangeResponse {
2831 pub cidr_ip: Option<String>,
2832 pub description: Option<String>,
2833}
2834
2835#[derive(Debug, Clone, Deserialize)]
2836#[serde(rename_all = "camelCase")]
2837pub struct Ipv6RangeSet {
2838 #[serde(rename = "item", default)]
2839 pub items: Vec<Ipv6RangeResponse>,
2840}
2841
2842#[derive(Debug, Clone, Deserialize)]
2843#[serde(rename_all = "camelCase")]
2844pub struct Ipv6RangeResponse {
2845 pub cidr_ipv6: Option<String>,
2846 pub description: Option<String>,
2847}
2848
2849#[derive(Debug, Clone, Deserialize)]
2850#[serde(rename_all = "camelCase")]
2851pub struct UserIdGroupPairSet {
2852 #[serde(rename = "item", default)]
2853 pub items: Vec<UserIdGroupPairResponse>,
2854}
2855
2856#[derive(Debug, Clone, Deserialize)]
2857#[serde(rename_all = "camelCase")]
2858pub struct UserIdGroupPairResponse {
2859 pub group_id: Option<String>,
2860 pub user_id: Option<String>,
2861 pub description: Option<String>,
2862}
2863
2864#[derive(Debug, Clone, Serialize, Builder)]
2866pub struct CreateSecurityGroupRequest {
2867 pub group_name: String,
2868 pub description: String,
2869 pub vpc_id: String,
2870 #[serde(skip_serializing_if = "Option::is_none")]
2871 pub tag_specifications: Option<Vec<TagSpecification>>,
2872}
2873
2874#[derive(Debug, Deserialize)]
2876#[serde(rename_all = "camelCase")]
2877pub struct CreateSecurityGroupResponse {
2878 pub group_id: Option<String>,
2879}
2880
2881#[derive(Debug, Clone, Serialize, Builder)]
2883pub struct IpPermission {
2884 pub ip_protocol: String,
2886 #[serde(skip_serializing_if = "Option::is_none")]
2888 pub from_port: Option<i32>,
2889 #[serde(skip_serializing_if = "Option::is_none")]
2891 pub to_port: Option<i32>,
2892 #[serde(skip_serializing_if = "Option::is_none")]
2894 pub ip_ranges: Option<Vec<IpRange>>,
2895 #[serde(skip_serializing_if = "Option::is_none")]
2897 pub ipv6_ranges: Option<Vec<Ipv6Range>>,
2898 #[serde(skip_serializing_if = "Option::is_none")]
2900 pub user_id_group_pairs: Option<Vec<UserIdGroupPair>>,
2901}
2902
2903#[derive(Debug, Clone, Serialize, Builder)]
2905pub struct IpRange {
2906 pub cidr_ip: String,
2907 #[serde(skip_serializing_if = "Option::is_none")]
2908 pub description: Option<String>,
2909}
2910
2911#[derive(Debug, Clone, Serialize, Builder)]
2913pub struct Ipv6Range {
2914 pub cidr_ipv6: String,
2915 #[serde(skip_serializing_if = "Option::is_none")]
2916 pub description: Option<String>,
2917}
2918
2919#[derive(Debug, Clone, Serialize, Builder)]
2921pub struct UserIdGroupPair {
2922 #[serde(skip_serializing_if = "Option::is_none")]
2923 pub group_id: Option<String>,
2924 #[serde(skip_serializing_if = "Option::is_none")]
2925 pub user_id: Option<String>,
2926 #[serde(skip_serializing_if = "Option::is_none")]
2927 pub description: Option<String>,
2928}
2929
2930#[derive(Debug, Clone, Serialize, Builder)]
2932pub struct AuthorizeSecurityGroupIngressRequest {
2933 pub group_id: String,
2934 pub ip_permissions: Vec<IpPermission>,
2935}
2936
2937#[derive(Debug, Clone, Serialize, Builder)]
2939pub struct AuthorizeSecurityGroupEgressRequest {
2940 pub group_id: String,
2941 pub ip_permissions: Vec<IpPermission>,
2942}
2943
2944#[derive(Debug, Clone, Serialize, Builder)]
2946pub struct RevokeSecurityGroupIngressRequest {
2947 pub group_id: String,
2948 pub ip_permissions: Vec<IpPermission>,
2949}
2950
2951#[derive(Debug, Clone, Serialize, Builder)]
2953pub struct RevokeSecurityGroupEgressRequest {
2954 pub group_id: String,
2955 pub ip_permissions: Vec<IpPermission>,
2956}
2957
2958#[derive(Debug, Clone, Serialize, Builder, Default)]
2964pub struct DescribeAvailabilityZonesRequest {
2965 #[serde(skip_serializing_if = "Option::is_none")]
2966 pub zone_names: Option<Vec<String>>,
2967 #[serde(skip_serializing_if = "Option::is_none")]
2968 pub zone_ids: Option<Vec<String>>,
2969 #[serde(skip_serializing_if = "Option::is_none")]
2970 pub filters: Option<Vec<Filter>>,
2971 #[serde(skip_serializing_if = "Option::is_none")]
2972 pub all_availability_zones: Option<bool>,
2973}
2974
2975#[derive(Debug, Deserialize)]
2977#[serde(rename_all = "camelCase")]
2978pub struct DescribeAvailabilityZonesResponse {
2979 #[serde(rename = "availabilityZoneInfo")]
2980 pub availability_zone_info: Option<AvailabilityZoneSet>,
2981}
2982
2983#[derive(Debug, Deserialize)]
2984#[serde(rename_all = "camelCase")]
2985pub struct AvailabilityZoneSet {
2986 #[serde(rename = "item", default)]
2987 pub items: Vec<AvailabilityZone>,
2988}
2989
2990#[derive(Debug, Clone, Deserialize)]
2992#[serde(rename_all = "camelCase")]
2993pub struct AvailabilityZone {
2994 pub zone_name: Option<String>,
2995 pub zone_id: Option<String>,
2996 pub zone_state: Option<String>,
2997 pub region_name: Option<String>,
2998 pub zone_type: Option<String>,
2999 pub opt_in_status: Option<String>,
3000}
3001
3002#[derive(Debug, Clone, Serialize, Builder, Default)]
3008pub struct DescribeImagesRequest {
3009 #[serde(skip_serializing_if = "Option::is_none")]
3011 pub image_ids: Option<Vec<String>>,
3012 #[serde(skip_serializing_if = "Option::is_none")]
3014 pub owners: Option<Vec<String>>,
3015 #[serde(skip_serializing_if = "Option::is_none")]
3017 pub executable_users: Option<Vec<String>>,
3018 #[serde(skip_serializing_if = "Option::is_none")]
3020 pub filters: Option<Vec<Filter>>,
3021 #[serde(skip_serializing_if = "Option::is_none")]
3023 pub include_deprecated: Option<bool>,
3024 #[serde(skip_serializing_if = "Option::is_none")]
3026 pub max_results: Option<i32>,
3027 #[serde(skip_serializing_if = "Option::is_none")]
3029 pub next_token: Option<String>,
3030}
3031
3032#[derive(Debug, Deserialize)]
3034#[serde(rename_all = "camelCase")]
3035pub struct DescribeImagesResponse {
3036 #[serde(rename = "imagesSet")]
3037 pub images_set: Option<ImageSet>,
3038 #[serde(rename = "nextToken")]
3039 pub next_token: Option<String>,
3040}
3041
3042#[derive(Debug, Deserialize)]
3043#[serde(rename_all = "camelCase")]
3044pub struct ImageSet {
3045 #[serde(rename = "item", default)]
3046 pub items: Vec<Image>,
3047}
3048
3049#[derive(Debug, Clone, Deserialize)]
3051#[serde(rename_all = "camelCase")]
3052pub struct Image {
3053 pub image_id: Option<String>,
3054 pub name: Option<String>,
3055 pub description: Option<String>,
3056 pub image_location: Option<String>,
3057 pub state: Option<String>,
3058 pub owner_id: Option<String>,
3059 pub creation_date: Option<String>,
3060 pub architecture: Option<String>,
3061 pub platform: Option<String>,
3062 pub platform_details: Option<String>,
3063 pub image_type: Option<String>,
3064 pub root_device_type: Option<String>,
3065 pub root_device_name: Option<String>,
3066 pub virtualization_type: Option<String>,
3067 pub hypervisor: Option<String>,
3068 pub is_public: Option<bool>,
3069 pub deprecation_time: Option<String>,
3070 #[serde(rename = "tagSet")]
3071 pub tag_set: Option<TagSet>,
3072 #[serde(rename = "blockDeviceMapping")]
3073 pub block_device_mapping: Option<BlockDeviceMappingSet>,
3074}
3075
3076#[derive(Debug, Clone, Deserialize)]
3077#[serde(rename_all = "camelCase")]
3078pub struct BlockDeviceMappingSet {
3079 #[serde(rename = "item", default)]
3080 pub items: Vec<BlockDeviceMapping>,
3081}
3082
3083#[derive(Debug, Clone, Deserialize)]
3084#[serde(rename_all = "camelCase")]
3085pub struct BlockDeviceMapping {
3086 pub device_name: Option<String>,
3087 pub ebs: Option<EbsBlockDevice>,
3088 pub virtual_name: Option<String>,
3089 pub no_device: Option<String>,
3090}
3091
3092#[derive(Debug, Clone, Deserialize)]
3093#[serde(rename_all = "camelCase")]
3094pub struct EbsBlockDevice {
3095 pub snapshot_id: Option<String>,
3096 pub volume_size: Option<i32>,
3097 pub volume_type: Option<String>,
3098 pub delete_on_termination: Option<bool>,
3099 pub encrypted: Option<bool>,
3100 pub iops: Option<i32>,
3101 pub throughput: Option<i32>,
3102}
3103
3104#[derive(Debug, Deserialize)]
3110#[serde(rename_all = "camelCase")]
3111pub struct TerminateInstancesResponse {
3112 #[serde(rename = "instancesSet")]
3113 pub instances_set: Option<TerminatingInstanceSet>,
3114}
3115
3116#[derive(Debug, Deserialize)]
3117#[serde(rename_all = "camelCase")]
3118pub struct TerminatingInstanceSet {
3119 #[serde(rename = "item", default)]
3120 pub items: Vec<TerminatingInstance>,
3121}
3122
3123#[derive(Debug, Clone, Deserialize)]
3124#[serde(rename_all = "camelCase")]
3125pub struct TerminatingInstance {
3126 pub instance_id: Option<String>,
3127 pub current_state: Option<InstanceState>,
3128 pub previous_state: Option<InstanceState>,
3129}
3130
3131#[derive(Debug, Clone, Deserialize)]
3132#[serde(rename_all = "camelCase")]
3133pub struct InstanceState {
3134 pub code: Option<i32>,
3135 pub name: Option<String>,
3136}
3137
3138#[derive(Debug, Clone, Serialize, Builder, Default)]
3140pub struct DescribeInstancesRequest {
3141 #[serde(skip_serializing_if = "Option::is_none")]
3142 pub instance_ids: Option<Vec<String>>,
3143 #[serde(skip_serializing_if = "Option::is_none")]
3144 pub filters: Option<Vec<Filter>>,
3145 #[serde(skip_serializing_if = "Option::is_none")]
3146 pub max_results: Option<i32>,
3147 #[serde(skip_serializing_if = "Option::is_none")]
3148 pub next_token: Option<String>,
3149}
3150
3151#[derive(Debug, Deserialize)]
3153#[serde(rename_all = "camelCase")]
3154pub struct DescribeInstancesResponse {
3155 #[serde(rename = "reservationSet")]
3156 pub reservation_set: Option<ReservationSet>,
3157 #[serde(rename = "nextToken")]
3158 pub next_token: Option<String>,
3159}
3160
3161#[derive(Debug, Deserialize)]
3162#[serde(rename_all = "camelCase")]
3163pub struct ReservationSet {
3164 #[serde(rename = "item", default)]
3165 pub items: Vec<Reservation>,
3166}
3167
3168#[derive(Debug, Clone, Deserialize)]
3169#[serde(rename_all = "camelCase")]
3170pub struct Reservation {
3171 pub reservation_id: Option<String>,
3172 pub owner_id: Option<String>,
3173 #[serde(rename = "instancesSet")]
3174 pub instances_set: Option<InstanceSet>,
3175}
3176
3177#[derive(Debug, Clone, Deserialize)]
3178#[serde(rename_all = "camelCase")]
3179pub struct InstanceSet {
3180 #[serde(rename = "item", default)]
3181 pub items: Vec<Instance>,
3182}
3183
3184#[derive(Debug, Clone, Deserialize)]
3185#[serde(rename_all = "camelCase")]
3186pub struct Instance {
3187 pub instance_id: Option<String>,
3188 pub image_id: Option<String>,
3189 pub instance_type: Option<String>,
3190 pub key_name: Option<String>,
3191 pub launch_time: Option<String>,
3192 pub placement: Option<Placement>,
3193 pub private_dns_name: Option<String>,
3194 pub private_ip_address: Option<String>,
3195 pub public_dns_name: Option<String>,
3196 pub public_ip_address: Option<String>,
3197 pub state_reason: Option<StateReason>,
3198 pub instance_state: Option<InstanceState>,
3199 pub subnet_id: Option<String>,
3200 pub vpc_id: Option<String>,
3201 pub architecture: Option<String>,
3202 pub root_device_type: Option<String>,
3203 pub root_device_name: Option<String>,
3204 #[serde(rename = "tagSet")]
3205 pub tag_set: Option<TagSet>,
3206}
3207
3208#[derive(Debug, Clone, Deserialize)]
3209#[serde(rename_all = "camelCase")]
3210pub struct Placement {
3211 pub availability_zone: Option<String>,
3212 pub tenancy: Option<String>,
3213}
3214
3215#[derive(Debug, Clone, Deserialize)]
3216#[serde(rename_all = "camelCase")]
3217pub struct StateReason {
3218 pub code: Option<String>,
3219 pub message: Option<String>,
3220}
3221
3222#[derive(Debug, Clone, Serialize, Builder)]
3228pub struct CreateVolumeRequest {
3229 pub availability_zone: String,
3231 #[serde(skip_serializing_if = "Option::is_none")]
3233 pub client_token: Option<String>,
3234 #[serde(skip_serializing_if = "Option::is_none")]
3236 pub size: Option<i32>,
3237 #[serde(skip_serializing_if = "Option::is_none")]
3239 pub snapshot_id: Option<String>,
3240 #[serde(skip_serializing_if = "Option::is_none")]
3242 pub volume_type: Option<String>,
3243 #[serde(skip_serializing_if = "Option::is_none")]
3245 pub iops: Option<i32>,
3246 #[serde(skip_serializing_if = "Option::is_none")]
3248 pub throughput: Option<i32>,
3249 #[serde(skip_serializing_if = "Option::is_none")]
3251 pub encrypted: Option<bool>,
3252 #[serde(skip_serializing_if = "Option::is_none")]
3254 pub kms_key_id: Option<String>,
3255 #[serde(skip_serializing_if = "Option::is_none")]
3257 pub tag_specifications: Option<Vec<TagSpecification>>,
3258}
3259
3260#[derive(Debug, Deserialize)]
3262#[serde(rename_all = "camelCase")]
3263pub struct CreateVolumeResponse {
3264 pub volume_id: Option<String>,
3265 pub size: Option<i32>,
3266 pub availability_zone: Option<String>,
3267 pub state: Option<String>,
3268 pub volume_type: Option<String>,
3269 pub iops: Option<i32>,
3270 pub throughput: Option<i32>,
3271 pub encrypted: Option<bool>,
3272 pub create_time: Option<String>,
3273 #[serde(rename = "tagSet")]
3274 pub tag_set: Option<TagSet>,
3275}
3276
3277#[derive(Debug, Clone, Serialize, Builder)]
3279pub struct ModifyVolumeRequest {
3280 pub volume_id: String,
3282 pub size: i32,
3284}
3285
3286#[derive(Debug, Deserialize)]
3288#[serde(rename_all = "camelCase")]
3289pub struct ModifyVolumeResponse {
3290 pub volume_modification: Option<VolumeModification>,
3291}
3292
3293#[derive(Debug, Clone, Serialize, Builder, Default)]
3295pub struct DescribeVolumesModificationsRequest {
3296 #[serde(skip_serializing_if = "Option::is_none")]
3297 pub volume_ids: Option<Vec<String>>,
3298 #[serde(skip_serializing_if = "Option::is_none")]
3299 pub filters: Option<Vec<Filter>>,
3300 #[serde(skip_serializing_if = "Option::is_none")]
3301 pub max_results: Option<i32>,
3302 #[serde(skip_serializing_if = "Option::is_none")]
3303 pub next_token: Option<String>,
3304}
3305
3306#[derive(Debug, Deserialize)]
3308#[serde(rename_all = "camelCase")]
3309pub struct DescribeVolumesModificationsResponse {
3310 #[serde(rename = "volumeModificationSet")]
3311 pub volume_modification_set: Option<VolumeModificationSet>,
3312 pub next_token: Option<String>,
3313}
3314
3315#[derive(Debug, Deserialize)]
3317#[serde(rename_all = "camelCase")]
3318pub struct VolumeModificationSet {
3319 #[serde(rename = "item", default)]
3320 pub items: Vec<VolumeModification>,
3321}
3322
3323#[derive(Debug, Clone, Deserialize)]
3325#[serde(rename_all = "camelCase")]
3326pub struct VolumeModification {
3327 pub volume_id: Option<String>,
3328 pub modification_state: Option<String>,
3329 pub status_message: Option<String>,
3330 pub progress: Option<i64>,
3331 pub original_size: Option<i32>,
3332 pub target_size: Option<i32>,
3333 pub start_time: Option<String>,
3334 pub end_time: Option<String>,
3335}
3336
3337#[derive(Debug, Clone, Serialize, Builder, Default)]
3339pub struct DescribeVolumesRequest {
3340 #[serde(skip_serializing_if = "Option::is_none")]
3341 pub volume_ids: Option<Vec<String>>,
3342 #[serde(skip_serializing_if = "Option::is_none")]
3343 pub filters: Option<Vec<Filter>>,
3344 #[serde(skip_serializing_if = "Option::is_none")]
3345 pub max_results: Option<i32>,
3346 #[serde(skip_serializing_if = "Option::is_none")]
3347 pub next_token: Option<String>,
3348}
3349
3350#[derive(Debug, Deserialize)]
3352#[serde(rename_all = "camelCase")]
3353pub struct DescribeVolumesResponse {
3354 #[serde(rename = "volumeSet")]
3355 pub volume_set: Option<VolumeSet>,
3356 #[serde(rename = "nextToken")]
3357 pub next_token: Option<String>,
3358}
3359
3360#[derive(Debug, Deserialize)]
3361#[serde(rename_all = "camelCase")]
3362pub struct VolumeSet {
3363 #[serde(rename = "item", default)]
3364 pub items: Vec<Volume>,
3365}
3366
3367#[derive(Debug, Clone, Deserialize)]
3369#[serde(rename_all = "camelCase")]
3370pub struct Volume {
3371 pub volume_id: Option<String>,
3372 pub size: Option<i32>,
3373 pub availability_zone: Option<String>,
3374 #[serde(rename = "status")]
3375 pub state: Option<String>,
3376 pub volume_type: Option<String>,
3377 pub iops: Option<i32>,
3378 pub throughput: Option<i32>,
3379 pub encrypted: Option<bool>,
3380 pub snapshot_id: Option<String>,
3381 pub create_time: Option<String>,
3382 #[serde(rename = "attachmentSet")]
3383 pub attachment_set: Option<VolumeAttachmentSet>,
3384 #[serde(rename = "tagSet")]
3385 pub tag_set: Option<TagSet>,
3386}
3387
3388#[derive(Debug, Clone, Deserialize)]
3389#[serde(rename_all = "camelCase")]
3390pub struct VolumeAttachmentSet {
3391 #[serde(rename = "item", default)]
3392 pub items: Vec<VolumeAttachment>,
3393}
3394
3395#[derive(Debug, Clone, Deserialize)]
3396#[serde(rename_all = "camelCase")]
3397pub struct VolumeAttachment {
3398 pub volume_id: Option<String>,
3399 pub instance_id: Option<String>,
3400 pub device: Option<String>,
3401 pub state: Option<String>,
3402 pub attach_time: Option<String>,
3403 pub delete_on_termination: Option<bool>,
3404}
3405
3406#[derive(Debug, Clone, Serialize, Builder)]
3408pub struct AttachVolumeRequest {
3409 pub volume_id: String,
3411 pub instance_id: String,
3413 pub device: String,
3415}
3416
3417#[derive(Debug, Deserialize)]
3419#[serde(rename_all = "camelCase")]
3420pub struct AttachVolumeResponse {
3421 pub volume_id: Option<String>,
3422 pub instance_id: Option<String>,
3423 pub device: Option<String>,
3424 pub state: Option<String>,
3425 pub attach_time: Option<String>,
3426}
3427
3428#[derive(Debug, Clone, Serialize, Builder)]
3430pub struct DetachVolumeRequest {
3431 pub volume_id: String,
3433 #[serde(skip_serializing_if = "Option::is_none")]
3435 pub instance_id: Option<String>,
3436 #[serde(skip_serializing_if = "Option::is_none")]
3438 pub device: Option<String>,
3439 #[serde(skip_serializing_if = "Option::is_none")]
3441 pub force: Option<bool>,
3442}
3443
3444#[derive(Debug, Deserialize)]
3446#[serde(rename_all = "camelCase")]
3447pub struct DetachVolumeResponse {
3448 pub volume_id: Option<String>,
3449 pub instance_id: Option<String>,
3450 pub device: Option<String>,
3451 pub state: Option<String>,
3452 pub attach_time: Option<String>,
3453}
3454
3455#[derive(Debug, Clone, Serialize, Builder)]
3461pub struct CreateLaunchTemplateRequest {
3462 pub launch_template_name: String,
3464 #[serde(skip_serializing_if = "Option::is_none")]
3466 pub version_description: Option<String>,
3467 pub launch_template_data: RequestLaunchTemplateData,
3469 #[serde(skip_serializing_if = "Option::is_none")]
3471 pub tag_specifications: Option<Vec<TagSpecification>>,
3472}
3473
3474#[derive(Debug, Clone, Serialize, Builder, Default)]
3476pub struct RequestLaunchTemplateData {
3477 #[serde(skip_serializing_if = "Option::is_none")]
3479 pub image_id: Option<String>,
3480 #[serde(skip_serializing_if = "Option::is_none")]
3482 pub instance_type: Option<String>,
3483 #[serde(skip_serializing_if = "Option::is_none")]
3485 pub key_name: Option<String>,
3486 #[serde(skip_serializing_if = "Option::is_none")]
3488 pub user_data: Option<String>,
3489 #[serde(skip_serializing_if = "Option::is_none")]
3491 pub security_group_ids: Option<Vec<String>>,
3492 #[serde(skip_serializing_if = "Option::is_none")]
3494 pub iam_instance_profile: Option<LaunchTemplateIamInstanceProfileSpecification>,
3495 #[serde(skip_serializing_if = "Option::is_none")]
3497 pub block_device_mappings: Option<Vec<LaunchTemplateBlockDeviceMapping>>,
3498 #[serde(skip_serializing_if = "Option::is_none")]
3500 pub network_interfaces: Option<Vec<LaunchTemplateNetworkInterface>>,
3501 #[serde(skip_serializing_if = "Option::is_none")]
3503 pub metadata_options: Option<LaunchTemplateInstanceMetadataOptions>,
3504 #[serde(skip_serializing_if = "Option::is_none")]
3506 pub cpu_options: Option<LaunchTemplateCpuOptions>,
3507 #[serde(skip_serializing_if = "Option::is_none")]
3509 pub tag_specifications: Option<Vec<TagSpecification>>,
3510}
3511
3512#[derive(Debug, Clone, Serialize, Builder)]
3514pub struct LaunchTemplateIamInstanceProfileSpecification {
3515 #[serde(skip_serializing_if = "Option::is_none")]
3516 pub arn: Option<String>,
3517 #[serde(skip_serializing_if = "Option::is_none")]
3518 pub name: Option<String>,
3519}
3520
3521#[derive(Debug, Clone, Serialize, Builder)]
3523pub struct LaunchTemplateBlockDeviceMapping {
3524 #[serde(skip_serializing_if = "Option::is_none")]
3525 pub device_name: Option<String>,
3526 #[serde(skip_serializing_if = "Option::is_none")]
3527 pub ebs: Option<LaunchTemplateEbsBlockDevice>,
3528}
3529
3530#[derive(Debug, Clone, Serialize, Builder)]
3532pub struct LaunchTemplateEbsBlockDevice {
3533 #[serde(skip_serializing_if = "Option::is_none")]
3534 pub volume_size: Option<i32>,
3535 #[serde(skip_serializing_if = "Option::is_none")]
3536 pub volume_type: Option<String>,
3537 #[serde(skip_serializing_if = "Option::is_none")]
3538 pub delete_on_termination: Option<bool>,
3539 #[serde(skip_serializing_if = "Option::is_none")]
3540 pub encrypted: Option<bool>,
3541 #[serde(skip_serializing_if = "Option::is_none")]
3542 pub iops: Option<i32>,
3543 #[serde(skip_serializing_if = "Option::is_none")]
3544 pub throughput: Option<i32>,
3545}
3546
3547#[derive(Debug, Clone, Serialize, Builder)]
3549pub struct LaunchTemplateNetworkInterface {
3550 #[serde(skip_serializing_if = "Option::is_none")]
3551 pub device_index: Option<i32>,
3552 #[serde(skip_serializing_if = "Option::is_none")]
3553 pub associate_public_ip_address: Option<bool>,
3554 #[serde(skip_serializing_if = "Option::is_none")]
3555 pub subnet_id: Option<String>,
3556 #[serde(skip_serializing_if = "Option::is_none")]
3557 pub groups: Option<Vec<String>>,
3558}
3559
3560#[derive(Debug, Clone, Serialize, Builder)]
3562pub struct LaunchTemplateInstanceMetadataOptions {
3563 #[serde(skip_serializing_if = "Option::is_none")]
3564 pub http_tokens: Option<String>,
3565 #[serde(skip_serializing_if = "Option::is_none")]
3566 pub http_endpoint: Option<String>,
3567 #[serde(skip_serializing_if = "Option::is_none")]
3568 pub http_put_response_hop_limit: Option<i32>,
3569 #[serde(skip_serializing_if = "Option::is_none")]
3570 pub instance_metadata_tags: Option<String>,
3571}
3572
3573#[derive(Debug, Clone, Serialize, Builder, Default)]
3583pub struct LaunchTemplateCpuOptions {
3584 #[serde(skip_serializing_if = "Option::is_none")]
3586 pub nested_virtualization: Option<String>,
3587}
3588
3589#[derive(Debug, Deserialize)]
3591#[serde(rename_all = "camelCase")]
3592pub struct CreateLaunchTemplateResponse {
3593 pub launch_template: Option<LaunchTemplate>,
3594}
3595
3596#[derive(Debug, Clone, Builder, Default)]
3599pub struct CreateLaunchTemplateVersionRequest {
3600 pub launch_template_id: Option<String>,
3601 pub launch_template_name: Option<String>,
3602 pub source_version: Option<String>,
3603 pub version_description: Option<String>,
3604 pub launch_template_data: RequestLaunchTemplateData,
3605}
3606
3607#[derive(Debug, Deserialize)]
3609pub struct CreateLaunchTemplateVersionResponse {
3610 #[serde(rename = "launchTemplateVersion")]
3611 pub launch_template_version: Option<LaunchTemplateVersion>,
3612}
3613
3614#[derive(Debug, Clone, Deserialize)]
3616pub struct LaunchTemplateVersion {
3617 #[serde(rename = "launchTemplateId")]
3618 pub launch_template_id: Option<String>,
3619 #[serde(rename = "versionNumber")]
3620 pub version_number: Option<i64>,
3621}
3622
3623#[derive(Debug, Clone, Serialize, Builder, Default)]
3625pub struct DeleteLaunchTemplateRequest {
3626 #[serde(skip_serializing_if = "Option::is_none")]
3627 pub launch_template_id: Option<String>,
3628 #[serde(skip_serializing_if = "Option::is_none")]
3629 pub launch_template_name: Option<String>,
3630}
3631
3632#[derive(Debug, Deserialize)]
3634#[serde(rename_all = "camelCase")]
3635pub struct DeleteLaunchTemplateResponse {
3636 pub launch_template: Option<LaunchTemplate>,
3637}
3638
3639#[derive(Debug, Clone, Serialize, Builder, Default)]
3641pub struct DescribeLaunchTemplatesRequest {
3642 #[serde(skip_serializing_if = "Option::is_none")]
3643 pub launch_template_ids: Option<Vec<String>>,
3644 #[serde(skip_serializing_if = "Option::is_none")]
3645 pub launch_template_names: Option<Vec<String>>,
3646 #[serde(skip_serializing_if = "Option::is_none")]
3647 pub filters: Option<Vec<Filter>>,
3648 #[serde(skip_serializing_if = "Option::is_none")]
3649 pub max_results: Option<i32>,
3650 #[serde(skip_serializing_if = "Option::is_none")]
3651 pub next_token: Option<String>,
3652}
3653
3654#[derive(Debug, Deserialize)]
3656#[serde(rename_all = "camelCase")]
3657pub struct DescribeLaunchTemplatesResponse {
3658 #[serde(rename = "launchTemplates")]
3659 pub launch_templates: Option<LaunchTemplateSet>,
3660 #[serde(rename = "nextToken")]
3661 pub next_token: Option<String>,
3662}
3663
3664#[derive(Debug, Deserialize)]
3665#[serde(rename_all = "camelCase")]
3666pub struct LaunchTemplateSet {
3667 #[serde(rename = "item", default)]
3668 pub items: Vec<LaunchTemplate>,
3669}
3670
3671#[derive(Debug, Clone, Deserialize)]
3673#[serde(rename_all = "camelCase")]
3674pub struct LaunchTemplate {
3675 pub launch_template_id: Option<String>,
3676 pub launch_template_name: Option<String>,
3677 pub create_time: Option<String>,
3678 pub created_by: Option<String>,
3679 pub default_version_number: Option<i64>,
3680 pub latest_version_number: Option<i64>,
3681 #[serde(rename = "tagSet")]
3682 pub tag_set: Option<TagSet>,
3683}
3684
3685#[derive(Debug, Deserialize, Default)]
3688#[serde(rename_all = "camelCase")]
3689pub struct GetConsoleOutputResponse {
3690 #[serde(rename = "instanceId")]
3692 pub instance_id: Option<String>,
3693 pub output: Option<String>,
3695 pub timestamp: Option<String>,
3697}
3698
3699#[cfg(test)]
3700mod volume_operation_tests {
3701 use super::*;
3702
3703 #[test]
3704 fn create_volume_maps_idempotency_token_to_ec2_query_parameter() {
3705 let request = CreateVolumeRequest::builder()
3706 .availability_zone("us-west-2a".to_string())
3707 .client_token("deployment-disk-ordinal-3".to_string())
3708 .size(64)
3709 .volume_type("gp3".to_string())
3710 .encrypted(true)
3711 .build();
3712
3713 let form = Ec2Client::create_volume_form_data(&request);
3714
3715 assert_eq!(form.get("Action").map(String::as_str), Some("CreateVolume"));
3716 assert_eq!(
3717 form.get("ClientToken").map(String::as_str),
3718 Some("deployment-disk-ordinal-3")
3719 );
3720 assert_eq!(
3721 form.get("AvailabilityZone").map(String::as_str),
3722 Some("us-west-2a")
3723 );
3724 assert_eq!(form.get("Size").map(String::as_str), Some("64"));
3725 assert_eq!(form.get("VolumeType").map(String::as_str), Some("gp3"));
3726 assert_eq!(form.get("Encrypted").map(String::as_str), Some("true"));
3727 }
3728
3729 #[test]
3730 fn volume_growth_operations_map_to_ec2_query_parameters() {
3731 let modify = ModifyVolumeRequest::builder()
3732 .volume_id("vol-0123456789abcdef0".to_string())
3733 .size(128)
3734 .build();
3735 let modify_form = Ec2Client::modify_volume_form_data(&modify);
3736 assert_eq!(
3737 modify_form.get("Action").map(String::as_str),
3738 Some("ModifyVolume")
3739 );
3740 assert_eq!(
3741 modify_form.get("VolumeId").map(String::as_str),
3742 Some("vol-0123456789abcdef0")
3743 );
3744 assert_eq!(modify_form.get("Size").map(String::as_str), Some("128"));
3745
3746 let describe = DescribeVolumesModificationsRequest::builder()
3747 .volume_ids(vec![
3748 "vol-0123456789abcdef0".to_string(),
3749 "vol-0123456789abcdef1".to_string(),
3750 ])
3751 .max_results(2)
3752 .next_token("next-page".to_string())
3753 .build();
3754 let describe_form = Ec2Client::describe_volumes_modifications_form_data(&describe);
3755 assert_eq!(
3756 describe_form.get("Action").map(String::as_str),
3757 Some("DescribeVolumesModifications")
3758 );
3759 assert_eq!(
3760 describe_form.get("VolumeId.1").map(String::as_str),
3761 Some("vol-0123456789abcdef0")
3762 );
3763 assert_eq!(
3764 describe_form.get("VolumeId.2").map(String::as_str),
3765 Some("vol-0123456789abcdef1")
3766 );
3767 assert_eq!(
3768 describe_form.get("MaxResults").map(String::as_str),
3769 Some("2")
3770 );
3771 assert_eq!(
3772 describe_form.get("NextToken").map(String::as_str),
3773 Some("next-page")
3774 );
3775 }
3776
3777 #[test]
3778 fn volume_modification_responses_deserialize_from_ec2_xml() {
3779 let modify: ModifyVolumeResponse = quick_xml::de::from_str(
3780 r#"<ModifyVolumeResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
3781 <volumeModification>
3782 <volumeId>vol-0123456789abcdef0</volumeId>
3783 <modificationState>modifying</modificationState>
3784 <progress>0</progress>
3785 <originalSize>64</originalSize>
3786 <targetSize>128</targetSize>
3787 </volumeModification>
3788 </ModifyVolumeResponse>"#,
3789 )
3790 .expect("ModifyVolume response should deserialize");
3791 let modification = modify
3792 .volume_modification
3793 .expect("ModifyVolume should include its modification");
3794 assert_eq!(
3795 modification.volume_id.as_deref(),
3796 Some("vol-0123456789abcdef0")
3797 );
3798 assert_eq!(
3799 modification.modification_state.as_deref(),
3800 Some("modifying")
3801 );
3802 assert_eq!(modification.progress, Some(0));
3803 assert_eq!(modification.original_size, Some(64));
3804 assert_eq!(modification.target_size, Some(128));
3805
3806 let described: DescribeVolumesModificationsResponse = quick_xml::de::from_str(
3807 r#"<DescribeVolumesModificationsResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
3808 <volumeModificationSet>
3809 <item>
3810 <volumeId>vol-0123456789abcdef0</volumeId>
3811 <modificationState>completed</modificationState>
3812 <progress>100</progress>
3813 <originalSize>64</originalSize>
3814 <targetSize>128</targetSize>
3815 <endTime>2026-07-20T10:00:00.000Z</endTime>
3816 </item>
3817 </volumeModificationSet>
3818 <nextToken>next-page</nextToken>
3819 </DescribeVolumesModificationsResponse>"#,
3820 )
3821 .expect("DescribeVolumesModifications response should deserialize");
3822 let modifications = described
3823 .volume_modification_set
3824 .expect("response should include modifications");
3825 assert_eq!(modifications.items.len(), 1);
3826 assert_eq!(
3827 modifications.items[0].modification_state.as_deref(),
3828 Some("completed")
3829 );
3830 assert_eq!(modifications.items[0].progress, Some(100));
3831 assert_eq!(described.next_token.as_deref(), Some("next-page"));
3832 }
3833}
3834
3835impl GetConsoleOutputResponse {
3836 pub fn decode_output(&self) -> Option<String> {
3838 self.output.as_ref().and_then(|b64| {
3839 STANDARD
3840 .decode(b64.trim())
3841 .ok()
3842 .and_then(|bytes| String::from_utf8(bytes).ok())
3843 })
3844 }
3845}
3846
3847#[cfg(test)]
3848mod tests {
3849 use super::*;
3850
3851 #[test]
3852 fn describe_volumes_deserializes_aws_status_as_volume_state() {
3853 let response: DescribeVolumesResponse = quick_xml::de::from_str(
3854 r#"<DescribeVolumesResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
3855 <volumeSet>
3856 <item>
3857 <volumeId>vol-0123456789abcdef0</volumeId>
3858 <size>10</size>
3859 <availabilityZone>us-west-2a</availabilityZone>
3860 <status>available</status>
3861 <volumeType>gp3</volumeType>
3862 </item>
3863 </volumeSet>
3864 </DescribeVolumesResponse>"#,
3865 )
3866 .expect("AWS DescribeVolumes response should deserialize");
3867
3868 let volumes = response
3869 .volume_set
3870 .expect("volumeSet should be present")
3871 .items;
3872 assert_eq!(volumes.len(), 1);
3873 assert_eq!(
3874 volumes[0].volume_id.as_deref(),
3875 Some("vol-0123456789abcdef0")
3876 );
3877 assert_eq!(volumes[0].state.as_deref(), Some("available"));
3878 }
3879
3880 #[test]
3881 fn launch_template_already_exists_is_a_typed_resource_conflict() {
3882 let response = r#"<?xml version="1.0" encoding="UTF-8"?>
3883 <Response><Errors><Error>
3884 <Code>InvalidLaunchTemplateName.AlreadyExistsException</Code>
3885 <Message>Launch template name already in use.</Message>
3886 </Error></Errors><RequestID>request-id</RequestID></Response>"#;
3887
3888 let error = Ec2Client::map_ec2_error(
3889 StatusCode::BAD_REQUEST,
3890 response,
3891 "CreateLaunchTemplate",
3892 "deployment-compute-general-lt",
3893 Some("LaunchTemplateName=deployment-compute-general-lt"),
3894 );
3895
3896 assert!(matches!(
3897 error,
3898 Some(ErrorData::RemoteResourceConflict {
3899 resource_type,
3900 resource_name,
3901 ..
3902 }) if resource_type == "EC2 Resource"
3903 && resource_name == "deployment-compute-general-lt"
3904 ));
3905 }
3906
3907 #[test]
3908 fn missing_volume_is_a_typed_remote_resource_not_found() {
3909 let response = r#"<?xml version="1.0" encoding="UTF-8"?>
3910 <Response><Errors><Error>
3911 <Code>InvalidVolume.NotFound</Code>
3912 <Message>The volume does not exist.</Message>
3913 </Error></Errors><RequestID>request-id</RequestID></Response>"#;
3914
3915 let error = Ec2Client::map_ec2_error(
3916 StatusCode::BAD_REQUEST,
3917 response,
3918 "DescribeVolumesModifications",
3919 "vol-0123456789abcdef0",
3920 Some("VolumeId.1=vol-0123456789abcdef0"),
3921 );
3922
3923 assert!(matches!(
3924 error,
3925 Some(ErrorData::RemoteResourceNotFound {
3926 resource_type,
3927 resource_name,
3928 }) if resource_type == "Volume" && resource_name == "vol-0123456789abcdef0"
3929 ));
3930 }
3931
3932 #[test]
3937 fn add_cpu_options_emits_nested_virtualization_key() {
3938 let mut form_data = HashMap::new();
3939 let cpu_options = LaunchTemplateCpuOptions::builder()
3940 .nested_virtualization("enabled".to_string())
3941 .build();
3942 Ec2Client::add_cpu_options(&mut form_data, Some(&cpu_options));
3943 assert_eq!(
3944 form_data.get("LaunchTemplateData.CpuOptions.NestedVirtualization"),
3945 Some(&"enabled".to_string())
3946 );
3947 assert_eq!(form_data.len(), 1);
3948 }
3949
3950 #[test]
3953 fn add_cpu_options_with_none_emits_nothing() {
3954 let mut form_data = HashMap::new();
3955 Ec2Client::add_cpu_options(&mut form_data, None);
3956 assert!(form_data.is_empty());
3957 }
3958
3959 #[test]
3963 fn add_cpu_options_with_unset_nested_field_emits_nothing() {
3964 let mut form_data = HashMap::new();
3965 let cpu_options = LaunchTemplateCpuOptions::builder().build();
3966 Ec2Client::add_cpu_options(&mut form_data, Some(&cpu_options));
3967 assert!(form_data.is_empty());
3968 }
3969}