1use super::*;
2
3pub const PLACEMENT_DOMAIN_LOCAL_DISK: &str = "local-disk";
4pub const PLACEMENT_DOMAIN_LOCAL_NODE: &str = "local-node";
5pub const PLACEMENT_DOMAIN_RACK: &str = "rack";
6pub const PLACEMENT_DOMAIN_ZONE: &str = "zone";
7pub const PLACEMENT_DOMAIN_REGION: &str = "region";
8
9const PLACEMENT_DOMAIN_CAPABILITIES: &[&str] = &[
10 "disk-placement",
11 "node-placement",
12 "rack-placement",
13 "zone-placement",
14 "region-placement",
15 "failure-domain",
16 "affinity",
17 "anti-affinity",
18 "native-support-state",
19 "semantic-parity",
20 "configuration-admin-surface",
21 "security-governance-impact",
22 "observability-evidence",
23 "failure-mode-behavior",
24 "validation-test-coverage",
25 "product-specific-caveats",
26];
27
28const PLACEMENT_DOMAIN_CAVEATS: &[&str] = &[
29 "BucketWarden supports deterministic local disk and local node placement metadata for runtime proof.",
30 "Rack, zone, and region placement are tracked but fail closed outside the current single-runtime boundary.",
31 "Affinity and anti-affinity are validated as placement policy metadata before placement decisions are accepted.",
32 "Placement-domain proof is local runtime behavior and does not claim Kubernetes, cloud-region, rack-aware, or multi-node scheduling semantics.",
33];
34
35const PLACEMENT_DOMAIN_FAILURE_MODES: &[&str] = &[
36 "unsupported-domain-rejected",
37 "invalid-affinity-policy-rejected",
38 "conflicting-affinity-anti-affinity-rejected",
39 "out-of-bound-region-zone-rack-rejected",
40];
41
42#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
43pub struct PlacementDomainSupportEntry {
44 pub domain: &'static str,
45 pub native_support: bool,
46 pub semantic_parity: &'static str,
47 pub failure_domain: &'static str,
48 pub affinity_behavior: &'static str,
49 pub failure_mode: &'static str,
50 pub caveat: &'static str,
51}
52
53#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
54pub struct PlacementDomainSupportReport {
55 pub active_profile: &'static str,
56 pub supported_domains: Vec<&'static str>,
57 pub unsupported_domains: Vec<&'static str>,
58 pub default_failure_domain: &'static str,
59 pub affinity_policy: &'static str,
60 pub capabilities: Vec<&'static str>,
61 pub failure_modes: Vec<&'static str>,
62 pub caveats: Vec<&'static str>,
63 pub entries: Vec<PlacementDomainSupportEntry>,
64}
65
66#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
67pub struct PlacementPolicy {
68 pub domain: String,
69 pub disk_id: Option<String>,
70 pub node_id: Option<String>,
71 pub rack_id: Option<String>,
72 pub zone_id: Option<String>,
73 pub region_id: Option<String>,
74 pub affinity_group: Option<String>,
75 pub anti_affinity_groups: Vec<String>,
76}
77
78#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
79pub struct PlacementDecision {
80 pub domain: String,
81 pub failure_domain: String,
82 pub disk_id: Option<String>,
83 pub node_id: Option<String>,
84 pub affinity_group: Option<String>,
85 pub anti_affinity_groups: Vec<String>,
86}
87
88impl BucketWarden {
89 pub fn placement_domain_support_report(&self) -> PlacementDomainSupportReport {
90 PlacementDomainSupportReport {
91 active_profile: "local-single-node",
92 supported_domains: vec![PLACEMENT_DOMAIN_LOCAL_DISK, PLACEMENT_DOMAIN_LOCAL_NODE],
93 unsupported_domains: vec![
94 PLACEMENT_DOMAIN_RACK,
95 PLACEMENT_DOMAIN_ZONE,
96 PLACEMENT_DOMAIN_REGION,
97 ],
98 default_failure_domain: PLACEMENT_DOMAIN_LOCAL_NODE,
99 affinity_policy: "metadata-validated-local-placement",
100 capabilities: PLACEMENT_DOMAIN_CAPABILITIES.to_vec(),
101 failure_modes: PLACEMENT_DOMAIN_FAILURE_MODES.to_vec(),
102 caveats: PLACEMENT_DOMAIN_CAVEATS.to_vec(),
103 entries: vec![
104 PlacementDomainSupportEntry {
105 domain: PLACEMENT_DOMAIN_LOCAL_DISK,
106 native_support: true,
107 semantic_parity: "Object versions are assigned deterministic local disk placement metadata.",
108 failure_domain: "local disk within the active BucketWarden runtime.",
109 affinity_behavior: "Affinity labels are preserved and validated as local placement metadata.",
110 failure_mode: "Invalid local disk placement metadata is rejected.",
111 caveat: "Local disk placement does not claim RAID, JBOD, or cloud block-device semantics.",
112 },
113 PlacementDomainSupportEntry {
114 domain: PLACEMENT_DOMAIN_LOCAL_NODE,
115 native_support: true,
116 semantic_parity: "Object versions are assigned deterministic local node placement metadata.",
117 failure_domain: "single local node runtime boundary.",
118 affinity_behavior: "Anti-affinity conflicts are rejected before placement is accepted.",
119 failure_mode: "Conflicting affinity and anti-affinity policy is rejected.",
120 caveat: "Local node support does not claim cluster scheduling or multi-node availability.",
121 },
122 PlacementDomainSupportEntry {
123 domain: PLACEMENT_DOMAIN_RACK,
124 native_support: false,
125 semantic_parity: "No rack topology, rack IDs, or rack spread semantics are claimed.",
126 failure_domain: "rack placement is out of the current runtime boundary.",
127 affinity_behavior: "Rack affinity policy is rejected as unsupported.",
128 failure_mode: "Rack placement selection is rejected as unsupported.",
129 caveat: "Rack-aware placement needs inventory and scheduler integration before support.",
130 },
131 PlacementDomainSupportEntry {
132 domain: PLACEMENT_DOMAIN_ZONE,
133 native_support: false,
134 semantic_parity: "No availability-zone placement, zone spread, or zone failure semantics are claimed.",
135 failure_domain: "zone placement is out of the current runtime boundary.",
136 affinity_behavior: "Zone affinity policy is rejected as unsupported.",
137 failure_mode: "Zone placement selection is rejected as unsupported.",
138 caveat: "Zone-aware placement needs multi-zone topology and durability policy integration.",
139 },
140 PlacementDomainSupportEntry {
141 domain: PLACEMENT_DOMAIN_REGION,
142 native_support: false,
143 semantic_parity: "No cross-region placement or regional durability SLA semantics are claimed.",
144 failure_domain: "region placement is out of the current runtime boundary.",
145 affinity_behavior: "Region affinity policy is rejected as unsupported.",
146 failure_mode: "Region placement selection is rejected as unsupported.",
147 caveat: "Region placement belongs to a future distributed deployment boundary.",
148 },
149 ],
150 }
151 }
152
153 pub fn ensure_placement_domain_supported(&self, domain: &str) -> Result<(), RuntimeError> {
154 let report = self.placement_domain_support_report();
155 if report.supported_domains.contains(&domain) {
156 Ok(())
157 } else {
158 Err(RuntimeError::UnsupportedPlacementDomain(domain.to_string()))
159 }
160 }
161
162 pub fn evaluate_placement_policy(
163 &self,
164 policy: PlacementPolicy,
165 ) -> Result<PlacementDecision, RuntimeError> {
166 self.ensure_placement_domain_supported(&policy.domain)?;
167 if let Some(affinity_group) = &policy.affinity_group {
168 if policy
169 .anti_affinity_groups
170 .iter()
171 .any(|group| group == affinity_group)
172 {
173 return Err(RuntimeError::InvalidPlacementPolicy(
174 "affinity group cannot also be anti-affinity".to_string(),
175 ));
176 }
177 }
178 if matches!(policy.domain.as_str(), PLACEMENT_DOMAIN_LOCAL_DISK)
179 && policy.disk_id.as_deref().unwrap_or_default().is_empty()
180 {
181 return Err(RuntimeError::InvalidPlacementPolicy(
182 "local disk placement requires disk_id".to_string(),
183 ));
184 }
185 if matches!(policy.domain.as_str(), PLACEMENT_DOMAIN_LOCAL_NODE)
186 && policy.node_id.as_deref().unwrap_or_default().is_empty()
187 {
188 return Err(RuntimeError::InvalidPlacementPolicy(
189 "local node placement requires node_id".to_string(),
190 ));
191 }
192 Ok(PlacementDecision {
193 domain: policy.domain,
194 failure_domain: policy
195 .node_id
196 .clone()
197 .unwrap_or_else(|| "local-node".to_string()),
198 disk_id: policy.disk_id,
199 node_id: policy.node_id,
200 affinity_group: policy.affinity_group,
201 anti_affinity_groups: policy.anti_affinity_groups,
202 })
203 }
204}