├── .gitignore ├── Godeps ├── _workspace │ ├── .gitignore │ └── src │ │ └── github.com │ │ ├── go-martini │ │ └── martini │ │ │ ├── wercker.yml │ │ │ ├── go_version.go │ │ │ ├── Godeps │ │ │ └── Godeps.json │ │ │ ├── .gitignore │ │ │ ├── env.go │ │ │ ├── logger.go │ │ │ ├── LICENSE │ │ │ ├── return_handler.go │ │ │ ├── response_writer.go │ │ │ ├── static.go │ │ │ ├── recovery.go │ │ │ ├── martini.go │ │ │ └── translations │ │ │ └── README_zh_cn.md │ │ ├── codegangsta │ │ └── inject │ │ │ ├── .gitignore │ │ │ ├── update_readme.sh │ │ │ ├── LICENSE │ │ │ ├── translations │ │ │ └── README_zh_cn.md │ │ │ ├── README.md │ │ │ └── inject.go │ │ ├── hashicorp │ │ ├── serf │ │ │ ├── ops-misc │ │ │ │ └── debian │ │ │ │ │ └── copyright │ │ │ ├── coordinate │ │ │ │ ├── README.md │ │ │ │ ├── test_util.go │ │ │ │ ├── config.go │ │ │ │ ├── phantom.go │ │ │ │ ├── client.go │ │ │ │ └── coordinate.go │ │ │ └── website │ │ │ │ └── LICENSE.md │ │ ├── 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 │ │ └── go-cleanhttp │ │ │ ├── cleanhttp.go │ │ │ └── README.md │ │ └── divideandconquer │ │ ├── go-consul-client │ │ ├── src │ │ │ ├── balancer │ │ │ │ ├── balancer.go │ │ │ │ ├── mock.go │ │ │ │ └── random.go │ │ │ └── client │ │ │ │ ├── mock.go │ │ │ │ └── client.go │ │ └── LICENSE │ │ ├── negotiator │ │ ├── xmlEncoder.go │ │ ├── jsonEncoder.go │ │ ├── negotiator.go │ │ ├── README.md │ │ └── LICENSE │ │ └── go-merge │ │ ├── merge │ │ └── merge.go │ │ └── LICENSE ├── Readme └── Godeps.json ├── config └── dev.json ├── Dockerfile ├── src ├── v1 │ └── test │ │ ├── ping_test.go │ │ ├── ping.go │ │ └── injector.go └── main │ └── server.go ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | cover.out 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /config/dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "balancerTTL":"5s" 3 | } -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/go-martini/martini/wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/golang@1.1.1 -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | EXPOSE 8080 4 | 5 | ADD /bin/app /app 6 | 7 | CMD ["/app"] -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/inject/.gitignore: -------------------------------------------------------------------------------- 1 | inject 2 | inject.test 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/hashicorp/serf/ops-misc/debian/copyright: -------------------------------------------------------------------------------- 1 | Name: serf 2 | Copyright: Hashicorp 2013 3 | -------------------------------------------------------------------------------- /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/_workspace/src/github.com/codegangsta/inject/update_readme.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | go get github.com/robertkrimen/godocdown/godocdown 3 | godocdown > README.md 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/go-martini/martini/go_version.go: -------------------------------------------------------------------------------- 1 | // +build !go1.1 2 | 3 | package martini 4 | 5 | func MartiniDoesNotSupportGo1Point0() { 6 | "Martini requires Go 1.1 or greater." 7 | } 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/go-martini/martini/Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/go-martini/martini", 3 | "GoVersion": "go1.4.2", 4 | "Deps": [ 5 | { 6 | "ImportPath": "github.com/codegangsta/inject", 7 | "Comment": "v1.0-rc1-10-g33e0aa1", 8 | "Rev": "33e0aa1cb7c019ccc3fbe049a8262a6403d30504" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/divideandconquer/go-consul-client/src/balancer/balancer.go: -------------------------------------------------------------------------------- 1 | package balancer 2 | 3 | // DNS balancer finds services through dns and balances load across them 4 | type DNS interface { 5 | FindService(serviceName string) (*ServiceLocation, error) 6 | } 7 | 8 | // ServiceLocation is a represensation of where a service lives 9 | type ServiceLocation struct { 10 | URL string 11 | Port int 12 | } 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/v1/test/ping_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "net/http" 5 | "testing" 6 | ) 7 | 8 | func TestUnit_Ping_BasePath(t *testing.T) { 9 | handler := Ping() 10 | i := NewInjector("GET", "") 11 | InvokeAndCheck(t, handler, i, http.StatusOK, []byte(`{"message":"pong!"}`)) 12 | } 13 | 14 | func TestFunctional_Ping_BasePath(t *testing.T) { 15 | res, err := DoTestRequest(t, "GET", "test/ping", "", nil) 16 | VerifyResponseBody(t, res, err, http.StatusOK, []byte(`{"message":"pong!"}`)) 17 | } 18 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/divideandconquer/negotiator/xmlEncoder.go: -------------------------------------------------------------------------------- 1 | package negotiator 2 | 3 | import ( 4 | "encoding/xml" 5 | ) 6 | 7 | type XmlEncoder struct { 8 | PrettyPrint bool 9 | } 10 | 11 | func (xe XmlEncoder) Encode(data interface{}) ([]byte, error) { 12 | if xe.PrettyPrint { 13 | return xml.MarshalIndent(data, "", " ") 14 | } else { 15 | return xml.Marshal(data) 16 | } 17 | } 18 | 19 | func (xe XmlEncoder) ContentType() string { 20 | return "application/xml; charset=utf-8" 21 | } 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/go-martini/martini/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | 25 | /.godeps 26 | /.envrc 27 | 28 | # Godeps 29 | Godeps/_workspace 30 | Godeps/Readme 31 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/divideandconquer/negotiator/jsonEncoder.go: -------------------------------------------------------------------------------- 1 | package negotiator 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | type JsonEncoder struct { 8 | PrettyPrint bool 9 | } 10 | 11 | func (je JsonEncoder) Encode(data interface{}) ([]byte, error) { 12 | if je.PrettyPrint { 13 | return json.MarshalIndent(data, "", " ") 14 | } else { 15 | return json.Marshal(data) 16 | } 17 | } 18 | 19 | func (js JsonEncoder) ContentType() string { 20 | return "application/json; charset=utf-8" 21 | } 22 | -------------------------------------------------------------------------------- /src/v1/test/ping.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/divideandconquer/negotiator" 7 | "github.com/go-martini/martini" 8 | ) 9 | 10 | type pong struct { 11 | Message string `json:"message"` 12 | } 13 | 14 | // Ping returns a ping handler 15 | func Ping() martini.Handler { 16 | return func(w http.ResponseWriter, r *http.Request, neg negotiator.Negotiator) (int, []byte) { 17 | ret := pong{Message: "pong!"} 18 | return http.StatusOK, negotiator.Must(neg.Negotiate(r, ret)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/divideandconquer/go-consul-client/src/balancer/mock.go: -------------------------------------------------------------------------------- 1 | package balancer 2 | 3 | import "fmt" 4 | 5 | type mockBalancer struct { 6 | services map[string]*ServiceLocation 7 | } 8 | 9 | func NewMockDNSBalancer(services map[string]*ServiceLocation) DNS { 10 | return &mockBalancer{services: services} 11 | } 12 | 13 | func (m *mockBalancer) FindService(serviceName string) (*ServiceLocation, error) { 14 | if s, ok := m.services[serviceName]; ok { 15 | return s, nil 16 | } 17 | return nil, fmt.Errorf("Could not find %s", serviceName) 18 | } 19 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/go-martini/martini/env.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | // Envs 8 | const ( 9 | Dev string = "development" 10 | Prod string = "production" 11 | Test string = "test" 12 | ) 13 | 14 | // Env is the environment that Martini is executing in. The MARTINI_ENV is read on initialization to set this variable. 15 | var Env = Dev 16 | var Root string 17 | 18 | func setENV(e string) { 19 | if len(e) > 0 { 20 | Env = e 21 | } 22 | } 23 | 24 | func init() { 25 | setENV(os.Getenv("MARTINI_ENV")) 26 | var err error 27 | Root, err = os.Getwd() 28 | if err != nil { 29 | panic(err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/go-martini/martini/logger.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | // Logger returns a middleware handler that logs the request as it goes in and the response as it goes out. 10 | func Logger() Handler { 11 | return func(res http.ResponseWriter, req *http.Request, c Context, log *log.Logger) { 12 | start := time.Now() 13 | 14 | addr := req.Header.Get("X-Real-IP") 15 | if addr == "" { 16 | addr = req.Header.Get("X-Forwarded-For") 17 | if addr == "" { 18 | addr = req.RemoteAddr 19 | } 20 | } 21 | 22 | log.Printf("Started %s %s for %s", req.Method, req.URL.Path, addr) 23 | 24 | rw := res.(ResponseWriter) 25 | c.Next() 26 | 27 | log.Printf("Completed %v %s in %v\n", rw.Status(), http.StatusText(rw.Status()), time.Since(start)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-base 2 | 3 | **Note** this has been replaced with a revised version in the [healthimation](https://github.com/healthimation/go-base) organization. The code in this repo is deprecated. 4 | 5 | A base service to start new services from 6 | 7 | ## Getting Started 8 | 9 | * Clone to new service folder 10 | * Change the remote to your new service's URL 11 | * Change serviceName in run.sh 12 | * Push 13 | 14 | ```sh 15 | git clone git@github.com:divideandconquer/go-base.git new-service 16 | cd new-service 17 | git remote set-url origin git://new.url.here 18 | sed -i '' 's/"base"/"new"/' build/run.sh 19 | git add * 20 | git commit -m "initial clone" 21 | git push -u origin master 22 | ``` 23 | 24 | This service infrastructure assumes you are running it in docker and have access to consul. I recommend 25 | using [coreos-vagrant](https://github.com/coreos/coreos-vagrant) and then running consul as a 26 | [fleet unit](https://gist.github.com/divideandconquer/08405a4fb597319d3c3e). You should be able to copy 27 | the unit file at the previous link on to the coreos box and start it with: 28 | 29 | ```sh 30 | fleetctl start /path/to/consul.service 31 | ``` 32 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/inject/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jeremy Saenz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/go-martini/martini/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jeremy Saenz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/go-martini/martini/return_handler.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "github.com/codegangsta/inject" 5 | "net/http" 6 | "reflect" 7 | ) 8 | 9 | // ReturnHandler is a service that Martini provides that is called 10 | // when a route handler returns something. The ReturnHandler is 11 | // responsible for writing to the ResponseWriter based on the values 12 | // that are passed into this function. 13 | type ReturnHandler func(Context, []reflect.Value) 14 | 15 | func defaultReturnHandler() ReturnHandler { 16 | return func(ctx Context, vals []reflect.Value) { 17 | rv := ctx.Get(inject.InterfaceOf((*http.ResponseWriter)(nil))) 18 | res := rv.Interface().(http.ResponseWriter) 19 | var responseVal reflect.Value 20 | if len(vals) > 1 && vals[0].Kind() == reflect.Int { 21 | res.WriteHeader(int(vals[0].Int())) 22 | responseVal = vals[1] 23 | } else if len(vals) > 0 { 24 | responseVal = vals[0] 25 | } 26 | if canDeref(responseVal) { 27 | responseVal = responseVal.Elem() 28 | } 29 | if isByteSlice(responseVal) { 30 | res.Write(responseVal.Bytes()) 31 | } else { 32 | res.Write([]byte(responseVal.String())) 33 | } 34 | } 35 | } 36 | 37 | func isByteSlice(val reflect.Value) bool { 38 | return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8 39 | } 40 | 41 | func canDeref(val reflect.Value) bool { 42 | return val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr 43 | } 44 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/divideandconquer/go-base", 3 | "GoVersion": "go1.5.1", 4 | "Packages": [ 5 | "./..." 6 | ], 7 | "Deps": [ 8 | { 9 | "ImportPath": "github.com/codegangsta/inject", 10 | "Comment": "v1.0-rc1-10-g33e0aa1", 11 | "Rev": "33e0aa1cb7c019ccc3fbe049a8262a6403d30504" 12 | }, 13 | { 14 | "ImportPath": "github.com/divideandconquer/go-consul-client/src/balancer", 15 | "Rev": "0750e3b525eabd06580b7be2135c49baceb94302" 16 | }, 17 | { 18 | "ImportPath": "github.com/divideandconquer/go-consul-client/src/client", 19 | "Rev": "0750e3b525eabd06580b7be2135c49baceb94302" 20 | }, 21 | { 22 | "ImportPath": "github.com/divideandconquer/go-merge/merge", 23 | "Rev": "dc9048d04b65a57f75d8a3875a0fb85ca1f70624" 24 | }, 25 | { 26 | "ImportPath": "github.com/divideandconquer/negotiator", 27 | "Rev": "b9489ef4a29d717f22e08005955115087fcb51bb" 28 | }, 29 | { 30 | "ImportPath": "github.com/go-martini/martini", 31 | "Comment": "v1.0-169-g15a4762", 32 | "Rev": "15a47622d6a9b3e6a1eaca2681e4850f612471ea" 33 | }, 34 | { 35 | "ImportPath": "github.com/hashicorp/consul/api", 36 | "Comment": "v0.6.0-rc2-37-g71bffe8", 37 | "Rev": "71bffe81d1a28e0ad3bbcf605220a2bc1e9af24f" 38 | }, 39 | { 40 | "ImportPath": "github.com/hashicorp/go-cleanhttp", 41 | "Rev": "5df5ddc69534f1a4697289f1dca2193fbb40213f" 42 | }, 43 | { 44 | "ImportPath": "github.com/hashicorp/serf/coordinate", 45 | "Comment": "v0.6.4-150-ge9ac4bb", 46 | "Rev": "e9ac4bb0c5721826d5cd3d1cd582c12fbc664bb8" 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/divideandconquer/go-consul-client/src/client/mock.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | ) 8 | 9 | type mockLoader struct { 10 | data map[string]interface{} 11 | } 12 | 13 | func NewMockLoader(data map[string]interface{}) Loader { 14 | return &mockLoader{data} 15 | } 16 | func (m *mockLoader) Import(data []byte) error { 17 | return nil 18 | } 19 | func (m *mockLoader) Initialize() error { 20 | return nil 21 | } 22 | func (m *mockLoader) Get(key string) ([]byte, error) { 23 | if ret, ok := m.data[key]; ok { 24 | if result, ok := ret.([]byte); ok { 25 | return result, nil 26 | } 27 | } 28 | return nil, fmt.Errorf("Key (%s) not set in mock.", key) 29 | } 30 | 31 | func (m *mockLoader) MustGetString(key string) string { 32 | if ret, ok := m.data[key]; ok { 33 | if result, ok := ret.(string); ok { 34 | return result 35 | } 36 | } 37 | log.Fatalf("Key (%s) not set in mock", key) 38 | return "" 39 | } 40 | 41 | func (m *mockLoader) MustGetBool(key string) bool { 42 | if ret, ok := m.data[key]; ok { 43 | if result, ok := ret.(bool); ok { 44 | return result 45 | } 46 | } 47 | log.Fatalf("Key (%s) not set in mock", key) 48 | return false 49 | } 50 | 51 | func (m *mockLoader) MustGetInt(key string) int { 52 | if ret, ok := m.data[key]; ok { 53 | if result, ok := ret.(int); ok { 54 | return result 55 | } 56 | } 57 | log.Fatalf("Key (%s) not set in mock", key) 58 | return 0 59 | } 60 | 61 | func (m *mockLoader) MustGetDuration(key string) time.Duration { 62 | if ret, ok := m.data[key]; ok { 63 | if result, ok := ret.(time.Duration); ok { 64 | return result 65 | } 66 | } 67 | log.Fatalf("Key (%s) not set in mock", key) 68 | return 0 69 | } 70 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/inject/translations/README_zh_cn.md: -------------------------------------------------------------------------------- 1 | # inject 2 | -- 3 | import "github.com/codegangsta/inject" 4 | 5 | inject包提供了多种对实体的映射和依赖注入方式。 6 | 7 | ## 用法 8 | 9 | #### func InterfaceOf 10 | 11 | ```go 12 | func InterfaceOf(value interface{}) reflect.Type 13 | ``` 14 | 函数InterfaceOf返回指向接口类型的指针。如果传入的value值不是指向接口的指针,将抛出一个panic异常。 15 | 16 | #### type Applicator 17 | 18 | ```go 19 | type Applicator interface { 20 | // 在Type map中维持对结构体中每个域的引用并用'inject'来标记 21 | // 如果注入失败将会返回一个error. 22 | Apply(interface{}) error 23 | } 24 | ``` 25 | 26 | Applicator接口表示到结构体的依赖映射关系。 27 | 28 | #### type Injector 29 | 30 | ```go 31 | type Injector interface { 32 | Applicator 33 | Invoker 34 | TypeMapper 35 | // SetParent用来设置父injector. 如果在当前injector的Type map中找不到依赖, 36 | // 将会继续从它的父injector中找,直到返回error. 37 | SetParent(Injector) 38 | } 39 | ``` 40 | 41 | Injector接口表示对结构体、函数参数的映射和依赖注入。 42 | 43 | #### func New 44 | 45 | ```go 46 | func New() Injector 47 | ``` 48 | New创建并返回一个Injector. 49 | 50 | #### type Invoker 51 | 52 | ```go 53 | type Invoker interface { 54 | // Invoke尝试将interface{}作为一个函数来调用,并基于Type为函数提供参数。 55 | // 它将返回reflect.Value的切片,其中存放原函数的返回值。 56 | // 如果注入失败则返回error. 57 | Invoke(interface{}) ([]reflect.Value, error) 58 | } 59 | ``` 60 | 61 | Invoker接口表示通过反射进行函数调用。 62 | 63 | #### type TypeMapper 64 | 65 | ```go 66 | type TypeMapper interface { 67 | // 基于调用reflect.TypeOf得到的类型映射interface{}的值。 68 | Map(interface{}) TypeMapper 69 | // 基于提供的接口的指针映射interface{}的值。 70 | // 该函数仅用来将一个值映射为接口,因为接口无法不通过指针而直接引用到。 71 | MapTo(interface{}, interface{}) TypeMapper 72 | // 为直接插入基于类型和值的map提供一种可能性。 73 | // 它使得这一类直接映射成为可能:无法通过反射直接实例化的类型参数,如单向管道。 74 | Set(reflect.Type, reflect.Value) TypeMapper 75 | // 返回映射到当前类型的Value. 如果Type没被映射,将返回对应的零值。 76 | Get(reflect.Type) reflect.Value 77 | } 78 | ``` 79 | 80 | TypeMapper接口用来表示基于类型到接口值的映射。 81 | 82 | 83 | ## 译者 84 | 85 | 张强 (qqbunny@yeah.net) -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/main/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "os" 8 | 9 | "github.com/divideandconquer/go-base/src/v1/test" 10 | "github.com/divideandconquer/go-consul-client/src/balancer" 11 | "github.com/divideandconquer/go-consul-client/src/client" 12 | "github.com/divideandconquer/negotiator" 13 | "github.com/go-martini/martini" 14 | ) 15 | 16 | // config keys 17 | const ( 18 | configKeyBalancerTTL = "balancerTTL" 19 | ) 20 | 21 | func main() { 22 | consulAddress := mustGetEnvVar("CONSUL_HTTP_ADDR") 23 | environment := mustGetEnvVar("ENVIRONMENT") 24 | serviceName := mustGetEnvVar("SERVICE_NAME") 25 | 26 | conf, _ := setupConfig(serviceName, environment, consulAddress) 27 | //pull config and pass it around 28 | conf.MustGetDuration(configKeyBalancerTTL) 29 | 30 | // use loadbalancer to lookup services and db locations if necessary 31 | // Each request should repeat the lookup to make sure that this app 32 | // follows any services that move 33 | // s, err := loadbalancer.FindService("foo") 34 | 35 | //setup martini 36 | m := martini.New() 37 | // Setup middleware 38 | m.Use(martini.Recovery()) 39 | m.Use(martini.Logger()) 40 | 41 | // Setup routes 42 | router := martini.NewRouter() 43 | router.Group("/v1", func(v1router martini.Router) { 44 | //Setup v1 routes 45 | v1router.Group("/test", func(r martini.Router) { 46 | r.Get("/ping", test.Ping()) 47 | }) 48 | }) 49 | 50 | // Add the router action 51 | m.Action(router.Handle) 52 | 53 | // Inject dependencies 54 | m.Use(func(c martini.Context, w http.ResponseWriter) { 55 | enc := negotiator.JsonEncoder{PrettyPrint: false} 56 | cn := negotiator.NewContentNegotiator(enc, w) 57 | cn.AddEncoder(negotiator.MimeJSON, enc) 58 | c.MapTo(cn, (*negotiator.Negotiator)(nil)) 59 | }) 60 | 61 | // Start up the server 62 | m.RunOnAddr(":8080") 63 | } 64 | 65 | func setupConfig(service string, env string, consulAddr string) (client.Loader, balancer.DNS) { 66 | appNamespace := fmt.Sprintf("%s/%s", env, service) 67 | log.Printf("Initializing config for %s with consul %s", appNamespace, consulAddr) 68 | 69 | // create a cached loader 70 | conf, err := client.NewCachedLoader(appNamespace, consulAddr) 71 | if err != nil { 72 | log.Fatalf("Could not create a config cached loader: %v", err) 73 | } 74 | 75 | // initialize the cache 76 | err = conf.Initialize() 77 | if err != nil { 78 | log.Fatalf("Could not initialize the config cached loader: %v", err) 79 | } 80 | 81 | loadbalancer, err := balancer.NewRandomDNSBalancer(env, consulAddr, conf.MustGetDuration(configKeyBalancerTTL)) 82 | if err != nil { 83 | log.Fatalf("Could not create loadbalancer: %v", err) 84 | } 85 | 86 | return conf, loadbalancer 87 | } 88 | 89 | func mustGetEnvVar(key string) string { 90 | ret := os.Getenv(key) 91 | if ret == "" { 92 | log.Fatalf("Could not find %s environment variable", key) 93 | } 94 | return ret 95 | } 96 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/inject/README.md: -------------------------------------------------------------------------------- 1 | # inject 2 | -- 3 | import "github.com/codegangsta/inject" 4 | 5 | Package inject provides utilities for mapping and injecting dependencies in 6 | various ways. 7 | 8 | Language Translations: 9 | * [简体中文](translations/README_zh_cn.md) 10 | 11 | ## Usage 12 | 13 | #### func InterfaceOf 14 | 15 | ```go 16 | func InterfaceOf(value interface{}) reflect.Type 17 | ``` 18 | InterfaceOf dereferences a pointer to an Interface type. It panics if value is 19 | not an pointer to an interface. 20 | 21 | #### type Applicator 22 | 23 | ```go 24 | type Applicator interface { 25 | // Maps dependencies in the Type map to each field in the struct 26 | // that is tagged with 'inject'. Returns an error if the injection 27 | // fails. 28 | Apply(interface{}) error 29 | } 30 | ``` 31 | 32 | Applicator represents an interface for mapping dependencies to a struct. 33 | 34 | #### type Injector 35 | 36 | ```go 37 | type Injector interface { 38 | Applicator 39 | Invoker 40 | TypeMapper 41 | // SetParent sets the parent of the injector. If the injector cannot find a 42 | // dependency in its Type map it will check its parent before returning an 43 | // error. 44 | SetParent(Injector) 45 | } 46 | ``` 47 | 48 | Injector represents an interface for mapping and injecting dependencies into 49 | structs and function arguments. 50 | 51 | #### func New 52 | 53 | ```go 54 | func New() Injector 55 | ``` 56 | New returns a new Injector. 57 | 58 | #### type Invoker 59 | 60 | ```go 61 | type Invoker interface { 62 | // Invoke attempts to call the interface{} provided as a function, 63 | // providing dependencies for function arguments based on Type. Returns 64 | // a slice of reflect.Value representing the returned values of the function. 65 | // Returns an error if the injection fails. 66 | Invoke(interface{}) ([]reflect.Value, error) 67 | } 68 | ``` 69 | 70 | Invoker represents an interface for calling functions via reflection. 71 | 72 | #### type TypeMapper 73 | 74 | ```go 75 | type TypeMapper interface { 76 | // Maps the interface{} value based on its immediate type from reflect.TypeOf. 77 | Map(interface{}) TypeMapper 78 | // Maps the interface{} value based on the pointer of an Interface provided. 79 | // This is really only useful for mapping a value as an interface, as interfaces 80 | // cannot at this time be referenced directly without a pointer. 81 | MapTo(interface{}, interface{}) TypeMapper 82 | // Provides a possibility to directly insert a mapping based on type and value. 83 | // This makes it possible to directly map type arguments not possible to instantiate 84 | // with reflect like unidirectional channels. 85 | Set(reflect.Type, reflect.Value) TypeMapper 86 | // Returns the Value that is mapped to the current type. Returns a zeroed Value if 87 | // the Type has not been mapped. 88 | Get(reflect.Type) reflect.Value 89 | } 90 | ``` 91 | 92 | TypeMapper represents an interface for mapping interface{} values based on type. 93 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/go-martini/martini/response_writer.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | "net/http" 8 | ) 9 | 10 | // ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about 11 | // the response. It is recommended that middleware handlers use this construct to wrap a responsewriter 12 | // if the functionality calls for it. 13 | type ResponseWriter interface { 14 | http.ResponseWriter 15 | http.Flusher 16 | http.Hijacker 17 | // Status returns the status code of the response or 0 if the response has not been written. 18 | Status() int 19 | // Written returns whether or not the ResponseWriter has been written. 20 | Written() bool 21 | // Size returns the size of the response body. 22 | Size() int 23 | // Before allows for a function to be called before the ResponseWriter has been written to. This is 24 | // useful for setting headers or any other operations that must happen before a response has been written. 25 | Before(BeforeFunc) 26 | } 27 | 28 | // BeforeFunc is a function that is called before the ResponseWriter has been written to. 29 | type BeforeFunc func(ResponseWriter) 30 | 31 | // NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter 32 | func NewResponseWriter(rw http.ResponseWriter) ResponseWriter { 33 | return &responseWriter{rw, 0, 0, nil} 34 | } 35 | 36 | type responseWriter struct { 37 | http.ResponseWriter 38 | status int 39 | size int 40 | beforeFuncs []BeforeFunc 41 | } 42 | 43 | func (rw *responseWriter) WriteHeader(s int) { 44 | rw.callBefore() 45 | rw.ResponseWriter.WriteHeader(s) 46 | rw.status = s 47 | } 48 | 49 | func (rw *responseWriter) Write(b []byte) (int, error) { 50 | if !rw.Written() { 51 | // The status will be StatusOK if WriteHeader has not been called yet 52 | rw.WriteHeader(http.StatusOK) 53 | } 54 | size, err := rw.ResponseWriter.Write(b) 55 | rw.size += size 56 | return size, err 57 | } 58 | 59 | func (rw *responseWriter) Status() int { 60 | return rw.status 61 | } 62 | 63 | func (rw *responseWriter) Size() int { 64 | return rw.size 65 | } 66 | 67 | func (rw *responseWriter) Written() bool { 68 | return rw.status != 0 69 | } 70 | 71 | func (rw *responseWriter) Before(before BeforeFunc) { 72 | rw.beforeFuncs = append(rw.beforeFuncs, before) 73 | } 74 | 75 | func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 76 | hijacker, ok := rw.ResponseWriter.(http.Hijacker) 77 | if !ok { 78 | return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface") 79 | } 80 | return hijacker.Hijack() 81 | } 82 | 83 | func (rw *responseWriter) CloseNotify() <-chan bool { 84 | return rw.ResponseWriter.(http.CloseNotifier).CloseNotify() 85 | } 86 | 87 | func (rw *responseWriter) callBefore() { 88 | for i := len(rw.beforeFuncs) - 1; i >= 0; i-- { 89 | rw.beforeFuncs[i](rw) 90 | } 91 | } 92 | 93 | func (rw *responseWriter) Flush() { 94 | flusher, ok := rw.ResponseWriter.(http.Flusher) 95 | if ok { 96 | flusher.Flush() 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/divideandconquer/negotiator/negotiator.go: -------------------------------------------------------------------------------- 1 | package negotiator 2 | 3 | import ( 4 | "net/http" 5 | "strings" 6 | ) 7 | 8 | const MimeJSON = "application/json" 9 | const MimeXML = "application/xml" 10 | 11 | // Encoder is an interface for a struct that can encode data into []byte 12 | type Encoder interface { 13 | Encode(data interface{}) ([]byte, error) 14 | ContentType() string 15 | } 16 | 17 | // A Negotiator can Negotiate to determine what content type to convert 18 | // a struct into for client consumption 19 | type Negotiator interface { 20 | Negotiate(req *http.Request, data interface{}) ([]byte, error) 21 | } 22 | 23 | // ContentNegotiator is a Neotiator that supports a fallback/default 24 | // encoder as well as dynamically adding encoders 25 | type ContentNegotiator struct { 26 | DefaultEncoder Encoder 27 | ResponseWriter http.ResponseWriter 28 | encoderMap map[string]Encoder 29 | } 30 | 31 | // NewContentNegotiator creates a basic ContentNegotiator with out any attached 32 | // encoders 33 | func NewContentNegotiator(defaultEncoder Encoder, responseWriter http.ResponseWriter) *ContentNegotiator { 34 | result := &ContentNegotiator{} 35 | result.DefaultEncoder = defaultEncoder 36 | result.ResponseWriter = responseWriter 37 | return result 38 | } 39 | 40 | // NewJsonXmlContentNegotiator creates a basic ContentNegotiator and attaches 41 | // a JSON and an XML encoder to it. 42 | func NewJsonXmlContentNegotiator(defaultEncoder Encoder, responseWriter http.ResponseWriter, prettyPrint bool) *ContentNegotiator { 43 | result := NewContentNegotiator(defaultEncoder, responseWriter) 44 | result.AddEncoder(MimeJSON, JsonEncoder{prettyPrint}) 45 | result.AddEncoder(MimeXML, XmlEncoder{prettyPrint}) 46 | return result 47 | } 48 | 49 | // Negotiate inspects the request for the accept header and 50 | // encodes the response appropriately. 51 | func (cn *ContentNegotiator) Negotiate(req *http.Request, data interface{}) ([]byte, error) { 52 | if len(cn.encoderMap) <= 0 { 53 | panic("No Encoders present. Please add them using ContentNegotiator.AddEncoder()") 54 | } 55 | var e = cn.getEncoder(req) 56 | cn.ResponseWriter.Header().Set("Content-Type", e.ContentType()) 57 | return e.Encode(data) 58 | } 59 | 60 | // AddEncoder registers a mimetype and its encoder to be used if a client 61 | // requests that mimetype 62 | func (cn *ContentNegotiator) AddEncoder(mimeType string, enc Encoder) { 63 | if cn.encoderMap == nil { 64 | cn.encoderMap = make(map[string]Encoder) 65 | } 66 | cn.encoderMap[mimeType] = enc 67 | } 68 | 69 | // getEncoder parses the Accept header an returns the appropriate encoder to use 70 | func (cn *ContentNegotiator) getEncoder(req *http.Request) Encoder { 71 | var result = cn.DefaultEncoder 72 | accept := req.Header.Get("Accept") 73 | 74 | for k, v := range cn.encoderMap { 75 | if strings.Contains(accept, k) { 76 | result = v 77 | break 78 | } 79 | } 80 | return result 81 | } 82 | 83 | // Check for an error and panic 84 | func Must(data []byte, err error) []byte { 85 | if err != nil { 86 | panic(err) 87 | } 88 | return data 89 | } 90 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/divideandconquer/go-consul-client/src/balancer/random.go: -------------------------------------------------------------------------------- 1 | package balancer 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | 9 | "github.com/hashicorp/consul/api" 10 | ) 11 | 12 | type cachedServiceLocation struct { 13 | Services []*ServiceLocation 14 | CachedAt time.Time 15 | } 16 | 17 | type randomBalancer struct { 18 | environment string 19 | consulCatalog *api.Catalog 20 | cache map[string]cachedServiceLocation 21 | cacheLock sync.RWMutex //TODO lock per serviceName 22 | ttl time.Duration 23 | } 24 | 25 | func init() { 26 | rand.Seed(time.Now().UnixNano()) 27 | } 28 | 29 | func NewRandomDNSBalancer(environment string, consulAddr string, cacheTTL time.Duration) (DNS, error) { 30 | config := api.DefaultConfig() 31 | config.Address = consulAddr 32 | consul, err := api.NewClient(config) 33 | if err != nil { 34 | return nil, fmt.Errorf("Could not connect to consul: %v", err) 35 | } 36 | 37 | r := randomBalancer{} 38 | r.cache = make(map[string]cachedServiceLocation) 39 | r.environment = environment 40 | r.ttl = cacheTTL 41 | r.consulCatalog = consul.Catalog() 42 | return &r, nil 43 | } 44 | 45 | func (r *randomBalancer) FindService(serviceName string) (*ServiceLocation, error) { 46 | services, err := r.getServiceFromCache(serviceName) 47 | if err != nil || len(services) == 0 { 48 | services, err = r.writeServiceToCache(serviceName) 49 | if err != nil { 50 | return nil, err 51 | } 52 | } 53 | return r.pickService(services), nil 54 | } 55 | 56 | func (r *randomBalancer) pickService(services []*ServiceLocation) *ServiceLocation { 57 | return services[rand.Intn(len(services))] 58 | } 59 | 60 | func (r *randomBalancer) getServiceFromCache(serviceName string) ([]*ServiceLocation, error) { 61 | r.cacheLock.RLock() 62 | defer r.cacheLock.RUnlock() 63 | 64 | if result, ok := r.cache[serviceName]; ok { 65 | if time.Now().UTC().Before(result.CachedAt.Add(r.ttl)) { 66 | return result.Services, nil 67 | } 68 | return nil, fmt.Errorf("Cache for %s is expired", serviceName) 69 | } 70 | return nil, fmt.Errorf("Could not find %s in cache", serviceName) 71 | } 72 | 73 | // writeServiceToCache locks specifically to alleviate load on consul some additional lock time 74 | // is preferable to extra consul calls 75 | func (r *randomBalancer) writeServiceToCache(serviceName string) ([]*ServiceLocation, error) { 76 | //acquire a write lock 77 | r.cacheLock.Lock() 78 | defer r.cacheLock.Unlock() 79 | 80 | //check the cache again in case we've fetched since the last check 81 | //(our lock could have been waiting for another call to this function) 82 | if result, ok := r.cache[serviceName]; ok { 83 | if time.Now().UTC().Before(result.CachedAt.Add(r.ttl)) { 84 | return result.Services, nil 85 | } 86 | } 87 | 88 | //it still isn't in the cache, lets put it there 89 | consulServices, _, err := r.consulCatalog.Service(serviceName, r.environment, nil) 90 | if err != nil { 91 | return nil, fmt.Errorf("Error reaching consul for service lookup %v", err) 92 | } 93 | 94 | if len(consulServices) == 0 { 95 | return nil, fmt.Errorf("No services found for %s", serviceName) 96 | } 97 | 98 | //setup service locations 99 | var services []*ServiceLocation 100 | for _, v := range consulServices { 101 | s := &ServiceLocation{} 102 | s.URL = v.Address 103 | s.Port = v.ServicePort 104 | services = append(services, s) 105 | } 106 | 107 | // cache 108 | c := cachedServiceLocation{Services: services, CachedAt: time.Now().UTC()} 109 | r.cache[serviceName] = c 110 | return services, 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/go-martini/martini/static.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "net/url" 7 | "path" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | // StaticOptions is a struct for specifying configuration options for the martini.Static middleware. 13 | type StaticOptions struct { 14 | // Prefix is the optional prefix used to serve the static directory content 15 | Prefix string 16 | // SkipLogging will disable [Static] log messages when a static file is served. 17 | SkipLogging bool 18 | // IndexFile defines which file to serve as index if it exists. 19 | IndexFile string 20 | // Expires defines which user-defined function to use for producing a HTTP Expires Header 21 | // https://developers.google.com/speed/docs/insights/LeverageBrowserCaching 22 | Expires func() string 23 | // Fallback defines a default URL to serve when the requested resource was 24 | // not found. 25 | Fallback string 26 | // Exclude defines a pattern for URLs this handler should never process. 27 | Exclude string 28 | } 29 | 30 | func prepareStaticOptions(options []StaticOptions) StaticOptions { 31 | var opt StaticOptions 32 | if len(options) > 0 { 33 | opt = options[0] 34 | } 35 | 36 | // Defaults 37 | if len(opt.IndexFile) == 0 { 38 | opt.IndexFile = "index.html" 39 | } 40 | // Normalize the prefix if provided 41 | if opt.Prefix != "" { 42 | // Ensure we have a leading '/' 43 | if opt.Prefix[0] != '/' { 44 | opt.Prefix = "/" + opt.Prefix 45 | } 46 | // Remove any trailing '/' 47 | opt.Prefix = strings.TrimRight(opt.Prefix, "/") 48 | } 49 | return opt 50 | } 51 | 52 | // Static returns a middleware handler that serves static files in the given directory. 53 | func Static(directory string, staticOpt ...StaticOptions) Handler { 54 | if !filepath.IsAbs(directory) { 55 | directory = filepath.Join(Root, directory) 56 | } 57 | dir := http.Dir(directory) 58 | opt := prepareStaticOptions(staticOpt) 59 | 60 | return func(res http.ResponseWriter, req *http.Request, log *log.Logger) { 61 | if req.Method != "GET" && req.Method != "HEAD" { 62 | return 63 | } 64 | if opt.Exclude != "" && strings.HasPrefix(req.URL.Path, opt.Exclude) { 65 | return 66 | } 67 | file := req.URL.Path 68 | // if we have a prefix, filter requests by stripping the prefix 69 | if opt.Prefix != "" { 70 | if !strings.HasPrefix(file, opt.Prefix) { 71 | return 72 | } 73 | file = file[len(opt.Prefix):] 74 | if file != "" && file[0] != '/' { 75 | return 76 | } 77 | } 78 | f, err := dir.Open(file) 79 | if err != nil { 80 | // try any fallback before giving up 81 | if opt.Fallback != "" { 82 | file = opt.Fallback // so that logging stays true 83 | f, err = dir.Open(opt.Fallback) 84 | } 85 | 86 | if err != nil { 87 | // discard the error? 88 | return 89 | } 90 | } 91 | defer f.Close() 92 | 93 | fi, err := f.Stat() 94 | if err != nil { 95 | return 96 | } 97 | 98 | // try to serve index file 99 | if fi.IsDir() { 100 | // redirect if missing trailing slash 101 | if !strings.HasSuffix(req.URL.Path, "/") { 102 | dest := url.URL{ 103 | Path: req.URL.Path + "/", 104 | RawQuery: req.URL.RawQuery, 105 | Fragment: req.URL.Fragment, 106 | } 107 | http.Redirect(res, req, dest.String(), http.StatusFound) 108 | return 109 | } 110 | 111 | file = path.Join(file, opt.IndexFile) 112 | f, err = dir.Open(file) 113 | if err != nil { 114 | return 115 | } 116 | defer f.Close() 117 | 118 | fi, err = f.Stat() 119 | if err != nil || fi.IsDir() { 120 | return 121 | } 122 | } 123 | 124 | if !opt.SkipLogging { 125 | log.Println("[Static] Serving " + file) 126 | } 127 | 128 | // Add an Expires header to the static content 129 | if opt.Expires != nil { 130 | res.Header().Set("Expires", opt.Expires()) 131 | } 132 | 133 | http.ServeContent(res, req, file, fi.ModTime(), f) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/go-martini/martini/recovery.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "runtime" 10 | 11 | "github.com/codegangsta/inject" 12 | ) 13 | 14 | const ( 15 | panicHtml = ` 16 |
%s40 |
%s41 | 42 | ` 43 | ) 44 | 45 | var ( 46 | dunno = []byte("???") 47 | centerDot = []byte("·") 48 | dot = []byte(".") 49 | slash = []byte("/") 50 | ) 51 | 52 | // stack returns a nicely formated stack frame, skipping skip frames 53 | func stack(skip int) []byte { 54 | buf := new(bytes.Buffer) // the returned data 55 | // As we loop, we open files and read them. These variables record the currently 56 | // loaded file. 57 | var lines [][]byte 58 | var lastFile string 59 | for i := skip; ; i++ { // Skip the expected number of frames 60 | pc, file, line, ok := runtime.Caller(i) 61 | if !ok { 62 | break 63 | } 64 | // Print this much at least. If we can't find the source, it won't show. 65 | fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) 66 | if file != lastFile { 67 | data, err := ioutil.ReadFile(file) 68 | if err != nil { 69 | continue 70 | } 71 | lines = bytes.Split(data, []byte{'\n'}) 72 | lastFile = file 73 | } 74 | fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) 75 | } 76 | return buf.Bytes() 77 | } 78 | 79 | // source returns a space-trimmed slice of the n'th line. 80 | func source(lines [][]byte, n int) []byte { 81 | n-- // in stack trace, lines are 1-indexed but our array is 0-indexed 82 | if n < 0 || n >= len(lines) { 83 | return dunno 84 | } 85 | return bytes.TrimSpace(lines[n]) 86 | } 87 | 88 | // function returns, if possible, the name of the function containing the PC. 89 | func function(pc uintptr) []byte { 90 | fn := runtime.FuncForPC(pc) 91 | if fn == nil { 92 | return dunno 93 | } 94 | name := []byte(fn.Name()) 95 | // The name includes the path name to the package, which is unnecessary 96 | // since the file name is already included. Plus, it has center dots. 97 | // That is, we see 98 | // runtime/debug.*T·ptrmethod 99 | // and want 100 | // *T.ptrmethod 101 | // Also the package path might contains dot (e.g. code.google.com/...), 102 | // so first eliminate the path prefix 103 | if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 { 104 | name = name[lastslash+1:] 105 | } 106 | if period := bytes.Index(name, dot); period >= 0 { 107 | name = name[period+1:] 108 | } 109 | name = bytes.Replace(name, centerDot, dot, -1) 110 | return name 111 | } 112 | 113 | // Recovery returns a middleware that recovers from any panics and writes a 500 if there was one. 114 | // While Martini is in development mode, Recovery will also output the panic as HTML. 115 | func Recovery() Handler { 116 | return func(c Context, log *log.Logger) { 117 | defer func() { 118 | if err := recover(); err != nil { 119 | stack := stack(3) 120 | log.Printf("PANIC: %s\n%s", err, stack) 121 | 122 | // Lookup the current responsewriter 123 | val := c.Get(inject.InterfaceOf((*http.ResponseWriter)(nil))) 124 | res := val.Interface().(http.ResponseWriter) 125 | 126 | // respond with panic message while in development mode 127 | var body []byte 128 | if Env == Dev { 129 | res.Header().Set("Content-Type", "text/html") 130 | body = []byte(fmt.Sprintf(panicHtml, err, err, stack)) 131 | } else { 132 | body = []byte("500 Internal Server Error") 133 | } 134 | 135 | res.WriteHeader(http.StatusInternalServerError) 136 | if nil != body { 137 | res.Write(body) 138 | } 139 | } 140 | }() 141 | 142 | c.Next() 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/v1/test/injector.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "net/http/httptest" 8 | "os" 9 | "reflect" 10 | "strings" 11 | "testing" 12 | 13 | "github.com/codegangsta/inject" 14 | "github.com/divideandconquer/negotiator" 15 | "github.com/go-martini/martini" 16 | ) 17 | 18 | // Injector is a testing helper that can invoke martini handlers 19 | type Injector struct { 20 | inject.Injector 21 | r *http.Request 22 | } 23 | 24 | // NewInjector creates a TestInjector 25 | func NewInjector(method string, body string) *Injector { 26 | var i Injector 27 | i.r, _ = http.NewRequest(method, "http://localhost/v1/", strings.NewReader(body)) 28 | 29 | w := httptest.NewRecorder() 30 | 31 | enc := negotiator.JsonEncoder{PrettyPrint: false} 32 | cn := negotiator.NewContentNegotiator(enc, w) 33 | cn.AddEncoder(negotiator.MimeJSON, enc) 34 | 35 | i.Injector = inject.New() 36 | i.Injector.Map(i.r) 37 | i.Injector.MapTo(w, (*http.ResponseWriter)(nil)) 38 | i.Injector.MapTo(cn, (*negotiator.Negotiator)(nil)) 39 | 40 | return &i 41 | } 42 | 43 | // SetParams sets up DI for martinit params 44 | func (i *Injector) SetParams(p martini.Params) { 45 | i.Injector.Map(p) 46 | } 47 | 48 | // SetQueryParams will set URL Query parameters on the request 49 | func (i *Injector) SetQueryParams(params map[string]string) { 50 | q := i.r.URL.Query() 51 | for k, v := range params { 52 | q.Add(k, v) 53 | } 54 | i.r.URL.RawQuery = q.Encode() 55 | } 56 | 57 | // SetHeaders sets the headers on the internal request 58 | func (i *Injector) SetHeaders(h http.Header) { 59 | i.r.Header = h 60 | } 61 | 62 | ////////////////// 63 | // TEST HELPERS 64 | ////////////////// 65 | 66 | func injectAndInvoke(t *testing.T, handler martini.Handler, i *Injector) (int, []byte) { 67 | val, err := i.Invoke(handler) 68 | if err != nil { 69 | t.Fatalf("Unexpected error invoking handler: %v", err) 70 | } 71 | if len(val) != 2 { 72 | t.Fatalf("Unexpected number of return values: %d", len(val)) 73 | } 74 | 75 | intVal := val[0] 76 | if intVal.Kind() != reflect.Int { 77 | t.Fatalf("Unexpected type for int returned: %s", intVal.Kind().String()) 78 | } 79 | sliceVal := val[1] 80 | if sliceVal.Kind() != reflect.Slice { 81 | t.Fatalf("Unexpected body type returned: %s", sliceVal.Kind().String()) 82 | } 83 | 84 | return int(intVal.Int()), sliceVal.Bytes() 85 | } 86 | 87 | // InvokeAndCheck will invoke a handler with the provided injector and check its return values 88 | func InvokeAndCheck(t *testing.T, handler martini.Handler, i *Injector, expectedStatus int, expectedBody []byte) { 89 | status, body := injectAndInvoke(t, handler, i) 90 | if status != expectedStatus { 91 | t.Fatalf("Unexpected status returned %d instead of %d", status, expectedStatus) 92 | } 93 | 94 | if !reflect.DeepEqual(body, expectedBody) { 95 | t.Fatalf("Unexpected body returned %s instead of %s", body, expectedBody) 96 | } 97 | } 98 | 99 | // DoTestRequest will execute a request againt the service specified in the SERVICE_HOST env var 100 | func DoTestRequest(t *testing.T, method string, slug string, body string, header http.Header) (*http.Response, error) { 101 | host := os.Getenv("SERVICE_HOST") 102 | if host == "" { 103 | t.Skipf("skipping request to %s. SERVICE_HOST env var not set.", slug) 104 | } 105 | URL := fmt.Sprintf("http://%s/v1/%s", host, slug) 106 | 107 | r, err := http.NewRequest(method, URL, strings.NewReader(body)) 108 | if err != nil { 109 | t.Fatalf("Could not create request to %s", URL) 110 | } 111 | 112 | if header != nil { 113 | r.Header = header 114 | } 115 | 116 | return http.DefaultClient.Do(r) 117 | } 118 | 119 | // VerifyResponse will make sure that the response matches the expected status 120 | func VerifyResponse(t *testing.T, res *http.Response, err error, expectedStatus int) []byte { 121 | defer res.Body.Close() 122 | if err != nil { 123 | t.Fatalf("Error was not nil: %v", err) 124 | } 125 | 126 | if res == nil { 127 | t.Fatal("res was nil") 128 | } 129 | 130 | if res.StatusCode != expectedStatus { 131 | t.Fatalf("Status code (%d) did not match expected (%d)", res.StatusCode, expectedStatus) 132 | } 133 | 134 | body, err := ioutil.ReadAll(res.Body) 135 | if err != nil { 136 | t.Fatalf("Error reading body: %v", err) 137 | } 138 | return body 139 | } 140 | 141 | // VerifyResponseBody will make sure the response matches the expected status and body 142 | func VerifyResponseBody(t *testing.T, res *http.Response, err error, expectedStatus int, expectedBody []byte) { 143 | body := VerifyResponse(t, res, err, expectedStatus) 144 | if !reflect.DeepEqual(body, expectedBody) { 145 | t.Fatalf("Body ( %s ) did not match expected ( %s )", body, expectedBody) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/divideandconquer/negotiator/README.md: -------------------------------------------------------------------------------- 1 | # Negotiator 2 | 3 | This is a simple golang [content negotiation](http://en.wikipedia.org/wiki/Content_negotiation) 4 | library that was built as a [Martini](http://martini.codegangsta.io/) 5 | middleware/handler but can be used standalone as well. The ContentNegotiator 6 | object will read the `Accepts` header from the `net/http` request and encode 7 | the given data appropriately or fallback to a default encoding if the `Accepts` 8 | header is not recognized. 9 | 10 | ## Supported Content-Types 11 | * application/json 12 | * application/xml 13 | 14 | **NOTE**: The above encoders are included in this repo and will automatically be 15 | setup if you use the `NewJsonXmlContentNegotiator` function but support for any 16 | mime type can be added dynamically. 17 | 18 | ## Usage 19 | 20 | To use the built in JSON and XML encoders use the `NewJsonXmlContentNegotiator` 21 | function to create the `ContentNegotiator`. 22 | 23 | If you dont want JSON and XML support use the `NewContentNegotiator` function 24 | to create a base `ContentNegotiator`. This negotiator will need at least one `Encoder` 25 | to function. You can add encoders to this using the `AddEncoder` function as seen 26 | in the examples below. 27 | 28 | ### General 29 | 30 | For general usage, i.e. non-martini use, simply import the package, create a 31 | `ContentNegotiator` object, and call `Negotiate`: 32 | 33 | ```go 34 | package main 35 | 36 | import ( 37 | "github.com/divideandconquer/negotiator" 38 | "net/http" 39 | "log" 40 | ) 41 | 42 | func main() { 43 | ... 44 | 45 | output := ... //some struct of data 46 | 47 | // This creates a content negotiator can handle JSON and XML, defaults to json, and doesn't pretty print 48 | fallbackEncoder := negotiator.JsonEncoder{false} 49 | cn := negotiator.NewJsonXmlContentNegotiator(fallbackEncoder, responseWriter, false) 50 | // To add your own mime types and encoders use the AddEncoder function: 51 | //cn.AddEncoder("text/html", htmlEncoder) 52 | log.Println(cn.Negotiate(request, output)) 53 | } 54 | ``` 55 | If you don't want to support XML you can use `NewContentNegotiator` instead: 56 | 57 | ```go 58 | // Don't want to support XML? Use the following lines: 59 | cn := negotiator.NewContentNegotiator(defaultEncoder, responseWriter) 60 | cn.AddEncoder("application/json", negotiator.JsonEncoder{true}) 61 | ``` 62 | 63 | ### Martini 64 | 65 | For use with the [Martini](http://martini.codegangsta.io/) framework add the content 66 | negotiator to the list of middlewares to use: 67 | 68 | ```go 69 | //Martini initialization 70 | m = martini.New() 71 | 72 | // add middleware 73 | m.Use(martini.Recovery()) 74 | m.Use(martini.Logger()) 75 | 76 | // setup content negotiation 77 | m.Use(func(c martini.Context, w http.ResponseWriter) { 78 | // This creates a content negotiator can handle JSON and XML, defaults to json, and doesn't pretty print 79 | fallbackEncoder := negotiator.JsonEncoder{false} 80 | cn := negotiator.NewJsonXmlContentNegotiator(fallbackEncoder, w, false) 81 | 82 | // To add your own mime types and encoders use the AddEncoder function: 83 | //cn.AddEncoder("text/html", htmlEncoder) 84 | 85 | c.MapTo(cn, (*negotiator.Negotiator)(nil)) 86 | }) 87 | 88 | // setup router 89 | router := martini.NewRouter() 90 | router.Get("/", func(r *http.Request, neg negotiator.Negotiator) (int, []byte) { 91 | data := ... //setup whatever data you want to return 92 | return http.StatusOK, negotiator.Must(neg.Negotiate(r, data))) 93 | } 94 | ``` 95 | 96 | ### Creating Encoders 97 | 98 | If you want to add support for additional mime types simple create a struct 99 | that implements the `Encoder` interface. Use the skeleton below as a starting 100 | point. 101 | 102 | ```go 103 | package main 104 | 105 | type FooEncoder struct {} 106 | 107 | func (foo FooEncoder) Encode(data interface{}) ([]byte, error) { 108 | // encode the data and return a byte array 109 | } 110 | 111 | // ContentType returns the string that will be used 112 | // for the Content-Type header on the response 113 | func (js JsonEncoder) ContentType() string { 114 | //return the appropriate Content-Type header string 115 | //return "application/foo; charset=utf-8" 116 | } 117 | ``` 118 | Once you have an encoder add it to the content negotiator with the `AddEncoder` 119 | function: 120 | 121 | ```go 122 | cn := negotiator.NewContentNegotiator(defaultEncoder, responseWriter) 123 | // Pass in the Accepts header string to respond to and the encoder itself 124 | cn.AddEncoder("application/foo", FooEncoder{}) 125 | ``` 126 | Now if the client sends an `Accepts` header of `application/foo` the `FooEncoder` 127 | will be used to encode the response. 128 | 129 | # License 130 | This module is licensed using the Apache-2.0 License: 131 | 132 | ``` 133 | Copyright (c) 2014, Kyle Boorky 134 | ``` 135 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/divideandconquer/go-consul-client/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-merge/merge" 10 | "github.com/hashicorp/consul/api" 11 | ) 12 | 13 | const divider = "/" 14 | 15 | // Loader is a object that can import, initialize, and Get config values 16 | type Loader interface { 17 | Import(data []byte) error 18 | Initialize() error 19 | Get(key string) ([]byte, error) 20 | 21 | // Must functions will panic if they can't do what is requested. 22 | // They are maingly meant for use with configs that are required for an app to start up 23 | MustGetString(key string) string 24 | MustGetBool(key string) bool 25 | MustGetInt(key string) int 26 | MustGetDuration(key string) time.Duration 27 | 28 | //TODO add array support? 29 | } 30 | 31 | type cachedLoader struct { 32 | namespace string 33 | cacheLock sync.RWMutex 34 | cache map[string][]byte 35 | consulKV *api.KV 36 | } 37 | 38 | // NewCachedLoader creates a Loader that will cache the provided namespace on initialization 39 | // and return data from that cache on Get 40 | func NewCachedLoader(namespace string, consulAddr string) (Loader, error) { 41 | config := api.DefaultConfig() 42 | config.Address = consulAddr 43 | consul, err := api.NewClient(config) 44 | if err != nil { 45 | return nil, fmt.Errorf("Could not connect to consul: %v", err) 46 | } 47 | 48 | return &cachedLoader{namespace: namespace, consulKV: consul.KV()}, nil 49 | } 50 | 51 | // Import takes a json byte array and inserts the key value pairs into consul prefixed by the namespace 52 | func (c *cachedLoader) Import(data []byte) error { 53 | conf := make(map[string]interface{}) 54 | err := json.Unmarshal(data, &conf) 55 | if err != nil { 56 | return fmt.Errorf("Unable to parse json data: %v", err) 57 | } 58 | kvMap, err := c.compileKeyValues(conf, c.namespace) 59 | if err != nil { 60 | return fmt.Errorf("Unable to complie KVs: %v", err) 61 | } 62 | 63 | if err != nil { 64 | return fmt.Errorf("Could not create consul client: %v", err) 65 | } 66 | for k, v := range kvMap { 67 | p := &api.KVPair{Key: k, Value: v} 68 | _, err = c.consulKV.Put(p, nil) 69 | if err != nil { 70 | return fmt.Errorf("Could not write key to consul (%s | %s) %v", k, v, err) 71 | } 72 | } 73 | return nil 74 | } 75 | 76 | func (c *cachedLoader) compileKeyValues(data map[string]interface{}, prefix string) (map[string][]byte, error) { 77 | result := make(map[string][]byte) 78 | for k, v := range data { 79 | if subMap, ok := v.(map[string]interface{}); ok { 80 | //recurse and merge results 81 | compiled, err := c.compileKeyValues(subMap, prefix+divider+k) 82 | if err != nil { 83 | return nil, err 84 | } 85 | merged := merge.Merge(result, compiled) 86 | if mm, ok := merged.(map[string][]byte); ok { 87 | result = mm 88 | } 89 | } else { 90 | //for other types json marshal will turn then into string byte slice for storage 91 | j, err := json.Marshal(v) 92 | if err != nil { 93 | return nil, err 94 | } 95 | result[prefix+divider+k] = j 96 | } 97 | } 98 | return result, nil 99 | } 100 | 101 | // Initialize loads the consul KV's from the namespace into cache for later retrieval 102 | func (c *cachedLoader) Initialize() error { 103 | pairs, _, err := c.consulKV.List(c.namespace, nil) 104 | if err != nil { 105 | return fmt.Errorf("Could not pull config from consul: %v", err) 106 | } 107 | 108 | //write lock the cache incase init is called more than once 109 | c.cacheLock.Lock() 110 | defer c.cacheLock.Unlock() 111 | 112 | c.cache = make(map[string][]byte) 113 | for _, kv := range pairs { 114 | c.cache[kv.Key] = kv.Value 115 | } 116 | return nil 117 | } 118 | 119 | // Get fetches the raw config from cache 120 | func (c *cachedLoader) Get(key string) ([]byte, error) { 121 | c.cacheLock.RLock() 122 | defer c.cacheLock.RUnlock() 123 | 124 | compiledKey := c.namespace + divider + key 125 | if ret, ok := c.cache[compiledKey]; ok { 126 | return ret, nil 127 | } 128 | return nil, fmt.Errorf("Could not find value for key: %s", compiledKey) 129 | } 130 | 131 | // MustGetString fetches the config and parses it into a string. Panics on failure. 132 | func (c *cachedLoader) MustGetString(key string) string { 133 | b, err := c.Get(key) 134 | if err != nil { 135 | panic(fmt.Sprintf("Could not fetch config (%s) %v", key, err)) 136 | } 137 | 138 | var s string 139 | err = json.Unmarshal(b, &s) 140 | if err != nil { 141 | panic(fmt.Sprintf("Could not unmarshal config (%s) %v", key, err)) 142 | } 143 | 144 | return s 145 | } 146 | 147 | // MustGetBool fetches the config and parses it into a bool. Panics on failure. 148 | func (c *cachedLoader) MustGetBool(key string) bool { 149 | b, err := c.Get(key) 150 | if err != nil { 151 | panic(fmt.Sprintf("Could not fetch config (%s) %v", key, err)) 152 | } 153 | var ret bool 154 | err = json.Unmarshal(b, &ret) 155 | if err != nil { 156 | panic(fmt.Sprintf("Could not unmarshal config (%s) %v", key, err)) 157 | } 158 | return ret 159 | } 160 | 161 | // MustGetInt fetches the config and parses it into an int. Panics on failure. 162 | func (c *cachedLoader) MustGetInt(key string) int { 163 | b, err := c.Get(key) 164 | if err != nil { 165 | panic(fmt.Sprintf("Could not fetch config (%s) %v", key, err)) 166 | } 167 | 168 | var ret int 169 | err = json.Unmarshal(b, &ret) 170 | if err != nil { 171 | panic(fmt.Sprintf("Could not unmarshal config (%s) %v", key, err)) 172 | } 173 | return ret 174 | } 175 | 176 | // MustGetDuration fetches the config and parses it into a duration. Panics on failure. 177 | func (c *cachedLoader) MustGetDuration(key string) time.Duration { 178 | s := c.MustGetString(key) 179 | ret, err := time.ParseDuration(s) 180 | if err != nil { 181 | panic(fmt.Sprintf("Could not parse config (%s) into a duration: %v", key, err)) 182 | } 183 | return ret 184 | } 185 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/go-martini/martini/martini.go: -------------------------------------------------------------------------------- 1 | // Package martini is a powerful package for quickly writing modular web applications/services in Golang. 2 | // 3 | // For a full guide visit http://github.com/go-martini/martini 4 | // 5 | // package main 6 | // 7 | // import "github.com/go-martini/martini" 8 | // 9 | // func main() { 10 | // m := martini.Classic() 11 | // 12 | // m.Get("/", func() string { 13 | // return "Hello world!" 14 | // }) 15 | // 16 | // m.Run() 17 | // } 18 | package martini 19 | 20 | import ( 21 | "log" 22 | "net/http" 23 | "os" 24 | "reflect" 25 | 26 | "github.com/codegangsta/inject" 27 | ) 28 | 29 | // Martini represents the top level web application. inject.Injector methods can be invoked to map services on a global level. 30 | type Martini struct { 31 | inject.Injector 32 | handlers []Handler 33 | action Handler 34 | logger *log.Logger 35 | } 36 | 37 | // New creates a bare bones Martini instance. Use this method if you want to have full control over the middleware that is used. 38 | func New() *Martini { 39 | m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(os.Stdout, "[martini] ", 0)} 40 | m.Map(m.logger) 41 | m.Map(defaultReturnHandler()) 42 | return m 43 | } 44 | 45 | // Handlers sets the entire middleware stack with the given Handlers. This will clear any current middleware handlers. 46 | // Will panic if any of the handlers is not a callable function 47 | func (m *Martini) Handlers(handlers ...Handler) { 48 | m.handlers = make([]Handler, 0) 49 | for _, handler := range handlers { 50 | m.Use(handler) 51 | } 52 | } 53 | 54 | // Action sets the handler that will be called after all the middleware has been invoked. This is set to martini.Router in a martini.Classic(). 55 | func (m *Martini) Action(handler Handler) { 56 | validateHandler(handler) 57 | m.action = handler 58 | } 59 | 60 | // Use adds a middleware Handler to the stack. Will panic if the handler is not a callable func. Middleware Handlers are invoked in the order that they are added. 61 | func (m *Martini) Use(handler Handler) { 62 | validateHandler(handler) 63 | 64 | m.handlers = append(m.handlers, handler) 65 | } 66 | 67 | // ServeHTTP is the HTTP Entry point for a Martini instance. Useful if you want to control your own HTTP server. 68 | func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) { 69 | m.createContext(res, req).run() 70 | } 71 | 72 | // Run the http server on a given host and port. 73 | func (m *Martini) RunOnAddr(addr string) { 74 | // TODO: Should probably be implemented using a new instance of http.Server in place of 75 | // calling http.ListenAndServer directly, so that it could be stored in the martini struct for later use. 76 | // This would also allow to improve testing when a custom host and port are passed. 77 | 78 | logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger) 79 | logger.Printf("listening on %s (%s)\n", addr, Env) 80 | logger.Fatalln(http.ListenAndServe(addr, m)) 81 | } 82 | 83 | // Run the http server. Listening on os.GetEnv("PORT") or 3000 by default. 84 | func (m *Martini) Run() { 85 | port := os.Getenv("PORT") 86 | if len(port) == 0 { 87 | port = "3000" 88 | } 89 | 90 | host := os.Getenv("HOST") 91 | 92 | m.RunOnAddr(host + ":" + port) 93 | } 94 | 95 | func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context { 96 | c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0} 97 | c.SetParent(m) 98 | c.MapTo(c, (*Context)(nil)) 99 | c.MapTo(c.rw, (*http.ResponseWriter)(nil)) 100 | c.Map(req) 101 | return c 102 | } 103 | 104 | // ClassicMartini represents a Martini with some reasonable defaults. Embeds the router functions for convenience. 105 | type ClassicMartini struct { 106 | *Martini 107 | Router 108 | } 109 | 110 | // Classic creates a classic Martini with some basic default middleware - martini.Logger, martini.Recovery and martini.Static. 111 | // Classic also maps martini.Routes as a service. 112 | func Classic() *ClassicMartini { 113 | r := NewRouter() 114 | m := New() 115 | m.Use(Logger()) 116 | m.Use(Recovery()) 117 | m.Use(Static("public")) 118 | m.MapTo(r, (*Routes)(nil)) 119 | m.Action(r.Handle) 120 | return &ClassicMartini{m, r} 121 | } 122 | 123 | // Handler can be any callable function. Martini attempts to inject services into the handler's argument list. 124 | // Martini will panic if an argument could not be fullfilled via dependency injection. 125 | type Handler interface{} 126 | 127 | func validateHandler(handler Handler) { 128 | if reflect.TypeOf(handler).Kind() != reflect.Func { 129 | panic("martini handler must be a callable func") 130 | } 131 | } 132 | 133 | // Context represents a request context. Services can be mapped on the request level from this interface. 134 | type Context interface { 135 | inject.Injector 136 | // Next is an optional function that Middleware Handlers can call to yield the until after 137 | // the other Handlers have been executed. This works really well for any operations that must 138 | // happen after an http request 139 | Next() 140 | // Written returns whether or not the response for this context has been written. 141 | Written() bool 142 | } 143 | 144 | type context struct { 145 | inject.Injector 146 | handlers []Handler 147 | action Handler 148 | rw ResponseWriter 149 | index int 150 | } 151 | 152 | func (c *context) handler() Handler { 153 | if c.index < len(c.handlers) { 154 | return c.handlers[c.index] 155 | } 156 | if c.index == len(c.handlers) { 157 | return c.action 158 | } 159 | panic("invalid index for context handler") 160 | } 161 | 162 | func (c *context) Next() { 163 | c.index += 1 164 | c.run() 165 | } 166 | 167 | func (c *context) Written() bool { 168 | return c.rw.Written() 169 | } 170 | 171 | func (c *context) run() { 172 | for c.index <= len(c.handlers) { 173 | _, err := c.Invoke(c.handler()) 174 | if err != nil { 175 | panic(err) 176 | } 177 | c.index += 1 178 | 179 | if c.Written() { 180 | return 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/inject/inject.go: -------------------------------------------------------------------------------- 1 | // Package inject provides utilities for mapping and injecting dependencies in various ways. 2 | package inject 3 | 4 | import ( 5 | "fmt" 6 | "reflect" 7 | ) 8 | 9 | // Injector represents an interface for mapping and injecting dependencies into structs 10 | // and function arguments. 11 | type Injector interface { 12 | Applicator 13 | Invoker 14 | TypeMapper 15 | // SetParent sets the parent of the injector. If the injector cannot find a 16 | // dependency in its Type map it will check its parent before returning an 17 | // error. 18 | SetParent(Injector) 19 | } 20 | 21 | // Applicator represents an interface for mapping dependencies to a struct. 22 | type Applicator interface { 23 | // Maps dependencies in the Type map to each field in the struct 24 | // that is tagged with 'inject'. Returns an error if the injection 25 | // fails. 26 | Apply(interface{}) error 27 | } 28 | 29 | // Invoker represents an interface for calling functions via reflection. 30 | type Invoker interface { 31 | // Invoke attempts to call the interface{} provided as a function, 32 | // providing dependencies for function arguments based on Type. Returns 33 | // a slice of reflect.Value representing the returned values of the function. 34 | // Returns an error if the injection fails. 35 | Invoke(interface{}) ([]reflect.Value, error) 36 | } 37 | 38 | // TypeMapper represents an interface for mapping interface{} values based on type. 39 | type TypeMapper interface { 40 | // Maps the interface{} value based on its immediate type from reflect.TypeOf. 41 | Map(interface{}) TypeMapper 42 | // Maps the interface{} value based on the pointer of an Interface provided. 43 | // This is really only useful for mapping a value as an interface, as interfaces 44 | // cannot at this time be referenced directly without a pointer. 45 | MapTo(interface{}, interface{}) TypeMapper 46 | // Provides a possibility to directly insert a mapping based on type and value. 47 | // This makes it possible to directly map type arguments not possible to instantiate 48 | // with reflect like unidirectional channels. 49 | Set(reflect.Type, reflect.Value) TypeMapper 50 | // Returns the Value that is mapped to the current type. Returns a zeroed Value if 51 | // the Type has not been mapped. 52 | Get(reflect.Type) reflect.Value 53 | } 54 | 55 | type injector struct { 56 | values map[reflect.Type]reflect.Value 57 | parent Injector 58 | } 59 | 60 | // InterfaceOf dereferences a pointer to an Interface type. 61 | // It panics if value is not an pointer to an interface. 62 | func InterfaceOf(value interface{}) reflect.Type { 63 | t := reflect.TypeOf(value) 64 | 65 | for t.Kind() == reflect.Ptr { 66 | t = t.Elem() 67 | } 68 | 69 | if t.Kind() != reflect.Interface { 70 | panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)") 71 | } 72 | 73 | return t 74 | } 75 | 76 | // New returns a new Injector. 77 | func New() Injector { 78 | return &injector{ 79 | values: make(map[reflect.Type]reflect.Value), 80 | } 81 | } 82 | 83 | // Invoke attempts to call the interface{} provided as a function, 84 | // providing dependencies for function arguments based on Type. 85 | // Returns a slice of reflect.Value representing the returned values of the function. 86 | // Returns an error if the injection fails. 87 | // It panics if f is not a function 88 | func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) { 89 | t := reflect.TypeOf(f) 90 | 91 | var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func 92 | for i := 0; i < t.NumIn(); i++ { 93 | argType := t.In(i) 94 | val := inj.Get(argType) 95 | if !val.IsValid() { 96 | return nil, fmt.Errorf("Value not found for type %v", argType) 97 | } 98 | 99 | in[i] = val 100 | } 101 | 102 | return reflect.ValueOf(f).Call(in), nil 103 | } 104 | 105 | // Maps dependencies in the Type map to each field in the struct 106 | // that is tagged with 'inject'. 107 | // Returns an error if the injection fails. 108 | func (inj *injector) Apply(val interface{}) error { 109 | v := reflect.ValueOf(val) 110 | 111 | for v.Kind() == reflect.Ptr { 112 | v = v.Elem() 113 | } 114 | 115 | if v.Kind() != reflect.Struct { 116 | return nil // Should not panic here ? 117 | } 118 | 119 | t := v.Type() 120 | 121 | for i := 0; i < v.NumField(); i++ { 122 | f := v.Field(i) 123 | structField := t.Field(i) 124 | if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") { 125 | ft := f.Type() 126 | v := inj.Get(ft) 127 | if !v.IsValid() { 128 | return fmt.Errorf("Value not found for type %v", ft) 129 | } 130 | 131 | f.Set(v) 132 | } 133 | 134 | } 135 | 136 | return nil 137 | } 138 | 139 | // Maps the concrete value of val to its dynamic type using reflect.TypeOf, 140 | // It returns the TypeMapper registered in. 141 | func (i *injector) Map(val interface{}) TypeMapper { 142 | i.values[reflect.TypeOf(val)] = reflect.ValueOf(val) 143 | return i 144 | } 145 | 146 | func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper { 147 | i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val) 148 | return i 149 | } 150 | 151 | // Maps the given reflect.Type to the given reflect.Value and returns 152 | // the Typemapper the mapping has been registered in. 153 | func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper { 154 | i.values[typ] = val 155 | return i 156 | } 157 | 158 | func (i *injector) Get(t reflect.Type) reflect.Value { 159 | val := i.values[t] 160 | 161 | if val.IsValid() { 162 | return val 163 | } 164 | 165 | // no concrete types found, try to find implementors 166 | // if t is an interface 167 | if t.Kind() == reflect.Interface { 168 | for k, v := range i.values { 169 | if k.Implements(t) { 170 | val = v 171 | break 172 | } 173 | } 174 | } 175 | 176 | // Still no type found, try to look it up on the parent 177 | if !val.IsValid() && i.parent != nil { 178 | val = i.parent.Get(t) 179 | } 180 | 181 | return val 182 | 183 | } 184 | 185 | func (i *injector) SetParent(parent Injector) { 186 | i.parent = parent 187 | } 188 | -------------------------------------------------------------------------------- /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/go-martini/martini/translations/README_zh_cn.md: -------------------------------------------------------------------------------- 1 | # Martini [](https://app.wercker.com/project/bykey/174bef7e3c999e103cacfe2770102266) [](http://godoc.org/github.com/go-martini/martini) 2 | 3 | Martini是一个强大为了编写模块化Web应用而生的GO语言框架. 4 | 5 | ## 第一个应用 6 | 7 | 在你安装了GO语言和设置了你的[GOPATH](http://golang.org/doc/code.html#GOPATH)之后, 创建你的自己的`.go`文件, 这里我们假设它的名字叫做 `server.go`. 8 | 9 | ~~~ go 10 | package main 11 | 12 | import "github.com/go-martini/martini" 13 | 14 | func main() { 15 | m := martini.Classic() 16 | m.Get("/", func() string { 17 | return "Hello world!" 18 | }) 19 | m.Run() 20 | } 21 | ~~~ 22 | 23 | 然后安装Martini的包. (注意Martini需要Go语言1.1或者以上的版本支持): 24 | ~~~ 25 | go get github.com/go-martini/martini 26 | ~~~ 27 | 28 | 最后运行你的服务: 29 | ~~~ 30 | go run server.go 31 | ~~~ 32 | 33 | 这时你将会有一个Martini的服务监听了, 地址是: `localhost:3000`. 34 | 35 | ## 获得帮助 36 | 37 | 请加入: [邮件列表](https://groups.google.com/forum/#!forum/martini-go) 38 | 39 | 或者可以查看在线演示地址: [演示视频](http://martini.codegangsta.io/#demo) 40 | 41 | ## 功能列表 42 | * 使用极其简单. 43 | * 无侵入式的设计. 44 | * 很好的与其他的Go语言包协同使用. 45 | * 超赞的路径匹配和路由. 46 | * 模块化的设计 - 容易插入功能件,也容易将其拔出来. 47 | * 已有很多的中间件可以直接使用. 48 | * 框架内已拥有很好的开箱即用的功能支持. 49 | * **完全兼容[http.HandlerFunc](http://godoc.org/net/http#HandlerFunc)接口.** 50 | 51 | ## 更多中间件 52 | 更多的中间件和功能组件, 请查看代码仓库: [martini-contrib](https://github.com/martini-contrib). 53 | 54 | ## 目录 55 | * [核心 Martini](#classic-martini) 56 | * [处理器](#handlers) 57 | * [路由](#routing) 58 | * [服务](#services) 59 | * [服务静态文件](#serving-static-files) 60 | * [中间件处理器](#middleware-handlers) 61 | * [Next()](#next) 62 | * [常见问答](#faq) 63 | 64 | ## 核心 Martini 65 | 为了更快速的启用Martini, [martini.Classic()](http://godoc.org/github.com/go-martini/martini#Classic) 提供了一些默认的方便Web开发的工具: 66 | ~~~ go 67 | m := martini.Classic() 68 | // ... middleware and routing goes here 69 | m.Run() 70 | ~~~ 71 | 72 | 下面是Martini核心已经包含的功能 [martini.Classic()](http://godoc.org/github.com/go-martini/martini#Classic): 73 | * Request/Response Logging (请求/响应日志) - [martini.Logger](http://godoc.org/github.com/go-martini/martini#Logger) 74 | * Panic Recovery (容错) - [martini.Recovery](http://godoc.org/github.com/go-martini/martini#Recovery) 75 | * Static File serving (静态文件服务) - [martini.Static](http://godoc.org/github.com/go-martini/martini#Static) 76 | * Routing (路由) - [martini.Router](http://godoc.org/github.com/go-martini/martini#Router) 77 | 78 | ### 处理器 79 | 处理器是Martini的灵魂和核心所在. 一个处理器基本上可以是任何的函数: 80 | ~~~ go 81 | m.Get("/", func() { 82 | println("hello world") 83 | }) 84 | ~~~ 85 | 86 | #### 返回值 87 | 当一个处理器返回结果的时候, Martini将会把返回值作为字符串写入到当前的[http.ResponseWriter](http://godoc.org/net/http#ResponseWriter)里面: 88 | ~~~ go 89 | m.Get("/", func() string { 90 | return "hello world" // HTTP 200 : "hello world" 91 | }) 92 | ~~~ 93 | 94 | 另外你也可以选择性的返回多一个状态码: 95 | ~~~ go 96 | m.Get("/", func() (int, string) { 97 | return 418, "i'm a teapot" // HTTP 418 : "i'm a teapot" 98 | }) 99 | ~~~ 100 | 101 | #### 服务的注入 102 | 处理器是通过反射来调用的. Martini 通过*Dependency Injection* *(依赖注入)* 来为处理器注入参数列表. **这样使得Martini与Go语言的`http.HandlerFunc`接口完全兼容.** 103 | 104 | 如果你加入一个参数到你的处理器, Martini将会搜索它参数列表中的服务,并且通过类型判断来解决依赖关系: 105 | ~~~ go 106 | m.Get("/", func(res http.ResponseWriter, req *http.Request) { // res 和 req 是通过Martini注入的 107 | res.WriteHeader(200) // HTTP 200 108 | }) 109 | ~~~ 110 | 111 | 下面的这些服务已经被包含在核心Martini中: [martini.Classic()](http://godoc.org/github.com/go-martini/martini#Classic): 112 | * [*log.Logger](http://godoc.org/log#Logger) - Martini的全局日志. 113 | * [martini.Context](http://godoc.org/github.com/go-martini/martini#Context) - http request context (请求上下文). 114 | * [martini.Params](http://godoc.org/github.com/go-martini/martini#Params) - `map[string]string` of named params found by route matching. (名字和参数键值对的参数列表) 115 | * [martini.Routes](http://godoc.org/github.com/go-martini/martini#Routes) - Route helper service. (路由协助处理) 116 | * [http.ResponseWriter](http://godoc.org/net/http/#ResponseWriter) - http Response writer interface. (响应结果的流接口) 117 | * [*http.Request](http://godoc.org/net/http/#Request) - http Request. (http请求) 118 | 119 | ### 路由 120 | 在Martini中, 路由是一个HTTP方法配对一个URL匹配模型. 每一个路由可以对应一个或多个处理器方法: 121 | ~~~ go 122 | m.Get("/", func() { 123 | // 显示 124 | }) 125 | 126 | m.Patch("/", func() { 127 | // 更新 128 | }) 129 | 130 | m.Post("/", func() { 131 | // 创建 132 | }) 133 | 134 | m.Put("/", func() { 135 | // 替换 136 | }) 137 | 138 | m.Delete("/", func() { 139 | // 删除 140 | }) 141 | 142 | m.Options("/", func() { 143 | // http 选项 144 | }) 145 | 146 | m.NotFound(func() { 147 | // 处理 404 148 | }) 149 | ~~~ 150 | 151 | 路由匹配的顺序是按照他们被定义的顺序执行的. 最先被定义的路由将会首先被用户请求匹配并调用. 152 | 153 | 路由模型可能包含参数列表, 可以通过[martini.Params](http://godoc.org/github.com/go-martini/martini#Params)服务来获取: 154 | ~~~ go 155 | m.Get("/hello/:name", func(params martini.Params) string { 156 | return "Hello " + params["name"] 157 | }) 158 | ~~~ 159 | 160 | 路由匹配可以通过正则表达式或者glob的形式: 161 | ~~~ go 162 | m.Get("/hello/**", func(params martini.Params) string { 163 | return "Hello " + params["_1"] 164 | }) 165 | ~~~ 166 | 167 | 也可以这样使用正则表达式: 168 | ~~~go 169 | m.Get("/hello/(?P