-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Implement schema2 manifest formats #1281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
7438f8e
to
9b89d7b
Compare
BTW, corresponding engine-side code is at https://github.com/aaronlehmann/docker/commits/new-manifest |
} | ||
|
||
// Target is not used with manifest lists. | ||
func (m ManifestList) Target() distribution.Descriptor { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Target()
is no longer in the interface
9b89d7b
to
91c1169
Compare
@@ -61,6 +61,12 @@ type Descriptor struct { | |||
// depend on the simplicity of this type. | |||
} | |||
|
|||
// Descriptor returns the descriptor, to make it satisfy the Describable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not satisfy the Describable
interface. Describable
must describe itself (in this case, the descriptor), not the thing it is describing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not satisfy the Describable interface. Describable must describe itself (in this case, the descriptor), not the thing it is describing.
There's a common use case where some code has a slice of descriptors and wants to add those descriptors to a manifest through a ManifestBuilder
. Since ManifestBuilder.AppendReference
takes a Describable
, not having this method defined would force the creation of a wrapper type just to return a descriptor.
Instead of having Descriptor
satisfy Describable
, would you be okay with changing AppendReference
to take a Descriptor
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. The idea of having an interface there is to allow implementations to type switch on elements being added to the manifest and support specialized behavior. I suppose having Descriptor
implement Describable
is a fine exception to make this work. Let's add a comment there, indicating this is the exception and not the rule.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment explaining this.
667ec84
to
2591280
Compare
case manifestlist.MediaTypeManifestList: | ||
return ms.manifestListHandler.Unmarshal(ctx, dgst, content) | ||
default: | ||
return nil, distribution.ErrManifestVerification{errors.New("unrecognized manifest content type")} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might be useful to either add mediaType.MediaType
to the error messages or add a debug log with it.
2591280
to
2b7e821
Compare
ManifestList | ||
|
||
// Canonical is the canonical byte representation of the Manifest. | ||
Canonical []byte `json:"-"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a fan of this getting. I see it is only used internally to the package and in api tests. I think it would be a better pattern to expose this only as a function then it can either be type asserted if it is needed or called directly, but never set outside the package. Same goes for manifest list. Schema1 does this pattern but schema1 is already all kinds of messed up 😛
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can probably be unexported in schema2 and manifestlist because Payload
returns this value. OTOH, Payload
returns an error value (currently hardcoded to nil), which will complicate engine code that needs to access the JSON.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unexported Canonical
in schema2 and manifestlist.
5183f5d
to
21dece4
Compare
LGTM |
@@ -83,6 +91,64 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http | |||
return | |||
} | |||
|
|||
supportsSchema2 := false | |||
supportsManifestList := false | |||
if acceptHeaders, ok := r.Header["Accept"]; ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's a problem since you control the client behavior. but strictly, accept headers are supposed to be comma separated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the Go HTTP library handles that when it generates this map? Note the map values are slices.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure to use http.CanonicalHeader
when accessing the header map directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure to use http.CanonicalHeader when accessing the header map directly.
I agree that this is necessary in the general case, but it looks like this would be a noop if the key is already properly formatted.
9570c1b
to
bb51139
Compare
// calls to AppendReference. | ||
layers []distribution.Descriptor | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you construct a builder from an existing manifest?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you construct a builder from an existing manifest?
The way this is done is by first creating a builder, then passing the references from the existing manifest to AppendReference
. See convertSchema2Manifest
for an example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should that be a specific method or does it not generalize well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it shouldn't be a method in the ManifestBuilder
interface, because it doesn't need to be implemented separately by every builder. It could be a utility function, but it would only factor out a few lines of code that only exist in one place, so I'm not sure it's a big win.
For reference, here's what this utility function would look like:
func ConvertManifest(builder distribution.ManifestBuilder, existingManifest distribution.Manifest) (distribution.Manifest, error) {
for _, d := range existingManifest.References() {
if err := builder.AppendReference(d); err != nil {
return nil, err
}
}
return builder.Build(imh)
}
@@ -67,6 +68,8 @@ type App struct { | |||
|
|||
redis *redis.Pool | |||
|
|||
trustKey libtrust.PrivateKey |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please document correct use of this field: only for signing backwards compat requests.
Current coverage is
|
} | ||
|
||
return nil | ||
panic("not supported") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not return 0, distribution.ErrUnsupported
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not
return 0, distribution.ErrUnsupported?
I suspect this came about through a rebase. Reverting it to the ErrUnsupported
version.
LGTM Most comments are for clarification. We can probably defer the additions to another PR. |
bb51139
to
26f43f7
Compare
Add schema2 manifest implementation. Add a schema2 builder that creates a schema2 manifest from descriptors and a configuration. It will add the configuration to the blob store if necessary. Rename the original schema1 manifest builder to ReferenceBuilder, and create a ConfigBuilder variant that can build a schema1 manifest from an image configuration and set of descriptors. This will be used to translate schema2 manifests to the schema1 format for backward compatibliity, by adding the descriptors from the existing schema2 manifest to the schema1 builder. It will also be used by engine-side push code to create schema1 manifests from the new-style image configration, when necessary to push a schema1 manifest. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
Create signedManifestHandler and schema2ManifestHandler. Use these to unmarshal and put the respective types of manifests from manifestStore. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
…o schema1 on the fly Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
Convert a default platform's manifest to schema1 on the fly. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This makes content type sniffing cleaner. The document just needs to be decoded into a manifest.Versioned structure. It's no longer a two-step process. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
26f43f7
to
6d17423
Compare
Also added tests checking the serialized format of schema2 and manifestlist against constant values. I think this is ready to go. |
Implement schema2 manifest formats
Implement schema2 manifest formats
Implement schema2 manifest formats
This PR introduces support for the schema2 manifest defined in #1068. This covers both an "image manifest", which supersedes the schema1 manifest format, and a "manifest list" that points to the appropriate manifests for specific platforms, to facilitate multi-architecture images.
The
Accept
header is used to gauge which schema versions are accepted by a client. If the client does not indicate that it accepts the schema2 format, and requests a manifest that is stored in this format, the registry will automatically convert it to schema1 format for backward compatibility.schema1's
ManifestBuilder
has been renamed toReferenceBuilder
. This PR introduces a second schema1ManifestBuilder
calledConfigBuilder
, which can generate a schema1 manifest from a Docker 1.10+ image configuration and a set of descriptors. This is used to enable the conversion of schema2 manifests to schema1 format for backward compatibility, and will also be used by the engine to push schema1 manifests to registries that don't yet support schema2.