Skip to content

Commit 5084142

Browse files
authored
Update bollard dependency (#1376)
### What does this PR do? This commit is an attempt to demonstrate the claude CLI tool for the purposes of mostly mechanical dependency upgrades. This kind of work is a non-trivial effort sink for lading and other SMP projects, it'd be swell if a machine could do it. I find the result here satisfying. I acted as critical reviewer, claude as implementor.
1 parent c17d9f9 commit 5084142

File tree

4 files changed

+60
-59
lines changed

4 files changed

+60
-59
lines changed

Cargo.lock

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lading/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ lading-signal = { version = "0.1", path = "../lading_signal" }
2222

2323
async-compression = { version = "0.4.23", features = ["tokio", "zstd"] }
2424
average = { version = "0.16", default-features = false, features = [] }
25-
bollard = { version = "0.18", default-features = false, features = ["pipe", "http"] }
25+
bollard = { version = "0.19", default-features = false, features = ["pipe", "http"] }
2626
byte-unit = { workspace = true }
2727
bytes = { workspace = true, features = ["std"] }
2828
clap = { version = "4.5", default-features = false, features = [

lading/src/generator/container.rs

Lines changed: 48 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
//! for a shutdown signal.
66
77
use bollard::Docker;
8-
use bollard::container::{
9-
Config as ContainerConfig, CreateContainerOptions, RemoveContainerOptions,
10-
StartContainerOptions, StopContainerOptions,
8+
use bollard::models::ContainerCreateBody;
9+
use bollard::query_parameters::{
10+
CreateContainerOptionsBuilder, CreateImageOptionsBuilder, InspectContainerOptionsBuilder,
11+
RemoveContainerOptionsBuilder, StartContainerOptions, StopContainerOptionsBuilder,
1112
};
12-
use bollard::image::CreateImageOptions;
1313
use bollard::secret::ContainerCreateResponse;
1414
use serde::{Deserialize, Serialize};
1515
use std::collections::{HashMap, VecDeque};
@@ -178,7 +178,8 @@ impl Container {
178178
// Check that containers are still running every 10 seconds
179179
_ = liveness_interval.tick() => {
180180
for container in &containers {
181-
if let Some(state) = docker.inspect_container(&container.id, None).await?.state {
181+
let inspect_options = InspectContainerOptionsBuilder::default().build();
182+
if let Some(state) = docker.inspect_container(&container.id, Some(inspect_options)).await?.state {
182183
if !state.running.unwrap_or(false) {
183184
return Err(Error::Generic(format!(
184185
"Container {id} is not running anymore",
@@ -212,14 +213,11 @@ impl Container {
212213
}
213214

214215
async fn pull_image(docker: &Docker, full_image: &str) -> Result<(), Error> {
215-
let mut pull_stream = docker.create_image(
216-
Some(CreateImageOptions::<&str> {
217-
from_image: full_image,
218-
..Default::default()
219-
}),
220-
None,
221-
None,
222-
);
216+
let create_image_options = CreateImageOptionsBuilder::default()
217+
.from_image(full_image)
218+
.build();
219+
220+
let mut pull_stream = docker.create_image(Some(create_image_options), None, None);
223221

224222
while let Some(item) = pull_stream.next().await {
225223
match item {
@@ -241,32 +239,35 @@ async fn pull_image(docker: &Docker, full_image: &str) -> Result<(), Error> {
241239
impl Config {
242240
/// Convert the `Container` instance to a `ContainerConfig` for the Docker API.
243241
#[must_use]
244-
fn to_container_config<'a>(&'a self, full_image: &'a str) -> ContainerConfig<&'a str> {
245-
ContainerConfig {
246-
image: Some(full_image),
242+
fn to_container_config(&self, full_image: &str) -> ContainerCreateBody {
243+
// The Docker API requires exposed ports in the format {"<port>/<protocol>": {}}.
244+
// For example: {"80/tcp": {}, "443/tcp": {}}
245+
// The port specification is in the key, and the value must be an empty object.
246+
// Bollard represents the empty object as HashMap<(), ()>, which is required by their API.
247+
#[allow(clippy::zero_sized_map_values)]
248+
let exposed_ports = self.exposed_ports.as_ref().map(|ports| {
249+
ports
250+
.iter()
251+
.map(|port| {
252+
// Ensure port includes protocol (default to tcp if not specified)
253+
let port_with_protocol = if port.contains('/') {
254+
port.clone()
255+
} else {
256+
format!("{port}/tcp")
257+
};
258+
(port_with_protocol, HashMap::<(), ()>::new())
259+
})
260+
.collect()
261+
});
262+
263+
ContainerCreateBody {
264+
image: Some(full_image.to_string()),
247265
tty: Some(true),
248-
cmd: self
249-
.args
250-
.as_ref()
251-
.map(|args| args.iter().map(String::as_str).collect()),
252-
env: self
253-
.env
254-
.as_ref()
255-
.map(|env| env.iter().map(String::as_str).collect()),
256-
labels: self.labels.as_ref().map(|labels| {
257-
labels
258-
.iter()
259-
.map(|(key, value)| (key.as_str(), value.as_str()))
260-
.collect()
261-
}),
266+
cmd: self.args.clone(),
267+
env: self.env.clone(),
268+
labels: self.labels.clone(),
262269
network_disabled: self.network_disabled,
263-
#[allow(clippy::zero_sized_map_values)]
264-
exposed_ports: self.exposed_ports.as_ref().map(|ports| {
265-
ports
266-
.iter()
267-
.map(|port| (port.as_str(), HashMap::new()))
268-
.collect()
269-
}),
270+
exposed_ports,
270271
..Default::default()
271272
}
272273
}
@@ -279,14 +280,12 @@ impl Config {
279280
let container_name = format!("lading_container_{}", Uuid::new_v4());
280281
debug!("Creating container: {container_name}");
281282

283+
let create_options = CreateContainerOptionsBuilder::default()
284+
.name(&container_name)
285+
.build();
286+
282287
let container = docker
283-
.create_container(
284-
Some(CreateContainerOptions {
285-
name: &container_name,
286-
platform: None,
287-
}),
288-
self.to_container_config(full_image),
289-
)
288+
.create_container(Some(create_options), self.to_container_config(full_image))
290289
.await?;
291290

292291
debug!("Created container with id: {id}", id = container.id);
@@ -295,7 +294,7 @@ impl Config {
295294
}
296295

297296
docker
298-
.start_container(&container.id, None::<StartContainerOptions<String>>)
297+
.start_container(&container.id, None::<StartContainerOptions>)
299298
.await?;
300299

301300
debug!("Started container: {id}", id = container.id);
@@ -309,22 +308,18 @@ async fn stop_and_remove_container(
309308
container: &ContainerCreateResponse,
310309
) -> Result<(), Error> {
311310
info!("Stopping container: {id}", id = container.id);
311+
let stop_options = StopContainerOptionsBuilder::default().t(5).build();
312312
if let Err(e) = docker
313-
.stop_container(&container.id, Some(StopContainerOptions { t: 5 }))
313+
.stop_container(&container.id, Some(stop_options))
314314
.await
315315
{
316316
warn!("Error stopping container {id}: {e}", id = container.id);
317317
}
318318

319319
debug!("Removing container: {id}", id = container.id);
320+
let remove_options = RemoveContainerOptionsBuilder::default().force(true).build();
320321
docker
321-
.remove_container(
322-
&container.id,
323-
Some(RemoveContainerOptions {
324-
force: true,
325-
..Default::default()
326-
}),
327-
)
322+
.remove_container(&container.id, Some(remove_options))
328323
.await?;
329324

330325
debug!("Removed container: {id}", id = container.id);

lading/src/target.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use std::{
2424
};
2525

2626
use bollard::Docker;
27+
use bollard::query_parameters::InspectContainerOptionsBuilder;
2728
use lading_signal::Broadcaster;
2829
use metrics::gauge;
2930
use nix::{
@@ -209,7 +210,11 @@ impl Server {
209210
let docker = Docker::connect_with_socket_defaults()?;
210211

211212
let pid: i64 = loop {
212-
if let Ok(container) = docker.inspect_container(&config.name, None).await {
213+
let inspect_options = InspectContainerOptionsBuilder::default().build();
214+
if let Ok(container) = docker
215+
.inspect_container(&config.name, Some(inspect_options))
216+
.await
217+
{
213218
if let Some(pid) = container.state.and_then(|state| state.pid) {
214219
// In some cases docker will report pid 0 as the pid for the
215220
// polled container. This is not usable by us and we believe

0 commit comments

Comments
 (0)