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

Tree implementation for podman images #1642

Merged
merged 1 commit into from
Mar 14, 2019

Conversation

kunalkushwaha
Copy link
Collaborator

Signed-off-by: Kunal Kushwaha [email protected]

@openshift-ci-robot openshift-ci-robot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Oct 15, 2018
@openshift-ci-robot openshift-ci-robot added size/L needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Oct 15, 2018
@kunalkushwaha
Copy link
Collaborator Author

kunalkushwaha commented Oct 15, 2018

This is much shorter version of initial implementation I discussed in #415 (comment).

After revisiting discussion points from @mtrmac, Instead of parsing information from Image history, just using image.GetParent() & image.GetChildren() the dependencies can be found.

I guess this PR is not yet complete in the sense Printing of tree should be done in cmd/tree.go instead of libpod/image/image.go, but function wise it works.

sample output

$ sudo podman tree localhost/kk/test2
[sudo] password for kunal:        
└─  ID: 5182e96772bf  Tags: [docker.io/library/centos:latest] VirtualSize: 208.3 MB
  └─  ID: 89138a91447e  Tags: [] VirtualSize: 50.0 B
  └─  ID: 45a35dc0a575  Tags: [] VirtualSize: 52.0 B
  └─  ID: 6eaf2f64d2f2  Tags: [] VirtualSize: 52.0 B
  └─  ID: 77dbce778311  Tags: [] VirtualSize: 52.0 B
  └─  ID: 73bb144ce61d  Tags: [] VirtualSize: 52.0 B
  └─  ID: 4c8dcacdbfb3  Tags: [] VirtualSize: 52.0 B
  └─  ID: 3e99b149c20f  Tags: [] VirtualSize: 1.1 KB
  └─  ID: 268e94b3876a  Tags: [localhost/kk/test1:latest] VirtualSize: 2.6 KB
    └─  ID: 243d869d10ea  Tags: [localhost/kk/test2:latest] VirtualSize: 2.8 KB

I would like to have a review and discussion about if we agree with

  • root, peer etc in tree.
  • Information to be shown in tree.
  • any other points regarding this PR.

//cc @vrothberg @mtrmac

@rhatdan
Copy link
Member

rhatdan commented Oct 15, 2018

@fatherlinux FYI
/approve

@openshift-ci-robot openshift-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Oct 15, 2018
}
}

func humanSize(raw uint64) string {
Copy link
Member

Choose a reason for hiding this comment

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

We use
"github.com/docker/go-units"

And

size = units.HumanSizeWithPrecision(0, 0)

@rhatdan
Copy link
Member

rhatdan commented Oct 15, 2018

Will need man pages and command completion.

Should this be a primary command or a subcommand?

podman tree
vs
podman image tree

cmd/podman/tree.go Outdated Show resolved Hide resolved
@giuseppe
Copy link
Member

very nice, as @rhatdan said, I think this should be a subcommand of "image"

return nil, err
}

