diff --git a/internal/cmd/load-balancer/describe/describe.go b/internal/cmd/load-balancer/describe/describe.go index fe28b86d7..ff6defaf0 100644 --- a/internal/cmd/load-balancer/describe/describe.go +++ b/internal/cmd/load-balancer/describe/describe.go @@ -162,6 +162,7 @@ func renderLoadBalancer(loadBalancer *loadbalancer.LoadBalancer) string { } table := tables.NewTable() + table.SetTitle("Load Balancer") table.AddRow("NAME", *loadBalancer.Name) table.AddSeparator() table.AddRow("STATE", *loadBalancer.Status) @@ -182,7 +183,8 @@ func renderLoadBalancer(loadBalancer *loadbalancer.LoadBalancer) string { func renderListeners(listeners []loadbalancer.Listener) string { table := tables.NewTable() - table.SetHeader("LISTENER NAME", "PORT", "PROTOCOL", "TARGET POOL") + table.SetTitle("Listeners") + table.SetHeader("NAME", "PORT", "PROTOCOL", "TARGET POOL") for i := range listeners { listener := listeners[i] table.AddRow(*listener.Name, *listener.Port, *listener.Protocol, *listener.TargetPool) @@ -192,7 +194,8 @@ func renderListeners(listeners []loadbalancer.Listener) string { func renderTargetPools(targetPools []loadbalancer.TargetPool) string { table := tables.NewTable() - table.SetHeader("TARGET POOL NAME", "PORT", "TARGETS") + table.SetTitle("Target Pools") + table.SetHeader("NAME", "PORT", "TARGETS") for _, targetPool := range targetPools { table.AddRow(*targetPool.Name, *targetPool.TargetPort, len(*targetPool.Targets)) } diff --git a/internal/cmd/mongodbflex/options/options.go b/internal/cmd/mongodbflex/options/options.go index 5811b0c09..217756194 100644 --- a/internal/cmd/mongodbflex/options/options.go +++ b/internal/cmd/mongodbflex/options/options.go @@ -222,6 +222,7 @@ func renderFlavors(flavors []mongodbflex.HandlersInfraFlavor) string { } table := tables.NewTable() + table.SetTitle("Flavors") table.SetHeader("ID", "CPU", "MEMORY", "DESCRIPTION", "VALID INSTANCE TYPES") for i := range flavors { f := flavors[i] @@ -236,6 +237,7 @@ func renderVersions(versions []string) string { } table := tables.NewTable() + table.SetTitle("Versions") table.SetHeader("VERSION") for i := range versions { v := versions[i] @@ -251,7 +253,8 @@ func renderStorages(resp *mongodbflex.ListStoragesResponse) string { storageClasses := *resp.StorageClasses table := tables.NewTable() - table.SetHeader("MIN STORAGE", "MAX STORAGE", "STORAGE CLASS") + table.SetTitle("Storages") + table.SetHeader("MINIMUM", "MAXIMUM", "STORAGE CLASS") for i := range storageClasses { sc := storageClasses[i] table.AddRow(*resp.StorageRange.Min, *resp.StorageRange.Max, sc) diff --git a/internal/cmd/postgresflex/options/options.go b/internal/cmd/postgresflex/options/options.go index c245a453c..7341f4ea3 100644 --- a/internal/cmd/postgresflex/options/options.go +++ b/internal/cmd/postgresflex/options/options.go @@ -222,6 +222,7 @@ func renderFlavors(flavors []postgresflex.Flavor) string { } table := tables.NewTable() + table.SetTitle("Flavors") table.SetHeader("ID", "CPU", "MEMORY", "DESCRIPTION") for i := range flavors { f := flavors[i] @@ -236,6 +237,7 @@ func renderVersions(versions []string) string { } table := tables.NewTable() + table.SetTitle("Versions") table.SetHeader("VERSION") for i := range versions { v := versions[i] @@ -251,7 +253,8 @@ func renderStorages(resp *postgresflex.ListStoragesResponse) string { storageClasses := *resp.StorageClasses table := tables.NewTable() - table.SetHeader("MIN STORAGE", "MAX STORAGE", "STORAGE CLASS") + table.SetTitle("Storages") + table.SetHeader("MINIMUM", "MAXIMUM", "STORAGE CLASS") for i := range storageClasses { sc := storageClasses[i] table.AddRow(*resp.StorageRange.Min, *resp.StorageRange.Max, sc) diff --git a/internal/cmd/ske/options/options.go b/internal/cmd/ske/options/options.go index 74b651ab2..b41682e08 100644 --- a/internal/cmd/ske/options/options.go +++ b/internal/cmd/ske/options/options.go @@ -181,7 +181,8 @@ func renderAvailabilityZones(resp *ske.ProviderOptions) string { zones := *resp.AvailabilityZones table := tables.NewTable() - table.SetHeader("AVAILABILITY ZONES") + table.SetTitle("Availability Zones") + table.SetHeader("ZONE") for i := range zones { z := zones[i] table.AddRow(*z.Name) @@ -193,7 +194,8 @@ func renderKubernetesVersions(resp *ske.ProviderOptions) (string, error) { versions := *resp.KubernetesVersions table := tables.NewTable() - table.SetHeader("KUBERNETES VERSION", "STATE", "EXPIRATION DATE", "FEATURE GATES") + table.SetTitle("Kubernetes Versions") + table.SetHeader("VERSION", "STATE", "EXPIRATION DATE", "FEATURE GATES") for i := range versions { v := versions[i] featureGate, err := json.Marshal(*v.FeatureGates) @@ -213,7 +215,8 @@ func renderMachineImages(resp *ske.ProviderOptions) string { images := *resp.MachineImages table := tables.NewTable() - table.SetHeader("MACHINE IMAGE NAME", "VERSION", "STATE", "EXPIRATION DATE", "SUPPORTED CRI") + table.SetTitle("Machine Images") + table.SetHeader("NAME", "VERSION", "STATE", "EXPIRATION DATE", "SUPPORTED CRI") for i := range images { image := images[i] versions := *image.Versions @@ -241,7 +244,8 @@ func renderMachineTypes(resp *ske.ProviderOptions) string { types := *resp.MachineTypes table := tables.NewTable() - table.SetHeader("MACHINE TYPE", "CPU", "MEMORY") + table.SetTitle("Machine Types") + table.SetHeader("TYPE", "CPU", "MEMORY") for i := range types { t := types[i] table.AddRow(*t.Name, *t.Cpu, *t.Memory) @@ -253,7 +257,8 @@ func renderVolumeTypes(resp *ske.ProviderOptions) string { types := *resp.VolumeTypes table := tables.NewTable() - table.SetHeader("VOLUME TYPE") + table.SetTitle("Volume Types") + table.SetHeader("TYPE") for i := range types { z := types[i] table.AddRow(*z.Name) diff --git a/internal/pkg/print/print.go b/internal/pkg/print/print.go index 2bdd483cc..aa08cc2f6 100644 --- a/internal/pkg/print/print.go +++ b/internal/pkg/print/print.go @@ -171,7 +171,13 @@ func (p *Printer) PagerDisplay(content string) error { if outputFormat == NoneOutputFormat { return nil } - pagerCmd := exec.Command("less", "-F", "-S", "-w") + + // less arguments + // -F: exits if the entire file fits on the first screen + // -S: disables line wrapping + // -w: highlight the first line after moving one full page down + // -R: interprets ANSI color and style sequences + pagerCmd := exec.Command("less", "-F", "-S", "-w", "-R") pagerCmd.Stdin = strings.NewReader(content) pagerCmd.Stdout = p.Cmd.OutOrStdout() diff --git a/internal/pkg/tables/tables.go b/internal/pkg/tables/tables.go index a60de1fc8..de8e0b397 100644 --- a/internal/pkg/tables/tables.go +++ b/internal/pkg/tables/tables.go @@ -6,6 +6,7 @@ import ( "github.com/stackitcloud/stackit-cli/internal/pkg/print" "github.com/jedib0t/go-pretty/v6/table" + "github.com/jedib0t/go-pretty/v6/text" ) type Table struct { @@ -20,6 +21,20 @@ func NewTable() Table { } } +// Sets the title of the table +func (t *Table) SetTitle(title string) { + t.table.SetTitle(title) + + // prevent title wrapping by setting the width of the first column to the length of the title + // this is a workaround for a bug in the tables pkg, see https://github.com/jedib0t/go-pretty/issues/135 + t.table.SetColumnConfigs([]table.ColumnConfig{ + { + Number: 1, + WidthMin: len(title), + }, + }) +} + // Sets the header of the table func (t *Table) SetHeader(header ...interface{}) { t.table.AppendHeader(table.Row(header)) @@ -47,6 +62,10 @@ func (t *Table) EnableAutoMergeOnColumns(columns ...int) { // Returns the table rendered func (t *Table) Render() string { t.table.SetStyle(table.StyleLight) + + t.table.Style().Title = table.TitleOptionsBlackOnCyan + t.table.Style().Title.Align = text.AlignCenter + t.table.Style().Options.DrawBorder = false t.table.Style().Options.SeparateRows = false t.table.Style().Options.SeparateColumns = true