-
Notifications
You must be signed in to change notification settings - Fork 0
/
prettyprint.go
203 lines (171 loc) · 5.03 KB
/
prettyprint.go
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package main
import (
"encoding/hex"
"fmt"
"io"
"sort"
"strings"
"github.com/docker/notary/client"
"github.com/docker/notary/trustmanager"
"github.com/docker/notary/tuf/data"
"github.com/olekukonko/tablewriter"
)
// returns a tablewriter
func getTable(headers []string, writer io.Writer) *tablewriter.Table {
table := tablewriter.NewWriter(writer)
table.SetBorder(false)
table.SetColumnSeparator(" ")
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetCenterSeparator("-")
table.SetAutoWrapText(false)
table.SetHeader(headers)
return table
}
// --- pretty printing certs ---
func truncateWithEllipsis(str string, maxWidth int, leftTruncate bool) string {
if len(str) <= maxWidth {
return str
}
if leftTruncate {
return fmt.Sprintf("...%s", str[len(str)-(maxWidth-3):])
}
return fmt.Sprintf("%s...", str[:maxWidth-3])
}
const (
maxGUNWidth = 25
maxLocWidth = 40
)
type keyInfo struct {
gun string // assumption that this is "" if role is root
role string
keyID string
location string
}
// We want to sort by gun, then by role, then by keyID, then by location
// In the case of a root role, then there is no GUN, and a root role comes
// first.
type keyInfoSorter []keyInfo
func (k keyInfoSorter) Len() int { return len(k) }
func (k keyInfoSorter) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
func (k keyInfoSorter) Less(i, j int) bool {
// special-case role
if k[i].role != k[j].role {
if k[i].role == data.CanonicalRootRole {
return true
}
if k[j].role == data.CanonicalRootRole {
return false
}
// otherwise, neither of them are root, they're just different, so
// go with the traditional sort order.
}
// sort order is GUN, role, keyID, location.
orderedI := []string{k[i].gun, k[i].role, k[i].keyID, k[i].location}
orderedJ := []string{k[j].gun, k[j].role, k[j].keyID, k[j].location}
for x := 0; x < 4; x++ {
switch {
case orderedI[x] < orderedJ[x]:
return true
case orderedI[x] > orderedJ[x]:
return false
}
// continue on and evalulate the next item
}
// this shouldn't happen - that means two values are exactly equal
return false
}
// Given a list of KeyStores in order of listing preference, pretty-prints the
// root keys and then the signing keys.
func prettyPrintKeys(keyStores []trustmanager.KeyStore, writer io.Writer) {
var info []keyInfo
for _, store := range keyStores {
for keyID, keyIDInfo := range store.ListKeys() {
info = append(info, keyInfo{
role: keyIDInfo.Role,
location: store.Name(),
gun: keyIDInfo.Gun,
keyID: keyID,
})
}
}
if len(info) == 0 {
writer.Write([]byte("No signing keys found.\n"))
return
}
sort.Stable(keyInfoSorter(info))
table := getTable([]string{"ROLE", "GUN", "KEY ID", "LOCATION"}, writer)
for _, oneKeyInfo := range info {
table.Append([]string{
oneKeyInfo.role,
truncateWithEllipsis(oneKeyInfo.gun, maxGUNWidth, true),
oneKeyInfo.keyID,
truncateWithEllipsis(oneKeyInfo.location, maxLocWidth, true),
})
}
table.Render()
}
// --- pretty printing targets ---
type targetsSorter []*client.TargetWithRole
func (t targetsSorter) Len() int { return len(t) }
func (t targetsSorter) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
func (t targetsSorter) Less(i, j int) bool {
return t[i].Name < t[j].Name
}
// --- pretty printing roles ---
type roleSorter []*data.Role
func (r roleSorter) Len() int { return len(r) }
func (r roleSorter) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r roleSorter) Less(i, j int) bool {
return r[i].Name < r[j].Name
}
// Pretty-prints the sorted list of TargetWithRoles.
func prettyPrintTargets(ts []*client.TargetWithRole, writer io.Writer) {
if len(ts) == 0 {
writer.Write([]byte("\nNo targets present in this repository.\n\n"))
return
}
sort.Stable(targetsSorter(ts))
table := getTable([]string{"Name", "Digest", "Size (bytes)", "Role"}, writer)
for _, t := range ts {
table.Append([]string{
t.Name,
hex.EncodeToString(t.Hashes["sha256"]),
fmt.Sprintf("%d", t.Length),
t.Role,
})
}
table.Render()
}
// Pretty-prints the list of provided Roles
func prettyPrintRoles(rs []*data.Role, writer io.Writer, roleType string) {
if len(rs) == 0 {
writer.Write([]byte(fmt.Sprintf("\nNo %s present in this repository.\n\n", roleType)))
return
}
// this sorter works for Role types
sort.Stable(roleSorter(rs))
table := getTable([]string{"Role", "Paths", "Key IDs", "Threshold"}, writer)
for _, r := range rs {
table.Append([]string{
r.Name,
prettyPrintPaths(r.Paths),
strings.Join(r.KeyIDs, "\n"),
fmt.Sprintf("%v", r.Threshold),
})
}
table.Render()
}
// Pretty-prints a list of delegation paths, and ensures the empty string is printed as "" in the console
func prettyPrintPaths(paths []string) string {
// sort paths first
sort.Strings(paths)
prettyPaths := []string{}
for _, path := range paths {
// manually escape "" and designate that it is all paths with an extra print <all paths>
if path == "" {
path = "\"\" <all paths>"
}
prettyPaths = append(prettyPaths, path)
}
return strings.Join(prettyPaths, "\n")
}