-
Notifications
You must be signed in to change notification settings - Fork 3k
Refactor image tree for API usage #5093
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| package image | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "strings" | ||
|
|
||
| "github.com/docker/go-units" | ||
| "github.com/pkg/errors" | ||
| ) | ||
|
|
||
| const ( | ||
| middleItem = "├── " | ||
| continueItem = "│ " | ||
| lastItem = "└── " | ||
| ) | ||
|
|
||
| type tree struct { | ||
| img *Image | ||
| imageInfo *InfoImage | ||
| layerInfo map[string]*LayerInfo | ||
| sb *strings.Builder | ||
| } | ||
|
|
||
| // GenerateTree creates an image tree string representation for displaying it | ||
| // to the user. | ||
| func (i *Image) GenerateTree(whatRequires bool) (string, error) { | ||
| // Fetch map of image-layers, which is used for printing output. | ||
| layerInfo, err := GetLayersMapWithImageInfo(i.imageruntime) | ||
| if err != nil { | ||
| return "", errors.Wrapf(err, "error while retrieving layers of image %q", i.InputName) | ||
| } | ||
|
|
||
| // Create an imageInfo and fill the image and layer info | ||
| imageInfo := &InfoImage{ | ||
| ID: i.ID(), | ||
| Tags: i.Names(), | ||
| } | ||
|
|
||
| if err := BuildImageHierarchyMap(imageInfo, layerInfo, i.TopLayer()); err != nil { | ||
| return "", err | ||
| } | ||
| sb := &strings.Builder{} | ||
| tree := &tree{i, imageInfo, layerInfo, sb} | ||
| if err := tree.print(whatRequires); err != nil { | ||
| return "", err | ||
| } | ||
| return tree.string(), nil | ||
| } | ||
|
|
||
| func (t *tree) string() string { | ||
| return t.sb.String() | ||
| } | ||
|
|
||
| func (t *tree) print(whatRequires bool) error { | ||
| size, err := t.img.Size(context.Background()) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| fmt.Fprintf(t.sb, "Image ID: %s\n", t.imageInfo.ID[:12]) | ||
| fmt.Fprintf(t.sb, "Tags: %s\n", t.imageInfo.Tags) | ||
| fmt.Fprintf(t.sb, "Size: %v\n", units.HumanSizeWithPrecision(float64(*size), 4)) | ||
| if t.img.TopLayer() != "" { | ||
| fmt.Fprintf(t.sb, "Image Layers\n") | ||
| } else { | ||
| fmt.Fprintf(t.sb, "No Image Layers\n") | ||
| } | ||
|
|
||
| if !whatRequires { | ||
| // fill imageInfo with layers associated with image. | ||
| // the layers will be filled such that | ||
| // (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End) | ||
| // Build output from imageInfo into buffer | ||
| t.printImageHierarchy(t.imageInfo) | ||
| } else { | ||
| // fill imageInfo with layers associated with image. | ||
| // the layers will be filled such that | ||
| // (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End) | ||
| // (Forks)... intermediate Child Layer(s) -> Child Top Layer(End) | ||
| return t.printImageChildren(t.layerInfo, t.img.TopLayer(), "", true) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // Stores all children layers which are created using given Image. | ||
| // Layers are stored as follows | ||
| // (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End) | ||
| // (Forks)... intermediate Child Layer(s) -> Child Top Layer(End) | ||
| func (t *tree) printImageChildren(layerMap map[string]*LayerInfo, layerID string, prefix string, last bool) error { | ||
| if layerID == "" { | ||
| return nil | ||
| } | ||
| ll, ok := layerMap[layerID] | ||
| if !ok { | ||
| return fmt.Errorf("lookup error: layerid %s, not found", layerID) | ||
| } | ||
| fmt.Fprint(t.sb, prefix) | ||
|
|
||
| //initialize intend with middleItem to reduce middleItem checks. | ||
| intend := middleItem | ||
| if !last { | ||
| // add continueItem i.e. '|' for next iteration prefix | ||
| prefix += continueItem | ||
| } else if len(ll.ChildID) > 1 || len(ll.ChildID) == 0 { | ||
| // The above condition ensure, alignment happens for node, which has more then 1 children. | ||
| // If node is last in printing hierarchy, it should not be printed as middleItem i.e. ├── | ||
| intend = lastItem | ||
| prefix += " " | ||
| } | ||
|
|
||
| var tags string | ||
| if len(ll.RepoTags) > 0 { | ||
| tags = fmt.Sprintf(" Top Layer of: %s", ll.RepoTags) | ||
| } | ||
| fmt.Fprintf(t.sb, "%sID: %s Size: %7v%s\n", intend, ll.ID[:12], units.HumanSizeWithPrecision(float64(ll.Size), 4), tags) | ||
| for count, childID := range ll.ChildID { | ||
| if err := t.printImageChildren(layerMap, childID, prefix, count == len(ll.ChildID)-1); err != nil { | ||
| return err | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // prints the layers info of image | ||
| func (t *tree) printImageHierarchy(imageInfo *InfoImage) { | ||
| for count, l := range imageInfo.Layers { | ||
| var tags string | ||
| intend := middleItem | ||
| if len(l.RepoTags) > 0 { | ||
| tags = fmt.Sprintf(" Top Layer of: %s", l.RepoTags) | ||
| } | ||
| if count == len(imageInfo.Layers)-1 { | ||
| intend = lastItem | ||
| } | ||
| fmt.Fprintf(t.sb, "%s ID: %s Size: %7v%s\n", intend, l.ID[:12], units.HumanSizeWithPrecision(float64(l.Size), 4), tags) | ||
| } | ||
| } | ||
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Can we move this to somewhere in pkg/? It doesn't seem much like an API call, given it only returns a string.
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 suggest to wait for @mtrmac's new package. We may be able to integrate it there.
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.
By "wait" I mean we could merge it as is now and let Miloslav pick it up in his new package.
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.
Hm. It's just libpod/image, not libpod itself, so sure
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.
Definitely don’t wait for me, I’m still mostly in learning phase, and I can adjust for whatever happens here.
(Jumping into what I’m sure was a well-considered decision, I’d instinctively prefer to primarily have a well-typed API that returns the structure, and keep the text formatter in the CLI (allowing, at least in principle, a nice GUI to be built on top, for example). OTOH, I guess that exposing the complete tree structure over Varlink/whatever would be a lot of work and pain — and for no benefit at least now that the only consumer is the CLI.)
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.
That's my big hold-up as well - I'd love to return a data structure that represents the tree, with a
.String()that returns the output. I'm willing to defer that to followup work, though.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.
(Honestly, though, I'd be fine keeping the tree local-only, and exposing only a literal output string over the API. There's precedence for this with things like
podman top)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.
(Or was it
podman stats... I forget which)