docker_sdk/
volume.rs

1//! Create and manage persistent storage that can be attached to containers.
2//!
3//! API Reference: <https://docs.docker.com/engine/api/v1.41/#tag/Volume>
4
5use std::{
6    collections::{BTreeMap, HashMap},
7    hash::Hash,
8};
9
10use hyper::Body;
11use serde::{Deserialize, Serialize};
12use serde_json::{json, Value};
13
14use crate::{
15    errors::{Error, Result},
16    Docker,
17};
18
19#[cfg(feature = "chrono")]
20use chrono::{DateTime, Utc};
21
22/// Interface for docker volumes
23///
24/// API Reference: <https://docs.docker.com/engine/api/v1.41/#tag/Volume>
25pub struct Volumes<'docker> {
26    docker: &'docker Docker,
27}
28
29impl<'docker> Volumes<'docker> {
30    /// Exports an interface for interacting with docker volumes
31    pub fn new(docker: &'docker Docker) -> Self {
32        Volumes { docker }
33    }
34
35    /// Creates a new docker volume.
36    ///
37    /// API Reference: <https://docs.docker.com/engine/api/v1.41/#operation/VolumeCreate>
38    pub async fn create(
39        &self,
40        opts: &VolumeCreateOptions,
41    ) -> Result<VolumeCreateInfo> {
42        let body: Body = opts.serialize()?.into();
43        let path = vec!["/volumes/create".to_owned()];
44
45        self.docker
46            .post_json(&path.join("?"), Some((body, mime::APPLICATION_JSON)))
47            .await
48    }
49
50    /// Lists the docker volumes on the current docker host
51    ///
52    /// API Reference: <https://docs.docker.com/engine/api/v1.41/#operation/VolumeList>
53    pub async fn list(&self) -> Result<Vec<VolumeInfo>> {
54        let path = vec!["/volumes".to_owned()];
55
56        let volumes_rep = self.docker.get_json::<VolumesInfo>(&path.join("?")).await?;
57        Ok(match volumes_rep.volumes {
58            Some(volumes) => volumes,
59            None => vec![],
60        })
61    }
62
63    /// Returns a reference to a set of operations available for a named volume
64    pub fn get(
65        &self,
66        name: &str,
67    ) -> Volume<'docker> {
68        Volume::new(self.docker, name)
69    }
70}
71
72/// Interface for accessing and manipulating a named docker volume
73///
74/// API Reference: <https://docs.docker.com/engine/api/v1.41/#tag/Volume>
75pub struct Volume<'docker> {
76    docker: &'docker Docker,
77    name: String,
78}
79
80impl<'docker> Volume<'docker> {
81    /// Exports an interface for operations that may be performed against a named volume
82    pub fn new<S>(
83        docker: &'docker Docker,
84        name: S,
85    ) -> Self
86    where
87        S: Into<String>,
88    {
89        Volume {
90            docker,
91            name: name.into(),
92        }
93    }
94
95    /// Deletes a volume
96    ///
97    /// API Reference: <https://docs.docker.com/engine/api/v1.41/#operation/VolumeDelete>
98    pub async fn delete(&self) -> Result<()> {
99        self.docker
100            .delete(&format!("/volumes/{}", self.name)[..])
101            .await?;
102        Ok(())
103    }
104}
105
106/// Interface for creating volumes
107#[derive(Serialize, Debug)]
108pub struct VolumeCreateOptions {
109    params: HashMap<&'static str, Value>,
110}
111
112impl VolumeCreateOptions {
113    /// serialize options as a string. returns None if no options are defined
114    pub fn serialize(&self) -> Result<String> {
115        serde_json::to_string(&self.params).map_err(Error::from)
116    }
117
118    pub fn parse_from<'a, K, V>(
119        &self,
120        params: &'a HashMap<K, V>,
121        body: &mut BTreeMap<String, Value>,
122    ) where
123        &'a HashMap<K, V>: IntoIterator,
124        K: ToString + Eq + Hash,
125        V: Serialize,
126    {
127        for (k, v) in params.iter() {
128            let key = k.to_string();
129            let value = serde_json::to_value(v).unwrap();
130
131            body.insert(key, value);
132        }
133    }
134
135    /// return a new instance of a builder for options
136    pub fn builder() -> VolumeCreateOptionsBuilder {
137        VolumeCreateOptionsBuilder::new()
138    }
139}
140
141#[derive(Default)]
142pub struct VolumeCreateOptionsBuilder {
143    params: HashMap<&'static str, Value>,
144}
145
146impl VolumeCreateOptionsBuilder {
147    pub(crate) fn new() -> Self {
148        let params = HashMap::new();
149        VolumeCreateOptionsBuilder { params }
150    }
151
152    pub fn driver(
153        &mut self,
154        driver_name: &str,
155        driver_opts: Option<&HashMap<&str, &str>>,
156    ) -> &mut Self {
157        self.params.insert("Driver", json!(driver_name));
158        if let Some(opts) = driver_opts {
159            self.params.insert("DriverOpts", json!(opts));
160        }
161        self
162    }
163
164    pub fn name(
165        &mut self,
166        name: &str,
167    ) -> &mut Self {
168        self.params.insert("Name", json!(name));
169        self
170    }
171
172    pub fn labels(
173        &mut self,
174        labels: &HashMap<&str, &str>,
175    ) -> &mut Self {
176        self.params.insert("Labels", json!(labels));
177        self
178    }
179
180    pub fn build(&self) -> VolumeCreateOptions {
181        VolumeCreateOptions {
182            params: self.params.clone(),
183        }
184    }
185}
186
187#[derive(Clone, Debug, Serialize, Deserialize)]
188#[serde(rename_all = "PascalCase")]
189pub struct VolumeCreateInfo {
190    pub name: String,
191}
192
193#[derive(Clone, Debug, Serialize, Deserialize)]
194#[serde(rename_all = "PascalCase")]
195pub struct VolumesInfo {
196    pub volumes: Option<Vec<VolumeInfo>>,
197    pub warnings: Option<Vec<String>>,
198}
199
200#[derive(Clone, Debug, Serialize, Deserialize)]
201#[serde(rename_all = "PascalCase")]
202pub struct VolumeInfo {
203    #[cfg(feature = "chrono")]
204    pub created_at: DateTime<Utc>,
205    #[cfg(not(feature = "chrono"))]
206    pub created_at: String,
207    pub driver: String,
208    pub labels: Option<HashMap<String, String>>,
209    pub name: String,
210    pub mountpoint: String,
211    pub options: Option<HashMap<String, String>>,
212    pub scope: String,
213}
214
215#[cfg(test)]
216mod test {
217    use super::*;
218
219    #[derive(Deserialize)]
220    #[serde(rename_all = "PascalCase")]
221    pub struct VolumeTestInfo {
222        pub driver: Option<String>,
223        pub name: Option<String>,
224        pub driver_opts: Option<HashMap<String, String>>,
225    }
226
227    #[test]
228    fn test_volumecreateoptionsbuilder_driver() {
229        let volume = VolumeCreateOptions::builder()
230            .driver("my_driver", None)
231            .build();
232
233        let serialized = volume.serialize().unwrap();
234        let volume_info: VolumeTestInfo = serde_json::from_str(&serialized).unwrap();
235
236        assert_eq!(volume_info.driver, Some("my_driver".to_string()));
237        assert_eq!(volume_info.name, None);
238        assert_eq!(volume_info.driver_opts, None)
239    }
240
241    #[test]
242    fn test_volumecreateoptionsbuilder_driver_opts() {
243        let opts: HashMap<&str, &str> = [("option", "value")].iter().cloned().collect();
244        let volume = VolumeCreateOptions::builder()
245            .driver("my_driver", Some(&opts))
246            .build();
247
248        let serialized = volume.serialize().unwrap();
249        let volume_info: VolumeTestInfo = serde_json::from_str(&serialized).unwrap();
250        let mut driver_options = HashMap::new();
251        driver_options.insert("option".to_string(), "value".to_string());
252
253        assert_eq!(volume_info.driver, Some("my_driver".to_string()));
254        assert_eq!(volume_info.name, None);
255        assert_eq!(volume_info.driver_opts, Some(driver_options))
256    }
257}