1
0
mirror of https://github.com/Luzifer/vault-openvpn.git synced 2024-09-19 09:32:56 +00:00

Update dependencies for using cobra

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2018-05-27 11:24:38 +02:00
parent 695e366e88
commit 2cbf5dd6ac
Signed by: luzifer
GPG Key ID: DC2729FDD34BE99E
2985 changed files with 80404 additions and 835165 deletions

184
Gopkg.lock generated
View File

@ -2,25 +2,12 @@
[[projects]]
name = "github.com/Luzifer/rconfig"
name = "github.com/fsnotify/fsnotify"
packages = ["."]
revision = "7aef1d393c1e2d0758901853b59981c7adc67c7e"
version = "v1.2.0"
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
version = "v1.4.7"
[[projects]]
name = "github.com/Sirupsen/logrus"
packages = ["."]
revision = "f006c2ac4710855cf0f916dd6b77acf6b048dc6e"
version = "v1.0.3"
[[projects]]
name = "github.com/fatih/structs"
packages = ["."]
revision = "a720dfa8df582c51dee1b36feabb906bde1588bd"
version = "v1.0"
[[projects]]
branch = "master"
name = "github.com/golang/snappy"
packages = ["."]
revision = "553a641470496b2327abcac10b36396bd98e45c9"
@ -35,13 +22,13 @@
branch = "master"
name = "github.com/hashicorp/go-cleanhttp"
packages = ["."]
revision = "3573b8b52aa7b37b9358d966a898feb387f62437"
revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d"
[[projects]]
branch = "master"
name = "github.com/hashicorp/go-multierror"
packages = ["."]
revision = "83588e72410abfbe4df460eeb6f30841ae47d4c4"
revision = "b7773ae218740a7be65057fc60b366a49b538a44"
[[projects]]
branch = "master"
@ -49,17 +36,53 @@
packages = ["."]
revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00"
[[projects]]
name = "github.com/hashicorp/go-sockaddr"
packages = ["."]
revision = "7165ee14aff120ee3642aa2bcf2dea8eebef29c3"
[[projects]]
branch = "master"
name = "github.com/hashicorp/hcl"
packages = [".","hcl/ast","hcl/parser","hcl/scanner","hcl/strconv","hcl/token","json/parser","json/scanner","json/token"]
revision = "42e33e2d55a0ff1d6263f738896ea8c13571a8d0"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/printer",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
"json/parser",
"json/scanner",
"json/token"
]
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
[[projects]]
name = "github.com/hashicorp/vault"
packages = ["api","helper/certutil","helper/compressutil","helper/errutil","helper/jsonutil","helper/parseutil"]
revision = "6b29fb2b7f70ed538ee2b3c057335d706b6d4e36"
version = "v0.8.3"
packages = [
"api",
"helper/certutil",
"helper/compressutil",
"helper/errutil",
"helper/jsonutil",
"helper/parseutil",
"helper/strutil"
]
revision = "756fdc4587350daf1c65b93647b2cc31a6f119cd"
version = "v0.10.1"
[[projects]]
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
version = "v1.0"
[[projects]]
name = "github.com/magiconair/properties"
packages = ["."]
revision = "c2353362d570a7bfa228149c62842019201cfb71"
version = "v1.8.0"
[[projects]]
name = "github.com/mattn/go-runewidth"
@ -71,71 +94,136 @@
branch = "master"
name = "github.com/mitchellh/go-homedir"
packages = ["."]
revision = "b8bc1bf767474819792c23f32d8286a45736f1c6"
revision = "3864e76763d94a6df2f9960b16a20a33da9f9a66"
[[projects]]
branch = "master"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
revision = "d0303fe809921458f417bcf828397a65db30a7e4"
revision = "a4e142e9c047c904fa2f1e144d9a84e6133024bc"
[[projects]]
branch = "master"
name = "github.com/olekukonko/tablewriter"
packages = ["."]
revision = "a7a4c189eb47ed33ce7b35f2880070a0c82a67d4"
revision = "d4647c9c7a84d847478d890b816b7d8b62b0b279"
[[projects]]
name = "github.com/pelletier/go-toml"
packages = ["."]
revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/ryanuber/go-glob"
packages = ["."]
revision = "256dc444b735e061061cf46c809487313d5b0065"
[[projects]]
name = "github.com/sethgrid/pester"
packages = ["."]
revision = "0af5bab1e1ea2860c5aef8e77427bab011d774d8"
revision = "ed9870dad3170c0b25ab9b11830cc57c3a7798fb"
[[projects]]
name = "github.com/sirupsen/logrus"
packages = ["."]
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
version = "v1.0.5"
[[projects]]
name = "github.com/spf13/afero"
packages = [
".",
"mem"
]
revision = "63644898a8da0bc22138abf860edaf5277b6102e"
version = "v1.1.0"
[[projects]]
name = "github.com/spf13/cast"
packages = ["."]
revision = "8965335b8c7107321228e3e3702cab9832751bac"
version = "v1.2.0"
[[projects]]
name = "github.com/spf13/cobra"
packages = ["."]
revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385"
version = "v0.0.3"
[[projects]]
branch = "master"
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
[[projects]]
name = "github.com/spf13/pflag"
packages = ["."]
revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66"
version = "v1.0.0"
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
version = "v1.0.1"
[[projects]]
name = "github.com/spf13/viper"
packages = ["."]
revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736"
version = "v1.0.2"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
revision = "9419663f5a44be8b34ca85f08abc5fe1be11f8a3"
revision = "ab813273cd59e1333f7ae7bff5d027d4aadf528c"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["http2","http2/hpack","idna","lex/httplex"]
revision = "a04bdaca5b32abe1c069418fb7088ae607de5bd0"
packages = [
"http2",
"http2/hpack",
"idna",
"lex/httplex"
]
revision = "f5dfe339be1d06f81b22525fe34671ee7d2c8904"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix","windows"]
revision = "ebfc5b4631820b793c9010c87fd8fef0f39eb082"
packages = [
"unix",
"windows"
]
revision = "c11f84a56e43e20a78cee75a7c034031ecf57d1f"
[[projects]]
branch = "master"
name = "golang.org/x/text"
packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
revision = "825fc78a2fd6fa0a5447e300189e3219e05e1f25"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable"
]
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "v2"
name = "gopkg.in/validator.v2"
packages = ["."]
revision = "460c83432a98c35224a6fe352acf8b23e067ad06"
[[projects]]
branch = "v2"
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
version = "v2.2.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "338132dcce86eed0dc26d5c2a448092d59a299761c47da97aed0c6e98e8c355d"
inputs-digest = "d5c94eece84d41716e83088082c119c84736465b21834e66ed176c3ba5bbccf5"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,7 +1,6 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
@ -19,23 +18,37 @@
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/Luzifer/rconfig"
version = "1.1.0"
[[constraint]]
name = "github.com/Sirupsen/logrus"
[[constraint]]
name = "github.com/hashicorp/vault"
version = "0.10.1"
[[constraint]]
branch = "master"
name = "github.com/mitchellh/go-homedir"
[[constraint]]
branch = "master"
name = "github.com/olekukonko/tablewriter"
[[constraint]]
name = "gopkg.in/yaml.v2"
name = "github.com/sirupsen/logrus"
version = "1.0.5"
[[constraint]]
name = "github.com/spf13/cobra"
version = "0.0.3"
[[constraint]]
name = "github.com/spf13/viper"
version = "1.0.2"
[prune]
go-tests = true
unused-packages = true

View File

@ -1,8 +0,0 @@
language: go
go:
- 1.6
- 1.7
- tip
script: go test -v -race -cover ./...

View File

@ -1,9 +0,0 @@
# 1.2.0 / 2017-06-19
* Add ParseAndValidate method
# 1.1.0 / 2016-06-28
* Support time.Duration config parameters
* Added goreportcard badge
* Added testcase for using bool with ENV and default

View File

@ -1,87 +0,0 @@
[![Build Status](https://travis-ci.org/Luzifer/rconfig.svg?branch=master)](https://travis-ci.org/Luzifer/rconfig)
[![License: Apache v2.0](https://badge.luzifer.io/v1/badge?color=5d79b5&title=license&text=Apache+v2.0)](http://www.apache.org/licenses/LICENSE-2.0)
[![Documentation](https://badge.luzifer.io/v1/badge?title=godoc&text=reference)](https://godoc.org/github.com/Luzifer/rconfig)
[![Go Report](http://goreportcard.com/badge/Luzifer/rconfig)](http://goreportcard.com/report/Luzifer/rconfig)
## Description
> Package rconfig implements a CLI configuration reader with struct-embedded defaults, environment variables and posix compatible flag parsing using the [pflag](https://github.com/spf13/pflag) library.
## Installation
Install by running:
```
go get -u github.com/Luzifer/rconfig
```
OR fetch a specific version:
```
go get -u gopkg.in/luzifer/rconfig.v1
```
Run tests by running:
```
go test -v -race -cover github.com/Luzifer/rconfig
```
## Usage
A very simple usecase is to just configure a struct inside the vars section of your `main.go` and to parse the commandline flags from the `main()` function:
```go
package main
import (
"fmt"
"github.com/Luzifer/rconfig"
)
var (
cfg = struct {
Username string `default:"unknown" flag:"user" description:"Your name"`
Details struct {
Age int `default:"25" flag:"age" env:"age" description:"Your age"`
}
}{}
)
func main() {
rconfig.Parse(&cfg)
fmt.Printf("Hello %s, happy birthday for your %dth birthday.",
cfg.Username,
cfg.Details.Age)
}
```
### Provide variable defaults by using a file
Given you have a file `~/.myapp.yml` containing some secrets or usernames (for the example below username is assumed to be "luzifer") as a default configuration for your application you can use this source code to load the defaults from that file using the `vardefault` tag in your configuration struct.
The order of the directives (lower number = higher precedence):
1. Flags provided in command line
1. Environment variables
1. Variable defaults (`vardefault` tag in the struct)
1. `default` tag in the struct
```go
var cfg = struct {
Username string `vardefault:"username" flag:"username" description:"Your username"`
}
func main() {
rconfig.SetVariableDefaults(rconfig.VarDefaultsFromYAMLFile("~/.myapp.yml"))
rconfig.Parse(&cfg)
fmt.Printf("Username = %s", cfg.Username)
// Output: Username = luzifer
}
```
## More info
You can see the full reference documentation of the rconfig package [at godoc.org](https://godoc.org/github.com/Luzifer/rconfig), or through go's standard documentation system by running `godoc -http=:6060` and browsing to [http://localhost:6060/pkg/github.com/Luzifer/rconfig](http://localhost:6060/pkg/github.com/Luzifer/rconfig) after installation.

View File

@ -1,70 +0,0 @@
package rconfig
import (
"os"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Testing bool parsing", func() {
type t struct {
Test1 bool `default:"true"`
Test2 bool `default:"false" flag:"test2"`
Test3 bool `default:"true" flag:"test3,t"`
Test4 bool `flag:"test4"`
}
var (
err error
args []string
cfg t
)
BeforeEach(func() {
cfg = t{}
args = []string{
"--test2",
"-t",
}
})
JustBeforeEach(func() {
err = parse(&cfg, args)
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values", func() {
Expect(cfg.Test1).To(Equal(true))
Expect(cfg.Test2).To(Equal(true))
Expect(cfg.Test3).To(Equal(true))
Expect(cfg.Test4).To(Equal(false))
})
})
var _ = Describe("Testing to set bool from ENV with default", func() {
type t struct {
Test1 bool `default:"true" env:"TEST1"`
}
var (
err error
args []string
cfg t
)
BeforeEach(func() {
cfg = t{}
args = []string{}
})
JustBeforeEach(func() {
os.Unsetenv("TEST1")
err = parse(&cfg, args)
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values", func() {
Expect(cfg.Test1).To(Equal(true))
})
})

View File

@ -1,356 +0,0 @@
// Package rconfig implements a CLI configuration reader with struct-embedded
// defaults, environment variables and posix compatible flag parsing using
// the pflag library.
package rconfig
import (
"errors"
"fmt"
"os"
"reflect"
"strconv"
"strings"
"time"
"github.com/spf13/pflag"
validator "gopkg.in/validator.v2"
)
var (
fs *pflag.FlagSet
variableDefaults map[string]string
)
func init() {
variableDefaults = make(map[string]string)
}
// Parse takes the pointer to a struct filled with variables which should be read
// from ENV, default or flag. The precedence in this is flag > ENV > default. So
// if a flag is specified on the CLI it will overwrite the ENV and otherwise ENV
// overwrites the default specified.
//
// For your configuration struct you can use the following struct-tags to control
// the behavior of rconfig:
//
// default: Set a default value
// vardefault: Read the default value from the variable defaults
// env: Read the value from this environment variable
// flag: Flag to read in format "long,short" (for example "listen,l")
// description: A help text for Usage output to guide your users
//
// The format you need to specify those values you can see in the example to this
// function.
//
func Parse(config interface{}) error {
return parse(config, nil)
}
// ParseAndValidate works exactly like Parse but implements an additional run of
// the go-validator package on the configuration struct. Therefore additonal struct
// tags are supported like described in the readme file of the go-validator package:
//
// https://github.com/go-validator/validator/tree/v2#usage
func ParseAndValidate(config interface{}) error {
return parseAndValidate(config, nil)
}
// Args returns the non-flag command-line arguments.
func Args() []string {
return fs.Args()
}
// Usage prints a basic usage with the corresponding defaults for the flags to
// os.Stdout. The defaults are derived from the `default` struct-tag and the ENV.
func Usage() {
if fs != nil && fs.Parsed() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fs.PrintDefaults()
}
}
// SetVariableDefaults presets the parser with a map of default values to be used
// when specifying the vardefault tag
func SetVariableDefaults(defaults map[string]string) {
variableDefaults = defaults
}
func parseAndValidate(in interface{}, args []string) error {
if err := parse(in, args); err != nil {
return err
}
return validator.Validate(in)
}
func parse(in interface{}, args []string) error {
if args == nil {
args = os.Args
}
fs = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
if err := execTags(in, fs); err != nil {
return err
}
return fs.Parse(args)
}
func execTags(in interface{}, fs *pflag.FlagSet) error {
if reflect.TypeOf(in).Kind() != reflect.Ptr {
return errors.New("Calling parser with non-pointer")
}
if reflect.ValueOf(in).Elem().Kind() != reflect.Struct {
return errors.New("Calling parser with pointer to non-struct")
}
st := reflect.ValueOf(in).Elem()
for i := 0; i < st.NumField(); i++ {
valField := st.Field(i)
typeField := st.Type().Field(i)
if typeField.Tag.Get("default") == "" && typeField.Tag.Get("env") == "" && typeField.Tag.Get("flag") == "" && typeField.Type.Kind() != reflect.Struct {
// None of our supported tags is present and it's not a sub-struct
continue
}
value := varDefault(typeField.Tag.Get("vardefault"), typeField.Tag.Get("default"))
value = envDefault(typeField.Tag.Get("env"), value)
parts := strings.Split(typeField.Tag.Get("flag"), ",")
switch typeField.Type {
case reflect.TypeOf(time.Duration(0)):
v, err := time.ParseDuration(value)
if err != nil {
if value == "" {
v = time.Duration(0)
} else {
return err
}
}
if typeField.Tag.Get("flag") != "" {
if len(parts) == 1 {
fs.DurationVar(valField.Addr().Interface().(*time.Duration), parts[0], v, typeField.Tag.Get("description"))
} else {
fs.DurationVarP(valField.Addr().Interface().(*time.Duration), parts[0], parts[1], v, typeField.Tag.Get("description"))
}
} else {
valField.Set(reflect.ValueOf(v))
}
continue
}
switch typeField.Type.Kind() {
case reflect.String:
if typeField.Tag.Get("flag") != "" {
if len(parts) == 1 {
fs.StringVar(valField.Addr().Interface().(*string), parts[0], value, typeField.Tag.Get("description"))
} else {
fs.StringVarP(valField.Addr().Interface().(*string), parts[0], parts[1], value, typeField.Tag.Get("description"))
}
} else {
valField.SetString(value)
}
case reflect.Bool:
v := value == "true"
if typeField.Tag.Get("flag") != "" {
if len(parts) == 1 {
fs.BoolVar(valField.Addr().Interface().(*bool), parts[0], v, typeField.Tag.Get("description"))
} else {
fs.BoolVarP(valField.Addr().Interface().(*bool), parts[0], parts[1], v, typeField.Tag.Get("description"))
}
} else {
valField.SetBool(v)
}
case reflect.Int, reflect.Int8, reflect.Int32, reflect.Int64:
vt, err := strconv.ParseInt(value, 10, 64)
if err != nil {
if value == "" {
vt = 0
} else {
return err
}
}
if typeField.Tag.Get("flag") != "" {
registerFlagInt(typeField.Type.Kind(), fs, valField.Addr().Interface(), parts, vt, typeField.Tag.Get("description"))
} else {
valField.SetInt(vt)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
vt, err := strconv.ParseUint(value, 10, 64)
if err != nil {
if value == "" {
vt = 0
} else {
return err
}
}
if typeField.Tag.Get("flag") != "" {
registerFlagUint(typeField.Type.Kind(), fs, valField.Addr().Interface(), parts, vt, typeField.Tag.Get("description"))
} else {
valField.SetUint(vt)
}
case reflect.Float32, reflect.Float64:
vt, err := strconv.ParseFloat(value, 64)
if err != nil {
if value == "" {
vt = 0.0
} else {
return err
}
}
if typeField.Tag.Get("flag") != "" {
registerFlagFloat(typeField.Type.Kind(), fs, valField.Addr().Interface(), parts, vt, typeField.Tag.Get("description"))
} else {
valField.SetFloat(vt)
}
case reflect.Struct:
if err := execTags(valField.Addr().Interface(), fs); err != nil {
return err
}
case reflect.Slice:
switch typeField.Type.Elem().Kind() {
case reflect.Int:
def := []int{}
for _, v := range strings.Split(value, ",") {
it, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
if err != nil {
return err
}
def = append(def, int(it))
}
if len(parts) == 1 {
fs.IntSliceVar(valField.Addr().Interface().(*[]int), parts[0], def, typeField.Tag.Get("description"))
} else {
fs.IntSliceVarP(valField.Addr().Interface().(*[]int), parts[0], parts[1], def, typeField.Tag.Get("description"))
}
case reflect.String:
del := typeField.Tag.Get("delimiter")
if len(del) == 0 {
del = ","
}
def := strings.Split(value, del)
if len(parts) == 1 {
fs.StringSliceVar(valField.Addr().Interface().(*[]string), parts[0], def, typeField.Tag.Get("description"))
} else {
fs.StringSliceVarP(valField.Addr().Interface().(*[]string), parts[0], parts[1], def, typeField.Tag.Get("description"))
}
}
}
}
return nil
}
func registerFlagFloat(t reflect.Kind, fs *pflag.FlagSet, field interface{}, parts []string, vt float64, desc string) {
switch t {
case reflect.Float32:
if len(parts) == 1 {
fs.Float32Var(field.(*float32), parts[0], float32(vt), desc)
} else {
fs.Float32VarP(field.(*float32), parts[0], parts[1], float32(vt), desc)
}
case reflect.Float64:
if len(parts) == 1 {
fs.Float64Var(field.(*float64), parts[0], float64(vt), desc)
} else {
fs.Float64VarP(field.(*float64), parts[0], parts[1], float64(vt), desc)
}
}
}
func registerFlagInt(t reflect.Kind, fs *pflag.FlagSet, field interface{}, parts []string, vt int64, desc string) {
switch t {
case reflect.Int:
if len(parts) == 1 {
fs.IntVar(field.(*int), parts[0], int(vt), desc)
} else {
fs.IntVarP(field.(*int), parts[0], parts[1], int(vt), desc)
}
case reflect.Int8:
if len(parts) == 1 {
fs.Int8Var(field.(*int8), parts[0], int8(vt), desc)
} else {
fs.Int8VarP(field.(*int8), parts[0], parts[1], int8(vt), desc)
}
case reflect.Int32:
if len(parts) == 1 {
fs.Int32Var(field.(*int32), parts[0], int32(vt), desc)
} else {
fs.Int32VarP(field.(*int32), parts[0], parts[1], int32(vt), desc)
}
case reflect.Int64:
if len(parts) == 1 {
fs.Int64Var(field.(*int64), parts[0], int64(vt), desc)
} else {
fs.Int64VarP(field.(*int64), parts[0], parts[1], int64(vt), desc)
}
}
}
func registerFlagUint(t reflect.Kind, fs *pflag.FlagSet, field interface{}, parts []string, vt uint64, desc string) {
switch t {
case reflect.Uint:
if len(parts) == 1 {
fs.UintVar(field.(*uint), parts[0], uint(vt), desc)
} else {
fs.UintVarP(field.(*uint), parts[0], parts[1], uint(vt), desc)
}
case reflect.Uint8:
if len(parts) == 1 {
fs.Uint8Var(field.(*uint8), parts[0], uint8(vt), desc)
} else {
fs.Uint8VarP(field.(*uint8), parts[0], parts[1], uint8(vt), desc)
}
case reflect.Uint16:
if len(parts) == 1 {
fs.Uint16Var(field.(*uint16), parts[0], uint16(vt), desc)
} else {
fs.Uint16VarP(field.(*uint16), parts[0], parts[1], uint16(vt), desc)
}
case reflect.Uint32:
if len(parts) == 1 {
fs.Uint32Var(field.(*uint32), parts[0], uint32(vt), desc)
} else {
fs.Uint32VarP(field.(*uint32), parts[0], parts[1], uint32(vt), desc)
}
case reflect.Uint64:
if len(parts) == 1 {
fs.Uint64Var(field.(*uint64), parts[0], uint64(vt), desc)
} else {
fs.Uint64VarP(field.(*uint64), parts[0], parts[1], uint64(vt), desc)
}
}
}
func envDefault(env, def string) string {
value := def
if env != "" {
if e := os.Getenv(env); e != "" {
value = e
}
}
return value
}
func varDefault(name, def string) string {
value := def
if name != "" {
if v, ok := variableDefaults[name]; ok {
value = v
}
}
return value
}

View File

@ -1,41 +0,0 @@
package rconfig
import (
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Duration", func() {
type t struct {
Test time.Duration `flag:"duration"`
TestS time.Duration `flag:"other-duration,o"`
TestDef time.Duration `default:"30h"`
}
var (
err error
args []string
cfg t
)
BeforeEach(func() {
cfg = t{}
args = []string{
"--duration=23s", "-o", "45m",
}
})
JustBeforeEach(func() {
err = parse(&cfg, args)
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values", func() {
Expect(cfg.Test).To(Equal(23 * time.Second))
Expect(cfg.TestS).To(Equal(45 * time.Minute))
Expect(cfg.TestDef).To(Equal(30 * time.Hour))
})
})

View File

@ -1,56 +0,0 @@
package rconfig
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Testing errors", func() {
It("should not accept string as int", func() {
Expect(parse(&struct {
A int `default:"a"`
}{}, []string{})).To(HaveOccurred())
})
It("should not accept string as float", func() {
Expect(parse(&struct {
A float32 `default:"a"`
}{}, []string{})).To(HaveOccurred())
})
It("should not accept string as uint", func() {
Expect(parse(&struct {
A uint `default:"a"`
}{}, []string{})).To(HaveOccurred())
})
It("should not accept string as uint in sub-struct", func() {
Expect(parse(&struct {
B struct {
A uint `default:"a"`
}
}{}, []string{})).To(HaveOccurred())
})
It("should not accept string slice as int slice", func() {
Expect(parse(&struct {
A []int `default:"a,bn"`
}{}, []string{})).To(HaveOccurred())
})
It("should not accept variables not being pointers", func() {
cfg := struct {
A string `default:"a"`
}{}
Expect(parse(cfg, []string{})).To(HaveOccurred())
})
It("should not accept variables not being pointers to structs", func() {
cfg := "test"
Expect(parse(cfg, []string{})).To(HaveOccurred())
})
})

View File

@ -1,37 +0,0 @@
package rconfig
import (
"fmt"
"os"
)
func ExampleParse() {
// We're building an example configuration with a sub-struct to be filled
// by the Parse command.
config := struct {
Username string `default:"unknown" flag:"user,u" description:"Your name"`
Details struct {
Age int `default:"25" flag:"age" description:"Your age"`
}
}{}
// To have more relieable results we're setting os.Args to a known value.
// In real-life use cases you wouldn't do this but parse the original
// commandline arguments.
os.Args = []string{
"example",
"--user=Luzifer",
}
Parse(&config)
fmt.Printf("Hello %s, happy birthday for your %dth birthday.",
config.Username,
config.Details.Age)
// You can also show an usage message for your user
Usage()
// Output:
// Hello Luzifer, happy birthday for your 25th birthday.
}

View File

@ -1,44 +0,0 @@
package rconfig
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Testing float parsing", func() {
type t struct {
Test32 float32 `flag:"float32"`
Test32P float32 `flag:"float32p,3"`
Test64 float64 `flag:"float64"`
Test64P float64 `flag:"float64p,6"`
TestDef float32 `default:"66.256"`
}
var (
err error
args []string
cfg t
)
BeforeEach(func() {
cfg = t{}
args = []string{
"--float32=5.5", "-3", "6.6",
"--float64=7.7", "-6", "8.8",
}
})
JustBeforeEach(func() {
err = parse(&cfg, args)
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values", func() {
Expect(cfg.Test32).To(Equal(float32(5.5)))
Expect(cfg.Test32P).To(Equal(float32(6.6)))
Expect(cfg.Test64).To(Equal(float64(7.7)))
Expect(cfg.Test64P).To(Equal(float64(8.8)))
Expect(cfg.TestDef).To(Equal(float32(66.256)))
})
})

View File

@ -1,128 +0,0 @@
package rconfig
import (
"os"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Testing general parsing", func() {
type t struct {
Test string `default:"foo" env:"shell" flag:"shell" description:"Test"`
Test2 string `default:"blub" env:"testvar" flag:"testvar,t" description:"Test"`
DefaultFlag string `default:"goo"`
SadFlag string
}
type tValidated struct {
Test string `flag:"test" default:"" validate:"nonzero"`
}
var (
err error
args []string
cfg t
)
Context("with defined arguments", func() {
BeforeEach(func() {
cfg = t{}
args = []string{
"--shell=test23",
"-t", "bla",
}
})
JustBeforeEach(func() {
err = parse(&cfg, args)
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have parsed the expected values", func() {
Expect(cfg.Test).To(Equal("test23"))
Expect(cfg.Test2).To(Equal("bla"))
Expect(cfg.SadFlag).To(Equal(""))
Expect(cfg.DefaultFlag).To(Equal("goo"))
})
})
Context("with no arguments", func() {
BeforeEach(func() {
cfg = t{}
args = []string{}
})
JustBeforeEach(func() {
err = parse(&cfg, args)
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have used the default value", func() {
Expect(cfg.Test).To(Equal("foo"))
})
})
Context("with no arguments and set env", func() {
BeforeEach(func() {
cfg = t{}
args = []string{}
os.Setenv("shell", "test546")
})
AfterEach(func() {
os.Unsetenv("shell")
})
JustBeforeEach(func() {
err = parse(&cfg, args)
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have used the value from env", func() {
Expect(cfg.Test).To(Equal("test546"))
})
})
Context("with additional arguments", func() {
BeforeEach(func() {
cfg = t{}
args = []string{
"--shell=test23",
"-t", "bla",
"positional1", "positional2",
}
})
JustBeforeEach(func() {
err = parse(&cfg, args)
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have parsed the expected values", func() {
Expect(cfg.Test).To(Equal("test23"))
Expect(cfg.Test2).To(Equal("bla"))
Expect(cfg.SadFlag).To(Equal(""))
Expect(cfg.DefaultFlag).To(Equal("goo"))
})
It("should have detected the positional arguments", func() {
Expect(Args()).To(Equal([]string{"positional1", "positional2"}))
})
})
Context("making use of the validator package", func() {
var cfgValidated tValidated
BeforeEach(func() {
cfgValidated = tValidated{}
args = []string{}
})
JustBeforeEach(func() {
err = parseAndValidate(&cfgValidated, args)
})
It("should have errored", func() { Expect(err).To(HaveOccurred()) })
})
})

View File

@ -1,54 +0,0 @@
package rconfig
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Testing int parsing", func() {
type t struct {
Test int `flag:"int"`
TestP int `flag:"intp,i"`
Test8 int8 `flag:"int8"`
Test8P int8 `flag:"int8p,8"`
Test32 int32 `flag:"int32"`
Test32P int32 `flag:"int32p,3"`
Test64 int64 `flag:"int64"`
Test64P int64 `flag:"int64p,6"`
TestDef int8 `default:"66"`
}
var (
err error
args []string
cfg t
)
BeforeEach(func() {
cfg = t{}
args = []string{
"--int=1", "-i", "2",
"--int8=3", "-8", "4",
"--int32=5", "-3", "6",
"--int64=7", "-6", "8",
}
})
JustBeforeEach(func() {
err = parse(&cfg, args)
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values", func() {
Expect(cfg.Test).To(Equal(1))
Expect(cfg.TestP).To(Equal(2))
Expect(cfg.Test8).To(Equal(int8(3)))
Expect(cfg.Test8P).To(Equal(int8(4)))
Expect(cfg.Test32).To(Equal(int32(5)))
Expect(cfg.Test32P).To(Equal(int32(6)))
Expect(cfg.Test64).To(Equal(int64(7)))
Expect(cfg.Test64P).To(Equal(int64(8)))
Expect(cfg.TestDef).To(Equal(int8(66)))
})
})

View File

@ -1,40 +0,0 @@
package rconfig_test
import (
"os"
. "github.com/Luzifer/rconfig"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Testing os.Args", func() {
type t struct {
A string `default:"a" flag:"a"`
}
var (
err error
cfg t
)
JustBeforeEach(func() {
err = Parse(&cfg)
})
Context("With only valid arguments", func() {
BeforeEach(func() {
cfg = t{}
os.Args = []string{"--a=bar"}
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values", func() {
Expect(cfg.A).To(Equal("bar"))
})
})
})

View File

@ -1,87 +0,0 @@
package rconfig
import (
"os"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Precedence", func() {
type t struct {
A int `default:"1" vardefault:"a" env:"a" flag:"avar,a" description:"a"`
}
var (
err error
cfg t
args []string
vardefaults map[string]string
)
JustBeforeEach(func() {
cfg = t{}
SetVariableDefaults(vardefaults)
err = parse(&cfg, args)
})
Context("Provided: Flag, Env, Default, VarDefault", func() {
BeforeEach(func() {
args = []string{"-a", "5"}
os.Setenv("a", "8")
vardefaults = map[string]string{
"a": "3",
}
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have used the flag value", func() {
Expect(cfg.A).To(Equal(5))
})
})
Context("Provided: Env, Default, VarDefault", func() {
BeforeEach(func() {
args = []string{}
os.Setenv("a", "8")
vardefaults = map[string]string{
"a": "3",
}
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have used the env value", func() {
Expect(cfg.A).To(Equal(8))
})
})
Context("Provided: Default, VarDefault", func() {
BeforeEach(func() {
args = []string{}
os.Unsetenv("a")
vardefaults = map[string]string{
"a": "3",
}
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have used the vardefault value", func() {
Expect(cfg.A).To(Equal(3))
})
})
Context("Provided: Default", func() {
BeforeEach(func() {
args = []string{}
os.Unsetenv("a")
vardefaults = map[string]string{}
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have used the default value", func() {
Expect(cfg.A).To(Equal(1))
})
})
})

View File

@ -1,13 +0,0 @@
package rconfig_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
)
func TestRconfig(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Rconfig Suite")
}

View File

@ -1,51 +0,0 @@
package rconfig
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Testing slices", func() {
type t struct {
Int []int `default:"1,2,3" flag:"int"`
String []string `default:"a,b,c" flag:"string"`
IntP []int `default:"1,2,3" flag:"intp,i"`
StringP []string `default:"a,b,c" flag:"stringp,s"`
}
var (
err error
args []string
cfg t
)
BeforeEach(func() {
cfg = t{}
args = []string{
"--int=4,5", "-s", "hallo,welt",
}
})
JustBeforeEach(func() {
err = parse(&cfg, args)
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values for int-slice", func() {
Expect(len(cfg.Int)).To(Equal(2))
Expect(cfg.Int).To(Equal([]int{4, 5}))
Expect(cfg.Int).NotTo(Equal([]int{5, 4}))
})
It("should have the expected values for int-shorthand-slice", func() {
Expect(len(cfg.IntP)).To(Equal(3))
Expect(cfg.IntP).To(Equal([]int{1, 2, 3}))
})
It("should have the expected values for string-slice", func() {
Expect(len(cfg.String)).To(Equal(3))
Expect(cfg.String).To(Equal([]string{"a", "b", "c"}))
})
It("should have the expected values for string-shorthand-slice", func() {
Expect(len(cfg.StringP)).To(Equal(2))
Expect(cfg.StringP).To(Equal([]string{"hallo", "welt"}))
})
})

View File

@ -1,36 +0,0 @@
package rconfig
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Testing sub-structs", func() {
type t struct {
Test string `default:"blubb"`
Sub struct {
Test string `default:"Hallo"`
}
}
var (
err error
args []string
cfg t
)
BeforeEach(func() {
cfg = t{}
args = []string{}
})
JustBeforeEach(func() {
err = parse(&cfg, args)
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values", func() {
Expect(cfg.Test).To(Equal("blubb"))
Expect(cfg.Sub.Test).To(Equal("Hallo"))
})
})

View File

