use crate::aws::aws_request_utils::{AwsRequestBuilderExt, AwsSignConfig};
use crate::aws::AwsClientConfig;
use crate::aws::AwsClientConfigExt;
use alien_client_core::{ErrorData, Result};
use alien_error::ContextError;
use async_trait::async_trait;
use bon::Builder;
use form_urlencoded;
use reqwest::{Client, Method, StatusCode};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[cfg(feature = "test-utils")]
use mockall::automock;
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Elbv2ErrorResponse {
pub error: Elbv2ErrorWrapper,
pub request_id: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Elbv2ErrorWrapper {
#[serde(rename = "Code")]
pub code: String,
#[serde(rename = "Message")]
pub message: String,
}
#[cfg_attr(feature = "test-utils", automock)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait Elbv2Api: Send + Sync + std::fmt::Debug {
async fn create_load_balancer(
&self,
request: CreateLoadBalancerRequest,
) -> Result<CreateLoadBalancerResponse>;
async fn describe_load_balancers(
&self,
request: DescribeLoadBalancersRequest,
) -> Result<DescribeLoadBalancersResponse>;
async fn delete_load_balancer(&self, load_balancer_arn: &str) -> Result<()>;
async fn create_target_group(
&self,
request: CreateTargetGroupRequest,
) -> Result<CreateTargetGroupResponse>;
async fn describe_target_groups(
&self,
request: DescribeTargetGroupsRequest,
) -> Result<DescribeTargetGroupsResponse>;
async fn modify_target_group(
&self,
request: ModifyTargetGroupRequest,
) -> Result<ModifyTargetGroupResponse>;
async fn delete_target_group(&self, target_group_arn: &str) -> Result<()>;
async fn register_targets(&self, request: RegisterTargetsRequest) -> Result<()>;
async fn deregister_targets(&self, request: DeregisterTargetsRequest) -> Result<()>;
async fn describe_target_health(
&self,
request: DescribeTargetHealthRequest,
) -> Result<DescribeTargetHealthResponse>;
async fn create_listener(
&self,
request: CreateListenerRequest,
) -> Result<CreateListenerResponse>;
async fn describe_listeners(
&self,
request: DescribeListenersRequest,
) -> Result<DescribeListenersResponse>;
async fn modify_listener(
&self,
request: ModifyListenerRequest,
) -> Result<ModifyListenerResponse>;
async fn delete_listener(&self, listener_arn: &str) -> Result<()>;
}
#[derive(Debug, Clone)]
pub struct Elbv2Client {
client: Client,
config: AwsClientConfig,
}
impl Elbv2Client {
pub fn new(client: Client, config: AwsClientConfig) -> Self {
Self { client, config }
}
fn sign_config(&self) -> AwsSignConfig {
AwsSignConfig {
service_name: "elasticloadbalancing".into(),
region: self.config.region.clone(),
credentials: self.config.get_credentials(),
signing_region: None,
}
}
fn get_base_url(&self) -> String {
if let Some(override_url) = self
.config
.get_service_endpoint_option("elasticloadbalancing")
{
override_url.to_string()
} else {
format!(
"https://elasticloadbalancing.{}.amazonaws.com",
self.config.region
)
}
}
fn get_host(&self) -> String {
format!("elasticloadbalancing.{}.amazonaws.com", self.config.region)
}
async fn send_form<T: DeserializeOwned + Send + 'static>(
&self,
form_data: HashMap<String, String>,
operation: &str,
resource: &str,
) -> Result<T> {
let url = self.get_base_url();
let form_body = form_urlencoded::Serializer::new(String::new())
.extend_pairs(form_data.iter())
.finish();
let builder = self
.client
.request(Method::POST, &url)
.host(&self.get_host())
.content_type_form()
.content_sha256(&form_body)
.body(form_body.clone());
let result =
crate::aws::aws_request_utils::sign_send_xml(builder, &self.sign_config()).await;
Self::map_result(result, operation, resource, Some(&form_body))
}
async fn send_form_no_body(
&self,
form_data: HashMap<String, String>,
operation: &str,
resource: &str,
) -> Result<()> {
let url = self.get_base_url();
let form_body = form_urlencoded::Serializer::new(String::new())
.extend_pairs(form_data.iter())
.finish();
let builder = self
.client
.request(Method::POST, &url)
.host(&self.get_host())
.content_type_form()
.content_sha256(&form_body)
.body(form_body.clone());
let result =
crate::aws::aws_request_utils::sign_send_no_response(builder, &self.sign_config())
.await;
Self::map_result(result, operation, resource, Some(&form_body))
}
fn map_result<T>(
result: Result<T>,
operation: &str,
resource: &str,
request_body: Option<&str>,
) -> Result<T> {
match result {
Ok(v) => Ok(v),
Err(e) => {
if let Some(ErrorData::HttpResponseError {
http_status,
http_response_text: Some(ref text),
..
}) = &e.error
{
let status = StatusCode::from_u16(*http_status)
.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
if let Some(mapped) =
Self::map_elbv2_error(status, text, operation, resource, request_body)
{
Err(e.context(mapped))
} else {
Err(e)
}
} else {
Err(e)
}
}
}
}
fn map_elbv2_error(
status: StatusCode,
body: &str,
_operation: &str,
resource: &str,
request_body: Option<&str>,
) -> Option<ErrorData> {
if body.trim().is_empty() {
return match status {
StatusCode::NOT_FOUND => Some(ErrorData::RemoteResourceNotFound {
resource_type: "LoadBalancer".into(),
resource_name: resource.into(),
}),
StatusCode::CONFLICT => Some(ErrorData::RemoteResourceConflict {
message: "Resource conflict".into(),
resource_type: "LoadBalancer".into(),
resource_name: resource.into(),
}),
StatusCode::FORBIDDEN | StatusCode::UNAUTHORIZED => {
Some(ErrorData::RemoteAccessDenied {
resource_type: "LoadBalancer".into(),
resource_name: resource.into(),
})
}
StatusCode::TOO_MANY_REQUESTS => Some(ErrorData::RateLimitExceeded {
message: "Too many requests".into(),
}),
StatusCode::SERVICE_UNAVAILABLE
| StatusCode::BAD_GATEWAY
| StatusCode::GATEWAY_TIMEOUT => Some(ErrorData::RemoteServiceUnavailable {
message: "Service unavailable".into(),
}),
_ => None,
};
}
let parsed: std::result::Result<Elbv2ErrorResponse, _> = quick_xml::de::from_str(body);
let (code, message) = match parsed {
Ok(e) => (e.error.code, e.error.message),
Err(_) => {
return None;
}
};
Some(match code.as_str() {
"AccessDenied" | "UnauthorizedAccess" => ErrorData::RemoteAccessDenied {
resource_type: "LoadBalancer".into(),
resource_name: resource.into(),
},
"Throttling" | "RequestLimitExceeded" => ErrorData::RateLimitExceeded { message },
"ServiceUnavailable" | "InternalFailure" => {
ErrorData::RemoteServiceUnavailable { message }
}
"LoadBalancerNotFound" | "LoadBalancerNotFoundException" => {
ErrorData::RemoteResourceNotFound {
resource_type: "LoadBalancer".into(),
resource_name: resource.into(),
}
}
"TargetGroupNotFound" | "TargetGroupNotFoundException" => {
ErrorData::RemoteResourceNotFound {
resource_type: "TargetGroup".into(),
resource_name: resource.into(),
}
}
"ListenerNotFound" | "ListenerNotFoundException" => ErrorData::RemoteResourceNotFound {
resource_type: "Listener".into(),
resource_name: resource.into(),
},
"DuplicateLoadBalancerName" | "DuplicateLoadBalancerNameException" => {
ErrorData::RemoteResourceConflict {
message,
resource_type: "LoadBalancer".into(),
resource_name: resource.into(),
}
}
"DuplicateTargetGroupName" | "DuplicateTargetGroupNameException" => {
ErrorData::RemoteResourceConflict {
message,
resource_type: "TargetGroup".into(),
resource_name: resource.into(),
}
}
"DuplicateListener" | "DuplicateListenerException" => {
ErrorData::RemoteResourceConflict {
message,
resource_type: "Listener".into(),
resource_name: resource.into(),
}
}
"ResourceInUse" | "ResourceInUseException" => ErrorData::RemoteResourceConflict {
message,
resource_type: "LoadBalancer".into(),
resource_name: resource.into(),
},
"TooManyLoadBalancers"
| "TooManyTargetGroups"
| "TooManyListeners"
| "TooManyTargets"
| "TooManyRegistrationsForTargetId"
| "TooManyTags" => ErrorData::QuotaExceeded { message },
"InvalidConfigurationRequest" | "ValidationError" => ErrorData::InvalidInput {
message,
field_name: None,
},
"InvalidTarget" | "InvalidTargetException" => ErrorData::InvalidInput {
message,
field_name: Some("target".into()),
},
"InvalidSubnet" | "SubnetNotFound" => ErrorData::InvalidInput {
message,
field_name: Some("subnet".into()),
},
"InvalidSecurityGroup" | "SecurityGroupNotFound" => ErrorData::InvalidInput {
message,
field_name: Some("security_group".into()),
},
_ => match status {
StatusCode::NOT_FOUND => ErrorData::RemoteResourceNotFound {
resource_type: "LoadBalancer".into(),
resource_name: resource.into(),
},
StatusCode::CONFLICT => ErrorData::RemoteResourceConflict {
message,
resource_type: "LoadBalancer".into(),
resource_name: resource.into(),
},
StatusCode::FORBIDDEN | StatusCode::UNAUTHORIZED => ErrorData::RemoteAccessDenied {
resource_type: "LoadBalancer".into(),
resource_name: resource.into(),
},
StatusCode::TOO_MANY_REQUESTS => ErrorData::RateLimitExceeded { message },
StatusCode::SERVICE_UNAVAILABLE
| StatusCode::BAD_GATEWAY
| StatusCode::GATEWAY_TIMEOUT => ErrorData::RemoteServiceUnavailable { message },
_ => ErrorData::HttpResponseError {
message: format!("ELBv2 operation failed: {}", message),
url: "elasticloadbalancing.amazonaws.com".into(),
http_status: status.as_u16(),
http_response_text: Some(body.into()),
http_request_text: request_body.map(|s| s.to_string()),
},
},
})
}
fn add_tags(form_data: &mut HashMap<String, String>, tags: &[ElbTag]) {
for (i, tag) in tags.iter().enumerate() {
let idx = i + 1;
form_data.insert(format!("Tags.member.{}.Key", idx), tag.key.clone());
form_data.insert(format!("Tags.member.{}.Value", idx), tag.value.clone());
}
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl Elbv2Api for Elbv2Client {
async fn create_load_balancer(
&self,
request: CreateLoadBalancerRequest,
) -> Result<CreateLoadBalancerResponse> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "CreateLoadBalancer".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
form_data.insert("Name".to_string(), request.name.clone());
for (i, subnet) in request.subnets.iter().enumerate() {
form_data.insert(format!("Subnets.member.{}", i + 1), subnet.clone());
}
if let Some(ref subnet_mappings) = request.subnet_mappings {
for (i, mapping) in subnet_mappings.iter().enumerate() {
let idx = i + 1;
form_data.insert(
format!("SubnetMappings.member.{}.SubnetId", idx),
mapping.subnet_id.clone(),
);
if let Some(ref allocation_id) = mapping.allocation_id {
form_data.insert(
format!("SubnetMappings.member.{}.AllocationId", idx),
allocation_id.clone(),
);
}
if let Some(ref private_ipv4_address) = mapping.private_ipv4_address {
form_data.insert(
format!("SubnetMappings.member.{}.PrivateIPv4Address", idx),
private_ipv4_address.clone(),
);
}
}
}
if let Some(ref security_groups) = request.security_groups {
for (i, sg) in security_groups.iter().enumerate() {
form_data.insert(format!("SecurityGroups.member.{}", i + 1), sg.clone());
}
}
if let Some(ref scheme) = request.scheme {
form_data.insert("Scheme".to_string(), scheme.clone());
}
if let Some(ref lb_type) = request.load_balancer_type {
form_data.insert("Type".to_string(), lb_type.clone());
}
if let Some(ref ip_address_type) = request.ip_address_type {
form_data.insert("IpAddressType".to_string(), ip_address_type.clone());
}
if let Some(ref tags) = request.tags {
Self::add_tags(&mut form_data, tags);
}
self.send_form(form_data, "CreateLoadBalancer", &request.name)
.await
}
async fn describe_load_balancers(
&self,
request: DescribeLoadBalancersRequest,
) -> Result<DescribeLoadBalancersResponse> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "DescribeLoadBalancers".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
if let Some(ref arns) = request.load_balancer_arns {
for (i, arn) in arns.iter().enumerate() {
form_data.insert(format!("LoadBalancerArns.member.{}", i + 1), arn.clone());
}
}
if let Some(ref names) = request.names {
for (i, name) in names.iter().enumerate() {
form_data.insert(format!("Names.member.{}", i + 1), name.clone());
}
}
if let Some(ref marker) = request.marker {
form_data.insert("Marker".to_string(), marker.clone());
}
if let Some(page_size) = request.page_size {
form_data.insert("PageSize".to_string(), page_size.to_string());
}
self.send_form(form_data, "DescribeLoadBalancers", "LoadBalancer")
.await
}
async fn delete_load_balancer(&self, load_balancer_arn: &str) -> Result<()> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "DeleteLoadBalancer".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
form_data.insert("LoadBalancerArn".to_string(), load_balancer_arn.to_string());
self.send_form_no_body(form_data, "DeleteLoadBalancer", load_balancer_arn)
.await
}
async fn create_target_group(
&self,
request: CreateTargetGroupRequest,
) -> Result<CreateTargetGroupResponse> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "CreateTargetGroup".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
form_data.insert("Name".to_string(), request.name.clone());
if let Some(ref protocol) = request.protocol {
form_data.insert("Protocol".to_string(), protocol.clone());
}
if let Some(ref protocol_version) = request.protocol_version {
form_data.insert("ProtocolVersion".to_string(), protocol_version.clone());
}
if let Some(port) = request.port {
form_data.insert("Port".to_string(), port.to_string());
}
if let Some(ref vpc_id) = request.vpc_id {
form_data.insert("VpcId".to_string(), vpc_id.clone());
}
if let Some(ref health_check_protocol) = request.health_check_protocol {
form_data.insert(
"HealthCheckProtocol".to_string(),
health_check_protocol.clone(),
);
}
if let Some(ref health_check_port) = request.health_check_port {
form_data.insert("HealthCheckPort".to_string(), health_check_port.clone());
}
if let Some(health_check_enabled) = request.health_check_enabled {
form_data.insert(
"HealthCheckEnabled".to_string(),
health_check_enabled.to_string(),
);
}
if let Some(ref health_check_path) = request.health_check_path {
form_data.insert("HealthCheckPath".to_string(), health_check_path.clone());
}
if let Some(health_check_interval_seconds) = request.health_check_interval_seconds {
form_data.insert(
"HealthCheckIntervalSeconds".to_string(),
health_check_interval_seconds.to_string(),
);
}
if let Some(health_check_timeout_seconds) = request.health_check_timeout_seconds {
form_data.insert(
"HealthCheckTimeoutSeconds".to_string(),
health_check_timeout_seconds.to_string(),
);
}
if let Some(healthy_threshold_count) = request.healthy_threshold_count {
form_data.insert(
"HealthyThresholdCount".to_string(),
healthy_threshold_count.to_string(),
);
}
if let Some(unhealthy_threshold_count) = request.unhealthy_threshold_count {
form_data.insert(
"UnhealthyThresholdCount".to_string(),
unhealthy_threshold_count.to_string(),
);
}
if let Some(ref matcher) = request.matcher {
if let Some(ref http_code) = matcher.http_code {
form_data.insert("Matcher.HttpCode".to_string(), http_code.clone());
}
if let Some(ref grpc_code) = matcher.grpc_code {
form_data.insert("Matcher.GrpcCode".to_string(), grpc_code.clone());
}
}
if let Some(ref target_type) = request.target_type {
form_data.insert("TargetType".to_string(), target_type.clone());
}
if let Some(ref ip_address_type) = request.ip_address_type {
form_data.insert("IpAddressType".to_string(), ip_address_type.clone());
}
if let Some(ref tags) = request.tags {
Self::add_tags(&mut form_data, tags);
}
self.send_form(form_data, "CreateTargetGroup", &request.name)
.await
}
async fn describe_target_groups(
&self,
request: DescribeTargetGroupsRequest,
) -> Result<DescribeTargetGroupsResponse> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "DescribeTargetGroups".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
if let Some(ref lb_arn) = request.load_balancer_arn {
form_data.insert("LoadBalancerArn".to_string(), lb_arn.clone());
}
if let Some(ref arns) = request.target_group_arns {
for (i, arn) in arns.iter().enumerate() {
form_data.insert(format!("TargetGroupArns.member.{}", i + 1), arn.clone());
}
}
if let Some(ref names) = request.names {
for (i, name) in names.iter().enumerate() {
form_data.insert(format!("Names.member.{}", i + 1), name.clone());
}
}
if let Some(ref marker) = request.marker {
form_data.insert("Marker".to_string(), marker.clone());
}
if let Some(page_size) = request.page_size {
form_data.insert("PageSize".to_string(), page_size.to_string());
}
self.send_form(form_data, "DescribeTargetGroups", "TargetGroup")
.await
}
async fn modify_target_group(
&self,
request: ModifyTargetGroupRequest,
) -> Result<ModifyTargetGroupResponse> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "ModifyTargetGroup".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
form_data.insert(
"TargetGroupArn".to_string(),
request.target_group_arn.clone(),
);
if let Some(ref health_check_protocol) = request.health_check_protocol {
form_data.insert(
"HealthCheckProtocol".to_string(),
health_check_protocol.clone(),
);
}
if let Some(ref health_check_port) = request.health_check_port {
form_data.insert("HealthCheckPort".to_string(), health_check_port.clone());
}
if let Some(ref health_check_path) = request.health_check_path {
form_data.insert("HealthCheckPath".to_string(), health_check_path.clone());
}
if let Some(health_check_enabled) = request.health_check_enabled {
form_data.insert(
"HealthCheckEnabled".to_string(),
health_check_enabled.to_string(),
);
}
if let Some(health_check_interval_seconds) = request.health_check_interval_seconds {
form_data.insert(
"HealthCheckIntervalSeconds".to_string(),
health_check_interval_seconds.to_string(),
);
}
if let Some(health_check_timeout_seconds) = request.health_check_timeout_seconds {
form_data.insert(
"HealthCheckTimeoutSeconds".to_string(),
health_check_timeout_seconds.to_string(),
);
}
if let Some(healthy_threshold_count) = request.healthy_threshold_count {
form_data.insert(
"HealthyThresholdCount".to_string(),
healthy_threshold_count.to_string(),
);
}
if let Some(unhealthy_threshold_count) = request.unhealthy_threshold_count {
form_data.insert(
"UnhealthyThresholdCount".to_string(),
unhealthy_threshold_count.to_string(),
);
}
if let Some(ref matcher) = request.matcher {
if let Some(ref http_code) = matcher.http_code {
form_data.insert("Matcher.HttpCode".to_string(), http_code.clone());
}
}
self.send_form(form_data, "ModifyTargetGroup", &request.target_group_arn)
.await
}
async fn delete_target_group(&self, target_group_arn: &str) -> Result<()> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "DeleteTargetGroup".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
form_data.insert("TargetGroupArn".to_string(), target_group_arn.to_string());
self.send_form_no_body(form_data, "DeleteTargetGroup", target_group_arn)
.await
}
async fn register_targets(&self, request: RegisterTargetsRequest) -> Result<()> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "RegisterTargets".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
form_data.insert(
"TargetGroupArn".to_string(),
request.target_group_arn.clone(),
);
for (i, target) in request.targets.iter().enumerate() {
let idx = i + 1;
form_data.insert(format!("Targets.member.{}.Id", idx), target.id.clone());
if let Some(port) = target.port {
form_data.insert(format!("Targets.member.{}.Port", idx), port.to_string());
}
if let Some(ref az) = target.availability_zone {
form_data.insert(
format!("Targets.member.{}.AvailabilityZone", idx),
az.clone(),
);
}
}
self.send_form_no_body(form_data, "RegisterTargets", &request.target_group_arn)
.await
}
async fn deregister_targets(&self, request: DeregisterTargetsRequest) -> Result<()> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "DeregisterTargets".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
form_data.insert(
"TargetGroupArn".to_string(),
request.target_group_arn.clone(),
);
for (i, target) in request.targets.iter().enumerate() {
let idx = i + 1;
form_data.insert(format!("Targets.member.{}.Id", idx), target.id.clone());
if let Some(port) = target.port {
form_data.insert(format!("Targets.member.{}.Port", idx), port.to_string());
}
}
self.send_form_no_body(form_data, "DeregisterTargets", &request.target_group_arn)
.await
}
async fn describe_target_health(
&self,
request: DescribeTargetHealthRequest,
) -> Result<DescribeTargetHealthResponse> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "DescribeTargetHealth".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
form_data.insert(
"TargetGroupArn".to_string(),
request.target_group_arn.clone(),
);
if let Some(ref targets) = request.targets {
for (i, target) in targets.iter().enumerate() {
let idx = i + 1;
form_data.insert(format!("Targets.member.{}.Id", idx), target.id.clone());
if let Some(port) = target.port {
form_data.insert(format!("Targets.member.{}.Port", idx), port.to_string());
}
}
}
self.send_form(form_data, "DescribeTargetHealth", &request.target_group_arn)
.await
}
async fn create_listener(
&self,
request: CreateListenerRequest,
) -> Result<CreateListenerResponse> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "CreateListener".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
form_data.insert(
"LoadBalancerArn".to_string(),
request.load_balancer_arn.clone(),
);
form_data.insert("Port".to_string(), request.port.to_string());
form_data.insert("Protocol".to_string(), request.protocol.clone());
for (i, action) in request.default_actions.iter().enumerate() {
let idx = i + 1;
form_data.insert(
format!("DefaultActions.member.{}.Type", idx),
action.action_type.clone(),
);
if let Some(ref tg_arn) = action.target_group_arn {
form_data.insert(
format!("DefaultActions.member.{}.TargetGroupArn", idx),
tg_arn.clone(),
);
}
if let Some(order) = action.order {
form_data.insert(
format!("DefaultActions.member.{}.Order", idx),
order.to_string(),
);
}
if let Some(ref forward_config) = action.forward_config {
if let Some(ref target_groups) = forward_config.target_groups {
for (j, tg) in target_groups.iter().enumerate() {
let tg_idx = j + 1;
form_data.insert(
format!("DefaultActions.member.{}.ForwardConfig.TargetGroups.member.{}.TargetGroupArn", idx, tg_idx),
tg.target_group_arn.clone(),
);
if let Some(weight) = tg.weight {
form_data.insert(
format!("DefaultActions.member.{}.ForwardConfig.TargetGroups.member.{}.Weight", idx, tg_idx),
weight.to_string(),
);
}
}
}
}
if let Some(ref redirect_config) = action.redirect_config {
form_data.insert(
format!("DefaultActions.member.{}.RedirectConfig.StatusCode", idx),
redirect_config.status_code.clone(),
);
if let Some(ref protocol) = redirect_config.protocol {
form_data.insert(
format!("DefaultActions.member.{}.RedirectConfig.Protocol", idx),
protocol.clone(),
);
}
if let Some(ref port) = redirect_config.port {
form_data.insert(
format!("DefaultActions.member.{}.RedirectConfig.Port", idx),
port.clone(),
);
}
if let Some(ref host) = redirect_config.host {
form_data.insert(
format!("DefaultActions.member.{}.RedirectConfig.Host", idx),
host.clone(),
);
}
if let Some(ref path) = redirect_config.path {
form_data.insert(
format!("DefaultActions.member.{}.RedirectConfig.Path", idx),
path.clone(),
);
}
if let Some(ref query) = redirect_config.query {
form_data.insert(
format!("DefaultActions.member.{}.RedirectConfig.Query", idx),
query.clone(),
);
}
}
if let Some(ref fixed_response) = action.fixed_response_config {
form_data.insert(
format!(
"DefaultActions.member.{}.FixedResponseConfig.StatusCode",
idx
),
fixed_response.status_code.clone(),
);
if let Some(ref content_type) = fixed_response.content_type {
form_data.insert(
format!(
"DefaultActions.member.{}.FixedResponseConfig.ContentType",
idx
),
content_type.clone(),
);
}
if let Some(ref message_body) = fixed_response.message_body {
form_data.insert(
format!(
"DefaultActions.member.{}.FixedResponseConfig.MessageBody",
idx
),
message_body.clone(),
);
}
}
}
if let Some(ref ssl_policy) = request.ssl_policy {
form_data.insert("SslPolicy".to_string(), ssl_policy.clone());
}
if let Some(ref certificates) = request.certificates {
for (i, cert) in certificates.iter().enumerate() {
let idx = i + 1;
form_data.insert(
format!("Certificates.member.{}.CertificateArn", idx),
cert.certificate_arn.clone(),
);
}
}
if let Some(ref alpn_policy) = request.alpn_policy {
for (i, policy) in alpn_policy.iter().enumerate() {
form_data.insert(format!("AlpnPolicy.member.{}", i + 1), policy.clone());
}
}
if let Some(ref tags) = request.tags {
Self::add_tags(&mut form_data, tags);
}
self.send_form(form_data, "CreateListener", &request.load_balancer_arn)
.await
}
async fn describe_listeners(
&self,
request: DescribeListenersRequest,
) -> Result<DescribeListenersResponse> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "DescribeListeners".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
if let Some(ref lb_arn) = request.load_balancer_arn {
form_data.insert("LoadBalancerArn".to_string(), lb_arn.clone());
}
if let Some(ref listener_arns) = request.listener_arns {
for (i, arn) in listener_arns.iter().enumerate() {
form_data.insert(format!("ListenerArns.member.{}", i + 1), arn.clone());
}
}
if let Some(ref marker) = request.marker {
form_data.insert("Marker".to_string(), marker.clone());
}
if let Some(page_size) = request.page_size {
form_data.insert("PageSize".to_string(), page_size.to_string());
}
self.send_form(form_data, "DescribeListeners", "Listener")
.await
}
async fn modify_listener(
&self,
request: ModifyListenerRequest,
) -> Result<ModifyListenerResponse> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "ModifyListener".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
form_data.insert("ListenerArn".to_string(), request.listener_arn.clone());
if let Some(port) = request.port {
form_data.insert("Port".to_string(), port.to_string());
}
if let Some(ref protocol) = request.protocol {
form_data.insert("Protocol".to_string(), protocol.clone());
}
if let Some(ref ssl_policy) = request.ssl_policy {
form_data.insert("SslPolicy".to_string(), ssl_policy.clone());
}
if let Some(ref certificates) = request.certificates {
for (i, cert) in certificates.iter().enumerate() {
let idx = i + 1;
form_data.insert(
format!("Certificates.member.{}.CertificateArn", idx),
cert.certificate_arn.clone(),
);
}
}
if let Some(ref default_actions) = request.default_actions {
for (i, action) in default_actions.iter().enumerate() {
let idx = i + 1;
form_data.insert(
format!("DefaultActions.member.{}.Type", idx),
action.action_type.clone(),
);
if let Some(ref tg_arn) = action.target_group_arn {
form_data.insert(
format!("DefaultActions.member.{}.TargetGroupArn", idx),
tg_arn.clone(),
);
}
}
}
self.send_form(form_data, "ModifyListener", &request.listener_arn)
.await
}
async fn delete_listener(&self, listener_arn: &str) -> Result<()> {
let mut form_data = HashMap::new();
form_data.insert("Action".to_string(), "DeleteListener".to_string());
form_data.insert("Version".to_string(), "2015-12-01".to_string());
form_data.insert("ListenerArn".to_string(), listener_arn.to_string());
self.send_form_no_body(form_data, "DeleteListener", listener_arn)
.await
}
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct ElbTag {
pub key: String,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct SubnetMapping {
pub subnet_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub allocation_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub private_ipv4_address: Option<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct TargetDescription {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub port: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub availability_zone: Option<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct Certificate {
pub certificate_arn: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_default: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct Matcher {
#[serde(skip_serializing_if = "Option::is_none")]
pub http_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub grpc_code: Option<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct CreateLoadBalancerRequest {
pub name: String,
pub subnets: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subnet_mappings: Option<Vec<SubnetMapping>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub security_groups: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scheme: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub load_balancer_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ip_address_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<ElbTag>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CreateLoadBalancerResponse {
pub create_load_balancer_result: CreateLoadBalancerResult,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CreateLoadBalancerResult {
pub load_balancers: Option<LoadBalancersWrapper>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct LoadBalancersWrapper {
#[serde(rename = "member", default)]
pub members: Vec<LoadBalancer>,
}
#[derive(Debug, Clone, Serialize, Builder, Default)]
pub struct DescribeLoadBalancersRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub load_balancer_arns: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub marker: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_size: Option<i32>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct DescribeLoadBalancersResponse {
pub describe_load_balancers_result: DescribeLoadBalancersResult,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct DescribeLoadBalancersResult {
pub load_balancers: Option<LoadBalancersWrapper>,
pub next_marker: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct LoadBalancer {
pub load_balancer_arn: Option<String>,
pub load_balancer_name: Option<String>,
#[serde(rename = "DNSName")]
pub dns_name: Option<String>,
pub canonical_hosted_zone_id: Option<String>,
pub created_time: Option<String>,
pub scheme: Option<String>,
pub state: Option<LoadBalancerState>,
#[serde(rename = "Type")]
pub load_balancer_type: Option<String>,
pub availability_zones: Option<AvailabilityZonesWrapper>,
pub security_groups: Option<SecurityGroupsWrapper>,
pub ip_address_type: Option<String>,
pub vpc_id: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct LoadBalancerState {
pub code: Option<String>,
pub reason: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct AvailabilityZonesWrapper {
#[serde(rename = "member", default)]
pub members: Vec<AvailabilityZone>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct AvailabilityZone {
pub zone_name: Option<String>,
pub subnet_id: Option<String>,
pub outpost_id: Option<String>,
pub load_balancer_addresses: Option<LoadBalancerAddressesWrapper>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct LoadBalancerAddressesWrapper {
#[serde(rename = "member", default)]
pub members: Vec<LoadBalancerAddress>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct LoadBalancerAddress {
pub ip_address: Option<String>,
pub allocation_id: Option<String>,
pub private_i_pv4_address: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct SecurityGroupsWrapper {
#[serde(rename = "member", default)]
pub members: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct CreateTargetGroupRequest {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub protocol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub protocol_version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub port: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vpc_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub health_check_protocol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub health_check_port: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub health_check_enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub health_check_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub health_check_interval_seconds: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub health_check_timeout_seconds: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub healthy_threshold_count: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub unhealthy_threshold_count: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub matcher: Option<Matcher>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ip_address_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<ElbTag>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CreateTargetGroupResponse {
pub create_target_group_result: CreateTargetGroupResult,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CreateTargetGroupResult {
pub target_groups: Option<TargetGroupsWrapper>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TargetGroupsWrapper {
#[serde(rename = "member", default)]
pub members: Vec<TargetGroup>,
}
#[derive(Debug, Clone, Serialize, Builder, Default)]
pub struct DescribeTargetGroupsRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub load_balancer_arn: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_group_arns: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub marker: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_size: Option<i32>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct DescribeTargetGroupsResponse {
pub describe_target_groups_result: DescribeTargetGroupsResult,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct DescribeTargetGroupsResult {
pub target_groups: Option<TargetGroupsWrapper>,
pub next_marker: Option<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct ModifyTargetGroupRequest {
pub target_group_arn: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub health_check_protocol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub health_check_port: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub health_check_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub health_check_enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub health_check_interval_seconds: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub health_check_timeout_seconds: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub healthy_threshold_count: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub unhealthy_threshold_count: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub matcher: Option<Matcher>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ModifyTargetGroupResponse {
pub modify_target_group_result: ModifyTargetGroupResult,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ModifyTargetGroupResult {
pub target_groups: Option<TargetGroupsWrapper>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TargetGroup {
pub target_group_arn: Option<String>,
pub target_group_name: Option<String>,
pub protocol: Option<String>,
pub protocol_version: Option<String>,
pub port: Option<i32>,
pub vpc_id: Option<String>,
pub health_check_protocol: Option<String>,
pub health_check_port: Option<String>,
pub health_check_enabled: Option<bool>,
pub health_check_interval_seconds: Option<i32>,
pub health_check_timeout_seconds: Option<i32>,
pub healthy_threshold_count: Option<i32>,
pub unhealthy_threshold_count: Option<i32>,
pub health_check_path: Option<String>,
pub matcher: Option<MatcherResponse>,
pub load_balancer_arns: Option<LoadBalancerArnsWrapper>,
pub target_type: Option<String>,
pub ip_address_type: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct MatcherResponse {
pub http_code: Option<String>,
pub grpc_code: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct LoadBalancerArnsWrapper {
#[serde(rename = "member", default)]
pub members: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct RegisterTargetsRequest {
pub target_group_arn: String,
pub targets: Vec<TargetDescription>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct DeregisterTargetsRequest {
pub target_group_arn: String,
pub targets: Vec<TargetDescription>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct DescribeTargetHealthRequest {
pub target_group_arn: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub targets: Option<Vec<TargetDescription>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct DescribeTargetHealthResponse {
pub describe_target_health_result: DescribeTargetHealthResult,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct DescribeTargetHealthResult {
pub target_health_descriptions: Option<TargetHealthDescriptionsWrapper>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TargetHealthDescriptionsWrapper {
#[serde(rename = "member", default)]
pub members: Vec<TargetHealthDescription>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TargetHealthDescription {
pub target: Option<TargetDescriptionResponse>,
pub health_check_port: Option<String>,
pub target_health: Option<TargetHealth>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TargetDescriptionResponse {
pub id: Option<String>,
pub port: Option<i32>,
pub availability_zone: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TargetHealth {
pub state: Option<String>,
pub reason: Option<String>,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct Action {
pub action_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_group_arn: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub forward_config: Option<ForwardActionConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub redirect_config: Option<RedirectActionConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fixed_response_config: Option<FixedResponseActionConfig>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct ForwardActionConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub target_groups: Option<Vec<TargetGroupTuple>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_group_stickiness_config: Option<TargetGroupStickinessConfig>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct TargetGroupTuple {
pub target_group_arn: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub weight: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct TargetGroupStickinessConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_seconds: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct RedirectActionConfig {
pub status_code: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub protocol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub port: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub host: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub query: Option<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct FixedResponseActionConfig {
pub status_code: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub content_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message_body: Option<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct CreateListenerRequest {
pub load_balancer_arn: String,
pub port: i32,
pub protocol: String,
pub default_actions: Vec<Action>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ssl_policy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub certificates: Option<Vec<Certificate>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub alpn_policy: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<ElbTag>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CreateListenerResponse {
pub create_listener_result: CreateListenerResult,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CreateListenerResult {
pub listeners: Option<ListenersWrapper>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ListenersWrapper {
#[serde(rename = "member", default)]
pub members: Vec<Listener>,
}
#[derive(Debug, Clone, Serialize, Builder, Default)]
pub struct DescribeListenersRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub load_balancer_arn: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub listener_arns: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub marker: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_size: Option<i32>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct DescribeListenersResponse {
pub describe_listeners_result: DescribeListenersResult,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct DescribeListenersResult {
pub listeners: Option<ListenersWrapper>,
pub next_marker: Option<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
pub struct ModifyListenerRequest {
pub listener_arn: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub port: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub protocol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ssl_policy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub certificates: Option<Vec<Certificate>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_actions: Option<Vec<Action>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ModifyListenerResponse {
pub modify_listener_result: ModifyListenerResult,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ModifyListenerResult {
pub listeners: Option<ListenersWrapper>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Listener {
pub listener_arn: Option<String>,
pub load_balancer_arn: Option<String>,
pub port: Option<i32>,
pub protocol: Option<String>,
pub certificates: Option<CertificatesWrapper>,
pub ssl_policy: Option<String>,
pub default_actions: Option<ActionsWrapper>,
pub alpn_policy: Option<AlpnPolicyWrapper>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CertificatesWrapper {
#[serde(rename = "member", default)]
pub members: Vec<CertificateResponse>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CertificateResponse {
pub certificate_arn: Option<String>,
pub is_default: Option<bool>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ActionsWrapper {
#[serde(rename = "member", default)]
pub members: Vec<ActionResponse>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ActionResponse {
#[serde(rename = "Type")]
pub action_type: Option<String>,
pub target_group_arn: Option<String>,
pub order: Option<i32>,
pub forward_config: Option<ForwardActionConfigResponse>,
pub redirect_config: Option<RedirectActionConfigResponse>,
pub fixed_response_config: Option<FixedResponseActionConfigResponse>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ForwardActionConfigResponse {
pub target_groups: Option<TargetGroupTuplesWrapper>,
pub target_group_stickiness_config: Option<TargetGroupStickinessConfigResponse>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TargetGroupTuplesWrapper {
#[serde(rename = "member", default)]
pub members: Vec<TargetGroupTupleResponse>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TargetGroupTupleResponse {
pub target_group_arn: Option<String>,
pub weight: Option<i32>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TargetGroupStickinessConfigResponse {
pub enabled: Option<bool>,
pub duration_seconds: Option<i32>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct RedirectActionConfigResponse {
pub protocol: Option<String>,
pub port: Option<String>,
pub host: Option<String>,
pub path: Option<String>,
pub query: Option<String>,
pub status_code: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct FixedResponseActionConfigResponse {
pub status_code: Option<String>,
pub content_type: Option<String>,
pub message_body: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct AlpnPolicyWrapper {
#[serde(rename = "member", default)]
pub members: Vec<String>,
}