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

manifest: prepare internal EditInstances #1896

Merged
merged 1 commit into from
Jun 1, 2023

Conversation

flouthoc
Copy link
Contributor

Introduces a new private API which is modification of UpdateInstances so it can allow and support more that one operation on exisiting manifest list.

@flouthoc flouthoc marked this pull request as draft March 25, 2023 11:53
@flouthoc
Copy link
Contributor Author

@mtrmac I was thinking of introducing a new ListEdit type which can contain instanceOperation. WDYT ?

Copy link
Collaborator

@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! We do need a new API, and it’s better to add it private first.


This doesn’t deal in any way with the design questions in #1883 (review) ; the API in this PR right now requires the caller to handle ordering (which I think this subpackage should be handling), and eventually we need to think about deletes.

(It’s perfectly fine to not implement deletes now when we don’t need them, not even the enum value needs to be defined now — but it seems worthwhile to make it possible to add that without another large refactoring; and anyway the ordering point suggests a delete-friendly design.


Compare #1883 (comment) .)

So, maybe structurally something like

enum ListEdit {
// By default, all existing entries are preserved unmodified, unless .update or .delete instructs otherwise
// .update never changes the relative priority (i.e. position) of an entry.
case .update(oldDigest: Digest, newDigest: Digest?, newSize: Int64?, newMIMEType: String?, )
// The `List` instance is responsible for adding the new entry with the right priority (position), compared to other entries. The caller just specifies what to add.
case .add(digest: Digest, size: Int64, mimeType: String, )
// case .delete(digestToDelete: Digest) // unimplemented
}

(The comments on OCI apply symmetrically to schema2.)

internal/manifest/list.go Outdated Show resolved Hide resolved
internal/manifest/list.go Outdated Show resolved Hide resolved
internal/manifest/list.go Outdated Show resolved Hide resolved
internal/manifest/list.go Outdated Show resolved Hide resolved
internal/manifest/oci_index.go Outdated Show resolved Hide resolved
internal/manifest/oci_index.go Outdated Show resolved Hide resolved
internal/manifest/oci_index.go Outdated Show resolved Hide resolved
internal/manifest/list.go Outdated Show resolved Hide resolved
internal/manifest/oci_index.go Outdated Show resolved Hide resolved
@mtrmac mtrmac changed the title manfiest: prepare internal EditInstances manifest: prepare internal EditInstances Apr 4, 2023
@mtrmac mtrmac added the kind/feature A request for, or a PR adding, new functionality label Apr 28, 2023
@mtrmac
Copy link
Collaborator

mtrmac commented May 10, 2023

I think we need something like #1896 (review) :

type ListEdit struct {
  Op ListEditOperation

  // if Op == ListEditUpdate (basically the previous UpdateInstances). All fields must be set.
  UpdateOldDigest digest.Digest
  UpdateDigest digest.Digest
  UpdateSize int64
  UpdateMediaType string

  // If Op = ListEditAdd. All fields must be set.
  AddDigest digest.Digest
  AddSize int64
  AddMediaType string
  AddPlatform imgspecv1.Platform
  AddAnnotations map[string]string
}

func (index *…) EditInstaces(edits []ListEdit) error {
  addedEntries := []Descriptor{}
  for _, edit := range edits {
    switch edit.op {
    case ListEditUpdate:
      i := /*index of entry matching UpdateOldDigest in index.Manifests */
      // Existing UpdateInstances code on index.Manifests[i]
    case ListEditAdd:
      addedEntries = append(addedEntries, /* data from edit */)
    }
  }
  // SOMEHOW sort addedEntries, and index.Manifests, to a reasonable resulting list of instances, and update index.Manifests.
}

with the “somehow sort” part being really the hard problem. When adding a Zstd instance to an existing gzip instance, sort the new one after; when adding a gzip instance to an existing Zstd one, sort the new one before. Hypothetically, if we get 3 different compression formats, the pre-existing manifest could already contain 2 of them in the “wrong” order, and I don’t know what we would do when adding the third one.

Maybe the only long-term sustainable thing is to just ignore the pre-existing order of index.Manifests, and whenever we are adding an entry, to re-sort them all (first by platform, just for human readability; then for platform variants - least compatible first?, then for compression variants).

@flouthoc
Copy link
Contributor Author

@mtrmac One small question ( since I am not sure 😁 ) why is sorting important here ? We are anyways doing the comparison in ChooseInstance while instance is selected from the manifest list https://github.com/containers/image/blob/main/internal/manifest/oci_index.go#L153

@mtrmac
Copy link
Collaborator

mtrmac commented May 17, 2023

why is sorting important here

To allow other implementations unaware of Zstd to choose the right instance. (And potentially to similarly deal with other “dimensions” of priority that might emerge in the future.)

Copy link
Collaborator

@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, that’s some progress.

There is still some fundamental disconnect about the “update” operation — the caller should only need to edit just the wanted updates, not replacements for everything.

Note also the part about not sorting when no updates happen.


The schema2 comments apply to OCI similarly.

internal/manifest/docker_schema2_list.go Outdated Show resolved Hide resolved
internal/manifest/docker_schema2_list.go Outdated Show resolved Hide resolved
internal/manifest/docker_schema2_list.go Outdated Show resolved Hide resolved
internal/manifest/docker_schema2_list.go Outdated Show resolved Hide resolved
internal/manifest/list.go Outdated Show resolved Hide resolved
internal/manifest/oci_index.go Outdated Show resolved Hide resolved
internal/manifest/oci_index.go Outdated Show resolved Hide resolved
internal/manifest/oci_index.go Outdated Show resolved Hide resolved
internal/manifest/oci_index.go Outdated Show resolved Hide resolved
internal/manifest/list.go Outdated Show resolved Hide resolved
Copy link
Collaborator

@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, that’s some progress.

There is still some fundamental disconnect about the “update” operation — the caller should only need to edit just the wanted updates, not replacements for everything.

Note also the part about not sorting when no updates happen.


The schema2 comments apply to OCI similarly.

@flouthoc flouthoc force-pushed the edit-instances branch 2 times, most recently from d42222a to 63c6faf Compare May 22, 2023 16:10
Copy link
Collaborator

@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.

(nice work on the recent changes.)

internal/manifest/oci_index.go Outdated Show resolved Hide resolved
internal/manifest/oci_index.go Outdated Show resolved Hide resolved
@flouthoc flouthoc force-pushed the edit-instances branch 2 times, most recently from 62e3020 to 6ef5f5a Compare May 24, 2023 12:39
@flouthoc
Copy link
Contributor Author

@mtrmac PTAL I made some changes, once it looks good I'll start working on adding some tests.

Copy link
Collaborator

@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.

There are several earlier unaddressed review comments.

internal/manifest/docker_schema2_list.go Outdated Show resolved Hide resolved
internal/manifest/docker_schema2_list.go Outdated Show resolved Hide resolved
internal/manifest/docker_schema2_list.go Outdated Show resolved Hide resolved
internal/manifest/docker_schema2_list.go Outdated Show resolved Hide resolved
internal/manifest/docker_schema2_list.go Outdated Show resolved Hide resolved
Copy link
Collaborator

@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.

Collecting the earlier review comments:

  • ListEdit with modifications irrelevant to matching should not sort
  • Then make UpdateInstances a wrapper over EditInstances
  • (x/exp/slices.SortStableFunc would be a tiny bit more concise than sort.SliceStable)

internal/manifest/list.go Outdated Show resolved Hide resolved
@flouthoc
Copy link
Contributor Author

Updated

(x/exp/slices.SortStableFunc would be a tiny bit more concise than sort.SliceStable)

I just avoided this part cause x/exp mentioned that all functions are experimental/deprecated and they can be removed anytime.

Copy link
Collaborator

@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, looks pretty good!

Some tests — basic smoke tests, and something for the “no changes do not sort” aspect and for Zstd prioritization, would be nice.

internal/manifest/docker_schema2_list.go Outdated Show resolved Hide resolved
internal/manifest/docker_schema2_list.go Outdated Show resolved Hide resolved
internal/manifest/list.go Outdated Show resolved Hide resolved
internal/manifest/docker_schema2_list.go Show resolved Hide resolved
internal/manifest/oci_index.go Outdated Show resolved Hide resolved
internal/manifest/docker_schema2_list.go Outdated Show resolved Hide resolved
@flouthoc
Copy link
Contributor Author

Added a test with zstdpriority check for OCI1Index.

Copy link
Collaborator

@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.

The schema2 part is incorrect now.

internal/manifest/docker_schema2_list.go Outdated Show resolved Hide resolved
internal/manifest/list.go Outdated Show resolved Hide resolved
internal/manifest/oci_index_test.go Outdated Show resolved Hide resolved
internal/manifest/oci_index_test.go Outdated Show resolved Hide resolved
internal/manifest/oci_index_test.go Outdated Show resolved Hide resolved
ListOperation: ListOpUpdate})
err = list.EditInstances(editInstances)
require.NoError(t, err)
assert.Equal(t, list.Instances()[0], editInstances[0].UpdateDigest, "editInstanceTest")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just assert.Equal(t, …, list.Instances(), …), checking all entries?
And potentially make a full comparison of each list.Instance() to make sure the update breaks nothing else, though that might be too verbose.