@ -1,59 +0,0 @@
package rconfig
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Testing uint parsing", func() {
type t struct {
Test uint `flag:"int"`
TestP uint `flag:"intp,i"`
Test8 uint8 `flag:"int8"`
Test8P uint8 `flag:"int8p,8"`
Test16 uint16 `flag:"int16"`
Test16P uint16 `flag:"int16p,1"`
Test32 uint32 `flag:"int32"`
Test32P uint32 `flag:"int32p,3"`
Test64 uint64 `flag:"int64"`
Test64P uint64 `flag:"int64p,6"`
TestDef uint8 `default:"66"`
}
var (
err error
args []string
cfg t
)
BeforeEach(func() {
cfg = t{}
args = []string{
"--int=1", "-i", "2",
"--int8=3", "-8", "4",
"--int32=5", "-3", "6",
"--int64=7", "-6", "8",
"--int16=9", "-1", "10",
}
})
JustBeforeEach(func() {
err = parse(&cfg, args)
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values", func() {
Expect(cfg.Test).To(Equal(uint(1)))
Expect(cfg.TestP).To(Equal(uint(2)))
Expect(cfg.Test8).To(Equal(uint8(3)))
Expect(cfg.Test8P).To(Equal(uint8(4)))
Expect(cfg.Test32).To(Equal(uint32(5)))
Expect(cfg.Test32P).To(Equal(uint32(6)))
Expect(cfg.Test64).To(Equal(uint64(7)))
Expect(cfg.Test64P).To(Equal(uint64(8)))
Expect(cfg.Test16).To(Equal(uint16(9)))
Expect(cfg.Test16P).To(Equal(uint16(10)))
Expect(cfg.TestDef).To(Equal(uint8(66)))
})
})

View File

@ -1,27 +0,0 @@
package rconfig
import (
"io/ioutil"
"gopkg.in/yaml.v2"
)
// VarDefaultsFromYAMLFile reads contents of a file and calls VarDefaultsFromYAML
func VarDefaultsFromYAMLFile(filename string) map[string]string {
data, err := ioutil.ReadFile(filename)
if err != nil {
return make(map[string]string)
}
return VarDefaultsFromYAML(data)
}
// VarDefaultsFromYAML creates a vardefaults map from YAML raw data
func VarDefaultsFromYAML(in []byte) map[string]string {
out := make(map[string]string)
err := yaml.Unmarshal(in, &out)
if err != nil {
return make(map[string]string)
}
return out
}

View File

@ -1,122 +0,0 @@
package rconfig
import (
"io/ioutil"
"os"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Testing variable defaults", func() {
type t struct {
MySecretValue string `default:"secret" env:"foo" vardefault:"my_secret_value"`
MyUsername string `default:"luzifer" vardefault:"username"`
SomeVar string `flag:"var" description:"some variable"`
IntVar int64 `vardefault:"int_var" default:"23"`
}
var (
err error
cfg t
args = []string{}
vardefaults = map[string]string{
"my_secret_value": "veryverysecretkey",
"unkownkey": "hi there",
"int_var": "42",
}
)
BeforeEach(func() {
cfg = t{}
})
JustBeforeEach(func() {
err = parse(&cfg, args)
})
Context("With manually provided variables", func() {
BeforeEach(func() {
SetVariableDefaults(vardefaults)
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values", func() {
Expect(cfg.IntVar).To(Equal(int64(42)))
Expect(cfg.MySecretValue).To(Equal("veryverysecretkey"))
Expect(cfg.MyUsername).To(Equal("luzifer"))
Expect(cfg.SomeVar).To(Equal(""))
})
})
Context("With defaults from YAML data", func() {
BeforeEach(func() {
yamlData := []byte("---\nmy_secret_value: veryverysecretkey\nunknownkey: hi there\nint_var: 42\n")
SetVariableDefaults(VarDefaultsFromYAML(yamlData))
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values", func() {
Expect(cfg.IntVar).To(Equal(int64(42)))
Expect(cfg.MySecretValue).To(Equal("veryverysecretkey"))
Expect(cfg.MyUsername).To(Equal("luzifer"))
Expect(cfg.SomeVar).To(Equal(""))
})
})
Context("With defaults from YAML file", func() {
var tmp *os.File
BeforeEach(func() {
tmp, _ = ioutil.TempFile("", "")
yamlData := "---\nmy_secret_value: veryverysecretkey\nunknownkey: hi there\nint_var: 42\n"
tmp.WriteString(yamlData)
SetVariableDefaults(VarDefaultsFromYAMLFile(tmp.Name()))
})
AfterEach(func() {
tmp.Close()
os.Remove(tmp.Name())
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values", func() {
Expect(cfg.IntVar).To(Equal(int64(42)))
Expect(cfg.MySecretValue).To(Equal("veryverysecretkey"))
Expect(cfg.MyUsername).To(Equal("luzifer"))
Expect(cfg.SomeVar).To(Equal(""))
})
})
Context("With defaults from invalid YAML data", func() {
BeforeEach(func() {
yamlData := []byte("---\nmy_secret_value = veryverysecretkey\nunknownkey = hi there\nint_var = 42\n")
SetVariableDefaults(VarDefaultsFromYAML(yamlData))
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values", func() {
Expect(cfg.IntVar).To(Equal(int64(23)))
Expect(cfg.MySecretValue).To(Equal("secret"))
Expect(cfg.MyUsername).To(Equal("luzifer"))
Expect(cfg.SomeVar).To(Equal(""))
})
})
Context("With defaults from non existent YAML file", func() {
BeforeEach(func() {
file := "/tmp/this_file_should_not_exist_146e26723r"
SetVariableDefaults(VarDefaultsFromYAMLFile(file))
})
It("should not have errored", func() { Expect(err).NotTo(HaveOccurred()) })
It("should have the expected values", func() {
Expect(cfg.IntVar).To(Equal(int64(23)))
Expect(cfg.MySecretValue).To(Equal("secret"))
Expect(cfg.MyUsername).To(Equal("luzifer"))
Expect(cfg.SomeVar).To(Equal(""))
})
})
})

View File

@ -1,83 +0,0 @@
package logrus
import (
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"testing"
"time"
)
func TestRegister(t *testing.T) {
current := len(handlers)
RegisterExitHandler(func() {})
if len(handlers) != current+1 {
t.Fatalf("expected %d handlers, got %d", current+1, len(handlers))
}
}
func TestHandler(t *testing.T) {
tempDir, err := ioutil.TempDir("", "test_handler")
if err != nil {
log.Fatalf("can't create temp dir. %q", err)
}
defer os.RemoveAll(tempDir)
gofile := filepath.Join(tempDir, "gofile.go")
if err := ioutil.WriteFile(gofile, testprog, 0666); err != nil {
t.Fatalf("can't create go file. %q", err)
}
outfile := filepath.Join(tempDir, "outfile.out")
arg := time.Now().UTC().String()
err = exec.Command("go", "run", gofile, outfile, arg).Run()
if err == nil {
t.Fatalf("completed normally, should have failed")
}
data, err := ioutil.ReadFile(outfile)
if err != nil {
t.Fatalf("can't read output file %s. %q", outfile, err)
}
if string(data) != arg {
t.Fatalf("bad data. Expected %q, got %q", data, arg)
}
}
var testprog = []byte(`
// Test program for atexit, gets output file and data as arguments and writes
// data to output file in atexit handler.
package main
import (
"github.com/sirupsen/logrus"
"flag"
"fmt"
"io/ioutil"
)
var outfile = ""
var data = ""
func handler() {
ioutil.WriteFile(outfile, []byte(data), 0666)
}
func badHandler() {
n := 0
fmt.Println(1/n)
}
func main() {
flag.Parse()
outfile = flag.Arg(0)
data = flag.Arg(1)
logrus.RegisterExitHandler(handler)
logrus.RegisterExitHandler(badHandler)
logrus.Fatal("Bye bye")
}
`)

View File

@ -1,77 +0,0 @@
package logrus
import (
"bytes"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestEntryWithError(t *testing.T) {
assert := assert.New(t)
defer func() {
ErrorKey = "error"
}()
err := fmt.Errorf("kaboom at layer %d", 4711)
assert.Equal(err, WithError(err).Data["error"])
logger := New()
logger.Out = &bytes.Buffer{}
entry := NewEntry(logger)
assert.Equal(err, entry.WithError(err).Data["error"])
ErrorKey = "err"
assert.Equal(err, entry.WithError(err).Data["err"])
}
func TestEntryPanicln(t *testing.T) {
errBoom := fmt.Errorf("boom time")
defer func() {
p := recover()
assert.NotNil(t, p)
switch pVal := p.(type) {
case *Entry:
assert.Equal(t, "kaboom", pVal.Message)
assert.Equal(t, errBoom, pVal.Data["err"])
default:
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
}
}()
logger := New()
logger.Out = &bytes.Buffer{}
entry := NewEntry(logger)
entry.WithField("err", errBoom).Panicln("kaboom")
}
func TestEntryPanicf(t *testing.T) {
errBoom := fmt.Errorf("boom again")
defer func() {
p := recover()
assert.NotNil(t, p)
switch pVal := p.(type) {
case *Entry:
assert.Equal(t, "kaboom true", pVal.Message)
assert.Equal(t, errBoom, pVal.Data["err"])
default:
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
}
}()
logger := New()
logger.Out = &bytes.Buffer{}
entry := NewEntry(logger)
entry.WithField("err", errBoom).Panicf("kaboom %v", true)
}

View File

@ -1,69 +0,0 @@
package logrus_test
import (
"github.com/sirupsen/logrus"
"os"
)
func Example_basic() {
var log = logrus.New()
log.Formatter = new(logrus.JSONFormatter)
log.Formatter = new(logrus.TextFormatter) //default
log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output
log.Level = logrus.DebugLevel
log.Out = os.Stdout
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
// if err == nil {
// log.Out = file
// } else {
// log.Info("Failed to log to file, using default stderr")
// }
defer func() {
err := recover()
if err != nil {
entry := err.(*logrus.Entry)
log.WithFields(logrus.Fields{
"omg": true,
"err_animal": entry.Data["animal"],
"err_size": entry.Data["size"],
"err_level": entry.Level,
"err_message": entry.Message,
"number": 100,
}).Error("The ice breaks!") // or use Fatal() to force the process to exit with a nonzero code
}
}()
log.WithFields(logrus.Fields{
"animal": "walrus",
"number": 8,
}).Debug("Started observing beach")
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(logrus.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(logrus.Fields{
"temperature": -4,
}).Debug("Temperature changes")
log.WithFields(logrus.Fields{
"animal": "orca",
"size": 9009,
}).Panic("It's over 9000!")
// Output:
// level=debug msg="Started observing beach" animal=walrus number=8
// level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
// level=warning msg="The group's number increased tremendously!" number=122 omg=true
// level=debug msg="Temperature changes" temperature=-4
// level=panic msg="It's over 9000!" animal=orca size=9009
// level=error msg="The ice breaks!" err_animal=orca err_level=panic err_message="It's over 9000!" err_size=9009 number=100 omg=true
}

View File

@ -1,35 +0,0 @@
package logrus_test
import (
"github.com/sirupsen/logrus"
"gopkg.in/gemnasium/logrus-airbrake-hook.v2"
"os"
)
func Example_hook() {
var log = logrus.New()
log.Formatter = new(logrus.TextFormatter) // default
log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output
log.Hooks.Add(airbrake.NewHook(123, "xyz", "development"))
log.Out = os.Stdout
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(logrus.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(logrus.Fields{
"omg": true,
"number": 100,
}).Error("The ice breaks!")
// Output:
// level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
// level=warning msg="The group's number increased tremendously!" number=122 omg=true
// level=error msg="The ice breaks!" number=100 omg=true
}

View File

@ -1,101 +0,0 @@
package logrus
import (
"fmt"
"testing"
"time"
)
// smallFields is a small size data set for benchmarking
var smallFields = Fields{
"foo": "bar",
"baz": "qux",
"one": "two",
"three": "four",
}
// largeFields is a large size data set for benchmarking
var largeFields = Fields{
"foo": "bar",
"baz": "qux",
"one": "two",
"three": "four",
"five": "six",
"seven": "eight",
"nine": "ten",
"eleven": "twelve",
"thirteen": "fourteen",
"fifteen": "sixteen",
"seventeen": "eighteen",
"nineteen": "twenty",
"a": "b",
"c": "d",
"e": "f",
"g": "h",
"i": "j",
"k": "l",
"m": "n",
"o": "p",
"q": "r",
"s": "t",
"u": "v",
"w": "x",
"y": "z",
"this": "will",
"make": "thirty",
"entries": "yeah",
}
var errorFields = Fields{
"foo": fmt.Errorf("bar"),
"baz": fmt.Errorf("qux"),
}
func BenchmarkErrorTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields)
}
func BenchmarkSmallTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
}
func BenchmarkLargeTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
}
func BenchmarkSmallColoredTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
}
func BenchmarkLargeColoredTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
}
func BenchmarkSmallJSONFormatter(b *testing.B) {
doBenchmark(b, &JSONFormatter{}, smallFields)
}
func BenchmarkLargeJSONFormatter(b *testing.B) {
doBenchmark(b, &JSONFormatter{}, largeFields)
}
func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
logger := New()
entry := &Entry{
Time: time.Time{},
Level: InfoLevel,
Message: "message",
Data: fields,
Logger: logger,
}
var d []byte
var err error
for i := 0; i < b.N; i++ {
d, err = formatter.Format(entry)
if err != nil {
b.Fatal(err)
}
b.SetBytes(int64(len(d)))
}
}

View File

@ -1,122 +0,0 @@
package logrus
import (
"testing"
"github.com/stretchr/testify/assert"
)
type TestHook struct {
Fired bool
}
func (hook *TestHook) Fire(entry *Entry) error {
hook.Fired = true
return nil
}
func (hook *TestHook) Levels() []Level {
return []Level{
DebugLevel,
InfoLevel,
WarnLevel,
ErrorLevel,
FatalLevel,
PanicLevel,
}
}
func TestHookFires(t *testing.T) {
hook := new(TestHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
assert.Equal(t, hook.Fired, false)
log.Print("test")
}, func(fields Fields) {
assert.Equal(t, hook.Fired, true)
})
}
type ModifyHook struct {
}
func (hook *ModifyHook) Fire(entry *Entry) error {
entry.Data["wow"] = "whale"
return nil
}
func (hook *ModifyHook) Levels() []Level {
return []Level{
DebugLevel,
InfoLevel,
WarnLevel,
ErrorLevel,
FatalLevel,
PanicLevel,
}
}
func TestHookCanModifyEntry(t *testing.T) {
hook := new(ModifyHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
log.WithField("wow", "elephant").Print("test")
}, func(fields Fields) {
assert.Equal(t, fields["wow"], "whale")
})
}
func TestCanFireMultipleHooks(t *testing.T) {
hook1 := new(ModifyHook)
hook2 := new(TestHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook1)
log.Hooks.Add(hook2)
log.WithField("wow", "elephant").Print("test")
}, func(fields Fields) {
assert.Equal(t, fields["wow"], "whale")
assert.Equal(t, hook2.Fired, true)
})
}
type ErrorHook struct {
Fired bool
}
func (hook *ErrorHook) Fire(entry *Entry) error {
hook.Fired = true
return nil
}
func (hook *ErrorHook) Levels() []Level {
return []Level{
ErrorLevel,
}
}
func TestErrorHookShouldntFireOnInfo(t *testing.T) {
hook := new(ErrorHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
log.Info("test")
}, func(fields Fields) {
assert.Equal(t, hook.Fired, false)
})
}
func TestErrorHookShouldFireOnError(t *testing.T) {
hook := new(ErrorHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
log.Error("test")
}, func(fields Fields) {
assert.Equal(t, hook.Fired, true)
})
}

View File

@ -1,39 +0,0 @@
# Syslog Hooks for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>
## Usage
```go
import (
"log/syslog"
"github.com/sirupsen/logrus"
lSyslog "github.com/sirupsen/logrus/hooks/syslog"
)
func main() {
log := logrus.New()
hook, err := lSyslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err == nil {
log.Hooks.Add(hook)
}
}
```
If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). Just assign empty string to the first two parameters of `NewSyslogHook`. It should look like the following.
```go
import (
"log/syslog"
"github.com/sirupsen/logrus"
lSyslog "github.com/sirupsen/logrus/hooks/syslog"
)
func main() {
log := logrus.New()
hook, err := lSyslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
if err == nil {
log.Hooks.Add(hook)
}
}
```

View File

@ -1,55 +0,0 @@
// +build !windows,!nacl,!plan9
package syslog
import (
"fmt"
"log/syslog"
"os"
"github.com/sirupsen/logrus"
)
// SyslogHook to send logs via syslog.
type SyslogHook struct {
Writer *syslog.Writer
SyslogNetwork string
SyslogRaddr string
}
// Creates a hook to be added to an instance of logger. This is called with
// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
// `if err == nil { log.Hooks.Add(hook) }`
func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
w, err := syslog.Dial(network, raddr, priority, tag)
return &SyslogHook{w, network, raddr}, err
}
func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
line, err := entry.String()
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
return err
}
switch entry.Level {
case logrus.PanicLevel:
return hook.Writer.Crit(line)
case logrus.FatalLevel:
return hook.Writer.Crit(line)
case logrus.ErrorLevel:
return hook.Writer.Err(line)
case logrus.WarnLevel:
return hook.Writer.Warning(line)
case logrus.InfoLevel:
return hook.Writer.Info(line)
case logrus.DebugLevel:
return hook.Writer.Debug(line)
default:
return nil
}
}
func (hook *SyslogHook) Levels() []logrus.Level {
return logrus.AllLevels
}

View File

@ -1,27 +0,0 @@
package syslog
import (
"log/syslog"
"testing"
"github.com/sirupsen/logrus"
)
func TestLocalhostAddAndPrint(t *testing.T) {
log := logrus.New()
hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err != nil {
t.Errorf("Unable to connect to local syslog.")
}
log.Hooks.Add(hook)
for _, level := range hook.Levels() {
if len(log.Hooks[level]) != 1 {
t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level]))
}
}
log.Info("Congratulations!")
}

View File

@ -1,95 +0,0 @@
// The Test package is used for testing logrus. It is here for backwards
// compatibility from when logrus' organization was upper-case. Please use
// lower-case logrus and the `null` package instead of this one.
package test
import (
"io/ioutil"
"sync"
"github.com/sirupsen/logrus"
)
// Hook is a hook designed for dealing with logs in test scenarios.
type Hook struct {
// Entries is an array of all entries that have been received by this hook.
// For safe access, use the AllEntries() method, rather than reading this
// value directly.
Entries []*logrus.Entry
mu sync.RWMutex
}
// NewGlobal installs a test hook for the global logger.
func NewGlobal() *Hook {
hook := new(Hook)
logrus.AddHook(hook)
return hook
}
// NewLocal installs a test hook for a given local logger.
func NewLocal(logger *logrus.Logger) *Hook {
hook := new(Hook)
logger.Hooks.Add(hook)
return hook
}
// NewNullLogger creates a discarding logger and installs the test hook.
func NewNullLogger() (*logrus.Logger, *Hook) {
logger := logrus.New()
logger.Out = ioutil.Discard
return logger, NewLocal(logger)
}
func (t *Hook) Fire(e *logrus.Entry) error {
t.mu.Lock()
defer t.mu.Unlock()
t.Entries = append(t.Entries, e)
return nil
}
func (t *Hook) Levels() []logrus.Level {
return logrus.AllLevels
}
// LastEntry returns the last entry that was logged or nil.
func (t *Hook) LastEntry() *logrus.Entry {
t.mu.RLock()
defer t.mu.RUnlock()
i := len(t.Entries) - 1
if i < 0 {
return nil
}
// Make a copy, for safety
e := *t.Entries[i]
return &e
}
// AllEntries returns all entries that were logged.
func (t *Hook) AllEntries() []*logrus.Entry {
t.mu.RLock()
defer t.mu.RUnlock()
// Make a copy so the returned value won't race with future log requests
entries := make([]*logrus.Entry, len(t.Entries))
for i, entry := range t.Entries {
// Make a copy, for safety
e := *entry
entries[i] = &e
}
return entries
}
// Reset removes all Entries from this test hook.
func (t *Hook) Reset() {
t.mu.Lock()
defer t.mu.Unlock()
t.Entries = make([]*logrus.Entry, 0)
}

View File

@ -1,39 +0,0 @@
package test
import (
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)
func TestAllHooks(t *testing.T) {
assert := assert.New(t)
logger, hook := NewNullLogger()
assert.Nil(hook.LastEntry())
assert.Equal(0, len(hook.Entries))
logger.Error("Hello error")
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
assert.Equal("Hello error", hook.LastEntry().Message)
assert.Equal(1, len(hook.Entries))
logger.Warn("Hello warning")
assert.Equal(logrus.WarnLevel, hook.LastEntry().Level)
assert.Equal("Hello warning", hook.LastEntry().Message)
assert.Equal(2, len(hook.Entries))
hook.Reset()
assert.Nil(hook.LastEntry())
assert.Equal(0, len(hook.Entries))
hook = NewGlobal()
logrus.Error("Hello error")
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
assert.Equal("Hello error", hook.LastEntry().Message)
assert.Equal(1, len(hook.Entries))
}

View File

@ -1,199 +0,0 @@
package logrus
import (
"encoding/json"
"errors"
"strings"
"testing"
)
func TestErrorNotLost(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("error", errors.New("wild walrus")))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["error"] != "wild walrus" {
t.Fatal("Error field not set")
}
}
func TestErrorNotLostOnFieldNotNamedError(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("omg", errors.New("wild walrus")))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["omg"] != "wild walrus" {
t.Fatal("Error field not set")
}
}
func TestFieldClashWithTime(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("time", "right now!"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["fields.time"] != "right now!" {
t.Fatal("fields.time not set to original time field")
}
if entry["time"] != "0001-01-01T00:00:00Z" {
t.Fatal("time field not set to current time, was: ", entry["time"])
}
}
func TestFieldClashWithMsg(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("msg", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["fields.msg"] != "something" {
t.Fatal("fields.msg not set to original msg field")
}
}
func TestFieldClashWithLevel(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["fields.level"] != "something" {
t.Fatal("fields.level not set to original level field")
}
}
func TestJSONEntryEndsWithNewline(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
if b[len(b)-1] != '\n' {
t.Fatal("Expected JSON log entry to end with a newline")
}
}
func TestJSONMessageKey(t *testing.T) {
formatter := &JSONFormatter{
FieldMap: FieldMap{
FieldKeyMsg: "message",
},
}
b, err := formatter.Format(&Entry{Message: "oh hai"})
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if !(strings.Contains(s, "message") && strings.Contains(s, "oh hai")) {
t.Fatal("Expected JSON to format message key")
}
}
func TestJSONLevelKey(t *testing.T) {
formatter := &JSONFormatter{
FieldMap: FieldMap{
FieldKeyLevel: "somelevel",
},
}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if !strings.Contains(s, "somelevel") {
t.Fatal("Expected JSON to format level key")
}
}
func TestJSONTimeKey(t *testing.T) {
formatter := &JSONFormatter{
FieldMap: FieldMap{
FieldKeyTime: "timeywimey",
},
}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if !strings.Contains(s, "timeywimey") {
t.Fatal("Expected JSON to format time key")
}
}
func TestJSONDisableTimestamp(t *testing.T) {
formatter := &JSONFormatter{
DisableTimestamp: true,
}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if strings.Contains(s, FieldKeyTime) {
t.Error("Did not prevent timestamp", s)
}
}
func TestJSONEnableTimestamp(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if !strings.Contains(s, FieldKeyTime) {
t.Error("Timestamp not present", s)
}
}

View File

@ -1,61 +0,0 @@
package logrus
import (
"os"
"testing"
)
// smallFields is a small size data set for benchmarking
var loggerFields = Fields{
"foo": "bar",
"baz": "qux",
"one": "two",
"three": "four",
}
func BenchmarkDummyLogger(b *testing.B) {
nullf, err := os.OpenFile("/dev/null", os.O_WRONLY, 0666)
if err != nil {
b.Fatalf("%v", err)
}
defer nullf.Close()
doLoggerBenchmark(b, nullf, &TextFormatter{DisableColors: true}, smallFields)
}
func BenchmarkDummyLoggerNoLock(b *testing.B) {
nullf, err := os.OpenFile("/dev/null", os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
b.Fatalf("%v", err)
}
defer nullf.Close()
doLoggerBenchmarkNoLock(b, nullf, &TextFormatter{DisableColors: true}, smallFields)
}
func doLoggerBenchmark(b *testing.B, out *os.File, formatter Formatter, fields Fields) {
logger := Logger{
Out: out,
Level: InfoLevel,
Formatter: formatter,
}
entry := logger.WithFields(fields)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
entry.Info("aaa")
}
})
}
func doLoggerBenchmarkNoLock(b *testing.B, out *os.File, formatter Formatter, fields Fields) {
logger := Logger{
Out: out,
Level: InfoLevel,
Formatter: formatter,
}
logger.SetNoLock()
entry := logger.WithFields(fields)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
entry.Info("aaa")
}
})
}

View File

@ -1,386 +0,0 @@
package logrus
import (
"bytes"
"encoding/json"
"strconv"
"strings"
"sync"
"testing"
"github.com/stretchr/testify/assert"
)
func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
var buffer bytes.Buffer
var fields Fields
logger := New()
logger.Out = &buffer
logger.Formatter = new(JSONFormatter)
log(logger)
err := json.Unmarshal(buffer.Bytes(), &fields)
assert.Nil(t, err)
assertions(fields)
}
func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
var buffer bytes.Buffer
logger := New()
logger.Out = &buffer
logger.Formatter = &TextFormatter{
DisableColors: true,
}
log(logger)
fields := make(map[string]string)
for _, kv := range strings.Split(buffer.String(), " ") {
if !strings.Contains(kv, "=") {
continue
}
kvArr := strings.Split(kv, "=")
key := strings.TrimSpace(kvArr[0])
val := kvArr[1]
if kvArr[1][0] == '"' {
var err error
val, err = strconv.Unquote(val)
assert.NoError(t, err)
}
fields[key] = val
}
assertions(fields)
}
func TestPrint(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Print("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["level"], "info")
})
}
func TestInfo(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["level"], "info")
})
}
func TestWarn(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Warn("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["level"], "warning")
})
}
func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln("test", "test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test test")
})
}
func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln("test", 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test 10")
})
}
func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln(10, 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "10 10")
})
}
func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln(10, 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "10 10")
})
}
func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Info("test", 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test10")
})
}
func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Info("test", "test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "testtest")
})
}
func TestWithFieldsShouldAllowAssignments(t *testing.T) {
var buffer bytes.Buffer
var fields Fields
logger := New()
logger.Out = &buffer
logger.Formatter = new(JSONFormatter)
localLog := logger.WithFields(Fields{
"key1": "value1",
})
localLog.WithField("key2", "value2").Info("test")
err := json.Unmarshal(buffer.Bytes(), &fields)
assert.Nil(t, err)
assert.Equal(t, "value2", fields["key2"])
assert.Equal(t, "value1", fields["key1"])
buffer = bytes.Buffer{}
fields = Fields{}
localLog.Info("test")
err = json.Unmarshal(buffer.Bytes(), &fields)
assert.Nil(t, err)
_, ok := fields["key2"]
assert.Equal(t, false, ok)
assert.Equal(t, "value1", fields["key1"])
}
func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("msg", "hello").Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
})
}
func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("msg", "hello").Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["fields.msg"], "hello")
})
}
func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("time", "hello").Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["fields.time"], "hello")
})
}
func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("level", 1).Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["level"], "info")
assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only
})
}
func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
LogAndAssertText(t, func(log *Logger) {
ll := log.WithField("herp", "derp")
ll.Info("hello")
ll.Info("bye")
}, func(fields map[string]string) {
for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
if _, ok := fields[fieldName]; ok {
t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
}
}
})
}
func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
var buffer bytes.Buffer
var fields Fields
logger := New()
logger.Out = &buffer
logger.Formatter = new(JSONFormatter)
llog := logger.WithField("context", "eating raw fish")
llog.Info("looks delicious")
err := json.Unmarshal(buffer.Bytes(), &fields)
assert.NoError(t, err, "should have decoded first message")
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
assert.Equal(t, fields["msg"], "looks delicious")
assert.Equal(t, fields["context"], "eating raw fish")
buffer.Reset()
llog.Warn("omg it is!")
err = json.Unmarshal(buffer.Bytes(), &fields)
assert.NoError(t, err, "should have decoded second message")
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
assert.Equal(t, fields["msg"], "omg it is!")
assert.Equal(t, fields["context"], "eating raw fish")
assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
}
func TestConvertLevelToString(t *testing.T) {
assert.Equal(t, "debug", DebugLevel.String())
assert.Equal(t, "info", InfoLevel.String())
assert.Equal(t, "warning", WarnLevel.String())
assert.Equal(t, "error", ErrorLevel.String())
assert.Equal(t, "fatal", FatalLevel.String())
assert.Equal(t, "panic", PanicLevel.String())
}
func TestParseLevel(t *testing.T) {
l, err := ParseLevel("panic")
assert.Nil(t, err)
assert.Equal(t, PanicLevel, l)
l, err = ParseLevel("PANIC")
assert.Nil(t, err)
assert.Equal(t, PanicLevel, l)
l, err = ParseLevel("fatal")
assert.Nil(t, err)
assert.Equal(t, FatalLevel, l)
l, err = ParseLevel("FATAL")
assert.Nil(t, err)
assert.Equal(t, FatalLevel, l)
l, err = ParseLevel("error")
assert.Nil(t, err)
assert.Equal(t, ErrorLevel, l)
l, err = ParseLevel("ERROR")
assert.Nil(t, err)
assert.Equal(t, ErrorLevel, l)
l, err = ParseLevel("warn")
assert.Nil(t, err)
assert.Equal(t, WarnLevel, l)
l, err = ParseLevel("WARN")
assert.Nil(t, err)
assert.Equal(t, WarnLevel, l)
l, err = ParseLevel("warning")
assert.Nil(t, err)
assert.Equal(t, WarnLevel, l)
l, err = ParseLevel("WARNING")
assert.Nil(t, err)
assert.Equal(t, WarnLevel, l)
l, err = ParseLevel("info")
assert.Nil(t, err)
assert.Equal(t, InfoLevel, l)
l, err = ParseLevel("INFO")
assert.Nil(t, err)
assert.Equal(t, InfoLevel, l)
l, err = ParseLevel("debug")
assert.Nil(t, err)
assert.Equal(t, DebugLevel, l)
l, err = ParseLevel("DEBUG")
assert.Nil(t, err)
assert.Equal(t, DebugLevel, l)
l, err = ParseLevel("invalid")
assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
}
func TestGetSetLevelRace(t *testing.T) {
wg := sync.WaitGroup{}
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
if i%2 == 0 {
SetLevel(InfoLevel)
} else {
GetLevel()
}
}(i)
}
wg.Wait()
}
func TestLoggingRace(t *testing.T) {
logger := New()
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 100; i++ {
go func() {
logger.Info("info")
wg.Done()
}()
}
wg.Wait()
}
// Compile test
func TestLogrusInterface(t *testing.T) {
var buffer bytes.Buffer
fn := func(l FieldLogger) {
b := l.WithField("key", "value")
b.Debug("Test")
}
// test logger
logger := New()
logger.Out = &buffer
fn(logger)
// test Entry
e := logger.WithField("another", "value")
fn(e)
}
// Implements io.Writer using channels for synchronization, so we can wait on
// the Entry.Writer goroutine to write in a non-racey way. This does assume that
// there is a single call to Logger.Out for each message.
type channelWriter chan []byte
func (cw channelWriter) Write(p []byte) (int, error) {
cw <- p
return len(p), nil
}
func TestEntryWriter(t *testing.T) {
cw := channelWriter(make(chan []byte, 1))
log := New()
log.Out = cw
log.Formatter = new(JSONFormatter)
log.WithField("foo", "bar").WriterLevel(WarnLevel).Write([]byte("hello\n"))
bs := <-cw
var fields Fields
err := json.Unmarshal(bs, &fields)
assert.Nil(t, err)
assert.Equal(t, fields["foo"], "bar")
assert.Equal(t, fields["level"], "warning")
}

View File

