Browse Source

Merge pull request #39012 from thaJeztah/bump_dependencies

Bump various dependencies
Sebastiaan van Stijn 6 năm trước cách đây
mục cha
commit
c2cb72e085
54 tập tin đã thay đổi với 1121 bổ sung409 xóa
  1. 9 9
      vendor.conf
  2. 16 0
      vendor/github.com/RackSec/srslog/README.md
  3. 17 0
      vendor/github.com/RackSec/srslog/dialer.go
  4. 12 2
      vendor/github.com/RackSec/srslog/formatter.go
  5. 34 6
      vendor/github.com/RackSec/srslog/srslog.go
  6. 22 6
      vendor/github.com/RackSec/srslog/writer.go
  7. 17 1
      vendor/github.com/imdario/mergo/README.md
  8. 1 0
      vendor/github.com/imdario/mergo/map.go
  9. 11 8
      vendor/github.com/imdario/mergo/merge.go
  10. 1 0
      vendor/github.com/konsorten/go-windows-terminal-sequences/README.md
  11. 11 0
      vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go
  12. 64 0
      vendor/github.com/kr/pty/README.md
  13. 2 0
      vendor/github.com/kr/pty/ioctl.go
  14. 10 5
      vendor/github.com/kr/pty/pty_darwin.go
  15. 80 0
      vendor/github.com/kr/pty/pty_dragonfly.go
  16. 12 7
      vendor/github.com/kr/pty/pty_freebsd.go
  17. 8 3
      vendor/github.com/kr/pty/pty_linux.go
  18. 33 0
      vendor/github.com/kr/pty/pty_openbsd.go
  19. 1 1
      vendor/github.com/kr/pty/pty_unsupported.go
  20. 32 4
      vendor/github.com/kr/pty/run.go
  21. 39 10
      vendor/github.com/kr/pty/util.go
  22. 14 0
      vendor/github.com/kr/pty/ztypes_dragonfly_amd64.go
  23. 12 0
      vendor/github.com/kr/pty/ztypes_mipsx.go
  24. 13 0
      vendor/github.com/kr/pty/ztypes_openbsd_386.go
  25. 13 0
      vendor/github.com/kr/pty/ztypes_openbsd_amd64.go
  26. 62 12
      vendor/github.com/mattn/go-shellwords/shellwords.go
  27. 24 0
      vendor/github.com/mattn/go-shellwords/util_go15.go
  28. 4 1
      vendor/github.com/mattn/go-shellwords/util_posix.go
  29. 5 0
      vendor/github.com/mattn/go-shellwords/util_windows.go
  30. 4 0
      vendor/github.com/philhofer/fwd/reader.go
  31. 0 6
      vendor/github.com/tchap/go-patricia/README.md
  32. 38 0
      vendor/github.com/tchap/go-patricia/patricia/children.go
  33. 12 0
      vendor/github.com/tchap/go-patricia/patricia/patricia.go
  34. 1 2
      vendor/golang.org/x/sync/errgroup/errgroup.go
  35. 4 8
      vendor/golang.org/x/sync/semaphore/semaphore.go
  36. 10 199
      vendor/gotest.tools/LICENSE
  37. 5 1
      vendor/gotest.tools/README.md
  38. 46 2
      vendor/gotest.tools/assert/cmp/compare.go
  39. 13 7
      vendor/gotest.tools/assert/cmp/result.go
  40. 0 1
      vendor/gotest.tools/assert/result.go
  41. 3 0
      vendor/gotest.tools/env/env.go
  42. 14 10
      vendor/gotest.tools/fs/file.go
  43. 14 4
      vendor/gotest.tools/fs/manifest.go
  44. 29 8
      vendor/gotest.tools/fs/ops.go
  45. 50 2
      vendor/gotest.tools/fs/path.go
  46. 71 9
      vendor/gotest.tools/fs/report.go
  47. 6 3
      vendor/gotest.tools/icmd/command.go
  48. 34 0
      vendor/gotest.tools/icmd/ops.go
  49. 14 11
      vendor/gotest.tools/internal/difflib/difflib.go
  50. 53 0
      vendor/gotest.tools/internal/source/defers.go
  51. 58 55
      vendor/gotest.tools/internal/source/source.go
  52. 39 0
      vendor/gotest.tools/poll/check.go
  53. 1 1
      vendor/gotest.tools/poll/poll.go
  54. 23 5
      vendor/gotest.tools/skip/skip.go

+ 9 - 9
vendor.conf

@@ -8,23 +8,23 @@ github.com/golang/gddo 9b12a26f3fbd7397dee4e20939ddca719d840d2a
 github.com/google/uuid v1.1.1
 github.com/gorilla/mux v1.7.0
 github.com/Microsoft/opengcs a10967154e143a36014584a6f664344e3bb0aa64
-github.com/konsorten/go-windows-terminal-sequences v1.0.1
-github.com/kr/pty 5cf931ef8f
-github.com/mattn/go-shellwords v1.0.3
+github.com/konsorten/go-windows-terminal-sequences f55edac94c9bbba5d6182a4be46d86a2c9b5b50e # v1.0.2
+github.com/kr/pty 521317be5ebc228a0f0ede099fa2a0b5ece22e49 # v1.1.4
+github.com/mattn/go-shellwords a72fbe27a1b0ed0df2f02754945044ce1456608b # v1.0.5
 github.com/sirupsen/logrus 8bdbc7bcc01dcbb8ec23dc8a28e332258d25251f # v1.4.1
-github.com/tchap/go-patricia v2.2.6
+github.com/tchap/go-patricia a7f0089c6f496e8e70402f61733606daa326cac5 # v2.3.0
 github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 # v0.1.0
 golang.org/x/net eb5bcb51f2a31c7d5141d810b70815c05d9c9146
 golang.org/x/sys 4b34438f7a67ee5f45cc6132e2bad873a20324e9
 github.com/docker/go-units 47565b4f722fb6ceae66b95f853feed578a4a51c # v0.3.3
 github.com/docker/go-connections 7395e3f8aa162843a74ed6d48e79627d9792ac55 # v0.4.0
 golang.org/x/text f21a4dfb5e38f5895301dc265a8def02365cc3d0 # v0.3.0
-gotest.tools v2.1.0
+gotest.tools 1083505acf35a0bd8a696b26837e1fb3187a7a83 # v2.3.0
 github.com/google/go-cmp v0.2.0
 
-github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5
-github.com/imdario/mergo v0.3.6
-golang.org/x/sync 1d60e4601c6fd243af51cc01ddf169918a5407ca
+github.com/RackSec/srslog a4725f04ec91af1a91b380da679d6e0c2f061e59
+github.com/imdario/mergo 7c29201646fa3de8506f701213473dd407f19646 # v0.3.7
+golang.org/x/sync e225da77a7e68af35c70ccbf71af2b83e6acac3c
 
 # buildkit
 github.com/moby/buildkit b3028967ae6259c9a31c1a1deeccd30fe3469cce
@@ -97,7 +97,7 @@ github.com/Graylog2/go-gelf 4143646226541087117ff2f83334ea48b3201841
 
 github.com/fluent/fluent-logger-golang v1.3.0
 # fluent-logger-golang deps
-github.com/philhofer/fwd 98c11a7a6ec829d672b03833c3d69a7fae1ca972
+github.com/philhofer/fwd bb6d471dc95d4fe11e432687f8b70ff496cf3136 # v1.0.0
 github.com/tinylib/msgp 3b556c64540842d4f82967be066a7f7fffc3adad
 
 # fsnotify

+ 16 - 0
vendor/github.com/RackSec/srslog/README.md

