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

Correctly compute UncompressedSize on zstd:chunked pull, don’t set it on estargz #2130

Merged
merged 3 commits into from
Oct 15, 2024

Conversation

mtrmac
Copy link
Collaborator

@mtrmac mtrmac commented Oct 10, 2024

The current value obtained by summing the sizes of regular file contents does not match the size of the uncompressed layer tarball.

We don't have a convenient source to compute the correct size for estargz without pulling the full layer and defeating the point.

For recent zstd:chunked images, we have the full tar-split, so we can compute the correct size; for now, this doesn't do that. That might slow down image size computation.


Absolutely untested, and we probably do want the tar-split-based computation to happen.

@mtrmac
Copy link
Collaborator Author

mtrmac commented Oct 10, 2024

Cc: @giuseppe . This should fix containers/skopeo#2437 (comment) .

@edsantiago
Copy link
Member

I picked this into my pet pr, and it works slightly better, but now it breaks in the podman additional-store test:

...
<+011ms> # # podman pull -q quay.io/libpod/testimage:20241010
<+070ms> # 9c6e6209f54a048342fd899e1e0885be64dfc836ed3664b33d6d07bcb4fc1c51
         # 263622a183c94c2433a43be5464a954b9c4e5b0a77cb177f6fe60dacfff66f80
         # #/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
         # #|     FAIL: pull -q quay.io/libpod/testimage:20241010, using storage.conf
         # #| expected: '263622a183c94c2433a43be5464a954b9c4e5b0a77cb177f6fe60dacfff66f80'
         # #|   actual: '9c6e6209f54a048342fd899e1e0885be64dfc836ed3664b33d6d07bcb4fc1c51'
         # #|         > '263622a183c94c2433a43be5464a954b9c4e5b0a77cb177f6fe60dacfff66f80'
         # #\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Basically, podman pull now spits out two hashes instead of one. Fails very reliably on my laptop too:

$ hack/bats -T --root 010
...
same failure, down to the same hashes

@edsantiago
Copy link
Member

Skopeo is involved, though (for the copy to the store dir), and I haven't rebuilt skopeo, so it's possible that the bad thing is happening in that step.

@mtrmac
Copy link
Collaborator Author

mtrmac commented Oct 10, 2024

Thanks! I can‘t see how the two-ID output can happen.

I’ll set up a VM and test (a better version of) this PR, as well as those tests, tomorrow.

@mtrmac mtrmac changed the title Don't set UncompressedSize on chunked pull Correctly compute UncompressedSize on zstd:chunked pull, don’t set it on estargz Oct 11, 2024
@mtrmac
Copy link
Collaborator Author

mtrmac commented Oct 11, 2024

PR updated to compute the size correctly for zstd:chunked with tar-split, manually tested per containers/skopeo#2437 (comment) (and inspecting the created layer metadata).

Ready for review.

@mtrmac
Copy link
Collaborator Author

mtrmac commented Oct 12, 2024

$ hack/bats -T --root 010
...
same failure, down to the same hashes

Testing that on a slightly unclean VM, Podman commit 2aacd4e212525db4ee06be8e44e4405400d4df9d + this c/storage fix passes 010 successfully in make localsystem; the command above fails with a lot of unexpected, different, errors, e.g. about localhost/podman-pause. It’s very possible there’s something wrong about that environment, I know very little about Podman tests.

And, on the command line, podman pull -q quay.io/libpod/testimage:20241010 outputs just 9c6e6209f54a048342fd899e1e0885be64dfc836ed3664b33d6d07bcb4fc1c51

@mtrmac
Copy link
Collaborator Author

mtrmac commented Oct 12, 2024

Scratch that last part, that’s not invoking the tested version.

@edsantiago
Copy link
Member

Ugh. The podman-pause thing is my fault, I couldn't find a good solution to a nasty problem. Please try this:
containers/podman@9d8e3b0

Copy link
Contributor

@cgwalters cgwalters left a comment

Choose a reason for hiding this comment

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

What about adding the uncompressed size to the ToC? Seems like it'd possibly simplify things?

This looks sane to me but I only gave it a superficial review.

@@ -288,6 +288,36 @@ func ensureTOCMatchesTarSplit(toc *internal.TOC, tarSplit []byte) error {
return nil
}

// tarSizeFromTarSplit computes the total tarball size, computing only tarSplit
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
// tarSizeFromTarSplit computes the total tarball size, computing only tarSplit
// tarSizeFromTarSplit computes the total tarball size using only the tarSplit metadata

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks, I don’t know what I was thinking.

case storage.FileType:
// entry.Size is the “logical size”, which might not be the physical size for sparse entries;
// but the way tar-split/tar/asm.WriteOutputTarStream combines FileType entries and returned files contents,
// sparse files are not supported.
Copy link
Contributor

Choose a reason for hiding this comment

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

But we don't error out today?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If we should be erroring out, that’s something that should happen at tar-split construction.

The physical size outright isn’t available in the tar-split format, so here this consumer is only documenting the impact of that assumption.

(As is, sparse files are barely supported by archive/tar or the tar-split fork: they are indicated either by a special file type, or as a regular file a special PAX record; and there is no API to expose the data / hole segments. Also, c/storage/pkg/archive does not special-case them at all.)

@mtrmac
Copy link
Collaborator Author

mtrmac commented Oct 14, 2024

What about adding the uncompressed size to the ToC? Seems like it'd possibly simplify things?

  • It might be a bit late for that. The format is documented “as normal” with no warning that we might want to change the format and break images. IIRC we sort of did that when introducing the tar-split element, but already at that time we had to relent and re-introduce at least the previous level of support of the no-tar-split format. Still, if this were the only concern, we should at least document the idea around pkg/chunked/internal.TOC for future consideration.
  • If we did include the size in the TOC, we would have to worry about producers recording incorrect sizes. I mean, we have to worry about that anyway… but the tar-split metadata must be correct exactly for the operation which primarily relies on the uncompressed size value, so that is not introducing a new assumption. Compare also how we have 3 different sources of metadata in zstd:chunked files — it seems to me that reducing redundancy is a better trade-off here anyway.

