From 2f2153d9be78e06baeb146505e993376a0ce48f7 Mon Sep 17 00:00:00 2001 From: Niel Drummond Date: Sat, 11 Jun 2022 13:50:44 +0100 Subject: [PATCH 1/8] Build using official rust image --- Dockerfile | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index fd36eaec..88477aaf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,7 @@ -FROM ekidd/rust-musl-builder:stable AS builder +FROM rust:1.61.0-buster -WORKDIR /tmp/bollard +WORKDIR /usr/src/bollard -COPY . ./ - -RUN sudo chown -R rust:rust /tmp/bollard \ - && sudo groupadd --gid 999 docker \ - && sudo usermod -a -G docker rust +COPY . . RUN cargo build From 038b607f86e04291f887732044752614179b73e0 Mon Sep 17 00:00:00 2001 From: Niel Drummond Date: Wed, 12 Jan 2022 16:32:34 +0000 Subject: [PATCH 2/8] Migrate from chrono to time 0.3 --- Cargo.toml | 4 +- .../src/main/java/bollard/BollardCodegen.java | 6 +- .../src/main/resources/bollard/Cargo.mustache | 2 +- .../main/resources/bollard/models.mustache | 31 +++++- codegen/target/generated-sources/Cargo.toml | 2 +- codegen/target/generated-sources/README.md | 2 +- .../target/generated-sources/src/models.rs | 100 +++++++++++++----- examples/hoover.rs | 6 +- src/container.rs | 14 ++- src/docker.rs | 25 ++++- src/system.rs | 19 ++-- tests/system_test.rs | 4 +- 12 files changed, 156 insertions(+), 59 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dd1ef330..3b392b08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,9 +24,8 @@ ct_logs = ["ssl", "ct-logs"] [dependencies] base64 = "0.13" -bollard-stubs = { version = "=1.42.0-rc.2" } +bollard-stubs = { version = "=1.42.0-rc.2", path = "codegen/target/generated-sources" } bytes = "1" -chrono = { version = "0.4", features = ["serde"] } ct-logs = { version = "0.9.0", optional = true } dirs-next = { version = "2.0", optional = true } futures-core = "0.3" @@ -46,6 +45,7 @@ serde_json = "1.0" serde_urlencoded = "0.7" tokio = { version = "1.7", features = ["time", "net", "io-util"] } thiserror = "1.0" +time = { version = "0.3", features = ["formatting", "parsing"] } tokio-util = { version = "0.7", features = ["codec"] } url = "2.2" webpki-roots = { version = "0.22", optional = true } diff --git a/codegen/src/main/java/bollard/BollardCodegen.java b/codegen/src/main/java/bollard/BollardCodegen.java index 9a526bb5..fd207061 100644 --- a/codegen/src/main/java/bollard/BollardCodegen.java +++ b/codegen/src/main/java/bollard/BollardCodegen.java @@ -22,6 +22,7 @@ public class BollardCodegen extends RustServerCodegen { public BollardCodegen() { super(); + typeMapping.put("DateTime", "OffsetDateTime"); } // Declare custom additions to inline enums that are behaving differently @@ -123,9 +124,10 @@ public Map postProcessAllModels(Map objs) { } else if (prop.name.equals("_type")) { prop.name = "typ"; } - if (prop.dataFormat != null && prop.dataFormat.equals("dateTime")) { + if (prop.dataFormat != null && (prop.dataFormat.equals("dateTime") || prop.datatype.equals("OffsetDateTime"))) { // set DateTime format on properties where appropriate - prop.datatype = "DateTime"; + prop.vendorExtensions.put("x-rustgen-is-datetime", true); + prop.datatype = "OffsetDateTime"; } if (prop.isEnum) { if (enumToString.contains(model.classname)) { diff --git a/codegen/src/main/resources/bollard/Cargo.mustache b/codegen/src/main/resources/bollard/Cargo.mustache index d71d9a1c..3a242c4d 100644 --- a/codegen/src/main/resources/bollard/Cargo.mustache +++ b/codegen/src/main/resources/bollard/Cargo.mustache @@ -7,7 +7,7 @@ license = "Apache-2.0" edition = "2018" [dependencies] -chrono = { version = "0.4", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } +time = { version = "0.3", features = ["formatting", "parsing"] } serde_with = "1.4" diff --git a/codegen/src/main/resources/bollard/models.mustache b/codegen/src/main/resources/bollard/models.mustache index 6a932c77..50bd5a82 100644 --- a/codegen/src/main/resources/bollard/models.mustache +++ b/codegen/src/main/resources/bollard/models.mustache @@ -9,8 +9,7 @@ use std::collections::HashMap; use std::default::Default; use std::hash::Hash; -use chrono::DateTime; -use chrono::Utc; +use time::OffsetDateTime; fn deserialize_nonoptional_vec<'de, D: Deserializer<'de>, T: DeserializeOwned>( d: D, @@ -24,6 +23,28 @@ fn deserialize_nonoptional_map<'de, D: Deserializer<'de>, T: DeserializeOwned>( serde::Deserialize::deserialize(d).map(|x: Option<_>| x.unwrap_or(HashMap::new())) } +fn deserialize_timestamp<'de, D: Deserializer<'de>>( + d: D +) -> Result, D::Error> { + let opt: Option = serde::Deserialize::deserialize(d)?; + if let Some(s) = opt { + Ok(Some( + OffsetDateTime::parse(&s, &time::format_description::well_known::Rfc3339) + .map_err(|e| serde::de::Error::custom(format!("{:?}", e)))?, + )) + } else { + Ok(None) + } +} + +fn serialize_timestamp(date: &Option, s: S) -> Result { + match date { + Some(inner) => Ok(s.serialize_str(&inner.format(&time::format_description::well_known::Rfc3339) + .map_err(|e| serde::ser::Error::custom(format!("{:?}", e)))?)?), + None => Ok(s.serialize_str("")?) + } +} + {{#models}}{{#model}} {{#description}}/// {{{description}}} {{/description}}{{#isEnum}}/// Enumeration of values. @@ -68,10 +89,12 @@ pub struct {{classname}} { #[serde(deserialize_with = "deserialize_nonoptional_map")]{{/isListContainer}}{{#isListContainer}} #[serde(deserialize_with = "deserialize_nonoptional_vec")]{{/isListContainer}}{{/isContainer}}{{#isEnum}} #[serde(skip_serializing_if="Option::is_none")] - #[serde(with = "serde_with::rust::string_empty_as_none")]{{/isEnum}} + #[serde(with = "serde_with::rust::string_empty_as_none")]{{/isEnum}}{{#vendorExtensions.x-rustgen-is-datetime}} + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")]{{/vendorExtensions.x-rustgen-is-datetime}} pub {{name}}: {{#isEnum}}Option<{{classname}}{{enumName}}>{{/isEnum}}{{^isEnum}}{{#isListContainer}}Vec<{{#items}}{{{datatype}}}{{/items}}>{{/isListContainer}}{{^isListContainer}}{{#isContainer}}HashMap{{/isContainer}}{{^isContainer}}{{{datatype}}}{{/isContainer}}{{/isListContainer}}{{/isEnum}}{{#vendorExtensions}}{{/vendorExtensions}}, {{/required}}{{^required}} - #[serde(skip_serializing_if="Option::is_none")] + #[serde(skip_serializing_if="Option::is_none")]{{#vendorExtensions.x-rustgen-is-datetime}} + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")]{{/vendorExtensions.x-rustgen-is-datetime}} pub {{name}}: Option<{{#isEnum}}{{classname}}{{enumName}}{{/isEnum}}{{^isEnum}}{{#isListContainer}}Vec<{{#items}}{{{datatype}}}{{/items}}>{{/isListContainer}}{{^isListContainer}}{{#isContainer}}HashMap{{/isContainer}}{{^isContainer}}{{{datatype}}}{{/isContainer}}{{/isListContainer}}{{/isEnum}}{{#vendorExtensions}}{{/vendorExtensions}}>, {{/required}} diff --git a/codegen/target/generated-sources/Cargo.toml b/codegen/target/generated-sources/Cargo.toml index 099feabd..225cc8f2 100644 --- a/codegen/target/generated-sources/Cargo.toml +++ b/codegen/target/generated-sources/Cargo.toml @@ -7,7 +7,7 @@ license = "Apache-2.0" edition = "2018" [dependencies] -chrono = { version = "0.4", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } +time = { version = "0.3", features = ["formatting", "parsing"] } serde_with = "1.4" diff --git a/codegen/target/generated-sources/README.md b/codegen/target/generated-sources/README.md index 418bbcb1..2a14d4f0 100644 --- a/codegen/target/generated-sources/README.md +++ b/codegen/target/generated-sources/README.md @@ -9,7 +9,7 @@ To see how to make this your own, look here: - API version: 1.42.0-rc.2 - Code generation suffix: 1.42.0-rc.2 -- Build date: 2022-05-31T18:04:02.348+01:00 +- Build date: 2022-06-11T11:56:09.243+01:00 This autogenerated project defines an API crate `bollard-stubs` which contains: * Data types representing the underlying data model. diff --git a/codegen/target/generated-sources/src/models.rs b/codegen/target/generated-sources/src/models.rs index ecf128b6..6367b04f 100644 --- a/codegen/target/generated-sources/src/models.rs +++ b/codegen/target/generated-sources/src/models.rs @@ -9,8 +9,7 @@ use std::collections::HashMap; use std::default::Default; use std::hash::Hash; -use chrono::DateTime; -use chrono::Utc; +use time::OffsetDateTime; fn deserialize_nonoptional_vec<'de, D: Deserializer<'de>, T: DeserializeOwned>( d: D, @@ -24,6 +23,28 @@ fn deserialize_nonoptional_map<'de, D: Deserializer<'de>, T: DeserializeOwned>( serde::Deserialize::deserialize(d).map(|x: Option<_>| x.unwrap_or(HashMap::new())) } +fn deserialize_timestamp<'de, D: Deserializer<'de>>( + d: D +) -> Result, D::Error> { + let opt: Option = serde::Deserialize::deserialize(d)?; + if let Some(s) = opt { + Ok(Some( + OffsetDateTime::parse(&s, &time::format_description::well_known::Rfc3339) + .map_err(|e| serde::de::Error::custom(format!("{:?}", e)))?, + )) + } else { + Ok(None) + } +} + +fn serialize_timestamp(date: &Option, s: S) -> Result { + match date { + Some(inner) => Ok(s.serialize_str(&inner.format(&time::format_description::well_known::Rfc3339) + .map_err(|e| serde::ser::Error::custom(format!("{:?}", e)))?)?), + None => Ok(s.serialize_str("")?) + } +} + /// Address represents an IPv4 or IPv6 IP address. #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] @@ -94,12 +115,14 @@ pub struct BuildCache { /// Date and time at which the build cache was created in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub created_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub created_at: Option, /// Date and time at which the build cache was last used in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "LastUsedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub last_used_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub last_used_at: Option, #[serde(rename = "UsageCount")] #[serde(skip_serializing_if="Option::is_none")] @@ -171,12 +194,14 @@ pub struct ClusterInfo { /// Date and time at which the swarm was initialised in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub created_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub created_at: Option, /// Date and time at which the swarm was last updated in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub updated_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub updated_at: Option, #[serde(rename = "Spec")] #[serde(skip_serializing_if="Option::is_none")] @@ -235,11 +260,13 @@ pub struct Config { #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub created_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub created_at: Option, #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub updated_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub updated_at: Option, #[serde(rename = "Spec")] #[serde(skip_serializing_if="Option::is_none")] @@ -1695,12 +1722,14 @@ pub struct HealthcheckResult { /// Date and time at which this check started in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "Start")] #[serde(skip_serializing_if="Option::is_none")] - pub start: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub start: Option, /// Date and time at which this check ended in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "End")] #[serde(skip_serializing_if="Option::is_none")] - pub end: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub end: Option, /// ExitCode meanings: - `0` healthy - `1` unhealthy - `2` reserved (considered unhealthy) - other values: error running probe #[serde(rename = "ExitCode")] @@ -2334,7 +2363,8 @@ pub struct ImageInspectMetadata { /// Date and time at which the image was last tagged in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. This information is only available if the image was tagged locally, and omitted otherwise. #[serde(rename = "LastTagTime")] #[serde(skip_serializing_if="Option::is_none")] - pub last_tag_time: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub last_tag_time: Option, } @@ -2934,7 +2964,8 @@ pub struct Network { #[serde(rename = "Created")] #[serde(skip_serializing_if="Option::is_none")] - pub created: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub created: Option, #[serde(rename = "Scope")] #[serde(skip_serializing_if="Option::is_none")] @@ -3241,12 +3272,14 @@ pub struct Node { /// Date and time at which the node was added to the swarm in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub created_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub created_at: Option, /// Date and time at which the node was last updated in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub updated_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub updated_at: Option, #[serde(rename = "Spec")] #[serde(skip_serializing_if="Option::is_none")] @@ -4425,11 +4458,13 @@ pub struct Secret { #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub created_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub created_at: Option, #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub updated_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub updated_at: Option, #[serde(rename = "Spec")] #[serde(skip_serializing_if="Option::is_none")] @@ -4478,11 +4513,13 @@ pub struct Service { #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub created_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub created_at: Option, #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub updated_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub updated_at: Option, #[serde(rename = "Spec")] #[serde(skip_serializing_if="Option::is_none")] @@ -4559,7 +4596,8 @@ pub struct ServiceJobStatus { /// The last time, as observed by the server, that this job was started. #[serde(rename = "LastExecution")] #[serde(skip_serializing_if="Option::is_none")] - pub last_execution: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub last_execution: Option, } @@ -4937,11 +4975,13 @@ pub struct ServiceUpdateStatus { #[serde(rename = "StartedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub started_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub started_at: Option, #[serde(rename = "CompletedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub completed_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub completed_at: Option, #[serde(rename = "Message")] #[serde(skip_serializing_if="Option::is_none")] @@ -5027,12 +5067,14 @@ pub struct Swarm { /// Date and time at which the swarm was initialised in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub created_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub created_at: Option, /// Date and time at which the swarm was last updated in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub updated_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub updated_at: Option, #[serde(rename = "Spec")] #[serde(skip_serializing_if="Option::is_none")] @@ -6017,11 +6059,13 @@ pub struct Task { #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub created_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub created_at: Option, #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub updated_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub updated_at: Option, /// Name of the task. #[serde(rename = "Name")] @@ -6744,7 +6788,8 @@ impl ::std::str::FromStr for TaskState { pub struct TaskStatus { #[serde(rename = "Timestamp")] #[serde(skip_serializing_if="Option::is_none")] - pub timestamp: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub timestamp: Option, #[serde(rename = "State")] #[serde(skip_serializing_if="Option::is_none")] @@ -6840,7 +6885,8 @@ pub struct Volume { /// Date/Time the volume was created. #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] - pub created_at: Option>, + #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] + pub created_at: Option, /// Low-level details about the volume, provided by the volume driver. Details are returned as a map with key/value pairs: `{\"key\":\"value\",\"key2\":\"value2\"}`. The `Status` field is optional, and is omitted if the volume driver does not support this feature. #[serde(rename = "Status")] diff --git a/examples/hoover.rs b/examples/hoover.rs index a20e4d74..947833e0 100644 --- a/examples/hoover.rs +++ b/examples/hoover.rs @@ -5,7 +5,7 @@ use bollard::{ container::PruneContainersOptions, image::PruneImagesOptions, network::PruneNetworksOptions, volume::PruneVolumesOptions, }; -use chrono::{Duration, Utc}; +use time::{Duration, OffsetDateTime}; use std::collections::HashMap; @@ -15,8 +15,8 @@ const THRESHOLD_DAYS: i64 = 90; async fn main() -> Result<(), Box> { let docker = Docker::connect_with_socket_defaults().unwrap(); - let date = Utc::now() - Duration::days(THRESHOLD_DAYS); - let timestamp = &date.timestamp().to_string()[..]; + let date = OffsetDateTime::now_utc() - Duration::days(THRESHOLD_DAYS); + let timestamp = &date.unix_timestamp().to_string()[..]; let mut prune_filters = HashMap::new(); prune_filters.insert("until", vec![timestamp]); diff --git a/src/container.rs b/src/container.rs index db40a55e..0e6d0556 100644 --- a/src/container.rs +++ b/src/container.rs @@ -1,11 +1,11 @@ //! Container API: run docker containers and manage their lifecycle -use chrono::{DateTime, Utc}; use futures_core::Stream; use http::header::{CONNECTION, CONTENT_TYPE, UPGRADE}; use http::request::Builder; use hyper::{body::Bytes, Body, Method}; use serde::Serialize; +use time::OffsetDateTime; use tokio::io::AsyncWrite; use tokio_util::codec::FramedRead; @@ -737,8 +737,16 @@ pub struct StorageStats { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[allow(missing_docs)] pub struct Stats { - pub read: DateTime, - pub preread: DateTime, + #[serde( + deserialize_with = "crate::docker::deserialize_rfc3339", + serialize_with = "crate::docker::serialize_rfc3339" + )] + pub read: OffsetDateTime, + #[serde( + deserialize_with = "crate::docker::deserialize_rfc3339", + serialize_with = "crate::docker::serialize_rfc3339" + )] + pub preread: OffsetDateTime, pub num_procs: u32, pub pids_stats: PidsStats, pub network: Option, diff --git a/src/docker.rs b/src/docker.rs index 23bc9c48..40f504d8 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -15,7 +15,6 @@ use std::time::Duration; #[cfg(feature = "ct_logs")] use std::time::SystemTime; -use chrono::{DateTime, Utc}; use futures_core::Stream; use futures_util::future::FutureExt; use futures_util::future::TryFutureExt; @@ -30,6 +29,7 @@ use hyper_rustls::HttpsConnector; use hyperlocal::UnixConnector; #[cfg(feature = "ssl")] use rustls::sign::{CertifiedKey, RsaSigningKey}; +use time::OffsetDateTime; use tokio::io::{split, AsyncRead, AsyncWrite}; use tokio_util::codec::FramedRead; @@ -187,15 +187,34 @@ where ) } +pub fn deserialize_rfc3339<'de, D: serde::Deserializer<'de>>( + d: D, +) -> Result { + let s: String = serde::Deserialize::deserialize(d)?; + OffsetDateTime::parse(&s, &time::format_description::well_known::Rfc3339) + .map_err(|e| serde::de::Error::custom(format!("{:?}", e))) +} + +pub fn serialize_rfc3339( + date: &OffsetDateTime, + s: S, +) -> Result { + s.serialize_str( + &date + .format(&time::format_description::well_known::Rfc3339) + .map_err(|e| serde::ser::Error::custom(format!("{:?}", e)))?, + ) +} + pub(crate) fn serialize_as_timestamp( - opt: &Option>, + opt: &Option, s: S, ) -> Result where S: serde::Serializer, { match opt { - Some(t) => s.serialize_str(&format!("{}.{}", t.timestamp(), t.timestamp_subsec_nanos())), + Some(t) => s.serialize_str(&format!("{}.{}", t.unix_timestamp(), t.unix_timestamp_nanos())), None => s.serialize_str(""), } } diff --git a/src/system.rs b/src/system.rs index 74325045..5b10ea5a 100644 --- a/src/system.rs +++ b/src/system.rs @@ -1,11 +1,11 @@ //! System API: interface for interacting with the Docker server and/or Registry. -use chrono::{DateTime, Utc}; use futures_core::Stream; use http::request::Builder; use hyper::{Body, Method}; use serde::ser::Serialize; use serde_json::value::Value; +use time::OffsetDateTime; use std::collections::HashMap; use std::hash::Hash; @@ -103,15 +103,14 @@ pub struct VersionComponents { /// ## Examples /// /// ```rust -/// # extern crate chrono; /// use bollard::system::EventsOptions; -/// use chrono::{Duration, Utc}; +/// use time::{Duration, OffsetDateTime}; /// use std::collections::HashMap; /// /// # fn main() { /// EventsOptions::{ -/// since: Some(Utc::now() - Duration::minutes(20)), -/// until: Some(Utc::now()), +/// since: Some(OffsetDateTime::now_utc() - Duration::minutes(20)), +/// until: Some(OffsetDateTime::now_utc()), /// filters: HashMap::new() /// }; /// # } @@ -123,10 +122,10 @@ where { /// Show events created since this timestamp then stream new events. #[serde(serialize_with = "crate::docker::serialize_as_timestamp")] - pub since: Option>, + pub since: Option, /// Show events created until this timestamp then stop streaming. #[serde(serialize_with = "crate::docker::serialize_as_timestamp")] - pub until: Option>, + pub until: Option, /// A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: /// - `config=` config name or ID /// - `container=` container name or ID @@ -250,15 +249,15 @@ impl Docker { /// /// ```rust /// use bollard::system::EventsOptions; - /// use chrono::{Duration, Utc}; + /// use time::{Duration, OffsetDateTime}; /// use std::collections::HashMap; /// /// # use bollard::Docker; /// # let docker = Docker::connect_with_http_defaults().unwrap(); /// /// docker.events(Some(EventsOptions:: { - /// since: Some(Utc::now() - Duration::minutes(20)), - /// until: Some(Utc::now()), + /// since: Some(OffsetDateTime::now_utc() - Duration::minutes(20)), + /// until: Some(OffsetDateTime::now_utc()), /// filters: HashMap::new(), /// })); /// ``` diff --git a/tests/system_test.rs b/tests/system_test.rs index c9a3caba..5d1fb347 100644 --- a/tests/system_test.rs +++ b/tests/system_test.rs @@ -5,11 +5,11 @@ use bollard::models::*; use bollard::system::*; use bollard::Docker; -use chrono::Utc; use futures_util::future; use futures_util::stream::select; use futures_util::stream::StreamExt; use futures_util::stream::TryStreamExt; +use time::OffsetDateTime; use tokio::runtime::Runtime; #[macro_use] @@ -78,7 +78,7 @@ async fn events_until_forever_test(docker: Docker) -> Result<(), Error> { format!("{}hello-world:linux", registry_http_addr()) }; - let start_time = Utc::now(); + let start_time = OffsetDateTime::now_utc(); let stream = docker.events(Some(EventsOptions:: { since: Some(start_time), From 26cf09fae10b516b71d480334a2e4fa8882a4ddb Mon Sep 17 00:00:00 2001 From: Niel Drummond Date: Sat, 11 Jun 2022 13:42:32 +0100 Subject: [PATCH 3/8] Make chrono and time optional --- .circleci/config.yml | 2 +- Cargo.toml | 7 +- .../src/main/java/bollard/BollardCodegen.java | 6 +- .../src/main/resources/bollard/Cargo.mustache | 3 +- .../main/resources/bollard/models.mustache | 32 ++++++-- codegen/target/generated-sources/Cargo.toml | 3 +- codegen/target/generated-sources/README.md | 2 +- .../target/generated-sources/src/models.rs | 82 ++++++++++++------- dockerfiles/bin/run_integration_tests.sh | 2 +- examples/hoover.rs | 27 +++++- src/container.rs | 15 +++- src/docker.rs | 32 ++++++-- src/read.rs | 1 - src/system.rs | 21 ++++- tests/service_test.rs | 1 - tests/system_test.rs | 9 +- 16 files changed, 179 insertions(+), 66 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 832dca83..d481eb68 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -37,7 +37,7 @@ jobs: - checkout - setup_remote_docker - run: docker build -t bollard . - - run: docker run -ti --rm bollard cargo test --target x86_64-unknown-linux-gnu --doc + - run: docker run -ti --rm bollard cargo test --features time --target x86_64-unknown-linux-gnu --doc test_clippy: docker: - image: docker:20.10.16 diff --git a/Cargo.toml b/Cargo.toml index 3b392b08..3ea909b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,11 +21,14 @@ test_macos = [] # Enable rustls / ssl ssl = ["dirs-next", "hyper-rustls", "rustls", "rustls-native-certs", "rustls-pemfile", "webpki", "webpki-roots"] ct_logs = ["ssl", "ct-logs"] +chrono = ["dep:chrono", "bollard-stubs/chrono"] +time = ["dep:time", "bollard-stubs/time"] [dependencies] base64 = "0.13" -bollard-stubs = { version = "=1.42.0-rc.2", path = "codegen/target/generated-sources" } +bollard-stubs = { version = "=1.42.0-rc.2", path = "codegen/target/generated-sources", default-features = false } bytes = "1" +chrono = { version = "0.4", features = ["serde"], optional = true } ct-logs = { version = "0.9.0", optional = true } dirs-next = { version = "2.0", optional = true } futures-core = "0.3" @@ -45,7 +48,7 @@ serde_json = "1.0" serde_urlencoded = "0.7" tokio = { version = "1.7", features = ["time", "net", "io-util"] } thiserror = "1.0" -time = { version = "0.3", features = ["formatting", "parsing"] } +time = { version = "0.3", features = ["formatting", "parsing"], optional = true } tokio-util = { version = "0.7", features = ["codec"] } url = "2.2" webpki-roots = { version = "0.22", optional = true } diff --git a/codegen/src/main/java/bollard/BollardCodegen.java b/codegen/src/main/java/bollard/BollardCodegen.java index fd207061..616623f1 100644 --- a/codegen/src/main/java/bollard/BollardCodegen.java +++ b/codegen/src/main/java/bollard/BollardCodegen.java @@ -22,7 +22,7 @@ public class BollardCodegen extends RustServerCodegen { public BollardCodegen() { super(); - typeMapping.put("DateTime", "OffsetDateTime"); + typeMapping.put("DateTime", "BollardDate"); } // Declare custom additions to inline enums that are behaving differently @@ -124,10 +124,10 @@ public Map postProcessAllModels(Map objs) { } else if (prop.name.equals("_type")) { prop.name = "typ"; } - if (prop.dataFormat != null && (prop.dataFormat.equals("dateTime") || prop.datatype.equals("OffsetDateTime"))) { + if (prop.dataFormat != null && (prop.dataFormat.equals("dateTime") || prop.datatype.equals("BollardDate"))) { // set DateTime format on properties where appropriate prop.vendorExtensions.put("x-rustgen-is-datetime", true); - prop.datatype = "OffsetDateTime"; + prop.datatype = "BollardDate"; } if (prop.isEnum) { if (enumToString.contains(model.classname)) { diff --git a/codegen/src/main/resources/bollard/Cargo.mustache b/codegen/src/main/resources/bollard/Cargo.mustache index 3a242c4d..be6986f8 100644 --- a/codegen/src/main/resources/bollard/Cargo.mustache +++ b/codegen/src/main/resources/bollard/Cargo.mustache @@ -7,7 +7,8 @@ license = "Apache-2.0" edition = "2018" [dependencies] +chrono = { version = "0.4", features = ["serde"], optional = true } serde = { version = "1.0", features = ["derive"] } -time = { version = "0.3", features = ["formatting", "parsing"] } +time = { version = "0.3", features = ["formatting", "parsing"], optional = true } serde_with = "1.4" diff --git a/codegen/src/main/resources/bollard/models.mustache b/codegen/src/main/resources/bollard/models.mustache index 50bd5a82..76f7d1ee 100644 --- a/codegen/src/main/resources/bollard/models.mustache +++ b/codegen/src/main/resources/bollard/models.mustache @@ -9,8 +9,6 @@ use std::collections::HashMap; use std::default::Default; use std::hash::Hash; -use time::OffsetDateTime; - fn deserialize_nonoptional_vec<'de, D: Deserializer<'de>, T: DeserializeOwned>( d: D, ) -> Result, D::Error> { @@ -23,13 +21,21 @@ fn deserialize_nonoptional_map<'de, D: Deserializer<'de>, T: DeserializeOwned>( serde::Deserialize::deserialize(d).map(|x: Option<_>| x.unwrap_or(HashMap::new())) } +#[cfg(feature = "time")] +pub type BollardDate = time::OffsetDateTime; +#[cfg(feature = "chrono")] +pub type BollardDate = chrono::DateTime; +#[cfg(not(any(feature = "chrono", feature = "time")))] +pub type BollardDate = String; + +#[cfg(feature = "time")] fn deserialize_timestamp<'de, D: Deserializer<'de>>( d: D -) -> Result, D::Error> { +) -> Result, D::Error> { let opt: Option = serde::Deserialize::deserialize(d)?; if let Some(s) = opt { Ok(Some( - OffsetDateTime::parse(&s, &time::format_description::well_known::Rfc3339) + time::OffsetDateTime::parse(&s, &time::format_description::well_known::Rfc3339) .map_err(|e| serde::de::Error::custom(format!("{:?}", e)))?, )) } else { @@ -37,7 +43,15 @@ fn deserialize_timestamp<'de, D: Deserializer<'de>>( } } -fn serialize_timestamp(date: &Option, s: S) -> Result { +#[cfg(not(feature = "time"))] +fn deserialize_timestamp<'de, D: Deserializer<'de>>( + d: D +) -> Result, D::Error> { + serde::Deserialize::deserialize(d) +} + +#[cfg(feature = "time")] +fn serialize_timestamp(date: &Option, s: S) -> Result { match date { Some(inner) => Ok(s.serialize_str(&inner.format(&time::format_description::well_known::Rfc3339) .map_err(|e| serde::ser::Error::custom(format!("{:?}", e)))?)?), @@ -45,6 +59,14 @@ fn serialize_timestamp(date: &Option, s: S) -> Re } } +#[cfg(not(feature = "time"))] +fn serialize_timestamp(date: &Option, s: S) -> Result { + match date { + Some(inner) => s.serialize_some(inner), + None => s.serialize_none() + } +} + {{#models}}{{#model}} {{#description}}/// {{{description}}} {{/description}}{{#isEnum}}/// Enumeration of values. diff --git a/codegen/target/generated-sources/Cargo.toml b/codegen/target/generated-sources/Cargo.toml index 225cc8f2..31ad109c 100644 --- a/codegen/target/generated-sources/Cargo.toml +++ b/codegen/target/generated-sources/Cargo.toml @@ -7,7 +7,8 @@ license = "Apache-2.0" edition = "2018" [dependencies] +chrono = { version = "0.4", features = ["serde"], optional = true } serde = { version = "1.0", features = ["derive"] } -time = { version = "0.3", features = ["formatting", "parsing"] } +time = { version = "0.3", features = ["formatting", "parsing"], optional = true } serde_with = "1.4" diff --git a/codegen/target/generated-sources/README.md b/codegen/target/generated-sources/README.md index 2a14d4f0..3a157dd5 100644 --- a/codegen/target/generated-sources/README.md +++ b/codegen/target/generated-sources/README.md @@ -9,7 +9,7 @@ To see how to make this your own, look here: - API version: 1.42.0-rc.2 - Code generation suffix: 1.42.0-rc.2 -- Build date: 2022-06-11T11:56:09.243+01:00 +- Build date: 2022-06-11T13:13:57.645+01:00 This autogenerated project defines an API crate `bollard-stubs` which contains: * Data types representing the underlying data model. diff --git a/codegen/target/generated-sources/src/models.rs b/codegen/target/generated-sources/src/models.rs index 6367b04f..28b073f8 100644 --- a/codegen/target/generated-sources/src/models.rs +++ b/codegen/target/generated-sources/src/models.rs @@ -9,8 +9,6 @@ use std::collections::HashMap; use std::default::Default; use std::hash::Hash; -use time::OffsetDateTime; - fn deserialize_nonoptional_vec<'de, D: Deserializer<'de>, T: DeserializeOwned>( d: D, ) -> Result, D::Error> { @@ -23,13 +21,21 @@ fn deserialize_nonoptional_map<'de, D: Deserializer<'de>, T: DeserializeOwned>( serde::Deserialize::deserialize(d).map(|x: Option<_>| x.unwrap_or(HashMap::new())) } +#[cfg(feature = "time")] +pub type BollardDate = time::OffsetDateTime; +#[cfg(feature = "chrono")] +pub type BollardDate = chrono::DateTime; +#[cfg(not(any(feature = "chrono", feature = "time")))] +pub type BollardDate = String; + +#[cfg(feature = "time")] fn deserialize_timestamp<'de, D: Deserializer<'de>>( d: D -) -> Result, D::Error> { +) -> Result, D::Error> { let opt: Option = serde::Deserialize::deserialize(d)?; if let Some(s) = opt { Ok(Some( - OffsetDateTime::parse(&s, &time::format_description::well_known::Rfc3339) + time::OffsetDateTime::parse(&s, &time::format_description::well_known::Rfc3339) .map_err(|e| serde::de::Error::custom(format!("{:?}", e)))?, )) } else { @@ -37,7 +43,15 @@ fn deserialize_timestamp<'de, D: Deserializer<'de>>( } } -fn serialize_timestamp(date: &Option, s: S) -> Result { +#[cfg(not(feature = "time"))] +fn deserialize_timestamp<'de, D: Deserializer<'de>>( + d: D +) -> Result, D::Error> { + serde::Deserialize::deserialize(d) +} + +#[cfg(feature = "time")] +fn serialize_timestamp(date: &Option, s: S) -> Result { match date { Some(inner) => Ok(s.serialize_str(&inner.format(&time::format_description::well_known::Rfc3339) .map_err(|e| serde::ser::Error::custom(format!("{:?}", e)))?)?), @@ -45,6 +59,14 @@ fn serialize_timestamp(date: &Option, s: S) -> Re } } +#[cfg(not(feature = "time"))] +fn serialize_timestamp(date: &Option, s: S) -> Result { + match date { + Some(inner) => s.serialize_some(inner), + None => s.serialize_none() + } +} + /// Address represents an IPv4 or IPv6 IP address. #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] @@ -116,13 +138,13 @@ pub struct BuildCache { #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub created_at: Option, + pub created_at: Option, /// Date and time at which the build cache was last used in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "LastUsedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub last_used_at: Option, + pub last_used_at: Option, #[serde(rename = "UsageCount")] #[serde(skip_serializing_if="Option::is_none")] @@ -195,13 +217,13 @@ pub struct ClusterInfo { #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub created_at: Option, + pub created_at: Option, /// Date and time at which the swarm was last updated in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub updated_at: Option, + pub updated_at: Option, #[serde(rename = "Spec")] #[serde(skip_serializing_if="Option::is_none")] @@ -261,12 +283,12 @@ pub struct Config { #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub created_at: Option, + pub created_at: Option, #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub updated_at: Option, + pub updated_at: Option, #[serde(rename = "Spec")] #[serde(skip_serializing_if="Option::is_none")] @@ -1723,13 +1745,13 @@ pub struct HealthcheckResult { #[serde(rename = "Start")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub start: Option, + pub start: Option, /// Date and time at which this check ended in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "End")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub end: Option, + pub end: Option, /// ExitCode meanings: - `0` healthy - `1` unhealthy - `2` reserved (considered unhealthy) - other values: error running probe #[serde(rename = "ExitCode")] @@ -2364,7 +2386,7 @@ pub struct ImageInspectMetadata { #[serde(rename = "LastTagTime")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub last_tag_time: Option, + pub last_tag_time: Option, } @@ -2965,7 +2987,7 @@ pub struct Network { #[serde(rename = "Created")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub created: Option, + pub created: Option, #[serde(rename = "Scope")] #[serde(skip_serializing_if="Option::is_none")] @@ -3273,13 +3295,13 @@ pub struct Node { #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub created_at: Option, + pub created_at: Option, /// Date and time at which the node was last updated in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub updated_at: Option, + pub updated_at: Option, #[serde(rename = "Spec")] #[serde(skip_serializing_if="Option::is_none")] @@ -4459,12 +4481,12 @@ pub struct Secret { #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub created_at: Option, + pub created_at: Option, #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub updated_at: Option, + pub updated_at: Option, #[serde(rename = "Spec")] #[serde(skip_serializing_if="Option::is_none")] @@ -4514,12 +4536,12 @@ pub struct Service { #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub created_at: Option, + pub created_at: Option, #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub updated_at: Option, + pub updated_at: Option, #[serde(rename = "Spec")] #[serde(skip_serializing_if="Option::is_none")] @@ -4597,7 +4619,7 @@ pub struct ServiceJobStatus { #[serde(rename = "LastExecution")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub last_execution: Option, + pub last_execution: Option, } @@ -4976,12 +4998,12 @@ pub struct ServiceUpdateStatus { #[serde(rename = "StartedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub started_at: Option, + pub started_at: Option, #[serde(rename = "CompletedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub completed_at: Option, + pub completed_at: Option, #[serde(rename = "Message")] #[serde(skip_serializing_if="Option::is_none")] @@ -5068,13 +5090,13 @@ pub struct Swarm { #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub created_at: Option, + pub created_at: Option, /// Date and time at which the swarm was last updated in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub updated_at: Option, + pub updated_at: Option, #[serde(rename = "Spec")] #[serde(skip_serializing_if="Option::is_none")] @@ -6060,12 +6082,12 @@ pub struct Task { #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub created_at: Option, + pub created_at: Option, #[serde(rename = "UpdatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub updated_at: Option, + pub updated_at: Option, /// Name of the task. #[serde(rename = "Name")] @@ -6789,7 +6811,7 @@ pub struct TaskStatus { #[serde(rename = "Timestamp")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub timestamp: Option, + pub timestamp: Option, #[serde(rename = "State")] #[serde(skip_serializing_if="Option::is_none")] @@ -6886,7 +6908,7 @@ pub struct Volume { #[serde(rename = "CreatedAt")] #[serde(skip_serializing_if="Option::is_none")] #[serde(default, deserialize_with = "deserialize_timestamp", serialize_with = "serialize_timestamp")] - pub created_at: Option, + pub created_at: Option, /// Low-level details about the volume, provided by the volume driver. Details are returned as a map with key/value pairs: `{\"key\":\"value\",\"key2\":\"value2\"}`. The `Status` field is optional, and is omitted if the volume driver does not support this feature. #[serde(rename = "Status")] diff --git a/dockerfiles/bin/run_integration_tests.sh b/dockerfiles/bin/run_integration_tests.sh index 73c744ae..47322ab9 100755 --- a/dockerfiles/bin/run_integration_tests.sh +++ b/dockerfiles/bin/run_integration_tests.sh @@ -20,4 +20,4 @@ docker push localhost:5000/hello-world:linux docker push localhost:5000/fussybeaver/uhttpd docker push localhost:5000/alpine docker swarm init -docker run -e RUST_LOG=bollard=debug -e REGISTRY_PASSWORD -e REGISTRY_HTTP_ADDR=localhost:5000 -v /var/run/docker.sock:/var/run/docker.sock -ti --rm bollard cargo test -- --test-threads 1 +docker run -e RUST_LOG=bollard=debug -e REGISTRY_PASSWORD -e REGISTRY_HTTP_ADDR=localhost:5000 -v /var/run/docker.sock:/var/run/docker.sock -ti --rm bollard cargo test --tests -- --test-threads 1 diff --git a/examples/hoover.rs b/examples/hoover.rs index 947833e0..5b74166f 100644 --- a/examples/hoover.rs +++ b/examples/hoover.rs @@ -5,8 +5,6 @@ use bollard::{ container::PruneContainersOptions, image::PruneImagesOptions, network::PruneNetworksOptions, volume::PruneVolumesOptions, }; -use time::{Duration, OffsetDateTime}; - use std::collections::HashMap; const THRESHOLD_DAYS: i64 = 90; @@ -15,8 +13,29 @@ const THRESHOLD_DAYS: i64 = 90; async fn main() -> Result<(), Box> { let docker = Docker::connect_with_socket_defaults().unwrap(); - let date = OffsetDateTime::now_utc() - Duration::days(THRESHOLD_DAYS); - let timestamp = &date.unix_timestamp().to_string()[..]; + #[cfg(feature = "time")] + let timestamp = { + let date = time::OffsetDateTime::now_utc() - time::Duration::days(THRESHOLD_DAYS); + &date.unix_timestamp().to_string()[..] + }; + + #[cfg(feature = "chrono")] + let timestamp = { + let date = chrono::Utc::now() - chrono::Duration::days(THRESHOLD_DAYS); + &date.timestamp().to_string()[..] + }; + + #[cfg(not(any(feature = "time", feature = "chrono")))] + let timestamp = { + use std::convert::TryInto; + let date = std::time::SystemTime::now() + - std::time::Duration::from_secs((THRESHOLD_DAYS * 86400).try_into().unwrap()); + &date + .duration_since(std::time::SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs() + .to_string()[..] + }; let mut prune_filters = HashMap::new(); prune_filters.insert("until", vec![timestamp]); diff --git a/src/container.rs b/src/container.rs index 0e6d0556..5f97a15e 100644 --- a/src/container.rs +++ b/src/container.rs @@ -5,7 +5,6 @@ use http::header::{CONNECTION, CONTENT_TYPE, UPGRADE}; use http::request::Builder; use hyper::{body::Bytes, Body, Method}; use serde::Serialize; -use time::OffsetDateTime; use tokio::io::AsyncWrite; use tokio_util::codec::FramedRead; @@ -737,16 +736,26 @@ pub struct StorageStats { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[allow(missing_docs)] pub struct Stats { + #[cfg(feature = "time")] #[serde( deserialize_with = "crate::docker::deserialize_rfc3339", serialize_with = "crate::docker::serialize_rfc3339" )] - pub read: OffsetDateTime, + pub read: time::OffsetDateTime, + #[cfg(feature = "time")] #[serde( deserialize_with = "crate::docker::deserialize_rfc3339", serialize_with = "crate::docker::serialize_rfc3339" )] - pub preread: OffsetDateTime, + pub preread: time::OffsetDateTime, + #[cfg(feature = "chrono")] + pub read: chrono::DateTime, + #[cfg(feature = "chrono")] + pub preread: chrono::DateTime, + #[cfg(not(any(feature = "chrono", feature = "time")))] + pub read: String, + #[cfg(not(any(feature = "chrono", feature = "time")))] + pub preread: String, pub num_procs: u32, pub pids_stats: PidsStats, pub network: Option, diff --git a/src/docker.rs b/src/docker.rs index 40f504d8..fa4694cc 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -29,7 +29,6 @@ use hyper_rustls::HttpsConnector; use hyperlocal::UnixConnector; #[cfg(feature = "ssl")] use rustls::sign::{CertifiedKey, RsaSigningKey}; -use time::OffsetDateTime; use tokio::io::{split, AsyncRead, AsyncWrite}; use tokio_util::codec::FramedRead; @@ -187,16 +186,18 @@ where ) } +#[cfg(feature = "time")] pub fn deserialize_rfc3339<'de, D: serde::Deserializer<'de>>( d: D, -) -> Result { +) -> Result { let s: String = serde::Deserialize::deserialize(d)?; - OffsetDateTime::parse(&s, &time::format_description::well_known::Rfc3339) + time::OffsetDateTime::parse(&s, &time::format_description::well_known::Rfc3339) .map_err(|e| serde::de::Error::custom(format!("{:?}", e))) } +#[cfg(feature = "time")] pub fn serialize_rfc3339( - date: &OffsetDateTime, + date: &time::OffsetDateTime, s: S, ) -> Result { s.serialize_str( @@ -206,15 +207,34 @@ pub fn serialize_rfc3339( ) } +#[cfg(feature = "time")] pub(crate) fn serialize_as_timestamp( - opt: &Option, + opt: &Option, s: S, ) -> Result where S: serde::Serializer, { match opt { - Some(t) => s.serialize_str(&format!("{}.{}", t.unix_timestamp(), t.unix_timestamp_nanos())), + Some(t) => s.serialize_str(&format!( + "{}.{}", + t.unix_timestamp(), + t.unix_timestamp_nanos() + )), + None => s.serialize_str(""), + } +} + +#[cfg(feature = "chrono")] +pub(crate) fn serialize_as_timestamp( + opt: &Option, + s: S, +) -> Result +where + S: serde::Serializer, +{ + match opt { + Some(t) => s.serialize_str(&format!("{}.{}", t.timestamp(), t.timestamp_subsec_nanos())), None => s.serialize_str(""), } } diff --git a/src/read.rs b/src/read.rs index ff57c656..8983158d 100644 --- a/src/read.rs +++ b/src/read.rs @@ -348,7 +348,6 @@ mod tests { #[test] fn json_decode_lacking_newline() { - env_logger::try_init().unwrap(); let mut buf = BytesMut::from(&b"{}"[..]); let mut codec: JsonLineDecoder> = JsonLineDecoder::new(); diff --git a/src/system.rs b/src/system.rs index 5b10ea5a..0a3c02a3 100644 --- a/src/system.rs +++ b/src/system.rs @@ -5,7 +5,6 @@ use http::request::Builder; use hyper::{Body, Method}; use serde::ser::Serialize; use serde_json::value::Value; -use time::OffsetDateTime; use std::collections::HashMap; use std::hash::Hash; @@ -121,11 +120,27 @@ where T: Into + Eq + Hash + Serialize, { /// Show events created since this timestamp then stream new events. + #[cfg(feature = "chrono")] #[serde(serialize_with = "crate::docker::serialize_as_timestamp")] - pub since: Option, + pub since: Option>, /// Show events created until this timestamp then stop streaming. + #[cfg(feature = "chrono")] #[serde(serialize_with = "crate::docker::serialize_as_timestamp")] - pub until: Option, + pub until: Option>, + /// Show events created since this timestamp then stream new events. + #[cfg(feature = "time")] + #[serde(serialize_with = "crate::docker::serialize_as_timestamp")] + pub since: Option, + /// Show events created until this timestamp then stop streaming. + #[cfg(feature = "time")] + #[serde(serialize_with = "crate::docker::serialize_as_timestamp")] + pub until: Option, + /// Show events created since this timestamp then stream new events. + #[cfg(not(any(feature = "time", feature = "chrono")))] + pub since: Option, + /// Show events created until this timestamp then stop streaming. + #[cfg(not(any(feature = "time", feature = "chrono")))] + pub until: Option, /// A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: /// - `config=` config name or ID /// - `container=` container name or ID diff --git a/tests/service_test.rs b/tests/service_test.rs index babc48f0..ebffc928 100644 --- a/tests/service_test.rs +++ b/tests/service_test.rs @@ -37,7 +37,6 @@ async fn service_create_test(docker: Docker) -> Result<(), Error> { } async fn service_list_test(docker: Docker) -> Result<(), Error> { - env_logger::init(); let image = if cfg!(windows) { format!("{}nanoserver/iis", registry_http_addr()) } else { diff --git a/tests/system_test.rs b/tests/system_test.rs index 5d1fb347..a1c7c13b 100644 --- a/tests/system_test.rs +++ b/tests/system_test.rs @@ -9,7 +9,6 @@ use futures_util::future; use futures_util::stream::select; use futures_util::stream::StreamExt; use futures_util::stream::TryStreamExt; -use time::OffsetDateTime; use tokio::runtime::Runtime; #[macro_use] @@ -71,6 +70,7 @@ async fn events_test(docker: Docker) -> Result<(), Error> { Ok(()) } +#[cfg(any(feature = "time", feature = "chrono"))] async fn events_until_forever_test(docker: Docker) -> Result<(), Error> { let image = if cfg!(windows) { format!("{}hello-world:nanoserver", registry_http_addr()) @@ -78,7 +78,10 @@ async fn events_until_forever_test(docker: Docker) -> Result<(), Error> { format!("{}hello-world:linux", registry_http_addr()) }; - let start_time = OffsetDateTime::now_utc(); + #[cfg(feature = "time")] + let start_time = time::OffsetDateTime::now_utc(); + #[cfg(feature = "chrono")] + let start_time = chrono::Utc::now(); let stream = docker.events(Some(EventsOptions:: { since: Some(start_time), @@ -161,7 +164,7 @@ fn integration_test_events() { } #[test] -#[cfg(not(windows))] +#[cfg(all(not(windows), any(feature = "chrono", feature = "time")))] fn integration_test_events_until_forever() { connect_to_docker_and_run!(events_until_forever_test); } From 39ed6d5674892cec3bde082a7da431fbaa43e686 Mon Sep 17 00:00:00 2001 From: Niel Drummond Date: Sat, 11 Jun 2022 14:06:30 +0100 Subject: [PATCH 4/8] Granular target in dockerignore --- .dockerignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index ee35a944..89e45807 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,8 @@ .DS_Store .git Cargo.lock -**/target +/target +/codegen/target/generated-sources/target doc main.rs **/workspace From ffb1b0bcdd290f2cc5d79b8d48fc4c60b714cd62 Mon Sep 17 00:00:00 2001 From: Niel Drummond Date: Sat, 11 Jun 2022 14:49:08 +0100 Subject: [PATCH 5/8] Add rustfmt to ci --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d481eb68..4a4c6012 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -62,7 +62,7 @@ jobs: - checkout - setup_remote_docker - run: docker build -t bollard . - - run: docker run -ti --rm bollard bash -c "cargo fmt -- --check --verbose" + - run: docker run -ti --rm bollard bash -c "rustup component add rustfmt && cargo fmt -- --check --verbose" workflows: version: 2 test-image: From 14fdfbeba1c23971292519ae16d6ed0e6aabe2dc Mon Sep 17 00:00:00 2001 From: Niel Drummond Date: Sat, 11 Jun 2022 15:20:37 +0100 Subject: [PATCH 6/8] Add chrono and time test ci steps --- .circleci/config.yml | 20 +++++++++++++++++++- appveyor.yml | 2 +- dockerfiles/bin/run_integration_tests.sh | 2 +- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4a4c6012..d75559c4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -29,7 +29,23 @@ jobs: - checkout - setup_remote_docker - run: docker build -t bollard . - - run: dockerfiles/bin/run_integration_tests.sh + - run: dockerfiles/bin/run_integration_tests.sh --tests + test_chrono: + docker: + - image: docker:20.10.16 + steps: + - checkout + - setup_remote_docker + - run: docker build -t bollard . + - run: dockerfiles/bin/run_integration_tests.sh --features chrono --tests + test_time: + docker: + - image: docker:20.10.16 + steps: + - checkout + - setup_remote_docker + - run: docker build -t bollard . + - run: dockerfiles/bin/run_integration_tests.sh --features time --tests test_doc: docker: - image: docker:20.10.16 @@ -70,6 +86,8 @@ workflows: - test_ssl - test_http - test_unix + - test_chrono + - test_time - test_doc - test_clippy - test_audit diff --git a/appveyor.yml b/appveyor.yml index 36d7cb4a..b7b23931 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -40,4 +40,4 @@ test_script: - ps: Set-Item -path env:RUST_BACKTRACE -value 1 - ps: Set-Item -path env:RUST_LOG -value "hyper=trace,bollard=debug" - ps: Set-Item -path env:REGISTRY_HTTP_ADDR -value localhost:5000 - - cargo test --verbose -- --nocapture --test-threads 1 + - cargo test --verbose --tests -- --nocapture --test-threads 1 diff --git a/dockerfiles/bin/run_integration_tests.sh b/dockerfiles/bin/run_integration_tests.sh index 47322ab9..fa9d04bc 100755 --- a/dockerfiles/bin/run_integration_tests.sh +++ b/dockerfiles/bin/run_integration_tests.sh @@ -20,4 +20,4 @@ docker push localhost:5000/hello-world:linux docker push localhost:5000/fussybeaver/uhttpd docker push localhost:5000/alpine docker swarm init -docker run -e RUST_LOG=bollard=debug -e REGISTRY_PASSWORD -e REGISTRY_HTTP_ADDR=localhost:5000 -v /var/run/docker.sock:/var/run/docker.sock -ti --rm bollard cargo test --tests -- --test-threads 1 +docker run -e RUST_LOG=bollard=debug -e REGISTRY_PASSWORD -e REGISTRY_HTTP_ADDR=localhost:5000 -v /var/run/docker.sock:/var/run/docker.sock -ti --rm bollard cargo test $@ -- --test-threads 1 From f7f438ef0daf352d3db93737075961b7b14f1593 Mon Sep 17 00:00:00 2001 From: Niel Drummond Date: Sat, 18 Jun 2022 09:47:29 +0100 Subject: [PATCH 7/8] Release bollard-stubs 1.42.0-rc.3 --- Cargo.toml | 4 ++-- codegen/pom.xml | 2 +- codegen/src/main/resources/bollard/Cargo.mustache | 2 +- codegen/target/generated-sources/Cargo.toml | 4 ++-- codegen/target/generated-sources/README.md | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3ea909b5..47549c77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/fussybeaver/bollard" documentation = "https://docs.rs/bollard" readme = "README.md" keywords = ["docker"] -edition = "2018" +edition = "2021" [features] # Enable tests specifically for the http connector @@ -26,7 +26,7 @@ time = ["dep:time", "bollard-stubs/time"] [dependencies] base64 = "0.13" -bollard-stubs = { version = "=1.42.0-rc.2", path = "codegen/target/generated-sources", default-features = false } +bollard-stubs = { version = "=1.42.0-rc.3", path = "codegen/target/generated-sources", default-features = false } bytes = "1" chrono = { version = "0.4", features = ["serde"], optional = true } ct-logs = { version = "0.9.0", optional = true } diff --git a/codegen/pom.xml b/codegen/pom.xml index 6c73d3e2..c785764f 100644 --- a/codegen/pom.xml +++ b/codegen/pom.xml @@ -31,7 +31,7 @@ models.rs,lib.rs,Cargo.toml,config,README.md bollard-stubs - 1.42.0-rc.2 + 1.42.0-rc.3 diff --git a/codegen/src/main/resources/bollard/Cargo.mustache b/codegen/src/main/resources/bollard/Cargo.mustache index be6986f8..b4b49b58 100644 --- a/codegen/src/main/resources/bollard/Cargo.mustache +++ b/codegen/src/main/resources/bollard/Cargo.mustache @@ -4,7 +4,7 @@ version = "{{appVersion}}" authors = [ "Bollard contributors" ] description = "Stubs used for the Bollard rust async Docker client API" license = "Apache-2.0" -edition = "2018" +edition = "2021" [dependencies] chrono = { version = "0.4", features = ["serde"], optional = true } diff --git a/codegen/target/generated-sources/Cargo.toml b/codegen/target/generated-sources/Cargo.toml index 31ad109c..d9fe2924 100644 --- a/codegen/target/generated-sources/Cargo.toml +++ b/codegen/target/generated-sources/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "bollard-stubs" -version = "1.42.0-rc.2" +version = "1.42.0-rc.3" authors = [ "Bollard contributors" ] description = "Stubs used for the Bollard rust async Docker client API" license = "Apache-2.0" -edition = "2018" +edition = "2021" [dependencies] chrono = { version = "0.4", features = ["serde"], optional = true } diff --git a/codegen/target/generated-sources/README.md b/codegen/target/generated-sources/README.md index 3a157dd5..595462c8 100644 --- a/codegen/target/generated-sources/README.md +++ b/codegen/target/generated-sources/README.md @@ -7,9 +7,9 @@ To see how to make this your own, look here: [README](https://github.com/swagger-api/swagger-codegen/blob/master/README.md) -- API version: 1.42.0-rc.2 -- Code generation suffix: 1.42.0-rc.2 -- Build date: 2022-06-11T13:13:57.645+01:00 +- API version: 1.42.0-rc.3 +- Code generation suffix: 1.42.0-rc.3 +- Build date: 2022-06-18T09:45:11.451+01:00 This autogenerated project defines an API crate `bollard-stubs` which contains: * Data types representing the underlying data model. From e891532943cee17da81688ec2de559179255e519 Mon Sep 17 00:00:00 2001 From: Niel Drummond Date: Sat, 18 Jun 2022 10:37:20 +0100 Subject: [PATCH 8/8] Peg bollard-stubs dependency to published crates --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 47549c77..d88892dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ time = ["dep:time", "bollard-stubs/time"] [dependencies] base64 = "0.13" -bollard-stubs = { version = "=1.42.0-rc.3", path = "codegen/target/generated-sources", default-features = false } +bollard-stubs = { version = "=1.42.0-rc.3", default-features = false } bytes = "1" chrono = { version = "0.4", features = ["serde"], optional = true } ct-logs = { version = "0.9.0", optional = true }