aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/hashicorp
diff options
context:
space:
mode:
authorNiall Sheridan <nsheridan@gmail.com>2017-10-18 13:15:14 +0100
committerNiall Sheridan <niall@intercom.io>2017-10-18 13:25:46 +0100
commit7b320119ba532fd409ec7dade7ad02011c309599 (patch)
treea39860f35b55e6cc499f8f5bfa969138c5dd6b73 /vendor/github.com/hashicorp
parent7c99874c7a3e7a89716f3ee0cdf696532e35ae35 (diff)
Update dependencies
Diffstat (limited to 'vendor/github.com/hashicorp')
-rw-r--r--vendor/github.com/hashicorp/go-multierror/multierror.go4
-rw-r--r--vendor/github.com/hashicorp/hcl/appveyor.yml2
-rw-r--r--vendor/github.com/hashicorp/hcl/decoder.go37
-rw-r--r--vendor/github.com/hashicorp/hcl/hcl/parser/parser.go20
-rw-r--r--vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go2
-rw-r--r--vendor/github.com/hashicorp/hcl/json/parser/parser.go2
-rw-r--r--vendor/github.com/hashicorp/hcl/json/scanner/scanner.go2
-rw-r--r--vendor/github.com/hashicorp/vault/api/auth_token.go20
-rw-r--r--vendor/github.com/hashicorp/vault/api/client.go78
-rw-r--r--vendor/github.com/hashicorp/vault/api/renewer.go302
-rw-r--r--vendor/github.com/hashicorp/vault/api/request.go9
-rw-r--r--vendor/github.com/hashicorp/vault/api/response.go5
-rw-r--r--vendor/github.com/hashicorp/vault/api/secret.go1
-rw-r--r--vendor/github.com/hashicorp/vault/api/ssh.go17
-rw-r--r--vendor/github.com/hashicorp/vault/api/sys_auth.go18
-rw-r--r--vendor/github.com/hashicorp/vault/api/sys_config_cors.go56
-rw-r--r--vendor/github.com/hashicorp/vault/api/sys_health.go29
-rw-r--r--vendor/github.com/hashicorp/vault/api/sys_leader.go7
-rw-r--r--vendor/github.com/hashicorp/vault/api/sys_leases.go (renamed from vendor/github.com/hashicorp/vault/api/sys_lease.go)8
-rw-r--r--vendor/github.com/hashicorp/vault/api/sys_mounts.go10
-rw-r--r--vendor/github.com/hashicorp/vault/helper/compressutil/compress.go40
-rw-r--r--vendor/github.com/hashicorp/vault/helper/parseutil/parseutil.go65
22 files changed, 676 insertions, 58 deletions
diff --git a/vendor/github.com/hashicorp/go-multierror/multierror.go b/vendor/github.com/hashicorp/go-multierror/multierror.go
index 2ea0827..89b1422 100644
--- a/vendor/github.com/hashicorp/go-multierror/multierror.go
+++ b/vendor/github.com/hashicorp/go-multierror/multierror.go
@@ -40,11 +40,11 @@ func (e *Error) GoString() string {
}
// WrappedErrors returns the list of errors that this Error is wrapping.
-// It is an implementatin of the errwrap.Wrapper interface so that
+// It is an implementation of the errwrap.Wrapper interface so that
// multierror.Error can be used with that library.
//
// This method is not safe to be called concurrently and is no different
-// than accessing the Errors field directly. It is implementd only to
+// than accessing the Errors field directly. It is implemented only to
// satisfy the errwrap.Wrapper interface.
func (e *Error) WrappedErrors() []error {
return e.Errors
diff --git a/vendor/github.com/hashicorp/hcl/appveyor.yml b/vendor/github.com/hashicorp/hcl/appveyor.yml
index 3c8cdf8..4db0b71 100644
--- a/vendor/github.com/hashicorp/hcl/appveyor.yml
+++ b/vendor/github.com/hashicorp/hcl/appveyor.yml
@@ -4,7 +4,7 @@ clone_folder: c:\gopath\src\github.com\hashicorp\hcl
environment:
GOPATH: c:\gopath
init:
- - git config --global core.autocrlf true
+ - git config --global core.autocrlf false
install:
- cmd: >-
echo %Path%
diff --git a/vendor/github.com/hashicorp/hcl/decoder.go b/vendor/github.com/hashicorp/hcl/decoder.go
index 0b39c1b..bed9ebb 100644
--- a/vendor/github.com/hashicorp/hcl/decoder.go
+++ b/vendor/github.com/hashicorp/hcl/decoder.go
@@ -89,7 +89,7 @@ func (d *decoder) decode(name string, node ast.Node, result reflect.Value) error
switch k.Kind() {
case reflect.Bool:
return d.decodeBool(name, node, result)
- case reflect.Float64:
+ case reflect.Float32, reflect.Float64:
return d.decodeFloat(name, node, result)
case reflect.Int, reflect.Int32, reflect.Int64:
return d.decodeInt(name, node, result)
@@ -137,13 +137,13 @@ func (d *decoder) decodeBool(name string, node ast.Node, result reflect.Value) e
func (d *decoder) decodeFloat(name string, node ast.Node, result reflect.Value) error {
switch n := node.(type) {
case *ast.LiteralType:
- if n.Token.Type == token.FLOAT {
+ if n.Token.Type == token.FLOAT || n.Token.Type == token.NUMBER {
v, err := strconv.ParseFloat(n.Token.Text, 64)
if err != nil {
return err
}
- result.Set(reflect.ValueOf(v))
+ result.Set(reflect.ValueOf(v).Convert(result.Type()))
return nil
}
}
@@ -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/parser/parser.go b/vendor/github.com/hashicorp/hcl/hcl/parser/parser.go
index 6e54bed..098e1bc 100644
--- a/vendor/github.com/hashicorp/hcl/hcl/parser/parser.go
+++ b/vendor/github.com/hashicorp/hcl/hcl/parser/parser.go
@@ -3,6 +3,7 @@
package parser
import (
+ "bytes"
"errors"
"fmt"
"strings"
@@ -36,6 +37,11 @@ func newParser(src []byte) *Parser {
// Parse returns the fully parsed source and returns the abstract syntax tree.
func Parse(src []byte) (*ast.File, error) {
+ // normalize all line endings
+ // since the scanner and output only work with "\n" line endings, we may
+ // end up with dangling "\r" characters in the parsed data.
+ src = bytes.Replace(src, []byte("\r\n"), []byte("\n"), -1)
+
p := newParser(src)
return p.Parse()
}
@@ -191,9 +197,12 @@ func (p *Parser) objectItem() (*ast.ObjectItem, error) {
keyStr = append(keyStr, k.Token.Text)
}
- return nil, fmt.Errorf(
- "key '%s' expected start of object ('{') or assignment ('=')",
- strings.Join(keyStr, " "))
+ return nil, &PosError{
+ Pos: p.tok.Pos,
+ Err: fmt.Errorf(
+ "key '%s' expected start of object ('{') or assignment ('=')",
+ strings.Join(keyStr, " ")),
+ }
}
// do a look-ahead for line comment
@@ -313,7 +322,10 @@ func (p *Parser) objectType() (*ast.ObjectType, error) {
// No error, scan and expect the ending to be a brace
if tok := p.scan(); tok.Type != token.RBRACE {
- return nil, fmt.Errorf("object expected closing RBRACE got: %s", tok.Type)
+ return nil, &PosError{
+ Pos: tok.Pos,
+ Err: fmt.Errorf("object expected closing RBRACE got: %s", tok.Type),
+ }
}
o.List = l
diff --git a/vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go b/vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go
index 6966236..6601ef7 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/parser/parser.go b/vendor/github.com/hashicorp/hcl/json/parser/parser.go
index 6f46085..125a5f0 100644
--- a/vendor/github.com/hashicorp/hcl/json/parser/parser.go
+++ b/vendor/github.com/hashicorp/hcl/json/parser/parser.go
@@ -147,7 +147,7 @@ func (p *Parser) objectKey() ([]*ast.ObjectKey, error) {
// Done
return keys, nil
case token.ILLEGAL:
- fmt.Println("illegal")
+ return nil, errors.New("illegal")
default:
return nil, fmt.Errorf("expected: STRING got: %s", p.tok.Type)
}
diff --git a/vendor/github.com/hashicorp/hcl/json/scanner/scanner.go b/vendor/github.com/hashicorp/hcl/json/scanner/scanner.go
index dd5c72b..fe3f0f0 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/vault/api/auth_token.go b/vendor/github.com/hashicorp/vault/api/auth_token.go
index aff10f4..4f74f61 100644
--- a/vendor/github.com/hashicorp/vault/api/auth_token.go
+++ b/vendor/github.com/hashicorp/vault/api/auth_token.go
@@ -135,6 +135,26 @@ func (c *TokenAuth) RenewSelf(increment int) (*Secret, error) {
return ParseSecret(resp.Body)
}
+// RenewTokenAsSelf behaves like renew-self, but authenticates using a provided
+// token instead of the token attached to the client.
+func (c *TokenAuth) RenewTokenAsSelf(token string, increment int) (*Secret, error) {
+ r := c.c.NewRequest("PUT", "/v1/auth/token/renew-self")
+ r.ClientToken = token
+
+ body := map[string]interface{}{"increment": increment}
+ if err := r.SetJSONBody(body); err != nil {
+ return nil, err
+ }
+
+ resp, err := c.c.RawRequest(r)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ return ParseSecret(resp.Body)
+}
+
// RevokeAccessor revokes a token associated with the given accessor
// along with all the child tokens.
func (c *TokenAuth) RevokeAccessor(accessor string) error {
diff --git a/vendor/github.com/hashicorp/vault/api/client.go b/vendor/github.com/hashicorp/vault/api/client.go
index 5f8a6f6..0204cec 100644
--- a/vendor/github.com/hashicorp/vault/api/client.go
+++ b/vendor/github.com/hashicorp/vault/api/client.go
@@ -3,9 +3,11 @@ package api
import (
"crypto/tls"
"fmt"
+ "net"
"net/http"
"net/url"
"os"
+ "path"
"strconv"
"strings"
"sync"
@@ -15,6 +17,7 @@ import (
"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/go-rootcerts"
+ "github.com/hashicorp/vault/helper/parseutil"
"github.com/sethgrid/pester"
)
@@ -23,6 +26,7 @@ const EnvVaultCACert = "VAULT_CACERT"
const EnvVaultCAPath = "VAULT_CAPATH"
const EnvVaultClientCert = "VAULT_CLIENT_CERT"
const EnvVaultClientKey = "VAULT_CLIENT_KEY"
+const EnvVaultClientTimeout = "VAULT_CLIENT_TIMEOUT"
const EnvVaultInsecure = "VAULT_SKIP_VERIFY"
const EnvVaultTLSServerName = "VAULT_TLS_SERVER_NAME"
const EnvVaultWrapTTL = "VAULT_WRAP_TTL"
@@ -53,6 +57,9 @@ type Config struct {
// MaxRetries controls the maximum number of times to retry when a 5xx error
// occurs. Set to 0 or less to disable retrying. Defaults to 0.
MaxRetries int
+
+ // Timeout is for setting custom timeout parameter in the HttpClient
+ Timeout time.Duration
}
// TLSConfig contains the parameters needed to configure TLS on the HTTP client
@@ -155,6 +162,7 @@ func (c *Config) ReadEnvironment() error {
var envCAPath string
var envClientCert string
var envClientKey string
+ var envClientTimeout time.Duration
var envInsecure bool
var envTLSServerName string
var envMaxRetries *uint64
@@ -182,6 +190,13 @@ func (c *Config) ReadEnvironment() error {
if v := os.Getenv(EnvVaultClientKey); v != "" {
envClientKey = v
}
+ if t := os.Getenv(EnvVaultClientTimeout); t != "" {
+ clientTimeout, err := parseutil.ParseDurationSecond(t)
+ if err != nil {
+ return fmt.Errorf("Could not parse %s", EnvVaultClientTimeout)
+ }
+ envClientTimeout = clientTimeout
+ }
if v := os.Getenv(EnvVaultInsecure); v != "" {
var err error
envInsecure, err = strconv.ParseBool(v)
@@ -214,6 +229,10 @@ func (c *Config) ReadEnvironment() error {
c.MaxRetries = int(*envMaxRetries) + 1
}
+ if envClientTimeout != 0 {
+ c.Timeout = envClientTimeout
+ }
+
return nil
}
@@ -223,6 +242,7 @@ type Client struct {
addr *url.URL
config *Config
token string
+ headers http.Header
wrappingLookupFunc WrappingLookupFunc
}
@@ -247,10 +267,14 @@ func NewClient(c *Config) (*Client, error) {
if c.HttpClient == nil {
c.HttpClient = DefaultConfig().HttpClient
}
+ if c.HttpClient.Transport == nil {
+ c.HttpClient.Transport = cleanhttp.DefaultTransport()
+ }
- tp := c.HttpClient.Transport.(*http.Transport)
- if err := http2.ConfigureTransport(tp); err != nil {
- return nil, err
+ if tp, ok := c.HttpClient.Transport.(*http.Transport); ok {
+ if err := http2.ConfigureTransport(tp); err != nil {
+ return nil, err
+ }
}
redirFunc := func() {
@@ -303,6 +327,11 @@ func (c *Client) SetMaxRetries(retries int) {
c.config.MaxRetries = retries
}
+// SetClientTimeout sets the client request timeout
+func (c *Client) SetClientTimeout(timeout time.Duration) {
+ c.config.Timeout = timeout
+}
+
// SetWrappingLookupFunc sets a lookup function that returns desired wrap TTLs
// for a given operation and path
func (c *Client) SetWrappingLookupFunc(lookupFunc WrappingLookupFunc) {
@@ -326,17 +355,38 @@ func (c *Client) ClearToken() {
c.token = ""
}
+// SetHeaders sets the headers to be used for future requests.
+func (c *Client) SetHeaders(headers http.Header) {
+ c.headers = headers
+}
+
+// Clone creates a copy of this client.
+func (c *Client) Clone() (*Client, error) {
+ return NewClient(c.config)
+}
+
// NewRequest creates a new raw request object to query the Vault server
// configured for this client. This is an advanced method and generally
// doesn't need to be called externally.
-func (c *Client) NewRequest(method, path string) *Request {
+func (c *Client) NewRequest(method, requestPath string) *Request {
+ // if SRV records exist (see https://tools.ietf.org/html/draft-andrews-http-srv-02), lookup the SRV
+ // record and take the highest match; this is not designed for high-availability, just discovery
+ var host string = c.addr.Host
+ if c.addr.Port() == "" {
+ // Internet Draft specifies that the SRV record is ignored if a port is given
+ _, addrs, err := net.LookupSRV("http", "tcp", c.addr.Hostname())
+ if err == nil && len(addrs) > 0 {
+ host = fmt.Sprintf("%s:%d", addrs[0].Target, addrs[0].Port)
+ }
+ }
+
req := &Request{
Method: method,
URL: &url.URL{
User: c.addr.User,
Scheme: c.addr.Scheme,
- Host: c.addr.Host,
- Path: path,
+ Host: host,
+ Path: path.Join(c.addr.Path, requestPath),
},
ClientToken: c.token,
Params: make(map[string][]string),
@@ -344,18 +394,24 @@ func (c *Client) NewRequest(method, path string) *Request {
var lookupPath string
switch {
- case strings.HasPrefix(path, "/v1/"):
- lookupPath = strings.TrimPrefix(path, "/v1/")
- case strings.HasPrefix(path, "v1/"):
- lookupPath = strings.TrimPrefix(path, "v1/")
+ case strings.HasPrefix(requestPath, "/v1/"):
+ lookupPath = strings.TrimPrefix(requestPath, "/v1/")
+ case strings.HasPrefix(requestPath, "v1/"):
+ lookupPath = strings.TrimPrefix(requestPath, "v1/")
default:
- lookupPath = path
+ lookupPath = requestPath
}
if c.wrappingLookupFunc != nil {
req.WrapTTL = c.wrappingLookupFunc(method, lookupPath)
} else {
req.WrapTTL = DefaultWrappingLookupFunc(method, lookupPath)
}
+ if c.config.Timeout != 0 {
+ c.config.HttpClient.Timeout = c.config.Timeout
+ }
+ if c.headers != nil {
+ req.Headers = c.headers
+ }
return req
}
diff --git a/vendor/github.com/hashicorp/vault/api/renewer.go b/vendor/github.com/hashicorp/vault/api/renewer.go
new file mode 100644
index 0000000..a2a4b66
--- /dev/null
+++ b/vendor/github.com/hashicorp/vault/api/renewer.go
@@ -0,0 +1,302 @@
+package api
+
+import (
+ "errors"
+ "math/rand"
+ "sync"
+ "time"
+)
+
+var (
+ ErrRenewerMissingInput = errors.New("missing input to renewer")
+ ErrRenewerMissingSecret = errors.New("missing secret to renew")
+ ErrRenewerNotRenewable = errors.New("secret is not renewable")
+ ErrRenewerNoSecretData = errors.New("returned empty secret data")
+
+ // DefaultRenewerGrace is the default grace period
+ DefaultRenewerGrace = 15 * time.Second
+
+ // DefaultRenewerRenewBuffer is the default size of the buffer for renew
+ // messages on the channel.
+ DefaultRenewerRenewBuffer = 5
+)
+
+// Renewer is a process for renewing a secret.
+//
+// renewer, err := client.NewRenewer(&RenewerInput{
+// Secret: mySecret,
+// })
+// go renewer.Renew()
+// defer renewer.Stop()
+//
+// for {
+// select {
+// case err := <-renewer.DoneCh():
+// if err != nil {
+// log.Fatal(err)
+// }
+//
+// // Renewal is now over
+// case renewal := <-renewer.RenewCh():
+// log.Printf("Successfully renewed: %#v", renewal)
+// }
+// }
+//
+//
+// The `DoneCh` will return if renewal fails or if the remaining lease duration
+// after a renewal is less than or equal to the grace (in number of seconds). In
+// both cases, the caller should attempt a re-read of the secret. Clients should
+// check the return value of the channel to see if renewal was successful.
+type Renewer struct {
+ l sync.Mutex
+
+ client *Client
+ secret *Secret
+ grace time.Duration
+ random *rand.Rand
+ doneCh chan error
+ renewCh chan *RenewOutput
+
+ stopped bool
+ stopCh chan struct{}
+}
+
+// RenewerInput is used as input to the renew function.
+type RenewerInput struct {
+ // Secret is the secret to renew
+ Secret *Secret
+
+ // Grace is a minimum renewal before returning so the upstream client
+ // can do a re-read. This can be used to prevent clients from waiting
+ // too long to read a new credential and incur downtime.
+ Grace time.Duration
+
+ // Rand is the randomizer to use for underlying randomization. If not
+ // provided, one will be generated and seeded automatically. If provided, it
+ // is assumed to have already been seeded.
+ Rand *rand.Rand
+
+ // RenewBuffer is the size of the buffered channel where renew messages are
+ // dispatched.
+ RenewBuffer int
+}
+
+// RenewOutput is the metadata returned to the client (if it's listening) to
+// renew messages.
+type RenewOutput struct {
+ // RenewedAt is the timestamp when the renewal took place (UTC).
+ RenewedAt time.Time
+
+ // Secret is the underlying renewal data. It's the same struct as all data
+ // that is returned from Vault, but since this is renewal data, it will not
+ // usually include the secret itself.
+ Secret *Secret
+}
+
+// NewRenewer creates a new renewer from the given input.
+func (c *Client) NewRenewer(i *RenewerInput) (*Renewer, error) {
+ if i == nil {
+ return nil, ErrRenewerMissingInput
+ }
+
+ secret := i.Secret
+ if secret == nil {
+ return nil, ErrRenewerMissingSecret
+ }
+
+ grace := i.Grace
+ if grace == 0 {
+ grace = DefaultRenewerGrace
+ }
+
+ random := i.Rand
+ if random == nil {
+ random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
+ }
+
+ renewBuffer := i.RenewBuffer
+ if renewBuffer == 0 {
+ renewBuffer = DefaultRenewerRenewBuffer
+ }
+
+ return &Renewer{
+ client: c,
+ secret: secret,
+ grace: grace,
+ random: random,
+ doneCh: make(chan error, 1),
+ renewCh: make(chan *RenewOutput, renewBuffer),
+
+ stopped: false,
+ stopCh: make(chan struct{}),
+ }, nil
+}
+
+// DoneCh returns the channel where the renewer will publish when renewal stops.
+// If there is an error, this will be an error.
+func (r *Renewer) DoneCh() <-chan error {
+ return r.doneCh
+}
+
+// RenewCh is a channel that receives a message when a successful renewal takes
+// place and includes metadata about the renewal.
+func (r *Renewer) RenewCh() <-chan *RenewOutput {
+ return r.renewCh
+}
+
+// Stop stops the renewer.
+func (r *Renewer) Stop() {
+ r.l.Lock()
+ if !r.stopped {
+ close(r.stopCh)
+ r.stopped = true
+ }
+ r.l.Unlock()
+}
+
+// Renew starts a background process for renewing this secret. When the secret
+// is has auth data, this attempts to renew the auth (token). When the secret
+// has a lease, this attempts to renew the lease.
+func (r *Renewer) Renew() {
+ var result error
+ if r.secret.Auth != nil {
+ result = r.renewAuth()
+ } else {
+ result = r.renewLease()
+ }
+
+ select {
+ case r.doneCh <- result:
+ case <-r.stopCh:
+ }
+}
+
+// renewAuth is a helper for renewing authentication.
+func (r *Renewer) renewAuth() error {
+ if !r.secret.Auth.Renewable || r.secret.Auth.ClientToken == "" {
+ return ErrRenewerNotRenewable
+ }
+
+ client, token := r.client, r.secret.Auth.ClientToken
+
+ for {
+ // Check if we are stopped.
+ select {
+ case <-r.stopCh:
+ return nil
+ default:
+ }
+
+ // Renew the auth.
+ renewal, err := client.Auth().Token().RenewTokenAsSelf(token, 0)
+ if err != nil {
+ return err
+ }
+
+ // Push a message that a renewal took place.
+ select {
+ case r.renewCh <- &RenewOutput{time.Now().UTC(), renewal}:
+ default:
+ }
+
+ // Somehow, sometimes, this happens.
+ if renewal == nil || renewal.Auth == nil {
+ return ErrRenewerNoSecretData
+ }
+
+ // Do nothing if we are not renewable
+ if !renewal.Auth.Renewable {
+ return ErrRenewerNotRenewable
+ }
+
+ // Grab the lease duration and sleep duration - note that we grab the auth
+ // lease duration, not the secret lease duration.
+ leaseDuration := time.Duration(renewal.Auth.LeaseDuration) * time.Second
+ sleepDuration := r.sleepDuration(leaseDuration)
+
+ // If we are within grace, return now.
+ if leaseDuration <= r.grace || sleepDuration <= r.grace {
+ return nil
+ }
+
+ select {
+ case <-r.stopCh:
+ return nil
+ case <-time.After(sleepDuration):
+ continue
+ }
+ }
+}
+
+// renewLease is a helper for renewing a lease.
+func (r *Renewer) renewLease() error {
+ if !r.secret.Renewable || r.secret.LeaseID == "" {
+ return ErrRenewerNotRenewable
+ }
+
+ client, leaseID := r.client, r.secret.LeaseID
+
+ for {
+ // Check if we are stopped.
+ select {
+ case <-r.stopCh:
+ return nil
+ default:
+ }
+
+ // Renew the lease.
+ renewal, err := client.Sys().Renew(leaseID, 0)
+ if err != nil {
+ return err
+ }
+
+ // Push a message that a renewal took place.
+ select {
+ case r.renewCh <- &RenewOutput{time.Now().UTC(), renewal}:
+ default:
+ }
+
+ // Somehow, sometimes, this happens.
+ if renewal == nil {
+ return ErrRenewerNoSecretData
+ }
+
+ // Do nothing if we are not renewable
+ if !renewal.Renewable {
+ return ErrRenewerNotRenewable
+ }
+
+ // Grab the lease duration and sleep duration
+ leaseDuration := time.Duration(renewal.LeaseDuration) * time.Second
+ sleepDuration := r.sleepDuration(leaseDuration)
+
+ // If we are within grace, return now.
+ if leaseDuration <= r.grace || sleepDuration <= r.grace {
+ return nil
+ }
+
+ select {
+ case <-r.stopCh:
+ return nil
+ case <-time.After(sleepDuration):
+ continue
+ }
+ }
+}
+
+// sleepDuration calculates the time to sleep given the base lease duration. The
+// base is the resulting lease duration. It will be reduced to 1/3 and
+// multiplied by a random float between 0.0 and 1.0. This extra randomness
+// prevents multiple clients from all trying to renew simultaneously.
+func (r *Renewer) sleepDuration(base time.Duration) time.Duration {
+ sleep := float64(base)
+
+ // Renew at 1/3 the remaining lease. This will give us an opportunity to retry
+ // at least one more time should the first renewal fail.
+ sleep = sleep / 3.0
+
+ // Use a randomness so many clients do not hit Vault simultaneously.
+ sleep = sleep * (r.random.Float64() + 1) / 2.0
+
+ return time.Duration(sleep)
+}
diff --git a/vendor/github.com/hashicorp/vault/api/request.go b/vendor/github.com/hashicorp/vault/api/request.go
index 685e2d7..83a28bd 100644
--- a/vendor/github.com/hashicorp/vault/api/request.go
+++ b/vendor/github.com/hashicorp/vault/api/request.go
@@ -14,6 +14,7 @@ type Request struct {
Method string
URL *url.URL
Params url.Values
+ Headers http.Header
ClientToken string
WrapTTL string
Obj interface{}
@@ -60,6 +61,14 @@ func (r *Request) ToHTTP() (*http.Request, error) {
req.URL.Host = r.URL.Host
req.Host = r.URL.Host
+ if r.Headers != nil {
+ for header, vals := range r.Headers {
+ for _, val := range vals {
+ req.Header.Add(header, val)
+ }
+ }
+ }
+
if len(r.ClientToken) != 0 {
req.Header.Set("X-Vault-Token", r.ClientToken)
}
diff --git a/vendor/github.com/hashicorp/vault/api/response.go b/vendor/github.com/hashicorp/vault/api/response.go
index 7c8ac9f..05502e1 100644
--- a/vendor/github.com/hashicorp/vault/api/response.go
+++ b/vendor/github.com/hashicorp/vault/api/response.go
@@ -25,8 +25,9 @@ func (r *Response) DecodeJSON(out interface{}) error {
// this will fully consume the response body, but will not close it. The
// body must still be closed manually.
func (r *Response) Error() error {
- // 200 to 399 are okay status codes
- if r.StatusCode >= 200 && r.StatusCode < 400 {
+ // 200 to 399 are okay status codes. 429 is the code for health status of
+ // standby nodes.
+ if (r.StatusCode >= 200 && r.StatusCode < 400) || r.StatusCode == 429 {
return nil
}
diff --git a/vendor/github.com/hashicorp/vault/api/secret.go b/vendor/github.com/hashicorp/vault/api/secret.go
index 14924f9..7478a0c 100644
--- a/vendor/github.com/hashicorp/vault/api/secret.go
+++ b/vendor/github.com/hashicorp/vault/api/secret.go
@@ -42,6 +42,7 @@ type SecretWrapInfo struct {
Token string `json:"token"`
TTL int `json:"ttl"`
CreationTime time.Time `json:"creation_time"`
+ CreationPath string `json:"creation_path"`
WrappedAccessor string `json:"wrapped_accessor"`
}
diff --git a/vendor/github.com/hashicorp/vault/api/ssh.go b/vendor/github.com/hashicorp/vault/api/ssh.go
index 7c3e56b..a17b0eb 100644
--- a/vendor/github.com/hashicorp/vault/api/ssh.go
+++ b/vendor/github.com/hashicorp/vault/api/ssh.go
@@ -36,3 +36,20 @@ func (c *SSH) Credential(role string, data map[string]interface{}) (*Secret, err
return ParseSecret(resp.Body)
}
+
+// SignKey signs the given public key and returns a signed public key to pass
+// along with the SSH request.
+func (c *SSH) SignKey(role string, data map[string]interface{}) (*Secret, error) {
+ r := c.c.NewRequest("PUT", fmt.Sprintf("/v1/%s/sign/%s", c.MountPoint, role))
+ if err := r.SetJSONBody(data); err != nil {
+ return nil, err
+ }
+
+ resp, err := c.c.RawRequest(r)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ return ParseSecret(resp.Body)
+}
diff --git a/vendor/github.com/hashicorp/vault/api/sys_auth.go b/vendor/github.com/hashicorp/vault/api/sys_auth.go
index f9f3c8c..32f4bbd 100644
--- a/vendor/github.com/hashicorp/vault/api/sys_auth.go
+++ b/vendor/github.com/hashicorp/vault/api/sys_auth.go
@@ -82,19 +82,27 @@ func (c *Sys) DisableAuth(path string) error {
// documentation. Please refer to that documentation for more details.
type EnableAuthOptions struct {
- Type string `json:"type" structs:"type"`
- Description string `json:"description" structs:"description"`
- Local bool `json:"local" structs:"local"`
+ Type string `json:"type" structs:"type"`
+ Description string `json:"description" structs:"description"`
+ Config AuthConfigInput `json:"config" structs:"config"`
+ Local bool `json:"local" structs:"local"`
+ PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty"`
+}
+
+type AuthConfigInput struct {
+ PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
}
type AuthMount struct {
Type string `json:"type" structs:"type" mapstructure:"type"`
Description string `json:"description" structs:"description" mapstructure:"description"`
+ Accessor string `json:"accessor" structs:"accessor" mapstructure:"accessor"`
Config AuthConfigOutput `json:"config" structs:"config" mapstructure:"config"`
Local bool `json:"local" structs:"local" mapstructure:"local"`
}
type AuthConfigOutput struct {
- DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
- MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
+ DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
+ MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
+ PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
}
diff --git a/vendor/github.com/hashicorp/vault/api/sys_config_cors.go b/vendor/github.com/hashicorp/vault/api/sys_config_cors.go
new file mode 100644
index 0000000..e7f2a59
--- /dev/null
+++ b/vendor/github.com/hashicorp/vault/api/sys_config_cors.go
@@ -0,0 +1,56 @@
+package api
+
+func (c *Sys) CORSStatus() (*CORSResponse, error) {
+ r := c.c.NewRequest("GET", "/v1/sys/config/cors")
+ resp, err := c.c.RawRequest(r)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ var result CORSResponse
+ err = resp.DecodeJSON(&result)
+ return &result, err
+}
+
+func (c *Sys) ConfigureCORS(req *CORSRequest) (*CORSResponse, error) {
+ r := c.c.NewRequest("PUT", "/v1/sys/config/cors")
+ if err := r.SetJSONBody(req); err != nil {
+ return nil, err
+ }
+
+ resp, err := c.c.RawRequest(r)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ var result CORSResponse
+ err = resp.DecodeJSON(&result)
+ return &result, err
+}
+
+func (c *Sys) DisableCORS() (*CORSResponse, error) {
+ r := c.c.NewRequest("DELETE", "/v1/sys/config/cors")
+
+ resp, err := c.c.RawRequest(r)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ var result CORSResponse
+ err = resp.DecodeJSON(&result)
+ return &result, err
+
+}
+
+type CORSRequest struct {
+ AllowedOrigins string `json:"allowed_origins"`
+ Enabled bool `json:"enabled"`
+}
+
+type CORSResponse struct {
+ AllowedOrigins string `json:"allowed_origins"`
+ Enabled bool `json:"enabled"`
+}
diff --git a/vendor/github.com/hashicorp/vault/api/sys_health.go b/vendor/github.com/hashicorp/vault/api/sys_health.go
new file mode 100644
index 0000000..822354c
--- /dev/null
+++ b/vendor/github.com/hashicorp/vault/api/sys_health.go
@@ -0,0 +1,29 @@
+package api
+
+func (c *Sys) Health() (*HealthResponse, error) {
+ r := c.c.NewRequest("GET", "/v1/sys/health")
+ // If the code is 400 or above it will automatically turn into an error,
+ // but the sys/health API defaults to returning 5xx when not sealed or
+ // inited, so we force this code to be something else so we parse correctly
+ r.Params.Add("sealedcode", "299")
+ r.Params.Add("uninitcode", "299")
+ resp, err := c.c.RawRequest(r)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ var result HealthResponse
+ err = resp.DecodeJSON(&result)
+ return &result, err
+}
+
+type HealthResponse struct {
+ Initialized bool `json:"initialized"`
+ Sealed bool `json:"sealed"`
+ Standby bool `json:"standby"`
+ ServerTimeUTC int64 `json:"server_time_utc"`
+ Version string `json:"version"`
+ ClusterName string `json:"cluster_name,omitempty"`
+ ClusterID string `json:"cluster_id,omitempty"`
+}
diff --git a/vendor/github.com/hashicorp/vault/api/sys_leader.go b/vendor/github.com/hashicorp/vault/api/sys_leader.go
index 201ac73..4951c46 100644
--- a/vendor/github.com/hashicorp/vault/api/sys_leader.go
+++ b/vendor/github.com/hashicorp/vault/api/sys_leader.go
@@ -14,7 +14,8 @@ func (c *Sys) Leader() (*LeaderResponse, error) {
}
type LeaderResponse struct {
- HAEnabled bool `json:"ha_enabled"`
- IsSelf bool `json:"is_self"`
- LeaderAddress string `json:"leader_address"`
+ HAEnabled bool `json:"ha_enabled"`
+ IsSelf bool `json:"is_self"`
+ LeaderAddress string `json:"leader_address"`
+ LeaderClusterAddress string `json:"leader_cluster_address"`
}
diff --git a/vendor/github.com/hashicorp/vault/api/sys_lease.go b/vendor/github.com/hashicorp/vault/api/sys_leases.go
index e5c19c4..34bd99e 100644
--- a/vendor/github.com/hashicorp/vault/api/sys_lease.go
+++ b/vendor/github.com/hashicorp/vault/api/sys_leases.go
@@ -1,7 +1,7 @@
package api
func (c *Sys) Renew(id string, increment int) (*Secret, error) {
- r := c.c.NewRequest("PUT", "/v1/sys/renew")
+ r := c.c.NewRequest("PUT", "/v1/sys/leases/renew")
body := map[string]interface{}{
"increment": increment,
@@ -21,7 +21,7 @@ func (c *Sys) Renew(id string, increment int) (*Secret, error) {
}
func (c *Sys) Revoke(id string) error {
- r := c.c.NewRequest("PUT", "/v1/sys/revoke/"+id)
+ r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke/"+id)
resp, err := c.c.RawRequest(r)
if err == nil {
defer resp.Body.Close()
@@ -30,7 +30,7 @@ func (c *Sys) Revoke(id string) error {
}
func (c *Sys) RevokePrefix(id string) error {
- r := c.c.NewRequest("PUT", "/v1/sys/revoke-prefix/"+id)
+ r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke-prefix/"+id)
resp, err := c.c.RawRequest(r)
if err == nil {
defer resp.Body.Close()
@@ -39,7 +39,7 @@ func (c *Sys) RevokePrefix(id string) error {
}
func (c *Sys) RevokeForce(id string) error {
- r := c.c.NewRequest("PUT", "/v1/sys/revoke-force/"+id)
+ r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke-force/"+id)
resp, err := c.c.RawRequest(r)
if err == nil {
defer resp.Body.Close()
diff --git a/vendor/github.com/hashicorp/vault/api/sys_mounts.go b/vendor/github.com/hashicorp/vault/api/sys_mounts.go
index 907fddb..091a8f6 100644
--- a/vendor/github.com/hashicorp/vault/api/sys_mounts.go
+++ b/vendor/github.com/hashicorp/vault/api/sys_mounts.go
@@ -124,23 +124,27 @@ type MountInput struct {
Description string `json:"description" structs:"description"`
Config MountConfigInput `json:"config" structs:"config"`
Local bool `json:"local" structs:"local"`
+ PluginName string `json:"plugin_name,omitempty" structs:"plugin_name"`
}
type MountConfigInput struct {
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
+ PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
}
type MountOutput struct {
Type string `json:"type" structs:"type"`
Description string `json:"description" structs:"description"`
+ Accessor string `json:"accessor" structs:"accessor"`
Config MountConfigOutput `json:"config" structs:"config"`
Local bool `json:"local" structs:"local"`
}
type MountConfigOutput struct {
- DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
- MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
- ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
+ DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
+ MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
+ ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
+ PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
}
diff --git a/vendor/github.com/hashicorp/vault/helper/compressutil/compress.go b/vendor/github.com/hashicorp/vault/helper/compressutil/compress.go
index e485f2f..31a2dcd 100644
--- a/vendor/github.com/hashicorp/vault/helper/compressutil/compress.go
+++ b/vendor/github.com/hashicorp/vault/helper/compressutil/compress.go
@@ -6,6 +6,8 @@ import (
"compress/lzw"
"fmt"
"io"
+
+ "github.com/golang/snappy"
)
const (
@@ -20,16 +22,35 @@ const (
// Byte value used as canary when using Lzw format
CompressionCanaryLzw byte = 'L'
+ // Byte value used as canary when using Snappy format
+ CompressionCanarySnappy byte = 'S'
+
CompressionTypeLzw = "lzw"
CompressionTypeGzip = "gzip"
+
+ CompressionTypeSnappy = "snappy"
)
+// SnappyReadCloser embeds the snappy reader which implements the io.Reader
+// interface. The decompress procedure in this utility expectes an
+// io.ReadCloser. This type implements the io.Closer interface to retain the
+// generic way of decompression.
+type SnappyReadCloser struct {
+ *snappy.Reader
+}
+
+// Close is a noop method implemented only to satisfy the io.Closer interface
+func (s *SnappyReadCloser) Close() error {
+ return nil
+}
+
// CompressionConfig is used to select a compression type to be performed by
// Compress and Decompress utilities.
// Supported types are:
// * CompressionTypeLzw
// * CompressionTypeGzip
+// * CompressionTypeSnappy
//
// When using CompressionTypeGzip, the compression levels can also be chosen:
// * gzip.DefaultCompression
@@ -78,9 +99,13 @@ func Compress(data []byte, config *CompressionConfig) ([]byte, error) {
config.GzipCompressionLevel = gzip.DefaultCompression
}
writer, err = gzip.NewWriterLevel(&buf, config.GzipCompressionLevel)
+ case CompressionTypeSnappy:
+ buf.Write([]byte{CompressionCanarySnappy})
+ writer = snappy.NewBufferedWriter(&buf)
default:
return nil, fmt.Errorf("unsupported compression type")
}
+
if err != nil {
return nil, fmt.Errorf("failed to create a compression writer; err: %v", err)
}
@@ -117,22 +142,29 @@ func Decompress(data []byte) ([]byte, bool, error) {
}
switch {
+ // If the first byte matches the canary byte, remove the canary
+ // byte and try to decompress the data that is after the canary.
case data[0] == CompressionCanaryGzip:
- // If the first byte matches the canary byte, remove the canary
- // byte and try to decompress the data that is after the canary.
if len(data) < 2 {
return nil, false, fmt.Errorf("invalid 'data' after the canary")
}
data = data[1:]
reader, err = gzip.NewReader(bytes.NewReader(data))
case data[0] == CompressionCanaryLzw:
- // If the first byte matches the canary byte, remove the canary
- // byte and try to decompress the data that is after the canary.
if len(data) < 2 {
return nil, false, fmt.Errorf("invalid 'data' after the canary")
}
data = data[1:]
reader = lzw.NewReader(bytes.NewReader(data), lzw.LSB, 8)
+
+ case data[0] == CompressionCanarySnappy:
+ if len(data) < 2 {
+ return nil, false, fmt.Errorf("invalid 'data' after the canary")
+ }
+ data = data[1:]
+ reader = &SnappyReadCloser{
+ Reader: snappy.NewReader(bytes.NewReader(data)),
+ }
default:
// If the first byte doesn't match the canary byte, it means
// that the content was not compressed at all. Indicate the
diff --git a/vendor/github.com/hashicorp/vault/helper/parseutil/parseutil.go b/vendor/github.com/hashicorp/vault/helper/parseutil/parseutil.go
new file mode 100644
index 0000000..957d533
--- /dev/null
+++ b/vendor/github.com/hashicorp/vault/helper/parseutil/parseutil.go
@@ -0,0 +1,65 @@
+package parseutil
+
+import (
+ "encoding/json"
+ "errors"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/mitchellh/mapstructure"
+)
+
+func ParseDurationSecond(in interface{}) (time.Duration, error) {
+ var dur time.Duration
+ jsonIn, ok := in.(json.Number)
+ if ok {
+ in = jsonIn.String()
+ }
+ switch in.(type) {
+ case string:
+ inp := in.(string)
+ if inp == "" {
+ return time.Duration(0), nil
+ }
+ var err error
+ // Look for a suffix otherwise its a plain second value
+ if strings.HasSuffix(inp, "s") || strings.HasSuffix(inp, "m") || strings.HasSuffix(inp, "h") {
+ dur, err = time.ParseDuration(inp)
+ if err != nil {
+ return dur, err
+ }
+ } else {
+ // Plain integer
+ secs, err := strconv.ParseInt(inp, 10, 64)
+ if err != nil {
+ return dur, err
+ }
+ dur = time.Duration(secs) * time.Second
+ }
+ case int:
+ dur = time.Duration(in.(int)) * time.Second
+ case int32:
+ dur = time.Duration(in.(int32)) * time.Second
+ case int64:
+ dur = time.Duration(in.(int64)) * time.Second
+ case uint:
+ dur = time.Duration(in.(uint)) * time.Second
+ case uint32:
+ dur = time.Duration(in.(uint32)) * time.Second
+ case uint64:
+ dur = time.Duration(in.(uint64)) * time.Second
+ default:
+ return 0, errors.New("could not parse duration from input")
+ }
+
+ return dur, nil
+}
+
+func ParseBool(in interface{}) (bool, error) {
+ var result bool
+ if err := mapstructure.WeakDecode(in, &result); err != nil {
+ return false, err
+ }
+ return result, nil
+}