package manifest import ( "fmt" "os" "path/filepath" "testing" "github.com/containers/image/v5/internal/manifest" "github.com/containers/image/v5/types" digest "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func pare(m List) { if impl, ok := m.(*manifest.OCI1Index); ok { impl.Annotations = nil } if impl, ok := m.(*manifest.Schema2List); ok { for i := range impl.Manifests { impl.Manifests[i].Platform.Features = nil } } } func TestParseLists(t *testing.T) { cases := []struct { path string mimeType string }{ {"ociv1.image.index.json", imgspecv1.MediaTypeImageIndex}, {"v2list.manifest.json", DockerV2ListMediaType}, } for _, c := range cases { manifest, err := os.ReadFile(filepath.Join("fixtures", c.path)) require.NoError(t, err, "error reading file %q", filepath.Join("fixtures", c.path)) assert.Equal(t, GuessMIMEType(manifest), c.mimeType) _, err = FromBlob(manifest, c.mimeType) require.Error(t, err, "manifest list %q should not parse as single images", c.path) m, err := ListFromBlob(manifest, c.mimeType) require.NoError(t, err, "manifest list %q should parse as list types", c.path) assert.Equal(t, m.MIMEType(), c.mimeType, "manifest %q is not of the expected MIME type", c.path) clone := m.Clone() assert.Equal(t, clone, m, "manifest %q is missing some fields after being cloned", c.path) pare(m) index, err := m.ConvertToMIMEType(imgspecv1.MediaTypeImageIndex) require.NoError(t, err, "error converting %q to an OCI1Index", c.path) list, err := m.ConvertToMIMEType(DockerV2ListMediaType) require.NoError(t, err, "error converting %q to an Schema2List", c.path) index2, err := list.ConvertToMIMEType(imgspecv1.MediaTypeImageIndex) require.NoError(t, err) assert.Equal(t, index, index2, "index %q lost data in conversion", c.path) list2, err := index.ConvertToMIMEType(DockerV2ListMediaType) require.NoError(t, err) assert.Equal(t, list, list2, "list %q lost data in conversion", c.path) } } func TestChooseInstance(t *testing.T) { type expectedMatch struct { arch, variant string instanceDigest digest.Digest } for _, manifestList := range []struct { listFile string matchedInstances []expectedMatch unmatchedInstances []string }{ { listFile: "schema2list.json", matchedInstances: []expectedMatch{ {"amd64", "", "sha256:030fcb92e1487b18c974784dcc110a93147c9fc402188370fbfd17efabffc6af"}, {"s390x", "", "sha256:e5aa1b0a24620228b75382997a0977f609b3ca3a95533dafdef84c74cc8df642"}, {"arm", "v7", "sha256:b5dbad4bdb4444d919294afe49a095c23e86782f98cdf0aa286198ddb814b50b"}, {"arm64", "", "sha256:dc472a59fb006797aa2a6bfb54cc9c57959bb0a6d11fadaa608df8c16dea39cf"}, }, unmatchedInstances: []string{ "unmatched", }, }, { // Focus on ARM variant field testing listFile: "schema2list-variants.json", matchedInstances: []expectedMatch{ {"amd64", "", "sha256:59eec8837a4d942cc19a52b8c09ea75121acc38114a2c68b98983ce9356b8610"}, {"arm", "v7", "sha256:f365626a556e58189fc21d099fc64603db0f440bff07f77c740989515c544a39"}, {"arm", "v6", "sha256:f365626a556e58189fc21d099fc64603db0f440bff07f77c740989515c544a39"}, {"arm", "v5", "sha256:c84b0a3a07b628bc4d62e5047d0f8dff80f7c00979e1e28a821a033ecda8fe53"}, {"arm", "", "sha256:c84b0a3a07b628bc4d62e5047d0f8dff80f7c00979e1e28a821a033ecda8fe53"}, {"arm", "unrecognized-present", "sha256:bcf9771c0b505e68c65440474179592ffdfa98790eb54ffbf129969c5e429990"}, {"arm", "unrecognized-not-present", "sha256:c84b0a3a07b628bc4d62e5047d0f8dff80f7c00979e1e28a821a033ecda8fe53"}, }, unmatchedInstances: []string{ "unmatched", }, }, { listFile: "oci1index.json", matchedInstances: []expectedMatch{ {"amd64", "", "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"}, {"ppc64le", "", "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"}, }, unmatchedInstances: []string{ "unmatched", }, }, } { rawManifest, err := os.ReadFile(filepath.Join("..", "internal", "manifest", "testdata", manifestList.listFile)) require.NoError(t, err) list, err := ListFromBlob(rawManifest, GuessMIMEType(rawManifest)) require.NoError(t, err) // Match found for _, match := range manifestList.matchedInstances { testName := fmt.Sprintf("%s %q+%q", manifestList.listFile, match.arch, match.variant) digest, err := list.ChooseInstance(&types.SystemContext{ ArchitectureChoice: match.arch, VariantChoice: match.variant, OSChoice: "linux", }) require.NoError(t, err, testName) assert.Equal(t, match.instanceDigest, digest, testName) } // Not found for _, arch := range manifestList.unmatchedInstances { _, err := list.ChooseInstance(&types.SystemContext{ ArchitectureChoice: arch, OSChoice: "linux", }) assert.Error(t, err) } } }