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

Add names-history support #422

Merged
merged 1 commit into from
Nov 22, 2019
Merged

Add names-history support #422

merged 1 commit into from
Nov 22, 2019

Conversation

saschagrunert
Copy link
Member

This commit adds a new NamesHistory field to the images.json, which
is basically a deduped list of names the image had in the past. The
first entry of the list is the latest history entry.

The main use case for this feature is to tell the end-user which
names/tags an image had in the past if it does not contain any names
any more.

Detailed use case:

  1. Pulling image:v1 into the local registry: names: [ "image:v1" ]
  2. Pushing a new image as image:v1 into the remote registry
  3. Pulling image:v1 again into the local registry:
    • first image: names: [ "image:v1" ]
    • previous v1 image: names: [], names-history: [ "image:v1" ]
  4. An consumer of the storage API can now process the image name and
    still display image as REPOSITORY, like:
    Before:
    > podman images
    REPOSITORY      TAG      IMAGE ID       CREATED          SIZE
    image           v1       25b62d1b654a   13 seconds ago   2.07 kB
    <none>          <none>   b134eff7b955   17 seconds ago   2.07 kB
    
    After:
    > podman images
    REPOSITORY      TAG      IMAGE ID       CREATED          SIZE
    image           v1       25b62d1b654a   13 seconds ago   2.07 kB
    image           <none>   b134eff7b955   17 seconds ago   2.07 kB
    
  5. Since the NamesHistory is a slice we would be able to tell the
    end-user which names an image ID had before.

The change should be backwards compatible with previous versions of
containers/storage.

/cc @mtrmac

@saschagrunert
Copy link
Member Author

@rhatdan @vrothberg @nalind @mtrmac WDYT? It is mainly another approach to improve the information density about images to end users without breaking past versions.

@rhatdan
Copy link
Member

rhatdan commented Aug 28, 2019

I like it. What happens if I tag the image? If I can tag it, can I still push it? Can I remove it via tag name?
Why is this a list, how could I have multiple former image names?

Copy link
Member

@vrothberg vrothberg left a comment

Choose a reason for hiding this comment

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

Sounds like a nice thing to have 👍

