Skip to main content

bucketwarden_server/
placement_domain_support.rs

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}