Skip to main content

alien_core/bindings/
artifact_registry.rs

1//! Service-type based artifact registry binding definitions
2
3use super::BindingValue;
4use serde::{Deserialize, Serialize};
5
6/// AWS ECR (Elastic Container Registry) binding configuration
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
9#[serde(rename_all = "camelCase")]
10pub struct EcrArtifactRegistryBinding {
11    /// Repository prefix for this registry (used to construct ECR repository names)
12    pub repository_prefix: BindingValue<String>,
13    /// Optional namespace for sandbox images served by regional ECR pull-through caches.
14    /// Configure cache rules before enabling this; workers keep using `repositoryPrefix`.
15    #[serde(default, skip_serializing_if = "Option::is_none")]
16    pub sandbox_repository_prefix: Option<BindingValue<String>>,
17    /// Repository prefixes this registry used before `repositoryPrefix`. Images already released
18    /// under them stay readable and keep their cross-account grants; nothing new is written there.
19    #[serde(default, skip_serializing_if = "Vec::is_empty")]
20    pub legacy_repository_prefixes: Vec<String>,
21    /// Sandbox namespaces this registry used before `sandboxRepositoryPrefix`, read the same way.
22    #[serde(default, skip_serializing_if = "Vec::is_empty")]
23    pub legacy_sandbox_repository_prefixes: Vec<String>,
24    /// ARN of the IAM role for pull permissions (optional — omit for single-account)
25    #[serde(default, skip_serializing_if = "Option::is_none")]
26    pub pull_role_arn: Option<BindingValue<String>>,
27    /// ARN of the IAM role for push+pull permissions (optional — omit for single-account)
28    #[serde(default, skip_serializing_if = "Option::is_none")]
29    pub push_role_arn: Option<BindingValue<String>>,
30}
31
32/// Azure Container Registry binding configuration
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
34#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
35#[serde(rename_all = "camelCase")]
36pub struct AcrArtifactRegistryBinding {
37    /// Registry name (e.g., "myregistry") - endpoint is derived from this
38    pub registry_name: BindingValue<String>,
39    /// Resource group name where the registry is located
40    pub resource_group_name: BindingValue<String>,
41    /// Repository prefix for this registry (used for proxy routing)
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub repository_prefix: Option<BindingValue<String>>,
44}
45
46/// Google Artifact Registry binding configuration
47#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
48#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
49#[serde(rename_all = "camelCase")]
50pub struct GarArtifactRegistryBinding {
51    /// Repository name in the Artifact Registry (e.g., "alien-test").
52    /// Used as the default when cross-account access methods are called
53    /// without a specific repo name.
54    pub repository_name: BindingValue<String>,
55    /// Optional service account email for pull permissions (omit for single-project)
56    #[serde(default, skip_serializing_if = "Option::is_none")]
57    pub pull_service_account_email: Option<BindingValue<String>>,
58    /// Optional service account email for push+pull permissions (omit for single-project)
59    #[serde(default, skip_serializing_if = "Option::is_none")]
60    pub push_service_account_email: Option<BindingValue<String>>,
61}
62
63/// Local container registry binding configuration.
64///
65/// The local registry runs on localhost only and does not require authentication.
66/// Security boundary is the OS process isolation on the customer's machine.
67/// External image access is secured by the manager's registry proxy (deployment tokens).
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
70#[serde(rename_all = "camelCase")]
71pub struct LocalArtifactRegistryBinding {
72    /// The registry URL endpoint (e.g., "localhost:5000")
73    pub registry_url: BindingValue<String>,
74    /// Optional base directory for registry data
75    pub data_dir: BindingValue<Option<String>>,
76}
77
78/// Service-type based artifact registry binding that supports multiple registry providers
79#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
80#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
81#[serde(tag = "service", rename_all = "lowercase")]
82pub enum ArtifactRegistryBinding {
83    /// AWS ECR (Elastic Container Registry)
84    Ecr(EcrArtifactRegistryBinding),
85    /// Azure Container Registry
86    Acr(AcrArtifactRegistryBinding),
87    /// Google Artifact Registry
88    Gar(GarArtifactRegistryBinding),
89    /// Local container registry
90    Local(LocalArtifactRegistryBinding),
91}
92
93impl ArtifactRegistryBinding {
94    /// Creates an ECR artifact registry binding
95    pub fn ecr(
96        repository_prefix: impl Into<BindingValue<String>>,
97        pull_role_arn: Option<impl Into<BindingValue<String>>>,
98        push_role_arn: Option<impl Into<BindingValue<String>>>,
99    ) -> Self {
100        Self::Ecr(EcrArtifactRegistryBinding {
101            sandbox_repository_prefix: None,
102            legacy_repository_prefixes: Vec::new(),
103            legacy_sandbox_repository_prefixes: Vec::new(),
104            repository_prefix: repository_prefix.into(),
105            pull_role_arn: pull_role_arn.map(|v| v.into()),
106            push_role_arn: push_role_arn.map(|v| v.into()),
107        })
108    }
109
110    /// Creates an ACR artifact registry binding
111    pub fn acr(
112        registry_name: impl Into<BindingValue<String>>,
113        resource_group_name: impl Into<BindingValue<String>>,
114    ) -> Self {
115        Self::Acr(AcrArtifactRegistryBinding {
116            registry_name: registry_name.into(),
117            resource_group_name: resource_group_name.into(),
118            repository_prefix: None,
119        })
120    }
121
122    /// Creates a GAR artifact registry binding
123    pub fn gar(
124        repository_name: impl Into<BindingValue<String>>,
125        pull_service_account_email: Option<impl Into<BindingValue<String>>>,
126        push_service_account_email: Option<impl Into<BindingValue<String>>>,
127    ) -> Self {
128        Self::Gar(GarArtifactRegistryBinding {
129            repository_name: repository_name.into(),
130            pull_service_account_email: pull_service_account_email.map(|v| v.into()),
131            push_service_account_email: push_service_account_email.map(|v| v.into()),
132        })
133    }
134
135    /// Creates a local artifact registry binding
136    pub fn local(
137        registry_url: impl Into<BindingValue<String>>,
138        data_dir: impl Into<BindingValue<Option<String>>>,
139    ) -> Self {
140        Self::Local(LocalArtifactRegistryBinding {
141            registry_url: registry_url.into(),
142            data_dir: data_dir.into(),
143        })
144    }
145}