52581cd472
Adds debug and warn logging around AWS credential chain generation, specifically to help users debugging auto-unseal problems on AWS, by logging which role is being used in the case of a webidentity token. Adds a deferred call to flush the log output as well, to ensure logs are output in the event of an initialization failure.
328 lines
7.1 KiB
Go
328 lines
7.1 KiB
Go
package pretty
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"strconv"
|
|
"text/tabwriter"
|
|
|
|
"github.com/kr/text"
|
|
)
|
|
|
|
type formatter struct {
|
|
v reflect.Value
|
|
force bool
|
|
quote bool
|
|
}
|
|
|
|
// Formatter makes a wrapper, f, that will format x as go source with line
|
|
// breaks and tabs. Object f responds to the "%v" formatting verb when both the
|
|
// "#" and " " (space) flags are set, for example:
|
|
//
|
|
// fmt.Sprintf("%# v", Formatter(x))
|
|
//
|
|
// If one of these two flags is not set, or any other verb is used, f will
|
|
// format x according to the usual rules of package fmt.
|
|
// In particular, if x satisfies fmt.Formatter, then x.Format will be called.
|
|
func Formatter(x interface{}) (f fmt.Formatter) {
|
|
return formatter{v: reflect.ValueOf(x), quote: true}
|
|
}
|
|
|
|
func (fo formatter) String() string {
|
|
return fmt.Sprint(fo.v.Interface()) // unwrap it
|
|
}
|
|
|
|
func (fo formatter) passThrough(f fmt.State, c rune) {
|
|
s := "%"
|
|
for i := 0; i < 128; i++ {
|
|
if f.Flag(i) {
|
|
s += string(i)
|
|
}
|
|
}
|
|
if w, ok := f.Width(); ok {
|
|
s += fmt.Sprintf("%d", w)
|
|
}
|
|
if p, ok := f.Precision(); ok {
|
|
s += fmt.Sprintf(".%d", p)
|
|
}
|
|
s += string(c)
|
|
fmt.Fprintf(f, s, fo.v.Interface())
|
|
}
|
|
|
|
func (fo formatter) Format(f fmt.State, c rune) {
|
|
if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') {
|
|
w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0)
|
|
p := &printer{tw: w, Writer: w, visited: make(map[visit]int)}
|
|
p.printValue(fo.v, true, fo.quote)
|
|
w.Flush()
|
|
return
|
|
}
|
|
fo.passThrough(f, c)
|
|
}
|
|
|
|
type printer struct {
|
|
io.Writer
|
|
tw *tabwriter.Writer
|
|
visited map[visit]int
|
|
depth int
|
|
}
|
|
|
|
func (p *printer) indent() *printer {
|
|
q := *p
|
|
q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0)
|
|
q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'})
|
|
return &q
|
|
}
|
|
|
|
func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) {
|
|
if showType {
|
|
io.WriteString(p, v.Type().String())
|
|
fmt.Fprintf(p, "(%#v)", x)
|
|
} else {
|
|
fmt.Fprintf(p, "%#v", x)
|
|
}
|
|
}
|
|
|
|
// printValue must keep track of already-printed pointer values to avoid
|
|
// infinite recursion.
|
|
type visit struct {
|
|
v uintptr
|
|
typ reflect.Type
|
|
}
|
|
|
|
func (p *printer) printValue(v reflect.Value, showType, quote bool) {
|
|
if p.depth > 10 {
|
|
io.WriteString(p, "!%v(DEPTH EXCEEDED)")
|
|
return
|
|
}
|
|
|
|
switch v.Kind() {
|
|
case reflect.Bool:
|
|
p.printInline(v, v.Bool(), showType)
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
p.printInline(v, v.Int(), showType)
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
p.printInline(v, v.Uint(), showType)
|
|
case reflect.Float32, reflect.Float64:
|
|
p.printInline(v, v.Float(), showType)
|
|
case reflect.Complex64, reflect.Complex128:
|
|
fmt.Fprintf(p, "%#v", v.Complex())
|
|
case reflect.String:
|
|
p.fmtString(v.String(), quote)
|
|
case reflect.Map:
|
|
t := v.Type()
|
|
if showType {
|
|
io.WriteString(p, t.String())
|
|
}
|
|
writeByte(p, '{')
|
|
if nonzero(v) {
|
|
expand := !canInline(v.Type())
|
|
pp := p
|
|
if expand {
|
|
writeByte(p, '\n')
|
|
pp = p.indent()
|
|
}
|
|
keys := v.MapKeys()
|
|
for i := 0; i < v.Len(); i++ {
|
|
k := keys[i]
|
|
mv := v.MapIndex(k)
|
|
pp.printValue(k, false, true)
|
|
writeByte(pp, ':')
|
|
if expand {
|
|
writeByte(pp, '\t')
|
|
}
|
|
showTypeInStruct := t.Elem().Kind() == reflect.Interface
|
|
pp.printValue(mv, showTypeInStruct, true)
|
|
if expand {
|
|
io.WriteString(pp, ",\n")
|
|
} else if i < v.Len()-1 {
|
|
io.WriteString(pp, ", ")
|
|
}
|
|
}
|
|
if expand {
|
|
pp.tw.Flush()
|
|
}
|
|
}
|
|
writeByte(p, '}')
|
|
case reflect.Struct:
|
|
t := v.Type()
|
|
if v.CanAddr() {
|
|
addr := v.UnsafeAddr()
|
|
vis := visit{addr, t}
|
|
if vd, ok := p.visited[vis]; ok && vd < p.depth {
|
|
p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false)
|
|
break // don't print v again
|
|
}
|
|
p.visited[vis] = p.depth
|
|
}
|
|
|
|
if showType {
|
|
io.WriteString(p, t.String())
|
|
}
|
|
writeByte(p, '{')
|
|
if nonzero(v) {
|
|
expand := !canInline(v.Type())
|
|
pp := p
|
|
if expand {
|
|
writeByte(p, '\n')
|
|
pp = p.indent()
|
|
}
|
|
for i := 0; i < v.NumField(); i++ {
|
|
showTypeInStruct := true
|
|
if f := t.Field(i); f.Name != "" {
|
|
io.WriteString(pp, f.Name)
|
|
writeByte(pp, ':')
|
|
if expand {
|
|
writeByte(pp, '\t')
|
|
}
|
|
showTypeInStruct = labelType(f.Type)
|
|
}
|
|
pp.printValue(getField(v, i), showTypeInStruct, true)
|
|
if expand {
|
|
io.WriteString(pp, ",\n")
|
|
} else if i < v.NumField()-1 {
|
|
io.WriteString(pp, ", ")
|
|
}
|
|
}
|
|
if expand {
|
|
pp.tw.Flush()
|
|
}
|
|
}
|
|
writeByte(p, '}')
|
|
case reflect.Interface:
|
|
switch e := v.Elem(); {
|
|
case e.Kind() == reflect.Invalid:
|
|
io.WriteString(p, "nil")
|
|
case e.IsValid():
|
|
pp := *p
|
|
pp.depth++
|
|
pp.printValue(e, showType, true)
|
|
default:
|
|
io.WriteString(p, v.Type().String())
|
|
io.WriteString(p, "(nil)")
|
|
}
|
|
case reflect.Array, reflect.Slice:
|
|
t := v.Type()
|
|
if showType {
|
|
io.WriteString(p, t.String())
|
|
}
|
|
if v.Kind() == reflect.Slice && v.IsNil() && showType {
|
|
io.WriteString(p, "(nil)")
|
|
break
|
|
}
|
|
if v.Kind() == reflect.Slice && v.IsNil() {
|
|
io.WriteString(p, "nil")
|
|
break
|
|
}
|
|
writeByte(p, '{')
|
|
expand := !canInline(v.Type())
|
|
pp := p
|
|
if expand {
|
|
writeByte(p, '\n')
|
|
pp = p.indent()
|
|
}
|
|
for i := 0; i < v.Len(); i++ {
|
|
showTypeInSlice := t.Elem().Kind() == reflect.Interface
|
|
pp.printValue(v.Index(i), showTypeInSlice, true)
|
|
if expand {
|
|
io.WriteString(pp, ",\n")
|
|
} else if i < v.Len()-1 {
|
|
io.WriteString(pp, ", ")
|
|
}
|
|
}
|
|
if expand {
|
|
pp.tw.Flush()
|
|
}
|
|
writeByte(p, '}')
|
|
case reflect.Ptr:
|
|
e := v.Elem()
|
|
if !e.IsValid() {
|
|
writeByte(p, '(')
|
|
io.WriteString(p, v.Type().String())
|
|
io.WriteString(p, ")(nil)")
|
|
} else {
|
|
pp := *p
|
|
pp.depth++
|
|
writeByte(pp, '&')
|
|
pp.printValue(e, true, true)
|
|
}
|
|
case reflect.Chan:
|
|
x := v.Pointer()
|
|
if showType {
|
|
writeByte(p, '(')
|
|
io.WriteString(p, v.Type().String())
|
|
fmt.Fprintf(p, ")(%#v)", x)
|
|
} else {
|
|
fmt.Fprintf(p, "%#v", x)
|
|
}
|
|
case reflect.Func:
|
|
io.WriteString(p, v.Type().String())
|
|
io.WriteString(p, " {...}")
|
|
case reflect.UnsafePointer:
|
|
p.printInline(v, v.Pointer(), showType)
|
|
case reflect.Invalid:
|
|
io.WriteString(p, "nil")
|
|
}
|
|
}
|
|
|
|
func canInline(t reflect.Type) bool {
|
|
switch t.Kind() {
|
|
case reflect.Map:
|
|
return !canExpand(t.Elem())
|
|
case reflect.Struct:
|
|
for i := 0; i < t.NumField(); i++ {
|
|
if canExpand(t.Field(i).Type) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case reflect.Interface:
|
|
return false
|
|
case reflect.Array, reflect.Slice:
|
|
return !canExpand(t.Elem())
|
|
case reflect.Ptr:
|
|
return false
|
|
case reflect.Chan, reflect.Func, reflect.UnsafePointer:
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func canExpand(t reflect.Type) bool {
|
|
switch t.Kind() {
|
|
case reflect.Map, reflect.Struct,
|
|
reflect.Interface, reflect.Array, reflect.Slice,
|
|
reflect.Ptr:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func labelType(t reflect.Type) bool {
|
|
switch t.Kind() {
|
|
case reflect.Interface, reflect.Struct:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (p *printer) fmtString(s string, quote bool) {
|
|
if quote {
|
|
s = strconv.Quote(s)
|
|
}
|
|
io.WriteString(p, s)
|
|
}
|
|
|
|
func writeByte(w io.Writer, b byte) {
|
|
w.Write([]byte{b})
|
|
}
|
|
|
|
func getField(v reflect.Value, i int) reflect.Value {
|
|
val := v.Field(i)
|
|
if val.Kind() == reflect.Interface && !val.IsNil() {
|
|
val = val.Elem()
|
|
}
|
|
return val
|
|
}
|