@@ -9,8 +9,17 @@ use crate::{cfsctl, integration_test};
99struct ContainerImage {
1010 /// Human-readable label for test output.
1111 label : & ' static str ,
12- /// OCI image reference (docker:// prefix).
12+ /// Primary OCI image reference — the ghcr.io mirror (docker:// prefix).
13+ ///
14+ /// The mirror is populated by the `mirror-fixture-images.yml` workflow after
15+ /// a PR that adds a new entry to `ci/fixture-images.txt` is merged to main.
16+ /// During the PR itself the mirror does not exist yet, so the test falls back
17+ /// to `upstream_ref` when this pull fails.
1318 image_ref : & ' static str ,
19+ /// Upstream OCI image reference used as a fallback when `image_ref` is
20+ /// unavailable (e.g. a PR that adds a new mirror entry before it has been
21+ /// pushed). Should be pinned by digest for reproducibility.
22+ upstream_ref : & ' static str ,
1423 /// Expected composefs image ID without `--bootable`.
1524 expected_id : & ' static str ,
1625 /// Expected composefs image ID with `--bootable`, or `None` if the
@@ -25,6 +34,7 @@ struct ContainerImage {
2534const UBI10 : ContainerImage = ContainerImage {
2635 label : "ubi10" ,
2736 image_ref : "docker://ghcr.io/composefs/ci-fixture-ubi10:10.1-1772441712" ,
37+ upstream_ref : "docker://registry.access.redhat.com/ubi10/ubi:10.1-1772441712" ,
2838 expected_id : "ff8dad033a3e6015d63d6b00c16918da27bf96cc8ddd824e521549db01013227\
2939 87c30a3f49e5716f8f6052d78b46308dfaaccf0dfc504d26fe58d468810c0b0e",
3040 expected_bootable_id : None ,
@@ -37,6 +47,7 @@ const UBI10: ContainerImage = ContainerImage {
3747const CENTOS_BOOTC : ContainerImage = ContainerImage {
3848 label : "centos-bootc" ,
3949 image_ref : "docker://ghcr.io/composefs/ci-fixture-centos-bootc:stream10-d1913e3d" ,
50+ upstream_ref : "docker://quay.io/centos-bootc/centos-bootc@sha256:d1913e3d616b9acb7fc2e3331be8baf844048bca2681a23d34e53e75eb18f3d0" ,
4051 expected_id : "ad575e0570dfb74cbc837f41715d3fba890dd983d992332eaeee93493ce112ee\
4152 50d3dc5f6f2a3214cc92412fe3ae936e2e9c0eac24ea787e83ef13c0a718a193",
4253 expected_bootable_id : Some (
@@ -54,7 +65,38 @@ fn skip_network() -> bool {
5465}
5566
5667/// Pull an OCI image and return the config digest from the pull output.
68+ ///
69+ /// Tries `image.image_ref` (the ghcr.io mirror) first. If that fails —
70+ /// which is expected for PRs that add a new mirror entry before it has been
71+ /// pushed — falls back to `image.upstream_ref` with a warning.
5772fn pull_image (
73+ sh : & Shell ,
74+ cfsctl : & std:: path:: Path ,
75+ repo : & std:: path:: Path ,
76+ image : & ContainerImage ,
77+ name : & str ,
78+ ) -> Result < String > {
79+ let candidates = [ ( image. image_ref , false ) , ( image. upstream_ref , true ) ] ;
80+ let mut last_err = None ;
81+ for ( image_ref, is_fallback) in candidates {
82+ if is_fallback {
83+ eprintln ! (
84+ "WARNING: mirror pull failed for {}; falling back to upstream {image_ref}" ,
85+ image. label,
86+ ) ;
87+ }
88+ match try_pull_image ( sh, cfsctl, repo, image_ref, name) {
89+ Ok ( config) => return Ok ( config) ,
90+ Err ( e) => {
91+ eprintln ! ( "Pull of {image_ref} failed: {e:#}" ) ;
92+ last_err = Some ( e) ;
93+ }
94+ }
95+ }
96+ Err ( last_err. unwrap ( ) )
97+ }
98+
99+ fn try_pull_image (
58100 sh : & Shell ,
59101 cfsctl : & std:: path:: Path ,
60102 repo : & std:: path:: Path ,
@@ -126,7 +168,7 @@ fn test_oci_container_digest_stability() -> Result<()> {
126168 cmd ! ( sh, "{cfsctl} --repo {repo} init --insecure" ) . read ( ) ?;
127169
128170 eprintln ! ( "Pulling {} (this may take a while)..." , image. label) ;
129- let config = pull_image ( & sh, & cfsctl, repo, image. image_ref , image. label ) ?;
171+ let config = pull_image ( & sh, & cfsctl, repo, image, image. label ) ?;
130172
131173 // Plain (non-bootable) image ID
132174 let plain_id = compute_id ( & sh, & cfsctl, repo, & config, false ) ?;
0 commit comments