use std::collections::HashMap;
use awsim_core::{AwsError, RequestContext};
use serde_json::{Value, json};
use crate::state::EcsState;
pub fn tag_resource(
state: &EcsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let resource_arn = input["resourceArn"].as_str().ok_or_else(|| {
AwsError::bad_request("InvalidParameterException", "resourceArn is required")
})?;
let mut entry = state
.resource_tags
.entry(resource_arn.to_string())
.or_default();
if let Some(tag_list) = input["tags"].as_array() {
for tag in tag_list {
if let (Some(k), Some(v)) = (tag["key"].as_str(), tag["value"].as_str()) {
entry.insert(k.to_string(), v.to_string());
}
}
}
Ok(json!({}))
}
pub fn untag_resource(
state: &EcsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let resource_arn = input["resourceArn"].as_str().ok_or_else(|| {
AwsError::bad_request("InvalidParameterException", "resourceArn is required")
})?;
let tag_keys: Vec<&str> = input["tagKeys"]
.as_array()
.map(|arr| arr.iter().filter_map(|v| v.as_str()).collect())
.unwrap_or_default();
if let Some(mut tags) = state.resource_tags.get_mut(resource_arn) {
for key in &tag_keys {
tags.remove(*key);
}
}
Ok(json!({}))
}
pub fn list_tags_for_resource(
state: &EcsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let resource_arn = input["resourceArn"].as_str().ok_or_else(|| {
AwsError::bad_request("InvalidParameterException", "resourceArn is required")
})?;
let tags: Vec<Value> = state
.resource_tags
.get(resource_arn)
.map(|t| {
t.iter()
.map(|(k, v)| json!({ "key": k, "value": v }))
.collect()
})
.unwrap_or_default();
Ok(json!({ "tags": tags }))
}
pub fn put_cluster_capacity_providers(
state: &EcsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let cluster_name = input["cluster"]
.as_str()
.ok_or_else(|| AwsError::bad_request("InvalidParameterException", "cluster is required"))?;
let name = if cluster_name.starts_with("arn:") {
cluster_name.split('/').next_back().unwrap_or(cluster_name)
} else {
cluster_name
};
let mut cluster = state.clusters.get_mut(name).ok_or_else(|| {
AwsError::not_found(
"ClusterNotFoundException",
format!("The specified cluster '{name}' does not exist"),
)
})?;
let providers: Vec<String> = input["capacityProviders"]
.as_array()
.map(|arr| {
arr.iter()
.filter_map(|v| v.as_str().map(|s| s.to_string()))
.collect()
})
.unwrap_or_default();
let strategy: Vec<Value> = input["defaultCapacityProviderStrategy"]
.as_array()
.cloned()
.unwrap_or_default();
cluster.capacity_providers = providers;
cluster.default_capacity_provider_strategy = strategy;
let cluster_json = json!({
"clusterArn": cluster.arn,
"clusterName": cluster.name,
"status": cluster.status,
"registeredContainerInstancesCount": 0,
"runningTasksCount": cluster.tasks.values().filter(|t| t.status == "RUNNING").count(),
"pendingTasksCount": 0,
"activeServicesCount": cluster.services.len(),
"statistics": [],
"tags": [],
"capacityProviders": cluster.capacity_providers,
"defaultCapacityProviderStrategy": cluster.default_capacity_provider_strategy,
});
Ok(json!({ "cluster": cluster_json }))
}
pub fn describe_capacity_providers(
state: &EcsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let requested: Vec<&str> = input["capacityProviders"]
.as_array()
.map(|arr| arr.iter().filter_map(|v| v.as_str()).collect())
.unwrap_or_default();
let mut providers: Vec<Value> = vec![
json!({
"capacityProviderArn": "arn:aws:ecs:::capacity-provider/FARGATE",
"name": "FARGATE",
"status": "ACTIVE",
"updateStatus": "DELETE_COMPLETE",
}),
json!({
"capacityProviderArn": "arn:aws:ecs:::capacity-provider/FARGATE_SPOT",
"name": "FARGATE_SPOT",
"status": "ACTIVE",
"updateStatus": "DELETE_COMPLETE",
}),
];
for entry in state.capacity_providers.iter() {
let cp = entry.value();
if requested.is_empty() || requested.contains(&cp.name.as_str()) {
providers.push(json!({
"capacityProviderArn": cp.arn,
"name": cp.name,
"status": cp.status,
}));
}
}
if !requested.is_empty() {
providers.retain(|p| requested.contains(&p["name"].as_str().unwrap_or("")));
}
Ok(json!({
"capacityProviders": providers,
"nextToken": null,
}))
}
pub fn put_account_setting(
state: &EcsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let name = input["name"]
.as_str()
.ok_or_else(|| AwsError::bad_request("InvalidParameterException", "name is required"))?;
let value = input["value"]
.as_str()
.ok_or_else(|| AwsError::bad_request("InvalidParameterException", "value is required"))?;
state
.account_settings
.insert(name.to_string(), value.to_string());
Ok(json!({
"setting": {
"name": name,
"value": value,
"principalArn": "arn:aws:iam::000000000000:root",
}
}))
}
pub fn list_account_settings(
state: &EcsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let filter_name = input["name"].as_str();
let effective_settings = input["effectiveSettings"].as_bool().unwrap_or(false);
let defaults: HashMap<&str, &str> = [
("containerInstanceLongArnFormat", "enabled"),
("serviceLongArnFormat", "enabled"),
("taskLongArnFormat", "enabled"),
("awsvpcTrunking", "disabled"),
("containerInsights", "disabled"),
]
.into_iter()
.collect();
let settings: Vec<Value> = if effective_settings || state.account_settings.is_empty() {
defaults
.iter()
.filter(|(name, _)| filter_name.is_none_or(|f| **name == f))
.map(|(name, default_val)| {
let val = state
.account_settings
.get(*name)
.map(|v| v.clone())
.unwrap_or_else(|| default_val.to_string());
json!({
"name": name,
"value": val,
"principalArn": "arn:aws:iam::000000000000:root",
})
})
.collect()
} else {
state
.account_settings
.iter()
.filter(|e| filter_name.is_none_or(|f| e.key() == f))
.map(|e| {
json!({
"name": e.key(),
"value": e.value(),
"principalArn": "arn:aws:iam::000000000000:root",
})
})
.collect()
};
Ok(json!({ "settings": settings }))
}
pub fn discover_poll_endpoint(
_state: &EcsState,
_input: &Value,
ctx: &RequestContext,
) -> Result<Value, AwsError> {
Ok(json!({
"endpoint": format!("http://ecs-agent.{}.amazonaws.com", ctx.region),
"telemetryEndpoint": format!("http://ecs-telemetry.{}.amazonaws.com", ctx.region),
}))
}
pub fn update_container_agent(
state: &EcsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let cluster_name = input["cluster"].as_str().unwrap_or("default");
let name = if cluster_name.starts_with("arn:") {
cluster_name.split('/').next_back().unwrap_or(cluster_name)
} else {
cluster_name
};
let cluster_exists = state.clusters.contains_key(name);
if !cluster_exists && name != "default" {
return Err(AwsError::not_found(
"ClusterNotFoundException",
format!("The specified cluster '{name}' does not exist"),
));
}
Ok(json!({
"containerInstance": {
"containerInstanceArn": format!("arn:aws:ecs:us-east-1:000000000000:container-instance/{}/stub-instance", name),
"ec2InstanceId": "i-stub",
"agentConnected": true,
"runningTasksCount": 0,
"pendingTasksCount": 0,
"status": "ACTIVE",
}
}))
}