crankshaft_docker/
lib.rs

1//! A Docker client that uses [`bollard`].
2
3use std::path::PathBuf;
4
5use bollard::query_parameters::ListNodesOptions;
6use bollard::secret::ImageDeleteResponseItem;
7use bollard::secret::ImageSummary;
8
9pub mod container;
10pub mod images;
11pub mod service;
12
13use bollard::secret::Node;
14use bollard::secret::SystemInfo;
15use crankshaft_events::Event;
16use thiserror::Error;
17use tokio::sync::broadcast;
18
19pub use crate::container::Container;
20use crate::images::*;
21
22/// A global error within this crate.
23#[derive(Error, Debug)]
24pub enum Error {
25    /// An error from [`bollard`].
26    #[error(transparent)]
27    Docker(#[from] bollard::errors::Error),
28    /// A required value was missing for a builder field.
29    #[error("missing required builder field `{0}`")]
30    MissingBuilderField(&'static str),
31    /// An error from a message.
32    #[error("{0}")]
33    Message(String),
34}
35
36/// A [`Result`](std::result::Result) with an [`Error`](enum@Error);
37pub type Result<T> = std::result::Result<T, Error>;
38
39/// A Docker client.
40#[derive(Clone, Debug)]
41pub struct Docker(bollard::Docker);
42
43impl Docker {
44    /// Creates a new [`Docker`] with the specified [client](bollard::Docker).
45    pub fn new(client: bollard::Docker) -> Self {
46        Self(client)
47    }
48
49    /// Attempts to create a new [`Docker`] with the default socket connection.
50    pub fn with_socket_defaults() -> Result<Self> {
51        let client = bollard::Docker::connect_with_socket_defaults().map_err(Error::Docker)?;
52        Ok(Self::new(client))
53    }
54
55    /// Attempts to create a new [`Docker`] with the default HTTP connection.
56    pub fn with_http_defaults() -> Result<Self> {
57        let client = bollard::Docker::connect_with_http_defaults().map_err(Error::Docker)?;
58        Ok(Self::new(client))
59    }
60
61    /// Attempts to create a new [`Docker`] with the default connection details.
62    pub fn with_defaults() -> Result<Self> {
63        let client = bollard::Docker::connect_with_defaults().map_err(Error::Docker)?;
64        Ok(Self::new(client))
65    }
66
67    /// Gets a reference to the inner [`bollard::Docker`].
68    pub fn inner(&self) -> &bollard::Docker {
69        &self.0
70    }
71
72    //----------------------------------------------------------------------------------
73    // Images
74    //----------------------------------------------------------------------------------
75
76    /// Gets all of the images stored in the Docker daemon.
77    pub async fn list_images(&self) -> Result<Vec<ImageSummary>> {
78        list_images(self).await
79    }
80
81    /// Ensures that an image exists in the Docker daemon.
82    ///
83    /// If the image does not specify a tag, a default tag of `latest` will be
84    /// used.
85    ///
86    /// It does this by:
87    ///
88    /// * Confirming that the image already exists there, or
89    /// * Pulling the image from the remote repository.
90    pub async fn ensure_image(&self, image: impl Into<String>) -> Result<()> {
91        ensure_image(self, image).await
92    }
93
94    /// Removes an image from the Docker daemon.
95    pub async fn remove_image<T: AsRef<str>, U: AsRef<str>>(
96        &self,
97        name: T,
98        tag: U,
99    ) -> Result<impl IntoIterator<Item = ImageDeleteResponseItem> + use<T, U>> {
100        remove_image(self, name, tag).await
101    }
102
103    /// Removes all images from the Docker daemon.
104    pub async fn remove_all_images(&self) -> Result<Vec<ImageDeleteResponseItem>> {
105        remove_all_images(self).await
106    }
107
108    //----------------------------------------------------------------------------------
109    // Containers
110    //----------------------------------------------------------------------------------
111
112    /// Creates a container builder.
113    ///
114    /// This is the typical way you will create containers.
115    pub fn container_builder(&self) -> container::Builder {
116        container::Builder::new(self.0.clone())
117    }
118
119    /// Creates a container from a known id.
120    ///
121    /// You should typically use [`Self::container_builder()`] unless you
122    /// receive the container name externally from a user (say, on the command
123    /// line as an argument).
124    pub fn container_from_name(
125        &self,
126        id: impl Into<String>,
127        stdout: Option<PathBuf>,
128        stderr: Option<PathBuf>,
129    ) -> Container {
130        Container::new(self.0.clone(), id.into(), stdout, stderr)
131    }
132
133    //----------------------------------------------------------------------------------
134    // Nodes
135    //----------------------------------------------------------------------------------
136
137    /// Gets the nodes of the swarm.
138    ///
139    /// This method should only be called for a Docker daemon that has been
140    /// joined to a swarm.
141    pub async fn nodes(&self) -> Result<Vec<Node>> {
142        self.0
143            .list_nodes(None::<ListNodesOptions>)
144            .await
145            .map_err(Into::into)
146    }
147
148    //----------------------------------------------------------------------------------
149    // Services
150    //----------------------------------------------------------------------------------
151
152    /// Creates a service builder.
153    ///
154    /// This is the typical way you will create services.
155    pub fn service_builder(&self) -> service::Builder {
156        service::Builder::new(self.0.clone())
157    }
158
159    //----------------------------------------------------------------------------------
160    // System
161    //----------------------------------------------------------------------------------
162
163    /// Gets the system information.
164    pub async fn info(&self) -> Result<SystemInfo> {
165        self.0.info().await.map_err(Into::into)
166    }
167}
168
169/// Represents options for sending events.
170#[derive(Debug, Clone)]
171pub struct EventOptions {
172    /// The sender for sending events.
173    pub sender: broadcast::Sender<Event>,
174    /// The task id for the events.
175    pub task_id: u64,
176    /// Whether or not send the task started event.
177    pub send_start: bool,
178}
179
180#[cfg(test)]
181mod tests {}