func printSubTree(root *Image, rootSize uint64, prefix string, treeDepth int) {
Copy link
Member

Choose a reason for hiding this comment

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

This function should have an error return value, so that I can be properly propagated.

@@ -654,6 +654,7 @@ type History struct {
CreatedBy string `json:"createdBy"`
Size int64 `json:"size"`
Comment string `json:"comment"`
Tags []string `json:"tags"`
Copy link
Member

Choose a reason for hiding this comment

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

Is this new field needed or an artifact from an earlier version?

@vrothberg
Copy link
Member

I guess this PR is not yet complete in the sense Printing of tree should be done in cmd/tree.go instead of libpod/image/image.go, but function wise it works.

I agree, this should go into cmd/podman/tree.go but it's working well, nice job!

}

var (
treeDescription = "Displays the dependent layers of an image. The information is printed as tree format"
Copy link
Member

Choose a reason for hiding this comment

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

suggest replacing "as tree format" with "in tree format"

}

//Print all subtree(peers) to the depth of input image
printSubTree(rootImage, 0, "└─ ", treeDepth)
Copy link
Member

Choose a reason for hiding this comment

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

Just to double check, are these standard ascii characters in the 3rd argument. Will there be any trouble displaying this on a Mac or other non-linux device?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I verified this ASCII character on both Mac & Windows, it works fine.

Copy link
Collaborator

Choose a reason for hiding this comment

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

ASCII is 0–127, this is not a part of it. But Golang source code is specified to be UTF-8, so it is unambiguous, and Unicode support has been good enough for about a decade.

@TomSweeneyRedHat
Copy link
Member

Looks good overall to me, thanks for working on this @kunalkushwaha !

@kunalkushwaha kunalkushwaha changed the title [WIP] Tree implementation for podman images Tree implementation for podman images Oct 17, 2018
@openshift-ci-robot openshift-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Oct 17, 2018
@kunalkushwaha
Copy link
Collaborator Author

@rhatdan need approval

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.

Most importantly, please see the big comment below; unless I’m missing something this does not quite do what we want, considering how widespread unsquashed images are.

I guess this PR is not yet complete in the sense Printing of tree should be done in cmd/tree.go instead of libpod/image/image.go.

Yes; please separate the computation of the tree from its presentation. Some users might well want to consume the same data in an easy-to-parse JSON, for example.

libpod/image/image.go Outdated Show resolved Hide resolved
libpod/image/image.go Outdated Show resolved Hide resolved
libpod/image/image.go Outdated Show resolved Hide resolved
libpod/image/image.go Outdated Show resolved Hide resolved
libpod/image/image.go Outdated Show resolved Hide resolved

for parent != nil {
rootImage = parent
parent, err = parent.GetParent()
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems problematic; the more complex code, or maybe (more likely?) an entirely different data source might still be needed.


Most importantly, AFAICS there may not be image objects available at all for the shared parents: consider a layer tree like this:

  • fedora
    • unnamed layer
      • unnamed layer
      • base-environment
        • app-0
        • unnamed layer
          • unnamed layer [A]
            • app-1
        • unnamed layer
          • unnamed layer
            • app-2

and a local environment which has done for x in 0 1 2; do podman pull app-$x; done, with no other images pulled or known, so that the fedora and base-environment images have never been seen locally, nor have any possible in-between images created by buildah.

AFAICS (but I have not tested this, to be fair), in such case the tree of layers in the containers/storage.LayerStore will mirror the structure above exactly, but only the app-$x images will be known to containers/storage.ImageStore.

So, app-1.GetParent() will return nil because the layer [A] has no corresponding image; similarly app-0.GetParent() will return nil because the base-environment image is not available locally.

In both cases, the tree will only show a single element app-$0. It seems to me (but, strictly speaking, I don’t know, because the specification is not yet precise enough) that the desired input is the full layer tree exactly as structured, with fedora and base-environment reported as “unnamed layer” [but after podman pull base-environment, it could report the right image name].


Second, as discussed in
#415 (comment) , the incomplete data may be ambiguous: there might be several images with the same top layer but different configs, and GetParent chooses one in random order (AFAICS the first one pulled/created in the local storage, but that’s not an API promise).

In this case, consider an image base-modified, derived from base-environment with an ENV a=a. Depending on the implementation, this might cause an empty extra layer to be created, but there’s no real need for it: it’s just a different image with a different config but exactly the same layers. So, the base-environment layer actually can have both base-environment and base-modified images as possible parents, and without knowing the true parent links somehow (AFAICS buildah/podman does not record that at all right now), there’s no way to tell.

If both are available locally, we can’t tell which one to output; if only the wrong one is available locally, we will probably report that incorrect base. (And, as discussed above, neither may be available locally, and we can’t tell between an unavailable base image and a non-image intermediate layer.)

AFAICS this is fundamentally unsolvable in general, but it is a design concern — do we want to report a single, possibly incorrect, image, or all of the candidates, of which all but one are certainly incorrect, and the last one may or may not be correct?)

Eventually, we have been talking about adding true/reliable “parent image” information, which would make this unambiguous — only if the image is new enough to have been built with such hypothetical parent information; we would still have to guess for any image that exists 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.

So, app-1.GetParent() will return nil because the layer [A] has no corresponding image; similarly app-0.GetParent() will return nil because the base-environment image is not available locally.

No, In this case a layerID (maybe without RepoTags) will be shown if image is not flatten/squashed by any tool.

  • If image is flatten/squashed, it has become independent, so no there will be no parent layer.
    While pulling children layer, always dependent parent layers are downloaded and that’s how the image rootfs should be build.
    For build information podman history should be used

with fedora and base-environment reported as “unnamed layer” [but after podman pull base-environment, it could report the right image name].

AFAIU tree or any other script that finds dependencies locally, resolves to local registry. i.e. information available locally instead of fetching to remote registry/store.

If it involves fetching information from remote registry, than we need to decide which all remote registries need to be looked at also, as podman supports multiple registries.

Copy link
Collaborator

@mtrmac mtrmac Oct 17, 2018

Choose a reason for hiding this comment

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

No, In this case a layerID (maybe without RepoTags) will be shown if image is not flatten/squashed by any tool.

How does that work? https://github.com/containers/libpod/blob/604728d65dd0110b28c79acafc5b214886b3b484/libpod/image/image.go#L984-L999 clearly can return nil if there is no image corresponding to a layer, and reading c/storage/layerStore.Put, creating a layer does not require a corresponding image to exist.

If it involves fetching information from remote registry, than we need to decide which all remote registries need to be looked at also, as podman supports multiple registries.

That’s the least of our problems; there is no way to look up a remote image config by the layer set (and again, this can be return many different results with no way to decide), and no way to lookup a remote manifest by the config.

@mheon
Copy link
Member

mheon commented Oct 17, 2018

/ok-to-test

@openshift-ci-robot openshift-ci-robot removed the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Oct 17, 2018
@rhatdan
Copy link
Member

rhatdan commented Oct 25, 2018

@kunalkushwaha Are you still working on this?

@kunalkushwaha
Copy link
Collaborator Author

@rhatdan This PR is stuck for what information is expected in this tree command.

If the information which all previous layers of given image is fine, I guess this PR is output wise working. Though the performance improvement is required as suggested by @mtrmac.

Also, I am not clear on the comments, the parent layer can be retured as nil, if there is no coresponding image present. I think to prepare a rootfs from Image, we need all layers to be present in local registry and if layers are present in local registry, Parent layer should be reachable.

@rhatdan
Copy link
Member

rhatdan commented Oct 26, 2018

@mheon @mtrmac I would like to move forward on this PR, what do you guys think?

@mtrmac
Copy link
Collaborator

mtrmac commented Oct 27, 2018

If the information which all previous layers of given image is fine, I guess this PR is output wise working.

Are you saying that my analysis in
#1642 (comment) is incorrect and that this works in some other way? (To be clear, it’s perfectly fine to say it is incorrect: I didn’t try this in practice at all, and I know I make mistakes. Proving me wrong is good :) )

Though the performance improvement is required as suggested by @mtrmac.

(I don’t think this should block on performance; it’s notable but it’s clear that that can be fixed later if it turns out to be necessary.)

Also, I am not clear on the comments, the parent layer can be retured as nil, if there is no coresponding image present. I think to prepare a rootfs from Image, we need all layers to be present in local registry and if layers are present in local registry, Parent layer should be reachable.

You’re right that a parent layer always exists locally (for derived images); but not all layers correspond to an image (= layer sequence + config). See the comment linked above.

@kunalkushwaha
Copy link
Collaborator Author

Updated this PR with following.

  1. The tree output have better output structure as discussed in Tree implementation for podman images #1642 (comment) & Tree implementation for podman images #1642 (comment)
  2. The implementation do not use any third party library though
  3. man pages updated with new output structure.

sample output

$ sudo podman image tree ae96a4ad4f3f --whatrequires                                                                                                                                           
Image ID: ae96a4ad4f3f                                                                                                                                                                         
Tags:    [docker.io/library/ruby:latest]                                                                                                                                                       
Size:    894.2MB                                                                                                                                                                               
Image Layers
└──  ID: 9c92106221c7 Size:  2.56kB Top Layer of: [docker.io/library/ruby:latest]
 ├──  ID: 1b90f2b80ba0 Size: 3.584kB
 │   ├──  ID: 42b7d43ae61c Size: 169.5MB
 │   ├──  ID: 26dc8ba99ec3 Size: 2.048kB
 │   ├──  ID: b4f822db8d95 Size: 3.957MB
 │   ├──  ID: 044e9616ef8a Size: 164.7MB
 │   ├──  ID: bf94b940200d Size: 11.75MB
 │   ├──  ID: 4938e71bfb3b Size: 8.532MB
 │   └──  ID: f513034bf553 Size: 1.141MB
 ├──  ID: 1e55901c3ea9 Size: 3.584kB
 ├──  ID: b62835a63f51 Size: 169.5MB
 ├──  ID: 9f4e8857f3fd Size: 2.048kB
 ├──  ID: c3b392020e8f Size: 3.957MB
 ├──  ID: 880163026a0a Size: 164.8MB
 ├──  ID: 8c78b2b14643 Size: 11.75MB
 ├──  ID: 830370cfa182 Size: 8.532MB
 └──  ID: 567fd7b7bd38 Size: 1.141MB Top Layer of: [docker.io/circleci/ruby:latest]

$ sudo podman image tree docker.io/library/ruby:latest               
Image ID: ae96a4ad4f3f
Tags:    [docker.io/library/ruby:latest]
Size:    894.2MB
Image Layers 
├──  ID: 13d5529fd232 Size: 105.6MB 
├──  ID: 2841480d8e9e Size: 24.07MB 
├──  ID: 966a53f93769 Size: 8.005MB 
├──  ID: c24db6b14372 Size: 146.4MB 
├──  ID: 66b79beaeea1 Size: 574.4MB 
├──  ID: 865a53f9d3ba Size: 3.584kB 
├──  ID: 2f80650c6669 Size: 35.73MB 
└──  ID: 9c92106221c7 Size:  2.56kB Top Layer of: [docker.io/library/ruby:latest]

@mtrmac @vrothberg PTAL

@vrothberg
Copy link
Member

vrothberg commented Mar 7, 2019

This looks really good! Thanks a lot, @kunalkushwaha.

I spotted just minor formatting issues that could addressed with the following code change (the comment must be updated as the intend is always set to middleItem):

diff --git a/cmd/podman/tree.go b/cmd/podman/tree.go
index ca23d8756dda..d4e9e85b9197 100644
--- a/cmd/podman/tree.go
+++ b/cmd/podman/tree.go
@@ -155,7 +155,7 @@ func printImageChildren(layerMap map[string]*image.LayerInfo, layerID string, pr
 		// If its not last node, it has to be treated as middleItem i.e. ├──
 		// add continueItem i.e. '|' for next itteration prefix
 		prefix = prefix + continueItem
-	} else if len(ll.ChildID) > 1 || len(ll.ChildID) == 0 {
+	} else if len(ll.ChildID) == 0 {
 		// The above condition ensure, alignment happens for node, which has more then 1 childern.
 		// If node is last in printing hierarchy, it should not be printed as middleItem i.e. ├──
 		intend = lastItem
@@ -164,9 +164,9 @@ func printImageChildren(layerMap map[string]*image.LayerInfo, layerID string, pr
 
 	var tags string
 	if len(ll.RepoTags) > 0 {
-		tags = fmt.Sprintf("Top Layer of: %s", ll.RepoTags)
+		tags = fmt.Sprintf(" Top Layer of: %s", ll.RepoTags)
 	}
-	fmt.Printf("%s ID: %s Size: %7v %s\n", intend, ll.ID[:12], units.HumanSizeWithPrecision(float64(ll.Size), 4), tags)
+	fmt.Printf("%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 := printImageChildren(layerMap, childID, prefix, (count == len(ll.ChildID)-1)); err != nil {
 			return err

The diff changes how a "fork/branch" looks like by left alinging the first child:

Image Layers
└──  ID: 9c92106221c7 Size:  2.56kB Top Layer of: [docker.io/library/ruby:latest]
 ├──  ID: 1b90f2b80ba0 Size: 3.584kB

... and it removes some leading and trailing whitespace. Maybe I am overseeing a side-effect of the upper diff but I figured it's easier to illustrate what to change formatting wise.

Other than that: LGTM

@kunalkushwaha
Copy link
Collaborator Author

diff --git a/cmd/podman/tree.go b/cmd/podman/tree.go
index ca23d8756dda..d4e9e85b9197 100644
--- a/cmd/podman/tree.go
+++ b/cmd/podman/tree.go
@@ -155,7 +155,7 @@ func printImageChildren(layerMap map[string]*image.LayerInfo, layerID string, pr
 		// If its not last node, it has to be treated as middleItem i.e. ├──
 		// add continueItem i.e. '|' for next itteration prefix
 		prefix = prefix + continueItem
-	} else if len(ll.ChildID) > 1 || len(ll.ChildID) == 0 {
+	} else if len(ll.ChildID) == 0 {

@vrothberg Are you really sure of this change? This will loose child parent relationship structure.
I feel only if only one child is there, then only child should be treated as peer to show its part of same image. In case of child a tree structure help to visualize better dependency.
e.g

There are three images as follows

  • foobar1 & foobar2 are build from docker.io/library/alpine:latest.
  • foobar1-2 is build from foobar1
    Now from current logic, the output is
$ sudo -E podman image tree docker.io/library/alpine --whatrequires
Image ID: 196d12cf6ab1
Tags:    [docker.io/library/alpine:latest]
Size:    4.674MB
Image Layers
└── ID: df64d3292fd6 Size: 4.672MB Top Layer of: [docker.io/library/alpine:latest]
 ├── ID: 9cf32d0a9daf Size: 3.584kB Top Layer of: [localhost/foobar1:latest]
 │   └── ID: 07b528118820 Size:  2.56kB Top Layer of: [localhost/foobar1-2:latest]
 └── ID: 49ee5ef86103 Size: 3.584kB Top Layer of: [localhost/foobar2:latest]

now if foobar1 is removed still output would be

$ sudo -E podman image tree docker.io/library/alpine --whatrequires
Image ID: 196d12cf6ab1
Tags:    [docker.io/library/alpine:latest]
Size:    4.674MB
Image Layers 
└── ID: df64d3292fd6 Size: 4.672MB Top Layer of: [docker.io/library/alpine:latest]
 ├── ID: 9cf32d0a9daf Size: 3.584kB
 │   └── ID: 07b528118820 Size:  2.56kB Top Layer of: [localhost/foobar1-2:latest]
 └── ID: 49ee5ef86103 Size: 3.584kB Top Layer of: [localhost/foobar2:latest]

With suggested change initial output would be as follows

$ sudo -E podman image tree docker.io/library/alpine --whatrequires                                                                                                                            Image ID: 196d12cf6ab1                                                                                                                        
Tags:    [docker.io/library/alpine:latest]                                                                                                                                                
Size:    4.674MB                                                                                                                                                                               
Image Layers                                                     
├── ID: df64d3292fd6 Size: 4.672MB Top Layer of: [docker.io/library/alpine:latest]
├── ID: 9cf32d0a9daf Size: 3.584kB Top Layer of: [localhost/foobar1:latest]
│   └── ID: 07b528118820 Size:  2.56kB Top Layer of: [localhost/foobar1-2:latest]                                                                                                              
└── ID: 49ee5ef86103 Size: 3.584kB Top Layer of: [localhost/foobar2:latest]

after removing the foobar1

$ sudo -E podman image tree docker.io/library/alpine --whatrequires
Image ID: 196d12cf6ab1
Tags:    [docker.io/library/alpine:latest]
Size:    4.674MB
Image Layers
├── ID: df64d3292fd6 Size: 4.672MB Top Layer of: [docker.io/library/alpine:latest]
├── ID: 9cf32d0a9daf Size: 3.584kB
│   └── ID: 07b528118820 Size:  2.56kB Top Layer of: [localhost/foobar1-2:latest]
└── ID: 49ee5ef86103 Size: 3.584kB Top Layer of: [localhost/foobar2:latest]

@mtrmac Please you also check this.
I don't mind making said changes, if that ensure no more changes further.

@vrothberg
Copy link
Member

/retest
bot, retest this please

@kunalkushwaha
Copy link
Collaborator Author

The CI is failing due to podman inspect testcase failing error

Test: podman inspect with GO format completed in 2.334304 seconds
• Failure [2.334 seconds]
Podman inspect
/go/src/github.com/containers/libpod/test/e2e/inspect_test.go:13
  podman inspect with GO format [It]
  /go/src/github.com/containers/libpod/test/e2e/inspect_test.go:52

  Expected
      <string>: "cb3aa0..."
  to equal       |
      <string>: "5cb3aa..."

  /go/src/github.com/containers/libpod/test/e2e/inspect_test.go:60

@vrothberg
Copy link
Member

/retest

@vrothberg
Copy link
Member

bot, retest this please

@vrothberg
Copy link
Member

@kunalkushwaha, it looks like a flake in the CI. If it doesn't get green, you may need to repush the change to kick off a fresh CI run.

@kunalkushwaha
Copy link
Collaborator Author

kunalkushwaha commented Mar 11, 2019

seems there is issue with CI, failing randomly on tests.
/retest

@rh-atomic-bot
Copy link
Collaborator

☔ The latest upstream changes (presumably #2527) made this pull request unmergeable. Please resolve the merge conflicts.

@kunalkushwaha kunalkushwaha force-pushed the image-tree branch 2 times, most recently from 4f28ac2 to c290648 Compare March 13, 2019 02:29
@kunalkushwaha
Copy link
Collaborator Author

@vrothberg Please help for this CI error.

2019/03/13 02:32:21 	Clone https://github.com/QiWang19/buildah to github.com/containers/buildah, revision 345ffc2b29b4255a83cfa763db88799d8ec9c569
2019/03/13 02:32:23 	Finished clone github.com/containers/buildah
2019/03/13 02:32:24 Errors on clone:
github.com/containers/buildah: Err: exit status 128, out: fatal: reference is not a tree: 345ffc2b29b4255a83cfa763db88799d8ec9c569
make: *** [Makefile:343: vendor] Error 1

also @mtrmac PTAL at output as described in #1642 (comment) and suggestion by @vrothberg in #1642 (comment). If any changes are required, I can do it in parallel.

@vrothberg
Copy link
Member

@kunalkushwaha can you do a git commit --amend and repush? The CI had some hiccups recently.

@vrothberg
Copy link
Member

@mtrmac @rhatdan PTAL. Let's get this PR merged. @kunalkushwaha, sorry for the flakes. We're also suffering from the CI atm.

@rhatdan
Copy link
Member

rhatdan commented Mar 14, 2019

/lgtm
@kunalkushwaha Thanks for persevering, If people have more problems with the output we can fix in future PRs.

@openshift-ci-robot openshift-ci-robot added the lgtm Indicates that a PR is ready to be merged. label Mar 14, 2019
@openshift-merge-robot openshift-merge-robot merged commit fc5951a into containers:master Mar 14, 2019
@TomSweeneyRedHat
Copy link
Member

@kunalkushwaha Thank you very much for you hard work on this PR over the past several months. I think you've earned Podman's first Triple Gold Star for the effort. It will be a nice addition to the tool.

@vrothberg
Copy link
Member

Absolutely. Thanks a lot, @kunalkushwaha!

@kunalkushwaha
Copy link
Collaborator Author

Thank you @TomSweeneyRedHat :).
Credit goes to @vrothberg @mtrmac for their patience for several rounds of reviews and input.

@github-actions github-actions bot added the locked - please file new issue/PR Assist humans wanting to comment on an old issue or PR with locked comments. label Sep 27, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 27, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. locked - please file new issue/PR Assist humans wanting to comment on an old issue or PR with locked comments. ok-to-test
Projects
None yet
Development

Successfully merging this pull request may close these issues.