@@ -90,6 +90,22 @@ w.Debug("this is debug")
 w.Write([]byte("these are some bytes"))
 ```
 
+If you need further control over connection attempts, you can use the DialWithCustomDialer
+function. To continue with the DialWithTLSConfig example:
+
+```
+netDialer := &net.Dialer{Timeout: time.Second*5} // easy timeouts
+realNetwork := "tcp" // real network, other vars your dail func can close over
+dial := func(network, addr string) (net.Conn, error) {
+    // cannot use "network" here as it'll simply be "custom" which will fail
+    return tls.DialWithDialer(netDialer, realNetwork, addr, &config)
+}
+
+w, err := DialWithCustomDialer("custom", "192.168.0.52:514", syslog.LOG_ERR, "testtag", dial)
+```
+
+Your custom dial func can set timeouts, proxy connections, and do whatever else it needs before returning a net.Conn.
+
 # Generating TLS Certificates
 
 We've provided a script that you can use to generate a self-signed keypair:

+ 17 - 0
vendor/github.com/RackSec/srslog/dialer.go

@@ -37,6 +37,7 @@ func (w *Writer) getDialer() dialerFunctionWrapper {
 	dialers := map[string]dialerFunctionWrapper{
 		"":        dialerFunctionWrapper{"unixDialer", w.unixDialer},
 		"tcp+tls": dialerFunctionWrapper{"tlsDialer", w.tlsDialer},
+		"custom":  dialerFunctionWrapper{"customDialer", w.customDialer},
 	}
 	dialer, ok := dialers[w.network]
 	if !ok {
@@ -85,3 +86,19 @@ func (w *Writer) basicDialer() (serverConn, string, error) {
 	}
 	return sc, hostname, err
 }
+
+// customDialer uses the custom dialer when the Writer was created
+// giving developers total control over how connections are made and returned.
+// Note it does not check if cdialer is nil, as it should only be referenced from getDialer.
+func (w *Writer) customDialer() (serverConn, string, error) {
+	c, err := w.customDial(w.network, w.raddr)
+	var sc serverConn
+	hostname := w.hostname
+	if err == nil {
+		sc = &netConn{conn: c}
+		if hostname == "" {
+			hostname = c.LocalAddr().String()
+		}
+	}
+	return sc, hostname, err
+}

+ 12 - 2
vendor/github.com/RackSec/srslog/formatter.go

@@ -6,6 +6,8 @@ import (
 	"time"
 )
 
+const appNameMaxLength = 48 // limit to 48 chars as per RFC5424
+
 // Formatter is a type of function that takes the consituent parts of a
 // syslog message and returns a formatted string. A different Formatter is
 // defined for each different syslog protocol we support.
@@ -37,12 +39,20 @@ func RFC3164Formatter(p Priority, hostname, tag, content string) string {
 	return msg
 }
 
+// if string's length is greater than max, then use the last part
+func truncateStartStr(s string, max int) string {
+	if (len(s) > max) {
+		return s[len(s) - max:]
+	}
+	return s
+}
+
 // RFC5424Formatter provides an RFC 5424 compliant message.
 func RFC5424Formatter(p Priority, hostname, tag, content string) string {
 	timestamp := time.Now().Format(time.RFC3339)
 	pid := os.Getpid()
-	appName := os.Args[0]
-	msg := fmt.Sprintf("<%d>%d %s %s %s %d %s %s",
+	appName := truncateStartStr(os.Args[0], appNameMaxLength)
+	msg := fmt.Sprintf("<%d>%d %s %s %s %d %s - %s",
 		p, 1, timestamp, hostname, appName, pid, tag, content)
 	return msg
 }

+ 34 - 6
vendor/github.com/RackSec/srslog/srslog.go

@@ -3,8 +3,10 @@ package srslog
 import (
 	"crypto/tls"
 	"crypto/x509"
+	"errors"
 	"io/ioutil"
 	"log"
+	"net"
 	"os"
 )
 
@@ -15,6 +17,10 @@ type serverConn interface {
 	close() error
 }
 
+// DialFunc is the function signature to be used for a custom dialer callback
+// with DialWithCustomDialer
+type DialFunc func(string, string) (net.Conn, error)
+
 // New establishes a new connection to the system log daemon.  Each
 // write to the returned Writer sends a log message with the given
 // priority and prefix.
@@ -31,6 +37,22 @@ func Dial(network, raddr string, priority Priority, tag string) (*Writer, error)
 	return DialWithTLSConfig(network, raddr, priority, tag, nil)
 }
 
+// ErrNilDialFunc is returned from DialWithCustomDialer when a nil DialFunc is passed,
+// avoiding a nil pointer deference panic.
+var ErrNilDialFunc = errors.New("srslog: nil DialFunc passed to DialWithCustomDialer")
+
+// DialWithCustomDialer establishes a connection by calling customDial.
+// Each write to the returned Writer sends a log message with the given facility, severity and tag.
+// Network must be "custom" in order for this package to use customDial.
+// While network and raddr will be passed to customDial, it is allowed for customDial to ignore them.
+// If customDial is nil, this function returns ErrNilDialFunc.
+func DialWithCustomDialer(network, raddr string, priority Priority, tag string, customDial DialFunc) (*Writer, error) {
+	if customDial == nil {
+		return nil, ErrNilDialFunc
+	}
+	return dialAllParameters(network, raddr, priority, tag, nil, customDial)
+}
+
 // DialWithTLSCertPath establishes a secure connection to a log daemon by connecting to
 // address raddr on the specified network. It uses certPath to load TLS certificates and configure
 // the secure connection.
@@ -59,6 +81,11 @@ func DialWithTLSCert(network, raddr string, priority Priority, tag string, serve
 // DialWithTLSConfig establishes a secure connection to a log daemon by connecting to
 // address raddr on the specified network. It uses tlsConfig to configure the secure connection.
 func DialWithTLSConfig(network, raddr string, priority Priority, tag string, tlsConfig *tls.Config) (*Writer, error) {
+	return dialAllParameters(network, raddr, priority, tag, tlsConfig, nil)
+}
+
+// implementation of the various functions above
+func dialAllParameters(network, raddr string, priority Priority, tag string, tlsConfig *tls.Config, customDial DialFunc) (*Writer, error) {
 	if err := validatePriority(priority); err != nil {
 		return nil, err
 	}
@@ -69,12 +96,13 @@ func DialWithTLSConfig(network, raddr string, priority Priority, tag string, tls
 	hostname, _ := os.Hostname()
 
 	w := &Writer{
-		priority:  priority,
-		tag:       tag,
-		hostname:  hostname,
-		network:   network,
-		raddr:     raddr,
-		tlsConfig: tlsConfig,
+		priority:   priority,
+		tag:        tag,
+		hostname:   hostname,
+		network:    network,
+		raddr:      raddr,
+		tlsConfig:  tlsConfig,
+		customDial: customDial,
 	}
 
 	_, err := w.connect()

+ 22 - 6
vendor/github.com/RackSec/srslog/writer.go

@@ -17,6 +17,9 @@ type Writer struct {
 	framer    Framer
 	formatter Formatter
 
+	//non-nil if custom dialer set, used in getDialer
+	customDial DialFunc
+
 	mu   sync.RWMutex // guards conn
 	conn serverConn
 }
@@ -71,15 +74,20 @@ func (w *Writer) SetFramer(f Framer) {
 	w.framer = f
 }
 
+// SetHostname changes the hostname for syslog messages if needed.
+func (w *Writer) SetHostname(hostname string) {
+	w.hostname = hostname
+}
+
 // Write sends a log message to the syslog daemon using the default priority
 // passed into `srslog.New` or the `srslog.Dial*` functions.
 func (w *Writer) Write(b []byte) (int, error) {
 	return w.writeAndRetry(w.priority, string(b))
 }
 
-// WriteWithPriority sends a log message with a custom priority
+// WriteWithPriority sends a log message with a custom priority.
 func (w *Writer) WriteWithPriority(p Priority, b []byte) (int, error) {
-	return w.writeAndRetry(p, string(b))
+	return w.writeAndRetryWithPriority(p, string(b))
 }
 
 // Close closes a connection to the syslog daemon.
@@ -149,12 +157,20 @@ func (w *Writer) Debug(m string) (err error) {
 	return err
 }
 
-func (w *Writer) writeAndRetry(p Priority, s string) (int, error) {
-	pr := (w.priority & facilityMask) | (p & severityMask)
+// writeAndRetry takes a severity and the string to write. Any facility passed to
+// it as part of the severity Priority will be ignored.
+func (w *Writer) writeAndRetry(severity Priority, s string) (int, error) {
+	pr := (w.priority & facilityMask) | (severity & severityMask)
+
+	return w.writeAndRetryWithPriority(pr, s)
+}
 
+// writeAndRetryWithPriority differs from writeAndRetry in that it allows setting
+// of both the facility and the severity.
+func (w *Writer) writeAndRetryWithPriority(p Priority, s string) (int, error) {
 	conn := w.getConn()
 	if conn != nil {
-		if n, err := w.write(conn, pr, s); err == nil {
+		if n, err := w.write(conn, p, s); err == nil {
 			return n, err
 		}
 	}
@@ -163,7 +179,7 @@ func (w *Writer) writeAndRetry(p Priority, s string) (int, error) {
 	if conn, err = w.connect(); err != nil {
 		return 0, err
 	}
-	return w.write(conn, pr, s)
+	return w.write(conn, p, s)
 }
 
 // write generates and writes a syslog formatted string. It formats the

+ 17 - 1
vendor/github.com/imdario/mergo/README.md

@@ -13,6 +13,7 @@ It is ready for production use. [It is used in several projects by Docker, Googl
 [![Build Status][1]][2]
 [![Coverage Status][7]][8]
 [![Sourcegraph][9]][10]
+[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_shield)
 
 [1]: https://travis-ci.org/imdario/mergo.png
 [2]: https://travis-ci.org/imdario/mergo
@@ -27,7 +28,7 @@ It is ready for production use. [It is used in several projects by Docker, Googl
 
 ### Latest release
 
-[Release v0.3.6](https://github.com/imdario/mergo/releases/tag/v0.3.6).
+[Release v0.3.7](https://github.com/imdario/mergo/releases/tag/v0.3.7).
 
 ### Important note
 
@@ -217,6 +218,21 @@ If I can help you, you have an idea or you are using Mergo in your projects, don
 
 Written by [Dario Castañé](http://dario.im).
 
+## Top Contributors
+
+[![0](https://sourcerer.io/fame/imdario/imdario/mergo/images/0)](https://sourcerer.io/fame/imdario/imdario/mergo/links/0)
+[![1](https://sourcerer.io/fame/imdario/imdario/mergo/images/1)](https://sourcerer.io/fame/imdario/imdario/mergo/links/1)
+[![2](https://sourcerer.io/fame/imdario/imdario/mergo/images/2)](https://sourcerer.io/fame/imdario/imdario/mergo/links/2)
+[![3](https://sourcerer.io/fame/imdario/imdario/mergo/images/3)](https://sourcerer.io/fame/imdario/imdario/mergo/links/3)
+[![4](https://sourcerer.io/fame/imdario/imdario/mergo/images/4)](https://sourcerer.io/fame/imdario/imdario/mergo/links/4)
+[![5](https://sourcerer.io/fame/imdario/imdario/mergo/images/5)](https://sourcerer.io/fame/imdario/imdario/mergo/links/5)
+[![6](https://sourcerer.io/fame/imdario/imdario/mergo/images/6)](https://sourcerer.io/fame/imdario/imdario/mergo/links/6)
+[![7](https://sourcerer.io/fame/imdario/imdario/mergo/images/7)](https://sourcerer.io/fame/imdario/imdario/mergo/links/7)
+
+
 ## License
 
 [BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) license, as [Go language](http://golang.org/LICENSE).
+
+
+[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_large)

+ 1 - 0
vendor/github.com/imdario/mergo/map.go

@@ -72,6 +72,7 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, conf
 	case reflect.Struct:
 		srcMap := src.Interface().(map[string]interface{})
 		for key := range srcMap {
+			config.overwriteWithEmptyValue = true
 			srcValue := srcMap[key]
 			fieldName := changeInitialCase(key, unicode.ToUpper)
 			dstElement := dst.FieldByName(fieldName)

+ 11 - 8
vendor/github.com/imdario/mergo/merge.go

@@ -26,9 +26,10 @@ func hasExportedField(dst reflect.Value) (exported bool) {
 }
 
 type Config struct {
-	Overwrite    bool
-	AppendSlice  bool
-	Transformers Transformers
+	Overwrite               bool
+	AppendSlice             bool
+	Transformers            Transformers
+	overwriteWithEmptyValue bool
 }
 
 type Transformers interface {
@@ -40,6 +41,8 @@ type Transformers interface {
 // short circuiting on recursive types.
 func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
 	overwrite := config.Overwrite
+	overwriteWithEmptySrc := config.overwriteWithEmptyValue
+	config.overwriteWithEmptyValue = false
 
 	if !src.IsValid() {
 		return
@@ -74,7 +77,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
 				}
 			}
 		} else {
-			if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
+			if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) {
 				dst.Set(src)
 			}
 		}
@@ -125,7 +128,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
 						dstSlice = reflect.ValueOf(dstElement.Interface())
 					}
 
-					if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
+					if (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
 						dstSlice = srcSlice
 					} else if config.AppendSlice {
 						if srcSlice.Type() != dstSlice.Type() {
@@ -136,7 +139,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
 					dst.SetMapIndex(key, dstSlice)
 				}
 			}
-			if dstElement.IsValid() && reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map {
+			if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) {
 				continue
 			}
 
@@ -151,7 +154,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
 		if !dst.CanSet() {
 			break
 		}
-		if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
+		if (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
 			dst.Set(src)
 		} else if config.AppendSlice {
 			if src.Type() != dst.Type() {
@@ -191,7 +194,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
 			return
 		}
 	default:
-		if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
+		if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) {
 			dst.Set(src)
 		}
 	}

+ 1 - 0
vendor/github.com/konsorten/go-windows-terminal-sequences/README.md

@@ -26,6 +26,7 @@ The tool is sponsored by the [marvin + konsorten GmbH](http://www.konsorten.de).
 We thank all the authors who provided code to this library:
 
 * Felix Kollmann
+* Nicolas Perraut
 
 ## License
 

+ 11 - 0
vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go

@@ -0,0 +1,11 @@
+// +build linux darwin
+
+package sequences
+
+import (
+	"fmt"
+)
+
+func EnableVirtualTerminalProcessing(stream uintptr, enable bool) error {
+	return fmt.Errorf("windows only package")
+}

+ 64 - 0
vendor/github.com/kr/pty/README.md

@@ -8,6 +8,8 @@ Pty is a Go package for using unix pseudo-terminals.
 
 ## Example
 
+### Command
+
 ```go
 package main
 