assert.Equal(t, list.Instances()[0], editInstances[0].UpdateDigest, "editInstanceTest")
instance, err := list.Instance(list.Instances()[0])
require.NoError(t, err)
assert.Equal(t, instance.MediaType, "something", "editInstanceTest")
Copy link
Collaborator

Choose a reason for hiding this comment

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

(Non-blocking, throughout: the parameters to assert.Equal are ordered (expected, actual), not (actual, expected). That only really matters if the test fails…)

err = list.EditInstances(editInstances)
require.NoError(t, err)

// Zstd should be kept on lowest priority as compared to the default gzip ones
Copy link
Collaborator

Choose a reason for hiding this comment

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

Test that all of list.Instances() has the expected number, and order, of entries, please.

Possibly even that the data is all preserved/set as expected — that might be a bit too verbose.

internal/manifest/oci_index_test.go Show resolved Hide resolved
@mtrmac
Copy link
Collaborator

mtrmac commented May 30, 2023

(x/exp/slices.SortStableFunc would be a tiny bit more concise than sort.SliceStable)

I just avoided this part cause x/exp mentioned that all functions are experimental/deprecated and they can be removed anytime.

See https://tip.golang.org/doc/go1.21 / https://pkg.go.dev/slices@master — it is coming in Go 1.21.

