-
Notifications
You must be signed in to change notification settings - Fork 785
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
RFC: copy: add --multi-arch=sparse:... #1987
base: main
Are you sure you want to change the base?
Conversation
5f4f050
to
75fe37f
Compare
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.
Code is fine (with the comparatively minor question of system
semantics)… but then I got stuck on the platforms.Parse
part below — and I’ll need to leave for the day anyway.
Quite possibly you have already thought about these things and there’s a good reason to interpret the inputs this way.
if platformSpec == "system" { | ||
platformList = append(platformList, imgspecv1.Platform{}) | ||
} else { | ||
p, err := platforms.Parse(platformSpec) |
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.
platforms.Parse
, if I’m reading it correctly, turns "linux"
into linux/$GOARCH
. Or, alternatively, "amd64"
into $GOOS/amd64
.
Hum, do we want that? Especially for Mac users?
Actually, should the UI be built around the OCI “platform” triplet at all, or should we have OS/arch filters separately? (And what does that mean for Zstd compression? Probably nothing…)
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.
platforms.Parse
, if I’m reading it correctly, turns"linux"
intolinux/$GOARCH
. Or, alternatively,"amd64"
into$GOOS/amd64
.Hum, do we want that? Especially for Mac users?
Hmm, for people on GOOS="darwin" I could actually see defaulting to "linux" as being reasonable, but for people running on GOOS="windows" I think it would be surprising.
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.
One more option is to just provide the full-platform filter now (with a more strict parser requiring a full specification?), and leave a nicer arch-filter-only UI for a future improvement — as long as we can see a path to making that possible, and, uh, --multi-arch=sparse2:…
is a last-resort option we always have.
“A nice arch-filter-only UI” is not what the primary motivating issue is asking for right now…
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.
#2175 also suggests using platforms.Parse
, with the same potential surprises.
Both PRs should probably end up with the same decision.
Just to write down an unfinished version of what I’ve been thinking, to make sure this doesn’t block for an extra day.
So maybe something in the direction of recognizing |
46f84eb
to
d494e22
Compare
Took a swing at that sparse syntax. it's not super-pretty, but I tried to reduce the opportunity for copy-paste errors. |
Add a "sparse:..." value for the copy --multi-arch option, where the "..." is a comma-separated list of "system", "arch=[...]", "digest=[]", and "platform=[]" lists. Signed-off-by: Nalin Dahyabhai <[email protected]>
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.
As a matter of picking a design for --multi-arch=sparse:
, sure, this works as a direction/plan.
WRT the semantics of the UI, the thing I conceptually struggle with here is that “filter for these architectures” is not really “filter for these architectures and an implied operating system. But then “filter for architectures, ignore OS” would need to be modeled as an entire new API in copy.Options
, and we almost certainly don’t need all that implementation complexity of doing both platform
and arch
in the first version if we don’t have known high-priority users.
Maybe we can, for now, just implement digest
and platform
(certainly allowing any use case), with a custom platform
parser that requires os/arch (and optionally allows variant), to avoid the defaulting surprises of #1987 (comment) .
Or, alternatively, implement digest
+arch
and not worry about platform
, for a more convenient UI probably also handling most practical uses (multi-OS images are hard for me to imagine currently…). That would require a new filter implementation in c/image/copy
, the existing ChooseInstance
can’t do it.
Implementing “system” cleanly might be simplest by adding an extra boolean to c/image, copy.Options.InstanceIncludeSystem
. [Or, alternatively, we could over-engineer copy.Options.InstanceSelectors
that allows multiple various criteria, but that seems way more of an effort than necessary.]
// we currently don't provide an option to choose the images to copy. That | ||
// could be added in the future. | ||
// It returns the copy.ImageListSelection, instance list, and platform list to use in image.Copy option | ||
func parseMultiArch(globalOptions *globalOptions, multiArch string) (copy.ImageListSelection, []digest.Digest, []imgspecv1.Platform, error) { |
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.
(Eventually, it might be cleaner for this to update fields of copy.Options
in-place instead of returning a list of values that need to be applied in the caller. For now, the three values have different types, which protects us against mistakes just fine.)
}); err != nil { | ||
return nil, nil, err | ||
} else if isSystem { | ||
continue | ||
} | ||
if isDigest, err := parseArg("digest=[", "]", func(digestSpecList string) (bool, error) { |
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 is probably very premature… Let’s settle on the UI and API first before beautifying the implementation.)
It seems to me that something vaguely like
keyword, remainder, err := findKeyword(remainder)
switch keyword {
case "system": …
case "digest": ourArgs, remainder, err = getArgs(remainder, "=[", "]"); …
case "arch": ourArgs, remainder, err = getArgs(remainder, "=[", "]"); …
}
should be possible to do; centralizing the remainder = newRemainder
code in a single place does help us not to forget to update remainder
, but it ends up with these copy&pasted sequences instead, which seems not too be worth it to me. Alternatively, we could end up with a whole type argDescriptor struct { start, end, handler … }
which also seem like an overkill.
for remainder != "" { | ||
parseArg := func(argStart string, argEnd string, fn func(argVal string) (bool, error)) (bool, error) { | ||
if newRemainder, isArg := strings.CutPrefix(remainder, ","+argStart); isArg { | ||
if !isArg { |
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 seems unreachable right now. (Aesthetically, I’d weakly prefer to flip the control flow so that the !isArg
code exits early at the top, instead of at the very bottom of the function.)
return false, nil | ||
} | ||
if isSystem, err := parseArg("system", "", func(string) (bool, error) { | ||
systemPlatform := imgspecv1.Platform{ |
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 continue to be worried about the design that copy.Options.InstancePlatforms
can contain “partial” platform specifications with some fields filled in, with the rest defaulted to runtime.GO*
.
E.g. we can’t represent “find the entry with arch="arm", variant=""
if Variant: ""
meant “the current runtime variant”. (Now, this special case already exists in the underlying ChooseInstance
, but do we need to document that in copy.Options.InstancePlatforms
?)
I’d prefer a copy.InstancePlatformIncludeSystem bool
and let c/image handle that, including how “system” interacts with SystemContext
= globalOptions.override*
.
wantedOS := runtime.GOOS | ||
if globalOptions.overrideOS != "" { | ||
wantedOS = globalOptions.overrideOS | ||
} |
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.
If Mac users must use --override-os=linux --multi-arch=sparse:arch=[amd64,arm64]
, that’s not really any simpler than --multi-arch=sparse:platform=[linux/amd64,linux/arm64]
.
I think if for an arch
, separate and distinct from platform
, to be worth adding (extra complexity in implementation and for users to read about), it should be an arch-only filter, not restricting the OS at all.
I also don’t think we need to build that arch-only filter at all for the first version… see the top-level comment.
} | ||
if isPlatform, err := parseArg("platform=[", "]", func(platformSpecList string) (bool, error) { | ||
for _, platformSpec := range strings.Split(platformSpecList, ",") { | ||
p, err := platforms.Parse(platformSpec) |
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.
If we are to ever have a separate arch=
and platform=
, at most one of them (possibly neither) should default to runtime.GOOS
.
Requiring the user to always provide os/arch (with an optional variant) here, using a custom parser instead of platforms.Parse
, would for now provide a clearly deterministic feature, while still giving us the flexibility to addd that defaulting to GOOS
in the future.
remainder := "," + sparseArg | ||
for remainder != "" { | ||
parseArg := func(argStart string, argEnd string, fn func(argVal string) (bool, error)) (bool, error) { | ||
if newRemainder, isArg := strings.CutPrefix(remainder, ","+argStart); isArg { |
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.
CutPrefix
was added in Go 1.20; currently we declare Go 1.18.
(It is also very neat, I can’t wait for this to be available…)
We are not necessarily stuck on 1.18, and updating is an option. But looking at https://bodhi.fedoraproject.org/updates/?packages=golang , we might be limited to 1.19.
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.
Doh, good catch.
A friendly reminder that this PR had no activity for 30 days. |
@nalind @mtrmac Is there any chance of this being picked up again? This feature would certainly be very useful for our use case, where we are only interested in two platforms Not sure that it has been mentioned already, but there would be an open question as to the behaviour if the source only supported a sub-set of the specified sparse filters. Here, it would probably make sense to warn on each of the unmatched filters, but error if there were none that matched. |
I should at least clean up the containers/image part. |
I think your RFC is very good, but the maintainers of skopeo think that they haven't thought about how to make it more elegant, so this function has never been online. |
Add a "sparse:..." value for the copy --multi-arch option, where the "..." is a comma-separated list of instance digests, platform specifications, or the literal word "system" to signify the local platform. Along with containers/image#1938, I think this one of the features described at #1935 (comment), give or take the presence of "[" and "]" around the list. If it looks about right, I'll see what sorts of tests I can add to the PR.