chore: add CREATED column to 'uc ps', fallback sorting by CREATED

This commit is contained in:
Pasha Sviderski
2025-12-12 20:01:49 +10:00
parent 596bc561b8
commit bc65c73c51
+22 -17
View File
@@ -4,11 +4,13 @@ import (
"context"
"fmt"
"sort"
"time"
"github.com/charmbracelet/huh/spinner"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/lipgloss/table"
"github.com/docker/docker/api/types/container"
"github.com/docker/go-units"
"github.com/spf13/cobra"
"github.com/psviderski/uncloud/internal/cli"
@@ -44,16 +46,19 @@ func NewPsCommand() *cobra.Command {
This command provides a comprehensive overview of all running containers that are part of a service,
making it easy to see the distribution and status of containers across the cluster.`,
RunE: func(cmd *cobra.Command, args []string) error {
uncli := cmd.Context().Value("cli").(*cli.CLI)
if opts.sortBy != sortByService && opts.sortBy != sortByMachine && opts.sortBy != sortByHealth {
return fmt.Errorf("invalid value for --sort: %q, must be one of '%s', '%s' or '%s'", opts.sortBy,
sortByService, sortByMachine, sortByHealth)
}
return runPs(cmd, opts)
return runPs(cmd.Context(), uncli, opts)
},
GroupID: "service",
}
cmd.Flags().StringVarP(&opts.sortBy, "sort", "s", sortByService,
"Sort containers by 'service', 'machine' or 'health'")
"Sort containers by 'service', 'machine', or 'health'.")
return cmd
}
@@ -65,15 +70,15 @@ type containerInfo struct {
image string
status string
highlight containerHighlight
created time.Time
}
func runPs(cmd *cobra.Command, opts psOptions) error {
uncli := cmd.Context().Value("cli").(*cli.CLI)
client, err := uncli.ConnectCluster(cmd.Context())
func runPs(ctx context.Context, uncli *cli.CLI, opts psOptions) error {
clusterClient, err := uncli.ConnectCluster(ctx)
if err != nil {
return fmt.Errorf("connect to cluster: %w", err)
}
defer client.Close()
defer clusterClient.Close()
var containers []containerInfo
err = spinner.New().
@@ -81,7 +86,7 @@ func runPs(cmd *cobra.Command, opts psOptions) error {
Type(spinner.MiniDot).
Style(lipgloss.NewStyle().Foreground(lipgloss.Color("3"))).
ActionWithErr(func(ctx context.Context) error {
containers, err = collectContainers(ctx, client)
containers, err = collectContainers(ctx, clusterClient)
return err
}).
Run()
@@ -89,7 +94,7 @@ func runPs(cmd *cobra.Command, opts psOptions) error {
return fmt.Errorf("collect containers: %w", err)
}
// Sort the containers based on the sorting option
// Sort the containers based on the sorting option.
sort.SliceStable(containers, func(i, j int) bool {
a, b := containers[i], containers[j]
switch opts.sortBy {
@@ -100,9 +105,6 @@ func runPs(cmd *cobra.Command, opts psOptions) error {
if a.serviceName != b.serviceName {
return a.serviceName < b.serviceName
}
if a.machineName != b.machineName {
return a.machineName < b.machineName
}
case sortByMachine:
if a.machineName != b.machineName {
return a.machineName < b.machineName
@@ -114,12 +116,9 @@ func runPs(cmd *cobra.Command, opts psOptions) error {
if a.serviceName != b.serviceName {
return a.serviceName < b.serviceName
}
if a.machineName != b.machineName {
return a.machineName < b.machineName
}
}
// Final tie-breaker
return a.name < b.name
// Fallback to creation time (newest first).
return a.created.After(b.created)
})
return printContainers(containers)
@@ -143,7 +142,7 @@ func printContainers(containers []containerInfo) error {
return lipgloss.NewStyle().PaddingRight(3)
})
t.Headers("SERVICE", "CONTAINER ID", "NAME", "IMAGE", "STATUS", "MACHINE")
t.Headers("SERVICE", "CONTAINER ID", "CONTAINER NAME", "IMAGE", "CREATED", "STATUS", "MACHINE")
for _, ctr := range containers {
id := ctr.id
@@ -151,6 +150,8 @@ func printContainers(containers []containerInfo) error {
id = id[:12]
}
created := units.HumanDuration(time.Now().UTC().Sub(ctr.created)) + " ago"
var statusStyle lipgloss.Style
switch ctr.highlight {
case highlightSuccess:
@@ -168,6 +169,7 @@ func printContainers(containers []containerInfo) error {
id,
ctr.name,
ctr.image,
created,
statusStyle.Render(ctr.status),
ctr.machineName,
)
@@ -253,6 +255,8 @@ func collectContainers(ctx context.Context, cli *client.Client) ([]containerInfo
highlight = highlightWarning
}
created, _ := time.Parse(time.RFC3339Nano, ctr.Container.Created)
info := containerInfo{
serviceName: ctr.ServiceName(),
machineName: machineName,
@@ -261,6 +265,7 @@ func collectContainers(ctx context.Context, cli *client.Client) ([]containerInfo
image: ctr.Container.Config.Image,
status: status,
highlight: highlight,
created: created,
}
containers = append(containers, info)
}