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 {}