use crate::{
ownership_policy_for_resource_type, ErrorData, Key, ManagementPermissions, Platform,
ResourceEntry, ResourceLifecycle, Result, Sandbox, SetupManagement, Stack, Storage,
};
use alien_error::AlienError;
pub fn validate_automatic_setup_resources(
stack: &Stack,
platform: Platform,
management: SetupManagement,
) -> Result<()> {
if management == SetupManagement::Automatic
&& matches!(platform, Platform::Aws | Platform::Gcp | Platform::Azure)
{
let unsupported = |resource_type: &str, resource_id: &str| {
AlienError::new(ErrorData::AutomaticSetupResourceUnsupported {
resource_type: resource_type.to_string(),
resource_id: resource_id.to_string(),
platform,
})
};
match stack.management() {
ManagementPermissions::Override(_) => {
return Err(unsupported(
"management permission override",
"permissions.management",
));
}
ManagementPermissions::Extend(profile) if platform == Platform::Aws => {
if let Some(scope) = profile.0.keys().find(|scope| scope.as_str() != "*") {
return Err(unsupported("resource-scoped management permission", scope));
}
}
_ => {}
}
for (id, entry) in stack.resources() {
if let Some(resource_type) = automatic_setup_unsupported_resource(entry, platform) {
return Err(unsupported(&resource_type, id));
}
}
}
if management == SetupManagement::Automatic && platform == Platform::Azure {
for (id, entry) in stack.resources() {
if entry.config.downcast_ref::<Sandbox>().is_some() {
return Err(AlienError::new(ErrorData::SandboxCapabilityUnsupported {
capability: format!(
"automatic infrastructure management for sandbox '{id}'; use manual setup to create and import its sandbox group"
),
platform: platform.to_string(),
}));
}
}
}
Ok(())
}
fn automatic_setup_unsupported_resource(
entry: &ResourceEntry,
platform: Platform,
) -> Option<String> {
if entry.config.downcast_ref::<Key>().is_some() {
return Some("key".to_string());
}
let resource_type = entry.config.resource_type();
if matches!(
resource_type.as_ref(),
"email" | "experimental/aws-opensearch" | "kubernetes-cluster"
) {
return Some(resource_type.as_ref().to_string());
}
if platform == Platform::Azure
&& entry
.config
.downcast_ref::<Storage>()
.is_some_and(|storage| !storage.cors_allowed_origins.is_empty())
{
return Some("storage with CORS origins".to_string());
}
None
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SetupConfigurationProfile {
Standard,
Advanced,
}
pub fn emits_package_scaffolding(
entry: &ResourceEntry,
management: SetupManagement,
platform: Platform,
) -> bool {
let resource_type = entry.config.resource_type();
if management == SetupManagement::Automatic {
is_automatic_setup_package_resource_type(resource_type.as_ref(), platform)
} else {
ownership_policy_for_resource_type(resource_type.as_ref())
.emits_setup_scaffolding(entry.lifecycle)
}
}
pub fn is_automatic_setup_package_resource_type(resource_type: &str, platform: Platform) -> bool {
matches!(
resource_type,
"remote-stack-management" | "vault" | "azure_resource_group" | "service_activation"
) || (platform == Platform::Azure
&& matches!(resource_type, "service-account" | "resource-access"))
}
pub fn removed_by_runtime_cleanup(
resource_type: &str,
lifecycle: ResourceLifecycle,
management: SetupManagement,
platform: Platform,
) -> bool {
if management == SetupManagement::Automatic {
return !is_automatic_setup_package_resource_type(resource_type, platform);
}
lifecycle == ResourceLifecycle::Live
|| ownership_policy_for_resource_type(resource_type).has_runtime_cleanup_before_teardown()
}
#[cfg(test)]
mod cors_tests {
use super::*;
#[test]
fn automatic_cors_requires_an_implementing_controller() {
let stack = Stack::new("browser-storage".into())
.add(
Storage::new("assets".into())
.cors_allowed_origins(vec!["https://app.example.com".into()])
.build(),
ResourceLifecycle::Live,
)
.build();
for platform in [Platform::Aws, Platform::Gcp] {
validate_automatic_setup_resources(&stack, platform, SetupManagement::Automatic)
.unwrap();
}
let error =
validate_automatic_setup_resources(&stack, Platform::Azure, SetupManagement::Automatic)
.unwrap_err();
assert!(matches!(
error.error,
Some(ErrorData::AutomaticSetupResourceUnsupported { .. })
));
}
}