|
8 | 8 | "sort"
|
9 | 9 | "strings"
|
10 | 10 |
|
| 11 | + "github.com/containers/image/v5/docker" |
11 | 12 | "github.com/containers/image/v5/manifest"
|
| 13 | + "github.com/containers/image/v5/types" |
12 | 14 | specsgov1 "github.com/opencontainers/image-spec/specs-go/v1"
|
13 | 15 | "oras.land/oras-go/v2"
|
14 | 16 | )
|
@@ -112,6 +114,67 @@ func ImageHasLabels(expectedLabels map[string]string) ImageCheck {
|
112 | 114 | }
|
113 | 115 | }
|
114 | 116 |
|
| 117 | +// RequiredPlatforms is a list of platforms that are required for the image to be considered valid. |
| 118 | +var RequiredPlatforms = []specsgov1.Platform{ |
| 119 | + {OS: "linux", Architecture: "amd64"}, |
| 120 | + {OS: "linux", Architecture: "arm64"}, |
| 121 | + {OS: "linux", Architecture: "ppc64le"}, |
| 122 | + {OS: "linux", Architecture: "s390x"}, |
| 123 | +} |
| 124 | + |
| 125 | +// ImageSupportsMultiArch verifies multi-arch support by inspecting the remote image manifest |
| 126 | +// list directly. We don’t use extract.UnpackImage and the check interfaces here because it picks |
| 127 | +// one platform based on OSChoice. On macOS, this fails if the image doesn’t support darwin/arm64 |
| 128 | +// or darwin/amd64 and to make easier develop and test on macOS we keep the check independent of |
| 129 | +// the unpacking logic where we set OSChoice = "linux" to avoid OS mismatch errors. |
| 130 | +func ImageSupportsMultiArch(imageRef string, expected []specsgov1.Platform, sysCtx *types.SystemContext) ImageCheck { |
| 131 | + return ImageCheck{ |
| 132 | + Name: "ImageSupportsMultiArch", |
| 133 | + // Do not use the unpacked image, but pull the manifest list directly from the remote. |
| 134 | + Fn: func(ctx context.Context, _ specsgov1.Descriptor, _ oras.ReadOnlyTarget) error { |
| 135 | + ref, err := docker.ParseReference("//" + imageRef) |
| 136 | + if err != nil { |
| 137 | + return fmt.Errorf("parse image ref: %w", err) |
| 138 | + } |
| 139 | + |
| 140 | + src, err := ref.NewImageSource(ctx, sysCtx) |
| 141 | + if err != nil { |
| 142 | + return fmt.Errorf("new image source: %w", err) |
| 143 | + } |
| 144 | + defer src.Close() |
| 145 | + |
| 146 | + manifestBytes, _, err := src.GetManifest(ctx, nil) |
| 147 | + if err != nil { |
| 148 | + return fmt.Errorf("get manifest: %w", err) |
| 149 | + } |
| 150 | + |
| 151 | + mf, err := manifest.Schema2ListFromManifest(manifestBytes) |
| 152 | + if err != nil { |
| 153 | + return fmt.Errorf("parse multiarch list: %w", err) |
| 154 | + } |
| 155 | + |
| 156 | + found := map[string]struct{}{} |
| 157 | + for _, desc := range mf.Manifests { |
| 158 | + key := fmt.Sprintf("%s/%s", desc.Platform.OS, desc.Platform.Architecture) |
| 159 | + found[key] = struct{}{} |
| 160 | + } |
| 161 | + |
| 162 | + var missing []string |
| 163 | + for _, p := range expected { |
| 164 | + key := fmt.Sprintf("%s/%s", p.OS, p.Architecture) |
| 165 | + if _, ok := found[key]; !ok { |
| 166 | + missing = append(missing, key) |
| 167 | + } |
| 168 | + } |
| 169 | + |
| 170 | + if len(missing) > 0 { |
| 171 | + return fmt.Errorf("missing required platforms: %v", missing) |
| 172 | + } |
| 173 | + return nil |
| 174 | + }, |
| 175 | + } |
| 176 | +} |
| 177 | + |
115 | 178 | func isValidMediaType(m specsgov1.Manifest) error {
|
116 | 179 | switch m.MediaType {
|
117 | 180 | case specsgov1.MediaTypeImageManifest, manifest.DockerV2Schema2MediaType:
|
|
0 commit comments