*shrug*, I can update that afterwards.

@flouthoc flouthoc force-pushed the edit-instances branch 2 times, most recently from db47a0f to 811c4ee Compare May 31, 2023 06:47
@flouthoc flouthoc requested a review from mtrmac May 31, 2023 06:49
@flouthoc flouthoc marked this pull request as ready for review May 31, 2023 12:34
Copy link
Collaborator

@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!

Feature implementation LGTM; one last round of test cleanups, please. (Keeping the two tests similar, at least where they check for similar things, would be nice.)

internal/manifest/oci_index_test.go Outdated Show resolved Hide resolved
internal/manifest/oci_index_test.go Outdated Show resolved Hide resolved
internal/manifest/oci_index_test.go Show resolved Hide resolved
internal/manifest/oci_index_test.go Outdated Show resolved Hide resolved
internal/manifest/docker_schema2_list_test.go Outdated Show resolved Hide resolved
assert.Equal(t, len(list.Instances()), oldListSize+2)

// Add new elements to the end of old list to maintain order
oldListOrder = append(oldListOrder, digest.Digest("sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
Copy link
Collaborator

Choose a reason for hiding this comment

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

(Absolutely non-blocking: Naming this old is a bit imprecise.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I meant that “old” was getting updated with the after-edit state; at that point it becomes imprecise. The new “original” name does not change that part.

Introduces a new private API which is modification of UpdateInstances so
it can allow and support more that one operation on exisiting manifest
list.

Signed-off-by: Aditya R <[email protected]>
@flouthoc
Copy link
Contributor Author

flouthoc commented Jun 1, 2023

@mtrmac PTAL again

Copy link
Collaborator

@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!

@mtrmac mtrmac merged commit e8c2d91 into containers:main Jun 1, 2023
@flouthoc
Copy link
Contributor Author

flouthoc commented Jun 1, 2023

Yay !!!!

flouthoc added a commit to flouthoc/image that referenced this pull request Jun 2, 2023
After containers#1896 `c/image` now
supports new API `EditInstances` which is more flexible in terms of
`Adding` new instances or `Modifying` exisiting instances so lets use
that.

Signed-off-by: Aditya R <[email protected]>
flouthoc added a commit to flouthoc/image that referenced this pull request Jun 3, 2023
After containers#1896 `c/image` now
supports new API `EditInstances` which is more flexible in terms of
`Adding` new instances or `Modifying` exisiting instances so lets use
that.

Signed-off-by: Aditya R <[email protected]>
flouthoc added a commit to flouthoc/image that referenced this pull request Jun 3, 2023
After containers#1896 `c/image` now
supports new API `EditInstances` which is more flexible in terms of
`Adding` new instances or `Modifying` exisiting instances so lets use
that.

Signed-off-by: Aditya R <[email protected]>
flouthoc added a commit to flouthoc/image that referenced this pull request Jun 3, 2023
After containers#1896 `c/image` now
supports new API `EditInstances` which is more flexible in terms of
`Adding` new instances or `Modifying` exisiting instances so lets use
that.

Signed-off-by: Aditya R <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/feature A request for, or a PR adding, new functionality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants