Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ jobs:
- setup_remote_docker
- run: docker build -t bollard .
- run: resources/dockerfiles/bin/run_integration_tests.sh --features time --tests
test_swarm:
docker:
- image: docker:27.3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can Dependabot be enabled? The latest release is 28.1.

steps:
- checkout
- setup_remote_docker
- run: docker build -t bollard .
- run: docker run -ti -e DOCKER_HOST='unix:///var/run/docker.sock' -v /var/run/docker.sock:/var/run/docker.sock --rm bollard cargo test --features test_swarm test_swarm
test_race:
docker:
- image: docker:27.3
Expand Down Expand Up @@ -145,3 +153,4 @@ workflows:
- test_fmt
- test_race
- test_sshforward
- test_swarm
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ test_aws_lc_rs = ["test_ssl", "aws-lc-rs"]
test_macos = []
# Enable tests specifically for buildkit's sshforward functionality
test_sshforward = []
# Enable tests specifically for swarm
test_swarm = []
# Enable JSON payload in deserialization errors
json_data_content = []
# Enable rustls / ssl
Expand Down
3 changes: 3 additions & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
edition = "2021"
ignore = [
'codegen/*'
]
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ pub mod network;
mod read;
pub mod secret;
pub mod service;
pub mod swarm;
pub mod system;
mod uri;
pub mod volume;
Expand Down
203 changes: 203 additions & 0 deletions src/swarm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
//! Swarm API: Docker swarm is a container orchestration tool, meaning that it allows the user to manage multiple containers deployed across multiple host machines.
use crate::docker::BodyType;

use hyper::Method;
use serde::{Deserialize, Serialize};

use bytes::Bytes;
use http::request::Builder;
use http_body_util::Full;

use std::cmp::Eq;
use std::hash::Hash;

use super::Docker;
use crate::errors::Error;

use crate::models::*;

/// Swam configuration used in the [Init Swarm API](Docker::init_swarm())
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct InitSwarmOptions<T>
where
T: Into<String> + Eq + Hash,
{
/// Listen address (format: <ip|interface>[:port])
pub listen_addr: T,
/// Externally reachable address advertised to other nodes.
pub advertise_addr: T,
}

/// Swam configuration used in the [Join Swarm API](Docker::join_swarm())
#[derive(Debug, Clone, Default, Serialize)]
pub struct JoinSwarmOptions<T>
where
T: Into<String> + Serialize,
{
/// Externally reachable address advertised to other nodes.
pub advertise_addr: T,
/// Secret token for joining this swarm
pub join_token: T,
}

/// Swam configuration used in the [Leave Swarm API](Docker::leave_swarm())
#[derive(Debug, Copy, Clone, Default, Serialize)]
pub struct LeaveSwarmOptions {
/// Force to leave to swarm.
pub force: bool,
}

impl Docker {
/// ---
///
/// # Init Swarm
///
/// Initialize a new swarm.
///
/// # Arguments
///
/// - [Init Swarm Options](InitSwarmOptions) struct.
///
/// # Returns
///
/// - A String wrapped in a
/// Future.
///
/// # Examples
///
/// ```rust
/// # use bollard::Docker;
/// # let docker = Docker::connect_with_http_defaults().unwrap();
/// # use bollard::swarm::InitSwarmOptions;
///
/// use std::default::Default;
///
/// let config = InitSwarmOptions {
/// advertise_addr: "127.0.0.1",
/// listen_addr: "0.0.0.0:2377"
/// };
///
/// docker.init_swarm(config);
/// ```
pub async fn init_swarm<T>(&self, config: InitSwarmOptions<T>) -> Result<String, Error>
where
T: Into<String> + Eq + Hash + Serialize,
{
let url = "/swarm/init";

let req = self.build_request(
url,
Builder::new().method(Method::POST),
None::<String>,
Docker::serialize_payload(Some(config)),
);

self.process_into_value(req).await
}

/// ---
///
/// # Inspect Swarm
///
/// Inspect swarm.
///
/// # Arguments
///
/// # Returns
///
/// - [Swarm](swarm) struct, wrapped in a Future.
///
/// # Examples
///
/// ```rust
/// # use bollard::Docker;
/// # let docker = Docker::connect_with_http_defaults().unwrap();
///
/// docker.inspect_swarm();
/// ```
pub async fn inspect_swarm(&self) -> Result<Swarm, Error> {
let url = "/swarm";

let req = self.build_request(
url,
Builder::new().method(Method::GET),
None::<String>,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);

self.process_into_value(req).await
}

/// ---
///
/// # Join a Swarm
///
/// # Arguments
///
/// - [Join Swarm Options](JoinSwarmOptions) struct.
///
/// # Returns
///
/// - unit type `()`, wrapped in a Future.
///
/// # Examples
///
/// ```rust
/// # use bollard::Docker;
/// # let docker = Docker::connect_with_http_defaults().unwrap();
/// # use bollard::swarm::JoinSwarmOptions;
///
/// let config = JoinSwarmOptions {
/// advertise_addr: "127.0.0.1",
/// join_token: "token",
/// };
/// docker.join_swarm(config);
/// ```
pub async fn join_swarm<T>(&self, config: JoinSwarmOptions<T>) -> Result<(), Error>
where
T: Into<String> + Eq + Hash + Serialize,
{
let url = "/swarm/join";

let req = self.build_request(
url,
Builder::new().method(Method::POST),
None::<String>,
Docker::serialize_payload(Some(config)),
);

self.process_into_unit(req).await
}

/// ---
///
/// # Leave a Swarm
///
/// # Arguments
///
/// # Returns
///
/// - unit type `()`, wrapped in a Future.
///
/// # Examples
///
/// ```rust
/// # use bollard::Docker;
/// # let docker = Docker::connect_with_http_defaults().unwrap();
///
/// docker.leave_swarm(None);
/// ```
pub async fn leave_swarm(&self, options: Option<LeaveSwarmOptions>) -> Result<(), Error> {
let url = "/swarm/leave";

let req = self.build_request(
url,
Builder::new().method(Method::POST),
options,
Ok(BodyType::Left(Full::new(Bytes::new()))),
);

self.process_into_unit(req).await
}
}
42 changes: 42 additions & 0 deletions tests/swarm_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#[macro_use]
pub mod common;

#[cfg(feature = "test_swarm")]
async fn swarm_test(docker: bollard::Docker) -> Result<(), bollard::errors::Error> {
use bollard::swarm::*;

// init swarm
let config = InitSwarmOptions {
listen_addr: "0.0.0.0:2377",
advertise_addr: "127.0.0.1",
};
let _ = &docker.init_swarm(config).await?;

// inspect swarm
let inspection_result = &docker.inspect_swarm().await?;
assert!(
inspection_result
.join_tokens
.as_ref()
.unwrap()
.worker
.as_ref()
.unwrap()
.len()
> 0
);

// leave swarm
let config = LeaveSwarmOptions { force: true };
let _ = &docker.leave_swarm(Some(config)).await?;
Ok(())
}

#[cfg(feature = "test_swarm")]
#[test]
fn integration_test_swarm() {
use crate::common::run_runtime;
use bollard::Docker;
use tokio::runtime::Runtime;
connect_to_docker_and_run!(swarm_test);
}