running_process/broker/server/
version_allow_list.rs1use std::cmp::Ordering;
4
5use crate::broker::protocol::ServiceDefinition;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum VersionPolicyBlock {
10 BelowMinVersion,
12 OutsideAllowList,
14}
15
16pub fn check_version_allowed(
18 wanted_version: &str,
19 service: &ServiceDefinition,
20) -> Result<(), VersionPolicyBlock> {
21 if !service.min_version.is_empty()
22 && compare_semver_core(wanted_version, &service.min_version) == Some(Ordering::Less)
23 {
24 return Err(VersionPolicyBlock::BelowMinVersion);
25 }
26 if !service.version_allow_list.is_empty()
27 && !service
28 .version_allow_list
29 .iter()
30 .any(|allowed| allowed == wanted_version)
31 {
32 return Err(VersionPolicyBlock::OutsideAllowList);
33 }
34 Ok(())
35}
36
37fn compare_semver_core(left: &str, right: &str) -> Option<Ordering> {
38 let left = parse_semver_core(left)?;
39 let right = parse_semver_core(right)?;
40 Some(left.cmp(&right))
41}
42
43fn parse_semver_core(version: &str) -> Option<[u64; 3]> {
44 let core = version.split_once('-').map_or(version, |(core, _)| core);
45 let mut parts = core.split('.');
46 let major = parts.next()?.parse().ok()?;
47 let minor = parts.next()?.parse().ok()?;
48 let patch = parts.next()?.parse().ok()?;
49 if parts.next().is_some() {
50 return None;
51 }
52 Some([major, minor, patch])
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 fn service(min_version: &str, allow: &[&str]) -> ServiceDefinition {
60 ServiceDefinition {
61 service_name: "zccache".into(),
62 binary_path: "/usr/bin/zccache".into(),
63 isolation: 0,
64 explicit_instance: String::new(),
65 per_version_binary_dir: String::new(),
66 min_version: min_version.into(),
67 version_allow_list: allow.iter().map(|v| (*v).into()).collect(),
68 labels: Default::default(),
69 }
70 }
71
72 #[test]
73 fn blocks_version_below_floor() {
74 assert_eq!(
75 check_version_allowed("1.9.9", &service("1.10.0", &[])),
76 Err(VersionPolicyBlock::BelowMinVersion)
77 );
78 }
79
80 #[test]
81 fn allows_version_at_floor() {
82 assert_eq!(
83 check_version_allowed("1.10.0", &service("1.10.0", &[])),
84 Ok(())
85 );
86 }
87
88 #[test]
89 fn blocks_version_outside_allow_list() {
90 assert_eq!(
91 check_version_allowed("1.12.0", &service("1.10.0", &["1.11.20"])),
92 Err(VersionPolicyBlock::OutsideAllowList)
93 );
94 }
95
96 #[test]
97 fn allows_version_inside_allow_list() {
98 assert_eq!(
99 check_version_allowed("1.11.20", &service("1.10.0", &["1.11.20"])),
100 Ok(())
101 );
102 }
103}