Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions lib/asciitable/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"strings"
"text/tabwriter"

"golang.org/x/exp/slices"
"golang.org/x/term"
)

Expand Down Expand Up @@ -208,6 +209,30 @@ func (t *Table) IsHeadless() bool {
return true
}

// SortRowsBy sorts the table rows with the given column indices as the sorting
// key, optionally performing a stable sort. Column indices out of range are
// ignored - it is the caller's responsibility to ensure the indices are in
// range.
func (t *Table) SortRowsBy(colIdxKey []int, stable bool) {
lessFn := func(a, b []string) bool {
for _, col := range colIdxKey {
limit := min(len(a), len(b))
if col >= limit {
continue
}
if a[col] != b[col] {
return a[col] < b[col]
}
}
return false
}
if stable {
slices.SortStableFunc(t.rows, lessFn)
} else {
slices.SortFunc(t.rows, lessFn)
}
}

func min(a, b int) int {
if a < b {
return a
Expand Down
36 changes: 31 additions & 5 deletions tool/tctl/common/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ func (c *databaseServerCollection) writeText(w io.Writer, verbose bool) error {
labels := common.FormatLabels(server.GetDatabase().GetAllLabels(), verbose)
rows = append(rows, []string{
server.GetHostname(),
server.GetDatabase().GetName(),
nameOrDiscoveredName(server.GetDatabase(), verbose),
server.GetDatabase().GetProtocol(),
server.GetDatabase().GetURI(),
labels,
Expand All @@ -702,6 +702,8 @@ func (c *databaseServerCollection) writeText(w io.Writer, verbose bool) error {
} else {
t = asciitable.MakeTableWithTruncatedColumn(headers, rows, "Labels")
}
// stable sort by hostname then by name.
t.SortRowsBy([]int{0, 1}, true)
_, err := t.AsBuffer().WriteTo(w)
return trace.Wrap(err)
}
Expand Down Expand Up @@ -730,7 +732,10 @@ func (c *databaseCollection) writeText(w io.Writer, verbose bool) error {
for _, database := range c.databases {
labels := common.FormatLabels(database.GetAllLabels(), verbose)
rows = append(rows, []string{
database.GetName(), database.GetProtocol(), database.GetURI(), labels,
nameOrDiscoveredName(database, verbose),
database.GetProtocol(),
database.GetURI(),
labels,
})
}
headers := []string{"Name", "Protocol", "URI", "Labels"}
Expand All @@ -740,6 +745,8 @@ func (c *databaseCollection) writeText(w io.Writer, verbose bool) error {
} else {
t = asciitable.MakeTableWithTruncatedColumn(headers, rows, "Labels")
}
// stable sort by name.
t.SortRowsBy([]int{0}, true)
_, err := t.AsBuffer().WriteTo(w)
return trace.Wrap(err)
}
Expand Down Expand Up @@ -870,7 +877,7 @@ func (c *kubeServerCollection) writeText(w io.Writer, verbose bool) error {
}
labels := common.FormatLabels(kube.GetAllLabels(), verbose)
rows = append(rows, []string{
kube.GetName(),
nameOrDiscoveredName(kube, verbose),
labels,
server.GetTeleportVersion(),
})
Expand All @@ -883,6 +890,8 @@ func (c *kubeServerCollection) writeText(w io.Writer, verbose bool) error {
} else {
t = asciitable.MakeTableWithTruncatedColumn(headers, rows, "Labels")
}
// stable sort by cluster name.
t.SortRowsBy([]int{0}, true)

_, err := t.AsBuffer().WriteTo(w)
return trace.Wrap(err)
Expand Down Expand Up @@ -916,12 +925,12 @@ func (c *kubeClusterCollection) resources() (r []types.Resource) {
// cluster4 owner=cluster4,region=southcentralus,resource-group=cluster4,subscription-id=subID
// If verbose is disabled, labels column can be truncated to fit into the console.
func (c *kubeClusterCollection) writeText(w io.Writer, verbose bool) error {
sort.Sort(types.KubeClusters(c.clusters))
var rows [][]string
for _, cluster := range c.clusters {
labels := common.FormatLabels(cluster.GetAllLabels(), verbose)
rows = append(rows, []string{
cluster.GetName(), labels,
nameOrDiscoveredName(cluster, verbose),
labels,
})
}
headers := []string{"Name", "Labels"}
Expand All @@ -931,6 +940,8 @@ func (c *kubeClusterCollection) writeText(w io.Writer, verbose bool) error {
} else {
t = asciitable.MakeTableWithTruncatedColumn(headers, rows, "Labels")
}
// stable sort by name.
t.SortRowsBy([]int{0}, true)
_, err := t.AsBuffer().WriteTo(w)
return trace.Wrap(err)
}
Expand Down Expand Up @@ -1181,3 +1192,18 @@ func (c *userGroupCollection) writeText(w io.Writer, verbose bool) error {
_, err := t.AsBuffer().WriteTo(w)
return trace.Wrap(err)
}

// nameOrDiscoveredName returns the resource's name or its name as originally
// discovered in the cloud by the Teleport Discovery Service.
// In verbose mode, it always returns the resource name.
// In non-verbose mode, if the resource came from discovery and has the
// discovered name label, it returns the discovered name.
func nameOrDiscoveredName(r types.ResourceWithLabels, verbose bool) string {
if !verbose {
originalName, ok := r.GetAllLabels()[types.DiscoveredNameLabel]
if ok && originalName != "" {
return originalName
}
}
return r.GetName()
}
Loading