@ -1,141 +0,0 @@
package logrus
import (
"bytes"
"errors"
"fmt"
"strings"
"testing"
"time"
)
func TestFormatting(t *testing.T) {
tf := &TextFormatter{DisableColors: true}
testCases := []struct {
value string
expected string
}{
{`foo`, "time=\"0001-01-01T00:00:00Z\" level=panic test=foo\n"},
}
for _, tc := range testCases {
b, _ := tf.Format(WithField("test", tc.value))
if string(b) != tc.expected {
t.Errorf("formatting expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected)
}
}
}
func TestQuoting(t *testing.T) {
tf := &TextFormatter{DisableColors: true}
checkQuoting := func(q bool, value interface{}) {
b, _ := tf.Format(WithField("test", value))
idx := bytes.Index(b, ([]byte)("test="))
cont := bytes.Contains(b[idx+5:], []byte("\""))
if cont != q {
if q {
t.Errorf("quoting expected for: %#v", value)
} else {
t.Errorf("quoting not expected for: %#v", value)
}
}
}
checkQuoting(false, "")
checkQuoting(false, "abcd")
checkQuoting(false, "v1.0")
checkQuoting(false, "1234567890")
checkQuoting(false, "/foobar")
checkQuoting(false, "foo_bar")
checkQuoting(false, "foo@bar")
checkQuoting(false, "foobar^")
checkQuoting(false, "+/-_^@f.oobar")
checkQuoting(true, "foobar$")
checkQuoting(true, "&foobar")
checkQuoting(true, "x y")
checkQuoting(true, "x,y")
checkQuoting(false, errors.New("invalid"))
checkQuoting(true, errors.New("invalid argument"))
// Test for quoting empty fields.
tf.QuoteEmptyFields = true
checkQuoting(true, "")
checkQuoting(false, "abcd")
checkQuoting(true, errors.New("invalid argument"))
}
func TestEscaping(t *testing.T) {
tf := &TextFormatter{DisableColors: true}
testCases := []struct {
value string
expected string
}{
{`ba"r`, `ba\"r`},
{`ba'r`, `ba'r`},
}
for _, tc := range testCases {
b, _ := tf.Format(WithField("test", tc.value))
if !bytes.Contains(b, []byte(tc.expected)) {
t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected)
}
}
}
func TestEscaping_Interface(t *testing.T) {
tf := &TextFormatter{DisableColors: true}
ts := time.Now()
testCases := []struct {
value interface{}
expected string
}{
{ts, fmt.Sprintf("\"%s\"", ts.String())},
{errors.New("error: something went wrong"), "\"error: something went wrong\""},
}
for _, tc := range testCases {
b, _ := tf.Format(WithField("test", tc.value))
if !bytes.Contains(b, []byte(tc.expected)) {
t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected)
}
}
}
func TestTimestampFormat(t *testing.T) {
checkTimeStr := func(format string) {
customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format}
customStr, _ := customFormatter.Format(WithField("test", "test"))
timeStart := bytes.Index(customStr, ([]byte)("time="))
timeEnd := bytes.Index(customStr, ([]byte)("level="))
timeStr := customStr[timeStart+5+len("\"") : timeEnd-1-len("\"")]
if format == "" {
format = time.RFC3339
}
_, e := time.Parse(format, (string)(timeStr))
if e != nil {
t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e)
}
}
checkTimeStr("2006-01-02T15:04:05.000000000Z07:00")
checkTimeStr("Mon Jan _2 15:04:05 2006")
checkTimeStr("")
}
func TestDisableTimestampWithColoredOutput(t *testing.T) {
tf := &TextFormatter{DisableTimestamp: true, ForceColors: true}
b, _ := tf.Format(WithField("test", "test"))
if strings.Contains(string(b), "[0000]") {
t.Error("timestamp not expected when DisableTimestamp is true")
}
}
// TODO add tests for sorting etc., this requires a parser for the text
// formatter output.

View File

@ -1,11 +0,0 @@
language: go
go:
- 1.7.x
- tip
sudo: false
before_install:
- go get github.com/axw/gocov/gocov
- go get github.com/mattn/goveralls
- if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
script:
- $HOME/gopath/bin/goveralls -service=travis-ci

View File

@ -1,163 +0,0 @@
# Structs [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/structs) [![Build Status](http://img.shields.io/travis/fatih/structs.svg?style=flat-square)](https://travis-ci.org/fatih/structs) [![Coverage Status](http://img.shields.io/coveralls/fatih/structs.svg?style=flat-square)](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

View File

@ -1,141 +0,0 @@
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
}

View File

@ -1,397 +0,0 @@
package structs
import (
"reflect"
"testing"
)
// A test struct that defines all cases
type Foo struct {
A string
B int `structs:"y"`
C bool `json:"c"`
d string // not exported
E *Baz
x string `xml:"x"` // not exported, with tag
Y []string
Z map[string]interface{}
*Bar // embedded
}
type Baz struct {
A string
B int
}
type Bar struct {
E string
F int
g []string
}
func newStruct() *Struct {
b := &Bar{
E: "example",
F: 2,
g: []string{"zeynep", "fatih"},
}
// B and x is not initialized for testing
f := &Foo{
A: "gopher",
C: true,
d: "small",
E: nil,
Y: []string{"example"},
Z: nil,
}
f.Bar = b
return New(f)
}
func TestField_Set(t *testing.T) {
s := newStruct()
f := s.Field("A")
err := f.Set("fatih")
if err != nil {
t.Error(err)
}
if f.Value().(string) != "fatih" {
t.Errorf("Setted value is wrong: %s want: %s", f.Value().(string), "fatih")
}
f = s.Field("Y")
err = f.Set([]string{"override", "with", "this"})
if err != nil {
t.Error(err)
}
sliceLen := len(f.Value().([]string))
if sliceLen != 3 {
t.Errorf("Setted values slice length is wrong: %d, want: %d", sliceLen, 3)
}
f = s.Field("C")
err = f.Set(false)
if err != nil {
t.Error(err)
}
if f.Value().(bool) {
t.Errorf("Setted value is wrong: %t want: %t", f.Value().(bool), false)
}
// let's pass a different type
f = s.Field("A")
err = f.Set(123) // Field A is of type string, but we are going to pass an integer
if err == nil {
t.Error("Setting a field's value with a different type than the field's type should return an error")
}
// old value should be still there :)
if f.Value().(string) != "fatih" {
t.Errorf("Setted value is wrong: %s want: %s", f.Value().(string), "fatih")
}
// let's access an unexported field, which should give an error
f = s.Field("d")
err = f.Set("large")
if err != errNotExported {
t.Error(err)
}
// let's set a pointer to struct
b := &Bar{
E: "gopher",
F: 2,
}
f = s.Field("Bar")
err = f.Set(b)
if err != nil {
t.Error(err)
}
baz := &Baz{
A: "helloWorld",
B: 42,
}
f = s.Field("E")
err = f.Set(baz)
if err != nil {
t.Error(err)
}
ba := s.Field("E").Value().(*Baz)
if ba.A != "helloWorld" {
t.Errorf("could not set baz. Got: %s Want: helloWorld", ba.A)
}
}
func TestField_NotSettable(t *testing.T) {
a := map[int]Baz{
4: Baz{
A: "value",
},
}
s := New(a[4])
if err := s.Field("A").Set("newValue"); err != errNotSettable {
t.Errorf("Trying to set non-settable field should error with %q. Got %q instead.", errNotSettable, err)
}
}
func TestField_Zero(t *testing.T) {
s := newStruct()
f := s.Field("A")
err := f.Zero()
if err != nil {
t.Error(err)
}
if f.Value().(string) != "" {
t.Errorf("Zeroed value is wrong: %s want: %s", f.Value().(string), "")
}
f = s.Field("Y")
err = f.Zero()
if err != nil {
t.Error(err)
}
sliceLen := len(f.Value().([]string))
if sliceLen != 0 {
t.Errorf("Zeroed values slice length is wrong: %d, want: %d", sliceLen, 0)
}
f = s.Field("C")
err = f.Zero()
if err != nil {
t.Error(err)
}
if f.Value().(bool) {
t.Errorf("Zeroed value is wrong: %t want: %t", f.Value().(bool), false)
}
// let's access an unexported field, which should give an error
f = s.Field("d")
err = f.Zero()
if err != errNotExported {
t.Error(err)
}
f = s.Field("Bar")
err = f.Zero()
if err != nil {
t.Error(err)
}
f = s.Field("E")
err = f.Zero()
if err != nil {
t.Error(err)
}
v := s.Field("E").value
if !v.IsNil() {
t.Errorf("could not set baz. Got: %s Want: <nil>", v.Interface())
}
}
func TestField(t *testing.T) {
s := newStruct()
defer func() {
err := recover()
if err == nil {
t.Error("Retrieveing a non existing field from the struct should panic")
}
}()
_ = s.Field("no-field")
}
func TestField_Kind(t *testing.T) {
s := newStruct()
f := s.Field("A")
if f.Kind() != reflect.String {
t.Errorf("Field A has wrong kind: %s want: %s", f.Kind(), reflect.String)
}
f = s.Field("B")
if f.Kind() != reflect.Int {
t.Errorf("Field B has wrong kind: %s want: %s", f.Kind(), reflect.Int)
}
// unexported
f = s.Field("d")
if f.Kind() != reflect.String {
t.Errorf("Field d has wrong kind: %s want: %s", f.Kind(), reflect.String)
}
}
func TestField_Tag(t *testing.T) {
s := newStruct()
v := s.Field("B").Tag("json")
if v != "" {
t.Errorf("Field's tag value of a non existing tag should return empty, got: %s", v)
}
v = s.Field("C").Tag("json")
if v != "c" {
t.Errorf("Field's tag value of the existing field C should return 'c', got: %s", v)
}
v = s.Field("d").Tag("json")
if v != "" {
t.Errorf("Field's tag value of a non exported field should return empty, got: %s", v)
}
v = s.Field("x").Tag("xml")
if v != "x" {
t.Errorf("Field's tag value of a non exported field with a tag should return 'x', got: %s", v)
}
v = s.Field("A").Tag("json")
if v != "" {
t.Errorf("Field's tag value of a existing field without a tag should return empty, got: %s", v)
}
}
func TestField_Value(t *testing.T) {
s := newStruct()
v := s.Field("A").Value()
val, ok := v.(string)
if !ok {
t.Errorf("Field's value of a A should be string")
}
if val != "gopher" {
t.Errorf("Field's value of a existing tag should return 'gopher', got: %s", val)
}
defer func() {
err := recover()
if err == nil {
t.Error("Value of a non exported field from the field should panic")
}
}()
// should panic
_ = s.Field("d").Value()
}
func TestField_IsEmbedded(t *testing.T) {
s := newStruct()
if !s.Field("Bar").IsEmbedded() {
t.Errorf("Fields 'Bar' field is an embedded field")
}
if s.Field("d").IsEmbedded() {
t.Errorf("Fields 'd' field is not an embedded field")
}
}
func TestField_IsExported(t *testing.T) {
s := newStruct()
if !s.Field("Bar").IsExported() {
t.Errorf("Fields 'Bar' field is an exported field")
}
if !s.Field("A").IsExported() {
t.Errorf("Fields 'A' field is an exported field")
}
if s.Field("d").IsExported() {
t.Errorf("Fields 'd' field is not an exported field")
}
}
func TestField_IsZero(t *testing.T) {
s := newStruct()
if s.Field("A").IsZero() {
t.Errorf("Fields 'A' field is an initialized field")
}
if !s.Field("B").IsZero() {
t.Errorf("Fields 'B' field is not an initialized field")
}
}
func TestField_Name(t *testing.T) {
s := newStruct()
if s.Field("A").Name() != "A" {
t.Errorf("Fields 'A' field should have the name 'A'")
}
}
func TestField_Field(t *testing.T) {
s := newStruct()
e := s.Field("Bar").Field("E")
val, ok := e.Value().(string)
if !ok {
t.Error("The value of the field 'e' inside 'Bar' struct should be string")
}
if val != "example" {
t.Errorf("The value of 'e' should be 'example, got: %s", val)
}
defer func() {
err := recover()
if err == nil {
t.Error("Field of a non existing nested struct should panic")
}
}()
_ = s.Field("Bar").Field("e")
}
func TestField_Fields(t *testing.T) {
s := newStruct()
fields := s.Field("Bar").Fields()
if len(fields) != 3 {
t.Errorf("We expect 3 fields in embedded struct, was: %d", len(fields))
}
}
func TestField_FieldOk(t *testing.T) {
s := newStruct()
b, ok := s.FieldOk("Bar")
if !ok {
t.Error("The field 'Bar' should exists.")
}
e, ok := b.FieldOk("E")
if !ok {
t.Error("The field 'E' should exists.")
}
val, ok := e.Value().(string)
if !ok {
t.Error("The value of the field 'e' inside 'Bar' struct should be string")
}
if val != "example" {
t.Errorf("The value of 'e' should be 'example, got: %s", val)
}
}

View File

@ -1,586 +0,0 @@
// 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:
// get the element type of the map
mapElem := val.Type()
switch val.Type().Kind() {
case reflect.Ptr, reflect.Array, reflect.Map,
reflect.Slice, reflect.Chan:
mapElem = val.Type().Elem()
if mapElem.Kind() == reflect.Ptr {
mapElem = mapElem.Elem()
}
}
// only iterate over struct types, ie: map[string]StructType,
// map[string][]StructType,
if mapElem.Kind() == reflect.Struct ||
(mapElem.Kind() == reflect.Slice &&
mapElem.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.
// i.e []foo or []*foo
if val.Type().Elem().Kind() != reflect.Struct &&
!(val.Type().Elem().Kind() == reflect.Ptr &&
val.Type().Elem().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
}

View File

@ -1,351 +0,0 @@
package structs
import (
"fmt"
"time"
)
func ExampleNew() {
type Server struct {
Name string
ID int32
Enabled bool
}
server := &Server{
Name: "Arslan",
ID: 123456,
Enabled: true,
}
s := New(server)
fmt.Printf("Name : %v\n", s.Name())
fmt.Printf("Values : %v\n", s.Values())
fmt.Printf("Value of ID : %v\n", s.Field("ID").Value())
// Output:
// Name : Server
// Values : [Arslan 123456 true]
// Value of ID : 123456
}
func ExampleMap() {
type Server struct {
Name string
ID int32
Enabled bool
}
s := &Server{
Name: "Arslan",
ID: 123456,
Enabled: true,
}
m := Map(s)
fmt.Printf("%#v\n", m["Name"])
fmt.Printf("%#v\n", m["ID"])
fmt.Printf("%#v\n", m["Enabled"])
// Output:
// "Arslan"
// 123456
// true
}
func ExampleMap_tags() {
// Custom tags can change the map keys instead of using the fields name
type Server struct {
Name string `structs:"server_name"`
ID int32 `structs:"server_id"`
Enabled bool `structs:"enabled"`
}
s := &Server{
Name: "Zeynep",
ID: 789012,
}
m := Map(s)
// access them by the custom tags defined above
fmt.Printf("%#v\n", m["server_name"])
fmt.Printf("%#v\n", m["server_id"])
fmt.Printf("%#v\n", m["enabled"])
// Output:
// "Zeynep"
// 789012
// false
}
func ExampleMap_omitNested() {
// By default field with struct types are processed too. We can stop
// processing them via "omitnested" tag option.
type Server struct {
Name string `structs:"server_name"`
ID int32 `structs:"server_id"`
Time time.Time `structs:"time,omitnested"` // do not convert to map[string]interface{}
}
const shortForm = "2006-Jan-02"
t, _ := time.Parse("2006-Jan-02", "2013-Feb-03")
s := &Server{
Name: "Zeynep",
ID: 789012,
Time: t,
}
m := Map(s)
// access them by the custom tags defined above
fmt.Printf("%v\n", m["server_name"])
fmt.Printf("%v\n", m["server_id"])
fmt.Printf("%v\n", m["time"].(time.Time))
// Output:
// Zeynep
// 789012
// 2013-02-03 00:00:00 +0000 UTC
}
func ExampleMap_omitEmpty() {
// By default field with struct types of zero values are processed too. We
// can stop processing them via "omitempty" tag option.
type Server struct {
Name string `structs:",omitempty"`
ID int32 `structs:"server_id,omitempty"`
Location string
}
// Only add location
s := &Server{
Location: "Tokyo",
}
m := Map(s)
// map contains only the Location field
fmt.Printf("%v\n", m)
// Output:
// map[Location:Tokyo]
}
func ExampleValues() {
type Server struct {
Name string
ID int32
Enabled bool
}
s := &Server{
Name: "Fatih",
ID: 135790,
Enabled: false,
}
m := Values(s)
fmt.Printf("Values: %+v\n", m)
// Output:
// Values: [Fatih 135790 false]
}
func ExampleValues_omitEmpty() {
// By default field with struct types of zero values are processed too. We
// can stop processing them via "omitempty" tag option.
type Server struct {
Name string `structs:",omitempty"`
ID int32 `structs:"server_id,omitempty"`
Location string
}
// Only add location
s := &Server{
Location: "Ankara",
}
m := Values(s)
// values contains only the Location field
fmt.Printf("Values: %+v\n", m)
// Output:
// Values: [Ankara]
}
func ExampleValues_tags() {
type Location struct {
City string
Country string
}
type Server struct {
Name string
ID int32
Enabled bool
Location Location `structs:"-"` // values from location are not included anymore
}
s := &Server{
Name: "Fatih",
ID: 135790,
Enabled: false,
Location: Location{City: "Ankara", Country: "Turkey"},
}
// Let get all values from the struct s. Note that we don't include values
// from the Location field
m := Values(s)
fmt.Printf("Values: %+v\n", m)
// Output:
// Values: [Fatih 135790 false]
}
func ExampleFields() {
type Access struct {
Name string
LastAccessed time.Time
Number int
}
s := &Access{
Name: "Fatih",
LastAccessed: time.Now(),
Number: 1234567,
}
fields := Fields(s)
for i, field := range fields {
fmt.Printf("[%d] %+v\n", i, field.Name())
}
// Output:
// [0] Name
// [1] LastAccessed
// [2] Number
}
func ExampleFields_nested() {
type Person struct {
Name string
Number int
}
type Access struct {
Person Person
HasPermission bool
LastAccessed time.Time
}
s := &Access{
Person: Person{Name: "fatih", Number: 1234567},
LastAccessed: time.Now(),
HasPermission: true,
}
// Let's get all fields from the struct s.
fields := Fields(s)
for _, field := range fields {
if field.Name() == "Person" {
fmt.Printf("Access.Person.Name: %+v\n", field.Field("Name").Value())
}
}
// Output:
// Access.Person.Name: fatih
}
func ExampleField() {
type Person struct {
Name string
Number int
}
type Access struct {
Person Person
HasPermission bool
LastAccessed time.Time
}
access := &Access{
Person: Person{Name: "fatih", Number: 1234567},
LastAccessed: time.Now(),
HasPermission: true,
}
// Create a new Struct type
s := New(access)
// Get the Field type for "Person" field
p := s.Field("Person")
// Get the underlying "Name field" and print the value of it
name := p.Field("Name")
fmt.Printf("Value of Person.Access.Name: %+v\n", name.Value())
// Output:
// Value of Person.Access.Name: fatih
}
func ExampleIsZero() {
type Server struct {
Name string
ID int32
Enabled bool
}
// Nothing is initalized
a := &Server{}
isZeroA := IsZero(a)
// Name and Enabled is initialized, but not ID
b := &Server{
Name: "Golang",
Enabled: true,
}
isZeroB := IsZero(b)
fmt.Printf("%#v\n", isZeroA)
fmt.Printf("%#v\n", isZeroB)
// Output:
// true
// false
}
func ExampleHasZero() {
// Let's define an Access struct. Note that the "Enabled" field is not
// going to be checked because we added the "structs" tag to the field.
type Access struct {
Name string
LastAccessed time.Time
Number int
Enabled bool `structs:"-"`
}
// Name and Number is not initialized.
a := &Access{
LastAccessed: time.Now(),
}
hasZeroA := HasZero(a)
// Name and Number is initialized.
b := &Access{
Name: "Fatih",
LastAccessed: time.Now(),
Number: 12345,
}
hasZeroB := HasZero(b)
fmt.Printf("%#v\n", hasZeroA)
fmt.Printf("%#v\n", hasZeroB)
// Output:
// true
// false
}

File diff suppressed because it is too large Load Diff

View File

@ -1,32 +0,0 @@
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:]
}

View File

@ -1,46 +0,0 @@
package structs
import "testing"
func TestParseTag_Name(t *testing.T) {
tags := []struct {
tag string
has bool
}{
{"", false},
{"name", true},
{"name,opt", true},
{"name , opt, opt2", false}, // has a single whitespace
{", opt, opt2", false},
}
for _, tag := range tags {
name, _ := parseTag(tag.tag)
if (name != "name") && tag.has {
t.Errorf("Parse tag should return name: %#v", tag)
}
}
}
func TestParseTag_Opts(t *testing.T) {
tags := []struct {
opts string
has bool
}{
{"name", false},
{"name,opt", true},
{"name , opt, opt2", false}, // has a single whitespace
{",opt, opt2", true},
{", opt3, opt4", false},
}
// search for "opt"
for _, tag := range tags {
_, opts := parseTag(tag.opts)
if opts.Has("opt") != tag.has {
t.Errorf("Tag opts should have opt: %#v", tag)
}
}
}

5
vendor/github.com/fsnotify/fsnotify/.editorconfig generated vendored Normal file
View File

@ -0,0 +1,5 @@
root = true
[*]
indent_style = tab
indent_size = 4

6
vendor/github.com/fsnotify/fsnotify/.gitignore generated vendored Normal file
View File

@ -0,0 +1,6 @@
# Setup a Global .gitignore for OS and editor generated files:
# https://help.github.com/articles/ignoring-files
# git config --global core.excludesfile ~/.gitignore_global
.vagrant
*.sublime-project

30
vendor/github.com/fsnotify/fsnotify/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,30 @@
sudo: false
language: go
go:
- 1.8.x
- 1.9.x
- tip
matrix:
allow_failures:
- go: tip
fast_finish: true
before_script:
- go get -u github.com/golang/lint/golint
script:
- go test -v --race ./...
after_script:
- test -z "$(gofmt -s -l -w . | tee /dev/stderr)"
- test -z "$(golint ./... | tee /dev/stderr)"
- go vet ./...
os:
- linux
- osx
notifications:
email: false

52
vendor/github.com/fsnotify/fsnotify/AUTHORS generated vendored Normal file
View File

@ -0,0 +1,52 @@
# Names should be added to this file as
# Name or Organization <email address>
# The email address is not required for organizations.
# You can update this list using the following command:
#
# $ git shortlog -se | awk '{print $2 " " $3 " " $4}'
# Please keep the list sorted.
Aaron L <aaron@bettercoder.net>
Adrien Bustany <adrien@bustany.org>
Amit Krishnan <amit.krishnan@oracle.com>
Anmol Sethi <me@anmol.io>
Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Bruno Bigras <bigras.bruno@gmail.com>
Caleb Spare <cespare@gmail.com>
Case Nelson <case@teammating.com>
Chris Howey <chris@howey.me> <howeyc@gmail.com>
Christoffer Buchholz <christoffer.buchholz@gmail.com>
Daniel Wagner-Hall <dawagner@gmail.com>
Dave Cheney <dave@cheney.net>
Evan Phoenix <evan@fallingsnow.net>
Francisco Souza <f@souza.cc>
Hari haran <hariharan.uno@gmail.com>
John C Barstow
Kelvin Fo <vmirage@gmail.com>
Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp>
Matt Layher <mdlayher@gmail.com>
Nathan Youngman <git@nathany.com>
Nickolai Zeldovich <nickolai@csail.mit.edu>
Patrick <patrick@dropbox.com>
Paul Hammond <paul@paulhammond.org>
Pawel Knap <pawelknap88@gmail.com>
Pieter Droogendijk <pieter@binky.org.uk>
Pursuit92 <JoshChase@techpursuit.net>
Riku Voipio <riku.voipio@linaro.org>
Rob Figueiredo <robfig@gmail.com>
Rodrigo Chiossi <rodrigochiossi@gmail.com>
Slawek Ligus <root@ooz.ie>
Soge Zhang <zhssoge@gmail.com>
Tiffany Jernigan <tiffany.jernigan@intel.com>
Tilak Sharma <tilaks@google.com>
Tom Payne <twpayne@gmail.com>
Travis Cline <travis.cline@gmail.com>
Tudor Golubenco <tudor.g@gmail.com>
Vahe Khachikyan <vahe@live.ca>
Yukang <moorekang@gmail.com>
bronze1man <bronze1man@gmail.com>
debrando <denis.brandolini@gmail.com>
henrikedwards <henrik.edwards@gmail.com>
铁哥 <guotie.9@gmail.com>

317
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md generated vendored Normal file
View File

