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

feat: add WithMount method to support cross repository blob mounting #632

Closed
wants to merge 2 commits into from

Conversation

ktarplee
Copy link
Contributor

@ktarplee ktarplee commented Oct 31, 2023

This includes everything from #631 but adds more (debatable) changes.

Closes #580

I realize I still need to add tests but I wanted to see if this approach is acceptable first.

@codecov-commenter
Copy link

codecov-commenter commented Oct 31, 2023

Codecov Report

Attention: 13 lines in your changes are missing coverage. Please review.

Comparison is base (faaa1dd) 75.41% compared to head (4becd16) 75.36%.

Files Patch % Lines
copy.go 64.86% 9 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #632      +/-   ##
==========================================
- Coverage   75.41%   75.36%   -0.06%     
==========================================
  Files          59       59              
  Lines        5606     5647      +41     
==========================================
+ Hits         4228     4256      +28     
- Misses       1015     1024       +9     
- Partials      363      367       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@ktarplee
Copy link
Contributor Author

ktarplee commented Nov 3, 2023

@Wwwsylvia Any feedback on this approach?

@Wwwsylvia
Copy link
Member

@ktarplee I'm busy with other stuffs these days, might take a closer look into this a bit later. Sorry for the inconvenience. 😟

Copy link
Member

@Wwwsylvia Wwwsylvia 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 approach works and looks concise. @shizhMSFT Do you have any concerns?

copy.go Outdated Show resolved Hide resolved
copy.go Outdated Show resolved Hide resolved
@ktarplee
Copy link
Contributor Author

Why does registry.Repository not include registry.Mounter?

@ktarplee ktarplee marked this pull request as ready for review November 17, 2023 18:14
@ktarplee ktarplee force-pushed the 580-mount-copy branch 2 times, most recently from 3a4bc30 to 45a1cb8 Compare November 22, 2023 11:21
@Wwwsylvia
Copy link
Member

Why does registry.Repository not include registry.Mounter?

registry.Mounter was introduced in v2.2.0 where breaking changes were not allowed.

// Mounter allows cross-repository blob mounts.
// For backward compatibility reasons, this is not implemented by
// BlobStore: use a type assertion to check availability.
type Mounter interface {
// Mount makes the blob with the given descriptor in fromRepo
// available in the repository signified by the receiver.
Mount(ctx context.Context,
desc ocispec.Descriptor,
fromRepo string,
getContent func() (io.ReadCloser, error),
) error
}

@Wwwsylvia
Copy link
Member

I need some more time to review this and do some integration tests.

Copy link
Member

@Wwwsylvia Wwwsylvia left a comment

Choose a reason for hiding this comment

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

I've tested this against ACR, zot and distribution.
The blobs can be mounted as expected in ACR and distribution, while they get skipped in zot. Looks like zot deduplicates blobs across repositories.

LGTM with a few nit suggestions.
BTW can we have the PR title start with "feat:"?

copy_test.go Outdated Show resolved Hide resolved
registry/remote/repository_test.go Outdated Show resolved Hide resolved
@Wwwsylvia
Copy link
Member

@qweeah Can you also help to review this and check if it meets the needs of ORAS CLI?

@ktarplee ktarplee changed the title Added a helper function to support cross repo blob mounting feat: Added a helper function to support cross repo blob mounting Nov 27, 2023
@ktarplee ktarplee changed the title feat: Added a helper function to support cross repo blob mounting feat: Add WithMount method to support cross repository blob mounting Nov 27, 2023
Copy link
Member

@Wwwsylvia Wwwsylvia left a comment

Choose a reason for hiding this comment

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

LGTM

@Wwwsylvia
Copy link
Member

@shizhMSFT Please take a look when you have a chance.

@shizhMSFT
Copy link
Contributor

shizhMSFT commented Nov 27, 2023

@shizhMSFT Please take a look when you have a chance.

Please expect delayed review since I’m currently OOF.

@qweeah
Copy link
Contributor

qweeah commented Nov 30, 2023

LGTM although IANAM

(Tried to implement an index creation command based on d5cc4c2 and it looks good, see asciicast)

@shizhMSFT shizhMSFT changed the title feat: Add WithMount method to support cross repository blob mounting feat: add WithMount method to support cross repository blob mounting Dec 7, 2023
Copy link
Contributor

@shizhMSFT shizhMSFT left a comment

