mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-11 22:49:54 +00:00
Fix tctl access-review table from overflowing terminal width (#190)
Export-Source-Commit: 5c488f3ad10371625e4ad061c4d3ef0dd9702995
This commit is contained in:
committed by
Gus Rivera
parent
490ebd42ac
commit
f3a8e939f4
+16
-5
@@ -33,6 +33,17 @@ import (
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// Layout constants, for callers sizing columns against a terminal width: a
|
||||
// column occupies max(MinColumnWidth, widest cell+ColumnPadding).
|
||||
const (
|
||||
// MinColumnWidth is the narrowest a column renders, whatever its contents.
|
||||
MinColumnWidth = 5
|
||||
// ColumnPadding follows every cell, including the last one on a row.
|
||||
ColumnPadding = 1
|
||||
// TruncationSuffix is appended to cells over [Column.MaxCellLength].
|
||||
TruncationSuffix = "..."
|
||||
)
|
||||
|
||||
// Column represents a column in the table.
|
||||
type Column struct {
|
||||
Title string
|
||||
@@ -103,16 +114,16 @@ func MakeTableWithTruncatedColumn(columnOrder []string, rows [][]string, truncat
|
||||
}
|
||||
if column.MaxCellLength > maxColWidth {
|
||||
column.MaxCellLength = maxColWidth
|
||||
totalLen += column.MaxCellLength + 4 // "...<space>"
|
||||
totalLen += column.MaxCellLength + len(TruncationSuffix) + ColumnPadding // "...<space>"
|
||||
} else {
|
||||
totalLen += column.MaxCellLength + 1 // +1 for column separator
|
||||
totalLen += column.MaxCellLength + ColumnPadding // +1 for column separator
|
||||
}
|
||||
columns = append(columns, column)
|
||||
}
|
||||
|
||||
for _, column := range columns {
|
||||
if column.Title == truncatedColumn {
|
||||
column.MaxCellLength = max(width-totalLen-len("... "), 0)
|
||||
column.MaxCellLength = max(width-totalLen-len(TruncationSuffix)-ColumnPadding, 0)
|
||||
}
|
||||
t.AddColumn(column)
|
||||
}
|
||||
@@ -151,7 +162,7 @@ func (t *Table) truncateCell(colIndex int, cell string) (string, bool) {
|
||||
if maxCellLength == 0 || len(cell) <= maxCellLength {
|
||||
return cell, false
|
||||
}
|
||||
truncatedCell := fmt.Sprintf("%v...", cell[:maxCellLength])
|
||||
truncatedCell := cell[:maxCellLength] + TruncationSuffix
|
||||
footnoteLabel := t.columns[colIndex].FootnoteLabel
|
||||
if footnoteLabel == "" {
|
||||
return truncatedCell, false
|
||||
@@ -179,7 +190,7 @@ func (t *Table) String() string {
|
||||
|
||||
// WriteTo writes the full table to [w] or else returns an error.
|
||||
func (t *Table) WriteTo(w io.Writer) error {
|
||||
writer := tabwriter.NewWriter(w, 5, 0, 1, ' ', 0)
|
||||
writer := tabwriter.NewWriter(w, MinColumnWidth, 0, ColumnPadding, ' ', 0)
|
||||
template := strings.Repeat("%v\t", len(t.columns))
|
||||
|
||||
// Header and separator.
|
||||
|
||||
@@ -125,7 +125,7 @@ func (c *AccessGraphCommand) AccessReview(ctx context.Context, client *accessgra
|
||||
output.ActivityUnavailable = utils.AllowWhitespace(*resp.IacError)
|
||||
}
|
||||
if truncated {
|
||||
output.Warnings = append(output.Warnings, fmt.Sprintf("results truncated at %d identities; narrow --query for the full set", args.limit))
|
||||
output.Warnings = append(output.Warnings, fmt.Sprintf("results truncated at %d identities; narrow --query or increase --limit for the full set", args.limit))
|
||||
}
|
||||
|
||||
return writeOutput(c.stdout, output, args.format, func(w io.Writer) error {
|
||||
@@ -501,9 +501,12 @@ func accessReviewDetailedRows(output accessReviewOutput, showActivity bool) ([]s
|
||||
return headers, rows
|
||||
}
|
||||
|
||||
const resourceColumn = "Resource"
|
||||
|
||||
const resourceColumnFloor = 16
|
||||
const (
|
||||
// resourceColumn is the header of the column we truncate to fit the terminal.
|
||||
resourceColumn = "Resource"
|
||||
// resourceColumnFloor is the narrowest we will truncate resourceColumn to.
|
||||
resourceColumnFloor = 16
|
||||
)
|
||||
|
||||
func writeAccessTable(out io.Writer, headers []string, rows [][]string) error {
|
||||
table := buildAccessTable(headers, rows, terminalWidth(out))
|
||||
@@ -511,30 +514,39 @@ func writeAccessTable(out io.Writer, headers []string, rows [][]string) error {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// buildAccessTable caps only the Resource column to the space the others leave,
|
||||
// so wide columns are never clipped just to fit the table width. The cap ignores
|
||||
// the "..." asciitable appends on truncation, so a clipped Resource can overflow
|
||||
// width by a few columns; the terminal wraps it.
|
||||
// buildAccessTable lets every column render at its full width and truncates
|
||||
// only resourceColumn to the space they leave over, so no column is clipped
|
||||
// just to fit the table width. The table exceeds width only when that space
|
||||
// falls under resourceColumnFloor, in which case the terminal wraps it.
|
||||
func buildAccessTable(headers []string, rows [][]string, width int) asciitable.Table {
|
||||
used := 0
|
||||
// Measure what the other columns cost, and how much room Resource wants, so
|
||||
// we can tell whether it needs truncating at all.
|
||||
used, resourceWidth := 0, 0
|
||||
for i, h := range headers {
|
||||
if h == resourceColumn {
|
||||
continue
|
||||
}
|
||||
colWidth := len(h)
|
||||
for _, row := range rows {
|
||||
if i < len(row) && len(row[i]) > colWidth {
|
||||
colWidth = len(row[i])
|
||||
}
|
||||
}
|
||||
used += colWidth + 1 // +1 for tabwriter's column padding
|
||||
if h == resourceColumn {
|
||||
resourceWidth = colWidth
|
||||
continue
|
||||
}
|
||||
used += max(colWidth+asciitable.ColumnPadding, asciitable.MinColumnWidth)
|
||||
}
|
||||
|
||||
// Room left for Resource's own contents, its padding excluded.
|
||||
available := width - used - asciitable.ColumnPadding
|
||||
|
||||
t := asciitable.MakeTable([]string{})
|
||||
for _, h := range headers {
|
||||
col := asciitable.Column{Title: h}
|
||||
if h == resourceColumn {
|
||||
col.MaxCellLength = max(width-used, resourceColumnFloor)
|
||||
// A zero MaxCellLength leaves the column unbounded, so bound Resource
|
||||
// only when it genuinely overflows; the suffix asciitable appends on
|
||||
// truncation comes out of the same room.
|
||||
if h == resourceColumn && resourceWidth > available {
|
||||
col.MaxCellLength = max(available-len(asciitable.TruncationSuffix), resourceColumnFloor)
|
||||
}
|
||||
t.AddColumn(col)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user