1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
package internal
import (
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"time"
metadata "github.com/checkpoint-restore/checkpointctl/lib"
)
func FormatTime(microseconds uint32) string {
if microseconds < 1000 {
return fmt.Sprintf("%d µs", microseconds)
}
var value float64
var unit string
if microseconds < 1000000 {
value = float64(microseconds) / 1000
unit = "ms"
} else {
duration := time.Duration(microseconds) * time.Microsecond
value = duration.Seconds()
unit = "s"
}
// Trim trailing zeros and dot
formatted := strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.5g", value), "0"), ".")
return fmt.Sprintf("%s %s", formatted, unit)
}
type Task struct {
CheckpointFilePath string
OutputDir string
}
func CreateTasks(args []string, requiredFiles []string) ([]Task, error) {
tasks := make([]Task, 0, len(args))
for _, input := range args {
tar, err := os.Stat(input)
if err != nil {
return nil, err
}
if !tar.Mode().IsRegular() {
return nil, fmt.Errorf("input %s not a regular file", input)
}
// Check if there is a checkpoint directory in the archive file
checkpointDirExists, err := isFileInArchive(input, metadata.CheckpointDirectory, true)
if err != nil {
return nil, err
}
if !checkpointDirExists {
return nil, fmt.Errorf("checkpoint directory is missing in the archive file: %s", input)
}
dir, err := os.MkdirTemp("", "checkpointctl")
if err != nil {
return nil, err
}
if err := UntarFiles(input, dir, requiredFiles); err != nil {
return nil, err
}
tasks = append(tasks, Task{CheckpointFilePath: input, OutputDir: dir})
}
return tasks, nil
}
// cleanupTasks removes all output directories of given tasks
func CleanupTasks(tasks []Task) {
for _, task := range tasks {
if err := os.RemoveAll(task.OutputDir); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
}
// Constant values are based on kubectl's tabwriter settings:
// https://github.com/kubernetes/cli-runtime/blob/master/pkg/printers/tabwriter.go
const (
tabwriterMinWidth = 6
tabwriterWidth = 4
tabwriterPadding = 3
tabwriterPadChar = ' '
tabwriterFlags = 0
)
// GetNewTabWriter returns a tabwriter that translates tabbed columns in input into properly aligned text.
func GetNewTabWriter(output io.Writer) *tabwriter.Writer {
return tabwriter.NewWriter(output, tabwriterMinWidth, tabwriterWidth, tabwriterPadding, tabwriterPadChar, tabwriterFlags)
}
// WriteTableHeader writes the header row and separator line for a table
func WriteTableHeader(w *tabwriter.Writer, header []string) {
// Print header
for i, h := range header {
if i > 0 {
fmt.Fprint(w, "\t")
}
fmt.Fprint(w, strings.ToUpper(h))
}
fmt.Fprintln(w)
// Print separator line
for i := range header {
if i > 0 {
fmt.Fprint(w, "\t")
}
fmt.Fprint(w, strings.Repeat("-", len(header[i])))
}
fmt.Fprintln(w)
}
// WriteTableRows writes the data rows for a table
func WriteTableRows(w *tabwriter.Writer, rows [][]string) {
for _, row := range rows {
for i, cell := range row {
if i > 0 {
fmt.Fprint(w, "\t")
}
fmt.Fprint(w, cell)
}
fmt.Fprintln(w)
}
}
|