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"
93 | "email" => frozen_only(),
96 "experimental/aws-opensearch" => frozen_only(),
99 "storage" | "queue" | "kv" | "vault" | "postgres" => user_choice(),
100 _ => user_choice(),
101 }
102}
103
104const fn frozen_only() -> ResourceOwnershipPolicy {
105 ResourceOwnershipPolicy::new(ResourceLifecycle::Frozen, true, false, true, false, false)
106}
107
108const fn frozen_with_management() -> ResourceOwnershipPolicy {
109 ResourceOwnershipPolicy::new(ResourceLifecycle::Frozen, true, false, true, true, false)
110}
111
112const fn frozen_with_runtime_cleanup() -> ResourceOwnershipPolicy {
113 ResourceOwnershipPolicy::new(ResourceLifecycle::Frozen, true, false, true, true, true)
114}
115
116const fn live_only() -> ResourceOwnershipPolicy {
117 ResourceOwnershipPolicy::new(ResourceLifecycle::Live, false, true, false, false, false)
118}
119
120const fn removed_resource_type() -> ResourceOwnershipPolicy {
121 ResourceOwnershipPolicy::new(ResourceLifecycle::Live, false, false, false, false, false)
122}
123
124const fn user_choice() -> ResourceOwnershipPolicy {
125 ResourceOwnershipPolicy::new(ResourceLifecycle::Frozen, true, true, true, false, false)
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn workload_resources_are_live_only() {
134 for resource_type in ["worker", "daemon", "container"] {
135 let policy = ownership_policy_for_resource_type(resource_type);
136 assert_eq!(policy.default_lifecycle(), ResourceLifecycle::Live);
137 assert!(!policy.allows_lifecycle(ResourceLifecycle::Frozen));
138 assert!(policy.allows_lifecycle(ResourceLifecycle::Live));
139 assert!(!policy.should_emit_in_setup(ResourceLifecycle::Live));
140 }
141 }
142
143 #[test]
144 fn compute_cluster_is_frozen_with_runtime_cleanup() {
145 let policy = ownership_policy_for_resource_type("compute-cluster");
146 assert_eq!(policy.default_lifecycle(), ResourceLifecycle::Frozen);
147 assert!(policy.allows_lifecycle(ResourceLifecycle::Frozen));
148 assert!(!policy.allows_lifecycle(ResourceLifecycle::Live));
149 assert!(policy.should_emit_in_setup(ResourceLifecycle::Frozen));
150 assert!(policy.requires_management_permissions());
151 assert!(policy.has_runtime_cleanup_before_teardown());
152 }
153
154 #[test]
155 fn artifact_registry_is_frozen_with_management() {
156 let policy = ownership_policy_for_resource_type("artifact-registry");
157 assert_eq!(policy.default_lifecycle(), ResourceLifecycle::Frozen);
158 assert!(policy.allows_lifecycle(ResourceLifecycle::Frozen));
159 assert!(!policy.allows_lifecycle(ResourceLifecycle::Live));
160 assert!(policy.should_emit_in_setup(ResourceLifecycle::Frozen));
161 assert!(policy.requires_management_permissions());
162 assert!(!policy.has_runtime_cleanup_before_teardown());
163 }
164
165 #[test]
166 fn removed_resource_type_tags_are_not_normal_policy_entries() {
167 for resource_type in ["function", "container-cluster"] {
168 let policy = ownership_policy_for_resource_type(resource_type);
169 assert!(!policy.allows_lifecycle(ResourceLifecycle::Frozen));
170 assert!(!policy.allows_lifecycle(ResourceLifecycle::Live));
171 assert!(!policy.requires_management_permissions());
172 assert!(!policy.has_runtime_cleanup_before_teardown());
173 }
174 }
175
176 #[test]
177 fn data_resources_can_be_frozen_or_live() {
178 for resource_type in ["storage", "queue", "kv", "vault", "postgres"] {
179 let policy = ownership_policy_for_resource_type(resource_type);
180 assert_eq!(policy.default_lifecycle(), ResourceLifecycle::Frozen);
181 assert!(policy.allows_lifecycle(ResourceLifecycle::Frozen));
182 assert!(policy.allows_lifecycle(ResourceLifecycle::Live));
183 assert!(policy.should_emit_in_setup(ResourceLifecycle::Frozen));
184 assert!(!policy.should_emit_in_setup(ResourceLifecycle::Live));
185 }
186 }
187
188 #[test]
189 fn experimental_aws_opensearch_is_frozen_only() {
190 let policy = ownership_policy_for_resource_type("experimental/aws-opensearch");
191 assert_eq!(policy.default_lifecycle(), ResourceLifecycle::Frozen);
192 assert!(policy.allows_lifecycle(ResourceLifecycle::Frozen));
193 assert!(!policy.allows_lifecycle(ResourceLifecycle::Live));
194 assert!(policy.should_emit_in_setup(ResourceLifecycle::Frozen));
195 assert!(!policy.requires_management_permissions());
196 }
197
198 #[test]
199 fn setup_resources_are_frozen_only() {
200 for resource_type in [
201 "build",
202 "network",
203 "remote-stack-management",
204 "service-account",
205 "service_activation",
206 "azure_resource_group",
207 "azure_storage_account",
208 "azure_container_apps_environment",
209 "azure_service_bus_namespace",
210 "email",
211 ] {
212 let policy = ownership_policy_for_resource_type(resource_type);
213 assert!(policy.allows_lifecycle(ResourceLifecycle::Frozen));
214 assert!(!policy.allows_lifecycle(ResourceLifecycle::Live));
215 assert!(policy.should_emit_in_setup(ResourceLifecycle::Frozen));
216 }
217 }
218}