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

fea(#1619): skopeo sync support use src or dest use oci format #1620

Closed
wants to merge 3 commits into from

Conversation

tanguofu
Copy link

No description provided.

Signed-off-by: root <root@guofutan-NB6.tencent.com>
Copy link
Contributor

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

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

Thanks!

For now, just a quick look:

  • You have already identified the downside: OCI can’t represent the original format of all images. Still, that might not be an issue for some.
  • I feel rather strongly that the code to parse/list images from an OCI repository should exist as a public API in c/image, not here.
  • Is there a specific reason to use this layout (one OCI repo per registry repo)? It seems to unnecessary limit data sharing, which was the motivation for this feature in the first place.

cmd/skopeo/sync.go Outdated Show resolved Hide resolved
cmd/skopeo/sync.go Outdated Show resolved Hide resolved
defer indexJSON.Close()

index := &imgspecv1.Index{}
if err := json.NewDecoder(indexJSON).Decode(index); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

This deep knowledge of image formats really isn’t Skopeo’s business.

Instead, this should look like something very similar to containers/image#1381 ’s oci/archive.Reader: maybe an oci/layout.Reader that can just return oci/layout references (but much simpler than containers/image#1381, because it doesn’t need to maintain state for a decompressed temporary directory), and Skopeo should just consume that API to list the images.

cmd/skopeo/sync.go Outdated Show resolved Hide resolved
cmd/skopeo/sync.go Outdated Show resolved Hide resolved
}

