Skip to content

Commit bc74c9f

Browse files
pRizzclaude
andauthored
Upgrade bollard from 0.18 to 0.20.1 (#17)
Breaking API changes in bollard 0.20: - Move options structs to query_parameters module (CreateContainerOptions, StartContainerOptions, StopContainerOptions, RemoveContainerOptions, LogsOptions, BuildImageOptions, CreateImageOptions, TagImageOptions, CreateVolumeOptions, RemoveVolumeOptions, PruneContainersOptions, PruneImagesOptions, PruneNetworksOptions, DataUsageOptions) - Replace container Config with ContainerCreateBody (models module) - Replace CreateVolumeOptions with VolumeCreateRequest (models module) - Change exposed_ports from HashMap to Vec<String> - Add required platform/target fields to BuildImageOptions - Change t field from i64 to Option<i32> in StopContainerOptions - Add signal field to StopContainerOptions - Remove generics from StartContainerOptions and LogsOptions - Change build_image body to Either<Full<Bytes>, StreamBody<...>> - Change BuildInfo.error to error_detail.message (nested Option) - Change CreateImageInfo.error to error_detail.message - Update SystemDataUsageResponse with new *DiskUsage sub-structs - Remove generics from prune_* methods - Add required argument to df() method Closes #15 https://claude.ai/code/session_01HC3UStk1vUaD7rjrsJ3vUA Co-authored-by: Claude <noreply@anthropic.com>
1 parent bee062f commit bc74c9f

11 files changed

Lines changed: 161 additions & 265 deletions

File tree

Cargo.lock

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

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ categories = ["command-line-utilities", "development-tools"]
1919
[workspace.dependencies]
2020
opencode-cloud-core = { version = "12.0.2", path = "packages/core" }
2121
clap = { version = "4.5", features = ["derive"] }
22-
tokio = { version = "1.43", features = ["rt-multi-thread", "macros", "signal"] }
22+
tokio = { version = "1.43", features = ["rt-multi-thread", "macros", "signal", "io-std", "io-util"] }
2323
serde = { version = "1.0", features = ["derive"] }
2424
serde_json = "1.0"
2525
jsonc-parser = { version = "0.29", features = ["serde"] }
@@ -34,7 +34,7 @@ napi-derive = "2"
3434
tempfile = "3"
3535

3636
# Docker integration
37-
bollard = { version = "0.18", features = ["chrono", "buildkit"] }
37+
bollard = { version = "0.20.1", features = ["chrono", "buildkit"] }
3838
futures-util = "0.3"
3939
tar = "0.4"
4040
flate2 = "1.0"

packages/cli-rust/src/commands/disk_usage.rs

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,10 @@ pub fn format_host_disk_report(
6868
}
6969

7070
pub async fn get_disk_usage_report(client: &DockerClient) -> Result<DiskUsageReport> {
71+
use opencode_cloud_core::bollard::query_parameters::DataUsageOptions;
7172
let data_usage = client
7273
.inner()
73-
.df()
74+
.df(None::<DataUsageOptions>)
7475
.await
7576
.map_err(|e| anyhow!("Failed to read Docker disk usage: {e}"))?;
7677
Ok(build_disk_usage_report(&data_usage))
@@ -92,22 +93,23 @@ pub fn format_bytes_i64(value: i64) -> String {
9293
}
9394

9495
fn build_disk_usage_report(data_usage: &SystemDataUsageResponse) -> DiskUsageReport {
95-
let images = data_usage.layers_size;
96+
// Bollard v0.20+ uses new disk usage structures
97+
let images = data_usage
98+
.images_disk_usage
99+
.as_ref()
100+
.and_then(|u| u.total_size);
96101
let containers = data_usage
97-
.containers
102+
.containers_disk_usage
103+
.as_ref()
104+
.and_then(|u| u.total_size);
105+
let volumes = data_usage
106+
.volumes_disk_usage
98107
.as_ref()
99-
.and_then(|containers| sum_optional_sizes(containers.iter().map(|c| c.size_rw)));
100-
let volumes = data_usage.volumes.as_ref().and_then(|volumes| {
101-
sum_optional_sizes(
102-
volumes
103-
.iter()
104-
.map(|v| v.usage_data.as_ref().map(|u| u.size)),
105-
)
106-
});
108+
.and_then(|u| u.total_size);
107109
let build_cache = data_usage
108-
.build_cache
110+
.build_cache_disk_usage
109111
.as_ref()
110-
.and_then(|cache| sum_optional_sizes(cache.iter().map(|entry| entry.size)));
112+
.and_then(|u| u.total_size);
111113

112114
let total = match (images, containers, volumes, build_cache) {
113115
(Some(images), Some(containers), Some(volumes), Some(build_cache)) => {
@@ -143,25 +145,6 @@ fn build_host_disk_report(disks: &Disks) -> Option<HostDiskReport> {
143145
})
144146
}
145147

146-
fn sum_optional_sizes<T>(values: T) -> Option<i64>
147-
where
148-
T: Iterator<Item = Option<i64>>,
149-
{
150-
let mut total = 0i64;
151-
let mut any = false;
152-
for value in values {
153-
let Some(value) = value else {
154-
continue;
155-
};
156-
if value < 0 {
157-
continue;
158-
}
159-
total += value;
160-
any = true;
161-
}
162-
if any { Some(total) } else { None }
163-
}
164-
165148
fn format_delta_i64(after: Option<i64>, before: Option<i64>) -> Option<String> {
166149
let (after, before) = (after?, before?);
167150
let delta = after - before;

packages/cli-rust/src/commands/logs.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use anyhow::{Result, anyhow};
77
use clap::Args;
88
use console::style;
99
use futures_util::StreamExt;
10-
use opencode_cloud_core::bollard::container::{LogOutput, LogsOptions};
10+
use opencode_cloud_core::bollard::container::LogOutput;
11+
use opencode_cloud_core::bollard::query_parameters::LogsOptions;
1112
use opencode_cloud_core::docker::{CONTAINER_NAME, container_is_running};
1213

1314
/// Arguments for the logs command
@@ -82,7 +83,7 @@ pub async fn cmd_logs(args: &LogsArgs, maybe_host: Option<&str>, quiet: bool) ->
8283
}
8384

8485
// Create log options
85-
let options = LogsOptions::<String> {
86+
let options = LogsOptions {
8687
stdout: true,
8788
stderr: true,
8889
follow,

packages/cli-rust/src/commands/start.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use anyhow::{Result, anyhow};
1313
use clap::Args;
1414
use console::style;
1515
use futures_util::stream::StreamExt;
16-
use opencode_cloud_core::bollard::container::{LogOutput, LogsOptions};
16+
use opencode_cloud_core::bollard::container::LogOutput;
17+
use opencode_cloud_core::bollard::query_parameters::LogsOptions;
1718
use opencode_cloud_core::config::save_config;
1819
use opencode_cloud_core::docker::{
1920
CONTAINER_NAME, DEFAULT_STOP_TIMEOUT_SECS, DockerClient, DockerError, IMAGE_NAME_GHCR,
@@ -1230,7 +1231,7 @@ const FATAL_ERROR_PATTERNS: &[&str] = &[
12301231

12311232
/// Check container logs for fatal errors that indicate the service cannot start
12321233
async fn check_for_fatal_errors(client: &DockerClient) -> Option<String> {
1233-
let options = LogsOptions::<String> {
1234+
let options = LogsOptions {
12341235
stdout: true,
12351236
stderr: true,
12361237
tail: "20".to_string(),
@@ -1368,7 +1369,7 @@ pub(crate) async fn wait_for_broker_ready(
13681369

13691370
/// Show recent container logs for debugging
13701371
async fn show_recent_logs(client: &DockerClient, lines: usize) {
1371-
let options = LogsOptions::<String> {
1372+
let options = LogsOptions {
13721373
stdout: true,
13731374
stderr: true,
13741375
tail: lines.to_string(),

packages/cli-rust/src/commands/update.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,19 +1246,23 @@ async fn purge_unused_docker_resources(client: &DockerClient, quiet: bool) -> Re
12461246
let mut reclaimed = 0i64;
12471247
let mut has_reclaimed = false;
12481248

1249+
use opencode_cloud_core::bollard::query_parameters::PruneContainersOptions;
12491250
let container_prune = client
12501251
.inner()
1251-
.prune_containers::<String>(None)
1252+
.prune_containers(None::<PruneContainersOptions>)
12521253
.await
12531254
.map_err(|e| anyhow!("Failed to prune containers: {e}"))?;
12541255
if let Some(value) = container_prune.space_reclaimed {
12551256
reclaimed += value;
12561257
has_reclaimed = true;
12571258
}
12581259

1260+
use opencode_cloud_core::bollard::query_parameters::{
1261+
PruneImagesOptions, PruneNetworksOptions,
1262+
};
12591263
let image_prune = client
12601264
.inner()
1261-
.prune_images::<String>(None)
1265+
.prune_images(None::<PruneImagesOptions>)
12621266
.await
12631267
.map_err(|e| anyhow!("Failed to prune images: {e}"))?;
12641268
if let Some(value) = image_prune.space_reclaimed {
@@ -1268,7 +1272,7 @@ async fn purge_unused_docker_resources(client: &DockerClient, quiet: bool) -> Re
12681272

12691273
client
12701274
.inner()
1271-
.prune_networks::<String>(None)
1275+
.prune_networks(None::<PruneNetworksOptions>)
12721276
.await
12731277
.map_err(|e| anyhow!("Failed to prune networks: {e}"))?;
12741278

packages/core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ napi = { version = "2", features = ["tokio_rt", "napi9"], optional = true }
3939
napi-derive = { version = "2", optional = true }
4040

4141
# Docker integration
42-
bollard = { version = "0.18", features = ["chrono", "buildkit"] }
42+
bollard = { version = "0.20.1", features = ["chrono", "buildkit"] }
4343
futures-util = "0.3"
4444
tar = "0.4"
4545
flate2 = "1.0"

packages/core/src/docker/container.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ use super::volume::{
1010
VOLUME_CACHE, VOLUME_CONFIG, VOLUME_PROJECTS, VOLUME_SESSION, VOLUME_STATE, VOLUME_USERS,
1111
};
1212
use super::{DockerClient, DockerError};
13-
use bollard::container::{
14-
Config, CreateContainerOptions, RemoveContainerOptions, StartContainerOptions,
15-
StopContainerOptions,
13+
use bollard::models::ContainerCreateBody;
14+
use bollard::query_parameters::{
15+
CreateContainerOptions, RemoveContainerOptions, StartContainerOptions, StopContainerOptions,
1616
};
1717
use bollard::service::{
1818
HostConfig, Mount, MountPointTypeEnum, MountTypeEnum, PortBinding, PortMap,
@@ -155,11 +155,10 @@ pub async fn create_container(
155155
);
156156
}
157157

158-
// Create exposed ports map
159-
let mut exposed_ports = HashMap::new();
160-
exposed_ports.insert("3000/tcp".to_string(), HashMap::new());
158+
// Create exposed ports list (bollard v0.20+ uses Vec<String>)
159+
let mut exposed_ports = vec!["3000/tcp".to_string()];
161160
if cockpit_enabled_val {
162-
exposed_ports.insert("9090/tcp".to_string(), HashMap::new());
161+
exposed_ports.push("9090/tcp".to_string());
163162
}
164163

165164
// Create host config
@@ -224,8 +223,8 @@ pub async fn create_container(
224223
}
225224
let final_env = if env.is_empty() { None } else { Some(env) };
226225

227-
// Create container config
228-
let config = Config {
226+
// Create container config (bollard v0.20+ uses ContainerCreateBody)
227+
let config = ContainerCreateBody {
229228
image: Some(image_name.to_string()),
230229
hostname: Some(CONTAINER_NAME.to_string()),
231230
working_dir: Some("/home/opencode/workspace".to_string()),
@@ -237,8 +236,8 @@ pub async fn create_container(
237236

238237
// Create container
239238
let options = CreateContainerOptions {
240-
name: container_name,
241-
platform: None,
239+
name: Some(container_name.to_string()),
240+
platform: String::new(),
242241
};
243242

244243
let response = client
@@ -266,7 +265,7 @@ pub async fn start_container(client: &DockerClient, name: &str) -> Result<(), Do
266265

267266
client
268267
.inner()
269-
.start_container(name, None::<StartContainerOptions<String>>)
268+
.start_container(name, None::<StartContainerOptions>)
270269
.await
271270
.map_err(|e| DockerError::Container(format!("Failed to start container {name}: {e}")))?;
272271

@@ -285,10 +284,13 @@ pub async fn stop_container(
285284
name: &str,
286285
timeout_secs: Option<i64>,
287286
) -> Result<(), DockerError> {
288-
let timeout = timeout_secs.unwrap_or(10);
287+
let timeout = timeout_secs.unwrap_or(10) as i32;
289288
debug!("Stopping container {} with {}s timeout", name, timeout);
290289

291-
let options = StopContainerOptions { t: timeout };
290+
let options = StopContainerOptions {
291+
signal: None,
292+
t: Some(timeout),
293+
};
292294

293295
client
294296
.inner()

packages/core/src/docker/image.rs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ use super::progress::ProgressReporter;
77
use super::{
88
DOCKERFILE, DockerClient, DockerError, IMAGE_NAME_DOCKERHUB, IMAGE_NAME_GHCR, IMAGE_TAG_DEFAULT,
99
};
10-
use bollard::image::{BuildImageOptions, BuilderVersion, CreateImageOptions};
1110
use bollard::moby::buildkit::v1::StatusResponse as BuildkitStatusResponse;
1211
use bollard::models::BuildInfoAux;
12+
use bollard::query_parameters::{BuildImageOptions, BuilderVersion, CreateImageOptions};
1313
use bytes::Bytes;
1414
use flate2::Compression;
1515
use flate2::write::GzEncoder;
1616
use futures_util::StreamExt;
17+
use http_body_util::{Either, Full};
1718
use std::collections::{HashMap, VecDeque};
1819
use std::env;
1920
use std::time::{SystemTime, UNIX_EPOCH};
@@ -103,18 +104,20 @@ pub async fn build_image(
103104
);
104105
let build_args = build_args.unwrap_or_default();
105106
let options = BuildImageOptions {
106-
t: full_name.clone(),
107+
t: Some(full_name.clone()),
107108
dockerfile: "Dockerfile".to_string(),
108109
version: BuilderVersion::BuilderBuildKit,
109110
session: Some(session_id),
110111
rm: true,
111112
nocache: no_cache,
112-
buildargs: build_args,
113+
buildargs: Some(build_args),
114+
platform: String::new(),
115+
target: String::new(),
113116
..Default::default()
114117
};
115118

116119
// Create build body from context
117-
let body = Bytes::from(context);
120+
let body: Either<Full<Bytes>, _> = Either::Left(Full::new(Bytes::from(context)));
118121

119122
// Start build with streaming output
120123
let mut stream = client.inner().build_image(options, None, Some(body));
@@ -137,10 +140,12 @@ pub async fn build_image(
137140

138141
handle_stream_message(&info, progress, &mut log_state);
139142

140-
if let Some(error_msg) = info.error {
141-
progress.abandon_all(&error_msg);
143+
if let Some(error_detail) = &info.error_detail
144+
&& let Some(error_msg) = &error_detail.message
145+
{
146+
progress.abandon_all(error_msg);
142147
let context = format_build_error_with_context(
143-
&error_msg,
148+
error_msg,
144149
&log_state.recent_logs,
145150
&log_state.error_logs,
146151
&log_state.recent_buildkit_logs,
@@ -620,8 +625,9 @@ async fn do_pull(
620625
let full_name = format!("{image}:{tag}");
621626

622627
let options = CreateImageOptions {
623-
from_image: image,
624-
tag,
628+
from_image: Some(image.to_string()),
629+
tag: Some(tag.to_string()),
630+
platform: String::new(),
625631
..Default::default()
626632
};
627633

@@ -634,9 +640,11 @@ async fn do_pull(
634640
match result {
635641
Ok(info) => {
636642
// Handle errors from the stream
637-
if let Some(error_msg) = info.error {
638-
progress.abandon_all(&error_msg);
639-
return Err(DockerError::Pull(error_msg));
643+
if let Some(error_detail) = &info.error_detail
644+
&& let Some(error_msg) = &error_detail.message
645+
{
646+
progress.abandon_all(error_msg);
647+
return Err(DockerError::Pull(error_msg.to_string()));
640648
}
641649

642650
// Handle layer progress

packages/core/src/docker/update.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use super::image::{image_exists, pull_image};
77
use super::progress::ProgressReporter;
88
use super::{DockerClient, DockerError, IMAGE_NAME_GHCR, IMAGE_TAG_DEFAULT};
9-
use bollard::image::TagImageOptions;
9+
use bollard::query_parameters::TagImageOptions;
1010
use tracing::debug;
1111

1212
/// Tag for the previous image version (used for rollback)
@@ -45,8 +45,8 @@ pub async fn tag_current_as_previous(client: &DockerClient) -> Result<(), Docker
4545

4646
// Tag current as previous
4747
let options = TagImageOptions {
48-
repo: IMAGE_NAME_GHCR,
49-
tag: PREVIOUS_TAG,
48+
repo: Some(IMAGE_NAME_GHCR.to_string()),
49+
tag: Some(PREVIOUS_TAG.to_string()),
5050
};
5151

5252
client
@@ -124,8 +124,8 @@ pub async fn rollback_image(client: &DockerClient) -> Result<(), DockerError> {
124124

125125
// Re-tag previous as latest
126126
let options = TagImageOptions {
127-
repo: IMAGE_NAME_GHCR,
128-
tag: IMAGE_TAG_DEFAULT,
127+
repo: Some(IMAGE_NAME_GHCR.to_string()),
128+
tag: Some(IMAGE_TAG_DEFAULT.to_string()),
129129
};
130130

131131
client

0 commit comments

Comments
 (0)