mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 19:13:34 +00:00
chore: add CREATED column to 'uc ps', fallback sorting by CREATED
This commit is contained in:
+22
-17
@@ -4,11 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/charmbracelet/huh/spinner"
|
"github.com/charmbracelet/huh/spinner"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/charmbracelet/lipgloss/table"
|
"github.com/charmbracelet/lipgloss/table"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/go-units"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/psviderski/uncloud/internal/cli"
|
"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,
|
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.`,
|
making it easy to see the distribution and status of containers across the cluster.`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
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 {
|
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,
|
return fmt.Errorf("invalid value for --sort: %q, must be one of '%s', '%s' or '%s'", opts.sortBy,
|
||||||
sortByService, sortByMachine, sortByHealth)
|
sortByService, sortByMachine, sortByHealth)
|
||||||
}
|
}
|
||||||
return runPs(cmd, opts)
|
|
||||||
|
return runPs(cmd.Context(), uncli, opts)
|
||||||
},
|
},
|
||||||
GroupID: "service",
|
GroupID: "service",
|
||||||
}
|
}
|
||||||
cmd.Flags().StringVarP(&opts.sortBy, "sort", "s", sortByService,
|
cmd.Flags().StringVarP(&opts.sortBy, "sort", "s", sortByService,
|
||||||
"Sort containers by 'service', 'machine' or 'health'")
|
"Sort containers by 'service', 'machine', or 'health'.")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,15 +70,15 @@ type containerInfo struct {
|
|||||||
image string
|
image string
|
||||||
status string
|
status string
|
||||||
highlight containerHighlight
|
highlight containerHighlight
|
||||||
|
created time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func runPs(cmd *cobra.Command, opts psOptions) error {
|
func runPs(ctx context.Context, uncli *cli.CLI, opts psOptions) error {
|
||||||
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
clusterClient, err := uncli.ConnectCluster(ctx)
|
||||||
client, err := uncli.ConnectCluster(cmd.Context())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("connect to cluster: %w", err)
|
return fmt.Errorf("connect to cluster: %w", err)
|
||||||
}
|
}
|
||||||
defer client.Close()
|
defer clusterClient.Close()
|
||||||
|
|
||||||
var containers []containerInfo
|
var containers []containerInfo
|
||||||
err = spinner.New().
|
err = spinner.New().
|
||||||
@@ -81,7 +86,7 @@ func runPs(cmd *cobra.Command, opts psOptions) error {
|
|||||||
Type(spinner.MiniDot).
|
Type(spinner.MiniDot).
|
||||||
Style(lipgloss.NewStyle().Foreground(lipgloss.Color("3"))).
|
Style(lipgloss.NewStyle().Foreground(lipgloss.Color("3"))).
|
||||||
ActionWithErr(func(ctx context.Context) error {
|
ActionWithErr(func(ctx context.Context) error {
|
||||||
containers, err = collectContainers(ctx, client)
|
containers, err = collectContainers(ctx, clusterClient)
|
||||||
return err
|
return err
|
||||||
}).
|
}).
|
||||||
Run()
|
Run()
|
||||||
@@ -89,7 +94,7 @@ func runPs(cmd *cobra.Command, opts psOptions) error {
|
|||||||
return fmt.Errorf("collect containers: %w", err)
|
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 {
|
sort.SliceStable(containers, func(i, j int) bool {
|
||||||
a, b := containers[i], containers[j]
|
a, b := containers[i], containers[j]
|
||||||
switch opts.sortBy {
|
switch opts.sortBy {
|
||||||
@@ -100,9 +105,6 @@ func runPs(cmd *cobra.Command, opts psOptions) error {
|
|||||||
if a.serviceName != b.serviceName {
|
if a.serviceName != b.serviceName {
|
||||||
return a.serviceName < b.serviceName
|
return a.serviceName < b.serviceName
|
||||||
}
|
}
|
||||||
if a.machineName != b.machineName {
|
|
||||||
return a.machineName < b.machineName
|
|
||||||
}
|
|
||||||
case sortByMachine:
|
case sortByMachine:
|
||||||
if a.machineName != b.machineName {
|
if a.machineName != b.machineName {
|
||||||
return 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 {
|
if a.serviceName != b.serviceName {
|
||||||
return a.serviceName < b.serviceName
|
return a.serviceName < b.serviceName
|
||||||
}
|
}
|
||||||
if a.machineName != b.machineName {
|
|
||||||
return a.machineName < b.machineName
|
|
||||||
}
|
}
|
||||||
}
|
// Fallback to creation time (newest first).
|
||||||
// Final tie-breaker
|
return a.created.After(b.created)
|
||||||
return a.name < b.name
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return printContainers(containers)
|
return printContainers(containers)
|
||||||
@@ -143,7 +142,7 @@ func printContainers(containers []containerInfo) error {
|
|||||||
return lipgloss.NewStyle().PaddingRight(3)
|
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 {
|
for _, ctr := range containers {
|
||||||
id := ctr.id
|
id := ctr.id
|
||||||
@@ -151,6 +150,8 @@ func printContainers(containers []containerInfo) error {
|
|||||||
id = id[:12]
|
id = id[:12]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
created := units.HumanDuration(time.Now().UTC().Sub(ctr.created)) + " ago"
|
||||||
|
|
||||||
var statusStyle lipgloss.Style
|
var statusStyle lipgloss.Style
|
||||||
switch ctr.highlight {
|
switch ctr.highlight {
|
||||||
case highlightSuccess:
|
case highlightSuccess:
|
||||||
@@ -168,6 +169,7 @@ func printContainers(containers []containerInfo) error {
|
|||||||
id,
|
id,
|
||||||
ctr.name,
|
ctr.name,
|
||||||
ctr.image,
|
ctr.image,
|
||||||
|
created,
|
||||||
statusStyle.Render(ctr.status),
|
statusStyle.Render(ctr.status),
|
||||||
ctr.machineName,
|
ctr.machineName,
|
||||||
)
|
)
|
||||||
@@ -253,6 +255,8 @@ func collectContainers(ctx context.Context, cli *client.Client) ([]containerInfo
|
|||||||
highlight = highlightWarning
|
highlight = highlightWarning
|
||||||
}
|
}
|
||||||
|
|
||||||
|
created, _ := time.Parse(time.RFC3339Nano, ctr.Container.Created)
|
||||||
|
|
||||||
info := containerInfo{
|
info := containerInfo{
|
||||||
serviceName: ctr.ServiceName(),
|
serviceName: ctr.ServiceName(),
|
||||||
machineName: machineName,
|
machineName: machineName,
|
||||||
@@ -261,6 +265,7 @@ func collectContainers(ctx context.Context, cli *client.Client) ([]containerInfo
|
|||||||
image: ctr.Container.Config.Image,
|
image: ctr.Container.Config.Image,
|
||||||
status: status,
|
status: status,
|
||||||
highlight: highlight,
|
highlight: highlight,
|
||||||
|
created: created,
|
||||||
}
|
}
|
||||||
containers = append(containers, info)
|
containers = append(containers, info)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user