Replace olekukonko/tablewriter with cheynewallace/tabby

- Closes #1497
- Closes #1498

This library is simpler and doesn't produce extraneous whitespace around
the output.

A local helper function restores "--" for empty output.

Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
Jonathon Anderson
2024-11-07 16:17:40 -07:00
parent 3cbb295e7b
commit a8554b0af5
11 changed files with 194 additions and 168 deletions

View File

@@ -56,6 +56,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Remove a 3-second sleep during iPXE boot. #1500
- Don't package the API in RPM packages by default. #1493
- Update default `warewulfd` port to match shipped configuration. #1448
- Replace `olekukonko/tablewriter` with `cheynewallace/tabby`. #1497, #1498
### Removed

4
go.mod
View File

@@ -5,6 +5,7 @@ go 1.21
require (
dario.cat/mergo v1.0.0
github.com/Masterminds/sprig/v3 v3.2.3
github.com/cheynewallace/tabby v1.1.1
github.com/containers/image/v5 v5.30.1
github.com/containers/storage v1.55.0
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
@@ -17,7 +18,6 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1
github.com/hashicorp/go-version v1.7.0
github.com/manifoldco/promptui v0.9.0
github.com/olekukonko/tablewriter v0.0.5
github.com/opencontainers/image-spec v1.1.0
github.com/opencontainers/umoci v0.4.7
github.com/pkg/errors v0.9.1
@@ -91,7 +91,7 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect

9
go.sum
View File

@@ -40,6 +40,8 @@ github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheynewallace/tabby v1.1.1 h1:JvUR8waht4Y0S3JF17G6Vhyt+FRhnqVCkk8l4YrOU54=
github.com/cheynewallace/tabby v1.1.1/go.mod h1:Pba/6cUL8uYqvOc9RkyvFbHGrQ9wShyrn6/S/1OYVys=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
@@ -301,9 +303,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
@@ -340,8 +341,6 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=

View File

@@ -5,17 +5,18 @@ import (
"time"
"github.com/spf13/cobra"
"github.com/warewulf/warewulf/internal/app/wwctl/helper"
"github.com/warewulf/warewulf/internal/app/wwctl/table"
apicontainer "github.com/warewulf/warewulf/internal/pkg/api/container"
"github.com/warewulf/warewulf/internal/pkg/container"
"github.com/warewulf/warewulf/internal/pkg/util"
"github.com/warewulf/warewulf/internal/pkg/wwlog"
)
var containerList = apicontainer.ContainerList
func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err error) {
return func(cmd *cobra.Command, args []string) (err error) {
t := table.New(cmd.OutOrStdout())
showSize := vars.size || vars.chroot || vars.compressed
if showSize || vars.full || vars.kernel {
containerInfo, err := containerList()
@@ -23,7 +24,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
return err
}
if vars.full {
ph := helper.New([]string{"CONTAINER NAME", "NODES", "KERNEL VERSION", "CREATION TIME", "MODIFICATION TIME", "SIZE"})
t.AddHeader("CONTAINER NAME", "NODES", "KERNEL VERSION", "CREATION TIME", "MODIFICATION TIME", "SIZE")
for i := 0; i < len(containerInfo); i++ {
createTime := time.Unix(int64(containerInfo[i].CreateDate), 0)
modTime := time.Unix(int64(containerInfo[i].ModDate), 0)
@@ -34,30 +35,26 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
if vars.chroot {
sz = util.ByteToString(int64(containerInfo[i].Size))
}
ph.Append([]string{
t.AddLine(
containerInfo[i].Name,
strconv.FormatUint(uint64(containerInfo[i].NodeCount), 10),
containerInfo[i].KernelVersion,
createTime.Format(time.RFC822),
modTime.Format(time.RFC822),
sz,
})
)
}
ph.Render()
wwlog.Info("%s", ph.String())
} else if vars.kernel {
ph := helper.New([]string{"CONTAINER NAME", "NODES", "KERNEL VERSION"})
t.AddHeader("CONTAINER NAME", "NODES", "KERNEL VERSION")
for i := 0; i < len(containerInfo); i++ {
ph.Append([]string{
t.AddLine(
containerInfo[i].Name,
strconv.FormatUint(uint64(containerInfo[i].NodeCount), 10),
containerInfo[i].KernelVersion,
})
)
}
ph.Render()
wwlog.Info("%s", ph.String())
} else if showSize {
ph := helper.New([]string{"CONTAINER NAME", "NODES", "SIZE"})
t.AddHeader("CONTAINER NAME", "NODES", "SIZE")
for i := 0; i < len(containerInfo); i++ {
sz := util.ByteToString(int64(containerInfo[i].ImgSize))
if vars.compressed {
@@ -67,24 +64,21 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
sz = util.ByteToString(int64(containerInfo[i].Size))
}
ph.Append([]string{
t.AddLine(
containerInfo[i].Name,
strconv.FormatUint(uint64(containerInfo[i].NodeCount), 10),
sz,
})
)
}
ph.Render()
wwlog.Info("%s", ph.String())
}
} else {
ph := helper.New([]string{"CONTAINER NAME"})
t.AddHeader("CONTAINER NAME")
list, _ := container.ListSources()
for _, cont := range list {
ph.Append([]string{cont})
t.AddLine(cont)
}
ph.Render()
wwlog.Info("%s", ph.String())
}
t.Print()
return
}
}

