Permalink
Browse files

feat: create, list, and delete volumes

  • Loading branch information...
schrieveslaach committed Dec 14, 2018
1 parent 79d65c2 commit 6e425ea99691f96a6159f143aee48b8364e5c963
Showing with 259 additions and 5 deletions.
  1. +32 −0 examples/volumecreate.rs
  2. +23 −0 examples/volumedelete.rs
  3. +21 −0 examples/volumes.rs
  4. +69 −0 src/builder.rs
  5. +87 −3 src/lib.rs
  6. +25 −0 src/rep.rs
  7. +2 −2 src/tty.rs
@@ -0,0 +1,32 @@
extern crate shiplift;
extern crate tokio;

use shiplift::builder::VolumeCreateOptions;
use shiplift::Docker;
use std::collections::HashMap;
use std::env;
use tokio::prelude::Future;

fn main() {
let docker = Docker::new();
let volumes = docker.volumes();

let volume_name = env::args()
.nth(1)
.expect("You need to specify an volume name");

let mut labels = HashMap::new();
labels.insert("com.github.softprops", "shiplift");

let fut = volumes
.create(
&VolumeCreateOptions::builder()
.name(volume_name.as_ref())
.labels(&labels)
.build(),
)
.map(|info| println!("{:?}", info))
.map_err(|e| eprintln!("Error: {}", e));

tokio::run(fut);
}
@@ -0,0 +1,23 @@
extern crate shiplift;
extern crate tokio;

use shiplift::Docker;
use std::env;
use tokio::prelude::Future;

fn main() {
let docker = Docker::new();
let volumes = docker.volumes();

let volume_name = env::args()
.nth(1)
.expect("You need to specify an volume name");

let fut = volumes
.get(&volume_name)
.delete()
.map(|info| println!("{:?}", info))
.map_err(|e| eprintln!("Error: {}", e));

tokio::run(fut);
}
@@ -0,0 +1,21 @@
extern crate shiplift;
extern crate tokio;

use shiplift::Docker;
use tokio::prelude::Future;

fn main() {
let docker = Docker::new();
let volumes = docker.volumes();

let fut = volumes
.list()
.map(|volumes| {
for v in volumes {
println!("volume -> {:#?}", v)
}
})
.map_err(|e| eprintln!("Error: {}", e));;

tokio::run(fut);
}
@@ -1213,6 +1213,75 @@ impl ContainerConnectionOptions {
}
}

/// Interface for creating volumes
#[derive(Serialize)]
pub struct VolumeCreateOptions {
params: HashMap<&'static str, Value>,
}

impl VolumeCreateOptions {
/// serialize options as a string. returns None if no options are defined
pub fn serialize(&self) -> Result<String> {
serde_json::to_string(&self.params).map_err(Error::from)
}

pub fn parse_from<'a, K, V>(
&self,
params: &'a HashMap<K, V>,
body: &mut BTreeMap<String, Value>,
) where
&'a HashMap<K, V>: IntoIterator,
K: ToString + Eq + Hash,
V: Serialize,
{
for (k, v) in params.iter() {
let key = k.to_string();
let value = serde_json::to_value(v).unwrap();

body.insert(key, value);
}
}

/// return a new instance of a builder for options
pub fn builder() -> VolumeCreateOptionsBuilder {
VolumeCreateOptionsBuilder::new()
}
}

#[derive(Default)]
pub struct VolumeCreateOptionsBuilder {
params: HashMap<&'static str, Value>,
}

impl VolumeCreateOptionsBuilder {
pub(crate) fn new() -> Self {
let params = HashMap::new();
VolumeCreateOptionsBuilder { params }
}

pub fn name(
&mut self,
name: &str,
) -> &mut Self {
self.params.insert("Name", json!(name));
self
}

pub fn labels(
&mut self,
labels: &HashMap<&str, &str>,
) -> &mut Self {
self.params.insert("Labels", json!(labels));
self
}

pub fn build(&self) -> VolumeCreateOptions {
VolumeCreateOptions {
params: self.params.clone(),
}
}
}

