diff options
| author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-05-10 13:58:45 +0000 |
|---|---|---|
| committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-05-10 13:58:45 +0000 |
| commit | cbe004439cae170ad5455fbb881495795e42bf5a (patch) | |
| tree | 540a4ef020ede79fa8b4d22e5ce3091fc4ceba92 /recipes-containers/container-registry | |
| parent | af92db59a7d2367528bc86cb37d969e87ef36659 (diff) | |
| download | meta-virtualization-cbe004439cae170ad5455fbb881495795e42bf5a.tar.gz | |
container-registry: add multi-arch OCI push support and tests
The registry push script (container-registry-index.bb) treated all OCI
directories as single-arch, calling 'skopeo copy oci:<dir>' which fails
with "more than one image in oci, choose an image" when the directory
contains a multi-arch image index. The original push implementation
predated multi-arch OCI support and only handled the single-manifest
case.
Detect multi-arch OCI Image Index directories (both flat and nested
layouts) in the direct-path push mode and use 'skopeo copy --all' to
push the entire manifest list to the registry in one operation. This
preserves the multi-platform structure so that clients pulling from the
registry automatically get the correct architecture.
Also strip the '-multiarch' suffix from directory names when deriving
the registry image name, so container-base-multiarch-multiarch-oci
pushes as 'container-base' rather than 'container-base-multiarch'.
Add build-profiles.md documentation for the vcontainer distro, container
multiconfigs, and multi-arch container build workflow.
Add test_vcontainer_distro.py with 54 tests across three tiers:
- Tier 1: Static file assertions (vruntime-base.inc, vcontainer.conf,
multiconfigs, bbclass defaults, recipe structure)
- Tier 2: Cross-file consistency (shared base, distro-MC alignment,
bbclass-to-multiconfig file matching)
- Tier 3: Build output verification (OCI index structure, platform
entries, blob integrity, manifest validation)
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'recipes-containers/container-registry')
| -rw-r--r-- | recipes-containers/container-registry/container-registry-index.bb | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/recipes-containers/container-registry/container-registry-index.bb b/recipes-containers/container-registry/container-registry-index.bb index 7d53e28e..1b977ea5 100644 --- a/recipes-containers/container-registry/container-registry-index.bb +++ b/recipes-containers/container-registry/container-registry-index.bb | |||
| @@ -1223,7 +1223,61 @@ cmd_push() {{ | |||
| 1223 | local name=$(basename "$oci_dir" | sed 's/-latest-oci$//' | sed 's/-oci$//') | 1223 | local name=$(basename "$oci_dir" | sed 's/-latest-oci$//' | sed 's/-oci$//') |
| 1224 | name=$(echo "$name" | sed 's/-qemux86-64//' | sed 's/-qemuarm64//') | 1224 | name=$(echo "$name" | sed 's/-qemux86-64//' | sed 's/-qemuarm64//') |
| 1225 | name=$(echo "$name" | sed 's/\\.rootfs-[0-9]*//') | 1225 | name=$(echo "$name" | sed 's/\\.rootfs-[0-9]*//') |
| 1226 | # Strip -multiarch suffix for cleaner registry names | ||
| 1227 | name=$(echo "$name" | sed 's/-multiarch$//') | ||
| 1228 | |||
| 1229 | # Detect multi-arch OCI Image Index | ||
| 1230 | # Flat layout: index.json has multiple manifests with platform info | ||
| 1231 | # Nested layout: index.json → single image index blob → platform manifests | ||
| 1232 | local is_multiarch=0 | ||
| 1233 | local platform_file="$oci_dir/index.json" | ||
| 1234 | local manifest_count=$(grep -c '"digest"' "$platform_file" 2>/dev/null || echo "0") | ||
| 1235 | local has_platform=$(grep -c '"platform"' "$platform_file" 2>/dev/null || echo "0") | ||
| 1236 | |||
| 1237 | if [ "$manifest_count" -gt 1 ] && [ "$has_platform" -gt 0 ]; then | ||
| 1238 | is_multiarch=1 | ||
| 1239 | elif grep -q 'image\\.index' "$platform_file" 2>/dev/null; then | ||
| 1240 | # Nested layout: follow digest to blob | ||
| 1241 | local idx_digest=$(grep -o '"sha256:[a-f0-9]*"' "$platform_file" 2>/dev/null | head -1 | tr -d '"' | sed 's/sha256://') | ||
| 1242 | if [ -n "$idx_digest" ] && [ -f "$oci_dir/blobs/sha256/$idx_digest" ]; then | ||
| 1243 | if grep -q '"platform"' "$oci_dir/blobs/sha256/$idx_digest" 2>/dev/null; then | ||
| 1244 | platform_file="$oci_dir/blobs/sha256/$idx_digest" | ||
| 1245 | is_multiarch=1 | ||
| 1246 | fi | ||
| 1247 | fi | ||
| 1248 | fi | ||
| 1249 | |||
| 1250 | if [ "$is_multiarch" = "1" ]; then | ||
| 1251 | # Multi-arch OCI Image Index: push with --all to preserve manifest list | ||
| 1252 | local platforms=$(grep -o '"architecture"[[:space:]]*:[[:space:]]*"[^"]*"' "$platform_file" | \\ | ||
| 1253 | sed 's/.*"\\([^"]*\\)"$/\\1/' | tr '\\n' ' ') | ||
| 1254 | |||
| 1255 | echo "Pushing multi-arch OCI Image Index: $oci_dir" | ||
| 1256 | echo " Image name: $name" | ||
| 1257 | echo " Platforms: $platforms" | ||
| 1258 | echo " To registry: $REGISTRY_URL/$REGISTRY_NAMESPACE/" | ||
| 1259 | echo " Tags: $tags" | ||
| 1260 | echo "" | ||
| 1261 | |||
| 1262 | local tls_args=$(get_tls_args dest) | ||
| 1263 | for tag in $tags; do | ||
| 1264 | echo " Pushing manifest list: $name:$tag" | ||
| 1265 | if "$SKOPEO_BIN" copy --all $tls_args $auth_args \\ | ||
| 1266 | "oci:$oci_dir" \\ | ||
| 1267 | "docker://$REGISTRY_URL/$REGISTRY_NAMESPACE/$name:$tag" 2>&1; then | ||
| 1268 | echo " -> $REGISTRY_URL/$REGISTRY_NAMESPACE/$name:$tag (manifest list: $platforms)" | ||
| 1269 | else | ||
| 1270 | echo " ERROR: Failed to push multi-arch image" | ||
| 1271 | return 1 | ||
| 1272 | fi | ||
| 1273 | done | ||
| 1274 | |||
| 1275 | echo "" | ||
| 1276 | echo "Done." | ||
| 1277 | return 0 | ||
| 1278 | fi | ||
| 1226 | 1279 | ||
| 1280 | # Single-arch OCI: push normally | ||
| 1227 | local arch=$(get_oci_arch "$oci_dir") | 1281 | local arch=$(get_oci_arch "$oci_dir") |
| 1228 | [ -z "$arch" ] && arch="unknown" | 1282 | [ -z "$arch" ] && arch="unknown" |
| 1229 | 1283 | ||
