Skip to content

Commit 070825b

Browse files
committed
manifest: add support for oci image types
Signed-off-by: Justin Chadwell <me@jedevc.com>
1 parent 645395c commit 070825b

6 files changed

Lines changed: 349 additions & 19 deletions

File tree

cli/command/manifest/push.go

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
registryclient "github.com/docker/cli/cli/registry/client"
1313
"github.com/docker/distribution"
1414
"github.com/docker/distribution/manifest/manifestlist"
15+
"github.com/docker/distribution/manifest/ocischema"
1516
"github.com/docker/distribution/manifest/schema2"
1617
"github.com/docker/distribution/reference"
1718
"github.com/docker/docker/registry"
@@ -217,18 +218,34 @@ func buildPutManifestRequest(imageManifest types.ImageManifest, targetRef refere
217218
return mountRequest{}, err
218219
}
219220

220-
// This indentation has to be added to ensure sha parity with the registry
221-
v2ManifestBytes, err := json.MarshalIndent(imageManifest.SchemaV2Manifest, "", " ")
222-
if err != nil {
223-
return mountRequest{}, err
224-
}
225-
// indent only the DeserializedManifest portion of this, in order to maintain parity with the registry
226-
// and not alter the sha
227-
var v2Manifest schema2.DeserializedManifest
228-
if err = v2Manifest.UnmarshalJSON(v2ManifestBytes); err != nil {
229-
return mountRequest{}, err
221+
switch {
222+
case imageManifest.SchemaV2Manifest != nil:
223+
// This indentation has to be added to ensure sha parity with the registry
224+
dt, err := json.MarshalIndent(imageManifest.SchemaV2Manifest, "", " ")
225+
if err != nil {
226+
return mountRequest{}, err
227+
}
228+
// indent only the DeserializedManifest portion of this, in order to maintain parity with the registry
229+
// and not alter the sha
230+
var manifest schema2.DeserializedManifest
231+
if err = manifest.UnmarshalJSON(dt); err != nil {
232+
return mountRequest{}, err
233+
}
234+
imageManifest.SchemaV2Manifest = &manifest
235+
case imageManifest.OCIManifest != nil:
236+
// This indentation has to be added to ensure sha parity with the registry
237+
dt, err := json.MarshalIndent(imageManifest.OCIManifest, "", " ")
238+
if err != nil {
239+
return mountRequest{}, err
240+
}
241+
// indent only the DeserializedManifest portion of this, in order to maintain parity with the registry
242+
// and not alter the sha
243+
var manifest ocischema.DeserializedManifest
244+
if err = manifest.UnmarshalJSON(dt); err != nil {
245+
return mountRequest{}, err
246+
}
247+
imageManifest.OCIManifest = &manifest
230248
}
231-
imageManifest.SchemaV2Manifest = &v2Manifest
232249

233250
return mountRequest{ref: mountRef, manifest: imageManifest}, err
234251
}

cli/manifest/types/types.go

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/docker/distribution"
77
"github.com/docker/distribution/manifest/manifestlist"
8+
"github.com/docker/distribution/manifest/ocischema"
89
"github.com/docker/distribution/manifest/schema2"
910
"github.com/docker/distribution/reference"
1011
"github.com/opencontainers/go-digest"
@@ -17,9 +18,12 @@ type ImageManifest struct {
1718
Ref *SerializableNamed
1819
Descriptor ocispec.Descriptor
1920

20-
// SchemaV2Manifest is used for inspection
2121
// TODO: Deprecate this and store manifest blobs
22+
23+
// SchemaV2Manifest is used for inspection
2224
SchemaV2Manifest *schema2.DeserializedManifest `json:",omitempty"`
25+
// OCIManifest is used for inspection
26+
OCIManifest *ocischema.DeserializedManifest `json:",omitempty"`
2327
}
2428