View File

@@ -24,8 +24,11 @@ func Test_List(t *testing.T) {
{
name: "container list test",
args: []string{"-l"},
stdout: ` CONTAINER NAME NODES KERNEL VERSION CREATION TIME MODIFICATION TIME SIZE
test 1 kernel`,
stdout: `
CONTAINER NAME NODES KERNEL VERSION CREATION TIME MODIFICATION TIME SIZE
-------------- ----- -------------- ------------- ----------------- ----
test 1 kernel 01 Jan 70 00:00 UTC 01 Jan 70 00:00 UTC 0 B
`,
inDb: `WW_INTERNAL: 43
nodeprofiles:
default: {}
@@ -61,14 +64,12 @@ nodes:
buf := new(bytes.Buffer)
baseCmd := GetCommand()
baseCmd.SetArgs(tt.args)
baseCmd.SetOut(nil)
baseCmd.SetErr(nil)
baseCmd.SetOut(buf)
baseCmd.SetErr(buf)
wwlog.SetLogWriter(buf)
err := baseCmd.Execute()
assert.NoError(t, err)
assert.Contains(t,
strings.Join(strings.Fields(buf.String()), ""),
strings.Join(strings.Fields(tt.stdout), ""))
assert.Equal(t, strings.TrimSpace(tt.stdout), strings.TrimSpace(buf.String()))
})
}
}

View File

@@ -1,36 +0,0 @@
package helper
import (
"regexp"
"strings"
"github.com/olekukonko/tablewriter"
)
type PrintHelper struct {
*tablewriter.Table
sb *strings.Builder
}
func New(header []string) *PrintHelper {
sb := &strings.Builder{}
tb := tablewriter.NewWriter(sb)
tb.SetHeader(header)
tb.SetAutoWrapText(false)
tb.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
tb.SetAlignment(tablewriter.ALIGN_LEFT)
tb.SetCenterSeparator("")
tb.SetColumnSeparator("")
tb.SetRowSeparator("")
tb.SetHeaderLine(false)
tb.SetBorder(false)
return &PrintHelper{
Table: tb,
sb: sb,
}
}
func (p *PrintHelper) String() string {
exp := regexp.MustCompile("(?m) *$")
return string(exp.ReplaceAll([]byte(p.sb.String()), []byte("")))
}

View File

@@ -4,7 +4,8 @@ import (
"strings"
"github.com/spf13/cobra"
"github.com/warewulf/warewulf/internal/app/wwctl/helper"
"github.com/warewulf/warewulf/internal/app/wwctl/table"
apinode "github.com/warewulf/warewulf/internal/pkg/api/node"
"github.com/warewulf/warewulf/internal/pkg/api/routes/wwapiv1"
"github.com/warewulf/warewulf/internal/pkg/wwlog"
@@ -38,12 +39,12 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
if req.Type == wwapiv1.GetNodeList_YAML || req.Type == wwapiv1.GetNodeList_JSON {
wwlog.Info(nodeInfo.Output[0])
} else {
ph := helper.New(strings.Split(nodeInfo.Output[0], ":=:"))
t := table.New(cmd.OutOrStdout())
t.AddHeader(table.Prep(strings.Split(nodeInfo.Output[0], ":=:"))...)
for _, val := range nodeInfo.Output[1:] {
ph.Append(strings.Split(val, ":=:"))
t.AddLine(table.Prep(strings.Split(val, ":=:"))...)
}
ph.Render()
wwlog.Info("%s", ph.String())
t.Print()
}
}
return

View File

@@ -28,8 +28,10 @@ func Test_List(t *testing.T) {
name: "single node list",
args: []string{},
wantErr: false,
stdout: ` NODE NAME PROFILES NETWORK
n01 [default]
stdout: `
NODE NAME PROFILES NETWORK
--------- -------- -------
n01 [default] --
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -44,9 +46,11 @@ nodes:
name: "multiple nodes list",
args: []string{},
wantErr: false,
stdout: ` NODE NAME PROFILES NETWORK
n01 [default]
n02 [default]
stdout: `
NODE NAME PROFILES NETWORK
--------- -------- -------
n01 [default] --
n02 [default] --
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -64,9 +68,11 @@ nodes:
name: "node list returns multiple nodes",
args: []string{"n01,n02"},
wantErr: false,
stdout: ` NODE NAME PROFILES NETWORK
n01 [default]
n02 [default]
stdout: `
NODE NAME PROFILES NETWORK
--------- -------- -------
n01 [default] --
n02 [default] --
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -84,9 +90,11 @@ nodes:
name: "node list returns multiple nodes (case 2)",
args: []string{"n01,n03"},
wantErr: false,
stdout: ` NODE NAME PROFILES NETWORK
n01 [default]
n03 [default]
stdout: `
NODE NAME PROFILES NETWORK
--------- -------- -------
n01 [default] --
n03 [default] --
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -113,8 +121,10 @@ nodes:
name: "node list returns one node",
args: []string{"n01,"},
wantErr: false,
stdout: ` NODE NAME PROFILES NETWORK
n01 [default]
stdout: `
NODE NAME PROFILES NETWORK
--------- -------- -------
n01 [default] --
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -132,8 +142,10 @@ nodes:
name: "node list profile with network",
args: []string{},
wantErr: false,
stdout: ` NODE NAME PROFILES NETWORK
n01 [default] default
stdout: `
NODE NAME PROFILES NETWORK
--------- -------- -------
n01 [default] default
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -150,9 +162,11 @@ nodes:
name: "node list profile with comment",
args: []string{"-a"},
wantErr: false,
stdout: ` NODE FIELD PROFILE VALUE
n01 Comment default profilecomment
n01 Profiles default
stdout: `
NODE FIELD PROFILE VALUE
---- ----- ------- -----
n01 Comment default profilecomment
n01 Profiles -- default
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -167,9 +181,11 @@ nodes:
name: "node list profile with comment superseded",
args: []string{"-a"},
wantErr: false,
stdout: ` NODE FIELD PROFILE VALUE
n01 Comment SUPERSEDED nodecomment
n01 Profiles default
stdout: `
NODE FIELD PROFILE VALUE
---- ----- ------- -----
n01 Comment SUPERSEDED nodecomment
n01 Profiles -- default
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -185,8 +201,10 @@ nodes:
name: "node list profile with ipmi user",
args: []string{"-i"},
wantErr: false,
stdout: ` NODE IPMI IPADDR IPMI PORT IPMI USERNAME IPMI INTERFACE
n01 <nil> admin
stdout: `
NODE IPMI IPADDR IPMI PORT IPMI USERNAME IPMI INTERFACE
---- ----------- --------- ------------- --------------
n01 <nil> -- admin --
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -202,8 +220,10 @@ nodes:
name: "node list profile with ipmi user superseded",
args: []string{"-i"},
wantErr: false,
stdout: ` NODE IPMI IPADDR IPMI PORT IPMI USERNAME IPMI INTERFACE
n01 <nil> user
stdout: `
NODE IPMI IPADDR IPMI PORT IPMI USERNAME IPMI INTERFACE
---- ----------- --------- ------------- --------------
n01 <nil> -- user --
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -221,8 +241,10 @@ nodes:
name: "multiple profiles list",
args: []string{},
wantErr: false,
stdout: ` NODE NAME PROFILES NETWORK
n01 [p1 p2]
stdout: `
NODE NAME PROFILES NETWORK
--------- -------- -------
n01 [p1 p2] --
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -238,8 +260,10 @@ nodes:
name: "multiple profiles list all",
args: []string{"-a"},
wantErr: false,
stdout: ` NODE FIELD PROFILE VALUE
n01 Profiles p1,p2
stdout: `
NODE FIELD PROFILE VALUE
---- ----- ------- -----
n01 Profiles -- p1,p2
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -255,8 +279,10 @@ nodes:
name: "multiple overlays list long with negation",
args: []string{"-l"},
wantErr: false,
stdout: ` NODE NAME KERNEL OVERRIDE CONTAINER OVERLAYS (S/R)
n01 /rop1,rop2
stdout: `
NODE NAME KERNEL OVERRIDE CONTAINER OVERLAYS (S/R)
--------- --------------- --------- --------------
n01 -- -- /rop1,rop2
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -273,8 +299,10 @@ nodes:
name: "multiple overlays list long",
args: []string{"-l"},
wantErr: false,
stdout: ` NODE NAME KERNEL OVERRIDE CONTAINER OVERLAYS (S/R)
n01 sop1/nop1,~rop1,rop1,rop2
stdout: `
NODE NAME KERNEL OVERRIDE CONTAINER OVERLAYS (S/R)
--------- --------------- --------- --------------
n01 -- -- sop1/nop1,~rop1,rop1,rop2
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -296,10 +324,12 @@ nodes:
name: "multiple overlays list all with negation",
args: []string{"-a"},
wantErr: false,
stdout: ` NODE FIELD PROFILE VALUE
n01 Profiles p1
n01 RuntimeOverlay p1+ nop1,~rop1,rop1,rop2
n01 SystemOverlay p1 sop1
stdout: `
NODE FIELD PROFILE VALUE
---- ----- ------- -----
n01 Profiles -- p1
n01 RuntimeOverlay p1+ nop1,~rop1,rop1,rop2
n01 SystemOverlay p1 sop1
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -321,9 +351,11 @@ nodes:
name: "multiple overlays list all",
args: []string{"-a"},
wantErr: false,
stdout: ` NODE FIELD PROFILE VALUE
n01 Profiles p1
n01 RuntimeOverlay p1+ nop1,rop1,rop2
stdout: `
NODE FIELD PROFILE VALUE
---- ----- ------- -----
n01 Profiles -- p1
n01 RuntimeOverlay p1+ nop1,rop1,rop2
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
@@ -342,8 +374,10 @@ nodes:
name: "network onboot",
args: []string{"-a"},
wantErr: false,
stdout: ` NODE FIELD PROFILE VALUE
n1 NetDevs[default].OnBoot true
stdout: `
NODE FIELD PROFILE VALUE
---- ----- ------- -----
n1 NetDevs[default].OnBoot -- true
`,
inDb: `WW_INTERNAL: 45
nodes:
@@ -356,7 +390,9 @@ nodes:
name: "empty network device",
args: []string{"-a"},
wantErr: false,
stdout: ` NODE FIELD PROFILE VALUE
stdout: `
NODE FIELD PROFILE VALUE
---- ----- ------- -----
`,
inDb: `WW_INTERNAL: 46
nodes:
@@ -393,13 +429,14 @@ nodes:
baseCmd := GetCommand()
baseCmd.SetArgs(tt.args)
buf := new(bytes.Buffer)
baseCmd.SetOut(buf)
baseCmd.SetErr(buf)
wwlog.SetLogWriter(buf)
wwlog.SetLogWriterErr(buf)
wwlog.SetLogWriterInfo(buf)
err := baseCmd.Execute()
assert.NoError(t, err)
assert.NotEmpty(t, buf, "output should not be empty")
assert.Equal(t, tt.stdout, buf.String())
assert.Equal(t, strings.TrimSpace(tt.stdout), strings.TrimSpace(buf.String()))
})
}
@@ -492,9 +529,7 @@ nodes:
} else {
assert.NoError(t, err)
}
assert.Contains(t,
strings.Join(strings.Fields(tt.stdout), ""),
strings.Join(strings.Fields(buf.String()), ""))
assert.Equal(t, strings.TrimSpace(buf.String()), strings.TrimSpace(tt.stdout))
})
}
}

View File

@@ -3,12 +3,12 @@ package list
import (
"strings"
"github.com/warewulf/warewulf/internal/app/wwctl/helper"
apiprofile "github.com/warewulf/warewulf/internal/pkg/api/profile"
"github.com/warewulf/warewulf/internal/pkg/wwlog"
"github.com/spf13/cobra"
"github.com/warewulf/warewulf/internal/app/wwctl/table"
apiprofile "github.com/warewulf/warewulf/internal/pkg/api/profile"
"github.com/warewulf/warewulf/internal/pkg/api/routes/wwapiv1"
"github.com/warewulf/warewulf/internal/pkg/wwlog"
)
func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err error) {
@@ -30,12 +30,12 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
if vars.showYaml || vars.showJson {
wwlog.Info(profileInfo.Output[0])
} else {
ph := helper.New(strings.Split(profileInfo.Output[0], ":=:"))
t := table.New(cmd.OutOrStdout())
t.AddHeader(table.Prep(strings.Split(profileInfo.Output[0], ":=:"))...)
for _, val := range profileInfo.Output[1:] {
ph.Append(strings.Split(val, ":=:"))
t.AddLine(table.Prep(strings.Split(val, ":=:"))...)
}
ph.Render()
wwlog.Info("%s", ph.String())
t.Print()
}
}
return

View File

@@ -4,9 +4,9 @@ import (
"bytes"
"io"
"os"
"strings"
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
"github.com/warewulf/warewulf/internal/pkg/node"
@@ -24,8 +24,11 @@ func Test_List(t *testing.T) {
{
name: "profile list test",
args: []string{},
stdout: `PROFILE NAME COMMENT/DESCRIPTION
default`,
stdout: `
PROFILE NAME COMMENT/DESCRIPTION
------------ -------------------
default --
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
default: {}
@@ -38,9 +41,12 @@ nodes:
{
name: "profile list returns multiple profiles",
args: []string{"default,test"},
stdout: `PROFILE NAME COMMENT/DESCRIPTION
default
test`,
stdout: `
PROFILE NAME COMMENT/DESCRIPTION
------------ -------------------
default --
test --
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
default: {}
@@ -54,8 +60,11 @@ nodes:
{
name: "profile list returns one profile",
args: []string{"test,"},
stdout: `PROFILE NAME COMMENT/DESCRIPTION
test`,
stdout: `
PROFILE NAME COMMENT/DESCRIPTION
------------ -------------------
test --
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
default: {}
@@ -69,9 +78,12 @@ nodes:
{
name: "profile list returns all profiles",
args: []string{","},
stdout: `PROFILE NAME COMMENT/DESCRIPTION
default
test`,
stdout: `
PROFILE NAME COMMENT/DESCRIPTION
------------ -------------------
default --
test --
`,
inDb: `WW_INTERNAL: 45
nodeprofiles:
default: {}
@@ -111,7 +123,26 @@ nodes:
t.Run(tt.name, func(t *testing.T) {
baseCmd := GetCommand()
baseCmd.SetArgs(tt.args)
verifyOutput(t, baseCmd, tt.stdout)
stdoutR, stdoutW, _ := os.Pipe()
oriout := os.Stdout
os.Stdout = stdoutW
wwlog.SetLogWriter(os.Stdout)
baseCmd.SetOut(os.Stdout)
baseCmd.SetErr(os.Stdout)
err := baseCmd.Execute()
assert.NoError(t, err)
stdoutC := make(chan string)
go func() {
var buf bytes.Buffer
_, _ = io.Copy(&buf, stdoutR)
stdoutC <- buf.String()
}()
stdoutW.Close()
os.Stdout = oriout
stdout := <-stdoutC
assert.Equal(t, strings.TrimSpace(tt.stdout), strings.TrimSpace(stdout))
})
}
}
@@ -219,27 +250,3 @@ nodes:
})
}
}
func verifyOutput(t *testing.T, baseCmd *cobra.Command, content string) {
stdoutR, stdoutW, _ := os.Pipe()
oriout := os.Stdout
os.Stdout = stdoutW
wwlog.SetLogWriter(os.Stdout)
baseCmd.SetOut(os.Stdout)
baseCmd.SetErr(os.Stdout)
err := baseCmd.Execute()
assert.NoError(t, err)
stdoutC := make(chan string)
go func() {
var buf bytes.Buffer
_, _ = io.Copy(&buf, stdoutR)
stdoutC <- buf.String()
}()
stdoutW.Close()
os.Stdout = oriout
stdout := <-stdoutC
assert.NotEmpty(t, stdout)
assert.Contains(t, stdout, content)
}

View File

@@ -0,0 +1,24 @@
package table
import (
"io"
"text/tabwriter"
"github.com/cheynewallace/tabby"
)
func Prep(parts []string) []interface{} {
args := make([]interface{}, len(parts))
for i, v := range parts {
if v == "" {
args[i] = "--"
} else {
args[i] = v
}
}
return args
}
func New(writer io.Writer) *tabby.Tabby {
return tabby.NewCustom(tabwriter.NewWriter(writer, 0, 0, 2, ' ', 0))
}