1use std::collections::{BTreeMap, HashMap};
2use std::sync::Arc;
3
4use chrono::{DateTime, Utc};
5use parking_lot::RwLock;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9pub type SharedEcrState = Arc<RwLock<fakecloud_core::multi_account::MultiAccountState<EcrState>>>;
10
11impl fakecloud_core::multi_account::AccountState for EcrState {
12 fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
13 Self::new(account_id, region)
14 }
15}
16
17pub const ECR_SNAPSHOT_SCHEMA_VERSION: u32 = 3;
18
19#[derive(Clone, Debug, Serialize, Deserialize)]
23pub struct EcrSnapshot {
24 pub schema_version: u32,
25 pub accounts: Option<fakecloud_core::multi_account::MultiAccountState<EcrState>>,
26}
27
28#[derive(Clone, Debug, Default, Serialize, Deserialize)]
29pub struct EcrState {
30 pub account_id: String,
31 pub region: String,
32 pub repositories: BTreeMap<String, Repository>,
34 pub registry_policy: Option<String>,
37 pub registry_scanning_configuration: RegistryScanningConfiguration,
41 pub replication_configuration: Option<ReplicationConfiguration>,
43 pub account_settings: HashMap<String, String>,
46 #[serde(default)]
49 pub layer_uploads: BTreeMap<String, LayerUpload>,
50 #[serde(default)]
53 pub pull_time_exclusions: BTreeMap<String, PullTimeExclusion>,
54 #[serde(default)]
56 pub pull_through_cache_rules: BTreeMap<String, PullThroughCacheRule>,
57 #[serde(default)]
59 pub repository_creation_templates: BTreeMap<String, RepositoryCreationTemplate>,
60 #[serde(default)]
62 pub signing_configuration: Option<SigningConfiguration>,
63}
64
65impl EcrState {
66 pub fn new(account_id: &str, region: &str) -> Self {
67 Self {
68 account_id: account_id.to_string(),
69 region: region.to_string(),
70 repositories: BTreeMap::new(),
71 registry_policy: None,
72 registry_scanning_configuration: RegistryScanningConfiguration::default(),
73 replication_configuration: None,
74 account_settings: HashMap::new(),
75 layer_uploads: BTreeMap::new(),
76 pull_time_exclusions: BTreeMap::new(),
77 pull_through_cache_rules: BTreeMap::new(),
78 repository_creation_templates: BTreeMap::new(),
79 signing_configuration: None,
80 }
81 }
82
83 pub fn reset(&mut self) {
84 self.repositories.clear();
85 self.registry_policy = None;
86 self.registry_scanning_configuration = RegistryScanningConfiguration::default();
87 self.replication_configuration = None;
88 self.account_settings.clear();
89 self.layer_uploads.clear();
90 self.pull_time_exclusions.clear();
91 self.pull_through_cache_rules.clear();
92 self.repository_creation_templates.clear();
93 self.signing_configuration = None;
94 }
95
96 pub fn repository_arn(&self, repository_name: &str) -> String {
97 format!(
98 "arn:aws:ecr:{}:{}:repository/{}",
99 self.region, self.account_id, repository_name
100 )
101 }
102
103 pub fn registry_id(&self) -> &str {
104 &self.account_id
105 }
106}
107
108#[derive(Clone, Debug, Serialize, Deserialize)]
109pub struct Repository {
110 pub repository_name: String,
111 pub repository_arn: String,
112 pub registry_id: String,
113 pub repository_uri: String,
114 pub created_at: DateTime<Utc>,
115 pub image_tag_mutability: String,
116 pub image_scanning_configuration: ImageScanningConfiguration,
117 pub encryption_configuration: EncryptionConfiguration,
118 pub tags: BTreeMap<String, String>,
119 pub policy: Option<String>,
122 pub lifecycle_policy: Option<String>,
124 #[serde(default)]
126 pub scan_findings: BTreeMap<String, ImageScanFindings>,
127 #[serde(default)]
130 pub images: BTreeMap<String, Image>,
131 #[serde(default)]
134 pub image_tags: BTreeMap<String, String>,
135 #[serde(default)]
139 pub layers: BTreeMap<String, Layer>,
140}
141
142impl Repository {
143 pub fn new(
144 repository_name: &str,
145 repository_arn: String,
146 registry_id: &str,
147 endpoint: &str,
148 ) -> Self {
149 let host = endpoint
151 .trim_start_matches("http://")
152 .trim_start_matches("https://")
153 .trim_end_matches('/')
154 .to_string();
155 Self {
156 repository_name: repository_name.to_string(),
157 repository_arn,
158 registry_id: registry_id.to_string(),
159 repository_uri: format!("{host}/{repository_name}"),
160 created_at: Utc::now(),
161 image_tag_mutability: "MUTABLE".to_string(),
162 image_scanning_configuration: ImageScanningConfiguration::default(),
163 encryption_configuration: EncryptionConfiguration::default(),
164 tags: BTreeMap::new(),
165 policy: None,
166 lifecycle_policy: None,
167 scan_findings: BTreeMap::new(),
168 images: BTreeMap::new(),
169 image_tags: BTreeMap::new(),
170 layers: BTreeMap::new(),
171 }
172 }
173}
174
175#[derive(Clone, Debug, Serialize, Deserialize)]
176pub struct PullTimeExclusion {
177 pub principal_arn: String,
178 pub registered_at: DateTime<Utc>,
179}
180
181#[derive(Clone, Debug, Serialize, Deserialize)]
182pub struct ImageScanFindings {
183 pub image_digest: String,
184 pub scan_status: String,
185 pub scan_completed_at: Option<DateTime<Utc>>,
186 pub vulnerability_source_updated_at: Option<DateTime<Utc>>,
187 pub finding_severity_counts: BTreeMap<String, i64>,
188 pub findings: Vec<Value>,
189}
190
191#[derive(Clone, Debug, Serialize, Deserialize)]
192pub struct PullThroughCacheRule {
193 pub ecr_repository_prefix: String,
194 pub upstream_registry_url: String,
195 pub upstream_registry: Option<String>,
196 pub credential_arn: Option<String>,
197 pub created_at: DateTime<Utc>,
198 pub updated_at: DateTime<Utc>,
199 pub custom_role_arn: Option<String>,
200}
201
202#[derive(Clone, Debug, Serialize, Deserialize)]
203pub struct RepositoryCreationTemplate {
204 pub prefix: String,
205 pub description: Option<String>,
206 pub image_tag_mutability: String,
207 pub applied_for: Vec<String>,
208 pub resource_tags: Vec<Value>,
209 pub created_at: DateTime<Utc>,
210 pub updated_at: DateTime<Utc>,
211 pub custom_role_arn: Option<String>,
212 pub repository_policy: Option<String>,
213 pub lifecycle_policy: Option<String>,
214 pub encryption_configuration: Option<EncryptionConfiguration>,
215}
216
217#[derive(Clone, Debug, Default, Serialize, Deserialize)]
218pub struct SigningConfiguration {
219 pub rules: Vec<Value>,
223 #[serde(default)]
228 pub trusted_keys: Vec<crate::signing::TrustedKey>,
229}
230
231#[derive(Clone, Debug, Serialize, Deserialize)]
232pub struct Image {
233 pub image_digest: String,
234 pub image_manifest: String,
235 pub image_manifest_media_type: String,
236 pub artifact_media_type: Option<String>,
237 pub image_size_in_bytes: u64,
238 pub image_pushed_at: DateTime<Utc>,
239 pub last_recorded_pull_time: Option<DateTime<Utc>>,
240}
241
242#[derive(Clone, Debug, Serialize, Deserialize)]
243pub struct Layer {
244 pub digest: String,
245 pub size: u64,
246 pub blob_b64: String,
252 pub media_type: String,
253 #[serde(default)]
259 pub encrypted_with_kms_key: Option<String>,
260}
261
262#[derive(Clone, Debug, Serialize, Deserialize)]
263pub struct LayerUpload {
264 pub upload_id: String,
265 pub repository_name: String,
266 pub created_at: DateTime<Utc>,
267 #[serde(default)]
275 pub spool_path: String,
276 pub last_byte_received: u64,
277}
278
279#[derive(Clone, Debug, Default, Serialize, Deserialize)]
280pub struct ImageScanningConfiguration {
281 pub scan_on_push: bool,
283}
284
285#[derive(Clone, Debug, Serialize, Deserialize)]
286pub struct EncryptionConfiguration {
287 pub encryption_type: String,
289 pub kms_key: Option<String>,
291}
292
293impl Default for EncryptionConfiguration {
294 fn default() -> Self {
295 Self {
296 encryption_type: "AES256".to_string(),
297 kms_key: None,
298 }
299 }
300}
301
302#[derive(Clone, Debug, Serialize, Deserialize)]
303pub struct RegistryScanningConfiguration {
304 pub scan_type: String,
306 pub rules: Vec<RegistryScanningRule>,
307}
308
309impl Default for RegistryScanningConfiguration {
310 fn default() -> Self {
311 Self {
312 scan_type: "BASIC".to_string(),
313 rules: Vec::new(),
314 }
315 }
316}
317
318#[derive(Clone, Debug, Serialize, Deserialize)]
319pub struct RegistryScanningRule {
320 pub scan_frequency: String,
321 pub repository_filters: Vec<RepositoryFilter>,
322}
323
324#[derive(Clone, Debug, Serialize, Deserialize)]
325pub struct RepositoryFilter {
326 pub filter: String,
327 pub filter_type: String,
328}
329
330#[derive(Clone, Debug, Serialize, Deserialize)]
331pub struct ReplicationConfiguration {
332 pub rules: Vec<ReplicationRule>,
333}
334
335#[derive(Clone, Debug, Serialize, Deserialize)]
336pub struct ReplicationRule {
337 pub destinations: Vec<ReplicationDestination>,
338 pub repository_filters: Vec<RepositoryFilter>,
339}
340
341#[derive(Clone, Debug, Serialize, Deserialize)]
342pub struct ReplicationDestination {
343 pub region: String,
344 pub registry_id: String,
345}