Choose a reason for hiding this comment

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

@ktarplee Sorry for the late review as I was OOF.

registry/remote/repository.go Outdated Show resolved Hide resolved

opts := oras.CopyOptions{}
// Enable cross-repository blob mounting
opts.WithMount("source", dst.(registry.Mounter), nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

What will happen if other target instead of dst is passed to WithMount?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let's say the true destination target is dst but instead they pass in dst2. The blobs would be mounted or copied to dst2 but the manifest would be copied to dst and fail due to missing blobs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we avoid that situation in the first place with a better API design?


opts := oras.CopyOptions{}
// Enable cross-repository blob mounting
opts.WithMount("source", dst.(registry.Mounter), nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

What will happen if someone call WithMount twice?

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 is a fun one. I think it would work and not produce an error (but I have not tried it). Let's call the preCopy functions preCopy1, preCopy2, preCopy3. The last one is the one created by the last call to WithMount. The first thing that would happen is the call to preCopy3 which would attempt a mount. Let's assume the mount failed, then getContent would get called (defined by preCopy3) and it would call preCopy2. preCopy2 was created by the first call to WithMount so it will try to mount the blob with that source repo. Let's assume it also fails, then getContent would call preCopy1. Then getContent (from preCopy2) would return errdef.ErrUnsupported (to be renamed) and the blob would be copied. Then getContent (from preCopy3) would return errdef.ErrUnsupported (to be renamed) and the blob would be copied again. So it seems like the blob would be copied twice with this design. Not fatal but not ideal either.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could add a field to CopyOptions, something like tryingMount bool. In WithMount() we can check that tryingMount is false (else panic) and then set it to true. Then if the programmer makes the error to call WithMount twice they get a reasonable error.


tagName := "latest"

opts := oras.CopyOptions{}
Copy link
Contributor

Choose a reason for hiding this comment

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

What if someone reuse oras.CopyOptions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The CopyOptions after a WithMount would only be usable when the same source and destination are not changed. That is not ideal. A shallow copy of the oras.CopyOptions before calling WithMount() solves this problem. That is how I use this in my code.

Copy link
Contributor

Choose a reason for hiding this comment

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

It would be ideal if we would reuse the CopyOptions. Currently, only PackOptions is not re-usable.

Copy link
Contributor Author

@ktarplee ktarplee Dec 29, 2023

Choose a reason for hiding this comment

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

We could change the signature of the WithMount() to return a modified copy of the receiver. It would not modify the receiver so opts below could be reused. It would then be used like so:

desc, err := oras.Copy(ctx, src, tagName, dst, tagName, 
                       opts.WithMount("source", dst.(registry.Mounter), nil))

I think that would reduce the misuse and make it more obvious what it does.

Implements a WithMount method on CopyGraphOptions

Also allows for getContent to return ErrUnsupported to fall back to default behavior.

Signed-off-by: Kyle M. Tarplee <[email protected]>
@ktarplee
Copy link
Contributor Author

ktarplee commented Jan 2, 2024

I have an idea to improve the API design of this and allow for trying to mount from multiple source locations. I will work on that very soon. The worst part about this design is that it is too limiting. For example, imagine someone is copying from reg1.example.com/repo/a to reg2.example.com/repo/b. In this case the blobs cannot be mounted from reg1.example.com since it is a different registry. However, maybe blob1 can be mounted from reg2.example.com/repo/c and blob2 can be mounted from reg2.example.com/repo/d. The WithMount(src, ...) approach is too limiting in the sense that it only allows for the common case of a single source repository for all blobs in the copy. It does not allow for the flexibility of having

  1. blobs come from different source repos (common in the database case)
  2. blobs coming from one of any number of repos (common in the database case)

I think some of the design issues highlighted above with the WithMount approach would go away if we went with an approach similar to this.

@ktarplee
Copy link
Contributor Author

ktarplee commented Jan 5, 2024

Closing this in favor of #665

@ktarplee ktarplee closed this Jan 5, 2024
shizhMSFT pushed a commit that referenced this pull request Jan 10, 2024
Adds MountFrom and OnMounted to CopyGraphOptions.
Allows for trying to mount from multiple repositories.

Closes #580 

I think this is a better approach than my other PR #632

Signed-off-by: Kyle M. Tarplee <[email protected]>
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.

Support cross-repository mounting in Copy
5 participants