Skip to content

Commit dcef214

Browse files
committed
Replace olekukonko/tablewriter with text/tabwriter
Drop external tablewriter dependency in favor of Go's built-in text/tabwriter package to reduce dependencies and simplify maintenance. Changes: - Replace tablewriter.NewWriter() with tabwriter.NewWriter() in all table display functions - Update table formatting logic to use tab-separated output with headers and separator lines - Remove olekukonko/tablewriter and related dependencies from go.mod - Update test expectations to match new table output format - Fix test line number references after table format changes All tests pass with the new implementation. Assisted-by: Claude AI for dependency replacement and test updates Signed-off-by: Adrian Reber <areber@redhat.com>
1 parent 6e6a8c6 commit dcef214

48 files changed

Lines changed: 134 additions & 19243 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,25 @@ To display an overview of a checkpoint archive you can just use
3030
```console
3131
$ checkpointctl show /tmp/dump.tar
3232

33-
+-----------------+------------------------------------------+--------------+---------+----------------------+--------+------------+-------------------+
34-
| CONTAINER | IMAGE | ID | RUNTIME | CREATED | ENGINE | CHKPT SIZE | ROOT FS DIFF SIZE |
35-
+-----------------+------------------------------------------+--------------+---------+----------------------+--------+------------+-------------------+
36-
| magical_murdock | quay.io/adrianreber/wildfly-hello:latest | f11d11844af0 | crun | 2023-02-28T09:43:52Z | Podman | 338.2 MiB | 177.0 KiB |
37-
+-----------------+------------------------------------------+--------------+---------+----------------------+--------+------------+-------------------+
33+
Displaying container checkpoint data from /root/dump.tar
34+
35+
Container Image ID Runtime Created Engine CHKPT Size Root Fs Diff Size
36+
--------- ----- -- ------- ------- ------ ---------- -----------------
37+
looper docker.io/library/busybox:latest 8b5c2ca15082 crun 2021-09-28T10:03:56Z Podman 130.8 KiB 204 B
3838
```
3939

4040
For a checkpoint archive created by Kubernetes with *CRI-O* the output would
4141
look like this:
4242

4343
```console
44-
$ checkpointctl show /var/lib/kubelet/checkpoints/checkpoint-counters_default-counter-2023-02-13T16\:20\:09Z.tar
44+
$ checkpointctl show /var/lib/kubelet/checkpoints/checkpoint-counters_default-counter-2025-05-22T14\:31\:35Z.tar
45+
46+
Displaying container checkpoint data from /var/lib/kubelet/checkpoints/checkpoint-counters_default-counter-2025-05-22T14:31:35Z.tar
47+
48+
Container Image ID Runtime Created Engine IP CHKPT Size Root Fs Diff Size
49+
--------- ----- -- ------- ------- ------ -- ---------- -----------------
50+
counter quay.io/adrianreber/counter:latest 29ed106ef467 runc 2025-05-22T14:31:24.818422898Z CRI-O 10.0.0.70 9.2 MiB 2.0 KiB
4551

46-
+-----------+------------------------------------+--------------+---------+--------------------------------+--------+------------+------------+
47-
| CONTAINER | IMAGE | ID | RUNTIME | CREATED | ENGINE | IP | CHKPT SIZE |
48-
+-----------+------------------------------------+--------------+---------+--------------------------------+--------+------------+------------+
49-
| counter | quay.io/adrianreber/counter:latest | 7eb9680287f1 | runc | 2023-02-13T16:12:25.843774934Z | CRI-O | 10.88.0.24 | 8.5 MiB |
50-
+-----------+------------------------------------+--------------+---------+--------------------------------+--------+------------+------------+
5152
```
5253

5354
### `inspect` sub-command
@@ -145,13 +146,10 @@ $ sudo checkpointctl memparse /tmp/jira.tar.gz
145146

146147
Displaying processes memory sizes from /tmp/jira.tar.gz
147148

