Skip to main content

awsim_ecs/
handler.rs

1use std::sync::Arc;
2
3use awsim_core::{
4    AccountRegionStore, AwsError, CloudMapRegistrar, ParameterLookup, PrincipalLookup, Protocol,
5    RequestContext, SecretLookup, ServiceHandler,
6};
7use serde_json::Value;
8use tracing::debug;
9
10use crate::operations::{clusters, container_instances, extras, services, task_definitions, tasks};
11use crate::state::EcsState;
12
13/// The ECS service handler.
14pub struct EcsService {
15    store: AccountRegionStore<EcsState>,
16    /// IAM principal lookup used to validate `taskRoleArn` /
17    /// `executionRoleArn` at RegisterTaskDefinition. `None` keeps the
18    /// service working in standalone test setups that don't wire IAM.
19    iam_lookup: Option<Arc<dyn PrincipalLookup>>,
20    /// SecretsManager lookup used to validate
21    /// `containerDefinitions[].repositoryCredentials.credentialsParameter`
22    /// at RegisterTaskDefinition and `containerDefinitions[].secrets[]`
23    /// SecretsManager refs at RunTask. `None` skips the validation.
24    secrets_lookup: Option<Arc<dyn SecretLookup>>,
25    /// SSM Parameter lookup used to validate
26    /// `containerDefinitions[].secrets[]` references that point at
27    /// SSM parameters. `None` skips the validation.
28    parameters_lookup: Option<Arc<dyn ParameterLookup>>,
29    /// Cloud Map registrar used to publish ECS services into a Cloud
30    /// Map service whenever CreateService passes `serviceRegistries[]`.
31    cloudmap_registrar: Option<Arc<dyn CloudMapRegistrar>>,
32}
33
34impl EcsService {
35    pub fn new() -> Self {
36        Self {
37            store: AccountRegionStore::new(),
38            iam_lookup: None,
39            secrets_lookup: None,
40            parameters_lookup: None,
41            cloudmap_registrar: None,
42        }
43    }
44
45    /// Plug in the IAM principal lookup so task / execution role ARNs
46    /// are verified against the IAM store at registration time.
47    pub fn with_iam_lookup(mut self, lookup: Arc<dyn PrincipalLookup>) -> Self {
48        self.iam_lookup = Some(lookup);
49        self
50    }
51
52    /// Plug in the SecretsManager lookup so private-registry
53    /// credentials referenced via `repositoryCredentials.credentialsParameter`
54    /// are verified against the secrets store at registration time.
55    pub fn with_secrets_lookup(mut self, lookup: Arc<dyn SecretLookup>) -> Self {
56        self.secrets_lookup = Some(lookup);
57        self
58    }
59
60    /// Plug in the SSM Parameter Store lookup so container
61    /// `secrets[].valueFrom` references that point at SSM parameters
62    /// are validated when RunTask materialises the task.
63    pub fn with_parameters_lookup(mut self, lookup: Arc<dyn ParameterLookup>) -> Self {
64        self.parameters_lookup = Some(lookup);
65        self
66    }
67
68    /// Plug in the Cloud Map registrar so CreateService with
69    /// `serviceRegistries[]` registers an instance per registry, and
70    /// DeleteService cleans them up.
71    pub fn with_cloudmap_registrar(mut self, registrar: Arc<dyn CloudMapRegistrar>) -> Self {
72        self.cloudmap_registrar = Some(registrar);
73        self
74    }
75}
76
77impl Default for EcsService {
78    fn default() -> Self {
79        Self::new()
80    }
81}
82
83#[async_trait::async_trait]
84impl ServiceHandler for EcsService {
85    fn service_name(&self) -> &str {
86        "ecs"
87    }
88
89    fn signing_name(&self) -> &str {
90        "ecs"
91    }
92
93    fn protocol(&self) -> Protocol {
94        Protocol::AwsJson1_1
95    }
96
97    async fn handle(
98        &self,
99        operation: &str,
100        input: Value,
101        ctx: &RequestContext,
102    ) -> Result<Value, AwsError> {
103        debug!(operation = %operation, "ECS operation");
104
105        let state = self.store.get(&ctx.account_id, &ctx.region);
106
107        match operation {
108            // Clusters
109            "CreateCluster" => clusters::create_cluster(&state, &input, ctx),
110            "DeleteCluster" => clusters::delete_cluster(&state, &input, ctx),
111            "DescribeClusters" => clusters::describe_clusters(&state, &input, ctx),
112            "ListClusters" => clusters::list_clusters(&state, &input, ctx),
113
114            // Task Definitions
115            "RegisterTaskDefinition" => task_definitions::register_task_definition(
116                &state,
117                &input,
118                ctx,
119                self.iam_lookup.as_deref(),
120                self.secrets_lookup.as_deref(),
121            ),
122            "DeregisterTaskDefinition" => {
123                task_definitions::deregister_task_definition(&state, &input, ctx)
124            }
125            "DescribeTaskDefinition" => {
126                task_definitions::describe_task_definition(&state, &input, ctx)
127            }
128            "ListTaskDefinitions" => task_definitions::list_task_definitions(&state, &input, ctx),
129            "ListTaskDefinitionFamilies" => {
130                task_definitions::list_task_definition_families(&state, &input, ctx)
131            }
132
133            // Services
134            "CreateService" => {
135                services::create_service(&state, &input, ctx, self.cloudmap_registrar.as_deref())
136            }
137            "DeleteService" => {
138                services::delete_service(&state, &input, ctx, self.cloudmap_registrar.as_deref())
139            }
140            "DescribeServices" => services::describe_services(&state, &input, ctx),
141            "ListServices" => services::list_services(&state, &input, ctx),
142            "UpdateService" => services::update_service(&state, &input, ctx),
143
144            // Tasks
145            "RunTask" => tasks::run_task(
146                &state,
147                &input,
148                ctx,
149                self.secrets_lookup.as_deref(),
150                self.parameters_lookup.as_deref(),
151            ),
152            "StopTask" => tasks::stop_task(&state, &input, ctx),
153            "DescribeTasks" => tasks::describe_tasks(&state, &input, ctx),
154            "ListTasks" => tasks::list_tasks(&state, &input, ctx),
155
156            // Tagging
157            "TagResource" => extras::tag_resource(&state, &input, ctx),
158            "UntagResource" => extras::untag_resource(&state, &input, ctx),
159            "ListTagsForResource" => extras::list_tags_for_resource(&state, &input, ctx),
160
161            // Capacity Providers
162            "PutClusterCapacityProviders" => {
163                extras::put_cluster_capacity_providers(&state, &input, ctx)
164            }
165            "DescribeCapacityProviders" => extras::describe_capacity_providers(&state, &input, ctx),
166
167            // Account Settings
168            "PutAccountSetting" => extras::put_account_setting(&state, &input, ctx),
169            "PutAccountSettingDefault" => extras::put_account_setting(&state, &input, ctx),
170            "ListAccountSettings" => extras::list_account_settings(&state, &input, ctx),
171
172            // Container agent
173            "DiscoverPollEndpoint" => extras::discover_poll_endpoint(&state, &input, ctx),
174            "UpdateContainerAgent" => extras::update_container_agent(&state, &input, ctx),
175
176            // Container Instances + Attributes
177            "DescribeContainerInstances" => {
178                container_instances::describe_container_instances(&state, &input, ctx)
179            }
180            "ListContainerInstances" => {
181                container_instances::list_container_instances(&state, &input, ctx)
182            }
183            "ListAttributes" => container_instances::list_attributes(&state, &input, ctx),
184            "PutAttributes" => container_instances::put_attributes(&state, &input, ctx),
185            "DeleteAttributes" => container_instances::delete_attributes(&state, &input, ctx),
186            "ListServicesByNamespace" => {
187                container_instances::list_services_by_namespace(&state, &input, ctx)
188            }
189
190            _ => Err(AwsError::unknown_operation(operation)),
191        }
192    }
193}