|
@@ -270,6 +270,20 @@ pub async fn sync_image_to_nodes(
|
|
|
);
|
|
);
|
|
|
match run_ssh_long_running(&config, &src_node, pipe, false).await {
|
|
match run_ssh_long_running(&config, &src_node, pipe, false).await {
|
|
|
Ok(_) => {
|
|
Ok(_) => {
|
|
|
|
|
+ // A digest-only ref (`repo@sha256:...`) loses its
|
|
|
|
|
+ // RepoDigest association through save/load — the tar
|
|
|
|
|
+ // carries no tags, so the loaded image is untagged
|
|
|
|
|
+ // and `docker run <ref>` fails with "No such image"
|
|
|
|
|
+ // on the peer. Re-attach it with a pull-by-digest:
|
|
|
|
|
+ // all layers are local, this fetches nothing but the
|
|
|
|
|
+ // manifest (and pins the association).
|
|
|
|
|
+ if image.contains('@') {
|
|
|
|
|
+ let pull = format!("docker pull {}", shq(&image));
|
|
|
|
|
+ if let Err(e) = run_ssh(&config, &target, pull, false).await {
|
|
|
|
|
+ last_err = Some(format!("pull-by-digest re-attach: {e}"));
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
let verify = get_image_digest(&config, &target, &image).await;
|
|
let verify = get_image_digest(&config, &target, &image).await;
|
|
|
match verify {
|
|
match verify {
|
|
|
Ok(v) if v.as_deref() == Some(expected.as_str()) => {
|
|
Ok(v) if v.as_deref() == Some(expected.as_str()) => {
|