148-
+-----+--------------+-------------+
149-
| PID | PROCESS NAME | MEMORY SIZE |
150-
+-----+--------------+-------------+
151-
| 1 | tini | 100.0 KiB |
152-
+-----+--------------+-------------+
153-
| 2 | java | 553.5 MiB |
154-
+-----+--------------+-------------+
149+
PID Process name Memory size Shared memory size
150+
--- ------------ ----------- ------------------
151+
1 tini 100.0 KiB 0 B
152+
2 java 553.5 MiB 0 B
155153
```
156154

157155
In this example, given the large size of the java process, it is better to write its output to a file.

cmd/list.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import (
99
"log"
1010
"os"
1111
"path/filepath"
12+
"text/tabwriter"
1213
"time"
1314

1415
"github.com/checkpoint-restore/checkpointctl/internal"
15-
"github.com/olekukonko/tablewriter"
1616
"github.com/spf13/cobra"
1717
)
1818

@@ -38,7 +38,7 @@ func list(cmd *cobra.Command, args []string) error {
3838
}()
3939
showTable := false
4040

41-
table := tablewriter.NewWriter(os.Stdout)
41+
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
4242
header := []string{
4343
"Namespace",
4444
"Pod",
@@ -48,9 +48,7 @@ func list(cmd *cobra.Command, args []string) error {
4848
"Checkpoint Name",
4949
}
5050

51-
table.SetHeader(header)
52-
table.SetAutoMergeCells(false)
53-
table.SetRowLine(true)
51+
var rows [][]string
5452

5553
for _, checkpointPath := range allPaths {
5654
files, err := filepath.Glob(filepath.Join(checkpointPath, "checkpoint-*"))
@@ -81,7 +79,7 @@ func list(cmd *cobra.Command, args []string) error {
8179
filepath.Base(file),
8280
}
8381

84-
table.Append(row)
82+
rows = append(rows, row)
8583
}
8684
}
8785

@@ -90,6 +88,19 @@ func list(cmd *cobra.Command, args []string) error {
9088
return nil
9189
}
9290

93-
table.Render()
91+
internal.WriteTableHeader(w, header)
92+
93+
// Print rows
94+
for _, row := range rows {
95+
for i, cell := range row {
96+
if i > 0 {
97+
fmt.Fprint(w, "\t")
98+
}
99+
fmt.Fprint(w, cell)
100+
}
101+
fmt.Fprintln(w)
102+
}
103+
104+
w.Flush()
94105
return nil
95106
}

cmd/memparse.go

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import (
1010
"io"
1111
"os"
1212
"path/filepath"
13+
"text/tabwriter"
1314

1415
"github.com/checkpoint-restore/checkpointctl/internal"
1516
metadata "github.com/checkpoint-restore/checkpointctl/lib"
1617
"github.com/checkpoint-restore/go-criu/v7/crit"
17-
"github.com/olekukonko/tablewriter"
1818
"github.com/spf13/cobra"
1919
)
2020

@@ -116,21 +116,16 @@ func memparse(cmd *cobra.Command, args []string) error {
116116

117117
// Display processes memory sizes within the given container checkpoints.
118118
func showProcessMemorySizeTables(tasks []internal.Task) error {
119-
// Initialize the table
120-
table := tablewriter.NewWriter(os.Stdout)
121119
header := []string{
122120
"PID",
123121
"Process name",
124122
"Memory size",
125123
"Shared memory size",
126124
}
127-
table.SetHeader(header)
128-
table.SetAutoMergeCells(false)
129-
table.SetRowLine(true)
130125

131126
// Function to recursively traverse the process tree and populate the table rows
132-
var traverseTree func(*crit.PsTree, string) error
133-
traverseTree = func(root *crit.PsTree, checkpointOutputDir string) error {
127+
var traverseTree func(*crit.PsTree, string, *[][]string) error
128+
traverseTree = func(root *crit.PsTree, checkpointOutputDir string, rows *[][]string) error {
134129
memReader, err := crit.NewMemoryReader(
135130
filepath.Join(checkpointOutputDir, metadata.CheckpointDirectory),
136131
root.PID, pageSize,
@@ -152,24 +147,25 @@ func showProcessMemorySizeTables(tasks []internal.Task) error {
152147
return err
153148
}
154149

155-
table.Append([]string{
150+
row := []string{
156151
fmt.Sprintf("%d", root.PID),
157152
root.Comm,
158153
metadata.ByteToString(memSize),
159154
metadata.ByteToString(shmemSize),
160-
})
155+
}
156+
*rows = append(*rows, row)
161157

162158
for _, child := range root.Children {
163-
if err := traverseTree(child, checkpointOutputDir); err != nil {
159+
if err := traverseTree(child, checkpointOutputDir, rows); err != nil {
164160
return err
165161
}
166162
}
167163
return nil
168164
}
169165

170166
for _, task := range tasks {
171-
// Clear the table before processing each checkpoint task
172-
table.ClearRows()
167+
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
168+
var rows [][]string
173169

174170
c := crit.New(nil, nil, filepath.Join(task.OutputDir, "checkpoint"), false, false)
175171
psTree, err := c.ExplorePs()
@@ -178,12 +174,26 @@ func showProcessMemorySizeTables(tasks []internal.Task) error {
178174
}
179175

180176
// Populate the table rows
181-
if err := traverseTree(psTree, task.OutputDir); err != nil {
177+
if err := traverseTree(psTree, task.OutputDir, &rows); err != nil {
182178
return err
183179
}
184180

185181
fmt.Printf("\nDisplaying processes memory sizes from %s\n\n", task.CheckpointFilePath)
186-
table.Render()
182+
183+
internal.WriteTableHeader(w, header)
184+
185+
// Print rows
186+
for _, row := range rows {
187+
for i, cell := range row {
188+
if i > 0 {
189+
fmt.Fprint(w, "\t")
190+
}
191+
fmt.Fprint(w, cell)
192+
}
193+
fmt.Fprintln(w)
194+
}
195+
196+
w.Flush()
187197
}
188198

189199
return nil
@@ -348,21 +358,27 @@ func printMemorySearchResultForPID(task internal.Task) error {
348358
return nil
349359
}
350360

351-
table := tablewriter.NewWriter(os.Stdout)
352-
table.SetHeader([]string{"Address", "Match", "Instance"})
353-
table.SetAutoMergeCells(false)
354-
table.SetRowLine(true)
361+
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
362+
header := []string{"Address", "Match", "Instance"}
363+
364+
internal.WriteTableHeader(w, header)
355365

366+
// Print rows
356367
for i, result := range results {
357-
table.Append([]string{
358-
fmt.Sprintf(
359-
"%016x", result.Vaddr),
368+
row := []string{
369+
fmt.Sprintf("%016x", result.Vaddr),
360370
result.Match,
361371
fmt.Sprintf("%d", i+1),
362-
})
372+
}
373+
for j, cell := range row {
374+
if j > 0 {
375+
fmt.Fprint(w, "\t")
376+
}
377+
fmt.Fprint(w, cell)
378+
}
379+
fmt.Fprintln(w)
363380
}
364381

365-
table.Render()
366-
382+
w.Flush()
367383
return nil
368384
}

go.mod

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ toolchain go1.24.2
77
require (
88
github.com/checkpoint-restore/go-criu/v7 v7.2.0
99
github.com/containers/storage v1.58.0
10-
github.com/olekukonko/tablewriter v0.0.5
1110
github.com/opencontainers/runtime-spec v1.2.1
1211
github.com/spf13/cobra v1.9.1
1312
github.com/xlab/treeprint v1.2.0
@@ -18,11 +17,9 @@ require (
1817
github.com/inconshreveable/mousetrap v1.1.0 // indirect
1918
github.com/klauspost/compress v1.18.0 // indirect
2019
github.com/klauspost/pgzip v1.2.6 // indirect
21-
github.com/mattn/go-runewidth v0.0.16 // indirect
2220
github.com/moby/sys/capability v0.4.0 // indirect
2321
github.com/moby/sys/mountinfo v0.7.2 // indirect
2422
github.com/moby/sys/user v0.4.0 // indirect
25-
github.com/rivo/uniseg v0.4.7 // indirect
2623
github.com/sirupsen/logrus v1.9.3 // indirect
2724
github.com/spf13/pflag v1.0.6 // indirect
2825
github.com/ulikunitz/xz v0.5.12 // indirect

go.sum

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,16 @@ github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zt
1616
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
1717
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
1818
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
19-
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
20-
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
21-
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
2219
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
2320
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
2421
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
2522
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
2623
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
2724
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
28-
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
29-
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
3025
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
3126
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
3227
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
3328
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
34-
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
35-
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
36-
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
3729
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
3830
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
3931
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=

internal/container.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ import (
1313
"os"
1414
"path/filepath"
1515
"strings"
16+
"text/tabwriter"
1617
"time"
1718

1819
metadata "github.com/checkpoint-restore/checkpointctl/lib"
1920
"github.com/checkpoint-restore/go-criu/v7/crit"
2021
"github.com/containers/storage/pkg/archive"
21-
"github.com/olekukonko/tablewriter"
2222
spec "github.com/opencontainers/runtime-spec/specs-go"
2323
)
2424

@@ -107,7 +107,8 @@ func getCheckpointInfo(task Task) (*checkpointInfo, error) {
107107
}
108108

109109
func ShowContainerCheckpoints(tasks []Task) error {
110-
table := tablewriter.NewWriter(os.Stdout)
110+
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
111+
111112
header := []string{
112113
"Container",
113114
"Image",
@@ -121,6 +122,8 @@ func ShowContainerCheckpoints(tasks []Task) error {
121122
header = append(header, "IP", "MAC", "CHKPT Size", "Root Fs Diff Size")
122123
}
123124

125+
var rows [][]string
126+
124127
for _, task := range tasks {
125128
info, err := getCheckpointInfo(task)
126129
if err != nil {
@@ -167,14 +170,23 @@ func ShowContainerCheckpoints(tasks []Task) error {
167170
row = append(row, metadata.ByteToString(info.archiveSizes.rootFsDiffTarSize))
168171
}
169172

170-
table.Append(row)
173+
rows = append(rows, row)
171174
}
172175

173-
table.SetHeader(header)
174-
table.SetAutoMergeCells(false)
175-
table.SetRowLine(true)
176-
table.Render()
176+
WriteTableHeader(w, header)
177+
178+
// Print rows
179+
for _, row := range rows {
180+
for i, cell := range row {
181+
if i > 0 {
182+
fmt.Fprint(w, "\t")
183+
}
184+
fmt.Fprint(w, cell)
185+
}
186+
fmt.Fprintln(w)
187+
}
177188

189+
w.Flush()
178190
return nil
179191
}
180192

internal/utils.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"strings"
7+
"text/tabwriter"
78
"time"
89

910
metadata "github.com/checkpoint-restore/checkpointctl/lib"
@@ -82,3 +83,24 @@ func CleanupTasks(tasks []Task) {
8283
}
8384
}
8485
}
86+
87+
// WriteTableHeader writes the header row and separator line for a table
88+
func WriteTableHeader(w *tabwriter.Writer, header []string) {
89+
// Print header
90+
for i, h := range header {
91+
if i > 0 {
92+
fmt.Fprint(w, "\t")
93+
}
94+
fmt.Fprint(w, h)
95+
}
96+
fmt.Fprintln(w)
97+
98+
// Print separator line
99+
for i := range header {
100+
if i > 0 {
101+
fmt.Fprint(w, "\t")
102+
}
103+
fmt.Fprint(w, strings.Repeat("-", len(header[i])))
104+
}
105+
fmt.Fprintln(w)
106+
}

0 commit comments

Comments
 (0)