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}