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:
@@ -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
4
go.mod
@@ -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
9
go.sum
@@ -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=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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("")))
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
24
internal/app/wwctl/table/table.go
Normal file
24
internal/app/wwctl/table/table.go
Normal 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))
|
||||
}
|
||||
Reference in New Issue
Block a user