automation-tests/common/pkg/manifests/manifests_test.go

368 lines
10 KiB
Go

package manifests
import (
"io/ioutil"
"os"
"reflect"
"testing"
"github.com/containers/image/v5/manifest"
"github.com/containers/storage/pkg/reexec"
digest "github.com/opencontainers/go-digest"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
)
const (
expectedInstance = digest.Digest("sha256:c829b1810d2dbb456e74a695fd3847530c8319e5a95dca623e9f1b1b89020d8b")
ociFixture = "testdata/fedora.index.json"
dockerFixture = "testdata/fedora.list.json"
)
var _ List = &list{}
func TestMain(m *testing.M) {
if reexec.Init() {
return
}
os.Exit(m.Run())
}
func TestCreate(t *testing.T) {
list := Create()
if list == nil {
t.Fatalf("error creating an empty list")
}
}
func TestFromBlob(t *testing.T) {
for _, version := range []string{
ociFixture,
dockerFixture,
} {
bytes, err := ioutil.ReadFile(version)
if err != nil {
t.Fatalf("error loading %s: %v", version, err)
}
list, err := FromBlob(bytes)
if err != nil {
t.Fatalf("error parsing %s: %v", version, err)
}
if len(list.Docker().Manifests) != len(list.OCIv1().Manifests) {
t.Fatalf("%s: expected the same number of manifests, but %d != %d", version, len(list.Docker().Manifests), len(list.OCIv1().Manifests))
}
for i := range list.Docker().Manifests {
d := list.Docker().Manifests[i]
o := list.OCIv1().Manifests[i]
if d.Platform.OS != o.Platform.OS {
t.Fatalf("%s: expected the same OS", version)
}
if d.Platform.Architecture != o.Platform.Architecture {
t.Fatalf("%s: expected the same Architecture", version)
}
}
}
}
func TestAddInstance(t *testing.T) {
manifestBytes, err := ioutil.ReadFile("testdata/fedora-minimal.schema2.json")
if err != nil {
t.Fatalf("error loading testdata/fedora-minimal.schema2.json: %v", err)
}
manifestType := manifest.GuessMIMEType(manifestBytes)
manifestDigest, err := manifest.Digest(manifestBytes)
if err != nil {
t.Fatalf("error digesting testdata/fedora-minimal.schema2.json: %v", err)
}
for _, version := range []string{
ociFixture,
dockerFixture,
} {
bytes, err := ioutil.ReadFile(version)
if err != nil {
t.Fatalf("error loading %s: %v", version, err)
}
list, err := FromBlob(bytes)
if err != nil {
t.Fatalf("error parsing %s: %v", version, err)
}
if err = list.AddInstance(manifestDigest, int64(len(manifestBytes)), manifestType, "linux", "amd64", "", nil, "", nil, nil); err != nil {
t.Fatalf("adding an instance failed in %s: %v", version, err)
}
if d, err := list.findDocker(manifestDigest); d == nil || err != nil {
t.Fatalf("adding an instance failed in %s: %v", version, err)
}
if o, err := list.findOCIv1(manifestDigest); o == nil || err != nil {
t.Fatalf("adding an instance failed in %s: %v", version, err)
}
}
}
func TestRemove(t *testing.T) {
bytes, err := ioutil.ReadFile(ociFixture)
if err != nil {
t.Fatalf("error loading blob: %v", err)
}
list, err := FromBlob(bytes)
if err != nil {
t.Fatalf("error parsing blob: %v", err)
}
before := len(list.OCIv1().Manifests)
instanceDigest := expectedInstance
if d, err := list.findDocker(instanceDigest); d == nil || err != nil {
t.Fatalf("finding expected instance failed: %v", err)
}
if o, err := list.findOCIv1(instanceDigest); o == nil || err != nil {
t.Fatalf("finding expected instance failed: %v", err)
}
err = list.Remove(instanceDigest)
if err != nil {
t.Fatalf("error parsing blob: %v", err)
}
after := len(list.Docker().Manifests)
if after != before-1 {
t.Fatalf("removing instance should have succeeded")
}
if d, err := list.findDocker(instanceDigest); d != nil || err == nil {
t.Fatalf("finding instance should have failed")
}
if o, err := list.findOCIv1(instanceDigest); o != nil || err == nil {
t.Fatalf("finding instance should have failed")
}
}
func testString(t *testing.T, values []string, set func(List, digest.Digest, string) error, get func(List, digest.Digest) (string, error)) {
bytes, err := ioutil.ReadFile(ociFixture)
if err != nil {
t.Fatalf("error loading blob: %v", err)
}
list, err := FromBlob(bytes)
if err != nil {
t.Fatalf("error parsing blob: %v", err)
}
for _, testString := range values {
if err = set(list, expectedInstance, testString); err != nil {
t.Fatalf("error setting %q: %v", testString, err)
}
b, err := list.Serialize("")
if err != nil {
t.Fatalf("error serializing list: %v", err)
}
list, err := FromBlob(b)
if err != nil {
t.Fatalf("error parsing list: %v", err)
}
value, err := get(list, expectedInstance)
if err != nil {
t.Fatalf("error retrieving value %q: %v", testString, err)
}
if value != testString {
t.Fatalf("expected value %q, got %q: %v", value, testString, err)
}
}
}
func testStringSlice(t *testing.T, values [][]string, set func(List, digest.Digest, []string) error, get func(List, digest.Digest) ([]string, error)) {
bytes, err := ioutil.ReadFile(ociFixture)
if err != nil {
t.Fatalf("error loading blob: %v", err)
}
list, err := FromBlob(bytes)
if err != nil {
t.Fatalf("error parsing blob: %v", err)
}
for _, testSlice := range values {
if err = set(list, expectedInstance, testSlice); err != nil {
t.Fatalf("error setting %v: %v", testSlice, err)
}
b, err := list.Serialize("")
if err != nil {
t.Fatalf("error serializing list: %v", err)
}
list, err := FromBlob(b)
if err != nil {
t.Fatalf("error parsing list: %v", err)
}
values, err := get(list, expectedInstance)
if err != nil {
t.Fatalf("error retrieving value %v: %v", testSlice, err)
}
if !reflect.DeepEqual(values, testSlice) {
t.Fatalf("expected values %v, got %v: %v", testSlice, values, err)
}
}
}
func testMap(t *testing.T, values []map[string]string, set func(List, *digest.Digest, map[string]string) error, get func(List, *digest.Digest) (map[string]string, error)) {
bytes, err := ioutil.ReadFile(ociFixture)
if err != nil {
t.Fatalf("error loading blob: %v", err)
}
list, err := FromBlob(bytes)
if err != nil {
t.Fatalf("error parsing blob: %v", err)
}
instance := expectedInstance
for _, instanceDigest := range []*digest.Digest{nil, &instance} {
for _, testMap := range values {
if err = set(list, instanceDigest, testMap); err != nil {
t.Fatalf("error setting %v: %v", testMap, err)
}
b, err := list.Serialize("")
if err != nil {
t.Fatalf("error serializing list: %v", err)
}
list, err := FromBlob(b)
if err != nil {
t.Fatalf("error parsing list: %v", err)
}
values, err := get(list, instanceDigest)
if err != nil {
t.Fatalf("error retrieving value %v: %v", testMap, err)
}
if len(values) != len(testMap) {
t.Fatalf("expected %d map entries, got %d", len(testMap), len(values))
}
for k, v := range testMap {
if values[k] != v {
t.Fatalf("expected map value %q=%q, got %q", k, v, values[k])
}
}
}
}
}
func TestAnnotations(t *testing.T) {
testMap(t,
[]map[string]string{{"A": "B", "C": "D"}, {"E": "F", "G": "H"}},
func(l List, i *digest.Digest, m map[string]string) error {
return l.SetAnnotations(i, m)
},
func(l List, i *digest.Digest) (map[string]string, error) {
return l.Annotations(i)
},
)
}
func TestArchitecture(t *testing.T) {
testString(t,
[]string{"abacus", "sliderule"},
func(l List, i digest.Digest, s string) error {
return l.SetArchitecture(i, s)
},
func(l List, i digest.Digest) (string, error) {
return l.Architecture(i)
},
)
}
func TestFeatures(t *testing.T) {
testStringSlice(t,
[][]string{{"chrome", "hubcaps"}, {"climate", "control"}},
func(l List, i digest.Digest, s []string) error {
return l.SetFeatures(i, s)
},
func(l List, i digest.Digest) ([]string, error) {
return l.Features(i)
},
)
}
func TestOS(t *testing.T) {
testString(t,
[]string{"linux", "darwin"},
func(l List, i digest.Digest, s string) error {
return l.SetOS(i, s)
},
func(l List, i digest.Digest) (string, error) {
return l.OS(i)
},
)
}
func TestOSFeatures(t *testing.T) {
testStringSlice(t,
[][]string{{"ipv6", "containers"}, {"nested", "virtualization"}},
func(l List, i digest.Digest, s []string) error {
return l.SetOSFeatures(i, s)
},
func(l List, i digest.Digest) ([]string, error) {
return l.OSFeatures(i)
},
)
}
func TestOSVersion(t *testing.T) {
testString(t,
[]string{"el7", "el8"},
func(l List, i digest.Digest, s string) error {
return l.SetOSVersion(i, s)
},
func(l List, i digest.Digest) (string, error) {
return l.OSVersion(i)
},
)
}
func TestURLs(t *testing.T) {
testStringSlice(t,
[][]string{{"https://example.com", "https://example.net"}, {"http://example.com", "http://example.net"}},
func(l List, i digest.Digest, s []string) error {
return l.SetURLs(i, s)
},
func(l List, i digest.Digest) ([]string, error) {
return l.URLs(i)
},
)
}
func TestVariant(t *testing.T) {
testString(t,
[]string{"workstation", "cloud", "server"},
func(l List, i digest.Digest, s string) error {
return l.SetVariant(i, s)
},
func(l List, i digest.Digest) (string, error) {
return l.Variant(i)
},
)
}
func TestSerialize(t *testing.T) {
for _, version := range []string{
ociFixture,
dockerFixture,
} {
bytes, err := ioutil.ReadFile(version)
if err != nil {
t.Fatalf("error loading %s: %v", version, err)
}
list, err := FromBlob(bytes)
if err != nil {
t.Fatalf("error parsing %s: %v", version, err)
}
for _, mimeType := range []string{"", v1.MediaTypeImageIndex, manifest.DockerV2ListMediaType} {
b, err := list.Serialize(mimeType)
if err != nil {
t.Fatalf("error serializing %s with type %q: %v", version, mimeType, err)
}
l, err := FromBlob(b)
if err != nil {
t.Fatalf("error parsing %s re-encoded as %q: %v\n%s", version, mimeType, err, string(b))
}
if !reflect.DeepEqual(list.Docker().Manifests, l.Docker().Manifests) {
t.Fatalf("re-encoded %s as %q was different\n%#v\n%#v", version, mimeType, list, l)
}
for i := range list.OCIv1().Manifests {
manifest := list.OCIv1().Manifests[i]
m := l.OCIv1().Manifests[i]
if manifest.Digest != m.Digest ||
manifest.MediaType != m.MediaType ||
manifest.Size != m.Size ||
!reflect.DeepEqual(list.OCIv1().Manifests[i].Platform, l.OCIv1().Manifests[i].Platform) {
t.Fatalf("re-encoded %s OCI %d as %q was different\n%#v\n%#v", version, i, mimeType, list, l)
}
}
}
}
}