crates/flotilla-controllers/src/reconcilers/environment.rs
Line | Count | Source |
1 | | use std::sync::Arc; |
2 | | |
3 | | use async_trait::async_trait; |
4 | | use flotilla_resources::{ |
5 | | controller::{ReconcileOutcome, Reconciler}, |
6 | | DockerEnvironmentSpec, Environment, EnvironmentPhase, EnvironmentStatusPatch, ResourceError, ResourceObject, |
7 | | }; |
8 | | |
9 | | #[async_trait] |
10 | | pub trait DockerEnvironmentRuntime: Send + Sync { |
11 | | async fn provision(&self, name: &str, spec: &DockerEnvironmentSpec) -> Result<String, String>; |
12 | | async fn destroy(&self, container_id: &str) -> Result<(), String>; |
13 | | } |
14 | | |
15 | | pub struct EnvironmentReconciler<R> { |
16 | | docker: Arc<R>, |
17 | | } |
18 | | |
19 | | impl<R> EnvironmentReconciler<R> { |
20 | 7 | pub fn new(docker: Arc<R>) -> Self { |
21 | 7 | Self { docker } |
22 | 7 | } |
23 | | } |
24 | | |
25 | | pub enum EnvironmentDeps { |
26 | | None, |
27 | | Ready { docker_container_id: Option<String> }, |
28 | | Failed(String), |
29 | | } |
30 | | |
31 | | impl<R> Reconciler for EnvironmentReconciler<R> |
32 | | where |
33 | | R: DockerEnvironmentRuntime + 'static, |
34 | | { |
35 | | type Resource = Environment; |
36 | | type Dependencies = EnvironmentDeps; |
37 | | |
38 | 14 | async fn fetch_dependencies(&self, obj: &ResourceObject<Self::Resource>) -> Result<Self::Dependencies, ResourceError> { |
39 | 14 | match obj.status.as_ref().map(|status| status.phase).unwrap_or(EnvironmentPhase::Pending) { |
40 | | EnvironmentPhase::Pending => { |
41 | 5 | if let Some(spec1 ) = &obj.spec.docker { |
42 | 1 | match self.docker.provision(&obj.metadata.name, spec).await { |
43 | 1 | Ok(container_id) => Ok(EnvironmentDeps::Ready { docker_container_id: Some(container_id) }), |
44 | 0 | Err(err) => Ok(EnvironmentDeps::Failed(err)), |
45 | | } |
46 | | } else { |
47 | 4 | Ok(EnvironmentDeps::None) |
48 | | } |
49 | | } |
50 | 9 | _ => Ok(EnvironmentDeps::None), |
51 | | } |
52 | 14 | } |
53 | | |
54 | 14 | fn reconcile( |
55 | 14 | &self, |
56 | 14 | obj: &ResourceObject<Self::Resource>, |
57 | 14 | deps: &Self::Dependencies, |
58 | 14 | _now: chrono::DateTime<chrono::Utc>, |
59 | 14 | ) -> ReconcileOutcome<Self::Resource> { |
60 | 14 | let patch = match obj.status.as_ref().map(|status| status.phase).unwrap_or(EnvironmentPhase::Pending) { |
61 | 5 | EnvironmentPhase::Pending if obj.spec.host_direct.is_some()4 => { |
62 | 4 | Some(EnvironmentStatusPatch::MarkReady { docker_container_id: None }) |
63 | | } |
64 | 1 | EnvironmentPhase::Pending => match deps { |
65 | 1 | EnvironmentDeps::Ready { docker_container_id } => { |
66 | 1 | Some(EnvironmentStatusPatch::MarkReady { docker_container_id: docker_container_id.clone() }) |
67 | | } |
68 | 0 | EnvironmentDeps::Failed(message) => Some(EnvironmentStatusPatch::MarkFailed { message: message.clone() }), |
69 | 0 | EnvironmentDeps::None => None, |
70 | | }, |
71 | 9 | _ => None, |
72 | | }; |
73 | | |
74 | 14 | ReconcileOutcome::new(patch) |
75 | 14 | } |
76 | | |
77 | 0 | async fn run_finalizer(&self, obj: &ResourceObject<Self::Resource>) -> Result<(), ResourceError> { |
78 | 0 | if let Some(container_id) = obj.status.as_ref().and_then(|status| status.docker_container_id.as_deref()) { |
79 | 0 | self.docker.destroy(container_id).await.map_err(ResourceError::other)?; |
80 | 0 | } |
81 | 0 | Ok(()) |
82 | 0 | } |
83 | | |
84 | 20 | fn finalizer_name(&self) -> Option<&'static str> { |
85 | 20 | Some("flotilla.work/environment-teardown") |
86 | 20 | } |
87 | | } |