From 843208fa9c03496af9fd76aeac0d8ff7feb970ed Mon Sep 17 00:00:00 2001 From: Dalibor Kricka Date: Mon, 16 Mar 2026 14:18:28 +0100 Subject: [PATCH 1/6] cmd/list, pkg/podman: Make podman.GetImages() flatten, not getImages() This is a step towards eliminating or reducing the code in getImages() by doing everything or more in podman.GetImages(). If nothing else, this removes the need to export Image.FlattenNames() from the podman package. The getImages() function originated as listContainers(), when it invoked 'podman images --filter label=...' through podman.GetImages() separately for each label used to identify Toolbx images, and then joined and sorted the results. This changed in commit 2369da5d31830e5c when getImages() started invoking 'podman images' only once through podman.GetImages() to get all available images, and then filtered them itself based on the labels. Before this change in commit 2369da5d31830e5c, it probably made some sense to keep podman.GetImages() only as a thin wrapper to invoke 'podman images' with different options, and to do everything else in getImages(). However, since then more is being done inside the podman package (eg., unmarshalling the 'podman images' JSON in commit 5f324d537ed875ca and flattening the images in commit 6aab0a61752dc9a3), and getImages() is the only caller of podman.GetImages(). Therefore, it looks awkward to have the code to get all Toolbx images split across two functions in different packages. https://github.com/containers/toolbox/pull/1724 https://github.com/containers/toolbox/pull/1772 --- src/cmd/list.go | 11 ++--------- src/pkg/podman/podman.go | 21 ++++++++++++++++++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/cmd/list.go b/src/cmd/list.go index ae2bf3987..a88c83103 100644 --- a/src/cmd/list.go +++ b/src/cmd/list.go @@ -154,21 +154,15 @@ func listHelp(cmd *cobra.Command, args []string) { func getImages(fillNameWithID bool) ([]podman.Image, error) { logrus.Debug("Fetching all images") var args []string - images, err := podman.GetImages(args...) + images, err := podman.GetImages(fillNameWithID, args...) if err != nil { logrus.Debugf("Fetching all images failed: %s", err) return nil, errors.New("failed to get images") } - processed := make(map[string]struct{}) var toolboxImages []podman.Image for _, image := range images { - if _, ok := processed[image.ID]; ok { - continue - } - - processed[image.ID] = struct{}{} var isToolboxImage bool for label := range toolboxLabels { @@ -179,8 +173,7 @@ func getImages(fillNameWithID bool) ([]podman.Image, error) { } if isToolboxImage { - flattenedImages := image.FlattenNames(fillNameWithID) - toolboxImages = append(toolboxImages, flattenedImages...) + toolboxImages = append(toolboxImages, image) } } diff --git a/src/pkg/podman/podman.go b/src/pkg/podman/podman.go index 08828c1df..e76667c18 100644 --- a/src/pkg/podman/podman.go +++ b/src/pkg/podman/podman.go @@ -53,7 +53,7 @@ var ( LogLevel = logrus.ErrorLevel ) -func (image *Image) FlattenNames(fillNameWithID bool) []Image { +func (image *Image) flattenNames(fillNameWithID bool) []Image { var ret []Image if len(image.Names) == 0 { @@ -190,12 +190,14 @@ func GetContainers(args ...string) (*Containers, error) { // GetImages is a wrapper function around `podman images --format json` command. // +// Parameter fillNameWithID is a boolean that indicates if the image names should be filled with the ID, when there +// are no names. // Parameter args accepts an array of strings to be passed to the wrapped command (eg. ["-a", "--filter", "123"]). // // Returned value is a slice of Images. // // If a problem happens during execution, first argument is nil and second argument holds the error message. -func GetImages(args ...string) ([]Image, error) { +func GetImages(fillNameWithID bool, args ...string) ([]Image, error) { var stdout bytes.Buffer logLevelString := LogLevel.String() @@ -210,7 +212,20 @@ func GetImages(args ...string) ([]Image, error) { return nil, err } - return images, nil + processedIDs := make(map[string]struct{}) + var processedImages []Image + + for _, image := range images { + if _, ok := processedIDs[image.ID]; ok { + continue + } + + processedIDs[image.ID] = struct{}{} + flattenedImages := image.flattenNames(fillNameWithID) + processedImages = append(processedImages, flattenedImages...) + } + + return processedImages, nil } // GetVersion returns version of Podman in a string From 163d86351e4c7be0a3de4bf498729cbed07b98ab Mon Sep 17 00:00:00 2001 From: Dalibor Kricka Date: Tue, 17 Mar 2026 22:27:59 +0100 Subject: [PATCH 2/6] pkg/podman: Shuffle some code around Subsequent commits will use isToolbx() for image labels. So, it can't be in a file that's specific to containers. https://github.com/containers/toolbox/pull/1724 https://github.com/containers/toolbox/pull/1772 --- src/pkg/podman/container.go | 8 -------- src/pkg/podman/podman.go | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pkg/podman/container.go b/src/pkg/podman/container.go index 4e7182763..bbc41a4e4 100644 --- a/src/pkg/podman/container.go +++ b/src/pkg/podman/container.go @@ -297,11 +297,3 @@ func (containers *Containers) Next() bool { func (containers *Containers) Reset() { containers.i = 0 } - -func isToolbx(labels map[string]string) bool { - if labels["com.github.containers.toolbox"] == "true" || labels["com.github.debarshiray.toolbox"] == "true" { - return true - } - - return false -} diff --git a/src/pkg/podman/podman.go b/src/pkg/podman/podman.go index e76667c18..be88211b7 100644 --- a/src/pkg/podman/podman.go +++ b/src/pkg/podman/podman.go @@ -378,6 +378,14 @@ func IsToolboxImage(image string) (bool, error) { return true, nil } +func isToolbx(labels map[string]string) bool { + if labels["com.github.containers.toolbox"] == "true" || labels["com.github.debarshiray.toolbox"] == "true" { + return true + } + + return false +} + func Logs(container string, since time.Time, stderr io.Writer) error { ctx := context.Background() err := LogsContext(ctx, container, false, since, stderr) From db4b2d82f5134a7a2291e044b76558e6a2a02233 Mon Sep 17 00:00:00 2001 From: Dalibor Kricka Date: Tue, 17 Mar 2026 22:37:55 +0100 Subject: [PATCH 3/6] cmd/list, pkg/podman: Make podman.GetImages() filter, not getImages() This is a step towards eliminating or reducing the code in getImages() by doing everything or more in podman.GetImages(). If nothing else, this removes the need to keep a copy of the labels used to identify Toolbx images for getImages(). The getImages() function originated as listContainers(), when it invoked 'podman images --filter label=...' through podman.GetImages() separately for each label used to identify Toolbx images, and then joined and sorted the results. This changed in commit 2369da5d31830e5c when getImages() started invoking 'podman images' only once through podman.GetImages() to get all available images, and then filtered them itself based on the labels. Before this change in commit 2369da5d31830e5c, it probably made some sense to keep podman.GetImages() only as a thin wrapper to invoke 'podman images' with different options, and to do everything else in getImages(). However, since then more is being done inside the podman package (eg., unmarshalling the 'podman images' JSON in commit 5f324d537ed875ca and flattening the images in commit 6aab0a61752dc9a3), and getImages() is the only caller of podman.GetImages(). Therefore, it looks awkward to have the code to get all Toolbx images split across two functions in different packages. https://github.com/containers/toolbox/pull/1724 https://github.com/containers/toolbox/pull/1772 --- src/cmd/list.go | 28 ++-------------------------- src/pkg/podman/podman.go | 12 +++++++----- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/src/cmd/list.go b/src/cmd/list.go index a88c83103..a4b06cbf2 100644 --- a/src/cmd/list.go +++ b/src/cmd/list.go @@ -35,12 +35,6 @@ var ( onlyContainers bool onlyImages bool } - - // toolboxLabels holds labels used by containers/images that mark them as compatible with Toolbx - toolboxLabels = map[string]string{ - "com.github.debarshiray.toolbox": "true", - "com.github.containers.toolbox": "true", - } ) var listCmd = &cobra.Command{ @@ -160,26 +154,8 @@ func getImages(fillNameWithID bool) ([]podman.Image, error) { return nil, errors.New("failed to get images") } - var toolboxImages []podman.Image - - for _, image := range images { - var isToolboxImage bool - - for label := range toolboxLabels { - if _, ok := image.Labels[label]; ok { - isToolboxImage = true - break - } - } - - if isToolboxImage { - toolboxImages = append(toolboxImages, image) - } - - } - - sort.Sort(podman.ImageSlice(toolboxImages)) - return toolboxImages, nil + sort.Sort(podman.ImageSlice(images)) + return images, nil } func listOutput(images []podman.Image, containers []podman.Container) { diff --git a/src/pkg/podman/podman.go b/src/pkg/podman/podman.go index be88211b7..a3f5a60a3 100644 --- a/src/pkg/podman/podman.go +++ b/src/pkg/podman/podman.go @@ -188,7 +188,7 @@ func GetContainers(args ...string) (*Containers, error) { return &Containers{containers, 0}, nil } -// GetImages is a wrapper function around `podman images --format json` command. +// GetImages is a wrapper function around `podman images --format json` command that returns all Toolbx images // // Parameter fillNameWithID is a boolean that indicates if the image names should be filled with the ID, when there // are no names. @@ -213,7 +213,7 @@ func GetImages(fillNameWithID bool, args ...string) ([]Image, error) { } processedIDs := make(map[string]struct{}) - var processedImages []Image + var toolbxImages []Image for _, image := range images { if _, ok := processedIDs[image.ID]; ok { @@ -221,11 +221,13 @@ func GetImages(fillNameWithID bool, args ...string) ([]Image, error) { } processedIDs[image.ID] = struct{}{} - flattenedImages := image.flattenNames(fillNameWithID) - processedImages = append(processedImages, flattenedImages...) + if isToolbx(image.Labels) { + flattenedImages := image.flattenNames(fillNameWithID) + toolbxImages = append(toolbxImages, flattenedImages...) + } } - return processedImages, nil + return toolbxImages, nil } // GetVersion returns version of Podman in a string From 2f72a0c4bda3da83390ec24f90c24aa77de4b4f0 Mon Sep 17 00:00:00 2001 From: Dalibor Kricka Date: Tue, 17 Mar 2026 22:54:42 +0100 Subject: [PATCH 4/6] cmd/list, pkg/podman: Make podman.GetImages() sort, not getImages() This is a step towards eliminating the code in getImages() by doing everything in podman.GetImages(). This removes the need to export the podman.ImageSlice type from the podman package. The getImages() function originated as listContainers(), when it invoked 'podman images --filter label=...' through podman.GetImages() separately for each label used to identify Toolbx images, and then joined and sorted the results. This changed in commit 2369da5d31830e5c when getImages() started invoking 'podman images' only once through podman.GetImages() to get all available images, and then filtered them itself based on the labels. Before this change in commit 2369da5d31830e5c, it probably made some sense to keep podman.GetImages() only as a thin wrapper to invoke 'podman images' with different options, and to do everything else in getImages(). However, since then more is being done inside the podman package (eg., unmarshalling the 'podman images' JSON in commit 5f324d537ed875ca and flattening the images in commit 6aab0a61752dc9a3), and getImages() is the only caller of podman.GetImages(). Therefore, it looks awkward to have the code to get all Toolbx images split across two functions in different packages. https://github.com/containers/toolbox/pull/1724 https://github.com/containers/toolbox/pull/1772 --- src/cmd/list.go | 2 -- src/pkg/podman/podman.go | 14 ++++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cmd/list.go b/src/cmd/list.go index a4b06cbf2..cb589ba0f 100644 --- a/src/cmd/list.go +++ b/src/cmd/list.go @@ -20,7 +20,6 @@ import ( "errors" "fmt" "os" - "sort" "text/tabwriter" "github.com/containers/toolbox/pkg/podman" @@ -154,7 +153,6 @@ func getImages(fillNameWithID bool) ([]podman.Image, error) { return nil, errors.New("failed to get images") } - sort.Sort(podman.ImageSlice(images)) return images, nil } diff --git a/src/pkg/podman/podman.go b/src/pkg/podman/podman.go index a3f5a60a3..28264a7ce 100644 --- a/src/pkg/podman/podman.go +++ b/src/pkg/podman/podman.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "io" + "sort" "strconv" "time" @@ -39,7 +40,7 @@ type Image struct { Names []string } -type ImageSlice []Image +type imageSlice []Image var ( podmanVersion string @@ -109,23 +110,23 @@ func (image *Image) UnmarshalJSON(data []byte) error { return nil } -func (images ImageSlice) Len() int { +func (images imageSlice) Len() int { return len(images) } -func (images ImageSlice) Less(i, j int) bool { +func (images imageSlice) Less(i, j int) bool { if len(images[i].Names) != 1 { - panic("cannot sort unflattened ImageSlice") + panic("cannot sort unflattened imageSlice") } if len(images[j].Names) != 1 { - panic("cannot sort unflattened ImageSlice") + panic("cannot sort unflattened imageSlice") } return images[i].Names[0] < images[j].Names[0] } -func (images ImageSlice) Swap(i, j int) { +func (images imageSlice) Swap(i, j int) { images[i], images[j] = images[j], images[i] } @@ -227,6 +228,7 @@ func GetImages(fillNameWithID bool, args ...string) ([]Image, error) { } } + sort.Sort(imageSlice(toolbxImages)) return toolbxImages, nil } From 0802dd1bbb72919338b1ee0d2562b4d47b8de4bb Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Thu, 19 Mar 2026 00:58:37 +0100 Subject: [PATCH 5/6] cmd/list, pkg/podman: Simplify code by removing a function parameter The getImages() function originated as listContainers(), when it invoked 'podman images --filter label=...' through podman.GetImages() separately for each label used to identify Toolbx images. The label was specified using the 'args' parameter. This changed in commit 2369da5d31830e5c when getImages() started invoking 'podman images' only once through podman.GetImages() to get all available images, with the 'args' parameter set to a fixed value. Then in commit 6aab0a61752dc9a3 the 'args' parameter became unused. https://github.com/containers/toolbox/pull/1772 --- src/cmd/list.go | 4 ++-- src/pkg/podman/podman.go | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/cmd/list.go b/src/cmd/list.go index cb589ba0f..071a255a6 100644 --- a/src/cmd/list.go +++ b/src/cmd/list.go @@ -146,8 +146,8 @@ func listHelp(cmd *cobra.Command, args []string) { func getImages(fillNameWithID bool) ([]podman.Image, error) { logrus.Debug("Fetching all images") - var args []string - images, err := podman.GetImages(fillNameWithID, args...) + + images, err := podman.GetImages(fillNameWithID) if err != nil { logrus.Debugf("Fetching all images failed: %s", err) return nil, errors.New("failed to get images") diff --git a/src/pkg/podman/podman.go b/src/pkg/podman/podman.go index 28264a7ce..26ff02781 100644 --- a/src/pkg/podman/podman.go +++ b/src/pkg/podman/podman.go @@ -193,16 +193,15 @@ func GetContainers(args ...string) (*Containers, error) { // // Parameter fillNameWithID is a boolean that indicates if the image names should be filled with the ID, when there // are no names. -// Parameter args accepts an array of strings to be passed to the wrapped command (eg. ["-a", "--filter", "123"]). // // Returned value is a slice of Images. // // If a problem happens during execution, first argument is nil and second argument holds the error message. -func GetImages(fillNameWithID bool, args ...string) ([]Image, error) { +func GetImages(fillNameWithID bool) ([]Image, error) { var stdout bytes.Buffer logLevelString := LogLevel.String() - args = append([]string{"--log-level", logLevelString, "images", "--format", "json"}, args...) + args := []string{"--log-level", logLevelString, "images", "--format", "json"} if err := shell.Run("podman", nil, &stdout, nil, args...); err != nil { return nil, err } From 4cd38d39351aa61469b98c6d65983d7b86c3110d Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Thu, 19 Mar 2026 01:59:32 +0100 Subject: [PATCH 6/6] cmd/completion, cmd/list, cmd/rmi: Remove getImages() https://github.com/containers/toolbox/pull/1772 --- src/cmd/completion.go | 16 ++++++++++++++-- src/cmd/list.go | 19 +++++-------------- src/cmd/rmi.go | 8 ++++++-- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/cmd/completion.go b/src/cmd/completion.go index b9113e72b..e5c4eea7e 100644 --- a/src/cmd/completion.go +++ b/src/cmd/completion.go @@ -20,7 +20,9 @@ import ( "os" "strings" + "github.com/containers/toolbox/pkg/podman" "github.com/containers/toolbox/pkg/utils" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -129,8 +131,13 @@ func completionImageNames(cmd *cobra.Command, _ []string, _ string) ([]string, c return nil, cobra.ShellCompDirectiveNoFileComp } + logrus.Debug("Getting all images") + var imageNames []string - if images, err := getImages(true); err == nil { + + if images, err := podman.GetImages(true); err != nil { + logrus.Debugf("Getting all images failed: %s", err) + } else { for _, image := range images { if len(image.Names) != 1 { panic("cannot complete unflattened Image") @@ -144,8 +151,13 @@ func completionImageNames(cmd *cobra.Command, _ []string, _ string) ([]string, c } func completionImageNamesFiltered(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { + logrus.Debug("Getting all images") + var imageNames []string - if images, err := getImages(true); err == nil { + + if images, err := podman.GetImages(true); err != nil { + logrus.Debugf("Getting all images failed: %s", err) + } else { for _, image := range images { skip := false diff --git a/src/cmd/list.go b/src/cmd/list.go index 071a255a6..eb1e96d9e 100644 --- a/src/cmd/list.go +++ b/src/cmd/list.go @@ -86,9 +86,12 @@ func list(cmd *cobra.Command, args []string) error { var err error if lsImages { - images, err = getImages(false) + logrus.Debug("Getting all images") + + images, err = podman.GetImages(false) if err != nil { - return err + logrus.Debugf("Getting all images failed: %s", err) + return errors.New("failed to get images") } } @@ -144,18 +147,6 @@ func listHelp(cmd *cobra.Command, args []string) { } } -func getImages(fillNameWithID bool) ([]podman.Image, error) { - logrus.Debug("Fetching all images") - - images, err := podman.GetImages(fillNameWithID) - if err != nil { - logrus.Debugf("Fetching all images failed: %s", err) - return nil, errors.New("failed to get images") - } - - return images, nil -} - func listOutput(images []podman.Image, containers []podman.Container) { if len(images) != 0 { writer := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) diff --git a/src/cmd/rmi.go b/src/cmd/rmi.go index 581bd3f23..4f84aa839 100644 --- a/src/cmd/rmi.go +++ b/src/cmd/rmi.go @@ -24,6 +24,7 @@ import ( "github.com/containers/toolbox/pkg/podman" "github.com/containers/toolbox/pkg/utils" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -67,9 +68,12 @@ func rmi(cmd *cobra.Command, args []string) error { } if rmiFlags.deleteAll { - toolboxImages, err := getImages(false) + logrus.Debug("Getting all images") + + toolboxImages, err := podman.GetImages(false) if err != nil { - return err + logrus.Debugf("Getting all images failed: %s", err) + return errors.New("failed to get images") } for _, image := range toolboxImages {