cmd/list: Filter images/containers using labels

Instead of executing 'podman ps|images' several times in a row, call
them only once and get output with all images/containers. Then, filter
out the JSON using labels and keep images/containers only with matching
labels.

This simplifies the code significantly and cuts down the execution time
of 'toolbox list'. The speed gain is noticeable:

- the system has 5 images and 10 containers

Before patch: ~1.45s
After patch:  ~0.85s
This commit is contained in:
Ondřej Míchal 2021-03-22 14:51:16 +01:00
parent 49460ebc56
commit 2369da5d31

View file

@ -51,6 +51,12 @@ var (
onlyContainers bool onlyContainers bool
onlyImages bool onlyImages bool
} }
// toolboxLabels holds labels used by containers/images that mark them as compatible with Toolbox
toolboxLabels = map[string]string{
"com.github.debarshiray.toolbox": "true",
"com.github.containers.toolbox": "true",
}
) )
var listCmd = &cobra.Command{ var listCmd = &cobra.Command{
@ -123,35 +129,22 @@ func list(cmd *cobra.Command, args []string) error {
} }
func getContainers() ([]toolboxContainer, error) { func getContainers() ([]toolboxContainer, error) {
logrus.Debug("Fetching containers with label=com.github.containers.toolbox=true") var err error
args := []string{"--all", "--filter", "label=com.github.containers.toolbox=true"} var args []string
containers_old, err := podman.GetContainers(args...)
if err != nil {
return nil, fmt.Errorf("failed to list containers with label=com.github.containers.toolbox=true: %w", err)
}
logrus.Debug("Fetching containers with label=com.github.debarshiray.toolbox=true")
args = []string{"--all", "--filter", "label=com.github.debarshiray.toolbox=true"}
containers_new, err := podman.GetContainers(args...)
if err != nil {
return nil, fmt.Errorf("failed to list containers with label=com.github.debarshiray.toolbox=true: %w", err)
}
var containers []map[string]interface{} var containers []map[string]interface{}
if podman.CheckVersion("2.0.0") { var toolboxContainers []toolboxContainer
containers = utils.JoinJSON("Id", containers_old, containers_new)
containers = utils.SortJSON(containers, "Names", true) logrus.Debug("Fetching all containers")
} else { args = []string{"--all", "--sort", "names"}
containers = utils.JoinJSON("ID", containers_old, containers_new) containers, err = podman.GetContainers(args...)
containers = utils.SortJSON(containers, "Names", false) if err != nil {
return nil, fmt.Errorf("failed to get containers: %w", err)
} }
// This section is a temporary solution that is here to prevent a major
// redesign of the way how toolbox containers are fetched.
// Remove this in Toolbox v0.2.0
var toolboxContainers []toolboxContainer
for _, container := range containers { for _, container := range containers {
var c toolboxContainer var c toolboxContainer
var isToolboxContainer bool = false
containerJSON, err := json.Marshal(container) containerJSON, err := json.Marshal(container)
if err != nil { if err != nil {
logrus.Errorf("failed to marshal container: %v", err) logrus.Errorf("failed to marshal container: %v", err)
@ -163,8 +156,18 @@ func getContainers() ([]toolboxContainer, error) {
logrus.Errorf("failed to unmarshal container: %v", err) logrus.Errorf("failed to unmarshal container: %v", err)
continue continue
} }
for label := range toolboxLabels {
if _, ok := c.Labels[label]; ok {
isToolboxContainer = true
break
}
}
if isToolboxContainer {
toolboxContainers = append(toolboxContainers, c) toolboxContainers = append(toolboxContainers, c)
} }
}
return toolboxContainers, nil return toolboxContainers, nil
} }
@ -191,38 +194,22 @@ func listHelp(cmd *cobra.Command, args []string) {
} }
func getImages() ([]toolboxImage, error) { func getImages() ([]toolboxImage, error) {
logrus.Debug("Fetching images with label=com.github.containers.toolbox=true") var err error
args := []string{"--filter", "label=com.github.containers.toolbox=true"} var args []string
images_old, err := podman.GetImages(args...)
if err != nil {
return nil, errors.New("failed to list images with label=com.github.containers.toolbox=true")
}
logrus.Debug("Fetching images with label=com.github.debarshiray.toolbox=true")
args = []string{"--filter", "label=com.github.debarshiray.toolbox=true"}
images_new, err := podman.GetImages(args...)
if err != nil {
return nil, errors.New("failed to list images with com.github.debarshiray.toolbox=true")
}
var images []map[string]interface{} var images []map[string]interface{}
if podman.CheckVersion("2.0.0") { var toolboxImages []toolboxImage
images = utils.JoinJSON("Id", images_old, images_new)
images = utils.SortJSON(images, "Names", true) logrus.Debug("Fetching all images")
} else if podman.CheckVersion("1.8.3") { args = []string{"--sort", "repository"}
images = utils.JoinJSON("ID", images_old, images_new) images, err = podman.GetImages(args...)
images = utils.SortJSON(images, "Names", true) if err != nil {
} else { return nil, fmt.Errorf("failed to get images: %w", err)
images = utils.JoinJSON("id", images_old, images_new)
images = utils.SortJSON(images, "names", true)
} }
// This section is a temporary solution that is here to prevent a major
// redesign of the way how toolbox images are fetched.
// Remove this in Toolbox v0.2.0
var toolboxImages []toolboxImage
for _, image := range images { for _, image := range images {
var i toolboxImage var i toolboxImage
var isToolboxImage bool = false
imageJSON, err := json.Marshal(image) imageJSON, err := json.Marshal(image)
if err != nil { if err != nil {
logrus.Errorf("failed to marshal toolbox image: %v", err) logrus.Errorf("failed to marshal toolbox image: %v", err)
@ -234,8 +221,18 @@ func getImages() ([]toolboxImage, error) {
logrus.Errorf("failed to unmarshal toolbox image: %v", err) logrus.Errorf("failed to unmarshal toolbox image: %v", err)
continue continue
} }
for label := range toolboxLabels {
if _, ok := i.Labels[label]; ok {
isToolboxImage = true
break
}
}
if isToolboxImage {
toolboxImages = append(toolboxImages, i) toolboxImages = append(toolboxImages, i)
} }
}
return toolboxImages, nil return toolboxImages, nil
} }