mirror of
https://github.com/kemko/nomad.git
synced 2026-01-03 17:05:43 +03:00
vendor + api
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
package config
|
||||
|
||||
import vault "github.com/hashicorp/nomad/api"
|
||||
import vault "github.com/hashicorp/vault/api"
|
||||
|
||||
// VaultConfig contains the configuration information necessary to
|
||||
// communicate with Vault in order to:
|
||||
@@ -105,7 +105,27 @@ func (a *VaultConfig) Merge(b *VaultConfig) *VaultConfig {
|
||||
}
|
||||
|
||||
// ApiConfig() returns a usable Vault config that can be passed directly to
|
||||
// hashicorp/vault/api.
|
||||
func (c *VaultConfig) ApiConfig() (*vault.Config, error) {
|
||||
return nil, nil
|
||||
// hashicorp/vault/api. If readEnv is true, the environment is read for
|
||||
// appropriate Vault variables.
|
||||
func (c *VaultConfig) ApiConfig(readEnv bool) (*vault.Config, error) {
|
||||
conf := vault.DefaultConfig()
|
||||
if readEnv {
|
||||
if err := conf.ReadEnvironment(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
tlsConf := &vault.TLSConfig{
|
||||
CACert: c.CACert,
|
||||
CAPath: c.CAPath,
|
||||
ClientCert: c.CertFile,
|
||||
ClientKey: c.KeyFile,
|
||||
TLSServerName: c.TLSServerName,
|
||||
Insecure: !c.VerifySSL,
|
||||
}
|
||||
if err := conf.ConfigureTLS(tlsConf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
Normal file
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Fatih Arslan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
163
vendor/github.com/fatih/structs/README.md
generated
vendored
Normal file
163
vendor/github.com/fatih/structs/README.md
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
# Structs [](http://godoc.org/github.com/fatih/structs) [](https://travis-ci.org/fatih/structs) [](https://coveralls.io/r/fatih/structs)
|
||||
|
||||
Structs contains various utilities to work with Go (Golang) structs. It was
|
||||
initially used by me to convert a struct into a `map[string]interface{}`. With
|
||||
time I've added other utilities for structs. It's basically a high level
|
||||
package based on primitives from the reflect package. Feel free to add new
|
||||
functions or improve the existing code.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/fatih/structs
|
||||
```
|
||||
|
||||
## Usage and Examples
|
||||
|
||||
Just like the standard lib `strings`, `bytes` and co packages, `structs` has
|
||||
many global functions to manipulate or organize your struct data. Lets define
|
||||
and declare a struct:
|
||||
|
||||
```go
|
||||
type Server struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
ID int
|
||||
Enabled bool
|
||||
users []string // not exported
|
||||
http.Server // embedded
|
||||
}
|
||||
|
||||
server := &Server{
|
||||
Name: "gopher",
|
||||
ID: 123456,
|
||||
Enabled: true,
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// Convert a struct to a map[string]interface{}
|
||||
// => {"Name":"gopher", "ID":123456, "Enabled":true}
|
||||
m := structs.Map(server)
|
||||
|
||||
// Convert the values of a struct to a []interface{}
|
||||
// => ["gopher", 123456, true]
|
||||
v := structs.Values(server)
|
||||
|
||||
// Convert the names of a struct to a []string
|
||||
// (see "Names methods" for more info about fields)
|
||||
n := structs.Names(server)
|
||||
|
||||
// Convert the values of a struct to a []*Field
|
||||
// (see "Field methods" for more info about fields)
|
||||
f := structs.Fields(server)
|
||||
|
||||
// Return the struct name => "Server"
|
||||
n := structs.Name(server)
|
||||
|
||||
// Check if any field of a struct is initialized or not.
|
||||
h := structs.HasZero(server)
|
||||
|
||||
// Check if all fields of a struct is initialized or not.
|
||||
z := structs.IsZero(server)
|
||||
|
||||
// Check if server is a struct or a pointer to struct
|
||||
i := structs.IsStruct(server)
|
||||
```
|
||||
|
||||
### Struct methods
|
||||
|
||||
The structs functions can be also used as independent methods by creating a new
|
||||
`*structs.Struct`. This is handy if you want to have more control over the
|
||||
structs (such as retrieving a single Field).
|
||||
|
||||
```go
|
||||
// Create a new struct type:
|
||||
s := structs.New(server)
|
||||
|
||||
m := s.Map() // Get a map[string]interface{}
|
||||
v := s.Values() // Get a []interface{}
|
||||
f := s.Fields() // Get a []*Field
|
||||
n := s.Names() // Get a []string
|
||||
f := s.Field(name) // Get a *Field based on the given field name
|
||||
f, ok := s.FieldOk(name) // Get a *Field based on the given field name
|
||||
n := s.Name() // Get the struct name
|
||||
h := s.HasZero() // Check if any field is initialized
|
||||
z := s.IsZero() // Check if all fields are initialized
|
||||
```
|
||||
|
||||
### Field methods
|
||||
|
||||
We can easily examine a single Field for more detail. Below you can see how we
|
||||
get and interact with various field methods:
|
||||
|
||||
|
||||
```go
|
||||
s := structs.New(server)
|
||||
|
||||
// Get the Field struct for the "Name" field
|
||||
name := s.Field("Name")
|
||||
|
||||
// Get the underlying value, value => "gopher"
|
||||
value := name.Value().(string)
|
||||
|
||||
// Set the field's value
|
||||
name.Set("another gopher")
|
||||
|
||||
// Get the field's kind, kind => "string"
|
||||
name.Kind()
|
||||
|
||||
// Check if the field is exported or not
|
||||
if name.IsExported() {
|
||||
fmt.Println("Name field is exported")
|
||||
}
|
||||
|
||||
// Check if the value is a zero value, such as "" for string, 0 for int
|
||||
if !name.IsZero() {
|
||||
fmt.Println("Name is initialized")
|
||||
}
|
||||
|
||||
// Check if the field is an anonymous (embedded) field
|
||||
if !name.IsEmbedded() {
|
||||
fmt.Println("Name is not an embedded field")
|
||||
}
|
||||
|
||||
// Get the Field's tag value for tag name "json", tag value => "name,omitempty"
|
||||
tagValue := name.Tag("json")
|
||||
```
|
||||
|
||||
Nested structs are supported too:
|
||||
|
||||
```go
|
||||
addrField := s.Field("Server").Field("Addr")
|
||||
|
||||
// Get the value for addr
|
||||
a := addrField.Value().(string)
|
||||
|
||||
// Or get all fields
|
||||
httpServer := s.Field("Server").Fields()
|
||||
```
|
||||
|
||||
We can also get a slice of Fields from the Struct type to iterate over all
|
||||
fields. This is handy if you wish to examine all fields:
|
||||
|
||||
```go
|
||||
s := structs.New(server)
|
||||
|
||||
for _, f := range s.Fields() {
|
||||
fmt.Printf("field name: %+v\n", f.Name())
|
||||
|
||||
if f.IsExported() {
|
||||
fmt.Printf("value : %+v\n", f.Value())
|
||||
fmt.Printf("is zero : %+v\n", f.IsZero())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
* [Fatih Arslan](https://github.com/fatih)
|
||||
* [Cihangir Savas](https://github.com/cihangir)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT) - see LICENSE.md for more details
|
||||
141
vendor/github.com/fatih/structs/field.go
generated
vendored
Normal file
141
vendor/github.com/fatih/structs/field.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
errNotExported = errors.New("field is not exported")
|
||||
errNotSettable = errors.New("field is not settable")
|
||||
)
|
||||
|
||||
// Field represents a single struct field that encapsulates high level
|
||||
// functions around the field.
|
||||
type Field struct {
|
||||
value reflect.Value
|
||||
field reflect.StructField
|
||||
defaultTag string
|
||||
}
|
||||
|
||||
// Tag returns the value associated with key in the tag string. If there is no
|
||||
// such key in the tag, Tag returns the empty string.
|
||||
func (f *Field) Tag(key string) string {
|
||||
return f.field.Tag.Get(key)
|
||||
}
|
||||
|
||||
// Value returns the underlying value of the field. It panics if the field
|
||||
// is not exported.
|
||||
func (f *Field) Value() interface{} {
|
||||
return f.value.Interface()
|
||||
}
|
||||
|
||||
// IsEmbedded returns true if the given field is an anonymous field (embedded)
|
||||
func (f *Field) IsEmbedded() bool {
|
||||
return f.field.Anonymous
|
||||
}
|
||||
|
||||
// IsExported returns true if the given field is exported.
|
||||
func (f *Field) IsExported() bool {
|
||||
return f.field.PkgPath == ""
|
||||
}
|
||||
|
||||
// IsZero returns true if the given field is not initialized (has a zero value).
|
||||
// It panics if the field is not exported.
|
||||
func (f *Field) IsZero() bool {
|
||||
zero := reflect.Zero(f.value.Type()).Interface()
|
||||
current := f.Value()
|
||||
|
||||
return reflect.DeepEqual(current, zero)
|
||||
}
|
||||
|
||||
// Name returns the name of the given field
|
||||
func (f *Field) Name() string {
|
||||
return f.field.Name
|
||||
}
|
||||
|
||||
// Kind returns the fields kind, such as "string", "map", "bool", etc ..
|
||||
func (f *Field) Kind() reflect.Kind {
|
||||
return f.value.Kind()
|
||||
}
|
||||
|
||||
// Set sets the field to given value v. It returns an error if the field is not
|
||||
// settable (not addressable or not exported) or if the given value's type
|
||||
// doesn't match the fields type.
|
||||
func (f *Field) Set(val interface{}) error {
|
||||
// we can't set unexported fields, so be sure this field is exported
|
||||
if !f.IsExported() {
|
||||
return errNotExported
|
||||
}
|
||||
|
||||
// do we get here? not sure...
|
||||
if !f.value.CanSet() {
|
||||
return errNotSettable
|
||||
}
|
||||
|
||||
given := reflect.ValueOf(val)
|
||||
|
||||
if f.value.Kind() != given.Kind() {
|
||||
return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind())
|
||||
}
|
||||
|
||||
f.value.Set(given)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Zero sets the field to its zero value. It returns an error if the field is not
|
||||
// settable (not addressable or not exported).
|
||||
func (f *Field) Zero() error {
|
||||
zero := reflect.Zero(f.value.Type()).Interface()
|
||||
return f.Set(zero)
|
||||
}
|
||||
|
||||
// Fields returns a slice of Fields. This is particular handy to get the fields
|
||||
// of a nested struct . A struct tag with the content of "-" ignores the
|
||||
// checking of that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field *http.Request `structs:"-"`
|
||||
//
|
||||
// It panics if field is not exported or if field's kind is not struct
|
||||
func (f *Field) Fields() []*Field {
|
||||
return getFields(f.value, f.defaultTag)
|
||||
}
|
||||
|
||||
// Field returns the field from a nested struct. It panics if the nested struct
|
||||
// is not exported or if the field was not found.
|
||||
func (f *Field) Field(name string) *Field {
|
||||
field, ok := f.FieldOk(name)
|
||||
if !ok {
|
||||
panic("field not found")
|
||||
}
|
||||
|
||||
return field
|
||||
}
|
||||
|
||||
// FieldOk returns the field from a nested struct. The boolean returns whether
|
||||
// the field was found (true) or not (false).
|
||||
func (f *Field) FieldOk(name string) (*Field, bool) {
|
||||
value := &f.value
|
||||
// value must be settable so we need to make sure it holds the address of the
|
||||
// variable and not a copy, so we can pass the pointer to strctVal instead of a
|
||||
// copy (which is not assigned to any variable, hence not settable).
|
||||
// see "https://blog.golang.org/laws-of-reflection#TOC_8."
|
||||
if f.value.Kind() != reflect.Ptr {
|
||||
a := f.value.Addr()
|
||||
value = &a
|
||||
}
|
||||
v := strctVal(value.Interface())
|
||||
t := v.Type()
|
||||
|
||||
field, ok := t.FieldByName(name)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &Field{
|
||||
field: field,
|
||||
value: v.FieldByName(name),
|
||||
}, true
|
||||
}
|
||||
576
vendor/github.com/fatih/structs/structs.go
generated
vendored
Normal file
576
vendor/github.com/fatih/structs/structs.go
generated
vendored
Normal file
@@ -0,0 +1,576 @@
|
||||
// Package structs contains various utilities functions to work with structs.
|
||||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultTagName is the default tag name for struct fields which provides
|
||||
// a more granular to tweak certain structs. Lookup the necessary functions
|
||||
// for more info.
|
||||
DefaultTagName = "structs" // struct's field default tag name
|
||||
)
|
||||
|
||||
// Struct encapsulates a struct type to provide several high level functions
|
||||
// around the struct.
|
||||
type Struct struct {
|
||||
raw interface{}
|
||||
value reflect.Value
|
||||
TagName string
|
||||
}
|
||||
|
||||
// New returns a new *Struct with the struct s. It panics if the s's kind is
|
||||
// not struct.
|
||||
func New(s interface{}) *Struct {
|
||||
return &Struct{
|
||||
raw: s,
|
||||
value: strctVal(s),
|
||||
TagName: DefaultTagName,
|
||||
}
|
||||
}
|
||||
|
||||
// Map converts the given struct to a map[string]interface{}, where the keys
|
||||
// of the map are the field names and the values of the map the associated
|
||||
// values of the fields. The default key string is the struct field name but
|
||||
// can be changed in the struct field's tag value. The "structs" key in the
|
||||
// struct's field tag value is the key name. Example:
|
||||
//
|
||||
// // Field appears in map as key "myName".
|
||||
// Name string `structs:"myName"`
|
||||
//
|
||||
// A tag value with the content of "-" ignores that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// A tag value with the content of "string" uses the stringer to get the value. Example:
|
||||
//
|
||||
// // The value will be output of Animal's String() func.
|
||||
// // Map will panic if Animal does not implement String().
|
||||
// Field *Animal `structs:"field,string"`
|
||||
//
|
||||
// A tag value with the option of "flatten" used in a struct field is to flatten its fields
|
||||
// in the output map. Example:
|
||||
//
|
||||
// // The FieldStruct's fields will be flattened into the output map.
|
||||
// FieldStruct time.Time `structs:",flatten"`
|
||||
//
|
||||
// A tag value with the option of "omitnested" stops iterating further if the type
|
||||
// is a struct. Example:
|
||||
//
|
||||
// // Field is not processed further by this package.
|
||||
// Field time.Time `structs:"myName,omitnested"`
|
||||
// Field *http.Request `structs:",omitnested"`
|
||||
//
|
||||
// A tag value with the option of "omitempty" ignores that particular field if
|
||||
// the field value is empty. Example:
|
||||
//
|
||||
// // Field appears in map as key "myName", but the field is
|
||||
// // skipped if empty.
|
||||
// Field string `structs:"myName,omitempty"`
|
||||
//
|
||||
// // Field appears in map as key "Field" (the default), but
|
||||
// // the field is skipped if empty.
|
||||
// Field string `structs:",omitempty"`
|
||||
//
|
||||
// Note that only exported fields of a struct can be accessed, non exported
|
||||
// fields will be neglected.
|
||||
func (s *Struct) Map() map[string]interface{} {
|
||||
out := make(map[string]interface{})
|
||||
s.FillMap(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// FillMap is the same as Map. Instead of returning the output, it fills the
|
||||
// given map.
|
||||
func (s *Struct) FillMap(out map[string]interface{}) {
|
||||
if out == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fields := s.structFields()
|
||||
|
||||
for _, field := range fields {
|
||||
name := field.Name
|
||||
val := s.value.FieldByName(name)
|
||||
isSubStruct := false
|
||||
var finalVal interface{}
|
||||
|
||||
tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||
if tagName != "" {
|
||||
name = tagName
|
||||
}
|
||||
|
||||
// if the value is a zero value and the field is marked as omitempty do
|
||||
// not include
|
||||
if tagOpts.Has("omitempty") {
|
||||
zero := reflect.Zero(val.Type()).Interface()
|
||||
current := val.Interface()
|
||||
|
||||
if reflect.DeepEqual(current, zero) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !tagOpts.Has("omitnested") {
|
||||
finalVal = s.nested(val)
|
||||
|
||||
v := reflect.ValueOf(val.Interface())
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Map, reflect.Struct:
|
||||
isSubStruct = true
|
||||
}
|
||||
} else {
|
||||
finalVal = val.Interface()
|
||||
}
|
||||
|
||||
if tagOpts.Has("string") {
|
||||
s, ok := val.Interface().(fmt.Stringer)
|
||||
if ok {
|
||||
out[name] = s.String()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if isSubStruct && (tagOpts.Has("flatten")) {
|
||||
for k := range finalVal.(map[string]interface{}) {
|
||||
out[k] = finalVal.(map[string]interface{})[k]
|
||||
}
|
||||
} else {
|
||||
out[name] = finalVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Values converts the given s struct's field values to a []interface{}. A
|
||||
// struct tag with the content of "-" ignores the that particular field.
|
||||
// Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field int `structs:"-"`
|
||||
//
|
||||
// A value with the option of "omitnested" stops iterating further if the type
|
||||
// is a struct. Example:
|
||||
//
|
||||
// // Fields is not processed further by this package.
|
||||
// Field time.Time `structs:",omitnested"`
|
||||
// Field *http.Request `structs:",omitnested"`
|
||||
//
|
||||
// A tag value with the option of "omitempty" ignores that particular field and
|
||||
// is not added to the values if the field value is empty. Example:
|
||||
//
|
||||
// // Field is skipped if empty
|
||||
// Field string `structs:",omitempty"`
|
||||
//
|
||||
// Note that only exported fields of a struct can be accessed, non exported
|
||||
// fields will be neglected.
|
||||
func (s *Struct) Values() []interface{} {
|
||||
fields := s.structFields()
|
||||
|
||||
var t []interface{}
|
||||
|
||||
for _, field := range fields {
|
||||
val := s.value.FieldByName(field.Name)
|
||||
|
||||
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||
|
||||
// if the value is a zero value and the field is marked as omitempty do
|
||||
// not include
|
||||
if tagOpts.Has("omitempty") {
|
||||
zero := reflect.Zero(val.Type()).Interface()
|
||||
current := val.Interface()
|
||||
|
||||
if reflect.DeepEqual(current, zero) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if tagOpts.Has("string") {
|
||||
s, ok := val.Interface().(fmt.Stringer)
|
||||
if ok {
|
||||
t = append(t, s.String())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||
// look out for embedded structs, and convert them to a
|
||||
// []interface{} to be added to the final values slice
|
||||
for _, embeddedVal := range Values(val.Interface()) {
|
||||
t = append(t, embeddedVal)
|
||||
}
|
||||
} else {
|
||||
t = append(t, val.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// Fields returns a slice of Fields. A struct tag with the content of "-"
|
||||
// ignores the checking of that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// It panics if s's kind is not struct.
|
||||
func (s *Struct) Fields() []*Field {
|
||||
return getFields(s.value, s.TagName)
|
||||
}
|
||||
|
||||
// Names returns a slice of field names. A struct tag with the content of "-"
|
||||
// ignores the checking of that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// It panics if s's kind is not struct.
|
||||
func (s *Struct) Names() []string {
|
||||
fields := getFields(s.value, s.TagName)
|
||||
|
||||
names := make([]string, len(fields))
|
||||
|
||||
for i, field := range fields {
|
||||
names[i] = field.Name()
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
func getFields(v reflect.Value, tagName string) []*Field {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
t := v.Type()
|
||||
|
||||
var fields []*Field
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
|
||||
if tag := field.Tag.Get(tagName); tag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
f := &Field{
|
||||
field: field,
|
||||
value: v.FieldByName(field.Name),
|
||||
}
|
||||
|
||||
fields = append(fields, f)
|
||||
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
// Field returns a new Field struct that provides several high level functions
|
||||
// around a single struct field entity. It panics if the field is not found.
|
||||
func (s *Struct) Field(name string) *Field {
|
||||
f, ok := s.FieldOk(name)
|
||||
if !ok {
|
||||
panic("field not found")
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// FieldOk returns a new Field struct that provides several high level functions
|
||||
// around a single struct field entity. The boolean returns true if the field
|
||||
// was found.
|
||||
func (s *Struct) FieldOk(name string) (*Field, bool) {
|
||||
t := s.value.Type()
|
||||
|
||||
field, ok := t.FieldByName(name)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &Field{
|
||||
field: field,
|
||||
value: s.value.FieldByName(name),
|
||||
defaultTag: s.TagName,
|
||||
}, true
|
||||
}
|
||||
|
||||
// IsZero returns true if all fields in a struct is a zero value (not
|
||||
// initialized) A struct tag with the content of "-" ignores the checking of
|
||||
// that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// A value with the option of "omitnested" stops iterating further if the type
|
||||
// is a struct. Example:
|
||||
//
|
||||
// // Field is not processed further by this package.
|
||||
// Field time.Time `structs:"myName,omitnested"`
|
||||
// Field *http.Request `structs:",omitnested"`
|
||||
//
|
||||
// Note that only exported fields of a struct can be accessed, non exported
|
||||
// fields will be neglected. It panics if s's kind is not struct.
|
||||
func (s *Struct) IsZero() bool {
|
||||
fields := s.structFields()
|
||||
|
||||
for _, field := range fields {
|
||||
val := s.value.FieldByName(field.Name)
|
||||
|
||||
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||
|
||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||
ok := IsZero(val.Interface())
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// zero value of the given field, such as "" for string, 0 for int
|
||||
zero := reflect.Zero(val.Type()).Interface()
|
||||
|
||||
// current value of the given field
|
||||
current := val.Interface()
|
||||
|
||||
if !reflect.DeepEqual(current, zero) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// HasZero returns true if a field in a struct is not initialized (zero value).
|
||||
// A struct tag with the content of "-" ignores the checking of that particular
|
||||
// field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field bool `structs:"-"`
|
||||
//
|
||||
// A value with the option of "omitnested" stops iterating further if the type
|
||||
// is a struct. Example:
|
||||
//
|
||||
// // Field is not processed further by this package.
|
||||
// Field time.Time `structs:"myName,omitnested"`
|
||||
// Field *http.Request `structs:",omitnested"`
|
||||
//
|
||||
// Note that only exported fields of a struct can be accessed, non exported
|
||||
// fields will be neglected. It panics if s's kind is not struct.
|
||||
func (s *Struct) HasZero() bool {
|
||||
fields := s.structFields()
|
||||
|
||||
for _, field := range fields {
|
||||
val := s.value.FieldByName(field.Name)
|
||||
|
||||
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||
|
||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
||||
ok := HasZero(val.Interface())
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// zero value of the given field, such as "" for string, 0 for int
|
||||
zero := reflect.Zero(val.Type()).Interface()
|
||||
|
||||
// current value of the given field
|
||||
current := val.Interface()
|
||||
|
||||
if reflect.DeepEqual(current, zero) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Name returns the structs's type name within its package. For more info refer
|
||||
// to Name() function.
|
||||
func (s *Struct) Name() string {
|
||||
return s.value.Type().Name()
|
||||
}
|
||||
|
||||
// structFields returns the exported struct fields for a given s struct. This
|
||||
// is a convenient helper method to avoid duplicate code in some of the
|
||||
// functions.
|
||||
func (s *Struct) structFields() []reflect.StructField {
|
||||
t := s.value.Type()
|
||||
|
||||
var f []reflect.StructField
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
// we can't access the value of unexported fields
|
||||
if field.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// don't check if it's omitted
|
||||
if tag := field.Tag.Get(s.TagName); tag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
f = append(f, field)
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func strctVal(s interface{}) reflect.Value {
|
||||
v := reflect.ValueOf(s)
|
||||
|
||||
// if pointer get the underlying element≤
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Struct {
|
||||
panic("not struct")
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Map converts the given struct to a map[string]interface{}. For more info
|
||||
// refer to Struct types Map() method. It panics if s's kind is not struct.
|
||||
func Map(s interface{}) map[string]interface{} {
|
||||
return New(s).Map()
|
||||
}
|
||||
|
||||
// FillMap is the same as Map. Instead of returning the output, it fills the
|
||||
// given map.
|
||||
func FillMap(s interface{}, out map[string]interface{}) {
|
||||
New(s).FillMap(out)
|
||||
}
|
||||
|
||||
// Values converts the given struct to a []interface{}. For more info refer to
|
||||
// Struct types Values() method. It panics if s's kind is not struct.
|
||||
func Values(s interface{}) []interface{} {
|
||||
return New(s).Values()
|
||||
}
|
||||
|
||||
// Fields returns a slice of *Field. For more info refer to Struct types
|
||||
// Fields() method. It panics if s's kind is not struct.
|
||||
func Fields(s interface{}) []*Field {
|
||||
return New(s).Fields()
|
||||
}
|
||||
|
||||
// Names returns a slice of field names. For more info refer to Struct types
|
||||
// Names() method. It panics if s's kind is not struct.
|
||||
func Names(s interface{}) []string {
|
||||
return New(s).Names()
|
||||
}
|
||||
|
||||
// IsZero returns true if all fields is equal to a zero value. For more info
|
||||
// refer to Struct types IsZero() method. It panics if s's kind is not struct.
|
||||
func IsZero(s interface{}) bool {
|
||||
return New(s).IsZero()
|
||||
}
|
||||
|
||||
// HasZero returns true if any field is equal to a zero value. For more info
|
||||
// refer to Struct types HasZero() method. It panics if s's kind is not struct.
|
||||
func HasZero(s interface{}) bool {
|
||||
return New(s).HasZero()
|
||||
}
|
||||
|
||||
// IsStruct returns true if the given variable is a struct or a pointer to
|
||||
// struct.
|
||||
func IsStruct(s interface{}) bool {
|
||||
v := reflect.ValueOf(s)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
// uninitialized zero value of a struct
|
||||
if v.Kind() == reflect.Invalid {
|
||||
return false
|
||||
}
|
||||
|
||||
return v.Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
// Name returns the structs's type name within its package. It returns an
|
||||
// empty string for unnamed types. It panics if s's kind is not struct.
|
||||
func Name(s interface{}) string {
|
||||
return New(s).Name()
|
||||
}
|
||||
|
||||
// nested retrieves recursively all types for the given value and returns the
|
||||
// nested value.
|
||||
func (s *Struct) nested(val reflect.Value) interface{} {
|
||||
var finalVal interface{}
|
||||
|
||||
v := reflect.ValueOf(val.Interface())
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
n := New(val.Interface())
|
||||
n.TagName = s.TagName
|
||||
m := n.Map()
|
||||
|
||||
// do not add the converted value if there are no exported fields, ie:
|
||||
// time.Time
|
||||
if len(m) == 0 {
|
||||
finalVal = val.Interface()
|
||||
} else {
|
||||
finalVal = m
|
||||
}
|
||||
case reflect.Map:
|
||||
v := val.Type().Elem()
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
// only iterate over struct types, ie: map[string]StructType,
|
||||
// map[string][]StructType,
|
||||
if v.Kind() == reflect.Struct ||
|
||||
(v.Kind() == reflect.Slice && v.Elem().Kind() == reflect.Struct) {
|
||||
m := make(map[string]interface{}, val.Len())
|
||||
for _, k := range val.MapKeys() {
|
||||
m[k.String()] = s.nested(val.MapIndex(k))
|
||||
}
|
||||
finalVal = m
|
||||
break
|
||||
}
|
||||
|
||||
// TODO(arslan): should this be optional?
|
||||
finalVal = val.Interface()
|
||||
case reflect.Slice, reflect.Array:
|
||||
if val.Type().Kind() == reflect.Interface {
|
||||
finalVal = val.Interface()
|
||||
break
|
||||
}
|
||||
|
||||
// TODO(arslan): should this be optional?
|
||||
// do not iterate of non struct types, just pass the value. Ie: []int,
|
||||
// []string, co... We only iterate further if it's a struct.
|
||||
if val.Type().Elem().Kind() != reflect.Struct {
|
||||
finalVal = val.Interface()
|
||||
break
|
||||
}
|
||||
|
||||
slices := make([]interface{}, val.Len(), val.Len())
|
||||
for x := 0; x < val.Len(); x++ {
|
||||
slices[x] = s.nested(val.Index(x))
|
||||
}
|
||||
finalVal = slices
|
||||
default:
|
||||
finalVal = val.Interface()
|
||||
}
|
||||
|
||||
return finalVal
|
||||
}
|
||||
32
vendor/github.com/fatih/structs/tags.go
generated
vendored
Normal file
32
vendor/github.com/fatih/structs/tags.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package structs
|
||||
|
||||
import "strings"
|
||||
|
||||
// tagOptions contains a slice of tag options
|
||||
type tagOptions []string
|
||||
|
||||
// Has returns true if the given optiton is available in tagOptions
|
||||
func (t tagOptions) Has(opt string) bool {
|
||||
for _, tagOpt := range t {
|
||||
if tagOpt == opt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// parseTag splits a struct field's tag into its name and a list of options
|
||||
// which comes after a name. A tag is in the form of: "name,option1,option2".
|
||||
// The name can be neglectected.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
// tag is one of followings:
|
||||
// ""
|
||||
// "name"
|
||||
// "name,opt"
|
||||
// "name,opt,opt2"
|
||||
// ",opt"
|
||||
|
||||
res := strings.Split(tag, ",")
|
||||
return res[0], res[1:]
|
||||
}
|
||||
363
vendor/github.com/hashicorp/go-rootcerts/LICENSE
generated
vendored
Normal file
363
vendor/github.com/hashicorp/go-rootcerts/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,363 @@
|
||||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
||||
|
||||
8
vendor/github.com/hashicorp/go-rootcerts/Makefile
generated
vendored
Normal file
8
vendor/github.com/hashicorp/go-rootcerts/Makefile
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
TEST?=./...
|
||||
|
||||
test:
|
||||
go test $(TEST) $(TESTARGS) -timeout=3s -parallel=4
|
||||
go vet $(TEST)
|
||||
go test $(TEST) -race
|
||||
|
||||
.PHONY: test
|
||||
43
vendor/github.com/hashicorp/go-rootcerts/README.md
generated
vendored
Normal file
43
vendor/github.com/hashicorp/go-rootcerts/README.md
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# rootcerts
|
||||
|
||||
Functions for loading root certificates for TLS connections.
|
||||
|
||||
-----
|
||||
|
||||
Go's standard library `crypto/tls` provides a common mechanism for configuring
|
||||
TLS connections in `tls.Config`. The `RootCAs` field on this struct is a pool
|
||||
of certificates for the client to use as a trust store when verifying server
|
||||
certificates.
|
||||
|
||||
This library contains utility functions for loading certificates destined for
|
||||
that field, as well as one other important thing:
|
||||
|
||||
When the `RootCAs` field is `nil`, the standard library attempts to load the
|
||||
host's root CA set. This behavior is OS-specific, and the Darwin
|
||||
implementation contains [a bug that prevents trusted certificates from the
|
||||
System and Login keychains from being loaded][1]. This library contains
|
||||
Darwin-specific behavior that works around that bug.
|
||||
|
||||
[1]: https://github.com/golang/go/issues/14514
|
||||
|
||||
## Example Usage
|
||||
|
||||
Here's a snippet demonstrating how this library is meant to be used:
|
||||
|
||||
```go
|
||||
func httpClient() (*http.Client, error)
|
||||
tlsConfig := &tls.Config{}
|
||||
err := rootcerts.ConfigureTLS(tlsConfig, &rootcerts.Config{
|
||||
CAFile: os.Getenv("MYAPP_CAFILE"),
|
||||
CAPath: os.Getenv("MYAPP_CAPATH"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := cleanhttp.DefaultClient()
|
||||
t := cleanhttp.DefaultTransport()
|
||||
t.TLSClientConfig = tlsConfig
|
||||
c.Transport = t
|
||||
return c, nil
|
||||
}
|
||||
```
|
||||
9
vendor/github.com/hashicorp/go-rootcerts/doc.go
generated
vendored
Normal file
9
vendor/github.com/hashicorp/go-rootcerts/doc.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Package rootcerts contains functions to aid in loading CA certificates for
|
||||
// TLS connections.
|
||||
//
|
||||
// In addition, its default behavior on Darwin works around an open issue [1]
|
||||
// in Go's crypto/x509 that prevents certicates from being loaded from the
|
||||
// System or Login keychains.
|
||||
//
|
||||
// [1] https://github.com/golang/go/issues/14514
|
||||
package rootcerts
|
||||
103
vendor/github.com/hashicorp/go-rootcerts/rootcerts.go
generated
vendored
Normal file
103
vendor/github.com/hashicorp/go-rootcerts/rootcerts.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
package rootcerts
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Config determines where LoadCACerts will load certificates from. When both
|
||||
// CAFile and CAPath are blank, this library's functions will either load
|
||||
// system roots explicitly and return them, or set the CertPool to nil to allow
|
||||
// Go's standard library to load system certs.
|
||||
type Config struct {
|
||||
// CAFile is a path to a PEM-encoded certificate file or bundle. Takes
|
||||
// precedence over CAPath.
|
||||
CAFile string
|
||||
|
||||
// CAPath is a path to a directory populated with PEM-encoded certificates.
|
||||
CAPath string
|
||||
}
|
||||
|
||||
// ConfigureTLS sets up the RootCAs on the provided tls.Config based on the
|
||||
// Config specified.
|
||||
func ConfigureTLS(t *tls.Config, c *Config) error {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
pool, err := LoadCACerts(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.RootCAs = pool
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadCACerts loads a CertPool based on the Config specified.
|
||||
func LoadCACerts(c *Config) (*x509.CertPool, error) {
|
||||
if c == nil {
|
||||
c = &Config{}
|
||||
}
|
||||
if c.CAFile != "" {
|
||||
return LoadCAFile(c.CAFile)
|
||||
}
|
||||
if c.CAPath != "" {
|
||||
return LoadCAPath(c.CAPath)
|
||||
}
|
||||
|
||||
return LoadSystemCAs()
|
||||
}
|
||||
|
||||
// LoadCAFile loads a single PEM-encoded file from the path specified.
|
||||
func LoadCAFile(caFile string) (*x509.CertPool, error) {
|
||||
pool := x509.NewCertPool()
|
||||
|
||||
pem, err := ioutil.ReadFile(caFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error loading CA File: %s", err)
|
||||
}
|
||||
|
||||
ok := pool.AppendCertsFromPEM(pem)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Error loading CA File: Couldn't parse PEM in: %s", caFile)
|
||||
}
|
||||
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
// LoadCAPath walks the provided path and loads all certificates encounted into
|
||||
// a pool.
|
||||
func LoadCAPath(caPath string) (*x509.CertPool, error) {
|
||||
pool := x509.NewCertPool()
|
||||
walkFn := func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
pem, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error loading file from CAPath: %s", err)
|
||||
}
|
||||
|
||||
ok := pool.AppendCertsFromPEM(pem)
|
||||
if !ok {
|
||||
return fmt.Errorf("Error loading CA Path: Couldn't parse PEM in: %s", path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err := filepath.Walk(caPath, walkFn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pool, nil
|
||||
}
|
||||
12
vendor/github.com/hashicorp/go-rootcerts/rootcerts_base.go
generated
vendored
Normal file
12
vendor/github.com/hashicorp/go-rootcerts/rootcerts_base.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// +build !darwin
|
||||
|
||||
package rootcerts
|
||||
|
||||
import "crypto/x509"
|
||||
|
||||
// LoadSystemCAs does nothing on non-Darwin systems. We return nil so that
|
||||
// default behavior of standard TLS config libraries is triggered, which is to
|
||||
// load system certs.
|
||||
func LoadSystemCAs() (*x509.CertPool, error) {
|
||||
return nil, nil
|
||||
}
|
||||
48
vendor/github.com/hashicorp/go-rootcerts/rootcerts_darwin.go
generated
vendored
Normal file
48
vendor/github.com/hashicorp/go-rootcerts/rootcerts_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package rootcerts
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"os/exec"
|
||||
"path"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// LoadSystemCAs has special behavior on Darwin systems to work around
|
||||
func LoadSystemCAs() (*x509.CertPool, error) {
|
||||
pool := x509.NewCertPool()
|
||||
|
||||
for _, keychain := range certKeychains() {
|
||||
err := addCertsFromKeychain(pool, keychain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
func addCertsFromKeychain(pool *x509.CertPool, keychain string) error {
|
||||
cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", keychain)
|
||||
data, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pool.AppendCertsFromPEM(data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func certKeychains() []string {
|
||||
keychains := []string{
|
||||
"/System/Library/Keychains/SystemRootCertificates.keychain",
|
||||
"/Library/Keychains/System.keychain",
|
||||
}
|
||||
home, err := homedir.Dir()
|
||||
if err == nil {
|
||||
loginKeychain := path.Join(home, "Library", "Keychains", "login.keychain")
|
||||
keychains = append(keychains, loginKeychain)
|
||||
}
|
||||
return keychains
|
||||
}
|
||||
21
vendor/github.com/sethgrid/pester/LICENSE.md
generated
vendored
Normal file
21
vendor/github.com/sethgrid/pester/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [2016] [Seth Ammons]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
126
vendor/github.com/sethgrid/pester/README.md
generated
vendored
Normal file
126
vendor/github.com/sethgrid/pester/README.md
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
# pester
|
||||
|
||||
`pester` wraps Go's standard lib http client to provide several options to increase resiliency in your request. If you experience poor network conditions or requests could experience varied delays, you can now pester the endpoint for data.
|
||||
- Send out multiple requests and get the first back (only used for GET calls)
|
||||
- Retry on errors
|
||||
- Backoff
|
||||
|
||||
### Simple Example
|
||||
Use `pester` where you would use the http client calls. By default, pester will use a concurrency of 1, and retry the endpoint 3 times with the `DefaultBackoff` strategy of waiting 1 second between retries.
|
||||
```go
|
||||
/* swap in replacement, just switch
|
||||
http.{Get|Post|PostForm|Head|Do} to
|
||||
pester.{Get|Post|PostForm|Head|Do}
|
||||
*/
|
||||
resp, err := pester.Get("http://sethammons.com")
|
||||
```
|
||||
|
||||
### Backoff Strategy
|
||||
Provide your own backoff strategy, or use one of the provided built in strategies:
|
||||
- `DefaultBackoff`: 1 second
|
||||
- `LinearBackoff`: n seconds where n is the retry number
|
||||
- `LinearJitterBackoff`: n seconds where n is the retry number, +/- 0-33%
|
||||
- `ExponentialBackoff`: n seconds where n is 2^(retry number)
|
||||
- `ExponentialJitterBackoff`: n seconds where n is 2^(retry number), +/- 0-33%
|
||||
|
||||
```go
|
||||
client := pester.New()
|
||||
client.Backoff = func(retry int) time.Duration {
|
||||
// set up something dynamic or use a look up table
|
||||
return time.Duration(retry) * time.Minute
|
||||
}
|
||||
```
|
||||
|
||||
### Complete example
|
||||
For a complete and working example, see the sample directory.
|
||||
`pester` allows you to use a constructor to control:
|
||||
- backoff strategy
|
||||
- reties
|
||||
- concurrency
|
||||
- keeping a log for debugging
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/sethgrid/pester"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Println("Starting...")
|
||||
|
||||
{ // drop in replacement for http.Get and other client methods
|
||||
resp, err := pester.Get("http://example.com")
|
||||
if err != nil {
|
||||
log.Println("error GETing example.com", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
log.Printf("example.com %s", resp.Status)
|
||||
}
|
||||
|
||||
{ // control the resiliency
|
||||
client := pester.New()
|
||||
client.Concurrency = 3
|
||||
client.MaxRetries = 5
|
||||
client.Backoff = pester.ExponentialBackoff
|
||||
client.KeepLog = true
|
||||
|
||||
resp, err := client.Get("http://example.com")
|
||||
if err != nil {
|
||||
log.Println("error GETing example.com", client.LogString())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
log.Printf("example.com %s", resp.Status)
|
||||
}
|
||||
|
||||
{ // use the pester version of http.Client.Do
|
||||
req, err := http.NewRequest("POST", "http://example.com", strings.NewReader("data"))
|
||||
if err != nil {
|
||||
log.Fatal("Unable to create a new http request", err)
|
||||
}
|
||||
resp, err := pester.Do(req)
|
||||
if err != nil {
|
||||
log.Println("error POSTing example.com", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
log.Printf("example.com %s", resp.Status)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Example Log
|
||||
`pester` also allows you to control the resiliency and can optionally log the errors.
|
||||
```go
|
||||
c := pester.New()
|
||||
c.KeepLog = true
|
||||
|
||||
nonExistantURL := "http://localhost:9000/foo"
|
||||
_, _ = c.Get(nonExistantURL)
|
||||
|
||||
fmt.Println(c.LogString())
|
||||
/*
|
||||
Output:
|
||||
|
||||
1432402837 Get [GET] http://localhost:9000/foo request-0 retry-0 error: Get http://localhost:9000/foo: dial tcp 127.0.0.1:9000: connection refused
|
||||
1432402838 Get [GET] http://localhost:9000/foo request-0 retry-1 error: Get http://localhost:9000/foo: dial tcp 127.0.0.1:9000: connection refused
|
||||
1432402839 Get [GET] http://localhost:9000/foo request-0 retry-2 error: Get http://localhost:9000/foo: dial tcp 127.0.0.1:9000: connection refused
|
||||
*/
|
||||
```
|
||||
|
||||
### Tests
|
||||
|
||||
You can run tests in the root directory with `$ go test`. There is a benchmark-like test available with `$ cd benchmarks; go test`.
|
||||
You can see `pester` in action with `$ cd sample; go run main.go`.
|
||||
|
||||
For watching open file descriptors, you can run `watch "lsof -i -P | grep main"` if you started the app with `go run main.go`.
|
||||
I did this for watching for FD leaks. My method was to alter `sample/main.go` to only run one case (`pester.Get with set backoff stategy, concurrency and retries increased`)
|
||||
and adding a sleep after the result came back. This let me verify if FDs were getting left open when they should have closed. If you know a better way, let me know!
|
||||
I was able to see that FDs are now closing when they should :)
|
||||
|
||||

|
||||
|
||||
Are we there yet? Are we there yet? Are we there yet? Are we there yet? ...
|
||||
423
vendor/github.com/sethgrid/pester/main.go
generated
vendored
Normal file
423
vendor/github.com/sethgrid/pester/main.go
generated
vendored
Normal file
@@ -0,0 +1,423 @@
|
||||
package pester
|
||||
|
||||
// pester provides additional resiliency over the standard http client methods by
|
||||
// allowing you to control concurrency, retries, and a backoff strategy.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Client wraps the http client and exposes all the functionality of the http.Client.
|
||||
// Additionally, Client provides pester specific values for handling resiliency.
|
||||
type Client struct {
|
||||
// wrap it to provide access to http built ins
|
||||
hc *http.Client
|
||||
|
||||
Transport http.RoundTripper
|
||||
CheckRedirect func(req *http.Request, via []*http.Request) error
|
||||
Jar http.CookieJar
|
||||
Timeout time.Duration
|
||||
|
||||
// pester specific
|
||||
Concurrency int
|
||||
MaxRetries int
|
||||
Backoff BackoffStrategy
|
||||
KeepLog bool
|
||||
|
||||
SuccessReqNum int
|
||||
SuccessRetryNum int
|
||||
|
||||
wg *sync.WaitGroup
|
||||
|
||||
sync.Mutex
|
||||
ErrLog []ErrEntry
|
||||
}
|
||||
|
||||
// ErrEntry is used to provide the LogString() data and is populated
|
||||
// each time an error happens if KeepLog is set.
|
||||
// ErrEntry.Retry is deprecated in favor of ErrEntry.Attempt
|
||||
type ErrEntry struct {
|
||||
Time time.Time
|
||||
Method string
|
||||
URL string
|
||||
Verb string
|
||||
Request int
|
||||
Retry int
|
||||
Attempt int
|
||||
Err error
|
||||
}
|
||||
|
||||
// result simplifies the channel communication for concurrent request handling
|
||||
type result struct {
|
||||
resp *http.Response
|
||||
err error
|
||||
req int
|
||||
retry int
|
||||
}
|
||||
|
||||
// params represents all the params needed to run http client calls and pester errors
|
||||
type params struct {
|
||||
method string
|
||||
verb string
|
||||
req *http.Request
|
||||
url string
|
||||
bodyType string
|
||||
body io.Reader
|
||||
data url.Values
|
||||
}
|
||||
|
||||
// New constructs a new DefaultClient with sensible default values
|
||||
func New() *Client {
|
||||
return &Client{
|
||||
Concurrency: DefaultClient.Concurrency,
|
||||
MaxRetries: DefaultClient.MaxRetries,
|
||||
Backoff: DefaultClient.Backoff,
|
||||
ErrLog: DefaultClient.ErrLog,
|
||||
wg: &sync.WaitGroup{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewExtendedClient allows you to pass in an http.Client that is previously set up
|
||||
// and extends it to have Pester's features of concurrency and retries.
|
||||
func NewExtendedClient(hc *http.Client) *Client {
|
||||
c := New()
|
||||
c.hc = hc
|
||||
return c
|
||||
}
|
||||
|
||||
// BackoffStrategy is used to determine how long a retry request should wait until attempted
|
||||
type BackoffStrategy func(retry int) time.Duration
|
||||
|
||||
// DefaultClient provides sensible defaults
|
||||
var DefaultClient = &Client{Concurrency: 1, MaxRetries: 3, Backoff: DefaultBackoff, ErrLog: []ErrEntry{}}
|
||||
|
||||
// DefaultBackoff always returns 1 second
|
||||
func DefaultBackoff(_ int) time.Duration {
|
||||
return 1 * time.Second
|
||||
}
|
||||
|
||||
// ExponentialBackoff returns ever increasing backoffs by a power of 2
|
||||
func ExponentialBackoff(i int) time.Duration {
|
||||
return time.Duration(math.Pow(2, float64(i))) * time.Second
|
||||
}
|
||||
|
||||
// ExponentialJitterBackoff returns ever increasing backoffs by a power of 2
|
||||
// with +/- 0-33% to prevent sychronized reuqests.
|
||||
func ExponentialJitterBackoff(i int) time.Duration {
|
||||
return jitter(int(math.Pow(2, float64(i))))
|
||||
}
|
||||
|
||||
// LinearBackoff returns increasing durations, each a second longer than the last
|
||||
func LinearBackoff(i int) time.Duration {
|
||||
return time.Duration(i) * time.Second
|
||||
}
|
||||
|
||||
// LinearJitterBackoff returns increasing durations, each a second longer than the last
|
||||
// with +/- 0-33% to prevent sychronized reuqests.
|
||||
func LinearJitterBackoff(i int) time.Duration {
|
||||
return jitter(i)
|
||||
}
|
||||
|
||||
// jitter keeps the +/- 0-33% logic in one place
|
||||
func jitter(i int) time.Duration {
|
||||
ms := i * 1000
|
||||
|
||||
maxJitter := ms / 3
|
||||
|
||||
rand.Seed(time.Now().Unix())
|
||||
jitter := rand.Intn(maxJitter + 1)
|
||||
|
||||
if rand.Intn(2) == 1 {
|
||||
ms = ms + jitter
|
||||
} else {
|
||||
ms = ms - jitter
|
||||
}
|
||||
|
||||
// a jitter of 0 messes up the time.Tick chan
|
||||
if ms <= 0 {
|
||||
ms = 1
|
||||
}
|
||||
|
||||
return time.Duration(ms) * time.Millisecond
|
||||
}
|
||||
|
||||
// Wait blocks until all pester requests have returned
|
||||
// Probably not that useful outside of testing.
|
||||
func (c *Client) Wait() {
|
||||
c.wg.Wait()
|
||||
}
|
||||
|
||||
// pester provides all the logic of retries, concurrency, backoff, and logging
|
||||
func (c *Client) pester(p params) (*http.Response, error) {
|
||||
resultCh := make(chan result)
|
||||
multiplexCh := make(chan result)
|
||||
finishCh := make(chan struct{})
|
||||
|
||||
// track all requests that go out so we can close the late listener routine that closes late incoming response bodies
|
||||
totalSentRequests := &sync.WaitGroup{}
|
||||
totalSentRequests.Add(1)
|
||||
defer totalSentRequests.Done()
|
||||
allRequestsBackCh := make(chan struct{})
|
||||
go func() {
|
||||
totalSentRequests.Wait()
|
||||
close(allRequestsBackCh)
|
||||
}()
|
||||
|
||||
// GET calls should be idempotent and can make use
|
||||
// of concurrency. Other verbs can mutate and should not
|
||||
// make use of the concurrency feature
|
||||
concurrency := c.Concurrency
|
||||
if p.verb != "GET" {
|
||||
concurrency = 1
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
if c.hc == nil {
|
||||
c.hc = &http.Client{}
|
||||
c.hc.Transport = c.Transport
|
||||
c.hc.CheckRedirect = c.CheckRedirect
|
||||
c.hc.Jar = c.Jar
|
||||
c.hc.Timeout = c.Timeout
|
||||
}
|
||||
c.Unlock()
|
||||
|
||||
// re-create the http client so we can leverage the std lib
|
||||
httpClient := http.Client{
|
||||
Transport: c.hc.Transport,
|
||||
CheckRedirect: c.hc.CheckRedirect,
|
||||
Jar: c.hc.Jar,
|
||||
Timeout: c.hc.Timeout,
|
||||
}
|
||||
|
||||
// if we have a request body, we need to save it for later
|
||||
var originalRequestBody []byte
|
||||
var originalBody []byte
|
||||
var err error
|
||||
if p.req != nil && p.req.Body != nil {
|
||||
originalRequestBody, err = ioutil.ReadAll(p.req.Body)
|
||||
if err != nil {
|
||||
return &http.Response{}, errors.New("error reading request body")
|
||||
}
|
||||
p.req.Body.Close()
|
||||
}
|
||||
if p.body != nil {
|
||||
originalBody, err = ioutil.ReadAll(p.body)
|
||||
if err != nil {
|
||||
return &http.Response{}, errors.New("error reading body")
|
||||
}
|
||||
}
|
||||
|
||||
AttemptLimit := c.MaxRetries
|
||||
if AttemptLimit <= 0 {
|
||||
AttemptLimit = 1
|
||||
}
|
||||
|
||||
for req := 0; req < concurrency; req++ {
|
||||
c.wg.Add(1)
|
||||
totalSentRequests.Add(1)
|
||||
go func(n int, p params) {
|
||||
defer c.wg.Done()
|
||||
defer totalSentRequests.Done()
|
||||
|
||||
var err error
|
||||
for i := 1; i <= AttemptLimit; i++ {
|
||||
c.wg.Add(1)
|
||||
defer c.wg.Done()
|
||||
select {
|
||||
case <-finishCh:
|
||||
return
|
||||
default:
|
||||
}
|
||||
resp := &http.Response{}
|
||||
|
||||
// rehydrate the body (it is drained each read)
|
||||
if len(originalRequestBody) > 0 {
|
||||
p.req.Body = ioutil.NopCloser(bytes.NewBuffer(originalRequestBody))
|
||||
}
|
||||
if len(originalBody) > 0 {
|
||||
p.body = bytes.NewBuffer(originalBody)
|
||||
}
|
||||
|
||||
// route the calls
|
||||
switch p.method {
|
||||
case "Do":
|
||||
resp, err = httpClient.Do(p.req)
|
||||
case "Get":
|
||||
resp, err = httpClient.Get(p.url)
|
||||
case "Head":
|
||||
resp, err = httpClient.Head(p.url)
|
||||
case "Post":
|
||||
resp, err = httpClient.Post(p.url, p.bodyType, p.body)
|
||||
case "PostForm":
|
||||
resp, err = httpClient.PostForm(p.url, p.data)
|
||||
}
|
||||
|
||||
// Early return if we have a valid result
|
||||
// Only retry (ie, continue the loop) on 5xx status codes
|
||||
if err == nil && resp.StatusCode < 500 {
|
||||
multiplexCh <- result{resp: resp, err: err, req: n, retry: i}
|
||||
return
|
||||
}
|
||||
|
||||
c.log(ErrEntry{
|
||||
Time: time.Now(),
|
||||
Method: p.method,
|
||||
Verb: p.verb,
|
||||
URL: p.url,
|
||||
Request: n,
|
||||
Retry: i + 1, // would remove, but would break backward compatibility
|
||||
Attempt: i,
|
||||
Err: err,
|
||||
})
|
||||
|
||||
// if it is the last iteration, grab the result (which is an error at this point)
|
||||
if i == AttemptLimit {
|
||||
multiplexCh <- result{resp: resp, err: err}
|
||||
return
|
||||
}
|
||||
|
||||
// if we are retrying, we should close this response body to free the fd
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
// prevent a 0 from causing the tick to block, pass additional microsecond
|
||||
<-time.Tick(c.Backoff(i) + 1*time.Microsecond)
|
||||
}
|
||||
}(req, p)
|
||||
}
|
||||
|
||||
// spin off the go routine so it can continually listen in on late results and close the response bodies
|
||||
go func() {
|
||||
gotFirstResult := false
|
||||
for {
|
||||
select {
|
||||
case res := <-multiplexCh:
|
||||
if !gotFirstResult {
|
||||
gotFirstResult = true
|
||||
close(finishCh)
|
||||
resultCh <- res
|
||||
} else if res.resp != nil {
|
||||
// we only return one result to the caller; close all other response bodies that come back
|
||||
// drain the body before close as to not prevent keepalive. see https://gist.github.com/mholt/eba0f2cc96658be0f717
|
||||
io.Copy(ioutil.Discard, res.resp.Body)
|
||||
res.resp.Body.Close()
|
||||
}
|
||||
case <-allRequestsBackCh:
|
||||
// don't leave this goroutine running
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case res := <-resultCh:
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.SuccessReqNum = res.req
|
||||
c.SuccessRetryNum = res.retry
|
||||
return res.resp, res.err
|
||||
}
|
||||
}
|
||||
|
||||
// LogString provides a string representation of the errors the client has seen
|
||||
func (c *Client) LogString() string {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
var res string
|
||||
for _, e := range c.ErrLog {
|
||||
res += fmt.Sprintf("%d %s [%s] %s request-%d retry-%d error: %s\n",
|
||||
e.Time.Unix(), e.Method, e.Verb, e.URL, e.Request, e.Retry, e.Err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// LogErrCount is a helper method used primarily for test validation
|
||||
func (c *Client) LogErrCount() int {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
return len(c.ErrLog)
|
||||
}
|
||||
|
||||
// EmbedHTTPClient allows you to extend an existing Pester client with an
|
||||
// underlying http.Client, such as https://godoc.org/golang.org/x/oauth2/google#DefaultClient
|
||||
func (c *Client) EmbedHTTPClient(hc *http.Client) {
|
||||
c.hc = hc
|
||||
}
|
||||
|
||||
func (c *Client) log(e ErrEntry) {
|
||||
if c.KeepLog {
|
||||
c.Lock()
|
||||
c.ErrLog = append(c.ErrLog, e)
|
||||
c.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Do provides the same functionality as http.Client.Do
|
||||
func (c *Client) Do(req *http.Request) (resp *http.Response, err error) {
|
||||
return c.pester(params{method: "Do", req: req, verb: req.Method, url: req.URL.String()})
|
||||
}
|
||||
|
||||
// Get provides the same functionality as http.Client.Get
|
||||
func (c *Client) Get(url string) (resp *http.Response, err error) {
|
||||
return c.pester(params{method: "Get", url: url, verb: "GET"})
|
||||
}
|
||||
|
||||
// Head provides the same functionality as http.Client.Head
|
||||
func (c *Client) Head(url string) (resp *http.Response, err error) {
|
||||
return c.pester(params{method: "Head", url: url, verb: "HEAD"})
|
||||
}
|
||||
|
||||
// Post provides the same functionality as http.Client.Post
|
||||
func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *http.Response, err error) {
|
||||
return c.pester(params{method: "Post", url: url, bodyType: bodyType, body: body, verb: "POST"})
|
||||
}
|
||||
|
||||
// PostForm provides the same functionality as http.Client.PostForm
|
||||
func (c *Client) PostForm(url string, data url.Values) (resp *http.Response, err error) {
|
||||
return c.pester(params{method: "PostForm", url: url, data: data, verb: "POST"})
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// Provide self-constructing variants //
|
||||
////////////////////////////////////////
|
||||
|
||||
// Do provides the same functionality as http.Client.Do and creates its own constructor
|
||||
func Do(req *http.Request) (resp *http.Response, err error) {
|
||||
c := New()
|
||||
return c.Do(req)
|
||||
}
|
||||
|
||||
// Get provides the same functionality as http.Client.Get and creates its own constructor
|
||||
func Get(url string) (resp *http.Response, err error) {
|
||||
c := New()
|
||||
return c.Get(url)
|
||||
}
|
||||
|
||||
// Head provides the same functionality as http.Client.Head and creates its own constructor
|
||||
func Head(url string) (resp *http.Response, err error) {
|
||||
c := New()
|
||||
return c.Head(url)
|
||||
}
|
||||
|
||||
// Post provides the same functionality as http.Client.Post and creates its own constructor
|
||||
func Post(url string, bodyType string, body io.Reader) (resp *http.Response, err error) {
|
||||
c := New()
|
||||
return c.Post(url, bodyType, body)
|
||||
}
|
||||
|
||||
// PostForm provides the same functionality as http.Client.PostForm and creates its own constructor
|
||||
func PostForm(url string, data url.Values) (resp *http.Response, err error) {
|
||||
c := New()
|
||||
return c.PostForm(url, data)
|
||||
}
|
||||
34
vendor/vendor.json
vendored
34
vendor/vendor.json
vendored
@@ -240,14 +240,14 @@
|
||||
{
|
||||
"checksumSHA1": "tdhmIGUaoOMEDymMC23qTS7bt0g=",
|
||||
"path": "github.com/docker/docker/pkg/ioutils",
|
||||
"revision": "da39e9a4f920a15683dd0f23923c302d4db6eed5",
|
||||
"revisionTime": "2016-05-28T08:11:04Z"
|
||||
"revision": "52debcd58ac91bf68503ce60561536911b74ff05",
|
||||
"revisionTime": "2016-05-20T15:17:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "tdhmIGUaoOMEDymMC23qTS7bt0g=",
|
||||
"path": "github.com/docker/docker/pkg/ioutils",
|
||||
"revision": "52debcd58ac91bf68503ce60561536911b74ff05",
|
||||
"revisionTime": "2016-05-20T15:17:10Z"
|
||||
"revision": "da39e9a4f920a15683dd0f23923c302d4db6eed5",
|
||||
"revisionTime": "2016-05-28T08:11:04Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ndnAFCfsGC3upNQ6jAEwzxcurww=",
|
||||
@@ -270,14 +270,14 @@
|
||||
{
|
||||
"checksumSHA1": "txf3EORYff4hO6PEvwBm2lyh1MU=",
|
||||
"path": "github.com/docker/docker/pkg/promise",
|
||||
"revision": "da39e9a4f920a15683dd0f23923c302d4db6eed5",
|
||||
"revisionTime": "2016-05-28T08:11:04Z"
|
||||
"revision": "52debcd58ac91bf68503ce60561536911b74ff05",
|
||||
"revisionTime": "2016-05-20T15:17:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "txf3EORYff4hO6PEvwBm2lyh1MU=",
|
||||
"path": "github.com/docker/docker/pkg/promise",
|
||||
"revision": "52debcd58ac91bf68503ce60561536911b74ff05",
|
||||
"revisionTime": "2016-05-20T15:17:10Z"
|
||||
"revision": "da39e9a4f920a15683dd0f23923c302d4db6eed5",
|
||||
"revisionTime": "2016-05-28T08:11:04Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "YDYbS5U2mDwfcOUJ6M09cP6Bubg=",
|
||||
@@ -306,6 +306,12 @@
|
||||
"path": "github.com/dustin/go-humanize",
|
||||
"revision": "8929fe90cee4b2cb9deb468b51fb34eba64d1bf0"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "QBkOnLnM6zZ158NJSVLqoE4V6fI=",
|
||||
"path": "github.com/fatih/structs",
|
||||
"revision": "14f46232cd7bc732dc67313a9e4d3d210e082587",
|
||||
"revisionTime": "2016-07-19T20:45:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "QhCS39gtKwqQb13GY4NnKOObZzY=",
|
||||
"path": "github.com/fsouza/go-dockerclient",
|
||||
@@ -508,6 +514,12 @@
|
||||
"revision": "886ce0458bc81ccca0fb7044c1be0e9ab590bed7",
|
||||
"revisionTime": "2016-07-18T23:34:41Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "A1PcINvF3UiwHRKn8UcgARgvGRs=",
|
||||
"path": "github.com/hashicorp/go-rootcerts",
|
||||
"revision": "6bb64b370b90e7ef1fa532be9e591a81c3493e00",
|
||||
"revisionTime": "2016-05-03T14:34:40Z"
|
||||
},
|
||||
{
|
||||
"path": "github.com/hashicorp/go-syslog",
|
||||
"revision": "42a2b573b664dbf281bd48c3cc12c086b17a39ba"
|
||||
@@ -721,6 +733,12 @@
|
||||
"revision": "50b9b539927dfe231b8be1caf81b2c81d5940276",
|
||||
"revisionTime": "2016-06-28T05:25:20Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "8Lm8nsMCFz4+gr9EvQLqK8+w+Ks=",
|
||||
"path": "github.com/sethgrid/pester",
|
||||
"revision": "8053687f99650573b28fb75cddf3f295082704d7",
|
||||
"revisionTime": "2016-04-29T17:20:22Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "UkExAUfzi5jJ08QYurU223nLThM=",
|
||||
"path": "github.com/shirou/gopsutil",
|
||||
|
||||
Reference in New Issue
Block a user