laskoviymishka commented on code in PR #3090:
URL: https://github.com/apache/iceberg-rust/pull/3090#discussion_r3892690476


##########
crates/iceberg/public-api.txt:
##########
@@ -739,6 +739,10 @@ impl core::clone::Clone for iceberg::io::FileIO
 pub fn iceberg::io::FileIO::clone(&self) -> iceberg::io::FileIO
 impl core::fmt::Debug for iceberg::io::FileIO
 pub fn iceberg::io::FileIO::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> 
core::fmt::Result
+impl serde_core::ser::Serialize for iceberg::io::FileIO
+pub fn iceberg::io::FileIO::serialize<__S>(&self, __serializer: __S) -> 
core::result::Result<<__S as serde_core::ser::Serializer>::Ok, <__S as 
serde_core::ser::Serializer>::Error> where __S: serde_core::ser::Serializer

Review Comment:
   This is the accurate version of the "hard to change later" worry from 
round-1 — the disclaimer covers the JSON shape, but `impl Serialize/Deserialize 
for FileIO` landing in `public-api.txt` is a semver commitment on its own, 
independent of the format. If we ever swap the mechanism (typetag → something 
else) and drop the impls, that's a breaking change for anyone doing 
`serde_json::to_string(&file_io)` or deriving over a wrapper.
   
   I'd gate the derives behind a `serde` feature now while it's a one-liner — 
`#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]` — so the impls 
are opt-in rather than a permanent part of the surface. Not blocking, but much 
cheaper now than walking it back.



##########
crates/iceberg/src/io/file_io.rs:
##########
@@ -544,4 +564,81 @@ mod tests {
         assert_eq!(file_io.config().get("key1"), Some(&"value1".to_string()));
         assert_eq!(file_io.config().get("key2"), Some(&"value2".to_string()));
     }
+
+    #[tokio::test]
+    async fn test_memory_file_io_serialization_roundtrip() {
+        let file_io = FileIOBuilder::new(Arc::new(MemoryStorageFactory))
+            .with_prop("test-property", "test-value")
+            .with_prop("s3.session-token", "test-token")
+            .build();

Review Comment:
   The loader fail-fast landed cleanly, and I flagged the lock-in risk last 
round — the unstable-format disclaimer plus dropping the shape-pin tests 
settles that half, so I'm no longer worried the format is hard to change.
   
   What's still here is the asymmetry: the loader now hard-errors on serialize, 
but the actual secret strings — `s3.secret-access-key`, `s3.session-token`, GCS 
service-account JSON, `hf.token` — pass straight through in plaintext, and this 
test pins that as intended. It reads backwards — we fail fast on the opaque 
process-local handle and silently emit the values most dangerous to leak. 
Someone who sees the loader path refuse to serialize will reasonably assume the 
path is credential-aware, and it isn't.
   
   A FileIO written to a log, telemetry, or disk unencrypted then leaks those 
verbatim (and for vended `s3.session-token` there's a second trap — it's 
short-lived, so a blob deserialized after the TTL silently fails auth with no 
expiry signal). I'd like safe-by-default before merge: redact or opaque-wrap 
the well-known credential keys in the serialized props, with full-fidelity 
passthrough as an explicit opt-in — a flag or a `serialize_with_credentials` 
path. That keeps the roundtrip usable for plain configs while making the 
dangerous case safe unless you ask for it. wdyt?



##########
crates/storage/opendal/src/lib.rs:
##########
@@ -104,6 +104,12 @@ pub use resolving::{OpenDalResolvingStorage, 
OpenDalResolvingStorageFactory};
 ///
 /// Maps scheme to the corresponding OpenDalStorage storage variant.
 /// Use this factory with `FileIOBuilder::new(factory)` to create FileIO 
instances.
+///
+/// # Serialization
+///
+/// Serialization fails when the [`OpenDalStorageFactory::S3`] variant 
contains a custom AWS

Review Comment:
   The intra-doc link to `OpenDalStorageFactory::S3` here points to a variant 
that only exists under `opendal-s3`. Under `--no-default-features` docs (which 
ASF CI tends to run with `-D warnings`), rustdoc's broken-intra-doc-links lint 
turns this into a hard error.
   
   Easiest is to drop the link and use a plain back-tick name, or gate the 
paragraph with `#[cfg_attr(feature = "opendal-s3", doc = "...")]`.



##########
crates/storage/opendal/src/lib.rs:
##########
@@ -133,6 +143,22 @@ pub enum OpenDalStorageFactory {
     Hf,
 }
 
+#[cfg(feature = "opendal-s3")]
+pub(crate) fn serialize_custom_credential_loader<S>(
+    loader: &Option<CustomAwsCredentialLoader>,
+    serializer: S,
+) -> std::result::Result<S::Ok, S::Error>
+where
+    S: serde::Serializer,
+{
+    match loader {
+        Some(_) => Err(serde::ser::Error::custom(
+            "custom AWS credential loaders cannot be serialized",
+        )),
+        None => serializer.serialize_none(),

Review Comment:
   Small thing — the `None` arm here is never reached: `skip_serializing_if = 
"Option::is_none"` at the call-site short-circuits the field before 
`serialize_with` runs, so only the `Some(_)` error path ever fires. The 
fail-fast is correct, this arm is just dead.
   
   I'd either drop the `skip_serializing_if` and let this fn own the None case, 
or keep the attribute and collapse the body to the `Some` path with a comment 
that None never gets here — right now the two don't quite agree.



##########
crates/storage/opendal/src/lib.rs:
##########
@@ -104,6 +104,12 @@ pub use resolving::{OpenDalResolvingStorage, 
OpenDalResolvingStorageFactory};
 ///
 /// Maps scheme to the corresponding OpenDalStorage storage variant.
 /// Use this factory with `FileIOBuilder::new(factory)` to create FileIO 
instances.
+///
+/// # Serialization

Review Comment:
   This new doc block covers the loader case well. One thing it doesn't cover: 
the variants here are feature-gated, so a blob serialized in a binary built 
with `opendal-gcs` fails to deserialize in one without it — serde reports 
`unknown variant "Gcs", expected one of [...]` listing only the compiled-in 
variants, with no hint that a feature flag is the cause.
   
   Worth a sentence noting the receiving binary must be built with the same 
`opendal-*` features. A `#[serde(other)]` catch-all would also give a cleaner 
error, but the doc note is the cheap part.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to