if opts.preserveDigests && opts.destination == oci.Transport.Name() {
return errors.New("oci DESTINATION transports not support --preserve-digests options")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return errors.New("oci DESTINATION transports not support --preserve-digests options")
return errors.New("using the oci transport as a destination does not support --preserve-digests")

Copy link
Contributor

Choose a reason for hiding this comment

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

This was removed entirely?!

Comment on lines 621 to 638
}
if !contains(opts.source, []string{docker.Transport.Name(), directory.Transport.Name(), "yaml"}) {
if !contains(opts.source, []string{docker.Transport.Name(), directory.Transport.Name(), "yaml", oci.Transport.Name()}) {
return errors.Errorf("%q is not a valid source transport", opts.source)
}

if len(opts.destination) == 0 {
return errors.New("A destination transport must be specified")
}
if !contains(opts.destination, []string{docker.Transport.Name(), directory.Transport.Name()}) {
if !contains(opts.destination, []string{docker.Transport.Name(), directory.Transport.Name(), oci.Transport.Name()}) {
return errors.Errorf("%q is not a valid destination transport", opts.destination)
}

if opts.source == opts.destination && opts.source == directory.Transport.Name() {
return errors.New("sync from 'dir' to 'dir' not implemented, consider using rsync instead")
if opts.source == opts.destination && contains(opts.source, []string{directory.Transport.Name(), oci.Transport.Name()}) {
return errors.Errorf("sync from '%s' to '%s' not implemented, consider using rsync instead", opts.source, opts.destination)
}

if opts.preserveDigests && opts.destination == oci.Transport.Name() {
return errors.New("oci DESTINATION transports not support --preserve-digests options")
Copy link
Contributor

Choose a reason for hiding this comment

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

Note to self: All of this looks increasingly clumsy; take a deeper look.

Copy link
Contributor

Choose a reason for hiding this comment

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

(To clarify, I meant the existing code structure — it feels like a collection of special cases disconnected from the actual implementation, and can get out of sync.)

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, let’s split this up, to remove the transport list duplication, and so that imagesToCopy and destinationReference don’t grow very large.

Turn imagesToCopy into a

var sourceImageListers map[string]func(…)… = {
   docker.Transport.Name(): listDockerSourceImages,
   …
}

(imagesToCopy has almost no shared code, so that can be just carved up)

and similarly destinationReference should turn into

var destinationReferenceConstructors map[string]func(…)… = {
   docker.Transport.Name(): createDockerDestinationReference,
   …
}

Then these contains checks turn into

sourceImageLister, ok := sourceImageListers[opts.source]
if !ok { fail }

That feels like a necessary but arguably unrelated cleanup — I can work on that in a separate PR, if you’d like.

cmd/skopeo/sync.go Outdated Show resolved Hide resolved
@mtrmac mtrmac linked an issue Apr 11, 2022 that may be closed by this pull request
@tanguofu
Copy link
Author

tanguofu commented Apr 12, 2022

  1. use library/mysql library/registry as dir name, then it will put the offline registry 127.0.0.1:5000 with same docker image namespace. keep the images name not change also can avoid same image;tag name conflict.

I’m not sure what conflict you mean. Note that the OCI image reference annotations are not limited to Docker-like tags; library/mysql:42.43.47 is a valid OCI image annotation, so oci:directory-path:library/mysql:42.43.47 is a valid way to refer to an image using the oci: transport. In particular, oci:directory:library/mysql:latest and oci:directory:library/registry:latest don’t conflict.

as your mention that OCI can’t represent the original format of all images, could u explain or give some case that oci
incompatible with docker image format.

Docker schema1/schema2 images (i. e. any images not already in the OCI format — most images in the wild, AFAIK) will be converted to OCI. That can preserve all features fine (except for health checks, I think) — but it inevitably changes the binary representation, and the image’s manifest digest. So a Docker schema2 image library/mysql@sha256:$digestvalue would, after transferring using the OCI format, have a different digest at the destination.

root added 2 commits April 14, 2022 17:27
…ll the images within one directory

Signed-off-by: root <root@guofutan-NB6.tencent.com>
Signed-off-by: root <root@guofutan-NB6.tencent.com>
@@ -100,6 +102,62 @@ type dockerImageOptions struct {
noCreds bool // Access the registry anonymously
}

// ociCryptOptions collects CLI flags specific to the encryptLayer and keys of encryption or decryption,
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe

Suggested change
// ociCryptOptions collects CLI flags specific to the encryptLayer and keys of encryption or decryption,
// ociCryptOptions collects CLI flags related to OCI image encryption/decryption

encryptLayer is defined by this struct, so ociCryptOptions … related to encryptLayer is somewhat circular.

Comment on lines +106 to +107
// encryption-key and decryption-key cannot be specified together,
// if set encryptLayer then encryptionKeys must be setted.
Copy link
Contributor

Choose a reason for hiding this comment

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

(Absolutely non-blocking: These might be better as comments on the individual fields. It would also be nice to be consistent about usage of --encryption-key (option name) vs encryptionKeys (struct field name) references.)

decryptionKeys []string // Keys needed to decrypt the image
}

func cryptFlags() (pflag.FlagSet, *ociCryptOptions) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
func cryptFlags() (pflag.FlagSet, *ociCryptOptions) {
func ociCryptFlags() (pflag.FlagSet, *ociCryptOptions) {

would be a tiny bit more consistent with the other CLI helpers.

}
// the directory holding the image must be created here
if err = os.MkdirAll(destination, 0755); err != nil {
return errors.Wrapf(err, "Error creating directory for image %s", destination)
Copy link
Contributor

Choose a reason for hiding this comment

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

Given the “Refusing to overwrite” behavior and this text saying “for image %s”, this seems unlikely to be reused for anything else — it is rather specific to the dir: transport; so I’m not sure that splitting this out is worthwhile.

I guess it depends on if we can find a good clear name for this behavior. checkExistAndMkdir is not that — that feels like two completely separate operations collected together, and checkExist doesn’t indicate anything about the consequences. So, for now it seems to me that leaving this in-line is much easier than finding a good name for a separate function, but there might well be a great name / way to express this that I’m missing.

@@ -19,14 +19,15 @@ One of the problems of prefixing a destination with its transport is that, the r
Available _source_ transports:
- _docker_ (i.e. `--src docker`): _source_ is a repository hosted on a container registry (e.g.: `registry.example.com/busybox`).
If no image tag is specified, skopeo sync copies all the tags found in that repository.
- _dir_ (i.e. `--src dir`): _source_ is a local directory path (e.g.: `/media/usb/`). Refer to skopeo(1) **dir:**_path_ for the local image format.
- _dir_ (i.e. `--src dir`): _source_ is a local c path (e.g.: `/media/usb/`). Refer to skopeo(1) **dir:**_path_ for the local image format.
Copy link
Contributor

Choose a reason for hiding this comment

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

This change seems to be a typo.

Comment on lines +677 to +680
// docker -> dir,docker,oci, trim domain
dockerRef := ref.DockerReference()
tagref, _ := reference.TagNameOnly(dockerRef).(reference.Tagged)
destSuffix = fmt.Sprintf("%s:%s", reference.Path(dockerRef), tagref.Tag())
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is any of this changing at all? We can’t break existing scripts for docker -> dir and docker-> docker copies. Even excepting that, I don’t see why OCI would want to trim the registry domain: skopeo sync --src docker --dest oci quay.io/foo/bar:tag ./oci-archive can, AFAICS, use a quay.io/foo/bar:tag name as the annotation name.

(Also, the source could possibly be a digested reference, where the reference.Tagged cast would crash.)


There are some RFEs about more control of the source/destination name mapping, but that feels quite orthogonal to this PR about OCI support.

@@ -166,36 +176,32 @@ func parseRepositoryReference(input string) (reference.Named, error) {
// destinationReference creates an image reference using the provided transport.
Copy link
Contributor

Choose a reason for hiding this comment

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

Something like

Suggested change
// destinationReference creates an image reference using the provided transport.
// destinationReference creates an image reference, combining the provided transport, the user-specified destination, and destSuffix containing a part of the source image’s name that should be used in the destination name.

otherwise the purpose of destSuffix is not obvious.

Possibly the order of arguments should then be changed to transport, destination, destSuffix so that it matches the usual order of the output.

if err != nil {
return nil, errors.Wrapf(err, "Cannot obtain a valid image reference for transport %q and reference %q", imageTransport.Name(), destination)
if destRefErr != nil {
return nil, errors.Wrapf(destRefErr, "Cannot obtain a valid image reference for transport %q and reference %q", transport, destString)
Copy link
Contributor

Choose a reason for hiding this comment

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

destString is not actually used on the OCI path … I guess this should list all of transport, destination, destSuffix individually..

}

logrus.Debugf("Destination for transport %q: %s", transport, destRef.StringWithinTransport())
Copy link
Contributor

Choose a reason for hiding this comment

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

This can now be just "Destination: %s", transports.ImageName(destRef) … or maybe this log line can be omitted entirely, because that value is already listed in fromToFields.

Originally, the log line was showing the input to ParseReference. The new equivalent would be showing (transport, destination, destSuffix), before the switch starts to interpret them.

}

if opts.preserveDigests && opts.destination == oci.Transport.Name() {
return errors.New("oci DESTINATION transports not support --preserve-digests options")
Copy link
Contributor

Choose a reason for hiding this comment

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

This was removed entirely?!

Copy link
Contributor

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

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

A full review now; #1620 (comment) is still the major structural blocker.

(Also note that tests are failing; see the logs, something like gofmt -s -w cmd/skopeo/copy.go might help.)

@github-actions
Copy link

A friendly reminder that this PR had no activity for 30 days.

@mtrmac mtrmac added the kind/feature A request for, or a PR adding, new functionality label Dec 7, 2022
@github-actions github-actions bot removed the stale-pr label Dec 8, 2022
@github-actions
Copy link

A friendly reminder that this PR had no activity for 30 days.

@github-actions github-actions bot closed this Jan 12, 2024
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 12, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
kind/feature A request for, or a PR adding, new functionality locked - please file new issue/PR stale-pr
Projects
None yet
Development

Successfully merging this pull request may close these issues.

fea: skopeo sync support use src or dest use oci format
2 participants