bamboo_engine/session_app/
supervisor.rs1use std::{collections::BTreeSet, io, sync::Arc};
4
5use bamboo_domain::{
6 ProjectId, Storage, SupervisorBootstrapReceipt, SupervisorLinkObservation,
7 SupervisorManagementMutation, SupervisorManagementReceipt, SupervisorManagementRequest,
8 SupervisorReference, SupervisorScopeObservation,
9};
10
11#[derive(Clone)]
15pub struct SupervisorSessionService {
16 storage: Arc<dyn Storage>,
17}
18
19impl SupervisorSessionService {
20 pub fn new(storage: Arc<dyn Storage>) -> Self {
21 Self { storage }
22 }
23
24 pub async fn get_or_create_default(
26 &self,
27 initial_model: &str,
28 ) -> io::Result<SupervisorBootstrapReceipt> {
29 self.storage
30 .get_or_create_default_supervisor(initial_model)
31 .await
32 }
33
34 pub async fn inspect_scope(
36 &self,
37 supervisor: &SupervisorReference,
38 ) -> io::Result<SupervisorScopeObservation> {
39 self.storage.inspect_supervisor_scope(supervisor).await
40 }
41
42 pub async fn configure_project_scope(
45 &self,
46 supervisor: &SupervisorReference,
47 expected_state_revision: u64,
48 allowed_projects: BTreeSet<ProjectId>,
49 ) -> io::Result<SupervisorManagementReceipt> {
50 self.mutate(
51 supervisor,
52 expected_state_revision,
53 SupervisorManagementMutation::ConfigureProjectScope { allowed_projects },
54 )
55 .await
56 }
57
58 pub async fn attach(
61 &self,
62 supervisor: &SupervisorReference,
63 expected_state_revision: u64,
64 target_session_id: &str,
65 ) -> io::Result<SupervisorManagementReceipt> {
66 self.mutate(
67 supervisor,
68 expected_state_revision,
69 SupervisorManagementMutation::Attach {
70 target_session_id: target_session_id.to_string(),
71 },
72 )
73 .await
74 }
75
76 pub async fn detach(
78 &self,
79 supervisor: &SupervisorReference,
80 expected_state_revision: u64,
81 target_session_id: &str,
82 ) -> io::Result<SupervisorManagementReceipt> {
83 self.mutate(
84 supervisor,
85 expected_state_revision,
86 SupervisorManagementMutation::Detach {
87 target_session_id: target_session_id.to_string(),
88 },
89 )
90 .await
91 }
92
93 pub async fn inspect_link(
96 &self,
97 supervisor: &SupervisorReference,
98 target_session_id: &str,
99 ) -> io::Result<SupervisorLinkObservation> {
100 self.storage
101 .inspect_supervisor_link(supervisor, target_session_id)
102 .await
103 }
104
105 async fn mutate(
106 &self,
107 supervisor: &SupervisorReference,
108 expected_state_revision: u64,
109 mutation: SupervisorManagementMutation,
110 ) -> io::Result<SupervisorManagementReceipt> {
111 self.storage
112 .mutate_supervisor_management(&SupervisorManagementRequest {
113 supervisor: supervisor.clone(),
114 expected_state_revision,
115 mutation,
116 })
117 .await
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use bamboo_domain::Session;
125
126 struct UnsupportedStore;
127
128 #[async_trait::async_trait]
129 impl Storage for UnsupportedStore {
130 async fn save_session(&self, _: &Session) -> io::Result<()> {
131 panic!("bootstrap must not fall back to ordinary save")
132 }
133 async fn load_session(&self, _: &str) -> io::Result<Option<Session>> {
134 panic!("authority must not fall back to ordinary load")
135 }
136 async fn delete_session(&self, _: &str) -> io::Result<bool> {
137 panic!("bootstrap must not delete sessions")
138 }
139 }
140
141 #[tokio::test]
142 async fn unsupported_authority_ports_fail_without_ordinary_fallback() {
143 let storage: Arc<dyn Storage> = Arc::new(UnsupportedStore);
144 let service = SupervisorSessionService::new(storage.clone());
145 assert_eq!(
146 service
147 .get_or_create_default("model")
148 .await
149 .unwrap_err()
150 .kind(),
151 io::ErrorKind::Unsupported
152 );
153 assert_eq!(
154 storage
155 .load_root_authority("root")
156 .await
157 .unwrap_err()
158 .kind(),
159 io::ErrorKind::Unsupported
160 );
161 let supervisor = SupervisorReference {
162 session_id: bamboo_domain::DEFAULT_SUPERVISOR_SESSION_ID.into(),
163 incarnation_id: uuid::Uuid::new_v4(),
164 };
165 assert_eq!(
166 service.inspect_scope(&supervisor).await.unwrap_err().kind(),
167 io::ErrorKind::Unsupported
168 );
169 assert_eq!(
170 service
171 .configure_project_scope(&supervisor, 0, BTreeSet::new())
172 .await
173 .unwrap_err()
174 .kind(),
175 io::ErrorKind::Unsupported
176 );
177 assert_eq!(
178 service
179 .attach(&supervisor, 0, "target")
180 .await
181 .unwrap_err()
182 .kind(),
183 io::ErrorKind::Unsupported
184 );
185 assert_eq!(
186 service
187 .detach(&supervisor, 0, "target")
188 .await
189 .unwrap_err()
190 .kind(),
191 io::ErrorKind::Unsupported
192 );
193 assert_eq!(
194 service
195 .inspect_link(&supervisor, "target")
196 .await
197 .unwrap_err()
198 .kind(),
199 io::ErrorKind::Unsupported
200 );
201 }
202}