edgehog_device_runtime_containers/docker/
mod.rs1use std::{
22 borrow::{Borrow, BorrowMut},
23 ops::{Deref, DerefMut},
24};
25
26use bollard::{models::EventMessage, query_parameters::EventsOptionsBuilder};
27use futures::{Stream, TryStreamExt};
28
29pub(crate) use crate::client::*;
30use crate::error::DockerError;
31
32pub(crate) mod container;
33pub(crate) mod image;
34pub(crate) mod network;
35pub(crate) mod volume;
36
37#[derive(Debug, Clone)]
39pub struct Docker {
40 pub(crate) client: Client,
41}
42
43impl Docker {
44 cfg_if::cfg_if! {
45 if #[cfg(feature = "__mock")] {
46 #[cfg(feature = "__mock")]
48 pub async fn connect() -> Result<Self, DockerError> {
49 let client = Client::new();
50
51 Ok(Self { client })
52 }
53 } else {
54 pub async fn connect() -> Result<Self, DockerError> {
56 let client = Client::connect_with_local_defaults()
57 .map_err(DockerError::Connection)?
58 .negotiate_version()
59 .await
60 .map_err(DockerError::Version)?;
61
62 Ok(Self { client })
63 }
64 }
65 }
66
67 pub async fn ping(&self) -> Result<(), DockerError> {
69 self.client.ping().await.map_err(DockerError::Ping)?;
71
72 Ok(())
73 }
74
75 pub fn events(&self) -> impl Stream<Item = Result<EventMessage, DockerError>> {
77 let types = ["container", "image", "volume", "network"]
78 .map(str::to_string)
79 .to_vec();
80
81 let filters = [("type".to_string(), types)].into_iter().collect();
82
83 let options = EventsOptionsBuilder::new().filters(&filters).build();
84
85 self.client
87 .events(Some(options))
88 .map_err(DockerError::Events)
89 }
90}
91
92impl From<Client> for Docker {
93 fn from(client: Client) -> Self {
94 Self { client }
95 }
96}
97
98impl Borrow<Client> for Docker {
99 fn borrow(&self) -> &Client {
100 &self.client
101 }
102}
103
104impl BorrowMut<Client> for Docker {
105 fn borrow_mut(&mut self) -> &mut Client {
106 &mut self.client
107 }
108}
109
110impl Deref for Docker {
111 type Target = Client;
112
113 fn deref(&self) -> &Self::Target {
114 &self.client
115 }
116}
117
118impl DerefMut for Docker {
119 fn deref_mut(&mut self) -> &mut Self::Target {
120 &mut self.client
121 }
122}
123
124#[cfg(test)]
125pub(crate) mod tests {
126 use super::*;
127
128 #[macro_export]
131 macro_rules! docker_mock {
132 ($mock:expr) => {{
133 #[cfg(feature = "__mock")]
134 let docker: $crate::Docker = {
135 let mock: $crate::client::Client = $mock;
136
137 Docker::from(mock)
138 };
139
140 #[cfg(not(feature = "__mock"))]
141 let docker: $crate::docker::Docker = $crate::docker::Docker::connect().unwrap();
142
143 docker
144 }};
145 ($default:expr, $mock:expr) => {{
146 #[cfg(feature = "__mock")]
147 let client: $crate::client::Client = $mock;
148
149 #[cfg(not(feature = "__mock"))]
150 let client: $crate::client::Client = $default;
151
152 $crate::Docker::from(client)
153 }};
154 }
155
156 #[cfg(feature = "__mock")]
157 pub(crate) fn not_found_response() -> bollard::errors::Error {
158 bollard::errors::Error::DockerResponseServerError {
159 status_code: 404,
160 message: "not found".to_string(),
161 }
162 }
163
164 #[tokio::test]
165 async fn test_ping_and_macro() {
166 let docker = docker_mock!(Client::connect_with_local_defaults().unwrap(), {
167 let mut mock = Client::new();
168
169 mock.expect_ping().returning(|| Ok(Default::default()));
170
171 mock
172 });
173
174 let res = docker.ping().await;
175
176 assert!(res.is_ok(), "Ping failed: {res:?}");
177 }
178}