#[cfg(test)]
mod tests {
use super::ContainerOptionsBuilder;
@@ -56,6 +56,7 @@ pub use builder::{
BuildOptions, ContainerConnectionOptions, ContainerFilter, ContainerListOptions,
ContainerOptions, EventsOptions, ExecContainerOptions, ImageFilter, ImageListOptions,
LogsOptions, NetworkCreateOptions, NetworkListOptions, PullOptions, RmContainerOptions,
VolumeCreateOptions,
};
pub use errors::Error;
use futures::{future::Either, Future, IntoFuture, Stream};
@@ -68,12 +69,12 @@ use hyperlocal::UnixConnector;
use mime::Mime;
use openssl::ssl::{SslConnector, SslFiletype, SslMethod};
use read::StreamReader;
use rep::Image as ImageRep;
use rep::{
Change, Container as ContainerRep, ContainerCreateInfo, ContainerDetails, Event, Exit, History,
ImageDetails, Info, SearchResult, Stats, Status, Top, Version,
Image as ImageRep, ImageDetails, Info, NetworkCreateInfo, NetworkDetails as NetworkInfo,
SearchResult, Stats, Status, Top, Version, Volume as VolumeRep, VolumeCreateInfo,
Volumes as VolumesRep,
};
use rep::{NetworkCreateInfo, NetworkDetails as NetworkInfo};
use serde_json::Value;
use std::borrow::Cow;
use std::env;
@@ -671,6 +672,85 @@ impl<'a, 'b> Network<'a, 'b> {
}
}

/// Interface for docker volumes
pub struct Volumes<'a> {
docker: &'a Docker,
}

impl<'a> Volumes<'a> {
/// Exports an interface for interacting with docker volumes
pub fn new(docker: &'a Docker) -> Volumes<'a> {
Volumes { docker }
}

pub fn create(
&self,
opts: &VolumeCreateOptions,
) -> impl Future<Item = VolumeCreateInfo, Error = Error> {
let data = match opts.serialize() {
Ok(data) => data,
Err(e) => return Either::A(futures::future::err(e)),
};

let bytes = data.into_bytes();
let path = vec!["/volumes/create".to_owned()];

Either::B(
self.docker
.post_json(&path.join("?"), Some((bytes, mime::APPLICATION_JSON))),
)
}

/// Lists the docker volumes on the current docker host
pub fn list(&self) -> impl Future<Item = Vec<VolumeRep>, Error = Error> {
let path = vec!["/volumes".to_owned()];

self.docker
.get_json::<VolumesRep>(&path.join("?"))
.map(|volumes: VolumesRep| match volumes.volumes {
Some(volumes) => volumes.clone(),
None => vec![],
})
}

/// Returns a reference to a set of operations available for a named volume
pub fn get<'b>(
&self,
name: &'b str,
) -> Volume<'a, 'b> {
Volume::new(self.docker, name)
}
}

/// Interface for accessing and manipulating a named docker volume
pub struct Volume<'a, 'b> {
docker: &'a Docker,
name: Cow<'b, str>,
}

impl<'a, 'b> Volume<'a, 'b> {
/// Exports an interface for operations that may be performed against a named volume
pub fn new<S>(
docker: &'a Docker,
name: S,
) -> Volume<'a, 'b>
where
S: Into<Cow<'b, str>>,
{
Volume {
docker,
name: name.into(),
}
}

/// Deletes a volume
pub fn delete(&self) -> impl Future<Item = (), Error = Error> {
self.docker
.delete(&format!("/volumes/{}", self.name)[..])
.map(|_| ())
}
}

// https://docs.docker.com/reference/api/docker_remote_api_v1.17/
impl Docker {
/// constructs a new Docker instance for a docker host listening at a url specified by an env var `DOCKER_HOST`,
@@ -780,6 +860,10 @@ impl Docker {
Networks::new(self)
}

pub fn volumes(&self) -> Volumes {
Volumes::new(self)
}

/// Returns version information associated with the docker daemon
pub fn version(&self) -> impl Future<Item = Version, Error = Error> {
self.get_json("/version")
@@ -456,3 +456,28 @@ pub enum Status {
Untagged(String),
Deleted(String),
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct VolumeCreateInfo {
pub name: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Volumes {
pub volumes: Option<Vec<Volume>>,
pub warnings: Option<Vec<String>>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Volume {
pub created_at: String,
pub driver: String,
pub labels: Option<HashMap<String, String>>,
pub name: String,
pub mountpoint: String,
pub options: Option<HashMap<String, String>>,
pub scope: String,
}
@@ -59,15 +59,15 @@ impl Decoder for TtyDecoder {
0 => {
return Err(Error::InvalidResponse(
"Unsupported stream of type stdin".to_string(),
))
));
}
1 => StreamType::StdOut,
2 => StreamType::StdErr,
n => {
return Err(Error::InvalidResponse(format!(
"Unsupported stream of type {}",
n
)))
)));
}
};

0 comments on commit 6e425ea

Please sign in to comment.