vendor: regenerate vendor directory with glide
This commit is contained in:
parent
d846c508c3
commit
35df9d5846
2
vendor/github.com/beorn7/perks/.gitignore
generated
vendored
Normal file
2
vendor/github.com/beorn7/perks/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*.test
|
||||||
|
*.prof
|
20
vendor/github.com/beorn7/perks/LICENSE
generated
vendored
Normal file
20
vendor/github.com/beorn7/perks/LICENSE
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Copyright (C) 2013 Blake Mizerany
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
31
vendor/github.com/beorn7/perks/README.md
generated
vendored
Normal file
31
vendor/github.com/beorn7/perks/README.md
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Perks for Go (golang.org)
|
||||||
|
|
||||||
|
Perks contains the Go package quantile that computes approximate quantiles over
|
||||||
|
an unbounded data stream within low memory and CPU bounds.
|
||||||
|
|
||||||
|
For more information and examples, see:
|
||||||
|
http://godoc.org/github.com/bmizerany/perks
|
||||||
|
|
||||||
|
A very special thank you and shout out to Graham Cormode (Rutgers University),
|
||||||
|
Flip Korn (AT&T Labs–Research), S. Muthukrishnan (Rutgers University), and
|
||||||
|
Divesh Srivastava (AT&T Labs–Research) for their research and publication of
|
||||||
|
[Effective Computation of Biased Quantiles over Data Streams](http://www.cs.rutgers.edu/~muthu/bquant.pdf)
|
||||||
|
|
||||||
|
Thank you, also:
|
||||||
|
* Armon Dadgar (@armon)
|
||||||
|
* Andrew Gerrand (@nf)
|
||||||
|
* Brad Fitzpatrick (@bradfitz)
|
||||||
|
* Keith Rarick (@kr)
|
||||||
|
|
||||||
|
FAQ:
|
||||||
|
|
||||||
|
Q: Why not move the quantile package into the project root?
|
||||||
|
A: I want to add more packages to perks later.
|
||||||
|
|
||||||
|
Copyright (C) 2013 Blake Mizerany
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26
vendor/github.com/beorn7/perks/histogram/bench_test.go
generated
vendored
Normal file
26
vendor/github.com/beorn7/perks/histogram/bench_test.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package histogram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkInsert10Bins(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
h := New(10)
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
f := rand.ExpFloat64()
|
||||||
|
h.Insert(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkInsert100Bins(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
h := New(100)
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
f := rand.ExpFloat64()
|
||||||
|
h.Insert(f)
|
||||||
|
}
|
||||||
|
}
|
108
vendor/github.com/beorn7/perks/histogram/histogram.go
generated
vendored
Normal file
108
vendor/github.com/beorn7/perks/histogram/histogram.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// Package histogram provides a Go implementation of BigML's histogram package
|
||||||
|
// for Clojure/Java. It is currently experimental.
|
||||||
|
package histogram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/heap"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Bin struct {
|
||||||
|
Count int
|
||||||
|
Sum float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bin) Update(x *Bin) {
|
||||||
|
b.Count += x.Count
|
||||||
|
b.Sum += x.Sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bin) Mean() float64 {
|
||||||
|
return b.Sum / float64(b.Count)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bins []*Bin
|
||||||
|
|
||||||
|
func (bs Bins) Len() int { return len(bs) }
|
||||||
|
func (bs Bins) Less(i, j int) bool { return bs[i].Mean() < bs[j].Mean() }
|
||||||
|
func (bs Bins) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] }
|
||||||
|
|
||||||
|
func (bs *Bins) Push(x interface{}) {
|
||||||
|
*bs = append(*bs, x.(*Bin))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *Bins) Pop() interface{} {
|
||||||
|
return bs.remove(len(*bs) - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *Bins) remove(n int) *Bin {
|
||||||
|
if n < 0 || len(*bs) < n {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
x := (*bs)[n]
|
||||||
|
*bs = append((*bs)[:n], (*bs)[n+1:]...)
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
type Histogram struct {
|
||||||
|
res *reservoir
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(maxBins int) *Histogram {
|
||||||
|
return &Histogram{res: newReservoir(maxBins)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Histogram) Insert(f float64) {
|
||||||
|
h.res.insert(&Bin{1, f})
|
||||||
|
h.res.compress()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Histogram) Bins() Bins {
|
||||||
|
return h.res.bins
|
||||||
|
}
|
||||||
|
|
||||||
|
type reservoir struct {
|
||||||
|
n int
|
||||||
|
maxBins int
|
||||||
|
bins Bins
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReservoir(maxBins int) *reservoir {
|
||||||
|
return &reservoir{maxBins: maxBins}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reservoir) insert(bin *Bin) {
|
||||||
|
r.n += bin.Count
|
||||||
|
i := sort.Search(len(r.bins), func(i int) bool {
|
||||||
|
return r.bins[i].Mean() >= bin.Mean()
|
||||||
|
})
|
||||||
|
if i < 0 || i == r.bins.Len() {
|
||||||
|
// TODO(blake): Maybe use an .insert(i, bin) instead of
|
||||||
|
// performing the extra work of a heap.Push.
|
||||||
|
heap.Push(&r.bins, bin)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.bins[i].Update(bin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reservoir) compress() {
|
||||||
|
for r.bins.Len() > r.maxBins {
|
||||||
|
minGapIndex := -1
|
||||||
|
minGap := math.MaxFloat64
|
||||||
|
for i := 0; i < r.bins.Len()-1; i++ {
|
||||||
|
gap := gapWeight(r.bins[i], r.bins[i+1])
|
||||||
|
if minGap > gap {
|
||||||
|
minGap = gap
|
||||||
|
minGapIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev := r.bins[minGapIndex]
|
||||||
|
next := r.bins.remove(minGapIndex + 1)
|
||||||
|
prev.Update(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func gapWeight(prev, next *Bin) float64 {
|
||||||
|
return next.Mean() - prev.Mean()
|
||||||
|
}
|
38
vendor/github.com/beorn7/perks/histogram/histogram_test.go
generated
vendored
Normal file
38
vendor/github.com/beorn7/perks/histogram/histogram_test.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package histogram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHistogram(t *testing.T) {
|
||||||
|
const numPoints = 1e6
|
||||||
|
const maxBins = 3
|
||||||
|
|
||||||
|
h := New(maxBins)
|
||||||
|
for i := 0; i < numPoints; i++ {
|
||||||
|
f := rand.ExpFloat64()
|
||||||
|
h.Insert(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
bins := h.Bins()
|
||||||
|
if g := len(bins); g > maxBins {
|
||||||
|
t.Fatalf("got %d bins, wanted <= %d", g, maxBins)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, b := range bins {
|
||||||
|
t.Logf("%+v", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
if g := count(h.Bins()); g != numPoints {
|
||||||
|
t.Fatalf("binned %d points, wanted %d", g, numPoints)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func count(bins Bins) int {
|
||||||
|
binCounts := 0
|
||||||
|
for _, b := range bins {
|
||||||
|
binCounts += b.Count
|
||||||
|
}
|
||||||
|
return binCounts
|
||||||
|
}
|
63
vendor/github.com/beorn7/perks/quantile/bench_test.go
generated
vendored
Normal file
63
vendor/github.com/beorn7/perks/quantile/bench_test.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package quantile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkInsertTargeted(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
s := NewTargeted(Targets)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := float64(0); i < float64(b.N); i++ {
|
||||||
|
s.Insert(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkInsertTargetedSmallEpsilon(b *testing.B) {
|
||||||
|
s := NewTargeted(TargetsSmallEpsilon)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := float64(0); i < float64(b.N); i++ {
|
||||||
|
s.Insert(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkInsertBiased(b *testing.B) {
|
||||||
|
s := NewLowBiased(0.01)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := float64(0); i < float64(b.N); i++ {
|
||||||
|
s.Insert(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkInsertBiasedSmallEpsilon(b *testing.B) {
|
||||||
|
s := NewLowBiased(0.0001)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := float64(0); i < float64(b.N); i++ {
|
||||||
|
s.Insert(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkQuery(b *testing.B) {
|
||||||
|
s := NewTargeted(Targets)
|
||||||
|
for i := float64(0); i < 1e6; i++ {
|
||||||
|
s.Insert(i)
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
n := float64(b.N)
|
||||||
|
for i := float64(0); i < n; i++ {
|
||||||
|
s.Query(i / n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkQuerySmallEpsilon(b *testing.B) {
|
||||||
|
s := NewTargeted(TargetsSmallEpsilon)
|
||||||
|
for i := float64(0); i < 1e6; i++ {
|
||||||
|
s.Insert(i)
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
n := float64(b.N)
|
||||||
|
for i := float64(0); i < n; i++ {
|
||||||
|
s.Query(i / n)
|
||||||
|
}
|
||||||
|
}
|
121
vendor/github.com/beorn7/perks/quantile/example_test.go
generated
vendored
Normal file
121
vendor/github.com/beorn7/perks/quantile/example_test.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// +build go1.1
|
||||||
|
|
||||||
|
package quantile_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/beorn7/perks/quantile"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example_simple() {
|
||||||
|
ch := make(chan float64)
|
||||||
|
go sendFloats(ch)
|
||||||
|
|
||||||
|
// Compute the 50th, 90th, and 99th percentile.
|
||||||
|
q := quantile.NewTargeted(map[float64]float64{
|
||||||
|
0.50: 0.005,
|
||||||
|
0.90: 0.001,
|
||||||
|
0.99: 0.0001,
|
||||||
|
})
|
||||||
|
for v := range ch {
|
||||||
|
q.Insert(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("perc50:", q.Query(0.50))
|
||||||
|
fmt.Println("perc90:", q.Query(0.90))
|
||||||
|
fmt.Println("perc99:", q.Query(0.99))
|
||||||
|
fmt.Println("count:", q.Count())
|
||||||
|
// Output:
|
||||||
|
// perc50: 5
|
||||||
|
// perc90: 16
|
||||||
|
// perc99: 223
|
||||||
|
// count: 2388
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_mergeMultipleStreams() {
|
||||||
|
// Scenario:
|
||||||
|
// We have multiple database shards. On each shard, there is a process
|
||||||
|
// collecting query response times from the database logs and inserting
|
||||||
|
// them into a Stream (created via NewTargeted(0.90)), much like the
|
||||||
|
// Simple example. These processes expose a network interface for us to
|
||||||
|
// ask them to serialize and send us the results of their
|
||||||
|
// Stream.Samples so we may Merge and Query them.
|
||||||
|
//
|
||||||
|
// NOTES:
|
||||||
|
// * These sample sets are small, allowing us to get them
|
||||||
|
// across the network much faster than sending the entire list of data
|
||||||
|
// points.
|
||||||
|
//
|
||||||
|
// * For this to work correctly, we must supply the same quantiles
|
||||||
|
// a priori the process collecting the samples supplied to NewTargeted,
|
||||||
|
// even if we do not plan to query them all here.
|
||||||
|
ch := make(chan quantile.Samples)
|
||||||
|
getDBQuerySamples(ch)
|
||||||
|
q := quantile.NewTargeted(map[float64]float64{0.90: 0.001})
|
||||||
|
for samples := range ch {
|
||||||
|
q.Merge(samples)
|
||||||
|
}
|
||||||
|
fmt.Println("perc90:", q.Query(0.90))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_window() {
|
||||||
|
// Scenario: We want the 90th, 95th, and 99th percentiles for each
|
||||||
|
// minute.
|
||||||
|
|
||||||
|
ch := make(chan float64)
|
||||||
|
go sendStreamValues(ch)
|
||||||
|
|
||||||
|
tick := time.NewTicker(1 * time.Minute)
|
||||||
|
q := quantile.NewTargeted(map[float64]float64{
|
||||||
|
0.90: 0.001,
|
||||||
|
0.95: 0.0005,
|
||||||
|
0.99: 0.0001,
|
||||||
|
})
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case t := <-tick.C:
|
||||||
|
flushToDB(t, q.Samples())
|
||||||
|
q.Reset()
|
||||||
|
case v := <-ch:
|
||||||
|
q.Insert(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendStreamValues(ch chan float64) {
|
||||||
|
// Use your imagination
|
||||||
|
}
|
||||||
|
|
||||||
|
func flushToDB(t time.Time, samples quantile.Samples) {
|
||||||
|
// Use your imagination
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a stub for the above example. In reality this would hit the remote
|
||||||
|
// servers via http or something like it.
|
||||||
|
func getDBQuerySamples(ch chan quantile.Samples) {}
|
||||||
|
|
||||||
|
func sendFloats(ch chan<- float64) {
|
||||||
|
f, err := os.Open("exampledata.txt")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
sc := bufio.NewScanner(f)
|
||||||
|
for sc.Scan() {
|
||||||
|
b := sc.Bytes()
|
||||||
|
v, err := strconv.ParseFloat(string(b), 64)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
ch <- v
|
||||||
|
}
|
||||||
|
if sc.Err() != nil {
|
||||||
|
log.Fatal(sc.Err())
|
||||||
|
}
|
||||||
|
close(ch)
|
||||||
|
}
|
2388
vendor/github.com/beorn7/perks/quantile/exampledata.txt
generated
vendored
Normal file
2388
vendor/github.com/beorn7/perks/quantile/exampledata.txt
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
292
vendor/github.com/beorn7/perks/quantile/stream.go
generated
vendored
Normal file
292
vendor/github.com/beorn7/perks/quantile/stream.go
generated
vendored
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
// Package quantile computes approximate quantiles over an unbounded data
|
||||||
|
// stream within low memory and CPU bounds.
|
||||||
|
//
|
||||||
|
// A small amount of accuracy is traded to achieve the above properties.
|
||||||
|
//
|
||||||
|
// Multiple streams can be merged before calling Query to generate a single set
|
||||||
|
// of results. This is meaningful when the streams represent the same type of
|
||||||
|
// data. See Merge and Samples.
|
||||||
|
//
|
||||||
|
// For more detailed information about the algorithm used, see:
|
||||||
|
//
|
||||||
|
// Effective Computation of Biased Quantiles over Data Streams
|
||||||
|
//
|
||||||
|
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
|
||||||
|
package quantile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sample holds an observed value and meta information for compression. JSON
|
||||||
|
// tags have been added for convenience.
|
||||||
|
type Sample struct {
|
||||||
|
Value float64 `json:",string"`
|
||||||
|
Width float64 `json:",string"`
|
||||||
|
Delta float64 `json:",string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Samples represents a slice of samples. It implements sort.Interface.
|
||||||
|
type Samples []Sample
|
||||||
|
|
||||||
|
func (a Samples) Len() int { return len(a) }
|
||||||
|
func (a Samples) Less(i, j int) bool { return a[i].Value < a[j].Value }
|
||||||
|
func (a Samples) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
|
||||||
|
type invariant func(s *stream, r float64) float64
|
||||||
|
|
||||||
|
// NewLowBiased returns an initialized Stream for low-biased quantiles
|
||||||
|
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
|
||||||
|
// error guarantees can still be given even for the lower ranks of the data
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// The provided epsilon is a relative error, i.e. the true quantile of a value
|
||||||
|
// returned by a query is guaranteed to be within (1±Epsilon)*Quantile.
|
||||||
|
//
|
||||||
|
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
|
||||||
|
// properties.
|
||||||
|
func NewLowBiased(epsilon float64) *Stream {
|
||||||
|
ƒ := func(s *stream, r float64) float64 {
|
||||||
|
return 2 * epsilon * r
|
||||||
|
}
|
||||||
|
return newStream(ƒ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHighBiased returns an initialized Stream for high-biased quantiles
|
||||||
|
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
|
||||||
|
// error guarantees can still be given even for the higher ranks of the data
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// The provided epsilon is a relative error, i.e. the true quantile of a value
|
||||||
|
// returned by a query is guaranteed to be within 1-(1±Epsilon)*(1-Quantile).
|
||||||
|
//
|
||||||
|
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
|
||||||
|
// properties.
|
||||||
|
func NewHighBiased(epsilon float64) *Stream {
|
||||||
|
ƒ := func(s *stream, r float64) float64 {
|
||||||
|
return 2 * epsilon * (s.n - r)
|
||||||
|
}
|
||||||
|
return newStream(ƒ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTargeted returns an initialized Stream concerned with a particular set of
|
||||||
|
// quantile values that are supplied a priori. Knowing these a priori reduces
|
||||||
|
// space and computation time. The targets map maps the desired quantiles to
|
||||||
|
// their absolute errors, i.e. the true quantile of a value returned by a query
|
||||||
|
// is guaranteed to be within (Quantile±Epsilon).
|
||||||
|
//
|
||||||
|
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
|
||||||
|
func NewTargeted(targets map[float64]float64) *Stream {
|
||||||
|
ƒ := func(s *stream, r float64) float64 {
|
||||||
|
var m = math.MaxFloat64
|
||||||
|
var f float64
|
||||||
|
for quantile, epsilon := range targets {
|
||||||
|
if quantile*s.n <= r {
|
||||||
|
f = (2 * epsilon * r) / quantile
|
||||||
|
} else {
|
||||||
|
f = (2 * epsilon * (s.n - r)) / (1 - quantile)
|
||||||
|
}
|
||||||
|
if f < m {
|
||||||
|
m = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
return newStream(ƒ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stream computes quantiles for a stream of float64s. It is not thread-safe by
|
||||||
|
// design. Take care when using across multiple goroutines.
|
||||||
|
type Stream struct {
|
||||||
|
*stream
|
||||||
|
b Samples
|
||||||
|
sorted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStream(ƒ invariant) *Stream {
|
||||||
|
x := &stream{ƒ: ƒ}
|
||||||
|
return &Stream{x, make(Samples, 0, 500), true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert inserts v into the stream.
|
||||||
|
func (s *Stream) Insert(v float64) {
|
||||||
|
s.insert(Sample{Value: v, Width: 1})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) insert(sample Sample) {
|
||||||
|
s.b = append(s.b, sample)
|
||||||
|
s.sorted = false
|
||||||
|
if len(s.b) == cap(s.b) {
|
||||||
|
s.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query returns the computed qth percentiles value. If s was created with
|
||||||
|
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
|
||||||
|
// will return an unspecified result.
|
||||||
|
func (s *Stream) Query(q float64) float64 {
|
||||||
|
if !s.flushed() {
|
||||||
|
// Fast path when there hasn't been enough data for a flush;
|
||||||
|
// this also yields better accuracy for small sets of data.
|
||||||
|
l := len(s.b)
|
||||||
|
if l == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
i := int(math.Ceil(float64(l) * q))
|
||||||
|
if i > 0 {
|
||||||
|
i -= 1
|
||||||
|
}
|
||||||
|
s.maybeSort()
|
||||||
|
return s.b[i].Value
|
||||||
|
}
|
||||||
|
s.flush()
|
||||||
|
return s.stream.query(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge merges samples into the underlying streams samples. This is handy when
|
||||||
|
// merging multiple streams from separate threads, database shards, etc.
|
||||||
|
//
|
||||||
|
// ATTENTION: This method is broken and does not yield correct results. The
|
||||||
|
// underlying algorithm is not capable of merging streams correctly.
|
||||||
|
func (s *Stream) Merge(samples Samples) {
|
||||||
|
sort.Sort(samples)
|
||||||
|
s.stream.merge(samples)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset reinitializes and clears the list reusing the samples buffer memory.
|
||||||
|
func (s *Stream) Reset() {
|
||||||
|
s.stream.reset()
|
||||||
|
s.b = s.b[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Samples returns stream samples held by s.
|
||||||
|
func (s *Stream) Samples() Samples {
|
||||||
|
if !s.flushed() {
|
||||||
|
return s.b
|
||||||
|
}
|
||||||
|
s.flush()
|
||||||
|
return s.stream.samples()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the total number of samples observed in the stream
|
||||||
|
// since initialization.
|
||||||
|
func (s *Stream) Count() int {
|
||||||
|
return len(s.b) + s.stream.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) flush() {
|
||||||
|
s.maybeSort()
|
||||||
|
s.stream.merge(s.b)
|
||||||
|
s.b = s.b[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) maybeSort() {
|
||||||
|
if !s.sorted {
|
||||||
|
s.sorted = true
|
||||||
|
sort.Sort(s.b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) flushed() bool {
|
||||||
|
return len(s.stream.l) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type stream struct {
|
||||||
|
n float64
|
||||||
|
l []Sample
|
||||||
|
ƒ invariant
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) reset() {
|
||||||
|
s.l = s.l[:0]
|
||||||
|
s.n = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) insert(v float64) {
|
||||||
|
s.merge(Samples{{v, 1, 0}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) merge(samples Samples) {
|
||||||
|
// TODO(beorn7): This tries to merge not only individual samples, but
|
||||||
|
// whole summaries. The paper doesn't mention merging summaries at
|
||||||
|
// all. Unittests show that the merging is inaccurate. Find out how to
|
||||||
|
// do merges properly.
|
||||||
|
var r float64
|
||||||
|
i := 0
|
||||||
|
for _, sample := range samples {
|
||||||
|
for ; i < len(s.l); i++ {
|
||||||
|
c := s.l[i]
|
||||||
|
if c.Value > sample.Value {
|
||||||
|
// Insert at position i.
|
||||||
|
s.l = append(s.l, Sample{})
|
||||||
|
copy(s.l[i+1:], s.l[i:])
|
||||||
|
s.l[i] = Sample{
|
||||||
|
sample.Value,
|
||||||
|
sample.Width,
|
||||||
|
math.Max(sample.Delta, math.Floor(s.ƒ(s, r))-1),
|
||||||
|
// TODO(beorn7): How to calculate delta correctly?
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
goto inserted
|
||||||
|
}
|
||||||
|
r += c.Width
|
||||||
|
}
|
||||||
|
s.l = append(s.l, Sample{sample.Value, sample.Width, 0})
|
||||||
|
i++
|
||||||
|
inserted:
|
||||||
|
s.n += sample.Width
|
||||||
|
r += sample.Width
|
||||||
|
}
|
||||||
|
s.compress()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) count() int {
|
||||||
|
return int(s.n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) query(q float64) float64 {
|
||||||
|
t := math.Ceil(q * s.n)
|
||||||
|
t += math.Ceil(s.ƒ(s, t) / 2)
|
||||||
|
p := s.l[0]
|
||||||
|
var r float64
|
||||||
|
for _, c := range s.l[1:] {
|
||||||
|
r += p.Width
|
||||||
|
if r+c.Width+c.Delta > t {
|
||||||
|
return p.Value
|
||||||
|
}
|
||||||
|
p = c
|
||||||
|
}
|
||||||
|
return p.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) compress() {
|
||||||
|
if len(s.l) < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
x := s.l[len(s.l)-1]
|
||||||
|
xi := len(s.l) - 1
|
||||||
|
r := s.n - 1 - x.Width
|
||||||
|
|
||||||
|
for i := len(s.l) - 2; i >= 0; i-- {
|
||||||
|
c := s.l[i]
|
||||||
|
if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
|
||||||
|
x.Width += c.Width
|
||||||
|
s.l[xi] = x
|
||||||
|
// Remove element at i.
|
||||||
|
copy(s.l[i:], s.l[i+1:])
|
||||||
|
s.l = s.l[:len(s.l)-1]
|
||||||
|
xi -= 1
|
||||||
|
} else {
|
||||||
|
x = c
|
||||||
|
xi = i
|
||||||
|
}
|
||||||
|
r -= c.Width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) samples() Samples {
|
||||||
|
samples := make(Samples, len(s.l))
|
||||||
|
copy(samples, s.l)
|
||||||
|
return samples
|
||||||
|
}
|
215
vendor/github.com/beorn7/perks/quantile/stream_test.go
generated
vendored
Normal file
215
vendor/github.com/beorn7/perks/quantile/stream_test.go
generated
vendored
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
package quantile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Targets = map[float64]float64{
|
||||||
|
0.01: 0.001,
|
||||||
|
0.10: 0.01,
|
||||||
|
0.50: 0.05,
|
||||||
|
0.90: 0.01,
|
||||||
|
0.99: 0.001,
|
||||||
|
}
|
||||||
|
TargetsSmallEpsilon = map[float64]float64{
|
||||||
|
0.01: 0.0001,
|
||||||
|
0.10: 0.001,
|
||||||
|
0.50: 0.005,
|
||||||
|
0.90: 0.001,
|
||||||
|
0.99: 0.0001,
|
||||||
|
}
|
||||||
|
LowQuantiles = []float64{0.01, 0.1, 0.5}
|
||||||
|
HighQuantiles = []float64{0.99, 0.9, 0.5}
|
||||||
|
)
|
||||||
|
|
||||||
|
const RelativeEpsilon = 0.01
|
||||||
|
|
||||||
|
func verifyPercsWithAbsoluteEpsilon(t *testing.T, a []float64, s *Stream) {
|
||||||
|
sort.Float64s(a)
|
||||||
|
for quantile, epsilon := range Targets {
|
||||||
|
n := float64(len(a))
|
||||||
|
k := int(quantile * n)
|
||||||
|
if k < 1 {
|
||||||
|
k = 1
|
||||||
|
}
|
||||||
|
lower := int((quantile - epsilon) * n)
|
||||||
|
if lower < 1 {
|
||||||
|
lower = 1
|
||||||
|
}
|
||||||
|
upper := int(math.Ceil((quantile + epsilon) * n))
|
||||||
|
if upper > len(a) {
|
||||||
|
upper = len(a)
|
||||||
|
}
|
||||||
|
w, min, max := a[k-1], a[lower-1], a[upper-1]
|
||||||
|
if g := s.Query(quantile); g < min || g > max {
|
||||||
|
t.Errorf("q=%f: want %v [%f,%f], got %v", quantile, w, min, max, g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyLowPercsWithRelativeEpsilon(t *testing.T, a []float64, s *Stream) {
|
||||||
|
sort.Float64s(a)
|
||||||
|
for _, qu := range LowQuantiles {
|
||||||
|
n := float64(len(a))
|
||||||
|
k := int(qu * n)
|
||||||
|
|
||||||
|
lowerRank := int((1 - RelativeEpsilon) * qu * n)
|
||||||
|
upperRank := int(math.Ceil((1 + RelativeEpsilon) * qu * n))
|
||||||
|
w, min, max := a[k-1], a[lowerRank-1], a[upperRank-1]
|
||||||
|
if g := s.Query(qu); g < min || g > max {
|
||||||
|
t.Errorf("q=%f: want %v [%f,%f], got %v", qu, w, min, max, g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyHighPercsWithRelativeEpsilon(t *testing.T, a []float64, s *Stream) {
|
||||||
|
sort.Float64s(a)
|
||||||
|
for _, qu := range HighQuantiles {
|
||||||
|
n := float64(len(a))
|
||||||
|
k := int(qu * n)
|
||||||
|
|
||||||
|
lowerRank := int((1 - (1+RelativeEpsilon)*(1-qu)) * n)
|
||||||
|
upperRank := int(math.Ceil((1 - (1-RelativeEpsilon)*(1-qu)) * n))
|
||||||
|
w, min, max := a[k-1], a[lowerRank-1], a[upperRank-1]
|
||||||
|
if g := s.Query(qu); g < min || g > max {
|
||||||
|
t.Errorf("q=%f: want %v [%f,%f], got %v", qu, w, min, max, g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func populateStream(s *Stream) []float64 {
|
||||||
|
a := make([]float64, 0, 1e5+100)
|
||||||
|
for i := 0; i < cap(a); i++ {
|
||||||
|
v := rand.NormFloat64()
|
||||||
|
// Add 5% asymmetric outliers.
|
||||||
|
if i%20 == 0 {
|
||||||
|
v = v*v + 1
|
||||||
|
}
|
||||||
|
s.Insert(v)
|
||||||
|
a = append(a, v)
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTargetedQuery(t *testing.T) {
|
||||||
|
rand.Seed(42)
|
||||||
|
s := NewTargeted(Targets)
|
||||||
|
a := populateStream(s)
|
||||||
|
verifyPercsWithAbsoluteEpsilon(t, a, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTargetedQuerySmallSampleSize(t *testing.T) {
|
||||||
|
rand.Seed(42)
|
||||||
|
s := NewTargeted(TargetsSmallEpsilon)
|
||||||
|
a := []float64{1, 2, 3, 4, 5}
|
||||||
|
for _, v := range a {
|
||||||
|
s.Insert(v)
|
||||||
|
}
|
||||||
|
verifyPercsWithAbsoluteEpsilon(t, a, s)
|
||||||
|
// If not yet flushed, results should be precise:
|
||||||
|
if !s.flushed() {
|
||||||
|
for φ, want := range map[float64]float64{
|
||||||
|
0.01: 1,
|
||||||
|
0.10: 1,
|
||||||
|
0.50: 3,
|
||||||
|
0.90: 5,
|
||||||
|
0.99: 5,
|
||||||
|
} {
|
||||||
|
if got := s.Query(φ); got != want {
|
||||||
|
t.Errorf("want %f for φ=%f, got %f", want, φ, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLowBiasedQuery(t *testing.T) {
|
||||||
|
rand.Seed(42)
|
||||||
|
s := NewLowBiased(RelativeEpsilon)
|
||||||
|
a := populateStream(s)
|
||||||
|
verifyLowPercsWithRelativeEpsilon(t, a, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHighBiasedQuery(t *testing.T) {
|
||||||
|
rand.Seed(42)
|
||||||
|
s := NewHighBiased(RelativeEpsilon)
|
||||||
|
a := populateStream(s)
|
||||||
|
verifyHighPercsWithRelativeEpsilon(t, a, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BrokenTestTargetedMerge is broken, see Merge doc comment.
|
||||||
|
func BrokenTestTargetedMerge(t *testing.T) {
|
||||||
|
rand.Seed(42)
|
||||||
|
s1 := NewTargeted(Targets)
|
||||||
|
s2 := NewTargeted(Targets)
|
||||||
|
a := populateStream(s1)
|
||||||
|
a = append(a, populateStream(s2)...)
|
||||||
|
s1.Merge(s2.Samples())
|
||||||
|
verifyPercsWithAbsoluteEpsilon(t, a, s1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BrokenTestLowBiasedMerge is broken, see Merge doc comment.
|
||||||
|
func BrokenTestLowBiasedMerge(t *testing.T) {
|
||||||
|
rand.Seed(42)
|
||||||
|
s1 := NewLowBiased(RelativeEpsilon)
|
||||||
|
s2 := NewLowBiased(RelativeEpsilon)
|
||||||
|
a := populateStream(s1)
|
||||||
|
a = append(a, populateStream(s2)...)
|
||||||
|
s1.Merge(s2.Samples())
|
||||||
|
verifyLowPercsWithRelativeEpsilon(t, a, s2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BrokenTestHighBiasedMerge is broken, see Merge doc comment.
|
||||||
|
func BrokenTestHighBiasedMerge(t *testing.T) {
|
||||||
|
rand.Seed(42)
|
||||||
|
s1 := NewHighBiased(RelativeEpsilon)
|
||||||
|
s2 := NewHighBiased(RelativeEpsilon)
|
||||||
|
a := populateStream(s1)
|
||||||
|
a = append(a, populateStream(s2)...)
|
||||||
|
s1.Merge(s2.Samples())
|
||||||
|
verifyHighPercsWithRelativeEpsilon(t, a, s2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUncompressed(t *testing.T) {
|
||||||
|
q := NewTargeted(Targets)
|
||||||
|
for i := 100; i > 0; i-- {
|
||||||
|
q.Insert(float64(i))
|
||||||
|
}
|
||||||
|
if g := q.Count(); g != 100 {
|
||||||
|
t.Errorf("want count 100, got %d", g)
|
||||||
|
}
|
||||||
|
// Before compression, Query should have 100% accuracy.
|
||||||
|
for quantile := range Targets {
|
||||||
|
w := quantile * 100
|
||||||
|
if g := q.Query(quantile); g != w {
|
||||||
|
t.Errorf("want %f, got %f", w, g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUncompressedSamples(t *testing.T) {
|
||||||
|
q := NewTargeted(map[float64]float64{0.99: 0.001})
|
||||||
|
for i := 1; i <= 100; i++ {
|
||||||
|
q.Insert(float64(i))
|
||||||
|
}
|
||||||
|
if g := q.Samples().Len(); g != 100 {
|
||||||
|
t.Errorf("want count 100, got %d", g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUncompressedOne(t *testing.T) {
|
||||||
|
q := NewTargeted(map[float64]float64{0.99: 0.01})
|
||||||
|
q.Insert(3.14)
|
||||||
|
if g := q.Query(0.90); g != 3.14 {
|
||||||
|
t.Error("want PI, got", g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaults(t *testing.T) {
|
||||||
|
if g := NewTargeted(map[float64]float64{0.99: 0.001}).Query(0.99); g != 0 {
|
||||||
|
t.Errorf("want 0, got %f", g)
|
||||||
|
}
|
||||||
|
}
|
90
vendor/github.com/beorn7/perks/topk/topk.go
generated
vendored
Normal file
90
vendor/github.com/beorn7/perks/topk/topk.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package topk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// http://www.cs.ucsb.edu/research/tech_reports/reports/2005-23.pdf
|
||||||
|
|
||||||
|
type Element struct {
|
||||||
|
Value string
|
||||||
|
Count int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Samples []*Element
|
||||||
|
|
||||||
|
func (sm Samples) Len() int {
|
||||||
|
return len(sm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm Samples) Less(i, j int) bool {
|
||||||
|
return sm[i].Count < sm[j].Count
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm Samples) Swap(i, j int) {
|
||||||
|
sm[i], sm[j] = sm[j], sm[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stream struct {
|
||||||
|
k int
|
||||||
|
mon map[string]*Element
|
||||||
|
|
||||||
|
// the minimum Element
|
||||||
|
min *Element
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(k int) *Stream {
|
||||||
|
s := new(Stream)
|
||||||
|
s.k = k
|
||||||
|
s.mon = make(map[string]*Element)
|
||||||
|
s.min = &Element{}
|
||||||
|
|
||||||
|
// Track k+1 so that less frequenet items contended for that spot,
|
||||||
|
// resulting in k being more accurate.
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) Insert(x string) {
|
||||||
|
s.insert(&Element{x, 1})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) Merge(sm Samples) {
|
||||||
|
for _, e := range sm {
|
||||||
|
s.insert(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) insert(in *Element) {
|
||||||
|
e := s.mon[in.Value]
|
||||||
|
if e != nil {
|
||||||
|
e.Count++
|
||||||
|
} else {
|
||||||
|
if len(s.mon) < s.k+1 {
|
||||||
|
e = &Element{in.Value, in.Count}
|
||||||
|
s.mon[in.Value] = e
|
||||||
|
} else {
|
||||||
|
e = s.min
|
||||||
|
delete(s.mon, e.Value)
|
||||||
|
e.Value = in.Value
|
||||||
|
e.Count += in.Count
|
||||||
|
s.min = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if e.Count < s.min.Count {
|
||||||
|
s.min = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) Query() Samples {
|
||||||
|
var sm Samples
|
||||||
|
for _, e := range s.mon {
|
||||||
|
sm = append(sm, e)
|
||||||
|
}
|
||||||
|
sort.Sort(sort.Reverse(sm))
|
||||||
|
|
||||||
|
if len(sm) < s.k {
|
||||||
|
return sm
|
||||||
|
}
|
||||||
|
|
||||||
|
return sm[:s.k]
|
||||||
|
}
|
57
vendor/github.com/beorn7/perks/topk/topk_test.go
generated
vendored
Normal file
57
vendor/github.com/beorn7/perks/topk/topk_test.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package topk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTopK(t *testing.T) {
|
||||||
|
stream := New(10)
|
||||||
|
ss := []*Stream{New(10), New(10), New(10)}
|
||||||
|
m := make(map[string]int)
|
||||||
|
for _, s := range ss {
|
||||||
|
for i := 0; i < 1e6; i++ {
|
||||||
|
v := fmt.Sprintf("%x", int8(rand.ExpFloat64()))
|
||||||
|
s.Insert(v)
|
||||||
|
m[v]++
|
||||||
|
}
|
||||||
|
stream.Merge(s.Query())
|
||||||
|
}
|
||||||
|
|
||||||
|
var sm Samples
|
||||||
|
for x, s := range m {
|
||||||
|
sm = append(sm, &Element{x, s})
|
||||||
|
}
|
||||||
|
sort.Sort(sort.Reverse(sm))
|
||||||
|
|
||||||
|
g := stream.Query()
|
||||||
|
if len(g) != 10 {
|
||||||
|
t.Fatalf("got %d, want 10", len(g))
|
||||||
|
}
|
||||||
|
for i, e := range g {
|
||||||
|
if sm[i].Value != e.Value {
|
||||||
|
t.Errorf("at %d: want %q, got %q", i, sm[i].Value, e.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQuery(t *testing.T) {
|
||||||
|
queryTests := []struct {
|
||||||
|
value string
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"a", 1},
|
||||||
|
{"b", 2},
|
||||||
|
{"c", 2},
|
||||||
|
}
|
||||||
|
|
||||||
|
stream := New(2)
|
||||||
|
for _, tt := range queryTests {
|
||||||
|
stream.Insert(tt.value)
|
||||||
|
if n := len(stream.Query()); n != tt.expected {
|
||||||
|
t.Errorf("want %d, got %d", tt.expected, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
vendor/github.com/coreos/pkg/.gitignore
generated
vendored
Normal file
27
vendor/github.com/coreos/pkg/.gitignore
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
|
||||||
|
bin/
|
||||||
|
coverage/
|
8
vendor/github.com/coreos/pkg/.travis.yml
generated
vendored
Normal file
8
vendor/github.com/coreos/pkg/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.5.4
|
||||||
|
- 1.6.2
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./test
|
71
vendor/github.com/coreos/pkg/CONTRIBUTING.md
generated
vendored
Normal file
71
vendor/github.com/coreos/pkg/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# How to Contribute
|
||||||
|
|
||||||
|
CoreOS projects are [Apache 2.0 licensed](LICENSE) and accept contributions via
|
||||||
|
GitHub pull requests. This document outlines some of the conventions on
|
||||||
|
development workflow, commit message formatting, contact points and other
|
||||||
|
resources to make it easier to get your contribution accepted.
|
||||||
|
|
||||||
|
# Certificate of Origin
|
||||||
|
|
||||||
|
By contributing to this project you agree to the Developer Certificate of
|
||||||
|
Origin (DCO). This document was created by the Linux Kernel community and is a
|
||||||
|
simple statement that you, as a contributor, have the legal right to make the
|
||||||
|
contribution. See the [DCO](DCO) file for details.
|
||||||
|
|
||||||
|
# Email and Chat
|
||||||
|
|
||||||
|
The project currently uses the general CoreOS email list and IRC channel:
|
||||||
|
- Email: [coreos-dev](https://groups.google.com/forum/#!forum/coreos-dev)
|
||||||
|
- IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org
|
||||||
|
|
||||||
|
Please avoid emailing maintainers found in the MAINTAINERS file directly. They
|
||||||
|
are very busy and read the mailing lists.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
- Fork the repository on GitHub
|
||||||
|
- Read the [README](README.md) for build and test instructions
|
||||||
|
- Play with the project, submit bugs, submit patches!
|
||||||
|
|
||||||
|
## Contribution Flow
|
||||||
|
|
||||||
|
This is a rough outline of what a contributor's workflow looks like:
|
||||||
|
|
||||||
|
- Create a topic branch from where you want to base your work (usually master).
|
||||||
|
- Make commits of logical units.
|
||||||
|
- Make sure your commit messages are in the proper format (see below).
|
||||||
|
- Push your changes to a topic branch in your fork of the repository.
|
||||||
|
- Make sure the tests pass, and add any new tests as appropriate.
|
||||||
|
- Submit a pull request to the original repository.
|
||||||
|
|
||||||
|
Thanks for your contributions!
|
||||||
|
|
||||||
|
### Format of the Commit Message
|
||||||
|
|
||||||
|
We follow a rough convention for commit messages that is designed to answer two
|
||||||
|
questions: what changed and why. The subject line should feature the what and
|
||||||
|
the body of the commit should describe the why.
|
||||||
|
|
||||||
|
```
|
||||||
|
scripts: add the test-cluster command
|
||||||
|
|
||||||
|
this uses tmux to setup a test cluster that you can easily kill and
|
||||||
|
start for debugging.
|
||||||
|
|
||||||
|
Fixes #38
|
||||||
|
```
|
||||||
|
|
||||||
|
The format can be described more formally as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
<subsystem>: <what changed>
|
||||||
|
<BLANK LINE>
|
||||||
|
<why this change was made>
|
||||||
|
<BLANK LINE>
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
The first line is the subject and should be no longer than 70 characters, the
|
||||||
|
second line is always blank, and other lines should be wrapped at 80 characters.
|
||||||
|
This allows the message to be easier to read on GitHub as well as in various
|
||||||
|
git tools.
|
36
vendor/github.com/coreos/pkg/DCO
generated
vendored
Normal file
36
vendor/github.com/coreos/pkg/DCO
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
Developer Certificate of Origin
|
||||||
|
Version 1.1
|
||||||
|
|
||||||
|
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||||
|
660 York Street, Suite 102,
|
||||||
|
San Francisco, CA 94110 USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this
|
||||||
|
license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
Developer's Certificate of Origin 1.1
|
||||||
|
|
||||||
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
|
(a) The contribution was created in whole or in part by me and I
|
||||||
|
have the right to submit it under the open source license
|
||||||
|
indicated in the file; or
|
||||||
|
|
||||||
|
(b) The contribution is based upon previous work that, to the best
|
||||||
|
of my knowledge, is covered under an appropriate open source
|
||||||
|
license and I have the right under that license to submit that
|
||||||
|
work with modifications, whether created in whole or in part
|
||||||
|
by me, under the same open source license (unless I am
|
||||||
|
permitted to submit under a different license), as indicated
|
||||||
|
in the file; or
|
||||||
|
|
||||||
|
(c) The contribution was provided directly to me by some other
|
||||||
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
|
it.
|
||||||
|
|
||||||
|
(d) I understand and agree that this project and the contribution
|
||||||
|
are public and that a record of the contribution (including all
|
||||||
|
personal information I submit with it, including my sign-off) is
|
||||||
|
maintained indefinitely and may be redistributed consistent with
|
||||||
|
this project or the open source license(s) involved.
|
202
vendor/github.com/coreos/pkg/LICENSE
generated
vendored
Normal file
202
vendor/github.com/coreos/pkg/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
1
vendor/github.com/coreos/pkg/MAINTAINERS
generated
vendored
Normal file
1
vendor/github.com/coreos/pkg/MAINTAINERS
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Ed Rooth <ed.rooth@coreos.com> (@sym3tri)
|
5
vendor/github.com/coreos/pkg/NOTICE
generated
vendored
Normal file
5
vendor/github.com/coreos/pkg/NOTICE
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
CoreOS Project
|
||||||
|
Copyright 2014 CoreOS, Inc
|
||||||
|
|
||||||
|
This product includes software developed at CoreOS, Inc.
|
||||||
|
(http://www.coreos.com/).
|
4
vendor/github.com/coreos/pkg/README.md
generated
vendored
Normal file
4
vendor/github.com/coreos/pkg/README.md
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
a collection of go utility packages
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/coreos/pkg.png?branch=master)](https://travis-ci.org/coreos/pkg)
|
||||||
|
[![Godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/coreos/pkg)
|
3
vendor/github.com/coreos/pkg/build
generated
vendored
Executable file
3
vendor/github.com/coreos/pkg/build
generated
vendored
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
go build ./...
|
39
vendor/github.com/coreos/pkg/capnslog/README.md
generated
vendored
Normal file
39
vendor/github.com/coreos/pkg/capnslog/README.md
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# capnslog, the CoreOS logging package
|
||||||
|
|
||||||
|
There are far too many logging packages out there, with varying degrees of licenses, far too many features (colorization, all sorts of log frameworks) or are just a pain to use (lack of `Fatalln()`?).
|
||||||
|
capnslog provides a simple but consistent logging interface suitable for all kinds of projects.
|
||||||
|
|
||||||
|
### Design Principles
|
||||||
|
|
||||||
|
##### `package main` is the place where logging gets turned on and routed
|
||||||
|
|
||||||
|
A library should not touch log options, only generate log entries. Libraries are silent until main lets them speak.
|
||||||
|
|
||||||
|
##### All log options are runtime-configurable.
|
||||||
|
|
||||||
|
Still the job of `main` to expose these configurations. `main` may delegate this to, say, a configuration webhook, but does so explicitly.
|
||||||
|
|
||||||
|
##### There is one log object per package. It is registered under its repository and package name.
|
||||||
|
|
||||||
|
`main` activates logging for its repository and any dependency repositories it would also like to have output in its logstream. `main` also dictates at which level each subpackage logs.
|
||||||
|
|
||||||
|
##### There is *one* output stream, and it is an `io.Writer` composed with a formatter.
|
||||||
|
|
||||||
|
Splitting streams is probably not the job of your program, but rather, your log aggregation framework. If you must split output streams, again, `main` configures this and you can write a very simple two-output struct that satisfies io.Writer.
|
||||||
|
|
||||||
|
Fancy colorful formatting and JSON output are beyond the scope of a basic logging framework -- they're application/log-collector dependant. These are, at best, provided as options, but more likely, provided by your application.
|
||||||
|
|
||||||
|
##### Log objects are an interface
|
||||||
|
|
||||||
|
An object knows best how to print itself. Log objects can collect more interesting metadata if they wish, however, because text isn't going away anytime soon, they must all be marshalable to text. The simplest log object is a string, which returns itself. If you wish to do more fancy tricks for printing your log objects, see also JSON output -- introspect and write a formatter which can handle your advanced log interface. Making strings is the only thing guaranteed.
|
||||||
|
|
||||||
|
##### Log levels have specific meanings:
|
||||||
|
|
||||||
|
* Critical: Unrecoverable. Must fail.
|
||||||
|
* Error: Data has been lost, a request has failed for a bad reason, or a required resource has been lost
|
||||||
|
* Warning: (Hopefully) Temporary conditions that may cause errors, but may work fine. A replica disappearing (that may reconnect) is a warning.
|
||||||
|
* Notice: Normal, but important (uncommon) log information.
|
||||||
|
* Info: Normal, working log information, everything is fine, but helpful notices for auditing or common operations.
|
||||||
|
* Debug: Everything is still fine, but even common operations may be logged, and less helpful but more quantity of notices.
|
||||||
|
* Trace: Anything goes, from logging every function call as part of a common operation, to tracing execution of a query.
|
||||||
|
|
57
vendor/github.com/coreos/pkg/capnslog/example/hello_dolly.go
generated
vendored
Normal file
57
vendor/github.com/coreos/pkg/capnslog/example/hello_dolly.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
oldlog "log"
|
||||||
|
|
||||||
|
"github.com/coreos/pkg/capnslog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logLevel = capnslog.INFO
|
||||||
|
var log = capnslog.NewPackageLogger("github.com/coreos/pkg/capnslog/cmd", "main")
|
||||||
|
var dlog = capnslog.NewPackageLogger("github.com/coreos/pkg/capnslog/cmd", "dolly")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.Var(&logLevel, "log-level", "Global log level.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
rl := capnslog.MustRepoLogger("github.com/coreos/pkg/capnslog/cmd")
|
||||||
|
|
||||||
|
// We can parse the log level configs from the command line
|
||||||
|
flag.Parse()
|
||||||
|
if flag.NArg() > 1 {
|
||||||
|
cfg, err := rl.ParseLogLevelConfig(flag.Arg(1))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
rl.SetLogLevel(cfg)
|
||||||
|
log.Infof("Setting output to %s", flag.Arg(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send some messages at different levels to the different packages
|
||||||
|
dlog.Infof("Hello Dolly")
|
||||||
|
dlog.Warningf("Well hello, Dolly")
|
||||||
|
log.Errorf("It's so nice to have you back where you belong")
|
||||||
|
dlog.Debugf("You're looking swell, Dolly")
|
||||||
|
dlog.Tracef("I can tell, Dolly")
|
||||||
|
|
||||||
|
// We also have control over the built-in "log" package.
|
||||||
|
capnslog.SetGlobalLogLevel(logLevel)
|
||||||
|
oldlog.Println("You're still glowin', you're still crowin', you're still lookin' strong")
|
||||||
|
log.Fatalf("Dolly'll never go away again")
|
||||||
|
}
|
157
vendor/github.com/coreos/pkg/capnslog/formatters.go
generated
vendored
Normal file
157
vendor/github.com/coreos/pkg/capnslog/formatters.go
generated
vendored
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package capnslog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Formatter interface {
|
||||||
|
Format(pkg string, level LogLevel, depth int, entries ...interface{})
|
||||||
|
Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStringFormatter(w io.Writer) Formatter {
|
||||||
|
return &StringFormatter{
|
||||||
|
w: bufio.NewWriter(w),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type StringFormatter struct {
|
||||||
|
w *bufio.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StringFormatter) Format(pkg string, l LogLevel, i int, entries ...interface{}) {
|
||||||
|
now := time.Now().UTC()
|
||||||
|
s.w.WriteString(now.Format(time.RFC3339))
|
||||||
|
s.w.WriteByte(' ')
|
||||||
|
writeEntries(s.w, pkg, l, i, entries...)
|
||||||
|
s.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeEntries(w *bufio.Writer, pkg string, _ LogLevel, _ int, entries ...interface{}) {
|
||||||
|
if pkg != "" {
|
||||||
|
w.WriteString(pkg + ": ")
|
||||||
|
}
|
||||||
|
str := fmt.Sprint(entries...)
|
||||||
|
endsInNL := strings.HasSuffix(str, "\n")
|
||||||
|
w.WriteString(str)
|
||||||
|
if !endsInNL {
|
||||||
|
w.WriteString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StringFormatter) Flush() {
|
||||||
|
s.w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrettyFormatter(w io.Writer, debug bool) Formatter {
|
||||||
|
return &PrettyFormatter{
|
||||||
|
w: bufio.NewWriter(w),
|
||||||
|
debug: debug,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrettyFormatter struct {
|
||||||
|
w *bufio.Writer
|
||||||
|
debug bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PrettyFormatter) Format(pkg string, l LogLevel, depth int, entries ...interface{}) {
|
||||||
|
now := time.Now()
|
||||||
|
ts := now.Format("2006-01-02 15:04:05")
|
||||||
|
c.w.WriteString(ts)
|
||||||
|
ms := now.Nanosecond() / 1000
|
||||||
|
c.w.WriteString(fmt.Sprintf(".%06d", ms))
|
||||||
|
if c.debug {
|
||||||
|
_, file, line, ok := runtime.Caller(depth) // It's always the same number of frames to the user's call.
|
||||||
|
if !ok {
|
||||||
|
file = "???"
|
||||||
|
line = 1
|
||||||
|
} else {
|
||||||
|
slash := strings.LastIndex(file, "/")
|
||||||
|
if slash >= 0 {
|
||||||
|
file = file[slash+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if line < 0 {
|
||||||
|
line = 0 // not a real line number
|
||||||
|
}
|
||||||
|
c.w.WriteString(fmt.Sprintf(" [%s:%d]", file, line))
|
||||||
|
}
|
||||||
|
c.w.WriteString(fmt.Sprint(" ", l.Char(), " | "))
|
||||||
|
writeEntries(c.w, pkg, l, depth, entries...)
|
||||||
|
c.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PrettyFormatter) Flush() {
|
||||||
|
c.w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogFormatter emulates the form of the traditional built-in logger.
|
||||||
|
type LogFormatter struct {
|
||||||
|
logger *log.Logger
|
||||||
|
prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLogFormatter is a helper to produce a new LogFormatter struct. It uses the
|
||||||
|
// golang log package to actually do the logging work so that logs look similar.
|
||||||
|
func NewLogFormatter(w io.Writer, prefix string, flag int) Formatter {
|
||||||
|
return &LogFormatter{
|
||||||
|
logger: log.New(w, "", flag), // don't use prefix here
|
||||||
|
prefix: prefix, // save it instead
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format builds a log message for the LogFormatter. The LogLevel is ignored.
|
||||||
|
func (lf *LogFormatter) Format(pkg string, _ LogLevel, _ int, entries ...interface{}) {
|
||||||
|
str := fmt.Sprint(entries...)
|
||||||
|
prefix := lf.prefix
|
||||||
|
if pkg != "" {
|
||||||
|
prefix = fmt.Sprintf("%s%s: ", prefix, pkg)
|
||||||
|
}
|
||||||
|
lf.logger.Output(5, fmt.Sprintf("%s%v", prefix, str)) // call depth is 5
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush is included so that the interface is complete, but is a no-op.
|
||||||
|
func (lf *LogFormatter) Flush() {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
// NilFormatter is a no-op log formatter that does nothing.
|
||||||
|
type NilFormatter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNilFormatter is a helper to produce a new LogFormatter struct. It logs no
|
||||||
|
// messages so that you can cause part of your logging to be silent.
|
||||||
|
func NewNilFormatter() Formatter {
|
||||||
|
return &NilFormatter{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format does nothing.
|
||||||
|
func (_ *NilFormatter) Format(_ string, _ LogLevel, _ int, _ ...interface{}) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush is included so that the interface is complete, but is a no-op.
|
||||||
|
func (_ *NilFormatter) Flush() {
|
||||||
|
// noop
|
||||||
|
}
|
96
vendor/github.com/coreos/pkg/capnslog/glog_formatter.go
generated
vendored
Normal file
96
vendor/github.com/coreos/pkg/capnslog/glog_formatter.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package capnslog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pid = os.Getpid()
|
||||||
|
|
||||||
|
type GlogFormatter struct {
|
||||||
|
StringFormatter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGlogFormatter(w io.Writer) *GlogFormatter {
|
||||||
|
g := &GlogFormatter{}
|
||||||
|
g.w = bufio.NewWriter(w)
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GlogFormatter) Format(pkg string, level LogLevel, depth int, entries ...interface{}) {
|
||||||
|
g.w.Write(GlogHeader(level, depth+1))
|
||||||
|
g.StringFormatter.Format(pkg, level, depth+1, entries...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GlogHeader(level LogLevel, depth int) []byte {
|
||||||
|
// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
|
||||||
|
now := time.Now().UTC()
|
||||||
|
_, file, line, ok := runtime.Caller(depth) // It's always the same number of frames to the user's call.
|
||||||
|
if !ok {
|
||||||
|
file = "???"
|
||||||
|
line = 1
|
||||||
|
} else {
|
||||||
|
slash := strings.LastIndex(file, "/")
|
||||||
|
if slash >= 0 {
|
||||||
|
file = file[slash+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if line < 0 {
|
||||||
|
line = 0 // not a real line number
|
||||||
|
}
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
buf.Grow(30)
|
||||||
|
_, month, day := now.Date()
|
||||||
|
hour, minute, second := now.Clock()
|
||||||
|
buf.WriteString(level.Char())
|
||||||
|
twoDigits(buf, int(month))
|
||||||
|
twoDigits(buf, day)
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
twoDigits(buf, hour)
|
||||||
|
buf.WriteByte(':')
|
||||||
|
twoDigits(buf, minute)
|
||||||
|
buf.WriteByte(':')
|
||||||
|
twoDigits(buf, second)
|
||||||
|
buf.WriteByte('.')
|
||||||
|
buf.WriteString(strconv.Itoa(now.Nanosecond() / 1000))
|
||||||
|
buf.WriteByte('Z')
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
buf.WriteString(strconv.Itoa(pid))
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
buf.WriteString(file)
|
||||||
|
buf.WriteByte(':')
|
||||||
|
buf.WriteString(strconv.Itoa(line))
|
||||||
|
buf.WriteByte(']')
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
const digits = "0123456789"
|
||||||
|
|
||||||
|
func twoDigits(b *bytes.Buffer, d int) {
|
||||||
|
c2 := digits[d%10]
|
||||||
|
d /= 10
|
||||||
|
c1 := digits[d%10]
|
||||||
|
b.WriteByte(c1)
|
||||||
|
b.WriteByte(c2)
|
||||||
|
}
|
49
vendor/github.com/coreos/pkg/capnslog/init.go
generated
vendored
Normal file
49
vendor/github.com/coreos/pkg/capnslog/init.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package capnslog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Here's where the opinionation comes in. We need some sensible defaults,
|
||||||
|
// especially after taking over the log package. Your project (whatever it may
|
||||||
|
// be) may see things differently. That's okay; there should be no defaults in
|
||||||
|
// the main package that cannot be controlled or overridden programatically,
|
||||||
|
// otherwise it's a bug. Doing so is creating your own init_log.go file much
|
||||||
|
// like this one.
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initHijack()
|
||||||
|
|
||||||
|
// Go `log` pacakge uses os.Stderr.
|
||||||
|
SetFormatter(NewDefaultFormatter(os.Stderr))
|
||||||
|
SetGlobalLogLevel(INFO)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultFormatter(out io.Writer) Formatter {
|
||||||
|
if syscall.Getppid() == 1 {
|
||||||
|
// We're running under init, which may be systemd.
|
||||||
|
f, err := NewJournaldFormatter()
|
||||||
|
if err == nil {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NewPrettyFormatter(out, false)
|
||||||
|
}
|
25
vendor/github.com/coreos/pkg/capnslog/init_windows.go
generated
vendored
Normal file
25
vendor/github.com/coreos/pkg/capnslog/init_windows.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package capnslog
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initHijack()
|
||||||
|
|
||||||
|
// Go `log` package uses os.Stderr.
|
||||||
|
SetFormatter(NewPrettyFormatter(os.Stderr, false))
|
||||||
|
SetGlobalLogLevel(INFO)
|
||||||
|
}
|
68
vendor/github.com/coreos/pkg/capnslog/journald_formatter.go
generated
vendored
Normal file
68
vendor/github.com/coreos/pkg/capnslog/journald_formatter.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package capnslog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/coreos/go-systemd/journal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewJournaldFormatter() (Formatter, error) {
|
||||||
|
if !journal.Enabled() {
|
||||||
|
return nil, errors.New("No systemd detected")
|
||||||
|
}
|
||||||
|
return &journaldFormatter{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type journaldFormatter struct{}
|
||||||
|
|
||||||
|
func (j *journaldFormatter) Format(pkg string, l LogLevel, _ int, entries ...interface{}) {
|
||||||
|
var pri journal.Priority
|
||||||
|
switch l {
|
||||||
|
case CRITICAL:
|
||||||
|
pri = journal.PriCrit
|
||||||
|
case ERROR:
|
||||||
|
pri = journal.PriErr
|
||||||
|
case WARNING:
|
||||||
|
pri = journal.PriWarning
|
||||||
|
case NOTICE:
|
||||||
|
pri = journal.PriNotice
|
||||||
|
case INFO:
|
||||||
|
pri = journal.PriInfo
|
||||||
|
case DEBUG:
|
||||||
|
pri = journal.PriDebug
|
||||||
|
case TRACE:
|
||||||
|
pri = journal.PriDebug
|
||||||
|
default:
|
||||||
|
panic("Unhandled loglevel")
|
||||||
|
}
|
||||||
|
msg := fmt.Sprint(entries...)
|
||||||
|
tags := map[string]string{
|
||||||
|
"PACKAGE": pkg,
|
||||||
|
"SYSLOG_IDENTIFIER": filepath.Base(os.Args[0]),
|
||||||
|
}
|
||||||
|
err := journal.Send(msg, pri, tags)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journaldFormatter) Flush() {}
|
39
vendor/github.com/coreos/pkg/capnslog/log_hijack.go
generated
vendored
Normal file
39
vendor/github.com/coreos/pkg/capnslog/log_hijack.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package capnslog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initHijack() {
|
||||||
|
pkg := NewPackageLogger("log", "")
|
||||||
|
w := packageWriter{pkg}
|
||||||
|
log.SetFlags(0)
|
||||||
|
log.SetPrefix("")
|
||||||
|
log.SetOutput(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
type packageWriter struct {
|
||||||
|
pl *PackageLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p packageWriter) Write(b []byte) (int, error) {
|
||||||
|
if p.pl.level < INFO {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
p.pl.internalLog(calldepth+2, INFO, string(b))
|
||||||
|
return len(b), nil
|
||||||
|
}
|
240
vendor/github.com/coreos/pkg/capnslog/logmap.go
generated
vendored
Normal file
240
vendor/github.com/coreos/pkg/capnslog/logmap.go
generated
vendored
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package capnslog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogLevel is the set of all log levels.
|
||||||
|
type LogLevel int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CRITICAL is the lowest log level; only errors which will end the program will be propagated.
|
||||||
|
CRITICAL LogLevel = iota - 1
|
||||||
|
// ERROR is for errors that are not fatal but lead to troubling behavior.
|
||||||
|
ERROR
|
||||||
|
// WARNING is for errors which are not fatal and not errors, but are unusual. Often sourced from misconfigurations.
|
||||||
|
WARNING
|
||||||
|
// NOTICE is for normal but significant conditions.
|
||||||
|
NOTICE
|
||||||
|
// INFO is a log level for common, everyday log updates.
|
||||||
|
INFO
|
||||||
|
// DEBUG is the default hidden level for more verbose updates about internal processes.
|
||||||
|
DEBUG
|
||||||
|
// TRACE is for (potentially) call by call tracing of programs.
|
||||||
|
TRACE
|
||||||
|
)
|
||||||
|
|
||||||
|
// Char returns a single-character representation of the log level.
|
||||||
|
func (l LogLevel) Char() string {
|
||||||
|
switch l {
|
||||||
|
case CRITICAL:
|
||||||
|
return "C"
|
||||||
|
case ERROR:
|
||||||
|
return "E"
|
||||||
|
case WARNING:
|
||||||
|
return "W"
|
||||||
|
case NOTICE:
|
||||||
|
return "N"
|
||||||
|
case INFO:
|
||||||
|
return "I"
|
||||||
|
case DEBUG:
|
||||||
|
return "D"
|
||||||
|
case TRACE:
|
||||||
|
return "T"
|
||||||
|
default:
|
||||||
|
panic("Unhandled loglevel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a multi-character representation of the log level.
|
||||||
|
func (l LogLevel) String() string {
|
||||||
|
switch l {
|
||||||
|
case CRITICAL:
|
||||||
|
return "CRITICAL"
|
||||||
|
case ERROR:
|
||||||
|
return "ERROR"
|
||||||
|
case WARNING:
|
||||||
|
return "WARNING"
|
||||||
|
case NOTICE:
|
||||||
|
return "NOTICE"
|
||||||
|
case INFO:
|
||||||
|
return "INFO"
|
||||||
|
case DEBUG:
|
||||||
|
return "DEBUG"
|
||||||
|
case TRACE:
|
||||||
|
return "TRACE"
|
||||||
|
default:
|
||||||
|
panic("Unhandled loglevel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update using the given string value. Fulfills the flag.Value interface.
|
||||||
|
func (l *LogLevel) Set(s string) error {
|
||||||
|
value, err := ParseLevel(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*l = value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseLevel translates some potential loglevel strings into their corresponding levels.
|
||||||
|
func ParseLevel(s string) (LogLevel, error) {
|
||||||
|
switch s {
|
||||||
|
case "CRITICAL", "C":
|
||||||
|
return CRITICAL, nil
|
||||||
|
case "ERROR", "0", "E":
|
||||||
|
return ERROR, nil
|
||||||
|
case "WARNING", "1", "W":
|
||||||
|
return WARNING, nil
|
||||||
|
case "NOTICE", "2", "N":
|
||||||
|
return NOTICE, nil
|
||||||
|
case "INFO", "3", "I":
|
||||||
|
return INFO, nil
|
||||||
|
case "DEBUG", "4", "D":
|
||||||
|
return DEBUG, nil
|
||||||
|
case "TRACE", "5", "T":
|
||||||
|
return TRACE, nil
|
||||||
|
}
|
||||||
|
return CRITICAL, errors.New("couldn't parse log level " + s)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RepoLogger map[string]*PackageLogger
|
||||||
|
|
||||||
|
type loggerStruct struct {
|
||||||
|
sync.Mutex
|
||||||
|
repoMap map[string]RepoLogger
|
||||||
|
formatter Formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
// logger is the global logger
|
||||||
|
var logger = new(loggerStruct)
|
||||||
|
|
||||||
|
// SetGlobalLogLevel sets the log level for all packages in all repositories
|
||||||
|
// registered with capnslog.
|
||||||
|
func SetGlobalLogLevel(l LogLevel) {
|
||||||
|
logger.Lock()
|
||||||
|
defer logger.Unlock()
|
||||||
|
for _, r := range logger.repoMap {
|
||||||
|
r.setRepoLogLevelInternal(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepoLogger may return the handle to the repository's set of packages' loggers.
|
||||||
|
func GetRepoLogger(repo string) (RepoLogger, error) {
|
||||||
|
logger.Lock()
|
||||||
|
defer logger.Unlock()
|
||||||
|
r, ok := logger.repoMap[repo]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("no packages registered for repo " + repo)
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustRepoLogger returns the handle to the repository's packages' loggers.
|
||||||
|
func MustRepoLogger(repo string) RepoLogger {
|
||||||
|
r, err := GetRepoLogger(repo)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRepoLogLevel sets the log level for all packages in the repository.
|
||||||
|
func (r RepoLogger) SetRepoLogLevel(l LogLevel) {
|
||||||
|
logger.Lock()
|
||||||
|
defer logger.Unlock()
|
||||||
|
r.setRepoLogLevelInternal(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RepoLogger) setRepoLogLevelInternal(l LogLevel) {
|
||||||
|
for _, v := range r {
|
||||||
|
v.level = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseLogLevelConfig parses a comma-separated string of "package=loglevel", in
|
||||||
|
// order, and returns a map of the results, for use in SetLogLevel.
|
||||||
|
func (r RepoLogger) ParseLogLevelConfig(conf string) (map[string]LogLevel, error) {
|
||||||
|
setlist := strings.Split(conf, ",")
|
||||||
|
out := make(map[string]LogLevel)
|
||||||
|
for _, setstring := range setlist {
|
||||||
|
setting := strings.Split(setstring, "=")
|
||||||
|
if len(setting) != 2 {
|
||||||
|
return nil, errors.New("oddly structured `pkg=level` option: " + setstring)
|
||||||
|
}
|
||||||
|
l, err := ParseLevel(setting[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out[setting[0]] = l
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLogLevel takes a map of package names within a repository to their desired
|
||||||
|
// loglevel, and sets the levels appropriately. Unknown packages are ignored.
|
||||||
|
// "*" is a special package name that corresponds to all packages, and will be
|
||||||
|
// processed first.
|
||||||
|
func (r RepoLogger) SetLogLevel(m map[string]LogLevel) {
|
||||||
|
logger.Lock()
|
||||||
|
defer logger.Unlock()
|
||||||
|
if l, ok := m["*"]; ok {
|
||||||
|
r.setRepoLogLevelInternal(l)
|
||||||
|
}
|
||||||
|
for k, v := range m {
|
||||||
|
l, ok := r[k]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
l.level = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFormatter sets the formatting function for all logs.
|
||||||
|
func SetFormatter(f Formatter) {
|
||||||
|
logger.Lock()
|
||||||
|
defer logger.Unlock()
|
||||||
|
logger.formatter = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPackageLogger creates a package logger object.
|
||||||
|
// This should be defined as a global var in your package, referencing your repo.
|
||||||
|
func NewPackageLogger(repo string, pkg string) (p *PackageLogger) {
|
||||||
|
logger.Lock()
|
||||||
|
defer logger.Unlock()
|
||||||
|
if logger.repoMap == nil {
|
||||||
|
logger.repoMap = make(map[string]RepoLogger)
|
||||||
|
}
|
||||||
|
r, rok := logger.repoMap[repo]
|
||||||
|
if !rok {
|
||||||
|
logger.repoMap[repo] = make(RepoLogger)
|
||||||
|
r = logger.repoMap[repo]
|
||||||
|
}
|
||||||
|
p, pok := r[pkg]
|
||||||
|
if !pok {
|
||||||
|
r[pkg] = &PackageLogger{
|
||||||
|
pkg: pkg,
|
||||||
|
level: INFO,
|
||||||
|
}
|
||||||
|
p = r[pkg]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
177
vendor/github.com/coreos/pkg/capnslog/pkg_logger.go
generated
vendored
Normal file
177
vendor/github.com/coreos/pkg/capnslog/pkg_logger.go
generated
vendored
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package capnslog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PackageLogger struct {
|
||||||
|
pkg string
|
||||||
|
level LogLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
const calldepth = 2
|
||||||
|
|
||||||
|
func (p *PackageLogger) internalLog(depth int, inLevel LogLevel, entries ...interface{}) {
|
||||||
|
logger.Lock()
|
||||||
|
defer logger.Unlock()
|
||||||
|
if inLevel != CRITICAL && p.level < inLevel {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if logger.formatter != nil {
|
||||||
|
logger.formatter.Format(p.pkg, inLevel, depth+1, entries...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) LevelAt(l LogLevel) bool {
|
||||||
|
logger.Lock()
|
||||||
|
defer logger.Unlock()
|
||||||
|
return p.level >= l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log a formatted string at any level between ERROR and TRACE
|
||||||
|
func (p *PackageLogger) Logf(l LogLevel, format string, args ...interface{}) {
|
||||||
|
p.internalLog(calldepth, l, fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log a message at any level between ERROR and TRACE
|
||||||
|
func (p *PackageLogger) Log(l LogLevel, args ...interface{}) {
|
||||||
|
p.internalLog(calldepth, l, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// log stdlib compatibility
|
||||||
|
|
||||||
|
func (p *PackageLogger) Println(args ...interface{}) {
|
||||||
|
p.internalLog(calldepth, INFO, fmt.Sprintln(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) Printf(format string, args ...interface{}) {
|
||||||
|
p.Logf(INFO, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) Print(args ...interface{}) {
|
||||||
|
p.internalLog(calldepth, INFO, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panic and fatal
|
||||||
|
|
||||||
|
func (p *PackageLogger) Panicf(format string, args ...interface{}) {
|
||||||
|
s := fmt.Sprintf(format, args...)
|
||||||
|
p.internalLog(calldepth, CRITICAL, s)
|
||||||
|
panic(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) Panic(args ...interface{}) {
|
||||||
|
s := fmt.Sprint(args...)
|
||||||
|
p.internalLog(calldepth, CRITICAL, s)
|
||||||
|
panic(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) Fatalf(format string, args ...interface{}) {
|
||||||
|
p.Logf(CRITICAL, format, args...)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) Fatal(args ...interface{}) {
|
||||||
|
s := fmt.Sprint(args...)
|
||||||
|
p.internalLog(calldepth, CRITICAL, s)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) Fatalln(args ...interface{}) {
|
||||||
|
s := fmt.Sprintln(args...)
|
||||||
|
p.internalLog(calldepth, CRITICAL, s)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error Functions
|
||||||
|
|
||||||
|
func (p *PackageLogger) Errorf(format string, args ...interface{}) {
|
||||||
|
p.Logf(ERROR, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) Error(entries ...interface{}) {
|
||||||
|
p.internalLog(calldepth, ERROR, entries...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning Functions
|
||||||
|
|
||||||
|
func (p *PackageLogger) Warningf(format string, args ...interface{}) {
|
||||||
|
p.Logf(WARNING, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) Warning(entries ...interface{}) {
|
||||||
|
p.internalLog(calldepth, WARNING, entries...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notice Functions
|
||||||
|
|
||||||
|
func (p *PackageLogger) Noticef(format string, args ...interface{}) {
|
||||||
|
p.Logf(NOTICE, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) Notice(entries ...interface{}) {
|
||||||
|
p.internalLog(calldepth, NOTICE, entries...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info Functions
|
||||||
|
|
||||||
|
func (p *PackageLogger) Infof(format string, args ...interface{}) {
|
||||||
|
p.Logf(INFO, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) Info(entries ...interface{}) {
|
||||||
|
p.internalLog(calldepth, INFO, entries...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug Functions
|
||||||
|
|
||||||
|
func (p *PackageLogger) Debugf(format string, args ...interface{}) {
|
||||||
|
if p.level < DEBUG {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Logf(DEBUG, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) Debug(entries ...interface{}) {
|
||||||
|
if p.level < DEBUG {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.internalLog(calldepth, DEBUG, entries...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace Functions
|
||||||
|
|
||||||
|
func (p *PackageLogger) Tracef(format string, args ...interface{}) {
|
||||||
|
if p.level < TRACE {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Logf(TRACE, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) Trace(entries ...interface{}) {
|
||||||
|
if p.level < TRACE {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.internalLog(calldepth, TRACE, entries...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PackageLogger) Flush() {
|
||||||
|
logger.Lock()
|
||||||
|
defer logger.Unlock()
|
||||||
|
logger.formatter.Flush()
|
||||||
|
}
|
65
vendor/github.com/coreos/pkg/capnslog/syslog_formatter.go
generated
vendored
Normal file
65
vendor/github.com/coreos/pkg/capnslog/syslog_formatter.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package capnslog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/syslog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewSyslogFormatter(w *syslog.Writer) Formatter {
|
||||||
|
return &syslogFormatter{w}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultSyslogFormatter(tag string) (Formatter, error) {
|
||||||
|
w, err := syslog.New(syslog.LOG_DEBUG, tag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewSyslogFormatter(w), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type syslogFormatter struct {
|
||||||
|
w *syslog.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *syslogFormatter) Format(pkg string, l LogLevel, _ int, entries ...interface{}) {
|
||||||
|
for _, entry := range entries {
|
||||||
|
str := fmt.Sprint(entry)
|
||||||
|
switch l {
|
||||||
|
case CRITICAL:
|
||||||
|
s.w.Crit(str)
|
||||||
|
case ERROR:
|
||||||
|
s.w.Err(str)
|
||||||
|
case WARNING:
|
||||||
|
s.w.Warning(str)
|
||||||
|
case NOTICE:
|
||||||
|
s.w.Notice(str)
|
||||||
|
case INFO:
|
||||||
|
s.w.Info(str)
|
||||||
|
case DEBUG:
|
||||||
|
s.w.Debug(str)
|
||||||
|
case TRACE:
|
||||||
|
s.w.Debug(str)
|
||||||
|
default:
|
||||||
|
panic("Unhandled loglevel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *syslogFormatter) Flush() {
|
||||||
|
}
|
94
vendor/github.com/coreos/pkg/cryptoutil/aes.go
generated
vendored
Normal file
94
vendor/github.com/coreos/pkg/cryptoutil/aes.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package cryptoutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// pad uses the PKCS#7 padding scheme to align the a payload to a specific block size
|
||||||
|
func pad(plaintext []byte, bsize int) ([]byte, error) {
|
||||||
|
if bsize >= 256 {
|
||||||
|
return nil, errors.New("bsize must be < 256")
|
||||||
|
}
|
||||||
|
pad := bsize - (len(plaintext) % bsize)
|
||||||
|
if pad == 0 {
|
||||||
|
pad = bsize
|
||||||
|
}
|
||||||
|
for i := 0; i < pad; i++ {
|
||||||
|
plaintext = append(plaintext, byte(pad))
|
||||||
|
}
|
||||||
|
return plaintext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpad strips the padding previously added using the PKCS#7 padding scheme
|
||||||
|
func unpad(paddedtext []byte) ([]byte, error) {
|
||||||
|
length := len(paddedtext)
|
||||||
|
paddedtext, lbyte := paddedtext[:length-1], paddedtext[length-1]
|
||||||
|
pad := int(lbyte)
|
||||||
|
if pad >= 256 || pad > length {
|
||||||
|
return nil, errors.New("padding malformed")
|
||||||
|
}
|
||||||
|
return paddedtext[:length-(pad)], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AESEncrypt encrypts a payload with an AES cipher.
|
||||||
|
// The returned ciphertext has three notable properties:
|
||||||
|
// 1. ciphertext is aligned to the standard AES block size
|
||||||
|
// 2. ciphertext is padded using PKCS#7
|
||||||
|
// 3. IV is prepended to the ciphertext
|
||||||
|
func AESEncrypt(plaintext, key []byte) ([]byte, error) {
|
||||||
|
plaintext, err := pad(plaintext, aes.BlockSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
|
||||||
|
iv := ciphertext[:aes.BlockSize]
|
||||||
|
if _, err := rand.Read(iv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := cipher.NewCBCEncrypter(block, iv)
|
||||||
|
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
|
||||||
|
|
||||||
|
return ciphertext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AESDecrypt decrypts an encrypted payload with an AES cipher.
|
||||||
|
// The decryption algorithm makes three assumptions:
|
||||||
|
// 1. ciphertext is aligned to the standard AES block size
|
||||||
|
// 2. ciphertext is padded using PKCS#7
|
||||||
|
// 3. the IV is prepended to ciphertext
|
||||||
|
func AESDecrypt(ciphertext, key []byte) ([]byte, error) {
|
||||||
|
if len(ciphertext) < aes.BlockSize {
|
||||||
|
return nil, errors.New("ciphertext too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
iv := ciphertext[:aes.BlockSize]
|
||||||
|
ciphertext = ciphertext[aes.BlockSize:]
|
||||||
|
|
||||||
|
if len(ciphertext)%aes.BlockSize != 0 {
|
||||||
|
return nil, errors.New("ciphertext is not a multiple of the block size")
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := cipher.NewCBCDecrypter(block, iv)
|
||||||
|
mode.CryptBlocks(ciphertext, ciphertext)
|
||||||
|
|
||||||
|
if len(ciphertext)%aes.BlockSize != 0 {
|
||||||
|
return nil, errors.New("ciphertext is not a multiple of the block size")
|
||||||
|
}
|
||||||
|
|
||||||
|
return unpad(ciphertext)
|
||||||
|
}
|
93
vendor/github.com/coreos/pkg/cryptoutil/aes_test.go
generated
vendored
Normal file
93
vendor/github.com/coreos/pkg/cryptoutil/aes_test.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package cryptoutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPadUnpad(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
plaintext []byte
|
||||||
|
bsize int
|
||||||
|
padded []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
plaintext: []byte{1, 2, 3, 4},
|
||||||
|
bsize: 7,
|
||||||
|
padded: []byte{1, 2, 3, 4, 3, 3, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plaintext: []byte{1, 2, 3, 4, 5, 6, 7},
|
||||||
|
bsize: 3,
|
||||||
|
padded: []byte{1, 2, 3, 4, 5, 6, 7, 2, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plaintext: []byte{9, 9, 9, 9},
|
||||||
|
bsize: 4,
|
||||||
|
padded: []byte{9, 9, 9, 9, 4, 4, 4, 4},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
padded, err := pad(tt.plaintext, tt.bsize)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(tt.padded, padded) {
|
||||||
|
t.Errorf("case %d: want=%v got=%v", i, tt.padded, padded)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
plaintext, err := unpad(tt.padded)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(tt.plaintext, plaintext) {
|
||||||
|
t.Errorf("case %d: want=%v got=%v", i, tt.plaintext, plaintext)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPadMaxBlockSize(t *testing.T) {
|
||||||
|
_, err := pad([]byte{1, 2, 3}, 256)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected non-nil error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAESEncryptDecrypt(t *testing.T) {
|
||||||
|
message := []byte("Let me worry about blank.")
|
||||||
|
key := append([]byte("shark"), make([]byte, 27)...)
|
||||||
|
|
||||||
|
ciphertext, err := AESEncrypt(message, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if reflect.DeepEqual(message, ciphertext) {
|
||||||
|
t.Fatal("Encrypted data matches original payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypted, err := AESDecrypt(ciphertext, key)
|
||||||
|
if !reflect.DeepEqual(message, decrypted) {
|
||||||
|
t.Fatalf("Decrypted data does not match original payload: want=%v got=%v", message, decrypted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAESDecryptWrongKey(t *testing.T) {
|
||||||
|
message := []byte("My bones!")
|
||||||
|
key := append([]byte("shark"), make([]byte, 27)...)
|
||||||
|
|
||||||
|
ciphertext, err := AESEncrypt(message, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wrongKey := append([]byte("sheep"), make([]byte, 27)...)
|
||||||
|
decrypted, _ := AESDecrypt(ciphertext, wrongKey)
|
||||||
|
if reflect.DeepEqual(message, decrypted) {
|
||||||
|
t.Fatalf("Data decrypted with different key matches original payload")
|
||||||
|
}
|
||||||
|
}
|
82
vendor/github.com/coreos/pkg/dlopen/dlopen.go
generated
vendored
Normal file
82
vendor/github.com/coreos/pkg/dlopen/dlopen.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// Copyright 2016 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package dlopen provides some convenience functions to dlopen a library and
|
||||||
|
// get its symbols.
|
||||||
|
package dlopen
|
||||||
|
|
||||||
|
// #cgo LDFLAGS: -ldl
|
||||||
|
// #include <stdlib.h>
|
||||||
|
// #include <dlfcn.h>
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrSoNotFound = errors.New("unable to open a handle to the library")
|
||||||
|
|
||||||
|
// LibHandle represents an open handle to a library (.so)
|
||||||
|
type LibHandle struct {
|
||||||
|
Handle unsafe.Pointer
|
||||||
|
Libname string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHandle tries to get a handle to a library (.so), attempting to access it
|
||||||
|
// by the names specified in libs and returning the first that is successfully
|
||||||
|
// opened. Callers are responsible for closing the handler. If no library can
|
||||||
|
// be successfully opened, an error is returned.
|
||||||
|
func GetHandle(libs []string) (*LibHandle, error) {
|
||||||
|
for _, name := range libs {
|
||||||
|
libname := C.CString(name)
|
||||||
|
defer C.free(unsafe.Pointer(libname))
|
||||||
|
handle := C.dlopen(libname, C.RTLD_LAZY)
|
||||||
|
if handle != nil {
|
||||||
|
h := &LibHandle{
|
||||||
|
Handle: handle,
|
||||||
|
Libname: name,
|
||||||
|
}
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, ErrSoNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSymbolPointer takes a symbol name and returns a pointer to the symbol.
|
||||||
|
func (l *LibHandle) GetSymbolPointer(symbol string) (unsafe.Pointer, error) {
|
||||||
|
sym := C.CString(symbol)
|
||||||
|
defer C.free(unsafe.Pointer(sym))
|
||||||
|
|
||||||
|
C.dlerror()
|
||||||
|
p := C.dlsym(l.Handle, sym)
|
||||||
|
e := C.dlerror()
|
||||||
|
if e != nil {
|
||||||
|
return nil, fmt.Errorf("error resolving symbol %q: %v", symbol, errors.New(C.GoString(e)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes a LibHandle.
|
||||||
|
func (l *LibHandle) Close() error {
|
||||||
|
C.dlerror()
|
||||||
|
C.dlclose(l.Handle)
|
||||||
|
e := C.dlerror()
|
||||||
|
if e != nil {
|
||||||
|
return fmt.Errorf("error closing %v: %v", l.Libname, errors.New(C.GoString(e)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
56
vendor/github.com/coreos/pkg/dlopen/dlopen_example.go
generated
vendored
Normal file
56
vendor/github.com/coreos/pkg/dlopen/dlopen_example.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package dlopen
|
||||||
|
|
||||||
|
// #include <string.h>
|
||||||
|
// #include <stdlib.h>
|
||||||
|
//
|
||||||
|
// int
|
||||||
|
// my_strlen(void *f, const char *s)
|
||||||
|
// {
|
||||||
|
// size_t (*strlen)(const char *);
|
||||||
|
//
|
||||||
|
// strlen = (size_t (*)(const char *))f;
|
||||||
|
// return strlen(s);
|
||||||
|
// }
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func strlen(libs []string, s string) (int, error) {
|
||||||
|
h, err := GetHandle(libs)
|
||||||
|
if err != nil {
|
||||||
|
return -1, fmt.Errorf(`couldn't get a handle to the library: %v`, err)
|
||||||
|
}
|
||||||
|
defer h.Close()
|
||||||
|
|
||||||
|
f := "strlen"
|
||||||
|
cs := C.CString(s)
|
||||||
|
defer C.free(unsafe.Pointer(cs))
|
||||||
|
|
||||||
|
strlen, err := h.GetSymbolPointer(f)
|
||||||
|
if err != nil {
|
||||||
|
return -1, fmt.Errorf(`couldn't get symbol %q: %v`, f, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
len := C.my_strlen(strlen, cs)
|
||||||
|
|
||||||
|
return int(len), nil
|
||||||
|
}
|
63
vendor/github.com/coreos/pkg/dlopen/dlopen_test.go
generated
vendored
Normal file
63
vendor/github.com/coreos/pkg/dlopen/dlopen_test.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
package dlopen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkFailure(shouldSucceed bool, err error) (rErr error) {
|
||||||
|
switch {
|
||||||
|
case err != nil && shouldSucceed:
|
||||||
|
rErr = fmt.Errorf("expected test to succeed, failed unexpectedly: %v", err)
|
||||||
|
case err == nil && !shouldSucceed:
|
||||||
|
rErr = fmt.Errorf("expected test to fail, succeeded unexpectedly")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDlopen(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
libs []string
|
||||||
|
shouldSucceed bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
libs: []string{
|
||||||
|
"libc.so.6",
|
||||||
|
"libc.so",
|
||||||
|
},
|
||||||
|
shouldSucceed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
libs: []string{
|
||||||
|
"libstrange.so",
|
||||||
|
},
|
||||||
|
shouldSucceed: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
expLen := 4
|
||||||
|
len, err := strlen(tt.libs, "test")
|
||||||
|
if checkFailure(tt.shouldSucceed, err) != nil {
|
||||||
|
t.Errorf("case %d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.shouldSucceed && len != expLen {
|
||||||
|
t.Errorf("case %d: expected length %d, got %d", i, expLen, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
vendor/github.com/coreos/pkg/flagutil/env.go
generated
vendored
Normal file
33
vendor/github.com/coreos/pkg/flagutil/env.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package flagutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetFlagsFromEnv parses all registered flags in the given flagset,
|
||||||
|
// and if they are not already set it attempts to set their values from
|
||||||
|
// environment variables. Environment variables take the name of the flag but
|
||||||
|
// are UPPERCASE, and any dashes are replaced by underscores. Environment
|
||||||
|
// variables additionally are prefixed by the given string followed by
|
||||||
|
// and underscore. For example, if prefix=PREFIX: some-flag => PREFIX_SOME_FLAG
|
||||||
|
func SetFlagsFromEnv(fs *flag.FlagSet, prefix string) (err error) {
|
||||||
|
alreadySet := make(map[string]bool)
|
||||||
|
fs.Visit(func(f *flag.Flag) {
|
||||||
|
alreadySet[f.Name] = true
|
||||||
|
})
|
||||||
|
fs.VisitAll(func(f *flag.Flag) {
|
||||||
|
if !alreadySet[f.Name] {
|
||||||
|
key := prefix + "_" + strings.ToUpper(strings.Replace(f.Name, "-", "_", -1))
|
||||||
|
val := os.Getenv(key)
|
||||||
|
if val != "" {
|
||||||
|
if serr := fs.Set(f.Name, val); serr != nil {
|
||||||
|
err = fmt.Errorf("invalid value %q for %s: %v", val, key, serr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
77
vendor/github.com/coreos/pkg/flagutil/env_file.go
generated
vendored
Normal file
77
vendor/github.com/coreos/pkg/flagutil/env_file.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package flagutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetFlagsFromEnvFile iterates the given flagset and if any flags are not
|
||||||
|
// already set it attempts to set their values from the given env file. Env
|
||||||
|
// files may have KEY=VALUE lines where the environment variable names are
|
||||||
|
// in UPPERCASE, prefixed by the given PREFIX, and dashes are replaced by
|
||||||
|
// underscores. For example, if prefix=PREFIX, some-flag is named
|
||||||
|
// PREFIX_SOME_FLAG.
|
||||||
|
// Comment lines are skipped, but more complex env file parsing is not
|
||||||
|
// performed.
|
||||||
|
func SetFlagsFromEnvFile(fs *flag.FlagSet, prefix string, path string) (err error) {
|
||||||
|
alreadySet := make(map[string]bool)
|
||||||
|
fs.Visit(func(f *flag.Flag) {
|
||||||
|
alreadySet[f.Name] = true
|
||||||
|
})
|
||||||
|
envs, err := parseEnvFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fs.VisitAll(func(f *flag.Flag) {
|
||||||
|
if !alreadySet[f.Name] {
|
||||||
|
key := prefix + "_" + strings.ToUpper(strings.Replace(f.Name, "-", "_", -1))
|
||||||
|
val := envs[key]
|
||||||
|
if val != "" {
|
||||||
|
if serr := fs.Set(f.Name, val); serr != nil {
|
||||||
|
err = fmt.Errorf("invalid value %q for %s: %v", val, key, serr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEnvFile(path string) (map[string]string, error) {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
envs := make(map[string]string)
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
token := scanner.Text()
|
||||||
|
if !skipLine(token) {
|
||||||
|
key, val, err := parseLine(token)
|
||||||
|
if err == nil {
|
||||||
|
envs[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return envs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipLine(line string) bool {
|
||||||
|
return len(line) == 0 || strings.HasPrefix(line, "#")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLine(line string) (key string, val string, err error) {
|
||||||
|
trimmed := strings.TrimSpace(line)
|
||||||
|
pair := strings.SplitN(trimmed, "=", 2)
|
||||||
|
if len(pair) != 2 {
|
||||||
|
err = fmt.Errorf("invalid KEY=value line: %q", line)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key = strings.TrimSpace(pair[0])
|
||||||
|
val = strings.TrimSpace(pair[1])
|
||||||
|
return
|
||||||
|
}
|
64
vendor/github.com/coreos/pkg/flagutil/env_test.go
generated
vendored
Normal file
64
vendor/github.com/coreos/pkg/flagutil/env_test.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package flagutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetFlagsFromEnv(t *testing.T) {
|
||||||
|
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||||
|
fs.String("a", "", "")
|
||||||
|
fs.String("b", "", "")
|
||||||
|
fs.String("c", "", "")
|
||||||
|
fs.Parse([]string{})
|
||||||
|
|
||||||
|
os.Clearenv()
|
||||||
|
// flags should be settable using env vars
|
||||||
|
os.Setenv("MYPROJ_A", "foo")
|
||||||
|
// and command-line flags
|
||||||
|
if err := fs.Set("b", "bar"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// command-line flags take precedence over env vars
|
||||||
|
os.Setenv("MYPROJ_C", "woof")
|
||||||
|
if err := fs.Set("c", "quack"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// first verify that flags are as expected before reading the env
|
||||||
|
for f, want := range map[string]string{
|
||||||
|
"a": "",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "quack",
|
||||||
|
} {
|
||||||
|
if got := fs.Lookup(f).Value.String(); got != want {
|
||||||
|
t.Fatalf("flag %q=%q, want %q", f, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now read the env and verify flags were updated as expected
|
||||||
|
err := SetFlagsFromEnv(fs, "MYPROJ")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err=%v, want nil", err)
|
||||||
|
}
|
||||||
|
for f, want := range map[string]string{
|
||||||
|
"a": "foo",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "quack",
|
||||||
|
} {
|
||||||
|
if got := fs.Lookup(f).Value.String(); got != want {
|
||||||
|
t.Errorf("flag %q=%q, want %q", f, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetFlagsFromEnvBad(t *testing.T) {
|
||||||
|
// now verify that an error is propagated
|
||||||
|
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||||
|
fs.Int("x", 0, "")
|
||||||
|
os.Setenv("MYPROJ_X", "not_a_number")
|
||||||
|
if err := SetFlagsFromEnv(fs, "MYPROJ"); err == nil {
|
||||||
|
t.Errorf("err=nil, want != nil")
|
||||||
|
}
|
||||||
|
}
|
107
vendor/github.com/coreos/pkg/flagutil/file_env_test.go
generated
vendored
Normal file
107
vendor/github.com/coreos/pkg/flagutil/file_env_test.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package flagutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var envFile = `
|
||||||
|
# some secret env vars
|
||||||
|
MYPROJ_A=foo
|
||||||
|
MYPROJ_C=woof
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestSetFlagsFromEnvFile(t *testing.T) {
|
||||||
|
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||||
|
fs.String("a", "", "")
|
||||||
|
fs.String("b", "", "")
|
||||||
|
fs.String("c", "", "")
|
||||||
|
fs.Parse([]string{})
|
||||||
|
|
||||||
|
// add command-line flags
|
||||||
|
if err := fs.Set("b", "bar"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := fs.Set("c", "quack"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// first verify that flags are as expected before reading the env
|
||||||
|
for f, want := range map[string]string{
|
||||||
|
"a": "",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "quack",
|
||||||
|
} {
|
||||||
|
if got := fs.Lookup(f).Value.String(); got != want {
|
||||||
|
t.Fatalf("flag %q=%q, want %q", f, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := ioutil.TempFile("", "env-file")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(file.Name())
|
||||||
|
file.Write([]byte(envFile))
|
||||||
|
|
||||||
|
// read env file and verify flags were updated as expected
|
||||||
|
err = SetFlagsFromEnvFile(fs, "MYPROJ", file.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err=%v, want nil", err)
|
||||||
|
}
|
||||||
|
for f, want := range map[string]string{
|
||||||
|
"a": "foo",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "quack",
|
||||||
|
} {
|
||||||
|
if got := fs.Lookup(f).Value.String(); got != want {
|
||||||
|
t.Errorf("flag %q=%q, want %q", f, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetFlagsFromEnvFile_FlagSetError(t *testing.T) {
|
||||||
|
// now verify that an error is propagated
|
||||||
|
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||||
|
fs.Int("x", 0, "")
|
||||||
|
file, err := ioutil.TempFile("", "env-file")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(file.Name())
|
||||||
|
file.Write([]byte("MYPROJ_X=not_a_number"))
|
||||||
|
if err := SetFlagsFromEnvFile(fs, "MYPROJ", file.Name()); err == nil {
|
||||||
|
t.Errorf("err=nil, want != nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseLine(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
line string
|
||||||
|
expectedKey string
|
||||||
|
expectedVal string
|
||||||
|
nilErr bool
|
||||||
|
}{
|
||||||
|
{"key=value", "key", "value", true},
|
||||||
|
{" key = value ", "key", "value", true},
|
||||||
|
{"key='#gopher' #blah", "key", "'#gopher' #blah", true},
|
||||||
|
// invalid
|
||||||
|
{"key:value", "", "", false},
|
||||||
|
{"keyvalue", "", "", false},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
key, val, err := parseLine(c.line)
|
||||||
|
if (err == nil) != c.nilErr {
|
||||||
|
if c.nilErr {
|
||||||
|
t.Errorf("got %s, want err=nil", err)
|
||||||
|
} else {
|
||||||
|
t.Errorf("got err=nil, want err!=nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.expectedKey != key || c.expectedVal != val {
|
||||||
|
t.Errorf("got %q=%q, want %q=%q", key, val, c.expectedKey, c.expectedVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
vendor/github.com/coreos/pkg/flagutil/types.go
generated
vendored
Normal file
44
vendor/github.com/coreos/pkg/flagutil/types.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package flagutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPv4Flag parses a string into a net.IP after asserting that it
|
||||||
|
// is an IPv4 address. This type implements the flag.Value interface.
|
||||||
|
type IPv4Flag struct {
|
||||||
|
val net.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *IPv4Flag) IP() net.IP {
|
||||||
|
return f.val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *IPv4Flag) Set(v string) error {
|
||||||
|
ip := net.ParseIP(v)
|
||||||
|
if ip == nil || ip.To4() == nil {
|
||||||
|
return errors.New("not an IPv4 address")
|
||||||
|
}
|
||||||
|
f.val = ip
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *IPv4Flag) String() string {
|
||||||
|
return f.val.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSliceFlag parses a comma-delimited list of strings into
|
||||||
|
// a []string. This type implements the flag.Value interface.
|
||||||
|
type StringSliceFlag []string
|
||||||
|
|
||||||
|
func (ss *StringSliceFlag) String() string {
|
||||||
|
return fmt.Sprintf("%+v", *ss)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *StringSliceFlag) Set(v string) error {
|
||||||
|
*ss = strings.Split(v, ",")
|
||||||
|
return nil
|
||||||
|
}
|
57
vendor/github.com/coreos/pkg/flagutil/types_test.go
generated
vendored
Normal file
57
vendor/github.com/coreos/pkg/flagutil/types_test.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package flagutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIPv4FlagSetInvalidArgument(t *testing.T) {
|
||||||
|
tests := []string{
|
||||||
|
"",
|
||||||
|
"foo",
|
||||||
|
"::",
|
||||||
|
"127.0.0.1:4328",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
var f IPv4Flag
|
||||||
|
if err := f.Set(tt); err == nil {
|
||||||
|
t.Errorf("case %d: expected non-nil error", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIPv4FlagSetValidArgument(t *testing.T) {
|
||||||
|
tests := []string{
|
||||||
|
"127.0.0.1",
|
||||||
|
"0.0.0.0",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
var f IPv4Flag
|
||||||
|
if err := f.Set(tt); err != nil {
|
||||||
|
t.Errorf("case %d: err=%v", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringSliceFlag(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{input: "", want: []string{""}},
|
||||||
|
{input: "foo", want: []string{"foo"}},
|
||||||
|
{input: "foo,bar", want: []string{"foo", "bar"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
var f StringSliceFlag
|
||||||
|
if err := f.Set(tt.input); err != nil {
|
||||||
|
t.Errorf("case %d: err=%v", i, err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(tt.want, []string(f)) {
|
||||||
|
t.Errorf("case %d: want=%v got=%v", i, tt.want, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
vendor/github.com/coreos/pkg/health/README.md
generated
vendored
Normal file
11
vendor/github.com/coreos/pkg/health/README.md
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
health
|
||||||
|
====
|
||||||
|
|
||||||
|
A simple framework for implementing an HTTP health check endpoint on servers.
|
||||||
|
|
||||||
|
Users implement their `health.Checkable` types, and create a `health.Checker`, from which they can get an `http.HandlerFunc` using `health.Checker.MakeHealthHandlerFunc`.
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
For more details, visit the docs on [gopkgdoc](http://godoc.org/github.com/coreos/pkg/health)
|
||||||
|
|
127
vendor/github.com/coreos/pkg/health/health.go
generated
vendored
Normal file
127
vendor/github.com/coreos/pkg/health/health.go
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
package health
|
||||||
|
|
||||||
|
import (
|
||||||
|
"expvar"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/coreos/pkg/httputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Checkables should return nil when the thing they are checking is healthy, and an error otherwise.
|
||||||
|
type Checkable interface {
|
||||||
|
Healthy() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checker provides a way to make an endpoint which can be probed for system health.
|
||||||
|
type Checker struct {
|
||||||
|
// Checks are the Checkables to be checked when probing.
|
||||||
|
Checks []Checkable
|
||||||
|
|
||||||
|
// Unhealthyhandler is called when one or more of the checks are unhealthy.
|
||||||
|
// If not provided DefaultUnhealthyHandler is called.
|
||||||
|
UnhealthyHandler UnhealthyHandler
|
||||||
|
|
||||||
|
// HealthyHandler is called when all checks are healthy.
|
||||||
|
// If not provided, DefaultHealthyHandler is called.
|
||||||
|
HealthyHandler http.HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Checker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
unhealthyHandler := c.UnhealthyHandler
|
||||||
|
if unhealthyHandler == nil {
|
||||||
|
unhealthyHandler = DefaultUnhealthyHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
successHandler := c.HealthyHandler
|
||||||
|
if successHandler == nil {
|
||||||
|
successHandler = DefaultHealthyHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method != "GET" {
|
||||||
|
w.Header().Set("Allow", "GET")
|
||||||
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Check(c.Checks); err != nil {
|
||||||
|
unhealthyHandler(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
successHandler(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnhealthyHandler func(w http.ResponseWriter, r *http.Request, err error)
|
||||||
|
|
||||||
|
type StatusResponse struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Details *StatusResponseDetails `json:"details,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatusResponseDetails struct {
|
||||||
|
Code int `json:"code,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Check(checks []Checkable) (err error) {
|
||||||
|
errs := []error{}
|
||||||
|
for _, c := range checks {
|
||||||
|
if e := c.Healthy(); e != nil {
|
||||||
|
errs = append(errs, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(errs) {
|
||||||
|
case 0:
|
||||||
|
err = nil
|
||||||
|
case 1:
|
||||||
|
err = errs[0]
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("multiple health check failure: %v", errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultHealthyHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := httputil.WriteJSONResponse(w, http.StatusOK, StatusResponse{
|
||||||
|
Status: "ok",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// TODO(bobbyrullo): replace with logging from new logging pkg,
|
||||||
|
// once it lands.
|
||||||
|
log.Printf("Failed to write JSON response: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultUnhealthyHandler(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
|
writeErr := httputil.WriteJSONResponse(w, http.StatusInternalServerError, StatusResponse{
|
||||||
|
Status: "error",
|
||||||
|
Details: &StatusResponseDetails{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
Message: err.Error(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if writeErr != nil {
|
||||||
|
// TODO(bobbyrullo): replace with logging from new logging pkg,
|
||||||
|
// once it lands.
|
||||||
|
log.Printf("Failed to write JSON response: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpvarHandler is copied from https://golang.org/src/expvar/expvar.go, where it's sadly unexported.
|
||||||
|
func ExpvarHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
fmt.Fprintf(w, "{\n")
|
||||||
|
first := true
|
||||||
|
expvar.Do(func(kv expvar.KeyValue) {
|
||||||
|
if !first {
|
||||||
|
fmt.Fprintf(w, ",\n")
|
||||||
|
}
|
||||||
|
first = false
|
||||||
|
fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
|
||||||
|
})
|
||||||
|
fmt.Fprintf(w, "\n}\n")
|
||||||
|
}
|
198
vendor/github.com/coreos/pkg/health/health_test.go
generated
vendored
Normal file
198
vendor/github.com/coreos/pkg/health/health_test.go
generated
vendored
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
package health
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coreos/pkg/httputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type boolChecker bool
|
||||||
|
|
||||||
|
func (b boolChecker) Healthy() error {
|
||||||
|
if b {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("Unhealthy")
|
||||||
|
}
|
||||||
|
|
||||||
|
func errString(err error) string {
|
||||||
|
if err == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheck(t *testing.T) {
|
||||||
|
for i, test := range []struct {
|
||||||
|
checks []Checkable
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{[]Checkable{}, ""},
|
||||||
|
|
||||||
|
{[]Checkable{boolChecker(true)}, ""},
|
||||||
|
|
||||||
|
{[]Checkable{boolChecker(true), boolChecker(true)}, ""},
|
||||||
|
|
||||||
|
{[]Checkable{boolChecker(true), boolChecker(false)}, "Unhealthy"},
|
||||||
|
|
||||||
|
{[]Checkable{boolChecker(true), boolChecker(false), boolChecker(false)}, "multiple health check failure: [Unhealthy Unhealthy]"},
|
||||||
|
} {
|
||||||
|
err := Check(test.checks)
|
||||||
|
|
||||||
|
if errString(err) != test.expected {
|
||||||
|
t.Errorf("case %d: want %v, got %v", i, test.expected, errString(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandlerFunc(t *testing.T) {
|
||||||
|
for i, test := range []struct {
|
||||||
|
checker Checker
|
||||||
|
method string
|
||||||
|
expectedStatus string
|
||||||
|
expectedCode int
|
||||||
|
expectedMessage string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Checker{
|
||||||
|
Checks: []Checkable{
|
||||||
|
boolChecker(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"GET",
|
||||||
|
"ok",
|
||||||
|
http.StatusOK,
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Wrong method.
|
||||||
|
{
|
||||||
|
Checker{
|
||||||
|
Checks: []Checkable{
|
||||||
|
boolChecker(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"POST",
|
||||||
|
"",
|
||||||
|
http.StatusMethodNotAllowed,
|
||||||
|
"GET only acceptable method",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Health check fails.
|
||||||
|
{
|
||||||
|
Checker{
|
||||||
|
Checks: []Checkable{
|
||||||
|
boolChecker(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"GET",
|
||||||
|
"error",
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
"Unhealthy",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Health check fails, with overridden ErrorHandler.
|
||||||
|
{
|
||||||
|
Checker{
|
||||||
|
Checks: []Checkable{
|
||||||
|
boolChecker(false),
|
||||||
|
},
|
||||||
|
UnhealthyHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
|
httputil.WriteJSONResponse(w,
|
||||||
|
http.StatusInternalServerError, StatusResponse{
|
||||||
|
Status: "error",
|
||||||
|
Details: &StatusResponseDetails{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
Message: "Override!",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"GET",
|
||||||
|
"error",
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
"Override!",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Health check succeeds, with overridden SuccessHandler.
|
||||||
|
{
|
||||||
|
Checker{
|
||||||
|
Checks: []Checkable{
|
||||||
|
boolChecker(true),
|
||||||
|
},
|
||||||
|
HealthyHandler: func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
httputil.WriteJSONResponse(w,
|
||||||
|
http.StatusOK, StatusResponse{
|
||||||
|
Status: "okey-dokey",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"GET",
|
||||||
|
"okey-dokey",
|
||||||
|
http.StatusOK,
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
r := &http.Request{}
|
||||||
|
r.Method = test.method
|
||||||
|
test.checker.ServeHTTP(w, r)
|
||||||
|
if w.Code != test.expectedCode {
|
||||||
|
t.Errorf("case %d: w.code == %v, want %v", i, w.Code, test.expectedCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.expectedStatus == "" {
|
||||||
|
// This is to handle the wrong-method case, when the
|
||||||
|
// body of the response is empty.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
statusMap := make(map[string]interface{})
|
||||||
|
err := json.Unmarshal(w.Body.Bytes(), &statusMap)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("case %d: failed to Unmarshal response body: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, ok := statusMap["status"].(string)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("case %d: status not present or not a string in json: %q", i, w.Body.Bytes())
|
||||||
|
}
|
||||||
|
if status != test.expectedStatus {
|
||||||
|
t.Errorf("case %d: status == %v, want %v", i, status, test.expectedStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
detailMap, ok := statusMap["details"].(map[string]interface{})
|
||||||
|
if test.expectedMessage != "" {
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("case %d: could not find/unmarshal detailMap", i)
|
||||||
|
}
|
||||||
|
message, ok := detailMap["message"].(string)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("case %d: message not present or not a string in json: %q",
|
||||||
|
i, w.Body.Bytes())
|
||||||
|
}
|
||||||
|
if message != test.expectedMessage {
|
||||||
|
t.Errorf("case %d: message == %v, want %v", i, message, test.expectedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
code, ok := detailMap["code"].(float64)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("case %d: code not present or not an int in json: %q",
|
||||||
|
i, w.Body.Bytes())
|
||||||
|
}
|
||||||
|
if int(code) != test.expectedCode {
|
||||||
|
t.Errorf("case %d: code == %v, want %v", i, code, test.expectedCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if ok {
|
||||||
|
t.Errorf("case %d: unwanted detailMap present: %q", i, detailMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
13
vendor/github.com/coreos/pkg/httputil/README.md
generated
vendored
Normal file
13
vendor/github.com/coreos/pkg/httputil/README.md
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
httputil
|
||||||
|
====
|
||||||
|
|
||||||
|
Common code for dealing with HTTP.
|
||||||
|
|
||||||
|
Includes:
|
||||||
|
|
||||||
|
* Code for returning JSON responses.
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
Visit the docs on [gopkgdoc](http://godoc.org/github.com/coreos/pkg/httputil)
|
||||||
|
|
21
vendor/github.com/coreos/pkg/httputil/cookie.go
generated
vendored
Normal file
21
vendor/github.com/coreos/pkg/httputil/cookie.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package httputil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeleteCookies effectively deletes all named cookies
|
||||||
|
// by wiping all data and setting to expire immediately.
|
||||||
|
func DeleteCookies(w http.ResponseWriter, cookieNames ...string) {
|
||||||
|
for _, n := range cookieNames {
|
||||||
|
c := &http.Cookie{
|
||||||
|
Name: n,
|
||||||
|
Value: "",
|
||||||
|
Path: "/",
|
||||||
|
MaxAge: -1,
|
||||||
|
Expires: time.Time{},
|
||||||
|
}
|
||||||
|
http.SetCookie(w, c)
|
||||||
|
}
|
||||||
|
}
|
51
vendor/github.com/coreos/pkg/httputil/cookie_test.go
generated
vendored
Normal file
51
vendor/github.com/coreos/pkg/httputil/cookie_test.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package httputil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDeleteCookies(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
// cookie names to delete
|
||||||
|
n []string
|
||||||
|
}{
|
||||||
|
// single
|
||||||
|
{
|
||||||
|
n: []string{"foo"},
|
||||||
|
},
|
||||||
|
// multiple
|
||||||
|
{
|
||||||
|
n: []string{"foo", "bar"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
DeleteCookies(w, tt.n...)
|
||||||
|
resp := &http.Response{}
|
||||||
|
resp.Header = w.Header()
|
||||||
|
cks := resp.Cookies()
|
||||||
|
|
||||||
|
if len(cks) != len(tt.n) {
|
||||||
|
t.Errorf("case %d: unexpected number of cookies, want: %d, got: %d", i, len(tt.n), len(cks))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cks {
|
||||||
|
if c.Value != "" {
|
||||||
|
t.Errorf("case %d: unexpected cookie value, want: %q, got: %q", i, "", c.Value)
|
||||||
|
}
|
||||||
|
if c.Path != "/" {
|
||||||
|
t.Errorf("case %d: unexpected cookie path, want: %q, got: %q", i, "/", c.Path)
|
||||||
|
}
|
||||||
|
if c.MaxAge != -1 {
|
||||||
|
t.Errorf("case %d: unexpected cookie max-age, want: %q, got: %q", i, -1, c.MaxAge)
|
||||||
|
}
|
||||||
|
if !c.Expires.IsZero() {
|
||||||
|
t.Errorf("case %d: unexpected cookie expires, want: %q, got: %q", i, time.Time{}, c.MaxAge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
vendor/github.com/coreos/pkg/httputil/json.go
generated
vendored
Normal file
27
vendor/github.com/coreos/pkg/httputil/json.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package httputil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
JSONContentType = "application/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
func WriteJSONResponse(w http.ResponseWriter, code int, resp interface{}) error {
|
||||||
|
enc, err := json.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", JSONContentType)
|
||||||
|
w.WriteHeader(code)
|
||||||
|
|
||||||
|
_, err = w.Write(enc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
56
vendor/github.com/coreos/pkg/httputil/json_test.go
generated
vendored
Normal file
56
vendor/github.com/coreos/pkg/httputil/json_test.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package httputil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWriteJSONResponse(t *testing.T) {
|
||||||
|
for i, test := range []struct {
|
||||||
|
code int
|
||||||
|
resp interface{}
|
||||||
|
expectedJSON string
|
||||||
|
expectErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
200,
|
||||||
|
struct {
|
||||||
|
A string
|
||||||
|
B string
|
||||||
|
}{A: "foo", B: "bar"},
|
||||||
|
`{"A":"foo","B":"bar"}`,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
500,
|
||||||
|
// Something that json.Marshal cannot serialize.
|
||||||
|
make(chan int),
|
||||||
|
"",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
err := WriteJSONResponse(w, test.code, test.resp)
|
||||||
|
|
||||||
|
if w.Code != test.code {
|
||||||
|
t.Errorf("case %d: w.code == %v, want %v", i, w.Code, test.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != nil) != test.expectErr {
|
||||||
|
t.Errorf("case %d: (err != nil) == %v, want %v. err: %v", i, err != nil, test.expectErr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(w.Body.Bytes()) != test.expectedJSON {
|
||||||
|
t.Errorf("case %d: w.Body.Bytes()) == %q, want %q", i,
|
||||||
|
string(w.Body.Bytes()), test.expectedJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !test.expectErr {
|
||||||
|
contentType := w.Header()["Content-Type"][0]
|
||||||
|
if contentType != JSONContentType {
|
||||||
|
t.Errorf("case %d: contentType == %v, want %v", i, contentType, JSONContentType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
140
vendor/github.com/coreos/pkg/k8s-tlsutil/k8s-tlsutil.go
generated
vendored
Normal file
140
vendor/github.com/coreos/pkg/k8s-tlsutil/k8s-tlsutil.go
generated
vendored
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package k8stlsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RSAKeySize = 2048
|
||||||
|
Duration365d = time.Hour * 24 * 365
|
||||||
|
)
|
||||||
|
|
||||||
|
type CertConfig struct {
|
||||||
|
CommonName string
|
||||||
|
Organization []string
|
||||||
|
AltNames AltNames
|
||||||
|
}
|
||||||
|
|
||||||
|
// AltNames contains the domain names and IP addresses that will be added
|
||||||
|
// to the API Server's x509 certificate SubAltNames field. The values will
|
||||||
|
// be passed directly to the x509.Certificate object.
|
||||||
|
type AltNames struct {
|
||||||
|
DNSNames []string
|
||||||
|
IPs []net.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrivateKey() (*rsa.PrivateKey, error) {
|
||||||
|
return rsa.GenerateKey(rand.Reader, RSAKeySize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodePublicKeyPEM(key *rsa.PublicKey) ([]byte, error) {
|
||||||
|
der, err := x509.MarshalPKIXPublicKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, err
|
||||||
|
}
|
||||||
|
block := pem.Block{
|
||||||
|
Type: "PUBLIC KEY",
|
||||||
|
Bytes: der,
|
||||||
|
}
|
||||||
|
return pem.EncodeToMemory(&block), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodePrivateKeyPEM(key *rsa.PrivateKey) []byte {
|
||||||
|
block := pem.Block{
|
||||||
|
Type: "RSA PRIVATE KEY",
|
||||||
|
Bytes: x509.MarshalPKCS1PrivateKey(key),
|
||||||
|
}
|
||||||
|
return pem.EncodeToMemory(&block)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodeCertificatePEM(cert *x509.Certificate) []byte {
|
||||||
|
block := pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Bytes: cert.Raw,
|
||||||
|
}
|
||||||
|
return pem.EncodeToMemory(&block)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSelfSignedCACertificate(cfg CertConfig, key *rsa.PrivateKey, validDuration time.Duration) (*x509.Certificate, error) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
dur := Duration365d * 10
|
||||||
|
if validDuration != 0 {
|
||||||
|
dur = validDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl := x509.Certificate{
|
||||||
|
SerialNumber: new(big.Int).SetInt64(0),
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: cfg.CommonName,
|
||||||
|
Organization: cfg.Organization,
|
||||||
|
},
|
||||||
|
NotBefore: now,
|
||||||
|
NotAfter: now.Add(dur),
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
IsCA: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
certDERBytes, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, key.Public(), key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x509.ParseCertificate(certDERBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParsePEMEncodedCACert(pemdata []byte) (*x509.Certificate, error) {
|
||||||
|
decoded, _ := pem.Decode(pemdata)
|
||||||
|
if decoded == nil {
|
||||||
|
return nil, errors.New("no PEM data found")
|
||||||
|
}
|
||||||
|
return x509.ParseCertificate(decoded.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParsePEMEncodedPrivateKey(pemdata []byte) (*rsa.PrivateKey, error) {
|
||||||
|
decoded, _ := pem.Decode(pemdata)
|
||||||
|
if decoded == nil {
|
||||||
|
return nil, errors.New("no PEM data found")
|
||||||
|
}
|
||||||
|
return x509.ParsePKCS1PrivateKey(decoded.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSignedCertificate(cfg CertConfig, key *rsa.PrivateKey, caCert *x509.Certificate, caKey *rsa.PrivateKey, validDuration time.Duration) (*x509.Certificate, error) {
|
||||||
|
serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dur := Duration365d
|
||||||
|
if validDuration != 0 {
|
||||||
|
dur = validDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
certTmpl := x509.Certificate{
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: cfg.CommonName,
|
||||||
|
Organization: caCert.Subject.Organization,
|
||||||
|
},
|
||||||
|
DNSNames: cfg.AltNames.DNSNames,
|
||||||
|
IPAddresses: cfg.AltNames.IPs,
|
||||||
|
SerialNumber: serial,
|
||||||
|
NotBefore: caCert.NotBefore,
|
||||||
|
NotAfter: time.Now().Add(dur),
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
||||||
|
}
|
||||||
|
certDERBytes, err := x509.CreateCertificate(rand.Reader, &certTmpl, caCert, key.Public(), caKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x509.ParseCertificate(certDERBytes)
|
||||||
|
}
|
32
vendor/github.com/coreos/pkg/multierror/multierror.go
generated
vendored
Normal file
32
vendor/github.com/coreos/pkg/multierror/multierror.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Package multierror wraps a slice of errors and implements the error interface.
|
||||||
|
// This can be used to collect a bunch of errors (such as during form validation)
|
||||||
|
// and then return them all together as a single error. To see usage examples
|
||||||
|
// refer to the unit tests.
|
||||||
|
package multierror
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Error []error
|
||||||
|
|
||||||
|
func (me Error) Error() string {
|
||||||
|
if me == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
strs := make([]string, len(me))
|
||||||
|
for i, err := range me {
|
||||||
|
strs[i] = fmt.Sprintf("[%d] %v", i, err)
|
||||||
|
}
|
||||||
|
return strings.Join(strs, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (me Error) AsError() error {
|
||||||
|
if len([]error(me)) <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return me
|
||||||
|
}
|
59
vendor/github.com/coreos/pkg/multierror/multierror_test.go
generated
vendored
Normal file
59
vendor/github.com/coreos/pkg/multierror/multierror_test.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package multierror
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAsError(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
multierr Error
|
||||||
|
want error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
multierr: Error([]error{errors.New("foo"), errors.New("bar")}),
|
||||||
|
want: Error([]error{errors.New("foo"), errors.New("bar")}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
multierr: Error([]error{}),
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
multierr: Error(nil),
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
got := tt.multierr.AsError()
|
||||||
|
if !reflect.DeepEqual(tt.want, got) {
|
||||||
|
t.Errorf("case %d: incorrect error value: want=%+v got=%+v", i, tt.want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorAppend(t *testing.T) {
|
||||||
|
var multierr Error
|
||||||
|
multierr = append(multierr, errors.New("foo"))
|
||||||
|
multierr = append(multierr, errors.New("bar"))
|
||||||
|
multierr = append(multierr, errors.New("baz"))
|
||||||
|
want := Error([]error{errors.New("foo"), errors.New("bar"), errors.New("baz")})
|
||||||
|
got := multierr.AsError()
|
||||||
|
if !reflect.DeepEqual(want, got) {
|
||||||
|
t.Fatalf("incorrect error value: want=%+v got=%+v", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorString(t *testing.T) {
|
||||||
|
var multierr Error
|
||||||
|
multierr = append(multierr, errors.New("foo"))
|
||||||
|
multierr = append(multierr, errors.New("bar"))
|
||||||
|
multierr = append(multierr, errors.New("baz"))
|
||||||
|
got := multierr.Error()
|
||||||
|
want := "[0] foo [1] bar [2] baz"
|
||||||
|
if want != got {
|
||||||
|
t.Fatalf("incorrect output: want=%q got=%q", want, got)
|
||||||
|
}
|
||||||
|
}
|
48
vendor/github.com/coreos/pkg/netutil/proxy.go
generated
vendored
Normal file
48
vendor/github.com/coreos/pkg/netutil/proxy.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package netutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProxyTCP proxies between two TCP connections.
|
||||||
|
// Because TLS connections don't have CloseRead() and CloseWrite() methods, our
|
||||||
|
// temporary solution is to use timeouts.
|
||||||
|
func ProxyTCP(conn1, conn2 net.Conn, tlsWriteDeadline, tlsReadDeadline time.Duration) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
go copyBytes(conn1, conn2, &wg, tlsWriteDeadline, tlsReadDeadline)
|
||||||
|
go copyBytes(conn2, conn1, &wg, tlsWriteDeadline, tlsReadDeadline)
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
conn1.Close()
|
||||||
|
conn2.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyBytes(dst, src net.Conn, wg *sync.WaitGroup, writeDeadline, readDeadline time.Duration) {
|
||||||
|
defer wg.Done()
|
||||||
|
_, err := io.Copy(dst, src)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("proxy i/o error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cr, ok := src.(*net.TCPConn); ok {
|
||||||
|
cr.CloseRead()
|
||||||
|
} else {
|
||||||
|
// For TLS connections.
|
||||||
|
wto := time.Now().Add(writeDeadline)
|
||||||
|
src.SetWriteDeadline(wto)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cw, ok := dst.(*net.TCPConn); ok {
|
||||||
|
cw.CloseWrite()
|
||||||
|
} else {
|
||||||
|
// For TLS connections.
|
||||||
|
rto := time.Now().Add(readDeadline)
|
||||||
|
dst.SetReadDeadline(rto)
|
||||||
|
}
|
||||||
|
}
|
17
vendor/github.com/coreos/pkg/netutil/url.go
generated
vendored
Normal file
17
vendor/github.com/coreos/pkg/netutil/url.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package netutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MergeQuery appends additional query values to an existing URL.
|
||||||
|
func MergeQuery(u url.URL, q url.Values) url.URL {
|
||||||
|
uv := u.Query()
|
||||||
|
for k, vs := range q {
|
||||||
|
for _, v := range vs {
|
||||||
|
uv.Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u.RawQuery = uv.Encode()
|
||||||
|
return u
|
||||||
|
}
|
86
vendor/github.com/coreos/pkg/netutil/url_test.go
generated
vendored
Normal file
86
vendor/github.com/coreos/pkg/netutil/url_test.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package netutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMergeQuery(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
u string
|
||||||
|
q url.Values
|
||||||
|
w string
|
||||||
|
}{
|
||||||
|
// No values
|
||||||
|
{
|
||||||
|
u: "http://example.com",
|
||||||
|
q: nil,
|
||||||
|
w: "http://example.com",
|
||||||
|
},
|
||||||
|
// No additional values
|
||||||
|
{
|
||||||
|
u: "http://example.com?foo=bar",
|
||||||
|
q: nil,
|
||||||
|
w: "http://example.com?foo=bar",
|
||||||
|
},
|
||||||
|
// Simple addition
|
||||||
|
{
|
||||||
|
u: "http://example.com",
|
||||||
|
q: url.Values{
|
||||||
|
"foo": []string{"bar"},
|
||||||
|
},
|
||||||
|
w: "http://example.com?foo=bar",
|
||||||
|
},
|
||||||
|
// Addition with existing values
|
||||||
|
{
|
||||||
|
u: "http://example.com?dog=boo",
|
||||||
|
q: url.Values{
|
||||||
|
"foo": []string{"bar"},
|
||||||
|
},
|
||||||
|
w: "http://example.com?dog=boo&foo=bar",
|
||||||
|
},
|
||||||
|
// Merge
|
||||||
|
{
|
||||||
|
u: "http://example.com?dog=boo",
|
||||||
|
q: url.Values{
|
||||||
|
"dog": []string{"elroy"},
|
||||||
|
},
|
||||||
|
w: "http://example.com?dog=boo&dog=elroy",
|
||||||
|
},
|
||||||
|
// Add and merge
|
||||||
|
{
|
||||||
|
u: "http://example.com?dog=boo",
|
||||||
|
q: url.Values{
|
||||||
|
"dog": []string{"elroy"},
|
||||||
|
"foo": []string{"bar"},
|
||||||
|
},
|
||||||
|
w: "http://example.com?dog=boo&dog=elroy&foo=bar",
|
||||||
|
},
|
||||||
|
// Multivalue merge
|
||||||
|
{
|
||||||
|
u: "http://example.com?dog=boo",
|
||||||
|
q: url.Values{
|
||||||
|
"dog": []string{"elroy", "penny"},
|
||||||
|
},
|
||||||
|
w: "http://example.com?dog=boo&dog=elroy&dog=penny",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
ur, err := url.Parse(tt.u)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("case %d: failed parsing test url: %v, error: %v", i, tt.u, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got := MergeQuery(*ur, tt.q)
|
||||||
|
want, err := url.Parse(tt.w)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("case %d: failed parsing want url: %v, error: %v", i, tt.w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(*want, got) {
|
||||||
|
t.Errorf("case %d: want: %v, got: %v", i, *want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
189
vendor/github.com/coreos/pkg/progressutil/iocopy.go
generated
vendored
Normal file
189
vendor/github.com/coreos/pkg/progressutil/iocopy.go
generated
vendored
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
// Copyright 2016 CoreOS Inc
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package progressutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrAlreadyStarted = errors.New("cannot add copies after PrintAndWait has been called")
|
||||||
|
)
|
||||||
|
|
||||||
|
type copyReader struct {
|
||||||
|
reader io.Reader
|
||||||
|
current int64
|
||||||
|
total int64
|
||||||
|
pb *ProgressBar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *copyReader) Read(p []byte) (int, error) {
|
||||||
|
n, err := cr.reader.Read(p)
|
||||||
|
cr.current += int64(n)
|
||||||
|
err1 := cr.updateProgressBar()
|
||||||
|
if err == nil {
|
||||||
|
err = err1
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *copyReader) updateProgressBar() error {
|
||||||
|
cr.pb.SetPrintAfter(cr.formattedProgress())
|
||||||
|
|
||||||
|
progress := float64(cr.current) / float64(cr.total)
|
||||||
|
if progress > 1 {
|
||||||
|
progress = 1
|
||||||
|
}
|
||||||
|
return cr.pb.SetCurrentProgress(progress)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCopyProgressPrinter returns a new CopyProgressPrinter
|
||||||
|
func NewCopyProgressPrinter() *CopyProgressPrinter {
|
||||||
|
return &CopyProgressPrinter{
|
||||||
|
results: make(chan error),
|
||||||
|
cancel: make(chan struct{}),
|
||||||
|
pbp: &ProgressBarPrinter{PadToBeEven: true},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyProgressPrinter will perform an arbitrary number of io.Copy calls, while
|
||||||
|
// continually printing the progress of each copy.
|
||||||
|
type CopyProgressPrinter struct {
|
||||||
|
results chan error
|
||||||
|
cancel chan struct{}
|
||||||
|
|
||||||
|
// `lock` mutex protects all fields below it in CopyProgressPrinter struct
|
||||||
|
lock sync.Mutex
|
||||||
|
readers []*copyReader
|
||||||
|
started bool
|
||||||
|
pbp *ProgressBarPrinter
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCopy adds a copy for this CopyProgressPrinter to perform. An io.Copy call
|
||||||
|
// will be made to copy bytes from reader to dest, and name and size will be
|
||||||
|
// used to label the progress bar and display how much progress has been made.
|
||||||
|
// If size is 0, the total size of the reader is assumed to be unknown.
|
||||||
|
// AddCopy can only be called before PrintAndWait; otherwise, ErrAlreadyStarted
|
||||||
|
// will be returned.
|
||||||
|
func (cpp *CopyProgressPrinter) AddCopy(reader io.Reader, name string, size int64, dest io.Writer) error {
|
||||||
|
cpp.lock.Lock()
|
||||||
|
defer cpp.lock.Unlock()
|
||||||
|
|
||||||
|
if cpp.started {
|
||||||
|
return ErrAlreadyStarted
|
||||||
|
}
|
||||||
|
|
||||||
|
cr := ©Reader{
|
||||||
|
reader: reader,
|
||||||
|
current: 0,
|
||||||
|
total: size,
|
||||||
|
pb: cpp.pbp.AddProgressBar(),
|
||||||
|
}
|
||||||
|
cr.pb.SetPrintBefore(name)
|
||||||
|
cr.pb.SetPrintAfter(cr.formattedProgress())
|
||||||
|
|
||||||
|
cpp.readers = append(cpp.readers, cr)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
_, err := io.Copy(dest, cr)
|
||||||
|
select {
|
||||||
|
case <-cpp.cancel:
|
||||||
|
return
|
||||||
|
case cpp.results <- err:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintAndWait will print the progress for each copy operation added with
|
||||||
|
// AddCopy to printTo every printInterval. This will continue until every added
|
||||||
|
// copy is finished, or until cancel is written to.
|
||||||
|
// PrintAndWait may only be called once; any subsequent calls will immediately
|
||||||
|
// return ErrAlreadyStarted. After PrintAndWait has been called, no more
|
||||||
|
// copies may be added to the CopyProgressPrinter.
|
||||||
|
func (cpp *CopyProgressPrinter) PrintAndWait(printTo io.Writer, printInterval time.Duration, cancel chan struct{}) error {
|
||||||
|
cpp.lock.Lock()
|
||||||
|
if cpp.started {
|
||||||
|
cpp.lock.Unlock()
|
||||||
|
return ErrAlreadyStarted
|
||||||
|
}
|
||||||
|
cpp.started = true
|
||||||
|
cpp.lock.Unlock()
|
||||||
|
|
||||||
|
n := len(cpp.readers)
|
||||||
|
if n == 0 {
|
||||||
|
// Nothing to do.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer close(cpp.cancel)
|
||||||
|
t := time.NewTicker(printInterval)
|
||||||
|
allDone := false
|
||||||
|
for i := 0; i < n; {
|
||||||
|
select {
|
||||||
|
case <-cancel:
|
||||||
|
return nil
|
||||||
|
case <-t.C:
|
||||||
|
_, err := cpp.pbp.Print(printTo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case err := <-cpp.results:
|
||||||
|
i++
|
||||||
|
// Once completion is signaled, further on this just drains
|
||||||
|
// (unlikely) errors from the channel.
|
||||||
|
if err == nil && !allDone {
|
||||||
|
allDone, err = cpp.pbp.Print(printTo)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *copyReader) formattedProgress() string {
|
||||||
|
var totalStr string
|
||||||
|
if cr.total == 0 {
|
||||||
|
totalStr = "?"
|
||||||
|
} else {
|
||||||
|
totalStr = ByteUnitStr(cr.total)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s / %s", ByteUnitStr(cr.current), totalStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var byteUnits = []string{"B", "KB", "MB", "GB", "TB", "PB"}
|
||||||
|
|
||||||
|
// ByteUnitStr pretty prints a number of bytes.
|
||||||
|
func ByteUnitStr(n int64) string {
|
||||||
|
var unit string
|
||||||
|
size := float64(n)
|
||||||
|
for i := 1; i < len(byteUnits); i++ {
|
||||||
|
if size < 1000 {
|
||||||
|
unit = byteUnits[i-1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
size = size / 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%.3g %s", size, unit)
|
||||||
|
}
|
140
vendor/github.com/coreos/pkg/progressutil/iocopy_test.go
generated
vendored
Normal file
140
vendor/github.com/coreos/pkg/progressutil/iocopy_test.go
generated
vendored
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Copyright 2016 CoreOS Inc
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package progressutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fakeReader struct {
|
||||||
|
input chan []byte
|
||||||
|
done bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fr *fakeReader) Read(p []byte) (int, error) {
|
||||||
|
if fr.done {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
i := copy(p, <-fr.input)
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyOne(t *testing.T) {
|
||||||
|
cpp := NewCopyProgressPrinter()
|
||||||
|
cpp.pbp.printToTTYAlways = true
|
||||||
|
|
||||||
|
sampleData := []byte("this is a test!")
|
||||||
|
|
||||||
|
fr := &fakeReader{make(chan []byte, 1), false}
|
||||||
|
fw := &bytes.Buffer{}
|
||||||
|
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
|
||||||
|
err := cpp.AddCopy(fr, "download", int64(len(sampleData)*10), fw)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
doneChan := make(chan error)
|
||||||
|
go func() {
|
||||||
|
doneChan <- cpp.PrintAndWait(out, time.Millisecond*10, nil)
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(time.Millisecond * 15)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
// Empty the buffer
|
||||||
|
printedData := out.Bytes()
|
||||||
|
sizeString := ByteUnitStr(int64(len(sampleData)*i)) + " / " + ByteUnitStr(int64(len(sampleData)*10))
|
||||||
|
bar := renderExpectedBar(80, "download", float64(i)/10, sizeString)
|
||||||
|
var expectedOutput string
|
||||||
|
if i == 0 {
|
||||||
|
expectedOutput = fmt.Sprintf("%s\n", bar)
|
||||||
|
} else {
|
||||||
|
expectedOutput = fmt.Sprintf("\033[1A%s\n", bar)
|
||||||
|
}
|
||||||
|
if string(printedData) != expectedOutput {
|
||||||
|
t.Errorf("unexpected output:\nexpected:\n\n%sactual:\n\n%s", expectedOutput, string(printedData))
|
||||||
|
}
|
||||||
|
if i == 9 {
|
||||||
|
fr.done = true
|
||||||
|
}
|
||||||
|
fr.input <- sampleData
|
||||||
|
|
||||||
|
out.Reset()
|
||||||
|
|
||||||
|
time.Sleep(time.Millisecond * 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = <-doneChan
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error from PrintAndWait: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(fw.Bytes(), bytes.Repeat(sampleData, 10)) != 0 {
|
||||||
|
t.Errorf("copied bytes don't match!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrAlreadyStarted(t *testing.T) {
|
||||||
|
cpp := NewCopyProgressPrinter()
|
||||||
|
fr := &fakeReader{make(chan []byte, 1), false}
|
||||||
|
fw := &bytes.Buffer{}
|
||||||
|
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
|
||||||
|
err := cpp.AddCopy(fr, "download", 10^10, fw)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel := make(chan struct{})
|
||||||
|
doneChan := make(chan error)
|
||||||
|
go func() {
|
||||||
|
doneChan <- cpp.PrintAndWait(out, time.Second, cancel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
cpp.lock.Lock()
|
||||||
|
started := cpp.started
|
||||||
|
cpp.lock.Unlock()
|
||||||
|
if !started {
|
||||||
|
time.Sleep(time.Millisecond * 5)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cpp.AddCopy(fr, "download", 10^10, fw)
|
||||||
|
if err != ErrAlreadyStarted {
|
||||||
|
t.Errorf("Was expecting ErrAlreadyStarted, got something else: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cpp.PrintAndWait(out, time.Second, cancel)
|
||||||
|
if err != ErrAlreadyStarted {
|
||||||
|
t.Errorf("Was expecting ErrAlreadyStarted, got something else: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel <- struct{}{}
|
||||||
|
|
||||||
|
err = <-doneChan
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v\n", err)
|
||||||
|
}
|
||||||
|
}
|
263
vendor/github.com/coreos/pkg/progressutil/progressbar.go
generated
vendored
Normal file
263
vendor/github.com/coreos/pkg/progressutil/progressbar.go
generated
vendored
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
// Copyright 2016 CoreOS Inc
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package progressutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrorProgressOutOfBounds is returned if the progress is set to a value
|
||||||
|
// not between 0 and 1.
|
||||||
|
ErrorProgressOutOfBounds = fmt.Errorf("progress is out of bounds (0 to 1)")
|
||||||
|
|
||||||
|
// ErrorNoBarsAdded is returned when no progress bars have been added to a
|
||||||
|
// ProgressBarPrinter before PrintAndWait is called.
|
||||||
|
ErrorNoBarsAdded = fmt.Errorf("AddProgressBar hasn't been called yet")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProgressBar represents one progress bar in a ProgressBarPrinter. Should not
|
||||||
|
// be created directly, use the AddProgressBar on a ProgressBarPrinter to
|
||||||
|
// create these.
|
||||||
|
type ProgressBar struct {
|
||||||
|
lock sync.Mutex
|
||||||
|
|
||||||
|
currentProgress float64
|
||||||
|
printBefore string
|
||||||
|
printAfter string
|
||||||
|
done bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *ProgressBar) clone() *ProgressBar {
|
||||||
|
pb.lock.Lock()
|
||||||
|
pbClone := &ProgressBar{
|
||||||
|
currentProgress: pb.currentProgress,
|
||||||
|
printBefore: pb.printBefore,
|
||||||
|
printAfter: pb.printAfter,
|
||||||
|
done: pb.done,
|
||||||
|
}
|
||||||
|
pb.lock.Unlock()
|
||||||
|
return pbClone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *ProgressBar) GetCurrentProgress() float64 {
|
||||||
|
pb.lock.Lock()
|
||||||
|
val := pb.currentProgress
|
||||||
|
pb.lock.Unlock()
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCurrentProgress sets the progress of this ProgressBar. The progress must
|
||||||
|
// be between 0 and 1 inclusive.
|
||||||
|
func (pb *ProgressBar) SetCurrentProgress(progress float64) error {
|
||||||
|
if progress < 0 || progress > 1 {
|
||||||
|
return ErrorProgressOutOfBounds
|
||||||
|
}
|
||||||
|
pb.lock.Lock()
|
||||||
|
pb.currentProgress = progress
|
||||||
|
pb.lock.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDone returns whether or not this progress bar is done
|
||||||
|
func (pb *ProgressBar) GetDone() bool {
|
||||||
|
pb.lock.Lock()
|
||||||
|
val := pb.done
|
||||||
|
pb.lock.Unlock()
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDone sets whether or not this progress bar is done
|
||||||
|
func (pb *ProgressBar) SetDone(val bool) {
|
||||||
|
pb.lock.Lock()
|
||||||
|
pb.done = val
|
||||||
|
pb.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrintBefore gets the text printed on the line before the progress bar.
|
||||||
|
func (pb *ProgressBar) GetPrintBefore() string {
|
||||||
|
pb.lock.Lock()
|
||||||
|
val := pb.printBefore
|
||||||
|
pb.lock.Unlock()
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPrintBefore sets the text printed on the line before the progress bar.
|
||||||
|
func (pb *ProgressBar) SetPrintBefore(before string) {
|
||||||
|
pb.lock.Lock()
|
||||||
|
pb.printBefore = before
|
||||||
|
pb.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrintAfter gets the text printed on the line after the progress bar.
|
||||||
|
func (pb *ProgressBar) GetPrintAfter() string {
|
||||||
|
pb.lock.Lock()
|
||||||
|
val := pb.printAfter
|
||||||
|
pb.lock.Unlock()
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPrintAfter sets the text printed on the line after the progress bar.
|
||||||
|
func (pb *ProgressBar) SetPrintAfter(after string) {
|
||||||
|
pb.lock.Lock()
|
||||||
|
pb.printAfter = after
|
||||||
|
pb.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProgressBarPrinter will print out the progress of some number of
|
||||||
|
// ProgressBars.
|
||||||
|
type ProgressBarPrinter struct {
|
||||||
|
lock sync.Mutex
|
||||||
|
|
||||||
|
// DisplayWidth can be set to influence how large the progress bars are.
|
||||||
|
// The bars will be scaled to attempt to produce lines of this number of
|
||||||
|
// characters, but lines of different lengths may still be printed. When
|
||||||
|
// this value is 0 (aka unset), 80 character columns are assumed.
|
||||||
|
DisplayWidth int
|
||||||
|
// PadToBeEven, when set to true, will make Print pad the printBefore text
|
||||||
|
// with trailing spaces and the printAfter text with leading spaces to make
|
||||||
|
// the progress bars the same length.
|
||||||
|
PadToBeEven bool
|
||||||
|
numLinesInLastPrint int
|
||||||
|
progressBars []*ProgressBar
|
||||||
|
maxBefore int
|
||||||
|
maxAfter int
|
||||||
|
|
||||||
|
// printToTTYAlways forces this ProgressBarPrinter to always behave as if
|
||||||
|
// in a tty. Used for tests.
|
||||||
|
printToTTYAlways bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddProgressBar will create a new ProgressBar, register it with this
|
||||||
|
// ProgressBarPrinter, and return it. This must be called at least once before
|
||||||
|
// PrintAndWait is called.
|
||||||
|
func (pbp *ProgressBarPrinter) AddProgressBar() *ProgressBar {
|
||||||
|
pb := &ProgressBar{}
|
||||||
|
pbp.lock.Lock()
|
||||||
|
pbp.progressBars = append(pbp.progressBars, pb)
|
||||||
|
pbp.lock.Unlock()
|
||||||
|
return pb
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print will print out progress information for each ProgressBar that has been
|
||||||
|
// added to this ProgressBarPrinter. The progress will be written to printTo,
|
||||||
|
// and if printTo is a terminal it will draw progress bars. AddProgressBar
|
||||||
|
// must be called at least once before Print is called. If printing to a
|
||||||
|
// terminal, all draws after the first one will move the cursor up to draw over
|
||||||
|
// the previously printed bars.
|
||||||
|
func (pbp *ProgressBarPrinter) Print(printTo io.Writer) (bool, error) {
|
||||||
|
pbp.lock.Lock()
|
||||||
|
var bars []*ProgressBar
|
||||||
|
for _, bar := range pbp.progressBars {
|
||||||
|
bars = append(bars, bar.clone())
|
||||||
|
}
|
||||||
|
numColumns := pbp.DisplayWidth
|
||||||
|
pbp.lock.Unlock()
|
||||||
|
|
||||||
|
if len(bars) == 0 {
|
||||||
|
return false, ErrorNoBarsAdded
|
||||||
|
}
|
||||||
|
|
||||||
|
if numColumns == 0 {
|
||||||
|
numColumns = 80
|
||||||
|
}
|
||||||
|
|
||||||
|
if pbp.isTerminal(printTo) {
|
||||||
|
moveCursorUp(printTo, pbp.numLinesInLastPrint)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bar := range bars {
|
||||||
|
beforeSize := len(bar.GetPrintBefore())
|
||||||
|
afterSize := len(bar.GetPrintAfter())
|
||||||
|
if beforeSize > pbp.maxBefore {
|
||||||
|
pbp.maxBefore = beforeSize
|
||||||
|
}
|
||||||
|
if afterSize > pbp.maxAfter {
|
||||||
|
pbp.maxAfter = afterSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allDone := true
|
||||||
|
for _, bar := range bars {
|
||||||
|
if pbp.isTerminal(printTo) {
|
||||||
|
bar.printToTerminal(printTo, numColumns, pbp.PadToBeEven, pbp.maxBefore, pbp.maxAfter)
|
||||||
|
} else {
|
||||||
|
bar.printToNonTerminal(printTo)
|
||||||
|
}
|
||||||
|
allDone = allDone && bar.GetCurrentProgress() == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pbp.numLinesInLastPrint = len(bars)
|
||||||
|
|
||||||
|
return allDone, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// moveCursorUp moves the cursor up numLines in the terminal
|
||||||
|
func moveCursorUp(printTo io.Writer, numLines int) {
|
||||||
|
if numLines > 0 {
|
||||||
|
fmt.Fprintf(printTo, "\033[%dA", numLines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *ProgressBar) printToTerminal(printTo io.Writer, numColumns int, padding bool, maxBefore, maxAfter int) {
|
||||||
|
before := pb.GetPrintBefore()
|
||||||
|
after := pb.GetPrintAfter()
|
||||||
|
|
||||||
|
if padding {
|
||||||
|
before = before + strings.Repeat(" ", maxBefore-len(before))
|
||||||
|
after = strings.Repeat(" ", maxAfter-len(after)) + after
|
||||||
|
}
|
||||||
|
|
||||||
|
progressBarSize := numColumns - (len(fmt.Sprintf("%s [] %s", before, after)))
|
||||||
|
progressBar := ""
|
||||||
|
if progressBarSize > 0 {
|
||||||
|
currentProgress := int(pb.GetCurrentProgress() * float64(progressBarSize))
|
||||||
|
progressBar = fmt.Sprintf("[%s%s] ",
|
||||||
|
strings.Repeat("=", currentProgress),
|
||||||
|
strings.Repeat(" ", progressBarSize-currentProgress))
|
||||||
|
} else {
|
||||||
|
// If we can't fit the progress bar, better to not pad the before/after.
|
||||||
|
before = pb.GetPrintBefore()
|
||||||
|
after = pb.GetPrintAfter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(printTo, "%s %s%s\n", before, progressBar, after)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *ProgressBar) printToNonTerminal(printTo io.Writer) {
|
||||||
|
if !pb.GetDone() {
|
||||||
|
fmt.Fprintf(printTo, "%s %s\n", pb.printBefore, pb.printAfter)
|
||||||
|
if pb.GetCurrentProgress() == 1 {
|
||||||
|
pb.SetDone(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTerminal returns True when w is going to a tty, and false otherwise.
|
||||||
|
func (pbp *ProgressBarPrinter) isTerminal(w io.Writer) bool {
|
||||||
|
if pbp.printToTTYAlways {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if f, ok := w.(*os.File); ok {
|
||||||
|
return terminal.IsTerminal(int(f.Fd()))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
116
vendor/github.com/coreos/pkg/progressutil/progressbar_test.go
generated
vendored
Normal file
116
vendor/github.com/coreos/pkg/progressutil/progressbar_test.go
generated
vendored
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// Copyright 2016 CoreOS Inc
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package progressutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNoBarsAdded(t *testing.T) {
|
||||||
|
pbp := &ProgressBarPrinter{}
|
||||||
|
allDone, err := pbp.Print(ioutil.Discard)
|
||||||
|
if allDone {
|
||||||
|
t.Errorf("shouldn't have gotten all done when no bars have been added")
|
||||||
|
}
|
||||||
|
if err != ErrorNoBarsAdded {
|
||||||
|
t.Errorf("was expecting ErrorNoBarsAdded, got this instead: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProgressOutOfBounds(t *testing.T) {
|
||||||
|
pbp := &ProgressBarPrinter{}
|
||||||
|
pb := pbp.AddProgressBar()
|
||||||
|
|
||||||
|
for _, testcase := range []struct {
|
||||||
|
progress float64
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{-0.1, ErrorProgressOutOfBounds},
|
||||||
|
{0, nil},
|
||||||
|
{0.5, nil},
|
||||||
|
{1, nil},
|
||||||
|
{1.1, ErrorProgressOutOfBounds},
|
||||||
|
} {
|
||||||
|
err := pb.SetCurrentProgress(testcase.progress)
|
||||||
|
if err != testcase.expectedErr {
|
||||||
|
t.Errorf("got unexpected error. expected=%v actual=%v", testcase.expectedErr, err)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
currProgress := pb.GetCurrentProgress()
|
||||||
|
if currProgress != testcase.progress {
|
||||||
|
t.Errorf("no error was returned, but the progress wasn't updated. should be: %f, actual: %f", testcase.progress, currProgress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDrawOne(t *testing.T) {
|
||||||
|
pbp := ProgressBarPrinter{}
|
||||||
|
pbp.printToTTYAlways = true
|
||||||
|
pb := pbp.AddProgressBar()
|
||||||
|
|
||||||
|
pbp.Print(ioutil.Discard)
|
||||||
|
|
||||||
|
for _, testcase := range []struct {
|
||||||
|
beforeText string
|
||||||
|
progress float64
|
||||||
|
afterText string
|
||||||
|
shouldBeDone bool
|
||||||
|
}{
|
||||||
|
{"before", 0, "after", false},
|
||||||
|
{"before2", 0.1, "after2", false},
|
||||||
|
{"before3", 0.5, "after3", false},
|
||||||
|
{"before4", 1, "after4", true},
|
||||||
|
} {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
pb.SetPrintBefore(testcase.beforeText)
|
||||||
|
pb.SetPrintAfter(testcase.afterText)
|
||||||
|
err := pb.SetCurrentProgress(testcase.progress)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
done, err := pbp.Print(buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if done != testcase.shouldBeDone {
|
||||||
|
t.Errorf("unexpected done, expected=%t actual=%t", testcase.shouldBeDone, done)
|
||||||
|
}
|
||||||
|
|
||||||
|
output := buf.String()
|
||||||
|
|
||||||
|
bar := renderExpectedBar(80, testcase.beforeText, testcase.progress, testcase.afterText)
|
||||||
|
|
||||||
|
expectedOutput := fmt.Sprintf("\033[1A%s\n", bar)
|
||||||
|
|
||||||
|
if output != expectedOutput {
|
||||||
|
t.Errorf("unexpected output:\nexpected:\n\n%sactual:\n\n%s", expectedOutput, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderExpectedBar(numColumns int, before string, progress float64, after string) string {
|
||||||
|
progressBarSize := numColumns - len(fmt.Sprintf("%s [] %s", before, after))
|
||||||
|
currentProgress := int(progress * float64(progressBarSize))
|
||||||
|
|
||||||
|
bar := fmt.Sprintf("[%s%s]",
|
||||||
|
strings.Repeat("=", currentProgress),
|
||||||
|
strings.Repeat(" ", progressBarSize-currentProgress))
|
||||||
|
return fmt.Sprintf("%s %s %s", before, bar, after)
|
||||||
|
}
|
56
vendor/github.com/coreos/pkg/test
generated
vendored
Executable file
56
vendor/github.com/coreos/pkg/test
generated
vendored
Executable file
@ -0,0 +1,56 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
#
|
||||||
|
# Run all tests (not including functional)
|
||||||
|
# ./test
|
||||||
|
# ./test -v
|
||||||
|
#
|
||||||
|
# Run tests for one package
|
||||||
|
# PKG=./unit ./test
|
||||||
|
# PKG=ssh ./test
|
||||||
|
#
|
||||||
|
|
||||||
|
# Invoke ./cover for HTML output
|
||||||
|
COVER=${COVER:-"-cover"}
|
||||||
|
|
||||||
|
source ./build
|
||||||
|
|
||||||
|
TESTABLE="cryptoutil flagutil timeutil netutil yamlutil httputil health multierror dlopen progressutil"
|
||||||
|
FORMATTABLE="$TESTABLE capnslog"
|
||||||
|
|
||||||
|
# user has not provided PKG override
|
||||||
|
if [ -z "$PKG" ]; then
|
||||||
|
TEST=$TESTABLE
|
||||||
|
FMT=$FORMATTABLE
|
||||||
|
|
||||||
|
# user has provided PKG override
|
||||||
|
else
|
||||||
|
# strip out slashes and dots from PKG=./foo/
|
||||||
|
TEST=${PKG//\//}
|
||||||
|
TEST=${TEST//./}
|
||||||
|
|
||||||
|
# only run gofmt on packages provided by user
|
||||||
|
FMT="$TEST"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# split TEST into an array and prepend repo path to each local package
|
||||||
|
split=(${TEST// / })
|
||||||
|
TEST=${split[@]/#/github.com/coreos/pkg/}
|
||||||
|
|
||||||
|
echo "Running tests..."
|
||||||
|
go test ${COVER} $@ ${TEST}
|
||||||
|
|
||||||
|
echo "Checking gofmt..."
|
||||||
|
fmtRes=$(gofmt -l $FMT)
|
||||||
|
if [ -n "${fmtRes}" ]; then
|
||||||
|
echo -e "gofmt checking failed:\n${fmtRes}"
|
||||||
|
exit 255
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Checking govet..."
|
||||||
|
vetRes=$(go vet $TEST)
|
||||||
|
if [ -n "${vetRes}" ]; then
|
||||||
|
echo -e "govet checking failed:\n${vetRes}"
|
||||||
|
exit 255
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Success"
|
15
vendor/github.com/coreos/pkg/timeutil/backoff.go
generated
vendored
Normal file
15
vendor/github.com/coreos/pkg/timeutil/backoff.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package timeutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExpBackoff(prev, max time.Duration) time.Duration {
|
||||||
|
if prev == 0 {
|
||||||
|
return time.Second
|
||||||
|
}
|
||||||
|
if prev > max/2 {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
return 2 * prev
|
||||||
|
}
|
52
vendor/github.com/coreos/pkg/timeutil/backoff_test.go
generated
vendored
Normal file
52
vendor/github.com/coreos/pkg/timeutil/backoff_test.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package timeutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExpBackoff(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
prev time.Duration
|
||||||
|
max time.Duration
|
||||||
|
want time.Duration
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
prev: time.Duration(0),
|
||||||
|
max: time.Minute,
|
||||||
|
want: time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prev: time.Second,
|
||||||
|
max: time.Minute,
|
||||||
|
want: 2 * time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prev: 16 * time.Second,
|
||||||
|
max: time.Minute,
|
||||||
|
want: 32 * time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prev: 32 * time.Second,
|
||||||
|
max: time.Minute,
|
||||||
|
want: time.Minute,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prev: time.Minute,
|
||||||
|
max: time.Minute,
|
||||||
|
want: time.Minute,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prev: 2 * time.Minute,
|
||||||
|
max: time.Minute,
|
||||||
|
want: time.Minute,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
got := ExpBackoff(tt.prev, tt.max)
|
||||||
|
if tt.want != got {
|
||||||
|
t.Errorf("case %d: want=%v got=%v", i, tt.want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
vendor/github.com/coreos/pkg/yamlutil/yaml.go
generated
vendored
Normal file
55
vendor/github.com/coreos/pkg/yamlutil/yaml.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package yamlutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetFlagsFromYaml goes through all registered flags in the given flagset,
|
||||||
|
// and if they are not already set it attempts to set their values from
|
||||||
|
// the YAML config. It will use the key REPLACE(UPPERCASE(flagname), '-', '_')
|
||||||
|
func SetFlagsFromYaml(fs *flag.FlagSet, rawYaml []byte) (err error) {
|
||||||
|
conf := make(map[string]string)
|
||||||
|
if err = yaml.Unmarshal(rawYaml, conf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
alreadySet := map[string]struct{}{}
|
||||||
|
fs.Visit(func(f *flag.Flag) {
|
||||||
|
alreadySet[f.Name] = struct{}{}
|
||||||
|
})
|
||||||
|
|
||||||
|
errs := make([]error, 0)
|
||||||
|
fs.VisitAll(func(f *flag.Flag) {
|
||||||
|
if f.Name == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, ok := alreadySet[f.Name]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tag := strings.Replace(strings.ToUpper(f.Name), "-", "_", -1)
|
||||||
|
val, ok := conf[tag]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if serr := fs.Set(f.Name, val); serr != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("invalid value %q for %s: %v", val, tag, serr))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if len(errs) != 0 {
|
||||||
|
err = ErrorSlice(errs)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorSlice []error
|
||||||
|
|
||||||
|
func (e ErrorSlice) Error() string {
|
||||||
|
s := ""
|
||||||
|
for _, err := range e {
|
||||||
|
s += ", " + err.Error()
|
||||||
|
}
|
||||||
|
return "Errors: " + s
|
||||||
|
}
|
80
vendor/github.com/coreos/pkg/yamlutil/yaml_test.go
generated
vendored
Normal file
80
vendor/github.com/coreos/pkg/yamlutil/yaml_test.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package yamlutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetFlagsFromYaml(t *testing.T) {
|
||||||
|
config := "A: foo\nC: woof"
|
||||||
|
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||||
|
fs.String("a", "", "")
|
||||||
|
fs.String("b", "", "")
|
||||||
|
fs.String("c", "", "")
|
||||||
|
fs.Parse([]string{})
|
||||||
|
|
||||||
|
// flags should be settable using yaml vars
|
||||||
|
// and command-line flags
|
||||||
|
if err := fs.Set("b", "bar"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// command-line flags take precedence over the file
|
||||||
|
if err := fs.Set("c", "quack"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// first verify that flags are as expected before reading the file
|
||||||
|
for f, want := range map[string]string{
|
||||||
|
"a": "",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "quack",
|
||||||
|
} {
|
||||||
|
if got := fs.Lookup(f).Value.String(); got != want {
|
||||||
|
t.Fatalf("flag %q=%q, want %q", f, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now read the yaml and verify flags were updated as expected
|
||||||
|
err := SetFlagsFromYaml(fs, []byte(config))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err=%v, want nil", err)
|
||||||
|
}
|
||||||
|
for f, want := range map[string]string{
|
||||||
|
"a": "foo",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "quack",
|
||||||
|
} {
|
||||||
|
if got := fs.Lookup(f).Value.String(); got != want {
|
||||||
|
t.Errorf("flag %q=%q, want %q", f, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetFlagsFromYamlBad(t *testing.T) {
|
||||||
|
// now verify that an error is propagated
|
||||||
|
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||||
|
fs.Int("x", 0, "")
|
||||||
|
badConf := "X: not_a_number"
|
||||||
|
if err := SetFlagsFromYaml(fs, []byte(badConf)); err == nil {
|
||||||
|
t.Errorf("got err=nil, flag x=%q, want err != nil", fs.Lookup("x").Value.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetFlagsFromYamlMultiError(t *testing.T) {
|
||||||
|
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||||
|
fs.Int("x", 0, "")
|
||||||
|
fs.Int("y", 0, "")
|
||||||
|
fs.Int("z", 0, "")
|
||||||
|
conf := "X: foo\nY: bar\nZ: 3"
|
||||||
|
err := SetFlagsFromYaml(fs, []byte(conf))
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("got err= nil, want err != nil")
|
||||||
|
}
|
||||||
|
es, ok := err.(ErrorSlice)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Got ok=false want ok=true")
|
||||||
|
}
|
||||||
|
if len(es) != 2 {
|
||||||
|
t.Errorf("2 errors should be contained in the error, got %d errors", len(es))
|
||||||
|
}
|
||||||
|
}
|
22
vendor/github.com/davecgh/go-spew/.gitignore
generated
vendored
Normal file
22
vendor/github.com/davecgh/go-spew/.gitignore
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
14
vendor/github.com/davecgh/go-spew/.travis.yml
generated
vendored
Normal file
14
vendor/github.com/davecgh/go-spew/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.5.4
|
||||||
|
- 1.6.3
|
||||||
|
- 1.7
|
||||||
|
install:
|
||||||
|
- go get -v golang.org/x/tools/cmd/cover
|
||||||
|
script:
|
||||||
|
- go test -v -tags=safe ./spew
|
||||||
|
- go test -v -tags=testcgo ./spew -covermode=count -coverprofile=profile.cov
|
||||||
|
after_success:
|
||||||
|
- go get -v github.com/mattn/goveralls
|
||||||
|
- export PATH=$PATH:$HOME/gopath/bin
|
||||||
|
- goveralls -coverprofile=profile.cov -service=travis-ci
|
15
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
Normal file
15
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2012-2013 Dave Collins <dave@davec.name>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
194
vendor/github.com/davecgh/go-spew/README.md
generated
vendored
Normal file
194
vendor/github.com/davecgh/go-spew/README.md
generated
vendored
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
go-spew
|
||||||
|
=======
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/davecgh/go-spew.png?branch=master)]
|
||||||
|
(https://travis-ci.org/davecgh/go-spew) [![Coverage Status]
|
||||||
|
(https://coveralls.io/repos/davecgh/go-spew/badge.png?branch=master)]
|
||||||
|
(https://coveralls.io/r/davecgh/go-spew?branch=master)
|
||||||
|
|
||||||
|
Go-spew implements a deep pretty printer for Go data structures to aid in
|
||||||
|
debugging. A comprehensive suite of tests with 100% test coverage is provided
|
||||||
|
to ensure proper functionality. See `test_coverage.txt` for the gocov coverage
|
||||||
|
report. Go-spew is licensed under the liberal ISC license, so it may be used in
|
||||||
|
open source or commercial projects.
|
||||||
|
|
||||||
|
If you're interested in reading about how this package came to life and some
|
||||||
|
of the challenges involved in providing a deep pretty printer, there is a blog
|
||||||
|
post about it
|
||||||
|
[here](https://web.archive.org/web/20160304013555/https://blog.cyphertite.com/go-spew-a-journey-into-dumping-go-data-structures/).
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
[![GoDoc](https://godoc.org/github.com/davecgh/go-spew/spew?status.png)]
|
||||||
|
(http://godoc.org/github.com/davecgh/go-spew/spew)
|
||||||
|
|
||||||
|
Full `go doc` style documentation for the project can be viewed online without
|
||||||
|
installing this package by using the excellent GoDoc site here:
|
||||||
|
http://godoc.org/github.com/davecgh/go-spew/spew
|
||||||
|
|
||||||
|
You can also view the documentation locally once the package is installed with
|
||||||
|
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
|
||||||
|
http://localhost:6060/pkg/github.com/davecgh/go-spew/spew
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ go get -u github.com/davecgh/go-spew/spew
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
Add this import line to the file you're working in:
|
||||||
|
|
||||||
|
```Go
|
||||||
|
import "github.com/davecgh/go-spew/spew"
|
||||||
|
```
|
||||||
|
|
||||||
|
To dump a variable with full newlines, indentation, type, and pointer
|
||||||
|
information use Dump, Fdump, or Sdump:
|
||||||
|
|
||||||
|
```Go
|
||||||
|
spew.Dump(myVar1, myVar2, ...)
|
||||||
|
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
||||||
|
str := spew.Sdump(myVar1, myVar2, ...)
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, if you would prefer to use format strings with a compacted inline
|
||||||
|
printing style, use the convenience wrappers Printf, Fprintf, etc with %v (most
|
||||||
|
compact), %+v (adds pointer addresses), %#v (adds types), or %#+v (adds types
|
||||||
|
and pointer addresses):
|
||||||
|
|
||||||
|
```Go
|
||||||
|
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
|
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging a Web Application Example
|
||||||
|
|
||||||
|
Here is an example of how you can use `spew.Sdump()` to help debug a web application. Please be sure to wrap your output using the `html.EscapeString()` function for safety reasons. You should also only use this debugging technique in a development environment, never in production.
|
||||||
|
|
||||||
|
```Go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"html"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "text/html")
|
||||||
|
fmt.Fprintf(w, "Hi there, %s!", r.URL.Path[1:])
|
||||||
|
fmt.Fprintf(w, "<!--\n" + html.EscapeString(spew.Sdump(w)) + "\n-->")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/", handler)
|
||||||
|
http.ListenAndServe(":8080", nil)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sample Dump Output
|
||||||
|
|
||||||
|
```
|
||||||
|
(main.Foo) {
|
||||||
|
unexportedField: (*main.Bar)(0xf84002e210)({
|
||||||
|
flag: (main.Flag) flagTwo,
|
||||||
|
data: (uintptr) <nil>
|
||||||
|
}),
|
||||||
|
ExportedField: (map[interface {}]interface {}) {
|
||||||
|
(string) "one": (bool) true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
([]uint8) {
|
||||||
|
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||||
|
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||||
|
00000020 31 32 |12|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sample Formatter Output
|
||||||
|
|
||||||
|
Double pointer to a uint8:
|
||||||
|
```
|
||||||
|
%v: <**>5
|
||||||
|
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
||||||
|
%#v: (**uint8)5
|
||||||
|
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
||||||
|
```
|
||||||
|
|
||||||
|
Pointer to circular struct with a uint8 field and a pointer to itself:
|
||||||
|
```
|
||||||
|
%v: <*>{1 <*><shown>}
|
||||||
|
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
||||||
|
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
||||||
|
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
Configuration of spew is handled by fields in the ConfigState type. For
|
||||||
|
convenience, all of the top-level functions use a global state available via the
|
||||||
|
spew.Config global.
|
||||||
|
|
||||||
|
It is also possible to create a ConfigState instance that provides methods
|
||||||
|
equivalent to the top-level functions. This allows concurrent configuration
|
||||||
|
options. See the ConfigState documentation for more details.
|
||||||
|
|
||||||
|
```
|
||||||
|
* Indent
|
||||||
|
String to use for each indentation level for Dump functions.
|
||||||
|
It is a single space by default. A popular alternative is "\t".
|
||||||
|
|
||||||
|
* MaxDepth
|
||||||
|
Maximum number of levels to descend into nested data structures.
|
||||||
|
There is no limit by default.
|
||||||
|
|
||||||
|
* DisableMethods
|
||||||
|
Disables invocation of error and Stringer interface methods.
|
||||||
|
Method invocation is enabled by default.
|
||||||
|
|
||||||
|
* DisablePointerMethods
|
||||||
|
Disables invocation of error and Stringer interface methods on types
|
||||||
|
which only accept pointer receivers from non-pointer variables. This option
|
||||||
|
relies on access to the unsafe package, so it will not have any effect when
|
||||||
|
running in environments without access to the unsafe package such as Google
|
||||||
|
App Engine or with the "safe" build tag specified.
|
||||||
|
Pointer method invocation is enabled by default.
|
||||||
|
|
||||||
|
* ContinueOnMethod
|
||||||
|
Enables recursion into types after invoking error and Stringer interface
|
||||||
|
methods. Recursion after method invocation is disabled by default.
|
||||||
|
|
||||||
|
* SortKeys
|
||||||
|
Specifies map keys should be sorted before being printed. Use
|
||||||
|
this to have a more deterministic, diffable output. Note that
|
||||||
|
only native types (bool, int, uint, floats, uintptr and string)
|
||||||
|
and types which implement error or Stringer interfaces are supported,
|
||||||
|
with other types sorted according to the reflect.Value.String() output
|
||||||
|
which guarantees display stability. Natural map order is used by
|
||||||
|
default.
|
||||||
|
|
||||||
|
* SpewKeys
|
||||||
|
SpewKeys specifies that, as a last resort attempt, map keys should be
|
||||||
|
spewed to strings and sorted by those strings. This is only considered
|
||||||
|
if SortKeys is true.
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unsafe Package Dependency
|
||||||
|
|
||||||
|
This package relies on the unsafe package to perform some of the more advanced
|
||||||
|
features, however it also supports a "limited" mode which allows it to work in
|
||||||
|
environments where the unsafe package is not available. By default, it will
|
||||||
|
operate in this mode on Google App Engine and when compiled with GopherJS. The
|
||||||
|
"safe" build tag may also be specified to force the package to build without
|
||||||
|
using the unsafe package.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Go-spew is licensed under the liberal ISC License.
|
22
vendor/github.com/davecgh/go-spew/cov_report.sh
generated
vendored
Normal file
22
vendor/github.com/davecgh/go-spew/cov_report.sh
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# This script uses gocov to generate a test coverage report.
|
||||||
|
# The gocov tool my be obtained with the following command:
|
||||||
|
# go get github.com/axw/gocov/gocov
|
||||||
|
#
|
||||||
|
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
|
||||||
|
|
||||||
|
# Check for gocov.
|
||||||
|
if ! type gocov >/dev/null 2>&1; then
|
||||||
|
echo >&2 "This script requires the gocov tool."
|
||||||
|
echo >&2 "You may obtain it with the following command:"
|
||||||
|
echo >&2 "go get github.com/axw/gocov/gocov"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Only run the cgo tests if gcc is installed.
|
||||||
|
if type gcc >/dev/null 2>&1; then
|
||||||
|
(cd spew && gocov test -tags testcgo | gocov report)
|
||||||
|
else
|
||||||
|
(cd spew && gocov test | gocov report)
|
||||||
|
fi
|
152
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
Normal file
152
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
// Copyright (c) 2015 Dave Collins <dave@davec.name>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||||
|
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
||||||
|
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
||||||
|
// tag is deprecated and thus should not be used.
|
||||||
|
// +build !js,!appengine,!safe,!disableunsafe
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||||
|
// not access to the unsafe package is available.
|
||||||
|
UnsafeDisabled = false
|
||||||
|
|
||||||
|
// ptrSize is the size of a pointer on the current arch.
|
||||||
|
ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
|
||||||
|
// internal reflect.Value fields. These values are valid before golang
|
||||||
|
// commit ecccf07e7f9d which changed the format. The are also valid
|
||||||
|
// after commit 82f48826c6c7 which changed the format again to mirror
|
||||||
|
// the original format. Code in the init function updates these offsets
|
||||||
|
// as necessary.
|
||||||
|
offsetPtr = uintptr(ptrSize)
|
||||||
|
offsetScalar = uintptr(0)
|
||||||
|
offsetFlag = uintptr(ptrSize * 2)
|
||||||
|
|
||||||
|
// flagKindWidth and flagKindShift indicate various bits that the
|
||||||
|
// reflect package uses internally to track kind information.
|
||||||
|
//
|
||||||
|
// flagRO indicates whether or not the value field of a reflect.Value is
|
||||||
|
// read-only.
|
||||||
|
//
|
||||||
|
// flagIndir indicates whether the value field of a reflect.Value is
|
||||||
|
// the actual data or a pointer to the data.
|
||||||
|
//
|
||||||
|
// These values are valid before golang commit 90a7c3c86944 which
|
||||||
|
// changed their positions. Code in the init function updates these
|
||||||
|
// flags as necessary.
|
||||||
|
flagKindWidth = uintptr(5)
|
||||||
|
flagKindShift = uintptr(flagKindWidth - 1)
|
||||||
|
flagRO = uintptr(1 << 0)
|
||||||
|
flagIndir = uintptr(1 << 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Older versions of reflect.Value stored small integers directly in the
|
||||||
|
// ptr field (which is named val in the older versions). Versions
|
||||||
|
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
|
||||||
|
// scalar for this purpose which unfortunately came before the flag
|
||||||
|
// field, so the offset of the flag field is different for those
|
||||||
|
// versions.
|
||||||
|
//
|
||||||
|
// This code constructs a new reflect.Value from a known small integer
|
||||||
|
// and checks if the size of the reflect.Value struct indicates it has
|
||||||
|
// the scalar field. When it does, the offsets are updated accordingly.
|
||||||
|
vv := reflect.ValueOf(0xf00)
|
||||||
|
if unsafe.Sizeof(vv) == (ptrSize * 4) {
|
||||||
|
offsetScalar = ptrSize * 2
|
||||||
|
offsetFlag = ptrSize * 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit 90a7c3c86944 changed the flag positions such that the low
|
||||||
|
// order bits are the kind. This code extracts the kind from the flags
|
||||||
|
// field and ensures it's the correct type. When it's not, the flag
|
||||||
|
// order has been changed to the newer format, so the flags are updated
|
||||||
|
// accordingly.
|
||||||
|
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
|
||||||
|
upfv := *(*uintptr)(upf)
|
||||||
|
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
|
||||||
|
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
|
||||||
|
flagKindShift = 0
|
||||||
|
flagRO = 1 << 5
|
||||||
|
flagIndir = 1 << 6
|
||||||
|
|
||||||
|
// Commit adf9b30e5594 modified the flags to separate the
|
||||||
|
// flagRO flag into two bits which specifies whether or not the
|
||||||
|
// field is embedded. This causes flagIndir to move over a bit
|
||||||
|
// and means that flagRO is the combination of either of the
|
||||||
|
// original flagRO bit and the new bit.
|
||||||
|
//
|
||||||
|
// This code detects the change by extracting what used to be
|
||||||
|
// the indirect bit to ensure it's set. When it's not, the flag
|
||||||
|
// order has been changed to the newer format, so the flags are
|
||||||
|
// updated accordingly.
|
||||||
|
if upfv&flagIndir == 0 {
|
||||||
|
flagRO = 3 << 5
|
||||||
|
flagIndir = 1 << 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
||||||
|
// the typical safety restrictions preventing access to unaddressable and
|
||||||
|
// unexported data. It works by digging the raw pointer to the underlying
|
||||||
|
// value out of the protected value and generating a new unprotected (unsafe)
|
||||||
|
// reflect.Value to it.
|
||||||
|
//
|
||||||
|
// This allows us to check for implementations of the Stringer and error
|
||||||
|
// interfaces to be used for pretty printing ordinarily unaddressable and
|
||||||
|
// inaccessible values such as unexported struct fields.
|
||||||
|
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
|
||||||
|
indirects := 1
|
||||||
|
vt := v.Type()
|
||||||
|
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
|
||||||
|
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
|
||||||
|
if rvf&flagIndir != 0 {
|
||||||
|
vt = reflect.PtrTo(v.Type())
|
||||||
|
indirects++
|
||||||
|
} else if offsetScalar != 0 {
|
||||||
|
// The value is in the scalar field when it's not one of the
|
||||||
|
// reference types.
|
||||||
|
switch vt.Kind() {
|
||||||
|
case reflect.Uintptr:
|
||||||
|
case reflect.Chan:
|
||||||
|
case reflect.Func:
|
||||||
|
case reflect.Map:
|
||||||
|
case reflect.Ptr:
|
||||||
|
case reflect.UnsafePointer:
|
||||||
|
default:
|
||||||
|
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
|
||||||
|
offsetScalar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pv := reflect.NewAt(vt, upv)
|
||||||
|
rv = pv
|
||||||
|
for i := 0; i < indirects; i++ {
|
||||||
|
rv = rv.Elem()
|
||||||
|
}
|
||||||
|
return rv
|
||||||
|
}
|
38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
Normal file
38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2015 Dave Collins <dave@davec.name>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||||
|
// when the code is running on Google App Engine, compiled by GopherJS, or
|
||||||
|
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
||||||
|
// tag is deprecated and thus should not be used.
|
||||||
|
// +build js appengine safe disableunsafe
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||||
|
// not access to the unsafe package is available.
|
||||||
|
UnsafeDisabled = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// unsafeReflectValue typically converts the passed reflect.Value into a one
|
||||||
|
// that bypasses the typical safety restrictions preventing access to
|
||||||
|
// unaddressable and unexported data. However, doing this relies on access to
|
||||||
|
// the unsafe package. This is a stub version which simply returns the passed
|
||||||
|
// reflect.Value when the unsafe package is not available.
|
||||||
|
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||||
|
return v
|
||||||
|
}
|
341
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
Normal file
341
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Some constants in the form of bytes to avoid string overhead. This mirrors
|
||||||
|
// the technique used in the fmt package.
|
||||||
|
var (
|
||||||
|
panicBytes = []byte("(PANIC=")
|
||||||
|
plusBytes = []byte("+")
|
||||||
|
iBytes = []byte("i")
|
||||||
|
trueBytes = []byte("true")
|
||||||
|
falseBytes = []byte("false")
|
||||||
|
interfaceBytes = []byte("(interface {})")
|
||||||
|
commaNewlineBytes = []byte(",\n")
|
||||||
|
newlineBytes = []byte("\n")
|
||||||
|
openBraceBytes = []byte("{")
|
||||||
|
openBraceNewlineBytes = []byte("{\n")
|
||||||
|
closeBraceBytes = []byte("}")
|
||||||
|
asteriskBytes = []byte("*")
|
||||||
|
colonBytes = []byte(":")
|
||||||
|
colonSpaceBytes = []byte(": ")
|
||||||
|
openParenBytes = []byte("(")
|
||||||
|
closeParenBytes = []byte(")")
|
||||||
|
spaceBytes = []byte(" ")
|
||||||
|
pointerChainBytes = []byte("->")
|
||||||
|
nilAngleBytes = []byte("<nil>")
|
||||||
|
maxNewlineBytes = []byte("<max depth reached>\n")
|
||||||
|
maxShortBytes = []byte("<max>")
|
||||||
|
circularBytes = []byte("<already shown>")
|
||||||
|
circularShortBytes = []byte("<shown>")
|
||||||
|
invalidAngleBytes = []byte("<invalid>")
|
||||||
|
openBracketBytes = []byte("[")
|
||||||
|
closeBracketBytes = []byte("]")
|
||||||
|
percentBytes = []byte("%")
|
||||||
|
precisionBytes = []byte(".")
|
||||||
|
openAngleBytes = []byte("<")
|
||||||
|
closeAngleBytes = []byte(">")
|
||||||
|
openMapBytes = []byte("map[")
|
||||||
|
closeMapBytes = []byte("]")
|
||||||
|
lenEqualsBytes = []byte("len=")
|
||||||
|
capEqualsBytes = []byte("cap=")
|
||||||
|
)
|
||||||
|
|
||||||
|
// hexDigits is used to map a decimal value to a hex digit.
|
||||||
|
var hexDigits = "0123456789abcdef"
|
||||||
|
|
||||||
|
// catchPanic handles any panics that might occur during the handleMethods
|
||||||
|
// calls.
|
||||||
|
func catchPanic(w io.Writer, v reflect.Value) {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
w.Write(panicBytes)
|
||||||
|
fmt.Fprintf(w, "%v", err)
|
||||||
|
w.Write(closeParenBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleMethods attempts to call the Error and String methods on the underlying
|
||||||
|
// type the passed reflect.Value represents and outputes the result to Writer w.
|
||||||
|
//
|
||||||
|
// It handles panics in any called methods by catching and displaying the error
|
||||||
|
// as the formatted value.
|
||||||
|
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
|
||||||
|
// We need an interface to check if the type implements the error or
|
||||||
|
// Stringer interface. However, the reflect package won't give us an
|
||||||
|
// interface on certain things like unexported struct fields in order
|
||||||
|
// to enforce visibility rules. We use unsafe, when it's available,
|
||||||
|
// to bypass these restrictions since this package does not mutate the
|
||||||
|
// values.
|
||||||
|
if !v.CanInterface() {
|
||||||
|
if UnsafeDisabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
v = unsafeReflectValue(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choose whether or not to do error and Stringer interface lookups against
|
||||||
|
// the base type or a pointer to the base type depending on settings.
|
||||||
|
// Technically calling one of these methods with a pointer receiver can
|
||||||
|
// mutate the value, however, types which choose to satisify an error or
|
||||||
|
// Stringer interface with a pointer receiver should not be mutating their
|
||||||
|
// state inside these interface methods.
|
||||||
|
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
|
||||||
|
v = unsafeReflectValue(v)
|
||||||
|
}
|
||||||
|
if v.CanAddr() {
|
||||||
|
v = v.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is it an error or Stringer?
|
||||||
|
switch iface := v.Interface().(type) {
|
||||||
|
case error:
|
||||||
|
defer catchPanic(w, v)
|
||||||
|
if cs.ContinueOnMethod {
|
||||||
|
w.Write(openParenBytes)
|
||||||
|
w.Write([]byte(iface.Error()))
|
||||||
|
w.Write(closeParenBytes)
|
||||||
|
w.Write(spaceBytes)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte(iface.Error()))
|
||||||
|
return true
|
||||||
|
|
||||||
|
case fmt.Stringer:
|
||||||
|
defer catchPanic(w, v)
|
||||||
|
if cs.ContinueOnMethod {
|
||||||
|
w.Write(openParenBytes)
|
||||||
|
w.Write([]byte(iface.String()))
|
||||||
|
w.Write(closeParenBytes)
|
||||||
|
w.Write(spaceBytes)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
w.Write([]byte(iface.String()))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// printBool outputs a boolean value as true or false to Writer w.
|
||||||
|
func printBool(w io.Writer, val bool) {
|
||||||
|
if val {
|
||||||
|
w.Write(trueBytes)
|
||||||
|
} else {
|
||||||
|
w.Write(falseBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// printInt outputs a signed integer value to Writer w.
|
||||||
|
func printInt(w io.Writer, val int64, base int) {
|
||||||
|
w.Write([]byte(strconv.FormatInt(val, base)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// printUint outputs an unsigned integer value to Writer w.
|
||||||
|
func printUint(w io.Writer, val uint64, base int) {
|
||||||
|
w.Write([]byte(strconv.FormatUint(val, base)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// printFloat outputs a floating point value using the specified precision,
|
||||||
|
// which is expected to be 32 or 64bit, to Writer w.
|
||||||
|
func printFloat(w io.Writer, val float64, precision int) {
|
||||||
|
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// printComplex outputs a complex value using the specified float precision
|
||||||
|
// for the real and imaginary parts to Writer w.
|
||||||
|
func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
||||||
|
r := real(c)
|
||||||
|
w.Write(openParenBytes)
|
||||||
|
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
|
||||||
|
i := imag(c)
|
||||||
|
if i >= 0 {
|
||||||
|
w.Write(plusBytes)
|
||||||
|
}
|
||||||
|
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
|
||||||
|
w.Write(iBytes)
|
||||||
|
w.Write(closeParenBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
|
||||||
|
// prefix to Writer w.
|
||||||
|
func printHexPtr(w io.Writer, p uintptr) {
|
||||||
|
// Null pointer.
|
||||||
|
num := uint64(p)
|
||||||
|
if num == 0 {
|
||||||
|
w.Write(nilAngleBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
|
||||||
|
buf := make([]byte, 18)
|
||||||
|
|
||||||
|
// It's simpler to construct the hex string right to left.
|
||||||
|
base := uint64(16)
|
||||||
|
i := len(buf) - 1
|
||||||
|
for num >= base {
|
||||||
|
buf[i] = hexDigits[num%base]
|
||||||
|
num /= base
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
buf[i] = hexDigits[num]
|
||||||
|
|
||||||
|
// Add '0x' prefix.
|
||||||
|
i--
|
||||||
|
buf[i] = 'x'
|
||||||
|
i--
|
||||||
|
buf[i] = '0'
|
||||||
|
|
||||||
|
// Strip unused leading bytes.
|
||||||
|
buf = buf[i:]
|
||||||
|
w.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
|
||||||
|
// elements to be sorted.
|
||||||
|
type valuesSorter struct {
|
||||||
|
values []reflect.Value
|
||||||
|
strings []string // either nil or same len and values
|
||||||
|
cs *ConfigState
|
||||||
|
}
|
||||||
|
|
||||||
|
// newValuesSorter initializes a valuesSorter instance, which holds a set of
|
||||||
|
// surrogate keys on which the data should be sorted. It uses flags in
|
||||||
|
// ConfigState to decide if and how to populate those surrogate keys.
|
||||||
|
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
|
||||||
|
vs := &valuesSorter{values: values, cs: cs}
|
||||||
|
if canSortSimply(vs.values[0].Kind()) {
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
if !cs.DisableMethods {
|
||||||
|
vs.strings = make([]string, len(values))
|
||||||
|
for i := range vs.values {
|
||||||
|
b := bytes.Buffer{}
|
||||||
|
if !handleMethods(cs, &b, vs.values[i]) {
|
||||||
|
vs.strings = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
vs.strings[i] = b.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if vs.strings == nil && cs.SpewKeys {
|
||||||
|
vs.strings = make([]string, len(values))
|
||||||
|
for i := range vs.values {
|
||||||
|
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
|
||||||
|
// directly, or whether it should be considered for sorting by surrogate keys
|
||||||
|
// (if the ConfigState allows it).
|
||||||
|
func canSortSimply(kind reflect.Kind) bool {
|
||||||
|
// This switch parallels valueSortLess, except for the default case.
|
||||||
|
switch kind {
|
||||||
|
case reflect.Bool:
|
||||||
|
return true
|
||||||
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
|
return true
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||||
|
return true
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return true
|
||||||
|
case reflect.String:
|
||||||
|
return true
|
||||||
|
case reflect.Uintptr:
|
||||||
|
return true
|
||||||
|
case reflect.Array:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of values in the slice. It is part of the
|
||||||
|
// sort.Interface implementation.
|
||||||
|
func (s *valuesSorter) Len() int {
|
||||||
|
return len(s.values)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap swaps the values at the passed indices. It is part of the
|
||||||
|
// sort.Interface implementation.
|
||||||
|
func (s *valuesSorter) Swap(i, j int) {
|
||||||
|
s.values[i], s.values[j] = s.values[j], s.values[i]
|
||||||
|
if s.strings != nil {
|
||||||
|
s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// valueSortLess returns whether the first value should sort before the second
|
||||||
|
// value. It is used by valueSorter.Less as part of the sort.Interface
|
||||||
|
// implementation.
|
||||||
|
func valueSortLess(a, b reflect.Value) bool {
|
||||||
|
switch a.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
return !a.Bool() && b.Bool()
|
||||||
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
|
return a.Int() < b.Int()
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||||
|
return a.Uint() < b.Uint()
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return a.Float() < b.Float()
|
||||||
|
case reflect.String:
|
||||||
|
return a.String() < b.String()
|
||||||
|
case reflect.Uintptr:
|
||||||
|
return a.Uint() < b.Uint()
|
||||||
|
case reflect.Array:
|
||||||
|
// Compare the contents of both arrays.
|
||||||
|
l := a.Len()
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
av := a.Index(i)
|
||||||
|
bv := b.Index(i)
|
||||||
|
if av.Interface() == bv.Interface() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return valueSortLess(av, bv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a.String() < b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less returns whether the value at index i should sort before the
|
||||||
|
// value at index j. It is part of the sort.Interface implementation.
|
||||||
|
func (s *valuesSorter) Less(i, j int) bool {
|
||||||
|
if s.strings == nil {
|
||||||
|
return valueSortLess(s.values[i], s.values[j])
|
||||||
|
}
|
||||||
|
return s.strings[i] < s.strings[j]
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortValues is a sort function that handles both native types and any type that
|
||||||
|
// can be converted to error or Stringer. Other inputs are sorted according to
|
||||||
|
// their Value.String() value to ensure display stability.
|
||||||
|
func sortValues(values []reflect.Value, cs *ConfigState) {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sort.Sort(newValuesSorter(values, cs))
|
||||||
|
}
|
298
vendor/github.com/davecgh/go-spew/spew/common_test.go
generated
vendored
Normal file
298
vendor/github.com/davecgh/go-spew/spew/common_test.go
generated
vendored
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
)
|
||||||
|
|
||||||
|
// custom type to test Stinger interface on non-pointer receiver.
|
||||||
|
type stringer string
|
||||||
|
|
||||||
|
// String implements the Stringer interface for testing invocation of custom
|
||||||
|
// stringers on types with non-pointer receivers.
|
||||||
|
func (s stringer) String() string {
|
||||||
|
return "stringer " + string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom type to test Stinger interface on pointer receiver.
|
||||||
|
type pstringer string
|
||||||
|
|
||||||
|
// String implements the Stringer interface for testing invocation of custom
|
||||||
|
// stringers on types with only pointer receivers.
|
||||||
|
func (s *pstringer) String() string {
|
||||||
|
return "stringer " + string(*s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// xref1 and xref2 are cross referencing structs for testing circular reference
|
||||||
|
// detection.
|
||||||
|
type xref1 struct {
|
||||||
|
ps2 *xref2
|
||||||
|
}
|
||||||
|
type xref2 struct {
|
||||||
|
ps1 *xref1
|
||||||
|
}
|
||||||
|
|
||||||
|
// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular
|
||||||
|
// reference for testing detection.
|
||||||
|
type indirCir1 struct {
|
||||||
|
ps2 *indirCir2
|
||||||
|
}
|
||||||
|
type indirCir2 struct {
|
||||||
|
ps3 *indirCir3
|
||||||
|
}
|
||||||
|
type indirCir3 struct {
|
||||||
|
ps1 *indirCir1
|
||||||
|
}
|
||||||
|
|
||||||
|
// embed is used to test embedded structures.
|
||||||
|
type embed struct {
|
||||||
|
a string
|
||||||
|
}
|
||||||
|
|
||||||
|
// embedwrap is used to test embedded structures.
|
||||||
|
type embedwrap struct {
|
||||||
|
*embed
|
||||||
|
e *embed
|
||||||
|
}
|
||||||
|
|
||||||
|
// panicer is used to intentionally cause a panic for testing spew properly
|
||||||
|
// handles them
|
||||||
|
type panicer int
|
||||||
|
|
||||||
|
func (p panicer) String() string {
|
||||||
|
panic("test panic")
|
||||||
|
}
|
||||||
|
|
||||||
|
// customError is used to test custom error interface invocation.
|
||||||
|
type customError int
|
||||||
|
|
||||||
|
func (e customError) Error() string {
|
||||||
|
return fmt.Sprintf("error: %d", int(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringizeWants converts a slice of wanted test output into a format suitable
|
||||||
|
// for a test error message.
|
||||||
|
func stringizeWants(wants []string) string {
|
||||||
|
s := ""
|
||||||
|
for i, want := range wants {
|
||||||
|
if i > 0 {
|
||||||
|
s += fmt.Sprintf("want%d: %s", i+1, want)
|
||||||
|
} else {
|
||||||
|
s += "want: " + want
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// testFailed returns whether or not a test failed by checking if the result
|
||||||
|
// of the test is in the slice of wanted strings.
|
||||||
|
func testFailed(result string, wants []string) bool {
|
||||||
|
for _, want := range wants {
|
||||||
|
if result == want {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type sortableStruct struct {
|
||||||
|
x int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss sortableStruct) String() string {
|
||||||
|
return fmt.Sprintf("ss.%d", ss.x)
|
||||||
|
}
|
||||||
|
|
||||||
|
type unsortableStruct struct {
|
||||||
|
x int
|
||||||
|
}
|
||||||
|
|
||||||
|
type sortTestCase struct {
|
||||||
|
input []reflect.Value
|
||||||
|
expected []reflect.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func helpTestSortValues(tests []sortTestCase, cs *spew.ConfigState, t *testing.T) {
|
||||||
|
getInterfaces := func(values []reflect.Value) []interface{} {
|
||||||
|
interfaces := []interface{}{}
|
||||||
|
for _, v := range values {
|
||||||
|
interfaces = append(interfaces, v.Interface())
|
||||||
|
}
|
||||||
|
return interfaces
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
spew.SortValues(test.input, cs)
|
||||||
|
// reflect.DeepEqual cannot really make sense of reflect.Value,
|
||||||
|
// probably because of all the pointer tricks. For instance,
|
||||||
|
// v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{}
|
||||||
|
// instead.
|
||||||
|
input := getInterfaces(test.input)
|
||||||
|
expected := getInterfaces(test.expected)
|
||||||
|
if !reflect.DeepEqual(input, expected) {
|
||||||
|
t.Errorf("Sort mismatch:\n %v != %v", input, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSortValues ensures the sort functionality for relect.Value based sorting
|
||||||
|
// works as intended.
|
||||||
|
func TestSortValues(t *testing.T) {
|
||||||
|
v := reflect.ValueOf
|
||||||
|
|
||||||
|
a := v("a")
|
||||||
|
b := v("b")
|
||||||
|
c := v("c")
|
||||||
|
embedA := v(embed{"a"})
|
||||||
|
embedB := v(embed{"b"})
|
||||||
|
embedC := v(embed{"c"})
|
||||||
|
tests := []sortTestCase{
|
||||||
|
// No values.
|
||||||
|
{
|
||||||
|
[]reflect.Value{},
|
||||||
|
[]reflect.Value{},
|
||||||
|
},
|
||||||
|
// Bools.
|
||||||
|
{
|
||||||
|
[]reflect.Value{v(false), v(true), v(false)},
|
||||||
|
[]reflect.Value{v(false), v(false), v(true)},
|
||||||
|
},
|
||||||
|
// Ints.
|
||||||
|
{
|
||||||
|
[]reflect.Value{v(2), v(1), v(3)},
|
||||||
|
[]reflect.Value{v(1), v(2), v(3)},
|
||||||
|
},
|
||||||
|
// Uints.
|
||||||
|
{
|
||||||
|
[]reflect.Value{v(uint8(2)), v(uint8(1)), v(uint8(3))},
|
||||||
|
[]reflect.Value{v(uint8(1)), v(uint8(2)), v(uint8(3))},
|
||||||
|
},
|
||||||
|
// Floats.
|
||||||
|
{
|
||||||
|
[]reflect.Value{v(2.0), v(1.0), v(3.0)},
|
||||||
|
[]reflect.Value{v(1.0), v(2.0), v(3.0)},
|
||||||
|
},
|
||||||
|
// Strings.
|
||||||
|
{
|
||||||
|
[]reflect.Value{b, a, c},
|
||||||
|
[]reflect.Value{a, b, c},
|
||||||
|
},
|
||||||
|
// Array
|
||||||
|
{
|
||||||
|
[]reflect.Value{v([3]int{3, 2, 1}), v([3]int{1, 3, 2}), v([3]int{1, 2, 3})},
|
||||||
|
[]reflect.Value{v([3]int{1, 2, 3}), v([3]int{1, 3, 2}), v([3]int{3, 2, 1})},
|
||||||
|
},
|
||||||
|
// Uintptrs.
|
||||||
|
{
|
||||||
|
[]reflect.Value{v(uintptr(2)), v(uintptr(1)), v(uintptr(3))},
|
||||||
|
[]reflect.Value{v(uintptr(1)), v(uintptr(2)), v(uintptr(3))},
|
||||||
|
},
|
||||||
|
// SortableStructs.
|
||||||
|
{
|
||||||
|
// Note: not sorted - DisableMethods is set.
|
||||||
|
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||||
|
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||||
|
},
|
||||||
|
// UnsortableStructs.
|
||||||
|
{
|
||||||
|
// Note: not sorted - SpewKeys is false.
|
||||||
|
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||||
|
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||||
|
},
|
||||||
|
// Invalid.
|
||||||
|
{
|
||||||
|
[]reflect.Value{embedB, embedA, embedC},
|
||||||
|
[]reflect.Value{embedB, embedA, embedC},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cs := spew.ConfigState{DisableMethods: true, SpewKeys: false}
|
||||||
|
helpTestSortValues(tests, &cs, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSortValuesWithMethods ensures the sort functionality for relect.Value
|
||||||
|
// based sorting works as intended when using string methods.
|
||||||
|
func TestSortValuesWithMethods(t *testing.T) {
|
||||||
|
v := reflect.ValueOf
|
||||||
|
|
||||||
|
a := v("a")
|
||||||
|
b := v("b")
|
||||||
|
c := v("c")
|
||||||
|
tests := []sortTestCase{
|
||||||
|
// Ints.
|
||||||
|
{
|
||||||
|
[]reflect.Value{v(2), v(1), v(3)},
|
||||||
|
[]reflect.Value{v(1), v(2), v(3)},
|
||||||
|
},
|
||||||
|
// Strings.
|
||||||
|
{
|
||||||
|
[]reflect.Value{b, a, c},
|
||||||
|
[]reflect.Value{a, b, c},
|
||||||
|
},
|
||||||
|
// SortableStructs.
|
||||||
|
{
|
||||||
|
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||||
|
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
|
||||||
|
},
|
||||||
|
// UnsortableStructs.
|
||||||
|
{
|
||||||
|
// Note: not sorted - SpewKeys is false.
|
||||||
|
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||||
|
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cs := spew.ConfigState{DisableMethods: false, SpewKeys: false}
|
||||||
|
helpTestSortValues(tests, &cs, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSortValuesWithSpew ensures the sort functionality for relect.Value
|
||||||
|
// based sorting works as intended when using spew to stringify keys.
|
||||||
|
func TestSortValuesWithSpew(t *testing.T) {
|
||||||
|
v := reflect.ValueOf
|
||||||
|
|
||||||
|
a := v("a")
|
||||||
|
b := v("b")
|
||||||
|
c := v("c")
|
||||||
|
tests := []sortTestCase{
|
||||||
|
// Ints.
|
||||||
|
{
|
||||||
|
[]reflect.Value{v(2), v(1), v(3)},
|
||||||
|
[]reflect.Value{v(1), v(2), v(3)},
|
||||||
|
},
|
||||||
|
// Strings.
|
||||||
|
{
|
||||||
|
[]reflect.Value{b, a, c},
|
||||||
|
[]reflect.Value{a, b, c},
|
||||||
|
},
|
||||||
|
// SortableStructs.
|
||||||
|
{
|
||||||
|
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||||
|
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
|
||||||
|
},
|
||||||
|
// UnsortableStructs.
|
||||||
|
{
|
||||||
|
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||||
|
[]reflect.Value{v(unsortableStruct{1}), v(unsortableStruct{2}), v(unsortableStruct{3})},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cs := spew.ConfigState{DisableMethods: true, SpewKeys: true}
|
||||||
|
helpTestSortValues(tests, &cs, t)
|
||||||
|
}
|
297
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
Normal file
297
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfigState houses the configuration options used by spew to format and
|
||||||
|
// display values. There is a global instance, Config, that is used to control
|
||||||
|
// all top-level Formatter and Dump functionality. Each ConfigState instance
|
||||||
|
// provides methods equivalent to the top-level functions.
|
||||||
|
//
|
||||||
|
// The zero value for ConfigState provides no indentation. You would typically
|
||||||
|
// want to set it to a space or a tab.
|
||||||
|
//
|
||||||
|
// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
|
||||||
|
// with default settings. See the documentation of NewDefaultConfig for default
|
||||||
|
// values.
|
||||||
|
type ConfigState struct {
|
||||||
|
// Indent specifies the string to use for each indentation level. The
|
||||||
|
// global config instance that all top-level functions use set this to a
|
||||||
|
// single space by default. If you would like more indentation, you might
|
||||||
|
// set this to a tab with "\t" or perhaps two spaces with " ".
|
||||||
|
Indent string
|
||||||
|
|
||||||
|
// MaxDepth controls the maximum number of levels to descend into nested
|
||||||
|
// data structures. The default, 0, means there is no limit.
|
||||||
|
//
|
||||||
|
// NOTE: Circular data structures are properly detected, so it is not
|
||||||
|
// necessary to set this value unless you specifically want to limit deeply
|
||||||
|
// nested data structures.
|
||||||
|
MaxDepth int
|
||||||
|
|
||||||
|
// DisableMethods specifies whether or not error and Stringer interfaces are
|
||||||
|
// invoked for types that implement them.
|
||||||
|
DisableMethods bool
|
||||||
|
|
||||||
|
// DisablePointerMethods specifies whether or not to check for and invoke
|
||||||
|
// error and Stringer interfaces on types which only accept a pointer
|
||||||
|
// receiver when the current type is not a pointer.
|
||||||
|
//
|
||||||
|
// NOTE: This might be an unsafe action since calling one of these methods
|
||||||
|
// with a pointer receiver could technically mutate the value, however,
|
||||||
|
// in practice, types which choose to satisify an error or Stringer
|
||||||
|
// interface with a pointer receiver should not be mutating their state
|
||||||
|
// inside these interface methods. As a result, this option relies on
|
||||||
|
// access to the unsafe package, so it will not have any effect when
|
||||||
|
// running in environments without access to the unsafe package such as
|
||||||
|
// Google App Engine or with the "safe" build tag specified.
|
||||||
|
DisablePointerMethods bool
|
||||||
|
|
||||||
|
// ContinueOnMethod specifies whether or not recursion should continue once
|
||||||
|
// a custom error or Stringer interface is invoked. The default, false,
|
||||||
|
// means it will print the results of invoking the custom error or Stringer
|
||||||
|
// interface and return immediately instead of continuing to recurse into
|
||||||
|
// the internals of the data type.
|
||||||
|
//
|
||||||
|
// NOTE: This flag does not have any effect if method invocation is disabled
|
||||||
|
// via the DisableMethods or DisablePointerMethods options.
|
||||||
|
ContinueOnMethod bool
|
||||||
|
|
||||||
|
// SortKeys specifies map keys should be sorted before being printed. Use
|
||||||
|
// this to have a more deterministic, diffable output. Note that only
|
||||||
|
// native types (bool, int, uint, floats, uintptr and string) and types
|
||||||
|
// that support the error or Stringer interfaces (if methods are
|
||||||
|
// enabled) are supported, with other types sorted according to the
|
||||||
|
// reflect.Value.String() output which guarantees display stability.
|
||||||
|
SortKeys bool
|
||||||
|
|
||||||
|
// SpewKeys specifies that, as a last resort attempt, map keys should
|
||||||
|
// be spewed to strings and sorted by those strings. This is only
|
||||||
|
// considered if SortKeys is true.
|
||||||
|
SpewKeys bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config is the active configuration of the top-level functions.
|
||||||
|
// The configuration can be changed by modifying the contents of spew.Config.
|
||||||
|
var Config = ConfigState{Indent: " "}
|
||||||
|
|
||||||
|
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the formatted string as a value that satisfies error. See NewFormatter
|
||||||
|
// for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
|
||||||
|
return fmt.Errorf(format, c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprint(w, c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintf(w, format, c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintln(w, c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Print(c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Printf(format, c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Println(c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the resulting string. See NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Sprint(a ...interface{}) string {
|
||||||
|
return fmt.Sprint(c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
||||||
|
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||||
|
// the resulting string. See NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
|
||||||
|
return fmt.Sprintf(format, c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
||||||
|
// were passed with a Formatter interface returned by c.NewFormatter. It
|
||||||
|
// returns the resulting string. See NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
|
||||||
|
func (c *ConfigState) Sprintln(a ...interface{}) string {
|
||||||
|
return fmt.Sprintln(c.convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||||
|
interface. As a result, it integrates cleanly with standard fmt package
|
||||||
|
printing functions. The formatter is useful for inline printing of smaller data
|
||||||
|
types similar to the standard %v format specifier.
|
||||||
|
|
||||||
|
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||||
|
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
|
||||||
|
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||||
|
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||||
|
the width and precision arguments (however they will still work on the format
|
||||||
|
specifiers not handled by the custom formatter).
|
||||||
|
|
||||||
|
Typically this function shouldn't be called directly. It is much easier to make
|
||||||
|
use of the custom formatter by calling one of the convenience functions such as
|
||||||
|
c.Printf, c.Println, or c.Printf.
|
||||||
|
*/
|
||||||
|
func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
|
||||||
|
return newFormatter(c, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||||
|
// exactly the same as Dump.
|
||||||
|
func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
|
||||||
|
fdump(c, w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Dump displays the passed parameters to standard out with newlines, customizable
|
||||||
|
indentation, and additional debug information such as complete types and all
|
||||||
|
pointer addresses used to indirect to the final value. It provides the
|
||||||
|
following features over the built-in printing facilities provided by the fmt
|
||||||
|
package:
|
||||||
|
|
||||||
|
* Pointers are dereferenced and followed
|
||||||
|
* Circular data structures are detected and handled properly
|
||||||
|
* Custom Stringer/error interfaces are optionally invoked, including
|
||||||
|
on unexported types
|
||||||
|
* Custom types which only implement the Stringer/error interfaces via
|
||||||
|
a pointer receiver are optionally invoked when passing non-pointer
|
||||||
|
variables
|
||||||
|
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||||
|
includes offsets, byte values in hex, and ASCII output
|
||||||
|
|
||||||
|
The configuration options are controlled by modifying the public members
|
||||||
|
of c. See ConfigState for options documentation.
|
||||||
|
|
||||||
|
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
||||||
|
get the formatted result as a string.
|
||||||
|
*/
|
||||||
|
func (c *ConfigState) Dump(a ...interface{}) {
|
||||||
|
fdump(c, os.Stdout, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sdump returns a string with the passed arguments formatted exactly the same
|
||||||
|
// as Dump.
|
||||||
|
func (c *ConfigState) Sdump(a ...interface{}) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fdump(c, &buf, a...)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertArgs accepts a slice of arguments and returns a slice of the same
|
||||||
|
// length with each argument converted to a spew Formatter interface using
|
||||||
|
// the ConfigState associated with s.
|
||||||
|
func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
|
||||||
|
formatters = make([]interface{}, len(args))
|
||||||
|
for index, arg := range args {
|
||||||
|
formatters[index] = newFormatter(c, arg)
|
||||||
|
}
|
||||||
|
return formatters
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultConfig returns a ConfigState with the following default settings.
|
||||||
|
//
|
||||||
|
// Indent: " "
|
||||||
|
// MaxDepth: 0
|
||||||
|
// DisableMethods: false
|
||||||
|
// DisablePointerMethods: false
|
||||||
|
// ContinueOnMethod: false
|
||||||
|
// SortKeys: false
|
||||||
|
func NewDefaultConfig() *ConfigState {
|
||||||
|
return &ConfigState{Indent: " "}
|
||||||
|
}
|
202
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
Normal file
202
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package spew implements a deep pretty printer for Go data structures to aid in
|
||||||
|
debugging.
|
||||||
|
|
||||||
|
A quick overview of the additional features spew provides over the built-in
|
||||||
|
printing facilities for Go data types are as follows:
|
||||||
|
|
||||||
|
* Pointers are dereferenced and followed
|
||||||
|
* Circular data structures are detected and handled properly
|
||||||
|
* Custom Stringer/error interfaces are optionally invoked, including
|
||||||
|
on unexported types
|
||||||
|
* Custom types which only implement the Stringer/error interfaces via
|
||||||
|
a pointer receiver are optionally invoked when passing non-pointer
|
||||||
|
variables
|
||||||
|
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||||
|
includes offsets, byte values in hex, and ASCII output (only when using
|
||||||
|
Dump style)
|
||||||
|
|
||||||
|
There are two different approaches spew allows for dumping Go data structures:
|
||||||
|
|
||||||
|
* Dump style which prints with newlines, customizable indentation,
|
||||||
|
and additional debug information such as types and all pointer addresses
|
||||||
|
used to indirect to the final value
|
||||||
|
* A custom Formatter interface that integrates cleanly with the standard fmt
|
||||||
|
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
|
||||||
|
similar to the default %v while providing the additional functionality
|
||||||
|
outlined above and passing unsupported format verbs such as %x and %q
|
||||||
|
along to fmt
|
||||||
|
|
||||||
|
Quick Start
|
||||||
|
|
||||||
|
This section demonstrates how to quickly get started with spew. See the
|
||||||
|
sections below for further details on formatting and configuration options.
|
||||||
|
|
||||||
|
To dump a variable with full newlines, indentation, type, and pointer
|
||||||
|
information use Dump, Fdump, or Sdump:
|
||||||
|
spew.Dump(myVar1, myVar2, ...)
|
||||||
|
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
||||||
|
str := spew.Sdump(myVar1, myVar2, ...)
|
||||||
|
|
||||||
|
Alternatively, if you would prefer to use format strings with a compacted inline
|
||||||
|
printing style, use the convenience wrappers Printf, Fprintf, etc with
|
||||||
|
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
|
||||||
|
%#+v (adds types and pointer addresses):
|
||||||
|
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
|
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
|
|
||||||
|
Configuration Options
|
||||||
|
|
||||||
|
Configuration of spew is handled by fields in the ConfigState type. For
|
||||||
|
convenience, all of the top-level functions use a global state available
|
||||||
|
via the spew.Config global.
|
||||||
|
|
||||||
|
It is also possible to create a ConfigState instance that provides methods
|
||||||
|
equivalent to the top-level functions. This allows concurrent configuration
|
||||||
|
options. See the ConfigState documentation for more details.
|
||||||
|
|
||||||
|
The following configuration options are available:
|
||||||
|
* Indent
|
||||||
|
String to use for each indentation level for Dump functions.
|
||||||
|
It is a single space by default. A popular alternative is "\t".
|
||||||
|
|
||||||
|
* MaxDepth
|
||||||
|
Maximum number of levels to descend into nested data structures.
|
||||||
|
There is no limit by default.
|
||||||
|
|
||||||
|
* DisableMethods
|
||||||
|
Disables invocation of error and Stringer interface methods.
|
||||||
|
Method invocation is enabled by default.
|
||||||
|
|
||||||
|
* DisablePointerMethods
|
||||||
|
Disables invocation of error and Stringer interface methods on types
|
||||||
|
which only accept pointer receivers from non-pointer variables.
|
||||||
|
Pointer method invocation is enabled by default.
|
||||||
|
|
||||||
|
* ContinueOnMethod
|
||||||
|
Enables recursion into types after invoking error and Stringer interface
|
||||||
|
methods. Recursion after method invocation is disabled by default.
|
||||||
|
|
||||||
|
* SortKeys
|
||||||
|
Specifies map keys should be sorted before being printed. Use
|
||||||
|
this to have a more deterministic, diffable output. Note that
|
||||||
|
only native types (bool, int, uint, floats, uintptr and string)
|
||||||
|
and types which implement error or Stringer interfaces are
|
||||||
|
supported with other types sorted according to the
|
||||||
|
reflect.Value.String() output which guarantees display
|
||||||
|
stability. Natural map order is used by default.
|
||||||
|
|
||||||
|
* SpewKeys
|
||||||
|
Specifies that, as a last resort attempt, map keys should be
|
||||||
|
spewed to strings and sorted by those strings. This is only
|
||||||
|
considered if SortKeys is true.
|
||||||
|
|
||||||
|
Dump Usage
|
||||||
|
|
||||||
|
Simply call spew.Dump with a list of variables you want to dump:
|
||||||
|
|
||||||
|
spew.Dump(myVar1, myVar2, ...)
|
||||||
|
|
||||||
|
You may also call spew.Fdump if you would prefer to output to an arbitrary
|
||||||
|
io.Writer. For example, to dump to standard error:
|
||||||
|
|
||||||
|
spew.Fdump(os.Stderr, myVar1, myVar2, ...)
|
||||||
|
|
||||||
|
A third option is to call spew.Sdump to get the formatted output as a string:
|
||||||
|
|
||||||
|
str := spew.Sdump(myVar1, myVar2, ...)
|
||||||
|
|
||||||
|
Sample Dump Output
|
||||||
|
|
||||||
|
See the Dump example for details on the setup of the types and variables being
|
||||||
|
shown here.
|
||||||
|
|
||||||
|
(main.Foo) {
|
||||||
|
unexportedField: (*main.Bar)(0xf84002e210)({
|
||||||
|
flag: (main.Flag) flagTwo,
|
||||||
|
data: (uintptr) <nil>
|
||||||
|
}),
|
||||||
|
ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||||
|
(string) (len=3) "one": (bool) true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
|
||||||
|
command as shown.
|
||||||
|
([]uint8) (len=32 cap=32) {
|
||||||
|
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||||
|
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||||
|
00000020 31 32 |12|
|
||||||
|
}
|
||||||
|
|
||||||
|
Custom Formatter
|
||||||
|
|
||||||
|
Spew provides a custom formatter that implements the fmt.Formatter interface
|
||||||
|
so that it integrates cleanly with standard fmt package printing functions. The
|
||||||
|
formatter is useful for inline printing of smaller data types similar to the
|
||||||
|
standard %v format specifier.
|
||||||
|
|
||||||
|
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||||
|
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||||
|
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||||
|
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||||
|
the width and precision arguments (however they will still work on the format
|
||||||
|
specifiers not handled by the custom formatter).
|
||||||
|
|
||||||
|
Custom Formatter Usage
|
||||||
|
|
||||||
|
The simplest way to make use of the spew custom formatter is to call one of the
|
||||||
|
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
|
||||||
|
functions have syntax you are most likely already familiar with:
|
||||||
|
|
||||||
|
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
|
spew.Println(myVar, myVar2)
|
||||||
|
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
|
|
||||||
|
See the Index for the full list convenience functions.
|
||||||
|
|
||||||
|
Sample Formatter Output
|
||||||
|
|
||||||
|
Double pointer to a uint8:
|
||||||
|
%v: <**>5
|
||||||
|
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
||||||
|
%#v: (**uint8)5
|
||||||
|
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
||||||
|
|
||||||
|
Pointer to circular struct with a uint8 field and a pointer to itself:
|
||||||
|
%v: <*>{1 <*><shown>}
|
||||||
|
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
||||||
|
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
||||||
|
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
||||||
|
|
||||||
|
See the Printf example for details on the setup of variables being shown
|
||||||
|
here.
|
||||||
|
|
||||||
|
Errors
|
||||||
|
|
||||||
|
Since it is possible for custom Stringer/error interfaces to panic, spew
|
||||||
|
detects them and handles them internally by printing the panic information
|
||||||
|
inline with the output. Since spew is intended to provide deep pretty printing
|
||||||
|
capabilities on structures, it intentionally does not return any errors.
|
||||||
|
*/
|
||||||
|
package spew
|
509
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
Normal file
509
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
Normal file
@ -0,0 +1,509 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// uint8Type is a reflect.Type representing a uint8. It is used to
|
||||||
|
// convert cgo types to uint8 slices for hexdumping.
|
||||||
|
uint8Type = reflect.TypeOf(uint8(0))
|
||||||
|
|
||||||
|
// cCharRE is a regular expression that matches a cgo char.
|
||||||
|
// It is used to detect character arrays to hexdump them.
|
||||||
|
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
|
||||||
|
|
||||||
|
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
||||||
|
// char. It is used to detect unsigned character arrays to hexdump
|
||||||
|
// them.
|
||||||
|
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
|
||||||
|
|
||||||
|
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
||||||
|
// It is used to detect uint8_t arrays to hexdump them.
|
||||||
|
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
|
||||||
|
)
|
||||||
|
|
||||||
|
// dumpState contains information about the state of a dump operation.
|
||||||
|
type dumpState struct {
|
||||||
|
w io.Writer
|
||||||
|
depth int
|
||||||
|
pointers map[uintptr]int
|
||||||
|
ignoreNextType bool
|
||||||
|
ignoreNextIndent bool
|
||||||
|
cs *ConfigState
|
||||||
|
}
|
||||||
|
|
||||||
|
// indent performs indentation according to the depth level and cs.Indent
|
||||||
|
// option.
|
||||||
|
func (d *dumpState) indent() {
|
||||||
|
if d.ignoreNextIndent {
|
||||||
|
d.ignoreNextIndent = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpackValue returns values inside of non-nil interfaces when possible.
|
||||||
|
// This is useful for data types like structs, arrays, slices, and maps which
|
||||||
|
// can contain varying types packed inside an interface.
|
||||||
|
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
|
||||||
|
if v.Kind() == reflect.Interface && !v.IsNil() {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// dumpPtr handles formatting of pointers by indirecting them as necessary.
|
||||||
|
func (d *dumpState) dumpPtr(v reflect.Value) {
|
||||||
|
// Remove pointers at or below the current depth from map used to detect
|
||||||
|
// circular refs.
|
||||||
|
for k, depth := range d.pointers {
|
||||||
|
if depth >= d.depth {
|
||||||
|
delete(d.pointers, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep list of all dereferenced pointers to show later.
|
||||||
|
pointerChain := make([]uintptr, 0)
|
||||||
|
|
||||||
|
// Figure out how many levels of indirection there are by dereferencing
|
||||||
|
// pointers and unpacking interfaces down the chain while detecting circular
|
||||||
|
// references.
|
||||||
|
nilFound := false
|
||||||
|
cycleFound := false
|
||||||
|
indirects := 0
|
||||||
|
ve := v
|
||||||
|
for ve.Kind() == reflect.Ptr {
|
||||||
|
if ve.IsNil() {
|
||||||
|
nilFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
indirects++
|
||||||
|
addr := ve.Pointer()
|
||||||
|
pointerChain = append(pointerChain, addr)
|
||||||
|
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
|
||||||
|
cycleFound = true
|
||||||
|
indirects--
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d.pointers[addr] = d.depth
|
||||||
|
|
||||||
|
ve = ve.Elem()
|
||||||
|
if ve.Kind() == reflect.Interface {
|
||||||
|
if ve.IsNil() {
|
||||||
|
nilFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ve = ve.Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display type information.
|
||||||
|
d.w.Write(openParenBytes)
|
||||||
|
d.w.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||||
|
d.w.Write([]byte(ve.Type().String()))
|
||||||
|
d.w.Write(closeParenBytes)
|
||||||
|
|
||||||
|
// Display pointer information.
|
||||||
|
if len(pointerChain) > 0 {
|
||||||
|
d.w.Write(openParenBytes)
|
||||||
|
for i, addr := range pointerChain {
|
||||||
|
if i > 0 {
|
||||||
|
d.w.Write(pointerChainBytes)
|
||||||
|
}
|
||||||
|
printHexPtr(d.w, addr)
|
||||||
|
}
|
||||||
|
d.w.Write(closeParenBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display dereferenced value.
|
||||||
|
d.w.Write(openParenBytes)
|
||||||
|
switch {
|
||||||
|
case nilFound == true:
|
||||||
|
d.w.Write(nilAngleBytes)
|
||||||
|
|
||||||
|
case cycleFound == true:
|
||||||
|
d.w.Write(circularBytes)
|
||||||
|
|
||||||
|
default:
|
||||||
|
d.ignoreNextType = true
|
||||||
|
d.dump(ve)
|
||||||
|
}
|
||||||
|
d.w.Write(closeParenBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
|
||||||
|
// reflection) arrays and slices are dumped in hexdump -C fashion.
|
||||||
|
func (d *dumpState) dumpSlice(v reflect.Value) {
|
||||||
|
// Determine whether this type should be hex dumped or not. Also,
|
||||||
|
// for types which should be hexdumped, try to use the underlying data
|
||||||
|
// first, then fall back to trying to convert them to a uint8 slice.
|
||||||
|
var buf []uint8
|
||||||
|
doConvert := false
|
||||||
|
doHexDump := false
|
||||||
|
numEntries := v.Len()
|
||||||
|
if numEntries > 0 {
|
||||||
|
vt := v.Index(0).Type()
|
||||||
|
vts := vt.String()
|
||||||
|
switch {
|
||||||
|
// C types that need to be converted.
|
||||||
|
case cCharRE.MatchString(vts):
|
||||||
|
fallthrough
|
||||||
|
case cUnsignedCharRE.MatchString(vts):
|
||||||
|
fallthrough
|
||||||
|
case cUint8tCharRE.MatchString(vts):
|
||||||
|
doConvert = true
|
||||||
|
|
||||||
|
// Try to use existing uint8 slices and fall back to converting
|
||||||
|
// and copying if that fails.
|
||||||
|
case vt.Kind() == reflect.Uint8:
|
||||||
|
// We need an addressable interface to convert the type
|
||||||
|
// to a byte slice. However, the reflect package won't
|
||||||
|
// give us an interface on certain things like
|
||||||
|
// unexported struct fields in order to enforce
|
||||||
|
// visibility rules. We use unsafe, when available, to
|
||||||
|
// bypass these restrictions since this package does not
|
||||||
|
// mutate the values.
|
||||||
|
vs := v
|
||||||
|
if !vs.CanInterface() || !vs.CanAddr() {
|
||||||
|
vs = unsafeReflectValue(vs)
|
||||||
|
}
|
||||||
|
if !UnsafeDisabled {
|
||||||
|
vs = vs.Slice(0, numEntries)
|
||||||
|
|
||||||
|
// Use the existing uint8 slice if it can be
|
||||||
|
// type asserted.
|
||||||
|
iface := vs.Interface()
|
||||||
|
if slice, ok := iface.([]uint8); ok {
|
||||||
|
buf = slice
|
||||||
|
doHexDump = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The underlying data needs to be converted if it can't
|
||||||
|
// be type asserted to a uint8 slice.
|
||||||
|
doConvert = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy and convert the underlying type if needed.
|
||||||
|
if doConvert && vt.ConvertibleTo(uint8Type) {
|
||||||
|
// Convert and copy each element into a uint8 byte
|
||||||
|
// slice.
|
||||||
|
buf = make([]uint8, numEntries)
|
||||||
|
for i := 0; i < numEntries; i++ {
|
||||||
|
vv := v.Index(i)
|
||||||
|
buf[i] = uint8(vv.Convert(uint8Type).Uint())
|
||||||
|
}
|
||||||
|
doHexDump = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hexdump the entire slice as needed.
|
||||||
|
if doHexDump {
|
||||||
|
indent := strings.Repeat(d.cs.Indent, d.depth)
|
||||||
|
str := indent + hex.Dump(buf)
|
||||||
|
str = strings.Replace(str, "\n", "\n"+indent, -1)
|
||||||
|
str = strings.TrimRight(str, d.cs.Indent)
|
||||||
|
d.w.Write([]byte(str))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively call dump for each item.
|
||||||
|
for i := 0; i < numEntries; i++ {
|
||||||
|
d.dump(d.unpackValue(v.Index(i)))
|
||||||
|
if i < (numEntries - 1) {
|
||||||
|
d.w.Write(commaNewlineBytes)
|
||||||
|
} else {
|
||||||
|
d.w.Write(newlineBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dump is the main workhorse for dumping a value. It uses the passed reflect
|
||||||
|
// value to figure out what kind of object we are dealing with and formats it
|
||||||
|
// appropriately. It is a recursive function, however circular data structures
|
||||||
|
// are detected and handled properly.
|
||||||
|
func (d *dumpState) dump(v reflect.Value) {
|
||||||
|
// Handle invalid reflect values immediately.
|
||||||
|
kind := v.Kind()
|
||||||
|
if kind == reflect.Invalid {
|
||||||
|
d.w.Write(invalidAngleBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle pointers specially.
|
||||||
|
if kind == reflect.Ptr {
|
||||||
|
d.indent()
|
||||||
|
d.dumpPtr(v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print type information unless already handled elsewhere.
|
||||||
|
if !d.ignoreNextType {
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(openParenBytes)
|
||||||
|
d.w.Write([]byte(v.Type().String()))
|
||||||
|
d.w.Write(closeParenBytes)
|
||||||
|
d.w.Write(spaceBytes)
|
||||||
|
}
|
||||||
|
d.ignoreNextType = false
|
||||||
|
|
||||||
|
// Display length and capacity if the built-in len and cap functions
|
||||||
|
// work with the value's kind and the len/cap itself is non-zero.
|
||||||
|
valueLen, valueCap := 0, 0
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Array, reflect.Slice, reflect.Chan:
|
||||||
|
valueLen, valueCap = v.Len(), v.Cap()
|
||||||
|
case reflect.Map, reflect.String:
|
||||||
|
valueLen = v.Len()
|
||||||
|
}
|
||||||
|
if valueLen != 0 || valueCap != 0 {
|
||||||
|
d.w.Write(openParenBytes)
|
||||||
|
if valueLen != 0 {
|
||||||
|
d.w.Write(lenEqualsBytes)
|
||||||
|
printInt(d.w, int64(valueLen), 10)
|
||||||
|
}
|
||||||
|
if valueCap != 0 {
|
||||||
|
if valueLen != 0 {
|
||||||
|
d.w.Write(spaceBytes)
|
||||||
|
}
|
||||||
|
d.w.Write(capEqualsBytes)
|
||||||
|
printInt(d.w, int64(valueCap), 10)
|
||||||
|
}
|
||||||
|
d.w.Write(closeParenBytes)
|
||||||
|
d.w.Write(spaceBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call Stringer/error interfaces if they exist and the handle methods flag
|
||||||
|
// is enabled
|
||||||
|
if !d.cs.DisableMethods {
|
||||||
|
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||||
|
if handled := handleMethods(d.cs, d.w, v); handled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch kind {
|
||||||
|
case reflect.Invalid:
|
||||||
|
// Do nothing. We should never get here since invalid has already
|
||||||
|
// been handled above.
|
||||||
|
|
||||||
|
case reflect.Bool:
|
||||||
|
printBool(d.w, v.Bool())
|
||||||
|
|
||||||
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
|
printInt(d.w, v.Int(), 10)
|
||||||
|
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||||
|
printUint(d.w, v.Uint(), 10)
|
||||||
|
|
||||||
|
case reflect.Float32:
|
||||||
|
printFloat(d.w, v.Float(), 32)
|
||||||
|
|
||||||
|
case reflect.Float64:
|
||||||
|
printFloat(d.w, v.Float(), 64)
|
||||||
|
|
||||||
|
case reflect.Complex64:
|
||||||
|
printComplex(d.w, v.Complex(), 32)
|
||||||
|
|
||||||
|
case reflect.Complex128:
|
||||||
|
printComplex(d.w, v.Complex(), 64)
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
if v.IsNil() {
|
||||||
|
d.w.Write(nilAngleBytes)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case reflect.Array:
|
||||||
|
d.w.Write(openBraceNewlineBytes)
|
||||||
|
d.depth++
|
||||||
|
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(maxNewlineBytes)
|
||||||
|
} else {
|
||||||
|
d.dumpSlice(v)
|
||||||
|
}
|
||||||
|
d.depth--
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(closeBraceBytes)
|
||||||
|
|
||||||
|
case reflect.String:
|
||||||
|
d.w.Write([]byte(strconv.Quote(v.String())))
|
||||||
|
|
||||||
|
case reflect.Interface:
|
||||||
|
// The only time we should get here is for nil interfaces due to
|
||||||
|
// unpackValue calls.
|
||||||
|
if v.IsNil() {
|
||||||
|
d.w.Write(nilAngleBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Ptr:
|
||||||
|
// Do nothing. We should never get here since pointers have already
|
||||||
|
// been handled above.
|
||||||
|
|
||||||
|
case reflect.Map:
|
||||||
|
// nil maps should be indicated as different than empty maps
|
||||||
|
if v.IsNil() {
|
||||||
|
d.w.Write(nilAngleBytes)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
d.w.Write(openBraceNewlineBytes)
|
||||||
|
d.depth++
|
||||||
|
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(maxNewlineBytes)
|
||||||
|
} else {
|
||||||
|
numEntries := v.Len()
|
||||||
|
keys := v.MapKeys()
|
||||||
|
if d.cs.SortKeys {
|
||||||
|
sortValues(keys, d.cs)
|
||||||
|
}
|
||||||
|
for i, key := range keys {
|
||||||
|
d.dump(d.unpackValue(key))
|
||||||
|
d.w.Write(colonSpaceBytes)
|
||||||
|
d.ignoreNextIndent = true
|
||||||
|
d.dump(d.unpackValue(v.MapIndex(key)))
|
||||||
|
if i < (numEntries - 1) {
|
||||||
|
d.w.Write(commaNewlineBytes)
|
||||||
|
} else {
|
||||||
|
d.w.Write(newlineBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.depth--
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(closeBraceBytes)
|
||||||
|
|
||||||
|
case reflect.Struct:
|
||||||
|
d.w.Write(openBraceNewlineBytes)
|
||||||
|
d.depth++
|
||||||
|
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(maxNewlineBytes)
|
||||||
|
} else {
|
||||||
|
vt := v.Type()
|
||||||
|
numFields := v.NumField()
|
||||||
|
for i := 0; i < numFields; i++ {
|
||||||
|
d.indent()
|
||||||
|
vtf := vt.Field(i)
|
||||||
|
d.w.Write([]byte(vtf.Name))
|
||||||
|
d.w.Write(colonSpaceBytes)
|
||||||
|
d.ignoreNextIndent = true
|
||||||
|
d.dump(d.unpackValue(v.Field(i)))
|
||||||
|
if i < (numFields - 1) {
|
||||||
|
d.w.Write(commaNewlineBytes)
|
||||||
|
} else {
|
||||||
|
d.w.Write(newlineBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.depth--
|
||||||
|
d.indent()
|
||||||
|
d.w.Write(closeBraceBytes)
|
||||||
|
|
||||||
|
case reflect.Uintptr:
|
||||||
|
printHexPtr(d.w, uintptr(v.Uint()))
|
||||||
|
|
||||||
|
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||||
|
printHexPtr(d.w, v.Pointer())
|
||||||
|
|
||||||
|
// There were not any other types at the time this code was written, but
|
||||||
|
// fall back to letting the default fmt package handle it in case any new
|
||||||
|
// types are added.
|
||||||
|
default:
|
||||||
|
if v.CanInterface() {
|
||||||
|
fmt.Fprintf(d.w, "%v", v.Interface())
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(d.w, "%v", v.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fdump is a helper function to consolidate the logic from the various public
|
||||||
|
// methods which take varying writers and config states.
|
||||||
|
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
|
||||||
|
for _, arg := range a {
|
||||||
|
if arg == nil {
|
||||||
|
w.Write(interfaceBytes)
|
||||||
|
w.Write(spaceBytes)
|
||||||
|
w.Write(nilAngleBytes)
|
||||||
|
w.Write(newlineBytes)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
d := dumpState{w: w, cs: cs}
|
||||||
|
d.pointers = make(map[uintptr]int)
|
||||||
|
d.dump(reflect.ValueOf(arg))
|
||||||
|
d.w.Write(newlineBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||||
|
// exactly the same as Dump.
|
||||||
|
func Fdump(w io.Writer, a ...interface{}) {
|
||||||
|
fdump(&Config, w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sdump returns a string with the passed arguments formatted exactly the same
|
||||||
|
// as Dump.
|
||||||
|
func Sdump(a ...interface{}) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fdump(&Config, &buf, a...)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Dump displays the passed parameters to standard out with newlines, customizable
|
||||||
|
indentation, and additional debug information such as complete types and all
|
||||||
|
pointer addresses used to indirect to the final value. It provides the
|
||||||
|
following features over the built-in printing facilities provided by the fmt
|
||||||
|
package:
|
||||||
|
|
||||||
|
* Pointers are dereferenced and followed
|
||||||
|
* Circular data structures are detected and handled properly
|
||||||
|
* Custom Stringer/error interfaces are optionally invoked, including
|
||||||
|
on unexported types
|
||||||
|
* Custom types which only implement the Stringer/error interfaces via
|
||||||
|
a pointer receiver are optionally invoked when passing non-pointer
|
||||||
|
variables
|
||||||
|
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||||
|
includes offsets, byte values in hex, and ASCII output
|
||||||
|
|
||||||
|
The configuration options are controlled by an exported package global,
|
||||||
|
spew.Config. See ConfigState for options documentation.
|
||||||
|
|
||||||
|
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
||||||
|
get the formatted result as a string.
|
||||||
|
*/
|
||||||
|
func Dump(a ...interface{}) {
|
||||||
|
fdump(&Config, os.Stdout, a...)
|
||||||
|
}
|
1042
vendor/github.com/davecgh/go-spew/spew/dump_test.go
generated
vendored
Normal file
1042
vendor/github.com/davecgh/go-spew/spew/dump_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
99
vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
generated
vendored
Normal file
99
vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||||
|
// when both cgo is supported and "-tags testcgo" is added to the go test
|
||||||
|
// command line. This means the cgo tests are only added (and hence run) when
|
||||||
|
// specifially requested. This configuration is used because spew itself
|
||||||
|
// does not require cgo to run even though it does handle certain cgo types
|
||||||
|
// specially. Rather than forcing all clients to require cgo and an external
|
||||||
|
// C compiler just to run the tests, this scheme makes them optional.
|
||||||
|
// +build cgo,testcgo
|
||||||
|
|
||||||
|
package spew_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew/testdata"
|
||||||
|
)
|
||||||
|
|
||||||
|
func addCgoDumpTests() {
|
||||||
|
// C char pointer.
|
||||||
|
v := testdata.GetCgoCharPointer()
|
||||||
|
nv := testdata.GetCgoNullCharPointer()
|
||||||
|
pv := &v
|
||||||
|
vcAddr := fmt.Sprintf("%p", v)
|
||||||
|
vAddr := fmt.Sprintf("%p", pv)
|
||||||
|
pvAddr := fmt.Sprintf("%p", &pv)
|
||||||
|
vt := "*testdata._Ctype_char"
|
||||||
|
vs := "116"
|
||||||
|
addDumpTest(v, "("+vt+")("+vcAddr+")("+vs+")\n")
|
||||||
|
addDumpTest(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n")
|
||||||
|
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n")
|
||||||
|
addDumpTest(nv, "("+vt+")(<nil>)\n")
|
||||||
|
|
||||||
|
// C char array.
|
||||||
|
v2, v2l, v2c := testdata.GetCgoCharArray()
|
||||||
|
v2Len := fmt.Sprintf("%d", v2l)
|
||||||
|
v2Cap := fmt.Sprintf("%d", v2c)
|
||||||
|
v2t := "[6]testdata._Ctype_char"
|
||||||
|
v2s := "(len=" + v2Len + " cap=" + v2Cap + ") " +
|
||||||
|
"{\n 00000000 74 65 73 74 32 00 " +
|
||||||
|
" |test2.|\n}"
|
||||||
|
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
|
||||||
|
|
||||||
|
// C unsigned char array.
|
||||||
|
v3, v3l, v3c := testdata.GetCgoUnsignedCharArray()
|
||||||
|
v3Len := fmt.Sprintf("%d", v3l)
|
||||||
|
v3Cap := fmt.Sprintf("%d", v3c)
|
||||||
|
v3t := "[6]testdata._Ctype_unsignedchar"
|
||||||
|
v3t2 := "[6]testdata._Ctype_uchar"
|
||||||
|
v3s := "(len=" + v3Len + " cap=" + v3Cap + ") " +
|
||||||
|
"{\n 00000000 74 65 73 74 33 00 " +
|
||||||
|
" |test3.|\n}"
|
||||||
|
addDumpTest(v3, "("+v3t+") "+v3s+"\n", "("+v3t2+") "+v3s+"\n")
|
||||||
|
|
||||||
|
// C signed char array.
|
||||||
|
v4, v4l, v4c := testdata.GetCgoSignedCharArray()
|
||||||
|
v4Len := fmt.Sprintf("%d", v4l)
|
||||||
|
v4Cap := fmt.Sprintf("%d", v4c)
|
||||||
|
v4t := "[6]testdata._Ctype_schar"
|
||||||
|
v4t2 := "testdata._Ctype_schar"
|
||||||
|
v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
|
||||||
|
"{\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 101,\n (" + v4t2 +
|
||||||
|
") 115,\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 52,\n (" + v4t2 +
|
||||||
|
") 0\n}"
|
||||||
|
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
|
||||||
|
|
||||||
|
// C uint8_t array.
|
||||||
|
v5, v5l, v5c := testdata.GetCgoUint8tArray()
|
||||||
|
v5Len := fmt.Sprintf("%d", v5l)
|
||||||
|
v5Cap := fmt.Sprintf("%d", v5c)
|
||||||
|
v5t := "[6]testdata._Ctype_uint8_t"
|
||||||
|
v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " +
|
||||||
|
"{\n 00000000 74 65 73 74 35 00 " +
|
||||||
|
" |test5.|\n}"
|
||||||
|
addDumpTest(v5, "("+v5t+") "+v5s+"\n")
|
||||||
|
|
||||||
|
// C typedefed unsigned char array.
|
||||||
|
v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray()
|
||||||
|
v6Len := fmt.Sprintf("%d", v6l)
|
||||||
|
v6Cap := fmt.Sprintf("%d", v6c)
|
||||||
|
v6t := "[6]testdata._Ctype_custom_uchar_t"
|
||||||
|
v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " +
|
||||||
|
"{\n 00000000 74 65 73 74 36 00 " +
|
||||||
|
" |test6.|\n}"
|
||||||
|
addDumpTest(v6, "("+v6t+") "+v6s+"\n")
|
||||||
|
}
|
26
vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go
generated
vendored
Normal file
26
vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||||
|
// when either cgo is not supported or "-tags testcgo" is not added to the go
|
||||||
|
// test command line. This file intentionally does not setup any cgo tests in
|
||||||
|
// this scenario.
|
||||||
|
// +build !cgo !testcgo
|
||||||
|
|
||||||
|
package spew_test
|
||||||
|
|
||||||
|
func addCgoDumpTests() {
|
||||||
|
// Don't add any tests for cgo since this file is only compiled when
|
||||||
|
// there should not be any cgo tests.
|
||||||
|
}
|
226
vendor/github.com/davecgh/go-spew/spew/example_test.go
generated
vendored
Normal file
226
vendor/github.com/davecgh/go-spew/spew/example_test.go
generated
vendored
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Flag int
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagOne Flag = iota
|
||||||
|
flagTwo
|
||||||
|
)
|
||||||
|
|
||||||
|
var flagStrings = map[Flag]string{
|
||||||
|
flagOne: "flagOne",
|
||||||
|
flagTwo: "flagTwo",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Flag) String() string {
|
||||||
|
if s, ok := flagStrings[f]; ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Unknown flag (%d)", int(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bar struct {
|
||||||
|
data uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
unexportedField Bar
|
||||||
|
ExportedField map[interface{}]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This example demonstrates how to use Dump to dump variables to stdout.
|
||||||
|
func ExampleDump() {
|
||||||
|
// The following package level declarations are assumed for this example:
|
||||||
|
/*
|
||||||
|
type Flag int
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagOne Flag = iota
|
||||||
|
flagTwo
|
||||||
|
)
|
||||||
|
|
||||||
|
var flagStrings = map[Flag]string{
|
||||||
|
flagOne: "flagOne",
|
||||||
|
flagTwo: "flagTwo",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Flag) String() string {
|
||||||
|
if s, ok := flagStrings[f]; ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Unknown flag (%d)", int(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bar struct {
|
||||||
|
data uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
unexportedField Bar
|
||||||
|
ExportedField map[interface{}]interface{}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Setup some sample data structures for the example.
|
||||||
|
bar := Bar{uintptr(0)}
|
||||||
|
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
|
||||||
|
f := Flag(5)
|
||||||
|
b := []byte{
|
||||||
|
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||||
|
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||||
|
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||||
|
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
|
||||||
|
0x31, 0x32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump!
|
||||||
|
spew.Dump(s1, f, b)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// (spew_test.Foo) {
|
||||||
|
// unexportedField: (spew_test.Bar) {
|
||||||
|
// data: (uintptr) <nil>
|
||||||
|
// },
|
||||||
|
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||||
|
// (string) (len=3) "one": (bool) true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// (spew_test.Flag) Unknown flag (5)
|
||||||
|
// ([]uint8) (len=34 cap=34) {
|
||||||
|
// 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||||
|
// 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||||
|
// 00000020 31 32 |12|
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
// This example demonstrates how to use Printf to display a variable with a
|
||||||
|
// format string and inline formatting.
|
||||||
|
func ExamplePrintf() {
|
||||||
|
// Create a double pointer to a uint 8.
|
||||||
|
ui8 := uint8(5)
|
||||||
|
pui8 := &ui8
|
||||||
|
ppui8 := &pui8
|
||||||
|
|
||||||
|
// Create a circular data type.
|
||||||
|
type circular struct {
|
||||||
|
ui8 uint8
|
||||||
|
c *circular
|
||||||
|
}
|
||||||
|
c := circular{ui8: 1}
|
||||||
|
c.c = &c
|
||||||
|
|
||||||
|
// Print!
|
||||||
|
spew.Printf("ppui8: %v\n", ppui8)
|
||||||
|
spew.Printf("circular: %v\n", c)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// ppui8: <**>5
|
||||||
|
// circular: {1 <*>{1 <*><shown>}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This example demonstrates how to use a ConfigState.
|
||||||
|
func ExampleConfigState() {
|
||||||
|
// Modify the indent level of the ConfigState only. The global
|
||||||
|
// configuration is not modified.
|
||||||
|
scs := spew.ConfigState{Indent: "\t"}
|
||||||
|
|
||||||
|
// Output using the ConfigState instance.
|
||||||
|
v := map[string]int{"one": 1}
|
||||||
|
scs.Printf("v: %v\n", v)
|
||||||
|
scs.Dump(v)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// v: map[one:1]
|
||||||
|
// (map[string]int) (len=1) {
|
||||||
|
// (string) (len=3) "one": (int) 1
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// This example demonstrates how to use ConfigState.Dump to dump variables to
|
||||||
|
// stdout
|
||||||
|
func ExampleConfigState_Dump() {
|
||||||
|
// See the top-level Dump example for details on the types used in this
|
||||||
|
// example.
|
||||||
|
|
||||||
|
// Create two ConfigState instances with different indentation.
|
||||||
|
scs := spew.ConfigState{Indent: "\t"}
|
||||||
|
scs2 := spew.ConfigState{Indent: " "}
|
||||||
|
|
||||||
|
// Setup some sample data structures for the example.
|
||||||
|
bar := Bar{uintptr(0)}
|
||||||
|
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
|
||||||
|
|
||||||
|
// Dump using the ConfigState instances.
|
||||||
|
scs.Dump(s1)
|
||||||
|
scs2.Dump(s1)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// (spew_test.Foo) {
|
||||||
|
// unexportedField: (spew_test.Bar) {
|
||||||
|
// data: (uintptr) <nil>
|
||||||
|
// },
|
||||||
|
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||||
|
// (string) (len=3) "one": (bool) true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// (spew_test.Foo) {
|
||||||
|
// unexportedField: (spew_test.Bar) {
|
||||||
|
// data: (uintptr) <nil>
|
||||||
|
// },
|
||||||
|
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||||
|
// (string) (len=3) "one": (bool) true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
// This example demonstrates how to use ConfigState.Printf to display a variable
|
||||||
|
// with a format string and inline formatting.
|
||||||
|
func ExampleConfigState_Printf() {
|
||||||
|
// See the top-level Dump example for details on the types used in this
|
||||||
|
// example.
|
||||||
|
|
||||||
|
// Create two ConfigState instances and modify the method handling of the
|
||||||
|
// first ConfigState only.
|
||||||
|
scs := spew.NewDefaultConfig()
|
||||||
|
scs2 := spew.NewDefaultConfig()
|
||||||
|
scs.DisableMethods = true
|
||||||
|
|
||||||
|
// Alternatively
|
||||||
|
// scs := spew.ConfigState{Indent: " ", DisableMethods: true}
|
||||||
|
// scs2 := spew.ConfigState{Indent: " "}
|
||||||
|
|
||||||
|
// This is of type Flag which implements a Stringer and has raw value 1.
|
||||||
|
f := flagTwo
|
||||||
|
|
||||||
|
// Dump using the ConfigState instances.
|
||||||
|
scs.Printf("f: %v\n", f)
|
||||||
|
scs2.Printf("f: %v\n", f)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// f: 1
|
||||||
|
// f: flagTwo
|
||||||
|
}
|
419
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
Normal file
419
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
Normal file
@ -0,0 +1,419 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// supportedFlags is a list of all the character flags supported by fmt package.
|
||||||
|
const supportedFlags = "0-+# "
|
||||||
|
|
||||||
|
// formatState implements the fmt.Formatter interface and contains information
|
||||||
|
// about the state of a formatting operation. The NewFormatter function can
|
||||||
|
// be used to get a new Formatter which can be used directly as arguments
|
||||||
|
// in standard fmt package printing calls.
|
||||||
|
type formatState struct {
|
||||||
|
value interface{}
|
||||||
|
fs fmt.State
|
||||||
|
depth int
|
||||||
|
pointers map[uintptr]int
|
||||||
|
ignoreNextType bool
|
||||||
|
cs *ConfigState
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildDefaultFormat recreates the original format string without precision
|
||||||
|
// and width information to pass in to fmt.Sprintf in the case of an
|
||||||
|
// unrecognized type. Unless new types are added to the language, this
|
||||||
|
// function won't ever be called.
|
||||||
|
func (f *formatState) buildDefaultFormat() (format string) {
|
||||||
|
buf := bytes.NewBuffer(percentBytes)
|
||||||
|
|
||||||
|
for _, flag := range supportedFlags {
|
||||||
|
if f.fs.Flag(int(flag)) {
|
||||||
|
buf.WriteRune(flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteRune('v')
|
||||||
|
|
||||||
|
format = buf.String()
|
||||||
|
return format
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructOrigFormat recreates the original format string including precision
|
||||||
|
// and width information to pass along to the standard fmt package. This allows
|
||||||
|
// automatic deferral of all format strings this package doesn't support.
|
||||||
|
func (f *formatState) constructOrigFormat(verb rune) (format string) {
|
||||||
|
buf := bytes.NewBuffer(percentBytes)
|
||||||
|
|
||||||
|
for _, flag := range supportedFlags {
|
||||||
|
if f.fs.Flag(int(flag)) {
|
||||||
|
buf.WriteRune(flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if width, ok := f.fs.Width(); ok {
|
||||||
|
buf.WriteString(strconv.Itoa(width))
|
||||||
|
}
|
||||||
|
|
||||||
|
if precision, ok := f.fs.Precision(); ok {
|
||||||
|
buf.Write(precisionBytes)
|
||||||
|
buf.WriteString(strconv.Itoa(precision))
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteRune(verb)
|
||||||
|
|
||||||
|
format = buf.String()
|
||||||
|
return format
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpackValue returns values inside of non-nil interfaces when possible and
|
||||||
|
// ensures that types for values which have been unpacked from an interface
|
||||||
|
// are displayed when the show types flag is also set.
|
||||||
|
// This is useful for data types like structs, arrays, slices, and maps which
|
||||||
|
// can contain varying types packed inside an interface.
|
||||||
|
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
|
||||||
|
if v.Kind() == reflect.Interface {
|
||||||
|
f.ignoreNextType = false
|
||||||
|
if !v.IsNil() {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatPtr handles formatting of pointers by indirecting them as necessary.
|
||||||
|
func (f *formatState) formatPtr(v reflect.Value) {
|
||||||
|
// Display nil if top level pointer is nil.
|
||||||
|
showTypes := f.fs.Flag('#')
|
||||||
|
if v.IsNil() && (!showTypes || f.ignoreNextType) {
|
||||||
|
f.fs.Write(nilAngleBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove pointers at or below the current depth from map used to detect
|
||||||
|
// circular refs.
|
||||||
|
for k, depth := range f.pointers {
|
||||||
|
if depth >= f.depth {
|
||||||
|
delete(f.pointers, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep list of all dereferenced pointers to possibly show later.
|
||||||
|
pointerChain := make([]uintptr, 0)
|
||||||
|
|
||||||
|
// Figure out how many levels of indirection there are by derferencing
|
||||||
|
// pointers and unpacking interfaces down the chain while detecting circular
|
||||||
|
// references.
|
||||||
|
nilFound := false
|
||||||
|
cycleFound := false
|
||||||
|
indirects := 0
|
||||||
|
ve := v
|
||||||
|
for ve.Kind() == reflect.Ptr {
|
||||||
|
if ve.IsNil() {
|
||||||
|
nilFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
indirects++
|
||||||
|
addr := ve.Pointer()
|
||||||
|
pointerChain = append(pointerChain, addr)
|
||||||
|
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
|
||||||
|
cycleFound = true
|
||||||
|
indirects--
|
||||||
|
break
|
||||||
|
}
|
||||||
|
f.pointers[addr] = f.depth
|
||||||
|
|
||||||
|
ve = ve.Elem()
|
||||||
|
if ve.Kind() == reflect.Interface {
|
||||||
|
if ve.IsNil() {
|
||||||
|
nilFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ve = ve.Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display type or indirection level depending on flags.
|
||||||
|
if showTypes && !f.ignoreNextType {
|
||||||
|
f.fs.Write(openParenBytes)
|
||||||
|
f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||||
|
f.fs.Write([]byte(ve.Type().String()))
|
||||||
|
f.fs.Write(closeParenBytes)
|
||||||
|
} else {
|
||||||
|
if nilFound || cycleFound {
|
||||||
|
indirects += strings.Count(ve.Type().String(), "*")
|
||||||
|
}
|
||||||
|
f.fs.Write(openAngleBytes)
|
||||||
|
f.fs.Write([]byte(strings.Repeat("*", indirects)))
|
||||||
|
f.fs.Write(closeAngleBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display pointer information depending on flags.
|
||||||
|
if f.fs.Flag('+') && (len(pointerChain) > 0) {
|
||||||
|
f.fs.Write(openParenBytes)
|
||||||
|
for i, addr := range pointerChain {
|
||||||
|
if i > 0 {
|
||||||
|
f.fs.Write(pointerChainBytes)
|
||||||
|
}
|
||||||
|
printHexPtr(f.fs, addr)
|
||||||
|
}
|
||||||
|
f.fs.Write(closeParenBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display dereferenced value.
|
||||||
|
switch {
|
||||||
|
case nilFound == true:
|
||||||
|
f.fs.Write(nilAngleBytes)
|
||||||
|
|
||||||
|
case cycleFound == true:
|
||||||
|
f.fs.Write(circularShortBytes)
|
||||||
|
|
||||||
|
default:
|
||||||
|
f.ignoreNextType = true
|
||||||
|
f.format(ve)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// format is the main workhorse for providing the Formatter interface. It
|
||||||
|
// uses the passed reflect value to figure out what kind of object we are
|
||||||
|
// dealing with and formats it appropriately. It is a recursive function,
|
||||||
|
// however circular data structures are detected and handled properly.
|
||||||
|
func (f *formatState) format(v reflect.Value) {
|
||||||
|
// Handle invalid reflect values immediately.
|
||||||
|
kind := v.Kind()
|
||||||
|
if kind == reflect.Invalid {
|
||||||
|
f.fs.Write(invalidAngleBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle pointers specially.
|
||||||
|
if kind == reflect.Ptr {
|
||||||
|
f.formatPtr(v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print type information unless already handled elsewhere.
|
||||||
|
if !f.ignoreNextType && f.fs.Flag('#') {
|
||||||
|
f.fs.Write(openParenBytes)
|
||||||
|
f.fs.Write([]byte(v.Type().String()))
|
||||||
|
f.fs.Write(closeParenBytes)
|
||||||
|
}
|
||||||
|
f.ignoreNextType = false
|
||||||
|
|
||||||
|
// Call Stringer/error interfaces if they exist and the handle methods
|
||||||
|
// flag is enabled.
|
||||||
|
if !f.cs.DisableMethods {
|
||||||
|
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||||
|
if handled := handleMethods(f.cs, f.fs, v); handled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch kind {
|
||||||
|
case reflect.Invalid:
|
||||||
|
// Do nothing. We should never get here since invalid has already
|
||||||
|
// been handled above.
|
||||||
|
|
||||||
|
case reflect.Bool:
|
||||||
|
printBool(f.fs, v.Bool())
|
||||||
|
|
||||||
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
|
printInt(f.fs, v.Int(), 10)
|
||||||
|
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||||
|
printUint(f.fs, v.Uint(), 10)
|
||||||
|
|
||||||
|
case reflect.Float32:
|
||||||
|
printFloat(f.fs, v.Float(), 32)
|
||||||
|
|
||||||
|
case reflect.Float64:
|
||||||
|
printFloat(f.fs, v.Float(), 64)
|
||||||
|
|
||||||
|
case reflect.Complex64:
|
||||||
|
printComplex(f.fs, v.Complex(), 32)
|
||||||
|
|
||||||
|
case reflect.Complex128:
|
||||||
|
printComplex(f.fs, v.Complex(), 64)
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
if v.IsNil() {
|
||||||
|
f.fs.Write(nilAngleBytes)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case reflect.Array:
|
||||||
|
f.fs.Write(openBracketBytes)
|
||||||
|
f.depth++
|
||||||
|
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||||
|
f.fs.Write(maxShortBytes)
|
||||||
|
} else {
|
||||||
|
numEntries := v.Len()
|
||||||
|
for i := 0; i < numEntries; i++ {
|
||||||
|
if i > 0 {
|
||||||
|
f.fs.Write(spaceBytes)
|
||||||
|
}
|
||||||
|
f.ignoreNextType = true
|
||||||
|
f.format(f.unpackValue(v.Index(i)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.depth--
|
||||||
|
f.fs.Write(closeBracketBytes)
|
||||||
|
|
||||||
|
case reflect.String:
|
||||||
|
f.fs.Write([]byte(v.String()))
|
||||||
|
|
||||||
|
case reflect.Interface:
|
||||||
|
// The only time we should get here is for nil interfaces due to
|
||||||
|
// unpackValue calls.
|
||||||
|
if v.IsNil() {
|
||||||
|
f.fs.Write(nilAngleBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Ptr:
|
||||||
|
// Do nothing. We should never get here since pointers have already
|
||||||
|
// been handled above.
|
||||||
|
|
||||||
|
case reflect.Map:
|
||||||
|
// nil maps should be indicated as different than empty maps
|
||||||
|
if v.IsNil() {
|
||||||
|
f.fs.Write(nilAngleBytes)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
f.fs.Write(openMapBytes)
|
||||||
|
f.depth++
|
||||||
|
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||||
|
f.fs.Write(maxShortBytes)
|
||||||
|
} else {
|
||||||
|
keys := v.MapKeys()
|
||||||
|
if f.cs.SortKeys {
|
||||||
|
sortValues(keys, f.cs)
|
||||||
|
}
|
||||||
|
for i, key := range keys {
|
||||||
|
if i > 0 {
|
||||||
|
f.fs.Write(spaceBytes)
|
||||||
|
}
|
||||||
|
f.ignoreNextType = true
|
||||||
|
f.format(f.unpackValue(key))
|
||||||
|
f.fs.Write(colonBytes)
|
||||||
|
f.ignoreNextType = true
|
||||||
|
f.format(f.unpackValue(v.MapIndex(key)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.depth--
|
||||||
|
f.fs.Write(closeMapBytes)
|
||||||
|
|
||||||
|
case reflect.Struct:
|
||||||
|
numFields := v.NumField()
|
||||||
|
f.fs.Write(openBraceBytes)
|
||||||
|
f.depth++
|
||||||
|
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||||
|
f.fs.Write(maxShortBytes)
|
||||||
|
} else {
|
||||||
|
vt := v.Type()
|
||||||
|
for i := 0; i < numFields; i++ {
|
||||||
|
if i > 0 {
|
||||||
|
f.fs.Write(spaceBytes)
|
||||||
|
}
|
||||||
|
vtf := vt.Field(i)
|
||||||
|
if f.fs.Flag('+') || f.fs.Flag('#') {
|
||||||
|
f.fs.Write([]byte(vtf.Name))
|
||||||
|
f.fs.Write(colonBytes)
|
||||||
|
}
|
||||||
|
f.format(f.unpackValue(v.Field(i)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.depth--
|
||||||
|
f.fs.Write(closeBraceBytes)
|
||||||
|
|
||||||
|
case reflect.Uintptr:
|
||||||
|
printHexPtr(f.fs, uintptr(v.Uint()))
|
||||||
|
|
||||||
|
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||||
|
printHexPtr(f.fs, v.Pointer())
|
||||||
|
|
||||||
|
// There were not any other types at the time this code was written, but
|
||||||
|
// fall back to letting the default fmt package handle it if any get added.
|
||||||
|
default:
|
||||||
|
format := f.buildDefaultFormat()
|
||||||
|
if v.CanInterface() {
|
||||||
|
fmt.Fprintf(f.fs, format, v.Interface())
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f.fs, format, v.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
|
||||||
|
// details.
|
||||||
|
func (f *formatState) Format(fs fmt.State, verb rune) {
|
||||||
|
f.fs = fs
|
||||||
|
|
||||||
|
// Use standard formatting for verbs that are not v.
|
||||||
|
if verb != 'v' {
|
||||||
|
format := f.constructOrigFormat(verb)
|
||||||
|
fmt.Fprintf(fs, format, f.value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.value == nil {
|
||||||
|
if fs.Flag('#') {
|
||||||
|
fs.Write(interfaceBytes)
|
||||||
|
}
|
||||||
|
fs.Write(nilAngleBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.format(reflect.ValueOf(f.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// newFormatter is a helper function to consolidate the logic from the various
|
||||||
|
// public methods which take varying config states.
|
||||||
|
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
|
||||||
|
fs := &formatState{value: v, cs: cs}
|
||||||
|
fs.pointers = make(map[uintptr]int)
|
||||||
|
return fs
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||||
|
interface. As a result, it integrates cleanly with standard fmt package
|
||||||
|
printing functions. The formatter is useful for inline printing of smaller data
|
||||||
|
types similar to the standard %v format specifier.
|
||||||
|
|
||||||
|
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||||
|
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||||
|
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||||
|
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||||
|
the width and precision arguments (however they will still work on the format
|
||||||
|
specifiers not handled by the custom formatter).
|
||||||
|
|
||||||
|
Typically this function shouldn't be called directly. It is much easier to make
|
||||||
|
use of the custom formatter by calling one of the convenience functions such as
|
||||||
|
Printf, Println, or Fprintf.
|
||||||
|
*/
|
||||||
|
func NewFormatter(v interface{}) fmt.Formatter {
|
||||||
|
return newFormatter(&Config, v)
|
||||||
|
}
|
1558
vendor/github.com/davecgh/go-spew/spew/format_test.go
generated
vendored
Normal file
1558
vendor/github.com/davecgh/go-spew/spew/format_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
87
vendor/github.com/davecgh/go-spew/spew/internal_test.go
generated
vendored
Normal file
87
vendor/github.com/davecgh/go-spew/spew/internal_test.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This test file is part of the spew package rather than than the spew_test
|
||||||
|
package because it needs access to internals to properly test certain cases
|
||||||
|
which are not possible via the public interface since they should never happen.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// dummyFmtState implements a fake fmt.State to use for testing invalid
|
||||||
|
// reflect.Value handling. This is necessary because the fmt package catches
|
||||||
|
// invalid values before invoking the formatter on them.
|
||||||
|
type dummyFmtState struct {
|
||||||
|
bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dfs *dummyFmtState) Flag(f int) bool {
|
||||||
|
if f == int('+') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dfs *dummyFmtState) Precision() (int, bool) {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dfs *dummyFmtState) Width() (int, bool) {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestInvalidReflectValue ensures the dump and formatter code handles an
|
||||||
|
// invalid reflect value properly. This needs access to internal state since it
|
||||||
|
// should never happen in real code and therefore can't be tested via the public
|
||||||
|
// API.
|
||||||
|
func TestInvalidReflectValue(t *testing.T) {
|
||||||
|
i := 1
|
||||||
|
|
||||||
|
// Dump invalid reflect value.
|
||||||
|
v := new(reflect.Value)
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
d := dumpState{w: buf, cs: &Config}
|
||||||
|
d.dump(*v)
|
||||||
|
s := buf.String()
|
||||||
|
want := "<invalid>"
|
||||||
|
if s != want {
|
||||||
|
t.Errorf("InvalidReflectValue #%d\n got: %s want: %s", i, s, want)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
|
||||||
|
// Formatter invalid reflect value.
|
||||||
|
buf2 := new(dummyFmtState)
|
||||||
|
f := formatState{value: *v, cs: &Config, fs: buf2}
|
||||||
|
f.format(*v)
|
||||||
|
s = buf2.String()
|
||||||
|
want = "<invalid>"
|
||||||
|
if s != want {
|
||||||
|
t.Errorf("InvalidReflectValue #%d got: %s want: %s", i, s, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortValues makes the internal sortValues function available to the test
|
||||||
|
// package.
|
||||||
|
func SortValues(values []reflect.Value, cs *ConfigState) {
|
||||||
|
sortValues(values, cs)
|
||||||
|
}
|
102
vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go
generated
vendored
Normal file
102
vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// Copyright (c) 2013-2015 Dave Collins <dave@davec.name>
|
||||||
|
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||||
|
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
||||||
|
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
||||||
|
// tag is deprecated and thus should not be used.
|
||||||
|
// +build !js,!appengine,!safe,!disableunsafe
|
||||||
|
|
||||||
|
/*
|
||||||
|
This test file is part of the spew package rather than than the spew_test
|
||||||
|
package because it needs access to internals to properly test certain cases
|
||||||
|
which are not possible via the public interface since they should never happen.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// changeKind uses unsafe to intentionally change the kind of a reflect.Value to
|
||||||
|
// the maximum kind value which does not exist. This is needed to test the
|
||||||
|
// fallback code which punts to the standard fmt library for new types that
|
||||||
|
// might get added to the language.
|
||||||
|
func changeKind(v *reflect.Value, readOnly bool) {
|
||||||
|
rvf := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + offsetFlag))
|
||||||
|
*rvf = *rvf | ((1<<flagKindWidth - 1) << flagKindShift)
|
||||||
|
if readOnly {
|
||||||
|
*rvf |= flagRO
|
||||||
|
} else {
|
||||||
|
*rvf &= ^uintptr(flagRO)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAddedReflectValue tests functionaly of the dump and formatter code which
|
||||||
|
// falls back to the standard fmt library for new types that might get added to
|
||||||
|
// the language.
|
||||||
|
func TestAddedReflectValue(t *testing.T) {
|
||||||
|
i := 1
|
||||||
|
|
||||||
|
// Dump using a reflect.Value that is exported.
|
||||||
|
v := reflect.ValueOf(int8(5))
|
||||||
|
changeKind(&v, false)
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
d := dumpState{w: buf, cs: &Config}
|
||||||
|
d.dump(v)
|
||||||
|
s := buf.String()
|
||||||
|
want := "(int8) 5"
|
||||||
|
if s != want {
|
||||||
|
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
|
||||||
|
// Dump using a reflect.Value that is not exported.
|
||||||
|
changeKind(&v, true)
|
||||||
|
buf.Reset()
|
||||||
|
d.dump(v)
|
||||||
|
s = buf.String()
|
||||||
|
want = "(int8) <int8 Value>"
|
||||||
|
if s != want {
|
||||||
|
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
|
||||||
|
// Formatter using a reflect.Value that is exported.
|
||||||
|
changeKind(&v, false)
|
||||||
|
buf2 := new(dummyFmtState)
|
||||||
|
f := formatState{value: v, cs: &Config, fs: buf2}
|
||||||
|
f.format(v)
|
||||||
|
s = buf2.String()
|
||||||
|
want = "5"
|
||||||
|
if s != want {
|
||||||
|
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
|
||||||
|
// Formatter using a reflect.Value that is not exported.
|
||||||
|
changeKind(&v, true)
|
||||||
|
buf2.Reset()
|
||||||
|
f = formatState{value: v, cs: &Config, fs: buf2}
|
||||||
|
f.format(v)
|
||||||
|
s = buf2.String()
|
||||||
|
want = "<int8 Value>"
|
||||||
|
if s != want {
|
||||||
|
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
|
||||||
|
}
|
||||||
|
}
|
148
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
Normal file
148
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the formatted string as a value that satisfies error. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Errorf(format string, a ...interface{}) (err error) {
|
||||||
|
return fmt.Errorf(format, convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprint(w, convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintf(w, format, convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintln(w, convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Print(a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Print(convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Printf(format string, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Printf(format, convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the number of bytes written and any write error encountered. See
|
||||||
|
// NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Println(a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Println(convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the resulting string. See NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Sprint(a ...interface{}) string {
|
||||||
|
return fmt.Sprint(convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
||||||
|
// passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the resulting string. See NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Sprintf(format string, a ...interface{}) string {
|
||||||
|
return fmt.Sprintf(format, convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
||||||
|
// were passed with a default Formatter interface returned by NewFormatter. It
|
||||||
|
// returns the resulting string. See NewFormatter for formatting details.
|
||||||
|
//
|
||||||
|
// This function is shorthand for the following syntax:
|
||||||
|
//
|
||||||
|
// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||||
|
func Sprintln(a ...interface{}) string {
|
||||||
|
return fmt.Sprintln(convertArgs(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertArgs accepts a slice of arguments and returns a slice of the same
|
||||||
|
// length with each argument converted to a default spew Formatter interface.
|
||||||
|
func convertArgs(args []interface{}) (formatters []interface{}) {
|
||||||
|
formatters = make([]interface{}, len(args))
|
||||||
|
for index, arg := range args {
|
||||||
|
formatters[index] = NewFormatter(arg)
|
||||||
|
}
|
||||||
|
return formatters
|
||||||
|
}
|
309
vendor/github.com/davecgh/go-spew/spew/spew_test.go
generated
vendored
Normal file
309
vendor/github.com/davecgh/go-spew/spew/spew_test.go
generated
vendored
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spew_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
)
|
||||||
|
|
||||||
|
// spewFunc is used to identify which public function of the spew package or
|
||||||
|
// ConfigState a test applies to.
|
||||||
|
type spewFunc int
|
||||||
|
|
||||||
|
const (
|
||||||
|
fCSFdump spewFunc = iota
|
||||||
|
fCSFprint
|
||||||
|
fCSFprintf
|
||||||
|
fCSFprintln
|
||||||
|
fCSPrint
|
||||||
|
fCSPrintln
|
||||||
|
fCSSdump
|
||||||
|
fCSSprint
|
||||||
|
fCSSprintf
|
||||||
|
fCSSprintln
|
||||||
|
fCSErrorf
|
||||||
|
fCSNewFormatter
|
||||||
|
fErrorf
|
||||||
|
fFprint
|
||||||
|
fFprintln
|
||||||
|
fPrint
|
||||||
|
fPrintln
|
||||||
|
fSdump
|
||||||
|
fSprint
|
||||||
|
fSprintf
|
||||||
|
fSprintln
|
||||||
|
)
|
||||||
|
|
||||||
|
// Map of spewFunc values to names for pretty printing.
|
||||||
|
var spewFuncStrings = map[spewFunc]string{
|
||||||
|
fCSFdump: "ConfigState.Fdump",
|
||||||
|
fCSFprint: "ConfigState.Fprint",
|
||||||
|
fCSFprintf: "ConfigState.Fprintf",
|
||||||
|
fCSFprintln: "ConfigState.Fprintln",
|
||||||
|
fCSSdump: "ConfigState.Sdump",
|
||||||
|
fCSPrint: "ConfigState.Print",
|
||||||
|
fCSPrintln: "ConfigState.Println",
|
||||||
|
fCSSprint: "ConfigState.Sprint",
|
||||||
|
fCSSprintf: "ConfigState.Sprintf",
|
||||||
|
fCSSprintln: "ConfigState.Sprintln",
|
||||||
|
fCSErrorf: "ConfigState.Errorf",
|
||||||
|
fCSNewFormatter: "ConfigState.NewFormatter",
|
||||||
|
fErrorf: "spew.Errorf",
|
||||||
|
fFprint: "spew.Fprint",
|
||||||
|
fFprintln: "spew.Fprintln",
|
||||||
|
fPrint: "spew.Print",
|
||||||
|
fPrintln: "spew.Println",
|
||||||
|
fSdump: "spew.Sdump",
|
||||||
|
fSprint: "spew.Sprint",
|
||||||
|
fSprintf: "spew.Sprintf",
|
||||||
|
fSprintln: "spew.Sprintln",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f spewFunc) String() string {
|
||||||
|
if s, ok := spewFuncStrings[f]; ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Unknown spewFunc (%d)", int(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// spewTest is used to describe a test to be performed against the public
|
||||||
|
// functions of the spew package or ConfigState.
|
||||||
|
type spewTest struct {
|
||||||
|
cs *spew.ConfigState
|
||||||
|
f spewFunc
|
||||||
|
format string
|
||||||
|
in interface{}
|
||||||
|
want string
|
||||||
|
}
|
||||||
|
|
||||||
|
// spewTests houses the tests to be performed against the public functions of
|
||||||
|
// the spew package and ConfigState.
|
||||||
|
//
|
||||||
|
// These tests are only intended to ensure the public functions are exercised
|
||||||
|
// and are intentionally not exhaustive of types. The exhaustive type
|
||||||
|
// tests are handled in the dump and format tests.
|
||||||
|
var spewTests []spewTest
|
||||||
|
|
||||||
|
// redirStdout is a helper function to return the standard output from f as a
|
||||||
|
// byte slice.
|
||||||
|
func redirStdout(f func()) ([]byte, error) {
|
||||||
|
tempFile, err := ioutil.TempFile("", "ss-test")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fileName := tempFile.Name()
|
||||||
|
defer os.Remove(fileName) // Ignore error
|
||||||
|
|
||||||
|
origStdout := os.Stdout
|
||||||
|
os.Stdout = tempFile
|
||||||
|
f()
|
||||||
|
os.Stdout = origStdout
|
||||||
|
tempFile.Close()
|
||||||
|
|
||||||
|
return ioutil.ReadFile(fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSpewTests() {
|
||||||
|
// Config states with various settings.
|
||||||
|
scsDefault := spew.NewDefaultConfig()
|
||||||
|
scsNoMethods := &spew.ConfigState{Indent: " ", DisableMethods: true}
|
||||||
|
scsNoPmethods := &spew.ConfigState{Indent: " ", DisablePointerMethods: true}
|
||||||
|
scsMaxDepth := &spew.ConfigState{Indent: " ", MaxDepth: 1}
|
||||||
|
scsContinue := &spew.ConfigState{Indent: " ", ContinueOnMethod: true}
|
||||||
|
|
||||||
|
// Variables for tests on types which implement Stringer interface with and
|
||||||
|
// without a pointer receiver.
|
||||||
|
ts := stringer("test")
|
||||||
|
tps := pstringer("test")
|
||||||
|
|
||||||
|
// depthTester is used to test max depth handling for structs, array, slices
|
||||||
|
// and maps.
|
||||||
|
type depthTester struct {
|
||||||
|
ic indirCir1
|
||||||
|
arr [1]string
|
||||||
|
slice []string
|
||||||
|
m map[string]int
|
||||||
|
}
|
||||||
|
dt := depthTester{indirCir1{nil}, [1]string{"arr"}, []string{"slice"},
|
||||||
|
map[string]int{"one": 1}}
|
||||||
|
|
||||||
|
// Variable for tests on types which implement error interface.
|
||||||
|
te := customError(10)
|
||||||
|
|
||||||
|
spewTests = []spewTest{
|
||||||
|
{scsDefault, fCSFdump, "", int8(127), "(int8) 127\n"},
|
||||||
|
{scsDefault, fCSFprint, "", int16(32767), "32767"},
|
||||||
|
{scsDefault, fCSFprintf, "%v", int32(2147483647), "2147483647"},
|
||||||
|
{scsDefault, fCSFprintln, "", int(2147483647), "2147483647\n"},
|
||||||
|
{scsDefault, fCSPrint, "", int64(9223372036854775807), "9223372036854775807"},
|
||||||
|
{scsDefault, fCSPrintln, "", uint8(255), "255\n"},
|
||||||
|
{scsDefault, fCSSdump, "", uint8(64), "(uint8) 64\n"},
|
||||||
|
{scsDefault, fCSSprint, "", complex(1, 2), "(1+2i)"},
|
||||||
|
{scsDefault, fCSSprintf, "%v", complex(float32(3), 4), "(3+4i)"},
|
||||||
|
{scsDefault, fCSSprintln, "", complex(float64(5), 6), "(5+6i)\n"},
|
||||||
|
{scsDefault, fCSErrorf, "%#v", uint16(65535), "(uint16)65535"},
|
||||||
|
{scsDefault, fCSNewFormatter, "%v", uint32(4294967295), "4294967295"},
|
||||||
|
{scsDefault, fErrorf, "%v", uint64(18446744073709551615), "18446744073709551615"},
|
||||||
|
{scsDefault, fFprint, "", float32(3.14), "3.14"},
|
||||||
|
{scsDefault, fFprintln, "", float64(6.28), "6.28\n"},
|
||||||
|
{scsDefault, fPrint, "", true, "true"},
|
||||||
|
{scsDefault, fPrintln, "", false, "false\n"},
|
||||||
|
{scsDefault, fSdump, "", complex(-10, -20), "(complex128) (-10-20i)\n"},
|
||||||
|
{scsDefault, fSprint, "", complex(-1, -2), "(-1-2i)"},
|
||||||
|
{scsDefault, fSprintf, "%v", complex(float32(-3), -4), "(-3-4i)"},
|
||||||
|
{scsDefault, fSprintln, "", complex(float64(-5), -6), "(-5-6i)\n"},
|
||||||
|
{scsNoMethods, fCSFprint, "", ts, "test"},
|
||||||
|
{scsNoMethods, fCSFprint, "", &ts, "<*>test"},
|
||||||
|
{scsNoMethods, fCSFprint, "", tps, "test"},
|
||||||
|
{scsNoMethods, fCSFprint, "", &tps, "<*>test"},
|
||||||
|
{scsNoPmethods, fCSFprint, "", ts, "stringer test"},
|
||||||
|
{scsNoPmethods, fCSFprint, "", &ts, "<*>stringer test"},
|
||||||
|
{scsNoPmethods, fCSFprint, "", tps, "test"},
|
||||||
|
{scsNoPmethods, fCSFprint, "", &tps, "<*>stringer test"},
|
||||||
|
{scsMaxDepth, fCSFprint, "", dt, "{{<max>} [<max>] [<max>] map[<max>]}"},
|
||||||
|
{scsMaxDepth, fCSFdump, "", dt, "(spew_test.depthTester) {\n" +
|
||||||
|
" ic: (spew_test.indirCir1) {\n <max depth reached>\n },\n" +
|
||||||
|
" arr: ([1]string) (len=1 cap=1) {\n <max depth reached>\n },\n" +
|
||||||
|
" slice: ([]string) (len=1 cap=1) {\n <max depth reached>\n },\n" +
|
||||||
|
" m: (map[string]int) (len=1) {\n <max depth reached>\n }\n}\n"},
|
||||||
|
{scsContinue, fCSFprint, "", ts, "(stringer test) test"},
|
||||||
|
{scsContinue, fCSFdump, "", ts, "(spew_test.stringer) " +
|
||||||
|
"(len=4) (stringer test) \"test\"\n"},
|
||||||
|
{scsContinue, fCSFprint, "", te, "(error: 10) 10"},
|
||||||
|
{scsContinue, fCSFdump, "", te, "(spew_test.customError) " +
|
||||||
|
"(error: 10) 10\n"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSpew executes all of the tests described by spewTests.
|
||||||
|
func TestSpew(t *testing.T) {
|
||||||
|
initSpewTests()
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(spewTests))
|
||||||
|
for i, test := range spewTests {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
switch test.f {
|
||||||
|
case fCSFdump:
|
||||||
|
test.cs.Fdump(buf, test.in)
|
||||||
|
|
||||||
|
case fCSFprint:
|
||||||
|
test.cs.Fprint(buf, test.in)
|
||||||
|
|
||||||
|
case fCSFprintf:
|
||||||
|
test.cs.Fprintf(buf, test.format, test.in)
|
||||||
|
|
||||||
|
case fCSFprintln:
|
||||||
|
test.cs.Fprintln(buf, test.in)
|
||||||
|
|
||||||
|
case fCSPrint:
|
||||||
|
b, err := redirStdout(func() { test.cs.Print(test.in) })
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v #%d %v", test.f, i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buf.Write(b)
|
||||||
|
|
||||||
|
case fCSPrintln:
|
||||||
|
b, err := redirStdout(func() { test.cs.Println(test.in) })
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v #%d %v", test.f, i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buf.Write(b)
|
||||||
|
|
||||||
|
case fCSSdump:
|
||||||
|
str := test.cs.Sdump(test.in)
|
||||||
|
buf.WriteString(str)
|
||||||
|
|
||||||
|
case fCSSprint:
|
||||||
|
str := test.cs.Sprint(test.in)
|
||||||
|
buf.WriteString(str)
|
||||||
|
|
||||||
|
case fCSSprintf:
|
||||||
|
str := test.cs.Sprintf(test.format, test.in)
|
||||||
|
buf.WriteString(str)
|
||||||
|
|
||||||
|
case fCSSprintln:
|
||||||
|
str := test.cs.Sprintln(test.in)
|
||||||
|
buf.WriteString(str)
|
||||||
|
|
||||||
|
case fCSErrorf:
|
||||||
|
err := test.cs.Errorf(test.format, test.in)
|
||||||
|
buf.WriteString(err.Error())
|
||||||
|
|
||||||
|
case fCSNewFormatter:
|
||||||
|
fmt.Fprintf(buf, test.format, test.cs.NewFormatter(test.in))
|
||||||
|
|
||||||
|
case fErrorf:
|
||||||
|
err := spew.Errorf(test.format, test.in)
|
||||||
|
buf.WriteString(err.Error())
|
||||||
|
|
||||||
|
case fFprint:
|
||||||
|
spew.Fprint(buf, test.in)
|
||||||
|
|
||||||
|
case fFprintln:
|
||||||
|
spew.Fprintln(buf, test.in)
|
||||||
|
|
||||||
|
case fPrint:
|
||||||
|
b, err := redirStdout(func() { spew.Print(test.in) })
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v #%d %v", test.f, i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buf.Write(b)
|
||||||
|
|
||||||
|
case fPrintln:
|
||||||
|
b, err := redirStdout(func() { spew.Println(test.in) })
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v #%d %v", test.f, i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buf.Write(b)
|
||||||
|
|
||||||
|
case fSdump:
|
||||||
|
str := spew.Sdump(test.in)
|
||||||
|
buf.WriteString(str)
|
||||||
|
|
||||||
|
case fSprint:
|
||||||
|
str := spew.Sprint(test.in)
|
||||||
|
buf.WriteString(str)
|
||||||
|
|
||||||
|
case fSprintf:
|
||||||
|
str := spew.Sprintf(test.format, test.in)
|
||||||
|
buf.WriteString(str)
|
||||||
|
|
||||||
|
case fSprintln:
|
||||||
|
str := spew.Sprintln(test.in)
|
||||||
|
buf.WriteString(str)
|
||||||
|
|
||||||
|
default:
|
||||||
|
t.Errorf("%v #%d unrecognized function", test.f, i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s := buf.String()
|
||||||
|
if test.want != s {
|
||||||
|
t.Errorf("ConfigState #%d\n got: %s want: %s", i, s, test.want)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
82
vendor/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go
generated
vendored
Normal file
82
vendor/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||||
|
// when both cgo is supported and "-tags testcgo" is added to the go test
|
||||||
|
// command line. This code should really only be in the dumpcgo_test.go file,
|
||||||
|
// but unfortunately Go will not allow cgo in test files, so this is a
|
||||||
|
// workaround to allow cgo types to be tested. This configuration is used
|
||||||
|
// because spew itself does not require cgo to run even though it does handle
|
||||||
|
// certain cgo types specially. Rather than forcing all clients to require cgo
|
||||||
|
// and an external C compiler just to run the tests, this scheme makes them
|
||||||
|
// optional.
|
||||||
|
// +build cgo,testcgo
|
||||||
|
|
||||||
|
package testdata
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdint.h>
|
||||||
|
typedef unsigned char custom_uchar_t;
|
||||||
|
|
||||||
|
char *ncp = 0;
|
||||||
|
char *cp = "test";
|
||||||
|
char ca[6] = {'t', 'e', 's', 't', '2', '\0'};
|
||||||
|
unsigned char uca[6] = {'t', 'e', 's', 't', '3', '\0'};
|
||||||
|
signed char sca[6] = {'t', 'e', 's', 't', '4', '\0'};
|
||||||
|
uint8_t ui8ta[6] = {'t', 'e', 's', 't', '5', '\0'};
|
||||||
|
custom_uchar_t tuca[6] = {'t', 'e', 's', 't', '6', '\0'};
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// GetCgoNullCharPointer returns a null char pointer via cgo. This is only
|
||||||
|
// used for tests.
|
||||||
|
func GetCgoNullCharPointer() interface{} {
|
||||||
|
return C.ncp
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCgoCharPointer returns a char pointer via cgo. This is only used for
|
||||||
|
// tests.
|
||||||
|
func GetCgoCharPointer() interface{} {
|
||||||
|
return C.cp
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCgoCharArray returns a char array via cgo and the array's len and cap.
|
||||||
|
// This is only used for tests.
|
||||||
|
func GetCgoCharArray() (interface{}, int, int) {
|
||||||
|
return C.ca, len(C.ca), cap(C.ca)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCgoUnsignedCharArray returns an unsigned char array via cgo and the
|
||||||
|
// array's len and cap. This is only used for tests.
|
||||||
|
func GetCgoUnsignedCharArray() (interface{}, int, int) {
|
||||||
|
return C.uca, len(C.uca), cap(C.uca)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCgoSignedCharArray returns a signed char array via cgo and the array's len
|
||||||
|
// and cap. This is only used for tests.
|
||||||
|
func GetCgoSignedCharArray() (interface{}, int, int) {
|
||||||
|
return C.sca, len(C.sca), cap(C.sca)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCgoUint8tArray returns a uint8_t array via cgo and the array's len and
|
||||||
|
// cap. This is only used for tests.
|
||||||
|
func GetCgoUint8tArray() (interface{}, int, int) {
|
||||||
|
return C.ui8ta, len(C.ui8ta), cap(C.ui8ta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCgoTypdefedUnsignedCharArray returns a typedefed unsigned char array via
|
||||||
|
// cgo and the array's len and cap. This is only used for tests.
|
||||||
|
func GetCgoTypdefedUnsignedCharArray() (interface{}, int, int) {
|
||||||
|
return C.tuca, len(C.tuca), cap(C.tuca)
|
||||||
|
}
|
61
vendor/github.com/davecgh/go-spew/test_coverage.txt
generated
vendored
Normal file
61
vendor/github.com/davecgh/go-spew/test_coverage.txt
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
github.com/davecgh/go-spew/spew/dump.go dumpState.dump 100.00% (88/88)
|
||||||
|
github.com/davecgh/go-spew/spew/format.go formatState.format 100.00% (82/82)
|
||||||
|
github.com/davecgh/go-spew/spew/format.go formatState.formatPtr 100.00% (52/52)
|
||||||
|
github.com/davecgh/go-spew/spew/dump.go dumpState.dumpPtr 100.00% (44/44)
|
||||||
|
github.com/davecgh/go-spew/spew/dump.go dumpState.dumpSlice 100.00% (39/39)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go handleMethods 100.00% (30/30)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go printHexPtr 100.00% (18/18)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go unsafeReflectValue 100.00% (13/13)
|
||||||
|
github.com/davecgh/go-spew/spew/format.go formatState.constructOrigFormat 100.00% (12/12)
|
||||||
|
github.com/davecgh/go-spew/spew/dump.go fdump 100.00% (11/11)
|
||||||
|
github.com/davecgh/go-spew/spew/format.go formatState.Format 100.00% (11/11)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go init 100.00% (10/10)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go printComplex 100.00% (9/9)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go valuesSorter.Less 100.00% (8/8)
|
||||||
|
github.com/davecgh/go-spew/spew/format.go formatState.buildDefaultFormat 100.00% (7/7)
|
||||||
|
github.com/davecgh/go-spew/spew/format.go formatState.unpackValue 100.00% (5/5)
|
||||||
|
github.com/davecgh/go-spew/spew/dump.go dumpState.indent 100.00% (4/4)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go catchPanic 100.00% (4/4)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.convertArgs 100.00% (4/4)
|
||||||
|
github.com/davecgh/go-spew/spew/spew.go convertArgs 100.00% (4/4)
|
||||||
|
github.com/davecgh/go-spew/spew/format.go newFormatter 100.00% (3/3)
|
||||||
|
github.com/davecgh/go-spew/spew/dump.go Sdump 100.00% (3/3)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go printBool 100.00% (3/3)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go sortValues 100.00% (3/3)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.Sdump 100.00% (3/3)
|
||||||
|
github.com/davecgh/go-spew/spew/dump.go dumpState.unpackValue 100.00% (3/3)
|
||||||
|
github.com/davecgh/go-spew/spew/spew.go Printf 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/spew.go Println 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/spew.go Sprint 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/spew.go Sprintf 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/spew.go Sprintln 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go printFloat 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go NewDefaultConfig 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go printInt 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go printUint 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go valuesSorter.Len 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/common.go valuesSorter.Swap 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.Errorf 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprint 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintf 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintln 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.Print 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.Printf 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.Println 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprint 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintf 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintln 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.NewFormatter 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.Fdump 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/config.go ConfigState.Dump 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/dump.go Fdump 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/dump.go Dump 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/spew.go Fprintln 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/format.go NewFormatter 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/spew.go Errorf 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/spew.go Fprint 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/spew.go Fprintf 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew/spew.go Print 100.00% (1/1)
|
||||||
|
github.com/davecgh/go-spew/spew ------------------------------- 100.00% (505/505)
|
||||||
|
|
20
vendor/github.com/fernet/fernet-go/License
generated
vendored
Normal file
20
vendor/github.com/fernet/fernet-go/License
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Copyright © 2013 Keith Rarick
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
vendor/github.com/fernet/fernet-go/Readme
generated
vendored
Normal file
22
vendor/github.com/fernet/fernet-go/Readme
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Fernet takes a user-provided *message* (an arbitrary sequence of
|
||||||
|
bytes), a *key* (256 bits), and the current time, and produces a
|
||||||
|
*token*, which contains the message in a form that can't be read
|
||||||
|
or altered without the key.
|
||||||
|
|
||||||
|
This package is compatible with the other implementations at
|
||||||
|
https://github.com/fernet. They can exchange tokens freely among
|
||||||
|
each other.
|
||||||
|
|
||||||
|
Documentation: http://godoc.org/github.com/fernet/fernet-go
|
||||||
|
|
||||||
|
|
||||||
|
INSTALL
|
||||||
|
|
||||||
|
$ go get github.com/fernet/fernet-go
|
||||||
|
|
||||||
|
|
||||||
|
For more information and background, see the Fernet spec at
|
||||||
|
https://github.com/fernet/spec.
|
||||||
|
|
||||||
|
Fernet is distributed under the terms of the MIT license.
|
||||||
|
See the License file for details.
|
19
vendor/github.com/fernet/fernet-go/cmd/fernet-keygen/main.go
generated
vendored
Normal file
19
vendor/github.com/fernet/fernet-go/cmd/fernet-keygen/main.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/fernet/fernet-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
log.SetPrefix("fernet: ")
|
||||||
|
|
||||||
|
var key fernet.Key
|
||||||
|
if err := key.Generate(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(key.Encode())
|
||||||
|
}
|
45
vendor/github.com/fernet/fernet-go/cmd/fernet-sign/main.go
generated
vendored
Normal file
45
vendor/github.com/fernet/fernet-go/cmd/fernet-sign/main.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/fernet/fernet-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Usage = `Usage: fernet-sign ENV
|
||||||
|
|
||||||
|
fernet-sign encrypts and signs its input and prints the resulting token.
|
||||||
|
It uses the key in environment variable ENV.`
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
log.SetPrefix("fernet: ")
|
||||||
|
|
||||||
|
if len(os.Args) != 2 {
|
||||||
|
fmt.Fprintln(os.Stderr, Usage)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := fernet.DecodeKey(os.Getenv(os.Args[1]))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := ioutil.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := fernet.EncryptAndSign(b, key)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = os.Stdout.Write(append(t, '\n'))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}
|
20
vendor/github.com/fernet/fernet-go/example_test.go
generated
vendored
Normal file
20
vendor/github.com/fernet/fernet-go/example_test.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package fernet_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fernet/fernet-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example() {
|
||||||
|
k := fernet.MustDecodeKeys("cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4=")
|
||||||
|
tok, err := fernet.EncryptAndSign([]byte("hello"), k[0])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
msg := fernet.VerifyAndDecrypt(tok, 60*time.Second, k)
|
||||||
|
fmt.Println(string(msg))
|
||||||
|
// Output:
|
||||||
|
// hello
|
||||||
|
}
|
168
vendor/github.com/fernet/fernet-go/fernet.go
generated
vendored
Normal file
168
vendor/github.com/fernet/fernet-go/fernet.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// Package fernet takes a user-provided message (an arbitrary
|
||||||
|
// sequence of bytes), a key (256 bits), and the current time,
|
||||||
|
// and produces a token, which contains the message in a form
|
||||||
|
// that can't be read or altered without the key.
|
||||||
|
//
|
||||||
|
// For more information and background, see the Fernet spec
|
||||||
|
// at https://github.com/fernet/spec.
|
||||||
|
//
|
||||||
|
// Subdirectories in this package provide command-line tools
|
||||||
|
// for working with Fernet keys and tokens.
|
||||||
|
package fernet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/subtle"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
version byte = 0x80
|
||||||
|
tsOffset = 1
|
||||||
|
ivOffset = tsOffset + 8
|
||||||
|
payOffset = ivOffset + aes.BlockSize
|
||||||
|
overhead = 1 + 8 + aes.BlockSize + sha256.Size // ver + ts + iv + hmac
|
||||||
|
maxClockSkew = 60 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
var encoding = base64.URLEncoding
|
||||||
|
|
||||||
|
// generates a token from msg, writes it into tok, and returns the
|
||||||
|
// number of bytes generated, which is encodedLen(msg).
|
||||||
|
// len(tok) must be >= encodedLen(len(msg))
|
||||||
|
func gen(tok, msg, iv []byte, ts time.Time, k *Key) int {
|
||||||
|
tok[0] = version
|
||||||
|
binary.BigEndian.PutUint64(tok[tsOffset:], uint64(ts.Unix()))
|
||||||
|
copy(tok[ivOffset:], iv)
|
||||||
|
p := tok[payOffset:]
|
||||||
|
n := pad(p, msg, aes.BlockSize)
|
||||||
|
bc, _ := aes.NewCipher(k.cryptBytes())
|
||||||
|
cipher.NewCBCEncrypter(bc, iv).CryptBlocks(p[:n], p[:n])
|
||||||
|
genhmac(p[n:n], tok[:payOffset+n], k.signBytes())
|
||||||
|
return payOffset + n + sha256.Size
|
||||||
|
}
|
||||||
|
|
||||||
|
// token length for input msg of length n, not including base64
|
||||||
|
func encodedLen(n int) int {
|
||||||
|
const k = aes.BlockSize
|
||||||
|
return n/k*k + k + overhead
|
||||||
|
}
|
||||||
|
|
||||||
|
// max msg length for tok of length n, for binary token (no base64)
|
||||||
|
// upper bound; not exact
|
||||||
|
func decodedLen(n int) int {
|
||||||
|
return n - overhead
|
||||||
|
}
|
||||||
|
|
||||||
|
// if msg is nil, decrypts in place and returns a slice of tok.
|
||||||
|
func verify(msg, tok []byte, ttl time.Duration, now time.Time, k *Key) []byte {
|
||||||
|
if len(tok) < 1 || tok[0] != version {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ts := time.Unix(int64(binary.BigEndian.Uint64(tok[1:])), 0)
|
||||||
|
if ttl >= 0 && (now.After(ts.Add(ttl)) || ts.After(now.Add(maxClockSkew))) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n := len(tok) - sha256.Size
|
||||||
|
var hmac [sha256.Size]byte
|
||||||
|
genhmac(hmac[:0], tok[:n], k.signBytes())
|
||||||
|
if subtle.ConstantTimeCompare(tok[n:], hmac[:]) != 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pay := tok[payOffset : len(tok)-sha256.Size]
|
||||||
|
if len(pay)%aes.BlockSize != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if msg != nil {
|
||||||
|
copy(msg, pay)
|
||||||
|
pay = msg
|
||||||
|
}
|
||||||
|
bc, _ := aes.NewCipher(k.cryptBytes())
|
||||||
|
iv := tok[9:][:aes.BlockSize]
|
||||||
|
cipher.NewCBCDecrypter(bc, iv).CryptBlocks(pay, pay)
|
||||||
|
return unpad(pay)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pads p to a multiple of k using PKCS #7 standard block padding.
|
||||||
|
// See http://tools.ietf.org/html/rfc5652#section-6.3.
|
||||||
|
func pad(q, p []byte, k int) int {
|
||||||
|
n := len(p)/k*k + k
|
||||||
|
copy(q, p)
|
||||||
|
c := byte(n - len(p))
|
||||||
|
for i := len(p); i < n; i++ {
|
||||||
|
q[i] = c
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes PKCS #7 standard block padding from p.
|
||||||
|
// See http://tools.ietf.org/html/rfc5652#section-6.3.
|
||||||
|
// This function is the inverse of pad.
|
||||||
|
// If the padding is not well-formed, unpad returns nil.
|
||||||
|
func unpad(p []byte) []byte {
|
||||||
|
c := p[len(p)-1]
|
||||||
|
for i := len(p) - int(c); i < len(p); i++ {
|
||||||
|
if i < 0 || p[i] != c {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p[:len(p)-int(c)]
|
||||||
|
}
|
||||||
|
|
||||||
|
func b64enc(src []byte) []byte {
|
||||||
|
dst := make([]byte, encoding.EncodedLen(len(src)))
|
||||||
|
encoding.Encode(dst, src)
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func b64dec(src []byte) []byte {
|
||||||
|
dst := make([]byte, encoding.DecodedLen(len(src)))
|
||||||
|
n, err := encoding.Decode(dst, src)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return dst[:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
func genhmac(q, p, k []byte) {
|
||||||
|
h := hmac.New(sha256.New, k)
|
||||||
|
h.Write(p)
|
||||||
|
h.Sum(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptAndSign encrypts and signs msg with key k and returns the resulting
|
||||||
|
// fernet token. If msg contains text, the text should be encoded
|
||||||
|
// with UTF-8 to follow fernet convention.
|
||||||
|
func EncryptAndSign(msg []byte, k *Key) (tok []byte, err error) {
|
||||||
|
iv := make([]byte, aes.BlockSize)
|
||||||
|
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b := make([]byte, encodedLen(len(msg)))
|
||||||
|
n := gen(b, msg, iv, time.Now(), k)
|
||||||
|
tok = make([]byte, encoding.EncodedLen(n))
|
||||||
|
encoding.Encode(tok, b[:n])
|
||||||
|
return tok, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyAndDecrypt verifies that tok is a valid fernet token that was signed
|
||||||
|
// with a key in k at most ttl time ago only if ttl is greater than zero.
|
||||||
|
// Returns the message contained in tok if tok is valid, otherwise nil.
|
||||||
|
func VerifyAndDecrypt(tok []byte, ttl time.Duration, k []*Key) (msg []byte) {
|
||||||
|
b := make([]byte, encoding.DecodedLen(len(tok)))
|
||||||
|
n, _ := encoding.Decode(b, tok)
|
||||||
|
for _, k1 := range k {
|
||||||
|
msg = verify(nil, b[:n], ttl, time.Now(), k1)
|
||||||
|
if msg != nil {
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
159
vendor/github.com/fernet/fernet-go/fernet_test.go
generated
vendored
Normal file
159
vendor/github.com/fernet/fernet-go/fernet_test.go
generated
vendored
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package fernet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
Secret string
|
||||||
|
Src string
|
||||||
|
IV [aes.BlockSize]byte
|
||||||
|
Now time.Time
|
||||||
|
TTLSec int `json:"ttl_sec"`
|
||||||
|
Token string
|
||||||
|
Desc string
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustLoadTests(path string) []test {
|
||||||
|
var ts []test
|
||||||
|
if f, err := os.Open(path); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else if err = json.NewDecoder(f).Decode(&ts); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerate(t *testing.T) {
|
||||||
|
for _, tok := range mustLoadTests("generate.json") {
|
||||||
|
k := MustDecodeKeys(tok.Secret)
|
||||||
|
g := make([]byte, encodedLen(len(tok.Src)))
|
||||||
|
n := gen(g, []byte(tok.Src), tok.IV[:], tok.Now, k[0])
|
||||||
|
if n != len(g) {
|
||||||
|
t.Errorf("want %v, got %v", len(g), n)
|
||||||
|
}
|
||||||
|
s := base64.URLEncoding.EncodeToString(g)
|
||||||
|
if s != tok.Token {
|
||||||
|
t.Errorf("want %q, got %q", tok.Token, g)
|
||||||
|
t.Log("want")
|
||||||
|
dumpTok(t, tok.Token, len(tok.Token))
|
||||||
|
t.Log("got")
|
||||||
|
dumpTok(t, s, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyOk(t *testing.T) {
|
||||||
|
for i, tok := range mustLoadTests("verify.json") {
|
||||||
|
t.Logf("test %d %s", i, tok.Desc)
|
||||||
|
k := MustDecodeKeys(tok.Secret)
|
||||||
|
t.Log("tok")
|
||||||
|
dumpTok(t, tok.Token, len(tok.Token))
|
||||||
|
ttl := time.Duration(tok.TTLSec) * time.Second
|
||||||
|
b := mustBase64DecodeString(tok.Token)
|
||||||
|
g := verify(nil, b, ttl, tok.Now, k[0])
|
||||||
|
if string(g) != tok.Src {
|
||||||
|
t.Errorf("got %#v != exp %#v", string(g), tok.Src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyBad(t *testing.T) {
|
||||||
|
for i, tok := range mustLoadTests("invalid.json") {
|
||||||
|
if tok.Desc == "invalid base64" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Logf("test %d %s", i, tok.Desc)
|
||||||
|
t.Log(tok.Token)
|
||||||
|
b, err := base64.URLEncoding.DecodeString(tok.Token)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
k := MustDecodeKeys(tok.Secret)
|
||||||
|
ttl := time.Duration(tok.TTLSec) * time.Second
|
||||||
|
if g := verify(nil, b, ttl, tok.Now, k[0]); g != nil {
|
||||||
|
t.Errorf("got %#v", string(g))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyBadBase64(t *testing.T) {
|
||||||
|
for i, tok := range mustLoadTests("invalid.json") {
|
||||||
|
if tok.Desc != "invalid base64" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Logf("test %d %s", i, tok.Desc)
|
||||||
|
t.Log(tok.Token)
|
||||||
|
k := MustDecodeKeys(tok.Secret)
|
||||||
|
ttl := time.Duration(tok.TTLSec) * time.Second
|
||||||
|
if g := VerifyAndDecrypt([]byte(tok.Token), ttl, k); g != nil {
|
||||||
|
t.Errorf("got %#v", string(g))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkGenerate(b *testing.B) {
|
||||||
|
k := new(Key)
|
||||||
|
k.Generate()
|
||||||
|
msg := []byte("hello")
|
||||||
|
g := make([]byte, encodedLen(len(msg)))
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
iv := make([]byte, aes.BlockSize)
|
||||||
|
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
gen(g, msg, iv, time.Now(), k)
|
||||||
|
//k.EncryptAndSign([]byte("hello"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkVerifyOk(b *testing.B) {
|
||||||
|
t := mustLoadTests("verify.json")[0]
|
||||||
|
k := MustDecodeKeys(t.Secret)
|
||||||
|
ttl := time.Duration(t.TTLSec) * time.Second
|
||||||
|
tok := mustBase64DecodeString(t.Token)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
verify(nil, tok, ttl, t.Now, k[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkVerifyBad(b *testing.B) {
|
||||||
|
t := mustLoadTests("invalid.json")[0]
|
||||||
|
k := MustDecodeKeys(t.Secret)
|
||||||
|
ttl := time.Duration(t.TTLSec) * time.Second
|
||||||
|
tok := mustBase64DecodeString(t.Token)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
verify(nil, tok, ttl, t.Now, k[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpTok(t *testing.T, s string, n int) {
|
||||||
|
tok := mustBase64DecodeString(s)
|
||||||
|
dumpField(t, tok, 0, 1)
|
||||||
|
dumpField(t, tok, 1, 1+8)
|
||||||
|
dumpField(t, tok, 1+8, 1+8+16)
|
||||||
|
dumpField(t, tok, 1+8+16, n-32)
|
||||||
|
dumpField(t, tok, n-32, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpField(t *testing.T, b []byte, n, e int) {
|
||||||
|
if len(b) < e {
|
||||||
|
e = len(b)
|
||||||
|
}
|
||||||
|
t.Log(b[n:e])
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustBase64DecodeString(s string) []byte {
|
||||||
|
b, err := base64.URLEncoding.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
9
vendor/github.com/fernet/fernet-go/generate.json
generated
vendored
Normal file
9
vendor/github.com/fernet/fernet-go/generate.json
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"token": "gAAAAAAdwJ6wAAECAwQFBgcICQoLDA0ODy021cpGVWKZ_eEwCGM4BLLF_5CV9dOPmrhuVUPgJobwOz7JcbmrR64jVmpU4IwqDA==",
|
||||||
|
"now": "1985-10-26T01:20:00-07:00",
|
||||||
|
"iv": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||||
|
"src": "hello",
|
||||||
|
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||||
|
}
|
||||||
|
]
|
58
vendor/github.com/fernet/fernet-go/invalid.json
generated
vendored
Normal file
58
vendor/github.com/fernet/fernet-go/invalid.json
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"desc": "incorrect mac",
|
||||||
|
"token": "gAAAAAAdwJ6xAAECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPAl1-szkFVzXTuGb4hR8AKtwcaX1YdykQUFBQUFBQUFBQQ==",
|
||||||
|
"now": "1985-10-26T01:20:01-07:00",
|
||||||
|
"ttl_sec": 60,
|
||||||
|
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"desc": "too short",
|
||||||
|
"token": "gAAAAAAdwJ6xAAECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPA==",
|
||||||
|
"now": "1985-10-26T01:20:01-07:00",
|
||||||
|
"ttl_sec": 60,
|
||||||
|
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"desc": "invalid base64",
|
||||||
|
"token": "%%%%%%%%%%%%%AECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPAl1-szkFVzXTuGb4hR8AKtwcaX1YdykRtfsH-p1YsUD2Q==",
|
||||||
|
"now": "1985-10-26T01:20:01-07:00",
|
||||||
|
"ttl_sec": 60,
|
||||||
|
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"desc": "payload size not multiple of block size",
|
||||||
|
"token": "gAAAAAAdwJ6xAAECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPOm73QeoCk9uGib28Xe5vz6oxq5nmxbx_v7mrfyudzUm",
|
||||||
|
"now": "1985-10-26T01:20:01-07:00",
|
||||||
|
"ttl_sec": 60,
|
||||||
|
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"desc": "payload padding error",
|
||||||
|
"token": "gAAAAAAdwJ6xAAECAwQFBgcICQoLDA0ODz4LEpdELGQAad7aNEHbf-JkLPIpuiYRLQ3RtXatOYREu2FWke6CnJNYIbkuKNqOhw==",
|
||||||
|
"now": "1985-10-26T01:20:01-07:00",
|
||||||
|
"ttl_sec": 60,
|
||||||
|
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"desc": "far-future TS (unacceptable clock skew)",
|
||||||
|
"token": "gAAAAAAdwStRAAECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPAnja1xKYyhd-Y6mSkTOyTGJmw2Xc2a6kBd-iX9b_qXQcw==",
|
||||||
|
"now": "1985-10-26T01:20:01-07:00",
|
||||||
|
"ttl_sec": 60,
|
||||||
|
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"desc": "expired TTL",
|
||||||
|
"token": "gAAAAAAdwJ6xAAECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPAl1-szkFVzXTuGb4hR8AKtwcaX1YdykRtfsH-p1YsUD2Q==",
|
||||||
|
"now": "1985-10-26T01:21:31-07:00",
|
||||||
|
"ttl_sec": 60,
|
||||||
|
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"desc": "incorrect IV (causes padding error)",
|
||||||
|
"token": "gAAAAAAdwJ6xBQECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPAkLhFLHpGtDBRLRTZeUfWgHSv49TF2AUEZ1TIvcZjK1zQ==",
|
||||||
|
"now": "1985-10-26T01:20:01-07:00",
|
||||||
|
"ttl_sec": 60,
|
||||||
|
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||||
|
}
|
||||||
|
]
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user