Skip to content

Commit 559e176

Browse files
committed
integration-tests: Handle varlinkctl incompatibility with zlink
systemd 257's varlinkctl sends org.varlink.service.GetInfo during its initial handshake, which zlink 0.4 cannot deserialize (the untagged enum fails to match). This causes connection reset and test failures on CI runners that upgraded systemd. Add a cached varlinkctl_is_compatible() probe that detects this at runtime, and: - Make test_varlink_exec_varlinkctl and test_varlink_introspect_varlinkctl skip gracefully when varlinkctl is incompatible - Add test_varlink_images_list_crosscheck which always exercises the Rust zlink client (the primary path) and optionally cross-checks against varlinkctl when it works See z-galaxy/zlink#233 Assisted-by: OpenCode (Claude Opus 4)
1 parent 620ea6f commit 559e176

1 file changed

Lines changed: 141 additions & 1 deletion

File tree

crates/integration-tests/src/tests/varlink.rs

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
use std::os::unix::net::UnixStream;
1414
use std::os::unix::process::CommandExt;
1515
use std::process::Command;
16-
use std::sync::Arc;
16+
use std::sync::{Arc, OnceLock};
1717

1818
use cap_std_ext::cmdext::CapStdExtCommandExt;
1919
use color_eyre::Result;
@@ -521,8 +521,139 @@ integration_test!(test_varlink_todisk_creates_disk);
521521
// Tests: cross-interface / varlinkctl
522522
// ===========================================================================
523523

