feat(images): list images filtered by name

This commit is contained in:
Pasha Sviderski
2025-10-07 20:39:31 +10:00
parent 509c9eb7d3
commit 1f9c2c873d
2 changed files with 56 additions and 8 deletions
+55 -7
View File
@@ -20,27 +20,38 @@ import (
) )
type listOptions struct { type listOptions struct {
machines []string machines []string
context string nameFilter string
context string
} }
func NewListCommand() *cobra.Command { func NewListCommand() *cobra.Command {
opts := listOptions{} opts := listOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "ls", Use: "ls [IMAGE]",
Aliases: []string{"list"}, Aliases: []string{"list"},
Short: "List images on machines in the cluster.", Short: "List images on machines in the cluster.",
Long: "List images on machines in the cluster. By default, on all machines.", Long: "List images on machines in the cluster. By default, on all machines. Optionally filter by image name.",
Example: ` # List images on all machines. Example: ` # List all images on all machines.
uc image ls uc image ls
# List images on specific machine. # List images on specific machine.
uc image ls -m machine1 uc image ls -m machine1
# List images on multiple machines. # List images on multiple machines.
uc image ls -m machine1,machine2`, uc image ls -m machine1,machine2
# List images filtered by name (with any tag) on all machines.
uc image ls myapp
# List images filtered by name and tag on specific machine.
uc image ls myapp:1.2.3 -m machine1`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
opts.nameFilter = args[0]
}
uncli := cmd.Context().Value("cli").(*cli.CLI) uncli := cmd.Context().Value("cli").(*cli.CLI)
return list(cmd.Context(), uncli, opts) return list(cmd.Context(), uncli, opts)
}, },
@@ -145,8 +156,14 @@ func list(ctx context.Context, uncli *cli.CLI, opts listOptions) error {
} }
} }
rows = filterImagesByName(rows, opts.nameFilter)
if len(rows) == 0 { if len(rows) == 0 {
fmt.Println("No images found.") if opts.nameFilter != "" {
fmt.Printf("No images matching '%s' found.\n", opts.nameFilter)
} else {
fmt.Println("No images found.")
}
return nil return nil
} }
@@ -164,6 +181,37 @@ func list(ctx context.Context, uncli *cli.CLI, opts listOptions) error {
return nil return nil
} }
// filterImagesByName filters the image rows by image name. It matches any tag of the image if only the name
// is provided. For example, if filter is "nginx", it matches "nginx", "nginx:latest", "nginx:1.25", etc.
// If filter is "nginx:latest", it matches only "nginx:latest".
func filterImagesByName(rows []imageRow, nameFilter string) []imageRow {
if nameFilter == "" {
return rows
}
var filteredRows []imageRow
for _, row := range rows {
if row.name == "<none>" {
continue
}
if strings.Contains(nameFilter, ":") {
// Exact match if filter contains a tag.
if row.name == nameFilter {
filteredRows = append(filteredRows, row)
}
continue
}
// Match by name prefix if filter does not contain a tag.
if strings.HasPrefix(row.name, nameFilter) &&
len(row.name) > len(nameFilter) && row.name[len(nameFilter)] == ':' {
filteredRows = append(filteredRows, row)
}
}
return filteredRows
}
// imagePlatforms returns a list of platforms supported by the image and a boolean indicating if it's multi-platform. // imagePlatforms returns a list of platforms supported by the image and a boolean indicating if it's multi-platform.
func imagePlatforms(img image.Summary) ([]string, bool) { func imagePlatforms(img image.Summary) ([]string, bool) {
var formattedPlatforms []string var formattedPlatforms []string
+1 -1
View File
@@ -10,7 +10,7 @@ import (
// NewImagesCommand returns the 'image ls' command modified to work as 'images'. // NewImagesCommand returns the 'image ls' command modified to work as 'images'.
func NewImagesCommand() *cobra.Command { func NewImagesCommand() *cobra.Command {
listCmd := image.NewListCommand() listCmd := image.NewListCommand()
listCmd.Use = "images" listCmd.Use = "images [IMAGE]"
// Remove 'list' alias since this command is already an alias. // Remove 'list' alias since this command is already an alias.
listCmd.Aliases = nil listCmd.Aliases = nil
listCmd.Example = strings.ReplaceAll(listCmd.Example, "uc image ls", "uc images") listCmd.Example = strings.ReplaceAll(listCmd.Example, "uc image ls", "uc images")