Skip to main content

crankshaft_docker/
lib.rs

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