2529
// OCIPlatform creates an OCI platform from a manifest list platform spec
@@ -53,8 +57,15 @@ func PlatformSpecFromOCI(p *ocispec.Platform) *manifestlist.PlatformSpec {
5357
// Blobs returns the digests for all the blobs referenced by this manifest
5458
func (i ImageManifest) Blobs() []digest.Digest {
5559
digests := []digest.Digest{}
56-
for _, descriptor := range i.SchemaV2Manifest.References() {
57-
digests = append(digests, descriptor.Digest)
60+
switch {
61+
case i.SchemaV2Manifest != nil:
62+
for _, descriptor := range i.SchemaV2Manifest.References() {
63+
digests = append(digests, descriptor.Digest)
64+
}
65+
case i.OCIManifest != nil:
66+
for _, descriptor := range i.OCIManifest.References() {
67+
digests = append(digests, descriptor.Digest)
68+
}
5869
}
5970
return digests
6071
}
@@ -65,6 +76,8 @@ func (i ImageManifest) Payload() (string, []byte, error) {
6576
switch {
6677
case i.SchemaV2Manifest != nil:
6778
return i.SchemaV2Manifest.Payload()
79+
case i.OCIManifest != nil:
80+
return i.OCIManifest.Payload()
6881
default:
6982
return "", nil, errors.Errorf("%s has no payload", i.Ref)
7083
}
@@ -76,6 +89,8 @@ func (i ImageManifest) References() []distribution.Descriptor {
7689
switch {
7790
case i.SchemaV2Manifest != nil:
7891
return i.SchemaV2Manifest.References()
92+
case i.OCIManifest != nil:
93+
return i.OCIManifest.References()
7994
default:
8095
return nil
8196
}
@@ -91,6 +106,16 @@ func NewImageManifest(ref reference.Named, desc ocispec.Descriptor, manifest *sc
91106
}
92107
}
93108

109+
// NewOCIImageManifest returns a new ImageManifest object. The values for
110+
// Platform are initialized from those in the image
111+
func NewOCIImageManifest(ref reference.Named, desc ocispec.Descriptor, manifest *ocischema.DeserializedManifest) ImageManifest {
112+
return ImageManifest{
113+
Ref: &SerializableNamed{Named: ref},
114+
Descriptor: desc,
115+
OCIManifest: manifest,
116+
}
117+
}
118+
94119
// SerializableNamed is a reference.Named that can be serialized and deserialized
95120
// from JSON
96121
type SerializableNamed struct {

cli/registry/client/fetcher.go

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/docker/cli/cli/manifest/types"
88
"github.com/docker/distribution"
99
"github.com/docker/distribution/manifest/manifestlist"
10+
"github.com/docker/distribution/manifest/ocischema"
1011
"github.com/docker/distribution/manifest/schema2"
1112
"github.com/docker/distribution/reference"
1213
"github.com/docker/distribution/registry/api/errcode"
@@ -35,6 +36,12 @@ func fetchManifest(ctx context.Context, repo distribution.Repository, ref refere
3536
return types.ImageManifest{}, err
3637
}
3738
return imageManifest, nil
39+
case *ocischema.DeserializedManifest:
40+
imageManifest, err := pullManifestOCISchema(ctx, ref, repo, *v)
41+
if err != nil {
42+
return types.ImageManifest{}, err
43+
}
44+
return imageManifest, nil
3845
case *manifestlist.DeserializedManifestList:
3946
return types.ImageManifest{}, errors.Errorf("%s is a manifest list", ref)
4047
}
@@ -94,6 +101,28 @@ func pullManifestSchemaV2(ctx context.Context, ref reference.Named, repo distrib
94101
return types.NewImageManifest(ref, manifestDesc, &mfst), nil
95102
}
96103

104+
func pullManifestOCISchema(ctx context.Context, ref reference.Named, repo distribution.Repository, mfst ocischema.DeserializedManifest) (types.ImageManifest, error) {
105+
manifestDesc, err := validateManifestDigest(ref, mfst)
106+
if err != nil {
107+
return types.ImageManifest{}, err
108+
}
109+
configJSON, err := pullManifestSchemaV2ImageConfig(ctx, mfst.Target().Digest, repo)
110+
if err != nil {
111+
return types.ImageManifest{}, err
112+
}
113+
114+
if manifestDesc.Platform == nil {
115+
manifestDesc.Platform = &ocispec.Platform{}
116+
}
117+
118+
// Fill in os and architecture fields from config JSON
119+
if err := json.Unmarshal(configJSON, manifestDesc.Platform); err != nil {
120+
return types.ImageManifest{}, err
121+
}
122+
123+
return types.NewOCIImageManifest(ref, manifestDesc, &mfst), nil
124+
}
125+
97126
func pullManifestSchemaV2ImageConfig(ctx context.Context, dgst digest.Digest, repo distribution.Repository) ([]byte, error) {
98127
blobs := repo.Blobs(ctx)
99128
configJSON, err := blobs.Get(ctx, dgst)
@@ -153,16 +182,21 @@ func pullManifestList(ctx context.Context, ref reference.Named, repo distributio
153182
if err != nil {
154183
return nil, err
155184
}
156-
v, ok := manifest.(*schema2.DeserializedManifest)
157-
if !ok {
158-
return nil, errors.Errorf("unsupported manifest format: %v", v)
159-
}
160185

161186
manifestRef, err := reference.WithDigest(ref, manifestDescriptor.Digest)
162187
if err != nil {
163188
return nil, err
164189
}
165-
imageManifest, err := pullManifestSchemaV2(ctx, manifestRef, repo, *v)
190+
191+
var imageManifest types.ImageManifest
192+
switch v := manifest.(type) {
193+
case *schema2.DeserializedManifest:
194+
imageManifest, err = pullManifestSchemaV2(ctx, manifestRef, repo, *v)
195+
case *ocischema.DeserializedManifest:
196+
imageManifest, err = pullManifestOCISchema(ctx, manifestRef, repo, *v)
197+
default:
198+
err = errors.Errorf("unsupported manifest type: %T", manifest)
199+
}
166200
if err != nil {
167201
return nil, err
168202
}

vendor/github.com/docker/distribution/manifest/ocischema/builder.go

Lines changed: 107 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)