8000 Implement schema2 manifest formats by aaronlehmann · Pull Request #1281 · distribution/distribution · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

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

Merged
merged 7 commits into from
Jan 8, 2016

Conversation

aaronlehmann
Copy link
Contributor

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 to ReferenceBuilder. This PR introduces a second schema1 ManifestBuilder called ConfigBuilder, 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.

@aaronlehmann
Copy link
Contributor Author

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 {

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

@@ -61,6 +61,12 @@ type Descriptor struct {
// depend on the simplicity of this type.
}

// Descriptor returns the descriptor, to make it satisfy the Describable
Copy link
Collaborator

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.

Copy link
Contributor Author

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?

Copy link
Collaborator

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.

Copy link
Contributor Author

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.

case manifestlist.MediaTypeManifestList:
return ms.manifestListHandler.Unmarshal(ctx, dgst, content)
default:
return nil, distribution.ErrManifestVerification{errors.New("unrecognized manifest content type")}
Copy link
Collaborator

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.

ManifestList

// Canonical is the canonical byte representation of the Manifest.
Canonical []byte `json:"-"`
Copy link
Collaborator

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 😛

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

@aaronlehmann aaronlehmann force-pushed the new-manifest branch 2 times, most recently from 5183f5d to 21dece4 Compare December 22, 2015 22:17
@dmcgowan
Copy link
Collaborator

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 {

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.

Copy link
Contributor Author

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8000

Make sure to use http.CanonicalHeader when accessing the header map directly.

Copy link
Contributor Author

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.

// calls to AppendReference.
layers []distribution.Descriptor
}

Copy link
Collaborator

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?

Copy link
Contributor Author

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.

Copy link
Collaborator

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?

Copy link
Contributor Author

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
Copy link
Collaborator

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.

@codecov-io
Copy link

Current coverage is 58.15%

Merging #1281 into master will increase coverage by +0.50% as of 6d17423

@@            master   #1281   diff @@
======================================
  Files          116     123     +7
  Stmts        10477   10948   +471
  Branches       718     763    +45
  Methods          0       0       
======================================
+ Hit           6041    6367   +326
- Partial        718     763    +45
- Missed        3718    3818   +100

Review entire Coverage Diff as of 6d17423


Uncovered Suggestions

  1. +0.29% via ...ge/driver/gcs/gcs.go#185...216
  2. +0.29% via ...ge/driver/gcs/gcs.go#104...135
  3. +0.22% via ...ge/driver/gcs/gcs.go#474...497
  4. See 7 more...

Powered by Codecov. Updated on successful CI builds.

}

return nil
panic("not supported")
Copy link
Collaborator

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?

Copy link
Contributor Author

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.

@stevvooe
Copy link
Collaborator
stevvooe commented Jan 7, 2016

LGTM

Most comments are for clarification. We can probably defer the additions to another PR.

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>
@aaronlehmann
Copy link
Contributor Author

Also added tests checking the serialized format of schema2 and manifestlist against constant values. I think this is ready to go.

stevvooe added a commit that referenced this pull request Jan 8, 2016
Implement schema2 manifest formats
@stevvooe stevvooe merged commit a7ae88d into distribution:master Jan 8, 2016
@aaronlehmann aaronlehmann deleted the new-manifest branch January 8, 2016 01:22
thaJeztah pushed a commit to thaJeztah/distribution that referenced this pull request Apr 22, 2021
thaJeztah pushed a commit to thaJeztah/distribution that referenced this pull request Jan 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants
0