@@ -34,3 +36,65 @@ func main() {
 	io.Copy(os.Stdout, f)
 }
 ```
+
+### Shell
+
+```go
+package main
+
+import (
+        "io"
+        "log"
+        "os"
+        "os/exec"
+        "os/signal"
+        "syscall"
+
+        "github.com/kr/pty"
+        "golang.org/x/crypto/ssh/terminal"
+)
+
+func test() error {
+        // Create arbitrary command.
+        c := exec.Command("bash")
+
+        // Start the command with a pty.
+        ptmx, err := pty.Start(c)
+        if err != nil {
+                return err
+        }
+        // Make sure to close the pty at the end.
+        defer func() { _ = ptmx.Close() }() // Best effort.
+
+        // Handle pty size.
+        ch := make(chan os.Signal, 1)
+        signal.Notify(ch, syscall.SIGWINCH)
+        go func() {
+                for range ch {
+                        if err := pty.InheritSize(os.Stdin, ptmx); err != nil {
+                                log.Printf("error resizing pty: %s", err)
+                        }
+                }
+        }()
+        ch <- syscall.SIGWINCH // Initial resize.
+
+        // Set stdin in raw mode.
+        oldState, err := terminal.MakeRaw(int(os.Stdin.Fd()))
+        if err != nil {
+                panic(err)
+        }
+        defer func() { _ = terminal.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.
+
+        // Copy stdin to the pty and the pty to stdout.
+        go func() { _, _ = io.Copy(ptmx, os.Stdin) }()
+        _, _ = io.Copy(os.Stdout, ptmx)
+
+        return nil
+}
+
+func main() {
+        if err := test(); err != nil {
+                log.Fatal(err)
+        }
+}
+```

+ 2 - 0
vendor/github.com/kr/pty/ioctl.go

@@ -1,3 +1,5 @@
+// +build !windows
+
 package pty
 
 import "syscall"

+ 10 - 5
vendor/github.com/kr/pty/pty_darwin.go

@@ -8,23 +8,28 @@ import (
 )
 
 func open() (pty, tty *os.File, err error) {
-	p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
+	pFD, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC, 0)
 	if err != nil {
 		return nil, nil, err
 	}
+	p := os.NewFile(uintptr(pFD), "/dev/ptmx")
+	// In case of error after this point, make sure we close the ptmx fd.
+	defer func() {
+		if err != nil {
+			_ = p.Close() // Best effort.
+		}
+	}()
 
 	sname, err := ptsname(p)
 	if err != nil {
 		return nil, nil, err
 	}
 
-	err = grantpt(p)
-	if err != nil {
+	if err := grantpt(p); err != nil {
 		return nil, nil, err
 	}
 
-	err = unlockpt(p)
-	if err != nil {
+	if err := unlockpt(p); err != nil {
 		return nil, nil, err
 	}
 

+ 80 - 0
vendor/github.com/kr/pty/pty_dragonfly.go

@@ -0,0 +1,80 @@
+package pty
+
+import (
+	"errors"
+	"os"
+	"strings"
+	"syscall"
+	"unsafe"
+)
+
+// same code as pty_darwin.go
+func open() (pty, tty *os.File, err error) {
+	p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	// In case of error after this point, make sure we close the ptmx fd.
+	defer func() {
+		if err != nil {
+			_ = p.Close() // Best effort.
+		}
+	}()
+
+	sname, err := ptsname(p)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	if err := grantpt(p); err != nil {
+		return nil, nil, err
+	}
+
+	if err := unlockpt(p); err != nil {
+		return nil, nil, err
+	}
+
+	t, err := os.OpenFile(sname, os.O_RDWR, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	return p, t, nil
+}
+
+func grantpt(f *os.File) error {
+	_, err := isptmaster(f.Fd())
+	return err
+}
+
+func unlockpt(f *os.File) error {
+	_, err := isptmaster(f.Fd())
+	return err
+}
+
+func isptmaster(fd uintptr) (bool, error) {
+	err := ioctl(fd, syscall.TIOCISPTMASTER, 0)
+	return err == nil, err
+}
+
+var (
+	emptyFiodgnameArg fiodgnameArg
+	ioctl_FIODNAME    = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
+)
+
+func ptsname(f *os.File) (string, error) {
+	name := make([]byte, _C_SPECNAMELEN)
+	fa := fiodgnameArg{Name: (*byte)(unsafe.Pointer(&name[0])), Len: _C_SPECNAMELEN, Pad_cgo_0: [4]byte{0, 0, 0, 0}}
+
+	err := ioctl(f.Fd(), ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa)))
+	if err != nil {
+		return "", err
+	}
+
+	for i, c := range name {
+		if c == 0 {
+			s := "/dev/" + string(name[:i])
+			return strings.Replace(s, "ptm", "pts", -1), nil
+		}
+	}
+	return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
+}

+ 12 - 7
vendor/github.com/kr/pty/pty_freebsd.go

@@ -7,22 +7,28 @@ import (
 	"unsafe"
 )
 
-func posix_openpt(oflag int) (fd int, err error) {
+func posixOpenpt(oflag int) (fd int, err error) {
 	r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0)
 	fd = int(r0)
 	if e1 != 0 {
 		err = e1
 	}
-	return
+	return fd, err
 }
 
 func open() (pty, tty *os.File, err error) {
-	fd, err := posix_openpt(syscall.O_RDWR | syscall.O_CLOEXEC)
+	fd, err := posixOpenpt(syscall.O_RDWR | syscall.O_CLOEXEC)
 	if err != nil {
 		return nil, nil, err
 	}
-
 	p := os.NewFile(uintptr(fd), "/dev/pts")
+	// In case of error after this point, make sure we close the pts fd.
+	defer func() {
+		if err != nil {
+			_ = p.Close() // Best effort.
+		}
+	}()
+
 	sname, err := ptsname(p)
 	if err != nil {
 		return nil, nil, err
@@ -42,7 +48,7 @@ func isptmaster(fd uintptr) (bool, error) {
 
 var (
 	emptyFiodgnameArg fiodgnameArg
-	ioctl_FIODGNAME   = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
+	ioctlFIODGNAME    = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
 )
 
 func ptsname(f *os.File) (string, error) {
@@ -59,8 +65,7 @@ func ptsname(f *os.File) (string, error) {
 		buf = make([]byte, n)
 		arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
 	)
-	err = ioctl(f.Fd(), ioctl_FIODGNAME, uintptr(unsafe.Pointer(&arg)))
-	if err != nil {
+	if err := ioctl(f.Fd(), ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil {
 		return "", err
 	}
 

+ 8 - 3
vendor/github.com/kr/pty/pty_linux.go

@@ -12,14 +12,19 @@ func open() (pty, tty *os.File, err error) {
 	if err != nil {
 		return nil, nil, err
 	}
+	// In case of error after this point, make sure we close the ptmx fd.
+	defer func() {
+		if err != nil {
+			_ = p.Close() // Best effort.
+		}
+	}()
 
 	sname, err := ptsname(p)
 	if err != nil {
 		return nil, nil, err
 	}
 
-	err = unlockpt(p)
-	if err != nil {
+	if err := unlockpt(p); err != nil {
 		return nil, nil, err
 	}
 
@@ -41,6 +46,6 @@ func ptsname(f *os.File) (string, error) {
 
 func unlockpt(f *os.File) error {
 	var u _C_int
-	// use TIOCSPTLCK with a zero valued arg to clear the slave pty lock
+	// use TIOCSPTLCK with a pointer to zero to clear the lock
 	return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
 }

+ 33 - 0
vendor/github.com/kr/pty/pty_openbsd.go

@@ -0,0 +1,33 @@
+package pty
+
+import (
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+func open() (pty, tty *os.File, err error) {
+	/*
+	 * from ptm(4):
+	 * The PTMGET command allocates a free pseudo terminal, changes its
+	 * ownership to the caller, revokes the access privileges for all previous
+	 * users, opens the file descriptors for the pty and tty devices and
+	 * returns them to the caller in struct ptmget.
+	 */
+
+	p, err := os.OpenFile("/dev/ptm", os.O_RDWR|syscall.O_CLOEXEC, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	defer p.Close()
+
+	var ptm ptmget
+	if err := ioctl(p.Fd(), uintptr(ioctl_PTMGET), uintptr(unsafe.Pointer(&ptm))); err != nil {
+		return nil, nil, err
+	}
+
+	pty = os.NewFile(uintptr(ptm.Cfd), "/dev/ptm")
+	tty = os.NewFile(uintptr(ptm.Sfd), "/dev/ptm")
+
+	return pty, tty, nil
+}

+ 1 - 1
vendor/github.com/kr/pty/pty_unsupported.go

@@ -1,4 +1,4 @@
-// +build !linux,!darwin,!freebsd
+// +build !linux,!darwin,!freebsd,!dragonfly,!openbsd
 
 package pty
 

+ 32 - 4
vendor/github.com/kr/pty/run.go

@@ -1,3 +1,5 @@
+// +build !windows
+
 package pty
 
 import (
@@ -10,15 +12,41 @@ import (
 // and c.Stderr, calls c.Start, and returns the File of the tty's
 // corresponding pty.
 func Start(c *exec.Cmd) (pty *os.File, err error) {
+	return StartWithSize(c, nil)
+}
+
+// StartWithSize assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
+// and c.Stderr, calls c.Start, and returns the File of the tty's
+// corresponding pty.
+//
+// This will resize the pty to the specified size before starting the command
+func StartWithSize(c *exec.Cmd, sz *Winsize) (pty *os.File, err error) {
 	pty, tty, err := Open()
 	if err != nil {
 		return nil, err
 	}
 	defer tty.Close()
-	c.Stdout = tty
-	c.Stdin = tty
-	c.Stderr = tty
-	c.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
+	if sz != nil {
+		err = Setsize(pty, sz)
+		if err != nil {
+			pty.Close()
+			return nil, err
+		}
+	}
+	if c.Stdout == nil {
+		c.Stdout = tty
+	}
+	if c.Stderr == nil {
+		c.Stderr = tty
+	}
+	if c.Stdin == nil {
+		c.Stdin = tty
+	}
+	if c.SysProcAttr == nil {
+		c.SysProcAttr = &syscall.SysProcAttr{}
+	}
+	c.SysProcAttr.Setctty = true
+	c.SysProcAttr.Setsid = true
 	err = c.Start()
 	if err != nil {
 		pty.Close()

+ 39 - 10
vendor/github.com/kr/pty/util.go

@@ -1,3 +1,5 @@
+// +build !windows
+
 package pty
 
 import (
@@ -6,26 +8,53 @@ import (
 	"unsafe"
 )
 
+// InheritSize applies the terminal size of pty to tty. This should be run
+// in a signal handler for syscall.SIGWINCH to automatically resize the tty when
+// the pty receives a window size change notification.
+func InheritSize(pty, tty *os.File) error {
+	size, err := GetsizeFull(pty)
+	if err != nil {
+		return err
+	}
+	err = Setsize(tty, size)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// Setsize resizes t to s.
+func Setsize(t *os.File, ws *Winsize) error {
+	return windowRectCall(ws, t.Fd(), syscall.TIOCSWINSZ)
+}
+
+// GetsizeFull returns the full terminal size description.
+func GetsizeFull(t *os.File) (size *Winsize, err error) {
+	var ws Winsize
+	err = windowRectCall(&ws, t.Fd(), syscall.TIOCGWINSZ)
+	return &ws, err
+}
+
 // Getsize returns the number of rows (lines) and cols (positions
 // in each line) in terminal t.
 func Getsize(t *os.File) (rows, cols int, err error) {
-	var ws winsize
-	err = windowrect(&ws, t.Fd())
-	return int(ws.ws_row), int(ws.ws_col), err
+	ws, err := GetsizeFull(t)
+	return int(ws.Rows), int(ws.Cols), err
 }
 
-type winsize struct {
-	ws_row    uint16
-	ws_col    uint16
-	ws_xpixel uint16
-	ws_ypixel uint16
+// Winsize describes the terminal size.
+type Winsize struct {
+	Rows uint16 // ws_row: Number of rows (in cells)
+	Cols uint16 // ws_col: Number of columns (in cells)
+	X    uint16 // ws_xpixel: Width in pixels
+	Y    uint16 // ws_ypixel: Height in pixels
 }
 
-func windowrect(ws *winsize, fd uintptr) error {
+func windowRectCall(ws *Winsize, fd, a2 uintptr) error {
 	_, _, errno := syscall.Syscall(
 		syscall.SYS_IOCTL,
 		fd,
-		syscall.TIOCGWINSZ,
+		a2,
 		uintptr(unsafe.Pointer(ws)),
 	)
 	if errno != 0 {

+ 14 - 0
vendor/github.com/kr/pty/ztypes_dragonfly_amd64.go

@@ -0,0 +1,14 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_dragonfly.go
+
+package pty
+
+const (
+	_C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+	Name      *byte
+	Len       uint32
+	Pad_cgo_0 [4]byte
+}

+ 12 - 0
vendor/github.com/kr/pty/ztypes_mipsx.go

@@ -0,0 +1,12 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+// +build linux
+// +build mips mipsle mips64 mips64le
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)

+ 13 - 0
vendor/github.com/kr/pty/ztypes_openbsd_386.go

@@ -0,0 +1,13 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_openbsd.go
+
+package pty
+
+type ptmget struct {
+	Cfd	int32
+	Sfd	int32
+	Cn	[16]int8
+	Sn	[16]int8
+}
+
+var ioctl_PTMGET = 0x40287401

+ 13 - 0
vendor/github.com/kr/pty/ztypes_openbsd_amd64.go

@@ -0,0 +1,13 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_openbsd.go
+
+package pty
+
+type ptmget struct {
+	Cfd int32
+	Sfd int32
+	Cn  [16]int8
+	Sn  [16]int8
+}
+
+var ioctl_PTMGET = 0x40287401

+ 62 - 12
vendor/github.com/mattn/go-shellwords/shellwords.go

@@ -4,6 +4,7 @@ import (
 	"errors"
 	"os"
 	"regexp"
+	"strings"
 )
 
 var (
@@ -21,13 +22,17 @@ func isSpace(r rune) bool {
 	return false
 }
 
-func replaceEnv(s string) string {
+func replaceEnv(getenv func(string) string, s string) string {
+	if getenv == nil {
+		getenv = os.Getenv
+	}
+
 	return envRe.ReplaceAllStringFunc(s, func(s string) string {
 		s = s[1:]
 		if s[0] == '{' {
 			s = s[1 : len(s)-1]
 		}
-		return os.Getenv(s)
+		return getenv(s)
 	})
 }
 
@@ -35,16 +40,24 @@ type Parser struct {
 	ParseEnv      bool
 	ParseBacktick bool
 	Position      int
+
+	// If ParseEnv is true, use this for getenv.
+	// If nil, use os.Getenv.
+	Getenv func(string) string
 }
 
 func NewParser() *Parser {
-	return &Parser{ParseEnv, ParseBacktick, 0}
+	return &Parser{
+		ParseEnv:      ParseEnv,
+		ParseBacktick: ParseBacktick,
+		Position:      0,
+	}
 }
 
 func (p *Parser) Parse(line string) ([]string, error) {
 	args := []string{}
 	buf := ""
-	var escaped, doubleQuoted, singleQuoted, backQuote bool
+	var escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote bool
 	backtick := ""
 
 	pos := -1
@@ -68,12 +81,12 @@ loop:
 		}
 
 		if isSpace(r) {
-			if singleQuoted || doubleQuoted || backQuote {
+			if singleQuoted || doubleQuoted || backQuote || dollarQuote {
 				buf += string(r)
 				backtick += string(r)
 			} else if got {
 				if p.ParseEnv {
-					buf = replaceEnv(buf)
+					buf = replaceEnv(p.Getenv, buf)
 				}
 				args = append(args, buf)
 				buf = ""
@@ -84,7 +97,7 @@ loop:
 
 		switch r {
 		case '`':
-			if !singleQuoted && !doubleQuoted {
+			if !singleQuoted && !doubleQuoted && !dollarQuote {
 				if p.ParseBacktick {
 					if backQuote {
 						out, err := shellRun(backtick)
@@ -100,18 +113,55 @@ loop:
 				backtick = ""
 				backQuote = !backQuote
 			}
+		case ')':
+			if !singleQuoted && !doubleQuoted && !backQuote {
+				if p.ParseBacktick {
+					if dollarQuote {
+						out, err := shellRun(backtick)
+						if err != nil {
+							return nil, err
+						}
+						if r == ')' {
+							buf = buf[:len(buf)-len(backtick)-2] + out
+						} else {
+							buf = buf[:len(buf)-len(backtick)-1] + out
+						}
+					}
+					backtick = ""
+					dollarQuote = !dollarQuote
+					continue
+				}
+				backtick = ""
+				dollarQuote = !dollarQuote
+			}
+		case '(':
+			if !singleQuoted && !doubleQuoted && !backQuote {
+				if !dollarQuote && strings.HasSuffix(buf, "$") {
+					dollarQuote = true
+					buf += "("
+					continue
+				} else {
+					return nil, errors.New("invalid command line string")
+				}
+			}
 		case '"':
-			if !singleQuoted {
+			if !singleQuoted && !dollarQuote {
 				doubleQuoted = !doubleQuoted
 				continue
 			}
 		case '\'':
-			if !doubleQuoted {
+			if !doubleQuoted && !dollarQuote {
 				singleQuoted = !singleQuoted
 				continue
 			}
 		case ';', '&', '|', '<', '>':
 			if !(escaped || singleQuoted || doubleQuoted || backQuote) {
+				if r == '>' && len(buf) > 0 {
+					if c := buf[0]; '0' <= c && c <= '9' {
+						i -= 1
+						got = false
+					}
+				}
 				pos = i
 				break loop
 			}
@@ -119,19 +169,19 @@ loop:
 
 		got = true
 		buf += string(r)
-		if backQuote {
+		if backQuote || dollarQuote {
 			backtick += string(r)
 		}
 	}
 
 	if got {
 		if p.ParseEnv {
-			buf = replaceEnv(buf)
+			buf = replaceEnv(p.Getenv, buf)
 		}
 		args = append(args, buf)
 	}
 
-	if escaped || singleQuoted || doubleQuoted || backQuote {
+	if escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote {
 		return nil, errors.New("invalid command line string")
 	}
 

+ 24 - 0
vendor/github.com/mattn/go-shellwords/util_go15.go

@@ -0,0 +1,24 @@
+// +build !go1.6
+
+package shellwords
+
+import (
+	"os"
+	"os/exec"
+	"runtime"
+	"strings"
+)
+
+func shellRun(line string) (string, error) {
+	var b []byte
+	var err error
+	if runtime.GOOS == "windows" {
+		b, err = exec.Command(os.Getenv("COMSPEC"), "/c", line).Output()
+	} else {
+		b, err = exec.Command(os.Getenv("SHELL"), "-c", line).Output()
+	}
+	if err != nil {
+		return "", err
+	}
+	return strings.TrimSpace(string(b)), nil
+}

+ 4 - 1
vendor/github.com/mattn/go-shellwords/util_posix.go

@@ -1,4 +1,4 @@
-// +build !windows
+// +build !windows,go1.6
 
 package shellwords
 
@@ -13,6 +13,9 @@ func shellRun(line string) (string, error) {
 	shell := os.Getenv("SHELL")
 	b, err := exec.Command(shell, "-c", line).Output()
 	if err != nil {
+		if eerr, ok := err.(*exec.ExitError); ok {
+			b = eerr.Stderr
+		}
 		return "", errors.New(err.Error() + ":" + string(b))
 	}
 	return strings.TrimSpace(string(b)), nil

+ 5 - 0
vendor/github.com/mattn/go-shellwords/util_windows.go

@@ -1,3 +1,5 @@
+// +build windows,go1.6
+
 package shellwords
 
 import (
@@ -11,6 +13,9 @@ func shellRun(line string) (string, error) {
 	shell := os.Getenv("COMSPEC")
 	b, err := exec.Command(shell, "/c", line).Output()
 	if err != nil {
+		if eerr, ok := err.(*exec.ExitError); ok {
+			b = eerr.Stderr
+		}
 		return "", errors.New(err.Error() + ":" + string(b))
 	}
 	return strings.TrimSpace(string(b)), nil

+ 4 - 0
vendor/github.com/philhofer/fwd/reader.go

@@ -109,6 +109,10 @@ func (r *Reader) more() {
 	if a == 0 && r.state == nil {
 		r.state = io.ErrNoProgress
 		return
+	} else if a > 0 && r.state == io.EOF {
+		// discard the io.EOF if we read more than 0 bytes.
+		// the next call to Read should return io.EOF again.
+		r.state = nil
 	}
 	r.data = r.data[:len(r.data)+a]
 }

+ 0 - 6
vendor/github.com/tchap/go-patricia/README.md

@@ -1,8 +1,6 @@
 # go-patricia #
 
 **Documentation**: [GoDoc](http://godoc.org/github.com/tchap/go-patricia/patricia)<br />
-**Build Status**: [![Build
-Status](https://drone.io/github.com/tchap/go-patricia/status.png)](https://drone.io/github.com/tchap/go-patricia/latest)<br />
 **Test Coverage**: [![Coverage
 Status](https://coveralls.io/repos/tchap/go-patricia/badge.png)](https://coveralls.io/r/tchap/go-patricia)
 
@@ -117,7 +115,3 @@ MIT, check the `LICENSE` file.
 [![Gittip
 Badge](http://img.shields.io/gittip/alanhamlett.png)](https://www.gittip.com/tchap/
 "Gittip Badge")
-
-[![Bitdeli
-Badge](https://d2weczhvl823v0.cloudfront.net/tchap/go-patricia/trend.png)](https://bitdeli.com/free
-"Bitdeli Badge")

+ 38 - 0
vendor/github.com/tchap/go-patricia/patricia/children.go

@@ -20,6 +20,7 @@ type childList interface {
 	next(b byte) *Trie
 	walk(prefix *Prefix, visitor VisitorFunc) error
 	print(w io.Writer, indent int)
+	clone() childList
 	total() int
 }
 
@@ -143,6 +144,17 @@ func (list *sparseChildList) total() int {
 	return tot
 }
 
+func (list *sparseChildList) clone() childList {
+	clones := make(tries, len(list.children), cap(list.children))
+	for i, child := range list.children {
+		clones[i] = child.Clone()
+	}
+
+	return &sparseChildList{
+		children: clones,
+	}
+}
+
 func (list *sparseChildList) print(w io.Writer, indent int) {
 	for _, child := range list.children {
 		if child != nil {
@@ -314,6 +326,32 @@ func (list *denseChildList) print(w io.Writer, indent int) {
 	}
 }
 
+func (list *denseChildList) clone() childList {
+	clones := make(tries, cap(list.children))
+
+	if list.numChildren != 0 {
+		clonedCount := 0
+		for i := list.headIndex; i < len(list.children); i++ {
+			child := list.children[i]
+			if child != nil {
+				clones[i] = child.Clone()
+				clonedCount++
+				if clonedCount == list.numChildren {
+					break
+				}
+			}
+		}
+	}
+
+	return &denseChildList{
+		min:         list.min,
+		max:         list.max,
+		numChildren: list.numChildren,
+		headIndex:   list.headIndex,
+		children:    clones,
+	}
+}
+
 func (list *denseChildList) total() int {
 	tot := 0
 	for _, child := range list.children {

+ 12 - 0
vendor/github.com/tchap/go-patricia/patricia/patricia.go

@@ -77,6 +77,18 @@ func MaxChildrenPerSparseNode(value int) Option {
 	}
 }
 
+// Clone makes a copy of an existing trie.
+// Items stored in both tries become shared, obviously.
+func (trie *Trie) Clone() *Trie {
+	return &Trie{
+		prefix:                   append(Prefix(nil), trie.prefix...),
+		item:                     trie.item,
+		maxPrefixPerNode:         trie.maxPrefixPerNode,
+		maxChildrenPerSparseNode: trie.maxChildrenPerSparseNode,
+		children:                 trie.children.clone(),
+	}
+}
+
 // Item returns the item stored in the root of this trie.
 func (trie *Trie) Item() Item {
 	return trie.item

+ 1 - 2
vendor/golang.org/x/sync/errgroup/errgroup.go

@@ -7,9 +7,8 @@
 package errgroup
 
 import (
+	"context"
 	"sync"
-
-	"golang.org/x/net/context"
 )
 
 // A Group is a collection of goroutines working on subtasks that are part of

+ 4 - 8
vendor/golang.org/x/sync/semaphore/semaphore.go

@@ -7,12 +7,8 @@ package semaphore // import "golang.org/x/sync/semaphore"
 
 import (
 	"container/list"
+	"context"
 	"sync"
-
-	// Use the old context because packages that depend on this one
-	// (e.g. cloud.google.com/go/...) must run on Go 1.6.
-	// TODO(jba): update to "context" when possible.
-	"golang.org/x/net/context"
 )
 
 type waiter struct {
@@ -36,9 +32,9 @@ type Weighted struct {
 	waiters list.List
 }
 
-// Acquire acquires the semaphore with a weight of n, blocking only until ctx
-// is done. On success, returns nil. On failure, returns ctx.Err() and leaves
-// the semaphore unchanged.
+// Acquire acquires the semaphore with a weight of n, blocking until resources
+// are available or ctx is done. On success, returns nil. On failure, returns
+// ctx.Err() and leaves the semaphore unchanged.
 //
 // If ctx is already done, Acquire may still succeed without blocking.
 func (s *Weighted) Acquire(ctx context.Context, n int64) error {

+ 10 - 199
vendor/gotest.tools/LICENSE

@@ -1,202 +1,13 @@
+Copyright 2018 gotest.tools authors
 
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
+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
 
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+    http://www.apache.org/licenses/LICENSE-2.0
 
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
+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.

+ 5 - 1
vendor/gotest.tools/README.md

@@ -3,7 +3,7 @@
 A collection of packages to augment `testing` and support common patterns.
 
 [![GoDoc](https://godoc.org/gotest.tools?status.svg)](https://godoc.org/gotest.tools)
-[![CircleCI](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master)
+[![CircleCI](https://circleci.com/gh/gotestyourself/gotest.tools/tree/master.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotest.tools/tree/master)
 [![Go Reportcard](https://goreportcard.com/badge/gotest.tools)](https://goreportcard.com/report/gotest.tools)
 
 
@@ -29,3 +29,7 @@ A collection of packages to augment `testing` and support common patterns.
 * [gotest.tools/gotestsum](https://github.com/gotestyourself/gotestsum) - go test runner with custom output
 * [maxbrunsfeld/counterfeiter](https://github.com/maxbrunsfeld/counterfeiter) - generate fakes for interfaces
 * [jonboulle/clockwork](https://github.com/jonboulle/clockwork) - a fake clock for testing code that uses `time`
+
+## Contributing
+
+See [CONTRIBUTING.md](CONTRIBUTING.md).

+ 46 - 2
vendor/gotest.tools/assert/cmp/compare.go

@@ -4,6 +4,7 @@ package cmp // import "gotest.tools/assert/cmp"
 import (
 	"fmt"
 	"reflect"
+	"regexp"
 	"strings"
 
 	"github.com/google/go-cmp/cmp"
@@ -58,6 +59,39 @@ func toResult(success bool, msg string) Result {
 	return ResultFailure(msg)
 }
 
+// RegexOrPattern may be either a *regexp.Regexp or a string that is a valid
+// regexp pattern.
+type RegexOrPattern interface{}
+
+// Regexp succeeds if value v matches regular expression re.
+//
+// Example:
+//   assert.Assert(t, cmp.Regexp("^[0-9a-f]{32}$", str))
+//   r := regexp.MustCompile("^[0-9a-f]{32}$")
+//   assert.Assert(t, cmp.Regexp(r, str))
+func Regexp(re RegexOrPattern, v string) Comparison {
+	match := func(re *regexp.Regexp) Result {
+		return toResult(
+			re.MatchString(v),
+			fmt.Sprintf("value %q does not match regexp %q", v, re.String()))
+	}
+
+	return func() Result {
+		switch regex := re.(type) {
+		case *regexp.Regexp:
+			return match(regex)
+		case string:
+			re, err := regexp.Compile(regex)
+			if err != nil {
+				return ResultFailure(err.Error())
+			}
+			return match(re)
+		default:
+			return ResultFailure(fmt.Sprintf("invalid type %T for regex pattern", regex))
+		}
+	}
+}
+
 // Equal succeeds if x == y. See assert.Equal for full documentation.
 func Equal(x, y interface{}) Comparison {
 	return func() Result {
@@ -186,7 +220,7 @@ func Error(err error, message string) Comparison {
 			return ResultFailure("expected an error, got nil")
 		case err.Error() != message:
 			return ResultFailure(fmt.Sprintf(
-				"expected error %q, got %+v", message, err))
+				"expected error %q, got %s", message, formatErrorMessage(err)))
 		}
 		return ResultSuccess
 	}
@@ -201,12 +235,22 @@ func ErrorContains(err error, substring string) Comparison {
 			return ResultFailure("expected an error, got nil")
 		case !strings.Contains(err.Error(), substring):
 			return ResultFailure(fmt.Sprintf(
-				"expected error to contain %q, got %+v", substring, err))
+				"expected error to contain %q, got %s", substring, formatErrorMessage(err)))
 		}
 		return ResultSuccess
 	}
 }
 
+func formatErrorMessage(err error) string {
+	if _, ok := err.(interface {
+		Cause() error
+	}); ok {
+		return fmt.Sprintf("%q\n%+v", err, err)
+	}
+	// This error was not wrapped with github.com/pkg/errors
+	return fmt.Sprintf("%q", err)
+}
+
 // Nil succeeds if obj is a nil interface, pointer, or function.
 //
 // Use NilError() for comparing errors. Use Len(obj, 0) for comparing slices,

+ 13 - 7
vendor/gotest.tools/assert/cmp/result.go

@@ -9,31 +9,37 @@ import (
 	"gotest.tools/internal/source"
 )
 
-// Result of a Comparison.
+// A Result of a Comparison.
 type Result interface {
 	Success() bool
 }
 
-type result struct {
+// StringResult is an implementation of Result that reports the error message
+// string verbatim and does not provide any templating or formatting of the
+// message.
+type StringResult struct {
 	success bool
 	message string
 }
 
-func (r result) Success() bool {
+// Success returns true if the comparison was successful.
+func (r StringResult) Success() bool {
 	return r.success
 }
 
-func (r result) FailureMessage() string {
+// FailureMessage returns the message used to provide additional information
+// about the failure.
+func (r StringResult) FailureMessage() string {
 	return r.message
 }
 
 // ResultSuccess is a constant which is returned by a ComparisonWithResult to
 // indicate success.
-var ResultSuccess = result{success: true}
+var ResultSuccess = StringResult{success: true}
 
 // ResultFailure returns a failed Result with a failure message.
-func ResultFailure(message string) Result {
-	return result{message: message}
+func ResultFailure(message string) StringResult {
+	return StringResult{message: message}
 }
 
 // ResultFromError returns ResultSuccess if err is nil. Otherwise ResultFailure

+ 0 - 1
vendor/gotest.tools/assert/result.go

@@ -70,7 +70,6 @@ func filterPrintableExpr(args []ast.Expr) []ast.Expr {
 			result[i] = starExpr.X
 			continue
 		}
-		result[i] = nil
 	}
 	return result
 }

+ 3 - 0
vendor/gotest.tools/env/env.go

@@ -79,6 +79,9 @@ func ToMap(env []string) map[string]string {
 }
 
 func getParts(raw string) (string, string) {
+	if raw == "" {
+		return "", ""
+	}
 	// Environment variables on windows can begin with =
 	// http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx
 	parts := strings.SplitN(raw[1:], "=", 2)

+ 14 - 10
vendor/gotest.tools/fs/file.go

@@ -7,6 +7,8 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"runtime"
+	"strings"
 
 	"gotest.tools/assert"
 	"gotest.tools/x/subtest"
@@ -40,20 +42,25 @@ func NewFile(t assert.TestingT, prefix string, ops ...PathOp) *File {
 	if ht, ok := t.(helperT); ok {
 		ht.Helper()
 	}
-	tempfile, err := ioutil.TempFile("", prefix+"-")
+	tempfile, err := ioutil.TempFile("", cleanPrefix(prefix)+"-")
 	assert.NilError(t, err)
 	file := &File{path: tempfile.Name()}
 	assert.NilError(t, tempfile.Close())
-
-	for _, op := range ops {
-		assert.NilError(t, op(file))
-	}
+	assert.NilError(t, applyPathOps(file, ops))
 	if tc, ok := t.(subtest.TestContext); ok {
 		tc.AddCleanup(file.Remove)
 	}
 	return file
 }
 
+func cleanPrefix(prefix string) string {
+	// windows requires both / and \ are replaced
+	if runtime.GOOS == "windows" {
+		prefix = strings.Replace(prefix, string(os.PathSeparator), "-", -1)
+	}
+	return strings.Replace(prefix, "/", "-", -1)
+}
+
 // Path returns the full path to the file
 func (f *File) Path() string {
 	return f.path
@@ -76,13 +83,10 @@ func NewDir(t assert.TestingT, prefix string, ops ...PathOp) *Dir {
 	if ht, ok := t.(helperT); ok {
 		ht.Helper()
 	}
-	path, err := ioutil.TempDir("", prefix+"-")
+	path, err := ioutil.TempDir("", cleanPrefix(prefix)+"-")
 	assert.NilError(t, err)
 	dir := &Dir{path: path}
-
-	for _, op := range ops {
-		assert.NilError(t, op(dir))
-	}
+	assert.NilError(t, applyPathOps(dir, ops))
 	if tc, ok := t.(subtest.TestContext); ok {
 		tc.AddCleanup(dir.Remove)
 	}

+ 14 - 4
vendor/gotest.tools/fs/manifest.go

@@ -24,7 +24,9 @@ type resource struct {
 
 type file struct {
 	resource
-	content io.ReadCloser
+	content             io.ReadCloser
+	ignoreCariageReturn bool
+	compareContentFunc  func(b []byte) CompareResult
 }
 
 func (f *file) Type() string {
@@ -42,7 +44,8 @@ func (f *symlink) Type() string {
 
 type directory struct {
 	resource
-	items map[string]dirEntry
+	items         map[string]dirEntry
+	filepathGlobs map[string]*filePath
 }
 
 func (f *directory) Type() string {
@@ -94,8 +97,9 @@ func newDirectory(path string, info os.FileInfo) (*directory, error) {
 	}
 
 	return &directory{
-		resource: newResourceFromInfo(info),
-		items:    items,
+		resource:      newResourceFromInfo(info),
+		items:         items,
+		filepathGlobs: make(map[string]*filePath),
 	}, nil
 }
 
@@ -113,6 +117,9 @@ func getTypedResource(path string, info os.FileInfo) (dirEntry, error) {
 
 func newSymlink(path string, info os.FileInfo) (*symlink, error) {
 	target, err := os.Readlink(path)
+	if err != nil {
+		return nil, err
+	}
 	return &symlink{
 		resource: newResourceFromInfo(info),
 		target:   target,
@@ -122,6 +129,9 @@ func newSymlink(path string, info os.FileInfo) (*symlink, error) {
 func newFile(path string, info os.FileInfo) (*file, error) {
 	// TODO: defer file opening to reduce number of open FDs?
 	readCloser, err := os.Open(path)
+	if err != nil {
+		return nil, err
+	}
 	return &file{
 		resource: newResourceFromInfo(info),
 		content:  readCloser,

+ 29 - 8
vendor/gotest.tools/fs/ops.go

@@ -10,6 +10,7 @@ import (
 	"time"
 
 	"github.com/pkg/errors"
+	"gotest.tools/assert"
 )
 
 const defaultFileMode = 0644
@@ -144,6 +145,14 @@ func WithDir(name string, ops ...PathOp) PathOp {
 	}
 }
 
+// Apply the PathOps to the File
+func Apply(t assert.TestingT, path Path, ops ...PathOp) {
+	if ht, ok := t.(helperT); ok {
+		ht.Helper()
+	}
+	assert.NilError(t, applyPathOps(path, ops))
+}
+
 func applyPathOps(path Path, ops []PathOp) error {
 	for _, op := range ops {
 		if err := op(path); err != nil {
@@ -172,23 +181,35 @@ func copyDirectory(source, dest string) error {
 	for _, entry := range entries {
 		sourcePath := filepath.Join(source, entry.Name())
 		destPath := filepath.Join(dest, entry.Name())
-		if entry.IsDir() {
+		switch {
+		case entry.IsDir():
 			if err := os.Mkdir(destPath, 0755); err != nil {
 				return err
 			}
 			if err := copyDirectory(sourcePath, destPath); err != nil {
 				return err
 			}
-			continue
-		}
-		// TODO: handle symlinks
-		if err := copyFile(sourcePath, destPath); err != nil {
-			return err
+		case entry.Mode()&os.ModeSymlink != 0:
+			if err := copySymLink(sourcePath, destPath); err != nil {
+				return err
+			}
+		default:
+			if err := copyFile(sourcePath, destPath); err != nil {
+				return err
+			}
 		}
 	}
 	return nil
 }
 
+func copySymLink(source, dest string) error {
+	link, err := os.Readlink(source)
+	if err != nil {
+		return err
+	}
+	return os.Symlink(link, dest)
+}
+
 func copyFile(source, dest string) error {
 	content, err := ioutil.ReadFile(source)
 	if err != nil {
@@ -219,7 +240,7 @@ func WithSymlink(path, target string) PathOp {
 func WithHardlink(path, target string) PathOp {
 	return func(root Path) error {
 		if _, ok := root.(manifestDirectory); ok {
-			return errors.New("WithHardlink yet implemented for manifests")
+			return errors.New("WithHardlink not implemented for manifests")
 		}
 		return os.Link(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path))
 	}
@@ -230,7 +251,7 @@ func WithHardlink(path, target string) PathOp {
 func WithTimestamps(atime, mtime time.Time) PathOp {
 	return func(root Path) error {
 		if _, ok := root.(manifestDirectory); ok {
-			return errors.New("WithTimestamp yet implemented for manifests")
+			return errors.New("WithTimestamp not implemented for manifests")
 		}
 		return os.Chtimes(root.Path(), atime, mtime)
 	}

+ 50 - 2
vendor/gotest.tools/fs/path.go

@@ -64,6 +64,13 @@ func (p *directoryPath) AddFile(path string, ops ...PathOp) error {
 	return applyPathOps(exp, ops)
 }
 
+func (p *directoryPath) AddGlobFiles(glob string, ops ...PathOp) error {
+	newFile := &file{resource: newResource(0)}
+	newFilePath := &filePath{file: newFile}
+	p.directory.filepathGlobs[glob] = newFilePath
+	return applyPathOps(newFilePath, ops)
+}
+
 func (p *directoryPath) AddDirectory(path string, ops ...PathOp) error {
 	newDir := newDirectoryWithDefaults()
 	p.directory.items[path] = newDir
@@ -87,8 +94,9 @@ func Expected(t assert.TestingT, ops ...PathOp) Manifest {
 
 func newDirectoryWithDefaults() *directory {
 	return &directory{
-		resource: newResource(defaultRootDirMode),
-		items:    make(map[string]dirEntry),
+		resource:      newResource(defaultRootDirMode),
+		items:         make(map[string]dirEntry),
+		filepathGlobs: make(map[string]*filePath),
 	}
 }
 
@@ -127,6 +135,15 @@ func MatchAnyFileContent(path Path) error {
 	return nil
 }
 
+// MatchContentIgnoreCarriageReturn is a PathOp that ignores cariage return
+// discrepancies.
+func MatchContentIgnoreCarriageReturn(path Path) error {
+	if m, ok := path.(*filePath); ok {
+		m.file.ignoreCariageReturn = true
+	}
+	return nil
+}
+
 const anyFile = "*"
 
 // MatchExtraFiles is a PathOp that updates a Manifest to allow a directory
@@ -138,6 +155,37 @@ func MatchExtraFiles(path Path) error {
 	return nil
 }
 
+// CompareResult is the result of comparison.
+//
+// See gotest.tools/assert/cmp.StringResult for a convenient implementation of
+// this interface.
+type CompareResult interface {
+	Success() bool
+	FailureMessage() string
+}
+
+// MatchFileContent is a PathOp that updates a Manifest to use the provided
+// function to determine if a file's content matches the expectation.
+func MatchFileContent(f func([]byte) CompareResult) PathOp {
+	return func(path Path) error {
+		if m, ok := path.(*filePath); ok {
+			m.file.compareContentFunc = f
+		}
+		return nil
+	}
+}
+
+// MatchFilesWithGlob is a PathOp that updates a Manifest to match files using
+// glob pattern, and check them using the ops.
+func MatchFilesWithGlob(glob string, ops ...PathOp) PathOp {
+	return func(path Path) error {
+		if m, ok := path.(*directoryPath); ok {
+			m.AddGlobFiles(glob, ops...)
+		}
+		return nil
+	}
+}
+
 // anyFileMode is represented by uint32_max
 const anyFileMode os.FileMode = 4294967295
 

+ 71 - 9
vendor/gotest.tools/fs/report.go

@@ -6,6 +6,7 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"runtime"
 	"sort"
 	"strings"
 
@@ -67,6 +68,11 @@ func eqResource(x, y resource) []problem {
 	return p
 }
 
+func removeCarriageReturn(in []byte) []byte {
+	return bytes.Replace(in, []byte("\r\n"), []byte("\n"), -1)
+}
+
+// nolint: gocyclo
 func eqFile(x, y *file) []problem {
 	p := eqResource(x.resource, y.resource)
 
@@ -96,6 +102,19 @@ func eqFile(x, y *file) []problem {
 		return p
 	}
 
+	if x.compareContentFunc != nil {
+		r := x.compareContentFunc(yContent)
+		if !r.Success() {
+			p = append(p, existenceProblem("content", r.FailureMessage()))
+		}
+		return p
+	}
+
+	if x.ignoreCariageReturn || y.ignoreCariageReturn {
+		xContent = removeCarriageReturn(xContent)
+		yContent = removeCarriageReturn(yContent)
+	}
+
 	if !bytes.Equal(xContent, yContent) {
 		p = append(p, diffContent(xContent, yContent))
 	}
@@ -126,7 +145,13 @@ func indent(s, prefix string) string {
 
 func eqSymlink(x, y *symlink) []problem {
 	p := eqResource(x.resource, y.resource)
-	if x.target != y.target {
+	xTarget := x.target
+	yTarget := y.target
+	if runtime.GOOS == "windows" {
+		xTarget = strings.ToLower(xTarget)
+		yTarget = strings.ToLower(yTarget)
+	}
+	if xTarget != yTarget {
 		p = append(p, notEqual("target", x.target, y.target))
 	}
 	return p
@@ -135,11 +160,13 @@ func eqSymlink(x, y *symlink) []problem {
 func eqDirectory(path string, x, y *directory) []failure {
 	p := eqResource(x.resource, y.resource)
 	var f []failure
+	matchedFiles := make(map[string]bool)
 
 	for _, name := range sortedKeys(x.items) {
 		if name == anyFile {
 			continue
 		}
+		matchedFiles[name] = true
 		xEntry := x.items[name]
 		yEntry, ok := y.items[name]
 		if !ok {
@@ -155,19 +182,30 @@ func eqDirectory(path string, x, y *directory) []failure {
 		f = append(f, eqEntry(filepath.Join(path, name), xEntry, yEntry)...)
 	}
 
-	if _, ok := x.items[anyFile]; !ok {
+	if len(x.filepathGlobs) != 0 {
 		for _, name := range sortedKeys(y.items) {
-			if _, ok := x.items[name]; !ok {
-				yEntry := y.items[name]
-				p = append(p, existenceProblem(name, "unexpected %s", yEntry.Type()))
-			}
+			m := matchGlob(name, y.items[name], x.filepathGlobs)
+			matchedFiles[name] = m.match
+			f = append(f, m.failures...)
 		}
 	}
 
-	if len(p) > 0 {
-		f = append(f, failure{path: path, problems: p})
+	if _, ok := x.items[anyFile]; ok {
+		return maybeAppendFailure(f, path, p)
 	}
-	return f
+	for _, name := range sortedKeys(y.items) {
+		if !matchedFiles[name] {
+			p = append(p, existenceProblem(name, "unexpected %s", y.items[name].Type()))
+		}
+	}
+	return maybeAppendFailure(f, path, p)
+}
+
+func maybeAppendFailure(failures []failure, path string, problems []problem) []failure {
+	if len(problems) > 0 {
+		return append(failures, failure{path: path, problems: problems})
+	}
+	return failures
 }
 
 func sortedKeys(items map[string]dirEntry) []string {
@@ -199,6 +237,30 @@ func eqEntry(path string, x, y dirEntry) []failure {
 	return nil
 }
 
+type globMatch struct {
+	match    bool
+	failures []failure
+}
+
+func matchGlob(name string, yEntry dirEntry, globs map[string]*filePath) globMatch {
+	m := globMatch{}
+
+	for glob, expectedFile := range globs {
+		ok, err := filepath.Match(glob, name)
+		if err != nil {
+			p := errProblem("failed to match glob pattern", err)
+			f := failure{path: name, problems: []problem{p}}
+			m.failures = append(m.failures, f)
+		}
+		if ok {
+			m.match = true
+			m.failures = eqEntry(name, expectedFile.file, yEntry)
+			return m
+		}
+	}
+	return m
+}
+
 func formatFailures(failures []failure) string {
 	sort.Slice(failures, func(i, j int) bool {
 		return failures[i].path < failures[j].path

+ 6 - 3
vendor/gotest.tools/icmd/command.go

@@ -132,18 +132,21 @@ func (r *Result) String() string {
 	if r.Timeout {
 		timeout = " (timeout)"
 	}
+	var errString string
+	if r.Error != nil {
+		errString = "\nError:    " + r.Error.Error()
+	}
 
 	return fmt.Sprintf(`
 Command:  %s
-ExitCode: %d%s
-Error:    %v
+ExitCode: %d%s%s
 Stdout:   %v
 Stderr:   %v
 `,
 		strings.Join(r.Cmd.Args, " "),
 		r.ExitCode,
 		timeout,
-		r.Error,
+		errString,
 		r.Stdout(),
 		r.Stderr())
 }

+ 34 - 0
vendor/gotest.tools/icmd/ops.go

@@ -1,4 +1,38 @@
 package icmd
 
+import (
+	"io"
+	"time"
+)
+
 // CmdOp is an operation which modified a Cmd structure used to execute commands
 type CmdOp func(*Cmd)
+
+// WithTimeout sets the timeout duration of the command
+func WithTimeout(timeout time.Duration) CmdOp {
+	return func(c *Cmd) {
+		c.Timeout = timeout
+	}
+}
+
+// WithEnv sets the environment variable of the command.
+// Each arguments are in the form of KEY=VALUE
+func WithEnv(env ...string) CmdOp {
+	return func(c *Cmd) {
+		c.Env = env
+	}
+}
+
+// Dir sets the working directory of the command
+func Dir(path string) CmdOp {
+	return func(c *Cmd) {
+		c.Dir = path
+	}
+}
+
+// WithStdin sets the standard input of the command to the specified reader
+func WithStdin(r io.Reader) CmdOp {
+	return func(c *Cmd) {
+		c.Stdin = r
+	}
+}

+ 14 - 11
vendor/gotest.tools/internal/difflib/difflib.go

@@ -1,4 +1,4 @@
-/* Package difflib is a partial port of Python difflib module.
+/*Package difflib is a partial port of Python difflib module.
 
 Original source: https://github.com/pmezard/go-difflib
 
@@ -20,12 +20,14 @@ func max(a, b int) int {
 	return b
 }
 
+// Match stores line numbers of size of match
 type Match struct {
 	A    int
 	B    int
 	Size int
 }
 
+// OpCode identifies the type of diff
 type OpCode struct {
 	Tag byte
 	I1  int
@@ -73,19 +75,20 @@ type SequenceMatcher struct {
 	opCodes        []OpCode
 }
 
+// NewMatcher returns a new SequenceMatcher
 func NewMatcher(a, b []string) *SequenceMatcher {
 	m := SequenceMatcher{autoJunk: true}
 	m.SetSeqs(a, b)
 	return &m
 }
 
-// Set two sequences to be compared.
+// SetSeqs sets two sequences to be compared.
 func (m *SequenceMatcher) SetSeqs(a, b []string) {
 	m.SetSeq1(a)
 	m.SetSeq2(b)
 }
 
-// Set the first sequence to be compared. The second sequence to be compared is
+// SetSeq1 sets the first sequence to be compared. The second sequence to be compared is
 // not changed.
 //
 // SequenceMatcher computes and caches detailed information about the second
@@ -103,7 +106,7 @@ func (m *SequenceMatcher) SetSeq1(a []string) {
 	m.opCodes = nil
 }
 
-// Set the second sequence to be compared. The first sequence to be compared is
+// SetSeq2 sets the second sequence to be compared. The first sequence to be compared is
 // not changed.
 func (m *SequenceMatcher) SetSeq2(b []string) {
 	if &b == &m.b {
@@ -129,12 +132,12 @@ func (m *SequenceMatcher) chainB() {
 	m.bJunk = map[string]struct{}{}
 	if m.IsJunk != nil {
 		junk := m.bJunk
-		for s, _ := range b2j {
+		for s := range b2j {
 			if m.IsJunk(s) {
 				junk[s] = struct{}{}
 			}
 		}
-		for s, _ := range junk {
+		for s := range junk {
 			delete(b2j, s)
 		}
 	}
@@ -149,7 +152,7 @@ func (m *SequenceMatcher) chainB() {
 				popular[s] = struct{}{}
 			}
 		}
-		for s, _ := range popular {
+		for s := range popular {
 			delete(b2j, s)
 		}
 	}
@@ -259,7 +262,7 @@ func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
 	return Match{A: besti, B: bestj, Size: bestsize}
 }
 
-// Return list of triples describing matching subsequences.
+// GetMatchingBlocks returns a list of triples describing matching subsequences.
 //
 // Each triple is of the form (i, j, n), and means that
 // a[i:i+n] == b[j:j+n].  The triples are monotonically increasing in
@@ -323,7 +326,7 @@ func (m *SequenceMatcher) GetMatchingBlocks() []Match {
 	return m.matchingBlocks
 }
 
-// Return list of 5-tuples describing how to turn a into b.
+// GetOpCodes returns a list of 5-tuples describing how to turn a into b.
 //
 // Each tuple is of the form (tag, i1, i2, j1, j2).  The first tuple
 // has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
@@ -374,7 +377,7 @@ func (m *SequenceMatcher) GetOpCodes() []OpCode {
 	return m.opCodes
 }
 
-// Isolate change clusters by eliminating ranges with no changes.
+// GetGroupedOpCodes isolates change clusters by eliminating ranges with no changes.
 //
 // Return a generator of groups with up to n lines of context.
 // Each group is in the same format as returned by GetOpCodes().
@@ -384,7 +387,7 @@ func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
 	}
 	codes := m.GetOpCodes()
 	if len(codes) == 0 {
-		codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
+		codes = []OpCode{{'e', 0, 1, 0, 1}}
 	}
 	// Fixup leading and trailing groups if they show no changes.
 	if codes[0].Tag == 'e' {

+ 53 - 0
vendor/gotest.tools/internal/source/defers.go

@@ -0,0 +1,53 @@
+package source
+
+import (
+	"go/ast"
+	"go/token"
+
+	"github.com/pkg/errors"
+)
+
+func scanToDeferLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node {
+	var matchedNode ast.Node
+	ast.Inspect(node, func(node ast.Node) bool {
+		switch {
+		case node == nil || matchedNode != nil:
+			return false
+		case fileset.Position(node.End()).Line == lineNum:
+			if funcLit, ok := node.(*ast.FuncLit); ok {
+				matchedNode = funcLit
+				return false
+			}
+		}
+		return true
+	})
+	debug("defer line node: %s", debugFormatNode{matchedNode})
+	return matchedNode
+}
+
+func guessDefer(node ast.Node) (ast.Node, error) {
+	defers := collectDefers(node)
+	switch len(defers) {
+	case 0:
+		return nil, errors.New("failed to expression in defer")
+	case 1:
+		return defers[0].Call, nil
+	default:
+		return nil, errors.Errorf(
+			"ambiguous call expression: multiple (%d) defers in call block",
+			len(defers))
+	}
+}
+
+func collectDefers(node ast.Node) []*ast.DeferStmt {
+	var defers []*ast.DeferStmt
+	ast.Inspect(node, func(node ast.Node) bool {
+		if d, ok := node.(*ast.DeferStmt); ok {
+			defers = append(defers, d)
+			debug("defer: %s", debugFormatNode{d})
+			return false
+		}
+		return true
+	})
+	return defers
+}

+ 58 - 55
vendor/gotest.tools/internal/source/source.go

@@ -24,9 +24,30 @@ func FormattedCallExprArg(stackIndex int, argPos int) (string, error) {
 	if err != nil {
 		return "", err
 	}
+	if argPos >= len(args) {
+		return "", errors.New("failed to find expression")
+	}
 	return FormatNode(args[argPos])
 }
 
+// CallExprArgs returns the ast.Expr slice for the args of an ast.CallExpr at
+// the index in the call stack.
+func CallExprArgs(stackIndex int) ([]ast.Expr, error) {
+	_, filename, lineNum, ok := runtime.Caller(baseStackIndex + stackIndex)
+	if !ok {
+		return nil, errors.New("failed to get call stack")
+	}
+	debug("call stack position: %s:%d", filename, lineNum)
+
+	node, err := getNodeAtLine(filename, lineNum)
+	if err != nil {
+		return nil, err
+	}
+	debug("found node: %s", debugFormatNode{node})
+
+	return getCallExprArgs(node)
+}
+
 func getNodeAtLine(filename string, lineNum int) (ast.Node, error) {
 	fileset := token.NewFileSet()
 	astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors)
@@ -34,49 +55,44 @@ func getNodeAtLine(filename string, lineNum int) (ast.Node, error) {
 		return nil, errors.Wrapf(err, "failed to parse source file: %s", filename)
 	}
 
-	node := scanToLine(fileset, astFile, lineNum)
-	if node == nil {
-		return nil, errors.Errorf(
-			"failed to find an expression on line %d in %s", lineNum, filename)
+	if node := scanToLine(fileset, astFile, lineNum); node != nil {
+		return node, nil
 	}
-	return node, nil
+	if node := scanToDeferLine(fileset, astFile, lineNum); node != nil {
+		node, err := guessDefer(node)
+		if err != nil || node != nil {
+			return node, err
+		}
+	}
+	return nil, errors.Errorf(
+		"failed to find an expression on line %d in %s", lineNum, filename)
 }
 
 func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node {
-	v := &scanToLineVisitor{lineNum: lineNum, fileset: fileset}
-	ast.Walk(v, node)
-	return v.matchedNode
-}
-
-type scanToLineVisitor struct {
-	lineNum     int
-	matchedNode ast.Node
-	fileset     *token.FileSet
-}
-
-func (v *scanToLineVisitor) Visit(node ast.Node) ast.Visitor {
-	if node == nil || v.matchedNode != nil {
-		return nil
-	}
-	if v.nodePosition(node).Line == v.lineNum {
-		v.matchedNode = node
-		return nil
-	}
-	return v
+	var matchedNode ast.Node
+	ast.Inspect(node, func(node ast.Node) bool {
+		switch {
+		case node == nil || matchedNode != nil:
+			return false
+		case nodePosition(fileset, node).Line == lineNum:
+			matchedNode = node
+			return false
+		}
+		return true
+	})
+	return matchedNode
 }
 
 // In golang 1.9 the line number changed from being the line where the statement
 // ended to the line where the statement began.
-func (v *scanToLineVisitor) nodePosition(node ast.Node) token.Position {
+func nodePosition(fileset *token.FileSet, node ast.Node) token.Position {
 	if goVersionBefore19 {
-		return v.fileset.Position(node.End())
+		return fileset.Position(node.End())
 	}
-	return v.fileset.Position(node.Pos())
+	return fileset.Position(node.Pos())
 }
 
-var goVersionBefore19 = isGOVersionBefore19()
-
-func isGOVersionBefore19() bool {
+var goVersionBefore19 = func() bool {
 	version := runtime.Version()
 	// not a release version
 	if !strings.HasPrefix(version, "go") {
@@ -89,7 +105,7 @@ func isGOVersionBefore19() bool {
 	}
 	minor, err := strconv.ParseInt(parts[1], 10, 32)
 	return err == nil && parts[0] == "1" && minor < 9
-}
+}()
 
 func getCallExprArgs(node ast.Node) ([]ast.Expr, error) {
 	visitor := &callExprVisitor{}
@@ -97,6 +113,7 @@ func getCallExprArgs(node ast.Node) ([]ast.Expr, error) {
 	if visitor.expr == nil {
 		return nil, errors.New("failed to find call expression")
 	}
+	debug("callExpr: %s", debugFormatNode{visitor.expr})
 	return visitor.expr.Args, nil
 }
 
@@ -108,10 +125,14 @@ func (v *callExprVisitor) Visit(node ast.Node) ast.Visitor {
 	if v.expr != nil || node == nil {
 		return nil
 	}
-	debug("visit (%T): %s", node, debugFormatNode{node})
+	debug("visit: %s", debugFormatNode{node})
 
-	if callExpr, ok := node.(*ast.CallExpr); ok {
-		v.expr = callExpr
+	switch typed := node.(type) {
+	case *ast.CallExpr:
+		v.expr = typed
+		return nil
+	case *ast.DeferStmt:
+		ast.Walk(v, typed.Call.Fun)
 		return nil
 	}
 	return v
@@ -124,25 +145,7 @@ func FormatNode(node ast.Node) (string, error) {
 	return buf.String(), err
 }
 
-// CallExprArgs returns the ast.Expr slice for the args of an ast.CallExpr at
-// the index in the call stack.
-func CallExprArgs(stackIndex int) ([]ast.Expr, error) {
-	_, filename, lineNum, ok := runtime.Caller(baseStackIndex + stackIndex)
-	if !ok {
-		return nil, errors.New("failed to get call stack")
-	}
-	debug("call stack position: %s:%d", filename, lineNum)
-
-	node, err := getNodeAtLine(filename, lineNum)
-	if err != nil {
-		return nil, err
-	}
-	debug("found node (%T): %s", node, debugFormatNode{node})
-
-	return getCallExprArgs(node)
-}
-
-var debugEnabled = os.Getenv("GOTESTYOURSELF_DEBUG") != ""
+var debugEnabled = os.Getenv("GOTESTTOOLS_DEBUG") != ""
 
 func debug(format string, args ...interface{}) {
 	if debugEnabled {
@@ -159,5 +162,5 @@ func (n debugFormatNode) String() string {
 	if err != nil {
 		return fmt.Sprintf("failed to format %s: %s", n.Node, err)
 	}
-	return out
+	return fmt.Sprintf("(%T) %s", n.Node, out)
 }

+ 39 - 0
vendor/gotest.tools/poll/check.go

@@ -0,0 +1,39 @@
+package poll
+
+import (
+	"net"
+	"os"
+)
+
+// Check is a function which will be used as check for the WaitOn method.
+type Check func(t LogT) Result
+
+// FileExists looks on filesystem and check that path exists.
+func FileExists(path string) Check {
+	return func(t LogT) Result {
+		_, err := os.Stat(path)
+		if os.IsNotExist(err) {
+			t.Logf("waiting on file %s to exist", path)
+			return Continue("file %s does not exist", path)
+		}
+		if err != nil {
+			return Error(err)
+		}
+
+		return Success()
+	}
+}
+
+// Connection try to open a connection to the address on the
+// named network. See net.Dial for a description of the network and
+// address parameters.
+func Connection(network, address string) Check {
+	return func(t LogT) Result {
+		_, err := net.Dial(network, address)
+		if err != nil {
+			t.Logf("waiting on socket %s://%s to be available...", network, address)
+			return Continue("socket %s://%s not available", network, address)
+		}
+		return Success()
+	}
+}

+ 1 - 1
vendor/gotest.tools/poll/poll.go

@@ -104,7 +104,7 @@ func Error(err error) Result {
 // WaitOn a condition or until a timeout. Poll by calling check and exit when
 // check returns a done Result. To fail a test and exit polling with an error
 // return a error result.
-func WaitOn(t TestingT, check func(t LogT) Result, pollOps ...SettingOp) {
+func WaitOn(t TestingT, check Check, pollOps ...SettingOp) {
 	if ht, ok := t.(helperT); ok {
 		ht.Helper()
 	}

+ 23 - 5
vendor/gotest.tools/skip/skip.go

@@ -19,17 +19,29 @@ type skipT interface {
 	Log(args ...interface{})
 }
 
+// Result of skip function
+type Result interface {
+	Skip() bool
+	Message() string
+}
+
 type helperT interface {
 	Helper()
 }
 
-// BoolOrCheckFunc can be a bool or func() bool, other types will panic
+// BoolOrCheckFunc can be a bool, func() bool, or func() Result. Other types will panic
 type BoolOrCheckFunc interface{}
 
-// If the condition expression evaluates to true, or the condition function returns
-// true, skip the test.
+// If the condition expression evaluates to true, skip the test.
+//
+// The condition argument may be one of three types: bool, func() bool, or
+// func() SkipResult.
+// When called with a bool, the test will be skip if the condition evaluates to true.
+// When called with a func() bool, the test will be skip if the function returns true.
+// When called with a func() Result, the test will be skip if the Skip method
+// of the result returns true.
 // The skip message will contain the source code of the expression.
-// Extra message text can be passed as a format string with args
+// Extra message text can be passed as a format string with args.
 func If(t skipT, condition BoolOrCheckFunc, msgAndArgs ...interface{}) {
 	if ht, ok := t.(helperT); ok {
 		ht.Helper()
@@ -41,12 +53,18 @@ func If(t skipT, condition BoolOrCheckFunc, msgAndArgs ...interface{}) {
 		if check() {
 			t.Skip(format.WithCustomMessage(getFunctionName(check), msgAndArgs...))
 		}
+	case func() Result:
+		result := check()
+		if result.Skip() {
+			msg := getFunctionName(check) + ": " + result.Message()
+			t.Skip(format.WithCustomMessage(msg, msgAndArgs...))
+		}
 	default:
 		panic(fmt.Sprintf("invalid type for condition arg: %T", check))
 	}
 }
 
-func getFunctionName(function func() bool) string {
+func getFunctionName(function interface{}) string {
 	funcPath := runtime.FuncForPC(reflect.ValueOf(function).Pointer()).Name()
 	return strings.SplitN(path.Base(funcPath), ".", 2)[1]
 }