diff options
Diffstat (limited to 'vendor/github.com')
195 files changed, 8185 insertions, 5484 deletions
diff --git a/vendor/github.com/NYTimes/gziphandler/gzip.go b/vendor/github.com/NYTimes/gziphandler/gzip.go index b6af9115a..13ee34a42 100644 --- a/vendor/github.com/NYTimes/gziphandler/gzip.go +++ b/vendor/github.com/NYTimes/gziphandler/gzip.go @@ -82,6 +82,7 @@ type GzipResponseWriter struct { buf []byte // Holds the first part of the write before reaching the minSize or the end of the write. contentTypes []string // Only compress if the response is one of these content-types. All are accepted if empty. + flushed bool // Indicate if the stream was already flushed } // Write appends data to the gzip writer. @@ -150,7 +151,9 @@ func (w *GzipResponseWriter) startGzip() error { // WriteHeader just saves the response code until close or GZIP effective writes. func (w *GzipResponseWriter) WriteHeader(code int) { - w.code = code + if w.code == 0 { + w.code = code + } } // init graps a new gzip writer from the gzipWriterPool and writes the correct @@ -167,7 +170,8 @@ func (w *GzipResponseWriter) init() { func (w *GzipResponseWriter) Close() error { if w.gw == nil { // Gzip not trigged yet, write out regular response. - if w.code != 0 { + // WriteHeader only if it wasn't already wrote by a Flush + if !w.flushed && w.code != 0 { w.ResponseWriter.WriteHeader(w.code) } if w.buf != nil { @@ -195,7 +199,11 @@ func (w *GzipResponseWriter) Flush() { } if fw, ok := w.ResponseWriter.(http.Flusher); ok { + if !w.flushed && w.code != 0 { + w.ResponseWriter.WriteHeader(w.code) + } fw.Flush() + w.flushed = true } } diff --git a/vendor/github.com/NYTimes/gziphandler/gzip_test.go b/vendor/github.com/NYTimes/gziphandler/gzip_test.go index 655a19373..7decfd17b 100644 --- a/vendor/github.com/NYTimes/gziphandler/gzip_test.go +++ b/vendor/github.com/NYTimes/gziphandler/gzip_test.go @@ -306,6 +306,39 @@ func TestStatusCodes(t *testing.T) { } } +func TestStatusCodesFlushed(t *testing.T) { + handler := GzipHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusNotFound) + rw.(http.Flusher).Flush() + rw.Write([]byte("Not found")) + })) + r := httptest.NewRequest(http.MethodGet, "/", nil) + r.Header.Set(acceptEncoding, "gzip") + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + + result := w.Result() + if result.StatusCode != http.StatusNotFound { + t.Errorf("StatusCode should have been 404 but was %d", result.StatusCode) + } +} + +func TestIgnoreSubsequentWriteHeader(t *testing.T) { + handler := GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(500) + w.WriteHeader(404) + })) + r := httptest.NewRequest("GET", "/", nil) + r.Header.Set("Accept-Encoding", "gzip") + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + + result := w.Result() + if result.StatusCode != 500 { + t.Errorf("StatusCode should have been 500 but was %d", result.StatusCode) + } +} + func TestDontWriteWhenNotWrittenTo(t *testing.T) { // When using gzip as middleware without ANY writes in the handler, // ensure the gzip middleware doesn't touch the actual ResponseWriter diff --git a/vendor/github.com/armon/go-metrics/inmem.go b/vendor/github.com/armon/go-metrics/inmem.go index cd1773042..8fe1de802 100644 --- a/vendor/github.com/armon/go-metrics/inmem.go +++ b/vendor/github.com/armon/go-metrics/inmem.go @@ -70,7 +70,7 @@ func NewIntervalMetrics(intv time.Time) *IntervalMetrics { // about a sample type AggregateSample struct { Count int // The count of emitted pairs - Rate float64 `json:"-"` // The count of emitted pairs per time unit (usually 1 second) + Rate float64 // The values rate per time unit (usually 1 second) Sum float64 // The sum of values SumSq float64 `json:"-"` // The sum of squared values Min float64 // Minimum value @@ -107,7 +107,7 @@ func (a *AggregateSample) Ingest(v float64, rateDenom float64) { if v > a.Max || a.Count == 1 { a.Max = v } - a.Rate = float64(a.Count) / rateDenom + a.Rate = float64(a.Sum) / rateDenom a.LastUpdated = time.Now() } diff --git a/vendor/github.com/armon/go-metrics/inmem_endpoint_test.go b/vendor/github.com/armon/go-metrics/inmem_endpoint_test.go index 326d56ada..f9c6793b6 100644 --- a/vendor/github.com/armon/go-metrics/inmem_endpoint_test.go +++ b/vendor/github.com/armon/go-metrics/inmem_endpoint_test.go @@ -61,7 +61,7 @@ func TestDisplayMetrics(t *testing.T) { Max: 22, Sum: 42, SumSq: 884, - Rate: 200, + Rate: 4200, }, Mean: 21, Stddev: 1.4142135623730951, @@ -75,7 +75,7 @@ func TestDisplayMetrics(t *testing.T) { Max: 40, Sum: 60, SumSq: 2000, - Rate: 200, + Rate: 6000, }, Mean: 30, Stddev: 14.142135623730951, @@ -92,7 +92,7 @@ func TestDisplayMetrics(t *testing.T) { Max: 24, Sum: 44, SumSq: 976, - Rate: 200, + Rate: 4400, }, Mean: 22, Stddev: 2.8284271247461903, @@ -106,7 +106,7 @@ func TestDisplayMetrics(t *testing.T) { Max: 33, Sum: 56, SumSq: 1618, - Rate: 200, + Rate: 5600, }, Mean: 28, Stddev: 7.0710678118654755, diff --git a/vendor/github.com/armon/go-metrics/inmem_test.go b/vendor/github.com/armon/go-metrics/inmem_test.go index 8d30e7c30..3b037c70d 100644 --- a/vendor/github.com/armon/go-metrics/inmem_test.go +++ b/vendor/github.com/armon/go-metrics/inmem_test.go @@ -53,7 +53,7 @@ func TestInmemSink(t *testing.T) { if agg.Count != 2 { t.Fatalf("bad val: %v", agg) } - if agg.Rate != 200 { + if agg.Rate != 4200 { t.Fatalf("bad val: %v", agg.Rate) } if agg.Sum != 42 { diff --git a/vendor/github.com/disintegration/imaging/clone.go b/vendor/github.com/disintegration/imaging/clone.go new file mode 100644 index 000000000..a22e01913 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/clone.go @@ -0,0 +1,312 @@ +package imaging + +import ( + "image" + "image/color" +) + +// Clone returns a copy of the given image. +func Clone(img image.Image) *image.NRGBA { + dstBounds := img.Bounds().Sub(img.Bounds().Min) + dst := image.NewNRGBA(dstBounds) + + switch src := img.(type) { + case *image.NRGBA: + copyNRGBA(dst, src) + case *image.NRGBA64: + copyNRGBA64(dst, src) + case *image.RGBA: + copyRGBA(dst, src) + case *image.RGBA64: + copyRGBA64(dst, src) + case *image.Gray: + copyGray(dst, src) + case *image.Gray16: + copyGray16(dst, src) + case *image.YCbCr: + copyYCbCr(dst, src) + case *image.Paletted: + copyPaletted(dst, src) + default: + copyImage(dst, src) + } + + return dst +} + +func copyNRGBA(dst *image.NRGBA, src *image.NRGBA) { + srcMinX := src.Rect.Min.X + srcMinY := src.Rect.Min.Y + dstW := dst.Rect.Dx() + dstH := dst.Rect.Dy() + rowSize := dstW * 4 + parallel(dstH, func(partStart, partEnd int) { + for dstY := partStart; dstY < partEnd; dstY++ { + di := dst.PixOffset(0, dstY) + si := src.PixOffset(srcMinX, srcMinY+dstY) + copy(dst.Pix[di:di+rowSize], src.Pix[si:si+rowSize]) + } + }) +} + +func copyNRGBA64(dst *image.NRGBA, src *image.NRGBA64) { + srcMinX := src.Rect.Min.X + srcMinY := src.Rect.Min.Y + dstW := dst.Rect.Dx() + dstH := dst.Rect.Dy() + parallel(dstH, func(partStart, partEnd int) { + for dstY := partStart; dstY < partEnd; dstY++ { + di := dst.PixOffset(0, dstY) + si := src.PixOffset(srcMinX, srcMinY+dstY) + for dstX := 0; dstX < dstW; dstX++ { + dst.Pix[di+0] = src.Pix[si+0] + dst.Pix[di+1] = src.Pix[si+2] + dst.Pix[di+2] = src.Pix[si+4] + dst.Pix[di+3] = src.Pix[si+6] + di += 4 + si += 8 + } + } + }) +} + +func copyRGBA(dst *image.NRGBA, src *image.RGBA) { + srcMinX := src.Rect.Min.X + srcMinY := src.Rect.Min.Y + dstW := dst.Rect.Dx() + dstH := dst.Rect.Dy() + parallel(dstH, func(partStart, partEnd int) { + for dstY := partStart; dstY < partEnd; dstY++ { + di := dst.PixOffset(0, dstY) + si := src.PixOffset(srcMinX, srcMinY+dstY) + for dstX := 0; dstX < dstW; dstX++ { + a := src.Pix[si+3] + dst.Pix[di+3] = a + + switch a { + case 0: + dst.Pix[di+0] = 0 + dst.Pix[di+1] = 0 + dst.Pix[di+2] = 0 + case 0xff: + dst.Pix[di+0] = src.Pix[si+0] + dst.Pix[di+1] = src.Pix[si+1] + dst.Pix[di+2] = src.Pix[si+2] + default: + var tmp uint16 + tmp = uint16(src.Pix[si+0]) * 0xff / uint16(a) + dst.Pix[di+0] = uint8(tmp) + tmp = uint16(src.Pix[si+1]) * 0xff / uint16(a) + dst.Pix[di+1] = uint8(tmp) + tmp = uint16(src.Pix[si+2]) * 0xff / uint16(a) + dst.Pix[di+2] = uint8(tmp) + } + + di += 4 + si += 4 + } + } + }) +} + +func copyRGBA64(dst *image.NRGBA, src *image.RGBA64) { + srcMinX := src.Rect.Min.X + srcMinY := src.Rect.Min.Y + dstW := dst.Rect.Dx() + dstH := dst.Rect.Dy() + parallel(dstH, func(partStart, partEnd int) { + for dstY := partStart; dstY < partEnd; dstY++ { + di := dst.PixOffset(0, dstY) + si := src.PixOffset(srcMinX, srcMinY+dstY) + for dstX := 0; dstX < dstW; dstX++ { + a := src.Pix[si+6] + dst.Pix[di+3] = a + + switch a { + case 0: + dst.Pix[di+0] = 0 + dst.Pix[di+1] = 0 + dst.Pix[di+2] = 0 + case 0xff: + dst.Pix[di+0] = src.Pix[si+0] + dst.Pix[di+1] = src.Pix[si+2] + dst.Pix[di+2] = src.Pix[si+4] + default: + var tmp uint16 + tmp = uint16(src.Pix[si+0]) * 0xff / uint16(a) + dst.Pix[di+0] = uint8(tmp) + tmp = uint16(src.Pix[si+2]) * 0xff / uint16(a) + dst.Pix[di+1] = uint8(tmp) + tmp = uint16(src.Pix[si+4]) * 0xff / uint16(a) + dst.Pix[di+2] = uint8(tmp) + } + + di += 4 + si += 8 + } + } + }) +} + +func copyGray(dst *image.NRGBA, src *image.Gray) { + srcMinX := src.Rect.Min.X + srcMinY := src.Rect.Min.Y + dstW := dst.Rect.Dx() + dstH := dst.Rect.Dy() + parallel(dstH, func(partStart, partEnd int) { + for dstY := partStart; dstY < partEnd; dstY++ { + di := dst.PixOffset(0, dstY) + si := src.PixOffset(srcMinX, srcMinY+dstY) + for dstX := 0; dstX < dstW; dstX++ { + c := src.Pix[si] + dst.Pix[di+0] = c + dst.Pix[di+1] = c + dst.Pix[di+2] = c + dst.Pix[di+3] = 0xff + di += 4 + si++ + } + } + }) +} + +func copyGray16(dst *image.NRGBA, src *image.Gray16) { + srcMinX := src.Rect.Min.X + srcMinY := src.Rect.Min.Y + dstW := dst.Rect.Dx() + dstH := dst.Rect.Dy() + parallel(dstH, func(partStart, partEnd int) { + for dstY := partStart; dstY < partEnd; dstY++ { + di := dst.PixOffset(0, dstY) + si := src.PixOffset(srcMinX, srcMinY+dstY) + for dstX := 0; dstX < dstW; dstX++ { + c := src.Pix[si] + dst.Pix[di+0] = c + dst.Pix[di+1] = c + dst.Pix[di+2] = c + dst.Pix[di+3] = 0xff + di += 4 + si += 2 + } + } + }) +} + +func copyYCbCr(dst *image.NRGBA, src *image.YCbCr) { + srcMinX := src.Rect.Min.X + srcMinY := src.Rect.Min.Y + dstW := dst.Rect.Dx() + dstH := dst.Rect.Dy() + parallel(dstH, func(partStart, partEnd int) { + for dstY := partStart; dstY < partEnd; dstY++ { + srcY := srcMinY + dstY + di := dst.PixOffset(0, dstY) + for dstX := 0; dstX < dstW; dstX++ { + srcX := srcMinX + dstX + + siy := (srcY-srcMinY)*src.YStride + (srcX - srcMinX) + + var sic int + switch src.SubsampleRatio { + case image.YCbCrSubsampleRatio444: + sic = (srcY-srcMinY)*src.CStride + (srcX - srcMinX) + case image.YCbCrSubsampleRatio422: + sic = (srcY-srcMinY)*src.CStride + (srcX/2 - srcMinX/2) + case image.YCbCrSubsampleRatio420: + sic = (srcY/2-srcMinY/2)*src.CStride + (srcX/2 - srcMinX/2) + case image.YCbCrSubsampleRatio440: + sic = (srcY/2-srcMinY/2)*src.CStride + (srcX - srcMinX) + default: + sic = src.COffset(srcX, srcY) + } + + y := int32(src.Y[siy]) + cb := int32(src.Cb[sic]) - 128 + cr := int32(src.Cr[sic]) - 128 + + r := (y<<16 + 91881*cr + 1<<15) >> 16 + if r > 255 { + r = 255 + } else if r < 0 { + r = 0 + } + + g := (y<<16 - 22554*cb - 46802*cr + 1<<15) >> 16 + if g > 255 { + g = 255 + } else if g < 0 { + g = 0 + } + + b := (y<<16 + 116130*cb + 1<<15) >> 16 + if b > 255 { + b = 255 + } else if b < 0 { + b = 0 + } + + dst.Pix[di+0] = uint8(r) + dst.Pix[di+1] = uint8(g) + dst.Pix[di+2] = uint8(b) + dst.Pix[di+3] = 255 + + di += 4 + } + } + }) +} + +func copyPaletted(dst *image.NRGBA, src *image.Paletted) { + srcMinX := src.Rect.Min.X + srcMinY := src.Rect.Min.Y + dstW := dst.Rect.Dx() + dstH := dst.Rect.Dy() + plen := len(src.Palette) + pnew := make([]color.NRGBA, plen) + for i := 0; i < plen; i++ { + pnew[i] = color.NRGBAModel.Convert(src.Palette[i]).(color.NRGBA) + } + parallel(dstH, func(partStart, partEnd int) { + for dstY := partStart; dstY < partEnd; dstY++ { + di := dst.PixOffset(0, dstY) + si := src.PixOffset(srcMinX, srcMinY+dstY) + for dstX := 0; dstX < dstW; dstX++ { + c := pnew[src.Pix[si]] + dst.Pix[di+0] = c.R + dst.Pix[di+1] = c.G + dst.Pix[di+2] = c.B + dst.Pix[di+3] = c.A + di += 4 + si++ + } + } + }) +} + +func copyImage(dst *image.NRGBA, src image.Image) { + srcMinX := src.Bounds().Min.X + srcMinY := src.Bounds().Min.Y + dstW := dst.Bounds().Dx() + dstH := dst.Bounds().Dy() + parallel(dstH, func(partStart, partEnd int) { + for dstY := partStart; dstY < partEnd; dstY++ { + di := dst.PixOffset(0, dstY) + for dstX := 0; dstX < dstW; dstX++ { + c := color.NRGBAModel.Convert(src.At(srcMinX+dstX, srcMinY+dstY)).(color.NRGBA) + dst.Pix[di+0] = c.R + dst.Pix[di+1] = c.G + dst.Pix[di+2] = c.B + dst.Pix[di+3] = c.A + di += 4 + } + } + }) +} + +// toNRGBA converts any image type to *image.NRGBA with min-point at (0, 0). +func toNRGBA(img image.Image) *image.NRGBA { + if img, ok := img.(*image.NRGBA); ok && img.Bounds().Min.Eq(image.ZP) { + return img + } + return Clone(img) +} diff --git a/vendor/github.com/disintegration/imaging/clone_test.go b/vendor/github.com/disintegration/imaging/clone_test.go new file mode 100644 index 000000000..36e309f0a --- /dev/null +++ b/vendor/github.com/disintegration/imaging/clone_test.go @@ -0,0 +1,247 @@ +package imaging + +import ( + "image" + "image/color" + "testing" +) + +func TestClone(t *testing.T) { + td := []struct { + desc string + src image.Image + want *image.NRGBA + }{ + { + "Clone NRGBA", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 0, 1), + Stride: 1 * 4, + Pix: []uint8{0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 2), + Stride: 1 * 4, + Pix: []uint8{0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff}, + }, + }, + { + "Clone NRGBA64", + &image.NRGBA64{ + Rect: image.Rect(-1, -1, 0, 1), + Stride: 1 * 8, + Pix: []uint8{ + 0x00, 0x00, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, + 0xcc, 0xcc, 0xdd, 0xdd, 0xee, 0xee, 0xff, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 2), + Stride: 1 * 4, + Pix: []uint8{0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff}, + }, + }, + { + "Clone RGBA", + &image.RGBA{ + Rect: image.Rect(-1, -1, 0, 2), + Stride: 1 * 4, + Pix: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 3), + Stride: 1 * 4, + Pix: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa, 0x33, 0xcc, 0xdd, 0xee, 0xff}, + }, + }, + { + "Clone RGBA64", + &image.RGBA64{ + Rect: image.Rect(-1, -1, 0, 2), + Stride: 1 * 8, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, + 0xcc, 0xcc, 0xdd, 0xdd, 0xee, 0xee, 0xff, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 3), + Stride: 1 * 4, + Pix: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa, 0x33, 0xcc, 0xdd, 0xee, 0xff}, + }, + }, + { + "Clone Gray", + &image.Gray{ + Rect: image.Rect(-1, -1, 0, 1), + Stride: 1 * 1, + Pix: []uint8{0x11, 0xee}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 2), + Stride: 1 * 4, + Pix: []uint8{0x11, 0x11, 0x11, 0xff, 0xee, 0xee, 0xee, 0xff}, + }, + }, + { + "Clone Gray16", + &image.Gray16{ + Rect: image.Rect(-1, -1, 0, 1), + Stride: 1 * 2, + Pix: []uint8{0x11, 0x11, 0xee, 0xee}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 2), + Stride: 1 * 4, + Pix: []uint8{0x11, 0x11, 0x11, 0xff, 0xee, 0xee, 0xee, 0xff}, + }, + }, + { + "Clone Alpha", + &image.Alpha{ + Rect: image.Rect(-1, -1, 0, 1), + Stride: 1 * 1, + Pix: []uint8{0x11, 0xee}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 2), + Stride: 1 * 4, + Pix: []uint8{0xff, 0xff, 0xff, 0x11, 0xff, 0xff, 0xff, 0xee}, + }, + }, + { + "Clone YCbCr", + &image.YCbCr{ + Rect: image.Rect(-1, -1, 5, 0), + SubsampleRatio: image.YCbCrSubsampleRatio444, + YStride: 6, + CStride: 6, + Y: []uint8{0x00, 0xff, 0x7f, 0x26, 0x4b, 0x0e}, + Cb: []uint8{0x80, 0x80, 0x80, 0x6b, 0x56, 0xc0}, + Cr: []uint8{0x80, 0x80, 0x80, 0xc0, 0x4b, 0x76}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 6, 1), + Stride: 6 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x7f, 0x7f, 0x7f, 0xff, + 0x7f, 0x00, 0x00, 0xff, + 0x00, 0x7f, 0x00, 0xff, + 0x00, 0x00, 0x7f, 0xff, + }, + }, + }, + { + "Clone YCbCr 444", + &image.YCbCr{ + Y: []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff}, + Cb: []uint8{0x55, 0xd4, 0xff, 0x8e, 0x2c, 0x01, 0x6b, 0xaa, 0xc0, 0x95, 0x56, 0x40, 0x80, 0x80, 0x80, 0x80}, + Cr: []uint8{0xff, 0xeb, 0x6b, 0x36, 0x15, 0x95, 0xc0, 0xb5, 0x76, 0x41, 0x4b, 0x8c, 0x80, 0x80, 0x80, 0x80}, + YStride: 4, + CStride: 4, + SubsampleRatio: image.YCbCrSubsampleRatio444, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + &image.NRGBA{ + Pix: []uint8{0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x49, 0xe1, 0xca, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0xff, 0x0, 0xff, 0x7f, 0x0, 0x0, 0xff, 0x7f, 0x0, 0x7f, 0xff, 0x0, 0x0, 0x7f, 0xff, 0x0, 0x7f, 0x7f, 0xff, 0x0, 0x7f, 0x0, 0xff, 0x82, 0x7f, 0x0, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff}, + Stride: 16, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + }, + { + "Clone YCbCr 440", + &image.YCbCr{ + Y: []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff}, + Cb: []uint8{0x2c, 0x01, 0x6b, 0xaa, 0x80, 0x80, 0x80, 0x80}, + Cr: []uint8{0x15, 0x95, 0xc0, 0xb5, 0x80, 0x80, 0x80, 0x80}, + YStride: 4, + CStride: 4, + SubsampleRatio: image.YCbCrSubsampleRatio440, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + &image.NRGBA{ + Pix: []uint8{0x0, 0xb5, 0x0, 0xff, 0x86, 0x86, 0x0, 0xff, 0x77, 0x0, 0x0, 0xff, 0xfb, 0x7d, 0xfb, 0xff, 0x0, 0xff, 0x1, 0xff, 0xff, 0xff, 0x1, 0xff, 0x80, 0x0, 0x1, 0xff, 0x7e, 0x0, 0x7e, 0xff, 0xe, 0xe, 0xe, 0xff, 0x59, 0x59, 0x59, 0xff, 0x4b, 0x4b, 0x4b, 0xff, 0x71, 0x71, 0x71, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff}, + Stride: 16, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + }, + { + "Clone YCbCr 422", + &image.YCbCr{ + Y: []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff}, + Cb: []uint8{0xd4, 0x8e, 0x01, 0xaa, 0x95, 0x40, 0x80, 0x80}, + Cr: []uint8{0xeb, 0x36, 0x95, 0xb5, 0x41, 0x8c, 0x80, 0x80}, + YStride: 4, + CStride: 2, + SubsampleRatio: image.YCbCrSubsampleRatio422, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + &image.NRGBA{ + Pix: []uint8{0xe2, 0x0, 0xe1, 0xff, 0xff, 0x0, 0xfe, 0xff, 0x0, 0x4d, 0x36, 0xff, 0x49, 0xe1, 0xca, 0xff, 0xb3, 0xb3, 0x0, 0xff, 0xff, 0xff, 0x1, 0xff, 0x70, 0x0, 0x70, 0xff, 0x7e, 0x0, 0x7e, 0xff, 0x0, 0x34, 0x33, 0xff, 0x1, 0x7f, 0x7e, 0xff, 0x5c, 0x58, 0x0, 0xff, 0x82, 0x7e, 0x0, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff}, + Stride: 16, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + }, + { + "Clone YCbCr 420", + &image.YCbCr{ + Y: []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff}, + Cb: []uint8{0x01, 0xaa, 0x80, 0x80}, + Cr: []uint8{0x95, 0xb5, 0x80, 0x80}, + YStride: 4, CStride: 2, + SubsampleRatio: image.YCbCrSubsampleRatio420, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + &image.NRGBA{ + Pix: []uint8{0x69, 0x69, 0x0, 0xff, 0x86, 0x86, 0x0, 0xff, 0x67, 0x0, 0x67, 0xff, 0xfb, 0x7d, 0xfb, 0xff, 0xb3, 0xb3, 0x0, 0xff, 0xff, 0xff, 0x1, 0xff, 0x70, 0x0, 0x70, 0xff, 0x7e, 0x0, 0x7e, 0xff, 0xe, 0xe, 0xe, 0xff, 0x59, 0x59, 0x59, 0xff, 0x4b, 0x4b, 0x4b, 0xff, 0x71, 0x71, 0x71, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff}, + Stride: 16, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + }, + { + "Clone Paletted", + &image.Paletted{ + Rect: image.Rect(-1, -1, 5, 0), + Stride: 6 * 1, + Palette: color.Palette{ + color.NRGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xff}, + color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}, + color.NRGBA{R: 0x7f, G: 0x7f, B: 0x7f, A: 0xff}, + color.NRGBA{R: 0x7f, G: 0x00, B: 0x00, A: 0xff}, + color.NRGBA{R: 0x00, G: 0x7f, B: 0x00, A: 0xff}, + color.NRGBA{R: 0x00, G: 0x00, B: 0x7f, A: 0xff}, + }, + Pix: []uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 6, 1), + Stride: 6 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x7f, 0x7f, 0x7f, 0xff, + 0x7f, 0x00, 0x00, 0xff, + 0x00, 0x7f, 0x00, 0xff, + 0x00, 0x00, 0x7f, 0xff, + }, + }, + }, + } + + for _, d := range td { + got := Clone(d.src) + want := d.want + + delta := 0 + if _, ok := d.src.(*image.YCbCr); ok { + delta = 1 + } + + if !compareNRGBA(got, want, delta) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} diff --git a/vendor/github.com/disintegration/imaging/helpers.go b/vendor/github.com/disintegration/imaging/helpers.go index 9184ccde6..5fb2b5aac 100644 --- a/vendor/github.com/disintegration/imaging/helpers.go +++ b/vendor/github.com/disintegration/imaging/helpers.go @@ -165,269 +165,3 @@ func New(width, height int, fillColor color.Color) *image.NRGBA { return dst } - -// Clone returns a copy of the given image. -func Clone(img image.Image) *image.NRGBA { - dstBounds := img.Bounds().Sub(img.Bounds().Min) - dst := image.NewNRGBA(dstBounds) - - switch src := img.(type) { - case *image.NRGBA: - copyNRGBA(dst, src) - case *image.NRGBA64: - copyNRGBA64(dst, src) - case *image.RGBA: - copyRGBA(dst, src) - case *image.RGBA64: - copyRGBA64(dst, src) - case *image.Gray: - copyGray(dst, src) - case *image.Gray16: - copyGray16(dst, src) - case *image.YCbCr: - copyYCbCr(dst, src) - case *image.Paletted: - copyPaletted(dst, src) - default: - copyImage(dst, src) - } - - return dst -} - -func copyNRGBA(dst *image.NRGBA, src *image.NRGBA) { - srcMinX := src.Rect.Min.X - srcMinY := src.Rect.Min.Y - dstW := dst.Rect.Dx() - dstH := dst.Rect.Dy() - rowSize := dstW * 4 - parallel(dstH, func(partStart, partEnd int) { - for dstY := partStart; dstY < partEnd; dstY++ { - di := dst.PixOffset(0, dstY) - si := src.PixOffset(srcMinX, srcMinY+dstY) - copy(dst.Pix[di:di+rowSize], src.Pix[si:si+rowSize]) - } - }) -} - -func copyNRGBA64(dst *image.NRGBA, src *image.NRGBA64) { - srcMinX := src.Rect.Min.X - srcMinY := src.Rect.Min.Y - dstW := dst.Rect.Dx() - dstH := dst.Rect.Dy() - parallel(dstH, func(partStart, partEnd int) { - for dstY := partStart; dstY < partEnd; dstY++ { - di := dst.PixOffset(0, dstY) - si := src.PixOffset(srcMinX, srcMinY+dstY) - for dstX := 0; dstX < dstW; dstX++ { - dst.Pix[di+0] = src.Pix[si+0] - dst.Pix[di+1] = src.Pix[si+2] - dst.Pix[di+2] = src.Pix[si+4] - dst.Pix[di+3] = src.Pix[si+6] - di += 4 - si += 8 - } - } - }) -} - -func copyRGBA(dst *image.NRGBA, src *image.RGBA) { - srcMinX := src.Rect.Min.X - srcMinY := src.Rect.Min.Y - dstW := dst.Rect.Dx() - dstH := dst.Rect.Dy() - parallel(dstH, func(partStart, partEnd int) { - for dstY := partStart; dstY < partEnd; dstY++ { - di := dst.PixOffset(0, dstY) - si := src.PixOffset(srcMinX, srcMinY+dstY) - for dstX := 0; dstX < dstW; dstX++ { - a := src.Pix[si+3] - dst.Pix[di+3] = a - - switch a { - case 0: - dst.Pix[di+0] = 0 - dst.Pix[di+1] = 0 - dst.Pix[di+2] = 0 - case 0xff: - dst.Pix[di+0] = src.Pix[si+0] - dst.Pix[di+1] = src.Pix[si+1] - dst.Pix[di+2] = src.Pix[si+2] - default: - var tmp uint16 - tmp = uint16(src.Pix[si+0]) * 0xff / uint16(a) - dst.Pix[di+0] = uint8(tmp) - tmp = uint16(src.Pix[si+1]) * 0xff / uint16(a) - dst.Pix[di+1] = uint8(tmp) - tmp = uint16(src.Pix[si+2]) * 0xff / uint16(a) - dst.Pix[di+2] = uint8(tmp) - } - - di += 4 - si += 4 - } - } - }) -} - -func copyRGBA64(dst *image.NRGBA, src *image.RGBA64) { - srcMinX := src.Rect.Min.X - srcMinY := src.Rect.Min.Y - dstW := dst.Rect.Dx() - dstH := dst.Rect.Dy() - parallel(dstH, func(partStart, partEnd int) { - for dstY := partStart; dstY < partEnd; dstY++ { - di := dst.PixOffset(0, dstY) - si := src.PixOffset(srcMinX, srcMinY+dstY) - for dstX := 0; dstX < dstW; dstX++ { - a := src.Pix[si+6] - dst.Pix[di+3] = a - - switch a { - case 0: - dst.Pix[di+0] = 0 - dst.Pix[di+1] = 0 - dst.Pix[di+2] = 0 - case 0xff: - dst.Pix[di+0] = src.Pix[si+0] - dst.Pix[di+1] = src.Pix[si+2] - dst.Pix[di+2] = src.Pix[si+4] - default: - var tmp uint16 - tmp = uint16(src.Pix[si+0]) * 0xff / uint16(a) - dst.Pix[di+0] = uint8(tmp) - tmp = uint16(src.Pix[si+2]) * 0xff / uint16(a) - dst.Pix[di+1] = uint8(tmp) - tmp = uint16(src.Pix[si+4]) * 0xff / uint16(a) - dst.Pix[di+2] = uint8(tmp) - } - - di += 4 - si += 8 - } - } - }) -} - -func copyGray(dst *image.NRGBA, src *image.Gray) { - srcMinX := src.Rect.Min.X - srcMinY := src.Rect.Min.Y - dstW := dst.Rect.Dx() - dstH := dst.Rect.Dy() - parallel(dstH, func(partStart, partEnd int) { - for dstY := partStart; dstY < partEnd; dstY++ { - di := dst.PixOffset(0, dstY) - si := src.PixOffset(srcMinX, srcMinY+dstY) - for dstX := 0; dstX < dstW; dstX++ { - c := src.Pix[si] - dst.Pix[di+0] = c - dst.Pix[di+1] = c - dst.Pix[di+2] = c - dst.Pix[di+3] = 0xff - di += 4 - si++ - } - } - }) -} - -func copyGray16(dst *image.NRGBA, src *image.Gray16) { - srcMinX := src.Rect.Min.X - srcMinY := src.Rect.Min.Y - dstW := dst.Rect.Dx() - dstH := dst.Rect.Dy() - parallel(dstH, func(partStart, partEnd int) { - for dstY := partStart; dstY < partEnd; dstY++ { - di := dst.PixOffset(0, dstY) - si := src.PixOffset(srcMinX, srcMinY+dstY) - for dstX := 0; dstX < dstW; dstX++ { - c := src.Pix[si] - dst.Pix[di+0] = c - dst.Pix[di+1] = c - dst.Pix[di+2] = c - dst.Pix[di+3] = 0xff - di += 4 - si += 2 - } - } - }) -} - -func copyYCbCr(dst *image.NRGBA, src *image.YCbCr) { - srcMinX := src.Rect.Min.X - srcMinY := src.Rect.Min.Y - dstW := dst.Rect.Dx() - dstH := dst.Rect.Dy() - parallel(dstH, func(partStart, partEnd int) { - for dstY := partStart; dstY < partEnd; dstY++ { - di := dst.PixOffset(0, dstY) - for dstX := 0; dstX < dstW; dstX++ { - srcX := srcMinX + dstX - srcY := srcMinY + dstY - siy := src.YOffset(srcX, srcY) - sic := src.COffset(srcX, srcY) - r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic]) - dst.Pix[di+0] = r - dst.Pix[di+1] = g - dst.Pix[di+2] = b - dst.Pix[di+3] = 0xff - di += 4 - } - } - }) -} - -func copyPaletted(dst *image.NRGBA, src *image.Paletted) { - srcMinX := src.Rect.Min.X - srcMinY := src.Rect.Min.Y - dstW := dst.Rect.Dx() - dstH := dst.Rect.Dy() - plen := len(src.Palette) - pnew := make([]color.NRGBA, plen) - for i := 0; i < plen; i++ { - pnew[i] = color.NRGBAModel.Convert(src.Palette[i]).(color.NRGBA) - } - parallel(dstH, func(partStart, partEnd int) { - for dstY := partStart; dstY < partEnd; dstY++ { - di := dst.PixOffset(0, dstY) - si := src.PixOffset(srcMinX, srcMinY+dstY) - for dstX := 0; dstX < dstW; dstX++ { - c := pnew[src.Pix[si]] - dst.Pix[di+0] = c.R - dst.Pix[di+1] = c.G - dst.Pix[di+2] = c.B - dst.Pix[di+3] = c.A - di += 4 - si++ - } - } - }) -} - -func copyImage(dst *image.NRGBA, src image.Image) { - srcMinX := src.Bounds().Min.X - srcMinY := src.Bounds().Min.Y - dstW := dst.Bounds().Dx() - dstH := dst.Bounds().Dy() - parallel(dstH, func(partStart, partEnd int) { - for dstY := partStart; dstY < partEnd; dstY++ { - di := dst.PixOffset(0, dstY) - for dstX := 0; dstX < dstW; dstX++ { - c := color.NRGBAModel.Convert(src.At(srcMinX+dstX, srcMinY+dstY)).(color.NRGBA) - dst.Pix[di+0] = c.R - dst.Pix[di+1] = c.G - dst.Pix[di+2] = c.B - dst.Pix[di+3] = c.A - di += 4 - } - } - }) -} - -// toNRGBA converts any image type to *image.NRGBA with min-point at (0, 0). -func toNRGBA(img image.Image) *image.NRGBA { - if img, ok := img.(*image.NRGBA); ok && img.Bounds().Min.Eq(image.ZP) { - return img - } - return Clone(img) -} diff --git a/vendor/github.com/disintegration/imaging/helpers_test.go b/vendor/github.com/disintegration/imaging/helpers_test.go index 1287ec588..c3b87f23c 100644 --- a/vendor/github.com/disintegration/imaging/helpers_test.go +++ b/vendor/github.com/disintegration/imaging/helpers_test.go @@ -140,246 +140,6 @@ func TestNew(t *testing.T) { } } -func TestClone(t *testing.T) { - td := []struct { - desc string - src image.Image - want *image.NRGBA - }{ - { - "Clone NRGBA", - &image.NRGBA{ - Rect: image.Rect(-1, -1, 0, 1), - Stride: 1 * 4, - Pix: []uint8{0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff}, - }, - &image.NRGBA{ - Rect: image.Rect(0, 0, 1, 2), - Stride: 1 * 4, - Pix: []uint8{0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff}, - }, - }, - { - "Clone NRGBA64", - &image.NRGBA64{ - Rect: image.Rect(-1, -1, 0, 1), - Stride: 1 * 8, - Pix: []uint8{ - 0x00, 0x00, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, - 0xcc, 0xcc, 0xdd, 0xdd, 0xee, 0xee, 0xff, 0xff, - }, - }, - &image.NRGBA{ - Rect: image.Rect(0, 0, 1, 2), - Stride: 1 * 4, - Pix: []uint8{0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff}, - }, - }, - { - "Clone RGBA", - &image.RGBA{ - Rect: image.Rect(-1, -1, 0, 2), - Stride: 1 * 4, - Pix: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff}, - }, - &image.NRGBA{ - Rect: image.Rect(0, 0, 1, 3), - Stride: 1 * 4, - Pix: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa, 0x33, 0xcc, 0xdd, 0xee, 0xff}, - }, - }, - { - "Clone RGBA64", - &image.RGBA64{ - Rect: image.Rect(-1, -1, 0, 2), - Stride: 1 * 8, - Pix: []uint8{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, - 0xcc, 0xcc, 0xdd, 0xdd, 0xee, 0xee, 0xff, 0xff, - }, - }, - &image.NRGBA{ - Rect: image.Rect(0, 0, 1, 3), - Stride: 1 * 4, - Pix: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa, 0x33, 0xcc, 0xdd, 0xee, 0xff}, - }, - }, - { - "Clone Gray", - &image.Gray{ - Rect: image.Rect(-1, -1, 0, 1), - Stride: 1 * 1, - Pix: []uint8{0x11, 0xee}, - }, - &image.NRGBA{ - Rect: image.Rect(0, 0, 1, 2), - Stride: 1 * 4, - Pix: []uint8{0x11, 0x11, 0x11, 0xff, 0xee, 0xee, 0xee, 0xff}, - }, - }, - { - "Clone Gray16", - &image.Gray16{ - Rect: image.Rect(-1, -1, 0, 1), - Stride: 1 * 2, - Pix: []uint8{0x11, 0x11, 0xee, 0xee}, - }, - &image.NRGBA{ - Rect: image.Rect(0, 0, 1, 2), - Stride: 1 * 4, - Pix: []uint8{0x11, 0x11, 0x11, 0xff, 0xee, 0xee, 0xee, 0xff}, - }, - }, - { - "Clone Alpha", - &image.Alpha{ - Rect: image.Rect(-1, -1, 0, 1), - Stride: 1 * 1, - Pix: []uint8{0x11, 0xee}, - }, - &image.NRGBA{ - Rect: image.Rect(0, 0, 1, 2), - Stride: 1 * 4, - Pix: []uint8{0xff, 0xff, 0xff, 0x11, 0xff, 0xff, 0xff, 0xee}, - }, - }, - { - "Clone YCbCr", - &image.YCbCr{ - Rect: image.Rect(-1, -1, 5, 0), - SubsampleRatio: image.YCbCrSubsampleRatio444, - YStride: 6, - CStride: 6, - Y: []uint8{0x00, 0xff, 0x7f, 0x26, 0x4b, 0x0e}, - Cb: []uint8{0x80, 0x80, 0x80, 0x6b, 0x56, 0xc0}, - Cr: []uint8{0x80, 0x80, 0x80, 0xc0, 0x4b, 0x76}, - }, - &image.NRGBA{ - Rect: image.Rect(0, 0, 6, 1), - Stride: 6 * 4, - Pix: []uint8{ - 0x00, 0x00, 0x00, 0xff, - 0xff, 0xff, 0xff, 0xff, - 0x7f, 0x7f, 0x7f, 0xff, - 0x7f, 0x00, 0x00, 0xff, - 0x00, 0x7f, 0x00, 0xff, - 0x00, 0x00, 0x7f, 0xff, - }, - }, - }, - { - "Clone YCbCr 444", - &image.YCbCr{ - Y: []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff}, - Cb: []uint8{0x55, 0xd4, 0xff, 0x8e, 0x2c, 0x01, 0x6b, 0xaa, 0xc0, 0x95, 0x56, 0x40, 0x80, 0x80, 0x80, 0x80}, - Cr: []uint8{0xff, 0xeb, 0x6b, 0x36, 0x15, 0x95, 0xc0, 0xb5, 0x76, 0x41, 0x4b, 0x8c, 0x80, 0x80, 0x80, 0x80}, - YStride: 4, - CStride: 4, - SubsampleRatio: image.YCbCrSubsampleRatio444, - Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, - }, - &image.NRGBA{ - Pix: []uint8{0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x49, 0xe1, 0xca, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0xff, 0x0, 0xff, 0x7f, 0x0, 0x0, 0xff, 0x7f, 0x0, 0x7f, 0xff, 0x0, 0x0, 0x7f, 0xff, 0x0, 0x7f, 0x7f, 0xff, 0x0, 0x7f, 0x0, 0xff, 0x82, 0x7f, 0x0, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff}, - Stride: 16, - Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, - }, - }, - { - "Clone YCbCr 440", - &image.YCbCr{ - Y: []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff}, - Cb: []uint8{0x2c, 0x01, 0x6b, 0xaa, 0x80, 0x80, 0x80, 0x80}, - Cr: []uint8{0x15, 0x95, 0xc0, 0xb5, 0x80, 0x80, 0x80, 0x80}, - YStride: 4, - CStride: 4, - SubsampleRatio: image.YCbCrSubsampleRatio440, - Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, - }, - &image.NRGBA{ - Pix: []uint8{0x0, 0xb5, 0x0, 0xff, 0x86, 0x86, 0x0, 0xff, 0x77, 0x0, 0x0, 0xff, 0xfb, 0x7d, 0xfb, 0xff, 0x0, 0xff, 0x1, 0xff, 0xff, 0xff, 0x1, 0xff, 0x80, 0x0, 0x1, 0xff, 0x7e, 0x0, 0x7e, 0xff, 0xe, 0xe, 0xe, 0xff, 0x59, 0x59, 0x59, 0xff, 0x4b, 0x4b, 0x4b, 0xff, 0x71, 0x71, 0x71, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff}, - Stride: 16, - Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, - }, - }, - { - "Clone YCbCr 422", - &image.YCbCr{ - Y: []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff}, - Cb: []uint8{0xd4, 0x8e, 0x01, 0xaa, 0x95, 0x40, 0x80, 0x80}, - Cr: []uint8{0xeb, 0x36, 0x95, 0xb5, 0x41, 0x8c, 0x80, 0x80}, - YStride: 4, - CStride: 2, - SubsampleRatio: image.YCbCrSubsampleRatio422, - Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, - }, - &image.NRGBA{ - Pix: []uint8{0xe2, 0x0, 0xe1, 0xff, 0xff, 0x0, 0xfe, 0xff, 0x0, 0x4d, 0x36, 0xff, 0x49, 0xe1, 0xca, 0xff, 0xb3, 0xb3, 0x0, 0xff, 0xff, 0xff, 0x1, 0xff, 0x70, 0x0, 0x70, 0xff, 0x7e, 0x0, 0x7e, 0xff, 0x0, 0x34, 0x33, 0xff, 0x1, 0x7f, 0x7e, 0xff, 0x5c, 0x58, 0x0, 0xff, 0x82, 0x7e, 0x0, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff}, - Stride: 16, - Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, - }, - }, - { - "Clone YCbCr 420", - &image.YCbCr{ - Y: []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff}, - Cb: []uint8{0x01, 0xaa, 0x80, 0x80}, - Cr: []uint8{0x95, 0xb5, 0x80, 0x80}, - YStride: 4, CStride: 2, - SubsampleRatio: image.YCbCrSubsampleRatio420, - Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, - }, - &image.NRGBA{ - Pix: []uint8{0x69, 0x69, 0x0, 0xff, 0x86, 0x86, 0x0, 0xff, 0x67, 0x0, 0x67, 0xff, 0xfb, 0x7d, 0xfb, 0xff, 0xb3, 0xb3, 0x0, 0xff, 0xff, 0xff, 0x1, 0xff, 0x70, 0x0, 0x70, 0xff, 0x7e, 0x0, 0x7e, 0xff, 0xe, 0xe, 0xe, 0xff, 0x59, 0x59, 0x59, 0xff, 0x4b, 0x4b, 0x4b, 0xff, 0x71, 0x71, 0x71, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff}, - Stride: 16, - Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, - }, - }, - { - "Clone Paletted", - &image.Paletted{ - Rect: image.Rect(-1, -1, 5, 0), - Stride: 6 * 1, - Palette: color.Palette{ - color.NRGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xff}, - color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}, - color.NRGBA{R: 0x7f, G: 0x7f, B: 0x7f, A: 0xff}, - color.NRGBA{R: 0x7f, G: 0x00, B: 0x00, A: 0xff}, - color.NRGBA{R: 0x00, G: 0x7f, B: 0x00, A: 0xff}, - color.NRGBA{R: 0x00, G: 0x00, B: 0x7f, A: 0xff}, - }, - Pix: []uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5}, - }, - &image.NRGBA{ - Rect: image.Rect(0, 0, 6, 1), - Stride: 6 * 4, - Pix: []uint8{ - 0x00, 0x00, 0x00, 0xff, - 0xff, 0xff, 0xff, 0xff, - 0x7f, 0x7f, 0x7f, 0xff, - 0x7f, 0x00, 0x00, 0xff, - 0x00, 0x7f, 0x00, 0xff, - 0x00, 0x00, 0x7f, 0xff, - }, - }, - }, - } - - for _, d := range td { - got := Clone(d.src) - want := d.want - - delta := 0 - if _, ok := d.src.(*image.YCbCr); ok { - delta = 1 - } - - if !compareNRGBA(got, want, delta) { - t.Errorf("test [%s] failed: %#v", d.desc, got) - } - } -} - func TestFormats(t *testing.T) { formatNames := map[Format]string{ JPEG: "JPEG", diff --git a/vendor/github.com/disintegration/imaging/histogram.go b/vendor/github.com/disintegration/imaging/histogram.go index 3afcb7ae2..967ee6a25 100644 --- a/vendor/github.com/disintegration/imaging/histogram.go +++ b/vendor/github.com/disintegration/imaging/histogram.go @@ -28,7 +28,7 @@ func Histogram(img image.Image) [256]float64 { g := src.Pix[i+1] b := src.Pix[i+2] - var y float32 = 0.299*float32(r) + 0.587*float32(g) + 0.114*float32(b) + y := 0.299*float32(r) + 0.587*float32(g) + 0.114*float32(b) histogram[int(y+0.5)]++ total++ diff --git a/vendor/github.com/disintegration/imaging/resize.go b/vendor/github.com/disintegration/imaging/resize.go index 1659bbce8..cd5bfb5d8 100644 --- a/vendor/github.com/disintegration/imaging/resize.go +++ b/vendor/github.com/disintegration/imaging/resize.go @@ -19,6 +19,7 @@ func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) [][]indexWei ru := math.Ceil(scale * filter.Support) out := make([][]indexWeight, dstSize) + tmp := make([]indexWeight, 0, dstSize*int(ru+2)*2) for v := 0; v < dstSize; v++ { fu := (float64(v)+0.5)*du - 0.5 @@ -37,14 +38,17 @@ func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) [][]indexWei w := filter.Kernel((float64(u) - fu) / scale) if w != 0 { sum += w - out[v] = append(out[v], indexWeight{index: u, weight: w}) + tmp = append(tmp, indexWeight{index: u, weight: w}) } } if sum != 0 { - for i := range out[v] { - out[v][i].weight /= sum + for i := range tmp { + tmp[i].weight /= sum } } + + out[v] = tmp + tmp = tmp[len(tmp):] } return out diff --git a/vendor/github.com/disintegration/imaging/transform.go b/vendor/github.com/disintegration/imaging/transform.go index 43668dfd7..94d72d556 100644 --- a/vendor/github.com/disintegration/imaging/transform.go +++ b/vendor/github.com/disintegration/imaging/transform.go @@ -235,7 +235,7 @@ func Rotate(img image.Image, angle float64, bgColor color.Color) *image.NRGBA { dstYOff := float64(dstH)/2 - 0.5 bgColorNRGBA := color.NRGBAModel.Convert(bgColor).(color.NRGBA) - sin, cos := math.Sincos(math.Pi * float64(angle) / 180) + sin, cos := math.Sincos(math.Pi * angle / 180) parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { @@ -259,7 +259,7 @@ func rotatedSize(w, h int, angle float64) (int, int) { return 0, 0 } - sin, cos := math.Sincos(math.Pi * float64(angle) / 180) + sin, cos := math.Sincos(math.Pi * angle / 180) x1, y1 := rotatePoint(float64(w-1), 0, sin, cos) x2, y2 := rotatePoint(float64(w-1), float64(h-1), sin, cos) x3, y3 := rotatePoint(0, float64(h-1), sin, cos) diff --git a/vendor/github.com/go-ini/ini/.github/ISSUE_TEMPLATE.md b/vendor/github.com/go-ini/ini/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..849f69f4b --- /dev/null +++ b/vendor/github.com/go-ini/ini/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,5 @@ +### Please give general description of the problem + +### Please provide code snippets to reproduce the problem described above + +### Do you have any suggestion to fix the problem?
\ No newline at end of file diff --git a/vendor/github.com/go-ini/ini/.github/PULL_REQUEST_TEMPLATE.md b/vendor/github.com/go-ini/ini/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..b4565aeb5 --- /dev/null +++ b/vendor/github.com/go-ini/ini/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,3 @@ +### What problem should be fixed? + +### Have you added test cases to catch the problem? diff --git a/vendor/github.com/go-ini/ini/.gitignore b/vendor/github.com/go-ini/ini/.gitignore index c5203bf6e..12411127b 100644 --- a/vendor/github.com/go-ini/ini/.gitignore +++ b/vendor/github.com/go-ini/ini/.gitignore @@ -3,3 +3,4 @@ ini.sublime-project ini.sublime-workspace testdata/conf_reflect.ini .idea +/.vscode diff --git a/vendor/github.com/go-ini/ini/.travis.yml b/vendor/github.com/go-ini/ini/.travis.yml index d9d1b8ffa..b097527e1 100644 --- a/vendor/github.com/go-ini/ini/.travis.yml +++ b/vendor/github.com/go-ini/ini/.travis.yml @@ -5,9 +5,11 @@ go: - 1.6.x - 1.7.x - 1.8.x - - master + - 1.9.x script: - go get golang.org/x/tools/cmd/cover - go get github.com/smartystreets/goconvey + - mkdir -p $HOME/gopath/src/gopkg.in + - ln -s $HOME/gopath/src/github.com/go-ini/ini $HOME/gopath/src/gopkg.in/ini.v1 - go test -v -cover -race diff --git a/vendor/github.com/go-ini/ini/LICENSE b/vendor/github.com/go-ini/ini/LICENSE index 37ec93a14..d361bbcdf 100644 --- a/vendor/github.com/go-ini/ini/LICENSE +++ b/vendor/github.com/go-ini/ini/LICENSE @@ -176,7 +176,7 @@ 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] + Copyright 2014 Unknwon Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/vendor/github.com/go-ini/ini/Makefile b/vendor/github.com/go-ini/ini/Makefile index ac034e525..1316911d2 100644 --- a/vendor/github.com/go-ini/ini/Makefile +++ b/vendor/github.com/go-ini/ini/Makefile @@ -1,4 +1,4 @@ -.PHONY: build test bench vet +.PHONY: build test bench vet coverage build: vet bench @@ -10,3 +10,6 @@ bench: vet: go vet + +coverage: + go test -coverprofile=c.out && go tool cover -html=c.out && rm c.out
\ No newline at end of file diff --git a/vendor/github.com/go-ini/ini/README.md b/vendor/github.com/go-ini/ini/README.md index e67d51f32..f4ff27cd3 100644 --- a/vendor/github.com/go-ini/ini/README.md +++ b/vendor/github.com/go-ini/ini/README.md @@ -101,7 +101,7 @@ skip-name-resolve By default, this is considered as missing value. But if you know you're going to deal with those cases, you can assign advanced load options: ```go -cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, "my.cnf")) +cfg, err := ini.LoadSources(ini.LoadOptions{AllowBooleanKeys: true}, "my.cnf")) ``` The value of those keys are always `true`, and when you save to a file, it will keep in the same foramt as you read. @@ -125,7 +125,7 @@ If you want to save a value with `#` or `;`, please quote them with ``` ` ``` or Alternatively, you can use following `LoadOptions` to completely ignore inline comments: ```go -cfg, err := LoadSources(LoadOptions{IgnoreInlineComment: true}, "app.ini")) +cfg, err := ini.LoadSources(ini.LoadOptions{IgnoreInlineComment: true}, "app.ini")) ``` ### Working with sections @@ -329,6 +329,20 @@ foo = "some value" // foo: some value bar = 'some value' // bar: some value ``` +Sometimes you downloaded file from [Crowdin](https://crowdin.com/) has values like the following (value is surrounded by double quotes and quotes in the value are escaped): + +```ini +create_repo="created repository <a href=\"%s\">%s</a>" +``` + +How do you transform this to regular format automatically? + +```go +cfg, err := ini.LoadSources(ini.LoadOptions{UnescapeValueDoubleQuotes: true}, "en-US.ini")) +cfg.Section("<name of your section>").Key("create_repo").String() +// You got: created repository <a href="%s">%s</a> +``` + That's all? Hmm, no. #### Helper methods of working with values @@ -480,7 +494,7 @@ cfg.Section("package.sub").ParentKeys() // ["CLONE_URL"] Sometimes, you have sections that do not contain key-value pairs but raw content, to handle such case, you can use `LoadOptions.UnparsableSections`: ```go -cfg, err := LoadSources(LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS] +cfg, err := ini.LoadSources(ini.LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS] <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`)) body := cfg.Section("COMMENTS").Body() @@ -573,7 +587,7 @@ Why not? ```go type Embeded struct { - Dates []time.Time `delim:"|"` + Dates []time.Time `delim:"|" comment:"Time data"` Places []string `ini:"places,omitempty"` None []int `ini:",omitempty"` } @@ -581,10 +595,10 @@ type Embeded struct { type Author struct { Name string `ini:"NAME"` Male bool - Age int + Age int `comment:"Author's age"` GPA float64 NeverMind string `ini:"-"` - *Embeded + *Embeded `comment:"Embeded section"` } func main() { @@ -605,10 +619,13 @@ So, what do I get? ```ini NAME = Unknwon Male = true +; Author's age Age = 21 GPA = 2.8 +; Embeded section [Embeded] +; Time data Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00 places = HangZhou,Boston ``` diff --git a/vendor/github.com/go-ini/ini/README_ZH.md b/vendor/github.com/go-ini/ini/README_ZH.md index 0cf419449..69aefef12 100644 --- a/vendor/github.com/go-ini/ini/README_ZH.md +++ b/vendor/github.com/go-ini/ini/README_ZH.md @@ -94,7 +94,7 @@ skip-name-resolve 默认情况下这被认为是缺失值而无法完成解析,但可以通过高级的加载选项对它们进行处理: ```go -cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, "my.cnf")) +cfg, err := ini.LoadSources(ini.LoadOptions{AllowBooleanKeys: true}, "my.cnf")) ``` 这些键的值永远为 `true`,且在保存到文件时也只会输出键名。 @@ -118,7 +118,7 @@ key, err := sec.NewBooleanKey("skip-host-cache") 除此之外,您还可以通过 `LoadOptions` 完全忽略行内注释: ```go -cfg, err := LoadSources(LoadOptions{IgnoreInlineComment: true}, "app.ini")) +cfg, err := ini.LoadSources(ini.LoadOptions{IgnoreInlineComment: true}, "app.ini")) ``` ### 操作分区(Section) @@ -322,6 +322,20 @@ foo = "some value" // foo: some value bar = 'some value' // bar: some value ``` +有时您会获得像从 [Crowdin](https://crowdin.com/) 网站下载的文件那样具有特殊格式的值(值使用双引号括起来,内部的双引号被转义): + +```ini +create_repo="创建了仓库 <a href=\"%s\">%s</a>" +``` + +那么,怎么自动地将这类值进行处理呢? + +```go +cfg, err := ini.LoadSources(ini.LoadOptions{UnescapeValueDoubleQuotes: true}, "en-US.ini")) +cfg.Section("<name of your section>").Key("create_repo").String() +// You got: 创建了仓库 <a href="%s">%s</a> +``` + 这就是全部了?哈哈,当然不是。 #### 操作键值的辅助方法 @@ -473,7 +487,7 @@ cfg.Section("package.sub").ParentKeys() // ["CLONE_URL"] 如果遇到一些比较特殊的分区,它们不包含常见的键值对,而是没有固定格式的纯文本,则可以使用 `LoadOptions.UnparsableSections` 进行处理: ```go -cfg, err := LoadSources(LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS] +cfg, err := LoadSources(ini.LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS] <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`)) body := cfg.Section("COMMENTS").Body() @@ -564,7 +578,7 @@ p := &Person{ ```go type Embeded struct { - Dates []time.Time `delim:"|"` + Dates []time.Time `delim:"|" comment:"Time data"` Places []string `ini:"places,omitempty"` None []int `ini:",omitempty"` } @@ -572,10 +586,10 @@ type Embeded struct { type Author struct { Name string `ini:"NAME"` Male bool - Age int + Age int `comment:"Author's age"` GPA float64 NeverMind string `ini:"-"` - *Embeded + *Embeded `comment:"Embeded section"` } func main() { @@ -596,10 +610,13 @@ func main() { ```ini NAME = Unknwon Male = true +; Author's age Age = 21 GPA = 2.8 +; Embeded section [Embeded] +; Time data Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00 places = HangZhou,Boston ``` diff --git a/vendor/github.com/go-ini/ini/bench_test.go b/vendor/github.com/go-ini/ini/bench_test.go new file mode 100644 index 000000000..fc1802469 --- /dev/null +++ b/vendor/github.com/go-ini/ini/bench_test.go @@ -0,0 +1,118 @@ +// Copyright 2017 Unknwon +// +// 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 ini_test + +import ( + "testing" + + "gopkg.in/ini.v1" +) + +func newTestFile(block bool) *ini.File { + c, _ := ini.Load([]byte(_CONF_DATA)) + c.BlockMode = block + return c +} + +func Benchmark_Key_Value(b *testing.B) { + c := newTestFile(true) + for i := 0; i < b.N; i++ { + c.Section("").Key("NAME").Value() + } +} + +func Benchmark_Key_Value_NonBlock(b *testing.B) { + c := newTestFile(false) + for i := 0; i < b.N; i++ { + c.Section("").Key("NAME").Value() + } +} + +func Benchmark_Key_Value_ViaSection(b *testing.B) { + c := newTestFile(true) + sec := c.Section("") + for i := 0; i < b.N; i++ { + sec.Key("NAME").Value() + } +} + +func Benchmark_Key_Value_ViaSection_NonBlock(b *testing.B) { + c := newTestFile(false) + sec := c.Section("") + for i := 0; i < b.N; i++ { + sec.Key("NAME").Value() + } +} + +func Benchmark_Key_Value_Direct(b *testing.B) { + c := newTestFile(true) + key := c.Section("").Key("NAME") + for i := 0; i < b.N; i++ { + key.Value() + } +} + +func Benchmark_Key_Value_Direct_NonBlock(b *testing.B) { + c := newTestFile(false) + key := c.Section("").Key("NAME") + for i := 0; i < b.N; i++ { + key.Value() + } +} + +func Benchmark_Key_String(b *testing.B) { + c := newTestFile(true) + for i := 0; i < b.N; i++ { + _ = c.Section("").Key("NAME").String() + } +} + +func Benchmark_Key_String_NonBlock(b *testing.B) { + c := newTestFile(false) + for i := 0; i < b.N; i++ { + _ = c.Section("").Key("NAME").String() + } +} + +func Benchmark_Key_String_ViaSection(b *testing.B) { + c := newTestFile(true) + sec := c.Section("") + for i := 0; i < b.N; i++ { + _ = sec.Key("NAME").String() + } +} + +func Benchmark_Key_String_ViaSection_NonBlock(b *testing.B) { + c := newTestFile(false) + sec := c.Section("") + for i := 0; i < b.N; i++ { + _ = sec.Key("NAME").String() + } +} + +func Benchmark_Key_SetValue(b *testing.B) { + c := newTestFile(true) + for i := 0; i < b.N; i++ { + c.Section("").Key("NAME").SetValue("10") + } +} + +func Benchmark_Key_SetValue_VisSection(b *testing.B) { + c := newTestFile(true) + sec := c.Section("") + for i := 0; i < b.N; i++ { + sec.Key("NAME").SetValue("10") + } +} diff --git a/vendor/github.com/go-ini/ini/file.go b/vendor/github.com/go-ini/ini/file.go new file mode 100644 index 000000000..93ac50836 --- /dev/null +++ b/vendor/github.com/go-ini/ini/file.go @@ -0,0 +1,392 @@ +// Copyright 2017 Unknwon +// +// 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 ini + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "strings" + "sync" +) + +// File represents a combination of a or more INI file(s) in memory. +type File struct { + options LoadOptions + dataSources []dataSource + + // Should make things safe, but sometimes doesn't matter. + BlockMode bool + lock sync.RWMutex + + // To keep data in order. + sectionList []string + // Actual data is stored here. + sections map[string]*Section + + NameMapper + ValueMapper +} + +// newFile initializes File object with given data sources. +func newFile(dataSources []dataSource, opts LoadOptions) *File { + return &File{ + BlockMode: true, + dataSources: dataSources, + sections: make(map[string]*Section), + sectionList: make([]string, 0, 10), + options: opts, + } +} + +// Empty returns an empty file object. +func Empty() *File { + // Ignore error here, we sure our data is good. + f, _ := Load([]byte("")) + return f +} + +// NewSection creates a new section. +func (f *File) NewSection(name string) (*Section, error) { + if len(name) == 0 { + return nil, errors.New("error creating new section: empty section name") + } else if f.options.Insensitive && name != DEFAULT_SECTION { + name = strings.ToLower(name) + } + + if f.BlockMode { + f.lock.Lock() + defer f.lock.Unlock() + } + + if inSlice(name, f.sectionList) { + return f.sections[name], nil + } + + f.sectionList = append(f.sectionList, name) + f.sections[name] = newSection(f, name) + return f.sections[name], nil +} + +// NewRawSection creates a new section with an unparseable body. +func (f *File) NewRawSection(name, body string) (*Section, error) { + section, err := f.NewSection(name) + if err != nil { + return nil, err + } + + section.isRawSection = true + section.rawBody = body + return section, nil +} + +// NewSections creates a list of sections. +func (f *File) NewSections(names ...string) (err error) { + for _, name := range names { + if _, err = f.NewSection(name); err != nil { + return err + } + } + return nil +} + +// GetSection returns section by given name. +func (f *File) GetSection(name string) (*Section, error) { + if len(name) == 0 { + name = DEFAULT_SECTION + } + if f.options.Insensitive { + name = strings.ToLower(name) + } + + if f.BlockMode { + f.lock.RLock() + defer f.lock.RUnlock() + } + + sec := f.sections[name] + if sec == nil { + return nil, fmt.Errorf("section '%s' does not exist", name) + } + return sec, nil +} + +// Section assumes named section exists and returns a zero-value when not. +func (f *File) Section(name string) *Section { + sec, err := f.GetSection(name) + if err != nil { + // Note: It's OK here because the only possible error is empty section name, + // but if it's empty, this piece of code won't be executed. + sec, _ = f.NewSection(name) + return sec + } + return sec +} + +// Section returns list of Section. +func (f *File) Sections() []*Section { + sections := make([]*Section, len(f.sectionList)) + for i := range f.sectionList { + sections[i] = f.Section(f.sectionList[i]) + } + return sections +} + +// ChildSections returns a list of child sections of given section name. +func (f *File) ChildSections(name string) []*Section { + return f.Section(name).ChildSections() +} + +// SectionStrings returns list of section names. +func (f *File) SectionStrings() []string { + list := make([]string, len(f.sectionList)) + copy(list, f.sectionList) + return list +} + +// DeleteSection deletes a section. +func (f *File) DeleteSection(name string) { + if f.BlockMode { + f.lock.Lock() + defer f.lock.Unlock() + } + + if len(name) == 0 { + name = DEFAULT_SECTION + } + + for i, s := range f.sectionList { + if s == name { + f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...) + delete(f.sections, name) + return + } + } +} + +func (f *File) reload(s dataSource) error { + r, err := s.ReadCloser() + if err != nil { + return err + } + defer r.Close() + + return f.parse(r) +} + +// Reload reloads and parses all data sources. +func (f *File) Reload() (err error) { + for _, s := range f.dataSources { + if err = f.reload(s); err != nil { + // In loose mode, we create an empty default section for nonexistent files. + if os.IsNotExist(err) && f.options.Loose { + f.parse(bytes.NewBuffer(nil)) + continue + } + return err + } + } + return nil +} + +// Append appends one or more data sources and reloads automatically. +func (f *File) Append(source interface{}, others ...interface{}) error { + ds, err := parseDataSource(source) + if err != nil { + return err + } + f.dataSources = append(f.dataSources, ds) + for _, s := range others { + ds, err = parseDataSource(s) + if err != nil { + return err + } + f.dataSources = append(f.dataSources, ds) + } + return f.Reload() +} + +func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) { + equalSign := "=" + if PrettyFormat { + equalSign = " = " + } + + // Use buffer to make sure target is safe until finish encoding. + buf := bytes.NewBuffer(nil) + for i, sname := range f.sectionList { + sec := f.Section(sname) + if len(sec.Comment) > 0 { + if sec.Comment[0] != '#' && sec.Comment[0] != ';' { + sec.Comment = "; " + sec.Comment + } else { + sec.Comment = sec.Comment[:1] + " " + strings.TrimSpace(sec.Comment[1:]) + } + if _, err := buf.WriteString(sec.Comment + LineBreak); err != nil { + return nil, err + } + } + + if i > 0 || DefaultHeader { + if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil { + return nil, err + } + } else { + // Write nothing if default section is empty + if len(sec.keyList) == 0 { + continue + } + } + + if sec.isRawSection { + if _, err := buf.WriteString(sec.rawBody); err != nil { + return nil, err + } + + if PrettySection { + // Put a line between sections + if _, err := buf.WriteString(LineBreak); err != nil { + return nil, err + } + } + continue + } + + // Count and generate alignment length and buffer spaces using the + // longest key. Keys may be modifed if they contain certain characters so + // we need to take that into account in our calculation. + alignLength := 0 + if PrettyFormat { + for _, kname := range sec.keyList { + keyLength := len(kname) + // First case will surround key by ` and second by """ + if strings.ContainsAny(kname, "\"=:") { + keyLength += 2 + } else if strings.Contains(kname, "`") { + keyLength += 6 + } + + if keyLength > alignLength { + alignLength = keyLength + } + } + } + alignSpaces := bytes.Repeat([]byte(" "), alignLength) + + KEY_LIST: + for _, kname := range sec.keyList { + key := sec.Key(kname) + if len(key.Comment) > 0 { + if len(indent) > 0 && sname != DEFAULT_SECTION { + buf.WriteString(indent) + } + if key.Comment[0] != '#' && key.Comment[0] != ';' { + key.Comment = "; " + key.Comment + } else { + key.Comment = key.Comment[:1] + " " + strings.TrimSpace(key.Comment[1:]) + } + if _, err := buf.WriteString(key.Comment + LineBreak); err != nil { + return nil, err + } + } + + if len(indent) > 0 && sname != DEFAULT_SECTION { + buf.WriteString(indent) + } + + switch { + case key.isAutoIncrement: + kname = "-" + case strings.ContainsAny(kname, "\"=:"): + kname = "`" + kname + "`" + case strings.Contains(kname, "`"): + kname = `"""` + kname + `"""` + } + + for _, val := range key.ValueWithShadows() { + if _, err := buf.WriteString(kname); err != nil { + return nil, err + } + + if key.isBooleanType { + if kname != sec.keyList[len(sec.keyList)-1] { + buf.WriteString(LineBreak) + } + continue KEY_LIST + } + + // Write out alignment spaces before "=" sign + if PrettyFormat { + buf.Write(alignSpaces[:alignLength-len(kname)]) + } + + // In case key value contains "\n", "`", "\"", "#" or ";" + if strings.ContainsAny(val, "\n`") { + val = `"""` + val + `"""` + } else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") { + val = "`" + val + "`" + } + if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil { + return nil, err + } + } + } + + if PrettySection { + // Put a line between sections + if _, err := buf.WriteString(LineBreak); err != nil { + return nil, err + } + } + } + + return buf, nil +} + +// WriteToIndent writes content into io.Writer with given indention. +// If PrettyFormat has been set to be true, +// it will align "=" sign with spaces under each section. +func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) { + buf, err := f.writeToBuffer(indent) + if err != nil { + return 0, err + } + return buf.WriteTo(w) +} + +// WriteTo writes file content into io.Writer. +func (f *File) WriteTo(w io.Writer) (int64, error) { + return f.WriteToIndent(w, "") +} + +// SaveToIndent writes content to file system with given value indention. +func (f *File) SaveToIndent(filename, indent string) error { + // Note: Because we are truncating with os.Create, + // so it's safer to save to a temporary file location and rename afte done. + buf, err := f.writeToBuffer(indent) + if err != nil { + return err + } + + return ioutil.WriteFile(filename, buf.Bytes(), 0666) +} + +// SaveTo writes content to file system. +func (f *File) SaveTo(filename string) error { + return f.SaveToIndent(filename, "") +} diff --git a/vendor/github.com/go-ini/ini/file_test.go b/vendor/github.com/go-ini/ini/file_test.go new file mode 100644 index 000000000..11612eb9a --- /dev/null +++ b/vendor/github.com/go-ini/ini/file_test.go @@ -0,0 +1,355 @@ +// Copyright 2017 Unknwon +// +// 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 ini_test + +import ( + "bytes" + "testing" + + . "github.com/smartystreets/goconvey/convey" + "gopkg.in/ini.v1" +) + +func TestEmpty(t *testing.T) { + Convey("Create an empty object", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + // Should only have the default section + So(len(f.Sections()), ShouldEqual, 1) + + // Default section should not contain any key + So(len(f.Section("").Keys()), ShouldBeZeroValue) + }) +} + +func TestFile_NewSection(t *testing.T) { + Convey("Create a new section", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + sec, err := f.NewSection("author") + So(err, ShouldBeNil) + So(sec, ShouldNotBeNil) + So(sec.Name(), ShouldEqual, "author") + + So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "author"}) + + Convey("With duplicated name", func() { + sec, err := f.NewSection("author") + So(err, ShouldBeNil) + So(sec, ShouldNotBeNil) + + // Does nothing if section already exists + So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "author"}) + }) + + Convey("With empty string", func() { + _, err := f.NewSection("") + So(err, ShouldNotBeNil) + }) + }) +} + +func TestFile_NewRawSection(t *testing.T) { + Convey("Create a new raw section", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000 +111111111111111111100000000000111000000000`) + So(err, ShouldBeNil) + So(sec, ShouldNotBeNil) + So(sec.Name(), ShouldEqual, "comments") + + So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "comments"}) + So(f.Section("comments").Body(), ShouldEqual, `1111111111111111111000000000000000001110000 +111111111111111111100000000000111000000000`) + + Convey("With duplicated name", func() { + sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000`) + So(err, ShouldBeNil) + So(sec, ShouldNotBeNil) + So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "comments"}) + + // Overwrite previous existed section + So(f.Section("comments").Body(), ShouldEqual, `1111111111111111111000000000000000001110000`) + }) + + Convey("With empty string", func() { + _, err := f.NewRawSection("", "") + So(err, ShouldNotBeNil) + }) + }) +} + +func TestFile_NewSections(t *testing.T) { + Convey("Create new sections", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + So(f.NewSections("package", "author"), ShouldBeNil) + So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "package", "author"}) + + Convey("With duplicated name", func() { + So(f.NewSections("author", "features"), ShouldBeNil) + + // Ignore section already exists + So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "package", "author", "features"}) + }) + + Convey("With empty string", func() { + So(f.NewSections("", ""), ShouldNotBeNil) + }) + }) +} + +func TestFile_GetSection(t *testing.T) { + Convey("Get a section", t, func() { + f, err := ini.Load(_FULL_CONF) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + sec, err := f.GetSection("author") + So(err, ShouldBeNil) + So(sec, ShouldNotBeNil) + So(sec.Name(), ShouldEqual, "author") + + Convey("Section not exists", func() { + _, err := f.GetSection("404") + So(err, ShouldNotBeNil) + }) + }) +} + +func TestFile_Section(t *testing.T) { + Convey("Get a section", t, func() { + f, err := ini.Load(_FULL_CONF) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + sec := f.Section("author") + So(sec, ShouldNotBeNil) + So(sec.Name(), ShouldEqual, "author") + + Convey("Section not exists", func() { + sec := f.Section("404") + So(sec, ShouldNotBeNil) + So(sec.Name(), ShouldEqual, "404") + }) + }) + + Convey("Get default section in lower case with insensitive load", t, func() { + f, err := ini.InsensitiveLoad([]byte(` +[default] +NAME = ini +VERSION = v1`)) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + So(f.Section("").Key("name").String(), ShouldEqual, "ini") + So(f.Section("").Key("version").String(), ShouldEqual, "v1") + }) +} + +func TestFile_Sections(t *testing.T) { + Convey("Get all sections", t, func() { + f, err := ini.Load(_FULL_CONF) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + secs := f.Sections() + names := []string{ini.DEFAULT_SECTION, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"} + So(len(secs), ShouldEqual, len(names)) + for i, name := range names { + So(secs[i].Name(), ShouldEqual, name) + } + }) +} + +func TestFile_ChildSections(t *testing.T) { + Convey("Get child sections by parent name", t, func() { + f, err := ini.Load([]byte(` +[node] +[node.biz1] +[node.biz2] +[node.biz3] +[node.bizN] +`)) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + children := f.ChildSections("node") + names := []string{"node.biz1", "node.biz2", "node.biz3", "node.bizN"} + So(len(children), ShouldEqual, len(names)) + for i, name := range names { + So(children[i].Name(), ShouldEqual, name) + } + }) +} + +func TestFile_SectionStrings(t *testing.T) { + Convey("Get all section names", t, func() { + f, err := ini.Load(_FULL_CONF) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + So(f.SectionStrings(), ShouldResemble, []string{ini.DEFAULT_SECTION, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"}) + }) +} + +func TestFile_DeleteSection(t *testing.T) { + Convey("Delete a section", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + f.NewSections("author", "package", "features") + f.DeleteSection("features") + f.DeleteSection("") + So(f.SectionStrings(), ShouldResemble, []string{"author", "package"}) + }) +} + +func TestFile_Append(t *testing.T) { + Convey("Append a data source", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + So(f.Append(_MINIMAL_CONF, []byte(` +[author] +NAME = Unknwon`)), ShouldBeNil) + + Convey("With bad input", func() { + So(f.Append(123), ShouldNotBeNil) + So(f.Append(_MINIMAL_CONF, 123), ShouldNotBeNil) + }) + }) +} + +func TestFile_WriteTo(t *testing.T) { + Convey("Write content to somewhere", t, func() { + f, err := ini.Load(_FULL_CONF) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + f.Section("author").Comment = `Information about package author +# Bio can be written in multiple lines.` + f.Section("author").Key("NAME").Comment = "This is author name" + f.Section("note").NewBooleanKey("boolean_key") + f.Section("note").NewKey("more", "notes") + + var buf bytes.Buffer + _, err = f.WriteTo(&buf) + So(err, ShouldBeNil) + So(buf.String(), ShouldEqual, `; Package name +NAME = ini +; Package version +VERSION = v1 +; Package import path +IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s + +; Information about package author +# Bio can be written in multiple lines. +[author] +; This is author name +NAME = Unknwon +E-MAIL = u@gogs.io +GITHUB = https://github.com/%(NAME)s +# Succeeding comment +BIO = """Gopher. +Coding addict. +Good man. +""" + +[package] +CLONE_URL = https://%(IMPORT_PATH)s + +[package.sub] +UNUSED_KEY = should be deleted + +[features] +- = Support read/write comments of keys and sections +- = Support auto-increment of key names +- = Support load multiple files to overwrite key values + +[types] +STRING = str +BOOL = true +BOOL_FALSE = false +FLOAT64 = 1.25 +INT = 10 +TIME = 2015-01-01T20:17:05Z +DURATION = 2h45m +UINT = 3 + +[array] +STRINGS = en, zh, de +FLOAT64S = 1.1, 2.2, 3.3 +INTS = 1, 2, 3 +UINTS = 1, 2, 3 +TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z + +[note] +empty_lines = next line is empty +boolean_key +more = notes + +; Comment before the section +; This is a comment for the section too +[comments] +; Comment before key +key = value +; This is a comment for key2 +key2 = value2 +key3 = "one", "two", "three" + +[string escapes] +key1 = value1, value2, value3 +key2 = value1\, value2 +key3 = val\ue1, value2 +key4 = value1\\, value\\\\2 +key5 = value1\,, value2 +key6 = aaa bbb\ and\ space ccc + +[advance] +value with quotes = some value +value quote2 again = some value +includes comment sign = `+"`"+"my#password"+"`"+` +includes comment sign2 = `+"`"+"my;password"+"`"+` +true = 2+3=5 +`+"`"+`1+1=2`+"`"+` = true +`+"`"+`6+1=7`+"`"+` = true +"""`+"`"+`5+5`+"`"+`""" = 10 +`+"`"+`"6+6"`+"`"+` = 12 +`+"`"+`7-2=4`+"`"+` = false +ADDRESS = """404 road, +NotFound, State, 50000""" +two_lines = how about continuation lines? +lots_of_lines = 1 2 3 4 + +`) + }) +} + +func TestFile_SaveTo(t *testing.T) { + Convey("Write content to somewhere", t, func() { + f, err := ini.Load(_FULL_CONF) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + So(f.SaveTo("testdata/conf_out.ini"), ShouldBeNil) + So(f.SaveToIndent("testdata/conf_out.ini", "\t"), ShouldBeNil) + }) +} diff --git a/vendor/github.com/go-ini/ini/ini.go b/vendor/github.com/go-ini/ini/ini.go index 7f3c4d1ed..508d60c19 100644 --- a/vendor/github.com/go-ini/ini/ini.go +++ b/vendor/github.com/go-ini/ini/ini.go @@ -17,15 +17,12 @@ package ini import ( "bytes" - "errors" "fmt" "io" "io/ioutil" "os" "regexp" "runtime" - "strings" - "sync" ) const ( @@ -35,7 +32,7 @@ const ( // Maximum allowed depth when recursively substituing variable names. _DEPTH_VALUES = 99 - _VERSION = "1.28.2" + _VERSION = "1.30.3" ) // Version returns current package version literal. @@ -92,18 +89,6 @@ func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) { return os.Open(s.name) } -type bytesReadCloser struct { - reader io.Reader -} - -func (rc *bytesReadCloser) Read(p []byte) (n int, err error) { - return rc.reader.Read(p) -} - -func (rc *bytesReadCloser) Close() error { - return nil -} - // sourceData represents an object that contains content in memory. type sourceData struct { data []byte @@ -122,38 +107,6 @@ func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) { return s.reader, nil } -// File represents a combination of a or more INI file(s) in memory. -type File struct { - // Should make things safe, but sometimes doesn't matter. - BlockMode bool - // Make sure data is safe in multiple goroutines. - lock sync.RWMutex - - // Allow combination of multiple data sources. - dataSources []dataSource - // Actual data is stored here. - sections map[string]*Section - - // To keep data in order. - sectionList []string - - options LoadOptions - - NameMapper - ValueMapper -} - -// newFile initializes File object with given data sources. -func newFile(dataSources []dataSource, opts LoadOptions) *File { - return &File{ - BlockMode: true, - dataSources: dataSources, - sections: make(map[string]*Section), - sectionList: make([]string, 0, 10), - options: opts, - } -} - func parseDataSource(source interface{}) (dataSource, error) { switch s := source.(type) { case string: @@ -181,6 +134,8 @@ type LoadOptions struct { AllowBooleanKeys bool // AllowShadows indicates whether to keep track of keys with same name under same section. AllowShadows bool + // UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value" + UnescapeValueDoubleQuotes bool // Some INI formats allow group blocks that store a block of raw content that doesn't otherwise // conform to key/value pairs. Specify the names of those blocks here. UnparseableSections []string @@ -229,328 +184,3 @@ func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) { func ShadowLoad(source interface{}, others ...interface{}) (*File, error) { return LoadSources(LoadOptions{AllowShadows: true}, source, others...) } - -// Empty returns an empty file object. -func Empty() *File { - // Ignore error here, we sure our data is good. - f, _ := Load([]byte("")) - return f -} - -// NewSection creates a new section. -func (f *File) NewSection(name string) (*Section, error) { - if len(name) == 0 { - return nil, errors.New("error creating new section: empty section name") - } else if f.options.Insensitive && name != DEFAULT_SECTION { - name = strings.ToLower(name) - } - - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - if inSlice(name, f.sectionList) { - return f.sections[name], nil - } - - f.sectionList = append(f.sectionList, name) - f.sections[name] = newSection(f, name) - return f.sections[name], nil -} - -// NewRawSection creates a new section with an unparseable body. -func (f *File) NewRawSection(name, body string) (*Section, error) { - section, err := f.NewSection(name) - if err != nil { - return nil, err - } - - section.isRawSection = true - section.rawBody = body - return section, nil -} - -// NewSections creates a list of sections. -func (f *File) NewSections(names ...string) (err error) { - for _, name := range names { - if _, err = f.NewSection(name); err != nil { - return err - } - } - return nil -} - -// GetSection returns section by given name. -func (f *File) GetSection(name string) (*Section, error) { - if len(name) == 0 { - name = DEFAULT_SECTION - } else if f.options.Insensitive { - name = strings.ToLower(name) - } - - if f.BlockMode { - f.lock.RLock() - defer f.lock.RUnlock() - } - - sec := f.sections[name] - if sec == nil { - return nil, fmt.Errorf("section '%s' does not exist", name) - } - return sec, nil -} - -// Section assumes named section exists and returns a zero-value when not. -func (f *File) Section(name string) *Section { - sec, err := f.GetSection(name) - if err != nil { - // Note: It's OK here because the only possible error is empty section name, - // but if it's empty, this piece of code won't be executed. - sec, _ = f.NewSection(name) - return sec - } - return sec -} - -// Section returns list of Section. -func (f *File) Sections() []*Section { - sections := make([]*Section, len(f.sectionList)) - for i := range f.sectionList { - sections[i] = f.Section(f.sectionList[i]) - } - return sections -} - -// ChildSections returns a list of child sections of given section name. -func (f *File) ChildSections(name string) []*Section { - return f.Section(name).ChildSections() -} - -// SectionStrings returns list of section names. -func (f *File) SectionStrings() []string { - list := make([]string, len(f.sectionList)) - copy(list, f.sectionList) - return list -} - -// DeleteSection deletes a section. -func (f *File) DeleteSection(name string) { - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - if len(name) == 0 { - name = DEFAULT_SECTION - } - - for i, s := range f.sectionList { - if s == name { - f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...) - delete(f.sections, name) - return - } - } -} - -func (f *File) reload(s dataSource) error { - r, err := s.ReadCloser() - if err != nil { - return err - } - defer r.Close() - - return f.parse(r) -} - -// Reload reloads and parses all data sources. -func (f *File) Reload() (err error) { - for _, s := range f.dataSources { - if err = f.reload(s); err != nil { - // In loose mode, we create an empty default section for nonexistent files. - if os.IsNotExist(err) && f.options.Loose { - f.parse(bytes.NewBuffer(nil)) - continue - } - return err - } - } - return nil -} - -// Append appends one or more data sources and reloads automatically. -func (f *File) Append(source interface{}, others ...interface{}) error { - ds, err := parseDataSource(source) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - for _, s := range others { - ds, err = parseDataSource(s) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - } - return f.Reload() -} - -func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) { - equalSign := "=" - if PrettyFormat { - equalSign = " = " - } - - // Use buffer to make sure target is safe until finish encoding. - buf := bytes.NewBuffer(nil) - for i, sname := range f.sectionList { - sec := f.Section(sname) - if len(sec.Comment) > 0 { - if sec.Comment[0] != '#' && sec.Comment[0] != ';' { - sec.Comment = "; " + sec.Comment - } - if _, err := buf.WriteString(sec.Comment + LineBreak); err != nil { - return nil, err - } - } - - if i > 0 || DefaultHeader { - if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil { - return nil, err - } - } else { - // Write nothing if default section is empty - if len(sec.keyList) == 0 { - continue - } - } - - if sec.isRawSection { - if _, err := buf.WriteString(sec.rawBody); err != nil { - return nil, err - } - continue - } - - // Count and generate alignment length and buffer spaces using the - // longest key. Keys may be modifed if they contain certain characters so - // we need to take that into account in our calculation. - alignLength := 0 - if PrettyFormat { - for _, kname := range sec.keyList { - keyLength := len(kname) - // First case will surround key by ` and second by """ - if strings.ContainsAny(kname, "\"=:") { - keyLength += 2 - } else if strings.Contains(kname, "`") { - keyLength += 6 - } - - if keyLength > alignLength { - alignLength = keyLength - } - } - } - alignSpaces := bytes.Repeat([]byte(" "), alignLength) - - KEY_LIST: - for _, kname := range sec.keyList { - key := sec.Key(kname) - if len(key.Comment) > 0 { - if len(indent) > 0 && sname != DEFAULT_SECTION { - buf.WriteString(indent) - } - if key.Comment[0] != '#' && key.Comment[0] != ';' { - key.Comment = "; " + key.Comment - } - if _, err := buf.WriteString(key.Comment + LineBreak); err != nil { - return nil, err - } - } - - if len(indent) > 0 && sname != DEFAULT_SECTION { - buf.WriteString(indent) - } - - switch { - case key.isAutoIncrement: - kname = "-" - case strings.ContainsAny(kname, "\"=:"): - kname = "`" + kname + "`" - case strings.Contains(kname, "`"): - kname = `"""` + kname + `"""` - } - - for _, val := range key.ValueWithShadows() { - if _, err := buf.WriteString(kname); err != nil { - return nil, err - } - - if key.isBooleanType { - if kname != sec.keyList[len(sec.keyList)-1] { - buf.WriteString(LineBreak) - } - continue KEY_LIST - } - - // Write out alignment spaces before "=" sign - if PrettyFormat { - buf.Write(alignSpaces[:alignLength-len(kname)]) - } - - // In case key value contains "\n", "`", "\"", "#" or ";" - if strings.ContainsAny(val, "\n`") { - val = `"""` + val + `"""` - } else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") { - val = "`" + val + "`" - } - if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil { - return nil, err - } - } - } - - if PrettySection { - // Put a line between sections - if _, err := buf.WriteString(LineBreak); err != nil { - return nil, err - } - } - } - - return buf, nil -} - -// WriteToIndent writes content into io.Writer with given indention. -// If PrettyFormat has been set to be true, -// it will align "=" sign with spaces under each section. -func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) { - buf, err := f.writeToBuffer(indent) - if err != nil { - return 0, err - } - return buf.WriteTo(w) -} - -// WriteTo writes file content into io.Writer. -func (f *File) WriteTo(w io.Writer) (int64, error) { - return f.WriteToIndent(w, "") -} - -// SaveToIndent writes content to file system with given value indention. -func (f *File) SaveToIndent(filename, indent string) error { - // Note: Because we are truncating with os.Create, - // so it's safer to save to a temporary file location and rename afte done. - buf, err := f.writeToBuffer(indent) - if err != nil { - return err - } - - return ioutil.WriteFile(filename, buf.Bytes(), 0666) -} - -// SaveTo writes content to file system. -func (f *File) SaveTo(filename string) error { - return f.SaveToIndent(filename, "") -} diff --git a/vendor/github.com/go-ini/ini/ini_internal_test.go b/vendor/github.com/go-ini/ini/ini_internal_test.go new file mode 100644 index 000000000..257ef1ebb --- /dev/null +++ b/vendor/github.com/go-ini/ini/ini_internal_test.go @@ -0,0 +1,35 @@ +// Copyright 2017 Unknwon +// +// 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 ini + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func Test_Version(t *testing.T) { + Convey("Get version", t, func() { + So(Version(), ShouldEqual, _VERSION) + }) +} + +func Test_isSlice(t *testing.T) { + Convey("Check if a string is in the slice", t, func() { + ss := []string{"a", "b", "c"} + So(inSlice("a", ss), ShouldBeTrue) + So(inSlice("d", ss), ShouldBeFalse) + }) +} diff --git a/vendor/github.com/go-ini/ini/ini_test.go b/vendor/github.com/go-ini/ini/ini_test.go index b3dd217c6..7a1efe4ec 100644 --- a/vendor/github.com/go-ini/ini/ini_test.go +++ b/vendor/github.com/go-ini/ini/ini_test.go @@ -12,480 +12,312 @@ // License for the specific language governing permissions and limitations // under the License. -package ini +package ini_test import ( "bytes" "io/ioutil" - "strings" "testing" - "time" . "github.com/smartystreets/goconvey/convey" + "gopkg.in/ini.v1" ) -func Test_Version(t *testing.T) { - Convey("Get version", t, func() { - So(Version(), ShouldEqual, _VERSION) - }) -} - -const _CONF_DATA = ` -; Package name -NAME = ini -; Package version -VERSION = v1 -; Package import path -IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s +const ( + _CONF_DATA = ` + ; Package name + NAME = ini + ; Package version + VERSION = v1 + ; Package import path + IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s + + # Information about package author + # Bio can be written in multiple lines. + [author] + NAME = Unknwon ; Succeeding comment + E-MAIL = fake@localhost + GITHUB = https://github.com/%(NAME)s + BIO = """Gopher. + Coding addict. + Good man. + """ # Succeeding comment` + _MINIMAL_CONF = "testdata/minimal.ini" + _FULL_CONF = "testdata/full.ini" + _NOT_FOUND_CONF = "testdata/404.ini" +) -# Information about package author -# Bio can be written in multiple lines. +func TestLoad(t *testing.T) { + Convey("Load from good data sources", t, func() { + f, err := ini.Load([]byte(` +NAME = ini +VERSION = v1 +IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s`), + "testdata/minimal.ini", + ioutil.NopCloser(bytes.NewReader([]byte(` [author] -NAME = Unknwon ; Succeeding comment -E-MAIL = fake@localhost -GITHUB = https://github.com/%(NAME)s -BIO = """Gopher. -Coding addict. -Good man. -""" # Succeeding comment - -[package] -CLONE_URL = https://%(IMPORT_PATH)s - -[package.sub] -UNUSED_KEY = should be deleted - -[features] --: Support read/write comments of keys and sections --: Support auto-increment of key names --: Support load multiple files to overwrite key values - -[types] -STRING = str -BOOL = true -BOOL_FALSE = false -FLOAT64 = 1.25 -INT = 10 -TIME = 2015-01-01T20:17:05Z -DURATION = 2h45m -UINT = 3 - -[array] -STRINGS = en, zh, de -FLOAT64S = 1.1, 2.2, 3.3 -INTS = 1, 2, 3 -UINTS = 1, 2, 3 -TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z - -[note] -empty_lines = next line is empty\ - -; Comment before the section -[comments] ; This is a comment for the section too -; Comment before key -key = "value" -key2 = "value2" ; This is a comment for key2 -key3 = "one", "two", "three" - -[advance] -value with quotes = "some value" -value quote2 again = 'some value' -includes comment sign = ` + "`" + "my#password" + "`" + ` -includes comment sign2 = ` + "`" + "my;password" + "`" + ` -true = 2+3=5 -"1+1=2" = true -"""6+1=7""" = true -"""` + "`" + `5+5` + "`" + `""" = 10 -` + "`" + `"6+6"` + "`" + ` = 12 -` + "`" + `7-2=4` + "`" + ` = false -ADDRESS = ` + "`" + `404 road, -NotFound, State, 50000` + "`" + ` - -two_lines = how about \ - continuation lines? -lots_of_lines = 1 \ - 2 \ - 3 \ - 4 \ -` - -func Test_Load(t *testing.T) { - Convey("Load from data sources", t, func() { - - Convey("Load with empty data", func() { - So(Empty(), ShouldNotBeNil) - }) - - Convey("Load with multiple data sources", func() { - cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini", ioutil.NopCloser(bytes.NewReader([]byte(_CONF_DATA)))) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - f, err := Load([]byte(_CONF_DATA), "testdata/404.ini") - So(err, ShouldNotBeNil) - So(f, ShouldBeNil) - }) +NAME = Unknwon +`))), + ) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) - Convey("Load with io.ReadCloser", func() { - cfg, err := Load(ioutil.NopCloser(bytes.NewReader([]byte(_CONF_DATA)))) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) + // Vaildate values make sure all sources are loaded correctly + sec := f.Section("") + So(sec.Key("NAME").String(), ShouldEqual, "ini") + So(sec.Key("VERSION").String(), ShouldEqual, "v1") + So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "gopkg.in/ini.v1") - So(cfg.Section("").Key("NAME").String(), ShouldEqual, "ini") - }) + sec = f.Section("author") + So(sec.Key("NAME").String(), ShouldEqual, "Unknwon") + So(sec.Key("E-MAIL").String(), ShouldEqual, "u@gogs.io") }) - Convey("Bad load process", t, func() { - - Convey("Load from invalid data sources", func() { - _, err := Load(_CONF_DATA) - So(err, ShouldNotBeNil) - - f, err := Load("testdata/404.ini") - So(err, ShouldNotBeNil) - So(f, ShouldBeNil) - - _, err = Load(1) + Convey("Load from bad data sources", t, func() { + Convey("Invalid input", func() { + _, err := ini.Load(_NOT_FOUND_CONF) So(err, ShouldNotBeNil) + }) - _, err = Load([]byte(""), 1) + Convey("Unsupported type", func() { + _, err := ini.Load(123) So(err, ShouldNotBeNil) }) + }) +} - Convey("Load with bad section name", func() { - _, err := Load([]byte("[]")) - So(err, ShouldNotBeNil) +func TestLoadSources(t *testing.T) { + Convey("Load from data sources with options", t, func() { + Convey("Ignore nonexistent files", func() { + f, err := ini.LooseLoad(_NOT_FOUND_CONF, _MINIMAL_CONF) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) - _, err = Load([]byte("[")) - So(err, ShouldNotBeNil) + Convey("Inverse case", func() { + _, err = ini.Load(_NOT_FOUND_CONF) + So(err, ShouldNotBeNil) + }) }) - Convey("Load with bad keys", func() { - _, err := Load([]byte(`"""name`)) - So(err, ShouldNotBeNil) + Convey("Insensitive to section and key names", func() { + f, err := ini.InsensitiveLoad(_MINIMAL_CONF) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) - _, err = Load([]byte(`"""name"""`)) - So(err, ShouldNotBeNil) + So(f.Section("Author").Key("e-mail").String(), ShouldEqual, "u@gogs.io") - _, err = Load([]byte(`""=1`)) - So(err, ShouldNotBeNil) + Convey("Write out", func() { + var buf bytes.Buffer + _, err := f.WriteTo(&buf) + So(err, ShouldBeNil) + So(buf.String(), ShouldEqual, `[author] +e-mail = u@gogs.io - _, err = Load([]byte(`=`)) - So(err, ShouldNotBeNil) +`) + }) - _, err = Load([]byte(`name`)) - So(err, ShouldNotBeNil) - }) + Convey("Inverse case", func() { + f, err := ini.Load(_MINIMAL_CONF) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) - Convey("Load with bad values", func() { - _, err := Load([]byte(`name="""Unknwon`)) - So(err, ShouldNotBeNil) + So(f.Section("Author").Key("e-mail").String(), ShouldBeEmpty) + }) }) - }) - Convey("Get section and key insensitively", t, func() { - cfg, err := InsensitiveLoad([]byte(_CONF_DATA), "testdata/conf.ini") - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) + Convey("Ignore continuation lines", func() { + f, err := ini.LoadSources(ini.LoadOptions{ + IgnoreContinuation: true, + }, []byte(` +key1=a\b\ +key2=c\d\ +key3=value`)) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) - sec, err := cfg.GetSection("Author") - So(err, ShouldBeNil) - So(sec, ShouldNotBeNil) + So(f.Section("").Key("key1").String(), ShouldEqual, `a\b\`) + So(f.Section("").Key("key2").String(), ShouldEqual, `c\d\`) + So(f.Section("").Key("key3").String(), ShouldEqual, "value") - key, err := sec.GetKey("E-mail") - So(err, ShouldBeNil) - So(key, ShouldNotBeNil) - }) - - Convey("Load with ignoring continuation lines", t, func() { - cfg, err := LoadSources(LoadOptions{IgnoreContinuation: true}, []byte(`key1=a\b\ + Convey("Inverse case", func() { + f, err := ini.Load([]byte(` +key1=a\b\ key2=c\d\`)) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) - So(cfg.Section("").Key("key1").String(), ShouldEqual, `a\b\`) - So(cfg.Section("").Key("key2").String(), ShouldEqual, `c\d\`) - }) - - Convey("Load with ignoring inline comments", t, func() { - cfg, err := LoadSources(LoadOptions{IgnoreInlineComment: true}, []byte(`key1=value ;comment -key2=value #comment2`)) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - So(cfg.Section("").Key("key1").String(), ShouldEqual, `value ;comment`) - So(cfg.Section("").Key("key2").String(), ShouldEqual, `value #comment2`) - - var buf bytes.Buffer - cfg.WriteTo(&buf) - So(buf.String(), ShouldEqual, `key1 = value ;comment -key2 = value #comment2 + So(f.Section("").Key("key1").String(), ShouldEqual, `a\bkey2=c\d`) + }) + }) -`) - }) + Convey("Ignore inline comments", func() { + f, err := ini.LoadSources(ini.LoadOptions{ + IgnoreInlineComment: true, + }, []byte(` +key1=value ;comment +key2=value2 #comment2`)) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + So(f.Section("").Key("key1").String(), ShouldEqual, `value ;comment`) + So(f.Section("").Key("key2").String(), ShouldEqual, `value2 #comment2`) + + Convey("Inverse case", func() { + f, err := ini.Load([]byte(` +key1=value ;comment +key2=value2 #comment2`)) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + So(f.Section("").Key("key1").String(), ShouldEqual, `value`) + So(f.Section("").Key("key1").Comment, ShouldEqual, `;comment`) + So(f.Section("").Key("key2").String(), ShouldEqual, `value2`) + So(f.Section("").Key("key2").Comment, ShouldEqual, `#comment2`) + }) + }) - Convey("Load with boolean type keys", t, func() { - cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, []byte(`key1=hello -key2 -#key3 -key4 -key5`)) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - So(strings.Join(cfg.Section("").KeyStrings(), ","), ShouldEqual, "key1,key2,key4,key5") - So(cfg.Section("").Key("key2").MustBool(false), ShouldBeTrue) - - var buf bytes.Buffer - cfg.WriteTo(&buf) - // there is always a trailing \n at the end of the section - So(buf.String(), ShouldEqual, `key1 = hello -key2 -#key3 -key4 -key5 + Convey("Allow boolean type keys", func() { + f, err := ini.LoadSources(ini.LoadOptions{ + AllowBooleanKeys: true, + }, []byte(` +key1=hello +#key2 +key3`)) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + So(f.Section("").KeyStrings(), ShouldResemble, []string{"key1", "key3"}) + So(f.Section("").Key("key3").MustBool(false), ShouldBeTrue) + + Convey("Write out", func() { + var buf bytes.Buffer + _, err := f.WriteTo(&buf) + So(err, ShouldBeNil) + So(buf.String(), ShouldEqual, `key1 = hello +# key2 +key3 `) - }) -} - -func Test_File_ChildSections(t *testing.T) { - Convey("Find child sections by parent name", t, func() { - cfg, err := Load([]byte(` -[node] - -[node.biz1] - -[node.biz2] - -[node.biz3] - -[node.bizN] -`)) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - children := cfg.ChildSections("node") - names := make([]string, len(children)) - for i := range children { - names[i] = children[i].name - } - So(strings.Join(names, ","), ShouldEqual, "node.biz1,node.biz2,node.biz3,node.bizN") - }) -} + }) + + Convey("Inverse case", func() { + _, err := ini.Load([]byte(` +key1=hello +#key2 +key3`)) + So(err, ShouldNotBeNil) + }) + }) -func Test_LooseLoad(t *testing.T) { - Convey("Loose load from data sources", t, func() { - Convey("Loose load mixed with nonexistent file", func() { - cfg, err := LooseLoad("testdata/404.ini") + Convey("Allow shadow keys", func() { + f, err := ini.ShadowLoad([]byte(` +[remote "origin"] +url = https://github.com/Antergone/test1.git +url = https://github.com/Antergone/test2.git +fetch = +refs/heads/*:refs/remotes/origin/*`)) So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - var fake struct { - Name string `ini:"name"` - } - So(cfg.MapTo(&fake), ShouldBeNil) + So(f, ShouldNotBeNil) + + So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test1.git") + So(f.Section(`remote "origin"`).Key("url").ValueWithShadows(), ShouldResemble, []string{ + "https://github.com/Antergone/test1.git", + "https://github.com/Antergone/test2.git", + }) + So(f.Section(`remote "origin"`).Key("fetch").String(), ShouldEqual, "+refs/heads/*:refs/remotes/origin/*") + + Convey("Write out", func() { + var buf bytes.Buffer + _, err := f.WriteTo(&buf) + So(err, ShouldBeNil) + So(buf.String(), ShouldEqual, `[remote "origin"] +url = https://github.com/Antergone/test1.git +url = https://github.com/Antergone/test2.git +fetch = +refs/heads/*:refs/remotes/origin/* - cfg, err = LooseLoad([]byte("name=Unknwon"), "testdata/404.ini") - So(err, ShouldBeNil) - So(cfg.Section("").Key("name").String(), ShouldEqual, "Unknwon") - So(cfg.MapTo(&fake), ShouldBeNil) - So(fake.Name, ShouldEqual, "Unknwon") +`) + }) + + Convey("Inverse case", func() { + f, err := ini.Load([]byte(` +[remote "origin"] +url = https://github.com/Antergone/test1.git +url = https://github.com/Antergone/test2.git`)) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test2.git") + }) }) - }) -} + Convey("Unescape double quotes inside value", func() { + f, err := ini.LoadSources(ini.LoadOptions{ + UnescapeValueDoubleQuotes: true, + }, []byte(` +create_repo="创建了仓库 <a href=\"%s\">%s</a>"`)) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) -func Test_File_Append(t *testing.T) { - Convey("Append data sources", t, func() { - cfg, err := Load([]byte("")) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) + So(f.Section("").Key("create_repo").String(), ShouldEqual, `创建了仓库 <a href="%s">%s</a>`) - So(cfg.Append([]byte(""), []byte("")), ShouldBeNil) + Convey("Inverse case", func() { + f, err := ini.Load([]byte(` +create_repo="创建了仓库 <a href=\"%s\">%s</a>"`)) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) - Convey("Append bad data sources", func() { - So(cfg.Append(1), ShouldNotBeNil) - So(cfg.Append([]byte(""), 1), ShouldNotBeNil) + So(f.Section("").Key("create_repo").String(), ShouldEqual, `"创建了仓库 <a href=\"%s\">%s</a>"`) + }) }) - }) -} - -func Test_File_WriteTo(t *testing.T) { - Convey("Write to somewhere", t, func() { - var buf bytes.Buffer - cfg := Empty() - cfg.WriteTo(&buf) - }) -} - -func Test_File_SaveTo_WriteTo(t *testing.T) { - Convey("Save file", t, func() { - cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini") - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - cfg.Section("").Key("NAME").Comment = "Package name" - cfg.Section("author").Comment = `Information about package author -# Bio can be written in multiple lines.` - cfg.Section("advanced").Key("val w/ pound").SetValue("my#password") - cfg.Section("advanced").Key("longest key has a colon : yes/no").SetValue("yes") - So(cfg.SaveTo("testdata/conf_out.ini"), ShouldBeNil) + Convey("Allow unparseable sections", func() { + f, err := ini.LoadSources(ini.LoadOptions{ + Insensitive: true, + UnparseableSections: []string{"core_lesson", "comments"}, + }, []byte(` +Lesson_Location = 87 +Lesson_Status = C +Score = 3 +Time = 00:02:30 - cfg.Section("author").Key("NAME").Comment = "This is author name" +[CORE_LESSON] +my lesson state data – 1111111111111111111000000000000000001110000 +111111111111111111100000000000111000000000 – end my lesson state data - So(cfg.SaveToIndent("testdata/conf_out.ini", "\t"), ShouldBeNil) +[COMMENTS] +<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`)) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + So(f.Section("").Key("score").String(), ShouldEqual, "3") + So(f.Section("").Body(), ShouldBeEmpty) + So(f.Section("core_lesson").Body(), ShouldEqual, `my lesson state data – 1111111111111111111000000000000000001110000 +111111111111111111100000000000111000000000 – end my lesson state data`) + So(f.Section("comments").Body(), ShouldEqual, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`) + + Convey("Write out", func() { + var buf bytes.Buffer + _, err := f.WriteTo(&buf) + So(err, ShouldBeNil) + So(buf.String(), ShouldEqual, `lesson_location = 87 +lesson_status = C +score = 3 +time = 00:02:30 + +[core_lesson] +my lesson state data – 1111111111111111111000000000000000001110000 +111111111111111111100000000000111000000000 – end my lesson state data - var buf bytes.Buffer - _, err = cfg.WriteToIndent(&buf, "\t") - So(err, ShouldBeNil) - So(buf.String(), ShouldEqual, `; Package name -NAME = ini -; Package version -VERSION = v1 -; Package import path -IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s - -; Information about package author -# Bio can be written in multiple lines. -[author] - ; This is author name - NAME = Unknwon - E-MAIL = u@gogs.io - GITHUB = https://github.com/%(NAME)s - # Succeeding comment - BIO = """Gopher. -Coding addict. -Good man. -""" - -[package] - CLONE_URL = https://%(IMPORT_PATH)s - -[package.sub] - UNUSED_KEY = should be deleted - -[features] - - = Support read/write comments of keys and sections - - = Support auto-increment of key names - - = Support load multiple files to overwrite key values - -[types] - STRING = str - BOOL = true - BOOL_FALSE = false - FLOAT64 = 1.25 - INT = 10 - TIME = 2015-01-01T20:17:05Z - DURATION = 2h45m - UINT = 3 - -[array] - STRINGS = en, zh, de - FLOAT64S = 1.1, 2.2, 3.3 - INTS = 1, 2, 3 - UINTS = 1, 2, 3 - TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z - -[note] - empty_lines = next line is empty - -; Comment before the section -; This is a comment for the section too [comments] - ; Comment before key - key = value - ; This is a comment for key2 - key2 = value2 - key3 = "one", "two", "three" - -[advance] - value with quotes = some value - value quote2 again = some value - includes comment sign = `+"`"+"my#password"+"`"+` - includes comment sign2 = `+"`"+"my;password"+"`"+` - true = 2+3=5 - `+"`"+`1+1=2`+"`"+` = true - `+"`"+`6+1=7`+"`"+` = true - """`+"`"+`5+5`+"`"+`""" = 10 - `+"`"+`"6+6"`+"`"+` = 12 - `+"`"+`7-2=4`+"`"+` = false - ADDRESS = """404 road, -NotFound, State, 50000""" - two_lines = how about continuation lines? - lots_of_lines = 1 2 3 4 - -[advanced] - val w/ pound = `+"`"+`my#password`+"`"+` - `+"`"+`longest key has a colon : yes/no`+"`"+` = yes - +<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1> `) - }) -} - -func Test_File_WriteTo_SectionRaw(t *testing.T) { - Convey("Write a INI with a raw section", t, func() { - var buf bytes.Buffer - cfg, err := LoadSources( - LoadOptions{ - UnparseableSections: []string{"CORE_LESSON", "COMMENTS"}, - }, - "testdata/aicc.ini") - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - cfg.WriteToIndent(&buf, "\t") - So(buf.String(), ShouldEqual, `[Core] - Lesson_Location = 87 - Lesson_Status = C - Score = 3 - Time = 00:02:30 + }) + Convey("Inverse case", func() { + _, err := ini.Load([]byte(` [CORE_LESSON] my lesson state data – 1111111111111111111000000000000000001110000 -111111111111111111100000000000111000000000 – end my lesson state data -[COMMENTS] -<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1> -`) +111111111111111111100000000000111000000000 – end my lesson state data`)) + So(err, ShouldNotBeNil) + }) + }) }) } - -// Helpers for slice tests. -func float64sEqual(values []float64, expected ...float64) { - So(values, ShouldHaveLength, len(expected)) - for i, v := range expected { - So(values[i], ShouldEqual, v) - } -} - -func intsEqual(values []int, expected ...int) { - So(values, ShouldHaveLength, len(expected)) - for i, v := range expected { - So(values[i], ShouldEqual, v) - } -} - -func int64sEqual(values []int64, expected ...int64) { - So(values, ShouldHaveLength, len(expected)) - for i, v := range expected { - So(values[i], ShouldEqual, v) - } -} - -func uintsEqual(values []uint, expected ...uint) { - So(values, ShouldHaveLength, len(expected)) - for i, v := range expected { - So(values[i], ShouldEqual, v) - } -} - -func uint64sEqual(values []uint64, expected ...uint64) { - So(values, ShouldHaveLength, len(expected)) - for i, v := range expected { - So(values[i], ShouldEqual, v) - } -} - -func timesEqual(values []time.Time, expected ...time.Time) { - So(values, ShouldHaveLength, len(expected)) - for i, v := range expected { - So(values[i].String(), ShouldEqual, v.String()) - } -} diff --git a/vendor/github.com/go-ini/ini/key.go b/vendor/github.com/go-ini/ini/key.go index 838356af0..ab566c2c1 100644 --- a/vendor/github.com/go-ini/ini/key.go +++ b/vendor/github.com/go-ini/ini/key.go @@ -15,6 +15,7 @@ package ini import ( + "bytes" "errors" "fmt" "strconv" @@ -25,6 +26,7 @@ import ( // Key represents a key under a section. type Key struct { s *Section + Comment string name string value string isAutoIncrement bool @@ -32,8 +34,6 @@ type Key struct { isShadow bool shadows []*Key - - Comment string } // newKey simply return a key object with given values. @@ -444,11 +444,39 @@ func (k *Key) Strings(delim string) []string { return []string{} } - vals := strings.Split(str, delim) - for i := range vals { - // vals[i] = k.transformValue(strings.TrimSpace(vals[i])) - vals[i] = strings.TrimSpace(vals[i]) + runes := []rune(str) + vals := make([]string, 0, 2) + var buf bytes.Buffer + escape := false + idx := 0 + for { + if escape { + escape = false + if runes[idx] != '\\' && !strings.HasPrefix(string(runes[idx:]), delim) { + buf.WriteRune('\\') + } + buf.WriteRune(runes[idx]) + } else { + if runes[idx] == '\\' { + escape = true + } else if strings.HasPrefix(string(runes[idx:]), delim) { + idx += len(delim) - 1 + vals = append(vals, strings.TrimSpace(buf.String())) + buf.Reset() + } else { + buf.WriteRune(runes[idx]) + } + } + idx += 1 + if idx == len(runes) { + break + } + } + + if buf.Len() > 0 { + vals = append(vals, strings.TrimSpace(buf.String())) } + return vals } diff --git a/vendor/github.com/go-ini/ini/key_test.go b/vendor/github.com/go-ini/ini/key_test.go index 1281d5bf0..588efd429 100644 --- a/vendor/github.com/go-ini/ini/key_test.go +++ b/vendor/github.com/go-ini/ini/key_test.go @@ -12,26 +12,108 @@ // License for the specific language governing permissions and limitations // under the License. -package ini +package ini_test import ( - "bytes" "fmt" "strings" "testing" "time" . "github.com/smartystreets/goconvey/convey" + "gopkg.in/ini.v1" ) -func Test_Key(t *testing.T) { - Convey("Test getting and setting values", t, func() { - cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini") +func TestKey_AddShadow(t *testing.T) { + Convey("Add shadow to a key", t, func() { + f, err := ini.ShadowLoad([]byte(` +[notes] +-: note1`)) So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) + So(f, ShouldNotBeNil) - Convey("Get values in default section", func() { - sec := cfg.Section("") + k, err := f.Section("").NewKey("NAME", "ini") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + + So(k.AddShadow("ini.v1"), ShouldBeNil) + So(k.ValueWithShadows(), ShouldResemble, []string{"ini", "ini.v1"}) + + Convey("Add shadow to boolean key", func() { + k, err := f.Section("").NewBooleanKey("published") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + So(k.AddShadow("beta"), ShouldNotBeNil) + }) + + Convey("Add shadow to auto-increment key", func() { + So(f.Section("notes").Key("#1").AddShadow("beta"), ShouldNotBeNil) + }) + }) + + Convey("Shadow is not allowed", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + k, err := f.Section("").NewKey("NAME", "ini") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + + So(k.AddShadow("ini.v1"), ShouldNotBeNil) + }) +} + +// Helpers for slice tests. +func float64sEqual(values []float64, expected ...float64) { + So(values, ShouldHaveLength, len(expected)) + for i, v := range expected { + So(values[i], ShouldEqual, v) + } +} + +func intsEqual(values []int, expected ...int) { + So(values, ShouldHaveLength, len(expected)) + for i, v := range expected { + So(values[i], ShouldEqual, v) + } +} + +func int64sEqual(values []int64, expected ...int64) { + So(values, ShouldHaveLength, len(expected)) + for i, v := range expected { + So(values[i], ShouldEqual, v) + } +} + +func uintsEqual(values []uint, expected ...uint) { + So(values, ShouldHaveLength, len(expected)) + for i, v := range expected { + So(values[i], ShouldEqual, v) + } +} + +func uint64sEqual(values []uint64, expected ...uint64) { + So(values, ShouldHaveLength, len(expected)) + for i, v := range expected { + So(values[i], ShouldEqual, v) + } +} + +func timesEqual(values []time.Time, expected ...time.Time) { + So(values, ShouldHaveLength, len(expected)) + for i, v := range expected { + So(values[i].String(), ShouldEqual, v.String()) + } +} + +func TestKey_Helpers(t *testing.T) { + Convey("Getting and setting values", t, func() { + f, err := ini.Load(_FULL_CONF) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + Convey("Get string representation", func() { + sec := f.Section("") So(sec, ShouldNotBeNil) So(sec.Key("NAME").Value(), ShouldEqual, "ini") So(sec.Key("NAME").String(), ShouldEqual, "ini") @@ -40,55 +122,65 @@ func Test_Key(t *testing.T) { }), ShouldEqual, "ini") So(sec.Key("NAME").Comment, ShouldEqual, "; Package name") So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "gopkg.in/ini.v1") + + Convey("With ValueMapper", func() { + f.ValueMapper = func(in string) string { + if in == "gopkg.in/%(NAME)s.%(VERSION)s" { + return "github.com/go-ini/ini" + } + return in + } + So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "github.com/go-ini/ini") + }) }) Convey("Get values in non-default section", func() { - sec := cfg.Section("author") + sec := f.Section("author") So(sec, ShouldNotBeNil) So(sec.Key("NAME").String(), ShouldEqual, "Unknwon") So(sec.Key("GITHUB").String(), ShouldEqual, "https://github.com/Unknwon") - sec = cfg.Section("package") + sec = f.Section("package") So(sec, ShouldNotBeNil) So(sec.Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1") }) Convey("Get auto-increment key names", func() { - keys := cfg.Section("features").Keys() + keys := f.Section("features").Keys() for i, k := range keys { So(k.Name(), ShouldEqual, fmt.Sprintf("#%d", i+1)) } }) Convey("Get parent-keys that are available to the child section", func() { - parentKeys := cfg.Section("package.sub").ParentKeys() + parentKeys := f.Section("package.sub").ParentKeys() for _, k := range parentKeys { So(k.Name(), ShouldEqual, "CLONE_URL") } }) Convey("Get overwrite value", func() { - So(cfg.Section("author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io") + So(f.Section("author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io") }) Convey("Get sections", func() { - sections := cfg.Sections() - for i, name := range []string{DEFAULT_SECTION, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "advance"} { + sections := f.Sections() + for i, name := range []string{ini.DEFAULT_SECTION, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"} { So(sections[i].Name(), ShouldEqual, name) } }) Convey("Get parent section value", func() { - So(cfg.Section("package.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1") - So(cfg.Section("package.fake.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1") + So(f.Section("package.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1") + So(f.Section("package.fake.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1") }) Convey("Get multiple line value", func() { - So(cfg.Section("author").Key("BIO").String(), ShouldEqual, "Gopher.\nCoding addict.\nGood man.\n") + So(f.Section("author").Key("BIO").String(), ShouldEqual, "Gopher.\nCoding addict.\nGood man.\n") }) Convey("Get values with type", func() { - sec := cfg.Section("types") + sec := f.Section("types") v1, err := sec.Key("BOOL").Bool() So(err, ShouldBeNil) So(v1, ShouldBeTrue) @@ -168,7 +260,7 @@ func Test_Key(t *testing.T) { }) Convey("Get value with candidates", func() { - sec := cfg.Section("types") + sec := f.Section("types") So(sec.Key("STRING").In("", []string{"str", "arr", "types"}), ShouldEqual, "str") So(sec.Key("FLOAT64").InFloat64(0, []float64{1.25, 2.5, 3.75}), ShouldEqual, 1.25) So(sec.Key("INT").InInt(0, []int{10, 20, 30}), ShouldEqual, 10) @@ -194,7 +286,7 @@ func Test_Key(t *testing.T) { }) Convey("Get values in range", func() { - sec := cfg.Section("types") + sec := f.Section("types") So(sec.Key("FLOAT64").RangeFloat64(0, 1, 2), ShouldEqual, 1.25) So(sec.Key("INT").RangeInt(0, 10, 20), ShouldEqual, 10) So(sec.Key("INT").RangeInt64(0, 10, 20), ShouldEqual, 10) @@ -218,7 +310,7 @@ func Test_Key(t *testing.T) { }) Convey("Get values into slice", func() { - sec := cfg.Section("array") + sec := f.Section("array") So(strings.Join(sec.Key("STRINGS").Strings(","), ","), ShouldEqual, "en,zh,de") So(len(sec.Key("STRINGS_404").Strings(",")), ShouldEqual, 0) @@ -243,8 +335,18 @@ func Test_Key(t *testing.T) { timesEqual(vals6, t, t, t) }) + Convey("Test string slice escapes", func() { + sec := f.Section("string escapes") + So(sec.Key("key1").Strings(","), ShouldResemble, []string{"value1", "value2", "value3"}) + So(sec.Key("key2").Strings(","), ShouldResemble, []string{"value1, value2"}) + So(sec.Key("key3").Strings(","), ShouldResemble, []string{`val\ue1`, "value2"}) + So(sec.Key("key4").Strings(","), ShouldResemble, []string{`value1\`, `value\\2`}) + So(sec.Key("key5").Strings(",,"), ShouldResemble, []string{"value1,, value2"}) + So(sec.Key("key6").Strings(" "), ShouldResemble, []string{"aaa", "bbb and space", "ccc"}) + }) + Convey("Get valid values into slice", func() { - sec := cfg.Section("array") + sec := f.Section("array") vals1 := sec.Key("FLOAT64S").ValidFloat64s(",") float64sEqual(vals1, 1.1, 2.2, 3.3) @@ -267,7 +369,7 @@ func Test_Key(t *testing.T) { }) Convey("Get values one type into slice of another type", func() { - sec := cfg.Section("array") + sec := f.Section("array") vals1 := sec.Key("STRINGS").ValidFloat64s(",") So(vals1, ShouldBeEmpty) @@ -288,7 +390,7 @@ func Test_Key(t *testing.T) { }) Convey("Get valid values into slice without errors", func() { - sec := cfg.Section("array") + sec := f.Section("array") vals1, err := sec.Key("FLOAT64S").StrictFloat64s(",") So(err, ShouldBeNil) float64sEqual(vals1, 1.1, 2.2, 3.3) @@ -317,7 +419,7 @@ func Test_Key(t *testing.T) { }) Convey("Get invalid values into slice", func() { - sec := cfg.Section("array") + sec := f.Section("array") vals1, err := sec.Key("STRINGS").StrictFloat64s(",") So(vals1, ShouldBeEmpty) So(err, ShouldNotBeNil) @@ -342,232 +444,37 @@ func Test_Key(t *testing.T) { So(vals6, ShouldBeEmpty) So(err, ShouldNotBeNil) }) - - Convey("Get key hash", func() { - cfg.Section("").KeysHash() - }) - - Convey("Set key value", func() { - k := cfg.Section("author").Key("NAME") - k.SetValue("无闻") - So(k.String(), ShouldEqual, "无闻") - }) - - Convey("Get key strings", func() { - So(strings.Join(cfg.Section("types").KeyStrings(), ","), ShouldEqual, "STRING,BOOL,BOOL_FALSE,FLOAT64,INT,TIME,DURATION,UINT") - }) - - Convey("Delete a key", func() { - cfg.Section("package.sub").DeleteKey("UNUSED_KEY") - _, err := cfg.Section("package.sub").GetKey("UNUSED_KEY") - So(err, ShouldNotBeNil) - }) - - Convey("Has Key (backwards compatible)", func() { - sec := cfg.Section("package.sub") - haskey1 := sec.Haskey("UNUSED_KEY") - haskey2 := sec.Haskey("CLONE_URL") - haskey3 := sec.Haskey("CLONE_URL_NO") - So(haskey1, ShouldBeTrue) - So(haskey2, ShouldBeTrue) - So(haskey3, ShouldBeFalse) - }) - - Convey("Has Key", func() { - sec := cfg.Section("package.sub") - haskey1 := sec.HasKey("UNUSED_KEY") - haskey2 := sec.HasKey("CLONE_URL") - haskey3 := sec.HasKey("CLONE_URL_NO") - So(haskey1, ShouldBeTrue) - So(haskey2, ShouldBeTrue) - So(haskey3, ShouldBeFalse) - }) - - Convey("Has Value", func() { - sec := cfg.Section("author") - hasvalue1 := sec.HasValue("Unknwon") - hasvalue2 := sec.HasValue("doc") - So(hasvalue1, ShouldBeTrue) - So(hasvalue2, ShouldBeFalse) - }) }) +} - Convey("Test getting and setting bad values", t, func() { - cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini") +func TestKey_StringsWithShadows(t *testing.T) { + Convey("Get strings of shadows of a key", t, func() { + f, err := ini.ShadowLoad([]byte("")) So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - Convey("Create new key with empty name", func() { - k, err := cfg.Section("").NewKey("", "") - So(err, ShouldNotBeNil) - So(k, ShouldBeNil) - }) - - Convey("Create new section with empty name", func() { - s, err := cfg.NewSection("") - So(err, ShouldNotBeNil) - So(s, ShouldBeNil) - }) - - Convey("Create new sections with empty name", func() { - So(cfg.NewSections(""), ShouldNotBeNil) - }) + So(f, ShouldNotBeNil) - Convey("Get section that not exists", func() { - s, err := cfg.GetSection("404") - So(err, ShouldNotBeNil) - So(s, ShouldBeNil) - - s = cfg.Section("404") - So(s, ShouldNotBeNil) - }) - }) - - Convey("Test key hash clone", t, func() { - cfg, err := Load([]byte(strings.Replace("network=tcp,addr=127.0.0.1:6379,db=4,pool_size=100,idle_timeout=180", ",", "\n", -1))) + k, err := f.Section("").NewKey("NUMS", "1,2") So(err, ShouldBeNil) - for _, v := range cfg.Section("").KeysHash() { - So(len(v), ShouldBeGreaterThan, 0) - } - }) - - Convey("Key has empty value", t, func() { - _conf := `key1= -key2= ; comment` - cfg, err := Load([]byte(_conf)) + So(k, ShouldNotBeNil) + k, err = f.Section("").NewKey("NUMS", "4,5,6") So(err, ShouldBeNil) - So(cfg.Section("").Key("key1").Value(), ShouldBeEmpty) - }) -} - -const _CONF_GIT_CONFIG = ` -[remote "origin"] - url = https://github.com/Antergone/test1.git - url = https://github.com/Antergone/test2.git -` - -func Test_Key_Shadows(t *testing.T) { - Convey("Shadows keys", t, func() { - Convey("Disable shadows", func() { - cfg, err := Load([]byte(_CONF_GIT_CONFIG)) - So(err, ShouldBeNil) - So(cfg.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test2.git") - }) - - Convey("Enable shadows", func() { - cfg, err := ShadowLoad([]byte(_CONF_GIT_CONFIG)) - So(err, ShouldBeNil) - So(cfg.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test1.git") - So(strings.Join(cfg.Section(`remote "origin"`).Key("url").ValueWithShadows(), " "), ShouldEqual, - "https://github.com/Antergone/test1.git https://github.com/Antergone/test2.git") + So(k, ShouldNotBeNil) - Convey("Save with shadows", func() { - var buf bytes.Buffer - _, err := cfg.WriteTo(&buf) - So(err, ShouldBeNil) - So(buf.String(), ShouldEqual, `[remote "origin"] -url = https://github.com/Antergone/test1.git -url = https://github.com/Antergone/test2.git - -`) - }) - }) + So(k.StringsWithShadows(","), ShouldResemble, []string{"1", "2", "4", "5", "6"}) }) } -func newTestFile(block bool) *File { - c, _ := Load([]byte(_CONF_DATA)) - c.BlockMode = block - return c -} - -func Benchmark_Key_Value(b *testing.B) { - c := newTestFile(true) - for i := 0; i < b.N; i++ { - c.Section("").Key("NAME").Value() - } -} - -func Benchmark_Key_Value_NonBlock(b *testing.B) { - c := newTestFile(false) - for i := 0; i < b.N; i++ { - c.Section("").Key("NAME").Value() - } -} +func TestKey_SetValue(t *testing.T) { + Convey("Set value of key", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) -func Benchmark_Key_Value_ViaSection(b *testing.B) { - c := newTestFile(true) - sec := c.Section("") - for i := 0; i < b.N; i++ { - sec.Key("NAME").Value() - } -} - -func Benchmark_Key_Value_ViaSection_NonBlock(b *testing.B) { - c := newTestFile(false) - sec := c.Section("") - for i := 0; i < b.N; i++ { - sec.Key("NAME").Value() - } -} - -func Benchmark_Key_Value_Direct(b *testing.B) { - c := newTestFile(true) - key := c.Section("").Key("NAME") - for i := 0; i < b.N; i++ { - key.Value() - } -} - -func Benchmark_Key_Value_Direct_NonBlock(b *testing.B) { - c := newTestFile(false) - key := c.Section("").Key("NAME") - for i := 0; i < b.N; i++ { - key.Value() - } -} - -func Benchmark_Key_String(b *testing.B) { - c := newTestFile(true) - for i := 0; i < b.N; i++ { - _ = c.Section("").Key("NAME").String() - } -} - -func Benchmark_Key_String_NonBlock(b *testing.B) { - c := newTestFile(false) - for i := 0; i < b.N; i++ { - _ = c.Section("").Key("NAME").String() - } -} - -func Benchmark_Key_String_ViaSection(b *testing.B) { - c := newTestFile(true) - sec := c.Section("") - for i := 0; i < b.N; i++ { - _ = sec.Key("NAME").String() - } -} - -func Benchmark_Key_String_ViaSection_NonBlock(b *testing.B) { - c := newTestFile(false) - sec := c.Section("") - for i := 0; i < b.N; i++ { - _ = sec.Key("NAME").String() - } -} - -func Benchmark_Key_SetValue(b *testing.B) { - c := newTestFile(true) - for i := 0; i < b.N; i++ { - c.Section("").Key("NAME").SetValue("10") - } -} + k, err := f.Section("").NewKey("NAME", "ini") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + So(k.Value(), ShouldEqual, "ini") -func Benchmark_Key_SetValue_VisSection(b *testing.B) { - c := newTestFile(true) - sec := c.Section("") - for i := 0; i < b.N; i++ { - sec.Key("NAME").SetValue("10") - } + k.SetValue("ini.v1") + So(k.Value(), ShouldEqual, "ini.v1") + }) } diff --git a/vendor/github.com/go-ini/ini/parser.go b/vendor/github.com/go-ini/ini/parser.go index 69d547627..f8ac8026a 100644 --- a/vendor/github.com/go-ini/ini/parser.go +++ b/vendor/github.com/go-ini/ini/parser.go @@ -193,7 +193,7 @@ func hasSurroundedQuote(in string, quote byte) bool { strings.IndexByte(in[1:], quote) == len(in)-2 } -func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment bool) (string, error) { +func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes bool) (string, error) { line := strings.TrimLeftFunc(string(in), unicode.IsSpace) if len(line) == 0 { return "", nil @@ -204,6 +204,8 @@ func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment bo valQuote = `"""` } else if line[0] == '`' { valQuote = "`" + } else if unescapeValueDoubleQuotes && line[0] == '"' { + valQuote = `"` } if len(valQuote) > 0 { @@ -214,6 +216,9 @@ func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment bo return p.readMultilines(line, line[startIdx:], valQuote) } + if unescapeValueDoubleQuotes && valQuote == `"` { + return strings.Replace(line[startIdx:pos+startIdx], `\"`, `"`, -1), nil + } return line[startIdx : pos+startIdx], nil } @@ -234,7 +239,7 @@ func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment bo } } - // Trim single quotes + // Trim single and double quotes if hasSurroundedQuote(line, '\'') || hasSurroundedQuote(line, '"') { line = line[1 : len(line)-1] @@ -250,7 +255,11 @@ func (f *File) parse(reader io.Reader) (err error) { } // Ignore error because default section name is never empty string. - section, _ := f.NewSection(DEFAULT_SECTION) + name := DEFAULT_SECTION + if f.options.Insensitive { + name = strings.ToLower(DEFAULT_SECTION) + } + section, _ := f.NewSection(name) var line []byte var inUnparseableSection bool @@ -321,7 +330,10 @@ func (f *File) parse(reader io.Reader) (err error) { if err != nil { // Treat as boolean key when desired, and whole line is key name. if IsErrDelimiterNotFound(err) && f.options.AllowBooleanKeys { - kname, err := p.readValue(line, f.options.IgnoreContinuation, f.options.IgnoreInlineComment) + kname, err := p.readValue(line, + f.options.IgnoreContinuation, + f.options.IgnoreInlineComment, + f.options.UnescapeValueDoubleQuotes) if err != nil { return err } @@ -344,7 +356,10 @@ func (f *File) parse(reader io.Reader) (err error) { p.count++ } - value, err := p.readValue(line[offset:], f.options.IgnoreContinuation, f.options.IgnoreInlineComment) + value, err := p.readValue(line[offset:], + f.options.IgnoreContinuation, + f.options.IgnoreInlineComment, + f.options.UnescapeValueDoubleQuotes) if err != nil { return err } diff --git a/vendor/github.com/go-ini/ini/parser_test.go b/vendor/github.com/go-ini/ini/parser_test.go index 05258195b..bb0f2665e 100644 --- a/vendor/github.com/go-ini/ini/parser_test.go +++ b/vendor/github.com/go-ini/ini/parser_test.go @@ -12,31 +12,66 @@ // License for the specific language governing permissions and limitations // under the License. -package ini +package ini_test import ( "testing" . "github.com/smartystreets/goconvey/convey" + "gopkg.in/ini.v1" ) -func Test_BOM(t *testing.T) { +func TestBOM(t *testing.T) { Convey("Test handling BOM", t, func() { Convey("UTF-8-BOM", func() { - cfg, err := Load("testdata/UTF-8-BOM.ini") + f, err := ini.Load("testdata/UTF-8-BOM.ini") So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) + So(f, ShouldNotBeNil) - So(cfg.Section("author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io") + So(f.Section("author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io") }) Convey("UTF-16-LE-BOM", func() { - cfg, err := Load("testdata/UTF-16-LE-BOM.ini") + f, err := ini.Load("testdata/UTF-16-LE-BOM.ini") So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) + So(f, ShouldNotBeNil) }) Convey("UTF-16-BE-BOM", func() { }) }) } + +func TestBadLoad(t *testing.T) { + Convey("Load with bad data", t, func() { + Convey("Bad section name", func() { + _, err := ini.Load([]byte("[]")) + So(err, ShouldNotBeNil) + + _, err = ini.Load([]byte("[")) + So(err, ShouldNotBeNil) + }) + + Convey("Bad keys", func() { + _, err := ini.Load([]byte(`"""name`)) + So(err, ShouldNotBeNil) + + _, err = ini.Load([]byte(`"""name"""`)) + So(err, ShouldNotBeNil) + + _, err = ini.Load([]byte(`""=1`)) + So(err, ShouldNotBeNil) + + _, err = ini.Load([]byte(`=`)) + So(err, ShouldNotBeNil) + + _, err = ini.Load([]byte(`name`)) + So(err, ShouldNotBeNil) + }) + + Convey("Bad values", func() { + _, err := ini.Load([]byte(`name="""Unknwon`)) + So(err, ShouldNotBeNil) + }) + }) +} diff --git a/vendor/github.com/go-ini/ini/section.go b/vendor/github.com/go-ini/ini/section.go index 94f7375ed..d8a402619 100644 --- a/vendor/github.com/go-ini/ini/section.go +++ b/vendor/github.com/go-ini/ini/section.go @@ -54,6 +54,14 @@ func (s *Section) Body() string { return strings.TrimSpace(s.rawBody) } +// SetBody updates body content only if section is raw. +func (s *Section) SetBody(body string) { + if !s.isRawSection { + return + } + s.rawBody = body +} + // NewKey creates a new key to given section. func (s *Section) NewKey(name, val string) (*Key, error) { if len(name) == 0 { @@ -136,6 +144,7 @@ func (s *Section) HasKey(name string) bool { } // Haskey is a backwards-compatible name for HasKey. +// TODO: delete me in v2 func (s *Section) Haskey(name string) bool { return s.HasKey(name) } diff --git a/vendor/github.com/go-ini/ini/section_test.go b/vendor/github.com/go-ini/ini/section_test.go index 80282c197..e9c347881 100644 --- a/vendor/github.com/go-ini/ini/section_test.go +++ b/vendor/github.com/go-ini/ini/section_test.go @@ -12,64 +12,302 @@ // License for the specific language governing permissions and limitations // under the License. -package ini +package ini_test import ( - "strings" "testing" . "github.com/smartystreets/goconvey/convey" + "gopkg.in/ini.v1" ) -func Test_Section(t *testing.T) { - Convey("Test CRD sections", t, func() { - cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini") +func TestSection_SetBody(t *testing.T) { + Convey("Set body of raw section", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000 +111111111111111111100000000000111000000000`) + So(err, ShouldBeNil) + So(sec, ShouldNotBeNil) + So(sec.Body(), ShouldEqual, `1111111111111111111000000000000000001110000 +111111111111111111100000000000111000000000`) + + sec.SetBody("1111111111111111111000000000000000001110000") + So(sec.Body(), ShouldEqual, `1111111111111111111000000000000000001110000`) + + Convey("Set for non-raw section", func() { + sec, err := f.NewSection("author") + So(err, ShouldBeNil) + So(sec, ShouldNotBeNil) + So(sec.Body(), ShouldBeEmpty) + + sec.SetBody("1111111111111111111000000000000000001110000") + So(sec.Body(), ShouldBeEmpty) + }) + }) +} + +func TestSection_NewKey(t *testing.T) { + Convey("Create a new key", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + k, err := f.Section("").NewKey("NAME", "ini") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + So(k.Name(), ShouldEqual, "NAME") + So(k.Value(), ShouldEqual, "ini") + + Convey("With duplicated name", func() { + k, err := f.Section("").NewKey("NAME", "ini.v1") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + + // Overwrite previous existed key + So(k.Value(), ShouldEqual, "ini.v1") + }) + + Convey("With empty string", func() { + _, err := f.Section("").NewKey("", "") + So(err, ShouldNotBeNil) + }) + }) + + Convey("Create keys with same name and allow shadow", t, func() { + f, err := ini.ShadowLoad([]byte("")) + So(err, ShouldBeNil) + So(f, ShouldNotBeNil) + + k, err := f.Section("").NewKey("NAME", "ini") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + k, err = f.Section("").NewKey("NAME", "ini.v1") So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) + So(k, ShouldNotBeNil) - Convey("Get section strings", func() { - So(strings.Join(cfg.SectionStrings(), ","), ShouldEqual, "DEFAULT,author,package,package.sub,features,types,array,note,comments,advance") + So(k.ValueWithShadows(), ShouldResemble, []string{"ini", "ini.v1"}) + }) +} + +func TestSection_NewBooleanKey(t *testing.T) { + Convey("Create a new boolean key", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + k, err := f.Section("").NewBooleanKey("start-ssh-server") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + So(k.Name(), ShouldEqual, "start-ssh-server") + So(k.Value(), ShouldEqual, "true") + + Convey("With empty string", func() { + _, err := f.Section("").NewBooleanKey("") + So(err, ShouldNotBeNil) }) + }) +} + +func TestSection_GetKey(t *testing.T) { + Convey("Get a key", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) - Convey("Delete a section", func() { - cfg.DeleteSection("") - So(cfg.SectionStrings()[0], ShouldNotEqual, DEFAULT_SECTION) + k, err := f.Section("").NewKey("NAME", "ini") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + + k, err = f.Section("").GetKey("NAME") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + So(k.Name(), ShouldEqual, "NAME") + So(k.Value(), ShouldEqual, "ini") + + Convey("Key not exists", func() { + _, err := f.Section("").GetKey("404") + So(err, ShouldNotBeNil) }) - Convey("Create new sections", func() { - cfg.NewSections("test", "test2") - _, err := cfg.GetSection("test") + Convey("Key exists in parent section", func() { + k, err := f.Section("parent").NewKey("AGE", "18") So(err, ShouldBeNil) - _, err = cfg.GetSection("test2") + So(k, ShouldNotBeNil) + + k, err = f.Section("parent.child.son").GetKey("AGE") So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + So(k.Value(), ShouldEqual, "18") }) }) } -func Test_SectionRaw(t *testing.T) { - Convey("Test section raw string", t, func() { - cfg, err := LoadSources( - LoadOptions{ - Insensitive: true, - UnparseableSections: []string{"core_lesson", "comments"}, - }, - "testdata/aicc.ini") +func TestSection_HasKey(t *testing.T) { + Convey("Check if a key exists", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + k, err := f.Section("").NewKey("NAME", "ini") So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) + So(k, ShouldNotBeNil) + + So(f.Section("").HasKey("NAME"), ShouldBeTrue) + So(f.Section("").Haskey("NAME"), ShouldBeTrue) + So(f.Section("").HasKey("404"), ShouldBeFalse) + So(f.Section("").Haskey("404"), ShouldBeFalse) + }) +} - Convey("Get section strings", func() { - So(strings.Join(cfg.SectionStrings(), ","), ShouldEqual, "DEFAULT,core,core_lesson,comments") +func TestSection_HasValue(t *testing.T) { + Convey("Check if contains a value in any key", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + k, err := f.Section("").NewKey("NAME", "ini") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + + So(f.Section("").HasValue("ini"), ShouldBeTrue) + So(f.Section("").HasValue("404"), ShouldBeFalse) + }) +} + +func TestSection_Key(t *testing.T) { + Convey("Get a key", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + k, err := f.Section("").NewKey("NAME", "ini") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + + k = f.Section("").Key("NAME") + So(k, ShouldNotBeNil) + So(k.Name(), ShouldEqual, "NAME") + So(k.Value(), ShouldEqual, "ini") + + Convey("Key not exists", func() { + k := f.Section("").Key("404") + So(k, ShouldNotBeNil) + So(k.Name(), ShouldEqual, "404") }) - Convey("Validate non-raw section", func() { - val, err := cfg.Section("core").GetKey("lesson_status") + Convey("Key exists in parent section", func() { + k, err := f.Section("parent").NewKey("AGE", "18") So(err, ShouldBeNil) - So(val.String(), ShouldEqual, "C") - }) + So(k, ShouldNotBeNil) - Convey("Validate raw section", func() { - So(cfg.Section("core_lesson").Body(), ShouldEqual, `my lesson state data – 1111111111111111111000000000000000001110000 -111111111111111111100000000000111000000000 – end my lesson state data`) + k = f.Section("parent.child.son").Key("AGE") + So(k, ShouldNotBeNil) + So(k.Value(), ShouldEqual, "18") }) }) -}
\ No newline at end of file +} + +func TestSection_Keys(t *testing.T) { + Convey("Get all keys in a section", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + k, err := f.Section("").NewKey("NAME", "ini") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + k, err = f.Section("").NewKey("VERSION", "v1") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + k, err = f.Section("").NewKey("IMPORT_PATH", "gopkg.in/ini.v1") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + + keys := f.Section("").Keys() + names := []string{"NAME", "VERSION", "IMPORT_PATH"} + So(len(keys), ShouldEqual, len(names)) + for i, name := range names { + So(keys[i].Name(), ShouldEqual, name) + } + }) +} + +func TestSection_ParentKeys(t *testing.T) { + Convey("Get all keys of parent sections", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + k, err := f.Section("package").NewKey("NAME", "ini") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + k, err = f.Section("package").NewKey("VERSION", "v1") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + k, err = f.Section("package").NewKey("IMPORT_PATH", "gopkg.in/ini.v1") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + + keys := f.Section("package.sub.sub2").ParentKeys() + names := []string{"NAME", "VERSION", "IMPORT_PATH"} + So(len(keys), ShouldEqual, len(names)) + for i, name := range names { + So(keys[i].Name(), ShouldEqual, name) + } + }) +} + +func TestSection_KeyStrings(t *testing.T) { + Convey("Get all key names in a section", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + k, err := f.Section("").NewKey("NAME", "ini") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + k, err = f.Section("").NewKey("VERSION", "v1") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + k, err = f.Section("").NewKey("IMPORT_PATH", "gopkg.in/ini.v1") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + + So(f.Section("").KeyStrings(), ShouldResemble, []string{"NAME", "VERSION", "IMPORT_PATH"}) + }) +} + +func TestSection_KeyHash(t *testing.T) { + Convey("Get clone of key hash", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + k, err := f.Section("").NewKey("NAME", "ini") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + k, err = f.Section("").NewKey("VERSION", "v1") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + k, err = f.Section("").NewKey("IMPORT_PATH", "gopkg.in/ini.v1") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + + hash := f.Section("").KeysHash() + relation := map[string]string{ + "NAME": "ini", + "VERSION": "v1", + "IMPORT_PATH": "gopkg.in/ini.v1", + } + for k, v := range hash { + So(v, ShouldEqual, relation[k]) + } + }) +} + +func TestSection_DeleteKey(t *testing.T) { + Convey("Delete a key", t, func() { + f := ini.Empty() + So(f, ShouldNotBeNil) + + k, err := f.Section("").NewKey("NAME", "ini") + So(err, ShouldBeNil) + So(k, ShouldNotBeNil) + + So(f.Section("").HasKey("NAME"), ShouldBeTrue) + f.Section("").DeleteKey("NAME") + So(f.Section("").HasKey("NAME"), ShouldBeFalse) + }) +} diff --git a/vendor/github.com/go-ini/ini/struct.go b/vendor/github.com/go-ini/ini/struct.go index eeb8dabaa..9719dc698 100644 --- a/vendor/github.com/go-ini/ini/struct.go +++ b/vendor/github.com/go-ini/ini/struct.go @@ -113,7 +113,7 @@ func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowSh default: return fmt.Errorf("unsupported type '[]%s'", sliceOf) } - if isStrict { + if err != nil && isStrict { return err } @@ -166,7 +166,7 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: durationVal, err := key.Duration() // Skip zero value - if err == nil && int(durationVal) > 0 { + if err == nil && int64(durationVal) > 0 { field.Set(reflect.ValueOf(durationVal)) return nil } @@ -450,6 +450,12 @@ func (s *Section) reflectFrom(val reflect.Value) error { // Note: fieldName can never be empty here, ignore error. sec, _ = s.f.NewSection(fieldName) } + + // Add comment from comment tag + if len(sec.Comment) == 0 { + sec.Comment = tpField.Tag.Get("comment") + } + if err = sec.reflectFrom(field); err != nil { return fmt.Errorf("error reflecting field (%s): %v", fieldName, err) } @@ -461,6 +467,12 @@ func (s *Section) reflectFrom(val reflect.Value) error { if err != nil { key, _ = s.NewKey(fieldName, "") } + + // Add comment from comment tag + if len(key.Comment) == 0 { + key.Comment = tpField.Tag.Get("comment") + } + if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil { return fmt.Errorf("error reflecting field (%s): %v", fieldName, err) } diff --git a/vendor/github.com/go-ini/ini/struct_test.go b/vendor/github.com/go-ini/ini/struct_test.go index b8ba25293..75987ea99 100644 --- a/vendor/github.com/go-ini/ini/struct_test.go +++ b/vendor/github.com/go-ini/ini/struct_test.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package ini +package ini_test import ( "bytes" @@ -22,6 +22,7 @@ import ( "time" . "github.com/smartystreets/goconvey/convey" + "gopkg.in/ini.v1" ) type testNested struct { @@ -126,11 +127,11 @@ Born = nil Cities = ` -func Test_Struct(t *testing.T) { +func Test_MapToStruct(t *testing.T) { Convey("Map to struct", t, func() { Convey("Map file to struct", func() { ts := new(testStruct) - So(MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil) + So(ini.MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil) So(ts.Name, ShouldEqual, "Unknwon") So(ts.Age, ShouldEqual, 21) @@ -159,7 +160,7 @@ func Test_Struct(t *testing.T) { Convey("Map section to struct", func() { foobar := new(fooBar) - f, err := Load([]byte(_CONF_DATA_STRUCT)) + f, err := ini.Load([]byte(_CONF_DATA_STRUCT)) So(err, ShouldBeNil) So(f.Section("foo.bar").MapTo(foobar), ShouldBeNil) @@ -168,58 +169,58 @@ func Test_Struct(t *testing.T) { }) Convey("Map to non-pointer struct", func() { - cfg, err := Load([]byte(_CONF_DATA_STRUCT)) + f, err := ini.Load([]byte(_CONF_DATA_STRUCT)) So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) + So(f, ShouldNotBeNil) - So(cfg.MapTo(testStruct{}), ShouldNotBeNil) + So(f.MapTo(testStruct{}), ShouldNotBeNil) }) Convey("Map to unsupported type", func() { - cfg, err := Load([]byte(_CONF_DATA_STRUCT)) + f, err := ini.Load([]byte(_CONF_DATA_STRUCT)) So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) + So(f, ShouldNotBeNil) - cfg.NameMapper = func(raw string) string { + f.NameMapper = func(raw string) string { if raw == "Byte" { return "NAME" } return raw } - So(cfg.MapTo(&unsupport{}), ShouldNotBeNil) - So(cfg.MapTo(&unsupport2{}), ShouldNotBeNil) - So(cfg.MapTo(&unsupport4{}), ShouldNotBeNil) + So(f.MapTo(&unsupport{}), ShouldNotBeNil) + So(f.MapTo(&unsupport2{}), ShouldNotBeNil) + So(f.MapTo(&unsupport4{}), ShouldNotBeNil) }) Convey("Map to omitempty field", func() { ts := new(testStruct) - So(MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil) + So(ini.MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil) So(ts.Omitted, ShouldEqual, true) }) Convey("Map with shadows", func() { - cfg, err := LoadSources(LoadOptions{AllowShadows: true}, []byte(_CONF_DATA_STRUCT)) + f, err := ini.LoadSources(ini.LoadOptions{AllowShadows: true}, []byte(_CONF_DATA_STRUCT)) So(err, ShouldBeNil) ts := new(testStruct) - So(cfg.MapTo(ts), ShouldBeNil) + So(f.MapTo(ts), ShouldBeNil) So(strings.Join(ts.Shadows, " "), ShouldEqual, "1 2 3 4") So(fmt.Sprintf("%v", ts.ShadowInts), ShouldEqual, "[1 2 3 4]") }) Convey("Map from invalid data source", func() { - So(MapTo(&testStruct{}, "hi"), ShouldNotBeNil) + So(ini.MapTo(&testStruct{}, "hi"), ShouldNotBeNil) }) Convey("Map to wrong types and gain default values", func() { - cfg, err := Load([]byte(_INVALID_DATA_CONF_STRUCT)) + f, err := ini.Load([]byte(_INVALID_DATA_CONF_STRUCT)) So(err, ShouldBeNil) t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z") So(err, ShouldBeNil) dv := &defaultValue{"Joe", 10, true, 1.25, t, []string{"HangZhou", "Boston"}} - So(cfg.MapTo(dv), ShouldBeNil) + So(f.MapTo(dv), ShouldBeNil) So(dv.Name, ShouldEqual, "Joe") So(dv.Age, ShouldEqual, 10) So(dv.Male, ShouldBeTrue) @@ -230,7 +231,7 @@ func Test_Struct(t *testing.T) { }) Convey("Map to struct in strict mode", t, func() { - cfg, err := Load([]byte(` + f, err := ini.Load([]byte(` name=bruce age=a30`)) So(err, ShouldBeNil) @@ -241,12 +242,28 @@ age=a30`)) } s := new(Strict) - So(cfg.Section("").StrictMapTo(s), ShouldNotBeNil) + So(f.Section("").StrictMapTo(s), ShouldNotBeNil) }) + Convey("Map slice in strict mode", t, func() { + f, err := ini.Load([]byte(` +names=alice, bruce`)) + So(err, ShouldBeNil) + + type Strict struct { + Names []string `ini:"names"` + } + s := new(Strict) + + So(f.Section("").StrictMapTo(s), ShouldBeNil) + So(fmt.Sprint(s.Names), ShouldEqual, "[alice bruce]") + }) +} + +func Test_ReflectFromStruct(t *testing.T) { Convey("Reflect from struct", t, func() { type Embeded struct { - Dates []time.Time `delim:"|"` + Dates []time.Time `delim:"|" comment:"Time data"` Places []string Years []int Numbers []int64 @@ -258,12 +275,12 @@ age=a30`)) type Author struct { Name string `ini:"NAME"` Male bool - Age int + Age int `comment:"Author's age"` Height uint GPA float64 Date time.Time NeverMind string `ini:"-"` - *Embeded `ini:"infos"` + *Embeded `ini:"infos" comment:"Embeded section"` } t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z") @@ -279,20 +296,23 @@ age=a30`)) []float64{192.168, 10.11}, []int{}, }} - cfg := Empty() - So(ReflectFrom(cfg, a), ShouldBeNil) + cfg := ini.Empty() + So(ini.ReflectFrom(cfg, a), ShouldBeNil) var buf bytes.Buffer _, err = cfg.WriteTo(&buf) So(err, ShouldBeNil) So(buf.String(), ShouldEqual, `NAME = Unknwon Male = true +; Author's age Age = 21 Height = 100 GPA = 2.8 Date = 1993-10-07T20:17:05Z +; Embeded section [infos] +; Time data Dates = 1993-10-07T20:17:05Z|1993-10-07T20:17:05Z Places = HangZhou,Boston Years = 1993,1994 @@ -305,11 +325,11 @@ None = `) Convey("Reflect from non-point struct", func() { - So(ReflectFrom(cfg, Author{}), ShouldNotBeNil) + So(ini.ReflectFrom(cfg, Author{}), ShouldNotBeNil) }) Convey("Reflect from struct with omitempty", func() { - cfg := Empty() + cfg := ini.Empty() type SpecialStruct struct { FirstName string `ini:"first_name"` LastName string `ini:"last_name"` @@ -319,7 +339,7 @@ None = NotEmpty int `ini:"omitempty"` } - So(ReflectFrom(cfg, &SpecialStruct{FirstName: "John", LastName: "Doe", NotEmpty: 9}), ShouldBeNil) + So(ini.ReflectFrom(cfg, &SpecialStruct{FirstName: "John", LastName: "Doe", NotEmpty: 9}), ShouldBeNil) var buf bytes.Buffer _, err = cfg.WriteTo(&buf) @@ -338,15 +358,30 @@ type testMapper struct { func Test_NameGetter(t *testing.T) { Convey("Test name mappers", t, func() { - So(MapToWithMapper(&testMapper{}, TitleUnderscore, []byte("packag_name=ini")), ShouldBeNil) + So(ini.MapToWithMapper(&testMapper{}, ini.TitleUnderscore, []byte("packag_name=ini")), ShouldBeNil) - cfg, err := Load([]byte("PACKAGE_NAME=ini")) + cfg, err := ini.Load([]byte("PACKAGE_NAME=ini")) So(err, ShouldBeNil) So(cfg, ShouldNotBeNil) - cfg.NameMapper = AllCapsUnderscore + cfg.NameMapper = ini.AllCapsUnderscore tg := new(testMapper) So(cfg.MapTo(tg), ShouldBeNil) So(tg.PackageName, ShouldEqual, "ini") }) } + +type testDurationStruct struct { + Duration time.Duration `ini:"Duration"` +} + +func Test_Duration(t *testing.T) { + Convey("Duration less than 16m50s", t, func() { + ds := new(testDurationStruct) + So(ini.MapTo(ds, []byte("Duration=16m49s")), ShouldBeNil) + + dur, err := time.ParseDuration("16m49s") + So(err, ShouldBeNil) + So(ds.Duration.Seconds(), ShouldEqual, dur.Seconds()) + }) +} diff --git a/vendor/github.com/go-ini/ini/testdata/aicc.ini b/vendor/github.com/go-ini/ini/testdata/aicc.ini deleted file mode 100644 index 59a61970d..000000000 --- a/vendor/github.com/go-ini/ini/testdata/aicc.ini +++ /dev/null @@ -1,11 +0,0 @@ -[Core] - Lesson_Location = 87 -Lesson_Status = C - Score = 3 -Time = 00:02:30 - -[CORE_LESSON] -my lesson state data – 1111111111111111111000000000000000001110000 -111111111111111111100000000000111000000000 – end my lesson state data -[COMMENTS] -<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1> diff --git a/vendor/github.com/go-ini/ini/testdata/full.ini b/vendor/github.com/go-ini/ini/testdata/full.ini new file mode 100644 index 000000000..469b1f13e --- /dev/null +++ b/vendor/github.com/go-ini/ini/testdata/full.ini @@ -0,0 +1,83 @@ +; Package name +NAME = ini +; Package version +VERSION = v1 +; Package import path +IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s + +# Information about package author +# Bio can be written in multiple lines. +[author] +NAME = Unknwon +E-MAIL = u@gogs.io +GITHUB = https://github.com/%(NAME)s +BIO = """Gopher. +Coding addict. +Good man. +""" # Succeeding comment + +[package] +CLONE_URL = https://%(IMPORT_PATH)s + +[package.sub] +UNUSED_KEY = should be deleted + +[features] +-: Support read/write comments of keys and sections +-: Support auto-increment of key names +-: Support load multiple files to overwrite key values + +[types] +STRING = str +BOOL = true +BOOL_FALSE = false +FLOAT64 = 1.25 +INT = 10 +TIME = 2015-01-01T20:17:05Z +DURATION = 2h45m +UINT = 3 + +[array] +STRINGS = en, zh, de +FLOAT64S = 1.1, 2.2, 3.3 +INTS = 1, 2, 3 +UINTS = 1, 2, 3 +TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z + +[note] +empty_lines = next line is empty\ + +; Comment before the section +[comments] ; This is a comment for the section too +; Comment before key +key = "value" +key2 = "value2" ; This is a comment for key2 +key3 = "one", "two", "three" + +[string escapes] +key1 = value1, value2, value3 +key2 = value1\, value2 +key3 = val\ue1, value2 +key4 = value1\\, value\\\\2 +key5 = value1\,, value2 +key6 = aaa bbb\ and\ space ccc + +[advance] +value with quotes = "some value" +value quote2 again = 'some value' +includes comment sign = `my#password` +includes comment sign2 = `my;password` +true = 2+3=5 +"1+1=2" = true +"""6+1=7""" = true +"""`5+5`""" = 10 +`"6+6"` = 12 +`7-2=4` = false +ADDRESS = `404 road, +NotFound, State, 50000` +two_lines = how about \ + continuation lines? +lots_of_lines = 1 \ + 2 \ + 3 \ + 4 \ diff --git a/vendor/github.com/go-ini/ini/testdata/conf.ini b/vendor/github.com/go-ini/ini/testdata/minimal.ini index f8e7ec89f..f8e7ec89f 100644 --- a/vendor/github.com/go-ini/ini/testdata/conf.ini +++ b/vendor/github.com/go-ini/ini/testdata/minimal.ini diff --git a/vendor/github.com/go-redis/redis/commands.go b/vendor/github.com/go-redis/redis/commands.go index a3b90f12d..c04b3c49b 100644 --- a/vendor/github.com/go-redis/redis/commands.go +++ b/vendor/github.com/go-redis/redis/commands.go @@ -214,6 +214,7 @@ type Cmdable interface { ScriptKill() *StatusCmd ScriptLoad(script string) *StringCmd DebugObject(key string) *StringCmd + Publish(channel string, message interface{}) *IntCmd PubSubChannels(pattern string) *StringSliceCmd PubSubNumSub(channels ...string) *StringIntMapCmd PubSubNumPat() *IntCmd @@ -1880,8 +1881,8 @@ func (c *cmdable) DebugObject(key string) *StringCmd { //------------------------------------------------------------------------------ // Publish posts the message to the channel. -func (c *cmdable) Publish(channel, message string) *IntCmd { - cmd := NewIntCmd("PUBLISH", channel, message) +func (c *cmdable) Publish(channel string, message interface{}) *IntCmd { + cmd := NewIntCmd("publish", channel, message) c.process(cmd) return cmd } diff --git a/vendor/github.com/go-redis/redis/internal/pool/pool.go b/vendor/github.com/go-redis/redis/internal/pool/pool.go index 836ec1045..ae81905ea 100644 --- a/vendor/github.com/go-redis/redis/internal/pool/pool.go +++ b/vendor/github.com/go-redis/redis/internal/pool/pool.go @@ -60,8 +60,10 @@ type Options struct { type ConnPool struct { opt *Options - dialErrorsNum uint32 // atomic - _lastDialError atomic.Value + dialErrorsNum uint32 // atomic + + lastDialError error + lastDialErrorMu sync.RWMutex queue chan struct{} @@ -98,7 +100,7 @@ func (p *ConnPool) NewConn() (*Conn, error) { } if atomic.LoadUint32(&p.dialErrorsNum) >= uint32(p.opt.PoolSize) { - return nil, p.lastDialError() + return nil, p.getLastDialError() } netConn, err := p.opt.Dialer() @@ -138,11 +140,16 @@ func (p *ConnPool) tryDial() { } func (p *ConnPool) setLastDialError(err error) { - p._lastDialError.Store(err) + p.lastDialErrorMu.Lock() + p.lastDialError = err + p.lastDialErrorMu.Unlock() } -func (p *ConnPool) lastDialError() error { - return p._lastDialError.Load().(error) +func (p *ConnPool) getLastDialError() error { + p.lastDialErrorMu.RLock() + err := p.lastDialError + p.lastDialErrorMu.RUnlock() + return err } // Get returns existed connection from the pool or creates a new one. diff --git a/vendor/github.com/go-redis/redis/internal/proto/scan.go b/vendor/github.com/go-redis/redis/internal/proto/scan.go index 0431a877d..03c8b59aa 100644 --- a/vendor/github.com/go-redis/redis/internal/proto/scan.go +++ b/vendor/github.com/go-redis/redis/internal/proto/scan.go @@ -120,8 +120,9 @@ func ScanSlice(data []string, slice interface{}) error { return fmt.Errorf("redis: ScanSlice(non-slice %T)", slice) } + next := internal.MakeSliceNextElemFunc(v) for i, s := range data { - elem := internal.SliceNextElem(v) + elem := next() if err := Scan(internal.StringToBytes(s), elem.Addr().Interface()); err != nil { return fmt.Errorf("redis: ScanSlice(index=%d value=%q) failed: %s", i, s, err) } diff --git a/vendor/github.com/go-redis/redis/internal/util.go b/vendor/github.com/go-redis/redis/internal/util.go index 520596fd9..1ba9805fe 100644 --- a/vendor/github.com/go-redis/redis/internal/util.go +++ b/vendor/github.com/go-redis/redis/internal/util.go @@ -28,20 +28,35 @@ func isLower(s string) bool { return true } -func SliceNextElem(v reflect.Value) reflect.Value { - if v.Len() < v.Cap() { - v.Set(v.Slice(0, v.Len()+1)) - return v.Index(v.Len() - 1) - } - +func MakeSliceNextElemFunc(v reflect.Value) func() reflect.Value { elemType := v.Type().Elem() if elemType.Kind() == reflect.Ptr { - elem := reflect.New(elemType.Elem()) - v.Set(reflect.Append(v, elem)) - return elem.Elem() + elemType = elemType.Elem() + return func() reflect.Value { + if v.Len() < v.Cap() { + v.Set(v.Slice(0, v.Len()+1)) + elem := v.Index(v.Len() - 1) + if elem.IsNil() { + elem.Set(reflect.New(elemType)) + } + return elem.Elem() + } + + elem := reflect.New(elemType) + v.Set(reflect.Append(v, elem)) + return elem.Elem() + } } - v.Set(reflect.Append(v, reflect.Zero(elemType))) - return v.Index(v.Len() - 1) + zero := reflect.Zero(elemType) + return func() reflect.Value { + if v.Len() < v.Cap() { + v.Set(v.Slice(0, v.Len()+1)) + return v.Index(v.Len() - 1) + } + + v.Set(reflect.Append(v, zero)) + return v.Index(v.Len() - 1) + } } diff --git a/vendor/github.com/go-redis/redis/pubsub.go b/vendor/github.com/go-redis/redis/pubsub.go index e754a16f2..01f8a61aa 100644 --- a/vendor/github.com/go-redis/redis/pubsub.go +++ b/vendor/github.com/go-redis/redis/pubsub.go @@ -29,6 +29,9 @@ type PubSub struct { closed bool cmd *Cmd + + chOnce sync.Once + ch chan *Message } func (c *PubSub) conn() (*pool.Conn, error) { @@ -346,24 +349,27 @@ func (c *PubSub) receiveMessage(timeout time.Duration) (*Message, error) { } } -// Channel returns a channel for concurrently receiving messages. -// The channel is closed with PubSub. +// Channel returns a Go channel for concurrently receiving messages. +// The channel is closed with PubSub. Receive or ReceiveMessage APIs +// can not be used after channel is created. func (c *PubSub) Channel() <-chan *Message { - ch := make(chan *Message, 100) - go func() { - for { - msg, err := c.ReceiveMessage() - if err != nil { - if err == pool.ErrClosed { - break + c.chOnce.Do(func() { + c.ch = make(chan *Message, 100) + go func() { + for { + msg, err := c.ReceiveMessage() + if err != nil { + if err == pool.ErrClosed { + break + } + continue } - continue + c.ch <- msg } - ch <- msg - } - close(ch) - }() - return ch + close(c.ch) + }() + }) + return c.ch } func appendIfNotExists(ss []string, es ...string) []string { diff --git a/vendor/github.com/go-redis/redis/pubsub_test.go b/vendor/github.com/go-redis/redis/pubsub_test.go index 6fc04a198..6a85bd038 100644 --- a/vendor/github.com/go-redis/redis/pubsub_test.go +++ b/vendor/github.com/go-redis/redis/pubsub_test.go @@ -424,4 +424,20 @@ var _ = Describe("PubSub", func() { wg.Wait() }) + + It("handles big message payload", func() { + pubsub := client.Subscribe("mychannel") + defer pubsub.Close() + + ch := pubsub.Channel() + + bigVal := bigVal() + err := client.Publish("mychannel", bigVal).Err() + Expect(err).NotTo(HaveOccurred()) + + var msg *redis.Message + Eventually(ch).Should(Receive(&msg)) + Expect(msg.Channel).To(Equal("mychannel")) + Expect(msg.Payload).To(Equal(string(bigVal))) + }) }) diff --git a/vendor/github.com/go-redis/redis/race_test.go b/vendor/github.com/go-redis/redis/race_test.go index 5bcb0768e..14264086c 100644 --- a/vendor/github.com/go-redis/redis/race_test.go +++ b/vendor/github.com/go-redis/redis/race_test.go @@ -105,7 +105,7 @@ var _ = Describe("races", func() { It("should handle big vals in Get", func() { C, N = 4, 100 - bigVal := bytes.Repeat([]byte{'*'}, 1<<17) // 128kb + bigVal := bigVal() err := client.Set("key", bigVal, 0).Err() Expect(err).NotTo(HaveOccurred()) @@ -126,8 +126,7 @@ var _ = Describe("races", func() { It("should handle big vals in Set", func() { C, N = 4, 100 - bigVal := bytes.Repeat([]byte{'*'}, 1<<17) // 128kb - + bigVal := bigVal() perform(C, func(id int) { for i := 0; i < N; i++ { err := client.Set("key", bigVal, 0).Err() @@ -245,3 +244,7 @@ var _ = Describe("races", func() { Expect(n).To(Equal(int64(N))) }) }) + +func bigVal() []byte { + return bytes.Repeat([]byte{'*'}, 1<<17) // 128kb +} diff --git a/vendor/github.com/golang/protobuf/README.md b/vendor/github.com/golang/protobuf/README.md index 207eb6b48..9c4c815c0 100644 --- a/vendor/github.com/golang/protobuf/README.md +++ b/vendor/github.com/golang/protobuf/README.md @@ -1,6 +1,7 @@ # Go support for Protocol Buffers [![Build Status](https://travis-ci.org/golang/protobuf.svg?branch=master)](https://travis-ci.org/golang/protobuf) +[![GoDoc](https://godoc.org/github.com/golang/protobuf?status.svg)](https://godoc.org/github.com/golang/protobuf) Google's data interchange format. Copyright 2010 The Go Authors. diff --git a/vendor/github.com/gorilla/handlers/.travis.yml b/vendor/github.com/gorilla/handlers/.travis.yml index 4ea1e7a1f..1ba74af10 100644 --- a/vendor/github.com/gorilla/handlers/.travis.yml +++ b/vendor/github.com/gorilla/handlers/.travis.yml @@ -7,6 +7,7 @@ matrix: - go: 1.5 - go: 1.6 - go: 1.7 + - go: 1.8 - go: tip allow_failures: - go: tip @@ -16,3 +17,4 @@ script: - diff -u <(echo -n) <(gofmt -d .) - go vet $(go list ./... | grep -v /vendor/) - go test -v -race ./... + diff --git a/vendor/github.com/gorilla/handlers/cors.go b/vendor/github.com/gorilla/handlers/cors.go index 1f92d1ad4..1cf7581ce 100644 --- a/vendor/github.com/gorilla/handlers/cors.go +++ b/vendor/github.com/gorilla/handlers/cors.go @@ -110,7 +110,17 @@ func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set(corsVaryHeader, corsOriginHeader) } - w.Header().Set(corsAllowOriginHeader, origin) + returnOrigin := origin + for _, o := range ch.allowedOrigins { + // A configuration of * is different than explicitly setting an allowed + // origin. Returning arbitrary origin headers an an access control allow + // origin header is unsafe and is not required by any use case. + if o == corsOriginMatchAll { + returnOrigin = "*" + break + } + } + w.Header().Set(corsAllowOriginHeader, returnOrigin) if r.Method == corsOptionMethod { return diff --git a/vendor/github.com/gorilla/handlers/cors_test.go b/vendor/github.com/gorilla/handlers/cors_test.go index c63913eee..61eb18f77 100644 --- a/vendor/github.com/gorilla/handlers/cors_test.go +++ b/vendor/github.com/gorilla/handlers/cors_test.go @@ -327,10 +327,45 @@ func TestCORSHandlerWithCustomValidator(t *testing.T) { return false } - CORS(AllowedOriginValidator(originValidator))(testHandler).ServeHTTP(rr, r) + // Specially craft a CORS object. + handleFunc := func(h http.Handler) http.Handler { + c := &cors{ + allowedMethods: defaultCorsMethods, + allowedHeaders: defaultCorsHeaders, + allowedOrigins: []string{"http://a.example.com"}, + h: h, + } + AllowedOriginValidator(originValidator)(c) + return c + } + + handleFunc(testHandler).ServeHTTP(rr, r) header := rr.HeaderMap.Get(corsAllowOriginHeader) if header != r.URL.String() { t.Fatalf("bad header: expected %s to be %s, got %s.", corsAllowOriginHeader, r.URL.String(), header) } } + +func TestCORSAllowStar(t *testing.T) { + r := newRequest("GET", "http://a.example.com") + r.Header.Set("Origin", r.URL.String()) + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + originValidator := func(origin string) bool { + if strings.HasSuffix(origin, ".example.com") { + return true + } + return false + } + + CORS(AllowedOriginValidator(originValidator))(testHandler).ServeHTTP(rr, r) + header := rr.HeaderMap.Get(corsAllowOriginHeader) + // Because * is the default CORS policy (which is safe), we should be + // expect a * returned here as the Access Control Allow Origin header + if header != "*" { + t.Fatalf("bad header: expected %s to be %s, got %s.", corsAllowOriginHeader, r.URL.String(), header) + } + +} diff --git a/vendor/github.com/gorilla/mux/.travis.yml b/vendor/github.com/gorilla/mux/.travis.yml index ca377e61c..3302233f3 100644 --- a/vendor/github.com/gorilla/mux/.travis.yml +++ b/vendor/github.com/gorilla/mux/.travis.yml @@ -3,13 +3,13 @@ sudo: false matrix: include: - - go: 1.2 - - go: 1.3 - - go: 1.4 - go: 1.5 - go: 1.6 - go: 1.7 - go: 1.8 + - go: 1.9 + - go: tip + allow_failures: - go: tip install: diff --git a/vendor/github.com/gorilla/mux/README.md b/vendor/github.com/gorilla/mux/README.md index 8dcd71887..67a79e00a 100644 --- a/vendor/github.com/gorilla/mux/README.md +++ b/vendor/github.com/gorilla/mux/README.md @@ -135,6 +135,14 @@ r.HandleFunc("/products", ProductsHandler). Schemes("http") ``` +Routes are tested in the order they were added to the router. If two routes match, the first one wins: + +```go +r := mux.NewRouter() +r.HandleFunc("/specific", specificHandler) +r.PathPrefix("/").Handler(catchAllHandler) +``` + Setting the same matching conditions again and again can be boring, so we have a way to group several routes that share the same requirements. We call it "subrouting". For example, let's say we have several URLs that should only match when the host is `www.example.com`. Create a route for that host and get a "subrouter" from it: @@ -193,22 +201,34 @@ func main() { r.HandleFunc("/products", handler).Methods("POST") r.HandleFunc("/articles", handler).Methods("GET") r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT") + r.HandleFunc("/authors", handler).Queries("surname", "{surname}") r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { t, err := route.GetPathTemplate() if err != nil { return err } + qt, err := route.GetQueriesTemplates() + if err != nil { + return err + } // p will contain regular expression is compatible with regular expression in Perl, Python, and other languages. // for instance the regular expression for path '/articles/{id}' will be '^/articles/(?P<v0>[^/]+)$' p, err := route.GetPathRegexp() if err != nil { return err } + // qr will contain a list of regular expressions with the same semantics as GetPathRegexp, + // just applied to the Queries pairs instead, e.g., 'Queries("surname", "{surname}") will return + // {"^surname=(?P<v0>.*)$}. Where each combined query pair will have an entry in the list. + qr, err := route.GetQueriesRegexp() + if err != nil { + return err + } m, err := route.GetMethods() if err != nil { return err } - fmt.Println(strings.Join(m, ","), t, p) + fmt.Println(strings.Join(m, ","), strings.Join(qt, ","), strings.Join(qr, ","), t, p) return nil }) http.Handle("/", r) @@ -331,22 +351,34 @@ r.HandleFunc("/", handler) r.HandleFunc("/products", handler).Methods("POST") r.HandleFunc("/articles", handler).Methods("GET") r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT") +r.HandleFunc("/authors", handler).Queries("surname", "{surname}") r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { t, err := route.GetPathTemplate() if err != nil { return err } + qt, err := route.GetQueriesTemplates() + if err != nil { + return err + } // p will contain a regular expression that is compatible with regular expressions in Perl, Python, and other languages. // For example, the regular expression for path '/articles/{id}' will be '^/articles/(?P<v0>[^/]+)$'. p, err := route.GetPathRegexp() if err != nil { return err } + // qr will contain a list of regular expressions with the same semantics as GetPathRegexp, + // just applied to the Queries pairs instead, e.g., 'Queries("surname", "{surname}") will return + // {"^surname=(?P<v0>.*)$}. Where each combined query pair will have an entry in the list. + qr, err := route.GetQueriesRegexp() + if err != nil { + return err + } m, err := route.GetMethods() if err != nil { return err } - fmt.Println(strings.Join(m, ","), t, p) + fmt.Println(strings.Join(m, ","), strings.Join(qt, ","), strings.Join(qr, ","), t, p) return nil }) ``` diff --git a/vendor/github.com/gorilla/mux/mux.go b/vendor/github.com/gorilla/mux/mux.go index fb69196db..49de78923 100644 --- a/vendor/github.com/gorilla/mux/mux.go +++ b/vendor/github.com/gorilla/mux/mux.go @@ -10,11 +10,11 @@ import ( "net/http" "path" "regexp" - "strings" ) var ( ErrMethodMismatch = errors.New("method is not allowed") + ErrNotFound = errors.New("no matching route was found") ) // NewRouter returns a new router instance. @@ -65,7 +65,17 @@ type Router struct { useEncodedPath bool } -// Match matches registered routes against the request. +// Match attempts to match the given request against the router's registered routes. +// +// If the request matches a route of this router or one of its subrouters the Route, +// Handler, and Vars fields of the the match argument are filled and this function +// returns true. +// +// If the request does not match any of this router's or its subrouters' routes +// then this function returns false. If available, a reason for the match failure +// will be filled in the match argument's MatchErr field. If the match failure type +// (eg: not found) has a registered handler, the handler is assigned to the Handler +// field of the match argument. func (r *Router) Match(req *http.Request, match *RouteMatch) bool { for _, route := range r.routes { if route.Match(req, match) { @@ -73,16 +83,23 @@ func (r *Router) Match(req *http.Request, match *RouteMatch) bool { } } - if match.MatchErr == ErrMethodMismatch && r.MethodNotAllowedHandler != nil { - match.Handler = r.MethodNotAllowedHandler - return true + if match.MatchErr == ErrMethodMismatch { + if r.MethodNotAllowedHandler != nil { + match.Handler = r.MethodNotAllowedHandler + return true + } else { + return false + } } // Closest match for a router (includes sub-routers) if r.NotFoundHandler != nil { match.Handler = r.NotFoundHandler + match.MatchErr = ErrNotFound return true } + + match.MatchErr = ErrNotFound return false } @@ -94,7 +111,7 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { if !r.skipClean { path := req.URL.Path if r.useEncodedPath { - path = getPath(req) + path = req.URL.EscapedPath() } // Clean path to canonical form and redirect. if p := cleanPath(path); p != path { @@ -409,28 +426,6 @@ func setCurrentRoute(r *http.Request, val interface{}) *http.Request { // Helpers // ---------------------------------------------------------------------------- -// getPath returns the escaped path if possible; doing what URL.EscapedPath() -// which was added in go1.5 does -func getPath(req *http.Request) string { - if req.RequestURI != "" { - // Extract the path from RequestURI (which is escaped unlike URL.Path) - // as detailed here as detailed in https://golang.org/pkg/net/url/#URL - // for < 1.5 server side workaround - // http://localhost/path/here?v=1 -> /path/here - path := req.RequestURI - path = strings.TrimPrefix(path, req.URL.Scheme+`://`) - path = strings.TrimPrefix(path, req.URL.Host) - if i := strings.LastIndex(path, "?"); i > -1 { - path = path[:i] - } - if i := strings.LastIndex(path, "#"); i > -1 { - path = path[:i] - } - return path - } - return req.URL.Path -} - // cleanPath returns the canonical path for p, eliminating . and .. elements. // Borrowed from the net/http package. func cleanPath(p string) string { diff --git a/vendor/github.com/gorilla/mux/mux_test.go b/vendor/github.com/gorilla/mux/mux_test.go index 484fab431..6c7e30d19 100644 --- a/vendor/github.com/gorilla/mux/mux_test.go +++ b/vendor/github.com/gorilla/mux/mux_test.go @@ -29,20 +29,22 @@ func (r *routeRegexp) GoString() string { } type routeTest struct { - title string // title of the test - route *Route // the route being tested - request *http.Request // a request to test the route - vars map[string]string // the expected vars of the match - scheme string // the expected scheme of the built URL - host string // the expected host of the built URL - path string // the expected path of the built URL - query string // the expected query string of the built URL - pathTemplate string // the expected path template of the route - hostTemplate string // the expected host template of the route - methods []string // the expected route methods - pathRegexp string // the expected path regexp - shouldMatch bool // whether the request is expected to match the route at all - shouldRedirect bool // whether the request should result in a redirect + title string // title of the test + route *Route // the route being tested + request *http.Request // a request to test the route + vars map[string]string // the expected vars of the match + scheme string // the expected scheme of the built URL + host string // the expected host of the built URL + path string // the expected path of the built URL + query string // the expected query string of the built URL + pathTemplate string // the expected path template of the route + hostTemplate string // the expected host template of the route + queriesTemplate string // the expected query template of the route + methods []string // the expected route methods + pathRegexp string // the expected path regexp + queriesRegexp string // the expected query regexp + shouldMatch bool // whether the request is expected to match the route at all + shouldRedirect bool // whether the request should result in a redirect } func TestHost(t *testing.T) { @@ -739,257 +741,309 @@ func TestMethods(t *testing.T) { func TestQueries(t *testing.T) { tests := []routeTest{ { - title: "Queries route, match", - route: new(Route).Queries("foo", "bar", "baz", "ding"), - request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), - vars: map[string]string{}, - host: "", - path: "", - query: "foo=bar&baz=ding", - shouldMatch: true, - }, - { - title: "Queries route, match with a query string", - route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"), - request: newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"), - vars: map[string]string{}, - host: "", - path: "", - query: "foo=bar&baz=ding", - pathTemplate: `/api`, - hostTemplate: `www.example.com`, - shouldMatch: true, - }, - { - title: "Queries route, match with a query string out of order", - route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"), - request: newRequest("GET", "http://www.example.com/api?baz=ding&foo=bar"), - vars: map[string]string{}, - host: "", - path: "", - query: "foo=bar&baz=ding", - pathTemplate: `/api`, - hostTemplate: `www.example.com`, - shouldMatch: true, - }, - { - title: "Queries route, bad query", - route: new(Route).Queries("foo", "bar", "baz", "ding"), - request: newRequest("GET", "http://localhost?foo=bar&baz=dong"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with pattern, match", - route: new(Route).Queries("foo", "{v1}"), - request: newRequest("GET", "http://localhost?foo=bar"), - vars: map[string]string{"v1": "bar"}, - host: "", - path: "", - query: "foo=bar", - shouldMatch: true, - }, - { - title: "Queries route with multiple patterns, match", - route: new(Route).Queries("foo", "{v1}", "baz", "{v2}"), - request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), - vars: map[string]string{"v1": "bar", "v2": "ding"}, - host: "", - path: "", - query: "foo=bar&baz=ding", - shouldMatch: true, - }, - { - title: "Queries route with regexp pattern, match", - route: new(Route).Queries("foo", "{v1:[0-9]+}"), - request: newRequest("GET", "http://localhost?foo=10"), - vars: map[string]string{"v1": "10"}, - host: "", - path: "", - query: "foo=10", - shouldMatch: true, - }, - { - title: "Queries route with regexp pattern, regexp does not match", - route: new(Route).Queries("foo", "{v1:[0-9]+}"), - request: newRequest("GET", "http://localhost?foo=a"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with regexp pattern with quantifier, match", - route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), - request: newRequest("GET", "http://localhost?foo=1"), - vars: map[string]string{"v1": "1"}, - host: "", - path: "", - query: "foo=1", - shouldMatch: true, - }, - { - title: "Queries route with regexp pattern with quantifier, additional variable in query string, match", - route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), - request: newRequest("GET", "http://localhost?bar=2&foo=1"), - vars: map[string]string{"v1": "1"}, - host: "", - path: "", - query: "foo=1", - shouldMatch: true, - }, - { - title: "Queries route with regexp pattern with quantifier, regexp does not match", - route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), - request: newRequest("GET", "http://localhost?foo=12"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with regexp pattern with quantifier, additional capturing group", - route: new(Route).Queries("foo", "{v1:[0-9]{1}(?:a|b)}"), - request: newRequest("GET", "http://localhost?foo=1a"), - vars: map[string]string{"v1": "1a"}, - host: "", - path: "", - query: "foo=1a", - shouldMatch: true, - }, - { - title: "Queries route with regexp pattern with quantifier, additional variable in query string, regexp does not match", - route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), - request: newRequest("GET", "http://localhost?foo=12"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with hyphenated name, match", - route: new(Route).Queries("foo", "{v-1}"), - request: newRequest("GET", "http://localhost?foo=bar"), - vars: map[string]string{"v-1": "bar"}, - host: "", - path: "", - query: "foo=bar", - shouldMatch: true, - }, - { - title: "Queries route with multiple hyphenated names, match", - route: new(Route).Queries("foo", "{v-1}", "baz", "{v-2}"), - request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), - vars: map[string]string{"v-1": "bar", "v-2": "ding"}, - host: "", - path: "", - query: "foo=bar&baz=ding", - shouldMatch: true, - }, - { - title: "Queries route with hyphenate name and pattern, match", - route: new(Route).Queries("foo", "{v-1:[0-9]+}"), - request: newRequest("GET", "http://localhost?foo=10"), - vars: map[string]string{"v-1": "10"}, - host: "", - path: "", - query: "foo=10", - shouldMatch: true, - }, - { - title: "Queries route with hyphenated name and pattern with quantifier, additional capturing group", - route: new(Route).Queries("foo", "{v-1:[0-9]{1}(?:a|b)}"), - request: newRequest("GET", "http://localhost?foo=1a"), - vars: map[string]string{"v-1": "1a"}, - host: "", - path: "", - query: "foo=1a", - shouldMatch: true, - }, - { - title: "Queries route with empty value, should match", - route: new(Route).Queries("foo", ""), - request: newRequest("GET", "http://localhost?foo=bar"), - vars: map[string]string{}, - host: "", - path: "", - query: "foo=", - shouldMatch: true, - }, - { - title: "Queries route with empty value and no parameter in request, should not match", - route: new(Route).Queries("foo", ""), - request: newRequest("GET", "http://localhost"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with empty value and empty parameter in request, should match", - route: new(Route).Queries("foo", ""), - request: newRequest("GET", "http://localhost?foo="), - vars: map[string]string{}, - host: "", - path: "", - query: "foo=", - shouldMatch: true, - }, - { - title: "Queries route with overlapping value, should not match", - route: new(Route).Queries("foo", "bar"), - request: newRequest("GET", "http://localhost?foo=barfoo"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with no parameter in request, should not match", - route: new(Route).Queries("foo", "{bar}"), - request: newRequest("GET", "http://localhost"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with empty parameter in request, should match", - route: new(Route).Queries("foo", "{bar}"), - request: newRequest("GET", "http://localhost?foo="), - vars: map[string]string{"foo": ""}, - host: "", - path: "", - query: "foo=", - shouldMatch: true, - }, - { - title: "Queries route, bad submatch", - route: new(Route).Queries("foo", "bar", "baz", "ding"), - request: newRequest("GET", "http://localhost?fffoo=bar&baz=dingggg"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with pattern, match, escaped value", - route: new(Route).Queries("foo", "{v1}"), - request: newRequest("GET", "http://localhost?foo=%25bar%26%20%2F%3D%3F"), - vars: map[string]string{"v1": "%bar& /=?"}, - host: "", - path: "", - query: "foo=%25bar%26+%2F%3D%3F", - shouldMatch: true, + title: "Queries route, match", + route: new(Route).Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), + vars: map[string]string{}, + host: "", + path: "", + query: "foo=bar&baz=ding", + queriesTemplate: "foo=bar,baz=ding", + queriesRegexp: "^foo=bar$,^baz=ding$", + shouldMatch: true, + }, + { + title: "Queries route, match with a query string", + route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"), + vars: map[string]string{}, + host: "", + path: "", + query: "foo=bar&baz=ding", + pathTemplate: `/api`, + hostTemplate: `www.example.com`, + queriesTemplate: "foo=bar,baz=ding", + queriesRegexp: "^foo=bar$,^baz=ding$", + shouldMatch: true, + }, + { + title: "Queries route, match with a query string out of order", + route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://www.example.com/api?baz=ding&foo=bar"), + vars: map[string]string{}, + host: "", + path: "", + query: "foo=bar&baz=ding", + pathTemplate: `/api`, + hostTemplate: `www.example.com`, + queriesTemplate: "foo=bar,baz=ding", + queriesRegexp: "^foo=bar$,^baz=ding$", + shouldMatch: true, + }, + { + title: "Queries route, bad query", + route: new(Route).Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://localhost?foo=bar&baz=dong"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo=bar,baz=ding", + queriesRegexp: "^foo=bar$,^baz=ding$", + shouldMatch: false, + }, + { + title: "Queries route with pattern, match", + route: new(Route).Queries("foo", "{v1}"), + request: newRequest("GET", "http://localhost?foo=bar"), + vars: map[string]string{"v1": "bar"}, + host: "", + path: "", + query: "foo=bar", + queriesTemplate: "foo={v1}", + queriesRegexp: "^foo=(?P<v0>.*)$", + shouldMatch: true, + }, + { + title: "Queries route with multiple patterns, match", + route: new(Route).Queries("foo", "{v1}", "baz", "{v2}"), + request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), + vars: map[string]string{"v1": "bar", "v2": "ding"}, + host: "", + path: "", + query: "foo=bar&baz=ding", + queriesTemplate: "foo={v1},baz={v2}", + queriesRegexp: "^foo=(?P<v0>.*)$,^baz=(?P<v0>.*)$", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern, match", + route: new(Route).Queries("foo", "{v1:[0-9]+}"), + request: newRequest("GET", "http://localhost?foo=10"), + vars: map[string]string{"v1": "10"}, + host: "", + path: "", + query: "foo=10", + queriesTemplate: "foo={v1:[0-9]+}", + queriesRegexp: "^foo=(?P<v0>[0-9]+)$", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern, regexp does not match", + route: new(Route).Queries("foo", "{v1:[0-9]+}"), + request: newRequest("GET", "http://localhost?foo=a"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo={v1:[0-9]+}", + queriesRegexp: "^foo=(?P<v0>[0-9]+)$", + shouldMatch: false, + }, + { + title: "Queries route with regexp pattern with quantifier, match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?foo=1"), + vars: map[string]string{"v1": "1"}, + host: "", + path: "", + query: "foo=1", + queriesTemplate: "foo={v1:[0-9]{1}}", + queriesRegexp: "^foo=(?P<v0>[0-9]{1})$", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern with quantifier, additional variable in query string, match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?bar=2&foo=1"), + vars: map[string]string{"v1": "1"}, + host: "", + path: "", + query: "foo=1", + queriesTemplate: "foo={v1:[0-9]{1}}", + queriesRegexp: "^foo=(?P<v0>[0-9]{1})$", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern with quantifier, regexp does not match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?foo=12"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo={v1:[0-9]{1}}", + queriesRegexp: "^foo=(?P<v0>[0-9]{1})$", + shouldMatch: false, + }, + { + title: "Queries route with regexp pattern with quantifier, additional capturing group", + route: new(Route).Queries("foo", "{v1:[0-9]{1}(?:a|b)}"), + request: newRequest("GET", "http://localhost?foo=1a"), + vars: map[string]string{"v1": "1a"}, + host: "", + path: "", + query: "foo=1a", + queriesTemplate: "foo={v1:[0-9]{1}(?:a|b)}", + queriesRegexp: "^foo=(?P<v0>[0-9]{1}(?:a|b))$", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern with quantifier, additional variable in query string, regexp does not match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?foo=12"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo={v1:[0-9]{1}}", + queriesRegexp: "^foo=(?P<v0>[0-9]{1})$", + shouldMatch: false, + }, + { + title: "Queries route with hyphenated name, match", + route: new(Route).Queries("foo", "{v-1}"), + request: newRequest("GET", "http://localhost?foo=bar"), + vars: map[string]string{"v-1": "bar"}, + host: "", + path: "", + query: "foo=bar", + queriesTemplate: "foo={v-1}", + queriesRegexp: "^foo=(?P<v0>.*)$", + shouldMatch: true, + }, + { + title: "Queries route with multiple hyphenated names, match", + route: new(Route).Queries("foo", "{v-1}", "baz", "{v-2}"), + request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), + vars: map[string]string{"v-1": "bar", "v-2": "ding"}, + host: "", + path: "", + query: "foo=bar&baz=ding", + queriesTemplate: "foo={v-1},baz={v-2}", + queriesRegexp: "^foo=(?P<v0>.*)$,^baz=(?P<v0>.*)$", + shouldMatch: true, + }, + { + title: "Queries route with hyphenate name and pattern, match", + route: new(Route).Queries("foo", "{v-1:[0-9]+}"), + request: newRequest("GET", "http://localhost?foo=10"), + vars: map[string]string{"v-1": "10"}, + host: "", + path: "", + query: "foo=10", + queriesTemplate: "foo={v-1:[0-9]+}", + queriesRegexp: "^foo=(?P<v0>[0-9]+)$", + shouldMatch: true, + }, + { + title: "Queries route with hyphenated name and pattern with quantifier, additional capturing group", + route: new(Route).Queries("foo", "{v-1:[0-9]{1}(?:a|b)}"), + request: newRequest("GET", "http://localhost?foo=1a"), + vars: map[string]string{"v-1": "1a"}, + host: "", + path: "", + query: "foo=1a", + queriesTemplate: "foo={v-1:[0-9]{1}(?:a|b)}", + queriesRegexp: "^foo=(?P<v0>[0-9]{1}(?:a|b))$", + shouldMatch: true, + }, + { + title: "Queries route with empty value, should match", + route: new(Route).Queries("foo", ""), + request: newRequest("GET", "http://localhost?foo=bar"), + vars: map[string]string{}, + host: "", + path: "", + query: "foo=", + queriesTemplate: "foo=", + queriesRegexp: "^foo=.*$", + shouldMatch: true, + }, + { + title: "Queries route with empty value and no parameter in request, should not match", + route: new(Route).Queries("foo", ""), + request: newRequest("GET", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo=", + queriesRegexp: "^foo=.*$", + shouldMatch: false, + }, + { + title: "Queries route with empty value and empty parameter in request, should match", + route: new(Route).Queries("foo", ""), + request: newRequest("GET", "http://localhost?foo="), + vars: map[string]string{}, + host: "", + path: "", + query: "foo=", + queriesTemplate: "foo=", + queriesRegexp: "^foo=.*$", + shouldMatch: true, + }, + { + title: "Queries route with overlapping value, should not match", + route: new(Route).Queries("foo", "bar"), + request: newRequest("GET", "http://localhost?foo=barfoo"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo=bar", + queriesRegexp: "^foo=bar$", + shouldMatch: false, + }, + { + title: "Queries route with no parameter in request, should not match", + route: new(Route).Queries("foo", "{bar}"), + request: newRequest("GET", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo={bar}", + queriesRegexp: "^foo=(?P<v0>.*)$", + shouldMatch: false, + }, + { + title: "Queries route with empty parameter in request, should match", + route: new(Route).Queries("foo", "{bar}"), + request: newRequest("GET", "http://localhost?foo="), + vars: map[string]string{"foo": ""}, + host: "", + path: "", + query: "foo=", + queriesTemplate: "foo={bar}", + queriesRegexp: "^foo=(?P<v0>.*)$", + shouldMatch: true, + }, + { + title: "Queries route, bad submatch", + route: new(Route).Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://localhost?fffoo=bar&baz=dingggg"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo=bar,baz=ding", + queriesRegexp: "^foo=bar$,^baz=ding$", + shouldMatch: false, + }, + { + title: "Queries route with pattern, match, escaped value", + route: new(Route).Queries("foo", "{v1}"), + request: newRequest("GET", "http://localhost?foo=%25bar%26%20%2F%3D%3F"), + vars: map[string]string{"v1": "%bar& /=?"}, + host: "", + path: "", + query: "foo=%25bar%26+%2F%3D%3F", + queriesTemplate: "foo={v1}", + queriesRegexp: "^foo=(?P<v0>.*)$", + shouldMatch: true, }, } for _, test := range tests { testRoute(t, test) testTemplate(t, test) + testQueriesTemplates(t, test) testUseEscapedRoute(t, test) + testQueriesRegexp(t, test) } } @@ -1717,20 +1771,38 @@ func testRegexp(t *testing.T, test routeTest) { } } +func testQueriesRegexp(t *testing.T, test routeTest) { + route := test.route + queries, queriesErr := route.GetQueriesRegexp() + gotQueries := strings.Join(queries, ",") + if test.queriesRegexp != "" && queriesErr == nil && gotQueries != test.queriesRegexp { + t.Errorf("(%v) GetQueriesRegexp not equal: expected %v, got %v", test.title, test.queriesRegexp, gotQueries) + } +} + +func testQueriesTemplates(t *testing.T, test routeTest) { + route := test.route + queries, queriesErr := route.GetQueriesTemplates() + gotQueries := strings.Join(queries, ",") + if test.queriesTemplate != "" && queriesErr == nil && gotQueries != test.queriesTemplate { + t.Errorf("(%v) GetQueriesTemplates not equal: expected %v, got %v", test.title, test.queriesTemplate, gotQueries) + } +} + type TestA301ResponseWriter struct { hh http.Header status int } -func (ho TestA301ResponseWriter) Header() http.Header { +func (ho *TestA301ResponseWriter) Header() http.Header { return http.Header(ho.hh) } -func (ho TestA301ResponseWriter) Write(b []byte) (int, error) { +func (ho *TestA301ResponseWriter) Write(b []byte) (int, error) { return 0, nil } -func (ho TestA301ResponseWriter) WriteHeader(code int) { +func (ho *TestA301ResponseWriter) WriteHeader(code int) { ho.status = code } @@ -1805,6 +1877,96 @@ func TestSubrouterHeader(t *testing.T) { } } +func TestNoMatchMethodErrorHandler(t *testing.T) { + func1 := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.HandleFunc("/", func1).Methods("GET", "POST") + + req, _ := http.NewRequest("PUT", "http://localhost/", nil) + match := new(RouteMatch) + matched := r.Match(req, match) + + if matched { + t.Error("Should not have matched route for methods") + } + + if match.MatchErr != ErrMethodMismatch { + t.Error("Should get ErrMethodMismatch error") + } + + resp := NewRecorder() + r.ServeHTTP(resp, req) + if resp.Code != 405 { + t.Errorf("Expecting code %v", 405) + } + + // Add matching route + r.HandleFunc("/", func1).Methods("PUT") + + match = new(RouteMatch) + matched = r.Match(req, match) + + if !matched { + t.Error("Should have matched route for methods") + } + + if match.MatchErr != nil { + t.Error("Should not have any matching error. Found:", match.MatchErr) + } +} + +func TestErrMatchNotFound(t *testing.T) { + emptyHandler := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.HandleFunc("/", emptyHandler) + s := r.PathPrefix("/sub/").Subrouter() + s.HandleFunc("/", emptyHandler) + + // Regular 404 not found + req, _ := http.NewRequest("GET", "/sub/whatever", nil) + match := new(RouteMatch) + matched := r.Match(req, match) + + if matched { + t.Errorf("Subrouter should not have matched that, got %v", match.Route) + } + // Even without a custom handler, MatchErr is set to ErrNotFound + if match.MatchErr != ErrNotFound { + t.Errorf("Expected ErrNotFound MatchErr, but was %v", match.MatchErr) + } + + // Now lets add a 404 handler to subrouter + s.NotFoundHandler = http.NotFoundHandler() + req, _ = http.NewRequest("GET", "/sub/whatever", nil) + + // Test the subrouter first + match = new(RouteMatch) + matched = s.Match(req, match) + // Now we should get a match + if !matched { + t.Errorf("Subrouter should have matched %s", req.RequestURI) + } + // But MatchErr should be set to ErrNotFound anyway + if match.MatchErr != ErrNotFound { + t.Errorf("Expected ErrNotFound MatchErr, but was %v", match.MatchErr) + } + + // Now test the parent (MatchErr should propagate) + match = new(RouteMatch) + matched = r.Match(req, match) + + // Now we should get a match + if !matched { + t.Errorf("Router should have matched %s via subrouter", req.RequestURI) + } + // But MatchErr should be set to ErrNotFound anyway + if match.MatchErr != ErrNotFound { + t.Errorf("Expected ErrNotFound MatchErr, but was %v", match.MatchErr) + } +} + // mapToPairs converts a string map to a slice of string pairs func mapToPairs(m map[string]string) []string { var i int @@ -1871,42 +2033,3 @@ func newRequest(method, url string) *http.Request { } return req } - -func TestNoMatchMethodErrorHandler(t *testing.T) { - func1 := func(w http.ResponseWriter, r *http.Request) {} - - r := NewRouter() - r.HandleFunc("/", func1).Methods("GET", "POST") - - req, _ := http.NewRequest("PUT", "http://localhost/", nil) - match := new(RouteMatch) - matched := r.Match(req, match) - - if matched { - t.Error("Should not have matched route for methods") - } - - if match.MatchErr != ErrMethodMismatch { - t.Error("Should get ErrMethodMismatch error") - } - - resp := NewRecorder() - r.ServeHTTP(resp, req) - if resp.Code != 405 { - t.Errorf("Expecting code %v", 405) - } - - // Add matching route - r.HandleFunc("/", func1).Methods("PUT") - - match = new(RouteMatch) - matched = r.Match(req, match) - - if !matched { - t.Error("Should have matched route for methods") - } - - if match.MatchErr != nil { - t.Error("Should not have any matching error. Found:", match.MatchErr) - } -} diff --git a/vendor/github.com/gorilla/mux/regexp.go b/vendor/github.com/gorilla/mux/regexp.go index 80d1f7858..e83213b7d 100644 --- a/vendor/github.com/gorilla/mux/regexp.go +++ b/vendor/github.com/gorilla/mux/regexp.go @@ -141,7 +141,7 @@ type routeRegexp struct { matchQuery bool // The strictSlash value defined on the route, but disabled if PathPrefix was used. strictSlash bool - // Determines whether to use encoded path from getPath function or unencoded + // Determines whether to use encoded req.URL.EnscapedPath() or unencoded // req.URL.Path for path matching useEncodedPath bool // Expanded regexp. @@ -162,7 +162,7 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { } path := req.URL.Path if r.useEncodedPath { - path = getPath(req) + path = req.URL.EscapedPath() } return r.regexp.MatchString(path) } @@ -272,7 +272,7 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) } path := req.URL.Path if r.useEncodedPath { - path = getPath(req) + path = req.URL.EscapedPath() } // Store path variables. if v.path != nil { diff --git a/vendor/github.com/gorilla/mux/route.go b/vendor/github.com/gorilla/mux/route.go index 6863adba5..69aeae791 100644 --- a/vendor/github.com/gorilla/mux/route.go +++ b/vendor/github.com/gorilla/mux/route.go @@ -72,7 +72,11 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool { return false } - match.MatchErr = nil + if match.MatchErr == ErrMethodMismatch { + // We found a route which matches request method, clear MatchErr + match.MatchErr = nil + } + // Yay, we have a match. Let's collect some info about it. if match.Route == nil { match.Route = r @@ -608,6 +612,44 @@ func (r *Route) GetPathRegexp() (string, error) { return r.regexp.path.regexp.String(), nil } +// GetQueriesRegexp returns the expanded regular expressions used to match the +// route queries. +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An empty list will be returned if the route does not have queries. +func (r *Route) GetQueriesRegexp() ([]string, error) { + if r.err != nil { + return nil, r.err + } + if r.regexp == nil || r.regexp.queries == nil { + return nil, errors.New("mux: route doesn't have queries") + } + var queries []string + for _, query := range r.regexp.queries { + queries = append(queries, query.regexp.String()) + } + return queries, nil +} + +// GetQueriesTemplates returns the templates used to build the +// query matching. +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An empty list will be returned if the route does not define queries. +func (r *Route) GetQueriesTemplates() ([]string, error) { + if r.err != nil { + return nil, r.err + } + if r.regexp == nil || r.regexp.queries == nil { + return nil, errors.New("mux: route doesn't have queries") + } + var queries []string + for _, query := range r.regexp.queries { + queries = append(queries, query.template) + } + return queries, nil +} + // GetMethods returns the methods the route matches against // This is useful for building simple REST API documentation and for instrumentation // against third-party services. diff --git a/vendor/github.com/hashicorp/go-sockaddr/ifaddr_test.go b/vendor/github.com/hashicorp/go-sockaddr/ifaddr_test.go index 859c3e421..45a0cc788 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/ifaddr_test.go +++ b/vendor/github.com/hashicorp/go-sockaddr/ifaddr_test.go @@ -411,6 +411,114 @@ func TestIfAddrMath(t *testing.T) { wantFail: true, }, { + name: "ipv4 mask operand equals input ipv4 subnet mask", + ifAddr: sockaddr.IfAddr{ + SockAddr: sockaddr.MustIPv4Addr("10.20.30.40/8"), + }, + operation: "mask", + value: "8", + expected: "10.0.0.0/8", + }, + { + name: "ipv4 mask operand larger than input ipv4 subnet mask", + ifAddr: sockaddr.IfAddr{ + SockAddr: sockaddr.MustIPv4Addr("192.168.10.20/24"), + }, + operation: "mask", + value: "16", + expected: "192.168.0.0/16", + }, + { + name: "ipv4 host upper bound mask operand larger than input ipv4 subnet mask", + ifAddr: sockaddr.IfAddr{ + SockAddr: sockaddr.MustIPv4Addr("192.168.255.255/24"), + }, + operation: "mask", + value: "16", + expected: "192.168.0.0/16", + }, + { + name: "ipv4 mask operand smaller than ipv4 subnet mask", + ifAddr: sockaddr.IfAddr{ + SockAddr: sockaddr.MustIPv4Addr("10.20.30.40/8"), + }, + operation: "mask", + value: "16", + expected: "10.20.0.0/8", + }, + { + name: "ipv4 host upper bound mask operand smaller than input ipv4 subnet mask", + ifAddr: sockaddr.IfAddr{ + SockAddr: sockaddr.MustIPv4Addr("10.20.255.255/8"), + }, + operation: "mask", + value: "16", + expected: "10.20.0.0/8", + }, + { + name: "ipv4 mask bad value upper bound", + ifAddr: sockaddr.IfAddr{ + SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), + }, + operation: "mask", + value: "33", + wantFail: true, + }, + { + name: "ipv4 mask bad value lower bound", + ifAddr: sockaddr.IfAddr{ + SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), + }, + operation: "mask", + value: "-1", + wantFail: true, + }, + { + name: "ipv6 mask operand equals input ipv6 subnet mask", + ifAddr: sockaddr.IfAddr{ + SockAddr: sockaddr.MustIPv6Addr("2001:0db8:85a3::8a2e:0370:7334/64"), + }, + operation: "mask", + value: "64", + expected: "2001:db8:85a3::/64", + }, + { + name: "ipv6 mask operand larger than input ipv6 subnet mask", + ifAddr: sockaddr.IfAddr{ + SockAddr: sockaddr.MustIPv6Addr("2001:0db8:85a3::8a2e:0370:7334/64"), + }, + operation: "mask", + value: "32", + expected: "2001:db8::/32", + }, + { + name: "ipv6 mask operand smaller than input ipv6 subnet mask", + ifAddr: sockaddr.IfAddr{ + SockAddr: sockaddr.MustIPv6Addr("2001:0db8:85a3::8a2e:0370:7334/64"), + }, + operation: "mask", + value: "96", + expected: "2001:db8:85a3::8a2e:0:0/64", + }, + { + name: "ipv6 mask bad value upper bound", + ifAddr: sockaddr.IfAddr{ + SockAddr: sockaddr.MustIPv6Addr("::1/128"), + }, + operation: "mask", + value: "129", + wantFail: true, + }, + { + name: "ipv6 mask bad value lower bound", + ifAddr: sockaddr.IfAddr{ + SockAddr: sockaddr.MustIPv6Addr("::1/128"), + }, + operation: "mask", + value: "-1", + wantFail: true, + }, + { name: "unix unsupported operation", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustUnixSock("/tmp/bar"), @@ -428,6 +536,15 @@ func TestIfAddrMath(t *testing.T) { value: "+123", wantFail: true, }, + { + name: "unix unsupported operation", + ifAddr: sockaddr.IfAddr{ + SockAddr: sockaddr.MustUnixSock("/tmp/foo"), + }, + operation: "mask", + value: "8", + wantFail: true, + }, } for i, test := range tests { diff --git a/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go b/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go index b87589a22..4b4e63808 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go +++ b/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go @@ -1,6 +1,7 @@ package sockaddr import ( + "encoding/binary" "errors" "fmt" "math/big" @@ -866,6 +867,80 @@ func IfAddrMath(operation, value string, inputIfAddr IfAddr) (IfAddr, error) { default: return IfAddr{}, fmt.Errorf("unsupported type for operation %q: %T", operation, sockType) } + case "mask": + // "mask" operates on the IP address and returns the IP address on + // which the given integer mask has been applied. If the applied mask + // corresponds to a larger network than the mask of the IP address, + // the latter will be replaced by the former. + switch sockType := inputIfAddr.SockAddr.Type(); sockType { + case TypeIPv4: + i, err := strconv.ParseUint(value, 10, 32) + if err != nil { + return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err) + } + + if i > 32 { + return IfAddr{}, fmt.Errorf("parameter for operation %q on ipv4 addresses must be between 0 and 32", operation) + } + + ipv4 := *ToIPv4Addr(inputIfAddr.SockAddr) + + ipv4Mask := net.CIDRMask(int(i), 32) + ipv4MaskUint32 := binary.BigEndian.Uint32(ipv4Mask) + + maskedIpv4 := ipv4.NetIP().Mask(ipv4Mask) + maskedIpv4Uint32 := binary.BigEndian.Uint32(maskedIpv4) + + maskedIpv4MaskUint32 := uint32(ipv4.Mask) + + if ipv4MaskUint32 < maskedIpv4MaskUint32 { + maskedIpv4MaskUint32 = ipv4MaskUint32 + } + + return IfAddr{ + SockAddr: IPv4Addr{ + Address: IPv4Address(maskedIpv4Uint32), + Mask: IPv4Mask(maskedIpv4MaskUint32), + }, + Interface: inputIfAddr.Interface, + }, nil + case TypeIPv6: + i, err := strconv.ParseUint(value, 10, 32) + if err != nil { + return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err) + } + + if i > 128 { + return IfAddr{}, fmt.Errorf("parameter for operation %q on ipv6 addresses must be between 0 and 64", operation) + } + + ipv6 := *ToIPv6Addr(inputIfAddr.SockAddr) + + ipv6Mask := net.CIDRMask(int(i), 128) + ipv6MaskBigInt := new(big.Int) + ipv6MaskBigInt.SetBytes(ipv6Mask) + + maskedIpv6 := ipv6.NetIP().Mask(ipv6Mask) + maskedIpv6BigInt := new(big.Int) + maskedIpv6BigInt.SetBytes(maskedIpv6) + + maskedIpv6MaskBigInt := new(big.Int) + maskedIpv6MaskBigInt.Set(ipv6.Mask) + + if ipv6MaskBigInt.Cmp(maskedIpv6MaskBigInt) == -1 { + maskedIpv6MaskBigInt = ipv6MaskBigInt + } + + return IfAddr{ + SockAddr: IPv6Addr{ + Address: IPv6Address(maskedIpv6BigInt), + Mask: IPv6Mask(maskedIpv6MaskBigInt), + }, + Interface: inputIfAddr.Interface, + }, nil + default: + return IfAddr{}, fmt.Errorf("unsupported type for operation %q: %T", operation, sockType) + } default: return IfAddr{}, fmt.Errorf("unsupported math operation: %q", operation) } diff --git a/vendor/github.com/hashicorp/go-sockaddr/template/doc.go b/vendor/github.com/hashicorp/go-sockaddr/template/doc.go index 90c8784a3..8cc6730a4 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/template/doc.go +++ b/vendor/github.com/hashicorp/go-sockaddr/template/doc.go @@ -212,6 +212,13 @@ Supported operations include: from the network's broadcast address (e.g. 127.0.0.1 `"network" "-1"` will return "127.255.255.255"). Values that overflow the network size will safely wrap. + - `mask`: Applies the given network mask to the address. The network mask is + expressed as a decimal value (e.g. network mask "24" corresponds to + `255.255.255.0`). After applying the network mask, the network mask of the + resulting address will be either the applied network mask or the network mask + of the input address depending on which network is larger + (e.g. 192.168.10.20/24 `"mask" "16"` will return "192.168.0.0/16" but + 192.168.10.20/24 `"mask" "28"` will return "192.168.10.16/24"). Example: @@ -219,6 +226,7 @@ Example: {{ GetPrivateInterfaces | include "type" "IP" | math "address" "-256" | attr "address" }} {{ GetPrivateInterfaces | include "type" "IP" | math "network" "+2" | attr "address" }} {{ GetPrivateInterfaces | include "type" "IP" | math "network" "-2" | attr "address" }} + {{ GetPrivateInterfaces | include "type" "IP" | math "mask" "24" | attr "address" }} {{ GetPrivateInterfaces | include "flags" "forwardable|up" | include "type" "IPv4" | math "network" "+2" | attr "address" }} diff --git a/vendor/github.com/hashicorp/hcl/.travis.yml b/vendor/github.com/hashicorp/hcl/.travis.yml index 3f83d9023..cb63a3216 100644 --- a/vendor/github.com/hashicorp/hcl/.travis.yml +++ b/vendor/github.com/hashicorp/hcl/.travis.yml @@ -3,7 +3,8 @@ sudo: false language: go go: - - 1.8 + - 1.x + - tip branches: only: diff --git a/vendor/github.com/hashicorp/hcl/decoder.go b/vendor/github.com/hashicorp/hcl/decoder.go index b88f322a8..bed9ebbe1 100644 --- a/vendor/github.com/hashicorp/hcl/decoder.go +++ b/vendor/github.com/hashicorp/hcl/decoder.go @@ -573,7 +573,11 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value) // Compile the list of all the fields that we're going to be decoding // from all the structs. - fields := make(map[*reflect.StructField]reflect.Value) + type field struct { + field reflect.StructField + val reflect.Value + } + fields := []field{} for len(structs) > 0 { structVal := structs[0] structs = structs[1:] @@ -616,7 +620,7 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value) } // Normal struct field, store it away - fields[&fieldType] = structVal.Field(i) + fields = append(fields, field{fieldType, structVal.Field(i)}) } } @@ -624,26 +628,27 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value) decodedFields := make([]string, 0, len(fields)) decodedFieldsVal := make([]reflect.Value, 0) unusedKeysVal := make([]reflect.Value, 0) - for fieldType, field := range fields { - if !field.IsValid() { + for _, f := range fields { + field, fieldValue := f.field, f.val + if !fieldValue.IsValid() { // This should never happen panic("field is not valid") } // If we can't set the field, then it is unexported or something, // and we just continue onwards. - if !field.CanSet() { + if !fieldValue.CanSet() { continue } - fieldName := fieldType.Name + fieldName := field.Name - tagValue := fieldType.Tag.Get(tagName) + tagValue := field.Tag.Get(tagName) tagParts := strings.SplitN(tagValue, ",", 2) if len(tagParts) >= 2 { switch tagParts[1] { case "decodedFields": - decodedFieldsVal = append(decodedFieldsVal, field) + decodedFieldsVal = append(decodedFieldsVal, fieldValue) continue case "key": if item == nil { @@ -654,10 +659,10 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value) } } - field.SetString(item.Keys[0].Token.Value().(string)) + fieldValue.SetString(item.Keys[0].Token.Value().(string)) continue case "unusedKeys": - unusedKeysVal = append(unusedKeysVal, field) + unusedKeysVal = append(unusedKeysVal, fieldValue) continue } } @@ -684,7 +689,7 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value) // because we actually want the value. fieldName = fmt.Sprintf("%s.%s", name, fieldName) if len(prefixMatches.Items) > 0 { - if err := d.decode(fieldName, prefixMatches, field); err != nil { + if err := d.decode(fieldName, prefixMatches, fieldValue); err != nil { return err } } @@ -694,12 +699,12 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value) decodeNode = &ast.ObjectList{Items: ot.List.Items} } - if err := d.decode(fieldName, decodeNode, field); err != nil { + if err := d.decode(fieldName, decodeNode, fieldValue); err != nil { return err } } - decodedFields = append(decodedFields, fieldType.Name) + decodedFields = append(decodedFields, field.Name) } if len(decodedFieldsVal) > 0 { diff --git a/vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go b/vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go index 69662367f..6601ef76e 100644 --- a/vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go +++ b/vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go @@ -351,7 +351,7 @@ func (s *Scanner) scanNumber(ch rune) token.Type { return token.NUMBER } -// scanMantissa scans the mantissa begining from the rune. It returns the next +// scanMantissa scans the mantissa beginning from the rune. It returns the next // non decimal rune. It's used to determine wheter it's a fraction or exponent. func (s *Scanner) scanMantissa(ch rune) rune { scanned := false diff --git a/vendor/github.com/hashicorp/hcl/json/scanner/scanner.go b/vendor/github.com/hashicorp/hcl/json/scanner/scanner.go index dd5c72bb3..fe3f0f095 100644 --- a/vendor/github.com/hashicorp/hcl/json/scanner/scanner.go +++ b/vendor/github.com/hashicorp/hcl/json/scanner/scanner.go @@ -246,7 +246,7 @@ func (s *Scanner) scanNumber(ch rune) token.Type { return token.NUMBER } -// scanMantissa scans the mantissa begining from the rune. It returns the next +// scanMantissa scans the mantissa beginning from the rune. It returns the next // non decimal rune. It's used to determine wheter it's a fraction or exponent. func (s *Scanner) scanMantissa(ch rune) rune { scanned := false diff --git a/vendor/github.com/hashicorp/memberlist/memberlist.go b/vendor/github.com/hashicorp/memberlist/memberlist.go index e1e38fc94..bdf333b43 100644 --- a/vendor/github.com/hashicorp/memberlist/memberlist.go +++ b/vendor/github.com/hashicorp/memberlist/memberlist.go @@ -308,23 +308,17 @@ func (m *Memberlist) tcpLookupIP(host string, defaultPort uint16) ([]ipPort, err // resolveAddr is used to resolve the address into an address, // port, and error. If no port is given, use the default func (m *Memberlist) resolveAddr(hostStr string) ([]ipPort, error) { - // Normalize the incoming string to host:port so we can apply Go's - // parser to it. - port := uint16(0) - if !hasPort(hostStr) { - hostStr += ":" + strconv.Itoa(m.config.BindPort) - } + // This captures the supplied port, or the default one. + hostStr = ensurePort(hostStr, m.config.BindPort) host, sport, err := net.SplitHostPort(hostStr) if err != nil { return nil, err } - - // This will capture the supplied port, or the default one added above. lport, err := strconv.ParseUint(sport, 10, 16) if err != nil { return nil, err } - port = uint16(lport) + port := uint16(lport) // If it looks like an IP address we are done. The SplitHostPort() above // will make sure the host part is in good shape for parsing, even for diff --git a/vendor/github.com/hashicorp/memberlist/memberlist_test.go b/vendor/github.com/hashicorp/memberlist/memberlist_test.go index ecda7fb55..ee2fc5d52 100644 --- a/vendor/github.com/hashicorp/memberlist/memberlist_test.go +++ b/vendor/github.com/hashicorp/memberlist/memberlist_test.go @@ -424,7 +424,7 @@ func TestMemberList_ResolveAddr_TCP_First(t *testing.T) { } port := uint16(m.config.BindPort) expected := []ipPort{ - ipPort{net.ParseIP("127.0.0.1").To4(), port}, + ipPort{net.ParseIP("127.0.0.1"), port}, ipPort{net.ParseIP("2001:db8:a0b:12f0::1"), port}, } if !reflect.DeepEqual(ips, expected) { diff --git a/vendor/github.com/hashicorp/memberlist/suspicion.go b/vendor/github.com/hashicorp/memberlist/suspicion.go index 5f573e1fc..f8aa9e20a 100644 --- a/vendor/github.com/hashicorp/memberlist/suspicion.go +++ b/vendor/github.com/hashicorp/memberlist/suspicion.go @@ -117,7 +117,7 @@ func (s *suspicion) Confirm(from string) bool { // stop the timer then we will call the timeout function directly from // here. n := atomic.AddInt32(&s.n, 1) - elapsed := time.Now().Sub(s.start) + elapsed := time.Since(s.start) remaining := remainingSuspicionTime(n, s.k, elapsed, s.min, s.max) if s.timer.Stop() { if remaining > 0 { diff --git a/vendor/github.com/hashicorp/memberlist/util.go b/vendor/github.com/hashicorp/memberlist/util.go index a4f926e3a..e2381a698 100644 --- a/vendor/github.com/hashicorp/memberlist/util.go +++ b/vendor/github.com/hashicorp/memberlist/util.go @@ -217,20 +217,6 @@ func decodeCompoundMessage(buf []byte) (trunc int, parts [][]byte, err error) { return } -// Given a string of the form "host", "host:port", -// "ipv6::addr" or "[ipv6::address]:port", -// return true if the string includes a port. -func hasPort(s string) bool { - last := strings.LastIndex(s, ":") - if last == -1 { - return false - } - if s[0] == '[' { - return s[last-1] == ']' - } - return strings.Index(s, ":") == last -} - // compressPayload takes an opaque input buffer, compresses it // and wraps it in a compress{} message that is encoded. func compressPayload(inp []byte) (*bytes.Buffer, error) { @@ -294,3 +280,31 @@ func decompressBuffer(c *compress) ([]byte, error) { func joinHostPort(host string, port uint16) string { return net.JoinHostPort(host, strconv.Itoa(int(port))) } + +// hasPort is given a string of the form "host", "host:port", "ipv6::address", +// or "[ipv6::address]:port", and returns true if the string includes a port. +func hasPort(s string) bool { + // IPv6 address in brackets. + if strings.LastIndex(s, "[") == 0 { + return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") + } + + // Otherwise the presence of a single colon determines if there's a port + // since IPv6 addresses outside of brackets (count > 1) can't have a + // port. + return strings.Count(s, ":") == 1 +} + +// ensurePort makes sure the given string has a port number on it, otherwise it +// appends the given port as a default. +func ensurePort(s string, port int) string { + if hasPort(s) { + return s + } + + // If this is an IPv6 address, the join call will add another set of + // brackets, so we have to trim before we add the default port. + s = strings.Trim(s, "[]") + s = net.JoinHostPort(s, strconv.Itoa(port)) + return s +} diff --git a/vendor/github.com/hashicorp/memberlist/util_test.go b/vendor/github.com/hashicorp/memberlist/util_test.go index e1d8eba01..b7f2b4199 100644 --- a/vendor/github.com/hashicorp/memberlist/util_test.go +++ b/vendor/github.com/hashicorp/memberlist/util_test.go @@ -7,24 +7,31 @@ import ( "time" ) -func Test_hasPort(t *testing.T) { - cases := []struct { - s string - expected bool +func TestUtil_PortFunctions(t *testing.T) { + tests := []struct { + addr string + hasPort bool + ensurePort string }{ - {"", false}, - {":80", true}, - {"127.0.0.1", false}, - {"127.0.0.1:80", true}, - {"::1", false}, - {"2001:db8:a0b:12f0::1", false}, - {"[2001:db8:a0b:12f0::1]", false}, - {"[2001:db8:a0b:12f0::1]:80", true}, - } - for _, c := range cases { - if hasPort(c.s) != c.expected { - t.Fatalf("bad: '%s' hasPort was not %v", c.s, c.expected) - } + {"1.2.3.4", false, "1.2.3.4:8301"}, + {"1.2.3.4:1234", true, "1.2.3.4:1234"}, + {"2600:1f14:e22:1501:f9a:2e0c:a167:67e8", false, "[2600:1f14:e22:1501:f9a:2e0c:a167:67e8]:8301"}, + {"[2600:1f14:e22:1501:f9a:2e0c:a167:67e8]", false, "[2600:1f14:e22:1501:f9a:2e0c:a167:67e8]:8301"}, + {"[2600:1f14:e22:1501:f9a:2e0c:a167:67e8]:1234", true, "[2600:1f14:e22:1501:f9a:2e0c:a167:67e8]:1234"}, + {"localhost", false, "localhost:8301"}, + {"localhost:1234", true, "localhost:1234"}, + {"hashicorp.com", false, "hashicorp.com:8301"}, + {"hashicorp.com:1234", true, "hashicorp.com:1234"}, + } + for _, tt := range tests { + t.Run(tt.addr, func(t *testing.T) { + if got, want := hasPort(tt.addr), tt.hasPort; got != want { + t.Fatalf("got %v want %v", got, want) + } + if got, want := ensurePort(tt.addr, 8301), tt.ensurePort; got != want { + t.Fatalf("got %v want %v", got, want) + } + }) } } diff --git a/vendor/github.com/lib/pq/.travis.sh b/vendor/github.com/lib/pq/.travis.sh index ebf447030..ead01df73 100755 --- a/vendor/github.com/lib/pq/.travis.sh +++ b/vendor/github.com/lib/pq/.travis.sh @@ -70,4 +70,29 @@ postgresql_uninstall() { sudo rm -rf /var/lib/postgresql } +megacheck_install() { + # Megacheck is Go 1.6+, so skip if Go 1.5. + if [[ "$(go version)" =~ "go1.5" ]] + then + echo "megacheck not supported, skipping installation" + return 0 + fi + # Lock megacheck version at $MEGACHECK_VERSION to prevent spontaneous + # new error messages in old code. + go get -d honnef.co/go/tools/... + git -C $GOPATH/src/honnef.co/go/tools/ checkout $MEGACHECK_VERSION + go install honnef.co/go/tools/cmd/megacheck + megacheck --version +} + +golint_install() { + # Golint is Go 1.6+, so skip if Go 1.5. + if [[ "$(go version)" =~ "go1.5" ]] + then + echo "golint not supported, skipping installation" + return 0 + fi + go get github.com/golang/lint/golint +} + $1 diff --git a/vendor/github.com/lib/pq/.travis.yml b/vendor/github.com/lib/pq/.travis.yml index 452515c66..01468f05c 100644 --- a/vendor/github.com/lib/pq/.travis.yml +++ b/vendor/github.com/lib/pq/.travis.yml @@ -16,7 +16,9 @@ env: - PQGOSSLTESTS=1 - PQSSLCERTTEST_PATH=$PWD/certs - PGHOST=127.0.0.1 + - MEGACHECK_VERSION=2017.1 matrix: + - PGVERSION=10 - PGVERSION=9.6 - PGVERSION=9.5 - PGVERSION=9.4 @@ -31,6 +33,8 @@ before_install: - ./.travis.sh postgresql_install - ./.travis.sh postgresql_configure - ./.travis.sh client_configure + - ./.travis.sh megacheck_install + - ./.travis.sh golint_install - go get golang.org/x/tools/cmd/goimports before_script: @@ -42,5 +46,15 @@ script: - > goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }' - go vet ./... + # For compatibility with Go 1.5, launch only if megacheck is present, + # ignore SA1019 (deprecation warnings) in conn_test.go (we have to use the + # deprecated driver.Execer and driver.Queryer interfaces) and S1024 + # (time.Until) everywhere. + - > + which megacheck > /dev/null + && megacheck -ignore 'github.com/lib/pq/conn_test.go:SA1019 github.com/lib/pq/*.go:S1024' ./... + || echo 'megacheck is not supported, skipping check' + # For compatibility with Go 1.5, launch only if golint is present. + - which golint > /dev/null && golint ./... || echo 'golint is not supported, skipping check' - PQTEST_BINARY_PARAMETERS=no go test -race -v ./... - PQTEST_BINARY_PARAMETERS=yes go test -race -v ./... diff --git a/vendor/github.com/lib/pq/README.md b/vendor/github.com/lib/pq/README.md index 7670fc87a..781c89eea 100644 --- a/vendor/github.com/lib/pq/README.md +++ b/vendor/github.com/lib/pq/README.md @@ -1,5 +1,6 @@ # pq - A pure Go postgres driver for Go's database/sql package +[![GoDoc](https://godoc.org/github.com/lib/pq?status.svg)](https://godoc.org/github.com/lib/pq) [![Build Status](https://travis-ci.org/lib/pq.svg?branch=master)](https://travis-ci.org/lib/pq) ## Install diff --git a/vendor/github.com/lib/pq/array_test.go b/vendor/github.com/lib/pq/array_test.go index 10b843184..f724bcd88 100644 --- a/vendor/github.com/lib/pq/array_test.go +++ b/vendor/github.com/lib/pq/array_test.go @@ -89,9 +89,7 @@ func TestParseArrayError(t *testing.T) { } func TestArrayScanner(t *testing.T) { - var s sql.Scanner - - s = Array(&[]bool{}) + var s sql.Scanner = Array(&[]bool{}) if _, ok := s.(*BoolArray); !ok { t.Errorf("Expected *BoolArray, got %T", s) } @@ -126,9 +124,7 @@ func TestArrayScanner(t *testing.T) { } func TestArrayValuer(t *testing.T) { - var v driver.Valuer - - v = Array([]bool{}) + var v driver.Valuer = Array([]bool{}) if _, ok := v.(*BoolArray); !ok { t.Errorf("Expected *BoolArray, got %T", v) } @@ -1193,9 +1189,7 @@ func TestGenericArrayValue(t *testing.T) { } func TestGenericArrayValueErrors(t *testing.T) { - var v []interface{} - - v = []interface{}{func() {}} + v := []interface{}{func() {}} if _, err := (GenericArray{v}).Value(); err == nil { t.Errorf("Expected error for %q, got nil", v) } diff --git a/vendor/github.com/lib/pq/conn.go b/vendor/github.com/lib/pq/conn.go index 338a0bc18..fadb88e5e 100644 --- a/vendor/github.com/lib/pq/conn.go +++ b/vendor/github.com/lib/pq/conn.go @@ -35,8 +35,12 @@ var ( errNoLastInsertID = errors.New("no LastInsertId available after the empty statement") ) +// Driver is the Postgres database driver. type Driver struct{} +// Open opens a new connection to the database. name is a connection string. +// Most users should only use it through database/sql package from the standard +// library. func (d *Driver) Open(name string) (driver.Conn, error) { return Open(name) } @@ -78,6 +82,8 @@ func (s transactionStatus) String() string { panic("not reached") } +// Dialer is the dialer interface. It can be used to obtain more control over +// how pq creates network connections. type Dialer interface { Dial(network, address string) (net.Conn, error) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) @@ -149,11 +155,7 @@ func (cn *conn) handleDriverSettings(o values) (err error) { if err != nil { return err } - err = boolSetting("binary_parameters", &cn.binaryParameters) - if err != nil { - return err - } - return nil + return boolSetting("binary_parameters", &cn.binaryParameters) } func (cn *conn) handlePgpass(o values) { @@ -165,11 +167,16 @@ func (cn *conn) handlePgpass(o values) { if filename == "" { // XXX this code doesn't work on Windows where the default filename is // XXX %APPDATA%\postgresql\pgpass.conf - user, err := user.Current() - if err != nil { - return + // Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470 + userHome := os.Getenv("HOME") + if userHome == "" { + user, err := user.Current() + if err != nil { + return + } + userHome = user.HomeDir } - filename = filepath.Join(user.HomeDir, ".pgpass") + filename = filepath.Join(userHome, ".pgpass") } fileinfo, err := os.Stat(filename) if err != nil { @@ -237,10 +244,14 @@ func (cn *conn) writeBuf(b byte) *writeBuf { } } +// Open opens a new connection to the database. name is a connection string. +// Most users should only use it through database/sql package from the standard +// library. func Open(name string) (_ driver.Conn, err error) { return DialOpen(defaultDialer{}, name) } +// DialOpen opens a new connection to the database using a dialer. func DialOpen(d Dialer, name string) (_ driver.Conn, err error) { // Handle any panics during connection initialization. Note that we // specifically do *not* want to use errRecover(), as that would turn any @@ -1431,7 +1442,8 @@ func (rs *rows) NextResultSet() error { // // tblname := "my_table" // data := "my_data" -// err = db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", pq.QuoteIdentifier(tblname)), data) +// quoted := pq.QuoteIdentifier(tblname) +// err := db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", quoted), data) // // Any double quotes in name will be escaped. The quoted identifier will be // case sensitive when used in a query. If the input string contains a zero diff --git a/vendor/github.com/lib/pq/conn_test.go b/vendor/github.com/lib/pq/conn_test.go index d3c01f343..030a798c9 100644 --- a/vendor/github.com/lib/pq/conn_test.go +++ b/vendor/github.com/lib/pq/conn_test.go @@ -935,12 +935,14 @@ func TestParseErrorInExtendedQuery(t *testing.T) { db := openTestConn(t) defer db.Close() - rows, err := db.Query("PARSE_ERROR $1", 1) - if err == nil { - t.Fatal("expected error") + _, err := db.Query("PARSE_ERROR $1", 1) + pqErr, _ := err.(*Error) + // Expecting a syntax error. + if err == nil || pqErr == nil || pqErr.Code != "42601" { + t.Fatalf("expected syntax error, got %s", err) } - rows, err = db.Query("SELECT 1") + rows, err := db.Query("SELECT 1") if err != nil { t.Fatal(err) } @@ -1205,16 +1207,11 @@ func TestParseComplete(t *testing.T) { tpc("SELECT foo", "", 0, true) // invalid row count } -func TestExecerInterface(t *testing.T) { - // Gin up a straw man private struct just for the type check - cn := &conn{c: nil} - var cni interface{} = cn - - _, ok := cni.(driver.Execer) - if !ok { - t.Fatal("Driver doesn't implement Execer") - } -} +// Test interface conformance. +var ( + _ driver.Execer = (*conn)(nil) + _ driver.Queryer = (*conn)(nil) +) func TestNullAfterNonNull(t *testing.T) { db := openTestConn(t) @@ -1392,36 +1389,29 @@ func TestParseOpts(t *testing.T) { } func TestRuntimeParameters(t *testing.T) { - type RuntimeTestResult int - const ( - ResultUnknown RuntimeTestResult = iota - ResultSuccess - ResultError // other error - ) - tests := []struct { - conninfo string - param string - expected string - expectedOutcome RuntimeTestResult + conninfo string + param string + expected string + success bool }{ // invalid parameter - {"DOESNOTEXIST=foo", "", "", ResultError}, + {"DOESNOTEXIST=foo", "", "", false}, // we can only work with a specific value for these two - {"client_encoding=SQL_ASCII", "", "", ResultError}, - {"datestyle='ISO, YDM'", "", "", ResultError}, + {"client_encoding=SQL_ASCII", "", "", false}, + {"datestyle='ISO, YDM'", "", "", false}, // "options" should work exactly as it does in libpq - {"options='-c search_path=pqgotest'", "search_path", "pqgotest", ResultSuccess}, + {"options='-c search_path=pqgotest'", "search_path", "pqgotest", true}, // pq should override client_encoding in this case - {"options='-c client_encoding=SQL_ASCII'", "client_encoding", "UTF8", ResultSuccess}, + {"options='-c client_encoding=SQL_ASCII'", "client_encoding", "UTF8", true}, // allow client_encoding to be set explicitly - {"client_encoding=UTF8", "client_encoding", "UTF8", ResultSuccess}, + {"client_encoding=UTF8", "client_encoding", "UTF8", true}, // test a runtime parameter not supported by libpq - {"work_mem='139kB'", "work_mem", "139kB", ResultSuccess}, + {"work_mem='139kB'", "work_mem", "139kB", true}, // test fallback_application_name - {"application_name=foo fallback_application_name=bar", "application_name", "foo", ResultSuccess}, - {"application_name='' fallback_application_name=bar", "application_name", "", ResultSuccess}, - {"fallback_application_name=bar", "application_name", "bar", ResultSuccess}, + {"application_name=foo fallback_application_name=bar", "application_name", "foo", true}, + {"application_name='' fallback_application_name=bar", "application_name", "", true}, + {"fallback_application_name=bar", "application_name", "bar", true}, } for _, test := range tests { @@ -1436,23 +1426,23 @@ func TestRuntimeParameters(t *testing.T) { continue } - tryGetParameterValue := func() (value string, outcome RuntimeTestResult) { + tryGetParameterValue := func() (value string, success bool) { defer db.Close() row := db.QueryRow("SELECT current_setting($1)", test.param) err = row.Scan(&value) if err != nil { - return "", ResultError + return "", false } - return value, ResultSuccess + return value, true } - value, outcome := tryGetParameterValue() - if outcome != test.expectedOutcome && outcome == ResultError { + value, success := tryGetParameterValue() + if success != test.success && !test.success { t.Fatalf("%v: unexpected error: %v", test.conninfo, err) } - if outcome != test.expectedOutcome { + if success != test.success { t.Fatalf("unexpected outcome %v (was expecting %v) for conninfo \"%s\"", - outcome, test.expectedOutcome, test.conninfo) + success, test.success, test.conninfo) } if value != test.expected { t.Fatalf("bad value for %s: got %s, want %s with conninfo \"%s\"", diff --git a/vendor/github.com/lib/pq/copy_test.go b/vendor/github.com/lib/pq/copy_test.go index 86745b38f..c1a3cd7fa 100644 --- a/vendor/github.com/lib/pq/copy_test.go +++ b/vendor/github.com/lib/pq/copy_test.go @@ -9,8 +9,7 @@ import ( ) func TestCopyInStmt(t *testing.T) { - var stmt string - stmt = CopyIn("table name") + stmt := CopyIn("table name") if stmt != `COPY "table name" () FROM STDIN` { t.Fatal(stmt) } @@ -27,8 +26,7 @@ func TestCopyInStmt(t *testing.T) { } func TestCopyInSchemaStmt(t *testing.T) { - var stmt string - stmt = CopyInSchema("schema name", "table name") + stmt := CopyInSchema("schema name", "table name") if stmt != `COPY "schema name"."table name" () FROM STDIN` { t.Fatal(stmt) } @@ -226,7 +224,7 @@ func TestCopyInTypes(t *testing.T) { if text != "Héllö\n ☃!\r\t\\" { t.Fatal("unexpected result", text) } - if bytes.Compare(blob, []byte{0, 255, 9, 10, 13}) != 0 { + if !bytes.Equal(blob, []byte{0, 255, 9, 10, 13}) { t.Fatal("unexpected result", blob) } if nothing.Valid { diff --git a/vendor/github.com/lib/pq/doc.go b/vendor/github.com/lib/pq/doc.go index 6d252ecee..b2c3582c8 100644 --- a/vendor/github.com/lib/pq/doc.go +++ b/vendor/github.com/lib/pq/doc.go @@ -11,7 +11,8 @@ using this package directly. For example: ) func main() { - db, err := sql.Open("postgres", "user=pqgotest dbname=pqgotest sslmode=verify-full") + connStr := "user=pqgotest dbname=pqgotest sslmode=verify-full" + db, err := sql.Open("postgres", connStr) if err != nil { log.Fatal(err) } @@ -23,7 +24,8 @@ using this package directly. For example: You can also connect to a database using a URL. For example: - db, err := sql.Open("postgres", "postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full") + connStr := "postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full" + db, err := sql.Open("postgres", connStr) Connection String Parameters @@ -43,21 +45,28 @@ supported: * dbname - The name of the database to connect to * user - The user to sign in as * password - The user's password - * host - The host to connect to. Values that start with / are for unix domain sockets. (default is localhost) + * host - The host to connect to. Values that start with / are for unix + domain sockets. (default is localhost) * port - The port to bind to. (default is 5432) - * sslmode - Whether or not to use SSL (default is require, this is not the default for libpq) + * sslmode - Whether or not to use SSL (default is require, this is not + the default for libpq) * fallback_application_name - An application_name to fall back to if one isn't provided. - * connect_timeout - Maximum wait for connection, in seconds. Zero or not specified means wait indefinitely. + * connect_timeout - Maximum wait for connection, in seconds. Zero or + not specified means wait indefinitely. * sslcert - Cert file location. The file must contain PEM encoded data. * sslkey - Key file location. The file must contain PEM encoded data. - * sslrootcert - The location of the root certificate file. The file must contain PEM encoded data. + * sslrootcert - The location of the root certificate file. The file + must contain PEM encoded data. Valid values for sslmode are: * disable - No SSL * require - Always SSL (skip verification) - * verify-ca - Always SSL (verify that the certificate presented by the server was signed by a trusted CA) - * verify-full - Always SSL (verify that the certification presented by the server was signed by a trusted CA and the server host name matches the one in the certificate) + * verify-ca - Always SSL (verify that the certificate presented by the + server was signed by a trusted CA) + * verify-full - Always SSL (verify that the certification presented by + the server was signed by a trusted CA and the server host name + matches the one in the certificate) See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING for more information about connection string parameters. @@ -68,7 +77,7 @@ Use single quotes for values that contain whitespace: A backslash will escape the next character in values: - "user=space\ man password='it\'s valid' + "user=space\ man password='it\'s valid'" Note that the connection parameter client_encoding (which sets the text encoding for the connection) may be set but must be "UTF8", @@ -129,7 +138,8 @@ This package returns the following types for values from the PostgreSQL backend: - integer types smallint, integer, and bigint are returned as int64 - floating-point types real and double precision are returned as float64 - character types char, varchar, and text are returned as string - - temporal types date, time, timetz, timestamp, and timestamptz are returned as time.Time + - temporal types date, time, timetz, timestamp, and timestamptz are + returned as time.Time - the boolean type is returned as bool - the bytea type is returned as []byte @@ -229,7 +239,7 @@ for more information). Note that the channel name will be truncated to 63 bytes by the PostgreSQL server. You can find a complete, working example of Listener usage at -http://godoc.org/github.com/lib/pq/listen_example. +http://godoc.org/github.com/lib/pq/examples/listen. */ package pq diff --git a/vendor/github.com/lib/pq/encode_test.go b/vendor/github.com/lib/pq/encode_test.go index 837d45bec..634a05c66 100644 --- a/vendor/github.com/lib/pq/encode_test.go +++ b/vendor/github.com/lib/pq/encode_test.go @@ -267,9 +267,7 @@ func TestTimestampWithOutTimezone(t *testing.T) { t.Fatalf("Could not run query: %v", err) } - n := r.Next() - - if n != true { + if !r.Next() { t.Fatal("Expected at least one row") } @@ -289,8 +287,7 @@ func TestTimestampWithOutTimezone(t *testing.T) { expected, result) } - n = r.Next() - if n != false { + if r.Next() { t.Fatal("Expected only one row") } } @@ -731,8 +728,7 @@ func TestAppendEscapedText(t *testing.T) { } func TestAppendEscapedTextExistingBuffer(t *testing.T) { - var buf []byte - buf = []byte("123\t") + buf := []byte("123\t") if esc := appendEscapedText(buf, "hallo\tescape"); string(esc) != "123\thallo\\tescape" { t.Fatal(string(esc)) } diff --git a/vendor/github.com/lib/pq/listen_example/doc.go b/vendor/github.com/lib/pq/example/listen/doc.go index 80f0a9b97..91e2ddbad 100644 --- a/vendor/github.com/lib/pq/listen_example/doc.go +++ b/vendor/github.com/lib/pq/example/listen/doc.go @@ -1,6 +1,6 @@ /* -Below you will find a self-contained Go program which uses the LISTEN / NOTIFY +Package listen is a self-contained Go program which uses the LISTEN / NOTIFY mechanism to avoid polling the database while waiting for more work to arrive. // @@ -77,7 +77,9 @@ mechanism to avoid polling the database while waiting for more work to arrive. } } - listener := pq.NewListener(conninfo, 10 * time.Second, time.Minute, reportProblem) + minReconn := 10 * time.Second + maxReconn := time.Minute + listener := pq.NewListener(conninfo, minReconn, maxReconn, reportProblem) err = listener.Listen("getwork") if err != nil { panic(err) @@ -93,4 +95,4 @@ mechanism to avoid polling the database while waiting for more work to arrive. */ -package listen_example +package listen diff --git a/vendor/github.com/lib/pq/hstore/hstore.go b/vendor/github.com/lib/pq/hstore/hstore.go index 72d5abf51..f1470db14 100644 --- a/vendor/github.com/lib/pq/hstore/hstore.go +++ b/vendor/github.com/lib/pq/hstore/hstore.go @@ -6,7 +6,7 @@ import ( "strings" ) -// A wrapper for transferring Hstore values back and forth easily. +// Hstore is a wrapper for transferring Hstore values back and forth easily. type Hstore struct { Map map[string]sql.NullString } diff --git a/vendor/github.com/lib/pq/notify.go b/vendor/github.com/lib/pq/notify.go index 09f94244b..412c6ac1e 100644 --- a/vendor/github.com/lib/pq/notify.go +++ b/vendor/github.com/lib/pq/notify.go @@ -60,7 +60,7 @@ type ListenerConn struct { replyChan chan message } -// Creates a new ListenerConn. Use NewListener instead. +// NewListenerConn creates a new ListenerConn. Use NewListener instead. func NewListenerConn(name string, notificationChan chan<- *Notification) (*ListenerConn, error) { return newDialListenerConn(defaultDialer{}, name, notificationChan) } @@ -214,17 +214,17 @@ func (l *ListenerConn) listenerConnMain() { // this ListenerConn is done } -// Send a LISTEN query to the server. See ExecSimpleQuery. +// Listen sends a LISTEN query to the server. See ExecSimpleQuery. func (l *ListenerConn) Listen(channel string) (bool, error) { return l.ExecSimpleQuery("LISTEN " + QuoteIdentifier(channel)) } -// Send an UNLISTEN query to the server. See ExecSimpleQuery. +// Unlisten sends an UNLISTEN query to the server. See ExecSimpleQuery. func (l *ListenerConn) Unlisten(channel string) (bool, error) { return l.ExecSimpleQuery("UNLISTEN " + QuoteIdentifier(channel)) } -// Send `UNLISTEN *` to the server. See ExecSimpleQuery. +// UnlistenAll sends an `UNLISTEN *` query to the server. See ExecSimpleQuery. func (l *ListenerConn) UnlistenAll() (bool, error) { return l.ExecSimpleQuery("UNLISTEN *") } @@ -267,8 +267,8 @@ func (l *ListenerConn) sendSimpleQuery(q string) (err error) { return nil } -// Execute a "simple query" (i.e. one with no bindable parameters) on the -// connection. The possible return values are: +// ExecSimpleQuery executes a "simple query" (i.e. one with no bindable +// parameters) on the connection. The possible return values are: // 1) "executed" is true; the query was executed to completion on the // database server. If the query failed, err will be set to the error // returned by the database, otherwise err will be nil. @@ -333,6 +333,7 @@ func (l *ListenerConn) ExecSimpleQuery(q string) (executed bool, err error) { } } +// Close closes the connection. func (l *ListenerConn) Close() error { l.connectionLock.Lock() if l.err != nil { @@ -346,7 +347,7 @@ func (l *ListenerConn) Close() error { return l.cn.c.Close() } -// Err() returns the reason the connection was closed. It is not safe to call +// Err returns the reason the connection was closed. It is not safe to call // this function until l.Notify has been closed. func (l *ListenerConn) Err() error { return l.err @@ -354,32 +355,43 @@ func (l *ListenerConn) Err() error { var errListenerClosed = errors.New("pq: Listener has been closed") +// ErrChannelAlreadyOpen is returned from Listen when a channel is already +// open. var ErrChannelAlreadyOpen = errors.New("pq: channel is already open") + +// ErrChannelNotOpen is returned from Unlisten when a channel is not open. var ErrChannelNotOpen = errors.New("pq: channel is not open") +// ListenerEventType is an enumeration of listener event types. type ListenerEventType int const ( - // Emitted only when the database connection has been initially - // initialized. err will always be nil. + // ListenerEventConnected is emitted only when the database connection + // has been initially initialized. The err argument of the callback + // will always be nil. ListenerEventConnected ListenerEventType = iota - // Emitted after a database connection has been lost, either because of an - // error or because Close has been called. err will be set to the reason - // the database connection was lost. + // ListenerEventDisconnected is emitted after a database connection has + // been lost, either because of an error or because Close has been + // called. The err argument will be set to the reason the database + // connection was lost. ListenerEventDisconnected - // Emitted after a database connection has been re-established after - // connection loss. err will always be nil. After this event has been - // emitted, a nil pq.Notification is sent on the Listener.Notify channel. + // ListenerEventReconnected is emitted after a database connection has + // been re-established after connection loss. The err argument of the + // callback will always be nil. After this event has been emitted, a + // nil pq.Notification is sent on the Listener.Notify channel. ListenerEventReconnected - // Emitted after a connection to the database was attempted, but failed. - // err will be set to an error describing why the connection attempt did - // not succeed. + // ListenerEventConnectionAttemptFailed is emitted after a connection + // to the database was attempted, but failed. The err argument will be + // set to an error describing why the connection attempt did not + // succeed. ListenerEventConnectionAttemptFailed ) +// EventCallbackType is the event callback type. See also ListenerEventType +// constants' documentation. type EventCallbackType func(event ListenerEventType, err error) // Listener provides an interface for listening to notifications from a @@ -454,9 +466,9 @@ func NewDialListener(d Dialer, return l } -// Returns the notification channel for this listener. This is the same -// channel as Notify, and will not be recreated during the life time of the -// Listener. +// NotificationChannel returns the notification channel for this listener. +// This is the same channel as Notify, and will not be recreated during the +// life time of the Listener. func (l *Listener) NotificationChannel() <-chan *Notification { return l.Notify } @@ -639,7 +651,7 @@ func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notificatio // close and then return the error message from the connection, as // per ListenerConn's interface. if err != nil { - for _ = range notificationChan { + for range notificationChan { } doneChan <- cn.Err() return diff --git a/vendor/github.com/lib/pq/notify_test.go b/vendor/github.com/lib/pq/notify_test.go index 82a77e1eb..075666ddb 100644 --- a/vendor/github.com/lib/pq/notify_test.go +++ b/vendor/github.com/lib/pq/notify_test.go @@ -123,6 +123,9 @@ func TestConnUnlisten(t *testing.T) { } _, err = db.Exec("NOTIFY notify_test") + if err != nil { + t.Fatal(err) + } err = expectNotification(t, channel, "notify_test", "") if err != nil { @@ -159,6 +162,9 @@ func TestConnUnlistenAll(t *testing.T) { } _, err = db.Exec("NOTIFY notify_test") + if err != nil { + t.Fatal(err) + } err = expectNotification(t, channel, "notify_test", "") if err != nil { diff --git a/vendor/github.com/magiconair/properties/CHANGELOG.md b/vendor/github.com/magiconair/properties/CHANGELOG.md index ebd1bcd32..b0d98d81d 100644 --- a/vendor/github.com/magiconair/properties/CHANGELOG.md +++ b/vendor/github.com/magiconair/properties/CHANGELOG.md @@ -1,62 +1,65 @@ ## Changelog -### Unreleased +### [1.7.4](https://github.com/magiconair/properties/tree/v1.7.4) - 31 Oct 2017 + * [Issue #23](https://github.com/magiconair/properties/issues/23): Ignore blank lines with whitespaces * [PR #24](https://github.com/magiconair/properties/pull/24): Update keys when DisableExpansion is enabled Thanks to @mgurov for the fix. -### [1.7.3](https://github.com/magiconair/properties/tags/v1.7.3) - 10 Jul 2017 +### [1.7.3](https://github.com/magiconair/properties/tree/v1.7.3) - 10 Jul 2017 * [Issue #17](https://github.com/magiconair/properties/issues/17): Add [SetValue()](http://godoc.org/github.com/magiconair/properties#Properties.SetValue) method to set values generically * [Issue #22](https://github.com/magiconair/properties/issues/22): Add [LoadMap()](http://godoc.org/github.com/magiconair/properties#LoadMap) function to load properties from a string map -### [1.7.2](https://github.com/magiconair/properties/tags/v1.7.2) - 20 Mar 2017 +### [1.7.2](https://github.com/magiconair/properties/tree/v1.7.2) - 20 Mar 2017 * [Issue #15](https://github.com/magiconair/properties/issues/15): Drop gocheck dependency * [PR #21](https://github.com/magiconair/properties/pull/21): Add [Map()](http://godoc.org/github.com/magiconair/properties#Properties.Map) and [FilterFunc()](http://godoc.org/github.com/magiconair/properties#Properties.FilterFunc) -### [1.7.1](https://github.com/magiconair/properties/tags/v1.7.1) - 13 Jan 2017 +### [1.7.1](https://github.com/magiconair/properties/tree/v1.7.1) - 13 Jan 2017 + * [Issue #14](https://github.com/magiconair/properties/issues/14): Decouple TestLoadExpandedFile from `$USER` + * [PR #12](https://github.com/magiconair/properties/pull/12): Load from files and URLs * [PR #16](https://github.com/magiconair/properties/pull/16): Keep gofmt happy * [PR #18](https://github.com/magiconair/properties/pull/18): Fix Delete() function -### [1.7.0](https://github.com/magiconair/properties/tags/v1.7.0) - 20 Mar 2016 +### [1.7.0](https://github.com/magiconair/properties/tree/v1.7.0) - 20 Mar 2016 * [Issue #10](https://github.com/magiconair/properties/issues/10): Add [LoadURL,LoadURLs,MustLoadURL,MustLoadURLs](http://godoc.org/github.com/magiconair/properties#LoadURL) method to load properties from a URL. * [Issue #11](https://github.com/magiconair/properties/issues/11): Add [LoadString,MustLoadString](http://godoc.org/github.com/magiconair/properties#LoadString) method to load properties from an UTF8 string. * [PR #8](https://github.com/magiconair/properties/pull/8): Add [MustFlag](http://godoc.org/github.com/magiconair/properties#Properties.MustFlag) method to provide overrides via command line flags. (@pascaldekloe) -### [1.6.0](https://github.com/magiconair/properties/tags/v1.6.0) - 11 Dec 2015 +### [1.6.0](https://github.com/magiconair/properties/tree/v1.6.0) - 11 Dec 2015 * Add [Decode](http://godoc.org/github.com/magiconair/properties#Properties.Decode) method to populate struct from properties via tags. -### [1.5.6](https://github.com/magiconair/properties/tags/v1.5.6) - 18 Oct 2015 +### [1.5.6](https://github.com/magiconair/properties/tree/v1.5.6) - 18 Oct 2015 * Vendored in gopkg.in/check.v1 -### [1.5.5](https://github.com/magiconair/properties/tags/v1.5.5) - 31 Jul 2015 +### [1.5.5](https://github.com/magiconair/properties/tree/v1.5.5) - 31 Jul 2015 * [PR #6](https://github.com/magiconair/properties/pull/6): Add [Delete](http://godoc.org/github.com/magiconair/properties#Properties.Delete) method to remove keys including comments. (@gerbenjacobs) -### [1.5.4](https://github.com/magiconair/properties/tags/v1.5.4) - 23 Jun 2015 +### [1.5.4](https://github.com/magiconair/properties/tree/v1.5.4) - 23 Jun 2015 * [Issue #5](https://github.com/magiconair/properties/issues/5): Allow disabling of property expansion [DisableExpansion](http://godoc.org/github.com/magiconair/properties#Properties.DisableExpansion). When property expansion is disabled Properties become a simple key/value store and don't check for circular references. -### [1.5.3](https://github.com/magiconair/properties/tags/v1.5.3) - 02 Jun 2015 +### [1.5.3](https://github.com/magiconair/properties/tree/v1.5.3) - 02 Jun 2015 * [Issue #4](https://github.com/magiconair/properties/issues/4): Maintain key order in [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) and [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp) -### [1.5.2](https://github.com/magiconair/properties/tags/v1.5.2) - 10 Apr 2015 +### [1.5.2](https://github.com/magiconair/properties/tree/v1.5.2) - 10 Apr 2015 * [Issue #3](https://github.com/magiconair/properties/issues/3): Don't print comments in [WriteComment()](http://godoc.org/github.com/magiconair/properties#Properties.WriteComment) if they are all empty * Add clickable links to README -### [1.5.1](https://github.com/magiconair/properties/tags/v1.5.1) - 08 Dec 2014 +### [1.5.1](https://github.com/magiconair/properties/tree/v1.5.1) - 08 Dec 2014 * Added [GetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.GetParsedDuration) and [MustGetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.MustGetParsedDuration) for values specified compatible with [time.ParseDuration()](http://golang.org/pkg/time/#ParseDuration). -### [1.5.0](https://github.com/magiconair/properties/tags/v1.5.0) - 18 Nov 2014 +### [1.5.0](https://github.com/magiconair/properties/tree/v1.5.0) - 18 Nov 2014 * Added support for single and multi-line comments (reading, writing and updating) * The order of keys is now preserved @@ -64,31 +67,31 @@ * Added a [MustSet()](http://godoc.org/github.com/magiconair/properties#Properties.MustSet) method * Migrated test library from launchpad.net/gocheck to [gopkg.in/check.v1](http://gopkg.in/check.v1) -### [1.4.2](https://github.com/magiconair/properties/tags/v1.4.2) - 15 Nov 2014 +### [1.4.2](https://github.com/magiconair/properties/tree/v1.4.2) - 15 Nov 2014 * [Issue #2](https://github.com/magiconair/properties/issues/2): Fixed goroutine leak in parser which created two lexers but cleaned up only one -### [1.4.1](https://github.com/magiconair/properties/tags/v1.4.1) - 13 Nov 2014 +### [1.4.1](https://github.com/magiconair/properties/tree/v1.4.1) - 13 Nov 2014 * [Issue #1](https://github.com/magiconair/properties/issues/1): Fixed bug in Keys() method which returned an empty string -### [1.4.0](https://github.com/magiconair/properties/tags/v1.4.0) - 23 Sep 2014 +### [1.4.0](https://github.com/magiconair/properties/tree/v1.4.0) - 23 Sep 2014 * Added [Keys()](http://godoc.org/github.com/magiconair/properties#Properties.Keys) to get the keys * Added [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp) and [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) to get a subset of the properties -### [1.3.0](https://github.com/magiconair/properties/tags/v1.3.0) - 18 Mar 2014 +### [1.3.0](https://github.com/magiconair/properties/tree/v1.3.0) - 18 Mar 2014 * Added support for time.Duration -* Made MustXXX() failure beha[ior configurable (log.Fatal, panic](https://github.com/magiconair/properties/tags/vior configurable (log.Fatal, panic) - custom) +* Made MustXXX() failure beha[ior configurable (log.Fatal, panic](https://github.com/magiconair/properties/tree/vior configurable (log.Fatal, panic) - custom) * Changed default of MustXXX() failure from panic to log.Fatal -### [1.2.0](https://github.com/magiconair/properties/tags/v1.2.0) - 05 Mar 2014 +### [1.2.0](https://github.com/magiconair/properties/tree/v1.2.0) - 05 Mar 2014 * Added MustGet... functions * Added support for int and uint with range checks on 32 bit platforms -### [1.1.0](https://github.com/magiconair/properties/tags/v1.1.0) - 20 Jan 2014 +### [1.1.0](https://github.com/magiconair/properties/tree/v1.1.0) - 20 Jan 2014 * Renamed from goproperties to properties * Added support for expansion of environment vars in @@ -96,6 +99,6 @@ * Fixed bug where value expressions were not at the start of the string -### [1.0.0](https://github.com/magiconair/properties/tags/v1.0.0) - 7 Jan 2014 +### [1.0.0](https://github.com/magiconair/properties/tree/v1.0.0) - 7 Jan 2014 * Initial release diff --git a/vendor/github.com/magiconair/properties/README.md b/vendor/github.com/magiconair/properties/README.md index eb3b8c435..258fbb2b5 100644 --- a/vendor/github.com/magiconair/properties/README.md +++ b/vendor/github.com/magiconair/properties/README.md @@ -1,7 +1,7 @@ Overview [![Build Status](https://travis-ci.org/magiconair/properties.svg?branch=master)](https://travis-ci.org/magiconair/properties) ======== -#### Current version: 1.7.3 +#### Current version: 1.7.4 properties is a Go library for reading and writing properties files. diff --git a/vendor/github.com/magiconair/properties/lex.go b/vendor/github.com/magiconair/properties/lex.go index a3cba0319..c63fcc60d 100644 --- a/vendor/github.com/magiconair/properties/lex.go +++ b/vendor/github.com/magiconair/properties/lex.go @@ -196,9 +196,8 @@ func lexBeforeKey(l *lexer) stateFn { return lexComment case isWhitespace(r): - l.acceptRun(whitespace) l.ignore() - return lexKey + return lexBeforeKey default: l.backup() diff --git a/vendor/github.com/magiconair/properties/properties_test.go b/vendor/github.com/magiconair/properties/properties_test.go index 7e92618e0..a22f48df2 100644 --- a/vendor/github.com/magiconair/properties/properties_test.go +++ b/vendor/github.com/magiconair/properties/properties_test.go @@ -44,6 +44,8 @@ var complexTests = [][]string{ {"\nkey=value\n", "key", "value"}, {"\rkey=value\r", "key", "value"}, {"\r\nkey=value\r\n", "key", "value"}, + {"\nkey=value\n \nkey2=value2", "key", "value", "key2", "value2"}, + {"\nkey=value\n\t\nkey2=value2", "key", "value", "key2", "value2"}, // escaped chars in key {"k\\ ey = value", "k ey", "value"}, diff --git a/vendor/github.com/miekg/dns/.travis.yml b/vendor/github.com/miekg/dns/.travis.yml index bb8b8d40b..542dd68c0 100644 --- a/vendor/github.com/miekg/dns/.travis.yml +++ b/vendor/github.com/miekg/dns/.travis.yml @@ -1,14 +1,20 @@ language: go sudo: false go: - - 1.7.x - - 1.8.x + - 1.9.x - tip +env: + - TESTS="-race -v -bench=. -coverprofile=coverage.txt -covermode=atomic" + - TESTS="-race -v ./..." + before_install: # don't use the miekg/dns when testing forks - mkdir -p $GOPATH/src/github.com/miekg - ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true script: - - go test -race -v -bench=. + - go test $TESTS + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md index 32a49cbf5..94b54fe25 100644 --- a/vendor/github.com/miekg/dns/README.md +++ b/vendor/github.com/miekg/dns/README.md @@ -1,19 +1,19 @@ [![Build Status](https://travis-ci.org/miekg/dns.svg?branch=master)](https://travis-ci.org/miekg/dns) +[![Code Coverage](https://img.shields.io/codecov/c/github/miekg/dns/master.svg)](https://codecov.io/github/miekg/dns?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/miekg/dns)](https://goreportcard.com/report/miekg/dns) [![](https://godoc.org/github.com/miekg/dns?status.svg)](https://godoc.org/github.com/miekg/dns) # Alternative (more granular) approach to a DNS library > Less is more. -Complete and usable DNS library. All widely used Resource Records are -supported, including the DNSSEC types. It follows a lean and mean philosophy. -If there is stuff you should know as a DNS programmer there isn't a convenience -function for it. Server side and client side programming is supported, i.e. you -can build servers and resolvers with it. +Complete and usable DNS library. All widely used Resource Records are supported, including the +DNSSEC types. It follows a lean and mean philosophy. If there is stuff you should know as a DNS +programmer there isn't a convenience function for it. Server side and client side programming is +supported, i.e. you can build servers and resolvers with it. -We try to keep the "master" branch as sane as possible and at the bleeding edge -of standards, avoiding breaking changes wherever reasonable. We support the last -two versions of Go, currently: 1.7 and 1.8. +We try to keep the "master" branch as sane as possible and at the bleeding edge of standards, +avoiding breaking changes wherever reasonable. We support the last two versions of Go. # Goals @@ -55,11 +55,13 @@ A not-so-up-to-date-list-that-may-be-actually-current: * https://github.com/mehrdadrad/mylg * https://github.com/bamarni/dockness * https://github.com/fffaraz/microdns -* http://quilt.io +* http://kelda.io * https://github.com/ipdcode/hades (JD.COM) * https://github.com/StackExchange/dnscontrol/ * https://www.dnsperf.com/ * https://dnssectest.net/ +* https://dns.apebits.com +* https://github.com/oif/apex Send pull request if you want to be listed here. @@ -86,8 +88,8 @@ Miek Gieben - 2010-2012 - <miek@miek.nl> # Building -Building is done with the `go` tool. If you have setup your GOPATH -correctly, the following should work: +Building is done with the `go` tool. If you have setup your GOPATH correctly, the following should +work: go get github.com/miekg/dns go build github.com/miekg/dns @@ -150,9 +152,8 @@ Example programs can be found in the `github.com/miekg/exdns` repository. * 7314 - DNS (EDNS) EXPIRE Option * 7828 - edns-tcp-keepalive EDNS0 Option * 7553 - URI record -* 7858 - DNS over TLS: Initiation and Performance Considerations (draft) +* 7858 - DNS over TLS: Initiation and Performance Considerations * 7873 - Domain Name System (DNS) Cookies (draft-ietf-dnsop-cookies) -* xxxx - EDNS0 DNS Update Lease (draft) ## Loosely based upon diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go index 359a0ab8f..0914482ab 100644 --- a/vendor/github.com/miekg/dns/client.go +++ b/vendor/github.com/miekg/dns/client.go @@ -78,6 +78,7 @@ func (c *Client) writeTimeout() time.Duration { return dnsTimeout } +// Dial connects to the address on the named network. func (c *Client) Dial(address string) (conn *Conn, err error) { // create a new dialer with the appropriate timeout var d net.Dialer @@ -454,8 +455,7 @@ func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) { // DialTimeout acts like Dial but takes a timeout. func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) { - - client := Client{Net: "udp", Dialer: &net.Dialer{Timeout: timeout}} + client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}} conn, err = client.Dial(address) if err != nil { return nil, err diff --git a/vendor/github.com/miekg/dns/client_test.go b/vendor/github.com/miekg/dns/client_test.go index 3ff619cfa..ef977aefc 100644 --- a/vendor/github.com/miekg/dns/client_test.go +++ b/vendor/github.com/miekg/dns/client_test.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "strconv" + "strings" "sync" "testing" "time" @@ -15,7 +16,7 @@ func TestDialUDP(t *testing.T) { HandleFunc("miek.nl.", HelloServer) defer HandleRemove("miek.nl.") - s, addrstr, err := RunLocalUDPServer("[::1]:0") + s, addrstr, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -38,7 +39,7 @@ func TestClientSync(t *testing.T) { HandleFunc("miek.nl.", HelloServer) defer HandleRemove("miek.nl.") - s, addrstr, err := RunLocalUDPServer("127.0.0.1:0") + s, addrstr, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -72,7 +73,7 @@ func TestClientLocalAddress(t *testing.T) { HandleFunc("miek.nl.", HelloServerEchoAddrPort) defer HandleRemove("miek.nl.") - s, addrstr, err := RunLocalUDPServer("127.0.0.1:0") + s, addrstr, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -82,11 +83,11 @@ func TestClientLocalAddress(t *testing.T) { m.SetQuestion("miek.nl.", TypeSOA) c := new(Client) - laddr := net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 12345, Zone: ""} + laddr := net.UDPAddr{IP: net.ParseIP("0.0.0.0"), Port: 12345, Zone: ""} c.Dialer = &net.Dialer{LocalAddr: &laddr} r, _, err := c.Exchange(m, addrstr) if err != nil { - t.Errorf("failed to exchange: %v", err) + t.Fatalf("failed to exchange: %v", err) } if r != nil && r.Rcode != RcodeSuccess { t.Errorf("failed to get an valid answer\n%v", r) @@ -98,7 +99,7 @@ func TestClientLocalAddress(t *testing.T) { if txt == nil { t.Errorf("invalid TXT response\n%v", txt) } - if len(txt.Txt) != 1 || txt.Txt[0] != "127.0.0.1:12345" { + if len(txt.Txt) != 1 || !strings.Contains(txt.Txt[0], ":12345") { t.Errorf("invalid TXT response\n%v", txt.Txt) } } @@ -116,7 +117,7 @@ func TestClientTLSSyncV4(t *testing.T) { Certificates: []tls.Certificate{cert}, } - s, addrstr, err := RunLocalTLSServer("127.0.0.1:0", &config) + s, addrstr, err := RunLocalTLSServer(":0", &config) if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -162,70 +163,11 @@ func TestClientTLSSyncV4(t *testing.T) { } } -func TestClientTLSSyncV6(t *testing.T) { - HandleFunc("miek.nl.", HelloServer) - defer HandleRemove("miek.nl.") - - cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock) - if err != nil { - t.Fatalf("unable to build certificate: %v", err) - } - - config := tls.Config{ - Certificates: []tls.Certificate{cert}, - } - - s, addrstr, err := RunLocalTLSServer("[::1]:0", &config) - if err != nil { - t.Fatalf("unable to run test server: %v", err) - } - defer s.Shutdown() - - m := new(Msg) - m.SetQuestion("miek.nl.", TypeSOA) - - c := new(Client) - - // test tcp-tls - c.Net = "tcp-tls" - c.TLSConfig = &tls.Config{ - InsecureSkipVerify: true, - } - - r, _, err := c.Exchange(m, addrstr) - if err != nil { - t.Fatalf("failed to exchange: %v", err) - } - if r == nil { - t.Fatal("response is nil") - } - if r.Rcode != RcodeSuccess { - t.Errorf("failed to get an valid answer\n%v", r) - } - - // test tcp6-tls - c.Net = "tcp6-tls" - c.TLSConfig = &tls.Config{ - InsecureSkipVerify: true, - } - - r, _, err = c.Exchange(m, addrstr) - if err != nil { - t.Fatalf("failed to exchange: %v", err) - } - if r == nil { - t.Fatal("response is nil") - } - if r.Rcode != RcodeSuccess { - t.Errorf("failed to get an valid answer\n%v", r) - } -} - func TestClientSyncBadID(t *testing.T) { HandleFunc("miek.nl.", HelloServerBadID) defer HandleRemove("miek.nl.") - s, addrstr, err := RunLocalUDPServer("127.0.0.1:0") + s, addrstr, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -248,7 +190,7 @@ func TestClientEDNS0(t *testing.T) { HandleFunc("miek.nl.", HelloServer) defer HandleRemove("miek.nl.") - s, addrstr, err := RunLocalUDPServer("127.0.0.1:0") + s, addrstr, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -295,7 +237,7 @@ func TestClientEDNS0Local(t *testing.T) { HandleFunc("miek.nl.", handler) defer HandleRemove("miek.nl.") - s, addrstr, err := RunLocalUDPServer("127.0.0.1:0") + s, addrstr, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %s", err) } @@ -321,7 +263,6 @@ func TestClientEDNS0Local(t *testing.T) { } if r.Rcode != RcodeSuccess { t.Fatal("failed to get a valid answer") - t.Logf("%v\n", r) } txt := r.Extra[0].(*TXT).Txt[0] @@ -333,41 +274,11 @@ func TestClientEDNS0Local(t *testing.T) { got := r.Extra[1].(*OPT).Option[0].(*EDNS0_LOCAL).String() if got != optStr1 { t.Errorf("failed to get local edns0 answer; got %s, expected %s", got, optStr1) - t.Logf("%v\n", r) } got = r.Extra[1].(*OPT).Option[1].(*EDNS0_LOCAL).String() if got != optStr2 { t.Errorf("failed to get local edns0 answer; got %s, expected %s", got, optStr2) - t.Logf("%v\n", r) - } -} - -// ExampleTsigSecret_updateLeaseTSIG shows how to update a lease signed with TSIG -func ExampleTsigSecret_updateLeaseTSIG() { - m := new(Msg) - m.SetUpdate("t.local.ip6.io.") - rr, _ := NewRR("t.local.ip6.io. 30 A 127.0.0.1") - rrs := make([]RR, 1) - rrs[0] = rr - m.Insert(rrs) - - leaseRr := new(OPT) - leaseRr.Hdr.Name = "." - leaseRr.Hdr.Rrtype = TypeOPT - e := new(EDNS0_UL) - e.Code = EDNS0UL - e.Lease = 120 - leaseRr.Option = append(leaseRr.Option, e) - m.Extra = append(m.Extra, leaseRr) - - c := new(Client) - m.SetTsig("polvi.", HmacMD5, 300, time.Now().Unix()) - c.TsigSecret = map[string]string{"polvi.": "pRZgBrBvI4NAHZYhxmhs/Q=="} - - _, _, err := c.Exchange(m, "127.0.0.1:53") - if err != nil { - panic(err) } } @@ -376,7 +287,7 @@ func TestClientConn(t *testing.T) { defer HandleRemove("miek.nl.") // This uses TCP just to make it slightly different than TestClientSync - s, addrstr, err := RunLocalTCPServer("127.0.0.1:0") + s, addrstr, err := RunLocalTCPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -558,7 +469,7 @@ func TestTruncatedMsg(t *testing.T) { func TestTimeout(t *testing.T) { // Set up a dummy UDP server that won't respond - addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") + addr, err := net.ResolveUDPAddr("udp", ":0") if err != nil { t.Fatalf("unable to resolve local udp address: %v", err) } @@ -614,7 +525,7 @@ func TestTimeout(t *testing.T) { length := time.Since(start) if length > allowable { - t.Errorf("exchange took longer (%v) than specified Timeout (%v)", length, timeout) + t.Errorf("exchange took longer %v than specified Timeout %v", length, allowable) } } @@ -640,7 +551,7 @@ func TestConcurrentExchanges(t *testing.T) { HandleFunc("miek.nl.", handler) defer HandleRemove("miek.nl.") - s, addrstr, err := RunLocalUDPServer("127.0.0.1:0") + s, addrstr, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %s", err) } @@ -673,8 +584,7 @@ func TestConcurrentExchanges(t *testing.T) { wg.Wait() if r[0] == r[1] { - t.Log("Got same response object, expected non-shared responses") - t.Fail() + t.Errorf("got same response, expected non-shared responses") } } } diff --git a/vendor/github.com/miekg/dns/clientconfig.go b/vendor/github.com/miekg/dns/clientconfig.go index 0a1f5a92c..6479fef93 100644 --- a/vendor/github.com/miekg/dns/clientconfig.go +++ b/vendor/github.com/miekg/dns/clientconfig.go @@ -2,6 +2,7 @@ package dns import ( "bufio" + "io" "os" "strconv" "strings" @@ -25,8 +26,13 @@ func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) { return nil, err } defer file.Close() + return ClientConfigFromReader(file) +} + +// ClientConfigFromReader works like ClientConfigFromFile but takes an io.Reader as argument +func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) { c := new(ClientConfig) - scanner := bufio.NewScanner(file) + scanner := bufio.NewScanner(resolvconf) c.Servers = make([]string, 0) c.Search = make([]string, 0) c.Port = "53" diff --git a/vendor/github.com/miekg/dns/clientconfig_test.go b/vendor/github.com/miekg/dns/clientconfig_test.go index 7755a8a6f..4c5d1fb60 100644 --- a/vendor/github.com/miekg/dns/clientconfig_test.go +++ b/vendor/github.com/miekg/dns/clientconfig_test.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "testing" ) @@ -20,6 +21,26 @@ nameserver 10.28.10.2 nameserver 11.28.10.1` // <- NOTE: NO newline. func testConfig(t *testing.T, data string) { + cc, err := ClientConfigFromReader(strings.NewReader(data)) + if err != nil { + t.Errorf("error parsing resolv.conf: %v", err) + } + if l := len(cc.Servers); l != 2 { + t.Errorf("incorrect number of nameservers detected: %d", l) + } + if l := len(cc.Search); l != 1 { + t.Errorf("domain directive not parsed correctly: %v", cc.Search) + } else { + if cc.Search[0] != "somedomain.com" { + t.Errorf("domain is unexpected: %v", cc.Search[0]) + } + } +} + +func TestNameserver(t *testing.T) { testConfig(t, normal) } +func TestMissingFinalNewLine(t *testing.T) { testConfig(t, missingNewline) } + +func TestReadFromFile(t *testing.T) { tempDir, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("tempDir: %v", err) @@ -27,7 +48,7 @@ func testConfig(t *testing.T, data string) { defer os.RemoveAll(tempDir) path := filepath.Join(tempDir, "resolv.conf") - if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil { + if err := ioutil.WriteFile(path, []byte(normal), 0644); err != nil { t.Fatalf("writeFile: %v", err) } cc, err := ClientConfigFromFile(path) @@ -46,9 +67,6 @@ func testConfig(t *testing.T, data string) { } } -func TestNameserver(t *testing.T) { testConfig(t, normal) } -func TestMissingFinalNewLine(t *testing.T) { testConfig(t, missingNewline) } - func TestNameList(t *testing.T) { cfg := ClientConfig{ Ndots: 1, diff --git a/vendor/github.com/miekg/dns/compress_generate.go b/vendor/github.com/miekg/dns/compress_generate.go index 1a301e9f3..d2e5db26e 100644 --- a/vendor/github.com/miekg/dns/compress_generate.go +++ b/vendor/github.com/miekg/dns/compress_generate.go @@ -51,8 +51,9 @@ func main() { fatalIfErr(err) scope := pkg.Scope() - domainTypes := map[string]bool{} // Types that have a domain name in them (either comressible or not). - cdomainTypes := map[string]bool{} // Types that have a compressible domain name in them (subset of domainType) + var domainTypes []string // Types that have a domain name in them (either compressible or not). + var cdomainTypes []string // Types that have a compressible domain name in them (subset of domainType) +Names: for _, name := range scope.Names() { o := scope.Lookup(name) if o == nil || !o.Exported() { @@ -73,21 +74,25 @@ func main() { for i := 1; i < st.NumFields(); i++ { if _, ok := st.Field(i).Type().(*types.Slice); ok { if st.Tag(i) == `dns:"domain-name"` { - domainTypes[o.Name()] = true + domainTypes = append(domainTypes, o.Name()) + continue Names } if st.Tag(i) == `dns:"cdomain-name"` { - cdomainTypes[o.Name()] = true - domainTypes[o.Name()] = true + cdomainTypes = append(cdomainTypes, o.Name()) + domainTypes = append(domainTypes, o.Name()) + continue Names } continue } switch { case st.Tag(i) == `dns:"domain-name"`: - domainTypes[o.Name()] = true + domainTypes = append(domainTypes, o.Name()) + continue Names case st.Tag(i) == `dns:"cdomain-name"`: - cdomainTypes[o.Name()] = true - domainTypes[o.Name()] = true + cdomainTypes = append(cdomainTypes, o.Name()) + domainTypes = append(domainTypes, o.Name()) + continue Names } } } @@ -99,7 +104,7 @@ func main() { fmt.Fprint(b, "func compressionLenHelperType(c map[string]int, r RR) {\n") fmt.Fprint(b, "switch x := r.(type) {\n") - for name, _ := range domainTypes { + for _, name := range domainTypes { o := scope.Lookup(name) st, _ := getTypeStruct(o.Type(), scope) @@ -135,7 +140,7 @@ func main() { fmt.Fprint(b, "func compressionLenSearchType(c map[string]int, r RR) (int, bool) {\n") fmt.Fprint(b, "switch x := r.(type) {\n") - for name, _ := range cdomainTypes { + for _, name := range cdomainTypes { o := scope.Lookup(name) st, _ := getTypeStruct(o.Type(), scope) diff --git a/vendor/github.com/miekg/dns/dns.go b/vendor/github.com/miekg/dns/dns.go index b3292287c..5133eac72 100644 --- a/vendor/github.com/miekg/dns/dns.go +++ b/vendor/github.com/miekg/dns/dns.go @@ -6,9 +6,12 @@ const ( year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits. defaultTtl = 3600 // Default internal TTL. - DefaultMsgSize = 4096 // DefaultMsgSize is the standard default for messages larger than 512 bytes. - MinMsgSize = 512 // MinMsgSize is the minimal size of a DNS packet. - MaxMsgSize = 65535 // MaxMsgSize is the largest possible DNS packet. + // DefaultMsgSize is the standard default for messages larger than 512 bytes. + DefaultMsgSize = 4096 + // MinMsgSize is the minimal size of a DNS packet. + MinMsgSize = 512 + // MaxMsgSize is the largest possible DNS packet. + MaxMsgSize = 65535 ) // Error represents a DNS error. diff --git a/vendor/github.com/miekg/dns/dns_bench_test.go b/vendor/github.com/miekg/dns/dns_bench_test.go index bccc3d540..7bf8bd211 100644 --- a/vendor/github.com/miekg/dns/dns_bench_test.go +++ b/vendor/github.com/miekg/dns/dns_bench_test.go @@ -17,7 +17,26 @@ func BenchmarkMsgLength(b *testing.B) { return msg } name1 := "12345678901234567890123456789012345.12345678.123." - rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1) + rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) + msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil) + b.StartTimer() + for i := 0; i < b.N; i++ { + msg.Len() + } +} + +func BenchmarkMsgLengthNoCompression(b *testing.B) { + b.StopTimer() + makeMsg := func(question string, ans, ns, e []RR) *Msg { + msg := new(Msg) + msg.SetQuestion(Fqdn(question), TypeANY) + msg.Answer = append(msg.Answer, ans...) + msg.Ns = append(msg.Ns, ns...) + msg.Extra = append(msg.Extra, e...) + return msg + } + name1 := "12345678901234567890123456789012345.12345678.123." + rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil) b.StartTimer() for i := 0; i < b.N; i++ { @@ -36,7 +55,7 @@ func BenchmarkMsgLengthPack(b *testing.B) { return msg } name1 := "12345678901234567890123456789012345.12345678.123." - rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1) + rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -77,11 +96,11 @@ func BenchmarkCopy(b *testing.B) { b.ReportAllocs() m := new(Msg) m.SetQuestion("miek.nl.", TypeA) - rr, _ := NewRR("miek.nl. 2311 IN A 127.0.0.1") + rr := testRR("miek.nl. 2311 IN A 127.0.0.1") m.Answer = []RR{rr} - rr, _ = NewRR("miek.nl. 2311 IN NS 127.0.0.1") + rr = testRR("miek.nl. 2311 IN NS 127.0.0.1") m.Ns = []RR{rr} - rr, _ = NewRR("miek.nl. 2311 IN A 127.0.0.1") + rr = testRR("miek.nl. 2311 IN A 127.0.0.1") m.Extra = []RR{rr} b.ResetTimer() @@ -139,7 +158,7 @@ func BenchmarkUnpackMX(b *testing.B) { } func BenchmarkPackAAAAA(b *testing.B) { - aaaa, _ := NewRR(". IN A ::1") + aaaa := testRR(". IN A ::1") buf := make([]byte, aaaa.len()) b.ReportAllocs() @@ -150,7 +169,7 @@ func BenchmarkPackAAAAA(b *testing.B) { } func BenchmarkUnpackAAAA(b *testing.B) { - aaaa, _ := NewRR(". IN A ::1") + aaaa := testRR(". IN A ::1") buf := make([]byte, aaaa.len()) PackRR(aaaa, buf, 0, nil, false) @@ -173,7 +192,7 @@ func BenchmarkPackMsg(b *testing.B) { return msg } name1 := "12345678901234567890123456789012345.12345678.123." - rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1) + rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil) buf := make([]byte, 512) b.ReportAllocs() @@ -194,7 +213,7 @@ func BenchmarkUnpackMsg(b *testing.B) { return msg } name1 := "12345678901234567890123456789012345.12345678.123." - rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1) + rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil) msgBuf, _ := msg.Pack() b.ReportAllocs() diff --git a/vendor/github.com/miekg/dns/dns_test.go b/vendor/github.com/miekg/dns/dns_test.go index 5568c316c..5835c13d5 100644 --- a/vendor/github.com/miekg/dns/dns_test.go +++ b/vendor/github.com/miekg/dns/dns_test.go @@ -126,60 +126,17 @@ func TestBailiwick(t *testing.T) { } } -func TestPack(t *testing.T) { - rr := []string{"US. 86400 IN NSEC 0-.us. NS SOA RRSIG NSEC DNSKEY TYPE65534"} - m := new(Msg) - var err error - m.Answer = make([]RR, 1) - for _, r := range rr { - m.Answer[0], err = NewRR(r) - if err != nil { - t.Errorf("failed to create RR: %v", err) - continue - } - if _, err := m.Pack(); err != nil { - t.Errorf("packing failed: %v", err) - } - } - x := new(Msg) - ns, _ := NewRR("pool.ntp.org. 390 IN NS a.ntpns.org") - ns.(*NS).Ns = "a.ntpns.org" - x.Ns = append(m.Ns, ns) - x.Ns = append(m.Ns, ns) - x.Ns = append(m.Ns, ns) - // This crashes due to the fact the a.ntpns.org isn't a FQDN - // How to recover() from a remove panic()? - if _, err := x.Pack(); err == nil { - t.Error("packing should fail") - } - x.Answer = make([]RR, 1) - x.Answer[0], err = NewRR(rr[0]) - if err != nil { - t.Fatal(err) - } - if _, err := x.Pack(); err == nil { - t.Error("packing should fail") - } - x.Question = make([]Question, 1) - x.Question[0] = Question{";sd#eddddséâèµââ
â¥âxzztsestxssweewwsssstx@s@Zåµe@cn.pool.ntp.org.", TypeA, ClassINET} - if _, err := x.Pack(); err == nil { - t.Error("packing should fail") - } -} - func TestPackNAPTR(t *testing.T) { for _, n := range []string{ `apple.com. IN NAPTR 100 50 "se" "SIP+D2U" "" _sip._udp.apple.com.`, `apple.com. IN NAPTR 90 50 "se" "SIP+D2T" "" _sip._tcp.apple.com.`, `apple.com. IN NAPTR 50 50 "se" "SIPS+D2T" "" _sips._tcp.apple.com.`, } { - rr, _ := NewRR(n) + rr := testRR(n) msg := make([]byte, rr.len()) if off, err := PackRR(rr, msg, 0, nil, false); err != nil { t.Errorf("packing failed: %v", err) t.Errorf("length %d, need more than %d", rr.len(), off) - } else { - t.Logf("buf size needed: %d", off) } } } @@ -207,8 +164,8 @@ func TestMsgCompressLength(t *testing.T) { } name1 := "12345678901234567890123456789012345.12345678.123." - rrA, _ := NewRR(name1 + " 3600 IN A 192.0.2.1") - rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1) + rrA := testRR(name1 + " 3600 IN A 192.0.2.1") + rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) tests := []*Msg{ makeMsg(name1, []RR{rrA}, nil, nil), makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)} @@ -237,8 +194,8 @@ func TestMsgLength(t *testing.T) { } name1 := "12345678901234567890123456789012345.12345678.123." - rrA, _ := NewRR(name1 + " 3600 IN A 192.0.2.1") - rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1) + rrA := testRR(name1 + " 3600 IN A 192.0.2.1") + rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) tests := []*Msg{ makeMsg(name1, []RR{rrA}, nil, nil), makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)} @@ -331,14 +288,14 @@ func TestMsgCompressLength2(t *testing.T) { } func TestToRFC3597(t *testing.T) { - a, _ := NewRR("miek.nl. IN A 10.0.1.1") + a := testRR("miek.nl. IN A 10.0.1.1") x := new(RFC3597) x.ToRFC3597(a) if x.String() != `miek.nl. 3600 CLASS1 TYPE1 \# 4 0a000101` { t.Errorf("string mismatch, got: %s", x) } - b, _ := NewRR("miek.nl. IN MX 10 mx.miek.nl.") + b := testRR("miek.nl. IN MX 10 mx.miek.nl.") x.ToRFC3597(b) if x.String() != `miek.nl. 3600 CLASS1 TYPE15 \# 14 000a026d78046d69656b026e6c00` { t.Errorf("string mismatch, got: %s", x) @@ -372,11 +329,9 @@ func TestNoRdataUnpack(t *testing.T) { t.Errorf("failed to pack RR: %v", err) continue } - rr, _, err := UnpackRR(data[:off], 0) - if err != nil { + if _, _, err := UnpackRR(data[:off], 0); err != nil { t.Errorf("failed to unpack RR with zero rdata: %s: %v", TypeToString[typ], err) } - t.Log(rr) } } @@ -397,7 +352,7 @@ func TestRdataOverflow(t *testing.T) { } func TestCopy(t *testing.T) { - rr, _ := NewRR("miek.nl. 2311 IN A 127.0.0.1") // Weird TTL to avoid catching TTL + rr := testRR("miek.nl. 2311 IN A 127.0.0.1") // Weird TTL to avoid catching TTL rr1 := Copy(rr) if rr.String() != rr1.String() { t.Fatalf("Copy() failed %s != %s", rr.String(), rr1.String()) @@ -407,9 +362,9 @@ func TestCopy(t *testing.T) { func TestMsgCopy(t *testing.T) { m := new(Msg) m.SetQuestion("miek.nl.", TypeA) - rr, _ := NewRR("miek.nl. 2311 IN A 127.0.0.1") + rr := testRR("miek.nl. 2311 IN A 127.0.0.1") m.Answer = []RR{rr} - rr, _ = NewRR("miek.nl. 2311 IN NS 127.0.0.1") + rr = testRR("miek.nl. 2311 IN NS 127.0.0.1") m.Ns = []RR{rr} m1 := m.Copy() @@ -417,12 +372,12 @@ func TestMsgCopy(t *testing.T) { t.Fatalf("Msg.Copy() failed %s != %s", m.String(), m1.String()) } - m1.Answer[0], _ = NewRR("somethingelse.nl. 2311 IN A 127.0.0.1") + m1.Answer[0] = testRR("somethingelse.nl. 2311 IN A 127.0.0.1") if m.String() == m1.String() { t.Fatalf("Msg.Copy() failed; change to copy changed template %s", m.String()) } - rr, _ = NewRR("miek.nl. 2311 IN A 127.0.0.2") + rr = testRR("miek.nl. 2311 IN A 127.0.0.2") m1.Answer = append(m1.Answer, rr) if m1.Ns[0].String() == m1.Answer[1].String() { t.Fatalf("Msg.Copy() failed; append changed underlying array %s", m1.Ns[0].String()) @@ -448,6 +403,5 @@ func TestMsgPackBuffer(t *testing.T) { t.Errorf("packet %d failed to unpack", i) continue } - t.Logf("packet %d %s", i, m.String()) } } diff --git a/vendor/github.com/miekg/dns/dnssec_test.go b/vendor/github.com/miekg/dns/dnssec_test.go index ca085ed3b..d5de439ba 100644 --- a/vendor/github.com/miekg/dns/dnssec_test.go +++ b/vendor/github.com/miekg/dns/dnssec_test.go @@ -35,57 +35,6 @@ func getSoa() *SOA { return soa } -func TestGenerateEC(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode.") - } - key := new(DNSKEY) - key.Hdr.Rrtype = TypeDNSKEY - key.Hdr.Name = "miek.nl." - key.Hdr.Class = ClassINET - key.Hdr.Ttl = 14400 - key.Flags = 256 - key.Protocol = 3 - key.Algorithm = ECDSAP256SHA256 - privkey, _ := key.Generate(256) - t.Log(key.String()) - t.Log(key.PrivateKeyString(privkey)) -} - -func TestGenerateDSA(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode.") - } - key := new(DNSKEY) - key.Hdr.Rrtype = TypeDNSKEY - key.Hdr.Name = "miek.nl." - key.Hdr.Class = ClassINET - key.Hdr.Ttl = 14400 - key.Flags = 256 - key.Protocol = 3 - key.Algorithm = DSA - privkey, _ := key.Generate(1024) - t.Log(key.String()) - t.Log(key.PrivateKeyString(privkey)) -} - -func TestGenerateRSA(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode.") - } - key := new(DNSKEY) - key.Hdr.Rrtype = TypeDNSKEY - key.Hdr.Name = "miek.nl." - key.Hdr.Class = ClassINET - key.Hdr.Ttl = 14400 - key.Flags = 256 - key.Protocol = 3 - key.Algorithm = RSASHA256 - privkey, _ := key.Generate(1024) - t.Log(key.String()) - t.Log(key.PrivateKeyString(privkey)) -} - func TestSecure(t *testing.T) { soa := getSoa() @@ -211,10 +160,9 @@ func TestSignVerify(t *testing.T) { continue } if err := sig.Verify(key, []RR{r}); err != nil { - t.Error("failure to validate") + t.Errorf("failure to validate: %s", r.Header().Name) continue } - t.Logf("validated: %s", r.Header().Name) } } @@ -248,9 +196,7 @@ func Test65534(t *testing.T) { } if err := sig.Verify(key, []RR{t6}); err != nil { t.Error(err) - t.Error("failure to validate") - } else { - t.Logf("validated: %s", t6.Header().Name) + t.Errorf("failure to validate %s", t6.Header().Name) } } @@ -381,7 +327,7 @@ Created: 20110302104537 Publish: 20110302104537 Activate: 20110302104537` - xk, _ := NewRR(pub) + xk := testRR(pub) k := xk.(*DNSKEY) p, err := k.NewPrivateKey(priv) if err != nil { @@ -432,10 +378,7 @@ func TestSignVerifyECDSA(t *testing.T) { Algorithm: 14 (ECDSAP384SHA384) PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR` - eckey, err := NewRR(pub) - if err != nil { - t.Fatal(err) - } + eckey := testRR(pub) privkey, err := eckey.(*DNSKEY).NewPrivateKey(priv) if err != nil { t.Fatal(err) @@ -448,7 +391,7 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR` if ds.Digest != "72d7b62976ce06438e9c0bf319013cf801f09ecc84b8d7e9495f27e305c6a9b0563a9b5f4d288405c3008a946df983d6" { t.Fatal("wrong DS Digest") } - a, _ := NewRR("www.example.net. 3600 IN A 192.0.2.1") + a := testRR("www.example.net. 3600 IN A 192.0.2.1") sig := new(RRSIG) sig.Hdr = RR_Header{"example.net.", TypeRRSIG, ClassINET, 14400, 0} sig.Expiration, _ = StringToTime("20100909102025") @@ -473,10 +416,7 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR` } func TestSignVerifyECDSA2(t *testing.T) { - srv1, err := NewRR("srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.") - if err != nil { - t.Fatal(err) - } + srv1 := testRR("srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.") srv := srv1.(*SRV) // With this key @@ -511,7 +451,7 @@ func TestSignVerifyECDSA2(t *testing.T) { err = sig.Verify(key, []RR{srv}) if err != nil { - t.Logf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v", + t.Errorf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v", key.String(), srv.String(), sig.String(), @@ -530,10 +470,7 @@ func TestRFC6605P256(t *testing.T) { exPriv := `Private-key-format: v1.2 Algorithm: 13 (ECDSAP256SHA256) PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=` - rrDNSKEY, err := NewRR(exDNSKEY) - if err != nil { - t.Fatal(err) - } + rrDNSKEY := testRR(exDNSKEY) priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv) if err != nil { t.Fatal(err) @@ -542,10 +479,7 @@ PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=` exDS := `example.net. 3600 IN DS 55648 13 2 ( b4c8c1fe2e7477127b27115656ad6256f424625bf5c1 e2770ce6d6e37df61d17 )` - rrDS, err := NewRR(exDS) - if err != nil { - t.Fatal(err) - } + rrDS := testRR(exDS) ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256) if !reflect.DeepEqual(ourDS, rrDS.(*DS)) { t.Errorf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS)) @@ -556,15 +490,9 @@ PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=` 20100909100439 20100812100439 55648 example.net. qx6wLYqmh+l9oCKTN6qIc+bw6ya+KJ8oMz0YP107epXA yGmt+3SNruPFKG7tZoLBLlUzGGus7ZwmwWep666VCw== )` - rrA, err := NewRR(exA) - if err != nil { - t.Fatal(err) - } - rrRRSIG, err := NewRR(exRRSIG) - if err != nil { - t.Fatal(err) - } - if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil { + rrA := testRR(exA) + rrRRSIG := testRR(exRRSIG) + if err := rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil { t.Errorf("failure to validate the spec RRSIG: %v", err) } @@ -604,10 +532,7 @@ func TestRFC6605P384(t *testing.T) { exPriv := `Private-key-format: v1.2 Algorithm: 14 (ECDSAP384SHA384) PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR` - rrDNSKEY, err := NewRR(exDNSKEY) - if err != nil { - t.Fatal(err) - } + rrDNSKEY := testRR(exDNSKEY) priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv) if err != nil { t.Fatal(err) @@ -617,10 +542,7 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR` 72d7b62976ce06438e9c0bf319013cf801f09ecc84b8 d7e9495f27e305c6a9b0563a9b5f4d288405c3008a94 6df983d6 )` - rrDS, err := NewRR(exDS) - if err != nil { - t.Fatal(err) - } + rrDS := testRR(exDS) ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA384) if !reflect.DeepEqual(ourDS, rrDS.(*DS)) { t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS)) @@ -632,11 +554,8 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR` /L5hDKIvGDyI1fcARX3z65qrmPsVz73QD1Mr5CEqOiLP 95hxQouuroGCeZOvzFaxsT8Glr74hbavRKayJNuydCuz WTSSPdz7wnqXL5bdcJzusdnI0RSMROxxwGipWcJm )` - rrA, err := NewRR(exA) - if err != nil { - t.Fatal(err) - } - rrRRSIG, err := NewRR(exRRSIG) + rrA := testRR(exA) + rrRRSIG := testRR(exRRSIG) if err != nil { t.Fatal(err) } diff --git a/vendor/github.com/miekg/dns/dnsutil/util.go b/vendor/github.com/miekg/dns/dnsutil/util.go index c8c6af7b3..76ac4de66 100644 --- a/vendor/github.com/miekg/dns/dnsutil/util.go +++ b/vendor/github.com/miekg/dns/dnsutil/util.go @@ -20,7 +20,9 @@ import ( func AddOrigin(s, origin string) string { // ("foo.", "origin.") -> "foo." (already a FQDN) // ("foo", "origin.") -> "foo.origin." - // ("foo"), "origin" -> "foo.origin" + // ("foo", "origin") -> "foo.origin" + // ("foo", ".") -> "foo." (Same as dns.Fqdn()) + // ("foo.", ".") -> "foo." (Same as dns.Fqdn()) // ("@", "origin.") -> "origin." (@ represents the apex (bare) domain) // ("", "origin.") -> "origin." (not obvious) // ("foo", "") -> "foo" (not obvious) @@ -34,32 +36,34 @@ func AddOrigin(s, origin string) string { if s == "@" || len(s) == 0 { return origin // Expand apex. } - if origin == "." { - return s + origin // AddOrigin(s, ".") is an expensive way to add a ".". + return dns.Fqdn(s) } return s + "." + origin // The simple case. } // TrimDomainName trims origin from s if s is a subdomain. -// This function will never return "", but returns "@" instead (@ represents the apex (bare) domain). +// This function will never return "", but returns "@" instead (@ represents the apex domain). func TrimDomainName(s, origin string) string { // An apex (bare) domain is always returned as "@". // If the return value ends in a ".", the domain was not the suffix. // origin can end in "." or not. Either way the results should be the same. if len(s) == 0 { - return "@" // Return the apex (@) rather than "". + return "@" } // Someone is using TrimDomainName(s, ".") to remove a dot if it exists. if origin == "." { return strings.TrimSuffix(s, origin) } - // Dude, you aren't even if the right subdomain! + original := s + s = dns.Fqdn(s) + origin = dns.Fqdn(origin) + if !dns.IsSubDomain(origin, s) { - return s + return original } slabels := dns.Split(s) diff --git a/vendor/github.com/miekg/dns/dnsutil/util_test.go b/vendor/github.com/miekg/dns/dnsutil/util_test.go index 0f1ecec8e..6754789bc 100644 --- a/vendor/github.com/miekg/dns/dnsutil/util_test.go +++ b/vendor/github.com/miekg/dns/dnsutil/util_test.go @@ -10,6 +10,8 @@ func TestAddOrigin(t *testing.T) { {"@", "example.com.", "example.com."}, {"foo", "example.com.", "foo.example.com."}, {"foo.", "example.com.", "foo."}, + {"example.com", ".", "example.com."}, + {"example.com.", ".", "example.com."}, // Oddball tests: // In general origin should not be "" or "." but at least // these tests verify we don't crash and will keep results @@ -26,16 +28,15 @@ func TestAddOrigin(t *testing.T) { for _, test := range tests { actual := AddOrigin(test.e1, test.e2) if test.expected != actual { - t.Errorf("AddOrigin(%#v, %#v) expected %#v, go %#v\n", test.e1, test.e2, test.expected, actual) + t.Errorf("AddOrigin(%#v, %#v) expected %#v, got %#v\n", test.e1, test.e2, test.expected, actual) } } } func TestTrimDomainName(t *testing.T) { - // Basic tests. // Try trimming "example.com" and "example.com." from typical use cases. - var tests_examplecom = []struct{ experiment, expected string }{ + testsEx := []struct{ experiment, expected string }{ {"foo.example.com", "foo"}, {"foo.example.com.", "foo"}, {".foo.example.com", ".foo"}, @@ -51,10 +52,10 @@ func TestTrimDomainName(t *testing.T) { {".foo.ronco.com.", ".foo.ronco.com."}, } for _, dom := range []string{"example.com", "example.com."} { - for i, test := range tests_examplecom { + for i, test := range testsEx { actual := TrimDomainName(test.experiment, dom) if test.expected != actual { - t.Errorf("%d TrimDomainName(%#v, %#v): expected (%v) got (%v)\n", i, test.experiment, dom, test.expected, actual) + t.Errorf("%d TrimDomainName(%#v, %#v): expected %v, got %v\n", i, test.experiment, dom, test.expected, actual) } } } @@ -63,7 +64,7 @@ func TestTrimDomainName(t *testing.T) { // These test shouldn't be needed but I was weary of off-by-one errors. // In theory, these can't happen because there are no single-letter TLDs, // but it is good to exercize the code this way. - var tests = []struct{ experiment, expected string }{ + tests := []struct{ experiment, expected string }{ {"", "@"}, {".", "."}, {"a.b.c.d.e.f.", "a.b.c.d.e"}, @@ -105,7 +106,7 @@ func TestTrimDomainName(t *testing.T) { for i, test := range tests { actual := TrimDomainName(test.experiment, dom) if test.expected != actual { - t.Errorf("%d TrimDomainName(%#v, %#v): expected (%v) got (%v)\n", i, test.experiment, dom, test.expected, actual) + t.Errorf("%d TrimDomainName(%#v, %#v): expected %v, got %v\n", i, test.experiment, dom, test.expected, actual) } } } @@ -114,17 +115,16 @@ func TestTrimDomainName(t *testing.T) { // These test cases provide both origin, s, and the expected result. // If you find a bug in the while, this is probably the easiest place // to add it as a test case. - var tests_wild = []struct{ e1, e2, expected string }{ + var testsWild = []struct{ e1, e2, expected string }{ {"mathoverflow.net.", ".", "mathoverflow.net"}, {"mathoverflow.net", ".", "mathoverflow.net"}, {"", ".", "@"}, {"@", ".", "@"}, } - for i, test := range tests_wild { + for i, test := range testsWild { actual := TrimDomainName(test.e1, test.e2) if test.expected != actual { - t.Errorf("%d TrimDomainName(%#v, %#v): expected (%v) got (%v)\n", i, test.e1, test.e2, test.expected, actual) + t.Errorf("%d TrimDomainName(%#v, %#v): expected %v, got %v\n", i, test.e1, test.e2, test.expected, actual) } } - } diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go index ceabd24c1..26e288dfe 100644 --- a/vendor/github.com/miekg/dns/doc.go +++ b/vendor/github.com/miekg/dns/doc.go @@ -168,6 +168,11 @@ Basic use pattern when querying with a TSIG name "axfr." (note that these key na must be fully qualified - as they are domain names) and the base64 secret "so6ZGir4GPAqINNh9U5c3A==": +If an incoming message contains a TSIG record it MUST be the last record in +the additional section (RFC2845 3.2). This means that you should make the +call to SetTsig last, right before executing the query. If you make any +changes to the RRset after calling SetTsig() the signature will be incorrect. + c := new(dns.Client) c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} m := new(dns.Msg) diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go index 718842f28..a6a36bdf2 100644 --- a/vendor/github.com/miekg/dns/edns.go +++ b/vendor/github.com/miekg/dns/edns.go @@ -106,15 +106,12 @@ func (rr *OPT) SetVersion(v uint8) { // ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL). func (rr *OPT) ExtendedRcode() int { - return int((rr.Hdr.Ttl&0xFF000000)>>24) + 15 + return int((rr.Hdr.Ttl & 0xFF000000) >> 24) } // SetExtendedRcode sets the EDNS extended RCODE field. func (rr *OPT) SetExtendedRcode(v uint8) { - if v < RcodeBadVers { // Smaller than 16.. Use the 4 bits you have! - return - } - rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v-15) << 24) + rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v) << 24) } // UDPSize returns the UDP buffer size. @@ -185,7 +182,8 @@ func (e *EDNS0_NSID) pack() ([]byte, error) { return h, nil } -func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID } +// Option implements the EDNS0 interface. +func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID } // Option returns the option code. func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil } func (e *EDNS0_NSID) String() string { return string(e.Nsid) } @@ -219,6 +217,7 @@ type EDNS0_SUBNET struct { DraftOption bool // Set to true if using the old (0x50fa) option code } +// Option implements the EDNS0 interface. func (e *EDNS0_SUBNET) Option() uint16 { if e.DraftOption { return EDNS0SUBNETDRAFT @@ -232,6 +231,12 @@ func (e *EDNS0_SUBNET) pack() ([]byte, error) { b[2] = e.SourceNetmask b[3] = e.SourceScope switch e.Family { + case 0: + // "dig" sets AddressFamily to 0 if SourceNetmask is also 0 + // We might don't need to complain either + if e.SourceNetmask != 0 { + return nil, errors.New("dns: bad address family") + } case 1: if e.SourceNetmask > net.IPv4len*8 { return nil, errors.New("dns: bad netmask") @@ -266,6 +271,13 @@ func (e *EDNS0_SUBNET) unpack(b []byte) error { e.SourceNetmask = b[2] e.SourceScope = b[3] switch e.Family { + case 0: + // "dig" sets AddressFamily to 0 if SourceNetmask is also 0 + // It's okay to accept such a packet + if e.SourceNetmask != 0 { + return errors.New("dns: bad address family") + } + e.Address = net.IPv4(0, 0, 0, 0) case 1: if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 { return errors.New("dns: bad netmask") @@ -335,6 +347,7 @@ func (e *EDNS0_COOKIE) pack() ([]byte, error) { return h, nil } +// Option implements the EDNS0 interface. func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE } func (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil } func (e *EDNS0_COOKIE) String() string { return e.Cookie } @@ -356,6 +369,7 @@ type EDNS0_UL struct { Lease uint32 } +// Option implements the EDNS0 interface. func (e *EDNS0_UL) Option() uint16 { return EDNS0UL } func (e *EDNS0_UL) String() string { return strconv.FormatUint(uint64(e.Lease), 10) } @@ -385,6 +399,7 @@ type EDNS0_LLQ struct { LeaseLife uint32 } +// Option implements the EDNS0 interface. func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ } func (e *EDNS0_LLQ) pack() ([]byte, error) { @@ -421,6 +436,7 @@ type EDNS0_DAU struct { AlgCode []uint8 } +// Option implements the EDNS0 interface. func (e *EDNS0_DAU) Option() uint16 { return EDNS0DAU } func (e *EDNS0_DAU) pack() ([]byte, error) { return e.AlgCode, nil } func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = b; return nil } @@ -442,6 +458,7 @@ type EDNS0_DHU struct { AlgCode []uint8 } +// Option implements the EDNS0 interface. func (e *EDNS0_DHU) Option() uint16 { return EDNS0DHU } func (e *EDNS0_DHU) pack() ([]byte, error) { return e.AlgCode, nil } func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = b; return nil } @@ -463,6 +480,7 @@ type EDNS0_N3U struct { AlgCode []uint8 } +// Option implements the EDNS0 interface. func (e *EDNS0_N3U) Option() uint16 { return EDNS0N3U } func (e *EDNS0_N3U) pack() ([]byte, error) { return e.AlgCode, nil } func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = b; return nil } @@ -485,6 +503,7 @@ type EDNS0_EXPIRE struct { Expire uint32 } +// Option implements the EDNS0 interface. func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE } func (e *EDNS0_EXPIRE) String() string { return strconv.FormatUint(uint64(e.Expire), 10) } @@ -523,6 +542,7 @@ type EDNS0_LOCAL struct { Data []byte } +// Option implements the EDNS0 interface. func (e *EDNS0_LOCAL) Option() uint16 { return e.Code } func (e *EDNS0_LOCAL) String() string { return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data) @@ -554,6 +574,7 @@ type EDNS0_TCP_KEEPALIVE struct { Timeout uint16 // an idle timeout value for the TCP connection, specified in units of 100 milliseconds, encoded in network byte order. } +// Option implements the EDNS0 interface. func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 { return EDNS0TCPKEEPALIVE } func (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) { @@ -606,7 +627,8 @@ type EDNS0_PADDING struct { Padding []byte } -func (e *EDNS0_PADDING) pack() ([]byte, error) { return e.Padding, nil } +// Option implements the EDNS0 interface. func (e *EDNS0_PADDING) Option() uint16 { return EDNS0PADDING } +func (e *EDNS0_PADDING) pack() ([]byte, error) { return e.Padding, nil } func (e *EDNS0_PADDING) unpack(b []byte) error { e.Padding = b; return nil } func (e *EDNS0_PADDING) String() string { return fmt.Sprintf("%0X", e.Padding) } diff --git a/vendor/github.com/miekg/dns/edns_test.go b/vendor/github.com/miekg/dns/edns_test.go index c290b0c8a..f7cf15754 100644 --- a/vendor/github.com/miekg/dns/edns_test.go +++ b/vendor/github.com/miekg/dns/edns_test.go @@ -63,6 +63,6 @@ func TestOPTTtl(t *testing.T) { e.SetExtendedRcode(42) if e.ExtendedRcode() != 42 { - t.Errorf("set 42, expected %d, got %d", 42-15, e.ExtendedRcode()) + t.Errorf("set 42, expected %d, got %d", 42, e.ExtendedRcode()) } } diff --git a/vendor/github.com/miekg/dns/issue_test.go b/vendor/github.com/miekg/dns/issue_test.go index 265ad56c0..7299d3143 100644 --- a/vendor/github.com/miekg/dns/issue_test.go +++ b/vendor/github.com/miekg/dns/issue_test.go @@ -26,10 +26,7 @@ func TestTCPRtt(t *testing.T) { } func TestNSEC3MissingSalt(t *testing.T) { - rr, err := NewRR("ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. NSEC3 1 1 12 aabbccdd K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H") - if err != nil { - t.Fatalf("failed to parse example rr: %s", err) - } + rr := testRR("ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. NSEC3 1 1 12 aabbccdd K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H") m := new(Msg) m.Answer = []RR{rr} mb, err := m.Pack() @@ -47,10 +44,7 @@ func TestNSEC3MissingSalt(t *testing.T) { } func TestNSEC3MixedNextDomain(t *testing.T) { - rr, err := NewRR("ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. NSEC3 1 1 12 - k8udemvp1j2f7eg6jebps17vp3n8i58h") - if err != nil { - t.Fatalf("failed to parse example rr: %s", err) - } + rr := testRR("ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. NSEC3 1 1 12 - k8udemvp1j2f7eg6jebps17vp3n8i58h") m := new(Msg) m.Answer = []RR{rr} mb, err := m.Pack() diff --git a/vendor/github.com/miekg/dns/labels.go b/vendor/github.com/miekg/dns/labels.go index 9538d9c3a..760b89e71 100644 --- a/vendor/github.com/miekg/dns/labels.go +++ b/vendor/github.com/miekg/dns/labels.go @@ -1,7 +1,5 @@ package dns -import "strings" - // Holds a bunch of helper functions for dealing with labels. // SplitDomainName splits a name string into it's labels. @@ -44,7 +42,7 @@ func SplitDomainName(s string) (labels []string) { // CompareDomainName compares the names s1 and s2 and // returns how many labels they have in common starting from the *right*. -// The comparison stops at the first inequality. The names are not downcased +// The comparison stops at the first inequality. The names are downcased // before the comparison. // // www.miek.nl. and miek.nl. have two labels in common: miek and nl @@ -52,24 +50,21 @@ func SplitDomainName(s string) (labels []string) { // // s1 and s2 must be syntactically valid domain names. func CompareDomainName(s1, s2 string) (n int) { - s1, s2 = strings.ToLower(s1), strings.ToLower(s2) - s1 = Fqdn(s1) - s2 = Fqdn(s2) - l1 := Split(s1) - l2 := Split(s2) - // the first check: root label - if l1 == nil || l2 == nil { - return + if s1 == "." || s2 == "." { + return 0 } + l1 := Split(s1) + l2 := Split(s2) + j1 := len(l1) - 1 // end i1 := len(l1) - 2 // start j2 := len(l2) - 1 i2 := len(l2) - 2 // the second check can be done here: last/only label // before we fall through into the for-loop below - if s1[l1[j1]:] == s2[l2[j2]:] { + if equal(s1[l1[j1]:], s2[l2[j2]:]) { n++ } else { return @@ -78,7 +73,7 @@ func CompareDomainName(s1, s2 string) (n int) { if i1 < 0 || i2 < 0 { break } - if s1[l1[i1]:l1[j1]] == s2[l2[i2]:l2[j2]] { + if equal(s1[l1[i1]:l1[j1]], s2[l2[i2]:l2[j2]]) { n++ } else { break @@ -169,3 +164,28 @@ func PrevLabel(s string, n int) (i int, start bool) { } return lab[len(lab)-n], false } + +// equal compares a and b while ignoring case. It returns true when equal otherwise false. +func equal(a, b string) bool { + // might be lifted into API function. + la := len(a) + lb := len(b) + if la != lb { + return false + } + + for i := la - 1; i >= 0; i-- { + ai := a[i] + bi := b[i] + if ai >= 'A' && ai <= 'Z' { + ai |= ('a' - 'A') + } + if bi >= 'A' && bi <= 'Z' { + bi |= ('a' - 'A') + } + if ai != bi { + return false + } + } + return true +} diff --git a/vendor/github.com/miekg/dns/labels_test.go b/vendor/github.com/miekg/dns/labels_test.go index 9875d6cd9..d9bb556df 100644 --- a/vendor/github.com/miekg/dns/labels_test.go +++ b/vendor/github.com/miekg/dns/labels_test.go @@ -7,8 +7,8 @@ func TestCompareDomainName(t *testing.T) { s2 := "miek.nl." s3 := "www.bla.nl." s4 := "nl.www.bla." - s5 := "nl" - s6 := "miek.nl" + s5 := "nl." + s6 := "miek.nl." if CompareDomainName(s1, s2) != 2 { t.Errorf("%s with %s should be %d", s1, s2, 2) @@ -54,8 +54,6 @@ func TestSplit(t *testing.T) { for s, i := range splitter { if x := len(Split(s)); x != i { t.Errorf("labels should be %d, got %d: %s %v", i, x, s, Split(s)) - } else { - t.Logf("%s %v", s, Split(s)) } } } @@ -87,19 +85,19 @@ func TestPrevLabel(t *testing.T) { int } prever := map[prev]int{ - prev{"www.miek.nl.", 0}: 12, - prev{"www.miek.nl.", 1}: 9, - prev{"www.miek.nl.", 2}: 4, + {"www.miek.nl.", 0}: 12, + {"www.miek.nl.", 1}: 9, + {"www.miek.nl.", 2}: 4, - prev{"www.miek.nl", 0}: 11, - prev{"www.miek.nl", 1}: 9, - prev{"www.miek.nl", 2}: 4, + {"www.miek.nl", 0}: 11, + {"www.miek.nl", 1}: 9, + {"www.miek.nl", 2}: 4, - prev{"www.miek.nl.", 5}: 0, - prev{"www.miek.nl", 5}: 0, + {"www.miek.nl.", 5}: 0, + {"www.miek.nl", 5}: 0, - prev{"www.miek.nl.", 3}: 0, - prev{"www.miek.nl", 3}: 0, + {"www.miek.nl.", 3}: 0, + {"www.miek.nl", 3}: 0, } for s, i := range prever { x, ok := PrevLabel(s.string, s.int) @@ -176,28 +174,28 @@ func TestIsDomainName(t *testing.T) { func BenchmarkSplitLabels(b *testing.B) { for i := 0; i < b.N; i++ { - Split("www.example.com") + Split("www.example.com.") } } func BenchmarkLenLabels(b *testing.B) { for i := 0; i < b.N; i++ { - CountLabel("www.example.com") + CountLabel("www.example.com.") } } -func BenchmarkCompareLabels(b *testing.B) { +func BenchmarkCompareDomainName(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - CompareDomainName("www.example.com", "aa.example.com") + CompareDomainName("www.example.com.", "aa.example.com.") } } func BenchmarkIsSubDomain(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - IsSubDomain("www.example.com", "aa.example.com") - IsSubDomain("example.com", "aa.example.com") - IsSubDomain("miek.nl", "aa.example.com") + IsSubDomain("www.example.com.", "aa.example.com.") + IsSubDomain("example.com.", "aa.example.com.") + IsSubDomain("miek.nl.", "aa.example.com.") } } diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go index 605fe6c5c..afce17635 100644 --- a/vendor/github.com/miekg/dns/msg.go +++ b/vendor/github.com/miekg/dns/msg.go @@ -26,6 +26,7 @@ const ( maxDomainNameWireOctets = 255 // See RFC 1035 section 2.3.4 ) +// Errors defined in this package. var ( ErrAlg error = &Error{err: "bad algorithm"} // ErrAlg indicates an error with the (DNSSEC) algorithm. ErrAuth error = &Error{err: "bad authentication"} // ErrAuth indicates an error in the TSIG authentication. @@ -57,7 +58,7 @@ var ( // For instance, to make it return a static value: // // dns.Id = func() uint16 { return 3 } -var Id func() uint16 = id +var Id = id var ( idLock sync.Mutex @@ -360,7 +361,7 @@ Loop: case '"', '\\': s = append(s, '\\', b) // presentation-format \X escapes add an extra byte - maxLen += 1 + maxLen++ default: if b < 32 || b >= 127 { // unprintable, use \DDD var buf [3]byte @@ -913,67 +914,55 @@ func (dns *Msg) Len() int { return compressedLen(dns, dns.Compress) } func compressedLen(dns *Msg, compress bool) int { // We always return one more than needed. l := 12 // Message header is always 12 bytes - compression := map[string]int{} - - for i := 0; i < len(dns.Question); i++ { - l += dns.Question[i].len() - if compress { - compressionLenHelper(compression, dns.Question[i].Name) + if compress { + compression := map[string]int{} + for _, r := range dns.Question { + l += r.len() + compressionLenHelper(compression, r.Name) } - } - for i := 0; i < len(dns.Answer); i++ { - if dns.Answer[i] == nil { - continue + l += compressionLenSlice(compression, dns.Answer) + l += compressionLenSlice(compression, dns.Ns) + l += compressionLenSlice(compression, dns.Extra) + } else { + for _, r := range dns.Question { + l += r.len() } - l += dns.Answer[i].len() - if compress { - k, ok := compressionLenSearch(compression, dns.Answer[i].Header().Name) - if ok { - l += 1 - k + for _, r := range dns.Answer { + if r != nil { + l += r.len() } - compressionLenHelper(compression, dns.Answer[i].Header().Name) - k, ok = compressionLenSearchType(compression, dns.Answer[i]) - if ok { - l += 1 - k - } - compressionLenHelperType(compression, dns.Answer[i]) - } - } - for i := 0; i < len(dns.Ns); i++ { - if dns.Ns[i] == nil { - continue } - l += dns.Ns[i].len() - if compress { - k, ok := compressionLenSearch(compression, dns.Ns[i].Header().Name) - if ok { - l += 1 - k + for _, r := range dns.Ns { + if r != nil { + l += r.len() } - compressionLenHelper(compression, dns.Ns[i].Header().Name) - k, ok = compressionLenSearchType(compression, dns.Ns[i]) - if ok { - l += 1 - k + } + for _, r := range dns.Extra { + if r != nil { + l += r.len() } - compressionLenHelperType(compression, dns.Ns[i]) } } - for i := 0; i < len(dns.Extra); i++ { - if dns.Extra[i] == nil { + return l +} + +func compressionLenSlice(c map[string]int, rs []RR) int { + var l int + for _, r := range rs { + if r == nil { continue } - l += dns.Extra[i].len() - if compress { - k, ok := compressionLenSearch(compression, dns.Extra[i].Header().Name) - if ok { - l += 1 - k - } - compressionLenHelper(compression, dns.Extra[i].Header().Name) - k, ok = compressionLenSearchType(compression, dns.Extra[i]) - if ok { - l += 1 - k - } - compressionLenHelperType(compression, dns.Extra[i]) + l += r.len() + k, ok := compressionLenSearch(c, r.Header().Name) + if ok { + l += 1 - k + } + compressionLenHelper(c, r.Header().Name) + k, ok = compressionLenSearchType(c, r) + if ok { + l += 1 - k } + compressionLenHelperType(c, r) } return l } diff --git a/vendor/github.com/miekg/dns/nsecx_test.go b/vendor/github.com/miekg/dns/nsecx_test.go index 8d5f71797..f8edd6db1 100644 --- a/vendor/github.com/miekg/dns/nsecx_test.go +++ b/vendor/github.com/miekg/dns/nsecx_test.go @@ -15,7 +15,7 @@ func TestPackNsec3(t *testing.T) { } func TestNsec3(t *testing.T) { - nsec3, _ := NewRR("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM") + nsec3 := testRR("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM") if !nsec3.(*NSEC3).Match("nl.") { // name hash = sk4e8fj94u78smusb40o1n0oltbblu2r t.Fatal("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. should match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.") } @@ -28,7 +28,7 @@ func TestNsec3(t *testing.T) { if nsec3.(*NSEC3).Match("test.nl.") { // name hash = gd0ptr5bnfpimpu2d3v6gd4n0bai7s0q t.Fatal("gd0ptr5bnfpimpu2d3v6gd4n0bai7s0q.nl. should not match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.") } - nsec3, _ = NewRR("nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM") + nsec3 = testRR("nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM") if nsec3.(*NSEC3).Match("nl.") { t.Fatal("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. should not match a record without a owner hash") } @@ -127,7 +127,7 @@ func TestNsec3(t *testing.T) { } { covers := tc.rr.Cover(tc.name) if tc.covers != covers { - t.Fatalf("Cover failed for %s: expected %t, got %t [record: %s]", tc.name, tc.covers, covers, tc.rr) + t.Fatalf("cover failed for %s: expected %t, got %t [record: %s]", tc.name, tc.covers, covers, tc.rr) } } } diff --git a/vendor/github.com/miekg/dns/parse_test.go b/vendor/github.com/miekg/dns/parse_test.go index c7ecb499d..e7ffd4084 100644 --- a/vendor/github.com/miekg/dns/parse_test.go +++ b/vendor/github.com/miekg/dns/parse_test.go @@ -13,7 +13,6 @@ import ( "strings" "testing" "testing/quick" - "time" ) func TestDotInName(t *testing.T) { @@ -53,14 +52,10 @@ func TestTooLongDomainName(t *testing.T) { _, err := NewRR(dom + " IN A 127.0.0.1") if err == nil { t.Error("should be too long") - } else { - t.Logf("error is %v", err) } _, err = NewRR("..com. IN A 127.0.0.1") if err == nil { t.Error("should fail") - } else { - t.Logf("error is %v", err) } } @@ -103,23 +98,14 @@ func TestDomainNameAndTXTEscapes(t *testing.T) { s := rr1.String() rr2, err := NewRR(s) if err != nil { - t.Errorf("Error parsing unpacked RR's string: %v", err) - t.Errorf(" Bytes: %v", rrbytes) - t.Errorf("String: %v", s) + t.Errorf("error parsing unpacked RR's string: %v", err) } repacked := make([]byte, len(rrbytes)) if _, err := PackRR(rr2, repacked, 0, nil, false); err != nil { t.Errorf("error packing parsed RR: %v", err) - t.Errorf(" original Bytes: %v", rrbytes) - t.Errorf("unpacked Struct: %v", rr1) - t.Errorf(" parsed Struct: %v", rr2) } if !bytes.Equal(repacked, rrbytes) { t.Error("packed bytes don't match original bytes") - t.Errorf(" original bytes: %v", rrbytes) - t.Errorf(" packed bytes: %v", repacked) - t.Errorf("unpacked struct: %v", rr1) - t.Errorf(" parsed struct: %v", rr2) } } } @@ -349,8 +335,6 @@ func TestParseDirectiveMisc(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -372,8 +356,6 @@ func TestNSEC(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -391,8 +373,6 @@ func TestParseLOC(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -409,8 +389,6 @@ func TestParseDS(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -443,8 +421,6 @@ func TestQuotes(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is\n`%s'", i, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -466,8 +442,6 @@ func TestParseClass(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is\n`%s'", i, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -517,8 +491,6 @@ func TestBrace(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -544,34 +516,6 @@ func TestParseFailure(t *testing.T) { } } -func TestZoneParsing(t *testing.T) { - // parse_test.db - db := ` -a.example.com. IN A 127.0.0.1 -8db7._openpgpkey.example.com. IN OPENPGPKEY mQCNAzIG -$ORIGIN a.example.com. -test IN A 127.0.0.1 - IN SSHFP 1 2 ( - BC6533CDC95A79078A39A56EA7635984ED655318ADA9 - B6159E30723665DA95BB ) -$ORIGIN b.example.com. -test IN CNAME test.a.example.com. -` - start := time.Now().UnixNano() - to := ParseZone(strings.NewReader(db), "", "parse_test.db") - var i int - for x := range to { - i++ - if x.Error != nil { - t.Error(x.Error) - continue - } - t.Log(x.RR) - } - delta := time.Now().UnixNano() - start - t.Logf("%d RRs parsed in %.2f s (%.2f RR/s)", i, float32(delta)/1e9, float32(i)/(float32(delta)/1e9)) -} - func TestOmittedTTL(t *testing.T) { zone := ` $ORIGIN example.com. @@ -667,7 +611,6 @@ b1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D if err != nil { t.Fatalf("failed to parse RR: %v", err) } - t.Logf("RR: %s", rr) msg := new(Msg) msg.Answer = []RR{rr, rr} bytes, err := msg.Pack() @@ -682,7 +625,6 @@ b1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D } for i, rr := range msg.Answer { rr := rr.(*HIP) - t.Logf("RR: %s", rr) if l := len(rr.RendezvousServers); l != 2 { t.Fatalf("2 servers expected, only %d in record %d:\n%v", l, i, msg) } @@ -827,20 +769,14 @@ func TestSRVPacking(t *testing.T) { } func TestParseBackslash(t *testing.T) { - if r, err := NewRR("nul\\000gap.test.globnix.net. 600 IN A 192.0.2.10"); err != nil { + if _, err := NewRR("nul\\000gap.test.globnix.net. 600 IN A 192.0.2.10"); err != nil { t.Errorf("could not create RR with \\000 in it") - } else { - t.Logf("parsed %s", r.String()) } - if r, err := NewRR(`nul\000gap.test.globnix.net. 600 IN TXT "Hello\123"`); err != nil { + if _, err := NewRR(`nul\000gap.test.globnix.net. 600 IN TXT "Hello\123"`); err != nil { t.Errorf("could not create RR with \\000 in it") - } else { - t.Logf("parsed %s", r.String()) } - if r, err := NewRR(`m\ @\ iek.nl. IN 3600 A 127.0.0.1`); err != nil { + if _, err := NewRR(`m\ @\ iek.nl. IN 3600 A 127.0.0.1`); err != nil { t.Errorf("could not create RR with \\ and \\@ in it") - } else { - t.Logf("parsed %s", r.String()) } } @@ -887,8 +823,6 @@ func TestGposEidNimloc(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -906,8 +840,6 @@ func TestPX(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -1053,7 +985,6 @@ func TestTXT(t *testing.T) { if rr.(*TXT).Txt[1] != "b" { t.Errorf("Txt should have two chunk, last one my be 'b', but is %s", rr.(*TXT).Txt[1]) } - t.Log(rr.String()) } func TestTypeXXXX(t *testing.T) { @@ -1095,7 +1026,6 @@ func TestDigit(t *testing.T) { t.Fatalf("failed to parse %v", err) } PackRR(r, buf, 0, nil, false) - t.Log(buf) if buf[5] != i { t.Fatalf("5 pos must be %d, is %d", i, buf[5]) } @@ -1128,7 +1058,6 @@ func TestTxtEqual(t *testing.T) { // This is not an error, but keep this test. t.Errorf("these two TXT records should match:\n%s\n%s", rr1.String(), rr2.String()) } - t.Logf("%s\n%s", rr1.String(), rr2.String()) } func TestTxtLong(t *testing.T) { @@ -1155,12 +1084,8 @@ func TestMalformedPackets(t *testing.T) { // com = 63 6f 6d for _, packet := range packets { data, _ := hex.DecodeString(packet) - // for _, v := range data { - // t.Log(v) - // } var msg Msg msg.Unpack(data) - // println(msg.String()) } } @@ -1317,7 +1242,6 @@ func TestParseTokenOverflow(t *testing.T) { if err == nil { t.Fatalf("token overflow should return an error") } - t.Logf("err: %s\n", err) } func TestParseTLSA(t *testing.T) { @@ -1334,8 +1258,6 @@ func TestParseTLSA(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", o, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -1355,8 +1277,6 @@ func TestParseSMIMEA(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", o, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -1377,8 +1297,6 @@ func TestParseSSHFP(t *testing.T) { } if rr.String() != result { t.Errorf("`%s' should be equal to\n\n`%s', but is \n`%s'", o, result, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -1402,8 +1320,6 @@ func TestParseHINFO(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -1424,8 +1340,6 @@ func TestParseCAA(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -1472,8 +1386,6 @@ func TestParseURI(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } @@ -1490,8 +1402,6 @@ func TestParseAVC(t *testing.T) { } if rr.String() != o { t.Errorf("`%s' should be equal to\n`%s', but is `%s'", avc, o, rr.String()) - } else { - t.Logf("RR is OK: `%s'", rr.String()) } } } diff --git a/vendor/github.com/miekg/dns/privaterr_test.go b/vendor/github.com/miekg/dns/privaterr_test.go index f50d0f48c..fa5ff48fa 100644 --- a/vendor/github.com/miekg/dns/privaterr_test.go +++ b/vendor/github.com/miekg/dns/privaterr_test.go @@ -59,8 +59,6 @@ func TestPrivateText(t *testing.T) { } if rr.String() != testrecord { t.Errorf("record string representation did not match original %#v != %#v", rr.String(), testrecord) - } else { - t.Log(rr.String()) } } @@ -96,8 +94,6 @@ func TestPrivateByteSlice(t *testing.T) { if rr1.String() != testrecord { t.Errorf("record string representation did not match original %#v != %#v", rr1.String(), testrecord) - } else { - t.Log(rr1.String()) } } @@ -166,6 +162,5 @@ func TestPrivateZoneParser(t *testing.T) { if err := x.Error; err != nil { t.Fatal(err) } - t.Log(x.RR) } } diff --git a/vendor/github.com/miekg/dns/rr_test.go b/vendor/github.com/miekg/dns/rr_test.go new file mode 100644 index 000000000..77a153b1e --- /dev/null +++ b/vendor/github.com/miekg/dns/rr_test.go @@ -0,0 +1,7 @@ +package dns + +// testRR returns the RR from string s. The error is thrown away. +func testRR(s string) RR { + r, _ := NewRR(s) + return r +} diff --git a/vendor/github.com/miekg/dns/sanitize_test.go b/vendor/github.com/miekg/dns/sanitize_test.go index 2ba3fe9a3..b2c0e2081 100644 --- a/vendor/github.com/miekg/dns/sanitize_test.go +++ b/vendor/github.com/miekg/dns/sanitize_test.go @@ -3,37 +3,36 @@ package dns import "testing" func TestDedup(t *testing.T) { - // make it []string testcases := map[[3]RR][]string{ [...]RR{ - newRR(t, "mIek.nl. IN A 127.0.0.1"), - newRR(t, "mieK.nl. IN A 127.0.0.1"), - newRR(t, "miek.Nl. IN A 127.0.0.1"), + testRR("mIek.nl. IN A 127.0.0.1"), + testRR("mieK.nl. IN A 127.0.0.1"), + testRR("miek.Nl. IN A 127.0.0.1"), }: {"mIek.nl.\t3600\tIN\tA\t127.0.0.1"}, [...]RR{ - newRR(t, "miEk.nl. 2000 IN A 127.0.0.1"), - newRR(t, "mieK.Nl. 1000 IN A 127.0.0.1"), - newRR(t, "Miek.nL. 500 IN A 127.0.0.1"), + testRR("miEk.nl. 2000 IN A 127.0.0.1"), + testRR("mieK.Nl. 1000 IN A 127.0.0.1"), + testRR("Miek.nL. 500 IN A 127.0.0.1"), }: {"miEk.nl.\t500\tIN\tA\t127.0.0.1"}, [...]RR{ - newRR(t, "miek.nl. IN A 127.0.0.1"), - newRR(t, "miek.nl. CH A 127.0.0.1"), - newRR(t, "miek.nl. IN A 127.0.0.1"), + testRR("miek.nl. IN A 127.0.0.1"), + testRR("miek.nl. CH A 127.0.0.1"), + testRR("miek.nl. IN A 127.0.0.1"), }: {"miek.nl.\t3600\tIN\tA\t127.0.0.1", "miek.nl.\t3600\tCH\tA\t127.0.0.1", }, [...]RR{ - newRR(t, "miek.nl. CH A 127.0.0.1"), - newRR(t, "miek.nl. IN A 127.0.0.1"), - newRR(t, "miek.de. IN A 127.0.0.1"), + testRR("miek.nl. CH A 127.0.0.1"), + testRR("miek.nl. IN A 127.0.0.1"), + testRR("miek.de. IN A 127.0.0.1"), }: {"miek.nl.\t3600\tCH\tA\t127.0.0.1", "miek.nl.\t3600\tIN\tA\t127.0.0.1", "miek.de.\t3600\tIN\tA\t127.0.0.1", }, [...]RR{ - newRR(t, "miek.de. IN A 127.0.0.1"), - newRR(t, "miek.nl. 200 IN A 127.0.0.1"), - newRR(t, "miek.nl. 300 IN A 127.0.0.1"), + testRR("miek.de. IN A 127.0.0.1"), + testRR("miek.nl. 200 IN A 127.0.0.1"), + testRR("miek.nl. 300 IN A 127.0.0.1"), }: {"miek.de.\t3600\tIN\tA\t127.0.0.1", "miek.nl.\t200\tIN\tA\t127.0.0.1", }, @@ -51,9 +50,9 @@ func TestDedup(t *testing.T) { func BenchmarkDedup(b *testing.B) { rrs := []RR{ - newRR(nil, "miEk.nl. 2000 IN A 127.0.0.1"), - newRR(nil, "mieK.Nl. 1000 IN A 127.0.0.1"), - newRR(nil, "Miek.nL. 500 IN A 127.0.0.1"), + testRR("miEk.nl. 2000 IN A 127.0.0.1"), + testRR("mieK.Nl. 1000 IN A 127.0.0.1"), + testRR("Miek.nL. 500 IN A 127.0.0.1"), } m := make(map[string]RR) for i := 0; i < b.N; i++ { @@ -63,9 +62,9 @@ func BenchmarkDedup(b *testing.B) { func TestNormalizedString(t *testing.T) { tests := map[RR]string{ - newRR(t, "mIEk.Nl. 3600 IN A 127.0.0.1"): "miek.nl.\tIN\tA\t127.0.0.1", - newRR(t, "m\\ iek.nL. 3600 IN A 127.0.0.1"): "m\\ iek.nl.\tIN\tA\t127.0.0.1", - newRR(t, "m\\\tIeK.nl. 3600 in A 127.0.0.1"): "m\\009iek.nl.\tIN\tA\t127.0.0.1", + testRR("mIEk.Nl. 3600 IN A 127.0.0.1"): "miek.nl.\tIN\tA\t127.0.0.1", + testRR("m\\ iek.nL. 3600 IN A 127.0.0.1"): "m\\ iek.nl.\tIN\tA\t127.0.0.1", + testRR("m\\\tIeK.nl. 3600 in A 127.0.0.1"): "m\\009iek.nl.\tIN\tA\t127.0.0.1", } for tc, expected := range tests { n := normalizedString(tc) @@ -74,11 +73,3 @@ func TestNormalizedString(t *testing.T) { } } } - -func newRR(t *testing.T, s string) RR { - r, err := NewRR(s) - if err != nil { - t.Logf("newRR: %v", err) - } - return r -} diff --git a/vendor/github.com/miekg/dns/scan.go b/vendor/github.com/miekg/dns/scan.go index c7b1eb19a..243b9cf18 100644 --- a/vendor/github.com/miekg/dns/scan.go +++ b/vendor/github.com/miekg/dns/scan.go @@ -38,7 +38,7 @@ const ( zOwner zClass zDirOrigin // $ORIGIN - zDirTtl // $TTL + zDirTTL // $TTL zDirInclude // $INCLUDE zDirGenerate // $GENERATE @@ -51,13 +51,13 @@ const ( zExpectAny // Expect rrtype, ttl or class zExpectAnyNoClass // Expect rrtype or ttl zExpectAnyNoClassBl // The whitespace after _EXPECT_ANY_NOCLASS - zExpectAnyNoTtl // Expect rrtype or class - zExpectAnyNoTtlBl // Whitespace after _EXPECT_ANY_NOTTL + zExpectAnyNoTTL // Expect rrtype or class + zExpectAnyNoTTLBl // Whitespace after _EXPECT_ANY_NOTTL zExpectRrtype // Expect rrtype zExpectRrtypeBl // Whitespace BEFORE rrtype zExpectRdata // The first element of the rdata - zExpectDirTtlBl // Space after directive $TTL - zExpectDirTtl // Directive $TTL + zExpectDirTTLBl // Space after directive $TTL + zExpectDirTTL // Directive $TTL zExpectDirOriginBl // Space after directive $ORIGIN zExpectDirOrigin // Directive $ORIGIN zExpectDirIncludeBl // Space after directive $INCLUDE @@ -231,8 +231,8 @@ func parseZone(r io.Reader, origin string, defttl *ttlState, f string, t chan *T h.Name = name prevName = h.Name st = zExpectOwnerBl - case zDirTtl: - st = zExpectDirTtlBl + case zDirTTL: + st = zExpectDirTTLBl case zDirOrigin: st = zExpectDirOriginBl case zDirInclude: @@ -251,7 +251,7 @@ func parseZone(r io.Reader, origin string, defttl *ttlState, f string, t chan *T // Discard, can happen when there is nothing on the // line except the RR type case zString: - ttl, ok := stringToTtl(l.token) + ttl, ok := stringToTTL(l.token) if !ok { t <- &Token{Error: &ParseError{f, "not a TTL", l}} return @@ -260,7 +260,7 @@ func parseZone(r io.Reader, origin string, defttl *ttlState, f string, t chan *T if defttl == nil || !defttl.isByDirective { defttl = &ttlState{ttl, false} } - st = zExpectAnyNoTtlBl + st = zExpectAnyNoTTLBl default: t <- &Token{Error: &ParseError{f, "syntax error at beginning", l}} @@ -307,13 +307,13 @@ func parseZone(r io.Reader, origin string, defttl *ttlState, f string, t chan *T } parseZone(r1, neworigin, defttl, l.token, t, include+1) st = zExpectOwnerDir - case zExpectDirTtlBl: + case zExpectDirTTLBl: if l.value != zBlank { t <- &Token{Error: &ParseError{f, "no blank after $TTL-directive", l}} return } - st = zExpectDirTtl - case zExpectDirTtl: + st = zExpectDirTTL + case zExpectDirTTL: if l.value != zString { t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}} return @@ -322,7 +322,7 @@ func parseZone(r io.Reader, origin string, defttl *ttlState, f string, t chan *T t <- &Token{Error: e} return } - ttl, ok := stringToTtl(l.token) + ttl, ok := stringToTTL(l.token) if !ok { t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}} return @@ -385,7 +385,7 @@ func parseZone(r io.Reader, origin string, defttl *ttlState, f string, t chan *T h.Class = l.torc st = zExpectAnyNoClassBl case zString: - ttl, ok := stringToTtl(l.token) + ttl, ok := stringToTTL(l.token) if !ok { t <- &Token{Error: &ParseError{f, "not a TTL", l}} return @@ -394,7 +394,7 @@ func parseZone(r io.Reader, origin string, defttl *ttlState, f string, t chan *T if defttl == nil || !defttl.isByDirective { defttl = &ttlState{ttl, false} } - st = zExpectAnyNoTtlBl + st = zExpectAnyNoTTLBl default: t <- &Token{Error: &ParseError{f, "expecting RR type, TTL or class, not this...", l}} return @@ -405,13 +405,13 @@ func parseZone(r io.Reader, origin string, defttl *ttlState, f string, t chan *T return } st = zExpectAnyNoClass - case zExpectAnyNoTtlBl: + case zExpectAnyNoTTLBl: if l.value != zBlank { t <- &Token{Error: &ParseError{f, "no blank before TTL", l}} return } - st = zExpectAnyNoTtl - case zExpectAnyNoTtl: + st = zExpectAnyNoTTL + case zExpectAnyNoTTL: switch l.value { case zClass: h.Class = l.torc @@ -426,7 +426,7 @@ func parseZone(r io.Reader, origin string, defttl *ttlState, f string, t chan *T case zExpectAnyNoClass: switch l.value { case zString: - ttl, ok := stringToTtl(l.token) + ttl, ok := stringToTTL(l.token) if !ok { t <- &Token{Error: &ParseError{f, "not a TTL", l}} return @@ -539,7 +539,7 @@ func zlexer(s *scan, c chan lex) { // escape $... start with a \ not a $, so this will work switch l.tokenUpper { case "$TTL": - l.value = zDirTtl + l.value = zDirTTL case "$ORIGIN": l.value = zDirOrigin case "$INCLUDE": @@ -837,8 +837,8 @@ func typeToInt(token string) (uint16, bool) { return uint16(typ), true } -// Parse things like 2w, 2m, etc, Return the time in seconds. -func stringToTtl(token string) (uint32, bool) { +// stringToTTL parses things like 2w, 2m, etc, and returns the time in seconds. +func stringToTTL(token string) (uint32, bool) { s := uint32(0) i := uint32(0) for _, c := range token { diff --git a/vendor/github.com/miekg/dns/scan_rr.go b/vendor/github.com/miekg/dns/scan_rr.go index 824b9c949..c53ba1d5b 100644 --- a/vendor/github.com/miekg/dns/scan_rr.go +++ b/vendor/github.com/miekg/dns/scan_rr.go @@ -590,7 +590,7 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, &ParseError{f, "bad SOA zone parameter", l}, "" } // We allow other fields to be unitful duration strings - if v, ok = stringToTtl(l.token); !ok { + if v, ok = stringToTTL(l.token); !ok { return nil, &ParseError{f, "bad SOA zone parameter", l}, "" } diff --git a/vendor/github.com/miekg/dns/server.go b/vendor/github.com/miekg/dns/server.go index 0ca6e008c..ee7e256fb 100644 --- a/vendor/github.com/miekg/dns/server.go +++ b/vendor/github.com/miekg/dns/server.go @@ -297,10 +297,7 @@ type Server struct { // DecorateWriter is optional, allows customization of the process that writes raw DNS messages. DecorateWriter DecorateWriter - // Graceful shutdown handling - - inFlight sync.WaitGroup - + // Shutdown handling lock sync.RWMutex started bool } @@ -412,10 +409,8 @@ func (srv *Server) ActivateAndServe() error { return &Error{err: "bad listeners"} } -// Shutdown gracefully shuts down a server. After a call to Shutdown, ListenAndServe and -// ActivateAndServe will return. All in progress queries are completed before the server -// is taken down. If the Shutdown is taking longer than the reading timeout an error -// is returned. +// Shutdown shuts down a server. After a call to Shutdown, ListenAndServe and +// ActivateAndServe will return. func (srv *Server) Shutdown() error { srv.lock.Lock() if !srv.started { @@ -431,19 +426,7 @@ func (srv *Server) Shutdown() error { if srv.Listener != nil { srv.Listener.Close() } - - fin := make(chan bool) - go func() { - srv.inFlight.Wait() - fin <- true - }() - - select { - case <-time.After(srv.getReadTimeout()): - return &Error{err: "server shutdown is pending"} - case <-fin: - return nil - } + return nil } // getReadTimeout is a helper func to use system timeout if server did not intend to change it. @@ -493,7 +476,6 @@ func (srv *Server) serveTCP(l net.Listener) error { if err != nil { continue } - srv.inFlight.Add(1) go srv.serve(rw.RemoteAddr(), handler, m, nil, nil, rw) } } @@ -529,15 +511,12 @@ func (srv *Server) serveUDP(l *net.UDPConn) error { if err != nil { continue } - srv.inFlight.Add(1) go srv.serve(s.RemoteAddr(), handler, m, l, s, nil) } } // Serve a new connection. func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *SessionUDP, t net.Conn) { - defer srv.inFlight.Done() - w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s} if srv.DecorateWriter != nil { w.writer = srv.DecorateWriter(w) diff --git a/vendor/github.com/miekg/dns/server_test.go b/vendor/github.com/miekg/dns/server_test.go index b74f2f1a8..1a4b7ca0c 100644 --- a/vendor/github.com/miekg/dns/server_test.go +++ b/vendor/github.com/miekg/dns/server_test.go @@ -147,7 +147,7 @@ func TestServing(t *testing.T) { defer HandleRemove("miek.nl.") defer HandleRemove("example.com.") - s, addrstr, err := RunLocalUDPServer("127.0.0.1:0") + s, addrstr, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -202,7 +202,7 @@ func TestServingTLS(t *testing.T) { Certificates: []tls.Certificate{cert}, } - s, addrstr, err := RunLocalTLSServer("127.0.0.1:0", &config) + s, addrstr, err := RunLocalTLSServer(":0", &config) if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -247,13 +247,77 @@ func TestServingTLS(t *testing.T) { } } +func TestServingListenAndServe(t *testing.T) { + HandleFunc("example.com.", AnotherHelloServer) + defer HandleRemove("example.com.") + + waitLock := sync.Mutex{} + server := &Server{Addr: ":0", Net: "udp", ReadTimeout: time.Hour, WriteTimeout: time.Hour, NotifyStartedFunc: waitLock.Unlock} + waitLock.Lock() + + go func() { + server.ListenAndServe() + }() + waitLock.Lock() + + c, m := new(Client), new(Msg) + m.SetQuestion("example.com.", TypeTXT) + addr := server.PacketConn.LocalAddr().String() // Get address via the PacketConn that gets set. + r, _, err := c.Exchange(m, addr) + if err != nil { + t.Fatal("failed to exchange example.com", err) + } + txt := r.Extra[0].(*TXT).Txt[0] + if txt != "Hello example" { + t.Error("unexpected result for example.com", txt, "!= Hello example") + } + server.Shutdown() +} + +func TestServingListenAndServeTLS(t *testing.T) { + HandleFunc("example.com.", AnotherHelloServer) + defer HandleRemove("example.com.") + + cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock) + if err != nil { + t.Fatalf("unable to build certificate: %v", err) + } + + config := &tls.Config{ + Certificates: []tls.Certificate{cert}, + } + + waitLock := sync.Mutex{} + server := &Server{Addr: ":0", Net: "tcp", TLSConfig: config, ReadTimeout: time.Hour, WriteTimeout: time.Hour, NotifyStartedFunc: waitLock.Unlock} + waitLock.Lock() + + go func() { + server.ListenAndServe() + }() + waitLock.Lock() + + c, m := new(Client), new(Msg) + c.Net = "tcp" + m.SetQuestion("example.com.", TypeTXT) + addr := server.Listener.Addr().String() // Get address via the Listener that gets set. + r, _, err := c.Exchange(m, addr) + if err != nil { + t.Fatal(err) + } + txt := r.Extra[0].(*TXT).Txt[0] + if txt != "Hello example" { + t.Error("unexpected result for example.com", txt, "!= Hello example") + } + server.Shutdown() +} + func BenchmarkServe(b *testing.B) { b.StopTimer() HandleFunc("miek.nl.", HelloServer) defer HandleRemove("miek.nl.") a := runtime.GOMAXPROCS(4) - s, addrstr, err := RunLocalUDPServer("127.0.0.1:0") + s, addrstr, err := RunLocalUDPServer(":0") if err != nil { b.Fatalf("unable to run test server: %v", err) } @@ -306,7 +370,7 @@ func BenchmarkServeCompress(b *testing.B) { HandleFunc("miek.nl.", HelloServerCompress) defer HandleRemove("miek.nl.") a := runtime.GOMAXPROCS(4) - s, addrstr, err := RunLocalUDPServer("127.0.0.1:0") + s, addrstr, err := RunLocalUDPServer(":0") if err != nil { b.Fatalf("unable to run test server: %v", err) } @@ -407,7 +471,7 @@ func TestServingLargeResponses(t *testing.T) { HandleFunc("example.", HelloServerLargeResponse) defer HandleRemove("example.") - s, addrstr, err := RunLocalUDPServer("127.0.0.1:0") + s, addrstr, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -447,7 +511,7 @@ func TestServingResponse(t *testing.T) { t.Skip("skipping test in short mode.") } HandleFunc("miek.nl.", HelloServer) - s, addrstr, err := RunLocalUDPServer("127.0.0.1:0") + s, addrstr, err := RunLocalUDPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -467,7 +531,7 @@ func TestServingResponse(t *testing.T) { } s.Shutdown() - s, addrstr, err = RunLocalUDPServerUnsafe("127.0.0.1:0") + s, addrstr, err = RunLocalUDPServerUnsafe(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -481,7 +545,7 @@ func TestServingResponse(t *testing.T) { } func TestShutdownTCP(t *testing.T) { - s, _, err := RunLocalTCPServer("127.0.0.1:0") + s, _, err := RunLocalTCPServer(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -501,7 +565,7 @@ func TestShutdownTLS(t *testing.T) { Certificates: []tls.Certificate{cert}, } - s, _, err := RunLocalTLSServer("127.0.0.1:0", &config) + s, _, err := RunLocalTLSServer(":0", &config) if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -529,7 +593,7 @@ func (t *trigger) Get() bool { func TestHandlerCloseTCP(t *testing.T) { - ln, err := net.Listen("tcp", "127.0.0.1:0") + ln, err := net.Listen("tcp", ":0") if err != nil { panic(err) } @@ -553,7 +617,7 @@ func TestHandlerCloseTCP(t *testing.T) { exchange: _, _, err := c.Exchange(m, addr) if err != nil && err != io.EOF { - t.Logf("exchange failed: %s\n", err) + t.Errorf("exchange failed: %s\n", err) if tries == 3 { return } @@ -569,7 +633,7 @@ func TestHandlerCloseTCP(t *testing.T) { } func TestShutdownUDP(t *testing.T) { - s, _, fin, err := RunLocalUDPServerWithFinChan("127.0.0.1:0") + s, _, fin, err := RunLocalUDPServerWithFinChan(":0") if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -580,7 +644,23 @@ func TestShutdownUDP(t *testing.T) { select { case <-fin: case <-time.After(2 * time.Second): - t.Error("Could not shutdown test UDP server. Gave up waiting") + t.Error("could not shutdown test UDP server. Gave up waiting") + } +} + +func TestServerStartStopRace(t *testing.T) { + for i := 0; i < 10; i++ { + var err error + s := &Server{} + s, _, _, err = RunLocalUDPServerWithFinChan(":0") + if err != nil { + t.Fatalf("Could not start server: %s", err) + } + go func() { + if err := s.Shutdown(); err != nil { + t.Fatalf("Could not stop server: %s", err) + } + }() } } @@ -600,7 +680,7 @@ func ExampleDecorateWriter() { }) // simple UDP server - pc, err := net.ListenPacket("udp", "127.0.0.1:0") + pc, err := net.ListenPacket("udp", ":0") if err != nil { fmt.Println(err.Error()) return @@ -687,43 +767,3 @@ zDCJkckCgYEAndqM5KXGk5xYo+MAA1paZcbTUXwaWwjLU+XSRSSoyBEi5xMtfvUb kFsxKCqxAnBVGEWAvVZAiiTOxleQFjz5RnL0BQp9Lg2cQe+dvuUmIAA= -----END RSA PRIVATE KEY-----`) ) - -func testShutdownBindPort(t *testing.T, protocol string, port string) { - handler := NewServeMux() - handler.HandleFunc(".", func(w ResponseWriter, r *Msg) {}) - startedCh := make(chan struct{}) - s := &Server{ - Addr: net.JoinHostPort("127.0.0.1", port), - Net: protocol, - Handler: handler, - NotifyStartedFunc: func() { - startedCh <- struct{}{} - }, - } - go func() { - if err := s.ListenAndServe(); err != nil { - t.Log(err) - } - }() - <-startedCh - t.Logf("DNS server is started on: %s", s.Addr) - if err := s.Shutdown(); err != nil { - t.Fatal(err) - } - time.Sleep(100 * time.Millisecond) - go func() { - if err := s.ListenAndServe(); err != nil { - t.Fatal(err) - } - }() - <-startedCh - t.Logf("DNS server is started on: %s", s.Addr) -} - -func TestShutdownBindPortUDP(t *testing.T) { - testShutdownBindPort(t, "udp", "1153") -} - -func TestShutdownBindPortTCP(t *testing.T) { - testShutdownBindPort(t, "tcp", "1154") -} diff --git a/vendor/github.com/miekg/dns/types.go b/vendor/github.com/miekg/dns/types.go index 57f065bc8..deddb301b 100644 --- a/vendor/github.com/miekg/dns/types.go +++ b/vendor/github.com/miekg/dns/types.go @@ -164,14 +164,15 @@ const ( _Z = 1 << 6 // Z _AD = 1 << 5 // authticated data _CD = 1 << 4 // checking disabled +) +// Various constants used in the LOC RR, See RFC 1887. +const ( LOC_EQUATOR = 1 << 31 // RFC 1876, Section 2. LOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2. - - LOC_HOURS = 60 * 1000 - LOC_DEGREES = 60 * LOC_HOURS - - LOC_ALTITUDEBASE = 100000 + LOC_HOURS = 60 * 1000 + LOC_DEGREES = 60 * LOC_HOURS + LOC_ALTITUDEBASE = 100000 ) // Different Certificate Types, see RFC 4398, Section 2.1 @@ -237,6 +238,7 @@ type ANY struct { func (rr *ANY) String() string { return rr.Hdr.String() } +// CNAME RR. See RFC 1034. type CNAME struct { Hdr RR_Header Target string `dns:"cdomain-name"` @@ -244,6 +246,7 @@ type CNAME struct { func (rr *CNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) } +// HINFO RR. See RFC 1034. type HINFO struct { Hdr RR_Header Cpu string @@ -254,6 +257,7 @@ func (rr *HINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Cpu, rr.Os}) } +// MB RR. See RFC 1035. type MB struct { Hdr RR_Header Mb string `dns:"cdomain-name"` @@ -261,6 +265,7 @@ type MB struct { func (rr *MB) String() string { return rr.Hdr.String() + sprintName(rr.Mb) } +// MG RR. See RFC 1035. type MG struct { Hdr RR_Header Mg string `dns:"cdomain-name"` @@ -268,6 +273,7 @@ type MG struct { func (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) } +// MINFO RR. See RFC 1035. type MINFO struct { Hdr RR_Header Rmail string `dns:"cdomain-name"` @@ -278,6 +284,7 @@ func (rr *MINFO) String() string { return rr.Hdr.String() + sprintName(rr.Rmail) + " " + sprintName(rr.Email) } +// MR RR. See RFC 1035. type MR struct { Hdr RR_Header Mr string `dns:"cdomain-name"` @@ -287,6 +294,7 @@ func (rr *MR) String() string { return rr.Hdr.String() + sprintName(rr.Mr) } +// MF RR. See RFC 1035. type MF struct { Hdr RR_Header Mf string `dns:"cdomain-name"` @@ -296,6 +304,7 @@ func (rr *MF) String() string { return rr.Hdr.String() + sprintName(rr.Mf) } +// MD RR. See RFC 1035. type MD struct { Hdr RR_Header Md string `dns:"cdomain-name"` @@ -305,6 +314,7 @@ func (rr *MD) String() string { return rr.Hdr.String() + sprintName(rr.Md) } +// MX RR. See RFC 1035. type MX struct { Hdr RR_Header Preference uint16 @@ -315,6 +325,7 @@ func (rr *MX) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Mx) } +// AFSDB RR. See RFC 1183. type AFSDB struct { Hdr RR_Header Subtype uint16 @@ -325,6 +336,7 @@ func (rr *AFSDB) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Subtype)) + " " + sprintName(rr.Hostname) } +// X25 RR. See RFC 1183, Section 3.1. type X25 struct { Hdr RR_Header PSDNAddress string @@ -334,6 +346,7 @@ func (rr *X25) String() string { return rr.Hdr.String() + rr.PSDNAddress } +// RT RR. See RFC 1183, Section 3.3. type RT struct { Hdr RR_Header Preference uint16 @@ -344,6 +357,7 @@ func (rr *RT) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Host) } +// NS RR. See RFC 1035. type NS struct { Hdr RR_Header Ns string `dns:"cdomain-name"` @@ -353,6 +367,7 @@ func (rr *NS) String() string { return rr.Hdr.String() + sprintName(rr.Ns) } +// PTR RR. See RFC 1035. type PTR struct { Hdr RR_Header Ptr string `dns:"cdomain-name"` @@ -362,6 +377,7 @@ func (rr *PTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) } +// RP RR. See RFC 1138, Section 2.2. type RP struct { Hdr RR_Header Mbox string `dns:"domain-name"` @@ -372,6 +388,7 @@ func (rr *RP) String() string { return rr.Hdr.String() + rr.Mbox + " " + sprintTxt([]string{rr.Txt}) } +// SOA RR. See RFC 1035. type SOA struct { Hdr RR_Header Ns string `dns:"cdomain-name"` @@ -392,6 +409,7 @@ func (rr *SOA) String() string { " " + strconv.FormatInt(int64(rr.Minttl), 10) } +// TXT RR. See RFC 1035. type TXT struct { Hdr RR_Header Txt []string `dns:"txt"` @@ -524,6 +542,7 @@ func nextByte(b []byte, offset int) (byte, int) { return b[offset+1], 2 } +// SPF RR. See RFC 4408, Section 3.1.1. type SPF struct { Hdr RR_Header Txt []string `dns:"txt"` @@ -531,6 +550,7 @@ type SPF struct { func (rr *SPF) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) } +// AVC RR. See https://www.iana.org/assignments/dns-parameters/AVC/avc-completed-template. type AVC struct { Hdr RR_Header Txt []string `dns:"txt"` @@ -538,6 +558,7 @@ type AVC struct { func (rr *AVC) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) } +// SRV RR. See RFC 2782. type SRV struct { Hdr RR_Header Priority uint16 @@ -553,6 +574,7 @@ func (rr *SRV) String() string { strconv.Itoa(int(rr.Port)) + " " + sprintName(rr.Target) } +// NAPTR RR. See RFC 2915. type NAPTR struct { Hdr RR_Header Order uint16 @@ -573,7 +595,7 @@ func (rr *NAPTR) String() string { rr.Replacement } -// The CERT resource record, see RFC 4398. +// CERT RR. See RFC 4398. type CERT struct { Hdr RR_Header Type uint16 @@ -599,7 +621,7 @@ func (rr *CERT) String() string { " " + rr.Certificate } -// The DNAME resource record, see RFC 2672. +// DNAME RR. See RFC 2672. type DNAME struct { Hdr RR_Header Target string `dns:"domain-name"` @@ -609,6 +631,7 @@ func (rr *DNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) } +// A RR. See RFC 1035. type A struct { Hdr RR_Header A net.IP `dns:"a"` @@ -621,6 +644,7 @@ func (rr *A) String() string { return rr.Hdr.String() + rr.A.String() } +// AAAA RR. See RFC 3596. type AAAA struct { Hdr RR_Header AAAA net.IP `dns:"aaaa"` @@ -633,6 +657,7 @@ func (rr *AAAA) String() string { return rr.Hdr.String() + rr.AAAA.String() } +// PX RR. See RFC 2163. type PX struct { Hdr RR_Header Preference uint16 @@ -644,6 +669,7 @@ func (rr *PX) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Map822) + " " + sprintName(rr.Mapx400) } +// GPOS RR. See RFC 1712. type GPOS struct { Hdr RR_Header Longitude string @@ -655,6 +681,7 @@ func (rr *GPOS) String() string { return rr.Hdr.String() + rr.Longitude + " " + rr.Latitude + " " + rr.Altitude } +// LOC RR. See RFC RFC 1876. type LOC struct { Hdr RR_Header Version uint8 @@ -731,11 +758,12 @@ func (rr *LOC) String() string { return s } -// SIG is identical to RRSIG and nowadays only used for SIG(0), RFC2931. +// SIG RR. See RFC 2535. The SIG RR is identical to RRSIG and nowadays only used for SIG(0), See RFC 2931. type SIG struct { RRSIG } +// RRSIG RR. See RFC 4034 and RFC 3755. type RRSIG struct { Hdr RR_Header TypeCovered uint16 @@ -763,6 +791,7 @@ func (rr *RRSIG) String() string { return s } +// NSEC RR. See RFC 4034 and RFC 3755. type NSEC struct { Hdr RR_Header NextDomain string `dns:"domain-name"` @@ -790,14 +819,13 @@ func (rr *NSEC) len() int { return l } -type DLV struct { - DS -} +// DLV RR. See RFC 4431. +type DLV struct{ DS } -type CDS struct { - DS -} +// CDS RR. See RFC 7344. +type CDS struct{ DS } +// DS RR. See RFC 4034 and RFC 3658. type DS struct { Hdr RR_Header KeyTag uint16 @@ -813,6 +841,7 @@ func (rr *DS) String() string { " " + strings.ToUpper(rr.Digest) } +// KX RR. See RFC 2230. type KX struct { Hdr RR_Header Preference uint16 @@ -824,6 +853,7 @@ func (rr *KX) String() string { " " + sprintName(rr.Exchanger) } +// TA RR. See http://www.watson.org/~weiler/INI1999-19.pdf. type TA struct { Hdr RR_Header KeyTag uint16 @@ -839,6 +869,7 @@ func (rr *TA) String() string { " " + strings.ToUpper(rr.Digest) } +// TALINK RR. See https://www.iana.org/assignments/dns-parameters/TALINK/talink-completed-template. type TALINK struct { Hdr RR_Header PreviousName string `dns:"domain-name"` @@ -850,6 +881,7 @@ func (rr *TALINK) String() string { sprintName(rr.PreviousName) + " " + sprintName(rr.NextName) } +// SSHFP RR. See RFC RFC 4255. type SSHFP struct { Hdr RR_Header Algorithm uint8 @@ -863,14 +895,17 @@ func (rr *SSHFP) String() string { " " + strings.ToUpper(rr.FingerPrint) } +// KEY RR. See RFC RFC 2535. type KEY struct { DNSKEY } +// CDNSKEY RR. See RFC 7344. type CDNSKEY struct { DNSKEY } +// DNSKEY RR. See RFC 4034 and RFC 3755. type DNSKEY struct { Hdr RR_Header Flags uint16 @@ -886,6 +921,7 @@ func (rr *DNSKEY) String() string { " " + rr.PublicKey } +// RKEY RR. See https://www.iana.org/assignments/dns-parameters/RKEY/rkey-completed-template. type RKEY struct { Hdr RR_Header Flags uint16 @@ -901,6 +937,7 @@ func (rr *RKEY) String() string { " " + rr.PublicKey } +// NSAPPTR RR. See RFC 1348. type NSAPPTR struct { Hdr RR_Header Ptr string `dns:"domain-name"` @@ -908,6 +945,7 @@ type NSAPPTR struct { func (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) } +// NSEC3 RR. See RFC 5155. type NSEC3 struct { Hdr RR_Header Hash uint8 @@ -946,6 +984,7 @@ func (rr *NSEC3) len() int { return l } +// NSEC3PARAM RR. See RFC 5155. type NSEC3PARAM struct { Hdr RR_Header Hash uint8 @@ -964,6 +1003,7 @@ func (rr *NSEC3PARAM) String() string { return s } +// TKEY RR. See RFC 2930. type TKEY struct { Hdr RR_Header Algorithm string `dns:"domain-name"` @@ -982,7 +1022,7 @@ func (rr *TKEY) String() string { return "" } -// RFC3597 represents an unknown/generic RR. +// RFC3597 represents an unknown/generic RR. See RFC 3597. type RFC3597 struct { Hdr RR_Header Rdata string `dns:"hex"` @@ -1006,6 +1046,7 @@ func rfc3597Header(h RR_Header) string { return s } +// URI RR. See RFC 7553. type URI struct { Hdr RR_Header Priority uint16 @@ -1018,6 +1059,7 @@ func (rr *URI) String() string { " " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target) } +// DHCID RR. See RFC 4701. type DHCID struct { Hdr RR_Header Digest string `dns:"base64"` @@ -1025,6 +1067,7 @@ type DHCID struct { func (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest } +// TLSA RR. See RFC 6698. type TLSA struct { Hdr RR_Header Usage uint8 @@ -1041,6 +1084,7 @@ func (rr *TLSA) String() string { " " + rr.Certificate } +// SMIMEA RR. See RFC 8162. type SMIMEA struct { Hdr RR_Header Usage uint8 @@ -1063,6 +1107,7 @@ func (rr *SMIMEA) String() string { return s } +// HIP RR. See RFC 8005. type HIP struct { Hdr RR_Header HitLength uint8 @@ -1084,6 +1129,7 @@ func (rr *HIP) String() string { return s } +// NINFO RR. See https://www.iana.org/assignments/dns-parameters/NINFO/ninfo-completed-template. type NINFO struct { Hdr RR_Header ZSData []string `dns:"txt"` @@ -1091,6 +1137,7 @@ type NINFO struct { func (rr *NINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.ZSData) } +// NID RR. See RFC RFC 6742. type NID struct { Hdr RR_Header Preference uint16 @@ -1104,6 +1151,7 @@ func (rr *NID) String() string { return s } +// L32 RR, See RFC 6742. type L32 struct { Hdr RR_Header Preference uint16 @@ -1118,6 +1166,7 @@ func (rr *L32) String() string { " " + rr.Locator32.String() } +// L64 RR, See RFC 6742. type L64 struct { Hdr RR_Header Preference uint16 @@ -1131,6 +1180,7 @@ func (rr *L64) String() string { return s } +// LP RR. See RFC 6742. type LP struct { Hdr RR_Header Preference uint16 @@ -1141,6 +1191,7 @@ func (rr *LP) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Fqdn) } +// EUI48 RR. See RFC 7043. type EUI48 struct { Hdr RR_Header Address uint64 `dns:"uint48"` @@ -1148,6 +1199,7 @@ type EUI48 struct { func (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) } +// EUI64 RR. See RFC 7043. type EUI64 struct { Hdr RR_Header Address uint64 @@ -1155,6 +1207,7 @@ type EUI64 struct { func (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) } +// CAA RR. See RFC 6844. type CAA struct { Hdr RR_Header Flag uint8 @@ -1166,6 +1219,7 @@ func (rr *CAA) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value) } +// UID RR. Deprecated, IANA-Reserved. type UID struct { Hdr RR_Header Uid uint32 @@ -1173,6 +1227,7 @@ type UID struct { func (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) } +// GID RR. Deprecated, IANA-Reserved. type GID struct { Hdr RR_Header Gid uint32 @@ -1180,6 +1235,7 @@ type GID struct { func (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) } +// UINFO RR. Deprecated, IANA-Reserved. type UINFO struct { Hdr RR_Header Uinfo string @@ -1187,6 +1243,7 @@ type UINFO struct { func (rr *UINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Uinfo}) } +// EID RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt. type EID struct { Hdr RR_Header Endpoint string `dns:"hex"` @@ -1194,6 +1251,7 @@ type EID struct { func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) } +// NIMLOC RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt. type NIMLOC struct { Hdr RR_Header Locator string `dns:"hex"` @@ -1201,6 +1259,7 @@ type NIMLOC struct { func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) } +// OPENPGPKEY RR. See RFC 7929. type OPENPGPKEY struct { Hdr RR_Header PublicKey string `dns:"base64"` diff --git a/vendor/github.com/miekg/dns/types_generate.go b/vendor/github.com/miekg/dns/types_generate.go index dd1310942..3de021ca7 100644 --- a/vendor/github.com/miekg/dns/types_generate.go +++ b/vendor/github.com/miekg/dns/types_generate.go @@ -27,7 +27,7 @@ var skipLen = map[string]struct{}{ var packageHdr = ` // *** DO NOT MODIFY *** -// AUTOGENERATED BY go generate from type_generate.go +// AUTOGENERATED BY go generate from types_generate.go package dns @@ -56,7 +56,6 @@ var TypeToString = map[uint16]string{ `)) var headerFunc = template.Must(template.New("headerFunc").Parse(` -// Header() functions {{range .}} func (rr *{{.}}) Header() *RR_Header { return &rr.Hdr } {{end}} diff --git a/vendor/github.com/miekg/dns/types_test.go b/vendor/github.com/miekg/dns/types_test.go index c117cfbc7..3dbddee1e 100644 --- a/vendor/github.com/miekg/dns/types_test.go +++ b/vendor/github.com/miekg/dns/types_test.go @@ -44,7 +44,7 @@ func TestCmToM(t *testing.T) { func TestSplitN(t *testing.T) { xs := splitN("abc", 5) if len(xs) != 1 && xs[0] != "abc" { - t.Errorf("Failure to split abc") + t.Errorf("failure to split abc") } s := "" diff --git a/vendor/github.com/miekg/dns/update_test.go b/vendor/github.com/miekg/dns/update_test.go index 12760a1ee..6813baa10 100644 --- a/vendor/github.com/miekg/dns/update_test.go +++ b/vendor/github.com/miekg/dns/update_test.go @@ -13,11 +13,8 @@ func TestDynamicUpdateParsing(t *testing.T) { typ == "Reserved" || typ == "None" || typ == "NXT" || typ == "MAILB" || typ == "MAILA" { continue } - r, err := NewRR(prefix + typ) - if err != nil { + if _, err := NewRR(prefix + typ); err != nil { t.Errorf("failure to parse: %s %s: %v", prefix, typ, err) - } else { - t.Logf("parsed: %s", r.String()) } } } @@ -56,10 +53,7 @@ func TestDynamicUpdateZeroRdataUnpack(t *testing.T) { func TestRemoveRRset(t *testing.T) { // Should add a zero data RR in Class ANY with a TTL of 0 // for each set mentioned in the RRs provided to it. - rr, err := NewRR(". 100 IN A 127.0.0.1") - if err != nil { - t.Fatalf("error constructing RR: %v", err) - } + rr := testRR(". 100 IN A 127.0.0.1") m := new(Msg) m.Ns = []RR{&RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY, Ttl: 0, Rdlength: 0}} expectstr := m.String() @@ -92,15 +86,15 @@ func TestPreReqAndRemovals(t *testing.T) { m.Id = 1234 // Use a full set of RRs each time, so we are sure the rdata is stripped. - rrName1, _ := NewRR("name_used. 3600 IN A 127.0.0.1") - rrName2, _ := NewRR("name_not_used. 3600 IN A 127.0.0.1") - rrRemove1, _ := NewRR("remove1. 3600 IN A 127.0.0.1") - rrRemove2, _ := NewRR("remove2. 3600 IN A 127.0.0.1") - rrRemove3, _ := NewRR("remove3. 3600 IN A 127.0.0.1") - rrInsert, _ := NewRR("insert. 3600 IN A 127.0.0.1") - rrRrset1, _ := NewRR("rrset_used1. 3600 IN A 127.0.0.1") - rrRrset2, _ := NewRR("rrset_used2. 3600 IN A 127.0.0.1") - rrRrset3, _ := NewRR("rrset_not_used. 3600 IN A 127.0.0.1") + rrName1 := testRR("name_used. 3600 IN A 127.0.0.1") + rrName2 := testRR("name_not_used. 3600 IN A 127.0.0.1") + rrRemove1 := testRR("remove1. 3600 IN A 127.0.0.1") + rrRemove2 := testRR("remove2. 3600 IN A 127.0.0.1") + rrRemove3 := testRR("remove3. 3600 IN A 127.0.0.1") + rrInsert := testRR("insert. 3600 IN A 127.0.0.1") + rrRrset1 := testRR("rrset_used1. 3600 IN A 127.0.0.1") + rrRrset2 := testRR("rrset_used2. 3600 IN A 127.0.0.1") + rrRrset3 := testRR("rrset_not_used. 3600 IN A 127.0.0.1") // Handle the prereqs. m.NameUsed([]RR{rrName1}) diff --git a/vendor/github.com/miekg/dns/xfr.go b/vendor/github.com/miekg/dns/xfr.go index 576c5590a..2e892ea38 100644 --- a/vendor/github.com/miekg/dns/xfr.go +++ b/vendor/github.com/miekg/dns/xfr.go @@ -51,18 +51,18 @@ func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) { env = make(chan *Envelope) go func() { if q.Question[0].Qtype == TypeAXFR { - go t.inAxfr(q.Id, env) + go t.inAxfr(q, env) return } if q.Question[0].Qtype == TypeIXFR { - go t.inIxfr(q.Id, env) + go t.inIxfr(q, env) return } }() return env, nil } -func (t *Transfer) inAxfr(id uint16, c chan *Envelope) { +func (t *Transfer) inAxfr(q *Msg, c chan *Envelope) { first := true defer t.Close() defer close(c) @@ -77,7 +77,7 @@ func (t *Transfer) inAxfr(id uint16, c chan *Envelope) { c <- &Envelope{nil, err} return } - if id != in.Id { + if q.Id != in.Id { c <- &Envelope{in.Answer, ErrId} return } @@ -110,9 +110,11 @@ func (t *Transfer) inAxfr(id uint16, c chan *Envelope) { } } -func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { +func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) { serial := uint32(0) // The first serial seen is the current server serial - first := true + axfr := true + n := 0 + qser := q.Ns[0].(*SOA).Serial defer t.Close() defer close(c) timeout := dnsTimeout @@ -126,21 +128,15 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { c <- &Envelope{nil, err} return } - if id != in.Id { + if q.Id != in.Id { c <- &Envelope{in.Answer, ErrId} return } - if first { - if in.Rcode != RcodeSuccess { - c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}} - return - } - // A single SOA RR signals "no changes" - if len(in.Answer) == 1 && isSOAFirst(in) { - c <- &Envelope{in.Answer, nil} - return - } - + if in.Rcode != RcodeSuccess { + c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}} + return + } + if n == 0 { // Check if the returned answer is ok if !isSOAFirst(in) { c <- &Envelope{in.Answer, ErrSoa} @@ -148,21 +144,30 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { } // This serial is important serial = in.Answer[0].(*SOA).Serial - first = !first + // Check if there are no changes in zone + if qser >= serial { + c <- &Envelope{in.Answer, nil} + return + } } - // Now we need to check each message for SOA records, to see what we need to do - if !first { - t.tsigTimersOnly = true - // If the last record in the IXFR contains the servers' SOA, we should quit - if v, ok := in.Answer[len(in.Answer)-1].(*SOA); ok { + t.tsigTimersOnly = true + for _, rr := range in.Answer { + if v, ok := rr.(*SOA); ok { if v.Serial == serial { - c <- &Envelope{in.Answer, nil} - return + n++ + // quit if it's a full axfr or the the servers' SOA is repeated the third time + if axfr && n == 2 || n == 3 { + c <- &Envelope{in.Answer, nil} + return + } + } else if axfr { + // it's an ixfr + axfr = false } } - c <- &Envelope{in.Answer, nil} } + c <- &Envelope{in.Answer, nil} } } diff --git a/vendor/github.com/miekg/dns/xfr_test.go b/vendor/github.com/miekg/dns/xfr_test.go index a478963a3..e7cc6b2fa 100644 --- a/vendor/github.com/miekg/dns/xfr_test.go +++ b/vendor/github.com/miekg/dns/xfr_test.go @@ -39,7 +39,7 @@ func TestAXFR_Miek(t *testing.T) { break } for _, rr := range ex.RR { - t.Log(rr.String()) + // Nothing } } } @@ -90,7 +90,7 @@ func TestAXFR_Miek_Tsig(t *testing.T) { break } for _, rr := range ex.RR { - t.Log(rr.String()) + // Nothing } } } diff --git a/vendor/github.com/miekg/dns/zcompress.go b/vendor/github.com/miekg/dns/zcompress.go index b277978b9..ee9841ed5 100644 --- a/vendor/github.com/miekg/dns/zcompress.go +++ b/vendor/github.com/miekg/dns/zcompress.go @@ -5,83 +5,80 @@ package dns func compressionLenHelperType(c map[string]int, r RR) { switch x := r.(type) { - case *PTR: - compressionLenHelper(c, x.Ptr) - case *SOA: - compressionLenHelper(c, x.Ns) - compressionLenHelper(c, x.Mbox) case *AFSDB: compressionLenHelper(c, x.Hostname) + case *CNAME: + compressionLenHelper(c, x.Target) + case *DNAME: + compressionLenHelper(c, x.Target) case *HIP: for i := range x.RendezvousServers { compressionLenHelper(c, x.RendezvousServers[i]) } + case *KX: + compressionLenHelper(c, x.Exchanger) case *LP: compressionLenHelper(c, x.Fqdn) - case *CNAME: - compressionLenHelper(c, x.Target) case *MB: compressionLenHelper(c, x.Mb) - case *RP: - compressionLenHelper(c, x.Mbox) - compressionLenHelper(c, x.Txt) - case *RRSIG: - compressionLenHelper(c, x.SignerName) + case *MD: + compressionLenHelper(c, x.Md) case *MF: compressionLenHelper(c, x.Mf) + case *MG: + compressionLenHelper(c, x.Mg) case *MINFO: compressionLenHelper(c, x.Rmail) compressionLenHelper(c, x.Email) - case *SIG: - compressionLenHelper(c, x.SignerName) - case *SRV: - compressionLenHelper(c, x.Target) - case *TSIG: - compressionLenHelper(c, x.Algorithm) - case *KX: - compressionLenHelper(c, x.Exchanger) - case *MG: - compressionLenHelper(c, x.Mg) - case *NSAPPTR: - compressionLenHelper(c, x.Ptr) - case *PX: - compressionLenHelper(c, x.Map822) - compressionLenHelper(c, x.Mapx400) - case *DNAME: - compressionLenHelper(c, x.Target) case *MR: compressionLenHelper(c, x.Mr) case *MX: compressionLenHelper(c, x.Mx) - case *TKEY: - compressionLenHelper(c, x.Algorithm) - case *NSEC: - compressionLenHelper(c, x.NextDomain) - case *TALINK: - compressionLenHelper(c, x.PreviousName) - compressionLenHelper(c, x.NextName) - case *MD: - compressionLenHelper(c, x.Md) case *NAPTR: compressionLenHelper(c, x.Replacement) case *NS: compressionLenHelper(c, x.Ns) + case *NSAPPTR: + compressionLenHelper(c, x.Ptr) + case *NSEC: + compressionLenHelper(c, x.NextDomain) + case *PTR: + compressionLenHelper(c, x.Ptr) + case *PX: + compressionLenHelper(c, x.Map822) + compressionLenHelper(c, x.Mapx400) + case *RP: + compressionLenHelper(c, x.Mbox) + compressionLenHelper(c, x.Txt) + case *RRSIG: + compressionLenHelper(c, x.SignerName) case *RT: compressionLenHelper(c, x.Host) + case *SIG: + compressionLenHelper(c, x.SignerName) + case *SOA: + compressionLenHelper(c, x.Ns) + compressionLenHelper(c, x.Mbox) + case *SRV: + compressionLenHelper(c, x.Target) + case *TALINK: + compressionLenHelper(c, x.PreviousName) + compressionLenHelper(c, x.NextName) + case *TKEY: + compressionLenHelper(c, x.Algorithm) + case *TSIG: + compressionLenHelper(c, x.Algorithm) } } func compressionLenSearchType(c map[string]int, r RR) (int, bool) { switch x := r.(type) { - case *MG: - k1, ok1 := compressionLenSearch(c, x.Mg) - return k1, ok1 - case *PTR: - k1, ok1 := compressionLenSearch(c, x.Ptr) - return k1, ok1 case *AFSDB: k1, ok1 := compressionLenSearch(c, x.Hostname) return k1, ok1 + case *CNAME: + k1, ok1 := compressionLenSearch(c, x.Target) + return k1, ok1 case *MB: k1, ok1 := compressionLenSearch(c, x.Mb) return k1, ok1 @@ -91,18 +88,8 @@ func compressionLenSearchType(c map[string]int, r RR) (int, bool) { case *MF: k1, ok1 := compressionLenSearch(c, x.Mf) return k1, ok1 - case *NS: - k1, ok1 := compressionLenSearch(c, x.Ns) - return k1, ok1 - case *RT: - k1, ok1 := compressionLenSearch(c, x.Host) - return k1, ok1 - case *SOA: - k1, ok1 := compressionLenSearch(c, x.Ns) - k2, ok2 := compressionLenSearch(c, x.Mbox) - return k1 + k2, ok1 && ok2 - case *CNAME: - k1, ok1 := compressionLenSearch(c, x.Target) + case *MG: + k1, ok1 := compressionLenSearch(c, x.Mg) return k1, ok1 case *MINFO: k1, ok1 := compressionLenSearch(c, x.Rmail) @@ -114,6 +101,19 @@ func compressionLenSearchType(c map[string]int, r RR) (int, bool) { case *MX: k1, ok1 := compressionLenSearch(c, x.Mx) return k1, ok1 + case *NS: + k1, ok1 := compressionLenSearch(c, x.Ns) + return k1, ok1 + case *PTR: + k1, ok1 := compressionLenSearch(c, x.Ptr) + return k1, ok1 + case *RT: + k1, ok1 := compressionLenSearch(c, x.Host) + return k1, ok1 + case *SOA: + k1, ok1 := compressionLenSearch(c, x.Ns) + k2, ok2 := compressionLenSearch(c, x.Mbox) + return k1 + k2, ok1 && ok2 } return 0, false } diff --git a/vendor/github.com/miekg/dns/ztypes.go b/vendor/github.com/miekg/dns/ztypes.go index 3e534f12e..0613ba09d 100644 --- a/vendor/github.com/miekg/dns/ztypes.go +++ b/vendor/github.com/miekg/dns/ztypes.go @@ -1,5 +1,5 @@ // *** DO NOT MODIFY *** -// AUTOGENERATED BY go generate from type_generate.go +// AUTOGENERATED BY go generate from types_generate.go package dns @@ -163,7 +163,6 @@ var TypeToString = map[uint16]string{ TypeNSAPPTR: "NSAP-PTR", } -// Header() functions func (rr *A) Header() *RR_Header { return &rr.Hdr } func (rr *AAAA) Header() *RR_Header { return &rr.Hdr } func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr } diff --git a/vendor/github.com/mitchellh/mapstructure/mapstructure.go b/vendor/github.com/mitchellh/mapstructure/mapstructure.go index 6ec5c3335..30a9957c6 100644 --- a/vendor/github.com/mitchellh/mapstructure/mapstructure.go +++ b/vendor/github.com/mitchellh/mapstructure/mapstructure.go @@ -686,7 +686,11 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) // Compile the list of all the fields that we're going to be decoding // from all the structs. - fields := make(map[*reflect.StructField]reflect.Value) + type field struct { + field reflect.StructField + val reflect.Value + } + fields := []field{} for len(structs) > 0 { structVal := structs[0] structs = structs[1:] @@ -718,14 +722,16 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) } // Normal struct field, store it away - fields[&fieldType] = structVal.Field(i) + fields = append(fields, field{fieldType, structVal.Field(i)}) } } - for fieldType, field := range fields { - fieldName := fieldType.Name + // for fieldType, field := range fields { + for _, f := range fields { + field, fieldValue := f.field, f.val + fieldName := field.Name - tagValue := fieldType.Tag.Get(d.config.TagName) + tagValue := field.Tag.Get(d.config.TagName) tagValue = strings.SplitN(tagValue, ",", 2)[0] if tagValue != "" { fieldName = tagValue @@ -760,14 +766,14 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) // Delete the key we're using from the unused map so we stop tracking delete(dataValKeysUnused, rawMapKey.Interface()) - if !field.IsValid() { + if !fieldValue.IsValid() { // This should never happen panic("field is not valid") } // If we can't set the field, then it is unexported or something, // and we just continue onwards. - if !field.CanSet() { + if !fieldValue.CanSet() { continue } @@ -777,7 +783,7 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) fieldName = fmt.Sprintf("%s.%s", name, fieldName) } - if err := d.decode(fieldName, rawMapVal.Interface(), field); err != nil { + if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil { errors = appendErrors(errors, err) } } diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go index 9a155efc5..b045a2751 100644 --- a/vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go @@ -45,7 +45,7 @@ func Parse(src string) []*Language { switch chr { case ',', ';', '.': tag := strings.TrimSpace(src[start:end]) - if spec := getPluralSpec(tag); spec != nil { + if spec := GetPluralSpec(tag); spec != nil { langs = append(langs, &Language{NormalizeTag(tag), spec}) } start = end + 1 @@ -53,12 +53,12 @@ func Parse(src string) []*Language { } if start > 0 { tag := strings.TrimSpace(src[start:]) - if spec := getPluralSpec(tag); spec != nil { + if spec := GetPluralSpec(tag); spec != nil { langs = append(langs, &Language{NormalizeTag(tag), spec}) } return dedupe(langs) } - if spec := getPluralSpec(src); spec != nil { + if spec := GetPluralSpec(src); spec != nil { langs = append(langs, &Language{NormalizeTag(src), spec}) } return langs diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go index 85b2a1b37..fc31e8807 100644 --- a/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go @@ -36,9 +36,9 @@ func (ps *PluralSpec) Plural(number interface{}) (Plural, error) { return ps.PluralFunc(ops), nil } -// getPluralSpec returns the PluralSpec that matches the longest prefix of tag. +// GetPluralSpec returns the PluralSpec that matches the longest prefix of tag. // It returns nil if no PluralSpec matches tag. -func getPluralSpec(tag string) *PluralSpec { +func GetPluralSpec(tag string) *PluralSpec { tag = NormalizeTag(tag) subtag := tag for { diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_test.go index 919b489bd..51c1685ad 100644 --- a/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_test.go +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_test.go @@ -68,7 +68,7 @@ func TestGetPluralSpec(t *testing.T) { {"xx", nil}, } for _, test := range tests { - spec := getPluralSpec(test.src) + spec := GetPluralSpec(test.src) if spec != test.spec { t.Errorf("getPluralSpec(%q) = %q expected %q", test.src, spec, test.spec) } diff --git a/vendor/github.com/pelletier/go-toml/.gitignore b/vendor/github.com/pelletier/go-toml/.gitignore index f1b619018..99e38bbc5 100644 --- a/vendor/github.com/pelletier/go-toml/.gitignore +++ b/vendor/github.com/pelletier/go-toml/.gitignore @@ -1 +1,2 @@ test_program/test_program_bin +fuzz/ diff --git a/vendor/github.com/pelletier/go-toml/.travis.yml b/vendor/github.com/pelletier/go-toml/.travis.yml index 496691166..6e644fdfd 100644 --- a/vendor/github.com/pelletier/go-toml/.travis.yml +++ b/vendor/github.com/pelletier/go-toml/.travis.yml @@ -1,9 +1,8 @@ sudo: false language: go go: - - 1.7.6 - - 1.8.3 - - 1.9 + - 1.8.4 + - 1.9.1 - tip matrix: allow_failures: diff --git a/vendor/github.com/pelletier/go-toml/README.md b/vendor/github.com/pelletier/go-toml/README.md index 2681690d5..0d357acf3 100644 --- a/vendor/github.com/pelletier/go-toml/README.md +++ b/vendor/github.com/pelletier/go-toml/README.md @@ -57,9 +57,9 @@ type Config struct { } doc := []byte(` -[postgres] -user = "pelletier" -password = "mypassword"`) +[Postgres] +User = "pelletier" +Password = "mypassword"`) config := Config{} toml.Unmarshal(doc, &config) @@ -114,6 +114,18 @@ You have to make sure two kind of tests run: You can run both of them using `./test.sh`. +### Fuzzing + +The script `./fuzz.sh` is available to +run [go-fuzz](https://github.com/dvyukov/go-fuzz) on go-toml. + +## Versioning + +Go-toml follows [Semantic Versioning](http://semver.org/). The supported version +of [TOML](https://github.com/toml-lang/toml) is indicated at the beginning of +this document. The last two major versions of Go are supported +(see [Go Release Policy](https://golang.org/doc/devel/release.html#policy)). + ## License The MIT License (MIT). Read [LICENSE](LICENSE). diff --git a/vendor/github.com/pelletier/go-toml/doc.go b/vendor/github.com/pelletier/go-toml/doc.go index 3c89619e8..d5fd98c02 100644 --- a/vendor/github.com/pelletier/go-toml/doc.go +++ b/vendor/github.com/pelletier/go-toml/doc.go @@ -17,7 +17,7 @@ // JSONPath-like queries // // The package github.com/pelletier/go-toml/query implements a system -// similar to JSONPath to quickly retrive elements of a TOML document using a +// similar to JSONPath to quickly retrieve elements of a TOML document using a // single expression. See the package documentation for more information. // package toml diff --git a/vendor/github.com/pelletier/go-toml/doc_test.go b/vendor/github.com/pelletier/go-toml/doc_test.go index a48c04b01..3b8171b22 100644 --- a/vendor/github.com/pelletier/go-toml/doc_test.go +++ b/vendor/github.com/pelletier/go-toml/doc_test.go @@ -61,19 +61,24 @@ func ExampleMarshal() { type Postgres struct { User string `toml:"user"` Password string `toml:"password"` + Database string `toml:"db" commented:"true" comment:"not used anymore"` } type Config struct { - Postgres Postgres `toml:"postgres"` + Postgres Postgres `toml:"postgres" comment:"Postgres configuration"` } - config := Config{Postgres{User: "pelletier", Password: "mypassword"}} + config := Config{Postgres{User: "pelletier", Password: "mypassword", Database: "old_database"}} b, err := toml.Marshal(config) if err != nil { log.Fatal(err) } fmt.Println(string(b)) // Output: + // # Postgres configuration // [postgres] + // + // # not used anymore + // # db = "old_database" // password = "mypassword" // user = "pelletier" } diff --git a/vendor/github.com/pelletier/go-toml/fuzz.go b/vendor/github.com/pelletier/go-toml/fuzz.go new file mode 100644 index 000000000..14570c8d3 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/fuzz.go @@ -0,0 +1,31 @@ +// +build gofuzz + +package toml + +func Fuzz(data []byte) int { + tree, err := LoadBytes(data) + if err != nil { + if tree != nil { + panic("tree must be nil if there is an error") + } + return 0 + } + + str, err := tree.ToTomlString() + if err != nil { + if str != "" { + panic(`str must be "" if there is an error`) + } + panic(err) + } + + tree, err = Load(str) + if err != nil { + if tree != nil { + panic("tree must be nil if there is an error") + } + return 0 + } + + return 1 +} diff --git a/vendor/github.com/pelletier/go-toml/fuzz.sh b/vendor/github.com/pelletier/go-toml/fuzz.sh new file mode 100755 index 000000000..3204b4c44 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/fuzz.sh @@ -0,0 +1,15 @@ +#! /bin/sh +set -eu + +go get github.com/dvyukov/go-fuzz/go-fuzz +go get github.com/dvyukov/go-fuzz/go-fuzz-build + +if [ ! -e toml-fuzz.zip ]; then + go-fuzz-build github.com/pelletier/go-toml +fi + +rm -fr fuzz +mkdir -p fuzz/corpus +cp *.toml fuzz/corpus + +go-fuzz -bin=toml-fuzz.zip -workdir=fuzz diff --git a/vendor/github.com/pelletier/go-toml/keysparsing.go b/vendor/github.com/pelletier/go-toml/keysparsing.go index d62ca5fd1..9707c6884 100644 --- a/vendor/github.com/pelletier/go-toml/keysparsing.go +++ b/vendor/github.com/pelletier/go-toml/keysparsing.go @@ -6,15 +6,37 @@ import ( "bytes" "errors" "fmt" + "strconv" "unicode" ) +var escapeSequenceMap = map[rune]rune{ + 'b': '\b', + 't': '\t', + 'n': '\n', + 'f': '\f', + 'r': '\r', + '"': '"', + '\\': '\\', +} + +type parseKeyState int + +const ( + BARE parseKeyState = iota + BASIC + LITERAL + ESC + UNICODE_4 + UNICODE_8 +) + func parseKey(key string) ([]string, error) { groups := []string{} var buffer bytes.Buffer - inQuotes := false + var hex bytes.Buffer + state := BARE wasInQuotes := false - escapeNext := false ignoreSpace := true expectDot := false @@ -25,25 +47,67 @@ func parseKey(key string) ([]string, error) { } ignoreSpace = false } - if escapeNext { - buffer.WriteRune(char) - escapeNext = false + + if state == ESC { + if char == 'u' { + state = UNICODE_4 + hex.Reset() + } else if char == 'U' { + state = UNICODE_8 + hex.Reset() + } else if newChar, ok := escapeSequenceMap[char]; ok { + buffer.WriteRune(newChar) + state = BASIC + } else { + return nil, fmt.Errorf(`invalid escape sequence \%c`, char) + } + continue + } + + if state == UNICODE_4 || state == UNICODE_8 { + if isHexDigit(char) { + hex.WriteRune(char) + } + if (state == UNICODE_4 && hex.Len() == 4) || (state == UNICODE_8 && hex.Len() == 8) { + if value, err := strconv.ParseInt(hex.String(), 16, 32); err == nil { + buffer.WriteRune(rune(value)) + } else { + return nil, err + } + state = BASIC + } continue } + switch char { case '\\': - escapeNext = true - continue + if state == BASIC { + state = ESC + } else if state == LITERAL { + buffer.WriteRune(char) + } + case '\'': + if state == BARE { + state = LITERAL + } else if state == LITERAL { + groups = append(groups, buffer.String()) + buffer.Reset() + wasInQuotes = true + state = BARE + } + expectDot = false case '"': - if inQuotes { + if state == BARE { + state = BASIC + } else if state == BASIC { groups = append(groups, buffer.String()) buffer.Reset() + state = BARE wasInQuotes = true } - inQuotes = !inQuotes expectDot = false case '.': - if inQuotes { + if state != BARE { buffer.WriteRune(char) } else { if !wasInQuotes { @@ -58,28 +122,31 @@ func parseKey(key string) ([]string, error) { wasInQuotes = false } case ' ': - if inQuotes { + if state == BASIC { buffer.WriteRune(char) } else { expectDot = true } default: - if !inQuotes && !isValidBareChar(char) { - return nil, fmt.Errorf("invalid bare character: %c", char) - } - if !inQuotes && expectDot { - return nil, errors.New("what?") + if state == BARE { + if !isValidBareChar(char) { + return nil, fmt.Errorf("invalid bare character: %c", char) + } else if expectDot { + return nil, errors.New("what?") + } } buffer.WriteRune(char) expectDot = false } } - if inQuotes { - return nil, errors.New("mismatched quotes") - } - if escapeNext { + + // state must be BARE at the end + if state == ESC { return nil, errors.New("unfinished escape sequence") + } else if state != BARE { + return nil, errors.New("mismatched quotes") } + if buffer.Len() > 0 { groups = append(groups, buffer.String()) } diff --git a/vendor/github.com/pelletier/go-toml/keysparsing_test.go b/vendor/github.com/pelletier/go-toml/keysparsing_test.go index 1a9ecccaa..7aa4cd64a 100644 --- a/vendor/github.com/pelletier/go-toml/keysparsing_test.go +++ b/vendor/github.com/pelletier/go-toml/keysparsing_test.go @@ -22,7 +22,10 @@ func testResult(t *testing.T, key string, expected []string) { } func testError(t *testing.T, key string, expectedError string) { - _, err := parseKey(key) + res, err := parseKey(key) + if err == nil { + t.Fatalf("Expected error, but succesfully parsed key %s", res) + } if fmt.Sprintf("%s", err) != expectedError { t.Fatalf("Expected error \"%s\", but got \"%s\".", expectedError, err) } @@ -47,6 +50,17 @@ func TestBaseKeyPound(t *testing.T) { func TestQuotedKeys(t *testing.T) { testResult(t, `hello."foo".bar`, []string{"hello", "foo", "bar"}) testResult(t, `"hello!"`, []string{"hello!"}) + testResult(t, `"hello\tworld"`, []string{"hello\tworld"}) + testResult(t, `"\U0001F914"`, []string{"\U0001F914"}) + testResult(t, `"\u2764"`, []string{"\u2764"}) + + testResult(t, `hello.'foo'.bar`, []string{"hello", "foo", "bar"}) + testResult(t, `'hello!'`, []string{"hello!"}) + testResult(t, `'hello\tworld'`, []string{`hello\tworld`}) + + testError(t, `"\w"`, `invalid escape sequence \w`) + testError(t, `"\`, `unfinished escape sequence`) + testError(t, `"\t`, `mismatched quotes`) } func TestEmptyKey(t *testing.T) { diff --git a/vendor/github.com/pelletier/go-toml/marshal.go b/vendor/github.com/pelletier/go-toml/marshal.go index 1a3176f97..1bbdfa1d8 100644 --- a/vendor/github.com/pelletier/go-toml/marshal.go +++ b/vendor/github.com/pelletier/go-toml/marshal.go @@ -4,17 +4,29 @@ import ( "bytes" "errors" "fmt" + "io" "reflect" + "strconv" "strings" "time" ) type tomlOpts struct { name string + comment string + commented bool include bool omitempty bool } +type encOpts struct { + quoteMapKeys bool +} + +var encOptsDefaults = encOpts{ + quoteMapKeys: false, +} + var timeType = reflect.TypeOf(time.Time{}) var marshalerType = reflect.TypeOf(new(Marshaler)).Elem() @@ -94,8 +106,15 @@ encoder, except that there is no concept of a Marshaler interface or MarshalTOML function for sub-structs, and currently only definite types can be marshaled (i.e. no `interface{}`). +The following struct annotations are supported: + + toml:"Field" Overrides the field's name to output. + omitempty When set, empty values and groups are not emitted. + comment:"comment" Emits a # comment on the same line. This supports new lines. + commented:"true" Emits the value as commented. + Note that pointers are automatically assigned the "omitempty" option, as TOML -explicity does not handle null values (saying instead the label should be +explicitly does not handle null values (saying instead the label should be dropped). Tree structural types and corresponding marshal types: @@ -115,6 +134,47 @@ Tree primitive types and corresponding marshal types: time.Time time.Time{}, pointers to same */ func Marshal(v interface{}) ([]byte, error) { + return NewEncoder(nil).marshal(v) +} + +// Encoder writes TOML values to an output stream. +type Encoder struct { + w io.Writer + encOpts +} + +// NewEncoder returns a new encoder that writes to w. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + w: w, + encOpts: encOptsDefaults, + } +} + +// Encode writes the TOML encoding of v to the stream. +// +// See the documentation for Marshal for details. +func (e *Encoder) Encode(v interface{}) error { + b, err := e.marshal(v) + if err != nil { + return err + } + if _, err := e.w.Write(b); err != nil { + return err + } + return nil +} + +// QuoteMapKeys sets up the encoder to encode +// maps with string type keys with quoted TOML keys. +// +// This relieves the character limitations on map keys. +func (e *Encoder) QuoteMapKeys(v bool) *Encoder { + e.quoteMapKeys = v + return e +} + +func (e *Encoder) marshal(v interface{}) ([]byte, error) { mtype := reflect.TypeOf(v) if mtype.Kind() != reflect.Struct { return []byte{}, errors.New("Only a struct can be marshaled to TOML") @@ -123,7 +183,7 @@ func Marshal(v interface{}) ([]byte, error) { if isCustomMarshaler(mtype) { return callCustomMarshaler(sval) } - t, err := valueToTree(mtype, sval) + t, err := e.valueToTree(mtype, sval) if err != nil { return []byte{}, err } @@ -132,9 +192,9 @@ func Marshal(v interface{}) ([]byte, error) { } // Convert given marshal struct or map value to toml tree -func valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) { +func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) { if mtype.Kind() == reflect.Ptr { - return valueToTree(mtype.Elem(), mval.Elem()) + return e.valueToTree(mtype.Elem(), mval.Elem()) } tval := newTree() switch mtype.Kind() { @@ -143,31 +203,39 @@ func valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) { mtypef, mvalf := mtype.Field(i), mval.Field(i) opts := tomlOptions(mtypef) if opts.include && (!opts.omitempty || !isZero(mvalf)) { - val, err := valueToToml(mtypef.Type, mvalf) + val, err := e.valueToToml(mtypef.Type, mvalf) if err != nil { return nil, err } - tval.Set(opts.name, val) + tval.Set(opts.name, opts.comment, opts.commented, val) } } case reflect.Map: for _, key := range mval.MapKeys() { mvalf := mval.MapIndex(key) - val, err := valueToToml(mtype.Elem(), mvalf) + val, err := e.valueToToml(mtype.Elem(), mvalf) if err != nil { return nil, err } - tval.Set(key.String(), val) + if e.quoteMapKeys { + keyStr, err := tomlValueStringRepresentation(key.String()) + if err != nil { + return nil, err + } + tval.SetPath([]string{keyStr}, "", false, val) + } else { + tval.Set(key.String(), "", false, val) + } } } return tval, nil } // Convert given marshal slice to slice of Toml trees -func valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*Tree, error) { +func (e *Encoder) valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*Tree, error) { tval := make([]*Tree, mval.Len(), mval.Len()) for i := 0; i < mval.Len(); i++ { - val, err := valueToTree(mtype.Elem(), mval.Index(i)) + val, err := e.valueToTree(mtype.Elem(), mval.Index(i)) if err != nil { return nil, err } @@ -177,10 +245,10 @@ func valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*Tree, error) { } // Convert given marshal slice to slice of toml values -func valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) { +func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) { tval := make([]interface{}, mval.Len(), mval.Len()) for i := 0; i < mval.Len(); i++ { - val, err := valueToToml(mtype.Elem(), mval.Index(i)) + val, err := e.valueToToml(mtype.Elem(), mval.Index(i)) if err != nil { return nil, err } @@ -190,19 +258,19 @@ func valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, err } // Convert given marshal value to toml value -func valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) { +func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) { if mtype.Kind() == reflect.Ptr { - return valueToToml(mtype.Elem(), mval.Elem()) + return e.valueToToml(mtype.Elem(), mval.Elem()) } switch { case isCustomMarshaler(mtype): return callCustomMarshaler(mval) case isTree(mtype): - return valueToTree(mtype, mval) + return e.valueToTree(mtype, mval) case isTreeSlice(mtype): - return valueToTreeSlice(mtype, mval) + return e.valueToTreeSlice(mtype, mval) case isOtherSlice(mtype): - return valueToOtherSlice(mtype, mval) + return e.valueToOtherSlice(mtype, mval) default: switch mtype.Kind() { case reflect.Bool: @@ -227,17 +295,16 @@ func valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) { // Neither Unmarshaler interfaces nor UnmarshalTOML functions are supported for // sub-structs, and only definite types can be unmarshaled. func (t *Tree) Unmarshal(v interface{}) error { - mtype := reflect.TypeOf(v) - if mtype.Kind() != reflect.Ptr || mtype.Elem().Kind() != reflect.Struct { - return errors.New("Only a pointer to struct can be unmarshaled from TOML") - } + d := Decoder{tval: t} + return d.unmarshal(v) +} - sval, err := valueFromTree(mtype.Elem(), t) - if err != nil { - return err - } - reflect.ValueOf(v).Elem().Set(sval) - return nil +// Marshal returns the TOML encoding of Tree. +// See Marshal() documentation for types mapping table. +func (t *Tree) Marshal() ([]byte, error) { + var buf bytes.Buffer + err := NewEncoder(&buf).Encode(t) + return buf.Bytes(), err } // Unmarshal parses the TOML-encoded data and stores the result in the value @@ -246,6 +313,10 @@ func (t *Tree) Unmarshal(v interface{}) error { // sub-structs, and currently only definite types can be unmarshaled to (i.e. no // `interface{}`). // +// The following struct annotations are supported: +// +// toml:"Field" Overrides the field's name to map to. +// // See Marshal() documentation for types mapping table. func Unmarshal(data []byte, v interface{}) error { t, err := LoadReader(bytes.NewReader(data)) @@ -255,10 +326,52 @@ func Unmarshal(data []byte, v interface{}) error { return t.Unmarshal(v) } +// Decoder reads and decodes TOML values from an input stream. +type Decoder struct { + r io.Reader + tval *Tree + encOpts +} + +// NewDecoder returns a new decoder that reads from r. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{ + r: r, + encOpts: encOptsDefaults, + } +} + +// Decode reads a TOML-encoded value from it's input +// and unmarshals it in the value pointed at by v. +// +// See the documentation for Marshal for details. +func (d *Decoder) Decode(v interface{}) error { + var err error + d.tval, err = LoadReader(d.r) + if err != nil { + return err + } + return d.unmarshal(v) +} + +func (d *Decoder) unmarshal(v interface{}) error { + mtype := reflect.TypeOf(v) + if mtype.Kind() != reflect.Ptr || mtype.Elem().Kind() != reflect.Struct { + return errors.New("Only a pointer to struct can be unmarshaled from TOML") + } + + sval, err := d.valueFromTree(mtype.Elem(), d.tval) + if err != nil { + return err + } + reflect.ValueOf(v).Elem().Set(sval) + return nil +} + // Convert toml tree to marshal struct or map, using marshal type -func valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) { +func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) { if mtype.Kind() == reflect.Ptr { - return unwrapPointer(mtype, tval) + return d.unwrapPointer(mtype, tval) } var mval reflect.Value switch mtype.Kind() { @@ -276,7 +389,7 @@ func valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) { continue } val := tval.Get(key) - mvalf, err := valueFromToml(mtypef.Type, val) + mvalf, err := d.valueFromToml(mtypef.Type, val) if err != nil { return mval, formatError(err, tval.GetPosition(key)) } @@ -288,8 +401,9 @@ func valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) { case reflect.Map: mval = reflect.MakeMap(mtype) for _, key := range tval.Keys() { - val := tval.Get(key) - mvalf, err := valueFromToml(mtype.Elem(), val) + // TODO: path splits key + val := tval.GetPath([]string{key}) + mvalf, err := d.valueFromToml(mtype.Elem(), val) if err != nil { return mval, formatError(err, tval.GetPosition(key)) } @@ -300,10 +414,10 @@ func valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) { } // Convert toml value to marshal struct/map slice, using marshal type -func valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) { +func (d *Decoder) valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) { mval := reflect.MakeSlice(mtype, len(tval), len(tval)) for i := 0; i < len(tval); i++ { - val, err := valueFromTree(mtype.Elem(), tval[i]) + val, err := d.valueFromTree(mtype.Elem(), tval[i]) if err != nil { return mval, err } @@ -313,10 +427,10 @@ func valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) } // Convert toml value to marshal primitive slice, using marshal type -func valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) { +func (d *Decoder) valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) { mval := reflect.MakeSlice(mtype, len(tval), len(tval)) for i := 0; i < len(tval); i++ { - val, err := valueFromToml(mtype.Elem(), tval[i]) + val, err := d.valueFromToml(mtype.Elem(), tval[i]) if err != nil { return mval, err } @@ -326,17 +440,30 @@ func valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, } // Convert toml value to marshal value, using marshal type -func valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) { +func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) { if mtype.Kind() == reflect.Ptr { - return unwrapPointer(mtype, tval) + return d.unwrapPointer(mtype, tval) } - switch { - case isTree(mtype): - return valueFromTree(mtype, tval.(*Tree)) - case isTreeSlice(mtype): - return valueFromTreeSlice(mtype, tval.([]*Tree)) - case isOtherSlice(mtype): - return valueFromOtherSlice(mtype, tval.([]interface{})) + + switch tval.(type) { + case *Tree: + if isTree(mtype) { + return d.valueFromTree(mtype, tval.(*Tree)) + } else { + return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval) + } + case []*Tree: + if isTreeSlice(mtype) { + return d.valueFromTreeSlice(mtype, tval.([]*Tree)) + } else { + return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval) + } + case []interface{}: + if isOtherSlice(mtype) { + return d.valueFromOtherSlice(mtype, tval.([]interface{})) + } else { + return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a slice", tval, tval) + } default: switch mtype.Kind() { case reflect.Bool: @@ -430,13 +557,13 @@ func valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) } return reflect.ValueOf(val), nil default: - return reflect.ValueOf(nil), fmt.Errorf("Unmarshal can't handle %v(%v)", mtype, mtype.Kind()) + return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v(%v)", tval, tval, mtype, mtype.Kind()) } } } -func unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) { - val, err := valueFromToml(mtype.Elem(), tval) +func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) { + val, err := d.valueFromToml(mtype.Elem(), tval) if err != nil { return reflect.ValueOf(nil), err } @@ -448,7 +575,12 @@ func unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) func tomlOptions(vf reflect.StructField) tomlOpts { tag := vf.Tag.Get("toml") parse := strings.Split(tag, ",") - result := tomlOpts{vf.Name, true, false} + var comment string + if c := vf.Tag.Get("comment"); c != "" { + comment = c + } + commented, _ := strconv.ParseBool(vf.Tag.Get("commented")) + result := tomlOpts{name: vf.Name, comment: comment, commented: commented, include: true, omitempty: false} if parse[0] != "" { if parse[0] == "-" && len(parse) == 1 { result.include = false diff --git a/vendor/github.com/pelletier/go-toml/marshal_test.go b/vendor/github.com/pelletier/go-toml/marshal_test.go index dbfc7c1d1..e7d1d6e4d 100644 --- a/vendor/github.com/pelletier/go-toml/marshal_test.go +++ b/vendor/github.com/pelletier/go-toml/marshal_test.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "reflect" + "strings" "testing" "time" ) @@ -508,6 +509,14 @@ func TestPointerUnmarshal(t *testing.T) { } } +func TestUnmarshalTypeMismatch(t *testing.T) { + result := pointerMarshalTestStruct{} + err := Unmarshal([]byte("List = 123"), &result) + if !strings.HasPrefix(err.Error(), "(1, 1): Can't convert 123(int64) to []string(slice)") { + t.Errorf("Type mismatch must be reported: got %v", err.Error()) + } +} + type nestedMarshalTestStruct struct { String [][]string //Struct [][]basicMarshalTestSubStruct @@ -598,3 +607,128 @@ func TestNestedCustomMarshaler(t *testing.T) { t.Errorf("Bad nested custom marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } + +var commentTestToml = []byte(` +# it's a comment on type +[postgres] + # isCommented = "dvalue" + noComment = "cvalue" + + # A comment on AttrB with a + # break line + password = "bvalue" + + # A comment on AttrA + user = "avalue" + + [[postgres.My]] + + # a comment on my on typeC + My = "Foo" + + [[postgres.My]] + + # a comment on my on typeC + My = "Baar" +`) + +func TestMarshalComment(t *testing.T) { + type TypeC struct { + My string `comment:"a comment on my on typeC"` + } + type TypeB struct { + AttrA string `toml:"user" comment:"A comment on AttrA"` + AttrB string `toml:"password" comment:"A comment on AttrB with a\n break line"` + AttrC string `toml:"noComment"` + AttrD string `toml:"isCommented" commented:"true"` + My []TypeC + } + type TypeA struct { + TypeB TypeB `toml:"postgres" comment:"it's a comment on type"` + } + + ta := []TypeC{{My: "Foo"}, {My: "Baar"}} + config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", AttrC: "cvalue", AttrD: "dvalue", My: ta}} + result, err := Marshal(config) + if err != nil { + t.Fatal(err) + } + expected := commentTestToml + if !bytes.Equal(result, expected) { + t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) + } +} + +type mapsTestStruct struct { + Simple map[string]string + Paths map[string]string + Other map[string]float64 + X struct { + Y struct { + Z map[string]bool + } + } +} + +var mapsTestData = mapsTestStruct{ + Simple: map[string]string{ + "one plus one": "two", + "next": "three", + }, + Paths: map[string]string{ + "/this/is/a/path": "/this/is/also/a/path", + "/heloo.txt": "/tmp/lololo.txt", + }, + Other: map[string]float64{ + "testing": 3.9999, + }, + X: struct{ Y struct{ Z map[string]bool } }{ + Y: struct{ Z map[string]bool }{ + Z: map[string]bool{ + "is.Nested": true, + }, + }, + }, +} +var mapsTestToml = []byte(` +[Other] + "testing" = 3.9999 + +[Paths] + "/heloo.txt" = "/tmp/lololo.txt" + "/this/is/a/path" = "/this/is/also/a/path" + +[Simple] + "next" = "three" + "one plus one" = "two" + +[X] + + [X.Y] + + [X.Y.Z] + "is.Nested" = true +`) + +func TestEncodeQuotedMapKeys(t *testing.T) { + var buf bytes.Buffer + if err := NewEncoder(&buf).QuoteMapKeys(true).Encode(mapsTestData); err != nil { + t.Fatal(err) + } + result := buf.Bytes() + expected := mapsTestToml + if !bytes.Equal(result, expected) { + t.Errorf("Bad maps marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) + } +} +func TestDecodeQuotedMapKeys(t *testing.T) { + result := mapsTestStruct{} + err := NewDecoder(bytes.NewBuffer(mapsTestToml)).Decode(&result) + expected := mapsTestData + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(result, expected) { + t.Errorf("Bad maps unmarshal: expected %v, got %v", expected, result) + } +} diff --git a/vendor/github.com/pelletier/go-toml/parser.go b/vendor/github.com/pelletier/go-toml/parser.go index 8ee49cb56..d492a1e6f 100644 --- a/vendor/github.com/pelletier/go-toml/parser.go +++ b/vendor/github.com/pelletier/go-toml/parser.go @@ -110,7 +110,7 @@ func (p *tomlParser) parseGroupArray() tomlParserStateFn { newTree := newTree() newTree.position = startToken.Position array = append(array, newTree) - p.tree.SetPath(p.currentTable, array) + p.tree.SetPath(p.currentTable, "", false, array) // remove all keys that were children of this table array prefix := key.val + "." @@ -205,7 +205,7 @@ func (p *tomlParser) parseAssign() tomlParserStateFn { case *Tree, []*Tree: toInsert = value default: - toInsert = &tomlValue{value, key.Position} + toInsert = &tomlValue{value: value, position: key.Position} } targetNode.values[keyVal] = toInsert return p.parseStart @@ -299,7 +299,7 @@ Loop: key := p.getToken() p.assume(tokenEqual) value := p.parseRvalue() - tree.Set(key.val, value) + tree.Set(key.val, "", false, value) case tokenComma: if previous == nil { p.raiseError(follow, "inline table cannot start with a comma") diff --git a/vendor/github.com/pelletier/go-toml/parser_test.go b/vendor/github.com/pelletier/go-toml/parser_test.go index 508cb65f0..029bc52c3 100644 --- a/vendor/github.com/pelletier/go-toml/parser_test.go +++ b/vendor/github.com/pelletier/go-toml/parser_test.go @@ -46,7 +46,7 @@ func assertTree(t *testing.T, tree *Tree, err error, ref map[string]interface{}) func TestCreateSubTree(t *testing.T) { tree := newTree() tree.createSubTree([]string{"a", "b", "c"}, Position{}) - tree.Set("a.b.c", 42) + tree.Set("a.b.c", "", false, 42) if tree.Get("a.b.c") != 42 { t.Fail() } diff --git a/vendor/github.com/pelletier/go-toml/query/doc.go b/vendor/github.com/pelletier/go-toml/query/doc.go index f999fc965..ed63c1109 100644 --- a/vendor/github.com/pelletier/go-toml/query/doc.go +++ b/vendor/github.com/pelletier/go-toml/query/doc.go @@ -139,7 +139,7 @@ // Compiled Queries // // Queries may be executed directly on a Tree object, or compiled ahead -// of time and executed discretely. The former is more convienent, but has the +// of time and executed discretely. The former is more convenient, but has the // penalty of having to recompile the query expression each time. // // // basic query diff --git a/vendor/github.com/pelletier/go-toml/toml.go b/vendor/github.com/pelletier/go-toml/toml.go index 64f19ed30..c3e324374 100644 --- a/vendor/github.com/pelletier/go-toml/toml.go +++ b/vendor/github.com/pelletier/go-toml/toml.go @@ -11,14 +11,18 @@ import ( ) type tomlValue struct { - value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list - position Position + value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list + comment string + commented bool + position Position } // Tree is the result of the parsing of a TOML file. type Tree struct { - values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree - position Position + values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree + comment string + commented bool + position Position } func newTree() *Tree { @@ -177,14 +181,14 @@ func (t *Tree) GetDefault(key string, def interface{}) interface{} { // Set an element in the tree. // Key is a dot-separated path (e.g. a.b.c). // Creates all necessary intermediate trees, if needed. -func (t *Tree) Set(key string, value interface{}) { - t.SetPath(strings.Split(key, "."), value) +func (t *Tree) Set(key string, comment string, commented bool, value interface{}) { + t.SetPath(strings.Split(key, "."), comment, commented, value) } // SetPath sets an element in the tree. // Keys is an array of path elements (e.g. {"a","b","c"}). // Creates all necessary intermediate trees, if needed. -func (t *Tree) SetPath(keys []string, value interface{}) { +func (t *Tree) SetPath(keys []string, comment string, commented bool, value interface{}) { subtree := t for _, intermediateKey := range keys[:len(keys)-1] { nextTree, exists := subtree.values[intermediateKey] @@ -209,13 +213,17 @@ func (t *Tree) SetPath(keys []string, value interface{}) { switch value.(type) { case *Tree: + tt := value.(*Tree) + tt.comment = comment toInsert = value case []*Tree: toInsert = value case *tomlValue: - toInsert = value + tt := value.(*tomlValue) + tt.comment = comment + toInsert = tt default: - toInsert = &tomlValue{value: value} + toInsert = &tomlValue{value: value, comment: comment, commented: commented} } subtree.values[keys[len(keys)-1]] = toInsert diff --git a/vendor/github.com/pelletier/go-toml/tomltree_create.go b/vendor/github.com/pelletier/go-toml/tomltree_create.go index 19d1c0dc6..79610e9b3 100644 --- a/vendor/github.com/pelletier/go-toml/tomltree_create.go +++ b/vendor/github.com/pelletier/go-toml/tomltree_create.go @@ -104,7 +104,7 @@ func sliceToTree(object interface{}) (interface{}, error) { } arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue)) } - return &tomlValue{arrayValue.Interface(), Position{}}, nil + return &tomlValue{value: arrayValue.Interface(), position: Position{}}, nil } func toTree(object interface{}) (interface{}, error) { @@ -127,7 +127,7 @@ func toTree(object interface{}) (interface{}, error) { } values[key.String()] = newValue } - return &Tree{values, Position{}}, nil + return &Tree{values: values, position: Position{}}, nil } if value.Kind() == reflect.Array || value.Kind() == reflect.Slice { @@ -138,5 +138,5 @@ func toTree(object interface{}) (interface{}, error) { if err != nil { return nil, err } - return &tomlValue{simpleValue, Position{}}, nil + return &tomlValue{value: simpleValue, position: Position{}}, nil } diff --git a/vendor/github.com/pelletier/go-toml/tomltree_write.go b/vendor/github.com/pelletier/go-toml/tomltree_write.go index ca763ed58..449f35a44 100644 --- a/vendor/github.com/pelletier/go-toml/tomltree_write.go +++ b/vendor/github.com/pelletier/go-toml/tomltree_write.go @@ -118,7 +118,24 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( return bytesCount, err } - writtenBytesCount, err := writeStrings(w, indent, k, " = ", repr, "\n") + if v.comment != "" { + comment := strings.Replace(v.comment, "\n", "\n"+indent+"#", -1) + start := "# " + if strings.HasPrefix(comment, "#") { + start = "" + } + writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment, "\n") + bytesCount += int64(writtenBytesCountComment) + if errc != nil { + return bytesCount, errc + } + } + + var commented string + if v.commented { + commented = "# " + } + writtenBytesCount, err := writeStrings(w, indent, commented, k, " = ", repr, "\n") bytesCount += int64(writtenBytesCount) if err != nil { return bytesCount, err @@ -132,11 +149,31 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( if keyspace != "" { combinedKey = keyspace + "." + combinedKey } + var commented string + if t.commented { + commented = "# " + } switch node := v.(type) { // node has to be of those two types given how keys are sorted above case *Tree: - writtenBytesCount, err := writeStrings(w, "\n", indent, "[", combinedKey, "]\n") + tv, ok := t.values[k].(*Tree) + if !ok { + return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) + } + if tv.comment != "" { + comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1) + start := "# " + if strings.HasPrefix(comment, "#") { + start = "" + } + writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment) + bytesCount += int64(writtenBytesCountComment) + if errc != nil { + return bytesCount, errc + } + } + writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n") bytesCount += int64(writtenBytesCount) if err != nil { return bytesCount, err @@ -147,7 +184,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( } case []*Tree: for _, subTree := range node { - writtenBytesCount, err := writeStrings(w, "\n", indent, "[[", combinedKey, "]]\n") + writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n") bytesCount += int64(writtenBytesCount) if err != nil { return bytesCount, err diff --git a/vendor/github.com/pelletier/go-toml/tomltree_write_test.go b/vendor/github.com/pelletier/go-toml/tomltree_write_test.go index c2a1ce3b7..5ea59bc1a 100644 --- a/vendor/github.com/pelletier/go-toml/tomltree_write_test.go +++ b/vendor/github.com/pelletier/go-toml/tomltree_write_test.go @@ -30,7 +30,7 @@ func (f *failingWriter) Write(p []byte) (n int, err error) { f.buffer.Write(p[:toWrite]) f.written = f.failAt - return toWrite, fmt.Errorf("failingWriter failed after writting %d bytes", f.written) + return toWrite, fmt.Errorf("failingWriter failed after writing %d bytes", f.written) } func assertErrorString(t *testing.T, expected string, err error) { @@ -161,13 +161,13 @@ func TestTreeWriteToInvalidTreeSimpleValue(t *testing.T) { } func TestTreeWriteToInvalidTreeTomlValue(t *testing.T) { - tree := Tree{values: map[string]interface{}{"foo": &tomlValue{int8(1), Position{}}}} + tree := Tree{values: map[string]interface{}{"foo": &tomlValue{value: int8(1), comment: "", position: Position{}}}} _, err := tree.ToTomlString() assertErrorString(t, "unsupported value type int8: 1", err) } func TestTreeWriteToInvalidTreeTomlValueArray(t *testing.T) { - tree := Tree{values: map[string]interface{}{"foo": &tomlValue{[]interface{}{int8(1)}, Position{}}}} + tree := Tree{values: map[string]interface{}{"foo": &tomlValue{value: int8(1), comment: "", position: Position{}}}} _, err := tree.ToTomlString() assertErrorString(t, "unsupported value type int8: 1", err) } @@ -176,7 +176,7 @@ func TestTreeWriteToFailingWriterInSimpleValue(t *testing.T) { toml, _ := Load(`a = 2`) writer := failingWriter{failAt: 0, written: 0} _, err := toml.WriteTo(&writer) - assertErrorString(t, "failingWriter failed after writting 0 bytes", err) + assertErrorString(t, "failingWriter failed after writing 0 bytes", err) } func TestTreeWriteToFailingWriterInTable(t *testing.T) { @@ -185,11 +185,11 @@ func TestTreeWriteToFailingWriterInTable(t *testing.T) { a = 2`) writer := failingWriter{failAt: 2, written: 0} _, err := toml.WriteTo(&writer) - assertErrorString(t, "failingWriter failed after writting 2 bytes", err) + assertErrorString(t, "failingWriter failed after writing 2 bytes", err) writer = failingWriter{failAt: 13, written: 0} _, err = toml.WriteTo(&writer) - assertErrorString(t, "failingWriter failed after writting 13 bytes", err) + assertErrorString(t, "failingWriter failed after writing 13 bytes", err) } func TestTreeWriteToFailingWriterInArray(t *testing.T) { @@ -198,11 +198,11 @@ func TestTreeWriteToFailingWriterInArray(t *testing.T) { a = 2`) writer := failingWriter{failAt: 2, written: 0} _, err := toml.WriteTo(&writer) - assertErrorString(t, "failingWriter failed after writting 2 bytes", err) + assertErrorString(t, "failingWriter failed after writing 2 bytes", err) writer = failingWriter{failAt: 15, written: 0} _, err = toml.WriteTo(&writer) - assertErrorString(t, "failingWriter failed after writting 15 bytes", err) + assertErrorString(t, "failingWriter failed after writing 15 bytes", err) } func TestTreeWriteToMapExampleFile(t *testing.T) { diff --git a/vendor/github.com/pkg/errors/format_test.go b/vendor/github.com/pkg/errors/format_test.go index 15fd7d89d..c2eef5f04 100644 --- a/vendor/github.com/pkg/errors/format_test.go +++ b/vendor/github.com/pkg/errors/format_test.go @@ -491,7 +491,7 @@ type wrapper struct { want []string } -func prettyBlocks(blocks []string, prefix ...string) string { +func prettyBlocks(blocks []string) string { var out []string for _, b := range blocks { diff --git a/vendor/github.com/prometheus/common/log/log.go b/vendor/github.com/prometheus/common/log/log.go index 04e906c07..108830255 100644 --- a/vendor/github.com/prometheus/common/log/log.go +++ b/vendor/github.com/prometheus/common/log/log.go @@ -56,11 +56,11 @@ func (s *loggerSettings) apply(ctx *kingpin.ParseContext) error { // To use the default Kingpin application, call AddFlags(kingpin.CommandLine) func AddFlags(a *kingpin.Application) { s := loggerSettings{} - kingpin.Flag("log.level", "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]"). + a.Flag("log.level", "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]"). Default(origLogger.Level.String()). StringVar(&s.level) defaultFormat := url.URL{Scheme: "logger", Opaque: "stderr"} - kingpin.Flag("log.format", `Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or "logger:stdout?json=true"`). + a.Flag("log.format", `Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or "logger:stdout?json=true"`). Default(defaultFormat.String()). StringVar(&s.format) a.Action(s.apply) @@ -345,6 +345,11 @@ func Fatalf(format string, args ...interface{}) { baseLogger.sourced().Fatalf(format, args...) } +// AddHook adds hook to Prometheus' original logger. +func AddHook(hook logrus.Hook) { + origLogger.Hooks.Add(hook) +} + type errorLogWriter struct{} func (errorLogWriter) Write(b []byte) (int, error) { diff --git a/vendor/github.com/prometheus/procfs/fixtures/26231/limits b/vendor/github.com/prometheus/procfs/fixtures/26231/limits index 23c6b6898..8b40108aa 100644 --- a/vendor/github.com/prometheus/procfs/fixtures/26231/limits +++ b/vendor/github.com/prometheus/procfs/fixtures/26231/limits @@ -8,7 +8,7 @@ Max resident set unlimited unlimited bytes Max processes 62898 62898 processes Max open files 2048 4096 files Max locked memory 65536 65536 bytes -Max address space unlimited unlimited bytes +Max address space 8589934592 unlimited bytes Max file locks unlimited unlimited locks Max pending signals 62898 62898 signals Max msgqueue size 819200 819200 bytes diff --git a/vendor/github.com/prometheus/procfs/proc_limits.go b/vendor/github.com/prometheus/procfs/proc_limits.go index 2df997ce1..b684a5b55 100644 --- a/vendor/github.com/prometheus/procfs/proc_limits.go +++ b/vendor/github.com/prometheus/procfs/proc_limits.go @@ -13,46 +13,46 @@ import ( // http://man7.org/linux/man-pages/man2/getrlimit.2.html. type ProcLimits struct { // CPU time limit in seconds. - CPUTime int + CPUTime int64 // Maximum size of files that the process may create. - FileSize int + FileSize int64 // Maximum size of the process's data segment (initialized data, // uninitialized data, and heap). - DataSize int + DataSize int64 // Maximum size of the process stack in bytes. - StackSize int + StackSize int64 // Maximum size of a core file. - CoreFileSize int + CoreFileSize int64 // Limit of the process's resident set in pages. - ResidentSet int + ResidentSet int64 // Maximum number of processes that can be created for the real user ID of // the calling process. - Processes int + Processes int64 // Value one greater than the maximum file descriptor number that can be // opened by this process. - OpenFiles int + OpenFiles int64 // Maximum number of bytes of memory that may be locked into RAM. - LockedMemory int + LockedMemory int64 // Maximum size of the process's virtual memory address space in bytes. - AddressSpace int + AddressSpace int64 // Limit on the combined number of flock(2) locks and fcntl(2) leases that // this process may establish. - FileLocks int + FileLocks int64 // Limit of signals that may be queued for the real user ID of the calling // process. - PendingSignals int + PendingSignals int64 // Limit on the number of bytes that can be allocated for POSIX message // queues for the real user ID of the calling process. - MsqqueueSize int + MsqqueueSize int64 // Limit of the nice priority set using setpriority(2) or nice(2). - NicePriority int + NicePriority int64 // Limit of the real-time priority set using sched_setscheduler(2) or // sched_setparam(2). - RealtimePriority int + RealtimePriority int64 // Limit (in microseconds) on the amount of CPU time that a process // scheduled under a real-time scheduling policy may consume without making // a blocking system call. - RealtimeTimeout int + RealtimeTimeout int64 } const ( @@ -125,13 +125,13 @@ func (p Proc) NewLimits() (ProcLimits, error) { return l, s.Err() } -func parseInt(s string) (int, error) { +func parseInt(s string) (int64, error) { if s == limitsUnlimited { return -1, nil } - i, err := strconv.ParseInt(s, 10, 32) + i, err := strconv.ParseInt(s, 10, 64) if err != nil { return 0, fmt.Errorf("couldn't parse value %s: %s", s, err) } - return int(i), nil + return i, nil } diff --git a/vendor/github.com/prometheus/procfs/proc_limits_test.go b/vendor/github.com/prometheus/procfs/proc_limits_test.go index 70bf04ec2..ac62a3bb6 100644 --- a/vendor/github.com/prometheus/procfs/proc_limits_test.go +++ b/vendor/github.com/prometheus/procfs/proc_limits_test.go @@ -15,14 +15,14 @@ func TestNewLimits(t *testing.T) { for _, test := range []struct { name string - want int - have int + want int64 + have int64 }{ {name: "cpu time", want: -1, have: l.CPUTime}, {name: "open files", want: 2048, have: l.OpenFiles}, {name: "msgqueue size", want: 819200, have: l.MsqqueueSize}, {name: "nice priority", want: 0, have: l.NicePriority}, - {name: "address space", want: -1, have: l.AddressSpace}, + {name: "address space", want: 8589934592, have: l.AddressSpace}, } { if test.want != test.have { t.Errorf("want %s %d, have %d", test.name, test.want, test.have) diff --git a/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/.gitignore b/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/.gitignore new file mode 100644 index 000000000..b6fadf4eb --- /dev/null +++ b/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/.gitignore @@ -0,0 +1,7 @@ +_go_.* +_gotest_.* +_obj +_test +_testmain.go +*.out +*.[568] diff --git a/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/README.md b/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/README.md new file mode 100644 index 000000000..8b6b6fc4f --- /dev/null +++ b/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/README.md @@ -0,0 +1,45 @@ +# Assert (c) Blake Mizerany and Keith Rarick -- MIT LICENCE + +## Assertions for Go tests + +## Install + + $ go get github.com/bmizerany/assert + +## Use + +**point.go** + + package point + + type Point struct { + x, y int + } + +**point_test.go** + + + package point + + import ( + "testing" + "github.com/bmizerany/assert" + ) + + func TestAsserts(t *testing.T) { + p1 := Point{1, 1} + p2 := Point{2, 1} + + assert.Equal(t, p1, p2) + } + +**output** + $ go test + --- FAIL: TestAsserts (0.00 seconds) + assert.go:15: /Users/flavio.barbosa/dev/stewie/src/point_test.go:12 + assert.go:24: ! X: 1 != 2 + FAIL + +## Docs + + http://github.com/bmizerany/assert diff --git a/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/assert.go b/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/assert.go new file mode 100644 index 000000000..7409f985e --- /dev/null +++ b/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/assert.go @@ -0,0 +1,76 @@ +package assert +// Testing helpers for doozer. + +import ( + "github.com/kr/pretty" + "reflect" + "testing" + "runtime" + "fmt" +) + +func assert(t *testing.T, result bool, f func(), cd int) { + if !result { + _, file, line, _ := runtime.Caller(cd + 1) + t.Errorf("%s:%d", file, line) + f() + t.FailNow() + } +} + +func equal(t *testing.T, exp, got interface{}, cd int, args ...interface{}) { + fn := func() { + for _, desc := range pretty.Diff(exp, got) { + t.Error("!", desc) + } + if len(args) > 0 { + t.Error("!", " -", fmt.Sprint(args...)) + } + } + result := reflect.DeepEqual(exp, got) + assert(t, result, fn, cd+1) +} + +func tt(t *testing.T, result bool, cd int, args ...interface{}) { + fn := func() { + t.Errorf("! Failure") + if len(args) > 0 { + t.Error("!", " -", fmt.Sprint(args...)) + } + } + assert(t, result, fn, cd+1) +} + +func T(t *testing.T, result bool, args ...interface{}) { + tt(t, result, 1, args...) +} + +func Tf(t *testing.T, result bool, format string, args ...interface{}) { + tt(t, result, 1, fmt.Sprintf(format, args...)) +} + +func Equal(t *testing.T, exp, got interface{}, args ...interface{}) { + equal(t, exp, got, 1, args...) +} + +func Equalf(t *testing.T, exp, got interface{}, format string, args ...interface{}) { + equal(t, exp, got, 1, fmt.Sprintf(format, args...)) +} + +func NotEqual(t *testing.T, exp, got interface{}, args ...interface{}) { + fn := func() { + t.Errorf("! Unexpected: <%#v>", exp) + if len(args) > 0 { + t.Error("!", " -", fmt.Sprint(args...)) + } + } + result := !reflect.DeepEqual(exp, got) + assert(t, result, fn, 1) +} + +func Panic(t *testing.T, err interface{}, fn func()) { + defer func() { + equal(t, err, recover(), 3) + }() + fn() +} diff --git a/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/assert_test.go b/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/assert_test.go new file mode 100644 index 000000000..162a590c6 --- /dev/null +++ b/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/assert_test.go @@ -0,0 +1,15 @@ +package assert + +import ( + "testing" +) + +func TestLineNumbers(t *testing.T) { + Equal(t, "foo", "foo", "msg!") + //Equal(t, "foo", "bar", "this should blow up") +} + +func TestNotEqual(t *testing.T) { + NotEqual(t, "foo", "bar", "msg!") + //NotEqual(t, "foo", "foo", "this should blow up") +} diff --git a/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/example/point.go b/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/example/point.go new file mode 100644 index 000000000..15789fe10 --- /dev/null +++ b/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/example/point.go @@ -0,0 +1,5 @@ +package point + +type Point struct { + X, Y int +} diff --git a/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/example/point_test.go b/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/example/point_test.go new file mode 100644 index 000000000..34e791a43 --- /dev/null +++ b/vendor/github.com/segmentio/backo-go/vendor/github.com/bmizerany/assert/example/point_test.go @@ -0,0 +1,13 @@ +package point + +import ( + "testing" + "assert" +) + +func TestAsserts(t *testing.T) { + p1 := Point{1, 1} + p2 := Point{2, 1} + + assert.Equal(t, p1, p2) +} diff --git a/vendor/github.com/spf13/afero/.travis.yml b/vendor/github.com/spf13/afero/.travis.yml index 6c296d293..618159a4e 100644 --- a/vendor/github.com/spf13/afero/.travis.yml +++ b/vendor/github.com/spf13/afero/.travis.yml @@ -16,5 +16,6 @@ matrix: fast_finish: true
script:
- - go test -v ./...
- go build
+ - go test -race -v ./...
+
diff --git a/vendor/github.com/spf13/afero/README.md b/vendor/github.com/spf13/afero/README.md index d9e332730..0c9b04b53 100644 --- a/vendor/github.com/spf13/afero/README.md +++ b/vendor/github.com/spf13/afero/README.md @@ -61,11 +61,11 @@ import "github.com/spf13/afero" First define a package variable and set it to a pointer to a filesystem. ```go -var AppFs afero.Fs = afero.NewMemMapFs() +var AppFs = afero.NewMemMapFs() or -var AppFs afero.Fs = afero.NewOsFs() +var AppFs = afero.NewOsFs() ``` It is important to note that if you repeat the composite literal you will be using a completely new and isolated filesystem. In the case of @@ -81,7 +81,10 @@ So if my application before had: ```go os.Open('/tmp/foo') ``` -We would replace it with a call to `AppFs.Open('/tmp/foo')`. +We would replace it with: +```go +AppFs.Open('/tmp/foo') +``` `AppFs` being the variable we defined above. @@ -166,8 +169,8 @@ f, err := afero.TempFile(fs,"", "ioutil-test") ### Calling via Afero ```go -fs := afero.NewMemMapFs -afs := &Afero{Fs: fs} +fs := afero.NewMemMapFs() +afs := &afero.Afero{Fs: fs} f, err := afs.TempFile("", "ioutil-test") ``` diff --git a/vendor/github.com/spf13/afero/appveyor.yml b/vendor/github.com/spf13/afero/appveyor.yml index 006f31534..a633ad500 100644 --- a/vendor/github.com/spf13/afero/appveyor.yml +++ b/vendor/github.com/spf13/afero/appveyor.yml @@ -12,4 +12,4 @@ build_script: go build github.com/spf13/afero test_script: -- cmd: go test -v github.com/spf13/afero +- cmd: go test -race -v github.com/spf13/afero/... diff --git a/vendor/github.com/spf13/afero/cacheOnReadFs.go b/vendor/github.com/spf13/afero/cacheOnReadFs.go index e54a4f8b4..b026e0de8 100644 --- a/vendor/github.com/spf13/afero/cacheOnReadFs.go +++ b/vendor/github.com/spf13/afero/cacheOnReadFs.go @@ -64,15 +64,10 @@ func (u *CacheOnReadFs) cacheStatus(name string) (state cacheState, fi os.FileIn return cacheHit, lfi, nil } - if err == syscall.ENOENT { + if err == syscall.ENOENT || os.IsNotExist(err) { return cacheMiss, nil, nil } - var ok bool - if err, ok = err.(*os.PathError); ok { - if err == os.ErrNotExist { - return cacheMiss, nil, nil - } - } + return cacheMiss, nil, err } diff --git a/vendor/github.com/spf13/afero/composite_test.go b/vendor/github.com/spf13/afero/composite_test.go index e8ac1a818..8e44611dc 100644 --- a/vendor/github.com/spf13/afero/composite_test.go +++ b/vendor/github.com/spf13/afero/composite_test.go @@ -1,6 +1,7 @@ package afero import ( + "bytes" "fmt" "io/ioutil" "os" @@ -366,3 +367,38 @@ func TestUnionCacheExpire(t *testing.T) { t.Errorf("cache time failed: <%s>", data) } } + +func TestCacheOnReadFsNotInLayer(t *testing.T) { + base := NewMemMapFs() + layer := NewMemMapFs() + fs := NewCacheOnReadFs(base, layer, 0) + + fh, err := base.Create("/file.txt") + if err != nil { + t.Fatal("unable to create file: ", err) + } + + txt := []byte("This is a test") + fh.Write(txt) + fh.Close() + + fh, err = fs.Open("/file.txt") + if err != nil { + t.Fatal("could not open file: ", err) + } + + b, err := ReadAll(fh) + fh.Close() + + if err != nil { + t.Fatal("could not read file: ", err) + } else if !bytes.Equal(txt, b) { + t.Fatalf("wanted file text %q, got %q", txt, b) + } + + fh, err = layer.Open("/file.txt") + if err != nil { + t.Fatal("could not open file from layer: ", err) + } + fh.Close() +} diff --git a/vendor/github.com/spf13/afero/mem/file.go b/vendor/github.com/spf13/afero/mem/file.go index e41e0123d..5401a3b7c 100644 --- a/vendor/github.com/spf13/afero/mem/file.go +++ b/vendor/github.com/spf13/afero/mem/file.go @@ -74,14 +74,24 @@ func CreateDir(name string) *FileData { } func ChangeFileName(f *FileData, newname string) { + f.Lock() f.name = newname + f.Unlock() } func SetMode(f *FileData, mode os.FileMode) { + f.Lock() f.mode = mode + f.Unlock() } func SetModTime(f *FileData, mtime time.Time) { + f.Lock() + setModTime(f, mtime) + f.Unlock() +} + +func setModTime(f *FileData, mtime time.Time) { f.modtime = mtime } @@ -102,7 +112,7 @@ func (f *File) Close() error { f.fileData.Lock() f.closed = true if !f.readOnly { - SetModTime(f.fileData, time.Now()) + setModTime(f.fileData, time.Now()) } f.fileData.Unlock() return nil @@ -197,7 +207,7 @@ func (f *File) Truncate(size int64) error { } else { f.fileData.data = f.fileData.data[0:size] } - SetModTime(f.fileData, time.Now()) + setModTime(f.fileData, time.Now()) return nil } @@ -236,7 +246,7 @@ func (f *File) Write(b []byte) (n int, err error) { f.fileData.data = append(f.fileData.data[:cur], b...) f.fileData.data = append(f.fileData.data, tail...) } - SetModTime(f.fileData, time.Now()) + setModTime(f.fileData, time.Now()) atomic.StoreInt64(&f.at, int64(len(f.fileData.data))) return @@ -261,17 +271,33 @@ type FileInfo struct { // Implements os.FileInfo func (s *FileInfo) Name() string { + s.Lock() _, name := filepath.Split(s.name) + s.Unlock() return name } -func (s *FileInfo) Mode() os.FileMode { return s.mode } -func (s *FileInfo) ModTime() time.Time { return s.modtime } -func (s *FileInfo) IsDir() bool { return s.dir } -func (s *FileInfo) Sys() interface{} { return nil } +func (s *FileInfo) Mode() os.FileMode { + s.Lock() + defer s.Unlock() + return s.mode +} +func (s *FileInfo) ModTime() time.Time { + s.Lock() + defer s.Unlock() + return s.modtime +} +func (s *FileInfo) IsDir() bool { + s.Lock() + defer s.Unlock() + return s.dir +} +func (s *FileInfo) Sys() interface{} { return nil } func (s *FileInfo) Size() int64 { if s.IsDir() { return int64(42) } + s.Lock() + defer s.Unlock() return int64(len(s.data)) } diff --git a/vendor/github.com/spf13/afero/mem/file_test.go b/vendor/github.com/spf13/afero/mem/file_test.go new file mode 100644 index 000000000..5769067a7 --- /dev/null +++ b/vendor/github.com/spf13/afero/mem/file_test.go @@ -0,0 +1,154 @@ +package mem + +import ( + "testing" + "time" +) + +func TestFileDataNameRace(t *testing.T) { + t.Parallel() + const someName = "someName" + const someOtherName = "someOtherName" + d := FileData{ + name: someName, + } + + if d.Name() != someName { + t.Errorf("Failed to read correct Name, was %v", d.Name()) + } + + ChangeFileName(&d, someOtherName) + if d.Name() != someOtherName { + t.Errorf("Failed to set Name, was %v", d.Name()) + } + + go func() { + ChangeFileName(&d, someName) + }() + + if d.Name() != someName && d.Name() != someOtherName { + t.Errorf("Failed to read either Name, was %v", d.Name()) + } +} + +func TestFileDataModTimeRace(t *testing.T) { + t.Parallel() + someTime := time.Now() + someOtherTime := someTime.Add(1 * time.Minute) + + d := FileData{ + modtime: someTime, + } + + s := FileInfo{ + FileData: &d, + } + + if s.ModTime() != someTime { + t.Errorf("Failed to read correct value, was %v", s.ModTime()) + } + + SetModTime(&d, someOtherTime) + if s.ModTime() != someOtherTime { + t.Errorf("Failed to set ModTime, was %v", s.ModTime()) + } + + go func() { + SetModTime(&d, someTime) + }() + + if s.ModTime() != someTime && s.ModTime() != someOtherTime { + t.Errorf("Failed to read either modtime, was %v", s.ModTime()) + } +} + +func TestFileDataModeRace(t *testing.T) { + t.Parallel() + const someMode = 0777 + const someOtherMode = 0660 + + d := FileData{ + mode: someMode, + } + + s := FileInfo{ + FileData: &d, + } + + if s.Mode() != someMode { + t.Errorf("Failed to read correct value, was %v", s.Mode()) + } + + SetMode(&d, someOtherMode) + if s.Mode() != someOtherMode { + t.Errorf("Failed to set Mode, was %v", s.Mode()) + } + + go func() { + SetMode(&d, someMode) + }() + + if s.Mode() != someMode && s.Mode() != someOtherMode { + t.Errorf("Failed to read either mode, was %v", s.Mode()) + } +} + +func TestFileDataIsDirRace(t *testing.T) { + t.Parallel() + + d := FileData{ + dir: true, + } + + s := FileInfo{ + FileData: &d, + } + + if s.IsDir() != true { + t.Errorf("Failed to read correct value, was %v", s.IsDir()) + } + + go func() { + s.Lock() + d.dir = false + s.Unlock() + }() + + //just logging the value to trigger a read: + t.Logf("Value is %v", s.IsDir()) +} + +func TestFileDataSizeRace(t *testing.T) { + t.Parallel() + + const someData = "Hello" + const someOtherDataSize = "Hello World" + + d := FileData{ + data: []byte(someData), + dir: false, + } + + s := FileInfo{ + FileData: &d, + } + + if s.Size() != int64(len(someData)) { + t.Errorf("Failed to read correct value, was %v", s.Size()) + } + + go func() { + s.Lock() + d.data = []byte(someOtherDataSize) + s.Unlock() + }() + + //just logging the value to trigger a read: + t.Logf("Value is %v", s.Size()) + + //Testing the Dir size case + d.dir = true + if s.Size() != int64(42) { + t.Errorf("Failed to read correct value for dir, was %v", s.Size()) + } +} diff --git a/vendor/github.com/spf13/afero/memmap.go b/vendor/github.com/spf13/afero/memmap.go index 14cd438fb..09498e70f 100644 --- a/vendor/github.com/spf13/afero/memmap.go +++ b/vendor/github.com/spf13/afero/memmap.go @@ -141,7 +141,7 @@ func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error { m.registerWithParent(item) m.mu.Unlock() - m.Chmod(name, perm) + m.Chmod(name, perm|os.ModeDir) return nil } @@ -151,9 +151,8 @@ func (m *MemMapFs) MkdirAll(path string, perm os.FileMode) error { if err != nil { if err.(*os.PathError).Err == ErrFileExists { return nil - } else { - return err } + return err } return nil } diff --git a/vendor/github.com/spf13/afero/memmap_test.go b/vendor/github.com/spf13/afero/memmap_test.go index d28e91220..09d8680f6 100644 --- a/vendor/github.com/spf13/afero/memmap_test.go +++ b/vendor/github.com/spf13/afero/memmap_test.go @@ -110,6 +110,8 @@ func TestPermSet(t *testing.T) { const dirPathAll = "/my/path/to/dir" const fileMode = os.FileMode(0765) + // directories will also have the directory bit set + const dirMode = fileMode | os.ModeDir fs := NewMemMapFs() @@ -132,7 +134,7 @@ func TestPermSet(t *testing.T) { } // Test Mkdir - err = fs.Mkdir(dirPath, fileMode) + err = fs.Mkdir(dirPath, dirMode) if err != nil { t.Errorf("MkDir Create failed: %s", err) return @@ -142,13 +144,14 @@ func TestPermSet(t *testing.T) { t.Errorf("Stat failed: %s", err) return } - if s.Mode().String() != fileMode.String() { - t.Errorf("Permissions Incorrect: %s != %s", s.Mode().String(), fileMode.String()) + // sets File + if s.Mode().String() != dirMode.String() { + t.Errorf("Permissions Incorrect: %s != %s", s.Mode().String(), dirMode.String()) return } // Test MkdirAll - err = fs.MkdirAll(dirPathAll, fileMode) + err = fs.MkdirAll(dirPathAll, dirMode) if err != nil { t.Errorf("MkDir Create failed: %s", err) return @@ -158,8 +161,8 @@ func TestPermSet(t *testing.T) { t.Errorf("Stat failed: %s", err) return } - if s.Mode().String() != fileMode.String() { - t.Errorf("Permissions Incorrect: %s != %s", s.Mode().String(), fileMode.String()) + if s.Mode().String() != dirMode.String() { + t.Errorf("Permissions Incorrect: %s != %s", s.Mode().String(), dirMode.String()) return } } @@ -384,3 +387,35 @@ loop: } } } + +func TestMemFsDirMode(t *testing.T) { + fs := NewMemMapFs() + err := fs.Mkdir("/testDir1", 0644) + if err != nil { + t.Error(err) + } + err = fs.MkdirAll("/sub/testDir2", 0644) + if err != nil { + t.Error(err) + } + info, err := fs.Stat("/testDir1") + if err != nil { + t.Error(err) + } + if !info.IsDir() { + t.Error("should be a directory") + } + if !info.Mode().IsDir() { + t.Error("FileMode is not directory") + } + info, err = fs.Stat("/sub/testDir2") + if err != nil { + t.Error(err) + } + if !info.IsDir() { + t.Error("should be a directory") + } + if !info.Mode().IsDir() { + t.Error("FileMode is not directory") + } +} diff --git a/vendor/github.com/spf13/afero/memradix.go b/vendor/github.com/spf13/afero/memradix.go deleted file mode 100644 index 87527f35a..000000000 --- a/vendor/github.com/spf13/afero/memradix.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright © 2014 Steve Francia <spf@spf13.com>. -// -// 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 afero diff --git a/vendor/github.com/spf13/afero/util.go b/vendor/github.com/spf13/afero/util.go index 2f44e6a10..7463887fd 100644 --- a/vendor/github.com/spf13/afero/util.go +++ b/vendor/github.com/spf13/afero/util.go @@ -157,7 +157,7 @@ func UnicodeSanitize(s string) string { return string(target) } -// Transform characters with accents into plan forms +// Transform characters with accents into plain forms. func NeuterAccents(s string) string { t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC) result, _, _ := transform.String(t, string(s)) diff --git a/vendor/github.com/spf13/cobra/README.md b/vendor/github.com/spf13/cobra/README.md index da9aa881e..d8b5c96c8 100644 --- a/vendor/github.com/spf13/cobra/README.md +++ b/vendor/github.com/spf13/cobra/README.md @@ -19,13 +19,33 @@ Many of the most widely used Go projects are built using Cobra including: * [GiantSwarm's swarm](https://github.com/giantswarm/cli) * [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack) * [rclone](http://rclone.org/) - +* [nehm](https://github.com/bogem/nehm) [![Build Status](https://travis-ci.org/spf13/cobra.svg "Travis CI status")](https://travis-ci.org/spf13/cobra) [![CircleCI status](https://circleci.com/gh/spf13/cobra.png?circle-token=:circle-token "CircleCI status")](https://circleci.com/gh/spf13/cobra) [![GoDoc](https://godoc.org/github.com/spf13/cobra?status.svg)](https://godoc.org/github.com/spf13/cobra) -![cobra](https://cloud.githubusercontent.com/assets/173412/10911369/84832a8e-8212-11e5-9f82-cc96660a4794.gif) +# Table of Contents + +- [Overview](#overview) +- [Concepts](#concepts) + * [Commands](#commands) + * [Flags](#flags) +- [Installing](#installing) +- [Getting Started](#getting-started) + * [Using the Cobra Generator](#using-the-cobra-generator) + * [Using the Cobra Library](#using-the-cobra-library) + * [Working with Flags](#working-with-flags) + * [Positional and Custom Arguments](#positional-and-custom-arguments) + * [Example](#example) + * [Help Command](#help-command) + * [Usage Message](#usage-message) + * [PreRun and PostRun Hooks](#prerun-and-postrun-hooks) + * [Suggestions when "unknown command" happens](#suggestions-when-unknown-command-happens) + * [Generating documentation for your command](#generating-documentation-for-your-command) + * [Generating bash completions](#generating-bash-completions) +- [Contributing](#contributing) +- [License](#license) # Overview @@ -43,7 +63,6 @@ Cobra provides: * Easy generation of applications & commands with `cobra init appname` & `cobra add cmdname` * Intelligent suggestions (`app srver`... did you mean `app server`?) * Automatic help generation for commands and flags -* Automatic detailed help for `app help [command]` * Automatic help flag recognition of `-h`, `--help`, etc. * Automatically generated bash autocomplete for your application * Automatically generated man pages for your application @@ -51,16 +70,6 @@ Cobra provides: * The flexibility to define your own help, usage, etc. * Optional tight integration with [viper](http://github.com/spf13/viper) for 12-factor apps -Cobra has an exceptionally clean interface and simple design without needless -constructors or initialization methods. - -Applications built with Cobra commands are designed to be as user-friendly as -possible. Flags can be placed before or after the command (as long as a -confusing space isn’t provided). Both short and long flags can be used. A -command need not even be fully typed. Help is automatically generated and -available for the application or for a specific command using either the help -command or the `--help` flag. - # Concepts Cobra is built on a structure of commands, arguments & flags. @@ -93,20 +102,11 @@ have children commands and optionally run an action. In the example above, 'server' is the command. -A Command has the following structure: - -```go -type Command struct { - Use string // The one-line usage message. - Short string // The short description shown in the 'help' output. - Long string // The long message shown in the 'help <this-command>' output. - Run func(cmd *Command, args []string) // Run runs the command. -} -``` +[More about cobra.Command](https://godoc.org/github.com/spf13/cobra#Command) ## Flags -A Flag is a way to modify the behavior of a command. Cobra supports +A flag is a way to modify the behavior of a command. Cobra supports fully POSIX-compliant flags as well as the Go [flag package](https://golang.org/pkg/flag/). A Cobra command can define flags that persist through to children commands and flags that are only available to that command. @@ -170,106 +170,14 @@ func main() { Cobra provides its own program that will create your application and add any commands you want. It's the easiest way to incorporate Cobra into your application. -In order to use the cobra command, compile it using the following command: +[Here](https://github.com/spf13/cobra/blob/master/cobra/README.md) you can find more information about it. - go get github.com/spf13/cobra/cobra - -This will create the cobra executable under your `$GOPATH/bin` directory. - -### cobra init - -The `cobra init [yourApp]` command will create your initial application code -for you. It is a very powerful application that will populate your program with -the right structure so you can immediately enjoy all the benefits of Cobra. It -will also automatically apply the license you specify to your application. - -Cobra init is pretty smart. You can provide it a full path, or simply a path -similar to what is expected in the import. - -``` -cobra init github.com/spf13/newAppName -``` - -### cobra add - -Once an application is initialized Cobra can create additional commands for you. -Let's say you created an app and you wanted the following commands for it: - -* app serve -* app config -* app config create - -In your project directory (where your main.go file is) you would run the following: - -``` -cobra add serve -cobra add config -cobra add create -p 'configCmd' -``` - -*Note: Use camelCase (not snake_case/snake-case) for command names. -Otherwise, you will encounter errors. -For example, `cobra add add-user` is incorrect, but `cobra add addUser` is valid.* - -Once you have run these three commands you would have an app structure similar to -the following: - -``` - ▾ app/ - ▾ cmd/ - serve.go - config.go - create.go - main.go -``` - -At this point you can run `go run main.go` and it would run your app. `go run -main.go serve`, `go run main.go config`, `go run main.go config create` along -with `go run main.go help serve`, etc. would all work. - -Obviously you haven't added your own code to these yet. The commands are ready -for you to give them their tasks. Have fun! - -### Configuring the cobra generator - -The Cobra generator will be easier to use if you provide a simple configuration -file which will help you eliminate providing a bunch of repeated information in -flags over and over. - -An example ~/.cobra.yaml file: - -```yaml -author: Steve Francia <spf@spf13.com> -license: MIT -``` - -You can specify no license by setting `license` to `none` or you can specify -a custom license: - -```yaml -license: - header: This file is part of {{ .appName }}. - text: | - {{ .copyright }} - - This is my license. There are many like it, but this one is mine. - My license is my best friend. It is my life. I must master it as I must - master my life. -``` - -You can also use built-in licenses. For example, **GPLv2**, **GPLv3**, **LGPL**, -**AGPL**, **MIT**, **2-Clause BSD** or **3-Clause BSD**. - -## Manually implementing Cobra +## Using the Cobra Library To manually implement Cobra you need to create a bare main.go file and a RootCmd file. You will optionally provide additional commands as you see fit. -### Create the root command - -The root command represents your binary itself. - -#### Manually create rootCmd +### Create rootCmd Cobra doesn't require any special constructors. Simply create your commands. @@ -316,10 +224,6 @@ func init() { viper.SetDefault("license", "apache") } -func Execute() { - RootCmd.Execute() -} - func initConfig() { // Don't forget to read config either from cfgFile or from home directory! if cfgFile != "" { @@ -400,17 +304,6 @@ var versionCmd = &cobra.Command{ } ``` -### Attach command to its parent - - -If you notice in the above example we attach the command to its parent. In -this case the parent is the rootCmd. In this example we are attaching it to the -root, but commands can be attached at any level. - -```go -RootCmd.AddCommand(versionCmd) -``` - ## Working with Flags Flags provide modifiers to control how the action command operates. @@ -446,6 +339,19 @@ A flag can also be assigned locally which will only apply to that specific comma RootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from") ``` +### Local Flag on Parent Commands + +By default Cobra only parses local flags on the target command, any local flags on +parent commands are ignored. By enabling `Command.TraverseChildren` Cobra will +parse local flags on each command before executing the target command. + +```go +command := cobra.Command{ + Use: "print [OPTIONS] [COMMANDS]", + TraverseChildren: true, +} +``` + ### Bind Flags with Config You can also bind your flags with [viper](https://github.com/spf13/viper): @@ -569,7 +475,7 @@ a count and a string.`, For a more complete example of a larger application, please checkout [Hugo](http://gohugo.io/). -## The Help Command +## Help Command Cobra automatically adds a help command to your application when you have subcommands. This will be called when a user runs 'app help'. Additionally, help will also @@ -582,60 +488,28 @@ create' is called. Every command will automatically have the '--help' flag adde The following output is automatically generated by Cobra. Nothing beyond the command and flag definitions are needed. - > hugo help + $ cobra help - hugo is the main command, used to build your Hugo site. - - Hugo is a Fast and Flexible Static Site Generator - built with love by spf13 and friends in Go. - - Complete documentation is available at http://gohugo.io/. + Cobra is a CLI library for Go that empowers applications. + This application is a tool to generate the needed files + to quickly create a Cobra application. Usage: - hugo [flags] - hugo [command] + cobra [command] Available Commands: - server Hugo runs its own webserver to render the files - version Print the version number of Hugo - config Print the site configuration - check Check content in the source directory - benchmark Benchmark hugo by building a site a number of times. - convert Convert your content to different formats - new Create new content for your site - list Listing out various types of content - undraft Undraft changes the content's draft status from 'True' to 'False' - genautocomplete Generate shell autocompletion script for Hugo - gendoc Generate Markdown documentation for the Hugo CLI. - genman Generate man page for Hugo - import Import your site from others. + add Add a command to a Cobra Application + help Help about any command + init Initialize a Cobra Application Flags: - -b, --baseURL="": hostname (and path) to the root, e.g. http://spf13.com/ - -D, --buildDrafts[=false]: include content marked as draft - -F, --buildFuture[=false]: include content with publishdate in the future - --cacheDir="": filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/ - --canonifyURLs[=false]: if true, all relative URLs will be canonicalized using baseURL - --config="": config file (default is path/config.yaml|json|toml) - -d, --destination="": filesystem path to write files to - --disableRSS[=false]: Do not build RSS files - --disableSitemap[=false]: Do not build Sitemap file - --editor="": edit new content with this editor, if provided - --ignoreCache[=false]: Ignores the cache directory for reading but still writes to it - --log[=false]: Enable Logging - --logFile="": Log File path (if set, logging enabled automatically) - --noTimes[=false]: Don't sync modification time of files - --pluralizeListTitles[=true]: Pluralize titles in lists using inflect - --preserveTaxonomyNames[=false]: Preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu") - -s, --source="": filesystem path to read files relative from - --stepAnalysis[=false]: display memory and timing of different steps of the program - -t, --theme="": theme to use (located in /themes/THEMENAME/) - --uglyURLs[=false]: if true, use /filename.html instead of /filename/ - -v, --verbose[=false]: verbose output - --verboseLog[=false]: verbose logging - -w, --watch[=false]: watch filesystem for changes and recreate as needed - - Use "hugo [command] --help" for more information about a command. + -a, --author string author name for copyright attribution (default "YOUR NAME") + --config string config file (default is $HOME/.cobra.yaml) + -h, --help help for cobra + -l, --license string name of license for the project + --viper use Viper for configuration (default true) + + Use "cobra [command] --help" for more information about a command. Help is just a command like any other. There is no special logic or behavior @@ -643,36 +517,18 @@ around it. In fact, you can provide your own if you want. ### Defining your own help -You can provide your own Help command or your own template for the default command to use. - -The default help command is +You can provide your own Help command or your own template for the default command to use +with followind functions: ```go -func (c *Command) initHelp() { - if c.helpCommand == nil { - c.helpCommand = &Command{ - Use: "help [command]", - Short: "Help about any command", - Long: `Help provides help for any command in the application. - Simply type ` + c.Name() + ` help [path to command] for full details.`, - Run: c.HelpFunc(), - } - } - c.AddCommand(c.helpCommand) -} -``` - -You can provide your own command, function or template through the following methods: - -```go -command.SetHelpCommand(cmd *Command) -command.SetHelpFunc(f func(*Command, []string)) -command.SetHelpTemplate(s string) +cmd.SetHelpCommand(cmd *Command) +cmd.SetHelpFunc(f func(*Command, []string)) +cmd.SetHelpTemplate(s string) ``` The latter two will also apply to any children commands. -## Usage +## Usage Message When the user provides an invalid flag or invalid command, Cobra responds by showing the user the 'usage'. @@ -681,71 +537,35 @@ showing the user the 'usage'. You may recognize this from the help above. That's because the default help embeds the usage as part of its output. + $ cobra --invalid + Error: unknown flag: --invalid Usage: - hugo [flags] - hugo [command] + cobra [command] Available Commands: - server Hugo runs its own webserver to render the files - version Print the version number of Hugo - config Print the site configuration - check Check content in the source directory - benchmark Benchmark hugo by building a site a number of times. - convert Convert your content to different formats - new Create new content for your site - list Listing out various types of content - undraft Undraft changes the content's draft status from 'True' to 'False' - genautocomplete Generate shell autocompletion script for Hugo - gendoc Generate Markdown documentation for the Hugo CLI. - genman Generate man page for Hugo - import Import your site from others. + add Add a command to a Cobra Application + help Help about any command + init Initialize a Cobra Application Flags: - -b, --baseURL="": hostname (and path) to the root, e.g. http://spf13.com/ - -D, --buildDrafts[=false]: include content marked as draft - -F, --buildFuture[=false]: include content with publishdate in the future - --cacheDir="": filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/ - --canonifyURLs[=false]: if true, all relative URLs will be canonicalized using baseURL - --config="": config file (default is path/config.yaml|json|toml) - -d, --destination="": filesystem path to write files to - --disableRSS[=false]: Do not build RSS files - --disableSitemap[=false]: Do not build Sitemap file - --editor="": edit new content with this editor, if provided - --ignoreCache[=false]: Ignores the cache directory for reading but still writes to it - --log[=false]: Enable Logging - --logFile="": Log File path (if set, logging enabled automatically) - --noTimes[=false]: Don't sync modification time of files - --pluralizeListTitles[=true]: Pluralize titles in lists using inflect - --preserveTaxonomyNames[=false]: Preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu") - -s, --source="": filesystem path to read files relative from - --stepAnalysis[=false]: display memory and timing of different steps of the program - -t, --theme="": theme to use (located in /themes/THEMENAME/) - --uglyURLs[=false]: if true, use /filename.html instead of /filename/ - -v, --verbose[=false]: verbose output - --verboseLog[=false]: verbose logging - -w, --watch[=false]: watch filesystem for changes and recreate as needed + -a, --author string author name for copyright attribution (default "YOUR NAME") + --config string config file (default is $HOME/.cobra.yaml) + -h, --help help for cobra + -l, --license string name of license for the project + --viper use Viper for configuration (default true) + + Use "cobra [command] --help" for more information about a command. ### Defining your own usage You can provide your own usage function or template for Cobra to use. - -The default usage function is: - -```go -return func(c *Command) error { - err := tmpl(c.Out(), c.UsageTemplate(), c) - return err -} -``` - Like help, the function and template are overridable through public methods: ```go -command.SetUsageFunc(f func(*Command) error) - -command.SetUsageTemplate(s string) +cmd.SetUsageFunc(f func(*Command) error) +cmd.SetUsageTemplate(s string) ``` -## PreRun or PostRun Hooks +## PreRun and PostRun Hooks It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherited by children if they do not declare their own. These functions are run in the following order: @@ -815,51 +635,19 @@ func main() { } ``` +Output: +``` +Inside rootCmd PersistentPreRun with args: [] +Inside rootCmd PreRun with args: [] +Inside rootCmd Run with args: [] +Inside rootCmd PostRun with args: [] +Inside rootCmd PersistentPostRun with args: [] -## Alternative Error Handling - -Cobra also has functions where the return signature is an error. This allows for errors to bubble up to the top, -providing a way to handle the errors in one location. The current list of functions that return an error is: - -* PersistentPreRunE -* PreRunE -* RunE -* PostRunE -* PersistentPostRunE - -If you would like to silence the default `error` and `usage` output in favor of your own, you can set `SilenceUsage` -and `SilenceErrors` to `true` on the command. A child command respects these flags if they are set on the parent -command. - -**Example Usage using RunE:** - -```go -package main - -import ( - "errors" - "log" - - "github.com/spf13/cobra" -) - -func main() { - var rootCmd = &cobra.Command{ - Use: "hugo", - Short: "Hugo is a very fast static site generator", - Long: `A Fast and Flexible Static Site Generator built with -love by spf13 and friends in Go. -Complete documentation is available at http://hugo.spf13.com`, - RunE: func(cmd *cobra.Command, args []string) error { - // Do Stuff Here - return errors.New("some random error") - }, - } - - if err := rootCmd.Execute(); err != nil { - log.Fatal(err) - } -} +Inside rootCmd PersistentPreRun with args: [arg1 arg2] +Inside subCmd PreRun with args: [arg1 arg2] +Inside subCmd Run with args: [arg1 arg2] +Inside subCmd PostRun with args: [arg1 arg2] +Inside subCmd PersistentPostRun with args: [arg1 arg2] ``` ## Suggestions when "unknown command" happens @@ -902,41 +690,28 @@ Did you mean this? Run 'kubectl help' for usage. ``` -## Generating Markdown-formatted documentation for your command +## Generating documentation for your command -Cobra can generate a Markdown-formatted document based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Markdown Docs](doc/md_docs.md). +Cobra can generate documentation based on subcommands, flags, etc. in the following formats: -## Generating man pages for your command +- [Markdown](doc/md_docs.md) +- [ReStructured Text](doc/rest_docs.md) +- [Man Page](doc/man_docs.md) -Cobra can generate a man page based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Man Docs](doc/man_docs.md). - -## Generating bash completions for your command +## Generating bash completions Cobra can generate a bash-completion file. If you add more information to your command, these completions can be amazingly powerful and flexible. Read more about it in [Bash Completions](bash_completions.md). - -## Extensions - -Libraries for extending Cobra: - -* [cmdns](https://github.com/gosuri/cmdns): Enables name spacing a command's immediate children. It provides an alternative way to structure subcommands, similar to `heroku apps:create` and `ovrclk clusters:launch`. - -## Contributing +# Contributing 1. Fork it -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Commit your changes (`git commit -am 'Add some feature'`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create new Pull Request - -## Contributors - -Names in no particular order: - -* [spf13](https://github.com/spf13), -[eparis](https://github.com/eparis), -[bep](https://github.com/bep), and many more! - -## License +2. Download your fork to your PC (`git clone https://github.com/your_username/cobra && cd cobra`) +3. Create your feature branch (`git checkout -b my-new-feature`) +4. Make changes and add them (`git add .`) +5. Commit your changes (`git commit -m 'Add some feature'`) +6. Push to the branch (`git push origin my-new-feature`) +7. Create new pull request + +# License Cobra is released under the Apache 2.0 license. See [LICENSE.txt](https://github.com/spf13/cobra/blob/master/LICENSE.txt) diff --git a/vendor/github.com/spf13/cobra/args.go b/vendor/github.com/spf13/cobra/args.go index 94a6ca273..a5d8a9273 100644 --- a/vendor/github.com/spf13/cobra/args.go +++ b/vendor/github.com/spf13/cobra/args.go @@ -16,14 +16,14 @@ func legacyArgs(cmd *Command, args []string) error { return nil } - // root command with subcommands, do subcommand checking + // root command with subcommands, do subcommand checking. if !cmd.HasParent() && len(args) > 0 { return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0])) } return nil } -// NoArgs returns an error if any args are included +// NoArgs returns an error if any args are included. func NoArgs(cmd *Command, args []string) error { if len(args) > 0 { return fmt.Errorf("unknown command %q for %q", args[0], cmd.CommandPath()) @@ -31,7 +31,7 @@ func NoArgs(cmd *Command, args []string) error { return nil } -// OnlyValidArgs returns an error if any args are not in the list of ValidArgs +// OnlyValidArgs returns an error if any args are not in the list of ValidArgs. func OnlyValidArgs(cmd *Command, args []string) error { if len(cmd.ValidArgs) > 0 { for _, v := range args { @@ -43,21 +43,12 @@ func OnlyValidArgs(cmd *Command, args []string) error { return nil } -func stringInSlice(a string, list []string) bool { - for _, b := range list { - if b == a { - return true - } - } - return false -} - -// ArbitraryArgs never returns an error +// ArbitraryArgs never returns an error. func ArbitraryArgs(cmd *Command, args []string) error { return nil } -// MinimumNArgs returns an error if there is not at least N args +// MinimumNArgs returns an error if there is not at least N args. func MinimumNArgs(n int) PositionalArgs { return func(cmd *Command, args []string) error { if len(args) < n { @@ -67,7 +58,7 @@ func MinimumNArgs(n int) PositionalArgs { } } -// MaximumNArgs returns an error if there are more than N args +// MaximumNArgs returns an error if there are more than N args. func MaximumNArgs(n int) PositionalArgs { return func(cmd *Command, args []string) error { if len(args) > n { @@ -77,7 +68,7 @@ func MaximumNArgs(n int) PositionalArgs { } } -// ExactArgs returns an error if there are not exactly n args +// ExactArgs returns an error if there are not exactly n args. func ExactArgs(n int) PositionalArgs { return func(cmd *Command, args []string) error { if len(args) != n { @@ -87,7 +78,7 @@ func ExactArgs(n int) PositionalArgs { } } -// RangeArgs returns an error if the number of args is not within the expected range +// RangeArgs returns an error if the number of args is not within the expected range. func RangeArgs(min int, max int) PositionalArgs { return func(cmd *Command, args []string) error { if len(args) < min || len(args) > max { diff --git a/vendor/github.com/spf13/cobra/args_test.go b/vendor/github.com/spf13/cobra/args_test.go new file mode 100644 index 000000000..d797b6f58 --- /dev/null +++ b/vendor/github.com/spf13/cobra/args_test.go @@ -0,0 +1,241 @@ +package cobra + +import ( + "strings" + "testing" +) + +func TestNoArgs(t *testing.T) { + c := &Command{Use: "c", Args: NoArgs, Run: emptyRun} + + output, err := executeCommand(c) + if output != "" { + t.Errorf("Unexpected string: %v", output) + } + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } +} + +func TestNoArgsWithArgs(t *testing.T) { + c := &Command{Use: "c", Args: NoArgs, Run: emptyRun} + + _, err := executeCommand(c, "illegal") + if err == nil { + t.Fatal("Expected an error") + } + + got := err.Error() + expected := `unknown command "illegal" for "c"` + if got != expected { + t.Errorf("Expected: %q, got: %q", expected, got) + } +} + +func TestOnlyValidArgs(t *testing.T) { + c := &Command{ + Use: "c", + Args: OnlyValidArgs, + ValidArgs: []string{"one", "two"}, + Run: emptyRun, + } + + output, err := executeCommand(c, "one", "two") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } +} + +func TestOnlyValidArgsWithInvalidArgs(t *testing.T) { + c := &Command{ + Use: "c", + Args: OnlyValidArgs, + ValidArgs: []string{"one", "two"}, + Run: emptyRun, + } + + _, err := executeCommand(c, "three") + if err == nil { + t.Fatal("Expected an error") + } + + got := err.Error() + expected := `invalid argument "three" for "c"` + if got != expected { + t.Errorf("Expected: %q, got: %q", expected, got) + } +} + +func TestArbitraryArgs(t *testing.T) { + c := &Command{Use: "c", Args: ArbitraryArgs, Run: emptyRun} + output, err := executeCommand(c, "a", "b") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } +} + +func TestMinimumNArgs(t *testing.T) { + c := &Command{Use: "c", Args: MinimumNArgs(2), Run: emptyRun} + output, err := executeCommand(c, "a", "b", "c") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } +} + +func TestMinimumNArgsWithLessArgs(t *testing.T) { + c := &Command{Use: "c", Args: MinimumNArgs(2), Run: emptyRun} + _, err := executeCommand(c, "a") + + if err == nil { + t.Fatal("Expected an error") + } + + got := err.Error() + expected := "requires at least 2 arg(s), only received 1" + if got != expected { + t.Fatalf("Expected %q, got %q", expected, got) + } +} + +func TestMaximumNArgs(t *testing.T) { + c := &Command{Use: "c", Args: MaximumNArgs(3), Run: emptyRun} + output, err := executeCommand(c, "a", "b") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } +} + +func TestMaximumNArgsWithMoreArgs(t *testing.T) { + c := &Command{Use: "c", Args: MaximumNArgs(2), Run: emptyRun} + _, err := executeCommand(c, "a", "b", "c") + + if err == nil { + t.Fatal("Expected an error") + } + + got := err.Error() + expected := "accepts at most 2 arg(s), received 3" + if got != expected { + t.Fatalf("Expected %q, got %q", expected, got) + } +} + +func TestExactArgs(t *testing.T) { + c := &Command{Use: "c", Args: ExactArgs(3), Run: emptyRun} + output, err := executeCommand(c, "a", "b", "c") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } +} + +func TestExactArgsWithInvalidCount(t *testing.T) { + c := &Command{Use: "c", Args: ExactArgs(2), Run: emptyRun} + _, err := executeCommand(c, "a", "b", "c") + + if err == nil { + t.Fatal("Expected an error") + } + + got := err.Error() + expected := "accepts 2 arg(s), received 3" + if got != expected { + t.Fatalf("Expected %q, got %q", expected, got) + } +} + +func TestRangeArgs(t *testing.T) { + c := &Command{Use: "c", Args: RangeArgs(2, 4), Run: emptyRun} + output, err := executeCommand(c, "a", "b", "c") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } +} + +func TestRangeArgsWithInvalidCount(t *testing.T) { + c := &Command{Use: "c", Args: RangeArgs(2, 4), Run: emptyRun} + _, err := executeCommand(c, "a") + + if err == nil { + t.Fatal("Expected an error") + } + + got := err.Error() + expected := "accepts between 2 and 4 arg(s), received 1" + if got != expected { + t.Fatalf("Expected %q, got %q", expected, got) + } +} + +func TestRootTakesNoArgs(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + childCmd := &Command{Use: "child", Run: emptyRun} + rootCmd.AddCommand(childCmd) + + _, err := executeCommand(rootCmd, "illegal", "args") + if err == nil { + t.Fatal("Expected an error") + } + + got := err.Error() + expected := `unknown command "illegal" for "root"` + if !strings.Contains(got, expected) { + t.Errorf("expected %q, got %q", expected, got) + } +} + +func TestRootTakesArgs(t *testing.T) { + rootCmd := &Command{Use: "root", Args: ArbitraryArgs, Run: emptyRun} + childCmd := &Command{Use: "child", Run: emptyRun} + rootCmd.AddCommand(childCmd) + + _, err := executeCommand(rootCmd, "legal", "args") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } +} + +func TestChildTakesNoArgs(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + childCmd := &Command{Use: "child", Args: NoArgs, Run: emptyRun} + rootCmd.AddCommand(childCmd) + + _, err := executeCommand(rootCmd, "child", "illegal", "args") + if err == nil { + t.Fatal("Expected an error") + } + + got := err.Error() + expected := `unknown command "illegal" for "root child"` + if !strings.Contains(got, expected) { + t.Errorf("expected %q, got %q", expected, got) + } +} + +func TestChildTakesArgs(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + childCmd := &Command{Use: "child", Args: ArbitraryArgs, Run: emptyRun} + rootCmd.AddCommand(childCmd) + + _, err := executeCommand(rootCmd, "child", "legal", "args") + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } +} diff --git a/vendor/github.com/spf13/cobra/bash_completions.go b/vendor/github.com/spf13/cobra/bash_completions.go index e402065dd..c19fe7a06 100644 --- a/vendor/github.com/spf13/cobra/bash_completions.go +++ b/vendor/github.com/spf13/cobra/bash_completions.go @@ -92,7 +92,7 @@ __handle_reply() cur="${cur#*=}" ${flags_completion[${index}]} if [ -n "${ZSH_VERSION}" ]; then - # zfs completion needs --flag= prefix + # zsh completion needs --flag= prefix eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" fi fi diff --git a/vendor/github.com/spf13/cobra/bash_completions_test.go b/vendor/github.com/spf13/cobra/bash_completions_test.go index a3b13a32a..a0da87148 100644 --- a/vendor/github.com/spf13/cobra/bash_completions_test.go +++ b/vendor/github.com/spf13/cobra/bash_completions_test.go @@ -10,13 +10,13 @@ import ( func checkOmit(t *testing.T, found, unexpected string) { if strings.Contains(found, unexpected) { - t.Errorf("Unexpected response.\nGot: %q\nBut should not have!\n", unexpected) + t.Errorf("Got: %q\nBut should not have!\n", unexpected) } } func check(t *testing.T, found, expected string) { if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) + t.Errorf("Expecting to contain: \n %q\nGot:\n %q\n", expected, found) } } @@ -33,162 +33,164 @@ func runShellCheck(s string) error { return err } go func() { - defer stdin.Close() stdin.Write([]byte(s)) + stdin.Close() }() return cmd.Run() } // World worst custom function, just keep telling you to enter hello! -const ( - bashCompletionFunc = `__custom_func() { -COMPREPLY=( "hello" ) +const bashCompletionFunc = `__custom_func() { + COMPREPLY=( "hello" ) } ` -) func TestBashCompletions(t *testing.T) { - c := initializeWithRootCmd() - cmdEcho.AddCommand(cmdTimes) - c.AddCommand(cmdEcho, cmdPrint, cmdDeprecated, cmdColon) - - // custom completion function - c.BashCompletionFunction = bashCompletionFunc - - // required flag - c.MarkFlagRequired("introot") - - // valid nouns - validArgs := []string{"pod", "node", "service", "replicationcontroller"} - c.ValidArgs = validArgs - - // noun aliases - argAliases := []string{"pods", "nodes", "services", "replicationcontrollers", "po", "no", "svc", "rc"} - c.ArgAliases = argAliases - - // filename - var flagval string - c.Flags().StringVar(&flagval, "filename", "", "Enter a filename") - c.MarkFlagFilename("filename", "json", "yaml", "yml") - - // persistent filename - var flagvalPersistent string - c.PersistentFlags().StringVar(&flagvalPersistent, "persistent-filename", "", "Enter a filename") - c.MarkPersistentFlagFilename("persistent-filename") - c.MarkPersistentFlagRequired("persistent-filename") - - // filename extensions - var flagvalExt string - c.Flags().StringVar(&flagvalExt, "filename-ext", "", "Enter a filename (extension limited)") - c.MarkFlagFilename("filename-ext") - - // filename extensions - var flagvalCustom string - c.Flags().StringVar(&flagvalCustom, "custom", "", "Enter a filename (extension limited)") - c.MarkFlagCustom("custom", "__complete_custom") - - // subdirectories in a given directory - var flagvalTheme string - c.Flags().StringVar(&flagvalTheme, "theme", "", "theme to use (located in /themes/THEMENAME/)") - c.Flags().SetAnnotation("theme", BashCompSubdirsInDir, []string{"themes"}) - - out := new(bytes.Buffer) - c.GenBashCompletion(out) - str := out.String() - - check(t, str, "_cobra-test") - check(t, str, "_cobra-test_echo") - check(t, str, "_cobra-test_echo_times") - check(t, str, "_cobra-test_print") - check(t, str, "_cobra-test_cmd__colon") + rootCmd := &Command{ + Use: "root", + ArgAliases: []string{"pods", "nodes", "services", "replicationcontrollers", "po", "no", "svc", "rc"}, + ValidArgs: []string{"pod", "node", "service", "replicationcontroller"}, + BashCompletionFunction: bashCompletionFunc, + Run: emptyRun, + } + rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot") + rootCmd.MarkFlagRequired("introot") + + // Filename. + rootCmd.Flags().String("filename", "", "Enter a filename") + rootCmd.MarkFlagFilename("filename", "json", "yaml", "yml") + + // Persistent filename. + rootCmd.PersistentFlags().String("persistent-filename", "", "Enter a filename") + rootCmd.MarkPersistentFlagFilename("persistent-filename") + rootCmd.MarkPersistentFlagRequired("persistent-filename") + + // Filename extensions. + rootCmd.Flags().String("filename-ext", "", "Enter a filename (extension limited)") + rootCmd.MarkFlagFilename("filename-ext") + rootCmd.Flags().String("custom", "", "Enter a filename (extension limited)") + rootCmd.MarkFlagCustom("custom", "__complete_custom") + + // Subdirectories in a given directory. + rootCmd.Flags().String("theme", "", "theme to use (located in /themes/THEMENAME/)") + rootCmd.Flags().SetAnnotation("theme", BashCompSubdirsInDir, []string{"themes"}) + + echoCmd := &Command{ + Use: "echo [string to echo]", + Aliases: []string{"say"}, + Short: "Echo anything to the screen", + Long: "an utterly useless command for testing.", + Example: "Just run cobra-test echo", + Run: emptyRun, + } + + printCmd := &Command{ + Use: "print [string to print]", + Args: MinimumNArgs(1), + Short: "Print anything to the screen", + Long: "an absolutely utterly useless command for testing.", + Run: emptyRun, + } + + deprecatedCmd := &Command{ + Use: "deprecated [can't do anything here]", + Args: NoArgs, + Short: "A command which is deprecated", + Long: "an absolutely utterly useless command for testing deprecation!.", + Deprecated: "Please use echo instead", + Run: emptyRun, + } + + colonCmd := &Command{ + Use: "cmd:colon", + Run: emptyRun, + } + + timesCmd := &Command{ + Use: "times [# times] [string to echo]", + SuggestFor: []string{"counts"}, + Args: OnlyValidArgs, + ValidArgs: []string{"one", "two", "three", "four"}, + Short: "Echo anything to the screen more times", + Long: "a slightly useless command for testing.", + Run: emptyRun, + } + + echoCmd.AddCommand(timesCmd) + rootCmd.AddCommand(echoCmd, printCmd, deprecatedCmd, colonCmd) + + buf := new(bytes.Buffer) + rootCmd.GenBashCompletion(buf) + output := buf.String() + + check(t, output, "_root") + check(t, output, "_root_echo") + check(t, output, "_root_echo_times") + check(t, output, "_root_print") + check(t, output, "_root_cmd__colon") // check for required flags - check(t, str, `must_have_one_flag+=("--introot=")`) - check(t, str, `must_have_one_flag+=("--persistent-filename=")`) + check(t, output, `must_have_one_flag+=("--introot=")`) + check(t, output, `must_have_one_flag+=("--persistent-filename=")`) // check for custom completion function - check(t, str, `COMPREPLY=( "hello" )`) + check(t, output, `COMPREPLY=( "hello" )`) // check for required nouns - check(t, str, `must_have_one_noun+=("pod")`) + check(t, output, `must_have_one_noun+=("pod")`) // check for noun aliases - check(t, str, `noun_aliases+=("pods")`) - check(t, str, `noun_aliases+=("rc")`) - checkOmit(t, str, `must_have_one_noun+=("pods")`) + check(t, output, `noun_aliases+=("pods")`) + check(t, output, `noun_aliases+=("rc")`) + checkOmit(t, output, `must_have_one_noun+=("pods")`) // check for filename extension flags - check(t, str, `flags_completion+=("_filedir")`) + check(t, output, `flags_completion+=("_filedir")`) // check for filename extension flags - check(t, str, `must_have_one_noun+=("three")`) + check(t, output, `must_have_one_noun+=("three")`) // check for filename extension flags - check(t, str, `flags_completion+=("__handle_filename_extension_flag json|yaml|yml")`) + check(t, output, `flags_completion+=("__handle_filename_extension_flag json|yaml|yml")`) // check for custom flags - check(t, str, `flags_completion+=("__complete_custom")`) + check(t, output, `flags_completion+=("__complete_custom")`) // check for subdirs_in_dir flags - check(t, str, `flags_completion+=("__handle_subdirs_in_dir_flag themes")`) + check(t, output, `flags_completion+=("__handle_subdirs_in_dir_flag themes")`) - checkOmit(t, str, cmdDeprecated.Name()) + checkOmit(t, output, deprecatedCmd.Name()) - // if available, run shellcheck against the script + // If available, run shellcheck against the script. if err := exec.Command("which", "shellcheck").Run(); err != nil { return } - err := runShellCheck(str) - if err != nil { + if err := runShellCheck(output); err != nil { t.Fatalf("shellcheck failed: %v", err) } } func TestBashCompletionHiddenFlag(t *testing.T) { - var cmdTrue = &Command{ - Use: "does nothing", - Run: func(cmd *Command, args []string) {}, - } + c := &Command{Use: "c", Run: emptyRun} - const flagName = "hidden-foo-bar-baz" + const flagName = "hiddenFlag" + c.Flags().Bool(flagName, false, "") + c.Flags().MarkHidden(flagName) - var flagValue bool - cmdTrue.Flags().BoolVar(&flagValue, flagName, false, "hidden flag") - cmdTrue.Flags().MarkHidden(flagName) + buf := new(bytes.Buffer) + c.GenBashCompletion(buf) + output := buf.String() - out := new(bytes.Buffer) - cmdTrue.GenBashCompletion(out) - bashCompletion := out.String() - if strings.Contains(bashCompletion, flagName) { - t.Errorf("expected completion to not include %q flag: Got %v", flagName, bashCompletion) + if strings.Contains(output, flagName) { + t.Errorf("Expected completion to not include %q flag: Got %v", flagName, output) } } func TestBashCompletionDeprecatedFlag(t *testing.T) { - var cmdTrue = &Command{ - Use: "does nothing", - Run: func(cmd *Command, args []string) {}, - } - - const flagName = "deprecated-foo-bar-baz" - - var flagValue bool - cmdTrue.Flags().BoolVar(&flagValue, flagName, false, "hidden flag") - cmdTrue.Flags().MarkDeprecated(flagName, "use --does-not-exist instead") - - out := new(bytes.Buffer) - cmdTrue.GenBashCompletion(out) - bashCompletion := out.String() - if strings.Contains(bashCompletion, flagName) { - t.Errorf("expected completion to not include %q flag: Got %v", flagName, bashCompletion) - } -} + c := &Command{Use: "c", Run: emptyRun} -func BenchmarkBashCompletion(b *testing.B) { - c := initializeWithRootCmd() - cmdEcho.AddCommand(cmdTimes) - c.AddCommand(cmdEcho, cmdPrint, cmdDeprecated, cmdColon) + const flagName = "deprecated-flag" + c.Flags().Bool(flagName, false, "") + c.Flags().MarkDeprecated(flagName, "use --not-deprecated instead") buf := new(bytes.Buffer) + c.GenBashCompletion(buf) + output := buf.String() - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf.Reset() - if err := c.GenBashCompletion(buf); err != nil { - b.Fatal(err) - } + if strings.Contains(output, flagName) { + t.Errorf("expected completion to not include %q flag: Got %v", flagName, output) } } diff --git a/vendor/github.com/spf13/cobra/cobra.go b/vendor/github.com/spf13/cobra/cobra.go index 8928cefc2..e4b910c5d 100644 --- a/vendor/github.com/spf13/cobra/cobra.go +++ b/vendor/github.com/spf13/cobra/cobra.go @@ -188,3 +188,12 @@ func ld(s, t string, ignoreCase bool) int { } return d[len(s)][len(t)] } + +func stringInSlice(a string, list []string) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} diff --git a/vendor/github.com/spf13/cobra/cobra/README.md b/vendor/github.com/spf13/cobra/cobra/README.md new file mode 100644 index 000000000..6054f95c5 --- /dev/null +++ b/vendor/github.com/spf13/cobra/cobra/README.md @@ -0,0 +1,94 @@ +# Cobra Generator + +Cobra provides its own program that will create your application and add any +commands you want. It's the easiest way to incorporate Cobra into your application. + +In order to use the cobra command, compile it using the following command: + + go get github.com/spf13/cobra/cobra + +This will create the cobra executable under your `$GOPATH/bin` directory. + +### cobra init + +The `cobra init [app]` command will create your initial application code +for you. It is a very powerful application that will populate your program with +the right structure so you can immediately enjoy all the benefits of Cobra. It +will also automatically apply the license you specify to your application. + +Cobra init is pretty smart. You can provide it a full path, or simply a path +similar to what is expected in the import. + +``` +cobra init github.com/spf13/newApp +``` + +### cobra add + +Once an application is initialized, Cobra can create additional commands for you. +Let's say you created an app and you wanted the following commands for it: + +* app serve +* app config +* app config create + +In your project directory (where your main.go file is) you would run the following: + +``` +cobra add serve +cobra add config +cobra add create -p 'configCmd' +``` + +*Note: Use camelCase (not snake_case/snake-case) for command names. +Otherwise, you will encounter errors. +For example, `cobra add add-user` is incorrect, but `cobra add addUser` is valid.* + +Once you have run these three commands you would have an app structure similar to +the following: + +``` + ▾ app/ + ▾ cmd/ + serve.go + config.go + create.go + main.go +``` + +At this point you can run `go run main.go` and it would run your app. `go run +main.go serve`, `go run main.go config`, `go run main.go config create` along +with `go run main.go help serve`, etc. would all work. + +Obviously you haven't added your own code to these yet. The commands are ready +for you to give them their tasks. Have fun! + +### Configuring the cobra generator + +The Cobra generator will be easier to use if you provide a simple configuration +file which will help you eliminate providing a bunch of repeated information in +flags over and over. + +An example ~/.cobra.yaml file: + +```yaml +author: Steve Francia <spf@spf13.com> +license: MIT +``` + +You can specify no license by setting `license` to `none` or you can specify +a custom license: + +```yaml +license: + header: This file is part of {{ .appName }}. + text: | + {{ .copyright }} + + This is my license. There are many like it, but this one is mine. + My license is my best friend. It is my life. I must master it as I must + master my life. +``` + +You can also use built-in licenses. For example, **GPLv2**, **GPLv3**, **LGPL**, +**AGPL**, **MIT**, **2-Clause BSD** or **3-Clause BSD**. diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/helpers.go b/vendor/github.com/spf13/cobra/cobra/cmd/helpers.go index c5e261ce3..e5b37ec72 100644 --- a/vendor/github.com/spf13/cobra/cobra/cmd/helpers.go +++ b/vendor/github.com/spf13/cobra/cobra/cmd/helpers.go @@ -18,6 +18,7 @@ import ( "fmt" "io" "os" + "os/exec" "path/filepath" "strings" "text/template" @@ -31,7 +32,27 @@ func init() { envGoPath := os.Getenv("GOPATH") goPaths := filepath.SplitList(envGoPath) if len(goPaths) == 0 { - er("$GOPATH is not set") + // Adapted from https://github.com/Masterminds/glide/pull/798/files. + // As of Go 1.8 the GOPATH is no longer required to be set. Instead there + // is a default value. If there is no GOPATH check for the default value. + // Note, checking the GOPATH first to avoid invoking the go toolchain if + // possible. + + goExecutable := os.Getenv("COBRA_GO_EXECUTABLE") + if len(goExecutable) <= 0 { + goExecutable = "go" + } + + out, err := exec.Command(goExecutable, "env", "GOPATH").Output() + if err != nil { + er(err) + } + + toolchainGoPath := strings.TrimSpace(string(out)) + goPaths = filepath.SplitList(toolchainGoPath) + if len(goPaths) == 0 { + er("$GOPATH is not set") + } } srcPaths = make([]string, 0, len(goPaths)) for _, goPath := range goPaths { diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/licenses.go b/vendor/github.com/spf13/cobra/cobra/cmd/licenses.go index cf2a6b7af..a070134dd 100644 --- a/vendor/github.com/spf13/cobra/cobra/cmd/licenses.go +++ b/vendor/github.com/spf13/cobra/cobra/cmd/licenses.go @@ -63,7 +63,7 @@ func getLicense() License { // If user wants to have custom license, use that. if viper.IsSet("license.header") || viper.IsSet("license.text") { return License{Header: viper.GetString("license.header"), - Text: "license.text"} + Text: viper.GetString("license.text")} } // If user wants to have built-in license, use that. diff --git a/vendor/github.com/spf13/cobra/cobra_test.go b/vendor/github.com/spf13/cobra/cobra_test.go index d5df951e6..0d1755bdb 100644 --- a/vendor/github.com/spf13/cobra/cobra_test.go +++ b/vendor/github.com/spf13/cobra/cobra_test.go @@ -1,1200 +1,10 @@ package cobra import ( - "bytes" - "fmt" - "os" - "reflect" - "runtime" - "strings" "testing" "text/template" - - "github.com/spf13/pflag" ) -var tp, te, tt, tr []string -var rootPersPre, echoPre, echoPersPre, timesPersPre []string -var flagb1, flagb2, flagb3, flagbr, flagbp bool -var flags1, flags2a, flags2b, flags3, outs string -var flagi1, flagi2, flagi3, flagi4, flagir int -var rootcalled bool -var versionUsed int - -const strtwoParentHelp = "help message for parent flag strtwo" -const strtwoChildHelp = "help message for child flag strtwo" - -var cmdHidden = &Command{ - Use: "hide [secret string to print]", - Short: "Print anything to screen (if command is known)", - Long: `an absolutely utterly useless command for testing.`, - Run: func(cmd *Command, args []string) { - outs = "hidden" - }, - Hidden: true, -} - -var cmdPrint = &Command{ - Use: "print [string to print]", - Args: MinimumNArgs(1), - Short: "Print anything to the screen", - Long: `an absolutely utterly useless command for testing.`, - Run: func(cmd *Command, args []string) { - tp = args - }, -} - -var cmdEcho = &Command{ - Use: "echo [string to echo]", - Aliases: []string{"say"}, - Short: "Echo anything to the screen", - Long: `an utterly useless command for testing.`, - Example: "Just run cobra-test echo", - PersistentPreRun: func(cmd *Command, args []string) { - echoPersPre = args - }, - PreRun: func(cmd *Command, args []string) { - echoPre = args - }, - Run: func(cmd *Command, args []string) { - te = args - }, -} - -var cmdEchoSub = &Command{ - Use: "echosub [string to print]", - Short: "second sub command for echo", - Long: `an absolutely utterly useless command for testing gendocs!.`, - Run: func(cmd *Command, args []string) { - }, -} - -var cmdDeprecated = &Command{ - Use: "deprecated [can't do anything here]", - Short: "A command which is deprecated", - Long: `an absolutely utterly useless command for testing deprecation!.`, - Deprecated: "Please use echo instead", - Run: func(cmd *Command, args []string) { - }, - Args: NoArgs, -} - -var cmdTimes = &Command{ - Use: "times [# times] [string to echo]", - SuggestFor: []string{"counts"}, - Short: "Echo anything to the screen more times", - Long: `a slightly useless command for testing.`, - PersistentPreRun: func(cmd *Command, args []string) { - timesPersPre = args - }, - Run: func(cmd *Command, args []string) { - tt = args - }, - Args: OnlyValidArgs, - ValidArgs: []string{"one", "two", "three", "four"}, -} - -var cmdRootNoRun = &Command{ - Use: "cobra-test", - Short: "The root can run its own function", - Long: "The root description for help", - PersistentPreRun: func(cmd *Command, args []string) { - rootPersPre = args - }, -} - -var cmdRootSameName = &Command{ - Use: "print", - Short: "Root with the same name as a subcommand", - Long: "The root description for help", -} - -var cmdRootTakesArgs = &Command{ - Use: "root-with-args [random args]", - Short: "The root can run it's own function and takes args!", - Long: "The root description for help, and some args", - Run: func(cmd *Command, args []string) { - tr = args - }, - Args: ArbitraryArgs, -} - -var cmdRootWithRun = &Command{ - Use: "cobra-test", - Short: "The root can run its own function", - Long: "The root description for help", - Run: func(cmd *Command, args []string) { - tr = args - rootcalled = true - }, -} - -var cmdSubNoRun = &Command{ - Use: "subnorun", - Short: "A subcommand without a Run function", - Long: "A long output about a subcommand without a Run function", -} - -var cmdCustomFlags = &Command{ - Use: "customflags [flags] -- REMOTE_COMMAND", - Short: "A command that expects flags in a custom location", - Long: "A long output about a command that expects flags in a custom location", - Run: func(cmd *Command, args []string) { - }, -} - -var cmdVersion1 = &Command{ - Use: "version", - Short: "Print the version number", - Long: `First version of the version command`, - Run: func(cmd *Command, args []string) { - versionUsed = 1 - }, -} - -var cmdVersion2 = &Command{ - Use: "version", - Short: "Print the version number", - Long: `Second version of the version command`, - Run: func(cmd *Command, args []string) { - versionUsed = 2 - }, -} - -var cmdColon = &Command{ - Use: "cmd:colon", - Run: func(cmd *Command, args []string) { - }, -} - -func flagInit() { - cmdEcho.ResetFlags() - cmdPrint.ResetFlags() - cmdTimes.ResetFlags() - cmdRootNoRun.ResetFlags() - cmdRootSameName.ResetFlags() - cmdRootWithRun.ResetFlags() - cmdSubNoRun.ResetFlags() - cmdCustomFlags.ResetFlags() - cmdVersion1.ResetFlags() - cmdVersion2.ResetFlags() - - cmdRootNoRun.PersistentFlags().StringVarP(&flags2a, "strtwo", "t", "two", strtwoParentHelp) - cmdCustomFlags.Flags().IntVar(&flagi4, "intfour", 456, "help message for flag intfour") - cmdEcho.Flags().BoolVarP(&flagb1, "boolone", "b", true, "help message for flag boolone") - cmdEcho.Flags().IntVarP(&flagi1, "intone", "i", 123, "help message for flag intone") - cmdEcho.PersistentFlags().BoolVarP(&flagbp, "persistentbool", "p", false, "help message for flag persistentbool") - cmdEcho.PersistentFlags().StringVarP(&flags1, "strone", "s", "one", "help message for flag strone") - cmdPrint.Flags().IntVarP(&flagi3, "intthree", "i", 345, "help message for flag intthree") - cmdTimes.Flags().BoolVarP(&flagb2, "booltwo", "c", false, "help message for flag booltwo") - cmdTimes.Flags().IntVarP(&flagi2, "inttwo", "j", 234, "help message for flag inttwo") - cmdTimes.Flags().StringVarP(&flags2b, "strtwo", "t", "2", strtwoChildHelp) - cmdTimes.PersistentFlags().StringVarP(&flags2b, "strtwo", "t", "2", strtwoChildHelp) - cmdPrint.Flags().BoolVarP(&flagb3, "boolthree", "b", true, "help message for flag boolthree") - cmdPrint.PersistentFlags().StringVarP(&flags3, "strthree", "s", "three", "help message for flag strthree") -} - -func commandInit() { - cmdEcho.ResetCommands() - cmdPrint.ResetCommands() - cmdTimes.ResetCommands() - cmdRootNoRun.ResetCommands() - cmdRootSameName.ResetCommands() - cmdRootWithRun.ResetCommands() - cmdSubNoRun.ResetCommands() - cmdCustomFlags.ResetCommands() -} - -func initialize() *Command { - tt, tp, te = nil, nil, nil - rootPersPre, echoPre, echoPersPre, timesPersPre = nil, nil, nil, nil - - var c = cmdRootNoRun - flagInit() - commandInit() - return c -} - -func initializeWithSameName() *Command { - tt, tp, te = nil, nil, nil - rootPersPre, echoPre, echoPersPre, timesPersPre = nil, nil, nil, nil - var c = cmdRootSameName - flagInit() - commandInit() - return c -} - -func initializeWithRootCmd() *Command { - cmdRootWithRun.ResetCommands() - tt, tp, te, tr, rootcalled = nil, nil, nil, nil, false - flagInit() - cmdRootWithRun.Flags().BoolVarP(&flagbr, "boolroot", "b", false, "help message for flag boolroot") - cmdRootWithRun.Flags().IntVarP(&flagir, "introot", "i", 321, "help message for flag introot") - commandInit() - return cmdRootWithRun -} - -type resulter struct { - Error error - Output string - Command *Command -} - -func fullSetupTest(args ...string) resulter { - c := initializeWithRootCmd() - - return fullTester(c, args...) -} - -func noRRSetupTestSilenced(args ...string) resulter { - c := initialize() - c.SilenceErrors = true - c.SilenceUsage = true - return fullTester(c, args...) -} - -func noRRSetupTest(args ...string) resulter { - c := initialize() - - return fullTester(c, args...) -} - -func rootOnlySetupTest(args ...string) resulter { - c := initializeWithRootCmd() - - return simpleTester(c, args...) -} - -func simpleTester(c *Command, args ...string) resulter { - buf := new(bytes.Buffer) - // Testing flag with invalid input - c.SetOutput(buf) - c.SetArgs(args) - - err := c.Execute() - output := buf.String() - - return resulter{err, output, c} -} - -func simpleTesterC(c *Command, args ...string) resulter { - buf := new(bytes.Buffer) - // Testing flag with invalid input - c.SetOutput(buf) - c.SetArgs(args) - - cmd, err := c.ExecuteC() - output := buf.String() - - return resulter{err, output, cmd} -} - -func fullTester(c *Command, args ...string) resulter { - buf := new(bytes.Buffer) - // Testing flag with invalid input - c.SetOutput(buf) - cmdEcho.AddCommand(cmdTimes) - c.AddCommand(cmdPrint, cmdEcho, cmdSubNoRun, cmdCustomFlags, cmdDeprecated) - c.SetArgs(args) - - err := c.Execute() - output := buf.String() - - return resulter{err, output, c} -} - -func logErr(t *testing.T, found, expected string) { - out := new(bytes.Buffer) - - _, _, line, ok := runtime.Caller(2) - if ok { - fmt.Fprintf(out, "Line: %d ", line) - } - fmt.Fprintf(out, "Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - t.Errorf(out.String()) -} - -func checkStringContains(t *testing.T, found, expected string) { - if !strings.Contains(found, expected) { - logErr(t, found, expected) - } -} - -func checkResultContains(t *testing.T, x resulter, check string) { - checkStringContains(t, x.Output, check) -} - -func checkStringOmits(t *testing.T, found, expected string) { - if strings.Contains(found, expected) { - logErr(t, found, expected) - } -} - -func checkResultOmits(t *testing.T, x resulter, check string) { - checkStringOmits(t, x.Output, check) -} - -func checkOutputContains(t *testing.T, c *Command, check string) { - buf := new(bytes.Buffer) - c.SetOutput(buf) - c.Execute() - - if !strings.Contains(buf.String(), check) { - logErr(t, buf.String(), check) - } -} - -func TestSingleCommand(t *testing.T) { - noRRSetupTest("print", "one", "two") - - if te != nil || tt != nil { - t.Error("Wrong command called") - } - if tp == nil { - t.Error("Wrong command called") - } - if strings.Join(tp, " ") != "one two" { - t.Error("Command didn't parse correctly") - } -} - -func TestChildCommand(t *testing.T) { - noRRSetupTest("echo", "times", "one", "two") - - if te != nil || tp != nil { - t.Error("Wrong command called") - } - if tt == nil { - t.Error("Wrong command called") - } - if strings.Join(tt, " ") != "one two" { - t.Error("Command didn't parse correctly") - } -} - -func TestCommandAlias(t *testing.T) { - noRRSetupTest("say", "times", "one", "two") - - if te != nil || tp != nil { - t.Error("Wrong command called") - } - if tt == nil { - t.Error("Wrong command called") - } - if strings.Join(tt, " ") != "one two" { - t.Error("Command didn't parse correctly") - } -} - -func TestPrefixMatching(t *testing.T) { - EnablePrefixMatching = true - noRRSetupTest("ech", "times", "one", "two") - - if te != nil || tp != nil { - t.Error("Wrong command called") - } - if tt == nil { - t.Error("Wrong command called") - } - if strings.Join(tt, " ") != "one two" { - t.Error("Command didn't parse correctly") - } - - EnablePrefixMatching = false -} - -func TestNoPrefixMatching(t *testing.T) { - EnablePrefixMatching = false - - noRRSetupTest("ech", "times", "one", "two") - - if !(tt == nil && te == nil && tp == nil) { - t.Error("Wrong command called") - } -} - -func TestAliasPrefixMatching(t *testing.T) { - EnablePrefixMatching = true - noRRSetupTest("sa", "times", "one", "two") - - if te != nil || tp != nil { - t.Error("Wrong command called") - } - if tt == nil { - t.Error("Wrong command called") - } - if strings.Join(tt, " ") != "one two" { - t.Error("Command didn't parse correctly") - } - EnablePrefixMatching = false -} - -func TestChildSameName(t *testing.T) { - c := initializeWithSameName() - c.AddCommand(cmdPrint, cmdEcho) - c.SetArgs([]string{"print", "one", "two"}) - c.Execute() - - if te != nil || tt != nil { - t.Error("Wrong command called") - } - if tp == nil { - t.Error("Wrong command called") - } - if strings.Join(tp, " ") != "one two" { - t.Error("Command didn't parse correctly") - } -} - -func TestGrandChildSameName(t *testing.T) { - c := initializeWithSameName() - cmdTimes.AddCommand(cmdPrint) - c.AddCommand(cmdTimes) - c.SetArgs([]string{"times", "print", "one", "two"}) - c.Execute() - - if te != nil || tt != nil { - t.Error("Wrong command called") - } - if tp == nil { - t.Error("Wrong command called") - } - if strings.Join(tp, " ") != "one two" { - t.Error("Command didn't parse correctly") - } -} - -func TestUsage(t *testing.T) { - x := fullSetupTest("help") - checkResultContains(t, x, cmdRootWithRun.Use+" [flags]") - x = fullSetupTest("help", "customflags") - checkResultContains(t, x, cmdCustomFlags.Use) - checkResultOmits(t, x, cmdCustomFlags.Use+" [flags]") -} - -func TestRootTakesNoArgs(t *testing.T) { - c := initializeWithSameName() - c.AddCommand(cmdPrint, cmdEcho) - result := simpleTester(c, "illegal") - - if result.Error == nil { - t.Fatal("Expected an error") - } - - expectedError := `unknown command "illegal" for "print"` - if !strings.Contains(result.Error.Error(), expectedError) { - t.Errorf("exptected %v, got %v", expectedError, result.Error.Error()) - } -} - -func TestRootTakesArgs(t *testing.T) { - c := cmdRootTakesArgs - result := simpleTester(c, "legal") - - if result.Error != nil { - t.Errorf("expected no error, but got %v", result.Error) - } -} - -func TestSubCmdTakesNoArgs(t *testing.T) { - result := fullSetupTest("deprecated", "illegal") - - if result.Error == nil { - t.Fatal("Expected an error") - } - - expectedError := `unknown command "illegal" for "cobra-test deprecated"` - if !strings.Contains(result.Error.Error(), expectedError) { - t.Errorf("expected %v, got %v", expectedError, result.Error.Error()) - } -} - -func TestSubCmdTakesArgs(t *testing.T) { - noRRSetupTest("echo", "times", "one", "two") - if strings.Join(tt, " ") != "one two" { - t.Error("Command didn't parse correctly") - } -} - -func TestCmdOnlyValidArgs(t *testing.T) { - result := noRRSetupTest("echo", "times", "one", "two", "five") - - if result.Error == nil { - t.Fatal("Expected an error") - } - - expectedError := `invalid argument "five"` - if !strings.Contains(result.Error.Error(), expectedError) { - t.Errorf("expected %v, got %v", expectedError, result.Error.Error()) - } -} - -func TestFlagLong(t *testing.T) { - noRRSetupTest("echo", "--intone=13", "something", "--", "here") - - if cmdEcho.ArgsLenAtDash() != 1 { - t.Errorf("expected argsLenAtDash: %d but got %d", 1, cmdRootNoRun.ArgsLenAtDash()) - } - if strings.Join(te, " ") != "something here" { - t.Errorf("flags didn't leave proper args remaining..%s given", te) - } - if flagi1 != 13 { - t.Errorf("int flag didn't get correct value, had %d", flagi1) - } - if flagi2 != 234 { - t.Errorf("default flag value changed, 234 expected, %d given", flagi2) - } -} - -func TestFlagShort(t *testing.T) { - noRRSetupTest("echo", "-i13", "--", "something", "here") - - if cmdEcho.ArgsLenAtDash() != 0 { - t.Errorf("expected argsLenAtDash: %d but got %d", 0, cmdRootNoRun.ArgsLenAtDash()) - } - if strings.Join(te, " ") != "something here" { - t.Errorf("flags didn't leave proper args remaining..%s given", te) - } - if flagi1 != 13 { - t.Errorf("int flag didn't get correct value, had %d", flagi1) - } - if flagi2 != 234 { - t.Errorf("default flag value changed, 234 expected, %d given", flagi2) - } - - noRRSetupTest("echo", "-i", "13", "something", "here") - - if strings.Join(te, " ") != "something here" { - t.Errorf("flags didn't leave proper args remaining..%s given", te) - } - if flagi1 != 13 { - t.Errorf("int flag didn't get correct value, had %d", flagi1) - } - if flagi2 != 234 { - t.Errorf("default flag value changed, 234 expected, %d given", flagi2) - } - - noRRSetupTest("print", "-i99", "one", "two") - - if strings.Join(tp, " ") != "one two" { - t.Errorf("flags didn't leave proper args remaining..%s given", tp) - } - if flagi3 != 99 { - t.Errorf("int flag didn't get correct value, had %d", flagi3) - } - if flagi1 != 123 { - t.Errorf("default flag value changed on different command with same shortname, 234 expected, %d given", flagi2) - } -} - -func TestChildCommandFlags(t *testing.T) { - noRRSetupTest("echo", "times", "-j", "99", "one", "two") - - if strings.Join(tt, " ") != "one two" { - t.Errorf("flags didn't leave proper args remaining..%s given", tt) - } - - // Testing with flag that shouldn't be persistent - r := noRRSetupTest("echo", "times", "-j", "99", "-i77", "one", "two") - - if r.Error == nil { - t.Errorf("invalid flag should generate error") - } - - if !strings.Contains(r.Error.Error(), "unknown shorthand") { - t.Errorf("Wrong error message displayed, \n %s", r.Error) - } - - if flagi2 != 99 { - t.Errorf("flag value should be 99, %d given", flagi2) - } - - if flagi1 != 123 { - t.Errorf("unset flag should have default value, expecting 123, given %d", flagi1) - } - - // Testing with flag only existing on child - r = noRRSetupTest("echo", "-j", "99", "-i77", "one", "two") - - if r.Error == nil { - t.Errorf("invalid flag should generate error") - } - if !strings.Contains(r.Error.Error(), "unknown shorthand flag") { - t.Errorf("Wrong error message displayed, \n %s", r.Error) - } - - // Testing with persistent flag overwritten by child - noRRSetupTest("echo", "times", "--strtwo=child", "one", "two") - - if flags2b != "child" { - t.Errorf("flag value should be child, %s given", flags2b) - } - - if flags2a != "two" { - t.Errorf("unset flag should have default value, expecting two, given %s", flags2a) - } - - // Testing flag with invalid input - r = noRRSetupTest("echo", "-i10E") - - if r.Error == nil { - t.Errorf("invalid input should generate error") - } - if !strings.Contains(r.Error.Error(), "invalid syntax") { - t.Errorf("Wrong error message displayed, \n %s", r.Error) - } -} - -func TestTrailingCommandFlags(t *testing.T) { - x := fullSetupTest("echo", "two", "-x") - - if x.Error == nil { - t.Errorf("invalid flag should generate error") - } -} - -func TestInvalidSubcommandFlags(t *testing.T) { - cmd := initializeWithRootCmd() - cmd.AddCommand(cmdTimes) - - result := simpleTester(cmd, "times", "--inttwo=2", "--badflag=bar") - // given that we are not checking here result.Error we check for - // stock usage message - checkResultContains(t, result, "cobra-test times [# times]") - if strings.Contains(result.Error.Error(), "unknown flag: --inttwo") { - t.Errorf("invalid --badflag flag shouldn't fail on 'unknown' --inttwo flag") - } - -} - -func TestSubcommandExecuteC(t *testing.T) { - cmd := initializeWithRootCmd() - double := &Command{ - Use: "double message", - Run: func(c *Command, args []string) { - msg := strings.Join(args, " ") - c.Println(msg, msg) - }, - } - - echo := &Command{ - Use: "echo message", - Run: func(c *Command, args []string) { - msg := strings.Join(args, " ") - c.Println(msg) - }, - } - - cmd.AddCommand(double, echo) - - result := simpleTesterC(cmd, "double", "hello", "world") - checkResultContains(t, result, "hello world hello world") - - if result.Command.Name() != "double" { - t.Errorf("invalid cmd returned from ExecuteC: should be 'double' but got %s", result.Command.Name()) - } - - result = simpleTesterC(cmd, "echo", "msg", "to", "be", "echoed") - checkResultContains(t, result, "msg to be echoed") - - if result.Command.Name() != "echo" { - t.Errorf("invalid cmd returned from ExecuteC: should be 'echo' but got %s", result.Command.Name()) - } -} - -func TestSubcommandArgEvaluation(t *testing.T) { - cmd := initializeWithRootCmd() - - first := &Command{ - Use: "first", - Run: func(cmd *Command, args []string) { - }, - } - cmd.AddCommand(first) - - second := &Command{ - Use: "second", - Run: func(cmd *Command, args []string) { - fmt.Fprintf(cmd.OutOrStdout(), "%v", args) - }, - } - first.AddCommand(second) - - result := simpleTester(cmd, "first", "second", "first", "third") - - expectedOutput := fmt.Sprint([]string{"first third"}) - if result.Output != expectedOutput { - t.Errorf("exptected %v, got %v", expectedOutput, result.Output) - } -} - -func TestPersistentFlags(t *testing.T) { - fullSetupTest("echo", "-s", "something", "-p", "more", "here") - - // persistentFlag should act like normal flag on its own command - if strings.Join(te, " ") != "more here" { - t.Errorf("flags didn't leave proper args remaining..%s given", te) - } - if flags1 != "something" { - t.Errorf("string flag didn't get correct value, had %v", flags1) - } - if !flagbp { - t.Errorf("persistent bool flag not parsed correctly. Expected true, had %v", flagbp) - } - - // persistentFlag should act like normal flag on its own command - fullSetupTest("echo", "times", "-s", "again", "-c", "-p", "one", "two") - - if strings.Join(tt, " ") != "one two" { - t.Errorf("flags didn't leave proper args remaining. %s given", tt) - } - - if flags1 != "again" { - t.Errorf("string flag didn't get correct value, had %v", flags1) - } - - if !flagb2 { - t.Errorf("local flag not parsed correctly. Expected true, had %v", flagb2) - } - if !flagbp { - t.Errorf("persistent bool flag not parsed correctly. Expected true, had %v", flagbp) - } -} - -func TestHelpCommand(t *testing.T) { - x := fullSetupTest("help") - checkResultContains(t, x, cmdRootWithRun.Long) - - x = fullSetupTest("help", "echo") - checkResultContains(t, x, cmdEcho.Long) - - x = fullSetupTest("help", "echo", "times") - checkResultContains(t, x, cmdTimes.Long) -} - -func TestChildCommandHelp(t *testing.T) { - c := noRRSetupTest("print", "--help") - checkResultContains(t, c, strtwoParentHelp) - r := noRRSetupTest("echo", "times", "--help") - checkResultContains(t, r, strtwoChildHelp) -} - -func TestNonRunChildHelp(t *testing.T) { - x := noRRSetupTest("subnorun") - checkResultContains(t, x, cmdSubNoRun.Long) -} - -func TestRunnableRootCommand(t *testing.T) { - x := fullSetupTest("") - - if !rootcalled { - t.Errorf("Root Function was not called\n out:%v", x.Error) - } -} - -func TestVisitParents(t *testing.T) { - c := &Command{Use: "app"} - sub := &Command{Use: "sub"} - dsub := &Command{Use: "dsub"} - sub.AddCommand(dsub) - c.AddCommand(sub) - total := 0 - add := func(x *Command) { - total++ - } - sub.VisitParents(add) - if total != 1 { - t.Errorf("Should have visited 1 parent but visited %d", total) - } - - total = 0 - dsub.VisitParents(add) - if total != 2 { - t.Errorf("Should have visited 2 parent but visited %d", total) - } - - total = 0 - c.VisitParents(add) - if total != 0 { - t.Errorf("Should have not visited any parent but visited %d", total) - } -} - -func TestRunnableRootCommandNilInput(t *testing.T) { - c := initializeWithRootCmd() - - buf := new(bytes.Buffer) - // Testing flag with invalid input - c.SetOutput(buf) - cmdEcho.AddCommand(cmdTimes) - c.AddCommand(cmdPrint, cmdEcho) - c.SetArgs([]string{}) - - err := c.Execute() - if err != nil { - t.Errorf("Execute() failed with %v", err) - } - - if !rootcalled { - t.Errorf("Root Function was not called") - } -} - -func TestRunnableRootCommandEmptyInput(t *testing.T) { - args := []string{"", "--introot=12", ""} - c := initializeWithRootCmd() - - buf := new(bytes.Buffer) - // Testing flag with invalid input - c.SetOutput(buf) - cmdEcho.AddCommand(cmdTimes) - c.AddCommand(cmdPrint, cmdEcho) - c.SetArgs(args) - - c.Execute() - - if !rootcalled { - t.Errorf("Root Function was not called.\nOutput was:\n%s\n", buf) - } -} - -func TestInvalidSubcommandWhenArgsAllowed(t *testing.T) { - fullSetupTest("echo", "invalid-sub") - - if te[0] != "invalid-sub" { - t.Errorf("Subcommand didn't work...") - } -} - -func TestRootFlags(t *testing.T) { - fullSetupTest("-i", "17", "-b") - - if !flagbr { - t.Errorf("flag value should be true, %v given", flagbr) - } - - if flagir != 17 { - t.Errorf("flag value should be 17, %d given", flagir) - } -} - -func TestRootHelp(t *testing.T) { - x := fullSetupTest("--help") - - checkResultContains(t, x, "Available Commands:") - checkResultContains(t, x, "for more information about a command") - - if strings.Contains(x.Output, "unknown flag: --help") { - t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) - } - - if strings.Contains(x.Output, cmdEcho.Use) { - t.Errorf("--help shouldn't display subcommand's usage, Got: \n %s", x.Output) - } - - x = fullSetupTest("echo", "--help") - - if strings.Contains(x.Output, cmdTimes.Use) { - t.Errorf("--help shouldn't display subsubcommand's usage, Got: \n %s", x.Output) - } - - checkResultContains(t, x, "Available Commands:") - checkResultContains(t, x, "for more information about a command") - - if strings.Contains(x.Output, "unknown flag: --help") { - t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) - } - -} - -func TestFlagAccess(t *testing.T) { - initialize() - - local := cmdTimes.LocalFlags() - inherited := cmdTimes.InheritedFlags() - - for _, f := range []string{"inttwo", "strtwo", "booltwo"} { - if local.Lookup(f) == nil { - t.Errorf("LocalFlags expected to contain %s, Got: nil", f) - } - } - if inherited.Lookup("strone") == nil { - t.Errorf("InheritedFlags expected to contain strone, Got: nil") - } - if inherited.Lookup("strtwo") != nil { - t.Errorf("InheritedFlags shouldn not contain overwritten flag strtwo") - } -} - -func TestNoNRunnableRootCommandNilInput(t *testing.T) { - c := initialize() - - buf := new(bytes.Buffer) - // Testing flag with invalid input - c.SetOutput(buf) - cmdEcho.AddCommand(cmdTimes) - c.AddCommand(cmdPrint, cmdEcho) - c.SetArgs([]string{}) - - c.Execute() - - if !strings.Contains(buf.String(), cmdRootNoRun.Long) { - t.Errorf("Expected to get help output, Got: \n %s", buf) - } -} - -func TestRootNoCommandHelp(t *testing.T) { - x := rootOnlySetupTest("--help") - - checkResultOmits(t, x, "Available Commands:") - checkResultOmits(t, x, "for more information about a command") - - if strings.Contains(x.Output, "unknown flag: --help") { - t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) - } - - x = rootOnlySetupTest("echo", "--help") - - checkResultOmits(t, x, "Available Commands:") - checkResultOmits(t, x, "for more information about a command") - - if strings.Contains(x.Output, "unknown flag: --help") { - t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) - } -} - -func TestRootUnknownCommand(t *testing.T) { - r := noRRSetupTest("bogus") - s := "Error: unknown command \"bogus\" for \"cobra-test\"\nRun 'cobra-test --help' for usage.\n" - - if r.Output != s { - t.Errorf("Unexpected response.\nExpecting to be:\n %q\nGot:\n %q\n", s, r.Output) - } - - r = noRRSetupTest("--strtwo=a", "bogus") - if r.Output != s { - t.Errorf("Unexpected response.\nExpecting to be:\n %q\nGot:\n %q\n", s, r.Output) - } -} - -func TestRootUnknownCommandSilenced(t *testing.T) { - r := noRRSetupTestSilenced("bogus") - - if r.Output != "" { - t.Errorf("Unexpected response.\nExpecting to be: \n\"\"\n Got:\n %q\n", r.Output) - } - - r = noRRSetupTestSilenced("--strtwo=a", "bogus") - if r.Output != "" { - t.Errorf("Unexpected response.\nExpecting to be:\n\"\"\nGot:\n %q\n", r.Output) - } -} - -func TestRootSuggestions(t *testing.T) { - outputWithSuggestions := "Error: unknown command \"%s\" for \"cobra-test\"\n\nDid you mean this?\n\t%s\n\nRun 'cobra-test --help' for usage.\n" - outputWithoutSuggestions := "Error: unknown command \"%s\" for \"cobra-test\"\nRun 'cobra-test --help' for usage.\n" - - cmd := initializeWithRootCmd() - cmd.AddCommand(cmdTimes) - - tests := map[string]string{ - "time": "times", - "tiems": "times", - "tims": "times", - "timeS": "times", - "rimes": "times", - "ti": "times", - "t": "times", - "timely": "times", - "ri": "", - "timezone": "", - "foo": "", - "counts": "times", - } - - for typo, suggestion := range tests { - for _, suggestionsDisabled := range []bool{false, true} { - cmd.DisableSuggestions = suggestionsDisabled - result := simpleTester(cmd, typo) - expected := "" - if len(suggestion) == 0 || suggestionsDisabled { - expected = fmt.Sprintf(outputWithoutSuggestions, typo) - } else { - expected = fmt.Sprintf(outputWithSuggestions, typo, suggestion) - } - if result.Output != expected { - t.Errorf("Unexpected response.\nExpecting to be:\n %q\nGot:\n %q\n", expected, result.Output) - } - } - } -} - -func TestFlagsBeforeCommand(t *testing.T) { - // short without space - x := fullSetupTest("-i10", "echo") - if x.Error != nil { - t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error) - } - - x = noRRSetupTest("echo", "-i=10") - if x.Error != nil { - t.Errorf("Valid Input shouldn't have errors, got:\n %s", x.Error) - } - - // long with equals - x = noRRSetupTest("--intone=123", "echo", "one", "two") - if x.Error != nil { - t.Errorf("Valid Input shouldn't have errors, got:\n %s", x.Error) - } - - // With parsing error properly reported - x = fullSetupTest("-i10E", "echo") - if !strings.Contains(x.Error.Error(), "invalid syntax") { - t.Errorf("Wrong error message displayed, \n %s", x.Error) - } -} - -func TestRemoveCommand(t *testing.T) { - versionUsed = 0 - c := initializeWithRootCmd() - c.AddCommand(cmdVersion1) - c.RemoveCommand(cmdVersion1) - x := fullTester(c, "version") - if x.Error == nil { - t.Errorf("Removed command should not have been called\n") - return - } -} - -func TestCommandWithoutSubcommands(t *testing.T) { - c := initializeWithRootCmd() - - x := simpleTester(c, "") - if x.Error != nil { - t.Errorf("Calling command without subcommands should not have error: %v", x.Error) - return - } -} - -func TestCommandWithoutSubcommandsWithArg(t *testing.T) { - c := initializeWithRootCmd() - expectedArgs := []string{"arg"} - - x := simpleTester(c, "arg") - if x.Error != nil { - t.Errorf("Calling command without subcommands but with arg should not have error: %v", x.Error) - return - } - if !reflect.DeepEqual(expectedArgs, tr) { - t.Errorf("Calling command without subcommands but with arg has wrong args: expected: %v, actual: %v", expectedArgs, tr) - return - } -} - -func TestReplaceCommandWithRemove(t *testing.T) { - versionUsed = 0 - c := initializeWithRootCmd() - c.AddCommand(cmdVersion1) - c.RemoveCommand(cmdVersion1) - c.AddCommand(cmdVersion2) - x := fullTester(c, "version") - if x.Error != nil { - t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error) - return - } - if versionUsed == 1 { - t.Errorf("Removed command shouldn't be called\n") - } - if versionUsed != 2 { - t.Errorf("Replacing command should have been called but didn't\n") - } -} - -func TestDeprecatedSub(t *testing.T) { - c := fullSetupTest("deprecated") - - checkResultContains(t, c, cmdDeprecated.Deprecated) -} - -func TestPreRun(t *testing.T) { - noRRSetupTest("echo", "one", "two") - if echoPre == nil || echoPersPre == nil { - t.Error("PreRun or PersistentPreRun not called") - } - if rootPersPre != nil || timesPersPre != nil { - t.Error("Wrong *Pre functions called!") - } - - noRRSetupTest("echo", "times", "one", "two") - if timesPersPre == nil { - t.Error("PreRun or PersistentPreRun not called") - } - if echoPre != nil || echoPersPre != nil || rootPersPre != nil { - t.Error("Wrong *Pre functions called!") - } - - noRRSetupTest("print", "one", "two") - if rootPersPre == nil { - t.Error("Parent PersistentPreRun not called but should not have been") - } - if echoPre != nil || echoPersPre != nil || timesPersPre != nil { - t.Error("Wrong *Pre functions called!") - } -} - -// Check if cmdEchoSub gets PersistentPreRun from rootCmd even if is added last -func TestPeristentPreRunPropagation(t *testing.T) { - rootCmd := initialize() - - // First add the cmdEchoSub to cmdPrint - cmdPrint.AddCommand(cmdEchoSub) - // Now add cmdPrint to rootCmd - rootCmd.AddCommand(cmdPrint) - - rootCmd.SetArgs([]string{"print", "echosub", "lala"}) - rootCmd.Execute() - - if len(rootPersPre) == 0 || rootPersPre[0] != "lala" { - t.Error("RootCmd PersistentPreRun not called but should have been") - } -} - -func TestGlobalNormFuncPropagation(t *testing.T) { - normFunc := func(f *pflag.FlagSet, name string) pflag.NormalizedName { - return pflag.NormalizedName(name) - } - - rootCmd := initialize() - rootCmd.SetGlobalNormalizationFunc(normFunc) - if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() { - t.Error("rootCmd seems to have a wrong normalization function") - } - - // First add the cmdEchoSub to cmdPrint - cmdPrint.AddCommand(cmdEchoSub) - if cmdPrint.GlobalNormalizationFunc() != nil && cmdEchoSub.GlobalNormalizationFunc() != nil { - t.Error("cmdPrint and cmdEchoSub should had no normalization functions") - } - - // Now add cmdPrint to rootCmd - rootCmd.AddCommand(cmdPrint) - if reflect.ValueOf(cmdPrint.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() || - reflect.ValueOf(cmdEchoSub.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() { - t.Error("cmdPrint and cmdEchoSub should had the normalization function of rootCmd") - } -} - -func TestFlagOnPflagCommandLine(t *testing.T) { - flagName := "flagOnCommandLine" - pflag.String(flagName, "", "about my flag") - r := fullSetupTest("--help") - - checkResultContains(t, r, flagName) - - // Reset pflag.CommandLine flagset. - pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError) -} - func TestAddTemplateFunctions(t *testing.T) { AddTemplateFunc("t", func() bool { return true }) AddTemplateFuncs(template.FuncMap{ @@ -1202,43 +12,11 @@ func TestAddTemplateFunctions(t *testing.T) { "h": func() string { return "Hello," }, "w": func() string { return "world." }}) - const usage = "Hello, world." - c := &Command{} c.SetUsageTemplate(`{{if t}}{{h}}{{end}}{{if f}}{{h}}{{end}} {{w}}`) - if us := c.UsageString(); us != usage { - t.Errorf("c.UsageString() != \"%s\", is \"%s\"", usage, us) - } -} - -func TestUsageIsNotPrintedTwice(t *testing.T) { - var cmd = &Command{Use: "root"} - var sub = &Command{Use: "sub"} - cmd.AddCommand(sub) - - r := simpleTester(cmd, "") - if strings.Count(r.Output, "Usage:") != 1 { - t.Error("Usage output is not printed exactly once") - } -} - -func BenchmarkInheritedFlags(b *testing.B) { - initialize() - cmdEcho.AddCommand(cmdTimes) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - cmdTimes.InheritedFlags() - } -} - -func BenchmarkLocalFlags(b *testing.B) { - initialize() - cmdEcho.AddCommand(cmdTimes) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - cmdTimes.LocalFlags() + const expected = "Hello, world." + if got := c.UsageString(); got != expected { + t.Errorf("Expected UsageString: %v\nGot: %v", expected, got) } } diff --git a/vendor/github.com/spf13/cobra/command.go b/vendor/github.com/spf13/cobra/command.go index 185e45263..6cb642647 100644 --- a/vendor/github.com/spf13/cobra/command.go +++ b/vendor/github.com/spf13/cobra/command.go @@ -125,8 +125,9 @@ type Command struct { // Must be > 0. SuggestionsMinimumDistance int - // name is the command name, usually the executable's name. - name string + // TraverseChildren parses flags on all parents before executing child command. + TraverseChildren bool + // commands is the list of commands supported by this program. commands []*Command // parent is a parent command for this command. @@ -475,13 +476,14 @@ func argsMinusFirstX(args []string, x string) []string { return args } +func isFlagArg(arg string) bool { + return ((len(arg) >= 3 && arg[1] == '-') || + (len(arg) >= 2 && arg[0] == '-' && arg[1] != '-')) +} + // Find the target command given the args and command tree // Meant to be run on the highest node. Only searches down. func (c *Command) Find(args []string) (*Command, []string, error) { - if c == nil { - return nil, nil, fmt.Errorf("Called find() on a nil Command") - } - var innerfind func(*Command, []string) (*Command, []string) innerfind = func(c *Command, innerArgs []string) (*Command, []string) { @@ -490,28 +492,11 @@ func (c *Command) Find(args []string) (*Command, []string, error) { return c, innerArgs } nextSubCmd := argsWOflags[0] - matches := make([]*Command, 0) - for _, cmd := range c.commands { - if cmd.Name() == nextSubCmd || cmd.HasAlias(nextSubCmd) { // exact name or alias match - return innerfind(cmd, argsMinusFirstX(innerArgs, nextSubCmd)) - } - if EnablePrefixMatching { - if strings.HasPrefix(cmd.Name(), nextSubCmd) { // prefix match - matches = append(matches, cmd) - } - for _, x := range cmd.Aliases { - if strings.HasPrefix(x, nextSubCmd) { - matches = append(matches, cmd) - } - } - } - } - // only accept a single prefix match - multiple matches would be ambiguous - if len(matches) == 1 { - return innerfind(matches[0], argsMinusFirstX(innerArgs, argsWOflags[0])) + cmd := c.findNext(nextSubCmd) + if cmd != nil { + return innerfind(cmd, argsMinusFirstX(innerArgs, nextSubCmd)) } - return c, innerArgs } @@ -539,6 +524,66 @@ func (c *Command) findSuggestions(arg string) string { return suggestionsString } +func (c *Command) findNext(next string) *Command { + matches := make([]*Command, 0) + for _, cmd := range c.commands { + if cmd.Name() == next || cmd.HasAlias(next) { + return cmd + } + if EnablePrefixMatching && cmd.hasNameOrAliasPrefix(next) { + matches = append(matches, cmd) + } + } + + if len(matches) == 1 { + return matches[0] + } + return nil +} + +// Traverse the command tree to find the command, and parse args for +// each parent. +func (c *Command) Traverse(args []string) (*Command, []string, error) { + flags := []string{} + inFlag := false + + for i, arg := range args { + switch { + // A long flag with a space separated value + case strings.HasPrefix(arg, "--") && !strings.Contains(arg, "="): + // TODO: this isn't quite right, we should really check ahead for 'true' or 'false' + inFlag = !hasNoOptDefVal(arg[2:], c.Flags()) + flags = append(flags, arg) + continue + // A short flag with a space separated value + case strings.HasPrefix(arg, "-") && !strings.Contains(arg, "=") && len(arg) == 2 && !shortHasNoOptDefVal(arg[1:], c.Flags()): + inFlag = true + flags = append(flags, arg) + continue + // The value for a flag + case inFlag: + inFlag = false + flags = append(flags, arg) + continue + // A flag without a value, or with an `=` separated value + case isFlagArg(arg): + flags = append(flags, arg) + continue + } + + cmd := c.findNext(arg) + if cmd == nil { + return c, args, nil + } + + if err := c.ParseFlags(flags); err != nil { + return nil, args, err + } + return cmd.Traverse(args[i+1:]) + } + return c, args, nil +} + // SuggestionsFor provides suggestions for the typedName. func (c *Command) SuggestionsFor(typedName string) []string { suggestions := []string{} @@ -576,10 +621,8 @@ func (c *Command) Root() *Command { return c } -// ArgsLenAtDash will return the length of f.Args at the moment when a -- was -// found during arg parsing. This allows your program to know which args were -// before the -- and which came after. (Description from -// https://godoc.org/github.com/spf13/pflag#FlagSet.ArgsLenAtDash). +// ArgsLenAtDash will return the length of c.Flags().Args at the moment +// when a -- was found during args parsing. func (c *Command) ArgsLenAtDash() int { return c.Flags().ArgsLenAtDash() } @@ -646,6 +689,9 @@ func (c *Command) execute(a []string) (err error) { c.PreRun(c, argWoFlags) } + if err := c.validateRequiredFlags(); err != nil { + return err + } if c.RunE != nil { if err := c.RunE(c, argWoFlags); err != nil { return err @@ -714,7 +760,12 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { args = c.args } - cmd, flags, err := c.Find(args) + var flags []string + if c.TraverseChildren { + cmd, flags, err = c.Traverse(args) + } else { + cmd, flags, err = c.Find(args) + } if err != nil { // If found parse to a subcommand and then failed, talk about the subcommand if cmd != nil { @@ -726,6 +777,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { } return c, err } + err = cmd.execute(flags) if err != nil { // Always show help if requested, even if SilenceErrors is in @@ -757,6 +809,25 @@ func (c *Command) ValidateArgs(args []string) error { return c.Args(c, args) } +func (c *Command) validateRequiredFlags() error { + flags := c.Flags() + missingFlagNames := []string{} + flags.VisitAll(func(pflag *flag.Flag) { + requiredAnnotation, found := pflag.Annotations[BashCompOneRequiredFlag] + if !found { + return + } + if (requiredAnnotation[0] == "true") && !pflag.Changed { + missingFlagNames = append(missingFlagNames, pflag.Name) + } + }) + + if len(missingFlagNames) > 0 { + return fmt.Errorf(`Required flag(s) "%s" have/has not been set`, strings.Join(missingFlagNames, `", "`)) + } + return nil +} + // InitDefaultHelpFlag adds default help flag to c. // It is called automatically by executing the c or by calling help and usage. // If c already has help flag, it will do nothing. @@ -804,8 +875,9 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`, c.AddCommand(c.helpCommand) } -// ResetCommands used for testing. +// ResetCommands delete parent, subcommand and help command from c. func (c *Command) ResetCommands() { + c.parent = nil c.commands = nil c.helpCommand = nil c.parentsPflags = nil @@ -971,15 +1043,12 @@ func (c *Command) DebugFlags() { // Name returns the command's name: the first word in the use line. func (c *Command) Name() string { - if c.name == "" { - name := c.Use - i := strings.Index(name, " ") - if i >= 0 { - name = name[:i] - } - c.name = name + name := c.Use + i := strings.Index(name, " ") + if i >= 0 { + name = name[:i] } - return c.name + return name } // HasAlias determines if a given string is an alias of the command. @@ -992,7 +1061,21 @@ func (c *Command) HasAlias(s string) bool { return false } -// NameAndAliases returns string containing name and all aliases +// hasNameOrAliasPrefix returns true if the Name or any of aliases start +// with prefix +func (c *Command) hasNameOrAliasPrefix(prefix string) bool { + if strings.HasPrefix(c.Name(), prefix) { + return true + } + for _, alias := range c.Aliases { + if strings.HasPrefix(alias, prefix) { + return true + } + } + return false +} + +// NameAndAliases returns a list of the command name and all aliases func (c *Command) NameAndAliases() string { return strings.Join(append([]string{c.Name()}, c.Aliases...), ", ") } @@ -1078,7 +1161,7 @@ func (c *Command) HasAvailableSubCommands() bool { } } - // the command either has no sub comamnds, or no available (non deprecated/help/hidden) + // the command either has no sub commands, or no available (non deprecated/help/hidden) // sub commands return false } @@ -1132,6 +1215,9 @@ func (c *Command) LocalFlags() *flag.FlagSet { c.lflags.SetOutput(c.flagErrorBuf) } c.lflags.SortFlags = c.Flags().SortFlags + if c.globNormFunc != nil { + c.lflags.SetNormalizeFunc(c.globNormFunc) + } addToLocal := func(f *flag.Flag) { if c.lflags.Lookup(f.Name) == nil && c.parentsPflags.Lookup(f.Name) == nil { @@ -1156,6 +1242,10 @@ func (c *Command) InheritedFlags() *flag.FlagSet { } local := c.LocalFlags() + if c.globNormFunc != nil { + c.iflags.SetNormalizeFunc(c.globNormFunc) + } + c.parentsPflags.VisitAll(func(f *flag.Flag) { if c.iflags.Lookup(f.Name) == nil && local.Lookup(f.Name) == nil { c.iflags.AddFlag(f) @@ -1181,7 +1271,7 @@ func (c *Command) PersistentFlags() *flag.FlagSet { return c.pflags } -// ResetFlags is used in testing. +// ResetFlags deletes all flags from command. func (c *Command) ResetFlags() { c.flagErrorBuf = new(bytes.Buffer) c.flagErrorBuf.Reset() @@ -1189,6 +1279,10 @@ func (c *Command) ResetFlags() { c.flags.SetOutput(c.flagErrorBuf) c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) c.pflags.SetOutput(c.flagErrorBuf) + + c.lflags = nil + c.iflags = nil + c.parentsPflags = nil } // HasFlags checks if the command contains any flags (local plus persistent from the entire structure). @@ -1264,6 +1358,9 @@ func (c *Command) ParseFlags(args []string) error { return nil } + if c.flagErrorBuf == nil { + c.flagErrorBuf = new(bytes.Buffer) + } beforeErrorBufLen := c.flagErrorBuf.Len() c.mergePersistentFlags() err := c.Flags().Parse(args) @@ -1298,6 +1395,10 @@ func (c *Command) updateParentsPflags() { c.parentsPflags.SortFlags = false } + if c.globNormFunc != nil { + c.parentsPflags.SetNormalizeFunc(c.globNormFunc) + } + c.Root().PersistentFlags().AddFlagSet(flag.CommandLine) c.VisitParents(func(parent *Command) { diff --git a/vendor/github.com/spf13/cobra/command_test.go b/vendor/github.com/spf13/cobra/command_test.go index aa6658f85..edffc1501 100644 --- a/vendor/github.com/spf13/cobra/command_test.go +++ b/vendor/github.com/spf13/cobra/command_test.go @@ -11,27 +11,450 @@ import ( "github.com/spf13/pflag" ) -// test to ensure hidden commands run as intended -func TestHiddenCommandExecutes(t *testing.T) { +func emptyRun(*Command, []string) {} + +func executeCommand(root *Command, args ...string) (output string, err error) { + _, output, err = executeCommandC(root, args...) + return output, err +} + +func executeCommandC(root *Command, args ...string) (c *Command, output string, err error) { + buf := new(bytes.Buffer) + root.SetOutput(buf) + root.SetArgs(args) + + c, err = root.ExecuteC() + + return c, buf.String(), err +} + +func resetCommandLineFlagSet() { + pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError) +} + +func checkStringContains(t *testing.T, got, expected string) { + if !strings.Contains(got, expected) { + t.Errorf("Expected to contain: \n %v\nGot:\n %v\n", expected, got) + } +} + +func checkStringOmits(t *testing.T, got, expected string) { + if strings.Contains(got, expected) { + t.Errorf("Expected to not contain: \n %v\nGot: %v", expected, got) + } +} - // ensure that outs does not already equal what the command will be setting it - // to, if it did this test would not actually be testing anything... - if outs == "hidden" { - t.Errorf("outs should NOT EQUAL hidden") +func TestSingleCommand(t *testing.T) { + var rootCmdArgs []string + rootCmd := &Command{ + Use: "root", + Args: ExactArgs(2), + Run: func(_ *Command, args []string) { rootCmdArgs = args }, } + aCmd := &Command{Use: "a", Args: NoArgs, Run: emptyRun} + bCmd := &Command{Use: "b", Args: NoArgs, Run: emptyRun} + rootCmd.AddCommand(aCmd, bCmd) - cmdHidden.Execute() + output, err := executeCommand(rootCmd, "one", "two") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } - // upon running the command, the value of outs should now be 'hidden' - if outs != "hidden" { - t.Errorf("Hidden command failed to run!") + got := strings.Join(rootCmdArgs, " ") + expected := "one two" + if got != expected { + t.Errorf("rootCmdArgs expected: %q, got: %q", expected, got) } } -// test to ensure hidden commands do not show up in usage/help text -func TestHiddenCommandIsHidden(t *testing.T) { - if cmdHidden.IsAvailableCommand() { - t.Errorf("Hidden command found!") +func TestChildCommand(t *testing.T) { + var child1CmdArgs []string + rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} + child1Cmd := &Command{ + Use: "child1", + Args: ExactArgs(2), + Run: func(_ *Command, args []string) { child1CmdArgs = args }, + } + child2Cmd := &Command{Use: "child2", Args: NoArgs, Run: emptyRun} + rootCmd.AddCommand(child1Cmd, child2Cmd) + + output, err := executeCommand(rootCmd, "child1", "one", "two") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + got := strings.Join(child1CmdArgs, " ") + expected := "one two" + if got != expected { + t.Errorf("child1CmdArgs expected: %q, got: %q", expected, got) + } +} + +func TestCallCommandWithoutSubcommands(t *testing.T) { + rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} + _, err := executeCommand(rootCmd) + if err != nil { + t.Errorf("Calling command without subcommands should not have error: %v", err) + } +} + +func TestRootExecuteUnknownCommand(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun}) + + output, _ := executeCommand(rootCmd, "unknown") + + expected := "Error: unknown command \"unknown\" for \"root\"\nRun 'root --help' for usage.\n" + + if output != expected { + t.Errorf("Expected:\n %q\nGot:\n %q\n", expected, output) + } +} + +func TestSubcommandExecuteC(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + childCmd := &Command{Use: "child", Run: emptyRun} + rootCmd.AddCommand(childCmd) + + c, output, err := executeCommandC(rootCmd, "child") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if c.Name() != "child" { + t.Errorf(`invalid command returned from ExecuteC: expected "child"', got %q`, c.Name()) + } +} + +func TestRootUnknownCommandSilenced(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + rootCmd.SilenceErrors = true + rootCmd.SilenceUsage = true + rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun}) + + output, _ := executeCommand(rootCmd, "unknown") + if output != "" { + t.Errorf("Expected blank output, because of silenced usage.\nGot:\n %q\n", output) + } +} + +func TestCommandAlias(t *testing.T) { + var timesCmdArgs []string + rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} + echoCmd := &Command{ + Use: "echo", + Aliases: []string{"say", "tell"}, + Args: NoArgs, + Run: emptyRun, + } + timesCmd := &Command{ + Use: "times", + Args: ExactArgs(2), + Run: func(_ *Command, args []string) { timesCmdArgs = args }, + } + echoCmd.AddCommand(timesCmd) + rootCmd.AddCommand(echoCmd) + + output, err := executeCommand(rootCmd, "tell", "times", "one", "two") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + got := strings.Join(timesCmdArgs, " ") + expected := "one two" + if got != expected { + t.Errorf("timesCmdArgs expected: %v, got: %v", expected, got) + } +} + +func TestEnablePrefixMatching(t *testing.T) { + EnablePrefixMatching = true + + var aCmdArgs []string + rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} + aCmd := &Command{ + Use: "aCmd", + Args: ExactArgs(2), + Run: func(_ *Command, args []string) { aCmdArgs = args }, + } + bCmd := &Command{Use: "bCmd", Args: NoArgs, Run: emptyRun} + rootCmd.AddCommand(aCmd, bCmd) + + output, err := executeCommand(rootCmd, "a", "one", "two") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + got := strings.Join(aCmdArgs, " ") + expected := "one two" + if got != expected { + t.Errorf("aCmdArgs expected: %q, got: %q", expected, got) + } + + EnablePrefixMatching = false +} + +func TestAliasPrefixMatching(t *testing.T) { + EnablePrefixMatching = true + + var timesCmdArgs []string + rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} + echoCmd := &Command{ + Use: "echo", + Aliases: []string{"say", "tell"}, + Args: NoArgs, + Run: emptyRun, + } + timesCmd := &Command{ + Use: "times", + Args: ExactArgs(2), + Run: func(_ *Command, args []string) { timesCmdArgs = args }, + } + echoCmd.AddCommand(timesCmd) + rootCmd.AddCommand(echoCmd) + + output, err := executeCommand(rootCmd, "sa", "times", "one", "two") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + got := strings.Join(timesCmdArgs, " ") + expected := "one two" + if got != expected { + t.Errorf("timesCmdArgs expected: %v, got: %v", expected, got) + } + + EnablePrefixMatching = false +} + +// TestChildSameName checks the correct behaviour of cobra in cases, +// when an application with name "foo" and with subcommand "foo" +// is executed with args "foo foo". +func TestChildSameName(t *testing.T) { + var fooCmdArgs []string + rootCmd := &Command{Use: "foo", Args: NoArgs, Run: emptyRun} + fooCmd := &Command{ + Use: "foo", + Args: ExactArgs(2), + Run: func(_ *Command, args []string) { fooCmdArgs = args }, + } + barCmd := &Command{Use: "bar", Args: NoArgs, Run: emptyRun} + rootCmd.AddCommand(fooCmd, barCmd) + + output, err := executeCommand(rootCmd, "foo", "one", "two") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + got := strings.Join(fooCmdArgs, " ") + expected := "one two" + if got != expected { + t.Errorf("fooCmdArgs expected: %v, got: %v", expected, got) + } +} + +// TestGrandChildSameName checks the correct behaviour of cobra in cases, +// when user has a root command and a grand child +// with the same name. +func TestGrandChildSameName(t *testing.T) { + var fooCmdArgs []string + rootCmd := &Command{Use: "foo", Args: NoArgs, Run: emptyRun} + barCmd := &Command{Use: "bar", Args: NoArgs, Run: emptyRun} + fooCmd := &Command{ + Use: "foo", + Args: ExactArgs(2), + Run: func(_ *Command, args []string) { fooCmdArgs = args }, + } + barCmd.AddCommand(fooCmd) + rootCmd.AddCommand(barCmd) + + output, err := executeCommand(rootCmd, "bar", "foo", "one", "two") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + got := strings.Join(fooCmdArgs, " ") + expected := "one two" + if got != expected { + t.Errorf("fooCmdArgs expected: %v, got: %v", expected, got) + } +} + +func TestFlagLong(t *testing.T) { + var cArgs []string + c := &Command{ + Use: "c", + Args: ArbitraryArgs, + Run: func(_ *Command, args []string) { cArgs = args }, + } + + var intFlagValue int + var stringFlagValue string + c.Flags().IntVar(&intFlagValue, "intf", -1, "") + c.Flags().StringVar(&stringFlagValue, "sf", "", "") + + output, err := executeCommand(c, "--intf=7", "--sf=abc", "one", "--", "two") + if output != "" { + t.Errorf("Unexpected output: %v", err) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if c.ArgsLenAtDash() != 1 { + t.Errorf("Expected ArgsLenAtDash: %v but got %v", 1, c.ArgsLenAtDash()) + } + if intFlagValue != 7 { + t.Errorf("Expected intFlagValue: %v, got %v", 7, intFlagValue) + } + if stringFlagValue != "abc" { + t.Errorf("Expected stringFlagValue: %q, got %q", "abc", stringFlagValue) + } + + got := strings.Join(cArgs, " ") + expected := "one two" + if got != expected { + t.Errorf("Expected arguments: %q, got %q", expected, got) + } +} + +func TestFlagShort(t *testing.T) { + var cArgs []string + c := &Command{ + Use: "c", + Args: ArbitraryArgs, + Run: func(_ *Command, args []string) { cArgs = args }, + } + + var intFlagValue int + var stringFlagValue string + c.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "") + c.Flags().StringVarP(&stringFlagValue, "sf", "s", "", "") + + output, err := executeCommand(c, "-i", "7", "-sabc", "one", "two") + if output != "" { + t.Errorf("Unexpected output: %v", err) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if intFlagValue != 7 { + t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue) + } + if stringFlagValue != "abc" { + t.Errorf("Expected stringFlagValue: %q, got %q", "abc", stringFlagValue) + } + + got := strings.Join(cArgs, " ") + expected := "one two" + if got != expected { + t.Errorf("Expected arguments: %q, got %q", expected, got) + } +} + +func TestChildFlag(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + childCmd := &Command{Use: "child", Run: emptyRun} + rootCmd.AddCommand(childCmd) + + var intFlagValue int + childCmd.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "") + + output, err := executeCommand(rootCmd, "child", "-i7") + if output != "" { + t.Errorf("Unexpected output: %v", err) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if intFlagValue != 7 { + t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue) + } +} + +func TestChildFlagWithParentLocalFlag(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + childCmd := &Command{Use: "child", Run: emptyRun} + rootCmd.AddCommand(childCmd) + + var intFlagValue int + rootCmd.Flags().StringP("sf", "s", "", "") + childCmd.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "") + + _, err := executeCommand(rootCmd, "child", "-i7", "-sabc") + if err == nil { + t.Errorf("Invalid flag should generate error") + } + + checkStringContains(t, err.Error(), "unknown shorthand") + + if intFlagValue != 7 { + t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue) + } +} + +func TestFlagInvalidInput(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + rootCmd.Flags().IntP("intf", "i", -1, "") + + _, err := executeCommand(rootCmd, "-iabc") + if err == nil { + t.Errorf("Invalid flag value should generate error") + } + + checkStringContains(t, err.Error(), "invalid syntax") +} + +func TestFlagBeforeCommand(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + childCmd := &Command{Use: "child", Run: emptyRun} + rootCmd.AddCommand(childCmd) + + var flagValue int + childCmd.Flags().IntVarP(&flagValue, "intf", "i", -1, "") + + // With short flag. + _, err := executeCommand(rootCmd, "-i7", "child") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + if flagValue != 7 { + t.Errorf("Expected flag value: %v, got %v", 7, flagValue) + } + + // With long flag. + _, err = executeCommand(rootCmd, "--intf=8", "child") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + if flagValue != 8 { + t.Errorf("Expected flag value: %v, got %v", 9, flagValue) } } @@ -45,11 +468,11 @@ func TestStripFlags(t *testing.T) { []string{"foo", "bar"}, }, { - []string{"foo", "--bar", "-b"}, + []string{"foo", "--str", "-s"}, []string{"foo"}, }, { - []string{"-b", "foo", "--bar", "bar"}, + []string{"-s", "foo", "--str", "bar"}, []string{}, }, { @@ -65,7 +488,7 @@ func TestStripFlags(t *testing.T) { []string{"echo"}, }, { - []string{"-ib", "echo", "-bfoo", "baz"}, + []string{"-ib", "echo", "-sfoo", "baz"}, []string{"echo", "baz"}, }, { @@ -73,15 +496,15 @@ func TestStripFlags(t *testing.T) { []string{"bar", "blah"}, }, { - []string{"--int=baz", "-bbar", "-i", "foo", "blah"}, + []string{"--int=baz", "-sbar", "-i", "foo", "blah"}, []string{"blah"}, }, { - []string{"--cat", "bar", "-i", "foo", "blah"}, + []string{"--bool", "bar", "-i", "foo", "blah"}, []string{"bar", "blah"}, }, { - []string{"-c", "bar", "-i", "foo", "blah"}, + []string{"-b", "bar", "-i", "foo", "blah"}, []string{"bar", "blah"}, }, { @@ -94,61 +517,757 @@ func TestStripFlags(t *testing.T) { }, } - cmdPrint := &Command{ - Use: "print [string to print]", - Short: "Print anything to the screen", - Long: `an utterly useless command for testing.`, - Run: func(cmd *Command, args []string) { - tp = args - }, - } - - var flagi int - var flagstr string - var flagbool bool - cmdPrint.PersistentFlags().BoolVarP(&flagbool, "persist", "p", false, "help for persistent one") - cmdPrint.Flags().IntVarP(&flagi, "int", "i", 345, "help message for flag int") - cmdPrint.Flags().StringVarP(&flagstr, "bar", "b", "bar", "help message for flag string") - cmdPrint.Flags().BoolVarP(&flagbool, "cat", "c", false, "help message for flag bool") + c := &Command{Use: "c", Run: emptyRun} + c.PersistentFlags().BoolP("persist", "p", false, "") + c.Flags().IntP("int", "i", -1, "") + c.Flags().StringP("str", "s", "", "") + c.Flags().BoolP("bool", "b", false, "") - for _, test := range tests { - output := stripFlags(test.input, cmdPrint) - if !reflect.DeepEqual(test.output, output) { - t.Errorf("expected: %v, got: %v", test.output, output) + for i, test := range tests { + got := stripFlags(test.input, c) + if !reflect.DeepEqual(test.output, got) { + t.Errorf("(%v) Expected: %v, got: %v", i, test.output, got) } } } func TestDisableFlagParsing(t *testing.T) { - targs := []string{} - cmdPrint := &Command{ + var cArgs []string + c := &Command{ + Use: "c", DisableFlagParsing: true, - Run: func(cmd *Command, args []string) { - targs = args + Run: func(_ *Command, args []string) { + cArgs = args }, } + args := []string{"cmd", "-v", "-race", "-file", "foo.go"} - cmdPrint.SetArgs(args) - err := cmdPrint.Execute() + output, err := executeCommand(c, args...) + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if !reflect.DeepEqual(args, cArgs) { + t.Errorf("Expected: %v, got: %v", args, cArgs) + } +} + +func TestPersistentFlagsOnSameCommand(t *testing.T) { + var rootCmdArgs []string + rootCmd := &Command{ + Use: "root", + Args: ArbitraryArgs, + Run: func(_ *Command, args []string) { rootCmdArgs = args }, + } + + var flagValue int + rootCmd.PersistentFlags().IntVarP(&flagValue, "intf", "i", -1, "") + + output, err := executeCommand(rootCmd, "-i7", "one", "two") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } if err != nil { - t.Error(err) + t.Errorf("Unexpected error: %v", err) } - if !reflect.DeepEqual(args, targs) { - t.Errorf("expected: %v, got: %v", args, targs) + + got := strings.Join(rootCmdArgs, " ") + expected := "one two" + if got != expected { + t.Errorf("rootCmdArgs expected: %q, got %q", expected, got) + } + if flagValue != 7 { + t.Errorf("flagValue expected: %v, got %v", 7, flagValue) + } +} + +// TestEmptyInputs checks, +// if flags correctly parsed with blank strings in args. +func TestEmptyInputs(t *testing.T) { + c := &Command{Use: "c", Run: emptyRun} + + var flagValue int + c.Flags().IntVarP(&flagValue, "intf", "i", -1, "") + + output, err := executeCommand(c, "", "-i7", "") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if flagValue != 7 { + t.Errorf("flagValue expected: %v, got %v", 7, flagValue) + } +} + +func TestOverwrittenFlag(t *testing.T) { + // TODO: This test fails, but should work. + t.Skip() + + parent := &Command{Use: "parent", Run: emptyRun} + child := &Command{Use: "child", Run: emptyRun} + + parent.PersistentFlags().Bool("boolf", false, "") + parent.PersistentFlags().Int("intf", -1, "") + child.Flags().String("strf", "", "") + child.Flags().Int("intf", -1, "") + + parent.AddCommand(child) + + childInherited := child.InheritedFlags() + childLocal := child.LocalFlags() + + if childLocal.Lookup("strf") == nil { + t.Error(`LocalFlags expected to contain "strf", got "nil"`) + } + if childInherited.Lookup("boolf") == nil { + t.Error(`InheritedFlags expected to contain "boolf", got "nil"`) + } + + if childInherited.Lookup("intf") != nil { + t.Errorf(`InheritedFlags should not contain overwritten flag "intf"`) + } + if childLocal.Lookup("intf") == nil { + t.Error(`LocalFlags expected to contain "intf", got "nil"`) + } +} + +func TestPersistentFlagsOnChild(t *testing.T) { + var childCmdArgs []string + rootCmd := &Command{Use: "root", Run: emptyRun} + childCmd := &Command{ + Use: "child", + Args: ArbitraryArgs, + Run: func(_ *Command, args []string) { childCmdArgs = args }, + } + rootCmd.AddCommand(childCmd) + + var parentFlagValue int + var childFlagValue int + rootCmd.PersistentFlags().IntVarP(&parentFlagValue, "parentf", "p", -1, "") + childCmd.Flags().IntVarP(&childFlagValue, "childf", "c", -1, "") + + output, err := executeCommand(rootCmd, "child", "-c7", "-p8", "one", "two") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + got := strings.Join(childCmdArgs, " ") + expected := "one two" + if got != expected { + t.Errorf("childCmdArgs expected: %q, got %q", expected, got) + } + if parentFlagValue != 8 { + t.Errorf("parentFlagValue expected: %v, got %v", 8, parentFlagValue) + } + if childFlagValue != 7 { + t.Errorf("childFlagValue expected: %v, got %v", 7, childFlagValue) + } +} + +func TestRequiredFlags(t *testing.T) { + c := &Command{Use: "c", Run: emptyRun} + c.Flags().String("foo1", "", "") + c.MarkFlagRequired("foo1") + c.Flags().String("foo2", "", "") + c.MarkFlagRequired("foo2") + c.Flags().String("bar", "", "") + + expected := fmt.Sprintf("Required flag(s) %q, %q have/has not been set", "foo1", "foo2") + + _, err := executeCommand(c) + got := err.Error() + + if got != expected { + t.Errorf("Expected error: %q, got: %q", expected, got) + } +} + +func TestPersistentRequiredFlags(t *testing.T) { + parent := &Command{Use: "parent", Run: emptyRun} + parent.PersistentFlags().String("foo1", "", "") + parent.MarkPersistentFlagRequired("foo1") + parent.PersistentFlags().String("foo2", "", "") + parent.MarkPersistentFlagRequired("foo2") + parent.Flags().String("foo3", "", "") + + child := &Command{Use: "child", Run: emptyRun} + child.Flags().String("bar1", "", "") + child.MarkFlagRequired("bar1") + child.Flags().String("bar2", "", "") + child.MarkFlagRequired("bar2") + child.Flags().String("bar3", "", "") + + parent.AddCommand(child) + + expected := fmt.Sprintf("Required flag(s) %q, %q, %q, %q have/has not been set", "bar1", "bar2", "foo1", "foo2") + + _, err := executeCommand(parent, "child") + if err.Error() != expected { + t.Errorf("Expected %q, got %q", expected, err.Error()) } } func TestInitHelpFlagMergesFlags(t *testing.T) { usage := "custom flag" - baseCmd := Command{Use: "testcmd"} - baseCmd.PersistentFlags().Bool("help", false, usage) - cmd := Command{Use: "do"} - baseCmd.AddCommand(&cmd) + rootCmd := &Command{Use: "root"} + rootCmd.PersistentFlags().Bool("help", false, "custom flag") + childCmd := &Command{Use: "child"} + rootCmd.AddCommand(childCmd) + + childCmd.InitDefaultHelpFlag() + got := childCmd.Flags().Lookup("help").Usage + if got != usage { + t.Errorf("Expected the help flag from the root command with usage: %v\nGot the default with usage: %v", usage, got) + } +} + +func TestHelpCommandExecuted(t *testing.T) { + rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun} + rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun}) + + output, err := executeCommand(rootCmd, "help") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + checkStringContains(t, output, rootCmd.Long) +} - cmd.InitDefaultHelpFlag() - actual := cmd.Flags().Lookup("help").Usage - if actual != usage { - t.Fatalf("Expected the help flag from the base command with usage '%s', but got the default with usage '%s'", usage, actual) +func TestHelpCommandExecutedOnChild(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + childCmd := &Command{Use: "child", Long: "Long description", Run: emptyRun} + rootCmd.AddCommand(childCmd) + + output, err := executeCommand(rootCmd, "help", "child") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + checkStringContains(t, output, childCmd.Long) +} + +func TestSetHelpCommand(t *testing.T) { + c := &Command{Use: "c", Run: emptyRun} + c.AddCommand(&Command{Use: "empty", Run: emptyRun}) + + expected := "WORKS" + c.SetHelpCommand(&Command{ + Use: "help [command]", + Short: "Help about any command", + Long: `Help provides help for any command in the application. + Simply type ` + c.Name() + ` help [path to command] for full details.`, + Run: func(c *Command, _ []string) { c.Print(expected) }, + }) + + got, err := executeCommand(c, "help") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if got != expected { + t.Errorf("Expected to contain %q, got %q", expected, got) + } +} + +func TestHelpFlagExecuted(t *testing.T) { + rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun} + + output, err := executeCommand(rootCmd, "--help") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + checkStringContains(t, output, rootCmd.Long) +} + +func TestHelpFlagExecutedOnChild(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + childCmd := &Command{Use: "child", Long: "Long description", Run: emptyRun} + rootCmd.AddCommand(childCmd) + + output, err := executeCommand(rootCmd, "child", "--help") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + checkStringContains(t, output, childCmd.Long) +} + +// TestHelpFlagInHelp checks, +// if '--help' flag is shown in help for child (executing `parent help child`), +// that has no other flags. +// Related to https://github.com/spf13/cobra/issues/302. +func TestHelpFlagInHelp(t *testing.T) { + parentCmd := &Command{Use: "parent", Run: func(*Command, []string) {}} + + childCmd := &Command{Use: "child", Run: func(*Command, []string) {}} + parentCmd.AddCommand(childCmd) + + output, err := executeCommand(parentCmd, "help", "child") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + checkStringContains(t, output, "[flags]") +} + +func TestFlagsInUsage(t *testing.T) { + rootCmd := &Command{Use: "root", Args: NoArgs, Run: func(*Command, []string) {}} + output, err := executeCommand(rootCmd, "--help") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + checkStringContains(t, output, "[flags]") +} + +func TestHelpExecutedOnNonRunnableChild(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + childCmd := &Command{Use: "child", Long: "Long description"} + rootCmd.AddCommand(childCmd) + + output, err := executeCommand(rootCmd, "child") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + checkStringContains(t, output, childCmd.Long) +} + +func TestUsageIsNotPrintedTwice(t *testing.T) { + var cmd = &Command{Use: "root"} + var sub = &Command{Use: "sub"} + cmd.AddCommand(sub) + + output, _ := executeCommand(cmd, "") + if strings.Count(output, "Usage:") != 1 { + t.Error("Usage output is not printed exactly once") + } +} + +func TestVisitParents(t *testing.T) { + c := &Command{Use: "app"} + sub := &Command{Use: "sub"} + dsub := &Command{Use: "dsub"} + sub.AddCommand(dsub) + c.AddCommand(sub) + + total := 0 + add := func(x *Command) { + total++ + } + sub.VisitParents(add) + if total != 1 { + t.Errorf("Should have visited 1 parent but visited %d", total) + } + + total = 0 + dsub.VisitParents(add) + if total != 2 { + t.Errorf("Should have visited 2 parents but visited %d", total) + } + + total = 0 + c.VisitParents(add) + if total != 0 { + t.Errorf("Should have visited no parents but visited %d", total) + } +} + +func TestSuggestions(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + timesCmd := &Command{ + Use: "times", + SuggestFor: []string{"counts"}, + Run: emptyRun, + } + rootCmd.AddCommand(timesCmd) + + templateWithSuggestions := "Error: unknown command \"%s\" for \"root\"\n\nDid you mean this?\n\t%s\n\nRun 'root --help' for usage.\n" + templateWithoutSuggestions := "Error: unknown command \"%s\" for \"root\"\nRun 'root --help' for usage.\n" + + tests := map[string]string{ + "time": "times", + "tiems": "times", + "tims": "times", + "timeS": "times", + "rimes": "times", + "ti": "times", + "t": "times", + "timely": "times", + "ri": "", + "timezone": "", + "foo": "", + "counts": "times", + } + + for typo, suggestion := range tests { + for _, suggestionsDisabled := range []bool{true, false} { + rootCmd.DisableSuggestions = suggestionsDisabled + + var expected string + output, _ := executeCommand(rootCmd, typo) + + if suggestion == "" || suggestionsDisabled { + expected = fmt.Sprintf(templateWithoutSuggestions, typo) + } else { + expected = fmt.Sprintf(templateWithSuggestions, typo, suggestion) + } + + if output != expected { + t.Errorf("Unexpected response.\nExpected:\n %q\nGot:\n %q\n", expected, output) + } + } + } +} + +func TestRemoveCommand(t *testing.T) { + rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} + childCmd := &Command{Use: "child", Run: emptyRun} + rootCmd.AddCommand(childCmd) + rootCmd.RemoveCommand(childCmd) + + _, err := executeCommand(rootCmd, "child") + if err == nil { + t.Error("Expected error on calling removed command. Got nil.") + } +} + +func TestReplaceCommandWithRemove(t *testing.T) { + childUsed := 0 + rootCmd := &Command{Use: "root", Run: emptyRun} + child1Cmd := &Command{ + Use: "child", + Run: func(*Command, []string) { childUsed = 1 }, + } + child2Cmd := &Command{ + Use: "child", + Run: func(*Command, []string) { childUsed = 2 }, + } + rootCmd.AddCommand(child1Cmd) + rootCmd.RemoveCommand(child1Cmd) + rootCmd.AddCommand(child2Cmd) + + output, err := executeCommand(rootCmd, "child") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if childUsed == 1 { + t.Error("Removed command shouldn't be called") + } + if childUsed != 2 { + t.Error("Replacing command should have been called but didn't") + } +} + +func TestDeprecatedCommand(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + deprecatedCmd := &Command{ + Use: "deprecated", + Deprecated: "This command is deprecated", + Run: emptyRun, + } + rootCmd.AddCommand(deprecatedCmd) + + output, err := executeCommand(rootCmd, "deprecated") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + checkStringContains(t, output, deprecatedCmd.Deprecated) +} + +func TestHooks(t *testing.T) { + var ( + persPreArgs string + preArgs string + runArgs string + postArgs string + persPostArgs string + ) + + c := &Command{ + Use: "c", + PersistentPreRun: func(_ *Command, args []string) { + persPreArgs = strings.Join(args, " ") + }, + PreRun: func(_ *Command, args []string) { + preArgs = strings.Join(args, " ") + }, + Run: func(_ *Command, args []string) { + runArgs = strings.Join(args, " ") + }, + PostRun: func(_ *Command, args []string) { + postArgs = strings.Join(args, " ") + }, + PersistentPostRun: func(_ *Command, args []string) { + persPostArgs = strings.Join(args, " ") + }, + } + + output, err := executeCommand(c, "one", "two") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if persPreArgs != "one two" { + t.Errorf("Expected persPreArgs %q, got %q", "one two", persPreArgs) + } + if preArgs != "one two" { + t.Errorf("Expected preArgs %q, got %q", "one two", preArgs) + } + if runArgs != "one two" { + t.Errorf("Expected runArgs %q, got %q", "one two", runArgs) + } + if postArgs != "one two" { + t.Errorf("Expected postArgs %q, got %q", "one two", postArgs) + } + if persPostArgs != "one two" { + t.Errorf("Expected persPostArgs %q, got %q", "one two", persPostArgs) + } +} + +func TestPersistentHooks(t *testing.T) { + var ( + parentPersPreArgs string + parentPreArgs string + parentRunArgs string + parentPostArgs string + parentPersPostArgs string + ) + + var ( + childPersPreArgs string + childPreArgs string + childRunArgs string + childPostArgs string + childPersPostArgs string + ) + + parentCmd := &Command{ + Use: "parent", + PersistentPreRun: func(_ *Command, args []string) { + parentPersPreArgs = strings.Join(args, " ") + }, + PreRun: func(_ *Command, args []string) { + parentPreArgs = strings.Join(args, " ") + }, + Run: func(_ *Command, args []string) { + parentRunArgs = strings.Join(args, " ") + }, + PostRun: func(_ *Command, args []string) { + parentPostArgs = strings.Join(args, " ") + }, + PersistentPostRun: func(_ *Command, args []string) { + parentPersPostArgs = strings.Join(args, " ") + }, + } + + childCmd := &Command{ + Use: "child", + PersistentPreRun: func(_ *Command, args []string) { + childPersPreArgs = strings.Join(args, " ") + }, + PreRun: func(_ *Command, args []string) { + childPreArgs = strings.Join(args, " ") + }, + Run: func(_ *Command, args []string) { + childRunArgs = strings.Join(args, " ") + }, + PostRun: func(_ *Command, args []string) { + childPostArgs = strings.Join(args, " ") + }, + PersistentPostRun: func(_ *Command, args []string) { + childPersPostArgs = strings.Join(args, " ") + }, + } + parentCmd.AddCommand(childCmd) + + output, err := executeCommand(parentCmd, "child", "one", "two") + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + // TODO: This test fails, but should not. + // Related to https://github.com/spf13/cobra/issues/252. + // + // if parentPersPreArgs != "one two" { + // t.Errorf("Expected parentPersPreArgs %q, got %q", "one two", parentPersPreArgs) + // } + if parentPreArgs != "" { + t.Errorf("Expected blank parentPreArgs, got %q", parentPreArgs) + } + if parentRunArgs != "" { + t.Errorf("Expected blank parentRunArgs, got %q", parentRunArgs) + } + if parentPostArgs != "" { + t.Errorf("Expected blank parentPostArgs, got %q", parentPostArgs) + } + // TODO: This test fails, but should not. + // Related to https://github.com/spf13/cobra/issues/252. + // + // if parentPersPostArgs != "one two" { + // t.Errorf("Expected parentPersPostArgs %q, got %q", "one two", parentPersPostArgs) + // } + + if childPersPreArgs != "one two" { + t.Errorf("Expected childPersPreArgs %q, got %q", "one two", childPersPreArgs) + } + if childPreArgs != "one two" { + t.Errorf("Expected childPreArgs %q, got %q", "one two", childPreArgs) + } + if childRunArgs != "one two" { + t.Errorf("Expected childRunArgs %q, got %q", "one two", childRunArgs) + } + if childPostArgs != "one two" { + t.Errorf("Expected childPostArgs %q, got %q", "one two", childPostArgs) + } + if childPersPostArgs != "one two" { + t.Errorf("Expected childPersPostArgs %q, got %q", "one two", childPersPostArgs) + } +} + +// Related to https://github.com/spf13/cobra/issues/521. +func TestGlobalNormFuncPropagation(t *testing.T) { + normFunc := func(f *pflag.FlagSet, name string) pflag.NormalizedName { + return pflag.NormalizedName(name) + } + + rootCmd := &Command{Use: "root", Run: emptyRun} + childCmd := &Command{Use: "child", Run: emptyRun} + rootCmd.AddCommand(childCmd) + + rootCmd.SetGlobalNormalizationFunc(normFunc) + if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() { + t.Error("rootCmd seems to have a wrong normalization function") + } + + if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(childCmd.GlobalNormalizationFunc()).Pointer() { + t.Error("childCmd should have had the normalization function of rootCmd") + } +} + +// Related to https://github.com/spf13/cobra/issues/521. +func TestNormPassedOnLocal(t *testing.T) { + toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName { + return pflag.NormalizedName(strings.ToUpper(name)) + } + + c := &Command{} + c.Flags().Bool("flagname", true, "this is a dummy flag") + c.SetGlobalNormalizationFunc(toUpper) + if c.LocalFlags().Lookup("flagname") != c.LocalFlags().Lookup("FLAGNAME") { + t.Error("Normalization function should be passed on to Local flag set") + } +} + +// Related to https://github.com/spf13/cobra/issues/521. +func TestNormPassedOnInherited(t *testing.T) { + toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName { + return pflag.NormalizedName(strings.ToUpper(name)) + } + + c := &Command{} + c.SetGlobalNormalizationFunc(toUpper) + + child1 := &Command{} + c.AddCommand(child1) + + c.PersistentFlags().Bool("flagname", true, "") + + child2 := &Command{} + c.AddCommand(child2) + + inherited := child1.InheritedFlags() + if inherited.Lookup("flagname") == nil || inherited.Lookup("flagname") != inherited.Lookup("FLAGNAME") { + t.Error("Normalization function should be passed on to inherited flag set in command added before flag") + } + + inherited = child2.InheritedFlags() + if inherited.Lookup("flagname") == nil || inherited.Lookup("flagname") != inherited.Lookup("FLAGNAME") { + t.Error("Normalization function should be passed on to inherited flag set in command added after flag") + } +} + +// Related to https://github.com/spf13/cobra/issues/521. +func TestConsistentNormalizedName(t *testing.T) { + toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName { + return pflag.NormalizedName(strings.ToUpper(name)) + } + n := func(f *pflag.FlagSet, name string) pflag.NormalizedName { + return pflag.NormalizedName(name) + } + + c := &Command{} + c.Flags().Bool("flagname", true, "") + c.SetGlobalNormalizationFunc(toUpper) + c.SetGlobalNormalizationFunc(n) + + if c.LocalFlags().Lookup("flagname") == c.LocalFlags().Lookup("FLAGNAME") { + t.Error("Normalizing flag names should not result in duplicate flags") + } +} + +func TestFlagOnPflagCommandLine(t *testing.T) { + flagName := "flagOnCommandLine" + pflag.String(flagName, "", "about my flag") + + c := &Command{Use: "c", Run: emptyRun} + c.AddCommand(&Command{Use: "child", Run: emptyRun}) + + output, _ := executeCommand(c, "--help") + checkStringContains(t, output, flagName) + + resetCommandLineFlagSet() +} + +// TestHiddenCommandExecutes checks, +// if hidden commands run as intended. +func TestHiddenCommandExecutes(t *testing.T) { + executed := false + c := &Command{ + Use: "c", + Hidden: true, + Run: func(*Command, []string) { executed = true }, + } + + output, err := executeCommand(c) + if output != "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if !executed { + t.Error("Hidden command should have been executed") + } +} + +// test to ensure hidden commands do not show up in usage/help text +func TestHiddenCommandIsHidden(t *testing.T) { + c := &Command{Use: "c", Hidden: true, Run: emptyRun} + if c.IsAvailableCommand() { + t.Errorf("Hidden command should be unavailable") } } @@ -158,15 +1277,16 @@ func TestCommandsAreSorted(t *testing.T) { originalNames := []string{"middle", "zlast", "afirst"} expectedNames := []string{"afirst", "middle", "zlast"} - var tmpCommand = &Command{Use: "tmp"} + var rootCmd = &Command{Use: "root"} for _, name := range originalNames { - tmpCommand.AddCommand(&Command{Use: name}) + rootCmd.AddCommand(&Command{Use: name}) } - for i, c := range tmpCommand.Commands() { - if expectedNames[i] != c.Name() { - t.Errorf("expected: %s, got: %s", expectedNames[i], c.Name()) + for i, c := range rootCmd.Commands() { + got := c.Name() + if expectedNames[i] != got { + t.Errorf("Expected: %s, got: %s", expectedNames[i], got) } } @@ -178,15 +1298,16 @@ func TestEnableCommandSortingIsDisabled(t *testing.T) { originalNames := []string{"middle", "zlast", "afirst"} - var tmpCommand = &Command{Use: "tmp"} + var rootCmd = &Command{Use: "root"} for _, name := range originalNames { - tmpCommand.AddCommand(&Command{Use: name}) + rootCmd.AddCommand(&Command{Use: name}) } - for i, c := range tmpCommand.Commands() { - if originalNames[i] != c.Name() { - t.Errorf("expected: %s, got: %s", originalNames[i], c.Name()) + for i, c := range rootCmd.Commands() { + got := c.Name() + if originalNames[i] != got { + t.Errorf("expected: %s, got: %s", originalNames[i], got) } } @@ -194,33 +1315,27 @@ func TestEnableCommandSortingIsDisabled(t *testing.T) { } func TestSetOutput(t *testing.T) { - cmd := &Command{} - cmd.SetOutput(nil) - if out := cmd.OutOrStdout(); out != os.Stdout { - t.Fatalf("expected setting output to nil to revert back to stdout, got %v", out) + c := &Command{} + c.SetOutput(nil) + if out := c.OutOrStdout(); out != os.Stdout { + t.Errorf("Expected setting output to nil to revert back to stdout") } } func TestFlagErrorFunc(t *testing.T) { - cmd := &Command{ - Use: "print", - RunE: func(cmd *Command, args []string) error { - return nil - }, - } - expectedFmt := "This is expected: %s" + c := &Command{Use: "c", Run: emptyRun} - cmd.SetFlagErrorFunc(func(c *Command, err error) error { + expectedFmt := "This is expected: %v" + c.SetFlagErrorFunc(func(_ *Command, err error) error { return fmt.Errorf(expectedFmt, err) }) - cmd.SetArgs([]string{"--bogus-flag"}) - cmd.SetOutput(new(bytes.Buffer)) - err := cmd.Execute() + _, err := executeCommand(c, "--unknown-flag") - expected := fmt.Sprintf(expectedFmt, "unknown flag: --bogus-flag") - if err.Error() != expected { - t.Errorf("expected %v, got %v", expected, err.Error()) + got := err.Error() + expected := fmt.Sprintf(expectedFmt, "unknown flag: --unknown-flag") + if got != expected { + t.Errorf("Expected %v, got %v", expected, got) } } @@ -228,19 +1343,19 @@ func TestFlagErrorFunc(t *testing.T) { // if cmd.LocalFlags() is unsorted when cmd.Flags().SortFlags set to false. // Related to https://github.com/spf13/cobra/issues/404. func TestSortedFlags(t *testing.T) { - cmd := &Command{} - cmd.Flags().SortFlags = false + c := &Command{} + c.Flags().SortFlags = false names := []string{"C", "B", "A", "D"} for _, name := range names { - cmd.Flags().Bool(name, false, "") + c.Flags().Bool(name, false, "") } i := 0 - cmd.LocalFlags().VisitAll(func(f *pflag.Flag) { + c.LocalFlags().VisitAll(func(f *pflag.Flag) { if i == len(names) { return } - if isStringInStringSlice(f.Name, names) { + if stringInSlice(f.Name, names) { if names[i] != f.Name { t.Errorf("Incorrect order. Expected %v, got %v", names[i], f.Name) } @@ -249,101 +1364,145 @@ func TestSortedFlags(t *testing.T) { }) } -// contains checks, if s is in ss. -func isStringInStringSlice(s string, ss []string) bool { - for _, v := range ss { - if v == s { - return true - } - } - return false -} - -// TestHelpFlagInHelp checks, -// if '--help' flag is shown in help for child (executing `parent help child`), -// that has no other flags. -// Related to https://github.com/spf13/cobra/issues/302. -func TestHelpFlagInHelp(t *testing.T) { - output := new(bytes.Buffer) - parent := &Command{Use: "parent", Run: func(*Command, []string) {}} - parent.SetOutput(output) - - child := &Command{Use: "child", Run: func(*Command, []string) {}} - parent.AddCommand(child) - - parent.SetArgs([]string{"help", "child"}) - err := parent.Execute() - if err != nil { - t.Fatal(err) - } - - if !strings.Contains(output.String(), "[flags]") { - t.Errorf("\nExpecting to contain: %v\nGot: %v", "[flags]", output.String()) - } -} - // TestMergeCommandLineToFlags checks, // if pflag.CommandLine is correctly merged to c.Flags() after first call // of c.mergePersistentFlags. // Related to https://github.com/spf13/cobra/issues/443. func TestMergeCommandLineToFlags(t *testing.T) { pflag.Bool("boolflag", false, "") - c := &Command{Use: "c", Run: func(*Command, []string) {}} + c := &Command{Use: "c", Run: emptyRun} c.mergePersistentFlags() if c.Flags().Lookup("boolflag") == nil { t.Fatal("Expecting to have flag from CommandLine in c.Flags()") } - // Reset pflag.CommandLine flagset. - pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError) + resetCommandLineFlagSet() } // TestUseDeprecatedFlags checks, // if cobra.Execute() prints a message, if a deprecated flag is used. // Related to https://github.com/spf13/cobra/issues/463. func TestUseDeprecatedFlags(t *testing.T) { - c := &Command{Use: "c", Run: func(*Command, []string) {}} - output := new(bytes.Buffer) - c.SetOutput(output) + c := &Command{Use: "c", Run: emptyRun} c.Flags().BoolP("deprecated", "d", false, "deprecated flag") c.Flags().MarkDeprecated("deprecated", "This flag is deprecated") - c.SetArgs([]string{"c", "-d"}) - if err := c.Execute(); err != nil { + output, err := executeCommand(c, "c", "-d") + if err != nil { t.Error("Unexpected error:", err) } - if !strings.Contains(output.String(), "This flag is deprecated") { - t.Errorf("Expected to contain deprecated message, but got %q", output.String()) + checkStringContains(t, output, "This flag is deprecated") +} + +func TestTraverseWithParentFlags(t *testing.T) { + rootCmd := &Command{Use: "root", TraverseChildren: true} + rootCmd.Flags().String("str", "", "") + rootCmd.Flags().BoolP("bool", "b", false, "") + + childCmd := &Command{Use: "child"} + childCmd.Flags().Int("int", -1, "") + + rootCmd.AddCommand(childCmd) + + c, args, err := rootCmd.Traverse([]string{"-b", "--str", "ok", "child", "--int"}) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + if len(args) != 1 && args[0] != "--add" { + t.Errorf("Wrong args: %v", args) + } + if c.Name() != childCmd.Name() { + t.Errorf("Expected command: %q, got: %q", childCmd.Name(), c.Name()) } } -// TestSetHelpCommand checks, if SetHelpCommand works correctly. -func TestSetHelpCommand(t *testing.T) { - c := &Command{Use: "c", Run: func(*Command, []string) {}} - output := new(bytes.Buffer) - c.SetOutput(output) - c.SetArgs([]string{"help"}) - - // Help will not be shown, if c has no subcommands. - c.AddCommand(&Command{ - Use: "empty", - Run: func(cmd *Command, args []string) {}, - }) +func TestTraverseNoParentFlags(t *testing.T) { + rootCmd := &Command{Use: "root", TraverseChildren: true} + rootCmd.Flags().String("foo", "", "foo things") - correctMessage := "WORKS" - c.SetHelpCommand(&Command{ - Use: "help [command]", - Short: "Help about any command", - Long: `Help provides help for any command in the application. - Simply type ` + c.Name() + ` help [path to command] for full details.`, - Run: func(c *Command, args []string) { c.Print(correctMessage) }, - }) + childCmd := &Command{Use: "child"} + childCmd.Flags().String("str", "", "") + rootCmd.AddCommand(childCmd) - if err := c.Execute(); err != nil { - t.Error("Unexpected error:", err) + c, args, err := rootCmd.Traverse([]string{"child"}) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + if len(args) != 0 { + t.Errorf("Wrong args %v", args) + } + if c.Name() != childCmd.Name() { + t.Errorf("Expected command: %q, got: %q", childCmd.Name(), c.Name()) + } +} + +func TestTraverseWithBadParentFlags(t *testing.T) { + rootCmd := &Command{Use: "root", TraverseChildren: true} + + childCmd := &Command{Use: "child"} + childCmd.Flags().String("str", "", "") + rootCmd.AddCommand(childCmd) + + expected := "unknown flag: --str" + + c, _, err := rootCmd.Traverse([]string{"--str", "ok", "child"}) + if err == nil || !strings.Contains(err.Error(), expected) { + t.Errorf("Expected error, %q, got %q", expected, err) + } + if c != nil { + t.Errorf("Expected nil command") } +} + +func TestTraverseWithBadChildFlag(t *testing.T) { + rootCmd := &Command{Use: "root", TraverseChildren: true} + rootCmd.Flags().String("str", "", "") + + childCmd := &Command{Use: "child"} + rootCmd.AddCommand(childCmd) + + // Expect no error because the last commands args shouldn't be parsed in + // Traverse. + c, args, err := rootCmd.Traverse([]string{"child", "--str"}) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + if len(args) != 1 && args[0] != "--str" { + t.Errorf("Wrong args: %v", args) + } + if c.Name() != childCmd.Name() { + t.Errorf("Expected command %q, got: %q", childCmd.Name(), c.Name()) + } +} + +func TestTraverseWithTwoSubcommands(t *testing.T) { + rootCmd := &Command{Use: "root", TraverseChildren: true} + + subCmd := &Command{Use: "sub", TraverseChildren: true} + rootCmd.AddCommand(subCmd) + + subsubCmd := &Command{ + Use: "subsub", + } + subCmd.AddCommand(subsubCmd) + + c, _, err := rootCmd.Traverse([]string{"sub", "subsub"}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if c.Name() != subsubCmd.Name() { + t.Fatalf("Expected command: %q, got %q", subsubCmd.Name(), c.Name()) + } +} + +// TestUpdateName checks if c.Name() updates on changed c.Use. +// Related to https://github.com/spf13/cobra/pull/422#discussion_r143918343. +func TestUpdateName(t *testing.T) { + c := &Command{Use: "name xyz"} + originalName := c.Name() - if output.String() != correctMessage { - t.Errorf("Expected to contain %q message, but got %q", correctMessage, output.String()) + c.Use = "changedName abc" + if originalName == c.Name() || c.Name() != "changedName" { + t.Error("c.Name() should be updated on changed c.Use") } } diff --git a/vendor/github.com/spf13/cobra/doc/cmd_test.go b/vendor/github.com/spf13/cobra/doc/cmd_test.go index a4b5568fa..d29c577df 100644 --- a/vendor/github.com/spf13/cobra/doc/cmd_test.go +++ b/vendor/github.com/spf13/cobra/doc/cmd_test.go @@ -1,145 +1,86 @@ package doc import ( - "bytes" - "fmt" - "runtime" "strings" "testing" "github.com/spf13/cobra" ) -var flagb1, flagb2, flagb3, flagbr, flagbp bool -var flags1, flags2a, flags2b, flags3 string -var flagi1, flagi2, flagi3, flagir int +func emptyRun(*cobra.Command, []string) {} -const strtwoParentHelp = "help message for parent flag strtwo" -const strtwoChildHelp = "help message for child flag strtwo" +func init() { + rootCmd.PersistentFlags().StringP("rootflag", "r", "two", "") + rootCmd.PersistentFlags().StringP("strtwo", "t", "two", "help message for parent flag strtwo") -var cmdEcho = &cobra.Command{ + echoCmd.PersistentFlags().StringP("strone", "s", "one", "help message for flag strone") + echoCmd.PersistentFlags().BoolP("persistentbool", "p", false, "help message for flag persistentbool") + echoCmd.Flags().IntP("intone", "i", 123, "help message for flag intone") + echoCmd.Flags().BoolP("boolone", "b", true, "help message for flag boolone") + + timesCmd.PersistentFlags().StringP("strtwo", "t", "2", "help message for child flag strtwo") + timesCmd.Flags().IntP("inttwo", "j", 234, "help message for flag inttwo") + timesCmd.Flags().BoolP("booltwo", "c", false, "help message for flag booltwo") + + printCmd.PersistentFlags().StringP("strthree", "s", "three", "help message for flag strthree") + printCmd.Flags().IntP("intthree", "i", 345, "help message for flag intthree") + printCmd.Flags().BoolP("boolthree", "b", true, "help message for flag boolthree") + + echoCmd.AddCommand(timesCmd, echoSubCmd, deprecatedCmd) + rootCmd.AddCommand(printCmd, echoCmd) +} + +var rootCmd = &cobra.Command{ + Use: "root", + Short: "Root short description", + Long: "Root long description", + Run: emptyRun, +} + +var echoCmd = &cobra.Command{ Use: "echo [string to echo]", Aliases: []string{"say"}, Short: "Echo anything to the screen", - Long: `an utterly useless command for testing.`, + Long: "an utterly useless command for testing", Example: "Just run cobra-test echo", } -var cmdEchoSub = &cobra.Command{ +var echoSubCmd = &cobra.Command{ Use: "echosub [string to print]", Short: "second sub command for echo", - Long: `an absolutely utterly useless command for testing gendocs!.`, - Run: func(cmd *cobra.Command, args []string) {}, + Long: "an absolutely utterly useless command for testing gendocs!.", + Run: emptyRun, +} + +var timesCmd = &cobra.Command{ + Use: "times [# times] [string to echo]", + SuggestFor: []string{"counts"}, + Short: "Echo anything to the screen more times", + Long: `a slightly useless command for testing.`, + Run: emptyRun, } -var cmdDeprecated = &cobra.Command{ +var deprecatedCmd = &cobra.Command{ Use: "deprecated [can't do anything here]", Short: "A command which is deprecated", Long: `an absolutely utterly useless command for testing deprecation!.`, Deprecated: "Please use echo instead", } -var cmdTimes = &cobra.Command{ - Use: "times [# times] [string to echo]", - SuggestFor: []string{"counts"}, - Short: "Echo anything to the screen more times", - Long: `a slightly useless command for testing.`, - PersistentPreRun: func(cmd *cobra.Command, args []string) {}, - Run: func(cmd *cobra.Command, args []string) {}, -} - -var cmdPrint = &cobra.Command{ +var printCmd = &cobra.Command{ Use: "print [string to print]", Short: "Print anything to the screen", Long: `an absolutely utterly useless command for testing.`, } -var cmdRootNoRun = &cobra.Command{ - Use: "cobra-test", - Short: "The root can run its own function", - Long: "The root description for help", -} - -var cmdRootSameName = &cobra.Command{ - Use: "print", - Short: "Root with the same name as a subcommand", - Long: "The root description for help", -} - -var cmdRootWithRun = &cobra.Command{ - Use: "cobra-test", - Short: "The root can run its own function", - Long: "The root description for help", -} - -var cmdSubNoRun = &cobra.Command{ - Use: "subnorun", - Short: "A subcommand without a Run function", - Long: "A long output about a subcommand without a Run function", -} - -var cmdVersion1 = &cobra.Command{ - Use: "version", - Short: "Print the version number", - Long: `First version of the version command`, -} - -var cmdVersion2 = &cobra.Command{ - Use: "version", - Short: "Print the version number", - Long: `Second version of the version command`, -} - -func flagInit() { - cmdEcho.ResetFlags() - cmdPrint.ResetFlags() - cmdTimes.ResetFlags() - cmdRootNoRun.ResetFlags() - cmdRootSameName.ResetFlags() - cmdRootWithRun.ResetFlags() - cmdSubNoRun.ResetFlags() - cmdRootNoRun.PersistentFlags().StringVarP(&flags2a, "strtwo", "t", "two", strtwoParentHelp) - cmdEcho.Flags().IntVarP(&flagi1, "intone", "i", 123, "help message for flag intone") - cmdTimes.Flags().IntVarP(&flagi2, "inttwo", "j", 234, "help message for flag inttwo") - cmdPrint.Flags().IntVarP(&flagi3, "intthree", "i", 345, "help message for flag intthree") - cmdEcho.PersistentFlags().StringVarP(&flags1, "strone", "s", "one", "help message for flag strone") - cmdEcho.PersistentFlags().BoolVarP(&flagbp, "persistentbool", "p", false, "help message for flag persistentbool") - cmdTimes.PersistentFlags().StringVarP(&flags2b, "strtwo", "t", "2", strtwoChildHelp) - cmdPrint.PersistentFlags().StringVarP(&flags3, "strthree", "s", "three", "help message for flag strthree") - cmdEcho.Flags().BoolVarP(&flagb1, "boolone", "b", true, "help message for flag boolone") - cmdTimes.Flags().BoolVarP(&flagb2, "booltwo", "c", false, "help message for flag booltwo") - cmdPrint.Flags().BoolVarP(&flagb3, "boolthree", "b", true, "help message for flag boolthree") - cmdVersion1.ResetFlags() - cmdVersion2.ResetFlags() -} - -func initializeWithRootCmd() *cobra.Command { - cmdRootWithRun.ResetCommands() - flagInit() - cmdRootWithRun.Flags().BoolVarP(&flagbr, "boolroot", "b", false, "help message for flag boolroot") - cmdRootWithRun.Flags().IntVarP(&flagir, "introot", "i", 321, "help message for flag introot") - return cmdRootWithRun -} - -func checkStringContains(t *testing.T, found, expected string) { - if !strings.Contains(found, expected) { - logErr(t, found, expected) +func checkStringContains(t *testing.T, got, expected string) { + if !strings.Contains(got, expected) { + t.Errorf("Expected to contain: \n %v\nGot:\n %v\n", expected, got) } } -func checkStringOmits(t *testing.T, found, expected string) { - if strings.Contains(found, expected) { - logErr(t, found, expected) - } -} - -func logErr(t *testing.T, found, expected string) { - out := new(bytes.Buffer) - - _, _, line, ok := runtime.Caller(2) - if ok { - fmt.Fprintf(out, "Line: %d ", line) +func checkStringOmits(t *testing.T, got, expected string) { + if strings.Contains(got, expected) { + t.Errorf("Expected to not contain: \n %v\nGot: %v", expected, got) } - fmt.Fprintf(out, "Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - t.Errorf(out.String()) } diff --git a/vendor/github.com/spf13/cobra/doc/man_docs_test.go b/vendor/github.com/spf13/cobra/doc/man_docs_test.go index 87991063e..62f85e47f 100644 --- a/vendor/github.com/spf13/cobra/doc/man_docs_test.go +++ b/vendor/github.com/spf13/cobra/doc/man_docs_test.go @@ -18,135 +18,97 @@ func translate(in string) string { } func TestGenManDoc(t *testing.T) { - c := initializeWithRootCmd() - // Need two commands to run the command alphabetical sort - cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated) - c.AddCommand(cmdPrint, cmdEcho) - cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp) - - out := new(bytes.Buffer) - header := &GenManHeader{ Title: "Project", Section: "2", } + // We generate on a subcommand so we have both subcommands and parents - if err := GenMan(cmdEcho, header, out); err != nil { + buf := new(bytes.Buffer) + if err := GenMan(echoCmd, header, buf); err != nil { t.Fatal(err) } - found := out.String() + output := buf.String() // Make sure parent has - in CommandPath() in SEE ALSO: - parentPath := cmdEcho.Parent().CommandPath() + parentPath := echoCmd.Parent().CommandPath() dashParentPath := strings.Replace(parentPath, " ", "-", -1) expected := translate(dashParentPath) expected = expected + "(" + header.Section + ")" - checkStringContains(t, found, expected) - - // Our description - expected = translate(cmdEcho.Name()) - checkStringContains(t, found, expected) - - // Better have our example - expected = translate(cmdEcho.Name()) - checkStringContains(t, found, expected) - - // A local flag - expected = "boolone" - checkStringContains(t, found, expected) - - // persistent flag on parent - expected = "rootflag" - checkStringContains(t, found, expected) - - // We better output info about our parent - expected = translate(cmdRootWithRun.Name()) - checkStringContains(t, found, expected) - - // And about subcommands - expected = translate(cmdEchoSub.Name()) - checkStringContains(t, found, expected) - - unexpected := translate(cmdDeprecated.Name()) - checkStringOmits(t, found, unexpected) - - // auto generated - expected = translate("Auto generated") - checkStringContains(t, found, expected) + checkStringContains(t, output, expected) + + checkStringContains(t, output, translate(echoCmd.Name())) + checkStringContains(t, output, translate(echoCmd.Name())) + checkStringContains(t, output, "boolone") + checkStringContains(t, output, "rootflag") + checkStringContains(t, output, translate(rootCmd.Name())) + checkStringContains(t, output, translate(echoSubCmd.Name())) + checkStringOmits(t, output, translate(deprecatedCmd.Name())) + checkStringContains(t, output, translate("Auto generated")) } func TestGenManNoGenTag(t *testing.T) { - c := initializeWithRootCmd() - // Need two commands to run the command alphabetical sort - cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated) - c.AddCommand(cmdPrint, cmdEcho) - cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp) - cmdEcho.DisableAutoGenTag = true - out := new(bytes.Buffer) + echoCmd.DisableAutoGenTag = true + defer func() { echoCmd.DisableAutoGenTag = false }() header := &GenManHeader{ Title: "Project", Section: "2", } + // We generate on a subcommand so we have both subcommands and parents - if err := GenMan(cmdEcho, header, out); err != nil { + buf := new(bytes.Buffer) + if err := GenMan(echoCmd, header, buf); err != nil { t.Fatal(err) } - found := out.String() + output := buf.String() unexpected := translate("#HISTORY") - checkStringOmits(t, found, unexpected) + checkStringOmits(t, output, unexpected) } func TestGenManSeeAlso(t *testing.T) { - noop := func(cmd *cobra.Command, args []string) {} - - top := &cobra.Command{Use: "top", Run: noop} - aaa := &cobra.Command{Use: "aaa", Run: noop, Hidden: true} // #229 - bbb := &cobra.Command{Use: "bbb", Run: noop} - ccc := &cobra.Command{Use: "ccc", Run: noop} - top.AddCommand(aaa, bbb, ccc) + rootCmd := &cobra.Command{Use: "root", Run: emptyRun} + aCmd := &cobra.Command{Use: "aaa", Run: emptyRun, Hidden: true} // #229 + bCmd := &cobra.Command{Use: "bbb", Run: emptyRun} + cCmd := &cobra.Command{Use: "ccc", Run: emptyRun} + rootCmd.AddCommand(aCmd, bCmd, cCmd) - out := new(bytes.Buffer) + buf := new(bytes.Buffer) header := &GenManHeader{} - if err := GenMan(top, header, out); err != nil { + if err := GenMan(rootCmd, header, buf); err != nil { t.Fatal(err) } + scanner := bufio.NewScanner(buf) - scanner := bufio.NewScanner(out) - - if err := AssertLineFound(scanner, ".SH SEE ALSO"); err != nil { - t.Fatal(fmt.Errorf("Couldn't find SEE ALSO section header: %s", err.Error())) + if err := assertLineFound(scanner, ".SH SEE ALSO"); err != nil { + t.Fatalf("Couldn't find SEE ALSO section header: %v", err) } - - if err := AssertNextLineEquals(scanner, ".PP"); err != nil { - t.Fatal(fmt.Errorf("First line after SEE ALSO wasn't break-indent: %s", err.Error())) + if err := assertNextLineEquals(scanner, ".PP"); err != nil { + t.Fatalf("First line after SEE ALSO wasn't break-indent: %v", err) } - - if err := AssertNextLineEquals(scanner, `\fBtop\-bbb(1)\fP, \fBtop\-ccc(1)\fP`); err != nil { - t.Fatal(fmt.Errorf("Second line after SEE ALSO wasn't correct: %s", err.Error())) + if err := assertNextLineEquals(scanner, `\fBroot\-bbb(1)\fP, \fBroot\-ccc(1)\fP`); err != nil { + t.Fatalf("Second line after SEE ALSO wasn't correct: %v", err) } } func TestManPrintFlagsHidesShortDeperecated(t *testing.T) { - cmd := &cobra.Command{} - flags := cmd.Flags() - flags.StringP("foo", "f", "default", "Foo flag") - flags.MarkShorthandDeprecated("foo", "don't use it no more") + c := &cobra.Command{} + c.Flags().StringP("foo", "f", "default", "Foo flag") + c.Flags().MarkShorthandDeprecated("foo", "don't use it no more") - out := new(bytes.Buffer) - manPrintFlags(out, flags) + buf := new(bytes.Buffer) + manPrintFlags(buf, c.Flags()) + got := buf.String() expected := "**--foo**=\"default\"\n\tFoo flag\n\n" - if out.String() != expected { - t.Fatalf("Expected %s, but got %s", expected, out.String()) + if got != expected { + t.Errorf("Expected %v, got %v", expected, got) } } func TestGenManTree(t *testing.T) { - cmd := &cobra.Command{ - Use: "do [OPTIONS] arg1 arg2", - } + c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"} header := &GenManHeader{Section: "2"} tmpdir, err := ioutil.TempDir("", "test-gen-man-tree") if err != nil { @@ -154,7 +116,7 @@ func TestGenManTree(t *testing.T) { } defer os.RemoveAll(tmpdir) - if err := GenManTree(cmd, header, tmpdir); err != nil { + if err := GenManTree(c, header, tmpdir); err != nil { t.Fatalf("GenManTree failed: %s", err.Error()) } @@ -167,7 +129,7 @@ func TestGenManTree(t *testing.T) { } } -func AssertLineFound(scanner *bufio.Scanner, expectedLine string) error { +func assertLineFound(scanner *bufio.Scanner, expectedLine string) error { for scanner.Scan() { line := scanner.Text() if line == expectedLine { @@ -176,30 +138,29 @@ func AssertLineFound(scanner *bufio.Scanner, expectedLine string) error { } if err := scanner.Err(); err != nil { - return fmt.Errorf("AssertLineFound: scan failed: %s", err.Error()) + return fmt.Errorf("scan failed: %s", err) } - return fmt.Errorf("AssertLineFound: hit EOF before finding %#v", expectedLine) + return fmt.Errorf("hit EOF before finding %v", expectedLine) } -func AssertNextLineEquals(scanner *bufio.Scanner, expectedLine string) error { +func assertNextLineEquals(scanner *bufio.Scanner, expectedLine string) error { if scanner.Scan() { line := scanner.Text() if line == expectedLine { return nil } - return fmt.Errorf("AssertNextLineEquals: got %#v, not %#v", line, expectedLine) + return fmt.Errorf("got %v, not %v", line, expectedLine) } if err := scanner.Err(); err != nil { - return fmt.Errorf("AssertNextLineEquals: scan failed: %s", err.Error()) + return fmt.Errorf("scan failed: %v", err) } - return fmt.Errorf("AssertNextLineEquals: hit EOF before finding %#v", expectedLine) + return fmt.Errorf("hit EOF before finding %v", expectedLine) } func BenchmarkGenManToFile(b *testing.B) { - c := initializeWithRootCmd() file, err := ioutil.TempFile("", "") if err != nil { b.Fatal(err) @@ -209,7 +170,7 @@ func BenchmarkGenManToFile(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - if err := GenMan(c, nil, file); err != nil { + if err := GenMan(rootCmd, nil, file); err != nil { b.Fatal(err) } } diff --git a/vendor/github.com/spf13/cobra/doc/md_docs_test.go b/vendor/github.com/spf13/cobra/doc/md_docs_test.go index ba6b9a46e..b0fa68c0b 100644 --- a/vendor/github.com/spf13/cobra/doc/md_docs_test.go +++ b/vendor/github.com/spf13/cobra/doc/md_docs_test.go @@ -5,100 +5,51 @@ import ( "io/ioutil" "os" "path/filepath" - "strings" "testing" "github.com/spf13/cobra" ) func TestGenMdDoc(t *testing.T) { - c := initializeWithRootCmd() - // Need two commands to run the command alphabetical sort - cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated) - c.AddCommand(cmdPrint, cmdEcho) - cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp) - - out := new(bytes.Buffer) - - // We generate on s subcommand so we have both subcommands and parents - if err := GenMarkdown(cmdEcho, out); err != nil { + // We generate on subcommand so we have both subcommands and parents. + buf := new(bytes.Buffer) + if err := GenMarkdown(echoCmd, buf); err != nil { t.Fatal(err) } - found := out.String() - - // Our description - expected := cmdEcho.Long - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // Better have our example - expected = cmdEcho.Example - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // A local flag - expected = "boolone" - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // persistent flag on parent - expected = "rootflag" - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // We better output info about our parent - expected = cmdRootWithRun.Short - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // And about subcommands - expected = cmdEchoSub.Short - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - unexpected := cmdDeprecated.Short - if strings.Contains(found, unexpected) { - t.Errorf("Unexpected response.\nFound: %v\nBut should not have!!\n", unexpected) - } + output := buf.String() + + checkStringContains(t, output, echoCmd.Long) + checkStringContains(t, output, echoCmd.Example) + checkStringContains(t, output, "boolone") + checkStringContains(t, output, "rootflag") + checkStringContains(t, output, rootCmd.Short) + checkStringContains(t, output, echoSubCmd.Short) + checkStringOmits(t, output, deprecatedCmd.Short) } func TestGenMdNoTag(t *testing.T) { - c := initializeWithRootCmd() - // Need two commands to run the command alphabetical sort - cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated) - c.AddCommand(cmdPrint, cmdEcho) - c.DisableAutoGenTag = true - cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp) - out := new(bytes.Buffer) + rootCmd.DisableAutoGenTag = true + defer func() { rootCmd.DisableAutoGenTag = false }() - if err := GenMarkdown(c, out); err != nil { + buf := new(bytes.Buffer) + if err := GenMarkdown(rootCmd, buf); err != nil { t.Fatal(err) } - found := out.String() - - unexpected := "Auto generated" - checkStringOmits(t, found, unexpected) + output := buf.String() + checkStringOmits(t, output, "Auto generated") } func TestGenMdTree(t *testing.T) { - cmd := &cobra.Command{ - Use: "do [OPTIONS] arg1 arg2", - } + c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"} tmpdir, err := ioutil.TempDir("", "test-gen-md-tree") if err != nil { - t.Fatalf("Failed to create tmpdir: %s", err.Error()) + t.Fatalf("Failed to create tmpdir: %v", err) } defer os.RemoveAll(tmpdir) - if err := GenMarkdownTree(cmd, tmpdir); err != nil { - t.Fatalf("GenMarkdownTree failed: %s", err.Error()) + if err := GenMarkdownTree(c, tmpdir); err != nil { + t.Fatalf("GenMarkdownTree failed: %v", err) } if _, err := os.Stat(filepath.Join(tmpdir, "do.md")); err != nil { @@ -107,7 +58,6 @@ func TestGenMdTree(t *testing.T) { } func BenchmarkGenMarkdownToFile(b *testing.B) { - c := initializeWithRootCmd() file, err := ioutil.TempFile("", "") if err != nil { b.Fatal(err) @@ -117,7 +67,7 @@ func BenchmarkGenMarkdownToFile(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - if err := GenMarkdown(c, file); err != nil { + if err := GenMarkdown(rootCmd, file); err != nil { b.Fatal(err) } } diff --git a/vendor/github.com/spf13/cobra/doc/rest_docs.go b/vendor/github.com/spf13/cobra/doc/rest_docs.go new file mode 100644 index 000000000..4913e3ee2 --- /dev/null +++ b/vendor/github.com/spf13/cobra/doc/rest_docs.go @@ -0,0 +1,185 @@ +//Copyright 2015 Red Hat Inc. All rights reserved. +// +// 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 doc + +import ( + "bytes" + "fmt" + "io" + "os" + "path/filepath" + "sort" + "strings" + "time" + + "github.com/spf13/cobra" +) + +func printOptionsReST(buf *bytes.Buffer, cmd *cobra.Command, name string) error { + flags := cmd.NonInheritedFlags() + flags.SetOutput(buf) + if flags.HasFlags() { + buf.WriteString("Options\n") + buf.WriteString("~~~~~~~\n\n::\n\n") + flags.PrintDefaults() + buf.WriteString("\n") + } + + parentFlags := cmd.InheritedFlags() + parentFlags.SetOutput(buf) + if parentFlags.HasFlags() { + buf.WriteString("Options inherited from parent commands\n") + buf.WriteString("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n::\n\n") + parentFlags.PrintDefaults() + buf.WriteString("\n") + } + return nil +} + +// linkHandler for default ReST hyperlink markup +func defaultLinkHandler(name, ref string) string { + return fmt.Sprintf("`%s <%s.rst>`_", name, ref) +} + +// GenReST creates reStructured Text output. +func GenReST(cmd *cobra.Command, w io.Writer) error { + return GenReSTCustom(cmd, w, defaultLinkHandler) +} + +// GenReSTCustom creates custom reStructured Text output. +func GenReSTCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string, string) string) error { + cmd.InitDefaultHelpCmd() + cmd.InitDefaultHelpFlag() + + buf := new(bytes.Buffer) + name := cmd.CommandPath() + + short := cmd.Short + long := cmd.Long + if len(long) == 0 { + long = short + } + ref := strings.Replace(name, " ", "_", -1) + + buf.WriteString(".. _" + ref + ":\n\n") + buf.WriteString(name + "\n") + buf.WriteString(strings.Repeat("-", len(name)) + "\n\n") + buf.WriteString(short + "\n\n") + buf.WriteString("Synopsis\n") + buf.WriteString("~~~~~~~~\n\n") + buf.WriteString("\n" + long + "\n\n") + + if cmd.Runnable() { + buf.WriteString(fmt.Sprintf("::\n\n %s\n\n", cmd.UseLine())) + } + + if len(cmd.Example) > 0 { + buf.WriteString("Examples\n") + buf.WriteString("~~~~~~~~\n\n") + buf.WriteString(fmt.Sprintf("::\n\n%s\n\n", indentString(cmd.Example, " "))) + } + + if err := printOptionsReST(buf, cmd, name); err != nil { + return err + } + if hasSeeAlso(cmd) { + buf.WriteString("SEE ALSO\n") + buf.WriteString("~~~~~~~~\n\n") + if cmd.HasParent() { + parent := cmd.Parent() + pname := parent.CommandPath() + ref = strings.Replace(pname, " ", "_", -1) + buf.WriteString(fmt.Sprintf("* %s \t - %s\n", linkHandler(pname, ref), parent.Short)) + cmd.VisitParents(func(c *cobra.Command) { + if c.DisableAutoGenTag { + cmd.DisableAutoGenTag = c.DisableAutoGenTag + } + }) + } + + children := cmd.Commands() + sort.Sort(byName(children)) + + for _, child := range children { + if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() { + continue + } + cname := name + " " + child.Name() + ref = strings.Replace(cname, " ", "_", -1) + buf.WriteString(fmt.Sprintf("* %s \t - %s\n", linkHandler(cname, ref), child.Short)) + } + buf.WriteString("\n") + } + if !cmd.DisableAutoGenTag { + buf.WriteString("*Auto generated by spf13/cobra on " + time.Now().Format("2-Jan-2006") + "*\n") + } + _, err := buf.WriteTo(w) + return err +} + +// GenReSTTree will generate a ReST page for this command and all +// descendants in the directory given. +// This function may not work correctly if your command names have `-` in them. +// If you have `cmd` with two subcmds, `sub` and `sub-third`, +// and `sub` has a subcommand called `third`, it is undefined which +// help output will be in the file `cmd-sub-third.1`. +func GenReSTTree(cmd *cobra.Command, dir string) error { + emptyStr := func(s string) string { return "" } + return GenReSTTreeCustom(cmd, dir, emptyStr, defaultLinkHandler) +} + +// GenReSTTreeCustom is the the same as GenReSTTree, but +// with custom filePrepender and linkHandler. +func GenReSTTreeCustom(cmd *cobra.Command, dir string, filePrepender func(string) string, linkHandler func(string, string) string) error { + for _, c := range cmd.Commands() { + if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() { + continue + } + if err := GenReSTTreeCustom(c, dir, filePrepender, linkHandler); err != nil { + return err + } + } + + basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".rst" + filename := filepath.Join(dir, basename) + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() + + if _, err := io.WriteString(f, filePrepender(filename)); err != nil { + return err + } + if err := GenReSTCustom(cmd, f, linkHandler); err != nil { + return err + } + return nil +} + +// adapted from: https://github.com/kr/text/blob/main/indent.go +func indentString(s, p string) string { + var res []byte + b := []byte(s) + prefix := []byte(p) + bol := true + for _, c := range b { + if bol && c != '\n' { + res = append(res, prefix...) + } + res = append(res, c) + bol = c == '\n' + } + return string(res) +} diff --git a/vendor/github.com/spf13/cobra/doc/rest_docs.md b/vendor/github.com/spf13/cobra/doc/rest_docs.md new file mode 100644 index 000000000..6098430ef --- /dev/null +++ b/vendor/github.com/spf13/cobra/doc/rest_docs.md @@ -0,0 +1,114 @@ +# Generating ReStructured Text Docs For Your Own cobra.Command + +Generating ReST pages from a cobra command is incredibly easy. An example is as follows: + +```go +package main + +import ( + "log" + + "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" +) + +func main() { + cmd := &cobra.Command{ + Use: "test", + Short: "my test program", + } + err := doc.GenReSTTree(cmd, "/tmp") + if err != nil { + log.Fatal(err) + } +} +``` + +That will get you a ReST document `/tmp/test.rst` + +## Generate ReST docs for the entire command tree + +This program can actually generate docs for the kubectl command in the kubernetes project + +```go +package main + +import ( + "log" + "io/ioutil" + "os" + + "k8s.io/kubernetes/pkg/kubectl/cmd" + cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + + "github.com/spf13/cobra/doc" +) + +func main() { + kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard) + err := doc.GenReSTTree(kubectl, "./") + if err != nil { + log.Fatal(err) + } +} +``` + +This will generate a whole series of files, one for each command in the tree, in the directory specified (in this case "./") + +## Generate ReST docs for a single command + +You may wish to have more control over the output, or only generate for a single command, instead of the entire command tree. If this is the case you may prefer to `GenReST` instead of `GenReSTTree` + +```go + out := new(bytes.Buffer) + err := doc.GenReST(cmd, out) + if err != nil { + log.Fatal(err) + } +``` + +This will write the ReST doc for ONLY "cmd" into the out, buffer. + +## Customize the output + +Both `GenReST` and `GenReSTTree` have alternate versions with callbacks to get some control of the output: + +```go +func GenReSTTreeCustom(cmd *Command, dir string, filePrepender func(string) string, linkHandler func(string, string) string) error { + //... +} +``` + +```go +func GenReSTCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string, string) string) error { + //... +} +``` + +The `filePrepender` will prepend the return value given the full filepath to the rendered ReST file. A common use case is to add front matter to use the generated documentation with [Hugo](http://gohugo.io/): + +```go +const fmTemplate = `--- +date: %s +title: "%s" +slug: %s +url: %s +--- +` +filePrepender := func(filename string) string { + now := time.Now().Format(time.RFC3339) + name := filepath.Base(filename) + base := strings.TrimSuffix(name, path.Ext(name)) + url := "/commands/" + strings.ToLower(base) + "/" + return fmt.Sprintf(fmTemplate, now, strings.Replace(base, "_", " ", -1), base, url) +} +``` + +The `linkHandler` can be used to customize the rendered links to the commands, given a command name and reference. This is useful while converting rst to html or while generating documentation with tools like Sphinx where `:ref:` is used: + +```go +// Sphinx cross-referencing format +linkHandler := func(name, ref string) string { + return fmt.Sprintf(":ref:`%s <%s>`", name, ref) +} +``` diff --git a/vendor/github.com/spf13/cobra/doc/rest_docs_test.go b/vendor/github.com/spf13/cobra/doc/rest_docs_test.go new file mode 100644 index 000000000..aa3186e8f --- /dev/null +++ b/vendor/github.com/spf13/cobra/doc/rest_docs_test.go @@ -0,0 +1,76 @@ +package doc + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/spf13/cobra" +) + +func TestGenRSTDoc(t *testing.T) { + // We generate on a subcommand so we have both subcommands and parents + buf := new(bytes.Buffer) + if err := GenReST(echoCmd, buf); err != nil { + t.Fatal(err) + } + output := buf.String() + + checkStringContains(t, output, echoCmd.Long) + checkStringContains(t, output, echoCmd.Example) + checkStringContains(t, output, "boolone") + checkStringContains(t, output, "rootflag") + checkStringContains(t, output, rootCmd.Short) + checkStringContains(t, output, echoSubCmd.Short) + checkStringOmits(t, output, deprecatedCmd.Short) +} + +func TestGenRSTNoTag(t *testing.T) { + rootCmd.DisableAutoGenTag = true + defer func() { rootCmd.DisableAutoGenTag = false }() + + buf := new(bytes.Buffer) + if err := GenReST(rootCmd, buf); err != nil { + t.Fatal(err) + } + output := buf.String() + + unexpected := "Auto generated" + checkStringOmits(t, output, unexpected) +} + +func TestGenRSTTree(t *testing.T) { + c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"} + + tmpdir, err := ioutil.TempDir("", "test-gen-rst-tree") + if err != nil { + t.Fatalf("Failed to create tmpdir: %s", err.Error()) + } + defer os.RemoveAll(tmpdir) + + if err := GenReSTTree(c, tmpdir); err != nil { + t.Fatalf("GenReSTTree failed: %s", err.Error()) + } + + if _, err := os.Stat(filepath.Join(tmpdir, "do.rst")); err != nil { + t.Fatalf("Expected file 'do.rst' to exist") + } +} + +func BenchmarkGenReSTToFile(b *testing.B) { + file, err := ioutil.TempFile("", "") + if err != nil { + b.Fatal(err) + } + defer os.Remove(file.Name()) + defer file.Close() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if err := GenReST(rootCmd, file); err != nil { + b.Fatal(err) + } + } +} diff --git a/vendor/github.com/spf13/cobra/doc/yaml_docs_test.go b/vendor/github.com/spf13/cobra/doc/yaml_docs_test.go index 29e985e40..c5a63594f 100644 --- a/vendor/github.com/spf13/cobra/doc/yaml_docs_test.go +++ b/vendor/github.com/spf13/cobra/doc/yaml_docs_test.go @@ -5,92 +5,42 @@ import ( "io/ioutil" "os" "path/filepath" - "strings" "testing" "github.com/spf13/cobra" ) func TestGenYamlDoc(t *testing.T) { - c := initializeWithRootCmd() - // Need two commands to run the command alphabetical sort - cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated) - c.AddCommand(cmdPrint, cmdEcho) - cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp) - - out := new(bytes.Buffer) - // We generate on s subcommand so we have both subcommands and parents - if err := GenYaml(cmdEcho, out); err != nil { + buf := new(bytes.Buffer) + if err := GenYaml(echoCmd, buf); err != nil { t.Fatal(err) } - found := out.String() - - // Our description - expected := cmdEcho.Long - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // Better have our example - expected = cmdEcho.Example - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // A local flag - expected = "boolone" - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // persistent flag on parent - expected = "rootflag" - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // We better output info about our parent - expected = cmdRootWithRun.Short - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // And about subcommands - expected = cmdEchoSub.Short - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - unexpected := cmdDeprecated.Short - if strings.Contains(found, unexpected) { - t.Errorf("Unexpected response.\nFound: %v\nBut should not have!!\n", unexpected) - } + output := buf.String() + + checkStringContains(t, output, echoCmd.Long) + checkStringContains(t, output, echoCmd.Example) + checkStringContains(t, output, "boolone") + checkStringContains(t, output, "rootflag") + checkStringContains(t, output, rootCmd.Short) + checkStringContains(t, output, echoSubCmd.Short) } func TestGenYamlNoTag(t *testing.T) { - c := initializeWithRootCmd() - // Need two commands to run the command alphabetical sort - cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated) - c.AddCommand(cmdPrint, cmdEcho) - c.DisableAutoGenTag = true - cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp) - out := new(bytes.Buffer) + rootCmd.DisableAutoGenTag = true + defer func() { rootCmd.DisableAutoGenTag = false }() - if err := GenYaml(c, out); err != nil { + buf := new(bytes.Buffer) + if err := GenYaml(rootCmd, buf); err != nil { t.Fatal(err) } - found := out.String() - - unexpected := "Auto generated" - checkStringOmits(t, found, unexpected) + output := buf.String() + checkStringOmits(t, output, "Auto generated") } func TestGenYamlTree(t *testing.T) { - cmd := &cobra.Command{ - Use: "do [OPTIONS] arg1 arg2", - } + c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"} tmpdir, err := ioutil.TempDir("", "test-gen-yaml-tree") if err != nil { @@ -98,7 +48,7 @@ func TestGenYamlTree(t *testing.T) { } defer os.RemoveAll(tmpdir) - if err := GenYamlTree(cmd, tmpdir); err != nil { + if err := GenYamlTree(c, tmpdir); err != nil { t.Fatalf("GenYamlTree failed: %s", err.Error()) } @@ -108,7 +58,6 @@ func TestGenYamlTree(t *testing.T) { } func BenchmarkGenYamlToFile(b *testing.B) { - c := initializeWithRootCmd() file, err := ioutil.TempFile("", "") if err != nil { b.Fatal(err) @@ -118,7 +67,7 @@ func BenchmarkGenYamlToFile(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - if err := GenYaml(c, file); err != nil { + if err := GenYaml(rootCmd, file); err != nil { b.Fatal(err) } } diff --git a/vendor/github.com/spf13/cobra/zsh_completions_test.go b/vendor/github.com/spf13/cobra/zsh_completions_test.go index 08b851591..34e69496f 100644 --- a/vendor/github.com/spf13/cobra/zsh_completions_test.go +++ b/vendor/github.com/spf13/cobra/zsh_completions_test.go @@ -77,10 +77,11 @@ func TestZshCompletion(t *testing.T) { t.Run(tc.name, func(t *testing.T) { buf := new(bytes.Buffer) tc.root.GenZshCompletion(buf) - completion := buf.String() + output := buf.String() + for _, expectedExpression := range tc.expectedExpressions { - if !strings.Contains(completion, expectedExpression) { - t.Errorf("expected completion to contain '%v' somewhere; got '%v'", expectedExpression, completion) + if !strings.Contains(output, expectedExpression) { + t.Errorf("Expected completion to contain %q somewhere; got %q", expectedExpression, output) } } }) diff --git a/vendor/github.com/spf13/viper/README.md b/vendor/github.com/spf13/viper/README.md index 848d92d6b..64bf47435 100644 --- a/vendor/github.com/spf13/viper/README.md +++ b/vendor/github.com/spf13/viper/README.md @@ -185,7 +185,7 @@ with ENV: * `AutomaticEnv()` * `BindEnv(string...) : error` * `SetEnvPrefix(string)` - * `SetEnvReplacer(string...) *strings.Replacer` + * `SetEnvKeyReplacer(string...) *strings.Replacer` _When working with ENV variables, it’s important to recognize that Viper treats ENV variables as case sensitive._ @@ -212,7 +212,7 @@ time a `viper.Get` request is made. It will apply the following rules. It will check for a environment variable with a name matching the key uppercased and prefixed with the `EnvPrefix` if set. -`SetEnvReplacer` allows you to use a `strings.Replacer` object to rewrite Env +`SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env keys to an extent. This is useful if you want to use `-` or something in your `Get()` calls, but want your environmental variables to use `_` delimiters. An example of using it can be found in `viper_test.go`. diff --git a/vendor/github.com/spf13/viper/util.go b/vendor/github.com/spf13/viper/util.go index 3ebada91a..c784dad40 100644 --- a/vendor/github.com/spf13/viper/util.go +++ b/vendor/github.com/spf13/viper/util.go @@ -24,6 +24,7 @@ import ( "github.com/hashicorp/hcl" "github.com/magiconair/properties" toml "github.com/pelletier/go-toml" + "github.com/spf13/afero" "github.com/spf13/cast" jww "github.com/spf13/jwalterweatherman" "gopkg.in/yaml.v2" @@ -121,8 +122,8 @@ func absPathify(inPath string) string { } // Check if File / Directory Exists -func exists(path string) (bool, error) { - _, err := v.fs.Stat(path) +func exists(fs afero.Fs, path string) (bool, error) { + _, err := fs.Stat(path) if err == nil { return true, nil } diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go index 2a221e53c..64f006a39 100644 --- a/vendor/github.com/spf13/viper/viper.go +++ b/vendor/github.com/spf13/viper/viper.go @@ -747,13 +747,16 @@ func (v *Viper) Unmarshal(rawVal interface{}) error { } // defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot -// of time.Duration values +// of time.Duration values & string slices func defaultDecoderConfig(output interface{}) *mapstructure.DecoderConfig { return &mapstructure.DecoderConfig{ Metadata: nil, Result: output, WeaklyTypedInput: true, - DecodeHook: mapstructure.StringToTimeDurationHookFunc(), + DecodeHook: mapstructure.ComposeDecodeHookFunc( + mapstructure.StringToTimeDurationHookFunc(), + mapstructure.StringToSliceHookFunc(","), + ), } } @@ -1534,7 +1537,7 @@ func (v *Viper) searchInPath(in string) (filename string) { jww.DEBUG.Println("Searching for config in ", in) for _, ext := range SupportedExts { jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext)) - if b, _ := exists(filepath.Join(in, v.configName+"."+ext)); b { + if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b { jww.DEBUG.Println("Found: ", filepath.Join(in, v.configName+"."+ext)) return filepath.Join(in, v.configName+"."+ext) } diff --git a/vendor/github.com/spf13/viper/viper_test.go b/vendor/github.com/spf13/viper/viper_test.go index 774ca1168..7050d5abd 100644 --- a/vendor/github.com/spf13/viper/viper_test.go +++ b/vendor/github.com/spf13/viper/viper_test.go @@ -417,7 +417,7 @@ func TestAutoEnvWithPrefix(t *testing.T) { assert.Equal(t, "13", Get("bar")) } -func TestSetEnvReplacer(t *testing.T) { +func TestSetEnvKeyReplacer(t *testing.T) { Reset() AutomaticEnv() diff --git a/vendor/github.com/xenolf/lego/.travis.yml b/vendor/github.com/xenolf/lego/.travis.yml index ff9ae963a..8cf9aed1b 100644 --- a/vendor/github.com/xenolf/lego/.travis.yml +++ b/vendor/github.com/xenolf/lego/.travis.yml @@ -1,7 +1,7 @@ language: go go: -- 1.7 - 1.8 +- 1.9 - tip services: - memcached diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge.go b/vendor/github.com/xenolf/lego/acme/dns_challenge.go index 7c4cb80de..133739748 100644 --- a/vendor/github.com/xenolf/lego/acme/dns_challenge.go +++ b/vendor/github.com/xenolf/lego/acme/dns_challenge.go @@ -11,7 +11,6 @@ import ( "time" "github.com/miekg/dns" - "golang.org/x/net/publicsuffix" ) type preCheckDNSFunc func(fqdn, value string) (bool, error) @@ -242,10 +241,6 @@ func FindZoneByFqdn(fqdn string, nameservers []string) (string, error) { labelIndexes := dns.Split(fqdn) for _, index := range labelIndexes { domain := fqdn[index:] - // Give up if we have reached the TLD - if isTLD(domain) { - break - } in, err := dnsQuery(domain, dns.TypeSOA, nameservers, true) if err != nil { @@ -273,14 +268,6 @@ func FindZoneByFqdn(fqdn string, nameservers []string) (string, error) { return "", fmt.Errorf("Could not find the start of authority") } -func isTLD(domain string) bool { - publicsuffix, _ := publicsuffix.PublicSuffix(UnFqdn(domain)) - if publicsuffix == UnFqdn(domain) { - return true - } - return false -} - // ClearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing. func ClearFqdnCache() { fqdnToZone = map[string]string{} diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge_test.go b/vendor/github.com/xenolf/lego/acme/dns_challenge_test.go index 597aaac17..4a2a7feac 100644 --- a/vendor/github.com/xenolf/lego/acme/dns_challenge_test.go +++ b/vendor/github.com/xenolf/lego/acme/dns_challenge_test.go @@ -37,14 +37,6 @@ var lookupNameserversTestsErr = []struct { {"_null.n0n0.", "Could not determine the zone", }, - // invalid domain - {"_null.com.", - "Could not determine the zone", - }, - // invalid domain - {"in-valid.co.uk.", - "Could not determine the zone", - }, } var findZoneByFqdnTests = []struct { @@ -53,6 +45,7 @@ var findZoneByFqdnTests = []struct { }{ {"mail.google.com.", "google.com."}, // domain is a CNAME {"foo.google.com.", "google.com."}, // domain is a non-existent subdomain + {"example.com.ac.", "ac."}, // domain is a eTLD } var checkAuthoritativeNssTests = []struct { diff --git a/vendor/github.com/xenolf/lego/cli_handlers.go b/vendor/github.com/xenolf/lego/cli_handlers.go index 79bbb37e5..b8790c4b2 100644 --- a/vendor/github.com/xenolf/lego/cli_handlers.go +++ b/vendor/github.com/xenolf/lego/cli_handlers.go @@ -329,8 +329,10 @@ func run(c *cli.Context) error { } func revoke(c *cli.Context) error { - - conf, _, client := setup(c) + conf, acc, client := setup(c) + if acc.Registration == nil { + logger().Fatalf("Account %s is not registered. Use 'run' to register a new account.\n", acc.Email) + } err := checkFolder(conf.CertPath()) if err != nil { @@ -355,7 +357,10 @@ func revoke(c *cli.Context) error { } func renew(c *cli.Context) error { - conf, _, client := setup(c) + conf, acc, client := setup(c) + if acc.Registration == nil { + logger().Fatalf("Account %s is not registered. Use 'run' to register a new account.\n", acc.Email) + } if len(c.GlobalStringSlice("domains")) <= 0 { logger().Fatal("Please specify at least one domain.") diff --git a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go index 94c8879b2..d7530f788 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go @@ -15,6 +15,7 @@ import ( "github.com/xenolf/lego/providers/dns/dyn" "github.com/xenolf/lego/providers/dns/exoscale" "github.com/xenolf/lego/providers/dns/gandi" + "github.com/xenolf/lego/providers/dns/godaddy" "github.com/xenolf/lego/providers/dns/googlecloud" "github.com/xenolf/lego/providers/dns/linode" "github.com/xenolf/lego/providers/dns/namecheap" @@ -54,6 +55,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) provider, err = gandi.NewDNSProvider() case "gcloud": provider, err = googlecloud.NewDNSProvider() + case "godaddy": + provider, err = godaddy.NewDNSProvider() case "linode": provider, err = linode.NewDNSProvider() case "manual": diff --git a/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go b/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go new file mode 100644 index 000000000..4112f6628 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go @@ -0,0 +1,155 @@ +// Package godaddy implements a DNS provider for solving the DNS-01 challenge using godaddy DNS. +package godaddy + +import ( + "fmt" + "io" + "net/http" + "os" + "time" + + "bytes" + "encoding/json" + "github.com/xenolf/lego/acme" + "io/ioutil" + "strings" +) + +// GoDaddyAPIURL represents the API endpoint to call. +const apiURL = "https://api.godaddy.com" + +// DNSProvider is an implementation of the acme.ChallengeProvider interface +type DNSProvider struct { + apiKey string + apiSecret string +} + +// NewDNSProvider returns a DNSProvider instance configured for godaddy. +// Credentials must be passed in the environment variables: GODADDY_API_KEY +// and GODADDY_API_SECRET. +func NewDNSProvider() (*DNSProvider, error) { + apikey := os.Getenv("GODADDY_API_KEY") + secret := os.Getenv("GODADDY_API_SECRET") + return NewDNSProviderCredentials(apikey, secret) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for godaddy. +func NewDNSProviderCredentials(apiKey, apiSecret string) (*DNSProvider, error) { + if apiKey == "" || apiSecret == "" { + return nil, fmt.Errorf("GoDaddy credentials missing") + } + + return &DNSProvider{apiKey, apiSecret}, nil +} + +// Timeout returns the timeout and interval to use when checking for DNS +// propagation. Adjusting here to cope with spikes in propagation times. +func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { + return 120 * time.Second, 2 * time.Second +} + +func (c *DNSProvider) extractRecordName(fqdn, domain string) string { + name := acme.UnFqdn(fqdn) + if idx := strings.Index(name, "."+domain); idx != -1 { + return name[:idx] + } + return name +} + +// Present creates a TXT record to fulfil the dns-01 challenge +func (c *DNSProvider) Present(domain, token, keyAuth string) error { + fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + domainZone, err := c.getZone(fqdn) + if err != nil { + return err + } + + if ttl < 600 { + ttl = 600 + } + + recordName := c.extractRecordName(fqdn, domainZone) + rec := []DNSRecord{ + { + Type: "TXT", + Name: recordName, + Data: value, + Ttl: ttl, + }, + } + + return c.updateRecords(rec, domainZone, recordName) +} + +func (c *DNSProvider) updateRecords(records []DNSRecord, domainZone string, recordName string) error { + body, err := json.Marshal(records) + if err != nil { + return err + } + + var resp *http.Response + resp, err = c.makeRequest("PUT", fmt.Sprintf("/v1/domains/%s/records/TXT/%s", domainZone, recordName), bytes.NewReader(body)) + if err != nil { + return err + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + bodyBytes, _ := ioutil.ReadAll(resp.Body) + return fmt.Errorf("Could not create record %v; Status: %v; Body: %s\n", string(body), resp.StatusCode, string(bodyBytes)) + } + return nil +} + +// CleanUp sets null value in the TXT DNS record as GoDaddy has no proper DELETE record method +func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { + fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + domainZone, err := c.getZone(fqdn) + if err != nil { + return err + } + + recordName := c.extractRecordName(fqdn, domainZone) + rec := []DNSRecord{ + { + Type: "TXT", + Name: recordName, + Data: "null", + }, + } + + return c.updateRecords(rec, domainZone, recordName) +} + +func (c *DNSProvider) getZone(fqdn string) (string, error) { + authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + if err != nil { + return "", err + } + + return acme.UnFqdn(authZone), nil +} + +func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Response, error) { + req, err := http.NewRequest(method, fmt.Sprintf("%s%s", apiURL, uri), body) + if err != nil { + return nil, err + } + + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("sso-key %s:%s", c.apiKey, c.apiSecret)) + + client := http.Client{Timeout: 30 * time.Second} + return client.Do(req) +} + +type DNSRecord struct { + Type string `json:"type"` + Name string `json:"name"` + Data string `json:"data"` + Priority int `json:"priority,omitempty"` + Ttl int `json:"ttl,omitempty"` +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy_test.go b/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy_test.go new file mode 100644 index 000000000..de84d827e --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy_test.go @@ -0,0 +1,60 @@ +package godaddy + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +var ( + godaddyAPIKey string + godaddyAPISecret string + godaddyDomain string + godaddyLiveTest bool +) + +func init() { + godaddyAPIKey = os.Getenv("GODADDY_API_KEY") + godaddyAPISecret = os.Getenv("GODADDY_API_SECRET") + godaddyDomain = os.Getenv("GODADDY_DOMAIN") + + if len(godaddyAPIKey) > 0 && len(godaddyAPISecret) > 0 && len(godaddyDomain) > 0 { + godaddyLiveTest = true + } +} + +func TestNewDNSProvider(t *testing.T) { + provider, err := NewDNSProvider() + + if !godaddyLiveTest { + assert.Error(t, err) + } else { + assert.NotNil(t, provider) + assert.NoError(t, err) + } +} + +func TestDNSProvider_Present(t *testing.T) { + if !godaddyLiveTest { + t.Skip("skipping live test") + } + + provider, err := NewDNSProvider() + assert.NoError(t, err) + + err = provider.Present(godaddyDomain, "", "123d==") + assert.NoError(t, err) +} + +func TestDNSProvider_CleanUp(t *testing.T) { + if !godaddyLiveTest { + t.Skip("skipping live test") + } + + provider, err := NewDNSProvider() + assert.NoError(t, err) + + err = provider.CleanUp(godaddyDomain, "", "123d==") + assert.NoError(t, err) +} |