Skip to content

OCPBUGS-56853, OPRUN-3925: [Default Catalog Consistency Test] (feat) add check to validate multi-arch support #364

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions openshift/default-catalog-consistency/pkg/check/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
"sort"
"strings"

"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
specsgov1 "github.com/opencontainers/image-spec/specs-go/v1"
"oras.land/oras-go/v2"
)
Expand Down Expand Up @@ -112,6 +114,67 @@ func ImageHasLabels(expectedLabels map[string]string) ImageCheck {
}
}

// RequiredPlatforms is a list of platforms that are required for the image to be considered valid.
var RequiredPlatforms = []specsgov1.Platform{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should restrict it to this set to get noisy if a new architecture/OS is added...?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just don't see anyone remembering to come and update this test if a new arch is added XD

Copy link
Contributor Author

@camilamacedo86 camilamacedo86 May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we must ensure those
If any pipeline builds more than that is probably fine 👍
But unlikely . Not sure .. If we should care .. I do not have a really strong opinion over that

{OS: "linux", Architecture: "amd64"},
{OS: "linux", Architecture: "arm64"},
{OS: "linux", Architecture: "ppc64le"},
{OS: "linux", Architecture: "s390x"},
}

// ImageSupportsMultiArch verifies multi-arch support by inspecting the remote image manifest
// list directly. We don’t use extract.UnpackImage and the check interfaces here because it picks
// one platform based on OSChoice. On macOS, this fails if the image doesn’t support darwin/arm64
// or darwin/amd64 and to make easier develop and test on macOS we keep the check independent of
// the unpacking logic where we set OSChoice = "linux" to avoid OS mismatch errors.
func ImageSupportsMultiArch(imageRef string, expected []specsgov1.Platform, sysCtx *types.SystemContext) ImageCheck {
return ImageCheck{
Name: "ImageSupportsMultiArch",
// Do not use the unpacked image, but pull the manifest list directly from the remote.
Fn: func(ctx context.Context, _ specsgov1.Descriptor, _ oras.ReadOnlyTarget) error {
ref, err := docker.ParseReference("//" + imageRef)
if err != nil {
return fmt.Errorf("parse image ref: %w", err)
}

src, err := ref.NewImageSource(ctx, sysCtx)
if err != nil {
return fmt.Errorf("new image source: %w", err)
}
defer src.Close()

manifestBytes, _, err := src.GetManifest(ctx, nil)
if err != nil {
return fmt.Errorf("get manifest: %w", err)
}

mf, err := manifest.Schema2ListFromManifest(manifestBytes)
if err != nil {
return fmt.Errorf("parse multiarch list: %w", err)
}

found := map[string]struct{}{}
for _, desc := range mf.Manifests {
key := fmt.Sprintf("%s/%s", desc.Platform.OS, desc.Platform.Architecture)
found[key] = struct{}{}
}

var missing []string
for _, p := range expected {
key := fmt.Sprintf("%s/%s", p.OS, p.Architecture)
if _, ok := found[key]; !ok {
missing = append(missing, key)
}
}

if len(missing) > 0 {
return fmt.Errorf("missing required platforms: %v", missing)
}
return nil
},
}
}

func isValidMediaType(m specsgov1.Manifest) error {
switch m.MediaType {
case specsgov1.MediaTypeImageManifest, manifest.DockerV2Schema2MediaType:
Expand Down
37 changes: 21 additions & 16 deletions openshift/default-catalog-consistency/test/validate/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
. "github.com/onsi/gomega"

"github.com/containers/image/v5/types"
specsgov1 "github.com/opencontainers/image-spec/specs-go/v1"

"github/operator-framework-operator-controller/openshift/default-catalog-consistency/pkg/check"
"github/operator-framework-operator-controller/openshift/default-catalog-consistency/pkg/extract"
Expand All @@ -28,32 +29,36 @@ var _ = Describe("Check Catalog Consistency", func() {
Expect(images).ToNot(BeEmpty(), "no images found")
authPath := os.Getenv("REGISTRY_AUTH_FILE")

// Force image resolution to Linux to avoid OS mismatch errors on macOS,
// like: "no image found for architecture 'arm64', OS 'darwin'".
//
// Setting OSChoice = "linux" ensures we always get a Linux image,
// even when running on macOS.
//
// This skips the full multi-arch index and gives us just one manifest.
// To check all supported architectures (e.g., amd64, arm64, ppc64le, s390x),
// we’d need to avoid setting OSChoice and inspect the full index manually.
//
// TODO: Update this to support checking all architectures.
// See: https://issues.redhat.com/browse/OPRUN-3793
sysCtx := &types.SystemContext{
OSChoice: "linux",
}
sysCtx := &types.SystemContext{}
if authPath != "" {
fmt.Println("Using registry auth file:", authPath)
sysCtx.AuthFilePath = authPath
}

for _, url := range images {
name := utils.ImageNameFromRef(url)
ctx := context.Background()

It(fmt.Sprintf("validates multiarch support for image: %s", name), func() {
By(fmt.Sprintf("Validating image: %s", url))
err := check.ImageSupportsMultiArch(
url,
check.RequiredPlatforms,
sysCtx,
).Fn(ctx, specsgov1.Descriptor{}, nil)
Expect(err).ToNot(HaveOccurred())
})

It(fmt.Sprintf("validates image: %s", name), func() {
ctx := context.Background()
By(fmt.Sprintf("Validating image: %s", url))
// Force image resolution to Linux to avoid OS mismatch errors on macOS,
// like: "no image found for architecture 'arm64', OS 'darwin'".
//
// Setting OSChoice = "linux" ensures we always get a Linux image,
// even when running on macOS.
//
// This skips the full multi-arch index and gives us just one manifest.
sysCtx.OSChoice = "linux"

extractedImage, err := extract.UnpackImage(ctx, url, name, sysCtx)
Expect(err).ToNot(HaveOccurred())
Expand Down