From cf352199b9e2634bb0a1380bd6905350ef23fa14 Mon Sep 17 00:00:00 2001 From: Aditya R Date: Thu, 28 Jul 2022 13:16:26 +0530 Subject: [PATCH] manifest_list: inspect add fields from both OCIv1 and docker format ManifestInspect should contain all known formats for a valid manifest list as of now only supported formats are `OCIv1` and `Docker` so inspect should support fields from `OCIv1` format as well. Following commit adds a new field to inspect i.e `Annotations` from `OCIv1`. Example output from podman ```console podman manifest inspect test { "schemaVersion": 2, "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", "manifests": [ { "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 528, "digest": "sha256:9b2a28eb47540823042a2ba401386845089bb7b62a9637d55816132c4c3c36eb", "platform": { "architecture": "amd64", "os": "linux" }, "annotations": { "annotationTest1": "annotationTest2" } } ] } ``` Closes: https://github.com/containers/podman/issues/15069 Signed-off-by: Aditya R --- libimage/manifest_list.go | 40 +++++++++++++++++++++++++++++-- libimage/manifest_list_test.go | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/libimage/manifest_list.go b/libimage/manifest_list.go index 4480df548..884771cd6 100644 --- a/libimage/manifest_list.go +++ b/libimage/manifest_list.go @@ -13,6 +13,7 @@ import ( "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" "github.com/containers/storage" + structcopier "github.com/jinzhu/copier" "github.com/opencontainers/go-digest" ) @@ -39,6 +40,28 @@ type ManifestList struct { list manifests.List } +// InspectListDescriptor references a platform-specific manifest. +// Contains exclusive field like `annotations` which is only present in +// OCI spec and not in docker image spec. +type InspectListDescriptor struct { + manifest.Schema2Descriptor + Platform manifest.Schema2PlatformSpec `json:"platform"` + // Annotations contains arbitrary metadata for the image index. + Annotations map[string]string `json:"annotations,omitempty"` +} + +// InspectListInfo is a list of platform-specific manifests, specifically used to +// generate output struct for `podman manifest inspect`. Reason for maintaining and +// having this type is to ensure we can have a common type which contains exclusive +// fields from both Docker manifest format and OCI manifest format. +type InspectListInfo struct { + SchemaVersion int `json:"schemaVersion"` + MediaType string `json:"mediaType"` + Manifests []InspectListDescriptor `json:"manifests"` + // Annotations contains arbitrary metadata for the image index. + Annotations map[string]string `json:"annotations,omitempty"` +} + // ID returns the ID of the manifest list. func (m *ManifestList) ID() string { return m.image.ID() @@ -210,8 +233,21 @@ func (i *Image) IsManifestList(ctx context.Context) (bool, error) { } // Inspect returns a dockerized version of the manifest list. -func (m *ManifestList) Inspect() (*manifest.Schema2List, error) { - return m.list.Docker(), nil +func (m *ManifestList) Inspect() (*InspectListInfo, error) { + inspectList := InspectListInfo{} + dockerFormat := m.list.Docker() + err := structcopier.Copy(&inspectList, &dockerFormat) + if err != nil { + return &inspectList, err + } + // Get missing annotation field from OCIv1 Spec + // and populate inspect data. + ociFormat := m.list.OCIv1() + inspectList.Annotations = ociFormat.Annotations + for i, manifest := range ociFormat.Manifests { + inspectList.Manifests[i].Annotations = manifest.Annotations + } + return &inspectList, nil } // Options for adding a manifest list. diff --git a/libimage/manifest_list_test.go b/libimage/manifest_list_test.go index 169dfa5be..287809583 100644 --- a/libimage/manifest_list_test.go +++ b/libimage/manifest_list_test.go @@ -39,6 +39,50 @@ func TestCreateManifestList(t *testing.T) { require.True(t, errors.Is(err, ErrNotAManifestList)) } +// Inspect must contain both formats i.e OCIv1 and docker +func TestInspectManifestListWithAnnotations(t *testing.T) { + listName := "testInspect" + runtime, cleanup := testNewRuntime(t) + defer cleanup() + ctx := context.Background() + + list, err := runtime.CreateManifestList(listName) + require.NoError(t, err) + require.NotNil(t, list) + + manifestListOpts := &ManifestListAddOptions{All: true} + _, err = list.Add(ctx, "docker://busybox", manifestListOpts) + require.NoError(t, err) + + list, err = runtime.LookupManifestList(listName) + require.NoError(t, err) + require.NotNil(t, list) + + inspectReport, err := list.Inspect() + // get digest of the first instance + digest := inspectReport.Manifests[0].Digest + require.NoError(t, err) + require.NotNil(t, inspectReport) + + annotateOptions := ManifestListAnnotateOptions{} + annotations := make(map[string]string) + annotations["hello"] = "world" + annotateOptions.Annotations = annotations + + err = list.AnnotateInstance(digest, &annotateOptions) + require.NoError(t, err) + // Inspect list again + inspectReport, err = list.Inspect() + require.NoError(t, err) + require.NotNil(t, inspectReport) + // verify annotation + inspectReportJSON, err := json.Marshal(inspectReport) + require.NoError(t, err) + inspectReportString := string(inspectReportJSON) + require.Contains(t, inspectReportString, "hello") + require.Contains(t, inspectReportString, "world") +} + // Following test ensure that `Tag` tags the manifest list instead of resolved image. // Both the tags should point to same image id func TestCreateAndTagManifestList(t *testing.T) {