Repository: distribution/distribution
Pull request #3810 (2022-11-29 22:25:19)

Descriptor: align field order with OCI image specification
I am looking at aligning the types defined in this repository with the
OCI image specification, and potentially exchanging local types with
those from the specification.

This patch is a stepping-stone towards that effort, but as this changes
the format of the serialized JSON, I wanted to put this up first before
proceeding with the other work in case there are concerns.



Patch:
diff --git a/blobs.go b/blobs.go
index d0729934..b7583cc7 100644
--- a/blobs.go
+++ b/blobs.go
@@ -63,13 +63,13 @@ type Descriptor struct {
 	// encoded as utf-8.
 	MediaType string `json:"mediaType,omitempty"`
 
-	// Size in bytes of content.
-	Size int64 `json:"size,omitempty"`
-
 	// Digest uniquely identifies the content. A byte stream can be verified
 	// against this digest.
 	Digest digest.Digest `json:"digest,omitempty"`
 
+	// Size in bytes of content.
+	Size int64 `json:"size,omitempty"`
+
 	// URLs contains the source URLs of this content.
 	URLs []string `json:"urls,omitempty"`
 
diff --git a/docs/notifications.md b/docs/notifications.md
index 44360505..69ac90d0 100644
--- a/docs/notifications.md
+++ b/docs/notifications.md
@@ -93,8 +93,8 @@ manifest:
       "action": "pull",
       "target": {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
-        "size": 708,
         "digest": "sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf",
+        "size": 708,
         "length": 708,
         "repository": "hello-world",
         "url": "http://192.168.100.227:5000/v2/hello-world/manifests/sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf",
@@ -171,8 +171,8 @@ Content-Type: application/vnd.docker.distribution.events.v1+json
       "action": "push",
       "target": {
         "mediaType": "application/vnd.docker.distribution.manifest.v1+json",
-        "length": 1,
         "digest": "sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf",
+        "length": 1,
         "repository": "library/test",
         "url": "https://example.com/v2/library/test/manifests/sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"
       },
@@ -196,8 +196,8 @@ Content-Type: application/vnd.docker.distribution.events.v1+json
       "action": "push",
       "target": {
         "mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
-        "length": 2,
         "digest": "sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
+        "length": 2,
         "repository": "library/test",
         "url": "https://example.com/v2/library/test/blobs/sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"
       },
@@ -221,8 +221,8 @@ Content-Type: application/vnd.docker.distribution.events.v1+json
       "action": "push",
       "target": {
         "mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
-        "length": 3,
         "digest": "sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
+        "length": 3,
         "repository": "library/test",
         "url": "https://example.com/v2/library/test/blobs/sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"
       },
diff --git a/docs/spec/manifest-v2-2.md b/docs/spec/manifest-v2-2.md
index f8a4671e..81f40a39 100644
--- a/docs/spec/manifest-v2-2.md
+++ b/docs/spec/manifest-v2-2.md
@@ -124,8 +124,8 @@ image manifest based on the Content-Type returned in the HTTP response.
   "manifests": [
     {
       "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
-      "size": 7143,
       "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
+      "size": 7143,
       "platform": {
         "architecture": "ppc64le",
         "os": "linux"
@@ -133,8 +133,8 @@ image manifest based on the Content-Type returned in the HTTP response.
     },
     {
       "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
-      "size": 7682,
       "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
+      "size": 7682,
       "platform": {
         "architecture": "amd64",
         "os": "linux",
@@ -232,24 +232,24 @@ image. It's the direct replacement for the schema-1 manifest.
     "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
     "config": {
         "mediaType": "application/vnd.docker.container.image.v1+json",
-        "size": 7023,
-        "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
+        "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7",
+        "size": 7023
     },
     "layers": [
         {
             "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
-            "size": 32654,
-            "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
+            "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
+            "size": 32654
         },
         {
             "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
-            "size": 16724,
-            "digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
+            "digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
+            "size": 16724
         },
         {
             "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
-            "size": 73109,
-            "digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
+            "digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
+            "size": 73109
         }
     ]
 }
diff --git a/manifest/schema1/manifest.go b/manifest/schema1/manifest.go
index c99a48be..7feeef7c 100644
--- a/manifest/schema1/manifest.go
+++ b/manifest/schema1/manifest.go
@@ -35,9 +35,9 @@ func init() {
 		}
 
 		desc := distribution.Descriptor{
+			MediaType: MediaTypeSignedManifest,
 			Digest:    digest.FromBytes(sm.Canonical),
 			Size:      int64(len(sm.Canonical)),
-			MediaType: MediaTypeSignedManifest,
 		}
 		return sm, desc, err
 	}
diff --git a/notifications/bridge.go b/notifications/bridge.go
index 58a1d3fc..ff5a7980 100644
--- a/notifications/bridge.go
+++ b/notifications/bridge.go
@@ -150,9 +150,9 @@ func (b *bridge) createManifestEvent(action string, repo reference.Named, sm dis
 	}
 
 	event.Target.MediaType = mt
-	event.Target.Length = desc.Size
-	event.Target.Size = desc.Size
 	event.Target.Digest = desc.Digest
+	event.Target.Size = desc.Size
+	event.Target.Length = desc.Size
 	if b.includeReferences {
 		event.Target.References = append(event.Target.References, manifest.References()...)
 	}


Test patch:
diff --git a/manifest/manifestlist/manifestlist_test.go b/manifest/manifestlist/manifestlist_test.go
index 9aacd54a..842ae310 100644
--- a/manifest/manifestlist/manifestlist_test.go
+++ b/manifest/manifestlist/manifestlist_test.go
@@ -18,8 +18,8 @@ const expectedManifestListSerialization = `{
    "manifests": [
       {
          "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
-         "size": 985,
          "digest": "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
+         "size": 985,
          "platform": {
             "architecture": "amd64",
             "os": "linux",
@@ -30,8 +30,8 @@ const expectedManifestListSerialization = `{
       },
       {
          "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
-         "size": 2392,
          "digest": "sha256:6346340964309634683409684360934680934608934608934608934068934608",
+         "size": 2392,
          "platform": {
             "architecture": "sun4m",
             "os": "sunos"
@@ -44,9 +44,9 @@ func makeTestManifestList(t *testing.T, mediaType string) ([]ManifestDescriptor,
 	manifestDescriptors := []ManifestDescriptor{
 		{
 			Descriptor: distribution.Descriptor{
+				MediaType: "application/vnd.docker.distribution.manifest.v2+json",
 				Digest:    "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
 				Size:      985,
-				MediaType: "application/vnd.docker.distribution.manifest.v2+json",
 			},
 			Platform: PlatformSpec{
 				Architecture: "amd64",
@@ -56,9 +56,9 @@ func makeTestManifestList(t *testing.T, mediaType string) ([]ManifestDescriptor,
 		},
 		{
 			Descriptor: distribution.Descriptor{
+				MediaType: "application/vnd.docker.distribution.manifest.v2+json",
 				Digest:    "sha256:6346340964309634683409684360934680934608934608934608934068934608",
 				Size:      2392,
-				MediaType: "application/vnd.docker.distribution.manifest.v2+json",
 			},
 			Platform: PlatformSpec{
 				Architecture: "sun4m",
@@ -142,8 +142,8 @@ const expectedOCIImageIndexSerialization = `{
    "manifests": [
       {
          "mediaType": "application/vnd.oci.image.manifest.v1+json",
-         "size": 985,
          "digest": "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
+         "size": 985,
          "platform": {
             "architecture": "amd64",
             "os": "linux",
@@ -154,8 +154,8 @@ const expectedOCIImageIndexSerialization = `{
       },
       {
          "mediaType": "application/vnd.oci.image.manifest.v1+json",
-         "size": 985,
          "digest": "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
+         "size": 985,
          "annotations": {
             "platform": "none"
          },
@@ -166,8 +166,8 @@ const expectedOCIImageIndexSerialization = `{
       },
       {
          "mediaType": "application/vnd.oci.image.manifest.v1+json",
-         "size": 2392,
          "digest": "sha256:6346340964309634683409684360934680934608934608934608934068934608",
+         "size": 2392,
          "annotations": {
             "what": "for"
          },
@@ -183,9 +183,9 @@ func makeTestOCIImageIndex(t *testing.T, mediaType string) ([]ManifestDescriptor
 	manifestDescriptors := []ManifestDescriptor{
 		{
 			Descriptor: distribution.Descriptor{
+				MediaType: "application/vnd.oci.image.manifest.v1+json",
 				Digest:    "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
 				Size:      985,
-				MediaType: "application/vnd.oci.image.manifest.v1+json",
 			},
 			Platform: PlatformSpec{
 				Architecture: "amd64",
@@ -195,17 +195,17 @@ func makeTestOCIImageIndex(t *testing.T, mediaType string) ([]ManifestDescriptor
 		},
 		{
 			Descriptor: distribution.Descriptor{
+				MediaType:   "application/vnd.oci.image.manifest.v1+json",
 				Digest:      "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
 				Size:        985,
-				MediaType:   "application/vnd.oci.image.manifest.v1+json",
 				Annotations: map[string]string{"platform": "none"},
 			},
 		},
 		{
 			Descriptor: distribution.Descriptor{
+				MediaType:   "application/vnd.oci.image.manifest.v1+json",
 				Digest:      "sha256:6346340964309634683409684360934680934608934608934608934068934608",
 				Size:        2392,
-				MediaType:   "application/vnd.oci.image.manifest.v1+json",
 				Annotations: map[string]string{"what": "for"},
 			},
 			Platform: PlatformSpec{
diff --git a/manifest/ocischema/builder_test.go b/manifest/ocischema/builder_test.go
index e1fe516c..b147111c 100644
--- a/manifest/ocischema/builder_test.go
+++ b/manifest/ocischema/builder_test.go
@@ -92,20 +92,20 @@ func TestBuilder(t *testing.T) {
 
 	descriptors := []distribution.Descriptor{
 		{
+			MediaType:   v1.MediaTypeImageLayerGzip,
 			Digest:      digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
 			Size:        5312,
-			MediaType:   v1.MediaTypeImageLayerGzip,
 			Annotations: map[string]string{"apple": "orange", "lettuce": "wrap"},
 		},
 		{
+			MediaType: v1.MediaTypeImageLayerGzip,
 			Digest:    digest.Digest("sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"),
 			Size:      235231,
-			MediaType: v1.MediaTypeImageLayerGzip,
 		},
 		{
+			MediaType: v1.MediaTypeImageLayerGzip,
 			Digest:    digest.Digest("sha256:b4ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
 			Size:      639152,
-			MediaType: v1.MediaTypeImageLayerGzip,
 		},
 	}
 	annotations := map[string]string{"hot": "potato"}
diff --git a/manifest/ocischema/manifest_test.go b/manifest/ocischema/manifest_test.go
index 528e303c..a906eab9 100644
--- a/manifest/ocischema/manifest_test.go
+++ b/manifest/ocischema/manifest_test.go
@@ -18,8 +18,8 @@ const expectedManifestSerialization = `{
    "mediaType": "application/vnd.oci.image.manifest.v1+json",
    "config": {
       "mediaType": "application/vnd.oci.image.config.v1+json",
-      "size": 985,
       "digest": "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
+      "size": 985,
       "annotations": {
          "apple": "orange"
       }
@@ -27,8 +27,8 @@ const expectedManifestSerialization = `{
    "layers": [
       {
          "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
-         "size": 153263,
          "digest": "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b",
+         "size": 153263,
          "annotations": {
             "lettuce": "wrap"
          }
@@ -46,16 +46,16 @@ func makeTestManifest(mediaType string) Manifest {
 			MediaType:     mediaType,
 		},
 		Config: distribution.Descriptor{
+			MediaType:   v1.MediaTypeImageConfig,
 			Digest:      "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
 			Size:        985,
-			MediaType:   v1.MediaTypeImageConfig,
 			Annotations: map[string]string{"apple": "orange"},
 		},
 		Layers: []distribution.Descriptor{
 			{
+				MediaType:   v1.MediaTypeImageLayerGzip,
 				Digest:      "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b",
 				Size:        153263,
-				MediaType:   v1.MediaTypeImageLayerGzip,
 				Annotations: map[string]string{"lettuce": "wrap"},
 			},
 		},
diff --git a/manifest/schema1/config_builder_test.go b/manifest/schema1/config_builder_test.go
index 688e73eb..32b7c1f5 100644
--- a/manifest/schema1/config_builder_test.go
+++ b/manifest/schema1/config_builder_test.go
@@ -29,9 +29,9 @@ func (bs *mockBlobService) Stat(ctx context.Context, dgst digest.Digest) (distri
 
 func (bs *mockBlobService) Put(ctx context.Context, mediaType string, p []byte) (distribution.Descriptor, error) {
 	d := distribution.Descriptor{
+		MediaType: mediaType,
 		Digest:    digest.FromBytes(p),
 		Size:      int64(len(p)),
-		MediaType: mediaType,
 	}
 	bs.descriptors[d.Digest] = d
 	return d, nil
diff --git a/manifest/schema2/builder_test.go b/manifest/schema2/builder_test.go
index 81b0ea05..aa9f17c1 100644
--- a/manifest/schema2/builder_test.go
+++ b/manifest/schema2/builder_test.go
@@ -23,9 +23,9 @@ func (bs *mockBlobService) Stat(ctx context.Context, dgst digest.Digest) (distri
 
 func (bs *mockBlobService) Put(ctx context.Context, mediaType string, p []byte) (distribution.Descriptor, error) {
 	d := distribution.Descriptor{
+		MediaType: "application/octet-stream",
 		Digest:    digest.FromBytes(p),
 		Size:      int64(len(p)),
-		MediaType: "application/octet-stream",
 	}
 	bs.descriptors[d.Digest] = d
 	return d, nil
@@ -134,19 +134,19 @@ func TestBuilder(t *testing.T) {
 
 	descriptors := []distribution.Descriptor{
 		{
+			MediaType: MediaTypeLayer,
 			Digest:    digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
 			Size:      5312,
-			MediaType: MediaTypeLayer,
 		},
 		{
+			MediaType: MediaTypeLayer,
 			Digest:    digest.Digest("sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"),
 			Size:      235231,
-			MediaType: MediaTypeLayer,
 		},
 		{
+			MediaType: MediaTypeLayer,
 			Digest:    digest.Digest("sha256:b4ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
 			Size:      639152,
-			MediaType: MediaTypeLayer,
 		},
 	}
 
diff --git a/manifest/schema2/manifest_test.go b/manifest/schema2/manifest_test.go
index da7f43fd..23d69f03 100644
--- a/manifest/schema2/manifest_test.go
+++ b/manifest/schema2/manifest_test.go
@@ -15,14 +15,14 @@ const expectedManifestSerialization = `{
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "config": {
       "mediaType": "application/vnd.docker.container.image.v1+json",
-      "size": 985,
-      "digest": "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b"
+      "digest": "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
+      "size": 985
    },
    "layers": [
       {
          "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
-         "size": 153263,
-         "digest": "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b"
+         "digest": "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b",
+         "size": 153263
       }
    ]
 }`
@@ -34,15 +34,15 @@ func makeTestManifest(mediaType string) Manifest {
 			MediaType:     mediaType,
 		},
 		Config: distribution.Descriptor{
+			MediaType: MediaTypeImageConfig,
 			Digest:    "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
 			Size:      985,
-			MediaType: MediaTypeImageConfig,
 		},
 		Layers: []distribution.Descriptor{
 			{
+				MediaType: MediaTypeLayer,
 				Digest:    "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b",
 				Size:      153263,
-				MediaType: MediaTypeLayer,
 			},
 		},
 	}
diff --git a/notifications/event_test.go b/notifications/event_test.go
index 44f64c6e..41562717 100644
--- a/notifications/event_test.go
+++ b/notifications/event_test.go
@@ -22,8 +22,8 @@ func TestEventEnvelopeJSONFormat(t *testing.T) {
          "action": "push",
          "target": {
             "mediaType": "application/vnd.docker.distribution.manifest.v1+prettyjws",
-            "size": 1,
             "digest": "sha256:0123456789abcdef0",
+            "size": 1,
             "length": 1,
             "repository": "library/test",
             "url": "http://example.com/v2/library/test/manifests/latest"
@@ -48,8 +48,8 @@ func TestEventEnvelopeJSONFormat(t *testing.T) {
          "action": "push",
          "target": {
             "mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
-            "size": 2,
             "digest": "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
+            "size": 2,
             "length": 2,
             "repository": "library/test",
             "url": "http://example.com/v2/library/test/manifests/latest"
@@ -74,8 +74,8 @@ func TestEventEnvelopeJSONFormat(t *testing.T) {
          "action": "push",
          "target": {
             "mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
-            "size": 3,
             "digest": "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d6",
+            "size": 3,
             "length": 3,
             "repository": "library/test",
             "url": "http://example.com/v2/library/test/manifests/latest"
@@ -116,28 +116,28 @@ func TestEventEnvelopeJSONFormat(t *testing.T) {
 
 	manifestPush := prototype
 	manifestPush.ID = "asdf-asdf-asdf-asdf-0"
+	manifestPush.Target.MediaType = schema1.MediaTypeSignedManifest
 	manifestPush.Target.Digest = "sha256:0123456789abcdef0"
-	manifestPush.Target.Length = 1
 	manifestPush.Target.Size = 1
-	manifestPush.Target.MediaType = schema1.MediaTypeSignedManifest
+	manifestPush.Target.Length = 1
 	manifestPush.Target.Repository = "library/test"
 	manifestPush.Target.URL = "http://example.com/v2/library/test/manifests/latest"
 
 	layerPush0 := prototype
 	layerPush0.ID = "asdf-asdf-asdf-asdf-1"
+	layerPush0.Target.MediaType = layerMediaType
 	layerPush0.Target.Digest = "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"
-	layerPush0.Target.Length = 2
 	layerPush0.Target.Size = 2
-	layerPush0.Target.MediaType = layerMediaType
+	layerPush0.Target.Length = 2
 	layerPush0.Target.Repository = "library/test"
 	layerPush0.Target.URL = "http://example.com/v2/library/test/manifests/latest"
 
 	layerPush1 := prototype
 	layerPush1.ID = "asdf-asdf-asdf-asdf-2"
+	layerPush1.Target.MediaType = layerMediaType
 	layerPush1.Target.Digest = "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d6"
-	layerPush1.Target.Length = 3
 	layerPush1.Target.Size = 3
-	layerPush1.Target.MediaType = layerMediaType
+	layerPush1.Target.Length = 3
 	layerPush1.Target.Repository = "library/test"
 	layerPush1.Target.URL = "http://example.com/v2/library/test/manifests/latest"