Unverified Commit 6dcf85a2 authored by Bryan Liles's avatar Bryan Liles Committed by GitHub
Browse files

Merge pull request #365 from bryanl/table-printer

Use table printer to print tables
parents 2d89ba5d 5a28ce9c
......@@ -23,7 +23,7 @@ import (
"github.com/ksonnet/ksonnet/metadata"
"github.com/ksonnet/ksonnet/metadata/parts"
str "github.com/ksonnet/ksonnet/strings"
"github.com/ksonnet/ksonnet/pkg/util/table"
"github.com/spf13/cobra"
)
......@@ -248,13 +248,9 @@ var pkgListCmd = &cobra.Command{
return err
}
headerRows := [][]string{
[]string{registryHeader, nameHeader, installedHeader},
[]string{
strings.Repeat("=", len(registryHeader)),
strings.Repeat("=", len(nameHeader)),
strings.Repeat("=", len(installedHeader))},
}
t := table.New(os.Stdout)
t.SetHeader([]string{registryHeader, nameHeader, installedHeader})
rows := make([][]string, 0)
for name := range app.Registries() {
reg, _, err := manager.GetRegistry(name)
......@@ -263,12 +259,15 @@ var pkgListCmd = &cobra.Command{
}
for libName := range reg.Libraries {
var row []string
_, isInstalled := app.Libraries()[libName]
if isInstalled {
rows = append(rows, []string{name, libName, installed})
row = []string{name, libName, installed}
} else {
rows = append(rows, []string{name, libName})
row = []string{name, libName}
}
rows = append(rows, row)
}
}
......@@ -279,13 +278,9 @@ var pkgListCmd = &cobra.Command{
return nameI < nameJ
})
rows = append(headerRows, rows...)
t.AppendBulk(rows)
t.Render()
formatted, err := str.PadRows(rows)
if err != nil {
return err
}
fmt.Print(formatted)
return nil
},
Long: `
......
......@@ -23,7 +23,7 @@ import (
"github.com/ksonnet/ksonnet/metadata"
"github.com/ksonnet/ksonnet/pkg/kubecfg"
str "github.com/ksonnet/ksonnet/strings"
"github.com/ksonnet/ksonnet/pkg/util/table"
"github.com/spf13/cobra"
)
......@@ -103,23 +103,15 @@ var registryListCmd = &cobra.Command{
return err
}
rows := [][]string{
[]string{nameHeader, protocolHeader, uriHeader},
[]string{
strings.Repeat("=", len(nameHeader)),
strings.Repeat("=", len(protocolHeader)),
strings.Repeat("=", len(uriHeader)),
},
}
t := table.New(os.Stdout)
t.SetHeader([]string{nameHeader, protocolHeader, uriHeader})
for name, regRef := range app.Registries() {
rows = append(rows, []string{name, regRef.Protocol, regRef.URI})
t.Append([]string{name, regRef.Protocol, regRef.URI})
}
formatted, err := str.PadRows(rows)
if err != nil {
return err
}
fmt.Print(formatted)
t.Render()
return nil
},
Long: `
......
......@@ -16,12 +16,10 @@
package kubecfg
import (
"fmt"
"io"
"sort"
"strings"
str "github.com/ksonnet/ksonnet/strings"
"github.com/ksonnet/ksonnet/pkg/util/table"
)
const (
......@@ -49,8 +47,7 @@ func (c *ComponentListCmd) Run(out io.Writer) error {
return err
}
_, err = printComponents(out, components)
return err
return printComponents(out, components)
}
// ComponentRmCmd stores the information necessary to remove a component from
......@@ -74,21 +71,14 @@ func (c *ComponentRmCmd) Run() error {
return manager.DeleteComponent(c.component)
}
func printComponents(out io.Writer, components []string) (string, error) {
rows := [][]string{
[]string{componentNameHeader},
[]string{strings.Repeat("=", len(componentNameHeader))},
}
func printComponents(out io.Writer, components []string) error {
t := table.New(out)
t.SetHeader([]string{componentNameHeader})
sort.Strings(components)
for _, component := range components {
rows = append(rows, []string{component})
t.Append([]string{component})
}
formatted, err := str.PadRows(rows)
if err != nil {
return "", err
}
_, err = fmt.Fprint(out, formatted)
return formatted, err
return t.Render()
}
......@@ -16,18 +16,20 @@
package kubecfg
import (
"os"
"bytes"
"testing"
"github.com/stretchr/testify/require"
)
func TestPrintComponents(t *testing.T) {
for _, tc := range []struct {
cases := []struct {
name string
components []string
expected string
}{
{
name: "print",
components: []string{"a", "b"},
expected: `COMPONENT
=========
......@@ -35,8 +37,8 @@ a
b
`,
},
// Check that components are displayed in alphabetical order
{
name: "Check that components are displayed in alphabetical order",
components: []string{"b", "a"},
expected: `COMPONENT
=========
......@@ -44,18 +46,21 @@ a
b
`,
},
// Check empty components scenario
{
name: "Check empty components scenario",
components: []string{},
expected: `COMPONENT
=========
`,
},
} {
out, err := printComponents(os.Stdout, tc.components)
if err != nil {
t.Fatalf(err.Error())
}
require.EqualValues(t, tc.expected, out)
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
var buf bytes.Buffer
err := printComponents(&buf, tc.components)
require.NoError(t, err)
require.EqualValues(t, tc.expected, buf.String())
})
}
}
......@@ -16,14 +16,12 @@
package kubecfg
import (
"fmt"
"io"
"sort"
"strings"
"github.com/ksonnet/ksonnet/env"
"github.com/ksonnet/ksonnet/metadata"
str "github.com/ksonnet/ksonnet/strings"
"github.com/ksonnet/ksonnet/pkg/util/table"
)
type EnvAddCmd struct {
......@@ -69,6 +67,7 @@ func NewEnvListCmd(manager metadata.Manager) (*EnvListCmd, error) {
return &EnvListCmd{manager: manager}, nil
}
// Run builds a list of environments.
func (c *EnvListCmd) Run(out io.Writer) error {
const (
nameHeader = "NAME"
......@@ -90,30 +89,19 @@ func (c *EnvListCmd) Run(out io.Writer) error {
// Sort environments by ascending alphabetical name
sort.Slice(envs, func(i, j int) bool { return envs[i].Name < envs[j].Name })
rows := [][]string{
[]string{nameHeader, k8sVersionHeader, namespaceHeader, serverHeader},
[]string{
strings.Repeat("=", len(nameHeader)),
strings.Repeat("=", len(k8sVersionHeader)),
strings.Repeat("=", len(namespaceHeader)),
strings.Repeat("=", len(serverHeader))},
}
t := table.New(out)
t.SetHeader([]string{nameHeader, k8sVersionHeader, namespaceHeader, serverHeader})
for _, env := range envs {
rows = append(rows, []string{
t.Append([]string{
env.Name,
env.KubernetesVersion,
env.Destination.Namespace(),
env.Destination.Server()})
}
formattedEnvsList, err := str.PadRows(rows)
if err != nil {
return err
}
_, err = fmt.Fprint(out, formattedEnvsList)
return err
t.Render()
return nil
}
// ==================================================================
......
......@@ -22,11 +22,11 @@ import (
"reflect"
"sort"
"strconv"
"strings"
"github.com/fatih/color"
"github.com/ksonnet/ksonnet/component"
param "github.com/ksonnet/ksonnet/metadata/params"
"github.com/ksonnet/ksonnet/pkg/util/table"
str "github.com/ksonnet/ksonnet/strings"
"github.com/pkg/errors"
"github.com/spf13/afero"
......@@ -162,56 +162,42 @@ func (c *ParamListCmd) Run(out io.Writer) error {
}
p := params[c.component]
return outputParamsFor(c.component, p, out)
outputParamsFor(c.component, p, out)
return nil
}
return outputParams(params, out)
outputParams(params, out)
return nil
}
func outputParamsFor(component string, params param.Params, out io.Writer) error {
func outputParamsFor(component string, params param.Params, out io.Writer) {
keys := sortedParams(params)
rows := [][]string{
[]string{paramNameHeader, paramValueHeader},
[]string{strings.Repeat("=", len(paramNameHeader)), strings.Repeat("=", len(paramValueHeader))},
}
t := table.New(out)
t.SetHeader([]string{paramNameHeader, paramValueHeader})
for _, k := range keys {
rows = append(rows, []string{k, params[k]})
t.Append([]string{k, params[k]})
}
formatted, err := str.PadRows(rows)
if err != nil {
return err
}
_, err = fmt.Fprint(out, formatted)
return err
t.Render()
}
func outputParams(params map[string]param.Params, out io.Writer) error {
func outputParams(params map[string]param.Params, out io.Writer) {
keys := sortedKeys(params)
rows := [][]string{
[]string{paramComponentHeader, paramNameHeader, paramValueHeader},
[]string{
strings.Repeat("=", len(paramComponentHeader)),
strings.Repeat("=", len(paramNameHeader)),
strings.Repeat("=", len(paramValueHeader))},
}
t := table.New(out)
t.SetHeader([]string{paramComponentHeader, paramNameHeader, paramValueHeader})
for _, k := range keys {
// sort params to display alphabetically
ps := sortedParams(params[k])
for _, p := range ps {
rows = append(rows, []string{k, p, params[k][p]})
t.Append([]string{k, p, params[k][p]})
}
}
formatted, err := str.PadRows(rows)
if err != nil {
return err
}
_, err = fmt.Fprint(out, formatted)
return err
t.Render()
}
// ----------------------------------------------------------------------------
......
// Copyright 2018 The ksonnet authors
//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package table
import (
"fmt"
"io"
"strings"
"github.com/pkg/errors"
)
const (
// sepChar is the character used to separate the header from the content in a table.
sepChar = "="
)
// Table creates an output table.
type Table struct {
w io.Writer
header []string
rows [][]string
}
// New creates an instance of table.
func New(w io.Writer) *Table {
return &Table{
w: w,
}
}
// SetHeader sets the header for the table.
func (t *Table) SetHeader(columns []string) {
t.header = columns
}
// Append appends a row to the table.
func (t *Table) Append(row []string) {
t.rows = append(t.rows, row)
}
// AppendBulk appends multiple rows to the table.
func (t *Table) AppendBulk(rows [][]string) {
t.rows = append(t.rows, rows...)
}
// Render writes the output to the table's writer.
func (t *Table) Render() error {
var output [][]string
if len(t.header) > 0 {
headerRow := make([]string, len(t.header), len(t.header))
sepRow := make([]string, len(t.header), len(t.header))
for i := range t.header {
sepLen := len(t.header[i])
headerRow[i] = strings.ToUpper(t.header[i])
sepRow[i] = strings.Repeat(sepChar, sepLen)
}
output = append(output, headerRow, sepRow)
}
output = append(output, t.rows...)
counts := colLens(output)
// print rows
for _, row := range output {
var parts []string
for i, col := range row {
val := col
if i < len(row)-1 {
format := fmt.Sprintf("%%-%ds", counts[i])
val = fmt.Sprintf(format, col)
}
parts = append(parts, val)
}
_, err := fmt.Fprintf(t.w, "%s\n", strings.Join(parts, " "))
if err != nil {
return errors.Wrap(err, "render table")
}
}
return nil
}
func colLens(rows [][]string) []int {
// count the number of columns
colCount := 0
for _, row := range rows {
if l := len(row); l > colCount {
colCount = l
}
}
// get the max len for each column
counts := make([]int, colCount, colCount)
for _, row := range rows {
for i := range row {
if l := len(row[i]); l > counts[i] {
counts[i] = l
}
}
}
return counts
}
// Copyright 2018 The ksonnet authors
//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package table
import (
"bytes"
"io/ioutil"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestTable(t *testing.T) {
var buf bytes.Buffer
table := New(&buf)
table.SetHeader([]string{"name", "version", "Namespace", "SERVER"})
table.Append([]string{"default", "v1.7.0", "default", "http://default"})
table.AppendBulk([][]string{
{"dev", "v1.8.0", "dev", "http://dev"},
{"east/prod", "v1.8.0", "east/prod", "http://east-prod"},
})
table.Render()
b, err := ioutil.ReadFile("testdata/table.txt")
require.NoError(t, err)
assert.Equal(t, string(b), buf.String())
}
func TestTable_no_header(t *testing.T) {
var buf bytes.Buffer
table := New(&buf)
table.Append([]string{"default", "v1.7.0", "default", "http://default"})
table.AppendBulk([][]string{
{"dev", "v1.8.0", "dev", "http://dev"},
{"east/prod", "v1.8.0", "east/prod", "http://east-prod"},
})
table.Render()
b, err := ioutil.ReadFile("testdata/table_no_header.txt")
require.NoError(t, err)
assert.Equal(t, string(b), buf.String())
}
NAME VERSION NAMESPACE SERVER
==== ======= ========= ======
default v1.7.0 default http://default
dev v1.8.0 dev http://dev
east/prod v1.8.0 east/prod http://east-prod
default v1.7.0 default http://default
dev v1.8.0 dev http://dev
east/prod v1.8.0 east/prod http://east-prod
......@@ -23,9 +23,8 @@ import (
"strings"
"github.com/blang/semver"
str "github.com/ksonnet/ksonnet/strings"
"github.com/ksonnet/ksonnet/pkg/util/table"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
//
......@@ -180,20 +179,17 @@ func (ss SpecificationSchemas) String() string {
sort.Slice(ss, func(i, j int) bool { return ss[i].Name < ss[j].Name })
rows := [][]string{
[]string{nameHeader, descriptionHeader},
[]string{strings.Repeat("=", len(nameHeader)), strings.Repeat("=", len(descriptionHeader))},
}
var buf bytes.Buffer
t := table.New(&buf)
t.SetHeader([]string{nameHeader, descriptionHeader})
for _, proto := range ss {
rows = append(rows, []string{proto.Name, proto.Template.ShortDescription})
t.Append([]string{proto.Name, proto.Template.ShortDescription})
}
formatted, err := str.PadRows(rows)
if err != nil {
log.Errorf("Failed to print spec rows:\n%v", err)
}
t.Render()
return formatted
return buf.String()
}
// RequiredParams retrieves all parameters that are required by a prototype.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment