Skip to content
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

Re-enable cross-repository blob mounts #1793

Merged
merged 12 commits into from
Jun 25, 2019
Merged

Re-enable cross-repository blob mounts #1793

merged 12 commits into from
Jun 25, 2019

Conversation

briandealwis
Copy link
Member

Re-enables mount/from for #169. The speed-up can be immense. Building and pushing an image with openjdk:8 as the base image to a new Docker Hub image takes more than 3 minutes, whereas with mount/from it takes about 8 seconds.

This implementation is is similar in spirit to #1024, but we do some additional checking to ensure we have permission to request a blob mount. Basically the Docker Registry Bearer Token includes the set of repositories and their granted permission. Private repositories won't be included if we don't have permission.

High-level summary of the changes:

  • Authorization now parses the provided Bearer Token to maintain a list of repositories and their granted permissions. It provides a new method, canAccess(repository, access-scope). For non-bearer tokens, canAccess() returns true.
  • RegistryClient.pushBlob() checks that the Authorization allows a cross-repository mount and ignores the mount point if not (this avoids the @chanseokoh's francium25 and francium25paran permission problem discovered previously)
  • BuildConfiguration.newTargetImageRegistryClientFactory() configures the target RegistryClient to request permissions for the base and target repositories when base and target registries are the same, which is recorded in the RegistryEndpointRequestProperties object as a source image name
  • RegistryAuthenticator requests permission for the source repository when set. A key change here is that if authorizing against the additional repository fails, we retry with only the target repository. NOTE: this doesn't seem to happen in practice with Docker Hub¸, and hence the need to check the resulting of the Docker Registry Bearer Token's access grants instead

I debated whether I should provide a property to disable blob mounts or if I should rework RegistryClient.pushBlob() to retry if the blob-mount fails. The Registry Spec says

If a mount fails due to invalid repository or digest arguments, the registry will fall back to the standard upload behavior and return a 202 Accepted with the upload URL in the Location header:

Fixes #169

Copy link
Member

@chanseokoh chanseokoh left a comment

Choose a reason for hiding this comment

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

I think this looks good.

* @return true if the repository was covered
*/
public boolean canAccess(String repository, String access) {
// if null then we assume that all repositories are granted
Copy link
Member

@chanseokoh chanseokoh Jun 21, 2019

Choose a reason for hiding this comment

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

Worth explaining this in the Javadoc too.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I don't understand why "null" is valid

Copy link
Member Author

Choose a reason for hiding this comment

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

The other non-Docker Container Registry Token methods don't provide this information.

Copy link
Member

Choose a reason for hiding this comment

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

I think the method name and the Javadoc can still be confusing. I think we just need to clarify (and justify). For example, I can create an Authorization instance with fromBasicCredentials("my-username", "my-passwd"). Then canAccess("any repository", "pull or push or anything") will always return true. I feel this is not intuitive.

Copy link
Member Author

Choose a reason for hiding this comment

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

What about making this specific to the blob-mount: canAttemptBlobMount(repository)

Copy link
Member

Choose a reason for hiding this comment

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

That technically works. Man, naming is hard. I see depending on the name, we may want to relocate this class from the .http package. (Relocation can be done later.) This class is meant for holding the very string value for the Authorization HTTP header (i.e., for Authorization: <some schema> <token value>) as explained in the class Javadoc, hence was holding the two fields schema and token. I thought repositoryGrants was OK, because it is actually information in token. But if we incorporate the notions of BLOb or cross-repo mount, it may make sense to get this out of .http.

That said, I don't have a strong opinion about what the name should be. Maybe the logic for canAttemptBlobMount can be outside of this method, I don't know.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm good point. I'll move this into RegistryClient — it's only called from pushBlobStep.

  - be more explicit for JWT payload source
  - use JsonTemplateMapper rather than third-party library for parsing JWT
  - simplify RegistryClient factory use
Copy link
Member

@chanseokoh chanseokoh left a comment

Choose a reason for hiding this comment

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

Overall, LGTM.

* @return true if the repository was covered
*/
public boolean canAccess(String repository, String access) {
// if null then we assume that all repositories are granted
Copy link
Member

Choose a reason for hiding this comment

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

I think the method name and the Javadoc can still be confusing. I think we just need to clarify (and justify). For example, I can create an Authorization instance with fromBasicCredentials("my-username", "my-passwd"). Then canAccess("any repository", "pull or push or anything") will always return true. I feel this is not intuitive.

@briandealwis
Copy link
Member Author

Moved the Docker Registry Bearer Token related code into RegistryClient, though I kept the test separate as DockerRegistryBearerTokenTest.

Copy link
Member

@loosebazooka loosebazooka left a comment

Choose a reason for hiding this comment

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

Curious as to why we are "re-enabling", did it work in the past?

@loosebazooka
Copy link
Member

Do you think we should hide this behind a flag for now?

@briandealwis
Copy link
Member Author

We had partially implemented it in 0.9.8 but it broke some people so we backed it out for 0.9.9: #808 #1654

I will add a flag to disable it. The wins from having it enabled are immense (8s vs 3:34m).

@loosebazooka
Copy link
Member

loosebazooka commented Jun 25, 2019

okay, so makes sense to enable by default

@briandealwis briandealwis merged commit 740c2ac into master Jun 25, 2019
@briandealwis briandealwis deleted the i169 branch June 25, 2019 03:36
@briandealwis
Copy link
Member Author

I somehow fat-fingered the commit button before I could edit that mess of a commit message. Blech.

@briandealwis briandealwis added this to the v1.4.0 milestone Jun 25, 2019
@loosebazooka
Copy link
Member

Life goes on

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Have cross-repo mount automatically put source repository for base image in same registry.
4 participants