165 lines
6.2 KiB
Go
165 lines
6.2 KiB
Go
|
package copy
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/containers/image/docker/reference"
|
||
|
"github.com/containers/image/manifest"
|
||
|
"github.com/containers/image/types"
|
||
|
"github.com/opencontainers/image-spec/specs-go/v1"
|
||
|
"github.com/stretchr/testify/assert"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
)
|
||
|
|
||
|
func TestOrderedSet(t *testing.T) {
|
||
|
for _, c := range []struct{ input, expected []string }{
|
||
|
{[]string{}, []string{}},
|
||
|
{[]string{"a", "b", "c"}, []string{"a", "b", "c"}},
|
||
|
{[]string{"a", "b", "a", "c"}, []string{"a", "b", "c"}},
|
||
|
} {
|
||
|
os := newOrderedSet()
|
||
|
for _, s := range c.input {
|
||
|
os.append(s)
|
||
|
}
|
||
|
assert.Equal(t, c.expected, os.list, fmt.Sprintf("%#v", c.input))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// fakeImageSource is an implementation of types.Image which only returns itself as a MIME type in Manifest
|
||
|
// except that "" means “reading the manifest should fail”
|
||
|
type fakeImageSource string
|
||
|
|
||
|
func (f fakeImageSource) Reference() types.ImageReference {
|
||
|
panic("Unexpected call to a mock function")
|
||
|
}
|
||
|
func (f fakeImageSource) Close() error {
|
||
|
panic("Unexpected call to a mock function")
|
||
|
}
|
||
|
func (f fakeImageSource) Manifest() ([]byte, string, error) {
|
||
|
if string(f) == "" {
|
||
|
return nil, "", errors.New("Manifest() directed to fail")
|
||
|
}
|
||
|
return nil, string(f), nil
|
||
|
}
|
||
|
func (f fakeImageSource) Signatures() ([][]byte, error) {
|
||
|
panic("Unexpected call to a mock function")
|
||
|
}
|
||
|
func (f fakeImageSource) ConfigInfo() types.BlobInfo {
|
||
|
panic("Unexpected call to a mock function")
|
||
|
}
|
||
|
func (f fakeImageSource) ConfigBlob() ([]byte, error) {
|
||
|
panic("Unexpected call to a mock function")
|
||
|
}
|
||
|
func (f fakeImageSource) OCIConfig() (*v1.Image, error) {
|
||
|
panic("Unexpected call to a mock function")
|
||
|
}
|
||
|
func (f fakeImageSource) LayerInfos() []types.BlobInfo {
|
||
|
panic("Unexpected call to a mock function")
|
||
|
}
|
||
|
func (f fakeImageSource) EmbeddedDockerReferenceConflicts(ref reference.Named) bool {
|
||
|
panic("Unexpected call to a mock function")
|
||
|
}
|
||
|
func (f fakeImageSource) Inspect() (*types.ImageInspectInfo, error) {
|
||
|
panic("Unexpected call to a mock function")
|
||
|
}
|
||
|
func (f fakeImageSource) UpdatedImageNeedsLayerDiffIDs(options types.ManifestUpdateOptions) bool {
|
||
|
panic("Unexpected call to a mock function")
|
||
|
}
|
||
|
func (f fakeImageSource) UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error) {
|
||
|
panic("Unexpected call to a mock function")
|
||
|
}
|
||
|
func (f fakeImageSource) IsMultiImage() bool {
|
||
|
panic("Unexpected call to a mock function")
|
||
|
}
|
||
|
func (f fakeImageSource) Size() (int64, error) {
|
||
|
panic("Unexpected call to a mock function")
|
||
|
}
|
||
|
|
||
|
func TestDetermineManifestConversion(t *testing.T) {
|
||
|
supportS1S2OCI := []string{
|
||
|
v1.MediaTypeImageManifest,
|
||
|
manifest.DockerV2Schema2MediaType,
|
||
|
manifest.DockerV2Schema1SignedMediaType,
|
||
|
manifest.DockerV2Schema1MediaType,
|
||
|
}
|
||
|
supportS1OCI := []string{
|
||
|
v1.MediaTypeImageManifest,
|
||
|
manifest.DockerV2Schema1SignedMediaType,
|
||
|
manifest.DockerV2Schema1MediaType,
|
||
|
}
|
||
|
supportS1S2 := []string{
|
||
|
manifest.DockerV2Schema2MediaType,
|
||
|
manifest.DockerV2Schema1SignedMediaType,
|
||
|
manifest.DockerV2Schema1MediaType,
|
||
|
}
|
||
|
supportOnlyS1 := []string{
|
||
|
manifest.DockerV2Schema1SignedMediaType,
|
||
|
manifest.DockerV2Schema1MediaType,
|
||
|
}
|
||
|
|
||
|
cases := []struct {
|
||
|
description string
|
||
|
sourceType string
|
||
|
destTypes []string
|
||
|
expectedUpdate string
|
||
|
expectedOtherCandidates []string
|
||
|
}{
|
||
|
// Destination accepts anything — no conversion necessary
|
||
|
{"s1→anything", manifest.DockerV2Schema1SignedMediaType, nil, "", []string{}},
|
||
|
{"s2→anything", manifest.DockerV2Schema2MediaType, nil, "", []string{}},
|
||
|
// Destination accepts the unmodified original
|
||
|
{"s1→s1s2", manifest.DockerV2Schema1SignedMediaType, supportS1S2, "", []string{manifest.DockerV2Schema2MediaType, manifest.DockerV2Schema1MediaType}},
|
||
|
{"s2→s1s2", manifest.DockerV2Schema2MediaType, supportS1S2, "", supportOnlyS1},
|
||
|
{"s1→s1", manifest.DockerV2Schema1SignedMediaType, supportOnlyS1, "", []string{manifest.DockerV2Schema1MediaType}},
|
||
|
// Conversion necessary, a preferred format is acceptable
|
||
|
{"s2→s1", manifest.DockerV2Schema2MediaType, supportOnlyS1, manifest.DockerV2Schema1SignedMediaType, []string{manifest.DockerV2Schema1MediaType}},
|
||
|
// Conversion necessary, a preferred format is not acceptable
|
||
|
{"s2→OCI", manifest.DockerV2Schema2MediaType, []string{v1.MediaTypeImageManifest}, v1.MediaTypeImageManifest, []string{}},
|
||
|
// Conversion necessary, try the preferred formats in order.
|
||
|
{
|
||
|
"special→s2", "this needs conversion", supportS1S2OCI, manifest.DockerV2Schema2MediaType,
|
||
|
[]string{manifest.DockerV2Schema1SignedMediaType, v1.MediaTypeImageManifest, manifest.DockerV2Schema1MediaType},
|
||
|
},
|
||
|
{
|
||
|
"special→s1", "this needs conversion", supportS1OCI, manifest.DockerV2Schema1SignedMediaType,
|
||
|
[]string{v1.MediaTypeImageManifest, manifest.DockerV2Schema1MediaType},
|
||
|
},
|
||
|
{
|
||
|
"special→OCI", "this needs conversion", []string{v1.MediaTypeImageManifest, "other options", "with lower priority"}, v1.MediaTypeImageManifest,
|
||
|
[]string{"other options", "with lower priority"},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, c := range cases {
|
||
|
src := fakeImageSource(c.sourceType)
|
||
|
mu := types.ManifestUpdateOptions{}
|
||
|
preferredMIMEType, otherCandidates, err := determineManifestConversion(&mu, src, c.destTypes, true)
|
||
|
require.NoError(t, err, c.description)
|
||
|
assert.Equal(t, c.expectedUpdate, mu.ManifestMIMEType, c.description)
|
||
|
if c.expectedUpdate == "" {
|
||
|
assert.Equal(t, c.sourceType, preferredMIMEType, c.description)
|
||
|
} else {
|
||
|
assert.Equal(t, c.expectedUpdate, preferredMIMEType, c.description)
|
||
|
}
|
||
|
assert.Equal(t, c.expectedOtherCandidates, otherCandidates, c.description)
|
||
|
}
|
||
|
|
||
|
// Whatever the input is, with !canModifyManifest we return "keep the original as is"
|
||
|
for _, c := range cases {
|
||
|
src := fakeImageSource(c.sourceType)
|
||
|
mu := types.ManifestUpdateOptions{}
|
||
|
preferredMIMEType, otherCandidates, err := determineManifestConversion(&mu, src, c.destTypes, false)
|
||
|
require.NoError(t, err, c.description)
|
||
|
assert.Equal(t, "", mu.ManifestMIMEType, c.description)
|
||
|
assert.Equal(t, c.sourceType, preferredMIMEType, c.description)
|
||
|
assert.Equal(t, []string{}, otherCandidates, c.description)
|
||
|
}
|
||
|
|
||
|
// Error reading the manifest — smoke test only.
|
||
|
mu := types.ManifestUpdateOptions{}
|
||
|
_, _, err := determineManifestConversion(&mu, fakeImageSource(""), supportS1S2, true)
|
||
|
assert.Error(t, err)
|
||
|
}
|