524+
/// Check whether `varlinkctl` can successfully talk to a zlink-based server.
525+
///
526+
/// Older versions of systemd's `varlinkctl` (or versions affected by
527+
/// <https://github.com/z-galaxy/zlink/issues/233>) send an introspection
528+
/// request that zlink cannot deserialize, causing the connection to be
529+
/// immediately dropped. Rather than failing hard on such systems, we
530+
/// detect the incompatibility here and let callers skip or adapt.
531+
fn varlinkctl_is_compatible() -> bool {
532+
static RESULT: OnceLock<bool> = OnceLock::new();
533+
*RESULT.get_or_init(|| {
534+
let bck = match get_bck_command() {
535+
Ok(b) => b,
536+
Err(e) => {
537+
eprintln!("note: varlinkctl probe: get_bck_command() failed: {e}");
538+
return false;
539+
}
540+
};
541+
let sh = match shell() {
542+
Ok(s) => s,
543+
Err(e) => {
544+
eprintln!("note: varlinkctl probe: shell() failed: {e}");
545+
return false;
546+
}
547+
};
548+
// Try a varlinkctl call; if it fails, the tool is either missing
549+
// or incompatible with our server. The most common incompatibility
550+
// is that varlinkctl sends org.varlink.service.GetInfo during its
551+
// initial handshake, which zlink cannot deserialize (zlink#233).
552+
let ok = xshell::cmd!(sh, "varlinkctl call exec:{bck} io.bootc.vk.images.List")
553+
.ignore_status()
554+
.read()
555+
.map(|output| {
556+
serde_json::from_str::<serde_json::Value>(&output)
557+
.ok()
558+
.and_then(|v| v.get("images").cloned())
559+
.is_some()
560+
})
561+
.unwrap_or(false);
562+
if !ok {
563+
eprintln!(
564+
"note: varlinkctl probe failed; varlinkctl-dependent tests will be skipped \
565+
(see https://github.com/z-galaxy/zlink/issues/233)"
566+
);
567+
}
568+
ok
569+
})
570+
}
571+
572+
/// Cross-check the images `List` API using the Rust varlink client, and
573+
/// optionally verify that `varlinkctl` returns the same result when it is
574+
/// available and compatible.
575+
///
576+
/// The Rust client path is the primary assertion — it always runs. The
577+
/// `varlinkctl` cross-check is best-effort: if the installed systemd is
578+
/// too old or suffers from the zlink introspection deserialization bug
579+
/// (<https://github.com/z-galaxy/zlink/issues/233>), the cross-check is
580+
/// skipped with a log message.
581+
fn test_varlink_images_list_crosscheck() -> Result<()> {
582+
let image = get_test_image();
583+
584+
// Ensure the test image is pulled so we have at least one image to compare
585+
let sh = shell()?;
586+
xshell::cmd!(sh, "podman pull -q {image}").run()?;
587+
588+
// Primary path: Rust varlink client
589+
let mut bcvk = activated_connection()?;
590+
let reply = bcvk.rt.block_on(async { bcvk.conn.list().await })??;
591+
assert!(
592+
!reply.images.is_empty(),
593+
"Rust client: expected at least one image"
594+
);
595+
assert!(
596+
reply.images.iter().any(|name| name.contains(&image)),
597+
"Rust client: expected test image {image} in list, got: {:?}",
598+
reply.images
599+
);
600+
601+
// Cross-check: varlinkctl (best-effort)
602+
if varlinkctl_is_compatible() {
603+
let bck = get_bck_command()?;
604+
let output =
605+
xshell::cmd!(sh, "varlinkctl call exec:{bck} io.bootc.vk.images.List").read()?;
606+
let parsed: serde_json::Value = serde_json::from_str(&output)?;
607+
let varlinkctl_images = parsed
608+
.get("images")
609+
.and_then(|v| v.as_array())
610+
.expect("varlinkctl response missing 'images' array");
611+
let varlinkctl_names: Vec<&str> = varlinkctl_images
612+
.iter()
613+
.filter_map(|v| v.as_str())
614+
.collect();
615+
616+
// Both should see the same set of images
617+
assert_eq!(
618+
reply.images.len(),
619+
varlinkctl_names.len(),
620+
"image count mismatch: Rust client={:?}, varlinkctl={:?}",
621+
reply.images,
622+
varlinkctl_names
623+
);
624+
for img in &reply.images {
625+
assert!(
626+
varlinkctl_names.contains(&img.as_str()),
627+
"varlinkctl missing image {img} that Rust client returned"
628+
);
629+
}
630+
eprintln!(
631+
"varlinkctl cross-check passed ({} images)",
632+
reply.images.len()
633+
);
634+
} else {
635+
eprintln!(
636+
"note: skipping varlinkctl cross-check (varlinkctl missing or incompatible \
637+
with this zlink server, see https://github.com/z-galaxy/zlink/issues/233)"
638+
);
639+
}
640+
641+
Ok(())
642+
}
643+
integration_test!(test_varlink_images_list_crosscheck);
644+
524645
/// Verify that `varlinkctl call` against the images List method works.
646+
///
647+
/// Skipped when `varlinkctl` is not compatible with the zlink server
648+
/// (e.g. systemd < 258 due to <https://github.com/z-galaxy/zlink/issues/233>).
525649
fn test_varlink_exec_varlinkctl() -> Result<()> {
650+
if !varlinkctl_is_compatible() {
651+
eprintln!(
652+
"note: skipping test_varlink_exec_varlinkctl (varlinkctl missing or incompatible, \
653+
see https://github.com/z-galaxy/zlink/issues/233)"
654+
);
655+
return Ok(());
656+
}
526657
let sh = shell()?;
527658
let bck = get_bck_command()?;
528659
let output = xshell::cmd!(sh, "varlinkctl call exec:{bck} io.bootc.vk.images.List").read()?;
@@ -536,7 +667,16 @@ fn test_varlink_exec_varlinkctl() -> Result<()> {
536667
integration_test!(test_varlink_exec_varlinkctl);
537668

538669
/// Test that `varlinkctl introspect` shows all three interface names.
670+
///
671+
/// Skipped when `varlinkctl` is not compatible with the zlink server.
539672
fn test_varlink_introspect_varlinkctl() -> Result<()> {
673+
if !varlinkctl_is_compatible() {
674+
eprintln!(
675+
"note: skipping test_varlink_introspect_varlinkctl (varlinkctl missing or incompatible, \
676+
see https://github.com/z-galaxy/zlink/issues/233)"
677+
);
678+
return Ok(());
679+
}
540680
let sh = shell()?;
541681
let bck = get_bck_command()?;
542682
let output = xshell::cmd!(sh, "varlinkctl introspect exec:{bck} io.bootc.vk.images").read()?;

0 commit comments

Comments
 (0)