@ -0,0 +1,317 @@
# Changelog
## v1.4.7 / 2018-01-09
* BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine)
* Tests: Fix missing verb on format string (thanks @rchiossi)
* Linux: Fix deadlock in Remove (thanks @aarondl)
* Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne)
* Docs: Moved FAQ into the README (thanks @vahe)
* Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich)
* Docs: replace references to OS X with macOS
## v1.4.2 / 2016-10-10
* Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack)
## v1.4.1 / 2016-10-04
* Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack)
## v1.4.0 / 2016-10-01
* add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie)
## v1.3.1 / 2016-06-28
* Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc)
## v1.3.0 / 2016-04-19
* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135)
## v1.2.10 / 2016-03-02
* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj)
## v1.2.9 / 2016-01-13
kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep)
## v1.2.8 / 2015-12-17
* kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test)
* inotify: fix race in test
* enable race detection for continuous integration (Linux, Mac, Windows)
## v1.2.5 / 2015-10-17
* inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki)
* inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken)
* kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie)
* kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion)
## v1.2.1 / 2015-10-14
* kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx)
## v1.2.0 / 2015-02-08
* inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD)
* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD)
* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59)
## v1.1.1 / 2015-02-05
* inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD)
## v1.1.0 / 2014-12-12
* kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43)
* add low-level functions
* only need to store flags on directories
* less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13)
* done can be an unbuffered channel
* remove calls to os.NewSyscallError
* More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher)
* kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48)
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
## v1.0.4 / 2014-09-07
* kqueue: add dragonfly to the build tags.
* Rename source code files, rearrange code so exported APIs are at the top.
* Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang)
## v1.0.3 / 2014-08-19
* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36)
## v1.0.2 / 2014-08-17
* [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
* [Fix] Make ./path and path equivalent. (thanks @zhsso)
## v1.0.0 / 2014-08-15
* [API] Remove AddWatch on Windows, use Add.
* Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30)
* Minor updates based on feedback from golint.
## dev / 2014-07-09
* Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify).
* Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno)
## dev / 2014-07-04
* kqueue: fix incorrect mutex used in Close()
* Update example to demonstrate usage of Op.
## dev / 2014-06-28
* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4)
* Fix for String() method on Event (thanks Alex Brainman)
* Don't build on Plan 9 or Solaris (thanks @4ad)
## dev / 2014-06-21
* Events channel of type Event rather than *Event.
* [internal] use syscall constants directly for inotify and kqueue.
* [internal] kqueue: rename events to kevents and fileEvent to event.
## dev / 2014-06-19
* Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally).
* [internal] remove cookie from Event struct (unused).
* [internal] Event struct has the same definition across every OS.
* [internal] remove internal watch and removeWatch methods.
## dev / 2014-06-12
* [API] Renamed Watch() to Add() and RemoveWatch() to Remove().
* [API] Pluralized channel names: Events and Errors.
* [API] Renamed FileEvent struct to Event.
* [API] Op constants replace methods like IsCreate().
## dev / 2014-06-12
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
## dev / 2014-05-23
* [API] Remove current implementation of WatchFlags.
* current implementation doesn't take advantage of OS for efficiency
* provides little benefit over filtering events as they are received, but has extra bookkeeping and mutexes
* no tests for the current implementation
* not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195)
## v0.9.3 / 2014-12-31
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
## v0.9.2 / 2014-08-17
* [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
## v0.9.1 / 2014-06-12
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
## v0.9.0 / 2014-01-17
* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany)
* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare)
* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library.
## v0.8.12 / 2013-11-13
* [API] Remove FD_SET and friends from Linux adapter
## v0.8.11 / 2013-11-02
* [Doc] Add Changelog [#72][] (thanks @nathany)
* [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond)
## v0.8.10 / 2013-10-19
* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott)
* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer)
* [Doc] specify OS-specific limits in README (thanks @debrando)
## v0.8.9 / 2013-09-08
* [Doc] Contributing (thanks @nathany)
* [Doc] update package path in example code [#63][] (thanks @paulhammond)
* [Doc] GoCI badge in README (Linux only) [#60][]
* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany)
## v0.8.8 / 2013-06-17
* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie)
## v0.8.7 / 2013-06-03
* [API] Make syscall flags internal
* [Fix] inotify: ignore event changes
* [Fix] race in symlink test [#45][] (reported by @srid)
* [Fix] tests on Windows
* lower case error messages
## v0.8.6 / 2013-05-23
* kqueue: Use EVT_ONLY flag on Darwin
* [Doc] Update README with full example
## v0.8.5 / 2013-05-09
* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg)
## v0.8.4 / 2013-04-07
* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz)
## v0.8.3 / 2013-03-13
* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin)
* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin)
## v0.8.2 / 2013-02-07
* [Doc] add Authors
* [Fix] fix data races for map access [#29][] (thanks @fsouza)
## v0.8.1 / 2013-01-09
* [Fix] Windows path separators
* [Doc] BSD License
## v0.8.0 / 2012-11-09
* kqueue: directory watching improvements (thanks @vmirage)
* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto)
* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr)
## v0.7.4 / 2012-10-09
* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji)
* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig)
* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig)
* [Fix] kqueue: modify after recreation of file
## v0.7.3 / 2012-09-27
* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage)
* [Fix] kqueue: no longer get duplicate CREATE events
## v0.7.2 / 2012-09-01
* kqueue: events for created directories
## v0.7.1 / 2012-07-14
* [Fix] for renaming files
## v0.7.0 / 2012-07-02
* [Feature] FSNotify flags
* [Fix] inotify: Added file name back to event path
## v0.6.0 / 2012-06-06
* kqueue: watch files after directory created (thanks @tmc)
## v0.5.1 / 2012-05-22
* [Fix] inotify: remove all watches before Close()
## v0.5.0 / 2012-05-03
* [API] kqueue: return errors during watch instead of sending over channel
* kqueue: match symlink behavior on Linux
* inotify: add `DELETE_SELF` (requested by @taralx)
* [Fix] kqueue: handle EINTR (reported by @robfig)
* [Doc] Godoc example [#1][] (thanks @davecheney)
## v0.4.0 / 2012-03-30
* Go 1 released: build with go tool
* [Feature] Windows support using winfsnotify
* Windows does not have attribute change notifications
* Roll attribute notifications into IsModify
## v0.3.0 / 2012-02-19
* kqueue: add files when watch directory
## v0.2.0 / 2011-12-30
* update to latest Go weekly code
## v0.1.0 / 2011-10-19
* kqueue: add watch on file creation to match inotify
* kqueue: create file event
* inotify: ignore `IN_IGNORED` events
* event String()
* linux: common FileEvent functions
* initial commit
[#79]: https://github.com/howeyc/fsnotify/pull/79
[#77]: https://github.com/howeyc/fsnotify/pull/77
[#72]: https://github.com/howeyc/fsnotify/issues/72
[#71]: https://github.com/howeyc/fsnotify/issues/71
[#70]: https://github.com/howeyc/fsnotify/issues/70
[#63]: https://github.com/howeyc/fsnotify/issues/63
[#62]: https://github.com/howeyc/fsnotify/issues/62
[#60]: https://github.com/howeyc/fsnotify/issues/60
[#59]: https://github.com/howeyc/fsnotify/issues/59
[#49]: https://github.com/howeyc/fsnotify/issues/49
[#45]: https://github.com/howeyc/fsnotify/issues/45
[#40]: https://github.com/howeyc/fsnotify/issues/40
[#36]: https://github.com/howeyc/fsnotify/issues/36
[#33]: https://github.com/howeyc/fsnotify/issues/33
[#29]: https://github.com/howeyc/fsnotify/issues/29
[#25]: https://github.com/howeyc/fsnotify/issues/25
[#24]: https://github.com/howeyc/fsnotify/issues/24
[#21]: https://github.com/howeyc/fsnotify/issues/21

77
vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md generated vendored Normal file
View File

@ -0,0 +1,77 @@
# Contributing
## Issues
* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/fsnotify/fsnotify/issues).
* Please indicate the platform you are using fsnotify on.
* A code example to reproduce the problem is appreciated.
## Pull Requests
### Contributor License Agreement
fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual).
Please indicate that you have signed the CLA in your pull request.
### How fsnotify is Developed
* Development is done on feature branches.
* Tests are run on BSD, Linux, macOS and Windows.
* Pull requests are reviewed and [applied to master][am] using [hub][].
* Maintainers may modify or squash commits rather than asking contributors to.
* To issue a new release, the maintainers will:
* Update the CHANGELOG
* Tag a version, which will become available through gopkg.in.
### How to Fork
For smooth sailing, always use the original import path. Installing with `go get` makes this easy.
1. Install from GitHub (`go get -u github.com/fsnotify/fsnotify`)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Ensure everything works and the tests pass (see below)
4. Commit your changes (`git commit -am 'Add some feature'`)
Contribute upstream:
1. Fork fsnotify on GitHub
2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`)
3. Push to the branch (`git push fork my-new-feature`)
4. Create a new Pull Request on GitHub
This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/).
### Testing
fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows.
Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on.
To aid in cross-platform testing there is a Vagrantfile for Linux and BSD.
* Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/)
* Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder.
* Run `vagrant up` from the project folder. You can also setup just one box with `vagrant up linux` or `vagrant up bsd` (note: the BSD box doesn't support Windows hosts at this time, and NFS may prompt for your host OS password)
* Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd fsnotify/fsnotify; go test'`.
* When you're done, you will want to halt or destroy the Vagrant boxes.
Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory.
Right now there is no equivalent solution for Windows and macOS, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads).
### Maintainers
Help maintaining fsnotify is welcome. To be a maintainer:
* Submit a pull request and sign the CLA as above.
* You must be able to run the test suite on Mac, Windows, Linux and BSD.
To keep master clean, the fsnotify project uses the "apply mail" workflow outlined in Nathaniel Talbott's post ["Merge pull request" Considered Harmful][am]. This requires installing [hub][].
All code changes should be internal pull requests.
Releases are tagged using [Semantic Versioning](http://semver.org/).
[hub]: https://github.com/github/hub
[am]: http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/#.VGa5yZPF_Zs

28
vendor/github.com/fsnotify/fsnotify/LICENSE generated vendored Normal file
View File

@ -0,0 +1,28 @@
Copyright (c) 2012 The Go Authors. All rights reserved.
Copyright (c) 2012 fsnotify Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

79
vendor/github.com/fsnotify/fsnotify/README.md generated vendored Normal file
View File

@ -0,0 +1,79 @@
# File system notifications for Go
[![GoDoc](https://godoc.org/github.com/fsnotify/fsnotify?status.svg)](https://godoc.org/github.com/fsnotify/fsnotify) [![Go Report Card](https://goreportcard.com/badge/github.com/fsnotify/fsnotify)](https://goreportcard.com/report/github.com/fsnotify/fsnotify)
fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather than `syscall` from the standard library. Ensure you have the latest version installed by running:
```console
go get -u golang.org/x/sys/...
```
Cross platform: Windows, Linux, BSD and macOS.
|Adapter |OS |Status |
|----------|----------|----------|
|inotify |Linux 2.6.27 or later, Android\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)|
|kqueue |BSD, macOS, iOS\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)|
|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)|
|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)|
|FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)|
|fanotify |Linux 2.6.37+ | |
|USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)|
|Polling |*All* |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)|
\* Android and iOS are untested.
Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information.
## API stability
fsnotify is a fork of [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA).
All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). Further API changes are [planned](https://github.com/fsnotify/fsnotify/milestones), and will be tagged with a new major revision number.
Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`.
## Contributing
Please refer to [CONTRIBUTING][] before opening an issue or pull request.
## Example
See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go).
## FAQ
**When a file is moved to another directory is it still being watched?**
No (it shouldn't be, unless you are watching where it was moved to).
**When I watch a directory, are all subdirectories watched as well?**
No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]).
**Do I have to watch the Error and Event channels in a separate goroutine?**
As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7])
**Why am I receiving multiple events for the same file on OS X?**
Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]).
**How many files can be watched at once?**
There are OS-specific limits as to how many watches can be created:
* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error.
* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
[#62]: https://github.com/howeyc/fsnotify/issues/62
[#18]: https://github.com/fsnotify/fsnotify/issues/18
[#11]: https://github.com/fsnotify/fsnotify/issues/11
[#7]: https://github.com/howeyc/fsnotify/issues/7
[contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md
## Related Projects
* [notify](https://github.com/rjeczalik/notify)
* [fsevents](https://github.com/fsnotify/fsevents)

37
vendor/github.com/fsnotify/fsnotify/fen.go generated vendored Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build solaris
package fsnotify
import (
"errors"
)
// Watcher watches a set of files, delivering events to a channel.
type Watcher struct {
Events chan Event
Errors chan error
}
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
func NewWatcher() (*Watcher, error) {
return nil, errors.New("FEN based watcher not yet supported for fsnotify\n")
}
// Close removes all watches and closes the events channel.
func (w *Watcher) Close() error {
return nil
}
// Add starts watching the named file or directory (non-recursively).
func (w *Watcher) Add(name string) error {
return nil
}
// Remove stops watching the the named file or directory (non-recursively).
func (w *Watcher) Remove(name string) error {
return nil
}

66
vendor/github.com/fsnotify/fsnotify/fsnotify.go generated vendored Normal file
View File

@ -0,0 +1,66 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !plan9
// Package fsnotify provides a platform-independent interface for file system notifications.
package fsnotify
import (
"bytes"
"errors"
"fmt"
)
// Event represents a single file system notification.
type Event struct {
Name string // Relative path to the file or directory.
Op Op // File operation that triggered the event.
}
// Op describes a set of file operations.
type Op uint32
// These are the generalized file operations that can trigger a notification.
const (
Create Op = 1 << iota
Write
Remove
Rename
Chmod
)
func (op Op) String() string {
// Use a buffer for efficient string concatenation
var buffer bytes.Buffer
if op&Create == Create {
buffer.WriteString("|CREATE")
}
if op&Remove == Remove {
buffer.WriteString("|REMOVE")
}
if op&Write == Write {
buffer.WriteString("|WRITE")
}
if op&Rename == Rename {
buffer.WriteString("|RENAME")
}
if op&Chmod == Chmod {
buffer.WriteString("|CHMOD")
}
if buffer.Len() == 0 {
return ""
}
return buffer.String()[1:] // Strip leading pipe
}
// String returns a string representation of the event in the form
// "file: REMOVE|WRITE|..."
func (e Event) String() string {
return fmt.Sprintf("%q: %s", e.Name, e.Op.String())
}
// Common errors that can be reported by a watcher
var ErrEventOverflow = errors.New("fsnotify queue overflow")

337
vendor/github.com/fsnotify/fsnotify/inotify.go generated vendored Normal file
View File

@ -0,0 +1,337 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux
package fsnotify
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"sync"
"unsafe"
"golang.org/x/sys/unix"
)
// Watcher watches a set of files, delivering events to a channel.
type Watcher struct {
Events chan Event
Errors chan error
mu sync.Mutex // Map access
fd int
poller *fdPoller
watches map[string]*watch // Map of inotify watches (key: path)
paths map[int]string // Map of watched paths (key: watch descriptor)
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
doneResp chan struct{} // Channel to respond to Close
}
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
func NewWatcher() (*Watcher, error) {
// Create inotify fd
fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC)
if fd == -1 {
return nil, errno
}
// Create epoll
poller, err := newFdPoller(fd)
if err != nil {
unix.Close(fd)
return nil, err
}
w := &Watcher{
fd: fd,
poller: poller,
watches: make(map[string]*watch),
paths: make(map[int]string),
Events: make(chan Event),
Errors: make(chan error),
done: make(chan struct{}),
doneResp: make(chan struct{}),
}
go w.readEvents()
return w, nil
}
func (w *Watcher) isClosed() bool {
select {
case <-w.done:
return true
default:
return false
}
}
// Close removes all watches and closes the events channel.
func (w *Watcher) Close() error {
if w.isClosed() {
return nil
}
// Send 'close' signal to goroutine, and set the Watcher to closed.
close(w.done)
// Wake up goroutine
w.poller.wake()
// Wait for goroutine to close
<-w.doneResp
return nil
}
// Add starts watching the named file or directory (non-recursively).
func (w *Watcher) Add(name string) error {
name = filepath.Clean(name)
if w.isClosed() {
return errors.New("inotify instance already closed")
}
const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF
var flags uint32 = agnosticEvents
w.mu.Lock()
defer w.mu.Unlock()
watchEntry := w.watches[name]
if watchEntry != nil {
flags |= watchEntry.flags | unix.IN_MASK_ADD
}
wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
if wd == -1 {
return errno
}
if watchEntry == nil {
w.watches[name] = &watch{wd: uint32(wd), flags: flags}
w.paths[wd] = name
} else {
watchEntry.wd = uint32(wd)
watchEntry.flags = flags
}
return nil
}
// Remove stops watching the named file or directory (non-recursively).
func (w *Watcher) Remove(name string) error {
name = filepath.Clean(name)
// Fetch the watch.
w.mu.Lock()
defer w.mu.Unlock()
watch, ok := w.watches[name]
// Remove it from inotify.
if !ok {
return fmt.Errorf("can't remove non-existent inotify watch for: %s", name)
}
// We successfully removed the watch if InotifyRmWatch doesn't return an
// error, we need to clean up our internal state to ensure it matches
// inotify's kernel state.
delete(w.paths, int(watch.wd))
delete(w.watches, name)
// inotify_rm_watch will return EINVAL if the file has been deleted;
// the inotify will already have been removed.
// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
// so that EINVAL means that the wd is being rm_watch()ed or its file removed
// by another thread and we have not received IN_IGNORE event.
success, errno := unix.InotifyRmWatch(w.fd, watch.wd)
if success == -1 {
// TODO: Perhaps it's not helpful to return an error here in every case.
// the only two possible errors are:
// EBADF, which happens when w.fd is not a valid file descriptor of any kind.
// EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor.
// Watch descriptors are invalidated when they are removed explicitly or implicitly;
// explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted.
return errno
}
return nil
}
type watch struct {
wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
}
// readEvents reads from the inotify file descriptor, converts the
// received events into Event objects and sends them via the Events channel
func (w *Watcher) readEvents() {
var (
buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
n int // Number of bytes read with read()
errno error // Syscall errno
ok bool // For poller.wait
)
defer close(w.doneResp)
defer close(w.Errors)
defer close(w.Events)
defer unix.Close(w.fd)
defer w.poller.close()
for {
// See if we have been closed.
if w.isClosed() {
return
}
ok, errno = w.poller.wait()
if errno != nil {
select {
case w.Errors <- errno:
case <-w.done:
return
}
continue
}
if !ok {
continue
}
n, errno = unix.Read(w.fd, buf[:])
// If a signal interrupted execution, see if we've been asked to close, and try again.
// http://man7.org/linux/man-pages/man7/signal.7.html :
// "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable"
if errno == unix.EINTR {
continue
}
// unix.Read might have been woken up by Close. If so, we're done.
if w.isClosed() {
return
}
if n < unix.SizeofInotifyEvent {
var err error
if n == 0 {
// If EOF is received. This should really never happen.
err = io.EOF
} else if n < 0 {
// If an error occurred while reading.
err = errno
} else {
// Read was too short.
err = errors.New("notify: short read in readEvents()")
}
select {
case w.Errors <- err:
case <-w.done:
return
}
continue
}
var offset uint32
// We don't know how many events we just read into the buffer
// While the offset points to at least one whole event...
for offset <= uint32(n-unix.SizeofInotifyEvent) {
// Point "raw" to the event in the buffer
raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
mask := uint32(raw.Mask)
nameLen := uint32(raw.Len)
if mask&unix.IN_Q_OVERFLOW != 0 {
select {
case w.Errors <- ErrEventOverflow:
case <-w.done:
return
}
}
// If the event happened to the watched directory or the watched file, the kernel
// doesn't append the filename to the event, but we would like to always fill the
// the "Name" field with a valid filename. We retrieve the path of the watch from
// the "paths" map.
w.mu.Lock()
name, ok := w.paths[int(raw.Wd)]
// IN_DELETE_SELF occurs when the file/directory being watched is removed.
// This is a sign to clean up the maps, otherwise we are no longer in sync
// with the inotify kernel state which has already deleted the watch
// automatically.
if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
delete(w.paths, int(raw.Wd))
delete(w.watches, name)
}
w.mu.Unlock()
if nameLen > 0 {
// Point "bytes" at the first byte of the filename
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))
// The filename is padded with NULL bytes. TrimRight() gets rid of those.
name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
}
event := newEvent(name, mask)
// Send the events that are not ignored on the events channel
if !event.ignoreLinux(mask) {
select {
case w.Events <- event:
case <-w.done:
return
}
}
// Move to the next event in the buffer
offset += unix.SizeofInotifyEvent + nameLen
}
}
}
// Certain types of events can be "ignored" and not sent over the Events
// channel. Such as events marked ignore by the kernel, or MODIFY events
// against files that do not exist.
func (e *Event) ignoreLinux(mask uint32) bool {
// Ignore anything the inotify API says to ignore
if mask&unix.IN_IGNORED == unix.IN_IGNORED {
return true
}
// If the event is not a DELETE or RENAME, the file must exist.
// Otherwise the event is ignored.
// *Note*: this was put in place because it was seen that a MODIFY
// event was sent after the DELETE. This ignores that MODIFY and
// assumes a DELETE will come or has come if the file doesn't exist.
if !(e.Op&Remove == Remove || e.Op&Rename == Rename) {
_, statErr := os.Lstat(e.Name)
return os.IsNotExist(statErr)
}
return false
}
// newEvent returns an platform-independent Event based on an inotify mask.
func newEvent(name string, mask uint32) Event {
e := Event{Name: name}
if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO {
e.Op |= Create
}
if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE {
e.Op |= Remove
}
if mask&unix.IN_MODIFY == unix.IN_MODIFY {
e.Op |= Write
}
if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM {
e.Op |= Rename
}
if mask&unix.IN_ATTRIB == unix.IN_ATTRIB {
e.Op |= Chmod
}
return e
}

187
vendor/github.com/fsnotify/fsnotify/inotify_poller.go generated vendored Normal file
View File

@ -0,0 +1,187 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux
package fsnotify
import (
"errors"
"golang.org/x/sys/unix"
)
type fdPoller struct {
fd int // File descriptor (as returned by the inotify_init() syscall)
epfd int // Epoll file descriptor
pipe [2]int // Pipe for waking up
}
func emptyPoller(fd int) *fdPoller {
poller := new(fdPoller)
poller.fd = fd
poller.epfd = -1
poller.pipe[0] = -1
poller.pipe[1] = -1
return poller
}
// Create a new inotify poller.
// This creates an inotify handler, and an epoll handler.
func newFdPoller(fd int) (*fdPoller, error) {
var errno error
poller := emptyPoller(fd)
defer func() {
if errno != nil {
poller.close()
}
}()
poller.fd = fd
// Create epoll fd
poller.epfd, errno = unix.EpollCreate1(0)
if poller.epfd == -1 {
return nil, errno
}
// Create pipe; pipe[0] is the read end, pipe[1] the write end.
errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK)
if errno != nil {
return nil, errno
}
// Register inotify fd with epoll
event := unix.EpollEvent{
Fd: int32(poller.fd),
Events: unix.EPOLLIN,
}
errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event)
if errno != nil {
return nil, errno
}
// Register pipe fd with epoll
event = unix.EpollEvent{
Fd: int32(poller.pipe[0]),
Events: unix.EPOLLIN,
}
errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event)
if errno != nil {
return nil, errno
}
return poller, nil
}
// Wait using epoll.
// Returns true if something is ready to be read,
// false if there is not.
func (poller *fdPoller) wait() (bool, error) {
// 3 possible events per fd, and 2 fds, makes a maximum of 6 events.
// I don't know whether epoll_wait returns the number of events returned,
// or the total number of events ready.
// I decided to catch both by making the buffer one larger than the maximum.
events := make([]unix.EpollEvent, 7)
for {
n, errno := unix.EpollWait(poller.epfd, events, -1)
if n == -1 {
if errno == unix.EINTR {
continue
}
return false, errno
}
if n == 0 {
// If there are no events, try again.
continue
}
if n > 6 {
// This should never happen. More events were returned than should be possible.
return false, errors.New("epoll_wait returned more events than I know what to do with")
}
ready := events[:n]
epollhup := false
epollerr := false
epollin := false
for _, event := range ready {
if event.Fd == int32(poller.fd) {
if event.Events&unix.EPOLLHUP != 0 {
// This should not happen, but if it does, treat it as a wakeup.
epollhup = true
}
if event.Events&unix.EPOLLERR != 0 {
// If an error is waiting on the file descriptor, we should pretend
// something is ready to read, and let unix.Read pick up the error.
epollerr = true
}
if event.Events&unix.EPOLLIN != 0 {
// There is data to read.
epollin = true
}
}
if event.Fd == int32(poller.pipe[0]) {
if event.Events&unix.EPOLLHUP != 0 {
// Write pipe descriptor was closed, by us. This means we're closing down the
// watcher, and we should wake up.
}
if event.Events&unix.EPOLLERR != 0 {
// If an error is waiting on the pipe file descriptor.
// This is an absolute mystery, and should never ever happen.
return false, errors.New("Error on the pipe descriptor.")
}
if event.Events&unix.EPOLLIN != 0 {
// This is a regular wakeup, so we have to clear the buffer.
err := poller.clearWake()
if err != nil {
return false, err
}
}
}
}
if epollhup || epollerr || epollin {
return true, nil
}
return false, nil
}
}
// Close the write end of the poller.
func (poller *fdPoller) wake() error {
buf := make([]byte, 1)
n, errno := unix.Write(poller.pipe[1], buf)
if n == -1 {
if errno == unix.EAGAIN {
// Buffer is full, poller will wake.
return nil
}
return errno
}
return nil
}
func (poller *fdPoller) clearWake() error {
// You have to be woken up a LOT in order to get to 100!
buf := make([]byte, 100)
n, errno := unix.Read(poller.pipe[0], buf)
if n == -1 {
if errno == unix.EAGAIN {
// Buffer is empty, someone else cleared our wake.
return nil
}
return errno
}
return nil
}
// Close all poller file descriptors, but not the one passed to it.
func (poller *fdPoller) close() {
if poller.pipe[1] != -1 {
unix.Close(poller.pipe[1])
}
if poller.pipe[0] != -1 {
unix.Close(poller.pipe[0])
}
if poller.epfd != -1 {
unix.Close(poller.epfd)
}
}

521
vendor/github.com/fsnotify/fsnotify/kqueue.go generated vendored Normal file
View File

@ -0,0 +1,521 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build freebsd openbsd netbsd dragonfly darwin
package fsnotify
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sync"
"time"
"golang.org/x/sys/unix"
)
// Watcher watches a set of files, delivering events to a channel.
type Watcher struct {
Events chan Event
Errors chan error
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
kq int // File descriptor (as returned by the kqueue() syscall).
mu sync.Mutex // Protects access to watcher data
watches map[string]int // Map of watched file descriptors (key: path).
externalWatches map[string]bool // Map of watches added by user of the library.
dirFlags map[string]uint32 // Map of watched directories to fflags used in kqueue.
paths map[int]pathInfo // Map file descriptors to path names for processing kqueue events.
fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events).
isClosed bool // Set to true when Close() is first called
}
type pathInfo struct {
name string
isDir bool
}
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
func NewWatcher() (*Watcher, error) {
kq, err := kqueue()
if err != nil {
return nil, err
}
w := &Watcher{
kq: kq,
watches: make(map[string]int),
dirFlags: make(map[string]uint32),
paths: make(map[int]pathInfo),
fileExists: make(map[string]bool),
externalWatches: make(map[string]bool),
Events: make(chan Event),
Errors: make(chan error),
done: make(chan struct{}),
}
go w.readEvents()
return w, nil
}
// Close removes all watches and closes the events channel.
func (w *Watcher) Close() error {
w.mu.Lock()
if w.isClosed {
w.mu.Unlock()
return nil
}
w.isClosed = true
// copy paths to remove while locked
var pathsToRemove = make([]string, 0, len(w.watches))
for name := range w.watches {
pathsToRemove = append(pathsToRemove, name)
}
w.mu.Unlock()
// unlock before calling Remove, which also locks
for _, name := range pathsToRemove {
w.Remove(name)
}
// send a "quit" message to the reader goroutine
close(w.done)
return nil
}
// Add starts watching the named file or directory (non-recursively).
func (w *Watcher) Add(name string) error {
w.mu.Lock()
w.externalWatches[name] = true
w.mu.Unlock()
_, err := w.addWatch(name, noteAllEvents)
return err
}
// Remove stops watching the the named file or directory (non-recursively).
func (w *Watcher) Remove(name string) error {
name = filepath.Clean(name)
w.mu.Lock()
watchfd, ok := w.watches[name]
w.mu.Unlock()
if !ok {
return fmt.Errorf("can't remove non-existent kevent watch for: %s", name)
}
const registerRemove = unix.EV_DELETE
if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil {
return err
}
unix.Close(watchfd)
w.mu.Lock()
isDir := w.paths[watchfd].isDir
delete(w.watches, name)
delete(w.paths, watchfd)
delete(w.dirFlags, name)
w.mu.Unlock()
// Find all watched paths that are in this directory that are not external.
if isDir {
var pathsToRemove []string
w.mu.Lock()
for _, path := range w.paths {
wdir, _ := filepath.Split(path.name)
if filepath.Clean(wdir) == name {
if !w.externalWatches[path.name] {
pathsToRemove = append(pathsToRemove, path.name)
}
}
}
w.mu.Unlock()
for _, name := range pathsToRemove {
// Since these are internal, not much sense in propagating error
// to the user, as that will just confuse them with an error about
// a path they did not explicitly watch themselves.
w.Remove(name)
}
}
return nil
}
// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
// keventWaitTime to block on each read from kevent
var keventWaitTime = durationToTimespec(100 * time.Millisecond)
// addWatch adds name to the watched file set.
// The flags are interpreted as described in kevent(2).
// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks.
func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
var isDir bool
// Make ./name and name equivalent
name = filepath.Clean(name)
w.mu.Lock()
if w.isClosed {
w.mu.Unlock()
return "", errors.New("kevent instance already closed")
}
watchfd, alreadyWatching := w.watches[name]
// We already have a watch, but we can still override flags.
if alreadyWatching {
isDir = w.paths[watchfd].isDir
}
w.mu.Unlock()
if !alreadyWatching {
fi, err := os.Lstat(name)
if err != nil {
return "", err
}
// Don't watch sockets.
if fi.Mode()&os.ModeSocket == os.ModeSocket {
return "", nil
}
// Don't watch named pipes.
if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe {
return "", nil
}
// Follow Symlinks
// Unfortunately, Linux can add bogus symlinks to watch list without
// issue, and Windows can't do symlinks period (AFAIK). To maintain
// consistency, we will act like everything is fine. There will simply
// be no file events for broken symlinks.
// Hence the returns of nil on errors.
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
name, err = filepath.EvalSymlinks(name)
if err != nil {
return "", nil
}
w.mu.Lock()
_, alreadyWatching = w.watches[name]
w.mu.Unlock()
if alreadyWatching {
return name, nil
}
fi, err = os.Lstat(name)
if err != nil {
return "", nil
}
}
watchfd, err = unix.Open(name, openMode, 0700)
if watchfd == -1 {
return "", err
}
isDir = fi.IsDir()
}
const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE
if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil {
unix.Close(watchfd)
return "", err
}
if !alreadyWatching {
w.mu.Lock()
w.watches[name] = watchfd
w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
w.mu.Unlock()
}
if isDir {
// Watch the directory if it has not been watched before,
// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
w.mu.Lock()
watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
(!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE)
// Store flags so this watch can be updated later
w.dirFlags[name] = flags
w.mu.Unlock()
if watchDir {
if err := w.watchDirectoryFiles(name); err != nil {
return "", err
}
}
}
return name, nil
}
// readEvents reads from kqueue and converts the received kevents into
// Event values that it sends down the Events channel.
func (w *Watcher) readEvents() {
eventBuffer := make([]unix.Kevent_t, 10)
loop:
for {
// See if there is a message on the "done" channel
select {
case <-w.done:
break loop
default:
}
// Get new events
kevents, err := read(w.kq, eventBuffer, &keventWaitTime)
// EINTR is okay, the syscall was interrupted before timeout expired.
if err != nil && err != unix.EINTR {
select {
case w.Errors <- err:
case <-w.done:
break loop
}
continue
}
// Flush the events we received to the Events channel
for len(kevents) > 0 {
kevent := &kevents[0]
watchfd := int(kevent.Ident)
mask := uint32(kevent.Fflags)
w.mu.Lock()
path := w.paths[watchfd]
w.mu.Unlock()
event := newEvent(path.name, mask)
if path.isDir && !(event.Op&Remove == Remove) {
// Double check to make sure the directory exists. This can happen when
// we do a rm -fr on a recursively watched folders and we receive a
// modification event first but the folder has been deleted and later
// receive the delete event
if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
// mark is as delete event
event.Op |= Remove
}
}
if event.Op&Rename == Rename || event.Op&Remove == Remove {
w.Remove(event.Name)
w.mu.Lock()
delete(w.fileExists, event.Name)
w.mu.Unlock()
}
if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) {
w.sendDirectoryChangeEvents(event.Name)
} else {
// Send the event on the Events channel.
select {
case w.Events <- event:
case <-w.done:
break loop
}
}
if event.Op&Remove == Remove {
// Look for a file that may have overwritten this.
// For example, mv f1 f2 will delete f2, then create f2.
if path.isDir {
fileDir := filepath.Clean(event.Name)
w.mu.Lock()
_, found := w.watches[fileDir]
w.mu.Unlock()
if found {
// make sure the directory exists before we watch for changes. When we
// do a recursive watch and perform rm -fr, the parent directory might
// have gone missing, ignore the missing directory and let the
// upcoming delete event remove the watch from the parent directory.
if _, err := os.Lstat(fileDir); err == nil {
w.sendDirectoryChangeEvents(fileDir)
}
}
} else {
filePath := filepath.Clean(event.Name)
if fileInfo, err := os.Lstat(filePath); err == nil {
w.sendFileCreatedEventIfNew(filePath, fileInfo)
}
}
}
// Move to next event
kevents = kevents[1:]
}
}
// cleanup
err := unix.Close(w.kq)
if err != nil {
// only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors.
select {
case w.Errors <- err:
default:
}
}
close(w.Events)
close(w.Errors)
}
// newEvent returns an platform-independent Event based on kqueue Fflags.
func newEvent(name string, mask uint32) Event {
e := Event{Name: name}
if mask&unix.NOTE_DELETE == unix.NOTE_DELETE {
e.Op |= Remove
}
if mask&unix.NOTE_WRITE == unix.NOTE_WRITE {
e.Op |= Write
}
if mask&unix.NOTE_RENAME == unix.NOTE_RENAME {
e.Op |= Rename
}
if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
e.Op |= Chmod
}
return e
}
func newCreateEvent(name string) Event {
return Event{Name: name, Op: Create}
}
// watchDirectoryFiles to mimic inotify when adding a watch on a directory
func (w *Watcher) watchDirectoryFiles(dirPath string) error {
// Get all files
files, err := ioutil.ReadDir(dirPath)
if err != nil {
return err
}
for _, fileInfo := range files {
filePath := filepath.Join(dirPath, fileInfo.Name())
filePath, err = w.internalWatch(filePath, fileInfo)
if err != nil {
return err
}
w.mu.Lock()
w.fileExists[filePath] = true
w.mu.Unlock()
}
return nil
}
// sendDirectoryEvents searches the directory for newly created files
// and sends them over the event channel. This functionality is to have
// the BSD version of fsnotify match Linux inotify which provides a
// create event for files created in a watched directory.
func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
// Get all files
files, err := ioutil.ReadDir(dirPath)
if err != nil {
select {
case w.Errors <- err:
case <-w.done:
return
}
}
// Search for new files
for _, fileInfo := range files {
filePath := filepath.Join(dirPath, fileInfo.Name())
err := w.sendFileCreatedEventIfNew(filePath, fileInfo)
if err != nil {
return
}
}
}
// sendFileCreatedEvent sends a create event if the file isn't already being tracked.
func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) {
w.mu.Lock()
_, doesExist := w.fileExists[filePath]
w.mu.Unlock()
if !doesExist {
// Send create event
select {
case w.Events <- newCreateEvent(filePath):
case <-w.done:
return
}
}
// like watchDirectoryFiles (but without doing another ReadDir)
filePath, err = w.internalWatch(filePath, fileInfo)
if err != nil {
return err
}
w.mu.Lock()
w.fileExists[filePath] = true
w.mu.Unlock()
return nil
}
func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) {
if fileInfo.IsDir() {
// mimic Linux providing delete events for subdirectories
// but preserve the flags used if currently watching subdirectory
w.mu.Lock()
flags := w.dirFlags[name]
w.mu.Unlock()
flags |= unix.NOTE_DELETE | unix.NOTE_RENAME
return w.addWatch(name, flags)
}
// watch file to mimic Linux inotify
return w.addWatch(name, noteAllEvents)
}
// kqueue creates a new kernel event queue and returns a descriptor.
func kqueue() (kq int, err error) {
kq, err = unix.Kqueue()
if kq == -1 {
return kq, err
}
return kq, nil
}
// register events with the queue
func register(kq int, fds []int, flags int, fflags uint32) error {
changes := make([]unix.Kevent_t, len(fds))
for i, fd := range fds {
// SetKevent converts int to the platform-specific types:
unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags)
changes[i].Fflags = fflags
}
// register the events
success, err := unix.Kevent(kq, changes, nil, nil)
if success == -1 {
return err
}
return nil
}
// read retrieves pending events, or waits until an event occurs.
// A timeout of nil blocks indefinitely, while 0 polls the queue.
func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) {
n, err := unix.Kevent(kq, nil, events, timeout)
if err != nil {
return nil, err
}
return events[0:n], nil
}
// durationToTimespec prepares a timeout value
func durationToTimespec(d time.Duration) unix.Timespec {
return unix.NsecToTimespec(d.Nanoseconds())
}

View File

@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build dragonfly freebsd netbsd openbsd
// +build freebsd openbsd netbsd dragonfly
package unix
package fsnotify
const ImplementsGetwd = false
import "golang.org/x/sys/unix"
func Getwd() (string, error) { return "", ENOTSUP }
const openMode = unix.O_NONBLOCK | unix.O_RDONLY

View File

@ -0,0 +1,12 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin
package fsnotify
import "golang.org/x/sys/unix"
// note: this constant is not defined on BSD
const openMode = unix.O_EVTONLY

561
vendor/github.com/fsnotify/fsnotify/windows.go generated vendored Normal file
View File

@ -0,0 +1,561 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows
package fsnotify
import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"sync"
"syscall"
"unsafe"
)
// Watcher watches a set of files, delivering events to a channel.
type Watcher struct {
Events chan Event
Errors chan error
isClosed bool // Set to true when Close() is first called
mu sync.Mutex // Map access
port syscall.Handle // Handle to completion port
watches watchMap // Map of watches (key: i-number)
input chan *input // Inputs to the reader are sent on this channel
quit chan chan<- error
}
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
func NewWatcher() (*Watcher, error) {
port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
if e != nil {
return nil, os.NewSyscallError("CreateIoCompletionPort", e)
}
w := &Watcher{
port: port,
watches: make(watchMap),
input: make(chan *input, 1),
Events: make(chan Event, 50),
Errors: make(chan error),
quit: make(chan chan<- error, 1),
}
go w.readEvents()
return w, nil
}
// Close removes all watches and closes the events channel.
func (w *Watcher) Close() error {
if w.isClosed {
return nil
}
w.isClosed = true
// Send "quit" message to the reader goroutine
ch := make(chan error)
w.quit <- ch
if err := w.wakeupReader(); err != nil {
return err
}
return <-ch
}
// Add starts watching the named file or directory (non-recursively).
func (w *Watcher) Add(name string) error {
if w.isClosed {
return errors.New("watcher already closed")
}
in := &input{
op: opAddWatch,
path: filepath.Clean(name),
flags: sysFSALLEVENTS,
reply: make(chan error),
}
w.input <- in
if err := w.wakeupReader(); err != nil {
return err
}
return <-in.reply
}
// Remove stops watching the the named file or directory (non-recursively).
func (w *Watcher) Remove(name string) error {
in := &input{
op: opRemoveWatch,
path: filepath.Clean(name),
reply: make(chan error),
}
w.input <- in
if err := w.wakeupReader(); err != nil {
return err
}
return <-in.reply
}
const (
// Options for AddWatch
sysFSONESHOT = 0x80000000
sysFSONLYDIR = 0x1000000
// Events
sysFSACCESS = 0x1
sysFSALLEVENTS = 0xfff
sysFSATTRIB = 0x4
sysFSCLOSE = 0x18
sysFSCREATE = 0x100
sysFSDELETE = 0x200
sysFSDELETESELF = 0x400
sysFSMODIFY = 0x2
sysFSMOVE = 0xc0
sysFSMOVEDFROM = 0x40
sysFSMOVEDTO = 0x80
sysFSMOVESELF = 0x800
// Special events
sysFSIGNORED = 0x8000
sysFSQOVERFLOW = 0x4000
)
func newEvent(name string, mask uint32) Event {
e := Event{Name: name}
if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO {
e.Op |= Create
}
if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF {
e.Op |= Remove
}
if mask&sysFSMODIFY == sysFSMODIFY {
e.Op |= Write
}
if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
e.Op |= Rename
}
if mask&sysFSATTRIB == sysFSATTRIB {
e.Op |= Chmod
}
return e
}
const (
opAddWatch = iota
opRemoveWatch
)
const (
provisional uint64 = 1 << (32 + iota)
)
type input struct {
op int
path string
flags uint32
reply chan error
}
type inode struct {
handle syscall.Handle
volume uint32
index uint64
}
type watch struct {
ov syscall.Overlapped
ino *inode // i-number
path string // Directory path
mask uint64 // Directory itself is being watched with these notify flags
names map[string]uint64 // Map of names being watched and their notify flags
rename string // Remembers the old name while renaming a file
buf [4096]byte
}
type indexMap map[uint64]*watch
type watchMap map[uint32]indexMap
func (w *Watcher) wakeupReader() error {
e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
if e != nil {
return os.NewSyscallError("PostQueuedCompletionStatus", e)
}
return nil
}
func getDir(pathname string) (dir string, err error) {
attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
if e != nil {
return "", os.NewSyscallError("GetFileAttributes", e)
}
if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
dir = pathname
} else {
dir, _ = filepath.Split(pathname)
dir = filepath.Clean(dir)
}
return
}
func getIno(path string) (ino *inode, err error) {
h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),
syscall.FILE_LIST_DIRECTORY,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
nil, syscall.OPEN_EXISTING,
syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)
if e != nil {
return nil, os.NewSyscallError("CreateFile", e)
}
var fi syscall.ByHandleFileInformation
if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {
syscall.CloseHandle(h)
return nil, os.NewSyscallError("GetFileInformationByHandle", e)
}
ino = &inode{
handle: h,
volume: fi.VolumeSerialNumber,
index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
}
return ino, nil
}
// Must run within the I/O thread.
func (m watchMap) get(ino *inode) *watch {
if i := m[ino.volume]; i != nil {
return i[ino.index]
}
return nil
}
// Must run within the I/O thread.
func (m watchMap) set(ino *inode, watch *watch) {
i := m[ino.volume]
if i == nil {
i = make(indexMap)
m[ino.volume] = i
}
i[ino.index] = watch
}
// Must run within the I/O thread.
func (w *Watcher) addWatch(pathname string, flags uint64) error {
dir, err := getDir(pathname)
if err != nil {
return err
}
if flags&sysFSONLYDIR != 0 && pathname != dir {
return nil
}
ino, err := getIno(dir)
if err != nil {
return err
}
w.mu.Lock()
watchEntry := w.watches.get(ino)
w.mu.Unlock()
if watchEntry == nil {
if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {
syscall.CloseHandle(ino.handle)
return os.NewSyscallError("CreateIoCompletionPort", e)
}
watchEntry = &watch{
ino: ino,
path: dir,
names: make(map[string]uint64),
}
w.mu.Lock()
w.watches.set(ino, watchEntry)
w.mu.Unlock()
flags |= provisional
} else {
syscall.CloseHandle(ino.handle)
}
if pathname == dir {
watchEntry.mask |= flags
} else {
watchEntry.names[filepath.Base(pathname)] |= flags
}
if err = w.startRead(watchEntry); err != nil {
return err
}
if pathname == dir {
watchEntry.mask &= ^provisional
} else {
watchEntry.names[filepath.Base(pathname)] &= ^provisional
}
return nil
}
// Must run within the I/O thread.
func (w *Watcher) remWatch(pathname string) error {
dir, err := getDir(pathname)
if err != nil {
return err
}
ino, err := getIno(dir)
if err != nil {
return err
}
w.mu.Lock()
watch := w.watches.get(ino)
w.mu.Unlock()
if watch == nil {
return fmt.Errorf("can't remove non-existent watch for: %s", pathname)
}
if pathname == dir {
w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
watch.mask = 0
} else {
name := filepath.Base(pathname)
w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED)
delete(watch.names, name)
}
return w.startRead(watch)
}
// Must run within the I/O thread.
func (w *Watcher) deleteWatch(watch *watch) {
for name, mask := range watch.names {
if mask&provisional == 0 {
w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED)
}
delete(watch.names, name)
}
if watch.mask != 0 {
if watch.mask&provisional == 0 {
w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
}
watch.mask = 0
}
}
// Must run within the I/O thread.
func (w *Watcher) startRead(watch *watch) error {
if e := syscall.CancelIo(watch.ino.handle); e != nil {
w.Errors <- os.NewSyscallError("CancelIo", e)
w.deleteWatch(watch)
}
mask := toWindowsFlags(watch.mask)
for _, m := range watch.names {
mask |= toWindowsFlags(m)
}
if mask == 0 {
if e := syscall.CloseHandle(watch.ino.handle); e != nil {
w.Errors <- os.NewSyscallError("CloseHandle", e)
}
w.mu.Lock()
delete(w.watches[watch.ino.volume], watch.ino.index)
w.mu.Unlock()
return nil
}
e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
if e != nil {
err := os.NewSyscallError("ReadDirectoryChanges", e)
if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
// Watched directory was probably removed
if w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) {
if watch.mask&sysFSONESHOT != 0 {
watch.mask = 0
}
}
err = nil
}
w.deleteWatch(watch)
w.startRead(watch)
return err
}
return nil
}
// readEvents reads from the I/O completion port, converts the
// received events into Event objects and sends them via the Events channel.
// Entry point to the I/O thread.
func (w *Watcher) readEvents() {
var (
n, key uint32
ov *syscall.Overlapped
)
runtime.LockOSThread()
for {
e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)
watch := (*watch)(unsafe.Pointer(ov))
if watch == nil {
select {
case ch := <-w.quit:
w.mu.Lock()
var indexes []indexMap
for _, index := range w.watches {
indexes = append(indexes, index)
}
w.mu.Unlock()
for _, index := range indexes {
for _, watch := range index {
w.deleteWatch(watch)
w.startRead(watch)
}
}
var err error
if e := syscall.CloseHandle(w.port); e != nil {
err = os.NewSyscallError("CloseHandle", e)
}
close(w.Events)
close(w.Errors)
ch <- err
return
case in := <-w.input:
switch in.op {
case opAddWatch:
in.reply <- w.addWatch(in.path, uint64(in.flags))
case opRemoveWatch:
in.reply <- w.remWatch(in.path)
}
default:
}
continue
}
switch e {
case syscall.ERROR_MORE_DATA:
if watch == nil {
w.Errors <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")
} else {
// The i/o succeeded but the buffer is full.
// In theory we should be building up a full packet.
// In practice we can get away with just carrying on.
n = uint32(unsafe.Sizeof(watch.buf))
}
case syscall.ERROR_ACCESS_DENIED:
// Watched directory was probably removed
w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
w.deleteWatch(watch)
w.startRead(watch)
continue
case syscall.ERROR_OPERATION_ABORTED:
// CancelIo was called on this handle
continue
default:
w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e)
continue
case nil:
}
var offset uint32
for {
if n == 0 {
w.Events <- newEvent("", sysFSQOVERFLOW)
w.Errors <- errors.New("short read in readEvents()")
break
}
// Point "raw" to the event in the buffer
raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
fullname := filepath.Join(watch.path, name)
var mask uint64
switch raw.Action {
case syscall.FILE_ACTION_REMOVED:
mask = sysFSDELETESELF
case syscall.FILE_ACTION_MODIFIED:
mask = sysFSMODIFY
case syscall.FILE_ACTION_RENAMED_OLD_NAME:
watch.rename = name
case syscall.FILE_ACTION_RENAMED_NEW_NAME:
if watch.names[watch.rename] != 0 {
watch.names[name] |= watch.names[watch.rename]
delete(watch.names, watch.rename)
mask = sysFSMOVESELF
}
}
sendNameEvent := func() {
if w.sendEvent(fullname, watch.names[name]&mask) {
if watch.names[name]&sysFSONESHOT != 0 {
delete(watch.names, name)
}
}
}
if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {
sendNameEvent()
}
if raw.Action == syscall.FILE_ACTION_REMOVED {
w.sendEvent(fullname, watch.names[name]&sysFSIGNORED)
delete(watch.names, name)
}
if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {
if watch.mask&sysFSONESHOT != 0 {
watch.mask = 0
}
}
if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
fullname = filepath.Join(watch.path, watch.rename)
sendNameEvent()
}
// Move to the next event in the buffer
if raw.NextEntryOffset == 0 {
break
}
offset += raw.NextEntryOffset
// Error!
if offset >= n {
w.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.")
break
}
}
if err := w.startRead(watch); err != nil {
w.Errors <- err
}
}
}
func (w *Watcher) sendEvent(name string, mask uint64) bool {
if mask == 0 {
return false
}
event := newEvent(name, uint32(mask))
select {
case ch := <-w.quit:
w.quit <- ch
case w.Events <- event:
}
return true
}
func toWindowsFlags(mask uint64) uint32 {
var m uint32
if mask&sysFSACCESS != 0 {
m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS
}
if mask&sysFSMODIFY != 0 {
m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE
}
if mask&sysFSATTRIB != 0 {
m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES
}
if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME
}
return m
}
func toFSnotifyFlags(action uint32) uint64 {
switch action {
case syscall.FILE_ACTION_ADDED:
return sysFSCREATE
case syscall.FILE_ACTION_REMOVED:
return sysFSDELETE
case syscall.FILE_ACTION_MODIFIED:
return sysFSMODIFY
case syscall.FILE_ACTION_RENAMED_OLD_NAME:
return sysFSMOVEDFROM
case syscall.FILE_ACTION_RENAMED_NEW_NAME:
return sysFSMOVEDTO
}
return 0
}

View File

@ -1,77 +0,0 @@
/*
To build the snappytool binary:
g++ main.cpp /usr/lib/libsnappy.a -o snappytool
or, if you have built the C++ snappy library from source:
g++ main.cpp /path/to/your/snappy/.libs/libsnappy.a -o snappytool
after running "make" from your snappy checkout directory.
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "snappy.h"
#define N 1000000
char dst[N];
char src[N];
int main(int argc, char** argv) {
// Parse args.
if (argc != 2) {
fprintf(stderr, "exactly one of -d or -e must be given\n");
return 1;
}
bool decode = strcmp(argv[1], "-d") == 0;
bool encode = strcmp(argv[1], "-e") == 0;
if (decode == encode) {
fprintf(stderr, "exactly one of -d or -e must be given\n");
return 1;
}
// Read all of stdin into src[:s].
size_t s = 0;
while (1) {
if (s == N) {
fprintf(stderr, "input too large\n");
return 1;
}
ssize_t n = read(0, src+s, N-s);
if (n == 0) {
break;
}
if (n < 0) {
fprintf(stderr, "read error: %s\n", strerror(errno));
// TODO: handle EAGAIN, EINTR?
return 1;
}
s += n;
}
// Encode or decode src[:s] to dst[:d], and write to stdout.
size_t d = 0;
if (encode) {
if (N < snappy::MaxCompressedLength(s)) {
fprintf(stderr, "input too large after encoding\n");
return 1;
}
snappy::RawCompress(src, s, dst, &d);
} else {
if (!snappy::GetUncompressedLength(src, s, &d)) {
fprintf(stderr, "could not get uncompressed length\n");
return 1;
}
if (N < d) {
fprintf(stderr, "input too large after decoding\n");
return 1;
}
if (!snappy::RawUncompress(src, s, dst)) {
fprintf(stderr, "input was not valid Snappy-compressed data\n");
return 1;
}
}
write(1, dst, d);
return 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,396 +0,0 @@
Produced by David Widger. The previous edition was updated by Jose
Menendez.
THE ADVENTURES OF TOM SAWYER
BY
MARK TWAIN
(Samuel Langhorne Clemens)
P R E F A C E
MOST of the adventures recorded in this book really occurred; one or
two were experiences of my own, the rest those of boys who were
schoolmates of mine. Huck Finn is drawn from life; Tom Sawyer also, but
not from an individual--he is a combination of the characteristics of
three boys whom I knew, and therefore belongs to the composite order of
architecture.
The odd superstitions touched upon were all prevalent among children
and slaves in the West at the period of this story--that is to say,
thirty or forty years ago.
Although my book is intended mainly for the entertainment of boys and
girls, I hope it will not be shunned by men and women on that account,
for part of my plan has been to try to pleasantly remind adults of what
they once were themselves, and of how they felt and thought and talked,
and what queer enterprises they sometimes engaged in.
THE AUTHOR.
HARTFORD, 1876.
T O M S A W Y E R
CHAPTER I
"TOM!"
No answer.
"TOM!"
No answer.
"What's gone with that boy, I wonder? You TOM!"
No answer.
The old lady pulled her spectacles down and looked over them about the
room; then she put them up and looked out under them. She seldom or
never looked THROUGH them for so small a thing as a boy; they were her
state pair, the pride of her heart, and were built for "style," not
service--she could have seen through a pair of stove-lids just as well.
She looked perplexed for a moment, and then said, not fiercely, but
still loud enough for the furniture to hear:
"Well, I lay if I get hold of you I'll--"
She did not finish, for by this time she was bending down and punching
under the bed with the broom, and so she needed breath to punctuate the
punches with. She resurrected nothing but the cat.
"I never did see the beat of that boy!"
She went to the open door and stood in it and looked out among the
tomato vines and "jimpson" weeds that constituted the garden. No Tom.
So she lifted up her voice at an angle calculated for distance and
shouted:
"Y-o-u-u TOM!"
There was a slight noise behind her and she turned just in time to
seize a small boy by the slack of his roundabout and arrest his flight.
"There! I might 'a' thought of that closet. What you been doing in
there?"
"Nothing."
"Nothing! Look at your hands. And look at your mouth. What IS that
truck?"
"I don't know, aunt."
"Well, I know. It's jam--that's what it is. Forty times I've said if
you didn't let that jam alone I'd skin you. Hand me that switch."
The switch hovered in the air--the peril was desperate--
"My! Look behind you, aunt!"
The old lady whirled round, and snatched her skirts out of danger. The
lad fled on the instant, scrambled up the high board-fence, and
disappeared over it.
His aunt Polly stood surprised a moment, and then broke into a gentle
laugh.
"Hang the boy, can't I never learn anything? Ain't he played me tricks
enough like that for me to be looking out for him by this time? But old
fools is the biggest fools there is. Can't learn an old dog new tricks,
as the saying is. But my goodness, he never plays them alike, two days,
and how is a body to know what's coming? He 'pears to know just how
long he can torment me before I get my dander up, and he knows if he
can make out to put me off for a minute or make me laugh, it's all down
again and I can't hit him a lick. I ain't doing my duty by that boy,
and that's the Lord's truth, goodness knows. Spare the rod and spile
the child, as the Good Book says. I'm a laying up sin and suffering for
us both, I know. He's full of the Old Scratch, but laws-a-me! he's my
own dead sister's boy, poor thing, and I ain't got the heart to lash
him, somehow. Every time I let him off, my conscience does hurt me so,
and every time I hit him my old heart most breaks. Well-a-well, man
that is born of woman is of few days and full of trouble, as the
Scripture says, and I reckon it's so. He'll play hookey this evening, *
and [* Southwestern for "afternoon"] I'll just be obleeged to make him
work, to-morrow, to punish him. It's mighty hard to make him work
Saturdays, when all the boys is having holiday, but he hates work more
than he hates anything else, and I've GOT to do some of my duty by him,
or I'll be the ruination of the child."
Tom did play hookey, and he had a very good time. He got back home
barely in season to help Jim, the small colored boy, saw next-day's
wood and split the kindlings before supper--at least he was there in
time to tell his adventures to Jim while Jim did three-fourths of the
work. Tom's younger brother (or rather half-brother) Sid was already
through with his part of the work (picking up chips), for he was a
quiet boy, and had no adventurous, troublesome ways.
While Tom was eating his supper, and stealing sugar as opportunity
offered, Aunt Polly asked him questions that were full of guile, and
very deep--for she wanted to trap him into damaging revealments. Like
many other simple-hearted souls, it was her pet vanity to believe she
was endowed with a talent for dark and mysterious diplomacy, and she
loved to contemplate her most transparent devices as marvels of low
cunning. Said she:
"Tom, it was middling warm in school, warn't it?"
"Yes'm."
"Powerful warm, warn't it?"
"Yes'm."
"Didn't you want to go in a-swimming, Tom?"
A bit of a scare shot through Tom--a touch of uncomfortable suspicion.
He searched Aunt Polly's face, but it told him nothing. So he said:
"No'm--well, not very much."
The old lady reached out her hand and felt Tom's shirt, and said:
"But you ain't too warm now, though." And it flattered her to reflect
that she had discovered that the shirt was dry without anybody knowing
that that was what she had in her mind. But in spite of her, Tom knew
where the wind lay, now. So he forestalled what might be the next move:
"Some of us pumped on our heads--mine's damp yet. See?"
Aunt Polly was vexed to think she had overlooked that bit of
circumstantial evidence, and missed a trick. Then she had a new
inspiration:
"Tom, you didn't have to undo your shirt collar where I sewed it, to
pump on your head, did you? Unbutton your jacket!"
The trouble vanished out of Tom's face. He opened his jacket. His
shirt collar was securely sewed.
"Bother! Well, go 'long with you. I'd made sure you'd played hookey
and been a-swimming. But I forgive ye, Tom. I reckon you're a kind of a
singed cat, as the saying is--better'n you look. THIS time."
She was half sorry her sagacity had miscarried, and half glad that Tom
had stumbled into obedient conduct for once.
But Sidney said:
"Well, now, if I didn't think you sewed his collar with white thread,
but it's black."
"Why, I did sew it with white! Tom!"
But Tom did not wait for the rest. As he went out at the door he said:
"Siddy, I'll lick you for that."
In a safe place Tom examined two large needles which were thrust into
the lapels of his jacket, and had thread bound about them--one needle
carried white thread and the other black. He said:
"She'd never noticed if it hadn't been for Sid. Confound it! sometimes
she sews it with white, and sometimes she sews it with black. I wish to
geeminy she'd stick to one or t'other--I can't keep the run of 'em. But
I bet you I'll lam Sid for that. I'll learn him!"
He was not the Model Boy of the village. He knew the model boy very
well though--and loathed him.
Within two minutes, or even less, he had forgotten all his troubles.
Not because his troubles were one whit less heavy and bitter to him
than a man's are to a man, but because a new and powerful interest bore
them down and drove them out of his mind for the time--just as men's
misfortunes are forgotten in the excitement of new enterprises. This
new interest was a valued novelty in whistling, which he had just
acquired from a negro, and he was suffering to practise it undisturbed.
It consisted in a peculiar bird-like turn, a sort of liquid warble,
produced by touching the tongue to the roof of the mouth at short
intervals in the midst of the music--the reader probably remembers how
to do it, if he has ever been a boy. Diligence and attention soon gave
him the knack of it, and he strode down the street with his mouth full
of harmony and his soul full of gratitude. He felt much as an
astronomer feels who has discovered a new planet--no doubt, as far as
strong, deep, unalloyed pleasure is concerned, the advantage was with
the boy, not the astronomer.
The summer evenings were long. It was not dark, yet. Presently Tom
checked his whistle. A stranger was before him--a boy a shade larger
than himself. A new-comer of any age or either sex was an impressive
curiosity in the poor little shabby village of St. Petersburg. This boy
was well dressed, too--well dressed on a week-day. This was simply
astounding. His cap was a dainty thing, his close-buttoned blue cloth
roundabout was new and natty, and so were his pantaloons. He had shoes
on--and it was only Friday. He even wore a necktie, a bright bit of
ribbon. He had a citified air about him that ate into Tom's vitals. The
more Tom stared at the splendid marvel, the higher he turned up his
nose at his finery and the shabbier and shabbier his own outfit seemed
to him to grow. Neither boy spoke. If one moved, the other moved--but
only sidewise, in a circle; they kept face to face and eye to eye all
the time. Finally Tom said:
"I can lick you!"
"I'd like to see you try it."
"Well, I can do it."
"No you can't, either."
"Yes I can."
"No you can't."
"I can."
"You can't."
"Can!"
"Can't!"
An uncomfortable pause. Then Tom said:
"What's your name?"
"'Tisn't any of your business, maybe."
"Well I 'low I'll MAKE it my business."
"Well why don't you?"
"If you say much, I will."
"Much--much--MUCH. There now."
"Oh, you think you're mighty smart, DON'T you? I could lick you with
one hand tied behind me, if I wanted to."
"Well why don't you DO it? You SAY you can do it."
"Well I WILL, if you fool with me."
"Oh yes--I've seen whole families in the same fix."
"Smarty! You think you're SOME, now, DON'T you? Oh, what a hat!"
"You can lump that hat if you don't like it. I dare you to knock it
off--and anybody that'll take a dare will suck eggs."
"You're a liar!"
"You're another."
"You're a fighting liar and dasn't take it up."
"Aw--take a walk!"
"Say--if you give me much more of your sass I'll take and bounce a
rock off'n your head."
"Oh, of COURSE you will."
"Well I WILL."
"Well why don't you DO it then? What do you keep SAYING you will for?
Why don't you DO it? It's because you're afraid."
"I AIN'T afraid."
"You are."
"I ain't."
"You are."
Another pause, and more eying and sidling around each other. Presently
they were shoulder to shoulder. Tom said:
"Get away from here!"
"Go away yourself!"
"I won't."
"I won't either."
So they stood, each with a foot placed at an angle as a brace, and
both shoving with might and main, and glowering at each other with
hate. But neither could get an advantage. After struggling till both
were hot and flushed, each relaxed his strain with watchful caution,
and Tom said:
"You're a coward and a pup. I'll tell my big brother on you, and he
can thrash you with his little finger, and I'll make him do it, too."
"What do I care for your big brother? I've got a brother that's bigger
than he is--and what's more, he can throw him over that fence, too."
[Both brothers were imaginary.]
"That's a lie."
"YOUR saying so don't make it so."
Tom drew a line in the dust with his big toe, and said:
"I dare you to step over that, and I'll lick you till you can't stand
up. Anybody that'll take a dare will steal sheep."
The new boy stepped over promptly, and said:
"Now you said you'd do it, now let's see you do it."
"Don't you crowd me now; you better look out."
"Well, you SAID you'd do it--why don't you do it?"
"By jingo! for two cents I WILL do it."
The new boy took two broad coppers out of his pocket and held them out
with derision. Tom struck them to the ground. In an instant both boys
were rolling and tumbling in the dirt, gripped together like cats; and
for the space of a minute they tugged and tore at each other's hair and
clothes, punched and scratched each other's nose, and covered
themselves with dust and glory. Presently the confusion took form, and
through the fog of battle Tom appeared, seated astride the new boy, and
pounding him with his fists. "Holler 'nuff!" said he.
The boy only struggled to free himself. He was crying--mainly from rage.
"Holler 'nuff!"--and the pounding went on.
At last the stranger got out a smothered "'Nuff!" and Tom let him up
and said:
"Now that'll learn you. Better look out who you're fooling with next
time."
The new boy went off brushing the dust from his clothes, sobbing,
snuffling, and occasionally looking back and shaking his head and
threatening what he would do to Tom the "next time he caught him out."
To which Tom responded with jeers, and started off in high feather, and
as soon as his back was turned the new boy snatched up a stone, threw
it and hit him between the shoulders and then turned tail and ran like
an antelope. Tom chased the traitor home, and thus found out where he
lived. He then held a position at the gate for some time, daring the
enemy to come outside, but the enemy only made faces at him through the
window and declined. At last the enemy's mother appeared, and called
Tom a bad, vicious, vulgar child, and ordered him away. So he went
away; but he said he "'lowed" to "lay" for that boy.
He got home pretty late that night, and when he climbed cautiously in
at the window, he uncovered an ambuscade, in the person of his aunt;
and when she saw the state his clothes were in her resolution to turn
his Saturday holiday into captivity at hard labor became adamantine in
its firmness.

View File

@ -1,94 +0,0 @@
package errwrap
import (
"fmt"
"testing"
)
func TestWrappedError_impl(t *testing.T) {
var _ error = new(wrappedError)
}
func TestGetAll(t *testing.T) {
cases := []struct {
Err error
Msg string
Len int
}{
{},
{
fmt.Errorf("foo"),
"foo",
1,
},
{
fmt.Errorf("bar"),
"foo",
0,
},
{
Wrapf("bar", fmt.Errorf("foo")),
"foo",
1,
},
{
Wrapf("{{err}}", fmt.Errorf("foo")),
"foo",
2,
},
{
Wrapf("bar", Wrapf("baz", fmt.Errorf("foo"))),
"foo",
1,
},
}
for i, tc := range cases {
actual := GetAll(tc.Err, tc.Msg)
if len(actual) != tc.Len {
t.Fatalf("%d: bad: %#v", i, actual)
}
for _, v := range actual {
if v.Error() != tc.Msg {
t.Fatalf("%d: bad: %#v", i, actual)
}
}
}
}
func TestGetAllType(t *testing.T) {
cases := []struct {
Err error
Type interface{}
Len int
}{
{},
{
fmt.Errorf("foo"),
"foo",
0,
},
{
fmt.Errorf("bar"),
fmt.Errorf("foo"),
1,
},
{
Wrapf("bar", fmt.Errorf("foo")),
fmt.Errorf("baz"),
2,
},
{
Wrapf("bar", Wrapf("baz", fmt.Errorf("foo"))),
Wrapf("", nil),
0,
},
}
for i, tc := range cases {
actual := GetAllType(tc.Err, tc.Type)
if len(actual) != tc.Len {
t.Fatalf("%d: bad: %#v", i, actual)
}
}
}

View File

@ -26,6 +26,7 @@ func DefaultPooledTransport() *http.Transport {
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,

43
vendor/github.com/hashicorp/go-cleanhttp/handlers.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
package cleanhttp
import (
"net/http"
"strings"
"unicode"
)
// HandlerInput provides input options to cleanhttp's handlers
type HandlerInput struct {
ErrStatus int
}
// PrintablePathCheckHandler is a middleware that ensures the request path
// contains only printable runes.
func PrintablePathCheckHandler(next http.Handler, input *HandlerInput) http.Handler {
// Nil-check on input to make it optional
if input == nil {
input = &HandlerInput{
ErrStatus: http.StatusBadRequest,
}
}
// Default to http.StatusBadRequest on error
if input.ErrStatus == 0 {
input.ErrStatus = http.StatusBadRequest
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Check URL path for non-printable characters
idx := strings.IndexFunc(r.URL.Path, func(c rune) bool {
return !unicode.IsPrint(c)
})
if idx != -1 {
w.WriteHeader(input.ErrStatus)
return
}
next.ServeHTTP(w, r)
return
})
}

View File

@ -3,7 +3,7 @@ sudo: false
language: go
go:
- 1.6
- 1.x
branches:
only:

View File

@ -1,82 +0,0 @@
package multierror
import (
"errors"
"testing"
)
func TestAppend_Error(t *testing.T) {
original := &Error{
Errors: []error{errors.New("foo")},
}
result := Append(original, errors.New("bar"))
if len(result.Errors) != 2 {
t.Fatalf("wrong len: %d", len(result.Errors))
}
original = &Error{}
result = Append(original, errors.New("bar"))
if len(result.Errors) != 1 {
t.Fatalf("wrong len: %d", len(result.Errors))
}
// Test when a typed nil is passed
var e *Error
result = Append(e, errors.New("baz"))
if len(result.Errors) != 1 {
t.Fatalf("wrong len: %d", len(result.Errors))
}
// Test flattening
original = &Error{
Errors: []error{errors.New("foo")},
}
result = Append(original, Append(nil, errors.New("foo"), errors.New("bar")))
if len(result.Errors) != 3 {
t.Fatalf("wrong len: %d", len(result.Errors))
}
}
func TestAppend_NilError(t *testing.T) {
var err error
result := Append(err, errors.New("bar"))
if len(result.Errors) != 1 {
t.Fatalf("wrong len: %d", len(result.Errors))
}
}
func TestAppend_NilErrorArg(t *testing.T) {
var err error
var nilErr *Error
result := Append(err, nilErr)
if len(result.Errors) != 0 {
t.Fatalf("wrong len: %d", len(result.Errors))
}
}
func TestAppend_NilErrorIfaceArg(t *testing.T) {
var err error
var nilErr error
result := Append(err, nilErr)
if len(result.Errors) != 0 {
t.Fatalf("wrong len: %d", len(result.Errors))
}
}
func TestAppend_NonError(t *testing.T) {
original := errors.New("foo")
result := Append(original, errors.New("bar"))
if len(result.Errors) != 2 {
t.Fatalf("wrong len: %d", len(result.Errors))
}
}
func TestAppend_NonError_Error(t *testing.T) {
original := errors.New("foo")
result := Append(original, Append(nil, errors.New("bar")))
if len(result.Errors) != 2 {
t.Fatalf("wrong len: %d", len(result.Errors))
}
}

View File

@ -1,48 +0,0 @@
package multierror
import (
"errors"
"fmt"
"reflect"
"strings"
"testing"
)
func TestFlatten(t *testing.T) {
original := &Error{
Errors: []error{
errors.New("one"),
&Error{
Errors: []error{
errors.New("two"),
&Error{
Errors: []error{
errors.New("three"),
},
},
},
},
},
}
expected := strings.TrimSpace(`
3 errors occurred:
* one
* two
* three
`)
actual := fmt.Sprintf("%s", Flatten(original))
if expected != actual {
t.Fatalf("expected: %s, got: %s", expected, actual)
}
}
func TestFlatten_nonError(t *testing.T) {
err := errors.New("foo")
actual := Flatten(err)
if !reflect.DeepEqual(actual, err) {
t.Fatalf("bad: %#v", actual)
}
}

View File

@ -1,38 +0,0 @@
package multierror
import (
"errors"
"testing"
)
func TestListFormatFuncSingle(t *testing.T) {
expected := `1 error occurred:
* foo`
errors := []error{
errors.New("foo"),
}
actual := ListFormatFunc(errors)
if actual != expected {
t.Fatalf("bad: %#v", actual)
}
}
func TestListFormatFuncMultiple(t *testing.T) {
expected := `2 errors occurred:
* foo
* bar`
errors := []error{
errors.New("foo"),
errors.New("bar"),
}
actual := ListFormatFunc(errors)
if actual != expected {
t.Fatalf("bad: %#v", actual)
}
}

View File

@ -1,70 +0,0 @@
package multierror
import (
"errors"
"reflect"
"testing"
)
func TestError_Impl(t *testing.T) {
var _ error = new(Error)
}
func TestErrorError_custom(t *testing.T) {
errors := []error{
errors.New("foo"),
errors.New("bar"),
}
fn := func(es []error) string {
return "foo"
}
multi := &Error{Errors: errors, ErrorFormat: fn}
if multi.Error() != "foo" {
t.Fatalf("bad: %s", multi.Error())
}
}
func TestErrorError_default(t *testing.T) {
expected := `2 errors occurred:
* foo
* bar`
errors := []error{
errors.New("foo"),
errors.New("bar"),
}
multi := &Error{Errors: errors}
if multi.Error() != expected {
t.Fatalf("bad: %s", multi.Error())
}
}
func TestErrorErrorOrNil(t *testing.T) {
err := new(Error)
if err.ErrorOrNil() != nil {
t.Fatalf("bad: %#v", err.ErrorOrNil())
}
err.Errors = []error{errors.New("foo")}
if v := err.ErrorOrNil(); v == nil {
t.Fatal("should not be nil")
} else if !reflect.DeepEqual(v, err) {
t.Fatalf("bad: %#v", v)
}
}
func TestErrorWrappedErrors(t *testing.T) {
errors := []error{
errors.New("foo"),
errors.New("bar"),
}
multi := &Error{Errors: errors}
if !reflect.DeepEqual(multi.Errors, multi.WrappedErrors()) {
t.Fatalf("bad: %s", multi.WrappedErrors())
}
}

View File

@ -1,33 +0,0 @@
package multierror
import (
"errors"
"testing"
)
func TestPrefix_Error(t *testing.T) {
original := &Error{
Errors: []error{errors.New("foo")},
}
result := Prefix(original, "bar")
if result.(*Error).Errors[0].Error() != "bar foo" {
t.Fatalf("bad: %s", result)
}
}
func TestPrefix_NilError(t *testing.T) {
var err error
result := Prefix(err, "bar")
if result != nil {
t.Fatalf("bad: %#v", result)
}
}
func TestPrefix_NonError(t *testing.T) {
original := errors.New("foo")
result := Prefix(original, "bar")
if result.Error() != "bar foo" {
t.Fatalf("bad: %s", result)
}
}

View File

@ -1,54 +0,0 @@
#!/usr/bin/env bash
#
# This script updates dependencies using a temporary directory. This is required
# to avoid any auxillary dependencies that sneak into GOPATH.
set -e
# Get the parent directory of where this script is.
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
DIR="$(cd -P "$(dirname "$SOURCE")/.." && pwd)"
# Change into that directory
cd "$DIR"
# Get the name from the directory
NAME=${NAME:-"$(basename $(pwd))"}
# Announce
echo "==> Updating dependencies..."
echo "--> Making tmpdir..."
tmpdir=$(mktemp -d)
function cleanup {
rm -rf "${tmpdir}"
}
trap cleanup EXIT
export GOPATH="${tmpdir}"
export PATH="${tmpdir}/bin:$PATH"
mkdir -p "${tmpdir}/src/github.com/hashicorp"
pushd "${tmpdir}/src/github.com/hashicorp" &>/dev/null
echo "--> Copying ${NAME}..."
cp -R "$DIR" "${tmpdir}/src/github.com/hashicorp/${NAME}"
pushd "${tmpdir}/src/github.com/hashicorp/${NAME}" &>/dev/null
rm -rf vendor/
echo "--> Installing dependency manager..."
go get -u github.com/kardianos/govendor
govendor init
echo "--> Installing all dependencies (may take some time)..."
govendor fetch -v +outside
echo "--> Vendoring..."
govendor add +external
echo "--> Moving into place..."
vpath="${tmpdir}/src/github.com/hashicorp/${NAME}/vendor"
popd &>/dev/null
popd &>/dev/null
rm -rf vendor/
cp -R "${vpath}" .

View File

@ -1,17 +0,0 @@
package rootcerts
import "testing"
func TestSystemCAsOnDarwin(t *testing.T) {
_, err := LoadSystemCAs()
if err != nil {
t.Fatalf("Got error: %s", err)
}
}
func TestCertKeychains(t *testing.T) {
keychains := certKeychains()
if len(keychains) != 3 {
t.Fatalf("Expected 3 keychains, got %#v", keychains)
}
}

View File

@ -1,52 +0,0 @@
package rootcerts
import (
"path/filepath"
"testing"
)
const fixturesDir = "./test-fixtures"
func TestConfigureTLSHandlesNil(t *testing.T) {
err := ConfigureTLS(nil, nil)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestLoadCACertsHandlesNil(t *testing.T) {
_, err := LoadCACerts(nil)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestLoadCACertsFromFile(t *testing.T) {
path := testFixture("cafile", "cacert.pem")
_, err := LoadCACerts(&Config{CAFile: path})
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestLoadCACertsFromDir(t *testing.T) {
path := testFixture("capath")
_, err := LoadCACerts(&Config{CAPath: path})
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestLoadCACertsFromDirWithSymlinks(t *testing.T) {
path := testFixture("capath-with-symlinks")
_, err := LoadCACerts(&Config{CAPath: path})
if err != nil {
t.Fatalf("err: %s", err)
}
}
func testFixture(n ...string) string {
parts := []string{fixturesDir}
parts = append(parts, n...)
return filepath.Join(parts...)
}

View File

@ -1,28 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIExDCCA6ygAwIBAgIJAJ7PV+3kJZqZMA0GCSqGSIb3DQEBBQUAMIGcMQswCQYD
VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xEjAQ
BgNVBAoTCUhhc2hpQ29ycDEUMBIGA1UECxMLRW5naW5lZXJpbmcxGzAZBgNVBAMU
EiouYXRsYXMucGhpbnplLmNvbTEhMB8GCSqGSIb3DQEJARYScGF1bEBoYXNoaWNv
cnAuY29tMB4XDTE2MDQyNzE1MjYyMVoXDTE3MDQyNzE1MjYyMVowgZwxCzAJBgNV
BAYTAlVTMREwDwYDVQQIEwhJbGxpbm9pczEQMA4GA1UEBxMHQ2hpY2FnbzESMBAG
A1UEChMJSGFzaGlDb3JwMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEbMBkGA1UEAxQS
Ki5hdGxhcy5waGluemUuY29tMSEwHwYJKoZIhvcNAQkBFhJwYXVsQGhhc2hpY29y
cC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWRXdMnsTpxpwZ
D2olsun9WO7SnMQ/SIR3DV/fttPIDHSQm2ad4r2pKEuiV+TKEFUgj/Id9bCAfQYs
jsa1qX1GmieXz+83OnK3MDEcDczpjGhSplTYGOjlxKLMhMBAOtdV5hJAYz3nwV3c
R+IQu/4213+em40shZAQRNZ2apnyE3+QB+gPlEs9Nw0OcbSKLmAiuKPbJpO+94ou
n1h0/w/+DPz6yO/fFPoA3vlisGM6B4R9U2JVwWjXrU71fU1i82ulFQdApdfUs1FP
wRrZxgX5ldUrRvFr8lJiMehdX8khO7Ue4rT6yxbI6KVM04Q5mNt1ARRLI69rN9My
pGXiItcxAgMBAAGjggEFMIIBATAdBgNVHQ4EFgQUjwsj8l0Y9HFQLH0GaJAsOHof
PhwwgdEGA1UdIwSByTCBxoAUjwsj8l0Y9HFQLH0GaJAsOHofPhyhgaKkgZ8wgZwx
CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhJbGxpbm9pczEQMA4GA1UEBxMHQ2hpY2Fn
bzESMBAGA1UEChMJSGFzaGlDb3JwMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEbMBkG
A1UEAxQSKi5hdGxhcy5waGluemUuY29tMSEwHwYJKoZIhvcNAQkBFhJwYXVsQGhh
c2hpY29ycC5jb22CCQCez1ft5CWamTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB
BQUAA4IBAQC4tFfxpB8xEk9ewb5CNhhac4oKwGths+oq45DjoNtlagDMmIs2bl18
q45PIB7fuFkAz/YHcOL0UEOAiw4jbuROp9jacHxBV21lRLLmNlK1Llc3eNVvLJ38
ud6/Skilv9XyC4JNk0P5KrghxR6SOGwRuYZNqF+tthf+Bp9wJvLyfqDuJfGBal7C
ezobMoh4tp8Dh1JeQlwvJcVt2k0UFJpa57MNr78c684Bq55ow+jd6wFG0XM0MMmy
u+QRgJEGfYuYDPFEO8C8IfRyrHuV7Ll9P6eyEEFCneznXY0yJc/Gn3ZcX7ANqJsc
ueMOWw/vUnonzxAFKW+I9U9ptyVSNMLY
-----END CERTIFICATE-----

View File

@ -1,22 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI
MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz
MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv
cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz
Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO
0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao
wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj
7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS
8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT
BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg
JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC
NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3
6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/
3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm
D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS
CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
-----END CERTIFICATE-----

View File

@ -1,25 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
jVaMaA==
-----END CERTIFICATE-----

26
vendor/github.com/hashicorp/go-sockaddr/.gitignore generated vendored Normal file
View File

@ -0,0 +1,26 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
.cover.out*
coverage.html

65
vendor/github.com/hashicorp/go-sockaddr/GNUmakefile generated vendored Normal file
View File

@ -0,0 +1,65 @@
TOOLS= golang.org/x/tools/cover
GOCOVER_TMPFILE?= $(GOCOVER_FILE).tmp
GOCOVER_FILE?= .cover.out
GOCOVERHTML?= coverage.html
FIND=`/usr/bin/which 2> /dev/null gfind find | /usr/bin/grep -v ^no | /usr/bin/head -n 1`
XARGS=`/usr/bin/which 2> /dev/null gxargs xargs | /usr/bin/grep -v ^no | /usr/bin/head -n 1`
test:: $(GOCOVER_FILE)
@$(MAKE) -C cmd/sockaddr test
cover:: coverage_report
$(GOCOVER_FILE)::
@${FIND} . -type d ! -path '*cmd*' ! -path '*.git*' -print0 | ${XARGS} -0 -I % sh -ec "cd % && rm -f $(GOCOVER_TMPFILE) && go test -coverprofile=$(GOCOVER_TMPFILE)"
@echo 'mode: set' > $(GOCOVER_FILE)
@${FIND} . -type f ! -path '*cmd*' ! -path '*.git*' -name "$(GOCOVER_TMPFILE)" -print0 | ${XARGS} -0 -n1 cat $(GOCOVER_TMPFILE) | grep -v '^mode: ' >> ${PWD}/$(GOCOVER_FILE)
$(GOCOVERHTML): $(GOCOVER_FILE)
go tool cover -html=$(GOCOVER_FILE) -o $(GOCOVERHTML)
coverage_report:: $(GOCOVER_FILE)
go tool cover -html=$(GOCOVER_FILE)
audit_tools::
@go get -u github.com/golang/lint/golint && echo "Installed golint:"
@go get -u github.com/fzipp/gocyclo && echo "Installed gocyclo:"
@go get -u github.com/remyoudompheng/go-misc/deadcode && echo "Installed deadcode:"
@go get -u github.com/client9/misspell/cmd/misspell && echo "Installed misspell:"
@go get -u github.com/gordonklaus/ineffassign && echo "Installed ineffassign:"
audit::
deadcode
go tool vet -all *.go
go tool vet -shadow=true *.go
golint *.go
ineffassign .
gocyclo -over 65 *.go
misspell *.go
clean::
rm -f $(GOCOVER_FILE) $(GOCOVERHTML)
dev::
@go build
@$(MAKE) -B -C cmd/sockaddr sockaddr
install::
@go install
@$(MAKE) -C cmd/sockaddr install
doc::
@echo Visit: http://127.0.0.1:6161/pkg/github.com/hashicorp/go-sockaddr/
godoc -http=:6161 -goroot $GOROOT
world::
@set -e; \
for os in solaris darwin freebsd linux windows; do \
for arch in amd64; do \
printf "Building on %s-%s\n" "$${os}" "$${arch}" ; \
env GOOS="$${os}" GOARCH="$${arch}" go build -o /dev/null; \
done; \
done
$(MAKE) -C cmd/sockaddr world

373
vendor/github.com/hashicorp/go-sockaddr/LICENSE generated vendored Normal file
View File

@ -0,0 +1,373 @@
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.

118
vendor/github.com/hashicorp/go-sockaddr/README.md generated vendored Normal file
View File

@ -0,0 +1,118 @@
# go-sockaddr
## `sockaddr` Library
Socket address convenience functions for Go. `go-sockaddr` is a convenience
library that makes doing the right thing with IP addresses easy. `go-sockaddr`
is loosely modeled after the UNIX `sockaddr_t` and creates a union of the family
of `sockaddr_t` types (see below for an ascii diagram). Library documentation
is available
at
[https://godoc.org/github.com/hashicorp/go-sockaddr](https://godoc.org/github.com/hashicorp/go-sockaddr).
The primary intent of the library was to make it possible to define heuristics
for selecting the correct IP addresses when a configuration is evaluated at
runtime. See
the
[docs](https://godoc.org/github.com/hashicorp/go-sockaddr),
[`template` package](https://godoc.org/github.com/hashicorp/go-sockaddr/template),
tests,
and
[CLI utility](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr)
for details and hints as to how to use this library.
For example, with this library it is possible to find an IP address that:
* is attached to a default route
([`GetDefaultInterfaces()`](https://godoc.org/github.com/hashicorp/go-sockaddr#GetDefaultInterfaces))
* is contained within a CIDR block ([`IfByNetwork()`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByNetwork))
* is an RFC1918 address
([`IfByRFC("1918")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByRFC))
* is ordered
([`OrderedIfAddrBy(args)`](https://godoc.org/github.com/hashicorp/go-sockaddr#OrderedIfAddrBy) where
`args` includes, but is not limited
to,
[`AscIfType`](https://godoc.org/github.com/hashicorp/go-sockaddr#AscIfType),
[`AscNetworkSize`](https://godoc.org/github.com/hashicorp/go-sockaddr#AscNetworkSize))
* excludes all IPv6 addresses
([`IfByType("^(IPv4)$")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByType))
* is larger than a `/32`
([`IfByMaskSize(32)`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByMaskSize))
* is not on a `down` interface
([`ExcludeIfs("flags", "down")`](https://godoc.org/github.com/hashicorp/go-sockaddr#ExcludeIfs))
* preferences an IPv6 address over an IPv4 address
([`SortIfByType()`](https://godoc.org/github.com/hashicorp/go-sockaddr#SortIfByType) +
[`ReverseIfAddrs()`](https://godoc.org/github.com/hashicorp/go-sockaddr#ReverseIfAddrs)); and
* excludes any IP in RFC6890 address
([`IfByRFC("6890")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByRFC))
Or any combination or variation therein.
There are also a few simple helper functions such as `GetPublicIP` and
`GetPrivateIP` which both return strings and select the first public or private
IP address on the default interface, respectively. Similarly, there is also a
helper function called `GetInterfaceIP` which returns the first usable IP
address on the named interface.
## `sockaddr` CLI
Given the possible complexity of the `sockaddr` library, there is a CLI utility
that accompanies the library, also
called
[`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr).
The
[`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr)
utility exposes nearly all of the functionality of the library and can be used
either as an administrative tool or testing tool. To install
the
[`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr),
run:
```text
$ go get -u github.com/hashicorp/go-sockaddr/cmd/sockaddr
```
If you're familiar with UNIX's `sockaddr` struct's, the following diagram
mapping the C `sockaddr` (top) to `go-sockaddr` structs (bottom) and
interfaces will be helpful:
```
+-------------------------------------------------------+
| |
| sockaddr |
| SockAddr |
| |
| +--------------+ +----------------------------------+ |
| | sockaddr_un | | | |
| | SockAddrUnix | | sockaddr_in{,6} | |
| +--------------+ | IPAddr | |
| | | |
| | +-------------+ +--------------+ | |
| | | sockaddr_in | | sockaddr_in6 | | |
| | | IPv4Addr | | IPv6Addr | | |
| | +-------------+ +--------------+ | |
| | | |
| +----------------------------------+ |
| |
+-------------------------------------------------------+
```
## Inspiration and Design
There were many subtle inspirations that led to this design, but the most direct
inspiration for the filtering syntax was
OpenBSD's
[`pf.conf(5)`](https://www.freebsd.org/cgi/man.cgi?query=pf.conf&apropos=0&sektion=0&arch=default&format=html#PARAMETERS) firewall
syntax that lets you select the first IP address on a given named interface.
The original problem stemmed from:
* needing to create immutable images using [Packer](https://www.packer.io) that
ran the [Consul](https://www.consul.io) process (Consul can only use one IP
address at a time);
* images that may or may not have multiple interfaces or IP addresses at
runtime; and
* we didn't want to rely on configuration management to render out the correct
IP address if the VM image was being used in an auto-scaling group.
Instead we needed some way to codify a heuristic that would correctly select the
right IP address but the input parameters were not known when the image was
created.

5
vendor/github.com/hashicorp/go-sockaddr/doc.go generated vendored Normal file
View File

@ -0,0 +1,5 @@
/*
Package sockaddr is a Go implementation of the UNIX socket family data types and
related helper functions.
*/
package sockaddr

254
vendor/github.com/hashicorp/go-sockaddr/ifaddr.go generated vendored Normal file
View File

@ -0,0 +1,254 @@
package sockaddr
import "strings"
// ifAddrAttrMap is a map of the IfAddr type-specific attributes.
var ifAddrAttrMap map[AttrName]func(IfAddr) string
var ifAddrAttrs []AttrName
func init() {
ifAddrAttrInit()
}
// GetPrivateIP returns a string with a single IP address that is part of RFC
// 6890 and has a default route. If the system can't determine its IP address
// or find an RFC 6890 IP address, an empty string will be returned instead.
// This function is the `eval` equivalent of:
//
// ```
// $ sockaddr eval -r '{{GetPrivateInterfaces | attr "address"}}'
/// ```
func GetPrivateIP() (string, error) {
privateIfs, err := GetPrivateInterfaces()
if err != nil {
return "", err
}
if len(privateIfs) < 1 {
return "", nil
}
ifAddr := privateIfs[0]
ip := *ToIPAddr(ifAddr.SockAddr)
return ip.NetIP().String(), nil
}
// GetPrivateIPs returns a string with all IP addresses that are part of RFC
// 6890 (regardless of whether or not there is a default route, unlike
// GetPublicIP). If the system can't find any RFC 6890 IP addresses, an empty
// string will be returned instead. This function is the `eval` equivalent of:
//
// ```
// $ sockaddr eval -r '{{GetAllInterfaces | include "RFC" "6890" | join "address" " "}}'
/// ```
func GetPrivateIPs() (string, error) {
ifAddrs, err := GetAllInterfaces()
if err != nil {
return "", err
} else if len(ifAddrs) < 1 {
return "", nil
}
ifAddrs, _ = FilterIfByType(ifAddrs, TypeIP)
if len(ifAddrs) == 0 {
return "", nil
}
OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(ifAddrs)
ifAddrs, _, err = IfByRFC("6890", ifAddrs)
if err != nil {
return "", err
} else if len(ifAddrs) == 0 {
return "", nil
}
_, ifAddrs, err = IfByRFC(ForwardingBlacklistRFC, ifAddrs)
if err != nil {
return "", err
} else if len(ifAddrs) == 0 {
return "", nil
}
ips := make([]string, 0, len(ifAddrs))
for _, ifAddr := range ifAddrs {
ip := *ToIPAddr(ifAddr.SockAddr)
s := ip.NetIP().String()
ips = append(ips, s)
}
return strings.Join(ips, " "), nil
}
// GetPublicIP returns a string with a single IP address that is NOT part of RFC
// 6890 and has a default route. If the system can't determine its IP address
// or find a non RFC 6890 IP address, an empty string will be returned instead.
// This function is the `eval` equivalent of:
//
// ```
// $ sockaddr eval -r '{{GetPublicInterfaces | attr "address"}}'
/// ```
func GetPublicIP() (string, error) {
publicIfs, err := GetPublicInterfaces()
if err != nil {
return "", err
} else if len(publicIfs) < 1 {
return "", nil
}
ifAddr := publicIfs[0]
ip := *ToIPAddr(ifAddr.SockAddr)
return ip.NetIP().String(), nil
}
// GetPublicIPs returns a string with all IP addresses that are NOT part of RFC
// 6890 (regardless of whether or not there is a default route, unlike
// GetPublicIP). If the system can't find any non RFC 6890 IP addresses, an
// empty string will be returned instead. This function is the `eval`
// equivalent of:
//
// ```
// $ sockaddr eval -r '{{GetAllInterfaces | exclude "RFC" "6890" | join "address" " "}}'
/// ```
func GetPublicIPs() (string, error) {
ifAddrs, err := GetAllInterfaces()
if err != nil {
return "", err
} else if len(ifAddrs) < 1 {
return "", nil
}
ifAddrs, _ = FilterIfByType(ifAddrs, TypeIP)
if len(ifAddrs) == 0 {
return "", nil
}
OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(ifAddrs)
_, ifAddrs, err = IfByRFC("6890", ifAddrs)
if err != nil {
return "", err
} else if len(ifAddrs) == 0 {
return "", nil
}
ips := make([]string, 0, len(ifAddrs))
for _, ifAddr := range ifAddrs {
ip := *ToIPAddr(ifAddr.SockAddr)
s := ip.NetIP().String()
ips = append(ips, s)
}
return strings.Join(ips, " "), nil
}
// GetInterfaceIP returns a string with a single IP address sorted by the size
// of the network (i.e. IP addresses with a smaller netmask, larger network
// size, are sorted first). This function is the `eval` equivalent of:
//
// ```
// $ sockaddr eval -r '{{GetAllInterfaces | include "name" <<ARG>> | sort "type,size" | include "flag" "forwardable" | attr "address" }}'
/// ```
func GetInterfaceIP(namedIfRE string) (string, error) {
ifAddrs, err := GetAllInterfaces()
if err != nil {
return "", err
}
ifAddrs, _, err = IfByName(namedIfRE, ifAddrs)
if err != nil {
return "", err
}
ifAddrs, _, err = IfByFlag("forwardable", ifAddrs)
if err != nil {
return "", err
}
ifAddrs, err = SortIfBy("+type,+size", ifAddrs)
if err != nil {
return "", err
}
if len(ifAddrs) == 0 {
return "", err
}
ip := ToIPAddr(ifAddrs[0].SockAddr)
if ip == nil {
return "", err
}
return IPAddrAttr(*ip, "address"), nil
}
// GetInterfaceIPs returns a string with all IPs, sorted by the size of the
// network (i.e. IP addresses with a smaller netmask, larger network size, are
// sorted first), on a named interface. This function is the `eval` equivalent
// of:
//
// ```
// $ sockaddr eval -r '{{GetAllInterfaces | include "name" <<ARG>> | sort "type,size" | join "address" " "}}'
/// ```
func GetInterfaceIPs(namedIfRE string) (string, error) {
ifAddrs, err := GetAllInterfaces()
if err != nil {
return "", err
}
ifAddrs, _, err = IfByName(namedIfRE, ifAddrs)
if err != nil {
return "", err
}
ifAddrs, err = SortIfBy("+type,+size", ifAddrs)
if err != nil {
return "", err
}
if len(ifAddrs) == 0 {
return "", err
}
ips := make([]string, 0, len(ifAddrs))
for _, ifAddr := range ifAddrs {
ip := *ToIPAddr(ifAddr.SockAddr)
s := ip.NetIP().String()
ips = append(ips, s)
}
return strings.Join(ips, " "), nil
}
// IfAddrAttrs returns a list of attributes supported by the IfAddr type
func IfAddrAttrs() []AttrName {
return ifAddrAttrs
}
// IfAddrAttr returns a string representation of an attribute for the given
// IfAddr.
func IfAddrAttr(ifAddr IfAddr, attrName AttrName) string {
fn, found := ifAddrAttrMap[attrName]
if !found {
return ""
}
return fn(ifAddr)
}
// ifAddrAttrInit is called once at init()
func ifAddrAttrInit() {
// Sorted for human readability
ifAddrAttrs = []AttrName{
"flags",
"name",
}
ifAddrAttrMap = map[AttrName]func(ifAddr IfAddr) string{
"flags": func(ifAddr IfAddr) string {
return ifAddr.Interface.Flags.String()
},
"name": func(ifAddr IfAddr) string {
return ifAddr.Interface.Name
},
}
}

1281
vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

65
vendor/github.com/hashicorp/go-sockaddr/ifattr.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
package sockaddr
import (
"fmt"
"net"
)
// IfAddr is a union of a SockAddr and a net.Interface.
type IfAddr struct {
SockAddr
net.Interface
}
// Attr returns the named attribute as a string
func (ifAddr IfAddr) Attr(attrName AttrName) (string, error) {
val := IfAddrAttr(ifAddr, attrName)
if val != "" {
return val, nil
}
return Attr(ifAddr.SockAddr, attrName)
}
// Attr returns the named attribute as a string
func Attr(sa SockAddr, attrName AttrName) (string, error) {
switch sockType := sa.Type(); {
case sockType&TypeIP != 0:
ip := *ToIPAddr(sa)
attrVal := IPAddrAttr(ip, attrName)
if attrVal != "" {
return attrVal, nil
}
if sockType == TypeIPv4 {
ipv4 := *ToIPv4Addr(sa)
attrVal := IPv4AddrAttr(ipv4, attrName)
if attrVal != "" {
return attrVal, nil
}
} else if sockType == TypeIPv6 {
ipv6 := *ToIPv6Addr(sa)
attrVal := IPv6AddrAttr(ipv6, attrName)
if attrVal != "" {
return attrVal, nil
}
}
case sockType == TypeUnix:
us := *ToUnixSock(sa)
attrVal := UnixSockAttr(us, attrName)
if attrVal != "" {
return attrVal, nil
}
}
// Non type-specific attributes
switch attrName {
case "string":
return sa.String(), nil
case "type":
return sa.Type().String(), nil
}
return "", fmt.Errorf("unsupported attribute name %q", attrName)
}

169
vendor/github.com/hashicorp/go-sockaddr/ipaddr.go generated vendored Normal file
View File

@ -0,0 +1,169 @@
package sockaddr
import (
"fmt"
"math/big"
"net"
"strings"
)
// Constants for the sizes of IPv3, IPv4, and IPv6 address types.
const (
IPv3len = 6
IPv4len = 4
IPv6len = 16
)
// IPAddr is a generic IP address interface for IPv4 and IPv6 addresses,
// networks, and socket endpoints.
type IPAddr interface {
SockAddr
AddressBinString() string
AddressHexString() string
Cmp(SockAddr) int
CmpAddress(SockAddr) int
CmpPort(SockAddr) int
FirstUsable() IPAddr
Host() IPAddr
IPPort() IPPort
LastUsable() IPAddr
Maskbits() int
NetIP() *net.IP
NetIPMask() *net.IPMask
NetIPNet() *net.IPNet
Network() IPAddr
Octets() []int
}
// IPPort is the type for an IP port number for the TCP and UDP IP transports.
type IPPort uint16
// IPPrefixLen is a typed integer representing the prefix length for a given
// IPAddr.
type IPPrefixLen byte
// ipAddrAttrMap is a map of the IPAddr type-specific attributes.
var ipAddrAttrMap map[AttrName]func(IPAddr) string
var ipAddrAttrs []AttrName
func init() {
ipAddrInit()
}
// NewIPAddr creates a new IPAddr from a string. Returns nil if the string is
// not an IPv4 or an IPv6 address.
func NewIPAddr(addr string) (IPAddr, error) {
ipv4Addr, err := NewIPv4Addr(addr)
if err == nil {
return ipv4Addr, nil
}
ipv6Addr, err := NewIPv6Addr(addr)
if err == nil {
return ipv6Addr, nil
}
return nil, fmt.Errorf("invalid IPAddr %v", addr)
}
// IPAddrAttr returns a string representation of an attribute for the given
// IPAddr.
func IPAddrAttr(ip IPAddr, selector AttrName) string {
fn, found := ipAddrAttrMap[selector]
if !found {
return ""
}
return fn(ip)
}
// IPAttrs returns a list of attributes supported by the IPAddr type
func IPAttrs() []AttrName {
return ipAddrAttrs
}
// MustIPAddr is a helper method that must return an IPAddr or panic on invalid
// input.
func MustIPAddr(addr string) IPAddr {
ip, err := NewIPAddr(addr)
if err != nil {
panic(fmt.Sprintf("Unable to create an IPAddr from %+q: %v", addr, err))
}
return ip
}
// ipAddrInit is called once at init()
func ipAddrInit() {
// Sorted for human readability
ipAddrAttrs = []AttrName{
"host",
"address",
"port",
"netmask",
"network",
"mask_bits",
"binary",
"hex",
"first_usable",
"last_usable",
"octets",
}
ipAddrAttrMap = map[AttrName]func(ip IPAddr) string{
"address": func(ip IPAddr) string {
return ip.NetIP().String()
},
"binary": func(ip IPAddr) string {
return ip.AddressBinString()
},
"first_usable": func(ip IPAddr) string {
return ip.FirstUsable().String()
},
"hex": func(ip IPAddr) string {
return ip.AddressHexString()
},
"host": func(ip IPAddr) string {
return ip.Host().String()
},
"last_usable": func(ip IPAddr) string {
return ip.LastUsable().String()
},
"mask_bits": func(ip IPAddr) string {
return fmt.Sprintf("%d", ip.Maskbits())
},
"netmask": func(ip IPAddr) string {
switch v := ip.(type) {
case IPv4Addr:
ipv4Mask := IPv4Addr{
Address: IPv4Address(v.Mask),
Mask: IPv4HostMask,
}
return ipv4Mask.String()
case IPv6Addr:
ipv6Mask := new(big.Int)
ipv6Mask.Set(v.Mask)
ipv6MaskAddr := IPv6Addr{
Address: IPv6Address(ipv6Mask),
Mask: ipv6HostMask,
}
return ipv6MaskAddr.String()
default:
return fmt.Sprintf("<unsupported type: %T>", ip)
}
},
"network": func(ip IPAddr) string {
return ip.Network().NetIP().String()
},
"octets": func(ip IPAddr) string {
octets := ip.Octets()
octetStrs := make([]string, 0, len(octets))
for _, octet := range octets {
octetStrs = append(octetStrs, fmt.Sprintf("%d", octet))
}
return strings.Join(octetStrs, " ")
},
"port": func(ip IPAddr) string {
return fmt.Sprintf("%d", ip.IPPort())
},
}
}

98
vendor/github.com/hashicorp/go-sockaddr/ipaddrs.go generated vendored Normal file
View File

@ -0,0 +1,98 @@
package sockaddr
import "bytes"
type IPAddrs []IPAddr
func (s IPAddrs) Len() int { return len(s) }
func (s IPAddrs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// // SortIPAddrsByCmp is a type that satisfies sort.Interface and can be used
// // by the routines in this package. The SortIPAddrsByCmp type is used to
// // sort IPAddrs by Cmp()
// type SortIPAddrsByCmp struct{ IPAddrs }
// // Less reports whether the element with index i should sort before the
// // element with index j.
// func (s SortIPAddrsByCmp) Less(i, j int) bool {
// // Sort by Type, then address, then port number.
// return Less(s.IPAddrs[i], s.IPAddrs[j])
// }
// SortIPAddrsBySpecificMaskLen is a type that satisfies sort.Interface and
// can be used by the routines in this package. The
// SortIPAddrsBySpecificMaskLen type is used to sort IPAddrs by smallest
// network (most specific to largest network).
type SortIPAddrsByNetworkSize struct{ IPAddrs }
// Less reports whether the element with index i should sort before the
// element with index j.
func (s SortIPAddrsByNetworkSize) Less(i, j int) bool {
// Sort masks with a larger binary value (i.e. fewer hosts per network
// prefix) after masks with a smaller value (larger number of hosts per
// prefix).
switch bytes.Compare([]byte(*s.IPAddrs[i].NetIPMask()), []byte(*s.IPAddrs[j].NetIPMask())) {
case 0:
// Fall through to the second test if the net.IPMasks are the
// same.
break
case 1:
return true
case -1:
return false
default:
panic("bad, m'kay?")
}
// Sort IPs based on the length (i.e. prefer IPv4 over IPv6).
iLen := len(*s.IPAddrs[i].NetIP())
jLen := len(*s.IPAddrs[j].NetIP())
if iLen != jLen {
return iLen > jLen
}
// Sort IPs based on their network address from lowest to highest.
switch bytes.Compare(s.IPAddrs[i].NetIPNet().IP, s.IPAddrs[j].NetIPNet().IP) {
case 0:
break
case 1:
return false
case -1:
return true
default:
panic("lol wut?")
}
// If a host does not have a port set, it always sorts after hosts
// that have a port (e.g. a host with a /32 and port number is more
// specific and should sort first over a host with a /32 but no port
// set).
if s.IPAddrs[i].IPPort() == 0 || s.IPAddrs[j].IPPort() == 0 {
return false
}
return s.IPAddrs[i].IPPort() < s.IPAddrs[j].IPPort()
}
// SortIPAddrsBySpecificMaskLen is a type that satisfies sort.Interface and
// can be used by the routines in this package. The
// SortIPAddrsBySpecificMaskLen type is used to sort IPAddrs by smallest
// network (most specific to largest network).
type SortIPAddrsBySpecificMaskLen struct{ IPAddrs }
// Less reports whether the element with index i should sort before the
// element with index j.
func (s SortIPAddrsBySpecificMaskLen) Less(i, j int) bool {
return s.IPAddrs[i].Maskbits() > s.IPAddrs[j].Maskbits()
}
// SortIPAddrsByBroadMaskLen is a type that satisfies sort.Interface and can
// be used by the routines in this package. The SortIPAddrsByBroadMaskLen
// type is used to sort IPAddrs by largest network (i.e. largest subnets
// first).
type SortIPAddrsByBroadMaskLen struct{ IPAddrs }
// Less reports whether the element with index i should sort before the
// element with index j.
func (s SortIPAddrsByBroadMaskLen) Less(i, j int) bool {
return s.IPAddrs[i].Maskbits() < s.IPAddrs[j].Maskbits()
}

516
vendor/github.com/hashicorp/go-sockaddr/ipv4addr.go generated vendored Normal file
View File

@ -0,0 +1,516 @@
package sockaddr
import (
"encoding/binary"
"fmt"
"net"
"regexp"
"strconv"
"strings"
)
type (
// IPv4Address is a named type representing an IPv4 address.
IPv4Address uint32
// IPv4Network is a named type representing an IPv4 network.
IPv4Network uint32
// IPv4Mask is a named type representing an IPv4 network mask.
IPv4Mask uint32
)
// IPv4HostMask is a constant represents a /32 IPv4 Address
// (i.e. 255.255.255.255).
const IPv4HostMask = IPv4Mask(0xffffffff)
// ipv4AddrAttrMap is a map of the IPv4Addr type-specific attributes.
var ipv4AddrAttrMap map[AttrName]func(IPv4Addr) string
var ipv4AddrAttrs []AttrName
var trailingHexNetmaskRE *regexp.Regexp
// IPv4Addr implements a convenience wrapper around the union of Go's
// built-in net.IP and net.IPNet types. In UNIX-speak, IPv4Addr implements
// `sockaddr` when the the address family is set to AF_INET
// (i.e. `sockaddr_in`).
type IPv4Addr struct {
IPAddr
Address IPv4Address
Mask IPv4Mask
Port IPPort
}
func init() {
ipv4AddrInit()
trailingHexNetmaskRE = regexp.MustCompile(`/([0f]{8})$`)
}
// NewIPv4Addr creates an IPv4Addr from a string. String can be in the form
// of either an IPv4:port (e.g. `1.2.3.4:80`, in which case the mask is
// assumed to be a `/32`), an IPv4 address (e.g. `1.2.3.4`, also with a `/32`
// mask), or an IPv4 CIDR (e.g. `1.2.3.4/24`, which has its IP port
// initialized to zero). ipv4Str can not be a hostname.
//
// NOTE: Many net.*() routines will initialize and return an IPv6 address.
// To create uint32 values from net.IP, always test to make sure the address
// returned can be converted to a 4 byte array using To4().
func NewIPv4Addr(ipv4Str string) (IPv4Addr, error) {
// Strip off any bogus hex-encoded netmasks that will be mis-parsed by Go. In
// particular, clients with the Barracuda VPN client will see something like:
// `192.168.3.51/00ffffff` as their IP address.
trailingHexNetmaskRe := trailingHexNetmaskRE.Copy()
if match := trailingHexNetmaskRe.FindStringIndex(ipv4Str); match != nil {
ipv4Str = ipv4Str[:match[0]]
}
// Parse as an IPv4 CIDR
ipAddr, network, err := net.ParseCIDR(ipv4Str)
if err == nil {
ipv4 := ipAddr.To4()
if ipv4 == nil {
return IPv4Addr{}, fmt.Errorf("Unable to convert %s to an IPv4 address", ipv4Str)
}
// If we see an IPv6 netmask, convert it to an IPv4 mask.
netmaskSepPos := strings.LastIndexByte(ipv4Str, '/')
if netmaskSepPos != -1 && netmaskSepPos+1 < len(ipv4Str) {
netMask, err := strconv.ParseUint(ipv4Str[netmaskSepPos+1:], 10, 8)
if err != nil {
return IPv4Addr{}, fmt.Errorf("Unable to convert %s to an IPv4 address: unable to parse CIDR netmask: %v", ipv4Str, err)
} else if netMask > 128 {
return IPv4Addr{}, fmt.Errorf("Unable to convert %s to an IPv4 address: invalid CIDR netmask", ipv4Str)
}
if netMask >= 96 {
// Convert the IPv6 netmask to an IPv4 netmask
network.Mask = net.CIDRMask(int(netMask-96), IPv4len*8)
}
}
ipv4Addr := IPv4Addr{
Address: IPv4Address(binary.BigEndian.Uint32(ipv4)),
Mask: IPv4Mask(binary.BigEndian.Uint32(network.Mask)),
}
return ipv4Addr, nil
}
// Attempt to parse ipv4Str as a /32 host with a port number.
tcpAddr, err := net.ResolveTCPAddr("tcp4", ipv4Str)
if err == nil {
ipv4 := tcpAddr.IP.To4()
if ipv4 == nil {
return IPv4Addr{}, fmt.Errorf("Unable to resolve %+q as an IPv4 address", ipv4Str)
}
ipv4Uint32 := binary.BigEndian.Uint32(ipv4)
ipv4Addr := IPv4Addr{
Address: IPv4Address(ipv4Uint32),
Mask: IPv4HostMask,
Port: IPPort(tcpAddr.Port),
}
return ipv4Addr, nil
}
// Parse as a naked IPv4 address
ip := net.ParseIP(ipv4Str)
if ip != nil {
ipv4 := ip.To4()
if ipv4 == nil {
return IPv4Addr{}, fmt.Errorf("Unable to string convert %+q to an IPv4 address", ipv4Str)
}
ipv4Uint32 := binary.BigEndian.Uint32(ipv4)
ipv4Addr := IPv4Addr{
Address: IPv4Address(ipv4Uint32),
Mask: IPv4HostMask,
}
return ipv4Addr, nil
}
return IPv4Addr{}, fmt.Errorf("Unable to parse %+q to an IPv4 address: %v", ipv4Str, err)
}
// AddressBinString returns a string with the IPv4Addr's Address represented
// as a sequence of '0' and '1' characters. This method is useful for
// debugging or by operators who want to inspect an address.
func (ipv4 IPv4Addr) AddressBinString() string {
return fmt.Sprintf("%032s", strconv.FormatUint(uint64(ipv4.Address), 2))
}
// AddressHexString returns a string with the IPv4Addr address represented as
// a sequence of hex characters. This method is useful for debugging or by
// operators who want to inspect an address.
func (ipv4 IPv4Addr) AddressHexString() string {
return fmt.Sprintf("%08s", strconv.FormatUint(uint64(ipv4.Address), 16))
}
// Broadcast is an IPv4Addr-only method that returns the broadcast address of
// the network.
//
// NOTE: IPv6 only supports multicast, so this method only exists for
// IPv4Addr.
func (ipv4 IPv4Addr) Broadcast() IPAddr {
// Nothing should listen on a broadcast address.
return IPv4Addr{
Address: IPv4Address(ipv4.BroadcastAddress()),
Mask: IPv4HostMask,
}
}
// BroadcastAddress returns a IPv4Network of the IPv4Addr's broadcast
// address.
func (ipv4 IPv4Addr) BroadcastAddress() IPv4Network {
return IPv4Network(uint32(ipv4.Address)&uint32(ipv4.Mask) | ^uint32(ipv4.Mask))
}
// CmpAddress follows the Cmp() standard protocol and returns:
//
// - -1 If the receiver should sort first because its address is lower than arg
// - 0 if the SockAddr arg is equal to the receiving IPv4Addr or the argument is
// of a different type.
// - 1 If the argument should sort first.
func (ipv4 IPv4Addr) CmpAddress(sa SockAddr) int {
ipv4b, ok := sa.(IPv4Addr)
if !ok {
return sortDeferDecision
}
switch {
case ipv4.Address == ipv4b.Address:
return sortDeferDecision
case ipv4.Address < ipv4b.Address:
return sortReceiverBeforeArg
default:
return sortArgBeforeReceiver
}
}
// CmpPort follows the Cmp() standard protocol and returns:
//
// - -1 If the receiver should sort first because its port is lower than arg
// - 0 if the SockAddr arg's port number is equal to the receiving IPv4Addr,
// regardless of type.
// - 1 If the argument should sort first.
func (ipv4 IPv4Addr) CmpPort(sa SockAddr) int {
var saPort IPPort
switch v := sa.(type) {
case IPv4Addr:
saPort = v.Port
case IPv6Addr:
saPort = v.Port
default:
return sortDeferDecision
}
switch {
case ipv4.Port == saPort:
return sortDeferDecision
case ipv4.Port < saPort:
return sortReceiverBeforeArg
default:
return sortArgBeforeReceiver
}
}
// CmpRFC follows the Cmp() standard protocol and returns:
//
// - -1 If the receiver should sort first because it belongs to the RFC and its
// arg does not
// - 0 if the receiver and arg both belong to the same RFC or neither do.
// - 1 If the arg belongs to the RFC but receiver does not.
func (ipv4 IPv4Addr) CmpRFC(rfcNum uint, sa SockAddr) int {
recvInRFC := IsRFC(rfcNum, ipv4)
ipv4b, ok := sa.(IPv4Addr)
if !ok {
// If the receiver is part of the desired RFC and the SockAddr
// argument is not, return -1 so that the receiver sorts before
// the non-IPv4 SockAddr. Conversely, if the receiver is not
// part of the RFC, punt on sorting and leave it for the next
// sorter.
if recvInRFC {
return sortReceiverBeforeArg
} else {
return sortDeferDecision
}
}
argInRFC := IsRFC(rfcNum, ipv4b)
switch {
case (recvInRFC && argInRFC), (!recvInRFC && !argInRFC):
// If a and b both belong to the RFC, or neither belong to
// rfcNum, defer sorting to the next sorter.
return sortDeferDecision
case recvInRFC && !argInRFC:
return sortReceiverBeforeArg
default:
return sortArgBeforeReceiver
}
}
// Contains returns true if the SockAddr is contained within the receiver.
func (ipv4 IPv4Addr) Contains(sa SockAddr) bool {
ipv4b, ok := sa.(IPv4Addr)
if !ok {
return false
}
return ipv4.ContainsNetwork(ipv4b)
}
// ContainsAddress returns true if the IPv4Address is contained within the
// receiver.
func (ipv4 IPv4Addr) ContainsAddress(x IPv4Address) bool {
return IPv4Address(ipv4.NetworkAddress()) <= x &&
IPv4Address(ipv4.BroadcastAddress()) >= x
}
// ContainsNetwork returns true if the network from IPv4Addr is contained
// within the receiver.
func (ipv4 IPv4Addr) ContainsNetwork(x IPv4Addr) bool {
return ipv4.NetworkAddress() <= x.NetworkAddress() &&
ipv4.BroadcastAddress() >= x.BroadcastAddress()
}
// DialPacketArgs returns the arguments required to be passed to
// net.DialUDP(). If the Mask of ipv4 is not a /32 or the Port is 0,
// DialPacketArgs() will fail. See Host() to create an IPv4Addr with its
// mask set to /32.
func (ipv4 IPv4Addr) DialPacketArgs() (network, dialArgs string) {
if ipv4.Mask != IPv4HostMask || ipv4.Port == 0 {
return "udp4", ""
}
return "udp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
}
// DialStreamArgs returns the arguments required to be passed to
// net.DialTCP(). If the Mask of ipv4 is not a /32 or the Port is 0,
// DialStreamArgs() will fail. See Host() to create an IPv4Addr with its
// mask set to /32.
func (ipv4 IPv4Addr) DialStreamArgs() (network, dialArgs string) {
if ipv4.Mask != IPv4HostMask || ipv4.Port == 0 {
return "tcp4", ""
}
return "tcp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
}
// Equal returns true if a SockAddr is equal to the receiving IPv4Addr.
func (ipv4 IPv4Addr) Equal(sa SockAddr) bool {
ipv4b, ok := sa.(IPv4Addr)
if !ok {
return false
}
if ipv4.Port != ipv4b.Port {
return false
}
if ipv4.Address != ipv4b.Address {
return false
}
if ipv4.NetIPNet().String() != ipv4b.NetIPNet().String() {
return false
}
return true
}
// FirstUsable returns an IPv4Addr set to the first address following the
// network prefix. The first usable address in a network is normally the
// gateway and should not be used except by devices forwarding packets
// between two administratively distinct networks (i.e. a router). This
// function does not discriminate against first usable vs "first address that
// should be used." For example, FirstUsable() on "192.168.1.10/24" would
// return the address "192.168.1.1/24".
func (ipv4 IPv4Addr) FirstUsable() IPAddr {
addr := ipv4.NetworkAddress()
// If /32, return the address itself. If /31 assume a point-to-point
// link and return the lower address.
if ipv4.Maskbits() < 31 {
addr++
}
return IPv4Addr{
Address: IPv4Address(addr),
Mask: IPv4HostMask,
}
}
// Host returns a copy of ipv4 with its mask set to /32 so that it can be
// used by DialPacketArgs(), DialStreamArgs(), ListenPacketArgs(), or
// ListenStreamArgs().
func (ipv4 IPv4Addr) Host() IPAddr {
// Nothing should listen on a broadcast address.
return IPv4Addr{
Address: ipv4.Address,
Mask: IPv4HostMask,
Port: ipv4.Port,
}
}
// IPPort returns the Port number attached to the IPv4Addr
func (ipv4 IPv4Addr) IPPort() IPPort {
return ipv4.Port
}
// LastUsable returns the last address before the broadcast address in a
// given network.
func (ipv4 IPv4Addr) LastUsable() IPAddr {
addr := ipv4.BroadcastAddress()
// If /32, return the address itself. If /31 assume a point-to-point
// link and return the upper address.
if ipv4.Maskbits() < 31 {
addr--
}
return IPv4Addr{
Address: IPv4Address(addr),
Mask: IPv4HostMask,
}
}
// ListenPacketArgs returns the arguments required to be passed to
// net.ListenUDP(). If the Mask of ipv4 is not a /32, ListenPacketArgs()
// will fail. See Host() to create an IPv4Addr with its mask set to /32.
func (ipv4 IPv4Addr) ListenPacketArgs() (network, listenArgs string) {
if ipv4.Mask != IPv4HostMask {
return "udp4", ""
}
return "udp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
}
// ListenStreamArgs returns the arguments required to be passed to
// net.ListenTCP(). If the Mask of ipv4 is not a /32, ListenStreamArgs()
// will fail. See Host() to create an IPv4Addr with its mask set to /32.
func (ipv4 IPv4Addr) ListenStreamArgs() (network, listenArgs string) {
if ipv4.Mask != IPv4HostMask {
return "tcp4", ""
}
return "tcp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
}
// Maskbits returns the number of network mask bits in a given IPv4Addr. For
// example, the Maskbits() of "192.168.1.1/24" would return 24.
func (ipv4 IPv4Addr) Maskbits() int {
mask := make(net.IPMask, IPv4len)
binary.BigEndian.PutUint32(mask, uint32(ipv4.Mask))
maskOnes, _ := mask.Size()
return maskOnes
}
// MustIPv4Addr is a helper method that must return an IPv4Addr or panic on
// invalid input.
func MustIPv4Addr(addr string) IPv4Addr {
ipv4, err := NewIPv4Addr(addr)
if err != nil {
panic(fmt.Sprintf("Unable to create an IPv4Addr from %+q: %v", addr, err))
}
return ipv4
}
// NetIP returns the address as a net.IP (address is always presized to
// IPv4).
func (ipv4 IPv4Addr) NetIP() *net.IP {
x := make(net.IP, IPv4len)
binary.BigEndian.PutUint32(x, uint32(ipv4.Address))
return &x
}
// NetIPMask create a new net.IPMask from the IPv4Addr.
func (ipv4 IPv4Addr) NetIPMask() *net.IPMask {
ipv4Mask := net.IPMask{}
ipv4Mask = make(net.IPMask, IPv4len)
binary.BigEndian.PutUint32(ipv4Mask, uint32(ipv4.Mask))
return &ipv4Mask
}
// NetIPNet create a new net.IPNet from the IPv4Addr.
func (ipv4 IPv4Addr) NetIPNet() *net.IPNet {
ipv4net := &net.IPNet{}
ipv4net.IP = make(net.IP, IPv4len)
binary.BigEndian.PutUint32(ipv4net.IP, uint32(ipv4.NetworkAddress()))
ipv4net.Mask = *ipv4.NetIPMask()
return ipv4net
}
// Network returns the network prefix or network address for a given network.
func (ipv4 IPv4Addr) Network() IPAddr {
return IPv4Addr{
Address: IPv4Address(ipv4.NetworkAddress()),
Mask: ipv4.Mask,
}
}
// NetworkAddress returns an IPv4Network of the IPv4Addr's network address.
func (ipv4 IPv4Addr) NetworkAddress() IPv4Network {
return IPv4Network(uint32(ipv4.Address) & uint32(ipv4.Mask))
}
// Octets returns a slice of the four octets in an IPv4Addr's Address. The
// order of the bytes is big endian.
func (ipv4 IPv4Addr) Octets() []int {
return []int{
int(ipv4.Address >> 24),
int((ipv4.Address >> 16) & 0xff),
int((ipv4.Address >> 8) & 0xff),
int(ipv4.Address & 0xff),
}
}
// String returns a string representation of the IPv4Addr
func (ipv4 IPv4Addr) String() string {
if ipv4.Port != 0 {
return fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
}
if ipv4.Maskbits() == 32 {
return ipv4.NetIP().String()
}
return fmt.Sprintf("%s/%d", ipv4.NetIP().String(), ipv4.Maskbits())
}
// Type is used as a type switch and returns TypeIPv4
func (IPv4Addr) Type() SockAddrType {
return TypeIPv4
}
// IPv4AddrAttr returns a string representation of an attribute for the given
// IPv4Addr.
func IPv4AddrAttr(ipv4 IPv4Addr, selector AttrName) string {
fn, found := ipv4AddrAttrMap[selector]
if !found {
return ""
}
return fn(ipv4)
}
// IPv4Attrs returns a list of attributes supported by the IPv4Addr type
func IPv4Attrs() []AttrName {
return ipv4AddrAttrs
}
// ipv4AddrInit is called once at init()
func ipv4AddrInit() {
// Sorted for human readability
ipv4AddrAttrs = []AttrName{
"size", // Same position as in IPv6 for output consistency
"broadcast",
"uint32",
}
ipv4AddrAttrMap = map[AttrName]func(ipv4 IPv4Addr) string{
"broadcast": func(ipv4 IPv4Addr) string {
return ipv4.Broadcast().String()
},
"size": func(ipv4 IPv4Addr) string {
return fmt.Sprintf("%d", 1<<uint(IPv4len*8-ipv4.Maskbits()))
},
"uint32": func(ipv4 IPv4Addr) string {
return fmt.Sprintf("%d", uint32(ipv4.Address))
},
}
}

591
vendor/github.com/hashicorp/go-sockaddr/ipv6addr.go generated vendored Normal file
View File

@ -0,0 +1,591 @@
package sockaddr
import (
"bytes"
"encoding/binary"
"fmt"
"math/big"
"net"
)
type (
// IPv6Address is a named type representing an IPv6 address.
IPv6Address *big.Int
// IPv6Network is a named type representing an IPv6 network.
IPv6Network *big.Int
// IPv6Mask is a named type representing an IPv6 network mask.
IPv6Mask *big.Int
)
// IPv6HostPrefix is a constant represents a /128 IPv6 Prefix.
const IPv6HostPrefix = IPPrefixLen(128)
// ipv6HostMask is an unexported big.Int representing a /128 IPv6 address.
// This value must be a constant and always set to all ones.
var ipv6HostMask IPv6Mask
// ipv6AddrAttrMap is a map of the IPv6Addr type-specific attributes.
var ipv6AddrAttrMap map[AttrName]func(IPv6Addr) string
var ipv6AddrAttrs []AttrName
func init() {
biMask := new(big.Int)
biMask.SetBytes([]byte{
0xff, 0xff,
0xff, 0xff,
0xff, 0xff,
0xff, 0xff,
0xff, 0xff,
0xff, 0xff,
0xff, 0xff,
0xff, 0xff,
},
)
ipv6HostMask = IPv6Mask(biMask)
ipv6AddrInit()
}
// IPv6Addr implements a convenience wrapper around the union of Go's
// built-in net.IP and net.IPNet types. In UNIX-speak, IPv6Addr implements
// `sockaddr` when the the address family is set to AF_INET6
// (i.e. `sockaddr_in6`).
type IPv6Addr struct {
IPAddr
Address IPv6Address
Mask IPv6Mask
Port IPPort
}
// NewIPv6Addr creates an IPv6Addr from a string. String can be in the form of
// an an IPv6:port (e.g. `[2001:4860:0:2001::68]:80`, in which case the mask is
// assumed to be a /128), an IPv6 address (e.g. `2001:4860:0:2001::68`, also
// with a `/128` mask), an IPv6 CIDR (e.g. `2001:4860:0:2001::68/64`, which has
// its IP port initialized to zero). ipv6Str can not be a hostname.
//
// NOTE: Many net.*() routines will initialize and return an IPv4 address.
// Always test to make sure the address returned cannot be converted to a 4 byte
// array using To4().
func NewIPv6Addr(ipv6Str string) (IPv6Addr, error) {
v6Addr := false
LOOP:
for i := 0; i < len(ipv6Str); i++ {
switch ipv6Str[i] {
case '.':
break LOOP
case ':':
v6Addr = true
break LOOP
}
}
if !v6Addr {
return IPv6Addr{}, fmt.Errorf("Unable to resolve %+q as an IPv6 address, appears to be an IPv4 address", ipv6Str)
}
// Attempt to parse ipv6Str as a /128 host with a port number.
tcpAddr, err := net.ResolveTCPAddr("tcp6", ipv6Str)
if err == nil {
ipv6 := tcpAddr.IP.To16()
if ipv6 == nil {
return IPv6Addr{}, fmt.Errorf("Unable to resolve %+q as a 16byte IPv6 address", ipv6Str)
}
ipv6BigIntAddr := new(big.Int)
ipv6BigIntAddr.SetBytes(ipv6)
ipv6BigIntMask := new(big.Int)
ipv6BigIntMask.Set(ipv6HostMask)
ipv6Addr := IPv6Addr{
Address: IPv6Address(ipv6BigIntAddr),
Mask: IPv6Mask(ipv6BigIntMask),
Port: IPPort(tcpAddr.Port),
}
return ipv6Addr, nil
}
// Parse as a naked IPv6 address. Trim square brackets if present.
if len(ipv6Str) > 2 && ipv6Str[0] == '[' && ipv6Str[len(ipv6Str)-1] == ']' {
ipv6Str = ipv6Str[1 : len(ipv6Str)-1]
}
ip := net.ParseIP(ipv6Str)
if ip != nil {
ipv6 := ip.To16()
if ipv6 == nil {
return IPv6Addr{}, fmt.Errorf("Unable to string convert %+q to a 16byte IPv6 address", ipv6Str)
}
ipv6BigIntAddr := new(big.Int)
ipv6BigIntAddr.SetBytes(ipv6)
ipv6BigIntMask := new(big.Int)
ipv6BigIntMask.Set(ipv6HostMask)
return IPv6Addr{
Address: IPv6Address(ipv6BigIntAddr),
Mask: IPv6Mask(ipv6BigIntMask),
}, nil
}
// Parse as an IPv6 CIDR
ipAddr, network, err := net.ParseCIDR(ipv6Str)
if err == nil {
ipv6 := ipAddr.To16()
if ipv6 == nil {
return IPv6Addr{}, fmt.Errorf("Unable to convert %+q to a 16byte IPv6 address", ipv6Str)
}
ipv6BigIntAddr := new(big.Int)
ipv6BigIntAddr.SetBytes(ipv6)
ipv6BigIntMask := new(big.Int)
ipv6BigIntMask.SetBytes(network.Mask)
ipv6Addr := IPv6Addr{
Address: IPv6Address(ipv6BigIntAddr),
Mask: IPv6Mask(ipv6BigIntMask),
}
return ipv6Addr, nil
}
return IPv6Addr{}, fmt.Errorf("Unable to parse %+q to an IPv6 address: %v", ipv6Str, err)
}
// AddressBinString returns a string with the IPv6Addr's Address represented
// as a sequence of '0' and '1' characters. This method is useful for
// debugging or by operators who want to inspect an address.
func (ipv6 IPv6Addr) AddressBinString() string {
bi := big.Int(*ipv6.Address)
return fmt.Sprintf("%0128s", bi.Text(2))
}
// AddressHexString returns a string with the IPv6Addr address represented as
// a sequence of hex characters. This method is useful for debugging or by
// operators who want to inspect an address.
func (ipv6 IPv6Addr) AddressHexString() string {
bi := big.Int(*ipv6.Address)
return fmt.Sprintf("%032s", bi.Text(16))
}
// CmpAddress follows the Cmp() standard protocol and returns:
//
// - -1 If the receiver should sort first because its address is lower than arg
// - 0 if the SockAddr arg equal to the receiving IPv6Addr or the argument is of a
// different type.
// - 1 If the argument should sort first.
func (ipv6 IPv6Addr) CmpAddress(sa SockAddr) int {
ipv6b, ok := sa.(IPv6Addr)
if !ok {
return sortDeferDecision
}
ipv6aBigInt := new(big.Int)
ipv6aBigInt.Set(ipv6.Address)
ipv6bBigInt := new(big.Int)
ipv6bBigInt.Set(ipv6b.Address)
return ipv6aBigInt.Cmp(ipv6bBigInt)
}
// CmpPort follows the Cmp() standard protocol and returns:
//
// - -1 If the receiver should sort first because its port is lower than arg
// - 0 if the SockAddr arg's port number is equal to the receiving IPv6Addr,
// regardless of type.
// - 1 If the argument should sort first.
func (ipv6 IPv6Addr) CmpPort(sa SockAddr) int {
var saPort IPPort
switch v := sa.(type) {
case IPv4Addr:
saPort = v.Port
case IPv6Addr:
saPort = v.Port
default:
return sortDeferDecision
}
switch {
case ipv6.Port == saPort:
return sortDeferDecision
case ipv6.Port < saPort:
return sortReceiverBeforeArg
default:
return sortArgBeforeReceiver
}
}
// CmpRFC follows the Cmp() standard protocol and returns:
//
// - -1 If the receiver should sort first because it belongs to the RFC and its
// arg does not
// - 0 if the receiver and arg both belong to the same RFC or neither do.
// - 1 If the arg belongs to the RFC but receiver does not.
func (ipv6 IPv6Addr) CmpRFC(rfcNum uint, sa SockAddr) int {
recvInRFC := IsRFC(rfcNum, ipv6)
ipv6b, ok := sa.(IPv6Addr)
if !ok {
// If the receiver is part of the desired RFC and the SockAddr
// argument is not, sort receiver before the non-IPv6 SockAddr.
// Conversely, if the receiver is not part of the RFC, punt on
// sorting and leave it for the next sorter.
if recvInRFC {
return sortReceiverBeforeArg
} else {
return sortDeferDecision
}
}
argInRFC := IsRFC(rfcNum, ipv6b)
switch {
case (recvInRFC && argInRFC), (!recvInRFC && !argInRFC):
// If a and b both belong to the RFC, or neither belong to
// rfcNum, defer sorting to the next sorter.
return sortDeferDecision
case recvInRFC && !argInRFC:
return sortReceiverBeforeArg
default:
return sortArgBeforeReceiver
}
}
// Contains returns true if the SockAddr is contained within the receiver.
func (ipv6 IPv6Addr) Contains(sa SockAddr) bool {
ipv6b, ok := sa.(IPv6Addr)
if !ok {
return false
}
return ipv6.ContainsNetwork(ipv6b)
}
// ContainsAddress returns true if the IPv6Address is contained within the
// receiver.
func (ipv6 IPv6Addr) ContainsAddress(x IPv6Address) bool {
xAddr := IPv6Addr{
Address: x,
Mask: ipv6HostMask,
}
{
xIPv6 := xAddr.FirstUsable().(IPv6Addr)
yIPv6 := ipv6.FirstUsable().(IPv6Addr)
if xIPv6.CmpAddress(yIPv6) >= 1 {
return false
}
}
{
xIPv6 := xAddr.LastUsable().(IPv6Addr)
yIPv6 := ipv6.LastUsable().(IPv6Addr)
if xIPv6.CmpAddress(yIPv6) <= -1 {
return false
}
}
return true
}
// ContainsNetwork returns true if the network from IPv6Addr is contained within
// the receiver.
func (x IPv6Addr) ContainsNetwork(y IPv6Addr) bool {
{
xIPv6 := x.FirstUsable().(IPv6Addr)
yIPv6 := y.FirstUsable().(IPv6Addr)
if ret := xIPv6.CmpAddress(yIPv6); ret >= 1 {
return false
}
}
{
xIPv6 := x.LastUsable().(IPv6Addr)
yIPv6 := y.LastUsable().(IPv6Addr)
if ret := xIPv6.CmpAddress(yIPv6); ret <= -1 {
return false
}
}
return true
}
// DialPacketArgs returns the arguments required to be passed to
// net.DialUDP(). If the Mask of ipv6 is not a /128 or the Port is 0,
// DialPacketArgs() will fail. See Host() to create an IPv6Addr with its
// mask set to /128.
func (ipv6 IPv6Addr) DialPacketArgs() (network, dialArgs string) {
ipv6Mask := big.Int(*ipv6.Mask)
if ipv6Mask.Cmp(ipv6HostMask) != 0 || ipv6.Port == 0 {
return "udp6", ""
}
return "udp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
}
// DialStreamArgs returns the arguments required to be passed to
// net.DialTCP(). If the Mask of ipv6 is not a /128 or the Port is 0,
// DialStreamArgs() will fail. See Host() to create an IPv6Addr with its
// mask set to /128.
func (ipv6 IPv6Addr) DialStreamArgs() (network, dialArgs string) {
ipv6Mask := big.Int(*ipv6.Mask)
if ipv6Mask.Cmp(ipv6HostMask) != 0 || ipv6.Port == 0 {
return "tcp6", ""
}
return "tcp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
}
// Equal returns true if a SockAddr is equal to the receiving IPv4Addr.
func (ipv6a IPv6Addr) Equal(sa SockAddr) bool {
ipv6b, ok := sa.(IPv6Addr)
if !ok {
return false
}
if ipv6a.NetIP().String() != ipv6b.NetIP().String() {
return false
}
if ipv6a.NetIPNet().String() != ipv6b.NetIPNet().String() {
return false
}
if ipv6a.Port != ipv6b.Port {
return false
}
return true
}
// FirstUsable returns an IPv6Addr set to the first address following the
// network prefix. The first usable address in a network is normally the
// gateway and should not be used except by devices forwarding packets
// between two administratively distinct networks (i.e. a router). This
// function does not discriminate against first usable vs "first address that
// should be used." For example, FirstUsable() on "2001:0db8::0003/64" would
// return "2001:0db8::00011".
func (ipv6 IPv6Addr) FirstUsable() IPAddr {
return IPv6Addr{
Address: IPv6Address(ipv6.NetworkAddress()),
Mask: ipv6HostMask,
}
}
// Host returns a copy of ipv6 with its mask set to /128 so that it can be
// used by DialPacketArgs(), DialStreamArgs(), ListenPacketArgs(), or
// ListenStreamArgs().
func (ipv6 IPv6Addr) Host() IPAddr {
// Nothing should listen on a broadcast address.
return IPv6Addr{
Address: ipv6.Address,
Mask: ipv6HostMask,
Port: ipv6.Port,
}
}
// IPPort returns the Port number attached to the IPv6Addr
func (ipv6 IPv6Addr) IPPort() IPPort {
return ipv6.Port
}
// LastUsable returns the last address in a given network.
func (ipv6 IPv6Addr) LastUsable() IPAddr {
addr := new(big.Int)
addr.Set(ipv6.Address)
mask := new(big.Int)
mask.Set(ipv6.Mask)
negMask := new(big.Int)
negMask.Xor(ipv6HostMask, mask)
lastAddr := new(big.Int)
lastAddr.And(addr, mask)
lastAddr.Or(lastAddr, negMask)
return IPv6Addr{
Address: IPv6Address(lastAddr),
Mask: ipv6HostMask,
}
}
// ListenPacketArgs returns the arguments required to be passed to
// net.ListenUDP(). If the Mask of ipv6 is not a /128, ListenPacketArgs()
// will fail. See Host() to create an IPv6Addr with its mask set to /128.
func (ipv6 IPv6Addr) ListenPacketArgs() (network, listenArgs string) {
ipv6Mask := big.Int(*ipv6.Mask)
if ipv6Mask.Cmp(ipv6HostMask) != 0 {
return "udp6", ""
}
return "udp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
}
// ListenStreamArgs returns the arguments required to be passed to
// net.ListenTCP(). If the Mask of ipv6 is not a /128, ListenStreamArgs()
// will fail. See Host() to create an IPv6Addr with its mask set to /128.
func (ipv6 IPv6Addr) ListenStreamArgs() (network, listenArgs string) {
ipv6Mask := big.Int(*ipv6.Mask)
if ipv6Mask.Cmp(ipv6HostMask) != 0 {
return "tcp6", ""
}
return "tcp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
}
// Maskbits returns the number of network mask bits in a given IPv6Addr. For
// example, the Maskbits() of "2001:0db8::0003/64" would return 64.
func (ipv6 IPv6Addr) Maskbits() int {
maskOnes, _ := ipv6.NetIPNet().Mask.Size()
return maskOnes
}
// MustIPv6Addr is a helper method that must return an IPv6Addr or panic on
// invalid input.
func MustIPv6Addr(addr string) IPv6Addr {
ipv6, err := NewIPv6Addr(addr)
if err != nil {
panic(fmt.Sprintf("Unable to create an IPv6Addr from %+q: %v", addr, err))
}
return ipv6
}
// NetIP returns the address as a net.IP.
func (ipv6 IPv6Addr) NetIP() *net.IP {
return bigIntToNetIPv6(ipv6.Address)
}
// NetIPMask create a new net.IPMask from the IPv6Addr.
func (ipv6 IPv6Addr) NetIPMask() *net.IPMask {
ipv6Mask := make(net.IPMask, IPv6len)
m := big.Int(*ipv6.Mask)
copy(ipv6Mask, m.Bytes())
return &ipv6Mask
}
// Network returns a pointer to the net.IPNet within IPv4Addr receiver.
func (ipv6 IPv6Addr) NetIPNet() *net.IPNet {
ipv6net := &net.IPNet{}
ipv6net.IP = make(net.IP, IPv6len)
copy(ipv6net.IP, *ipv6.NetIP())
ipv6net.Mask = *ipv6.NetIPMask()
return ipv6net
}
// Network returns the network prefix or network address for a given network.
func (ipv6 IPv6Addr) Network() IPAddr {
return IPv6Addr{
Address: IPv6Address(ipv6.NetworkAddress()),
Mask: ipv6.Mask,
}
}
// NetworkAddress returns an IPv6Network of the IPv6Addr's network address.
func (ipv6 IPv6Addr) NetworkAddress() IPv6Network {
addr := new(big.Int)
addr.SetBytes((*ipv6.Address).Bytes())
mask := new(big.Int)
mask.SetBytes(*ipv6.NetIPMask())
netAddr := new(big.Int)
netAddr.And(addr, mask)
return IPv6Network(netAddr)
}
// Octets returns a slice of the 16 octets in an IPv6Addr's Address. The
// order of the bytes is big endian.
func (ipv6 IPv6Addr) Octets() []int {
x := make([]int, IPv6len)
for i, b := range *bigIntToNetIPv6(ipv6.Address) {
x[i] = int(b)
}
return x
}
// String returns a string representation of the IPv6Addr
func (ipv6 IPv6Addr) String() string {
if ipv6.Port != 0 {
return fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
}
if ipv6.Maskbits() == 128 {
return ipv6.NetIP().String()
}
return fmt.Sprintf("%s/%d", ipv6.NetIP().String(), ipv6.Maskbits())
}
// Type is used as a type switch and returns TypeIPv6
func (IPv6Addr) Type() SockAddrType {
return TypeIPv6
}
// IPv6Attrs returns a list of attributes supported by the IPv6Addr type
func IPv6Attrs() []AttrName {
return ipv6AddrAttrs
}
// IPv6AddrAttr returns a string representation of an attribute for the given
// IPv6Addr.
func IPv6AddrAttr(ipv6 IPv6Addr, selector AttrName) string {
fn, found := ipv6AddrAttrMap[selector]
if !found {
return ""
}
return fn(ipv6)
}
// ipv6AddrInit is called once at init()
func ipv6AddrInit() {
// Sorted for human readability
ipv6AddrAttrs = []AttrName{
"size", // Same position as in IPv6 for output consistency
"uint128",
}
ipv6AddrAttrMap = map[AttrName]func(ipv6 IPv6Addr) string{
"size": func(ipv6 IPv6Addr) string {
netSize := big.NewInt(1)
netSize = netSize.Lsh(netSize, uint(IPv6len*8-ipv6.Maskbits()))
return netSize.Text(10)
},
"uint128": func(ipv6 IPv6Addr) string {
b := big.Int(*ipv6.Address)
return b.Text(10)
},
}
}
// bigIntToNetIPv6 is a helper function that correctly returns a net.IP with the
// correctly padded values.
func bigIntToNetIPv6(bi *big.Int) *net.IP {
x := make(net.IP, IPv6len)
ipv6Bytes := bi.Bytes()
// It's possibe for ipv6Bytes to be less than IPv6len bytes in size. If
// they are different sizes we to pad the size of response.
if len(ipv6Bytes) < IPv6len {
buf := new(bytes.Buffer)
buf.Grow(IPv6len)
for i := len(ipv6Bytes); i < IPv6len; i++ {
if err := binary.Write(buf, binary.BigEndian, byte(0)); err != nil {
panic(fmt.Sprintf("Unable to pad byte %d of input %v: %v", i, bi, err))
}
}
for _, b := range ipv6Bytes {
if err := binary.Write(buf, binary.BigEndian, b); err != nil {
panic(fmt.Sprintf("Unable to preserve endianness of input %v: %v", bi, err))
}
}
ipv6Bytes = buf.Bytes()
}
i := copy(x, ipv6Bytes)
if i != IPv6len {
panic("IPv6 wrong size")
}
return &x
}

948
vendor/github.com/hashicorp/go-sockaddr/rfc.go generated vendored Normal file
View File

@ -0,0 +1,948 @@
package sockaddr
// ForwardingBlacklist is a faux RFC that includes a list of non-forwardable IP
// blocks.
const ForwardingBlacklist = 4294967295
const ForwardingBlacklistRFC = "4294967295"
// IsRFC tests to see if an SockAddr matches the specified RFC
func IsRFC(rfcNum uint, sa SockAddr) bool {
rfcNetMap := KnownRFCs()
rfcNets, ok := rfcNetMap[rfcNum]
if !ok {
return false
}
var contained bool
for _, rfcNet := range rfcNets {
if rfcNet.Contains(sa) {
contained = true
break
}
}
return contained
}
// KnownRFCs returns an initial set of known RFCs.
//
// NOTE (sean@): As this list evolves over time, please submit patches to keep
// this list current. If something isn't right, inquire, as it may just be a
// bug on my part. Some of the inclusions were based on my judgement as to what
// would be a useful value (e.g. RFC3330).
//
// Useful resources:
//
// * https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml
// * https://www.iana.org/assignments/ipv6-unicast-address-assignments/ipv6-unicast-address-assignments.xhtml
// * https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml
func KnownRFCs() map[uint]SockAddrs {
// NOTE(sean@): Multiple SockAddrs per RFC lend themselves well to a
// RADIX tree, but `ENOTIME`. Patches welcome.
return map[uint]SockAddrs{
919: {
// [RFC919] Broadcasting Internet Datagrams
MustIPv4Addr("255.255.255.255/32"), // [RFC1122], §7 Broadcast IP Addressing - Proposed Standards
},
1122: {
// [RFC1122] Requirements for Internet Hosts -- Communication Layers
MustIPv4Addr("0.0.0.0/8"), // [RFC1122], §3.2.1.3
MustIPv4Addr("127.0.0.0/8"), // [RFC1122], §3.2.1.3
},
1112: {
// [RFC1112] Host Extensions for IP Multicasting
MustIPv4Addr("224.0.0.0/4"), // [RFC1112], §4 Host Group Addresses
},
1918: {
// [RFC1918] Address Allocation for Private Internets
MustIPv4Addr("10.0.0.0/8"),
MustIPv4Addr("172.16.0.0/12"),
MustIPv4Addr("192.168.0.0/16"),
},
2544: {
// [RFC2544] Benchmarking Methodology for Network
// Interconnect Devices
MustIPv4Addr("198.18.0.0/15"),
},
2765: {
// [RFC2765] Stateless IP/ICMP Translation Algorithm
// (SIIT) (obsoleted by RFCs 6145, which itself was
// later obsoleted by 7915).
// [RFC2765], §2.1 Addresses
MustIPv6Addr("0:0:0:0:0:ffff:0:0/96"),
},
2928: {
// [RFC2928] Initial IPv6 Sub-TLA ID Assignments
MustIPv6Addr("2001::/16"), // Superblock
//MustIPv6Addr("2001:0000::/23"), // IANA
//MustIPv6Addr("2001:0200::/23"), // APNIC
//MustIPv6Addr("2001:0400::/23"), // ARIN
//MustIPv6Addr("2001:0600::/23"), // RIPE NCC
//MustIPv6Addr("2001:0800::/23"), // (future assignment)
// ...
//MustIPv6Addr("2001:FE00::/23"), // (future assignment)
},
3056: { // 6to4 address
// [RFC3056] Connection of IPv6 Domains via IPv4 Clouds
// [RFC3056], §2 IPv6 Prefix Allocation
MustIPv6Addr("2002::/16"),
},
3068: {
// [RFC3068] An Anycast Prefix for 6to4 Relay Routers
// (obsolete by RFC7526)
// [RFC3068], § 6to4 Relay anycast address
MustIPv4Addr("192.88.99.0/24"),
// [RFC3068], §2.5 6to4 IPv6 relay anycast address
//
// NOTE: /120 == 128-(32-24)
MustIPv6Addr("2002:c058:6301::/120"),
},
3171: {
// [RFC3171] IANA Guidelines for IPv4 Multicast Address Assignments
MustIPv4Addr("224.0.0.0/4"),
},
3330: {
// [RFC3330] Special-Use IPv4 Addresses
// Addresses in this block refer to source hosts on
// "this" network. Address 0.0.0.0/32 may be used as a
// source address for this host on this network; other
// addresses within 0.0.0.0/8 may be used to refer to
// specified hosts on this network [RFC1700, page 4].
MustIPv4Addr("0.0.0.0/8"),
// 10.0.0.0/8 - This block is set aside for use in
// private networks. Its intended use is documented in
// [RFC1918]. Addresses within this block should not
// appear on the public Internet.
MustIPv4Addr("10.0.0.0/8"),
// 14.0.0.0/8 - This block is set aside for assignments
// to the international system of Public Data Networks
// [RFC1700, page 181]. The registry of assignments
// within this block can be accessed from the "Public
// Data Network Numbers" link on the web page at
// http://www.iana.org/numbers.html. Addresses within
// this block are assigned to users and should be
// treated as such.
// 24.0.0.0/8 - This block was allocated in early 1996
// for use in provisioning IP service over cable
// television systems. Although the IANA initially was
// involved in making assignments to cable operators,
// this responsibility was transferred to American
// Registry for Internet Numbers (ARIN) in May 2001.
// Addresses within this block are assigned in the
// normal manner and should be treated as such.
// 39.0.0.0/8 - This block was used in the "Class A
// Subnet Experiment" that commenced in May 1995, as
// documented in [RFC1797]. The experiment has been
// completed and this block has been returned to the
// pool of addresses reserved for future allocation or
// assignment. This block therefore no longer has a
// special use and is subject to allocation to a
// Regional Internet Registry for assignment in the
// normal manner.
// 127.0.0.0/8 - This block is assigned for use as the Internet host
// loopback address. A datagram sent by a higher level protocol to an
// address anywhere within this block should loop back inside the host.
// This is ordinarily implemented using only 127.0.0.1/32 for loopback,
// but no addresses within this block should ever appear on any network
// anywhere [RFC1700, page 5].
MustIPv4Addr("127.0.0.0/8"),
// 128.0.0.0/16 - This block, corresponding to the
// numerically lowest of the former Class B addresses,
// was initially and is still reserved by the IANA.
// Given the present classless nature of the IP address
// space, the basis for the reservation no longer
// applies and addresses in this block are subject to
// future allocation to a Regional Internet Registry for
// assignment in the normal manner.
// 169.254.0.0/16 - This is the "link local" block. It
// is allocated for communication between hosts on a
// single link. Hosts obtain these addresses by
// auto-configuration, such as when a DHCP server may
// not be found.
MustIPv4Addr("169.254.0.0/16"),
// 172.16.0.0/12 - This block is set aside for use in
// private networks. Its intended use is documented in
// [RFC1918]. Addresses within this block should not
// appear on the public Internet.
MustIPv4Addr("172.16.0.0/12"),
// 191.255.0.0/16 - This block, corresponding to the numerically highest
// to the former Class B addresses, was initially and is still reserved
// by the IANA. Given the present classless nature of the IP address
// space, the basis for the reservation no longer applies and addresses
// in this block are subject to future allocation to a Regional Internet
// Registry for assignment in the normal manner.
// 192.0.0.0/24 - This block, corresponding to the
// numerically lowest of the former Class C addresses,
// was initially and is still reserved by the IANA.
// Given the present classless nature of the IP address
// space, the basis for the reservation no longer
// applies and addresses in this block are subject to
// future allocation to a Regional Internet Registry for
// assignment in the normal manner.
// 192.0.2.0/24 - This block is assigned as "TEST-NET" for use in
// documentation and example code. It is often used in conjunction with
// domain names example.com or example.net in vendor and protocol
// documentation. Addresses within this block should not appear on the
// public Internet.
MustIPv4Addr("192.0.2.0/24"),
// 192.88.99.0/24 - This block is allocated for use as 6to4 relay
// anycast addresses, according to [RFC3068].
MustIPv4Addr("192.88.99.0/24"),
// 192.168.0.0/16 - This block is set aside for use in private networks.
// Its intended use is documented in [RFC1918]. Addresses within this
// block should not appear on the public Internet.
MustIPv4Addr("192.168.0.0/16"),
// 198.18.0.0/15 - This block has been allocated for use
// in benchmark tests of network interconnect devices.
// Its use is documented in [RFC2544].
MustIPv4Addr("198.18.0.0/15"),
// 223.255.255.0/24 - This block, corresponding to the
// numerically highest of the former Class C addresses,
// was initially and is still reserved by the IANA.
// Given the present classless nature of the IP address
// space, the basis for the reservation no longer
// applies and addresses in this block are subject to
// future allocation to a Regional Internet Registry for
// assignment in the normal manner.
// 224.0.0.0/4 - This block, formerly known as the Class
// D address space, is allocated for use in IPv4
// multicast address assignments. The IANA guidelines
// for assignments from this space are described in
// [RFC3171].
MustIPv4Addr("224.0.0.0/4"),
// 240.0.0.0/4 - This block, formerly known as the Class E address
// space, is reserved. The "limited broadcast" destination address
// 255.255.255.255 should never be forwarded outside the (sub-)net of
// the source. The remainder of this space is reserved
// for future use. [RFC1700, page 4]
MustIPv4Addr("240.0.0.0/4"),
},
3849: {
// [RFC3849] IPv6 Address Prefix Reserved for Documentation
MustIPv6Addr("2001:db8::/32"), // [RFC3849], §4 IANA Considerations
},
3927: {
// [RFC3927] Dynamic Configuration of IPv4 Link-Local Addresses
MustIPv4Addr("169.254.0.0/16"), // [RFC3927], §2.1 Link-Local Address Selection
},
4038: {
// [RFC4038] Application Aspects of IPv6 Transition
// [RFC4038], §4.2. IPv6 Applications in a Dual-Stack Node
MustIPv6Addr("0:0:0:0:0:ffff::/96"),
},
4193: {
// [RFC4193] Unique Local IPv6 Unicast Addresses
MustIPv6Addr("fc00::/7"),
},
4291: {
// [RFC4291] IP Version 6 Addressing Architecture
// [RFC4291], §2.5.2 The Unspecified Address
MustIPv6Addr("::/128"),
// [RFC4291], §2.5.3 The Loopback Address
MustIPv6Addr("::1/128"),
// [RFC4291], §2.5.5.1. IPv4-Compatible IPv6 Address
MustIPv6Addr("::/96"),
// [RFC4291], §2.5.5.2. IPv4-Mapped IPv6 Address
MustIPv6Addr("::ffff:0:0/96"),
// [RFC4291], §2.5.6 Link-Local IPv6 Unicast Addresses
MustIPv6Addr("fe80::/10"),
// [RFC4291], §2.5.7 Site-Local IPv6 Unicast Addresses
// (depreciated)
MustIPv6Addr("fec0::/10"),
// [RFC4291], §2.7 Multicast Addresses
MustIPv6Addr("ff00::/8"),
// IPv6 Multicast Information.
//
// In the following "table" below, `ff0x` is replaced
// with the following values depending on the scope of
// the query:
//
// IPv6 Multicast Scopes:
// * ff00/9 // reserved
// * ff01/9 // interface-local
// * ff02/9 // link-local
// * ff03/9 // realm-local
// * ff04/9 // admin-local
// * ff05/9 // site-local
// * ff08/9 // organization-local
// * ff0e/9 // global
// * ff0f/9 // reserved
//
// IPv6 Multicast Addresses:
// * ff0x::2 // All routers
// * ff02::5 // OSPFIGP
// * ff02::6 // OSPFIGP Designated Routers
// * ff02::9 // RIP Routers
// * ff02::a // EIGRP Routers
// * ff02::d // All PIM Routers
// * ff02::1a // All RPL Routers
// * ff0x::fb // mDNSv6
// * ff0x::101 // All Network Time Protocol (NTP) servers
// * ff02::1:1 // Link Name
// * ff02::1:2 // All-dhcp-agents
// * ff02::1:3 // Link-local Multicast Name Resolution
// * ff05::1:3 // All-dhcp-servers
// * ff02::1:ff00:0/104 // Solicited-node multicast address.
// * ff02::2:ff00:0/104 // Node Information Queries
},
4380: {
// [RFC4380] Teredo: Tunneling IPv6 over UDP through
// Network Address Translations (NATs)
// [RFC4380], §2.6 Global Teredo IPv6 Service Prefix
MustIPv6Addr("2001:0000::/32"),
},
4773: {
// [RFC4773] Administration of the IANA Special Purpose IPv6 Address Block
MustIPv6Addr("2001:0000::/23"), // IANA
},
4843: {
// [RFC4843] An IPv6 Prefix for Overlay Routable Cryptographic Hash Identifiers (ORCHID)
MustIPv6Addr("2001:10::/28"), // [RFC4843], §7 IANA Considerations
},
5180: {
// [RFC5180] IPv6 Benchmarking Methodology for Network Interconnect Devices
MustIPv6Addr("2001:0200::/48"), // [RFC5180], §8 IANA Considerations
},
5735: {
// [RFC5735] Special Use IPv4 Addresses
MustIPv4Addr("192.0.2.0/24"), // TEST-NET-1
MustIPv4Addr("198.51.100.0/24"), // TEST-NET-2
MustIPv4Addr("203.0.113.0/24"), // TEST-NET-3
MustIPv4Addr("198.18.0.0/15"), // Benchmarks
},
5737: {
// [RFC5737] IPv4 Address Blocks Reserved for Documentation
MustIPv4Addr("192.0.2.0/24"), // TEST-NET-1
MustIPv4Addr("198.51.100.0/24"), // TEST-NET-2
MustIPv4Addr("203.0.113.0/24"), // TEST-NET-3
},
6052: {
// [RFC6052] IPv6 Addressing of IPv4/IPv6 Translators
MustIPv6Addr("64:ff9b::/96"), // [RFC6052], §2.1. Well-Known Prefix
},
6333: {
// [RFC6333] Dual-Stack Lite Broadband Deployments Following IPv4 Exhaustion
MustIPv4Addr("192.0.0.0/29"), // [RFC6333], §5.7 Well-Known IPv4 Address
},
6598: {
// [RFC6598] IANA-Reserved IPv4 Prefix for Shared Address Space
MustIPv4Addr("100.64.0.0/10"),
},
6666: {
// [RFC6666] A Discard Prefix for IPv6
MustIPv6Addr("0100::/64"),
},
6890: {
// [RFC6890] Special-Purpose IP Address Registries
// From "RFC6890 §2.2.1 Information Requirements":
/*
The IPv4 and IPv6 Special-Purpose Address Registries maintain the
following information regarding each entry:
o Address Block - A block of IPv4 or IPv6 addresses that has been
registered for a special purpose.
o Name - A descriptive name for the special-purpose address block.
o RFC - The RFC through which the special-purpose address block was
requested.
o Allocation Date - The date upon which the special-purpose address
block was allocated.
o Termination Date - The date upon which the allocation is to be
terminated. This field is applicable for limited-use allocations
only.
o Source - A boolean value indicating whether an address from the
allocated special-purpose address block is valid when used as the
source address of an IP datagram that transits two devices.
o Destination - A boolean value indicating whether an address from
the allocated special-purpose address block is valid when used as
the destination address of an IP datagram that transits two
devices.
o Forwardable - A boolean value indicating whether a router may
forward an IP datagram whose destination address is drawn from the
allocated special-purpose address block between external
interfaces.
o Global - A boolean value indicating whether an IP datagram whose
destination address is drawn from the allocated special-purpose
address block is forwardable beyond a specified administrative
domain.
o Reserved-by-Protocol - A boolean value indicating whether the
special-purpose address block is reserved by IP, itself. This
value is "TRUE" if the RFC that created the special-purpose
address block requires all compliant IP implementations to behave
in a special way when processing packets either to or from
addresses contained by the address block.
If the value of "Destination" is FALSE, the values of "Forwardable"
and "Global" must also be false.
*/
/*+----------------------+----------------------------+
* | Attribute | Value |
* +----------------------+----------------------------+
* | Address Block | 0.0.0.0/8 |
* | Name | "This host on this network"|
* | RFC | [RFC1122], Section 3.2.1.3 |
* | Allocation Date | September 1981 |
* | Termination Date | N/A |
* | Source | True |
* | Destination | False |
* | Forwardable | False |
* | Global | False |
* | Reserved-by-Protocol | True |
* +----------------------+----------------------------+*/
MustIPv4Addr("0.0.0.0/8"),
/*+----------------------+---------------+
* | Attribute | Value |
* +----------------------+---------------+
* | Address Block | 10.0.0.0/8 |
* | Name | Private-Use |
* | RFC | [RFC1918] |
* | Allocation Date | February 1996 |
* | Termination Date | N/A |
* | Source | True |
* | Destination | True |
* | Forwardable | True |
* | Global | False |
* | Reserved-by-Protocol | False |
* +----------------------+---------------+ */
MustIPv4Addr("10.0.0.0/8"),
/*+----------------------+----------------------+
| Attribute | Value |
+----------------------+----------------------+
| Address Block | 100.64.0.0/10 |
| Name | Shared Address Space |
| RFC | [RFC6598] |
| Allocation Date | April 2012 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | True |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+----------------------+*/
MustIPv4Addr("100.64.0.0/10"),
/*+----------------------+----------------------------+
| Attribute | Value |
+----------------------+----------------------------+
| Address Block | 127.0.0.0/8 |
| Name | Loopback |
| RFC | [RFC1122], Section 3.2.1.3 |
| Allocation Date | September 1981 |
| Termination Date | N/A |
| Source | False [1] |
| Destination | False [1] |
| Forwardable | False [1] |
| Global | False [1] |
| Reserved-by-Protocol | True |
+----------------------+----------------------------+*/
// [1] Several protocols have been granted exceptions to
// this rule. For examples, see [RFC4379] and
// [RFC5884].
MustIPv4Addr("127.0.0.0/8"),
/*+----------------------+----------------+
| Attribute | Value |
+----------------------+----------------+
| Address Block | 169.254.0.0/16 |
| Name | Link Local |
| RFC | [RFC3927] |
| Allocation Date | May 2005 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | False |
| Global | False |
| Reserved-by-Protocol | True |
+----------------------+----------------+*/
MustIPv4Addr("169.254.0.0/16"),
/*+----------------------+---------------+
| Attribute | Value |
+----------------------+---------------+
| Address Block | 172.16.0.0/12 |
| Name | Private-Use |
| RFC | [RFC1918] |
| Allocation Date | February 1996 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | True |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+---------------+*/
MustIPv4Addr("172.16.0.0/12"),
/*+----------------------+---------------------------------+
| Attribute | Value |
+----------------------+---------------------------------+
| Address Block | 192.0.0.0/24 [2] |
| Name | IETF Protocol Assignments |
| RFC | Section 2.1 of this document |
| Allocation Date | January 2010 |
| Termination Date | N/A |
| Source | False |
| Destination | False |
| Forwardable | False |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+---------------------------------+*/
// [2] Not usable unless by virtue of a more specific
// reservation.
MustIPv4Addr("192.0.0.0/24"),
/*+----------------------+--------------------------------+
| Attribute | Value |
+----------------------+--------------------------------+
| Address Block | 192.0.0.0/29 |
| Name | IPv4 Service Continuity Prefix |
| RFC | [RFC6333], [RFC7335] |
| Allocation Date | June 2011 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | True |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+--------------------------------+*/
MustIPv4Addr("192.0.0.0/29"),
/*+----------------------+----------------------------+
| Attribute | Value |
+----------------------+----------------------------+
| Address Block | 192.0.2.0/24 |
| Name | Documentation (TEST-NET-1) |
| RFC | [RFC5737] |
| Allocation Date | January 2010 |
| Termination Date | N/A |
| Source | False |
| Destination | False |
| Forwardable | False |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+----------------------------+*/
MustIPv4Addr("192.0.2.0/24"),
/*+----------------------+--------------------+
| Attribute | Value |
+----------------------+--------------------+
| Address Block | 192.88.99.0/24 |
| Name | 6to4 Relay Anycast |
| RFC | [RFC3068] |
| Allocation Date | June 2001 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | True |
| Global | True |
| Reserved-by-Protocol | False |
+----------------------+--------------------+*/
MustIPv4Addr("192.88.99.0/24"),
/*+----------------------+----------------+
| Attribute | Value |
+----------------------+----------------+
| Address Block | 192.168.0.0/16 |
| Name | Private-Use |
| RFC | [RFC1918] |
| Allocation Date | February 1996 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | True |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+----------------+*/
MustIPv4Addr("192.168.0.0/16"),
/*+----------------------+---------------+
| Attribute | Value |
+----------------------+---------------+
| Address Block | 198.18.0.0/15 |
| Name | Benchmarking |
| RFC | [RFC2544] |
| Allocation Date | March 1999 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | True |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+---------------+*/
MustIPv4Addr("198.18.0.0/15"),
/*+----------------------+----------------------------+
| Attribute | Value |
+----------------------+----------------------------+
| Address Block | 198.51.100.0/24 |
| Name | Documentation (TEST-NET-2) |
| RFC | [RFC5737] |
| Allocation Date | January 2010 |
| Termination Date | N/A |
| Source | False |
| Destination | False |
| Forwardable | False |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+----------------------------+*/
MustIPv4Addr("198.51.100.0/24"),
/*+----------------------+----------------------------+
| Attribute | Value |
+----------------------+----------------------------+
| Address Block | 203.0.113.0/24 |
| Name | Documentation (TEST-NET-3) |
| RFC | [RFC5737] |
| Allocation Date | January 2010 |
| Termination Date | N/A |
| Source | False |
| Destination | False |
| Forwardable | False |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+----------------------------+*/
MustIPv4Addr("203.0.113.0/24"),
/*+----------------------+----------------------+
| Attribute | Value |
+----------------------+----------------------+
| Address Block | 240.0.0.0/4 |
| Name | Reserved |
| RFC | [RFC1112], Section 4 |
| Allocation Date | August 1989 |
| Termination Date | N/A |
| Source | False |
| Destination | False |
| Forwardable | False |
| Global | False |
| Reserved-by-Protocol | True |
+----------------------+----------------------+*/
MustIPv4Addr("240.0.0.0/4"),
/*+----------------------+----------------------+
| Attribute | Value |
+----------------------+----------------------+
| Address Block | 255.255.255.255/32 |
| Name | Limited Broadcast |
| RFC | [RFC0919], Section 7 |
| Allocation Date | October 1984 |
| Termination Date | N/A |
| Source | False |
| Destination | True |
| Forwardable | False |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+----------------------+*/
MustIPv4Addr("255.255.255.255/32"),
/*+----------------------+------------------+
| Attribute | Value |
+----------------------+------------------+
| Address Block | ::1/128 |
| Name | Loopback Address |
| RFC | [RFC4291] |
| Allocation Date | February 2006 |
| Termination Date | N/A |
| Source | False |
| Destination | False |
| Forwardable | False |
| Global | False |
| Reserved-by-Protocol | True |
+----------------------+------------------+*/
MustIPv6Addr("::1/128"),
/*+----------------------+---------------------+
| Attribute | Value |
+----------------------+---------------------+
| Address Block | ::/128 |
| Name | Unspecified Address |
| RFC | [RFC4291] |
| Allocation Date | February 2006 |
| Termination Date | N/A |
| Source | True |
| Destination | False |
| Forwardable | False |
| Global | False |
| Reserved-by-Protocol | True |
+----------------------+---------------------+*/
MustIPv6Addr("::/128"),
/*+----------------------+---------------------+
| Attribute | Value |
+----------------------+---------------------+
| Address Block | 64:ff9b::/96 |
| Name | IPv4-IPv6 Translat. |
| RFC | [RFC6052] |
| Allocation Date | October 2010 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | True |
| Global | True |
| Reserved-by-Protocol | False |
+----------------------+---------------------+*/
MustIPv6Addr("64:ff9b::/96"),
/*+----------------------+---------------------+
| Attribute | Value |
+----------------------+---------------------+
| Address Block | ::ffff:0:0/96 |
| Name | IPv4-mapped Address |
| RFC | [RFC4291] |
| Allocation Date | February 2006 |
| Termination Date | N/A |
| Source | False |
| Destination | False |
| Forwardable | False |
| Global | False |
| Reserved-by-Protocol | True |
+----------------------+---------------------+*/
MustIPv6Addr("::ffff:0:0/96"),
/*+----------------------+----------------------------+
| Attribute | Value |
+----------------------+----------------------------+
| Address Block | 100::/64 |
| Name | Discard-Only Address Block |
| RFC | [RFC6666] |
| Allocation Date | June 2012 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | True |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+----------------------------+*/
MustIPv6Addr("100::/64"),
/*+----------------------+---------------------------+
| Attribute | Value |
+----------------------+---------------------------+
| Address Block | 2001::/23 |
| Name | IETF Protocol Assignments |
| RFC | [RFC2928] |
| Allocation Date | September 2000 |
| Termination Date | N/A |
| Source | False[1] |
| Destination | False[1] |
| Forwardable | False[1] |
| Global | False[1] |
| Reserved-by-Protocol | False |
+----------------------+---------------------------+*/
// [1] Unless allowed by a more specific allocation.
MustIPv6Addr("2001::/16"),
/*+----------------------+----------------+
| Attribute | Value |
+----------------------+----------------+
| Address Block | 2001::/32 |
| Name | TEREDO |
| RFC | [RFC4380] |
| Allocation Date | January 2006 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | True |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+----------------+*/
// Covered by previous entry, included for completeness.
//
// MustIPv6Addr("2001::/16"),
/*+----------------------+----------------+
| Attribute | Value |
+----------------------+----------------+
| Address Block | 2001:2::/48 |
| Name | Benchmarking |
| RFC | [RFC5180] |
| Allocation Date | April 2008 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | True |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+----------------+*/
// Covered by previous entry, included for completeness.
//
// MustIPv6Addr("2001:2::/48"),
/*+----------------------+---------------+
| Attribute | Value |
+----------------------+---------------+
| Address Block | 2001:db8::/32 |
| Name | Documentation |
| RFC | [RFC3849] |
| Allocation Date | July 2004 |
| Termination Date | N/A |
| Source | False |
| Destination | False |
| Forwardable | False |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+---------------+*/
// Covered by previous entry, included for completeness.
//
// MustIPv6Addr("2001:db8::/32"),
/*+----------------------+--------------+
| Attribute | Value |
+----------------------+--------------+
| Address Block | 2001:10::/28 |
| Name | ORCHID |
| RFC | [RFC4843] |
| Allocation Date | March 2007 |
| Termination Date | March 2014 |
| Source | False |
| Destination | False |
| Forwardable | False |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+--------------+*/
// Covered by previous entry, included for completeness.
//
// MustIPv6Addr("2001:10::/28"),
/*+----------------------+---------------+
| Attribute | Value |
+----------------------+---------------+
| Address Block | 2002::/16 [2] |
| Name | 6to4 |
| RFC | [RFC3056] |
| Allocation Date | February 2001 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | True |
| Global | N/A [2] |
| Reserved-by-Protocol | False |
+----------------------+---------------+*/
// [2] See [RFC3056] for details.
MustIPv6Addr("2002::/16"),
/*+----------------------+--------------+
| Attribute | Value |
+----------------------+--------------+
| Address Block | fc00::/7 |
| Name | Unique-Local |
| RFC | [RFC4193] |
| Allocation Date | October 2005 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | True |
| Global | False |
| Reserved-by-Protocol | False |
+----------------------+--------------+*/
MustIPv6Addr("fc00::/7"),
/*+----------------------+-----------------------+
| Attribute | Value |
+----------------------+-----------------------+
| Address Block | fe80::/10 |
| Name | Linked-Scoped Unicast |
| RFC | [RFC4291] |
| Allocation Date | February 2006 |
| Termination Date | N/A |
| Source | True |
| Destination | True |
| Forwardable | False |
| Global | False |
| Reserved-by-Protocol | True |
+----------------------+-----------------------+*/
MustIPv6Addr("fe80::/10"),
},
7335: {
// [RFC7335] IPv4 Service Continuity Prefix
MustIPv4Addr("192.0.0.0/29"), // [RFC7335], §6 IANA Considerations
},
ForwardingBlacklist: { // Pseudo-RFC
// Blacklist of non-forwardable IP blocks taken from RFC6890
//
// TODO: the attributes for forwardable should be
// searcahble and embedded in the main list of RFCs
// above.
MustIPv4Addr("0.0.0.0/8"),
MustIPv4Addr("127.0.0.0/8"),
MustIPv4Addr("169.254.0.0/16"),
MustIPv4Addr("192.0.0.0/24"),
MustIPv4Addr("192.0.2.0/24"),
MustIPv4Addr("198.51.100.0/24"),
MustIPv4Addr("203.0.113.0/24"),
MustIPv4Addr("240.0.0.0/4"),
MustIPv4Addr("255.255.255.255/32"),
MustIPv6Addr("::1/128"),
MustIPv6Addr("::/128"),
MustIPv6Addr("::ffff:0:0/96"),
// There is no way of expressing a whitelist per RFC2928
// atm without creating a negative mask, which I don't
// want to do atm.
//MustIPv6Addr("2001::/23"),
MustIPv6Addr("2001:db8::/32"),
MustIPv6Addr("2001:10::/28"),
MustIPv6Addr("fe80::/10"),
},
}
}
// VisitAllRFCs iterates over all known RFCs and calls the visitor
func VisitAllRFCs(fn func(rfcNum uint, sockaddrs SockAddrs)) {
rfcNetMap := KnownRFCs()
// Blacklist of faux-RFCs. Don't show the world that we're abusing the
// RFC system in this library.
rfcBlacklist := map[uint]struct{}{
ForwardingBlacklist: {},
}
for rfcNum, sas := range rfcNetMap {
if _, found := rfcBlacklist[rfcNum]; !found {
fn(rfcNum, sas)
}
}
}

19
vendor/github.com/hashicorp/go-sockaddr/route_info.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
package sockaddr
// RouteInterface specifies an interface for obtaining memoized route table and
// network information from a given OS.
type RouteInterface interface {
// GetDefaultInterfaceName returns the name of the interface that has a
// default route or an error and an empty string if a problem was
// encountered.
GetDefaultInterfaceName() (string, error)
}
// VisitCommands visits each command used by the platform-specific RouteInfo
// implementation.
func (ri routeInfo) VisitCommands(fn func(name string, cmd []string)) {
for k, v := range ri.cmds {
cmds := append([]string(nil), v...)
fn(k, cmds)
}
}

View File

@ -0,0 +1,36 @@
// +build darwin dragonfly freebsd netbsd openbsd
package sockaddr
import "os/exec"
var cmds map[string][]string = map[string][]string{
"route": {"/sbin/route", "-n", "get", "default"},
}
type routeInfo struct {
cmds map[string][]string
}
// NewRouteInfo returns a BSD-specific implementation of the RouteInfo
// interface.
func NewRouteInfo() (routeInfo, error) {
return routeInfo{
cmds: cmds,
}, nil
}
// GetDefaultInterfaceName returns the interface name attached to the default
// route on the default interface.
func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
out, err := exec.Command(cmds["route"][0], cmds["route"][1:]...).Output()
if err != nil {
return "", err
}
var ifName string
if ifName, err = parseDefaultIfNameFromRoute(string(out)); err != nil {
return "", err
}
return ifName, nil
}

View File

@ -0,0 +1,10 @@
// +build android nacl plan9
package sockaddr
import "errors"
// getDefaultIfName is the default interface function for unsupported platforms.
func getDefaultIfName() (string, error) {
return "", errors.New("No default interface found (unsupported platform)")
}

View File

@ -0,0 +1,40 @@
package sockaddr
import (
"errors"
"os/exec"
)
type routeInfo struct {
cmds map[string][]string
}
// NewRouteInfo returns a Linux-specific implementation of the RouteInfo
// interface.
func NewRouteInfo() (routeInfo, error) {
// CoreOS Container Linux moved ip to /usr/bin/ip, so look it up on
// $PATH and fallback to /sbin/ip on error.
path, _ := exec.LookPath("ip")
if path == "" {
path = "/sbin/ip"
}
return routeInfo{
cmds: map[string][]string{"ip": {path, "route"}},
}, nil
}
// GetDefaultInterfaceName returns the interface name attached to the default
// route on the default interface.
func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
out, err := exec.Command(ri.cmds["ip"][0], ri.cmds["ip"][1:]...).Output()
if err != nil {
return "", err
}
var ifName string
if ifName, err = parseDefaultIfNameFromIPCmd(string(out)); err != nil {
return "", errors.New("No default interface found")
}
return ifName, nil
}

View File

@ -0,0 +1,37 @@
package sockaddr
import (
"errors"
"os/exec"
)
var cmds map[string][]string = map[string][]string{
"route": {"/usr/sbin/route", "-n", "get", "default"},
}
type routeInfo struct {
cmds map[string][]string
}
// NewRouteInfo returns a BSD-specific implementation of the RouteInfo
// interface.
func NewRouteInfo() (routeInfo, error) {
return routeInfo{
cmds: cmds,
}, nil
}
// GetDefaultInterfaceName returns the interface name attached to the default
// route on the default interface.
func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
out, err := exec.Command(cmds["route"][0], cmds["route"][1:]...).Output()
if err != nil {
return "", err
}
var ifName string
if ifName, err = parseDefaultIfNameFromRoute(string(out)); err != nil {
return "", errors.New("No default interface found")
}
return ifName, nil
}

View File

@ -0,0 +1,41 @@
package sockaddr
import "os/exec"
var cmds map[string][]string = map[string][]string{
"netstat": {"netstat", "-rn"},
"ipconfig": {"ipconfig"},
}
type routeInfo struct {
cmds map[string][]string
}
// NewRouteInfo returns a BSD-specific implementation of the RouteInfo
// interface.
func NewRouteInfo() (routeInfo, error) {
return routeInfo{
cmds: cmds,
}, nil
}
// GetDefaultInterfaceName returns the interface name attached to the default
// route on the default interface.
func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
ifNameOut, err := exec.Command(cmds["netstat"][0], cmds["netstat"][1:]...).Output()
if err != nil {
return "", err
}
ipconfigOut, err := exec.Command(cmds["ipconfig"][0], cmds["ipconfig"][1:]...).Output()
if err != nil {
return "", err
}
ifName, err := parseDefaultIfNameWindows(string(ifNameOut), string(ipconfigOut))
if err != nil {
return "", err
}
return ifName, nil
}

Some files were not shown because too many files have changed in this diff Show More