Anyway, I’m open to adding a comment to the TOC type.

The current value obtained by summing the sizes of regular file contents
does not match the size of the uncompressed layer tarball.

We don't have a convenient source to compute the correct size
for estargz without pulling the full layer and defeating the point;
so we must allow for the size being unknown.

For recent zstd:chunked images, we have the full tar-split,
so we can compute the correct size; that will happen in
the following commits.

Signed-off-by: Miloslav Trmač <[email protected]>
Empty tar-split shouldn't ever happen, but being precise
here doesn't hurt.

Signed-off-by: Miloslav Trmač <[email protected]>
@mtrmac
Copy link
Collaborator Author

mtrmac commented Oct 14, 2024

@edsantiago

On a VM manually building things:

All of:

pass make localsystem, apart from [500] podman networking: port with --userns=keep-id for rootless or --uidmap=* for rootful failing to find rootlessport, very likely unrelated.

Similarly, containers/podman@9d8e3b0 passes hack/bats -T --root 010, with or without this PR; and the podman main commit fails, with or without this PR.

Either way, I haven’t been able to reproduce this:

Basically, podman pull now spits out two hashes instead of one.

I have now filed containers/podman#24265 to exercise that in the usual environment.

Is there anything else I should be looking at?

mtrmac added a commit to mtrmac/libpod that referenced this pull request Oct 14, 2024
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@chunked-size
> go mod tidy && go mod vendor

+ a HACK to override the bloat check

Signed-off-by: Miloslav Trmač <[email protected]>
@edsantiago
Copy link
Member

@mtrmac please forgive me, I've switched gears for today and have commitments I must attend to. The crucial element I don't see in your comment is running the full CI suite using testimage:20241010 (or 1009). Those two images were pushed with zstd and are the ones that cause all the problems. Thing is, the failing tests use skopeo for one step, and I'm pretty sure you might also need to patch that. I never was able to figure that out, and can't look into it now.

@mtrmac
Copy link
Collaborator Author

mtrmac commented Oct 14, 2024

Thanks, I was looking for references to testimage:20241010 and I couldn’t find any. I’ll revisit tomorrow.

@edsantiago
Copy link
Member

Thank you. That's really pretty huge.

@rhatdan
Copy link
Member

rhatdan commented Oct 15, 2024

/lgtm

@openshift-ci openshift-ci bot added the lgtm label Oct 15, 2024
@openshift-merge-bot openshift-merge-bot bot merged commit 14d1fce into containers:main Oct 15, 2024
19 checks passed
Comment on lines +141 to +144
// - If UncompressedDigest is set, this must be set to a valid value.
// - Otherwise, if TOCDigest is set, this is either valid or -1.
// - If neither of this digests is set, this should be treated as if it were
// an uninitialized value.
Copy link
Contributor

Choose a reason for hiding this comment

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

You need indentation for list items (same as for code blocks, i.e. an extra space).

Suggested change
// - If UncompressedDigest is set, this must be set to a valid value.
// - Otherwise, if TOCDigest is set, this is either valid or -1.
// - If neither of this digests is set, this should be treated as if it were
// an uninitialized value.
// - If UncompressedDigest is set, this must be set to a valid value.
// - Otherwise, if TOCDigest is set, this is either valid or -1.
// - If neither of this digests is set, this should be treated as if it were
// an uninitialized value.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, gofmt didn’t want to help with this (and struct field comments are formatted in HTML just as an uninterpreted code block), so I punted.

You’re right, this is the right thing to do. Fixed in #2136 .

if tarSplit != nil {
uncompressedTarSize, err = tarSizeFromTarSplit(tarSplit)
if err != nil {
return nil, fmt.Errorf("computing size from tar-split")
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: errors.New, or fmt.Errorf("computing site from tar-split: %w", err), or just err.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks! Fixed in #2136 .

@mtrmac
Copy link
Collaborator Author

mtrmac commented Oct 15, 2024

So, for your purposes, change 20241011 above to 20240123, and import the 11-12-13 changes to 320-system-df, and skip (or ignore) the apiv2 tests.

Note to self (and in case it is useful to others): Commit “DO NOT MERGE: Test with a zstd:chunked testimage” in containers/podman#24287 .

@mtrmac mtrmac deleted the chunked-size branch October 15, 2024 22:31
@mtrmac mtrmac mentioned this pull request Oct 15, 2024
vrothberg added a commit that referenced this pull request Oct 16, 2024
@cgwalters
Copy link
Contributor

The zstd:chunked implementation changes how image IDs are computed: So far, (for schema2 and OCI), the image ID == config digest. With zstd:chunked, partially-pulled images and fully-pulled images (depending on the code path and other options) have different IDs, there can be 2^(layers) different IDs for “the same” image.

Is there more info on that? Sounds worth adding to docs/containers-storage-composefs.md perhaps.

@mtrmac
Copy link
Collaborator Author

mtrmac commented Oct 16, 2024

@cgwalters That’s not directly a c/storage property; it’s a c/image choice of a a deterministic image ID, in https://github.com/containers/image/blob/cba49408c0ea237a6aa6dba5e81b74f4a8f23480/storage/storage_dest.go#L671-L684 .

Yes, we might eventually need a user-facing explanation; I’m not sure where is a good place for it, the internal c/* projects are not too likely to be known to users. Maybe a Podman blog post.

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

Successfully merging this pull request may close these issues.

6 participants