docs/cmd/notary/prettyprint.go

204 lines
5.0 KiB
Go

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")
}