Skip to main content

alien_core/
ownership.rs

1use crate::ResourceLifecycle;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub struct ResourceOwnershipPolicy {
5    default_lifecycle: ResourceLifecycle,
6    allow_frozen: bool,
7    allow_live: bool,
8    emit_in_setup: bool,
9    requires_management_permissions: bool,
10    runtime_cleanup_before_teardown: bool,
11}
12
13impl ResourceOwnershipPolicy {
14    pub const fn new(
15        default_lifecycle: ResourceLifecycle,
16        allow_frozen: bool,
17        allow_live: bool,
18        emit_in_setup: bool,
19        requires_management_permissions: bool,
20        runtime_cleanup_before_teardown: bool,
21    ) -> Self {
22        Self {
23            default_lifecycle,
24            allow_frozen,
25            allow_live,
26            emit_in_setup,
27            requires_management_permissions,
28            runtime_cleanup_before_teardown,
29        }
30    }
31
32    pub const fn default_lifecycle(self) -> ResourceLifecycle {
33        self.default_lifecycle
34    }
35
36    pub const fn allows_frozen(self) -> bool {
37        self.allow_frozen
38    }
39
40    pub const fn allows_live(self) -> bool {
41        self.allow_live
42    }
43
44    pub const fn allows_lifecycle(self, lifecycle: ResourceLifecycle) -> bool {
45        match lifecycle {
46            ResourceLifecycle::Frozen => self.allow_frozen,
47            ResourceLifecycle::Live => self.allow_live,
48        }
49    }
50
51    pub const fn should_emit_in_setup(self, lifecycle: ResourceLifecycle) -> bool {
52        self.emit_in_setup && matches!(lifecycle, ResourceLifecycle::Frozen)
53    }
54
55    pub const fn requires_management_permissions(self) -> bool {
56        self.requires_management_permissions
57    }
58
59    pub const fn has_runtime_cleanup_before_teardown(self) -> bool {
60        self.runtime_cleanup_before_teardown
61    }
62
63    pub fn allowed_lifecycles(self) -> &'static str {
64        match (self.allow_frozen, self.allow_live) {
65            (true, true) => "Frozen or Live",
66            (true, false) => "Frozen",
67            (false, true) => "Live",
68            (false, false) => "no lifecycle",
69        }
70    }
71}
72
73pub fn ownership_policy_for_resource_type(resource_type: &str) -> ResourceOwnershipPolicy {
74    match resource_type {
75        "function" | "container-cluster" => removed_resource_type(),
76        "worker" | "daemon" | "container" => live_only(),
77        "compute-cluster" => frozen_with_runtime_cleanup(),
78        "artifact-registry" => frozen_with_management(),
79        "build"
80        | "network"
81        | "remote-stack-management"
82        | "service-account"
83        | "service_activation"
84        | "service-activation"
85        | "azure_resource_group"
86        | "azure-resource-group"
87        | "azure_storage_account"
88        | "azure-storage-account"
89        | "azure_container_apps_environment"
90        | "azure-container-apps-environment"
91        | "azure_service_bus_namespace"
92        | "azure-service-bus-namespace" => frozen_only(),
93        "storage" | "queue" | "kv" | "vault" => user_choice(),
94        _ => user_choice(),
95    }
96}
97
98const fn frozen_only() -> ResourceOwnershipPolicy {
99    ResourceOwnershipPolicy::new(ResourceLifecycle::Frozen, true, false, true, false, false)
100}
101
102const fn frozen_with_management() -> ResourceOwnershipPolicy {
103    ResourceOwnershipPolicy::new(ResourceLifecycle::Frozen, true, false, true, true, false)
104}
105
106const fn frozen_with_runtime_cleanup() -> ResourceOwnershipPolicy {
107    ResourceOwnershipPolicy::new(ResourceLifecycle::Frozen, true, false, true, true, true)
108}
109
110const fn live_only() -> ResourceOwnershipPolicy {
111    ResourceOwnershipPolicy::new(ResourceLifecycle::Live, false, true, false, false, false)
112}
113
114const fn removed_resource_type() -> ResourceOwnershipPolicy {
115    ResourceOwnershipPolicy::new(ResourceLifecycle::Live, false, false, false, false, false)
116}
117
118const fn user_choice() -> ResourceOwnershipPolicy {
119    ResourceOwnershipPolicy::new(ResourceLifecycle::Frozen, true, true, true, false, false)
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn workload_resources_are_live_only() {
128        for resource_type in ["worker", "daemon", "container"] {
129            let policy = ownership_policy_for_resource_type(resource_type);
130            assert_eq!(policy.default_lifecycle(), ResourceLifecycle::Live);
131            assert!(!policy.allows_lifecycle(ResourceLifecycle::Frozen));
132            assert!(policy.allows_lifecycle(ResourceLifecycle::Live));
133            assert!(!policy.should_emit_in_setup(ResourceLifecycle::Live));
134        }
135    }
136
137    #[test]
138    fn compute_cluster_is_frozen_with_runtime_cleanup() {
139        let policy = ownership_policy_for_resource_type("compute-cluster");
140        assert_eq!(policy.default_lifecycle(), ResourceLifecycle::Frozen);
141        assert!(policy.allows_lifecycle(ResourceLifecycle::Frozen));
142        assert!(!policy.allows_lifecycle(ResourceLifecycle::Live));
143        assert!(policy.should_emit_in_setup(ResourceLifecycle::Frozen));
144        assert!(policy.requires_management_permissions());
145        assert!(policy.has_runtime_cleanup_before_teardown());
146    }
147
148    #[test]
149    fn artifact_registry_is_frozen_with_management() {
150        let policy = ownership_policy_for_resource_type("artifact-registry");
151        assert_eq!(policy.default_lifecycle(), ResourceLifecycle::Frozen);
152        assert!(policy.allows_lifecycle(ResourceLifecycle::Frozen));
153        assert!(!policy.allows_lifecycle(ResourceLifecycle::Live));
154        assert!(policy.should_emit_in_setup(ResourceLifecycle::Frozen));
155        assert!(policy.requires_management_permissions());
156        assert!(!policy.has_runtime_cleanup_before_teardown());
157    }
158
159    #[test]
160    fn removed_resource_type_tags_are_not_normal_policy_entries() {
161        for resource_type in ["function", "container-cluster"] {
162            let policy = ownership_policy_for_resource_type(resource_type);
163            assert!(!policy.allows_lifecycle(ResourceLifecycle::Frozen));
164            assert!(!policy.allows_lifecycle(ResourceLifecycle::Live));
165            assert!(!policy.requires_management_permissions());
166            assert!(!policy.has_runtime_cleanup_before_teardown());
167        }
168    }
169
170    #[test]
171    fn data_resources_can_be_frozen_or_live() {
172        for resource_type in ["storage", "queue", "kv", "vault"] {
173            let policy = ownership_policy_for_resource_type(resource_type);
174            assert_eq!(policy.default_lifecycle(), ResourceLifecycle::Frozen);
175            assert!(policy.allows_lifecycle(ResourceLifecycle::Frozen));
176            assert!(policy.allows_lifecycle(ResourceLifecycle::Live));
177            assert!(policy.should_emit_in_setup(ResourceLifecycle::Frozen));
178            assert!(!policy.should_emit_in_setup(ResourceLifecycle::Live));
179        }
180    }
181
182    #[test]
183    fn setup_resources_are_frozen_only() {
184        for resource_type in [
185            "build",
186            "network",
187            "remote-stack-management",
188            "service-account",
189            "service_activation",
190            "azure_resource_group",
191            "azure_storage_account",
192            "azure_container_apps_environment",
193            "azure_service_bus_namespace",
194        ] {
195            let policy = ownership_policy_for_resource_type(resource_type);
196            assert!(policy.allows_lifecycle(ResourceLifecycle::Frozen));
197            assert!(!policy.allows_lifecycle(ResourceLifecycle::Live));
198            assert!(policy.should_emit_in_setup(ResourceLifecycle::Frozen));
199        }
200    }
201}