images.go Outdated
@@ -492,6 +504,7 @@ func (r *imageStore) SetNames(id string, names []string) error {
}
for _, name := range names {
if otherImage, ok := r.byname[name]; ok {
r.addNameToHistory(otherImage, name)
Copy link
Member

Choose a reason for hiding this comment

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

I suggest calling addNameToHistory(...) from removeName(...). This way, we're sure to not miss an execution path.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I changed it and bound the new method to the *Image type, which should be more clear. 👍

@saschagrunert
Copy link
Member Author

What happens if I tag the image?

So from my local experiments, if I do:

  1. Create a base image without names:
> ./bin/podman build -f Dockerfile-empty -t img:v1
  1. Now the images.json contains the name correctly but no history:
[
  {
    "id": "a13e0f4e4ce6ce32820739c1b892a2c24f694dfc676216a9e5f19d0596da6968",
    "digest": "sha256:108f68845a70455485fb8677c3db83f596dc05ec257ddb4626254ccbd9a0c686",
    "names": [
      "localhost/img:v1"
    ],
    "layer": "5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
    ...
  }
]
  1. Tag multiple other names:
> ./bin/podman tag img:v1 img:v2 img:v3
  1. We still have it as names, because they are all distinct:
[
  {
    "id": "a13e0f4e4ce6ce32820739c1b892a2c24f694dfc676216a9e5f19d0596da6968",
    "digest": "sha256:108f68845a70455485fb8677c3db83f596dc05ec257ddb4626254ccbd9a0c686",
    "names": [
      "localhost/img:v1",
      "localhost/img:v2",
      "localhost/img:v3"
    ],
    "layer": "5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
    ...
  }
]
> ./podman images
REPOSITORY      TAG   IMAGE ID       CREATED              SIZE
localhost/img   v1    a13e0f4e4ce6   About a minute ago   2.07 kB
localhost/img   v2    a13e0f4e4ce6   About a minute ago   2.07 kB
localhost/img   v3    a13e0f4e4ce6   About a minute ago   2.07 kB
  1. Now we steal the img:v1 tag by creating a new empty image:
> ./bin/podman build -f Dockerfile-empty -t img:v1
[
  {
    "id": "a13e0f4e4ce6ce32820739c1b892a2c24f694dfc676216a9e5f19d0596da6968",
    "digest": "sha256:108f68845a70455485fb8677c3db83f596dc05ec257ddb4626254ccbd9a0c686",
    "names": [
      "localhost/img:v2",
      "localhost/img:v3"
    ],
    "names-history": [
      "localhost/img:v1"
    ],
    ...
  },
  {
    "id": "12d9c90a46f52a4d6abaf2605fd0bf9a89fe8763e3f13993fb894b739b6c1111",
    "digest": "sha256:df4879cb75d87edc08639e0c7252cb70945cd304f6eceba43a2349c93f7777c3",
    "names": [
      "localhost/img:v1"
    ],
    "layer": "5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
    ...
  }
]
  1. Take another tag from the first image:
> ./bin/podman tag img:v1 img:v2
[
  {
    "id": "a13e0f4e4ce6ce32820739c1b892a2c24f694dfc676216a9e5f19d0596da6968",
    "digest": "sha256:108f68845a70455485fb8677c3db83f596dc05ec257ddb4626254ccbd9a0c686",
    "names": [
      "localhost/img:v3"
    ],
    "names-history": [
      "localhost/img:v2",
      "localhost/img:v1"
    ],
    ...
  },
  {
    "id": "12d9c90a46f52a4d6abaf2605fd0bf9a89fe8763e3f13993fb894b739b6c1111",
    "digest": "sha256:df4879cb75d87edc08639e0c7252cb70945cd304f6eceba43a2349c93f7777c3",
    "names": [
      "localhost/img:v1",
      "localhost/img:v2"
    ],
    "layer": "5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
    ...
  }
]

If I can tag it, can I still push it? Can I remove it via tag name?

I think we could add dedicated command line options to buildah/podman to interact with old images, for example something like podman rmi --cleanup.

Why is this a list, how could I have multiple former image names?

See my example above, I also thought about adding something like a timestamp to indicate when it has been retagged, but this would clash with the current de-duplication approach.

@saschagrunert saschagrunert changed the title WIP/RFC: Add names-history support Add names-history support Aug 28, 2019
@rhatdan
Copy link
Member

rhatdan commented Aug 28, 2019

I think we need @nalind and @mtrmac opinion on this. Also give @mrunalp and @vbatts a heads up.

@mtrmac
Copy link
Collaborator

mtrmac commented Aug 28, 2019

Can we first explicitly clarify whether the goal of the PR is intended to mirror existing docker images, or whether it is a new thing being designed?

For the former, I suppose we would get more similar behavior by implementing RepoDigests the way Docker does (notably docker tag does not allow creating a “name:” entry locally I think.

For the latter, it just seems weird to me to see a $name entry in any form in the output after an explicit rm/untag of $name . (That’s an UI concern not directly impacting the design of the c/storage data, but still.)

@mtrmac
Copy link
Collaborator

mtrmac commented Aug 28, 2019

For the former, I suppose we would get more similar behavior by implementing RepoDigests the way Docker does (notably docker tag does not allow creating a “name:” entry locally I think.

… and we would still need to implement RepoDigests correctly even after this PR and using the history data in docker images output would mask some of the impacts of not recording RepoDigests.

@saschagrunert
Copy link
Member Author

Can we first explicitly clarify whether the goal of the PR is intended to mirror existing docker images, or whether it is a new thing being designed?

I think the major use case is: Displaying the image name to the user if an image has no Names any more due to re-tag, point 4

In CRI-O, we could add these values to the ListImages RPC, whereas in podman we could either add an additional colum to podman images or stripping of the tag and showing it as REPOSITORY value.

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 implementation LGTM.

I’m not quite convinced by the proposed UI, but that’s really not something to be solved here and now.

// NamesHistory is an optional set of Names the image had in the past. The
// contained names are free from any duplicates, whereas the newest entry
// is the first one.
NamesHistory []string `json:"names-history,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Currently (and especially with the changes what I expect to happen for RepoDigests), Names can contain both name:tag and name@digest values. So, NamesHistory would currently contain both as well. Is that OK?

(Ideally, I’d like be to insist on strongly typed values like reference.NamedTagged, encoding the semantics in the type system, but with Names []string that doesn’t seem reasonable right now.)

Copy link
Member Author

Choose a reason for hiding this comment

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

Currently (and especially with the changes what I expect to happen for RepoDigests), Names can contain both name:tag and name@digest values. So, NamesHistory would currently contain both as well. Is that OK?

I think so.

(Ideally, I’d like be to insist on strongly typed values like reference.NamedTagged, encoding the semantics in the type system, but with Names []string that doesn’t seem reasonable right now.)

Yeah, we could change both Names and NamesHistory to be forcibly valid references, but I wanted to add the change as non-breaking as possible.

Copy link
Collaborator

Choose a reason for hiding this comment

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

ACK.

@mtrmac
Copy link
Collaborator

mtrmac commented Aug 29, 2019

I also thought about adding something like a timestamp to indicate when it has been retagged, but this would clash with the current de-duplication approach.

Well, the deduplication is really not all that much code; if we need to make it a bit more complex to get the data structure we want, we should just do that.

On the one hand, if nothing needs a timestamp now, it seems rather pointless to add them now.

On the other hand, if we do end up adding timestamps in the future, it can’t be done within the current NamesHistory array and we would have to either break the ABI by changing the field, or maintain two independent fields with redundant values forever. So, getting this correct right now is worth a bit of thought before committing to the API.

(One possible way to defer that decision: make the field private or documented-to-be-private, and add a public getter method; then we can enrich the recorded data without breaking the API.)


Up to you, @nalind and others, I really understand neither the UI ambitions nor the API design of c/storage sufficiently.

@rhatdan
Copy link
Member

rhatdan commented Nov 5, 2019

@saschagrunert You still interested in this?

@saschagrunert
Copy link
Member Author

Yes, I would stick to the current implementation approach and rebased on top of the latest master branch.

@mtrmac Do you see any blocker for this getting merged (if the CI passes)?
@nalind May I ask you to please take a look at this one?

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.

Overall I’m not too enthusiastic about the importance of use case (and the partial overlap with missing RepoDigests functionality feels vaguely suspect), but I’m completely fine with this existing — up to @nalind.

I got another look at the implementation, though, and the code does not match the documentation AFAICS.

images.go Outdated
@@ -478,9 +484,14 @@ func (r *imageStore) SetMetadata(id, metadata string) error {
}

func (r *imageStore) removeName(image *Image, name string) {
image.addNameToHistory(name)
Copy link
Collaborator

Choose a reason for hiding this comment

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

removeName is, AFAICS, only called on a name conflict (e.g. if SetNames adds another name to an image, and that name has been used by a different image); it is not called on untag (SetNames removing an existing name).

That’s good enough as far as the original motivation goes:

I think the major use case is: Displaying the image name to the user if an image has no Names any more due to re-tag, point 4

but not consistent with how NamesHistory is documented:

NamesHistory is an optional set of Names the image had in the past.

Copy link
Member Author

Choose a reason for hiding this comment

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

Alright, I updated the documentation as suggested.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I was somewhat expecting an implementation change instead of documenting the behavior, but, sure, this is consistent. Up to @nalind.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I can also change the implementation to match the previous documentation, this was not clear for me in the first place.

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed the implementation and added tests on top.

This commit adds a new `NamesHistory` field to the `images.json`, which
is basically a deduped list of names the image had in the past. The
first entry of the list is the latest history entry.

The main use case for this feature is to tell the end-user which
names/tags an image had in the past if it does not contain any `names`
any more.

Detailed use case:
1. Pulling `image:v1` into the local registry: `names: [ "image:v1" ]`
2. Pushing a new image as `image:v1` into the remote registry
3. Pulling `image:v1` again into the local registry:
    - first image: `names: [ "image:v1" ]`
    - previous v1 image: `names: [], names-history: [ "image:v1" ]`
4. An consumer of the storage API can now process the image name and
   still display `image` as REPOSITORY, like:
   * Before:
   ```
   > podman images
   REPOSITORY      TAG      IMAGE ID       CREATED          SIZE
   image           v1       25b62d1b654a   13 seconds ago   2.07 kB
   <none>          <none>   b134eff7b955   17 seconds ago   2.07 kB
   ```
   * After:
   ```
   > podman images
   REPOSITORY      TAG      IMAGE ID       CREATED          SIZE
   image           v1       25b62d1b654a   13 seconds ago   2.07 kB
   image           <none>   b134eff7b955   17 seconds ago   2.07 kB
   ```
5. Since the `NamesHistory` is a slice we would be able to tell the
   end-user which names an image ID had before.

The change should be backwards compatible with previous versions of
containers/storage.

Signed-off-by: Sascha Grunert <[email protected]>
@rhatdan
Copy link
Member

rhatdan commented Nov 22, 2019

LGTM

@rhatdan rhatdan merged commit 8aac4ac into containers:master Nov 22, 2019
@saschagrunert saschagrunert deleted the history-names branch November 22, 2019 21:24
@saschagrunert
Copy link
Member Author

Hey, may I ask regarding a new release that I can bring this feature into CRI-O and libpod?

@vrothberg
Copy link
Member

The last release was just 7 days ago but I think Dan wants throw out some of the recent changes soon. @rhatdan, is anything blocking us from cutting another release?

@rhatdan
Copy link
Member

rhatdan commented Nov 26, 2019

I will cut a release.

@rhatdan
Copy link
Member

rhatdan commented Nov 26, 2019

#468

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.

4 participants