├── Godeps ├── _workspace │ ├── .gitignore │ └── src │ │ └── github.com │ │ ├── hashicorp │ │ ├── serf │ │ │ ├── ops-misc │ │ │ │ └── debian │ │ │ │ │ └── copyright │ │ │ ├── coordinate │ │ │ │ ├── README.md │ │ │ │ ├── test_util.go │ │ │ │ ├── config.go │ │ │ │ ├── phantom.go │ │ │ │ ├── client.go │ │ │ │ └── coordinate.go │ │ │ ├── website │ │ │ │ └── LICENSE.md │ │ │ └── LICENSE │ │ ├── consul │ │ │ ├── website │ │ │ │ └── LICENSE.md │ │ │ ├── api │ │ │ │ ├── raw.go │ │ │ │ ├── README.md │ │ │ │ ├── status.go │ │ │ │ ├── coordinate.go │ │ │ │ ├── event.go │ │ │ │ ├── health.go │ │ │ │ ├── acl.go │ │ │ │ ├── catalog.go │ │ │ │ ├── prepared_query.go │ │ │ │ ├── session.go │ │ │ │ ├── kv.go │ │ │ │ ├── agent.go │ │ │ │ ├── lock.go │ │ │ │ ├── api.go │ │ │ │ └── semaphore.go │ │ │ └── LICENSE │ │ └── go-cleanhttp │ │ │ ├── cleanhttp.go │ │ │ └── README.md │ │ └── divideandconquer │ │ └── go-merge │ │ ├── merge │ │ └── merge.go │ │ └── LICENSE ├── Readme └── Godeps.json ├── Dockerfile ├── test.json ├── .gitignore ├── src ├── balancer │ ├── balancer.go │ ├── mock.go │ ├── map.go │ └── consul │ │ └── random.go ├── config │ ├── loader.go │ ├── map.go │ └── loadermock │ │ └── mock_loader.go ├── main │ └── main.go └── client │ ├── mock.go │ └── client.go ├── go.mod ├── README.md ├── go.sum └── LICENSE /Godeps/_workspace/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | ADD /bin/app /app 4 | 5 | ENTRYPOINT ["/app"] -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/serf/ops-misc/debian/copyright: -------------------------------------------------------------------------------- 1 | Name: serf 2 | Copyright: Hashicorp 2013 3 | -------------------------------------------------------------------------------- /test.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "bar", 3 | "baz": { 4 | "fiz": "buz", 5 | "pop": 5, 6 | "whomp": [1, 2, 3] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/serf/coordinate/README.md: -------------------------------------------------------------------------------- 1 | # TODO - I'll beef this up as I implement each of the enhancements. -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | bin 3 | 4 | # Dependency directories 5 | vendor 6 | 7 | # IDEs and editors 8 | .idea 9 | 10 | # OS 11 | .DS_Store 12 | -------------------------------------------------------------------------------- /src/balancer/balancer.go: -------------------------------------------------------------------------------- 1 | package balancer 2 | 3 | import "net/url" 4 | 5 | // DNS balancer finds services through dns and balances load across them 6 | type DNS interface { 7 | FindService(serviceName string) (*ServiceLocation, error) 8 | GetHttpUrl(serviceName string, useTLS bool) (url.URL, error) 9 | } 10 | 11 | // ServiceLocation is a represensation of where a service lives 12 | type ServiceLocation struct { 13 | URL string 14 | Port int 15 | } 16 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/divideandconquer/go-consul-client 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/divideandconquer/go-merge v0.0.0-20150616173453-dc9048d04b65 7 | github.com/golang/mock v1.5.0 // indirect 8 | github.com/hashicorp/consul v0.6.1-0.20151204164059-71bffe81d1a2 9 | github.com/hashicorp/go-cleanhttp v0.0.0-20151022142711-5df5ddc69534 // indirect 10 | github.com/hashicorp/serf v0.6.5-0.20151205003656-e9ac4bb0c572 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/serf/website/LICENSE.md: -------------------------------------------------------------------------------- 1 | # Proprietary License 2 | 3 | This license is temporary while a more official one is drafted. However, 4 | this should make it clear: 5 | 6 | * The text contents of this website are MPL 2.0 licensed. 7 | 8 | * The design contents of this website are proprietary and may not be reproduced 9 | or reused in any way other than to run the Serf website locally. The license 10 | for the design is owned solely by HashiCorp, Inc. 11 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/website/LICENSE.md: -------------------------------------------------------------------------------- 1 | # Proprietary License 2 | 3 | This license is temporary while a more official one is drafted. However, 4 | this should make it clear: 5 | 6 | * The text contents of this website are MPL 2.0 licensed. 7 | 8 | * The design contents of this website are proprietary and may not be reproduced 9 | or reused in any way other than to run the Consul website locally. The license 10 | for the design is owned solely by HashiCorp, Inc. 11 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/divideandconquer/go-consul-client", 3 | "GoVersion": "go1.5.1", 4 | "Packages": [ 5 | "./..." 6 | ], 7 | "Deps": [ 8 | { 9 | "ImportPath": "github.com/divideandconquer/go-merge/merge", 10 | "Rev": "dc9048d04b65a57f75d8a3875a0fb85ca1f70624" 11 | }, 12 | { 13 | "ImportPath": "github.com/hashicorp/consul/api", 14 | "Comment": "v0.6.0-rc2-37-g71bffe8", 15 | "Rev": "71bffe81d1a28e0ad3bbcf605220a2bc1e9af24f" 16 | }, 17 | { 18 | "ImportPath": "github.com/hashicorp/go-cleanhttp", 19 | "Rev": "5df5ddc69534f1a4697289f1dca2193fbb40213f" 20 | }, 21 | { 22 | "ImportPath": "github.com/hashicorp/serf/coordinate", 23 | "Comment": "v0.6.4-150-ge9ac4bb", 24 | "Rev": "e9ac4bb0c5721826d5cd3d1cd582c12fbc664bb8" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/go-cleanhttp/cleanhttp.go: -------------------------------------------------------------------------------- 1 | package cleanhttp 2 | 3 | import ( 4 | "net" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | // DefaultTransport returns a new http.Transport with the same default values 10 | // as http.DefaultTransport 11 | func DefaultTransport() *http.Transport { 12 | return &http.Transport{ 13 | Proxy: http.ProxyFromEnvironment, 14 | Dial: (&net.Dialer{ 15 | Timeout: 30 * time.Second, 16 | KeepAlive: 30 * time.Second, 17 | }).Dial, 18 | TLSHandshakeTimeout: 10 * time.Second, 19 | } 20 | } 21 | 22 | // DefaultClient returns a new http.Client with the same default values as 23 | // http.Client, but with a non-shared Transport 24 | func DefaultClient() *http.Client { 25 | return &http.Client{ 26 | Transport: DefaultTransport(), 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/config/loader.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "time" 4 | 5 | // Loader is a object that can import, initialize, and Get config values 6 | //go:generate go run -mod=mod github.com/golang/mock/mockgen -package loadermock -destination=./loadermock/mock_loader.go -source=../config/loader.go -build_flags=-mod=mod 7 | type Loader interface { 8 | Import(data []byte) error 9 | Initialize() error 10 | Get(key string) ([]byte, error) 11 | Put(key string, value []byte) error 12 | 13 | // Must functions will panic if they can't do what is requested. 14 | // They are maingly meant for use with configs that are required for an app to start up 15 | MustGetString(key string) string 16 | MustGetBool(key string) bool 17 | MustGetInt(key string) int 18 | MustGetDuration(key string) time.Duration 19 | 20 | //TODO add array support? 21 | } 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/serf/coordinate/test_util.go: -------------------------------------------------------------------------------- 1 | package coordinate 2 | 3 | import ( 4 | "math" 5 | "testing" 6 | ) 7 | 8 | // verifyEqualFloats will compare f1 and f2 and fail if they are not 9 | // "equal" within a threshold. 10 | func verifyEqualFloats(t *testing.T, f1 float64, f2 float64) { 11 | const zeroThreshold = 1.0e-6 12 | if math.Abs(f1-f2) > zeroThreshold { 13 | t.Fatalf("equal assertion fail, %9.6f != %9.6f", f1, f2) 14 | } 15 | } 16 | 17 | // verifyEqualVectors will compare vec1 and vec2 and fail if they are not 18 | // "equal" within a threshold. 19 | func verifyEqualVectors(t *testing.T, vec1 []float64, vec2 []float64) { 20 | if len(vec1) != len(vec2) { 21 | t.Fatalf("vector length mismatch, %d != %d", len(vec1), len(vec2)) 22 | } 23 | 24 | for i, _ := range vec1 { 25 | verifyEqualFloats(t, vec1[i], vec2[i]) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/raw.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | // Raw can be used to do raw queries against custom endpoints 4 | type Raw struct { 5 | c *Client 6 | } 7 | 8 | // Raw returns a handle to query endpoints 9 | func (c *Client) Raw() *Raw { 10 | return &Raw{c} 11 | } 12 | 13 | // Query is used to do a GET request against an endpoint 14 | // and deserialize the response into an interface using 15 | // standard Consul conventions. 16 | func (raw *Raw) Query(endpoint string, out interface{}, q *QueryOptions) (*QueryMeta, error) { 17 | return raw.c.query(endpoint, out, q) 18 | } 19 | 20 | // Write is used to do a PUT request against an endpoint 21 | // and serialize/deserialized using the standard Consul conventions. 22 | func (raw *Raw) Write(endpoint string, in, out interface{}, q *WriteOptions) (*WriteMeta, error) { 23 | return raw.c.write(endpoint, in, out, q) 24 | } 25 | -------------------------------------------------------------------------------- /src/balancer/mock.go: -------------------------------------------------------------------------------- 1 | package balancer 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/url" 7 | "strconv" 8 | ) 9 | 10 | type mockBalancer struct { 11 | services map[string]*ServiceLocation 12 | } 13 | 14 | func NewMockDNSBalancer(services map[string]*ServiceLocation) DNS { 15 | return &mockBalancer{services: services} 16 | } 17 | 18 | func (m *mockBalancer) FindService(serviceName string) (*ServiceLocation, error) { 19 | if s, ok := m.services[serviceName]; ok { 20 | return s, nil 21 | } 22 | return nil, fmt.Errorf("Could not find %s", serviceName) 23 | } 24 | 25 | func (r *mockBalancer) GetHttpUrl(serviceName string, useTLS bool) (url.URL, error) { 26 | result := url.URL{} 27 | loc, err := r.FindService(serviceName) 28 | if err != nil { 29 | return result, err 30 | } 31 | result.Host = loc.URL 32 | if loc.Port != 0 { 33 | result.Host = net.JoinHostPort(loc.URL, strconv.Itoa(loc.Port)) 34 | } 35 | if useTLS { 36 | result.Scheme = "https" 37 | } else { 38 | result.Scheme = "http" 39 | } 40 | return result, nil 41 | } 42 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/README.md: -------------------------------------------------------------------------------- 1 | Consul API client 2 | ================= 3 | 4 | This package provides the `api` package which attempts to 5 | provide programmatic access to the full Consul API. 6 | 7 | Currently, all of the Consul APIs included in version 0.6.0 are supported. 8 | 9 | Documentation 10 | ============= 11 | 12 | The full documentation is available on [Godoc](http://godoc.org/github.com/hashicorp/consul/api) 13 | 14 | Usage 15 | ===== 16 | 17 | Below is an example of using the Consul client: 18 | 19 | ```go 20 | // Get a new client 21 | client, err := api.NewClient(api.DefaultConfig()) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | // Get a handle to the KV API 27 | kv := client.KV() 28 | 29 | // PUT a new KV pair 30 | p := &api.KVPair{Key: "foo", Value: []byte("test")} 31 | _, err = kv.Put(p, nil) 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | // Lookup the pair 37 | pair, _, err := kv.Get("foo", nil) 38 | if err != nil { 39 | panic(err) 40 | } 41 | fmt.Printf("KV: %v", pair) 42 | 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/status.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | // Status can be used to query the Status endpoints 4 | type Status struct { 5 | c *Client 6 | } 7 | 8 | // Status returns a handle to the status endpoints 9 | func (c *Client) Status() *Status { 10 | return &Status{c} 11 | } 12 | 13 | // Leader is used to query for a known leader 14 | func (s *Status) Leader() (string, error) { 15 | r := s.c.newRequest("GET", "/v1/status/leader") 16 | _, resp, err := requireOK(s.c.doRequest(r)) 17 | if err != nil { 18 | return "", err 19 | } 20 | defer resp.Body.Close() 21 | 22 | var leader string 23 | if err := decodeBody(resp, &leader); err != nil { 24 | return "", err 25 | } 26 | return leader, nil 27 | } 28 | 29 | // Peers is used to query for a known raft peers 30 | func (s *Status) Peers() ([]string, error) { 31 | r := s.c.newRequest("GET", "/v1/status/peers") 32 | _, resp, err := requireOK(s.c.doRequest(r)) 33 | if err != nil { 34 | return nil, err 35 | } 36 | defer resp.Body.Close() 37 | 38 | var peers []string 39 | if err := decodeBody(resp, &peers); err != nil { 40 | return nil, err 41 | } 42 | return peers, nil 43 | } 44 | -------------------------------------------------------------------------------- /src/balancer/map.go: -------------------------------------------------------------------------------- 1 | package balancer 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/url" 7 | "strconv" 8 | ) 9 | 10 | type mapBalancer struct { 11 | services map[string]string 12 | } 13 | 14 | // NewMapBalancer will return the URL from the configured map name -> URL:port 15 | func NewMapBalancer(services map[string]string) DNS { 16 | return &mapBalancer{services: services} 17 | } 18 | 19 | func (m *mapBalancer) FindService(serviceName string) (*ServiceLocation, error) { 20 | s, ok := m.services[serviceName] 21 | if !ok { 22 | return nil, fmt.Errorf("Could not find %s", serviceName) 23 | } 24 | 25 | host, port, err := net.SplitHostPort(s) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | p, err := strconv.Atoi(port) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | return &ServiceLocation{URL: host, Port: p}, nil 36 | } 37 | 38 | func (m *mapBalancer) GetHttpUrl(serviceName string, useTLS bool) (url.URL, error) { 39 | result := url.URL{} 40 | loc, err := m.FindService(serviceName) 41 | if err != nil { 42 | return result, err 43 | } 44 | result.Host = loc.URL 45 | if loc.Port != 0 { 46 | result.Host = net.JoinHostPort(loc.URL, strconv.Itoa(loc.Port)) 47 | } 48 | if useTLS { 49 | result.Scheme = "https" 50 | } else { 51 | result.Scheme = "http" 52 | } 53 | return result, nil 54 | } 55 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/go-cleanhttp/README.md: -------------------------------------------------------------------------------- 1 | # cleanhttp 2 | 3 | Functions for accessing "clean" Go http.Client values 4 | 5 | ------------- 6 | 7 | The Go standard library contains a default `http.Client` called 8 | `http.DefaultClient`. It is a common idiom in Go code to start with 9 | `http.DefaultClient` and tweak it as necessary, and in fact, this is 10 | encouraged; from the `http` package documentation: 11 | 12 | > The Client's Transport typically has internal state (cached TCP connections), 13 | so Clients should be reused instead of created as needed. Clients are safe for 14 | concurrent use by multiple goroutines. 15 | 16 | Unfortunately, this is a shared value, and it is not uncommon for libraries to 17 | assume that they are free to modify it at will. With enough dependencies, it 18 | can be very easy to encounter strange problems and race conditions due to 19 | manipulation of this shared value across libraries and goroutines (clients are 20 | safe for concurrent use, but writing values to the client struct itself is not 21 | protected). 22 | 23 | Making things worse is the fact that a bare `http.Client` will use a default 24 | `http.Transport` called `http.DefaultTransport`, which is another global value 25 | that behaves the same way. So it is not simply enough to replace 26 | `http.DefaultClient` with `&http.Client{}`. 27 | 28 | This repository provides some simple functions to get a "clean" `http.Client` 29 | -- one that uses the same default values as the Go standard library, but 30 | returns a client that does not share any state with other clients. 31 | -------------------------------------------------------------------------------- /src/main/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | 9 | "github.com/divideandconquer/go-consul-client/src/client" 10 | ) 11 | 12 | var filepath = flag.String("file", "", "the path to the json file") 13 | var namespace = flag.String("namespace", "", "the consul namespace to use as a prefix") 14 | var consulAddr = flag.String("consul", "", "the consul address to use as a prefix") 15 | 16 | func main() { 17 | flag.Parse() 18 | if filepath == nil || *filepath == "" { 19 | log.Printf("Missing parameter -file") 20 | printHelp() 21 | } 22 | 23 | if consulAddr == nil || *consulAddr == "" { 24 | log.Printf("Missing parameter -consul") 25 | printHelp() 26 | } 27 | 28 | if _, err := os.Stat(*filepath); os.IsNotExist(err) { 29 | log.Fatalf("Given file does not exist: %s", *filepath) 30 | } 31 | data, err := ioutil.ReadFile(*filepath) 32 | if err != nil { 33 | log.Fatalf("Error reading file %s : %v", *filepath, err) 34 | } 35 | 36 | loader, err := client.NewCachedLoader(*namespace, *consulAddr) 37 | if err != nil { 38 | log.Fatalf("Error creating loader: %v", err) 39 | } 40 | 41 | err = loader.Import(data) 42 | if err != nil { 43 | log.Fatalf("Error importing data: %v", err) 44 | } 45 | log.Printf("Json from %s successfully loaded", *filepath) 46 | } 47 | 48 | func printHelp() { 49 | log.Println("Consul Client importer will import a json file into a consul KV store.") 50 | log.Println("Usage: ") 51 | log.Println("bin/importer -file /path/to/json/file -namespace dev/config") 52 | log.Println(" -file is the path to a json file to import") 53 | log.Println(" -namespace is a prefix to use in consul") 54 | log.Println(" -consul is the address for consul. e.g. 172.17.8.101:8500") 55 | os.Exit(1) 56 | } 57 | -------------------------------------------------------------------------------- /src/client/mock.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "github.com/divideandconquer/go-consul-client/src/config" 10 | ) 11 | 12 | type mockLoader struct { 13 | data map[string]interface{} 14 | } 15 | 16 | func NewMockLoader(data map[string]interface{}) config.Loader { 17 | return &mockLoader{data} 18 | } 19 | func (m *mockLoader) Import(data []byte) error { 20 | return nil 21 | } 22 | func (m *mockLoader) Initialize() error { 23 | return nil 24 | } 25 | func (m *mockLoader) Get(key string) ([]byte, error) { 26 | if ret, ok := m.data[key]; ok { 27 | if result, ok := ret.([]byte); ok { 28 | return result, nil 29 | } 30 | } 31 | return nil, fmt.Errorf("Key (%s) not set in mock.", key) 32 | } 33 | 34 | func (m *mockLoader) MustGetString(key string) string { 35 | if ret, ok := m.data[key]; ok { 36 | if result, ok := ret.(string); ok { 37 | return result 38 | } 39 | } 40 | log.Fatalf("Key (%s) not set in mock", key) 41 | return "" 42 | } 43 | 44 | func (m *mockLoader) MustGetBool(key string) bool { 45 | if ret, ok := m.data[key]; ok { 46 | if result, ok := ret.(bool); ok { 47 | return result 48 | } 49 | } 50 | log.Fatalf("Key (%s) not set in mock", key) 51 | return false 52 | } 53 | 54 | func (m *mockLoader) MustGetInt(key string) int { 55 | if ret, ok := m.data[key]; ok { 56 | if result, ok := ret.(int); ok { 57 | return result 58 | } 59 | } 60 | log.Fatalf("Key (%s) not set in mock", key) 61 | return 0 62 | } 63 | 64 | func (m *mockLoader) MustGetDuration(key string) time.Duration { 65 | if ret, ok := m.data[key]; ok { 66 | if result, ok := ret.(time.Duration); ok { 67 | return result 68 | } 69 | } 70 | log.Fatalf("Key (%s) not set in mock", key) 71 | return 0 72 | } 73 | 74 | func (m *mockLoader) Put(key string, value []byte) error { 75 | return errors.New("Not implemented") 76 | } 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-consul-client 2 | A consul library that can load json into consul KV and read a consul KV namespace into a config cache. 3 | This repo provides both an application capable of importing JSON into consul key values as well as a library 4 | for using consul as a configuration store for golang applications. 5 | 6 | The library provides a configuration cache as well as some helper functions to convert consul configuration into 7 | strings, ints, booleans, and durations. 8 | 9 | ## Usage 10 | 11 | ### Application 12 | The easiest way to use this application is as a docker container which has been made available through [docker hub](https://hub.docker.com/r/divideandconquer/go-consul-client/): 13 | 14 | ```bash 15 | docker run -v /path/to/json/file:/config.json divideandconquer/go-consul-client -file /config.json -namespace testing/fun -consul 172.17.8.101:8500 16 | ``` 17 | 18 | You can also build this application yourself with the provide build script in `build/build.sh` and run the application binary directly. 19 | 20 | ### Library 21 | You can import this library into you golang application and then use it to access your consul configuration: 22 | 23 | ```golang 24 | 25 | import "github.com/divideandconquer/go-consul-client/src/client" 26 | 27 | 28 | func main() { 29 | consulAddress := "172.17.8.101:8500" 30 | environment := "dev" 31 | appNamespace := environment + "/" + "my-app" 32 | 33 | // create a cached loader 34 | conf, err := client.NewCachedLoader(appNamespace, consulAddress) 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | // initialize the cache 40 | err = conf.Initialize() 41 | if err != nil { 42 | panic(err) 43 | } 44 | 45 | //fetch data from the cache 46 | myConfigString := conf.MustGetString("config_key") 47 | myConfigBool := conf.MustGetBool("config_key") 48 | myConfigInt := conf.MustGetInt("config_key") 49 | myConfigDuration := conf.MustGetDuration("config_key") 50 | 51 | ... 52 | } 53 | 54 | ``` 55 | 56 | 57 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/coordinate.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/hashicorp/serf/coordinate" 5 | ) 6 | 7 | // CoordinateEntry represents a node and its associated network coordinate. 8 | type CoordinateEntry struct { 9 | Node string 10 | Coord *coordinate.Coordinate 11 | } 12 | 13 | // CoordinateDatacenterMap represents a datacenter and its associated WAN 14 | // nodes and their associates coordinates. 15 | type CoordinateDatacenterMap struct { 16 | Datacenter string 17 | Coordinates []CoordinateEntry 18 | } 19 | 20 | // Coordinate can be used to query the coordinate endpoints 21 | type Coordinate struct { 22 | c *Client 23 | } 24 | 25 | // Coordinate returns a handle to the coordinate endpoints 26 | func (c *Client) Coordinate() *Coordinate { 27 | return &Coordinate{c} 28 | } 29 | 30 | // Datacenters is used to return the coordinates of all the servers in the WAN 31 | // pool. 32 | func (c *Coordinate) Datacenters() ([]*CoordinateDatacenterMap, error) { 33 | r := c.c.newRequest("GET", "/v1/coordinate/datacenters") 34 | _, resp, err := requireOK(c.c.doRequest(r)) 35 | if err != nil { 36 | return nil, err 37 | } 38 | defer resp.Body.Close() 39 | 40 | var out []*CoordinateDatacenterMap 41 | if err := decodeBody(resp, &out); err != nil { 42 | return nil, err 43 | } 44 | return out, nil 45 | } 46 | 47 | // Nodes is used to return the coordinates of all the nodes in the LAN pool. 48 | func (c *Coordinate) Nodes(q *QueryOptions) ([]*CoordinateEntry, *QueryMeta, error) { 49 | r := c.c.newRequest("GET", "/v1/coordinate/nodes") 50 | r.setQueryOptions(q) 51 | rtt, resp, err := requireOK(c.c.doRequest(r)) 52 | if err != nil { 53 | return nil, nil, err 54 | } 55 | defer resp.Body.Close() 56 | 57 | qm := &QueryMeta{} 58 | parseQueryMeta(resp, qm) 59 | qm.RequestTime = rtt 60 | 61 | var out []*CoordinateEntry 62 | if err := decodeBody(resp, &out); err != nil { 63 | return nil, nil, err 64 | } 65 | return out, qm, nil 66 | } 67 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/divideandconquer/go-merge v0.0.0-20150616173453-dc9048d04b65 h1:iG0la6YEkkGkUq9+FuJI6fvFF2iPNL/YsaHvrwd6DuQ= 2 | github.com/divideandconquer/go-merge v0.0.0-20150616173453-dc9048d04b65/go.mod h1:Y+Et20MYTm/6Do72xZ3niVupcTZXTnsj0Y663IpuUkA= 3 | github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g= 4 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 5 | github.com/hashicorp/consul v0.6.1-0.20151204164059-71bffe81d1a2 h1:6sddL74Us7IVKJA/ETcYFPPi/00rAvrefo8t2mgV7A0= 6 | github.com/hashicorp/consul v0.6.1-0.20151204164059-71bffe81d1a2/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= 7 | github.com/hashicorp/go-cleanhttp v0.0.0-20151022142711-5df5ddc69534 h1:kclNIBKOhHhSftv4ydInkliXwCYKEa7/bq2r40DR7eI= 8 | github.com/hashicorp/go-cleanhttp v0.0.0-20151022142711-5df5ddc69534/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 9 | github.com/hashicorp/serf v0.6.5-0.20151205003656-e9ac4bb0c572 h1:ubHpf3d51aRPAxLTnS+VNIg7sWLl2BwOi2UZGEHY1yo= 10 | github.com/hashicorp/serf v0.6.5-0.20151205003656-e9ac4bb0c572/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE= 11 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 12 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 13 | golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= 14 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 15 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 16 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 17 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 18 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 19 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 20 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 21 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ= 22 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 23 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 24 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= 25 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 26 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/serf/coordinate/config.go: -------------------------------------------------------------------------------- 1 | package coordinate 2 | 3 | // Config is used to set the parameters of the Vivaldi-based coordinate mapping 4 | // algorithm. 5 | // 6 | // The following references are called out at various points in the documentation 7 | // here: 8 | // 9 | // [1] Dabek, Frank, et al. "Vivaldi: A decentralized network coordinate system." 10 | // ACM SIGCOMM Computer Communication Review. Vol. 34. No. 4. ACM, 2004. 11 | // [2] Ledlie, Jonathan, Paul Gardner, and Margo I. Seltzer. "Network Coordinates 12 | // in the Wild." NSDI. Vol. 7. 2007. 13 | // [3] Lee, Sanghwan, et al. "On suitability of Euclidean embedding for 14 | // host-based network coordinate systems." Networking, IEEE/ACM Transactions 15 | // on 18.1 (2010): 27-40. 16 | type Config struct { 17 | // The dimensionality of the coordinate system. As discussed in [2], more 18 | // dimensions improves the accuracy of the estimates up to a point. Per [2] 19 | // we chose 4 dimensions plus a non-Euclidean height. 20 | Dimensionality uint 21 | 22 | // VivaldiErrorMax is the default error value when a node hasn't yet made 23 | // any observations. It also serves as an upper limit on the error value in 24 | // case observations cause the error value to increase without bound. 25 | VivaldiErrorMax float64 26 | 27 | // VivaldiCE is a tuning factor that controls the maximum impact an 28 | // observation can have on a node's confidence. See [1] for more details. 29 | VivaldiCE float64 30 | 31 | // VivaldiCC is a tuning factor that controls the maximum impact an 32 | // observation can have on a node's coordinate. See [1] for more details. 33 | VivaldiCC float64 34 | 35 | // AdjustmentWindowSize is a tuning factor that determines how many samples 36 | // we retain to calculate the adjustment factor as discussed in [3]. Setting 37 | // this to zero disables this feature. 38 | AdjustmentWindowSize uint 39 | 40 | // HeightMin is the minimum value of the height parameter. Since this 41 | // always must be positive, it will introduce a small amount error, so 42 | // the chosen value should be relatively small compared to "normal" 43 | // coordinates. 44 | HeightMin float64 45 | 46 | // LatencyFilterSamples is the maximum number of samples that are retained 47 | // per node, in order to compute a median. The intent is to ride out blips 48 | // but still keep the delay low, since our time to probe any given node is 49 | // pretty infrequent. See [2] for more details. 50 | LatencyFilterSize uint 51 | 52 | // GravityRho is a tuning factor that sets how much gravity has an effect 53 | // to try to re-center coordinates. See [2] for more details. 54 | GravityRho float64 55 | } 56 | 57 | // DefaultConfig returns a Config that has some default values suitable for 58 | // basic testing of the algorithm, but not tuned to any particular type of cluster. 59 | func DefaultConfig() *Config { 60 | return &Config{ 61 | Dimensionality: 8, 62 | VivaldiErrorMax: 1.5, 63 | VivaldiCE: 0.25, 64 | VivaldiCC: 0.25, 65 | AdjustmentWindowSize: 20, 66 | HeightMin: 10.0e-6, 67 | LatencyFilterSize: 3, 68 | GravityRho: 150.0, 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/event.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "bytes" 5 | "strconv" 6 | ) 7 | 8 | // Event can be used to query the Event endpoints 9 | type Event struct { 10 | c *Client 11 | } 12 | 13 | // UserEvent represents an event that was fired by the user 14 | type UserEvent struct { 15 | ID string 16 | Name string 17 | Payload []byte 18 | NodeFilter string 19 | ServiceFilter string 20 | TagFilter string 21 | Version int 22 | LTime uint64 23 | } 24 | 25 | // Event returns a handle to the event endpoints 26 | func (c *Client) Event() *Event { 27 | return &Event{c} 28 | } 29 | 30 | // Fire is used to fire a new user event. Only the Name, Payload and Filters 31 | // are respected. This returns the ID or an associated error. Cross DC requests 32 | // are supported. 33 | func (e *Event) Fire(params *UserEvent, q *WriteOptions) (string, *WriteMeta, error) { 34 | r := e.c.newRequest("PUT", "/v1/event/fire/"+params.Name) 35 | r.setWriteOptions(q) 36 | if params.NodeFilter != "" { 37 | r.params.Set("node", params.NodeFilter) 38 | } 39 | if params.ServiceFilter != "" { 40 | r.params.Set("service", params.ServiceFilter) 41 | } 42 | if params.TagFilter != "" { 43 | r.params.Set("tag", params.TagFilter) 44 | } 45 | if params.Payload != nil { 46 | r.body = bytes.NewReader(params.Payload) 47 | } 48 | 49 | rtt, resp, err := requireOK(e.c.doRequest(r)) 50 | if err != nil { 51 | return "", nil, err 52 | } 53 | defer resp.Body.Close() 54 | 55 | wm := &WriteMeta{RequestTime: rtt} 56 | var out UserEvent 57 | if err := decodeBody(resp, &out); err != nil { 58 | return "", nil, err 59 | } 60 | return out.ID, wm, nil 61 | } 62 | 63 | // List is used to get the most recent events an agent has received. 64 | // This list can be optionally filtered by the name. This endpoint supports 65 | // quasi-blocking queries. The index is not monotonic, nor does it provide provide 66 | // LastContact or KnownLeader. 67 | func (e *Event) List(name string, q *QueryOptions) ([]*UserEvent, *QueryMeta, error) { 68 | r := e.c.newRequest("GET", "/v1/event/list") 69 | r.setQueryOptions(q) 70 | if name != "" { 71 | r.params.Set("name", name) 72 | } 73 | rtt, resp, err := requireOK(e.c.doRequest(r)) 74 | if err != nil { 75 | return nil, nil, err 76 | } 77 | defer resp.Body.Close() 78 | 79 | qm := &QueryMeta{} 80 | parseQueryMeta(resp, qm) 81 | qm.RequestTime = rtt 82 | 83 | var entries []*UserEvent 84 | if err := decodeBody(resp, &entries); err != nil { 85 | return nil, nil, err 86 | } 87 | return entries, qm, nil 88 | } 89 | 90 | // IDToIndex is a bit of a hack. This simulates the index generation to 91 | // convert an event ID into a WaitIndex. 92 | func (e *Event) IDToIndex(uuid string) uint64 { 93 | lower := uuid[0:8] + uuid[9:13] + uuid[14:18] 94 | upper := uuid[19:23] + uuid[24:36] 95 | lowVal, err := strconv.ParseUint(lower, 16, 64) 96 | if err != nil { 97 | panic("Failed to convert " + lower) 98 | } 99 | highVal, err := strconv.ParseUint(upper, 16, 64) 100 | if err != nil { 101 | panic("Failed to convert " + upper) 102 | } 103 | return lowVal ^ highVal 104 | } 105 | -------------------------------------------------------------------------------- /src/config/map.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | type mappedLoader struct { 11 | data map[string]json.RawMessage 12 | dataLock sync.RWMutex 13 | } 14 | 15 | // NewMappedLoader creates a Loader that will build config from JSON and store it in a a basic map 16 | // This is meant to be a stubbed / example implementation of a loader, its not really actually meant to be used in a production environment. 17 | func NewMappedLoader(data []byte) (Loader, error) { 18 | ret := &mappedLoader{} 19 | err := ret.Import(data) 20 | return ret, err 21 | } 22 | 23 | // Import takes a json byte array and inserts the key value pairs into consul prefixed by the namespace 24 | func (m *mappedLoader) Import(data []byte) error { 25 | conf := make(map[string]json.RawMessage) 26 | err := json.Unmarshal(data, &conf) 27 | if err != nil { 28 | return fmt.Errorf("Unable to parse json data: %v", err) 29 | } 30 | m.data = conf 31 | return nil 32 | } 33 | 34 | // Initialize loads the consul KV's from the namespace into cache for later retrieval 35 | func (m *mappedLoader) Initialize() error { 36 | //noop 37 | return nil 38 | } 39 | 40 | // Get fetches the raw config from cache 41 | func (m *mappedLoader) Get(key string) ([]byte, error) { 42 | m.dataLock.RLock() 43 | defer m.dataLock.RUnlock() 44 | 45 | if ret, ok := m.data[key]; ok { 46 | return ret, nil 47 | } 48 | return nil, fmt.Errorf("Could not find value for key: %s", key) 49 | } 50 | 51 | // MustGetString fetches the config and parses it into a string. Panics on failure. 52 | func (m *mappedLoader) MustGetString(key string) string { 53 | b, err := m.Get(key) 54 | if err != nil { 55 | panic(fmt.Sprintf("Could not fetch config (%s) %v", key, err)) 56 | } 57 | 58 | var s string 59 | err = json.Unmarshal(b, &s) 60 | if err != nil { 61 | panic(fmt.Sprintf("Could not unmarshal config (%s) %v", key, err)) 62 | } 63 | 64 | return s 65 | } 66 | 67 | // MustGetBool fetches the config and parses it into a bool. Panics on failure. 68 | func (m *mappedLoader) MustGetBool(key string) bool { 69 | b, err := m.Get(key) 70 | if err != nil { 71 | panic(fmt.Sprintf("Could not fetch config (%s) %v", key, err)) 72 | } 73 | var ret bool 74 | err = json.Unmarshal(b, &ret) 75 | if err != nil { 76 | panic(fmt.Sprintf("Could not unmarshal config (%s) %v", key, err)) 77 | } 78 | return ret 79 | } 80 | 81 | // MustGetInt fetches the config and parses it into an int. Panics on failure. 82 | func (m *mappedLoader) MustGetInt(key string) int { 83 | b, err := m.Get(key) 84 | if err != nil { 85 | panic(fmt.Sprintf("Could not fetch config (%s) %v", key, err)) 86 | } 87 | 88 | var ret int 89 | err = json.Unmarshal(b, &ret) 90 | if err != nil { 91 | panic(fmt.Sprintf("Could not unmarshal config (%s) %v", key, err)) 92 | } 93 | return ret 94 | } 95 | 96 | // MustGetDuration fetches the config and parses it into a duration. Panics on failure. 97 | func (m *mappedLoader) MustGetDuration(key string) time.Duration { 98 | s := m.MustGetString(key) 99 | ret, err := time.ParseDuration(s) 100 | if err != nil { 101 | panic(fmt.Sprintf("Could not parse config (%s) into a duration: %v", key, err)) 102 | } 103 | return ret 104 | } 105 | 106 | func (m *mappedLoader) Put(key string, value []byte) error { 107 | m.dataLock.Lock() 108 | defer m.dataLock.Unlock() 109 | m.data[key] = value 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/health.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // HealthCheck is used to represent a single check 8 | type HealthCheck struct { 9 | Node string 10 | CheckID string 11 | Name string 12 | Status string 13 | Notes string 14 | Output string 15 | ServiceID string 16 | ServiceName string 17 | } 18 | 19 | // ServiceEntry is used for the health service endpoint 20 | type ServiceEntry struct { 21 | Node *Node 22 | Service *AgentService 23 | Checks []*HealthCheck 24 | } 25 | 26 | // Health can be used to query the Health endpoints 27 | type Health struct { 28 | c *Client 29 | } 30 | 31 | // Health returns a handle to the health endpoints 32 | func (c *Client) Health() *Health { 33 | return &Health{c} 34 | } 35 | 36 | // Node is used to query for checks belonging to a given node 37 | func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) { 38 | r := h.c.newRequest("GET", "/v1/health/node/"+node) 39 | r.setQueryOptions(q) 40 | rtt, resp, err := requireOK(h.c.doRequest(r)) 41 | if err != nil { 42 | return nil, nil, err 43 | } 44 | defer resp.Body.Close() 45 | 46 | qm := &QueryMeta{} 47 | parseQueryMeta(resp, qm) 48 | qm.RequestTime = rtt 49 | 50 | var out []*HealthCheck 51 | if err := decodeBody(resp, &out); err != nil { 52 | return nil, nil, err 53 | } 54 | return out, qm, nil 55 | } 56 | 57 | // Checks is used to return the checks associated with a service 58 | func (h *Health) Checks(service string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) { 59 | r := h.c.newRequest("GET", "/v1/health/checks/"+service) 60 | r.setQueryOptions(q) 61 | rtt, resp, err := requireOK(h.c.doRequest(r)) 62 | if err != nil { 63 | return nil, nil, err 64 | } 65 | defer resp.Body.Close() 66 | 67 | qm := &QueryMeta{} 68 | parseQueryMeta(resp, qm) 69 | qm.RequestTime = rtt 70 | 71 | var out []*HealthCheck 72 | if err := decodeBody(resp, &out); err != nil { 73 | return nil, nil, err 74 | } 75 | return out, qm, nil 76 | } 77 | 78 | // Service is used to query health information along with service info 79 | // for a given service. It can optionally do server-side filtering on a tag 80 | // or nodes with passing health checks only. 81 | func (h *Health) Service(service, tag string, passingOnly bool, q *QueryOptions) ([]*ServiceEntry, *QueryMeta, error) { 82 | r := h.c.newRequest("GET", "/v1/health/service/"+service) 83 | r.setQueryOptions(q) 84 | if tag != "" { 85 | r.params.Set("tag", tag) 86 | } 87 | if passingOnly { 88 | r.params.Set("passing", "1") 89 | } 90 | rtt, resp, err := requireOK(h.c.doRequest(r)) 91 | if err != nil { 92 | return nil, nil, err 93 | } 94 | defer resp.Body.Close() 95 | 96 | qm := &QueryMeta{} 97 | parseQueryMeta(resp, qm) 98 | qm.RequestTime = rtt 99 | 100 | var out []*ServiceEntry 101 | if err := decodeBody(resp, &out); err != nil { 102 | return nil, nil, err 103 | } 104 | return out, qm, nil 105 | } 106 | 107 | // State is used to retrieve all the checks in a given state. 108 | // The wildcard "any" state can also be used for all checks. 109 | func (h *Health) State(state string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) { 110 | switch state { 111 | case "any": 112 | case "warning": 113 | case "critical": 114 | case "passing": 115 | case "unknown": 116 | default: 117 | return nil, nil, fmt.Errorf("Unsupported state: %v", state) 118 | } 119 | r := h.c.newRequest("GET", "/v1/health/state/"+state) 120 | r.setQueryOptions(q) 121 | rtt, resp, err := requireOK(h.c.doRequest(r)) 122 | if err != nil { 123 | return nil, nil, err 124 | } 125 | defer resp.Body.Close() 126 | 127 | qm := &QueryMeta{} 128 | parseQueryMeta(resp, qm) 129 | qm.RequestTime = rtt 130 | 131 | var out []*HealthCheck 132 | if err := decodeBody(resp, &out); err != nil { 133 | return nil, nil, err 134 | } 135 | return out, qm, nil 136 | } 137 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/acl.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | const ( 4 | // ACLCLientType is the client type token 5 | ACLClientType = "client" 6 | 7 | // ACLManagementType is the management type token 8 | ACLManagementType = "management" 9 | ) 10 | 11 | // ACLEntry is used to represent an ACL entry 12 | type ACLEntry struct { 13 | CreateIndex uint64 14 | ModifyIndex uint64 15 | ID string 16 | Name string 17 | Type string 18 | Rules string 19 | } 20 | 21 | // ACL can be used to query the ACL endpoints 22 | type ACL struct { 23 | c *Client 24 | } 25 | 26 | // ACL returns a handle to the ACL endpoints 27 | func (c *Client) ACL() *ACL { 28 | return &ACL{c} 29 | } 30 | 31 | // Create is used to generate a new token with the given parameters 32 | func (a *ACL) Create(acl *ACLEntry, q *WriteOptions) (string, *WriteMeta, error) { 33 | r := a.c.newRequest("PUT", "/v1/acl/create") 34 | r.setWriteOptions(q) 35 | r.obj = acl 36 | rtt, resp, err := requireOK(a.c.doRequest(r)) 37 | if err != nil { 38 | return "", nil, err 39 | } 40 | defer resp.Body.Close() 41 | 42 | wm := &WriteMeta{RequestTime: rtt} 43 | var out struct{ ID string } 44 | if err := decodeBody(resp, &out); err != nil { 45 | return "", nil, err 46 | } 47 | return out.ID, wm, nil 48 | } 49 | 50 | // Update is used to update the rules of an existing token 51 | func (a *ACL) Update(acl *ACLEntry, q *WriteOptions) (*WriteMeta, error) { 52 | r := a.c.newRequest("PUT", "/v1/acl/update") 53 | r.setWriteOptions(q) 54 | r.obj = acl 55 | rtt, resp, err := requireOK(a.c.doRequest(r)) 56 | if err != nil { 57 | return nil, err 58 | } 59 | defer resp.Body.Close() 60 | 61 | wm := &WriteMeta{RequestTime: rtt} 62 | return wm, nil 63 | } 64 | 65 | // Destroy is used to destroy a given ACL token ID 66 | func (a *ACL) Destroy(id string, q *WriteOptions) (*WriteMeta, error) { 67 | r := a.c.newRequest("PUT", "/v1/acl/destroy/"+id) 68 | r.setWriteOptions(q) 69 | rtt, resp, err := requireOK(a.c.doRequest(r)) 70 | if err != nil { 71 | return nil, err 72 | } 73 | resp.Body.Close() 74 | 75 | wm := &WriteMeta{RequestTime: rtt} 76 | return wm, nil 77 | } 78 | 79 | // Clone is used to return a new token cloned from an existing one 80 | func (a *ACL) Clone(id string, q *WriteOptions) (string, *WriteMeta, error) { 81 | r := a.c.newRequest("PUT", "/v1/acl/clone/"+id) 82 | r.setWriteOptions(q) 83 | rtt, resp, err := requireOK(a.c.doRequest(r)) 84 | if err != nil { 85 | return "", nil, err 86 | } 87 | defer resp.Body.Close() 88 | 89 | wm := &WriteMeta{RequestTime: rtt} 90 | var out struct{ ID string } 91 | if err := decodeBody(resp, &out); err != nil { 92 | return "", nil, err 93 | } 94 | return out.ID, wm, nil 95 | } 96 | 97 | // Info is used to query for information about an ACL token 98 | func (a *ACL) Info(id string, q *QueryOptions) (*ACLEntry, *QueryMeta, error) { 99 | r := a.c.newRequest("GET", "/v1/acl/info/"+id) 100 | r.setQueryOptions(q) 101 | rtt, resp, err := requireOK(a.c.doRequest(r)) 102 | if err != nil { 103 | return nil, nil, err 104 | } 105 | defer resp.Body.Close() 106 | 107 | qm := &QueryMeta{} 108 | parseQueryMeta(resp, qm) 109 | qm.RequestTime = rtt 110 | 111 | var entries []*ACLEntry 112 | if err := decodeBody(resp, &entries); err != nil { 113 | return nil, nil, err 114 | } 115 | if len(entries) > 0 { 116 | return entries[0], qm, nil 117 | } 118 | return nil, qm, nil 119 | } 120 | 121 | // List is used to get all the ACL tokens 122 | func (a *ACL) List(q *QueryOptions) ([]*ACLEntry, *QueryMeta, error) { 123 | r := a.c.newRequest("GET", "/v1/acl/list") 124 | r.setQueryOptions(q) 125 | rtt, resp, err := requireOK(a.c.doRequest(r)) 126 | if err != nil { 127 | return nil, nil, err 128 | } 129 | defer resp.Body.Close() 130 | 131 | qm := &QueryMeta{} 132 | parseQueryMeta(resp, qm) 133 | qm.RequestTime = rtt 134 | 135 | var entries []*ACLEntry 136 | if err := decodeBody(resp, &entries); err != nil { 137 | return nil, nil, err 138 | } 139 | return entries, qm, nil 140 | } 141 | -------------------------------------------------------------------------------- /src/balancer/consul/random.go: -------------------------------------------------------------------------------- 1 | package consul 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "net" 7 | "net/url" 8 | "strconv" 9 | "sync" 10 | "time" 11 | 12 | "github.com/divideandconquer/go-consul-client/src/balancer" 13 | "github.com/hashicorp/consul/api" 14 | ) 15 | 16 | type cachedServiceLocation struct { 17 | Services []*balancer.ServiceLocation 18 | CachedAt time.Time 19 | } 20 | 21 | type randomBalancer struct { 22 | environment string 23 | consulCatalog *api.Health 24 | cache map[string]cachedServiceLocation 25 | cacheLock sync.RWMutex //TODO lock per serviceName 26 | ttl time.Duration 27 | } 28 | 29 | func init() { 30 | rand.Seed(time.Now().UnixNano()) 31 | } 32 | 33 | // NewRandomDNSBalancer will return a random balancer.DNS that looks up dns in consul. 34 | func NewRandomDNSBalancer(environment string, consulAddr string, cacheTTL time.Duration) (balancer.DNS, error) { 35 | config := api.DefaultConfig() 36 | config.Address = consulAddr 37 | consul, err := api.NewClient(config) 38 | if err != nil { 39 | return nil, fmt.Errorf("Could not connect to consul: %v", err) 40 | } 41 | 42 | r := randomBalancer{} 43 | r.cache = make(map[string]cachedServiceLocation) 44 | r.environment = environment 45 | r.ttl = cacheTTL 46 | r.consulCatalog = consul.Health() 47 | return &r, nil 48 | } 49 | 50 | func (r *randomBalancer) FindService(serviceName string) (*balancer.ServiceLocation, error) { 51 | services, err := r.getServiceFromCache(serviceName) 52 | if err != nil || len(services) == 0 { 53 | services, err = r.writeServiceToCache(serviceName) 54 | if err != nil { 55 | return nil, err 56 | } 57 | } 58 | return r.pickService(services), nil 59 | } 60 | 61 | func (r *randomBalancer) GetHttpUrl(serviceName string, useTLS bool) (url.URL, error) { 62 | result := url.URL{} 63 | loc, err := r.FindService(serviceName) 64 | if err != nil { 65 | return result, err 66 | } 67 | result.Host = loc.URL 68 | if loc.Port != 0 { 69 | result.Host = net.JoinHostPort(loc.URL, strconv.Itoa(loc.Port)) 70 | } 71 | if useTLS { 72 | result.Scheme = "https" 73 | } else { 74 | result.Scheme = "http" 75 | } 76 | return result, nil 77 | } 78 | 79 | func (r *randomBalancer) pickService(services []*balancer.ServiceLocation) *balancer.ServiceLocation { 80 | return services[rand.Intn(len(services))] 81 | } 82 | 83 | func (r *randomBalancer) getServiceFromCache(serviceName string) ([]*balancer.ServiceLocation, error) { 84 | r.cacheLock.RLock() 85 | defer r.cacheLock.RUnlock() 86 | 87 | if result, ok := r.cache[serviceName]; ok { 88 | if time.Now().UTC().Before(result.CachedAt.Add(r.ttl)) { 89 | return result.Services, nil 90 | } 91 | return nil, fmt.Errorf("Cache for %s is expired", serviceName) 92 | } 93 | return nil, fmt.Errorf("Could not find %s in cache", serviceName) 94 | } 95 | 96 | // writeServiceToCache locks specifically to alleviate load on consul some additional lock time 97 | // is preferable to extra consul calls 98 | func (r *randomBalancer) writeServiceToCache(serviceName string) ([]*balancer.ServiceLocation, error) { 99 | //acquire a write lock 100 | r.cacheLock.Lock() 101 | defer r.cacheLock.Unlock() 102 | 103 | //check the cache again in case we've fetched since the last check 104 | //(our lock could have been waiting for another call to this function) 105 | if result, ok := r.cache[serviceName]; ok { 106 | if time.Now().UTC().Before(result.CachedAt.Add(r.ttl)) { 107 | return result.Services, nil 108 | } 109 | } 110 | 111 | //it still isn't in the cache, lets put it there 112 | consulServices, _, err := r.consulCatalog.Service(serviceName, r.environment, true, nil) 113 | if err != nil { 114 | return nil, fmt.Errorf("Error reaching consul for service lookup %v", err) 115 | } 116 | 117 | if len(consulServices) == 0 { 118 | return nil, fmt.Errorf("No services found for %s", serviceName) 119 | } 120 | 121 | //setup service locations 122 | var services []*balancer.ServiceLocation 123 | for _, v := range consulServices { 124 | s := &balancer.ServiceLocation{} 125 | s.URL = v.Service.Address 126 | s.Port = v.Service.Port 127 | services = append(services, s) 128 | } 129 | 130 | // cache 131 | c := cachedServiceLocation{Services: services, CachedAt: time.Now().UTC()} 132 | r.cache[serviceName] = c 133 | return services, nil 134 | } 135 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/divideandconquer/go-merge/merge/merge.go: -------------------------------------------------------------------------------- 1 | package merge 2 | 3 | import "reflect" 4 | 5 | // Merge will take two data sets and merge them together - returning a new data set 6 | func Merge(base, override interface{}) interface{} { 7 | //reflect and recurse 8 | b := reflect.ValueOf(base) 9 | o := reflect.ValueOf(override) 10 | ret := mergeRecursive(b, o) 11 | 12 | return ret.Interface() 13 | } 14 | 15 | func mergeRecursive(base, override reflect.Value) reflect.Value { 16 | var result reflect.Value 17 | 18 | switch base.Kind() { 19 | case reflect.Ptr: 20 | switch base.Elem().Kind() { 21 | case reflect.Ptr: 22 | fallthrough 23 | case reflect.Interface: 24 | fallthrough 25 | case reflect.Struct: 26 | fallthrough 27 | case reflect.Map: 28 | // Pointers to complex types should recurse if they aren't nil 29 | if base.IsNil() { 30 | result = override 31 | } else if override.IsNil() { 32 | result = base 33 | } else { 34 | result = mergeRecursive(base.Elem(), override.Elem()) 35 | } 36 | default: 37 | // Pointers to basic types should just override 38 | if isEmptyValue(override) { 39 | result = base 40 | } else { 41 | result = override 42 | } 43 | } 44 | case reflect.Interface: 45 | // Interfaces should just be unwrapped and recursed through 46 | result = mergeRecursive(base.Elem(), override.Elem()) 47 | 48 | case reflect.Struct: 49 | // For structs we loop over fields and recurse 50 | // setup our result struct 51 | result = reflect.New(base.Type()) 52 | for i, n := 0, base.NumField(); i < n; i++ { 53 | // We cant set private fields so don't recurse on them 54 | if result.Elem().Field(i).CanSet() { 55 | // get the merged value of each field 56 | newVal := mergeRecursive(base.Field(i), override.Field(i)) 57 | 58 | //attempt to set that merged value on our result struct 59 | if result.Elem().Field(i).CanSet() && newVal.IsValid() { 60 | if newVal.Kind() == reflect.Ptr && result.Elem().Field(i).Kind() != reflect.Ptr { 61 | newVal = newVal.Elem() 62 | } else if result.Elem().Field(i).Kind() == reflect.Ptr && newVal.Kind() != reflect.Ptr && newVal.CanAddr() { 63 | newVal = newVal.Addr() 64 | } 65 | result.Elem().Field(i).Set(newVal) 66 | } 67 | } 68 | } 69 | 70 | case reflect.Map: 71 | // For Maps we copy the base data, and then replace it with merged data 72 | // We use two for loops to make sure all map keys from base and all keys from 73 | // override exist in the result just in case one of the maps is sparse. 74 | elementsAreValues := base.Type().Elem().Kind() != reflect.Ptr 75 | 76 | result = reflect.MakeMap(base.Type()) 77 | // Copy from base first 78 | for _, key := range base.MapKeys() { 79 | result.SetMapIndex(key, base.MapIndex(key)) 80 | } 81 | 82 | // Override with values from override if they exist 83 | if override.Kind() == reflect.Map { 84 | for _, key := range override.MapKeys() { 85 | overrideVal := override.MapIndex(key) 86 | baseVal := base.MapIndex(key) 87 | if !overrideVal.IsValid() { 88 | continue 89 | } 90 | 91 | // Merge the values and set in the result 92 | newVal := mergeRecursive(baseVal, overrideVal) 93 | if elementsAreValues && newVal.Kind() == reflect.Ptr { 94 | result.SetMapIndex(key, newVal.Elem()) 95 | 96 | } else { 97 | result.SetMapIndex(key, newVal) 98 | } 99 | } 100 | } 101 | 102 | default: 103 | // These are all generic types 104 | // override will be taken for generic types if it is set 105 | if isEmptyValue(override) { 106 | result = base 107 | } else { 108 | result = override 109 | } 110 | } 111 | return result 112 | } 113 | 114 | // Copied From http://golang.org/src/encoding/json/encode.go 115 | // Lines 280 - 296 116 | func isEmptyValue(v reflect.Value) bool { 117 | switch v.Kind() { 118 | case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 119 | return v.Len() == 0 120 | case reflect.Bool: 121 | return !v.Bool() 122 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 123 | return v.Int() == 0 124 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 125 | return v.Uint() == 0 126 | case reflect.Float32, reflect.Float64: 127 | return v.Float() == 0 128 | case reflect.Interface, reflect.Ptr: 129 | return v.IsNil() 130 | } 131 | return false 132 | } 133 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/catalog.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | type Node struct { 4 | Node string 5 | Address string 6 | } 7 | 8 | type CatalogService struct { 9 | Node string 10 | Address string 11 | ServiceID string 12 | ServiceName string 13 | ServiceAddress string 14 | ServiceTags []string 15 | ServicePort int 16 | } 17 | 18 | type CatalogNode struct { 19 | Node *Node 20 | Services map[string]*AgentService 21 | } 22 | 23 | type CatalogRegistration struct { 24 | Node string 25 | Address string 26 | Datacenter string 27 | Service *AgentService 28 | Check *AgentCheck 29 | } 30 | 31 | type CatalogDeregistration struct { 32 | Node string 33 | Address string 34 | Datacenter string 35 | ServiceID string 36 | CheckID string 37 | } 38 | 39 | // Catalog can be used to query the Catalog endpoints 40 | type Catalog struct { 41 | c *Client 42 | } 43 | 44 | // Catalog returns a handle to the catalog endpoints 45 | func (c *Client) Catalog() *Catalog { 46 | return &Catalog{c} 47 | } 48 | 49 | func (c *Catalog) Register(reg *CatalogRegistration, q *WriteOptions) (*WriteMeta, error) { 50 | r := c.c.newRequest("PUT", "/v1/catalog/register") 51 | r.setWriteOptions(q) 52 | r.obj = reg 53 | rtt, resp, err := requireOK(c.c.doRequest(r)) 54 | if err != nil { 55 | return nil, err 56 | } 57 | resp.Body.Close() 58 | 59 | wm := &WriteMeta{} 60 | wm.RequestTime = rtt 61 | 62 | return wm, nil 63 | } 64 | 65 | func (c *Catalog) Deregister(dereg *CatalogDeregistration, q *WriteOptions) (*WriteMeta, error) { 66 | r := c.c.newRequest("PUT", "/v1/catalog/deregister") 67 | r.setWriteOptions(q) 68 | r.obj = dereg 69 | rtt, resp, err := requireOK(c.c.doRequest(r)) 70 | if err != nil { 71 | return nil, err 72 | } 73 | resp.Body.Close() 74 | 75 | wm := &WriteMeta{} 76 | wm.RequestTime = rtt 77 | 78 | return wm, nil 79 | } 80 | 81 | // Datacenters is used to query for all the known datacenters 82 | func (c *Catalog) Datacenters() ([]string, error) { 83 | r := c.c.newRequest("GET", "/v1/catalog/datacenters") 84 | _, resp, err := requireOK(c.c.doRequest(r)) 85 | if err != nil { 86 | return nil, err 87 | } 88 | defer resp.Body.Close() 89 | 90 | var out []string 91 | if err := decodeBody(resp, &out); err != nil { 92 | return nil, err 93 | } 94 | return out, nil 95 | } 96 | 97 | // Nodes is used to query all the known nodes 98 | func (c *Catalog) Nodes(q *QueryOptions) ([]*Node, *QueryMeta, error) { 99 | r := c.c.newRequest("GET", "/v1/catalog/nodes") 100 | r.setQueryOptions(q) 101 | rtt, resp, err := requireOK(c.c.doRequest(r)) 102 | if err != nil { 103 | return nil, nil, err 104 | } 105 | defer resp.Body.Close() 106 | 107 | qm := &QueryMeta{} 108 | parseQueryMeta(resp, qm) 109 | qm.RequestTime = rtt 110 | 111 | var out []*Node 112 | if err := decodeBody(resp, &out); err != nil { 113 | return nil, nil, err 114 | } 115 | return out, qm, nil 116 | } 117 | 118 | // Services is used to query for all known services 119 | func (c *Catalog) Services(q *QueryOptions) (map[string][]string, *QueryMeta, error) { 120 | r := c.c.newRequest("GET", "/v1/catalog/services") 121 | r.setQueryOptions(q) 122 | rtt, resp, err := requireOK(c.c.doRequest(r)) 123 | if err != nil { 124 | return nil, nil, err 125 | } 126 | defer resp.Body.Close() 127 | 128 | qm := &QueryMeta{} 129 | parseQueryMeta(resp, qm) 130 | qm.RequestTime = rtt 131 | 132 | var out map[string][]string 133 | if err := decodeBody(resp, &out); err != nil { 134 | return nil, nil, err 135 | } 136 | return out, qm, nil 137 | } 138 | 139 | // Service is used to query catalog entries for a given service 140 | func (c *Catalog) Service(service, tag string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) { 141 | r := c.c.newRequest("GET", "/v1/catalog/service/"+service) 142 | r.setQueryOptions(q) 143 | if tag != "" { 144 | r.params.Set("tag", tag) 145 | } 146 | rtt, resp, err := requireOK(c.c.doRequest(r)) 147 | if err != nil { 148 | return nil, nil, err 149 | } 150 | defer resp.Body.Close() 151 | 152 | qm := &QueryMeta{} 153 | parseQueryMeta(resp, qm) 154 | qm.RequestTime = rtt 155 | 156 | var out []*CatalogService 157 | if err := decodeBody(resp, &out); err != nil { 158 | return nil, nil, err 159 | } 160 | return out, qm, nil 161 | } 162 | 163 | // Node is used to query for service information about a single node 164 | func (c *Catalog) Node(node string, q *QueryOptions) (*CatalogNode, *QueryMeta, error) { 165 | r := c.c.newRequest("GET", "/v1/catalog/node/"+node) 166 | r.setQueryOptions(q) 167 | rtt, resp, err := requireOK(c.c.doRequest(r)) 168 | if err != nil { 169 | return nil, nil, err 170 | } 171 | defer resp.Body.Close() 172 | 173 | qm := &QueryMeta{} 174 | parseQueryMeta(resp, qm) 175 | qm.RequestTime = rtt 176 | 177 | var out *CatalogNode 178 | if err := decodeBody(resp, &out); err != nil { 179 | return nil, nil, err 180 | } 181 | return out, qm, nil 182 | } 183 | -------------------------------------------------------------------------------- /src/config/loadermock/mock_loader.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: ../config/loader.go 3 | 4 | // Package loadermock is a generated GoMock package. 5 | package loadermock 6 | 7 | import ( 8 | reflect "reflect" 9 | time "time" 10 | 11 | gomock "github.com/golang/mock/gomock" 12 | ) 13 | 14 | // MockLoader is a mock of Loader interface. 15 | type MockLoader struct { 16 | ctrl *gomock.Controller 17 | recorder *MockLoaderMockRecorder 18 | } 19 | 20 | // MockLoaderMockRecorder is the mock recorder for MockLoader. 21 | type MockLoaderMockRecorder struct { 22 | mock *MockLoader 23 | } 24 | 25 | // NewMockLoader creates a new mock instance. 26 | func NewMockLoader(ctrl *gomock.Controller) *MockLoader { 27 | mock := &MockLoader{ctrl: ctrl} 28 | mock.recorder = &MockLoaderMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use. 33 | func (m *MockLoader) EXPECT() *MockLoaderMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // Get mocks base method. 38 | func (m *MockLoader) Get(key string) ([]byte, error) { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "Get", key) 41 | ret0, _ := ret[0].([]byte) 42 | ret1, _ := ret[1].(error) 43 | return ret0, ret1 44 | } 45 | 46 | // Get indicates an expected call of Get. 47 | func (mr *MockLoaderMockRecorder) Get(key interface{}) *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockLoader)(nil).Get), key) 50 | } 51 | 52 | // Import mocks base method. 53 | func (m *MockLoader) Import(data []byte) error { 54 | m.ctrl.T.Helper() 55 | ret := m.ctrl.Call(m, "Import", data) 56 | ret0, _ := ret[0].(error) 57 | return ret0 58 | } 59 | 60 | // Import indicates an expected call of Import. 61 | func (mr *MockLoaderMockRecorder) Import(data interface{}) *gomock.Call { 62 | mr.mock.ctrl.T.Helper() 63 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Import", reflect.TypeOf((*MockLoader)(nil).Import), data) 64 | } 65 | 66 | // Initialize mocks base method. 67 | func (m *MockLoader) Initialize() error { 68 | m.ctrl.T.Helper() 69 | ret := m.ctrl.Call(m, "Initialize") 70 | ret0, _ := ret[0].(error) 71 | return ret0 72 | } 73 | 74 | // Initialize indicates an expected call of Initialize. 75 | func (mr *MockLoaderMockRecorder) Initialize() *gomock.Call { 76 | mr.mock.ctrl.T.Helper() 77 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Initialize", reflect.TypeOf((*MockLoader)(nil).Initialize)) 78 | } 79 | 80 | // MustGetBool mocks base method. 81 | func (m *MockLoader) MustGetBool(key string) bool { 82 | m.ctrl.T.Helper() 83 | ret := m.ctrl.Call(m, "MustGetBool", key) 84 | ret0, _ := ret[0].(bool) 85 | return ret0 86 | } 87 | 88 | // MustGetBool indicates an expected call of MustGetBool. 89 | func (mr *MockLoaderMockRecorder) MustGetBool(key interface{}) *gomock.Call { 90 | mr.mock.ctrl.T.Helper() 91 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MustGetBool", reflect.TypeOf((*MockLoader)(nil).MustGetBool), key) 92 | } 93 | 94 | // MustGetDuration mocks base method. 95 | func (m *MockLoader) MustGetDuration(key string) time.Duration { 96 | m.ctrl.T.Helper() 97 | ret := m.ctrl.Call(m, "MustGetDuration", key) 98 | ret0, _ := ret[0].(time.Duration) 99 | return ret0 100 | } 101 | 102 | // MustGetDuration indicates an expected call of MustGetDuration. 103 | func (mr *MockLoaderMockRecorder) MustGetDuration(key interface{}) *gomock.Call { 104 | mr.mock.ctrl.T.Helper() 105 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MustGetDuration", reflect.TypeOf((*MockLoader)(nil).MustGetDuration), key) 106 | } 107 | 108 | // MustGetInt mocks base method. 109 | func (m *MockLoader) MustGetInt(key string) int { 110 | m.ctrl.T.Helper() 111 | ret := m.ctrl.Call(m, "MustGetInt", key) 112 | ret0, _ := ret[0].(int) 113 | return ret0 114 | } 115 | 116 | // MustGetInt indicates an expected call of MustGetInt. 117 | func (mr *MockLoaderMockRecorder) MustGetInt(key interface{}) *gomock.Call { 118 | mr.mock.ctrl.T.Helper() 119 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MustGetInt", reflect.TypeOf((*MockLoader)(nil).MustGetInt), key) 120 | } 121 | 122 | // MustGetString mocks base method. 123 | func (m *MockLoader) MustGetString(key string) string { 124 | m.ctrl.T.Helper() 125 | ret := m.ctrl.Call(m, "MustGetString", key) 126 | ret0, _ := ret[0].(string) 127 | return ret0 128 | } 129 | 130 | // MustGetString indicates an expected call of MustGetString. 131 | func (mr *MockLoaderMockRecorder) MustGetString(key interface{}) *gomock.Call { 132 | mr.mock.ctrl.T.Helper() 133 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MustGetString", reflect.TypeOf((*MockLoader)(nil).MustGetString), key) 134 | } 135 | 136 | // Put mocks base method. 137 | func (m *MockLoader) Put(key string, value []byte) error { 138 | m.ctrl.T.Helper() 139 | ret := m.ctrl.Call(m, "Put", key, value) 140 | ret0, _ := ret[0].(error) 141 | return ret0 142 | } 143 | 144 | // Put indicates an expected call of Put. 145 | func (mr *MockLoaderMockRecorder) Put(key, value interface{}) *gomock.Call { 146 | mr.mock.ctrl.T.Helper() 147 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockLoader)(nil).Put), key, value) 148 | } 149 | -------------------------------------------------------------------------------- /src/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "sync" 7 | "time" 8 | 9 | "github.com/divideandconquer/go-consul-client/src/config" 10 | "github.com/divideandconquer/go-merge/merge" 11 | "github.com/hashicorp/consul/api" 12 | ) 13 | 14 | const divider = "/" 15 | 16 | type cachedLoader struct { 17 | namespace string 18 | cacheLock sync.RWMutex 19 | cache map[string][]byte 20 | consulKV *api.KV 21 | } 22 | 23 | // NewCachedLoader creates a Loader that will cache the provided namespace on initialization 24 | // and return data from that cache on Get 25 | func NewCachedLoader(namespace string, consulAddr string) (config.Loader, error) { 26 | config := api.DefaultConfig() 27 | config.Address = consulAddr 28 | consul, err := api.NewClient(config) 29 | if err != nil { 30 | return nil, fmt.Errorf("Could not connect to consul: %v", err) 31 | } 32 | 33 | return &cachedLoader{namespace: namespace, consulKV: consul.KV()}, nil 34 | } 35 | 36 | // Import takes a json byte array and inserts the key value pairs into consul prefixed by the namespace 37 | func (c *cachedLoader) Import(data []byte) error { 38 | conf := make(map[string]interface{}) 39 | err := json.Unmarshal(data, &conf) 40 | if err != nil { 41 | return fmt.Errorf("Unable to parse json data: %v", err) 42 | } 43 | kvMap, err := c.compileKeyValues(conf, c.namespace) 44 | if err != nil { 45 | return fmt.Errorf("Unable to complie KVs: %v", err) 46 | } 47 | 48 | if err != nil { 49 | return fmt.Errorf("Could not create consul client: %v", err) 50 | } 51 | for k, v := range kvMap { 52 | p := &api.KVPair{Key: k, Value: v} 53 | _, err = c.consulKV.Put(p, nil) 54 | if err != nil { 55 | return fmt.Errorf("Could not write key to consul (%s | %s) %v", k, v, err) 56 | } 57 | } 58 | return nil 59 | } 60 | 61 | func (c *cachedLoader) compileKeyValues(data map[string]interface{}, prefix string) (map[string][]byte, error) { 62 | result := make(map[string][]byte) 63 | for k, v := range data { 64 | if subMap, ok := v.(map[string]interface{}); ok { 65 | //recurse and merge results 66 | 67 | compiled, err := c.compileKeyValues(subMap, c.qualify(prefix, k)) 68 | if err != nil { 69 | return nil, err 70 | } 71 | merged := merge.Merge(result, compiled) 72 | if mm, ok := merged.(map[string][]byte); ok { 73 | result = mm 74 | } 75 | } else { 76 | //for other types json marshal will turn then into string byte slice for storage 77 | j, err := json.Marshal(v) 78 | if err != nil { 79 | return nil, err 80 | } 81 | 82 | result[c.qualify(prefix, k)] = j 83 | } 84 | } 85 | return result, nil 86 | } 87 | 88 | func (c *cachedLoader) qualify(prefix, key string) string { 89 | if len(prefix) > 0 { 90 | return prefix + divider + key 91 | } 92 | return key 93 | } 94 | 95 | // Initialize loads the consul KV's from the namespace into cache for later retrieval 96 | func (c *cachedLoader) Initialize() error { 97 | pairs, _, err := c.consulKV.List(c.namespace, nil) 98 | if err != nil { 99 | return fmt.Errorf("Could not pull config from consul: %v", err) 100 | } 101 | 102 | //write lock the cache incase init is called more than once 103 | c.cacheLock.Lock() 104 | defer c.cacheLock.Unlock() 105 | 106 | c.cache = make(map[string][]byte) 107 | for _, kv := range pairs { 108 | c.cache[kv.Key] = kv.Value 109 | } 110 | return nil 111 | } 112 | 113 | // Get fetches the raw config from cache 114 | func (c *cachedLoader) Get(key string) ([]byte, error) { 115 | c.cacheLock.RLock() 116 | defer c.cacheLock.RUnlock() 117 | 118 | compiledKey := c.namespace + divider + key 119 | if ret, ok := c.cache[compiledKey]; ok { 120 | return ret, nil 121 | } 122 | return nil, fmt.Errorf("Could not find value for key: %s", compiledKey) 123 | } 124 | 125 | // MustGetString fetches the config and parses it into a string. Panics on failure. 126 | func (c *cachedLoader) MustGetString(key string) string { 127 | b, err := c.Get(key) 128 | if err != nil { 129 | panic(fmt.Sprintf("Could not fetch config (%s) %v", key, err)) 130 | } 131 | 132 | var s string 133 | err = json.Unmarshal(b, &s) 134 | if err != nil { 135 | panic(fmt.Sprintf("Could not unmarshal config (%s) %v", key, err)) 136 | } 137 | 138 | return s 139 | } 140 | 141 | // MustGetBool fetches the config and parses it into a bool. Panics on failure. 142 | func (c *cachedLoader) MustGetBool(key string) bool { 143 | b, err := c.Get(key) 144 | if err != nil { 145 | panic(fmt.Sprintf("Could not fetch config (%s) %v", key, err)) 146 | } 147 | var ret bool 148 | err = json.Unmarshal(b, &ret) 149 | if err != nil { 150 | panic(fmt.Sprintf("Could not unmarshal config (%s) %v", key, err)) 151 | } 152 | return ret 153 | } 154 | 155 | // MustGetInt fetches the config and parses it into an int. Panics on failure. 156 | func (c *cachedLoader) MustGetInt(key string) int { 157 | b, err := c.Get(key) 158 | if err != nil { 159 | panic(fmt.Sprintf("Could not fetch config (%s) %v", key, err)) 160 | } 161 | 162 | var ret int 163 | err = json.Unmarshal(b, &ret) 164 | if err != nil { 165 | panic(fmt.Sprintf("Could not unmarshal config (%s) %v", key, err)) 166 | } 167 | return ret 168 | } 169 | 170 | // MustGetDuration fetches the config and parses it into a duration. Panics on failure. 171 | func (c *cachedLoader) MustGetDuration(key string) time.Duration { 172 | s := c.MustGetString(key) 173 | ret, err := time.ParseDuration(s) 174 | if err != nil { 175 | panic(fmt.Sprintf("Could not parse config (%s) into a duration: %v", key, err)) 176 | } 177 | return ret 178 | } 179 | 180 | func (c *cachedLoader) Put(key string, value []byte) error { 181 | c.cacheLock.Lock() 182 | defer c.cacheLock.Unlock() 183 | c.cache[key] = value 184 | return nil 185 | } 186 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/prepared_query.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | // QueryDatacenterOptions sets options about how we fail over if there are no 4 | // healthy nodes in the local datacenter. 5 | type QueryDatacenterOptions struct { 6 | // NearestN is set to the number of remote datacenters to try, based on 7 | // network coordinates. 8 | NearestN int 9 | 10 | // Datacenters is a fixed list of datacenters to try after NearestN. We 11 | // never try a datacenter multiple times, so those are subtracted from 12 | // this list before proceeding. 13 | Datacenters []string 14 | } 15 | 16 | // QueryDNSOptions controls settings when query results are served over DNS. 17 | type QueryDNSOptions struct { 18 | // TTL is the time to live for the served DNS results. 19 | TTL string 20 | } 21 | 22 | // ServiceQuery is used to query for a set of healthy nodes offering a specific 23 | // service. 24 | type ServiceQuery struct { 25 | // Service is the service to query. 26 | Service string 27 | 28 | // Failover controls what we do if there are no healthy nodes in the 29 | // local datacenter. 30 | Failover QueryDatacenterOptions 31 | 32 | // If OnlyPassing is true then we will only include nodes with passing 33 | // health checks (critical AND warning checks will cause a node to be 34 | // discarded) 35 | OnlyPassing bool 36 | 37 | // Tags are a set of required and/or disallowed tags. If a tag is in 38 | // this list it must be present. If the tag is preceded with "!" then 39 | // it is disallowed. 40 | Tags []string 41 | } 42 | 43 | // PrepatedQueryDefinition defines a complete prepared query. 44 | type PreparedQueryDefinition struct { 45 | // ID is this UUID-based ID for the query, always generated by Consul. 46 | ID string 47 | 48 | // Name is an optional friendly name for the query supplied by the 49 | // user. NOTE - if this feature is used then it will reduce the security 50 | // of any read ACL associated with this query/service since this name 51 | // can be used to locate nodes with supplying any ACL. 52 | Name string 53 | 54 | // Session is an optional session to tie this query's lifetime to. If 55 | // this is omitted then the query will not expire. 56 | Session string 57 | 58 | // Token is the ACL token used when the query was created, and it is 59 | // used when a query is subsequently executed. This token, or a token 60 | // with management privileges, must be used to change the query later. 61 | Token string 62 | 63 | // Service defines a service query (leaving things open for other types 64 | // later). 65 | Service ServiceQuery 66 | 67 | // DNS has options that control how the results of this query are 68 | // served over DNS. 69 | DNS QueryDNSOptions 70 | } 71 | 72 | // PreparedQueryExecuteResponse has the results of executing a query. 73 | type PreparedQueryExecuteResponse struct { 74 | // Service is the service that was queried. 75 | Service string 76 | 77 | // Nodes has the nodes that were output by the query. 78 | Nodes []ServiceEntry 79 | 80 | // DNS has the options for serving these results over DNS. 81 | DNS QueryDNSOptions 82 | 83 | // Datacenter is the datacenter that these results came from. 84 | Datacenter string 85 | 86 | // Failovers is a count of how many times we had to query a remote 87 | // datacenter. 88 | Failovers int 89 | } 90 | 91 | // PreparedQuery can be used to query the prepared query endpoints. 92 | type PreparedQuery struct { 93 | c *Client 94 | } 95 | 96 | // PreparedQuery returns a handle to the prepared query endpoints. 97 | func (c *Client) PreparedQuery() *PreparedQuery { 98 | return &PreparedQuery{c} 99 | } 100 | 101 | // Create makes a new prepared query. The ID of the new query is returned. 102 | func (c *PreparedQuery) Create(query *PreparedQueryDefinition, q *WriteOptions) (string, *WriteMeta, error) { 103 | r := c.c.newRequest("POST", "/v1/query") 104 | r.setWriteOptions(q) 105 | r.obj = query 106 | rtt, resp, err := requireOK(c.c.doRequest(r)) 107 | if err != nil { 108 | return "", nil, err 109 | } 110 | defer resp.Body.Close() 111 | 112 | wm := &WriteMeta{} 113 | wm.RequestTime = rtt 114 | 115 | var out struct{ ID string } 116 | if err := decodeBody(resp, &out); err != nil { 117 | return "", nil, err 118 | } 119 | return out.ID, wm, nil 120 | } 121 | 122 | // Update makes updates to an existing prepared query. 123 | func (c *PreparedQuery) Update(query *PreparedQueryDefinition, q *WriteOptions) (*WriteMeta, error) { 124 | return c.c.write("/v1/query/"+query.ID, query, nil, q) 125 | } 126 | 127 | // List is used to fetch all the prepared queries (always requires a management 128 | // token). 129 | func (c *PreparedQuery) List(q *QueryOptions) ([]*PreparedQueryDefinition, *QueryMeta, error) { 130 | var out []*PreparedQueryDefinition 131 | qm, err := c.c.query("/v1/query", &out, q) 132 | if err != nil { 133 | return nil, nil, err 134 | } 135 | return out, qm, nil 136 | } 137 | 138 | // Get is used to fetch a specific prepared query. 139 | func (c *PreparedQuery) Get(queryID string, q *QueryOptions) ([]*PreparedQueryDefinition, *QueryMeta, error) { 140 | var out []*PreparedQueryDefinition 141 | qm, err := c.c.query("/v1/query/"+queryID, &out, q) 142 | if err != nil { 143 | return nil, nil, err 144 | } 145 | return out, qm, nil 146 | } 147 | 148 | // Delete is used to delete a specific prepared query. 149 | func (c *PreparedQuery) Delete(queryID string, q *QueryOptions) (*QueryMeta, error) { 150 | r := c.c.newRequest("DELETE", "/v1/query/"+queryID) 151 | r.setQueryOptions(q) 152 | rtt, resp, err := requireOK(c.c.doRequest(r)) 153 | if err != nil { 154 | return nil, err 155 | } 156 | defer resp.Body.Close() 157 | 158 | qm := &QueryMeta{} 159 | parseQueryMeta(resp, qm) 160 | qm.RequestTime = rtt 161 | return qm, nil 162 | } 163 | 164 | // Execute is used to execute a specific prepared query. You can execute using 165 | // a query ID or name. 166 | func (c *PreparedQuery) Execute(queryIDOrName string, q *QueryOptions) (*PreparedQueryExecuteResponse, *QueryMeta, error) { 167 | var out *PreparedQueryExecuteResponse 168 | qm, err := c.c.query("/v1/query/"+queryIDOrName+"/execute", &out, q) 169 | if err != nil { 170 | return nil, nil, err 171 | } 172 | return out, qm, nil 173 | } 174 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/session.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | const ( 10 | // SessionBehaviorRelease is the default behavior and causes 11 | // all associated locks to be released on session invalidation. 12 | SessionBehaviorRelease = "release" 13 | 14 | // SessionBehaviorDelete is new in Consul 0.5 and changes the 15 | // behavior to delete all associated locks on session invalidation. 16 | // It can be used in a way similar to Ephemeral Nodes in ZooKeeper. 17 | SessionBehaviorDelete = "delete" 18 | ) 19 | 20 | var ErrSessionExpired = errors.New("session expired") 21 | 22 | // SessionEntry represents a session in consul 23 | type SessionEntry struct { 24 | CreateIndex uint64 25 | ID string 26 | Name string 27 | Node string 28 | Checks []string 29 | LockDelay time.Duration 30 | Behavior string 31 | TTL string 32 | } 33 | 34 | // Session can be used to query the Session endpoints 35 | type Session struct { 36 | c *Client 37 | } 38 | 39 | // Session returns a handle to the session endpoints 40 | func (c *Client) Session() *Session { 41 | return &Session{c} 42 | } 43 | 44 | // CreateNoChecks is like Create but is used specifically to create 45 | // a session with no associated health checks. 46 | func (s *Session) CreateNoChecks(se *SessionEntry, q *WriteOptions) (string, *WriteMeta, error) { 47 | body := make(map[string]interface{}) 48 | body["Checks"] = []string{} 49 | if se != nil { 50 | if se.Name != "" { 51 | body["Name"] = se.Name 52 | } 53 | if se.Node != "" { 54 | body["Node"] = se.Node 55 | } 56 | if se.LockDelay != 0 { 57 | body["LockDelay"] = durToMsec(se.LockDelay) 58 | } 59 | if se.Behavior != "" { 60 | body["Behavior"] = se.Behavior 61 | } 62 | if se.TTL != "" { 63 | body["TTL"] = se.TTL 64 | } 65 | } 66 | return s.create(body, q) 67 | 68 | } 69 | 70 | // Create makes a new session. Providing a session entry can 71 | // customize the session. It can also be nil to use defaults. 72 | func (s *Session) Create(se *SessionEntry, q *WriteOptions) (string, *WriteMeta, error) { 73 | var obj interface{} 74 | if se != nil { 75 | body := make(map[string]interface{}) 76 | obj = body 77 | if se.Name != "" { 78 | body["Name"] = se.Name 79 | } 80 | if se.Node != "" { 81 | body["Node"] = se.Node 82 | } 83 | if se.LockDelay != 0 { 84 | body["LockDelay"] = durToMsec(se.LockDelay) 85 | } 86 | if len(se.Checks) > 0 { 87 | body["Checks"] = se.Checks 88 | } 89 | if se.Behavior != "" { 90 | body["Behavior"] = se.Behavior 91 | } 92 | if se.TTL != "" { 93 | body["TTL"] = se.TTL 94 | } 95 | } 96 | return s.create(obj, q) 97 | } 98 | 99 | func (s *Session) create(obj interface{}, q *WriteOptions) (string, *WriteMeta, error) { 100 | var out struct{ ID string } 101 | wm, err := s.c.write("/v1/session/create", obj, &out, q) 102 | if err != nil { 103 | return "", nil, err 104 | } 105 | return out.ID, wm, nil 106 | } 107 | 108 | // Destroy invalidates a given session 109 | func (s *Session) Destroy(id string, q *WriteOptions) (*WriteMeta, error) { 110 | wm, err := s.c.write("/v1/session/destroy/"+id, nil, nil, q) 111 | if err != nil { 112 | return nil, err 113 | } 114 | return wm, nil 115 | } 116 | 117 | // Renew renews the TTL on a given session 118 | func (s *Session) Renew(id string, q *WriteOptions) (*SessionEntry, *WriteMeta, error) { 119 | r := s.c.newRequest("PUT", "/v1/session/renew/"+id) 120 | r.setWriteOptions(q) 121 | rtt, resp, err := s.c.doRequest(r) 122 | if err != nil { 123 | return nil, nil, err 124 | } 125 | defer resp.Body.Close() 126 | 127 | wm := &WriteMeta{RequestTime: rtt} 128 | 129 | if resp.StatusCode == 404 { 130 | return nil, wm, nil 131 | } else if resp.StatusCode != 200 { 132 | return nil, nil, fmt.Errorf("Unexpected response code: %d", resp.StatusCode) 133 | } 134 | 135 | var entries []*SessionEntry 136 | if err := decodeBody(resp, &entries); err != nil { 137 | return nil, nil, fmt.Errorf("Failed to read response: %v", err) 138 | } 139 | if len(entries) > 0 { 140 | return entries[0], wm, nil 141 | } 142 | return nil, wm, nil 143 | } 144 | 145 | // RenewPeriodic is used to periodically invoke Session.Renew on a 146 | // session until a doneCh is closed. This is meant to be used in a long running 147 | // goroutine to ensure a session stays valid. 148 | func (s *Session) RenewPeriodic(initialTTL string, id string, q *WriteOptions, doneCh chan struct{}) error { 149 | ttl, err := time.ParseDuration(initialTTL) 150 | if err != nil { 151 | return err 152 | } 153 | 154 | waitDur := ttl / 2 155 | lastRenewTime := time.Now() 156 | var lastErr error 157 | for { 158 | if time.Since(lastRenewTime) > ttl { 159 | return lastErr 160 | } 161 | select { 162 | case <-time.After(waitDur): 163 | entry, _, err := s.Renew(id, q) 164 | if err != nil { 165 | waitDur = time.Second 166 | lastErr = err 167 | continue 168 | } 169 | if entry == nil { 170 | return ErrSessionExpired 171 | } 172 | 173 | // Handle the server updating the TTL 174 | ttl, _ = time.ParseDuration(entry.TTL) 175 | waitDur = ttl / 2 176 | lastRenewTime = time.Now() 177 | 178 | case <-doneCh: 179 | // Attempt a session destroy 180 | s.Destroy(id, q) 181 | return nil 182 | } 183 | } 184 | } 185 | 186 | // Info looks up a single session 187 | func (s *Session) Info(id string, q *QueryOptions) (*SessionEntry, *QueryMeta, error) { 188 | var entries []*SessionEntry 189 | qm, err := s.c.query("/v1/session/info/"+id, &entries, q) 190 | if err != nil { 191 | return nil, nil, err 192 | } 193 | if len(entries) > 0 { 194 | return entries[0], qm, nil 195 | } 196 | return nil, qm, nil 197 | } 198 | 199 | // List gets sessions for a node 200 | func (s *Session) Node(node string, q *QueryOptions) ([]*SessionEntry, *QueryMeta, error) { 201 | var entries []*SessionEntry 202 | qm, err := s.c.query("/v1/session/node/"+node, &entries, q) 203 | if err != nil { 204 | return nil, nil, err 205 | } 206 | return entries, qm, nil 207 | } 208 | 209 | // List gets all active sessions 210 | func (s *Session) List(q *QueryOptions) ([]*SessionEntry, *QueryMeta, error) { 211 | var entries []*SessionEntry 212 | qm, err := s.c.query("/v1/session/list", &entries, q) 213 | if err != nil { 214 | return nil, nil, err 215 | } 216 | return entries, qm, nil 217 | } 218 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/serf/coordinate/phantom.go: -------------------------------------------------------------------------------- 1 | package coordinate 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | // GenerateClients returns a slice with nodes number of clients, all with the 11 | // given config. 12 | func GenerateClients(nodes int, config *Config) ([]*Client, error) { 13 | clients := make([]*Client, nodes) 14 | for i, _ := range clients { 15 | client, err := NewClient(config) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | clients[i] = client 21 | } 22 | return clients, nil 23 | } 24 | 25 | // GenerateLine returns a truth matrix as if all the nodes are in a straight linke 26 | // with the given spacing between them. 27 | func GenerateLine(nodes int, spacing time.Duration) [][]time.Duration { 28 | truth := make([][]time.Duration, nodes) 29 | for i := range truth { 30 | truth[i] = make([]time.Duration, nodes) 31 | } 32 | 33 | for i := 0; i < nodes; i++ { 34 | for j := i + 1; j < nodes; j++ { 35 | rtt := time.Duration(j-i) * spacing 36 | truth[i][j], truth[j][i] = rtt, rtt 37 | } 38 | } 39 | return truth 40 | } 41 | 42 | // GenerateGrid returns a truth matrix as if all the nodes are in a two dimensional 43 | // grid with the given spacing between them. 44 | func GenerateGrid(nodes int, spacing time.Duration) [][]time.Duration { 45 | truth := make([][]time.Duration, nodes) 46 | for i := range truth { 47 | truth[i] = make([]time.Duration, nodes) 48 | } 49 | 50 | n := int(math.Sqrt(float64(nodes))) 51 | for i := 0; i < nodes; i++ { 52 | for j := i + 1; j < nodes; j++ { 53 | x1, y1 := float64(i%n), float64(i/n) 54 | x2, y2 := float64(j%n), float64(j/n) 55 | dx, dy := x2-x1, y2-y1 56 | dist := math.Sqrt(dx*dx + dy*dy) 57 | rtt := time.Duration(dist * float64(spacing)) 58 | truth[i][j], truth[j][i] = rtt, rtt 59 | } 60 | } 61 | return truth 62 | } 63 | 64 | // GenerateSplit returns a truth matrix as if half the nodes are close together in 65 | // one location and half the nodes are close together in another. The lan factor 66 | // is used to separate the nodes locally and the wan factor represents the split 67 | // between the two sides. 68 | func GenerateSplit(nodes int, lan time.Duration, wan time.Duration) [][]time.Duration { 69 | truth := make([][]time.Duration, nodes) 70 | for i := range truth { 71 | truth[i] = make([]time.Duration, nodes) 72 | } 73 | 74 | split := nodes / 2 75 | for i := 0; i < nodes; i++ { 76 | for j := i + 1; j < nodes; j++ { 77 | rtt := lan 78 | if (i <= split && j > split) || (i > split && j <= split) { 79 | rtt += wan 80 | } 81 | truth[i][j], truth[j][i] = rtt, rtt 82 | } 83 | } 84 | return truth 85 | } 86 | 87 | // GenerateCircle returns a truth matrix for a set of nodes, evenly distributed 88 | // around a circle with the given radius. The first node is at the "center" of the 89 | // circle because it's equidistant from all the other nodes, but we place it at 90 | // double the radius, so it should show up above all the other nodes in height. 91 | func GenerateCircle(nodes int, radius time.Duration) [][]time.Duration { 92 | truth := make([][]time.Duration, nodes) 93 | for i := range truth { 94 | truth[i] = make([]time.Duration, nodes) 95 | } 96 | 97 | for i := 0; i < nodes; i++ { 98 | for j := i + 1; j < nodes; j++ { 99 | var rtt time.Duration 100 | if i == 0 { 101 | rtt = 2 * radius 102 | } else { 103 | t1 := 2.0 * math.Pi * float64(i) / float64(nodes) 104 | x1, y1 := math.Cos(t1), math.Sin(t1) 105 | t2 := 2.0 * math.Pi * float64(j) / float64(nodes) 106 | x2, y2 := math.Cos(t2), math.Sin(t2) 107 | dx, dy := x2-x1, y2-y1 108 | dist := math.Sqrt(dx*dx + dy*dy) 109 | rtt = time.Duration(dist * float64(radius)) 110 | } 111 | truth[i][j], truth[j][i] = rtt, rtt 112 | } 113 | } 114 | return truth 115 | } 116 | 117 | // GenerateRandom returns a truth matrix for a set of nodes with normally 118 | // distributed delays, with the given mean and deviation. The RNG is re-seeded 119 | // so you always get the same matrix for a given size. 120 | func GenerateRandom(nodes int, mean time.Duration, deviation time.Duration) [][]time.Duration { 121 | rand.Seed(1) 122 | 123 | truth := make([][]time.Duration, nodes) 124 | for i := range truth { 125 | truth[i] = make([]time.Duration, nodes) 126 | } 127 | 128 | for i := 0; i < nodes; i++ { 129 | for j := i + 1; j < nodes; j++ { 130 | rttSeconds := rand.NormFloat64()*deviation.Seconds() + mean.Seconds() 131 | rtt := time.Duration(rttSeconds * secondsToNanoseconds) 132 | truth[i][j], truth[j][i] = rtt, rtt 133 | } 134 | } 135 | return truth 136 | } 137 | 138 | // Simulate runs the given number of cycles using the given list of clients and 139 | // truth matrix. On each cycle, each client will pick a random node and observe 140 | // the truth RTT, updating its coordinate estimate. The RNG is re-seeded for 141 | // each simulation run to get deterministic results (for this algorithm and the 142 | // underlying algorithm which will use random numbers for position vectors when 143 | // starting out with everything at the origin). 144 | func Simulate(clients []*Client, truth [][]time.Duration, cycles int) { 145 | rand.Seed(1) 146 | 147 | nodes := len(clients) 148 | for cycle := 0; cycle < cycles; cycle++ { 149 | for i, _ := range clients { 150 | if j := rand.Intn(nodes); j != i { 151 | c := clients[j].GetCoordinate() 152 | rtt := truth[i][j] 153 | node := fmt.Sprintf("node_%d", j) 154 | clients[i].Update(node, c, rtt) 155 | } 156 | } 157 | } 158 | } 159 | 160 | // Stats is returned from the Evaluate function with a summary of the algorithm 161 | // performance. 162 | type Stats struct { 163 | ErrorMax float64 164 | ErrorAvg float64 165 | } 166 | 167 | // Evaluate uses the coordinates of the given clients to calculate estimated 168 | // distances and compares them with the given truth matrix, returning summary 169 | // stats. 170 | func Evaluate(clients []*Client, truth [][]time.Duration) (stats Stats) { 171 | nodes := len(clients) 172 | count := 0 173 | for i := 0; i < nodes; i++ { 174 | for j := i + 1; j < nodes; j++ { 175 | est := clients[i].DistanceTo(clients[j].GetCoordinate()).Seconds() 176 | actual := truth[i][j].Seconds() 177 | error := math.Abs(est-actual) / actual 178 | stats.ErrorMax = math.Max(stats.ErrorMax, error) 179 | stats.ErrorAvg += error 180 | count += 1 181 | } 182 | } 183 | 184 | stats.ErrorAvg /= float64(count) 185 | fmt.Printf("Error avg=%9.6f max=%9.6f\n", stats.ErrorAvg, stats.ErrorMax) 186 | return 187 | } 188 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/serf/coordinate/client.go: -------------------------------------------------------------------------------- 1 | package coordinate 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "sort" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | // Client manages the estimated network coordinate for a given node, and adjusts 12 | // it as the node observes round trip times and estimated coordinates from other 13 | // nodes. The core algorithm is based on Vivaldi, see the documentation for Config 14 | // for more details. 15 | type Client struct { 16 | // coord is the current estimate of the client's network coordinate. 17 | coord *Coordinate 18 | 19 | // origin is a coordinate sitting at the origin. 20 | origin *Coordinate 21 | 22 | // config contains the tuning parameters that govern the performance of 23 | // the algorithm. 24 | config *Config 25 | 26 | // adjustmentIndex is the current index into the adjustmentSamples slice. 27 | adjustmentIndex uint 28 | 29 | // adjustment is used to store samples for the adjustment calculation. 30 | adjustmentSamples []float64 31 | 32 | // latencyFilterSamples is used to store the last several RTT samples, 33 | // keyed by node name. We will use the config's LatencyFilterSamples 34 | // value to determine how many samples we keep, per node. 35 | latencyFilterSamples map[string][]float64 36 | 37 | // mutex enables safe concurrent access to the client. 38 | mutex sync.RWMutex 39 | } 40 | 41 | // NewClient creates a new Client and verifies the configuration is valid. 42 | func NewClient(config *Config) (*Client, error) { 43 | if !(config.Dimensionality > 0) { 44 | return nil, fmt.Errorf("dimensionality must be >0") 45 | } 46 | 47 | return &Client{ 48 | coord: NewCoordinate(config), 49 | origin: NewCoordinate(config), 50 | config: config, 51 | adjustmentIndex: 0, 52 | adjustmentSamples: make([]float64, config.AdjustmentWindowSize), 53 | latencyFilterSamples: make(map[string][]float64), 54 | }, nil 55 | } 56 | 57 | // GetCoordinate returns a copy of the coordinate for this client. 58 | func (c *Client) GetCoordinate() *Coordinate { 59 | c.mutex.RLock() 60 | defer c.mutex.RUnlock() 61 | 62 | return c.coord.Clone() 63 | } 64 | 65 | // SetCoordinate forces the client's coordinate to a known state. 66 | func (c *Client) SetCoordinate(coord *Coordinate) { 67 | c.mutex.Lock() 68 | defer c.mutex.Unlock() 69 | 70 | c.coord = coord.Clone() 71 | } 72 | 73 | // ForgetNode removes any client state for the given node. 74 | func (c *Client) ForgetNode(node string) { 75 | c.mutex.Lock() 76 | defer c.mutex.Unlock() 77 | 78 | delete(c.latencyFilterSamples, node) 79 | } 80 | 81 | // latencyFilter applies a simple moving median filter with a new sample for 82 | // a node. This assumes that the mutex has been locked already. 83 | func (c *Client) latencyFilter(node string, rttSeconds float64) float64 { 84 | samples, ok := c.latencyFilterSamples[node] 85 | if !ok { 86 | samples = make([]float64, 0, c.config.LatencyFilterSize) 87 | } 88 | 89 | // Add the new sample and trim the list, if needed. 90 | samples = append(samples, rttSeconds) 91 | if len(samples) > int(c.config.LatencyFilterSize) { 92 | samples = samples[1:] 93 | } 94 | c.latencyFilterSamples[node] = samples 95 | 96 | // Sort a copy of the samples and return the median. 97 | sorted := make([]float64, len(samples)) 98 | copy(sorted, samples) 99 | sort.Float64s(sorted) 100 | return sorted[len(sorted)/2] 101 | } 102 | 103 | // updateVivialdi updates the Vivaldi portion of the client's coordinate. This 104 | // assumes that the mutex has been locked already. 105 | func (c *Client) updateVivaldi(other *Coordinate, rttSeconds float64) { 106 | const zeroThreshold = 1.0e-6 107 | 108 | dist := c.coord.DistanceTo(other).Seconds() 109 | if rttSeconds < zeroThreshold { 110 | rttSeconds = zeroThreshold 111 | } 112 | wrongness := math.Abs(dist-rttSeconds) / rttSeconds 113 | 114 | totalError := c.coord.Error + other.Error 115 | if totalError < zeroThreshold { 116 | totalError = zeroThreshold 117 | } 118 | weight := c.coord.Error / totalError 119 | 120 | c.coord.Error = c.config.VivaldiCE*weight*wrongness + c.coord.Error*(1.0-c.config.VivaldiCE*weight) 121 | if c.coord.Error > c.config.VivaldiErrorMax { 122 | c.coord.Error = c.config.VivaldiErrorMax 123 | } 124 | 125 | delta := c.config.VivaldiCC * weight 126 | force := delta * (rttSeconds - dist) 127 | c.coord = c.coord.ApplyForce(c.config, force, other) 128 | } 129 | 130 | // updateAdjustment updates the adjustment portion of the client's coordinate, if 131 | // the feature is enabled. This assumes that the mutex has been locked already. 132 | func (c *Client) updateAdjustment(other *Coordinate, rttSeconds float64) { 133 | if c.config.AdjustmentWindowSize == 0 { 134 | return 135 | } 136 | 137 | // Note that the existing adjustment factors don't figure in to this 138 | // calculation so we use the raw distance here. 139 | dist := c.coord.rawDistanceTo(other) 140 | c.adjustmentSamples[c.adjustmentIndex] = rttSeconds - dist 141 | c.adjustmentIndex = (c.adjustmentIndex + 1) % c.config.AdjustmentWindowSize 142 | 143 | sum := 0.0 144 | for _, sample := range c.adjustmentSamples { 145 | sum += sample 146 | } 147 | c.coord.Adjustment = sum / (2.0 * float64(c.config.AdjustmentWindowSize)) 148 | } 149 | 150 | // updateGravity applies a small amount of gravity to pull coordinates towards 151 | // the center of the coordinate system to combat drift. This assumes that the 152 | // mutex is locked already. 153 | func (c *Client) updateGravity() { 154 | dist := c.origin.DistanceTo(c.coord).Seconds() 155 | force := -1.0 * math.Pow(dist/c.config.GravityRho, 2.0) 156 | c.coord = c.coord.ApplyForce(c.config, force, c.origin) 157 | } 158 | 159 | // Update takes other, a coordinate for another node, and rtt, a round trip 160 | // time observation for a ping to that node, and updates the estimated position of 161 | // the client's coordinate. Returns the updated coordinate. 162 | func (c *Client) Update(node string, other *Coordinate, rtt time.Duration) *Coordinate { 163 | c.mutex.Lock() 164 | defer c.mutex.Unlock() 165 | 166 | rttSeconds := c.latencyFilter(node, rtt.Seconds()) 167 | c.updateVivaldi(other, rttSeconds) 168 | c.updateAdjustment(other, rttSeconds) 169 | c.updateGravity() 170 | return c.coord.Clone() 171 | } 172 | 173 | // DistanceTo returns the estimated RTT from the client's coordinate to other, the 174 | // coordinate for another node. 175 | func (c *Client) DistanceTo(other *Coordinate) time.Duration { 176 | c.mutex.RLock() 177 | defer c.mutex.RUnlock() 178 | 179 | return c.coord.DistanceTo(other) 180 | } 181 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/serf/coordinate/coordinate.go: -------------------------------------------------------------------------------- 1 | package coordinate 2 | 3 | import ( 4 | "math" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | // Coordinate is a specialized structure for holding network coordinates for the 10 | // Vivaldi-based coordinate mapping algorithm. All of the fields should be public 11 | // to enable this to be serialized. All values in here are in units of seconds. 12 | type Coordinate struct { 13 | // Vec is the Euclidean portion of the coordinate. This is used along 14 | // with the other fields to provide an overall distance estimate. The 15 | // units here are seconds. 16 | Vec []float64 17 | 18 | // Err reflects the confidence in the given coordinate and is updated 19 | // dynamically by the Vivaldi Client. This is dimensionless. 20 | Error float64 21 | 22 | // Adjustment is a distance offset computed based on a calculation over 23 | // observations from all other nodes over a fixed window and is updated 24 | // dynamically by the Vivaldi Client. The units here are seconds. 25 | Adjustment float64 26 | 27 | // Height is a distance offset that accounts for non-Euclidean effects 28 | // which model the access links from nodes to the core Internet. The access 29 | // links are usually set by bandwidth and congestion, and the core links 30 | // usually follow distance based on geography. 31 | Height float64 32 | } 33 | 34 | const ( 35 | // secondsToNanoseconds is used to convert float seconds to nanoseconds. 36 | secondsToNanoseconds = 1.0e9 37 | 38 | // zeroThreshold is used to decide if two coordinates are on top of each 39 | // other. 40 | zeroThreshold = 1.0e-6 41 | ) 42 | 43 | // ErrDimensionalityConflict will be panic-d if you try to perform operations 44 | // with incompatible dimensions. 45 | type DimensionalityConflictError struct{} 46 | 47 | // Adds the error interface. 48 | func (e DimensionalityConflictError) Error() string { 49 | return "coordinate dimensionality does not match" 50 | } 51 | 52 | // NewCoordinate creates a new coordinate at the origin, using the given config 53 | // to supply key initial values. 54 | func NewCoordinate(config *Config) *Coordinate { 55 | return &Coordinate{ 56 | Vec: make([]float64, config.Dimensionality), 57 | Error: config.VivaldiErrorMax, 58 | Adjustment: 0.0, 59 | Height: config.HeightMin, 60 | } 61 | } 62 | 63 | // Clone creates an independent copy of this coordinate. 64 | func (c *Coordinate) Clone() *Coordinate { 65 | vec := make([]float64, len(c.Vec)) 66 | copy(vec, c.Vec) 67 | return &Coordinate{ 68 | Vec: vec, 69 | Error: c.Error, 70 | Adjustment: c.Adjustment, 71 | Height: c.Height, 72 | } 73 | } 74 | 75 | // IsCompatibleWith checks to see if the two coordinates are compatible 76 | // dimensionally. If this returns true then you are guaranteed to not get 77 | // any runtime errors operating on them. 78 | func (c *Coordinate) IsCompatibleWith(other *Coordinate) bool { 79 | return len(c.Vec) == len(other.Vec) 80 | } 81 | 82 | // ApplyForce returns the result of applying the force from the direction of the 83 | // other coordinate. 84 | func (c *Coordinate) ApplyForce(config *Config, force float64, other *Coordinate) *Coordinate { 85 | if !c.IsCompatibleWith(other) { 86 | panic(DimensionalityConflictError{}) 87 | } 88 | 89 | ret := c.Clone() 90 | unit, mag := unitVectorAt(c.Vec, other.Vec) 91 | ret.Vec = add(ret.Vec, mul(unit, force)) 92 | if mag > zeroThreshold { 93 | ret.Height = (ret.Height+other.Height)*force/mag + ret.Height 94 | ret.Height = math.Max(ret.Height, config.HeightMin) 95 | } 96 | return ret 97 | } 98 | 99 | // DistanceTo returns the distance between this coordinate and the other 100 | // coordinate, including adjustments. 101 | func (c *Coordinate) DistanceTo(other *Coordinate) time.Duration { 102 | if !c.IsCompatibleWith(other) { 103 | panic(DimensionalityConflictError{}) 104 | } 105 | 106 | dist := c.rawDistanceTo(other) 107 | adjustedDist := dist + c.Adjustment + other.Adjustment 108 | if adjustedDist > 0.0 { 109 | dist = adjustedDist 110 | } 111 | return time.Duration(dist * secondsToNanoseconds) 112 | } 113 | 114 | // rawDistanceTo returns the Vivaldi distance between this coordinate and the 115 | // other coordinate in seconds, not including adjustments. This assumes the 116 | // dimensions have already been checked to be compatible. 117 | func (c *Coordinate) rawDistanceTo(other *Coordinate) float64 { 118 | return magnitude(diff(c.Vec, other.Vec)) + c.Height + other.Height 119 | } 120 | 121 | // add returns the sum of vec1 and vec2. This assumes the dimensions have 122 | // already been checked to be compatible. 123 | func add(vec1 []float64, vec2 []float64) []float64 { 124 | ret := make([]float64, len(vec1)) 125 | for i, _ := range ret { 126 | ret[i] = vec1[i] + vec2[i] 127 | } 128 | return ret 129 | } 130 | 131 | // diff returns the difference between the vec1 and vec2. This assumes the 132 | // dimensions have already been checked to be compatible. 133 | func diff(vec1 []float64, vec2 []float64) []float64 { 134 | ret := make([]float64, len(vec1)) 135 | for i, _ := range ret { 136 | ret[i] = vec1[i] - vec2[i] 137 | } 138 | return ret 139 | } 140 | 141 | // mul returns vec multiplied by a scalar factor. 142 | func mul(vec []float64, factor float64) []float64 { 143 | ret := make([]float64, len(vec)) 144 | for i, _ := range vec { 145 | ret[i] = vec[i] * factor 146 | } 147 | return ret 148 | } 149 | 150 | // magnitude computes the magnitude of the vec. 151 | func magnitude(vec []float64) float64 { 152 | sum := 0.0 153 | for i, _ := range vec { 154 | sum += vec[i] * vec[i] 155 | } 156 | return math.Sqrt(sum) 157 | } 158 | 159 | // unitVectorAt returns a unit vector pointing at vec1 from vec2. If the two 160 | // positions are the same then a random unit vector is returned. We also return 161 | // the distance between the points for use in the later height calculation. 162 | func unitVectorAt(vec1 []float64, vec2 []float64) ([]float64, float64) { 163 | ret := diff(vec1, vec2) 164 | 165 | // If the coordinates aren't on top of each other we can normalize. 166 | if mag := magnitude(ret); mag > zeroThreshold { 167 | return mul(ret, 1.0/mag), mag 168 | } 169 | 170 | // Otherwise, just return a random unit vector. 171 | for i, _ := range ret { 172 | ret[i] = rand.Float64() - 0.5 173 | } 174 | if mag := magnitude(ret); mag > zeroThreshold { 175 | return mul(ret, 1.0/mag), 0.0 176 | } 177 | 178 | // And finally just give up and make a unit vector along the first 179 | // dimension. This should be exceedingly rare. 180 | ret = make([]float64, len(ret)) 181 | ret[0] = 1.0 182 | return ret, 0.0 183 | } 184 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/kv.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | // KVPair is used to represent a single K/V entry 13 | type KVPair struct { 14 | Key string 15 | CreateIndex uint64 16 | ModifyIndex uint64 17 | LockIndex uint64 18 | Flags uint64 19 | Value []byte 20 | Session string 21 | } 22 | 23 | // KVPairs is a list of KVPair objects 24 | type KVPairs []*KVPair 25 | 26 | // KV is used to manipulate the K/V API 27 | type KV struct { 28 | c *Client 29 | } 30 | 31 | // KV is used to return a handle to the K/V apis 32 | func (c *Client) KV() *KV { 33 | return &KV{c} 34 | } 35 | 36 | // Get is used to lookup a single key 37 | func (k *KV) Get(key string, q *QueryOptions) (*KVPair, *QueryMeta, error) { 38 | resp, qm, err := k.getInternal(key, nil, q) 39 | if err != nil { 40 | return nil, nil, err 41 | } 42 | if resp == nil { 43 | return nil, qm, nil 44 | } 45 | defer resp.Body.Close() 46 | 47 | var entries []*KVPair 48 | if err := decodeBody(resp, &entries); err != nil { 49 | return nil, nil, err 50 | } 51 | if len(entries) > 0 { 52 | return entries[0], qm, nil 53 | } 54 | return nil, qm, nil 55 | } 56 | 57 | // List is used to lookup all keys under a prefix 58 | func (k *KV) List(prefix string, q *QueryOptions) (KVPairs, *QueryMeta, error) { 59 | resp, qm, err := k.getInternal(prefix, map[string]string{"recurse": ""}, q) 60 | if err != nil { 61 | return nil, nil, err 62 | } 63 | if resp == nil { 64 | return nil, qm, nil 65 | } 66 | defer resp.Body.Close() 67 | 68 | var entries []*KVPair 69 | if err := decodeBody(resp, &entries); err != nil { 70 | return nil, nil, err 71 | } 72 | return entries, qm, nil 73 | } 74 | 75 | // Keys is used to list all the keys under a prefix. Optionally, 76 | // a separator can be used to limit the responses. 77 | func (k *KV) Keys(prefix, separator string, q *QueryOptions) ([]string, *QueryMeta, error) { 78 | params := map[string]string{"keys": ""} 79 | if separator != "" { 80 | params["separator"] = separator 81 | } 82 | resp, qm, err := k.getInternal(prefix, params, q) 83 | if err != nil { 84 | return nil, nil, err 85 | } 86 | if resp == nil { 87 | return nil, qm, nil 88 | } 89 | defer resp.Body.Close() 90 | 91 | var entries []string 92 | if err := decodeBody(resp, &entries); err != nil { 93 | return nil, nil, err 94 | } 95 | return entries, qm, nil 96 | } 97 | 98 | func (k *KV) getInternal(key string, params map[string]string, q *QueryOptions) (*http.Response, *QueryMeta, error) { 99 | r := k.c.newRequest("GET", "/v1/kv/"+key) 100 | r.setQueryOptions(q) 101 | for param, val := range params { 102 | r.params.Set(param, val) 103 | } 104 | rtt, resp, err := k.c.doRequest(r) 105 | if err != nil { 106 | return nil, nil, err 107 | } 108 | 109 | qm := &QueryMeta{} 110 | parseQueryMeta(resp, qm) 111 | qm.RequestTime = rtt 112 | 113 | if resp.StatusCode == 404 { 114 | resp.Body.Close() 115 | return nil, qm, nil 116 | } else if resp.StatusCode != 200 { 117 | resp.Body.Close() 118 | return nil, nil, fmt.Errorf("Unexpected response code: %d", resp.StatusCode) 119 | } 120 | return resp, qm, nil 121 | } 122 | 123 | // Put is used to write a new value. Only the 124 | // Key, Flags and Value is respected. 125 | func (k *KV) Put(p *KVPair, q *WriteOptions) (*WriteMeta, error) { 126 | params := make(map[string]string, 1) 127 | if p.Flags != 0 { 128 | params["flags"] = strconv.FormatUint(p.Flags, 10) 129 | } 130 | _, wm, err := k.put(p.Key, params, p.Value, q) 131 | return wm, err 132 | } 133 | 134 | // CAS is used for a Check-And-Set operation. The Key, 135 | // ModifyIndex, Flags and Value are respected. Returns true 136 | // on success or false on failures. 137 | func (k *KV) CAS(p *KVPair, q *WriteOptions) (bool, *WriteMeta, error) { 138 | params := make(map[string]string, 2) 139 | if p.Flags != 0 { 140 | params["flags"] = strconv.FormatUint(p.Flags, 10) 141 | } 142 | params["cas"] = strconv.FormatUint(p.ModifyIndex, 10) 143 | return k.put(p.Key, params, p.Value, q) 144 | } 145 | 146 | // Acquire is used for a lock acquisition operation. The Key, 147 | // Flags, Value and Session are respected. Returns true 148 | // on success or false on failures. 149 | func (k *KV) Acquire(p *KVPair, q *WriteOptions) (bool, *WriteMeta, error) { 150 | params := make(map[string]string, 2) 151 | if p.Flags != 0 { 152 | params["flags"] = strconv.FormatUint(p.Flags, 10) 153 | } 154 | params["acquire"] = p.Session 155 | return k.put(p.Key, params, p.Value, q) 156 | } 157 | 158 | // Release is used for a lock release operation. The Key, 159 | // Flags, Value and Session are respected. Returns true 160 | // on success or false on failures. 161 | func (k *KV) Release(p *KVPair, q *WriteOptions) (bool, *WriteMeta, error) { 162 | params := make(map[string]string, 2) 163 | if p.Flags != 0 { 164 | params["flags"] = strconv.FormatUint(p.Flags, 10) 165 | } 166 | params["release"] = p.Session 167 | return k.put(p.Key, params, p.Value, q) 168 | } 169 | 170 | func (k *KV) put(key string, params map[string]string, body []byte, q *WriteOptions) (bool, *WriteMeta, error) { 171 | if len(key) > 0 && key[0] == '/' { 172 | return false, nil, fmt.Errorf("Invalid key. Key must not begin with a '/': %s", key) 173 | } 174 | 175 | r := k.c.newRequest("PUT", "/v1/kv/"+key) 176 | r.setWriteOptions(q) 177 | for param, val := range params { 178 | r.params.Set(param, val) 179 | } 180 | r.body = bytes.NewReader(body) 181 | rtt, resp, err := requireOK(k.c.doRequest(r)) 182 | if err != nil { 183 | return false, nil, err 184 | } 185 | defer resp.Body.Close() 186 | 187 | qm := &WriteMeta{} 188 | qm.RequestTime = rtt 189 | 190 | var buf bytes.Buffer 191 | if _, err := io.Copy(&buf, resp.Body); err != nil { 192 | return false, nil, fmt.Errorf("Failed to read response: %v", err) 193 | } 194 | res := strings.Contains(string(buf.Bytes()), "true") 195 | return res, qm, nil 196 | } 197 | 198 | // Delete is used to delete a single key 199 | func (k *KV) Delete(key string, w *WriteOptions) (*WriteMeta, error) { 200 | _, qm, err := k.deleteInternal(key, nil, w) 201 | return qm, err 202 | } 203 | 204 | // DeleteCAS is used for a Delete Check-And-Set operation. The Key 205 | // and ModifyIndex are respected. Returns true on success or false on failures. 206 | func (k *KV) DeleteCAS(p *KVPair, q *WriteOptions) (bool, *WriteMeta, error) { 207 | params := map[string]string{ 208 | "cas": strconv.FormatUint(p.ModifyIndex, 10), 209 | } 210 | return k.deleteInternal(p.Key, params, q) 211 | } 212 | 213 | // DeleteTree is used to delete all keys under a prefix 214 | func (k *KV) DeleteTree(prefix string, w *WriteOptions) (*WriteMeta, error) { 215 | _, qm, err := k.deleteInternal(prefix, map[string]string{"recurse": ""}, w) 216 | return qm, err 217 | } 218 | 219 | func (k *KV) deleteInternal(key string, params map[string]string, q *WriteOptions) (bool, *WriteMeta, error) { 220 | r := k.c.newRequest("DELETE", "/v1/kv/"+key) 221 | r.setWriteOptions(q) 222 | for param, val := range params { 223 | r.params.Set(param, val) 224 | } 225 | rtt, resp, err := requireOK(k.c.doRequest(r)) 226 | if err != nil { 227 | return false, nil, err 228 | } 229 | defer resp.Body.Close() 230 | 231 | qm := &WriteMeta{} 232 | qm.RequestTime = rtt 233 | 234 | var buf bytes.Buffer 235 | if _, err := io.Copy(&buf, resp.Body); err != nil { 236 | return false, nil, fmt.Errorf("Failed to read response: %v", err) 237 | } 238 | res := strings.Contains(string(buf.Bytes()), "true") 239 | return res, qm, nil 240 | } 241 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/agent.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // AgentCheck represents a check known to the agent 8 | type AgentCheck struct { 9 | Node string 10 | CheckID string 11 | Name string 12 | Status string 13 | Notes string 14 | Output string 15 | ServiceID string 16 | ServiceName string 17 | } 18 | 19 | // AgentService represents a service known to the agent 20 | type AgentService struct { 21 | ID string 22 | Service string 23 | Tags []string 24 | Port int 25 | Address string 26 | } 27 | 28 | // AgentMember represents a cluster member known to the agent 29 | type AgentMember struct { 30 | Name string 31 | Addr string 32 | Port uint16 33 | Tags map[string]string 34 | Status int 35 | ProtocolMin uint8 36 | ProtocolMax uint8 37 | ProtocolCur uint8 38 | DelegateMin uint8 39 | DelegateMax uint8 40 | DelegateCur uint8 41 | } 42 | 43 | // AgentServiceRegistration is used to register a new service 44 | type AgentServiceRegistration struct { 45 | ID string `json:",omitempty"` 46 | Name string `json:",omitempty"` 47 | Tags []string `json:",omitempty"` 48 | Port int `json:",omitempty"` 49 | Address string `json:",omitempty"` 50 | Check *AgentServiceCheck 51 | Checks AgentServiceChecks 52 | } 53 | 54 | // AgentCheckRegistration is used to register a new check 55 | type AgentCheckRegistration struct { 56 | ID string `json:",omitempty"` 57 | Name string `json:",omitempty"` 58 | Notes string `json:",omitempty"` 59 | ServiceID string `json:",omitempty"` 60 | AgentServiceCheck 61 | } 62 | 63 | // AgentServiceCheck is used to create an associated 64 | // check for a service 65 | type AgentServiceCheck struct { 66 | Script string `json:",omitempty"` 67 | DockerContainerID string `json:",omitempty"` 68 | Shell string `json:",omitempty"` // Only supported for Docker. 69 | Interval string `json:",omitempty"` 70 | Timeout string `json:",omitempty"` 71 | TTL string `json:",omitempty"` 72 | HTTP string `json:",omitempty"` 73 | TCP string `json:",omitempty"` 74 | Status string `json:",omitempty"` 75 | } 76 | type AgentServiceChecks []*AgentServiceCheck 77 | 78 | // Agent can be used to query the Agent endpoints 79 | type Agent struct { 80 | c *Client 81 | 82 | // cache the node name 83 | nodeName string 84 | } 85 | 86 | // Agent returns a handle to the agent endpoints 87 | func (c *Client) Agent() *Agent { 88 | return &Agent{c: c} 89 | } 90 | 91 | // Self is used to query the agent we are speaking to for 92 | // information about itself 93 | func (a *Agent) Self() (map[string]map[string]interface{}, error) { 94 | r := a.c.newRequest("GET", "/v1/agent/self") 95 | _, resp, err := requireOK(a.c.doRequest(r)) 96 | if err != nil { 97 | return nil, err 98 | } 99 | defer resp.Body.Close() 100 | 101 | var out map[string]map[string]interface{} 102 | if err := decodeBody(resp, &out); err != nil { 103 | return nil, err 104 | } 105 | return out, nil 106 | } 107 | 108 | // NodeName is used to get the node name of the agent 109 | func (a *Agent) NodeName() (string, error) { 110 | if a.nodeName != "" { 111 | return a.nodeName, nil 112 | } 113 | info, err := a.Self() 114 | if err != nil { 115 | return "", err 116 | } 117 | name := info["Config"]["NodeName"].(string) 118 | a.nodeName = name 119 | return name, nil 120 | } 121 | 122 | // Checks returns the locally registered checks 123 | func (a *Agent) Checks() (map[string]*AgentCheck, error) { 124 | r := a.c.newRequest("GET", "/v1/agent/checks") 125 | _, resp, err := requireOK(a.c.doRequest(r)) 126 | if err != nil { 127 | return nil, err 128 | } 129 | defer resp.Body.Close() 130 | 131 | var out map[string]*AgentCheck 132 | if err := decodeBody(resp, &out); err != nil { 133 | return nil, err 134 | } 135 | return out, nil 136 | } 137 | 138 | // Services returns the locally registered services 139 | func (a *Agent) Services() (map[string]*AgentService, error) { 140 | r := a.c.newRequest("GET", "/v1/agent/services") 141 | _, resp, err := requireOK(a.c.doRequest(r)) 142 | if err != nil { 143 | return nil, err 144 | } 145 | defer resp.Body.Close() 146 | 147 | var out map[string]*AgentService 148 | if err := decodeBody(resp, &out); err != nil { 149 | return nil, err 150 | } 151 | return out, nil 152 | } 153 | 154 | // Members returns the known gossip members. The WAN 155 | // flag can be used to query a server for WAN members. 156 | func (a *Agent) Members(wan bool) ([]*AgentMember, error) { 157 | r := a.c.newRequest("GET", "/v1/agent/members") 158 | if wan { 159 | r.params.Set("wan", "1") 160 | } 161 | _, resp, err := requireOK(a.c.doRequest(r)) 162 | if err != nil { 163 | return nil, err 164 | } 165 | defer resp.Body.Close() 166 | 167 | var out []*AgentMember 168 | if err := decodeBody(resp, &out); err != nil { 169 | return nil, err 170 | } 171 | return out, nil 172 | } 173 | 174 | // ServiceRegister is used to register a new service with 175 | // the local agent 176 | func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error { 177 | r := a.c.newRequest("PUT", "/v1/agent/service/register") 178 | r.obj = service 179 | _, resp, err := requireOK(a.c.doRequest(r)) 180 | if err != nil { 181 | return err 182 | } 183 | resp.Body.Close() 184 | return nil 185 | } 186 | 187 | // ServiceDeregister is used to deregister a service with 188 | // the local agent 189 | func (a *Agent) ServiceDeregister(serviceID string) error { 190 | r := a.c.newRequest("PUT", "/v1/agent/service/deregister/"+serviceID) 191 | _, resp, err := requireOK(a.c.doRequest(r)) 192 | if err != nil { 193 | return err 194 | } 195 | resp.Body.Close() 196 | return nil 197 | } 198 | 199 | // PassTTL is used to set a TTL check to the passing state 200 | func (a *Agent) PassTTL(checkID, note string) error { 201 | return a.UpdateTTL(checkID, note, "pass") 202 | } 203 | 204 | // WarnTTL is used to set a TTL check to the warning state 205 | func (a *Agent) WarnTTL(checkID, note string) error { 206 | return a.UpdateTTL(checkID, note, "warn") 207 | } 208 | 209 | // FailTTL is used to set a TTL check to the failing state 210 | func (a *Agent) FailTTL(checkID, note string) error { 211 | return a.UpdateTTL(checkID, note, "fail") 212 | } 213 | 214 | // UpdateTTL is used to update the TTL of a check 215 | func (a *Agent) UpdateTTL(checkID, note, status string) error { 216 | switch status { 217 | case "pass": 218 | case "warn": 219 | case "fail": 220 | default: 221 | return fmt.Errorf("Invalid status: %s", status) 222 | } 223 | endpoint := fmt.Sprintf("/v1/agent/check/%s/%s", status, checkID) 224 | r := a.c.newRequest("PUT", endpoint) 225 | r.params.Set("note", note) 226 | _, resp, err := requireOK(a.c.doRequest(r)) 227 | if err != nil { 228 | return err 229 | } 230 | resp.Body.Close() 231 | return nil 232 | } 233 | 234 | // CheckRegister is used to register a new check with 235 | // the local agent 236 | func (a *Agent) CheckRegister(check *AgentCheckRegistration) error { 237 | r := a.c.newRequest("PUT", "/v1/agent/check/register") 238 | r.obj = check 239 | _, resp, err := requireOK(a.c.doRequest(r)) 240 | if err != nil { 241 | return err 242 | } 243 | resp.Body.Close() 244 | return nil 245 | } 246 | 247 | // CheckDeregister is used to deregister a check with 248 | // the local agent 249 | func (a *Agent) CheckDeregister(checkID string) error { 250 | r := a.c.newRequest("PUT", "/v1/agent/check/deregister/"+checkID) 251 | _, resp, err := requireOK(a.c.doRequest(r)) 252 | if err != nil { 253 | return err 254 | } 255 | resp.Body.Close() 256 | return nil 257 | } 258 | 259 | // Join is used to instruct the agent to attempt a join to 260 | // another cluster member 261 | func (a *Agent) Join(addr string, wan bool) error { 262 | r := a.c.newRequest("PUT", "/v1/agent/join/"+addr) 263 | if wan { 264 | r.params.Set("wan", "1") 265 | } 266 | _, resp, err := requireOK(a.c.doRequest(r)) 267 | if err != nil { 268 | return err 269 | } 270 | resp.Body.Close() 271 | return nil 272 | } 273 | 274 | // ForceLeave is used to have the agent eject a failed node 275 | func (a *Agent) ForceLeave(node string) error { 276 | r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node) 277 | _, resp, err := requireOK(a.c.doRequest(r)) 278 | if err != nil { 279 | return err 280 | } 281 | resp.Body.Close() 282 | return nil 283 | } 284 | 285 | // EnableServiceMaintenance toggles service maintenance mode on 286 | // for the given service ID. 287 | func (a *Agent) EnableServiceMaintenance(serviceID, reason string) error { 288 | r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID) 289 | r.params.Set("enable", "true") 290 | r.params.Set("reason", reason) 291 | _, resp, err := requireOK(a.c.doRequest(r)) 292 | if err != nil { 293 | return err 294 | } 295 | resp.Body.Close() 296 | return nil 297 | } 298 | 299 | // DisableServiceMaintenance toggles service maintenance mode off 300 | // for the given service ID. 301 | func (a *Agent) DisableServiceMaintenance(serviceID string) error { 302 | r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID) 303 | r.params.Set("enable", "false") 304 | _, resp, err := requireOK(a.c.doRequest(r)) 305 | if err != nil { 306 | return err 307 | } 308 | resp.Body.Close() 309 | return nil 310 | } 311 | 312 | // EnableNodeMaintenance toggles node maintenance mode on for the 313 | // agent we are connected to. 314 | func (a *Agent) EnableNodeMaintenance(reason string) error { 315 | r := a.c.newRequest("PUT", "/v1/agent/maintenance") 316 | r.params.Set("enable", "true") 317 | r.params.Set("reason", reason) 318 | _, resp, err := requireOK(a.c.doRequest(r)) 319 | if err != nil { 320 | return err 321 | } 322 | resp.Body.Close() 323 | return nil 324 | } 325 | 326 | // DisableNodeMaintenance toggles node maintenance mode off for the 327 | // agent we are connected to. 328 | func (a *Agent) DisableNodeMaintenance() error { 329 | r := a.c.newRequest("PUT", "/v1/agent/maintenance") 330 | r.params.Set("enable", "false") 331 | _, resp, err := requireOK(a.c.doRequest(r)) 332 | if err != nil { 333 | return err 334 | } 335 | resp.Body.Close() 336 | return nil 337 | } 338 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/lock.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | const ( 11 | // DefaultLockSessionName is the Session Name we assign if none is provided 12 | DefaultLockSessionName = "Consul API Lock" 13 | 14 | // DefaultLockSessionTTL is the default session TTL if no Session is provided 15 | // when creating a new Lock. This is used because we do not have another 16 | // other check to depend upon. 17 | DefaultLockSessionTTL = "15s" 18 | 19 | // DefaultLockWaitTime is how long we block for at a time to check if lock 20 | // acquisition is possible. This affects the minimum time it takes to cancel 21 | // a Lock acquisition. 22 | DefaultLockWaitTime = 15 * time.Second 23 | 24 | // DefaultLockRetryTime is how long we wait after a failed lock acquisition 25 | // before attempting to do the lock again. This is so that once a lock-delay 26 | // is in effect, we do not hot loop retrying the acquisition. 27 | DefaultLockRetryTime = 5 * time.Second 28 | 29 | // DefaultMonitorRetryTime is how long we wait after a failed monitor check 30 | // of a lock (500 response code). This allows the monitor to ride out brief 31 | // periods of unavailability, subject to the MonitorRetries setting in the 32 | // lock options which is by default set to 0, disabling this feature. 33 | DefaultMonitorRetryTime = 2 * time.Second 34 | 35 | // LockFlagValue is a magic flag we set to indicate a key 36 | // is being used for a lock. It is used to detect a potential 37 | // conflict with a semaphore. 38 | LockFlagValue = 0x2ddccbc058a50c18 39 | ) 40 | 41 | var ( 42 | // ErrLockHeld is returned if we attempt to double lock 43 | ErrLockHeld = fmt.Errorf("Lock already held") 44 | 45 | // ErrLockNotHeld is returned if we attempt to unlock a lock 46 | // that we do not hold. 47 | ErrLockNotHeld = fmt.Errorf("Lock not held") 48 | 49 | // ErrLockInUse is returned if we attempt to destroy a lock 50 | // that is in use. 51 | ErrLockInUse = fmt.Errorf("Lock in use") 52 | 53 | // ErrLockConflict is returned if the flags on a key 54 | // used for a lock do not match expectation 55 | ErrLockConflict = fmt.Errorf("Existing key does not match lock use") 56 | ) 57 | 58 | // Lock is used to implement client-side leader election. It is follows the 59 | // algorithm as described here: https://consul.io/docs/guides/leader-election.html. 60 | type Lock struct { 61 | c *Client 62 | opts *LockOptions 63 | 64 | isHeld bool 65 | sessionRenew chan struct{} 66 | lockSession string 67 | l sync.Mutex 68 | } 69 | 70 | // LockOptions is used to parameterize the Lock behavior. 71 | type LockOptions struct { 72 | Key string // Must be set and have write permissions 73 | Value []byte // Optional, value to associate with the lock 74 | Session string // Optional, created if not specified 75 | SessionName string // Optional, defaults to DefaultLockSessionName 76 | SessionTTL string // Optional, defaults to DefaultLockSessionTTL 77 | MonitorRetries int // Optional, defaults to 0 which means no retries 78 | MonitorRetryTime time.Duration // Optional, defaults to DefaultMonitorRetryTime 79 | } 80 | 81 | // LockKey returns a handle to a lock struct which can be used 82 | // to acquire and release the mutex. The key used must have 83 | // write permissions. 84 | func (c *Client) LockKey(key string) (*Lock, error) { 85 | opts := &LockOptions{ 86 | Key: key, 87 | } 88 | return c.LockOpts(opts) 89 | } 90 | 91 | // LockOpts returns a handle to a lock struct which can be used 92 | // to acquire and release the mutex. The key used must have 93 | // write permissions. 94 | func (c *Client) LockOpts(opts *LockOptions) (*Lock, error) { 95 | if opts.Key == "" { 96 | return nil, fmt.Errorf("missing key") 97 | } 98 | if opts.SessionName == "" { 99 | opts.SessionName = DefaultLockSessionName 100 | } 101 | if opts.SessionTTL == "" { 102 | opts.SessionTTL = DefaultLockSessionTTL 103 | } else { 104 | if _, err := time.ParseDuration(opts.SessionTTL); err != nil { 105 | return nil, fmt.Errorf("invalid SessionTTL: %v", err) 106 | } 107 | } 108 | if opts.MonitorRetryTime == 0 { 109 | opts.MonitorRetryTime = DefaultMonitorRetryTime 110 | } 111 | l := &Lock{ 112 | c: c, 113 | opts: opts, 114 | } 115 | return l, nil 116 | } 117 | 118 | // Lock attempts to acquire the lock and blocks while doing so. 119 | // Providing a non-nil stopCh can be used to abort the lock attempt. 120 | // Returns a channel that is closed if our lock is lost or an error. 121 | // This channel could be closed at any time due to session invalidation, 122 | // communication errors, operator intervention, etc. It is NOT safe to 123 | // assume that the lock is held until Unlock() unless the Session is specifically 124 | // created without any associated health checks. By default Consul sessions 125 | // prefer liveness over safety and an application must be able to handle 126 | // the lock being lost. 127 | func (l *Lock) Lock(stopCh <-chan struct{}) (<-chan struct{}, error) { 128 | // Hold the lock as we try to acquire 129 | l.l.Lock() 130 | defer l.l.Unlock() 131 | 132 | // Check if we already hold the lock 133 | if l.isHeld { 134 | return nil, ErrLockHeld 135 | } 136 | 137 | // Check if we need to create a session first 138 | l.lockSession = l.opts.Session 139 | if l.lockSession == "" { 140 | if s, err := l.createSession(); err != nil { 141 | return nil, fmt.Errorf("failed to create session: %v", err) 142 | } else { 143 | l.sessionRenew = make(chan struct{}) 144 | l.lockSession = s 145 | session := l.c.Session() 146 | go session.RenewPeriodic(l.opts.SessionTTL, s, nil, l.sessionRenew) 147 | 148 | // If we fail to acquire the lock, cleanup the session 149 | defer func() { 150 | if !l.isHeld { 151 | close(l.sessionRenew) 152 | l.sessionRenew = nil 153 | } 154 | }() 155 | } 156 | } 157 | 158 | // Setup the query options 159 | kv := l.c.KV() 160 | qOpts := &QueryOptions{ 161 | WaitTime: DefaultLockWaitTime, 162 | } 163 | 164 | WAIT: 165 | // Check if we should quit 166 | select { 167 | case <-stopCh: 168 | return nil, nil 169 | default: 170 | } 171 | 172 | // Look for an existing lock, blocking until not taken 173 | pair, meta, err := kv.Get(l.opts.Key, qOpts) 174 | if err != nil { 175 | return nil, fmt.Errorf("failed to read lock: %v", err) 176 | } 177 | if pair != nil && pair.Flags != LockFlagValue { 178 | return nil, ErrLockConflict 179 | } 180 | locked := false 181 | if pair != nil && pair.Session == l.lockSession { 182 | goto HELD 183 | } 184 | if pair != nil && pair.Session != "" { 185 | qOpts.WaitIndex = meta.LastIndex 186 | goto WAIT 187 | } 188 | 189 | // Try to acquire the lock 190 | pair = l.lockEntry(l.lockSession) 191 | locked, _, err = kv.Acquire(pair, nil) 192 | if err != nil { 193 | return nil, fmt.Errorf("failed to acquire lock: %v", err) 194 | } 195 | 196 | // Handle the case of not getting the lock 197 | if !locked { 198 | // Determine why the lock failed 199 | qOpts.WaitIndex = 0 200 | pair, meta, err = kv.Get(l.opts.Key, qOpts) 201 | if pair != nil && pair.Session != "" { 202 | //If the session is not null, this means that a wait can safely happen 203 | //using a long poll 204 | qOpts.WaitIndex = meta.LastIndex 205 | goto WAIT 206 | } else { 207 | // If the session is empty and the lock failed to acquire, then it means 208 | // a lock-delay is in effect and a timed wait must be used 209 | select { 210 | case <-time.After(DefaultLockRetryTime): 211 | goto WAIT 212 | case <-stopCh: 213 | return nil, nil 214 | } 215 | } 216 | } 217 | 218 | HELD: 219 | // Watch to ensure we maintain leadership 220 | leaderCh := make(chan struct{}) 221 | go l.monitorLock(l.lockSession, leaderCh) 222 | 223 | // Set that we own the lock 224 | l.isHeld = true 225 | 226 | // Locked! All done 227 | return leaderCh, nil 228 | } 229 | 230 | // Unlock released the lock. It is an error to call this 231 | // if the lock is not currently held. 232 | func (l *Lock) Unlock() error { 233 | // Hold the lock as we try to release 234 | l.l.Lock() 235 | defer l.l.Unlock() 236 | 237 | // Ensure the lock is actually held 238 | if !l.isHeld { 239 | return ErrLockNotHeld 240 | } 241 | 242 | // Set that we no longer own the lock 243 | l.isHeld = false 244 | 245 | // Stop the session renew 246 | if l.sessionRenew != nil { 247 | defer func() { 248 | close(l.sessionRenew) 249 | l.sessionRenew = nil 250 | }() 251 | } 252 | 253 | // Get the lock entry, and clear the lock session 254 | lockEnt := l.lockEntry(l.lockSession) 255 | l.lockSession = "" 256 | 257 | // Release the lock explicitly 258 | kv := l.c.KV() 259 | _, _, err := kv.Release(lockEnt, nil) 260 | if err != nil { 261 | return fmt.Errorf("failed to release lock: %v", err) 262 | } 263 | return nil 264 | } 265 | 266 | // Destroy is used to cleanup the lock entry. It is not necessary 267 | // to invoke. It will fail if the lock is in use. 268 | func (l *Lock) Destroy() error { 269 | // Hold the lock as we try to release 270 | l.l.Lock() 271 | defer l.l.Unlock() 272 | 273 | // Check if we already hold the lock 274 | if l.isHeld { 275 | return ErrLockHeld 276 | } 277 | 278 | // Look for an existing lock 279 | kv := l.c.KV() 280 | pair, _, err := kv.Get(l.opts.Key, nil) 281 | if err != nil { 282 | return fmt.Errorf("failed to read lock: %v", err) 283 | } 284 | 285 | // Nothing to do if the lock does not exist 286 | if pair == nil { 287 | return nil 288 | } 289 | 290 | // Check for possible flag conflict 291 | if pair.Flags != LockFlagValue { 292 | return ErrLockConflict 293 | } 294 | 295 | // Check if it is in use 296 | if pair.Session != "" { 297 | return ErrLockInUse 298 | } 299 | 300 | // Attempt the delete 301 | didRemove, _, err := kv.DeleteCAS(pair, nil) 302 | if err != nil { 303 | return fmt.Errorf("failed to remove lock: %v", err) 304 | } 305 | if !didRemove { 306 | return ErrLockInUse 307 | } 308 | return nil 309 | } 310 | 311 | // createSession is used to create a new managed session 312 | func (l *Lock) createSession() (string, error) { 313 | session := l.c.Session() 314 | se := &SessionEntry{ 315 | Name: l.opts.SessionName, 316 | TTL: l.opts.SessionTTL, 317 | } 318 | id, _, err := session.Create(se, nil) 319 | if err != nil { 320 | return "", err 321 | } 322 | return id, nil 323 | } 324 | 325 | // lockEntry returns a formatted KVPair for the lock 326 | func (l *Lock) lockEntry(session string) *KVPair { 327 | return &KVPair{ 328 | Key: l.opts.Key, 329 | Value: l.opts.Value, 330 | Session: session, 331 | Flags: LockFlagValue, 332 | } 333 | } 334 | 335 | // monitorLock is a long running routine to monitor a lock ownership 336 | // It closes the stopCh if we lose our leadership. 337 | func (l *Lock) monitorLock(session string, stopCh chan struct{}) { 338 | defer close(stopCh) 339 | kv := l.c.KV() 340 | opts := &QueryOptions{RequireConsistent: true} 341 | WAIT: 342 | retries := l.opts.MonitorRetries 343 | RETRY: 344 | pair, meta, err := kv.Get(l.opts.Key, opts) 345 | if err != nil { 346 | // TODO (slackpad) - Make a real error type here instead of using 347 | // a string check. 348 | const serverError = "Unexpected response code: 500" 349 | 350 | // If configured we can try to ride out a brief Consul unavailability 351 | // by doing retries. Note that we have to attempt the retry in a non- 352 | // blocking fashion so that we have a clean place to reset the retry 353 | // counter if service is restored. 354 | if retries > 0 && strings.Contains(err.Error(), serverError) { 355 | time.Sleep(l.opts.MonitorRetryTime) 356 | retries-- 357 | opts.WaitIndex = 0 358 | goto RETRY 359 | } 360 | return 361 | } 362 | if pair != nil && pair.Session == session { 363 | opts.WaitIndex = meta.LastIndex 364 | goto WAIT 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/divideandconquer/go-merge/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Kyle Boorky. 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "{}" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright {yyyy} {name of copyright owner} 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "encoding/json" 7 | "fmt" 8 | "io" 9 | "log" 10 | "net" 11 | "net/http" 12 | "net/url" 13 | "os" 14 | "strconv" 15 | "strings" 16 | "time" 17 | 18 | "github.com/hashicorp/go-cleanhttp" 19 | ) 20 | 21 | // QueryOptions are used to parameterize a query 22 | type QueryOptions struct { 23 | // Providing a datacenter overwrites the DC provided 24 | // by the Config 25 | Datacenter string 26 | 27 | // AllowStale allows any Consul server (non-leader) to service 28 | // a read. This allows for lower latency and higher throughput 29 | AllowStale bool 30 | 31 | // RequireConsistent forces the read to be fully consistent. 32 | // This is more expensive but prevents ever performing a stale 33 | // read. 34 | RequireConsistent bool 35 | 36 | // WaitIndex is used to enable a blocking query. Waits 37 | // until the timeout or the next index is reached 38 | WaitIndex uint64 39 | 40 | // WaitTime is used to bound the duration of a wait. 41 | // Defaults to that of the Config, but can be overridden. 42 | WaitTime time.Duration 43 | 44 | // Token is used to provide a per-request ACL token 45 | // which overrides the agent's default token. 46 | Token string 47 | 48 | // Near is used to provide a node name that will sort the results 49 | // in ascending order based on the estimated round trip time from 50 | // that node. Setting this to "_agent" will use the agent's node 51 | // for the sort. 52 | Near string 53 | } 54 | 55 | // WriteOptions are used to parameterize a write 56 | type WriteOptions struct { 57 | // Providing a datacenter overwrites the DC provided 58 | // by the Config 59 | Datacenter string 60 | 61 | // Token is used to provide a per-request ACL token 62 | // which overrides the agent's default token. 63 | Token string 64 | } 65 | 66 | // QueryMeta is used to return meta data about a query 67 | type QueryMeta struct { 68 | // LastIndex. This can be used as a WaitIndex to perform 69 | // a blocking query 70 | LastIndex uint64 71 | 72 | // Time of last contact from the leader for the 73 | // server servicing the request 74 | LastContact time.Duration 75 | 76 | // Is there a known leader 77 | KnownLeader bool 78 | 79 | // How long did the request take 80 | RequestTime time.Duration 81 | } 82 | 83 | // WriteMeta is used to return meta data about a write 84 | type WriteMeta struct { 85 | // How long did the request take 86 | RequestTime time.Duration 87 | } 88 | 89 | // HttpBasicAuth is used to authenticate http client with HTTP Basic Authentication 90 | type HttpBasicAuth struct { 91 | // Username to use for HTTP Basic Authentication 92 | Username string 93 | 94 | // Password to use for HTTP Basic Authentication 95 | Password string 96 | } 97 | 98 | // Config is used to configure the creation of a client 99 | type Config struct { 100 | // Address is the address of the Consul server 101 | Address string 102 | 103 | // Scheme is the URI scheme for the Consul server 104 | Scheme string 105 | 106 | // Datacenter to use. If not provided, the default agent datacenter is used. 107 | Datacenter string 108 | 109 | // HttpClient is the client to use. Default will be 110 | // used if not provided. 111 | HttpClient *http.Client 112 | 113 | // HttpAuth is the auth info to use for http access. 114 | HttpAuth *HttpBasicAuth 115 | 116 | // WaitTime limits how long a Watch will block. If not provided, 117 | // the agent default values will be used. 118 | WaitTime time.Duration 119 | 120 | // Token is used to provide a per-request ACL token 121 | // which overrides the agent's default token. 122 | Token string 123 | } 124 | 125 | // DefaultConfig returns a default configuration for the client 126 | func DefaultConfig() *Config { 127 | config := &Config{ 128 | Address: "127.0.0.1:8500", 129 | Scheme: "http", 130 | HttpClient: cleanhttp.DefaultClient(), 131 | } 132 | 133 | if addr := os.Getenv("CONSUL_HTTP_ADDR"); addr != "" { 134 | config.Address = addr 135 | } 136 | 137 | if token := os.Getenv("CONSUL_HTTP_TOKEN"); token != "" { 138 | config.Token = token 139 | } 140 | 141 | if auth := os.Getenv("CONSUL_HTTP_AUTH"); auth != "" { 142 | var username, password string 143 | if strings.Contains(auth, ":") { 144 | split := strings.SplitN(auth, ":", 2) 145 | username = split[0] 146 | password = split[1] 147 | } else { 148 | username = auth 149 | } 150 | 151 | config.HttpAuth = &HttpBasicAuth{ 152 | Username: username, 153 | Password: password, 154 | } 155 | } 156 | 157 | if ssl := os.Getenv("CONSUL_HTTP_SSL"); ssl != "" { 158 | enabled, err := strconv.ParseBool(ssl) 159 | if err != nil { 160 | log.Printf("[WARN] client: could not parse CONSUL_HTTP_SSL: %s", err) 161 | } 162 | 163 | if enabled { 164 | config.Scheme = "https" 165 | } 166 | } 167 | 168 | if verify := os.Getenv("CONSUL_HTTP_SSL_VERIFY"); verify != "" { 169 | doVerify, err := strconv.ParseBool(verify) 170 | if err != nil { 171 | log.Printf("[WARN] client: could not parse CONSUL_HTTP_SSL_VERIFY: %s", err) 172 | } 173 | 174 | if !doVerify { 175 | config.HttpClient.Transport = &http.Transport{ 176 | TLSClientConfig: &tls.Config{ 177 | InsecureSkipVerify: true, 178 | }, 179 | } 180 | } 181 | } 182 | 183 | return config 184 | } 185 | 186 | // Client provides a client to the Consul API 187 | type Client struct { 188 | config Config 189 | } 190 | 191 | // NewClient returns a new client 192 | func NewClient(config *Config) (*Client, error) { 193 | // bootstrap the config 194 | defConfig := DefaultConfig() 195 | 196 | if len(config.Address) == 0 { 197 | config.Address = defConfig.Address 198 | } 199 | 200 | if len(config.Scheme) == 0 { 201 | config.Scheme = defConfig.Scheme 202 | } 203 | 204 | if config.HttpClient == nil { 205 | config.HttpClient = defConfig.HttpClient 206 | } 207 | 208 | if parts := strings.SplitN(config.Address, "unix://", 2); len(parts) == 2 { 209 | trans := cleanhttp.DefaultTransport() 210 | trans.Dial = func(_, _ string) (net.Conn, error) { 211 | return net.Dial("unix", parts[1]) 212 | } 213 | config.HttpClient = &http.Client{ 214 | Transport: trans, 215 | } 216 | config.Address = parts[1] 217 | } 218 | 219 | client := &Client{ 220 | config: *config, 221 | } 222 | return client, nil 223 | } 224 | 225 | // request is used to help build up a request 226 | type request struct { 227 | config *Config 228 | method string 229 | url *url.URL 230 | params url.Values 231 | body io.Reader 232 | obj interface{} 233 | } 234 | 235 | // setQueryOptions is used to annotate the request with 236 | // additional query options 237 | func (r *request) setQueryOptions(q *QueryOptions) { 238 | if q == nil { 239 | return 240 | } 241 | if q.Datacenter != "" { 242 | r.params.Set("dc", q.Datacenter) 243 | } 244 | if q.AllowStale { 245 | r.params.Set("stale", "") 246 | } 247 | if q.RequireConsistent { 248 | r.params.Set("consistent", "") 249 | } 250 | if q.WaitIndex != 0 { 251 | r.params.Set("index", strconv.FormatUint(q.WaitIndex, 10)) 252 | } 253 | if q.WaitTime != 0 { 254 | r.params.Set("wait", durToMsec(q.WaitTime)) 255 | } 256 | if q.Token != "" { 257 | r.params.Set("token", q.Token) 258 | } 259 | if q.Near != "" { 260 | r.params.Set("near", q.Near) 261 | } 262 | } 263 | 264 | // durToMsec converts a duration to a millisecond specified string 265 | func durToMsec(dur time.Duration) string { 266 | return fmt.Sprintf("%dms", dur/time.Millisecond) 267 | } 268 | 269 | // setWriteOptions is used to annotate the request with 270 | // additional write options 271 | func (r *request) setWriteOptions(q *WriteOptions) { 272 | if q == nil { 273 | return 274 | } 275 | if q.Datacenter != "" { 276 | r.params.Set("dc", q.Datacenter) 277 | } 278 | if q.Token != "" { 279 | r.params.Set("token", q.Token) 280 | } 281 | } 282 | 283 | // toHTTP converts the request to an HTTP request 284 | func (r *request) toHTTP() (*http.Request, error) { 285 | // Encode the query parameters 286 | r.url.RawQuery = r.params.Encode() 287 | 288 | // Check if we should encode the body 289 | if r.body == nil && r.obj != nil { 290 | if b, err := encodeBody(r.obj); err != nil { 291 | return nil, err 292 | } else { 293 | r.body = b 294 | } 295 | } 296 | 297 | // Create the HTTP request 298 | req, err := http.NewRequest(r.method, r.url.RequestURI(), r.body) 299 | if err != nil { 300 | return nil, err 301 | } 302 | 303 | req.URL.Host = r.url.Host 304 | req.URL.Scheme = r.url.Scheme 305 | req.Host = r.url.Host 306 | 307 | // Setup auth 308 | if r.config.HttpAuth != nil { 309 | req.SetBasicAuth(r.config.HttpAuth.Username, r.config.HttpAuth.Password) 310 | } 311 | 312 | return req, nil 313 | } 314 | 315 | // newRequest is used to create a new request 316 | func (c *Client) newRequest(method, path string) *request { 317 | r := &request{ 318 | config: &c.config, 319 | method: method, 320 | url: &url.URL{ 321 | Scheme: c.config.Scheme, 322 | Host: c.config.Address, 323 | Path: path, 324 | }, 325 | params: make(map[string][]string), 326 | } 327 | if c.config.Datacenter != "" { 328 | r.params.Set("dc", c.config.Datacenter) 329 | } 330 | if c.config.WaitTime != 0 { 331 | r.params.Set("wait", durToMsec(r.config.WaitTime)) 332 | } 333 | if c.config.Token != "" { 334 | r.params.Set("token", r.config.Token) 335 | } 336 | return r 337 | } 338 | 339 | // doRequest runs a request with our client 340 | func (c *Client) doRequest(r *request) (time.Duration, *http.Response, error) { 341 | req, err := r.toHTTP() 342 | if err != nil { 343 | return 0, nil, err 344 | } 345 | start := time.Now() 346 | resp, err := c.config.HttpClient.Do(req) 347 | diff := time.Now().Sub(start) 348 | return diff, resp, err 349 | } 350 | 351 | // Query is used to do a GET request against an endpoint 352 | // and deserialize the response into an interface using 353 | // standard Consul conventions. 354 | func (c *Client) query(endpoint string, out interface{}, q *QueryOptions) (*QueryMeta, error) { 355 | r := c.newRequest("GET", endpoint) 356 | r.setQueryOptions(q) 357 | rtt, resp, err := requireOK(c.doRequest(r)) 358 | if err != nil { 359 | return nil, err 360 | } 361 | defer resp.Body.Close() 362 | 363 | qm := &QueryMeta{} 364 | parseQueryMeta(resp, qm) 365 | qm.RequestTime = rtt 366 | 367 | if err := decodeBody(resp, out); err != nil { 368 | return nil, err 369 | } 370 | return qm, nil 371 | } 372 | 373 | // write is used to do a PUT request against an endpoint 374 | // and serialize/deserialized using the standard Consul conventions. 375 | func (c *Client) write(endpoint string, in, out interface{}, q *WriteOptions) (*WriteMeta, error) { 376 | r := c.newRequest("PUT", endpoint) 377 | r.setWriteOptions(q) 378 | r.obj = in 379 | rtt, resp, err := requireOK(c.doRequest(r)) 380 | if err != nil { 381 | return nil, err 382 | } 383 | defer resp.Body.Close() 384 | 385 | wm := &WriteMeta{RequestTime: rtt} 386 | if out != nil { 387 | if err := decodeBody(resp, &out); err != nil { 388 | return nil, err 389 | } 390 | } 391 | return wm, nil 392 | } 393 | 394 | // parseQueryMeta is used to help parse query meta-data 395 | func parseQueryMeta(resp *http.Response, q *QueryMeta) error { 396 | header := resp.Header 397 | 398 | // Parse the X-Consul-Index 399 | index, err := strconv.ParseUint(header.Get("X-Consul-Index"), 10, 64) 400 | if err != nil { 401 | return fmt.Errorf("Failed to parse X-Consul-Index: %v", err) 402 | } 403 | q.LastIndex = index 404 | 405 | // Parse the X-Consul-LastContact 406 | last, err := strconv.ParseUint(header.Get("X-Consul-LastContact"), 10, 64) 407 | if err != nil { 408 | return fmt.Errorf("Failed to parse X-Consul-LastContact: %v", err) 409 | } 410 | q.LastContact = time.Duration(last) * time.Millisecond 411 | 412 | // Parse the X-Consul-KnownLeader 413 | switch header.Get("X-Consul-KnownLeader") { 414 | case "true": 415 | q.KnownLeader = true 416 | default: 417 | q.KnownLeader = false 418 | } 419 | return nil 420 | } 421 | 422 | // decodeBody is used to JSON decode a body 423 | func decodeBody(resp *http.Response, out interface{}) error { 424 | dec := json.NewDecoder(resp.Body) 425 | return dec.Decode(out) 426 | } 427 | 428 | // encodeBody is used to encode a request body 429 | func encodeBody(obj interface{}) (io.Reader, error) { 430 | buf := bytes.NewBuffer(nil) 431 | enc := json.NewEncoder(buf) 432 | if err := enc.Encode(obj); err != nil { 433 | return nil, err 434 | } 435 | return buf, nil 436 | } 437 | 438 | // requireOK is used to wrap doRequest and check for a 200 439 | func requireOK(d time.Duration, resp *http.Response, e error) (time.Duration, *http.Response, error) { 440 | if e != nil { 441 | if resp != nil { 442 | resp.Body.Close() 443 | } 444 | return d, nil, e 445 | } 446 | if resp.StatusCode != 200 { 447 | var buf bytes.Buffer 448 | io.Copy(&buf, resp.Body) 449 | resp.Body.Close() 450 | return d, nil, fmt.Errorf("Unexpected response code: %d (%s)", resp.StatusCode, buf.Bytes()) 451 | } 452 | return d, resp, nil 453 | } 454 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/api/semaphore.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "path" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | const ( 12 | // DefaultSemaphoreSessionName is the Session Name we assign if none is provided 13 | DefaultSemaphoreSessionName = "Consul API Semaphore" 14 | 15 | // DefaultSemaphoreSessionTTL is the default session TTL if no Session is provided 16 | // when creating a new Semaphore. This is used because we do not have another 17 | // other check to depend upon. 18 | DefaultSemaphoreSessionTTL = "15s" 19 | 20 | // DefaultSemaphoreWaitTime is how long we block for at a time to check if semaphore 21 | // acquisition is possible. This affects the minimum time it takes to cancel 22 | // a Semaphore acquisition. 23 | DefaultSemaphoreWaitTime = 15 * time.Second 24 | 25 | // DefaultSemaphoreKey is the key used within the prefix to 26 | // use for coordination between all the contenders. 27 | DefaultSemaphoreKey = ".lock" 28 | 29 | // SemaphoreFlagValue is a magic flag we set to indicate a key 30 | // is being used for a semaphore. It is used to detect a potential 31 | // conflict with a lock. 32 | SemaphoreFlagValue = 0xe0f69a2baa414de0 33 | ) 34 | 35 | var ( 36 | // ErrSemaphoreHeld is returned if we attempt to double lock 37 | ErrSemaphoreHeld = fmt.Errorf("Semaphore already held") 38 | 39 | // ErrSemaphoreNotHeld is returned if we attempt to unlock a semaphore 40 | // that we do not hold. 41 | ErrSemaphoreNotHeld = fmt.Errorf("Semaphore not held") 42 | 43 | // ErrSemaphoreInUse is returned if we attempt to destroy a semaphore 44 | // that is in use. 45 | ErrSemaphoreInUse = fmt.Errorf("Semaphore in use") 46 | 47 | // ErrSemaphoreConflict is returned if the flags on a key 48 | // used for a semaphore do not match expectation 49 | ErrSemaphoreConflict = fmt.Errorf("Existing key does not match semaphore use") 50 | ) 51 | 52 | // Semaphore is used to implement a distributed semaphore 53 | // using the Consul KV primitives. 54 | type Semaphore struct { 55 | c *Client 56 | opts *SemaphoreOptions 57 | 58 | isHeld bool 59 | sessionRenew chan struct{} 60 | lockSession string 61 | l sync.Mutex 62 | } 63 | 64 | // SemaphoreOptions is used to parameterize the Semaphore 65 | type SemaphoreOptions struct { 66 | Prefix string // Must be set and have write permissions 67 | Limit int // Must be set, and be positive 68 | Value []byte // Optional, value to associate with the contender entry 69 | Session string // Optional, created if not specified 70 | SessionName string // Optional, defaults to DefaultLockSessionName 71 | SessionTTL string // Optional, defaults to DefaultLockSessionTTL 72 | } 73 | 74 | // semaphoreLock is written under the DefaultSemaphoreKey and 75 | // is used to coordinate between all the contenders. 76 | type semaphoreLock struct { 77 | // Limit is the integer limit of holders. This is used to 78 | // verify that all the holders agree on the value. 79 | Limit int 80 | 81 | // Holders is a list of all the semaphore holders. 82 | // It maps the session ID to true. It is used as a set effectively. 83 | Holders map[string]bool 84 | } 85 | 86 | // SemaphorePrefix is used to created a Semaphore which will operate 87 | // at the given KV prefix and uses the given limit for the semaphore. 88 | // The prefix must have write privileges, and the limit must be agreed 89 | // upon by all contenders. 90 | func (c *Client) SemaphorePrefix(prefix string, limit int) (*Semaphore, error) { 91 | opts := &SemaphoreOptions{ 92 | Prefix: prefix, 93 | Limit: limit, 94 | } 95 | return c.SemaphoreOpts(opts) 96 | } 97 | 98 | // SemaphoreOpts is used to create a Semaphore with the given options. 99 | // The prefix must have write privileges, and the limit must be agreed 100 | // upon by all contenders. If a Session is not provided, one will be created. 101 | func (c *Client) SemaphoreOpts(opts *SemaphoreOptions) (*Semaphore, error) { 102 | if opts.Prefix == "" { 103 | return nil, fmt.Errorf("missing prefix") 104 | } 105 | if opts.Limit <= 0 { 106 | return nil, fmt.Errorf("semaphore limit must be positive") 107 | } 108 | if opts.SessionName == "" { 109 | opts.SessionName = DefaultSemaphoreSessionName 110 | } 111 | if opts.SessionTTL == "" { 112 | opts.SessionTTL = DefaultSemaphoreSessionTTL 113 | } else { 114 | if _, err := time.ParseDuration(opts.SessionTTL); err != nil { 115 | return nil, fmt.Errorf("invalid SessionTTL: %v", err) 116 | } 117 | } 118 | s := &Semaphore{ 119 | c: c, 120 | opts: opts, 121 | } 122 | return s, nil 123 | } 124 | 125 | // Acquire attempts to reserve a slot in the semaphore, blocking until 126 | // success, interrupted via the stopCh or an error is encountered. 127 | // Providing a non-nil stopCh can be used to abort the attempt. 128 | // On success, a channel is returned that represents our slot. 129 | // This channel could be closed at any time due to session invalidation, 130 | // communication errors, operator intervention, etc. It is NOT safe to 131 | // assume that the slot is held until Release() unless the Session is specifically 132 | // created without any associated health checks. By default Consul sessions 133 | // prefer liveness over safety and an application must be able to handle 134 | // the session being lost. 135 | func (s *Semaphore) Acquire(stopCh <-chan struct{}) (<-chan struct{}, error) { 136 | // Hold the lock as we try to acquire 137 | s.l.Lock() 138 | defer s.l.Unlock() 139 | 140 | // Check if we already hold the semaphore 141 | if s.isHeld { 142 | return nil, ErrSemaphoreHeld 143 | } 144 | 145 | // Check if we need to create a session first 146 | s.lockSession = s.opts.Session 147 | if s.lockSession == "" { 148 | if sess, err := s.createSession(); err != nil { 149 | return nil, fmt.Errorf("failed to create session: %v", err) 150 | } else { 151 | s.sessionRenew = make(chan struct{}) 152 | s.lockSession = sess 153 | session := s.c.Session() 154 | go session.RenewPeriodic(s.opts.SessionTTL, sess, nil, s.sessionRenew) 155 | 156 | // If we fail to acquire the lock, cleanup the session 157 | defer func() { 158 | if !s.isHeld { 159 | close(s.sessionRenew) 160 | s.sessionRenew = nil 161 | } 162 | }() 163 | } 164 | } 165 | 166 | // Create the contender entry 167 | kv := s.c.KV() 168 | made, _, err := kv.Acquire(s.contenderEntry(s.lockSession), nil) 169 | if err != nil || !made { 170 | return nil, fmt.Errorf("failed to make contender entry: %v", err) 171 | } 172 | 173 | // Setup the query options 174 | qOpts := &QueryOptions{ 175 | WaitTime: DefaultSemaphoreWaitTime, 176 | } 177 | 178 | WAIT: 179 | // Check if we should quit 180 | select { 181 | case <-stopCh: 182 | return nil, nil 183 | default: 184 | } 185 | 186 | // Read the prefix 187 | pairs, meta, err := kv.List(s.opts.Prefix, qOpts) 188 | if err != nil { 189 | return nil, fmt.Errorf("failed to read prefix: %v", err) 190 | } 191 | 192 | // Decode the lock 193 | lockPair := s.findLock(pairs) 194 | if lockPair.Flags != SemaphoreFlagValue { 195 | return nil, ErrSemaphoreConflict 196 | } 197 | lock, err := s.decodeLock(lockPair) 198 | if err != nil { 199 | return nil, err 200 | } 201 | 202 | // Verify we agree with the limit 203 | if lock.Limit != s.opts.Limit { 204 | return nil, fmt.Errorf("semaphore limit conflict (lock: %d, local: %d)", 205 | lock.Limit, s.opts.Limit) 206 | } 207 | 208 | // Prune the dead holders 209 | s.pruneDeadHolders(lock, pairs) 210 | 211 | // Check if the lock is held 212 | if len(lock.Holders) >= lock.Limit { 213 | qOpts.WaitIndex = meta.LastIndex 214 | goto WAIT 215 | } 216 | 217 | // Create a new lock with us as a holder 218 | lock.Holders[s.lockSession] = true 219 | newLock, err := s.encodeLock(lock, lockPair.ModifyIndex) 220 | if err != nil { 221 | return nil, err 222 | } 223 | 224 | // Attempt the acquisition 225 | didSet, _, err := kv.CAS(newLock, nil) 226 | if err != nil { 227 | return nil, fmt.Errorf("failed to update lock: %v", err) 228 | } 229 | if !didSet { 230 | // Update failed, could have been a race with another contender, 231 | // retry the operation 232 | goto WAIT 233 | } 234 | 235 | // Watch to ensure we maintain ownership of the slot 236 | lockCh := make(chan struct{}) 237 | go s.monitorLock(s.lockSession, lockCh) 238 | 239 | // Set that we own the lock 240 | s.isHeld = true 241 | 242 | // Acquired! All done 243 | return lockCh, nil 244 | } 245 | 246 | // Release is used to voluntarily give up our semaphore slot. It is 247 | // an error to call this if the semaphore has not been acquired. 248 | func (s *Semaphore) Release() error { 249 | // Hold the lock as we try to release 250 | s.l.Lock() 251 | defer s.l.Unlock() 252 | 253 | // Ensure the lock is actually held 254 | if !s.isHeld { 255 | return ErrSemaphoreNotHeld 256 | } 257 | 258 | // Set that we no longer own the lock 259 | s.isHeld = false 260 | 261 | // Stop the session renew 262 | if s.sessionRenew != nil { 263 | defer func() { 264 | close(s.sessionRenew) 265 | s.sessionRenew = nil 266 | }() 267 | } 268 | 269 | // Get and clear the lock session 270 | lockSession := s.lockSession 271 | s.lockSession = "" 272 | 273 | // Remove ourselves as a lock holder 274 | kv := s.c.KV() 275 | key := path.Join(s.opts.Prefix, DefaultSemaphoreKey) 276 | READ: 277 | pair, _, err := kv.Get(key, nil) 278 | if err != nil { 279 | return err 280 | } 281 | if pair == nil { 282 | pair = &KVPair{} 283 | } 284 | lock, err := s.decodeLock(pair) 285 | if err != nil { 286 | return err 287 | } 288 | 289 | // Create a new lock without us as a holder 290 | if _, ok := lock.Holders[lockSession]; ok { 291 | delete(lock.Holders, lockSession) 292 | newLock, err := s.encodeLock(lock, pair.ModifyIndex) 293 | if err != nil { 294 | return err 295 | } 296 | 297 | // Swap the locks 298 | didSet, _, err := kv.CAS(newLock, nil) 299 | if err != nil { 300 | return fmt.Errorf("failed to update lock: %v", err) 301 | } 302 | if !didSet { 303 | goto READ 304 | } 305 | } 306 | 307 | // Destroy the contender entry 308 | contenderKey := path.Join(s.opts.Prefix, lockSession) 309 | if _, err := kv.Delete(contenderKey, nil); err != nil { 310 | return err 311 | } 312 | return nil 313 | } 314 | 315 | // Destroy is used to cleanup the semaphore entry. It is not necessary 316 | // to invoke. It will fail if the semaphore is in use. 317 | func (s *Semaphore) Destroy() error { 318 | // Hold the lock as we try to acquire 319 | s.l.Lock() 320 | defer s.l.Unlock() 321 | 322 | // Check if we already hold the semaphore 323 | if s.isHeld { 324 | return ErrSemaphoreHeld 325 | } 326 | 327 | // List for the semaphore 328 | kv := s.c.KV() 329 | pairs, _, err := kv.List(s.opts.Prefix, nil) 330 | if err != nil { 331 | return fmt.Errorf("failed to read prefix: %v", err) 332 | } 333 | 334 | // Find the lock pair, bail if it doesn't exist 335 | lockPair := s.findLock(pairs) 336 | if lockPair.ModifyIndex == 0 { 337 | return nil 338 | } 339 | if lockPair.Flags != SemaphoreFlagValue { 340 | return ErrSemaphoreConflict 341 | } 342 | 343 | // Decode the lock 344 | lock, err := s.decodeLock(lockPair) 345 | if err != nil { 346 | return err 347 | } 348 | 349 | // Prune the dead holders 350 | s.pruneDeadHolders(lock, pairs) 351 | 352 | // Check if there are any holders 353 | if len(lock.Holders) > 0 { 354 | return ErrSemaphoreInUse 355 | } 356 | 357 | // Attempt the delete 358 | didRemove, _, err := kv.DeleteCAS(lockPair, nil) 359 | if err != nil { 360 | return fmt.Errorf("failed to remove semaphore: %v", err) 361 | } 362 | if !didRemove { 363 | return ErrSemaphoreInUse 364 | } 365 | return nil 366 | } 367 | 368 | // createSession is used to create a new managed session 369 | func (s *Semaphore) createSession() (string, error) { 370 | session := s.c.Session() 371 | se := &SessionEntry{ 372 | Name: s.opts.SessionName, 373 | TTL: s.opts.SessionTTL, 374 | Behavior: SessionBehaviorDelete, 375 | } 376 | id, _, err := session.Create(se, nil) 377 | if err != nil { 378 | return "", err 379 | } 380 | return id, nil 381 | } 382 | 383 | // contenderEntry returns a formatted KVPair for the contender 384 | func (s *Semaphore) contenderEntry(session string) *KVPair { 385 | return &KVPair{ 386 | Key: path.Join(s.opts.Prefix, session), 387 | Value: s.opts.Value, 388 | Session: session, 389 | Flags: SemaphoreFlagValue, 390 | } 391 | } 392 | 393 | // findLock is used to find the KV Pair which is used for coordination 394 | func (s *Semaphore) findLock(pairs KVPairs) *KVPair { 395 | key := path.Join(s.opts.Prefix, DefaultSemaphoreKey) 396 | for _, pair := range pairs { 397 | if pair.Key == key { 398 | return pair 399 | } 400 | } 401 | return &KVPair{Flags: SemaphoreFlagValue} 402 | } 403 | 404 | // decodeLock is used to decode a semaphoreLock from an 405 | // entry in Consul 406 | func (s *Semaphore) decodeLock(pair *KVPair) (*semaphoreLock, error) { 407 | // Handle if there is no lock 408 | if pair == nil || pair.Value == nil { 409 | return &semaphoreLock{ 410 | Limit: s.opts.Limit, 411 | Holders: make(map[string]bool), 412 | }, nil 413 | } 414 | 415 | l := &semaphoreLock{} 416 | if err := json.Unmarshal(pair.Value, l); err != nil { 417 | return nil, fmt.Errorf("lock decoding failed: %v", err) 418 | } 419 | return l, nil 420 | } 421 | 422 | // encodeLock is used to encode a semaphoreLock into a KVPair 423 | // that can be PUT 424 | func (s *Semaphore) encodeLock(l *semaphoreLock, oldIndex uint64) (*KVPair, error) { 425 | enc, err := json.Marshal(l) 426 | if err != nil { 427 | return nil, fmt.Errorf("lock encoding failed: %v", err) 428 | } 429 | pair := &KVPair{ 430 | Key: path.Join(s.opts.Prefix, DefaultSemaphoreKey), 431 | Value: enc, 432 | Flags: SemaphoreFlagValue, 433 | ModifyIndex: oldIndex, 434 | } 435 | return pair, nil 436 | } 437 | 438 | // pruneDeadHolders is used to remove all the dead lock holders 439 | func (s *Semaphore) pruneDeadHolders(lock *semaphoreLock, pairs KVPairs) { 440 | // Gather all the live holders 441 | alive := make(map[string]struct{}, len(pairs)) 442 | for _, pair := range pairs { 443 | if pair.Session != "" { 444 | alive[pair.Session] = struct{}{} 445 | } 446 | } 447 | 448 | // Remove any holders that are dead 449 | for holder := range lock.Holders { 450 | if _, ok := alive[holder]; !ok { 451 | delete(lock.Holders, holder) 452 | } 453 | } 454 | } 455 | 456 | // monitorLock is a long running routine to monitor a semaphore ownership 457 | // It closes the stopCh if we lose our slot. 458 | func (s *Semaphore) monitorLock(session string, stopCh chan struct{}) { 459 | defer close(stopCh) 460 | kv := s.c.KV() 461 | opts := &QueryOptions{RequireConsistent: true} 462 | WAIT: 463 | pairs, meta, err := kv.List(s.opts.Prefix, opts) 464 | if err != nil { 465 | return 466 | } 467 | lockPair := s.findLock(pairs) 468 | lock, err := s.decodeLock(lockPair) 469 | if err != nil { 470 | return 471 | } 472 | s.pruneDeadHolders(lock, pairs) 473 | if _, ok := lock.Holders[session]; ok { 474 | opts.WaitIndex = meta.LastIndex 475 | goto WAIT 476 | } 477 | } 478 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/consul/LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License, version 2.0 2 | 3 | 1. Definitions 4 | 5 | 1.1. “Contributor” 6 | 7 | means each individual or legal entity that creates, contributes to the 8 | creation of, or owns Covered Software. 9 | 10 | 1.2. “Contributor Version” 11 | 12 | means the combination of the Contributions of others (if any) used by a 13 | Contributor and that particular Contributor’s Contribution. 14 | 15 | 1.3. “Contribution” 16 | 17 | means Covered Software of a particular Contributor. 18 | 19 | 1.4. “Covered Software” 20 | 21 | means Source Code Form to which the initial Contributor has attached the 22 | notice in Exhibit A, the Executable Form of such Source Code Form, and 23 | Modifications of such Source Code Form, in each case including portions 24 | thereof. 25 | 26 | 1.5. “Incompatible With Secondary Licenses” 27 | means 28 | 29 | a. that the initial Contributor has attached the notice described in 30 | Exhibit B to the Covered Software; or 31 | 32 | b. that the Covered Software was made available under the terms of version 33 | 1.1 or earlier of the License, but not also under the terms of a 34 | Secondary License. 35 | 36 | 1.6. “Executable Form” 37 | 38 | means any form of the work other than Source Code Form. 39 | 40 | 1.7. “Larger Work” 41 | 42 | means a work that combines Covered Software with other material, in a separate 43 | file or files, that is not Covered Software. 44 | 45 | 1.8. “License” 46 | 47 | means this document. 48 | 49 | 1.9. “Licensable” 50 | 51 | means having the right to grant, to the maximum extent possible, whether at the 52 | time of the initial grant or subsequently, any and all of the rights conveyed by 53 | this License. 54 | 55 | 1.10. “Modifications” 56 | 57 | means any of the following: 58 | 59 | a. any file in Source Code Form that results from an addition to, deletion 60 | from, or modification of the contents of Covered Software; or 61 | 62 | b. any new file in Source Code Form that contains any Covered Software. 63 | 64 | 1.11. “Patent Claims” of a Contributor 65 | 66 | means any patent claim(s), including without limitation, method, process, 67 | and apparatus claims, in any patent Licensable by such Contributor that 68 | would be infringed, but for the grant of the License, by the making, 69 | using, selling, offering for sale, having made, import, or transfer of 70 | either its Contributions or its Contributor Version. 71 | 72 | 1.12. “Secondary License” 73 | 74 | means either the GNU General Public License, Version 2.0, the GNU Lesser 75 | General Public License, Version 2.1, the GNU Affero General Public 76 | License, Version 3.0, or any later versions of those licenses. 77 | 78 | 1.13. “Source Code Form” 79 | 80 | means the form of the work preferred for making modifications. 81 | 82 | 1.14. “You” (or “Your”) 83 | 84 | means an individual or a legal entity exercising rights under this 85 | License. For legal entities, “You” includes any entity that controls, is 86 | controlled by, or is under common control with You. For purposes of this 87 | definition, “control” means (a) the power, direct or indirect, to cause 88 | the direction or management of such entity, whether by contract or 89 | otherwise, or (b) ownership of more than fifty percent (50%) of the 90 | outstanding shares or beneficial ownership of such entity. 91 | 92 | 93 | 2. License Grants and Conditions 94 | 95 | 2.1. Grants 96 | 97 | Each Contributor hereby grants You a world-wide, royalty-free, 98 | non-exclusive license: 99 | 100 | a. under intellectual property rights (other than patent or trademark) 101 | Licensable by such Contributor to use, reproduce, make available, 102 | modify, display, perform, distribute, and otherwise exploit its 103 | Contributions, either on an unmodified basis, with Modifications, or as 104 | part of a Larger Work; and 105 | 106 | b. under Patent Claims of such Contributor to make, use, sell, offer for 107 | sale, have made, import, and otherwise transfer either its Contributions 108 | or its Contributor Version. 109 | 110 | 2.2. Effective Date 111 | 112 | The licenses granted in Section 2.1 with respect to any Contribution become 113 | effective for each Contribution on the date the Contributor first distributes 114 | such Contribution. 115 | 116 | 2.3. Limitations on Grant Scope 117 | 118 | The licenses granted in this Section 2 are the only rights granted under this 119 | License. No additional rights or licenses will be implied from the distribution 120 | or licensing of Covered Software under this License. Notwithstanding Section 121 | 2.1(b) above, no patent license is granted by a Contributor: 122 | 123 | a. for any code that a Contributor has removed from Covered Software; or 124 | 125 | b. for infringements caused by: (i) Your and any other third party’s 126 | modifications of Covered Software, or (ii) the combination of its 127 | Contributions with other software (except as part of its Contributor 128 | Version); or 129 | 130 | c. under Patent Claims infringed by Covered Software in the absence of its 131 | Contributions. 132 | 133 | This License does not grant any rights in the trademarks, service marks, or 134 | logos of any Contributor (except as may be necessary to comply with the 135 | notice requirements in Section 3.4). 136 | 137 | 2.4. Subsequent Licenses 138 | 139 | No Contributor makes additional grants as a result of Your choice to 140 | distribute the Covered Software under a subsequent version of this License 141 | (see Section 10.2) or under the terms of a Secondary License (if permitted 142 | under the terms of Section 3.3). 143 | 144 | 2.5. Representation 145 | 146 | Each Contributor represents that the Contributor believes its Contributions 147 | are its original creation(s) or it has sufficient rights to grant the 148 | rights to its Contributions conveyed by this License. 149 | 150 | 2.6. Fair Use 151 | 152 | This License is not intended to limit any rights You have under applicable 153 | copyright doctrines of fair use, fair dealing, or other equivalents. 154 | 155 | 2.7. Conditions 156 | 157 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 158 | Section 2.1. 159 | 160 | 161 | 3. Responsibilities 162 | 163 | 3.1. Distribution of Source Form 164 | 165 | All distribution of Covered Software in Source Code Form, including any 166 | Modifications that You create or to which You contribute, must be under the 167 | terms of this License. You must inform recipients that the Source Code Form 168 | of the Covered Software is governed by the terms of this License, and how 169 | they can obtain a copy of this License. You may not attempt to alter or 170 | restrict the recipients’ rights in the Source Code Form. 171 | 172 | 3.2. Distribution of Executable Form 173 | 174 | If You distribute Covered Software in Executable Form then: 175 | 176 | a. such Covered Software must also be made available in Source Code Form, 177 | as described in Section 3.1, and You must inform recipients of the 178 | Executable Form how they can obtain a copy of such Source Code Form by 179 | reasonable means in a timely manner, at a charge no more than the cost 180 | of distribution to the recipient; and 181 | 182 | b. You may distribute such Executable Form under the terms of this License, 183 | or sublicense it under different terms, provided that the license for 184 | the Executable Form does not attempt to limit or alter the recipients’ 185 | rights in the Source Code Form under this License. 186 | 187 | 3.3. Distribution of a Larger Work 188 | 189 | You may create and distribute a Larger Work under terms of Your choice, 190 | provided that You also comply with the requirements of this License for the 191 | Covered Software. If the Larger Work is a combination of Covered Software 192 | with a work governed by one or more Secondary Licenses, and the Covered 193 | Software is not Incompatible With Secondary Licenses, this License permits 194 | You to additionally distribute such Covered Software under the terms of 195 | such Secondary License(s), so that the recipient of the Larger Work may, at 196 | their option, further distribute the Covered Software under the terms of 197 | either this License or such Secondary License(s). 198 | 199 | 3.4. Notices 200 | 201 | You may not remove or alter the substance of any license notices (including 202 | copyright notices, patent notices, disclaimers of warranty, or limitations 203 | of liability) contained within the Source Code Form of the Covered 204 | Software, except that You may alter any license notices to the extent 205 | required to remedy known factual inaccuracies. 206 | 207 | 3.5. Application of Additional Terms 208 | 209 | You may choose to offer, and to charge a fee for, warranty, support, 210 | indemnity or liability obligations to one or more recipients of Covered 211 | Software. However, You may do so only on Your own behalf, and not on behalf 212 | of any Contributor. You must make it absolutely clear that any such 213 | warranty, support, indemnity, or liability obligation is offered by You 214 | alone, and You hereby agree to indemnify every Contributor for any 215 | liability incurred by such Contributor as a result of warranty, support, 216 | indemnity or liability terms You offer. You may include additional 217 | disclaimers of warranty and limitations of liability specific to any 218 | jurisdiction. 219 | 220 | 4. Inability to Comply Due to Statute or Regulation 221 | 222 | If it is impossible for You to comply with any of the terms of this License 223 | with respect to some or all of the Covered Software due to statute, judicial 224 | order, or regulation then You must: (a) comply with the terms of this License 225 | to the maximum extent possible; and (b) describe the limitations and the code 226 | they affect. Such description must be placed in a text file included with all 227 | distributions of the Covered Software under this License. Except to the 228 | extent prohibited by statute or regulation, such description must be 229 | sufficiently detailed for a recipient of ordinary skill to be able to 230 | understand it. 231 | 232 | 5. Termination 233 | 234 | 5.1. The rights granted under this License will terminate automatically if You 235 | fail to comply with any of its terms. However, if You become compliant, 236 | then the rights granted under this License from a particular Contributor 237 | are reinstated (a) provisionally, unless and until such Contributor 238 | explicitly and finally terminates Your grants, and (b) on an ongoing basis, 239 | if such Contributor fails to notify You of the non-compliance by some 240 | reasonable means prior to 60 days after You have come back into compliance. 241 | Moreover, Your grants from a particular Contributor are reinstated on an 242 | ongoing basis if such Contributor notifies You of the non-compliance by 243 | some reasonable means, this is the first time You have received notice of 244 | non-compliance with this License from such Contributor, and You become 245 | compliant prior to 30 days after Your receipt of the notice. 246 | 247 | 5.2. If You initiate litigation against any entity by asserting a patent 248 | infringement claim (excluding declaratory judgment actions, counter-claims, 249 | and cross-claims) alleging that a Contributor Version directly or 250 | indirectly infringes any patent, then the rights granted to You by any and 251 | all Contributors for the Covered Software under Section 2.1 of this License 252 | shall terminate. 253 | 254 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 255 | license agreements (excluding distributors and resellers) which have been 256 | validly granted by You or Your distributors under this License prior to 257 | termination shall survive termination. 258 | 259 | 6. Disclaimer of Warranty 260 | 261 | Covered Software is provided under this License on an “as is” basis, without 262 | warranty of any kind, either expressed, implied, or statutory, including, 263 | without limitation, warranties that the Covered Software is free of defects, 264 | merchantable, fit for a particular purpose or non-infringing. The entire 265 | risk as to the quality and performance of the Covered Software is with You. 266 | Should any Covered Software prove defective in any respect, You (not any 267 | Contributor) assume the cost of any necessary servicing, repair, or 268 | correction. This disclaimer of warranty constitutes an essential part of this 269 | License. No use of any Covered Software is authorized under this License 270 | except under this disclaimer. 271 | 272 | 7. Limitation of Liability 273 | 274 | Under no circumstances and under no legal theory, whether tort (including 275 | negligence), contract, or otherwise, shall any Contributor, or anyone who 276 | distributes Covered Software as permitted above, be liable to You for any 277 | direct, indirect, special, incidental, or consequential damages of any 278 | character including, without limitation, damages for lost profits, loss of 279 | goodwill, work stoppage, computer failure or malfunction, or any and all 280 | other commercial damages or losses, even if such party shall have been 281 | informed of the possibility of such damages. This limitation of liability 282 | shall not apply to liability for death or personal injury resulting from such 283 | party’s negligence to the extent applicable law prohibits such limitation. 284 | Some jurisdictions do not allow the exclusion or limitation of incidental or 285 | consequential damages, so this exclusion and limitation may not apply to You. 286 | 287 | 8. Litigation 288 | 289 | Any litigation relating to this License may be brought only in the courts of 290 | a jurisdiction where the defendant maintains its principal place of business 291 | and such litigation shall be governed by laws of that jurisdiction, without 292 | reference to its conflict-of-law provisions. Nothing in this Section shall 293 | prevent a party’s ability to bring cross-claims or counter-claims. 294 | 295 | 9. Miscellaneous 296 | 297 | This License represents the complete agreement concerning the subject matter 298 | hereof. If any provision of this License is held to be unenforceable, such 299 | provision shall be reformed only to the extent necessary to make it 300 | enforceable. Any law or regulation which provides that the language of a 301 | contract shall be construed against the drafter shall not be used to construe 302 | this License against a Contributor. 303 | 304 | 305 | 10. Versions of the License 306 | 307 | 10.1. New Versions 308 | 309 | Mozilla Foundation is the license steward. Except as provided in Section 310 | 10.3, no one other than the license steward has the right to modify or 311 | publish new versions of this License. Each version will be given a 312 | distinguishing version number. 313 | 314 | 10.2. Effect of New Versions 315 | 316 | You may distribute the Covered Software under the terms of the version of 317 | the License under which You originally received the Covered Software, or 318 | under the terms of any subsequent version published by the license 319 | steward. 320 | 321 | 10.3. Modified Versions 322 | 323 | If you create software not governed by this License, and you want to 324 | create a new license for such software, you may create and use a modified 325 | version of this License if you rename the license and remove any 326 | references to the name of the license steward (except to note that such 327 | modified license differs from this License). 328 | 329 | 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses 330 | If You choose to distribute Source Code Form that is Incompatible With 331 | Secondary Licenses under the terms of this version of the License, the 332 | notice described in Exhibit B of this License must be attached. 333 | 334 | Exhibit A - Source Code Form License Notice 335 | 336 | This Source Code Form is subject to the 337 | terms of the Mozilla Public License, v. 338 | 2.0. If a copy of the MPL was not 339 | distributed with this file, You can 340 | obtain one at 341 | http://mozilla.org/MPL/2.0/. 342 | 343 | If it is not possible or desirable to put the notice in a particular file, then 344 | You may include the notice in a location (such as a LICENSE file in a relevant 345 | directory) where a recipient would be likely to look for such a notice. 346 | 347 | You may add additional accurate notices of copyright ownership. 348 | 349 | Exhibit B - “Incompatible With Secondary Licenses” Notice 350 | 351 | This Source Code Form is “Incompatible 352 | With Secondary Licenses”, as defined by 353 | the Mozilla Public License, v. 2.0. 354 | 355 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/serf/LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License, version 2.0 2 | 3 | 1. Definitions 4 | 5 | 1.1. “Contributor” 6 | 7 | means each individual or legal entity that creates, contributes to the 8 | creation of, or owns Covered Software. 9 | 10 | 1.2. “Contributor Version” 11 | 12 | means the combination of the Contributions of others (if any) used by a 13 | Contributor and that particular Contributor’s Contribution. 14 | 15 | 1.3. “Contribution” 16 | 17 | means Covered Software of a particular Contributor. 18 | 19 | 1.4. “Covered Software” 20 | 21 | means Source Code Form to which the initial Contributor has attached the 22 | notice in Exhibit A, the Executable Form of such Source Code Form, and 23 | Modifications of such Source Code Form, in each case including portions 24 | thereof. 25 | 26 | 1.5. “Incompatible With Secondary Licenses” 27 | means 28 | 29 | a. that the initial Contributor has attached the notice described in 30 | Exhibit B to the Covered Software; or 31 | 32 | b. that the Covered Software was made available under the terms of version 33 | 1.1 or earlier of the License, but not also under the terms of a 34 | Secondary License. 35 | 36 | 1.6. “Executable Form” 37 | 38 | means any form of the work other than Source Code Form. 39 | 40 | 1.7. “Larger Work” 41 | 42 | means a work that combines Covered Software with other material, in a separate 43 | file or files, that is not Covered Software. 44 | 45 | 1.8. “License” 46 | 47 | means this document. 48 | 49 | 1.9. “Licensable” 50 | 51 | means having the right to grant, to the maximum extent possible, whether at the 52 | time of the initial grant or subsequently, any and all of the rights conveyed by 53 | this License. 54 | 55 | 1.10. “Modifications” 56 | 57 | means any of the following: 58 | 59 | a. any file in Source Code Form that results from an addition to, deletion 60 | from, or modification of the contents of Covered Software; or 61 | 62 | b. any new file in Source Code Form that contains any Covered Software. 63 | 64 | 1.11. “Patent Claims” of a Contributor 65 | 66 | means any patent claim(s), including without limitation, method, process, 67 | and apparatus claims, in any patent Licensable by such Contributor that 68 | would be infringed, but for the grant of the License, by the making, 69 | using, selling, offering for sale, having made, import, or transfer of 70 | either its Contributions or its Contributor Version. 71 | 72 | 1.12. “Secondary License” 73 | 74 | means either the GNU General Public License, Version 2.0, the GNU Lesser 75 | General Public License, Version 2.1, the GNU Affero General Public 76 | License, Version 3.0, or any later versions of those licenses. 77 | 78 | 1.13. “Source Code Form” 79 | 80 | means the form of the work preferred for making modifications. 81 | 82 | 1.14. “You” (or “Your”) 83 | 84 | means an individual or a legal entity exercising rights under this 85 | License. For legal entities, “You” includes any entity that controls, is 86 | controlled by, or is under common control with You. For purposes of this 87 | definition, “control” means (a) the power, direct or indirect, to cause 88 | the direction or management of such entity, whether by contract or 89 | otherwise, or (b) ownership of more than fifty percent (50%) of the 90 | outstanding shares or beneficial ownership of such entity. 91 | 92 | 93 | 2. License Grants and Conditions 94 | 95 | 2.1. Grants 96 | 97 | Each Contributor hereby grants You a world-wide, royalty-free, 98 | non-exclusive license: 99 | 100 | a. under intellectual property rights (other than patent or trademark) 101 | Licensable by such Contributor to use, reproduce, make available, 102 | modify, display, perform, distribute, and otherwise exploit its 103 | Contributions, either on an unmodified basis, with Modifications, or as 104 | part of a Larger Work; and 105 | 106 | b. under Patent Claims of such Contributor to make, use, sell, offer for 107 | sale, have made, import, and otherwise transfer either its Contributions 108 | or its Contributor Version. 109 | 110 | 2.2. Effective Date 111 | 112 | The licenses granted in Section 2.1 with respect to any Contribution become 113 | effective for each Contribution on the date the Contributor first distributes 114 | such Contribution. 115 | 116 | 2.3. Limitations on Grant Scope 117 | 118 | The licenses granted in this Section 2 are the only rights granted under this 119 | License. No additional rights or licenses will be implied from the distribution 120 | or licensing of Covered Software under this License. Notwithstanding Section 121 | 2.1(b) above, no patent license is granted by a Contributor: 122 | 123 | a. for any code that a Contributor has removed from Covered Software; or 124 | 125 | b. for infringements caused by: (i) Your and any other third party’s 126 | modifications of Covered Software, or (ii) the combination of its 127 | Contributions with other software (except as part of its Contributor 128 | Version); or 129 | 130 | c. under Patent Claims infringed by Covered Software in the absence of its 131 | Contributions. 132 | 133 | This License does not grant any rights in the trademarks, service marks, or 134 | logos of any Contributor (except as may be necessary to comply with the 135 | notice requirements in Section 3.4). 136 | 137 | 2.4. Subsequent Licenses 138 | 139 | No Contributor makes additional grants as a result of Your choice to 140 | distribute the Covered Software under a subsequent version of this License 141 | (see Section 10.2) or under the terms of a Secondary License (if permitted 142 | under the terms of Section 3.3). 143 | 144 | 2.5. Representation 145 | 146 | Each Contributor represents that the Contributor believes its Contributions 147 | are its original creation(s) or it has sufficient rights to grant the 148 | rights to its Contributions conveyed by this License. 149 | 150 | 2.6. Fair Use 151 | 152 | This License is not intended to limit any rights You have under applicable 153 | copyright doctrines of fair use, fair dealing, or other equivalents. 154 | 155 | 2.7. Conditions 156 | 157 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 158 | Section 2.1. 159 | 160 | 161 | 3. Responsibilities 162 | 163 | 3.1. Distribution of Source Form 164 | 165 | All distribution of Covered Software in Source Code Form, including any 166 | Modifications that You create or to which You contribute, must be under the 167 | terms of this License. You must inform recipients that the Source Code Form 168 | of the Covered Software is governed by the terms of this License, and how 169 | they can obtain a copy of this License. You may not attempt to alter or 170 | restrict the recipients’ rights in the Source Code Form. 171 | 172 | 3.2. Distribution of Executable Form 173 | 174 | If You distribute Covered Software in Executable Form then: 175 | 176 | a. such Covered Software must also be made available in Source Code Form, 177 | as described in Section 3.1, and You must inform recipients of the 178 | Executable Form how they can obtain a copy of such Source Code Form by 179 | reasonable means in a timely manner, at a charge no more than the cost 180 | of distribution to the recipient; and 181 | 182 | b. You may distribute such Executable Form under the terms of this License, 183 | or sublicense it under different terms, provided that the license for 184 | the Executable Form does not attempt to limit or alter the recipients’ 185 | rights in the Source Code Form under this License. 186 | 187 | 3.3. Distribution of a Larger Work 188 | 189 | You may create and distribute a Larger Work under terms of Your choice, 190 | provided that You also comply with the requirements of this License for the 191 | Covered Software. If the Larger Work is a combination of Covered Software 192 | with a work governed by one or more Secondary Licenses, and the Covered 193 | Software is not Incompatible With Secondary Licenses, this License permits 194 | You to additionally distribute such Covered Software under the terms of 195 | such Secondary License(s), so that the recipient of the Larger Work may, at 196 | their option, further distribute the Covered Software under the terms of 197 | either this License or such Secondary License(s). 198 | 199 | 3.4. Notices 200 | 201 | You may not remove or alter the substance of any license notices (including 202 | copyright notices, patent notices, disclaimers of warranty, or limitations 203 | of liability) contained within the Source Code Form of the Covered 204 | Software, except that You may alter any license notices to the extent 205 | required to remedy known factual inaccuracies. 206 | 207 | 3.5. Application of Additional Terms 208 | 209 | You may choose to offer, and to charge a fee for, warranty, support, 210 | indemnity or liability obligations to one or more recipients of Covered 211 | Software. However, You may do so only on Your own behalf, and not on behalf 212 | of any Contributor. You must make it absolutely clear that any such 213 | warranty, support, indemnity, or liability obligation is offered by You 214 | alone, and You hereby agree to indemnify every Contributor for any 215 | liability incurred by such Contributor as a result of warranty, support, 216 | indemnity or liability terms You offer. You may include additional 217 | disclaimers of warranty and limitations of liability specific to any 218 | jurisdiction. 219 | 220 | 4. Inability to Comply Due to Statute or Regulation 221 | 222 | If it is impossible for You to comply with any of the terms of this License 223 | with respect to some or all of the Covered Software due to statute, judicial 224 | order, or regulation then You must: (a) comply with the terms of this License 225 | to the maximum extent possible; and (b) describe the limitations and the code 226 | they affect. Such description must be placed in a text file included with all 227 | distributions of the Covered Software under this License. Except to the 228 | extent prohibited by statute or regulation, such description must be 229 | sufficiently detailed for a recipient of ordinary skill to be able to 230 | understand it. 231 | 232 | 5. Termination 233 | 234 | 5.1. The rights granted under this License will terminate automatically if You 235 | fail to comply with any of its terms. However, if You become compliant, 236 | then the rights granted under this License from a particular Contributor 237 | are reinstated (a) provisionally, unless and until such Contributor 238 | explicitly and finally terminates Your grants, and (b) on an ongoing basis, 239 | if such Contributor fails to notify You of the non-compliance by some 240 | reasonable means prior to 60 days after You have come back into compliance. 241 | Moreover, Your grants from a particular Contributor are reinstated on an 242 | ongoing basis if such Contributor notifies You of the non-compliance by 243 | some reasonable means, this is the first time You have received notice of 244 | non-compliance with this License from such Contributor, and You become 245 | compliant prior to 30 days after Your receipt of the notice. 246 | 247 | 5.2. If You initiate litigation against any entity by asserting a patent 248 | infringement claim (excluding declaratory judgment actions, counter-claims, 249 | and cross-claims) alleging that a Contributor Version directly or 250 | indirectly infringes any patent, then the rights granted to You by any and 251 | all Contributors for the Covered Software under Section 2.1 of this License 252 | shall terminate. 253 | 254 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 255 | license agreements (excluding distributors and resellers) which have been 256 | validly granted by You or Your distributors under this License prior to 257 | termination shall survive termination. 258 | 259 | 6. Disclaimer of Warranty 260 | 261 | Covered Software is provided under this License on an “as is” basis, without 262 | warranty of any kind, either expressed, implied, or statutory, including, 263 | without limitation, warranties that the Covered Software is free of defects, 264 | merchantable, fit for a particular purpose or non-infringing. The entire 265 | risk as to the quality and performance of the Covered Software is with You. 266 | Should any Covered Software prove defective in any respect, You (not any 267 | Contributor) assume the cost of any necessary servicing, repair, or 268 | correction. This disclaimer of warranty constitutes an essential part of this 269 | License. No use of any Covered Software is authorized under this License 270 | except under this disclaimer. 271 | 272 | 7. Limitation of Liability 273 | 274 | Under no circumstances and under no legal theory, whether tort (including 275 | negligence), contract, or otherwise, shall any Contributor, or anyone who 276 | distributes Covered Software as permitted above, be liable to You for any 277 | direct, indirect, special, incidental, or consequential damages of any 278 | character including, without limitation, damages for lost profits, loss of 279 | goodwill, work stoppage, computer failure or malfunction, or any and all 280 | other commercial damages or losses, even if such party shall have been 281 | informed of the possibility of such damages. This limitation of liability 282 | shall not apply to liability for death or personal injury resulting from such 283 | party’s negligence to the extent applicable law prohibits such limitation. 284 | Some jurisdictions do not allow the exclusion or limitation of incidental or 285 | consequential damages, so this exclusion and limitation may not apply to You. 286 | 287 | 8. Litigation 288 | 289 | Any litigation relating to this License may be brought only in the courts of 290 | a jurisdiction where the defendant maintains its principal place of business 291 | and such litigation shall be governed by laws of that jurisdiction, without 292 | reference to its conflict-of-law provisions. Nothing in this Section shall 293 | prevent a party’s ability to bring cross-claims or counter-claims. 294 | 295 | 9. Miscellaneous 296 | 297 | This License represents the complete agreement concerning the subject matter 298 | hereof. If any provision of this License is held to be unenforceable, such 299 | provision shall be reformed only to the extent necessary to make it 300 | enforceable. Any law or regulation which provides that the language of a 301 | contract shall be construed against the drafter shall not be used to construe 302 | this License against a Contributor. 303 | 304 | 305 | 10. Versions of the License 306 | 307 | 10.1. New Versions 308 | 309 | Mozilla Foundation is the license steward. Except as provided in Section 310 | 10.3, no one other than the license steward has the right to modify or 311 | publish new versions of this License. Each version will be given a 312 | distinguishing version number. 313 | 314 | 10.2. Effect of New Versions 315 | 316 | You may distribute the Covered Software under the terms of the version of 317 | the License under which You originally received the Covered Software, or 318 | under the terms of any subsequent version published by the license 319 | steward. 320 | 321 | 10.3. Modified Versions 322 | 323 | If you create software not governed by this License, and you want to 324 | create a new license for such software, you may create and use a modified 325 | version of this License if you rename the license and remove any 326 | references to the name of the license steward (except to note that such 327 | modified license differs from this License). 328 | 329 | 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses 330 | If You choose to distribute Source Code Form that is Incompatible With 331 | Secondary Licenses under the terms of this version of the License, the 332 | notice described in Exhibit B of this License must be attached. 333 | 334 | Exhibit A - Source Code Form License Notice 335 | 336 | This Source Code Form is subject to the 337 | terms of the Mozilla Public License, v. 338 | 2.0. If a copy of the MPL was not 339 | distributed with this file, You can 340 | obtain one at 341 | http://mozilla.org/MPL/2.0/. 342 | 343 | If it is not possible or desirable to put the notice in a particular file, then 344 | You may include the notice in a location (such as a LICENSE file in a relevant 345 | directory) where a recipient would be likely to look for such a notice. 346 | 347 | You may add additional accurate notices of copyright ownership. 348 | 349 | Exhibit B - “Incompatible With Secondary Licenses” Notice 350 | 351 | This Source Code Form is “Incompatible 352 | With Secondary Licenses”, as defined by 353 | the Mozilla Public License, v. 2.0. 354 | 355 | --------------------------------------------------------------------------------