├── .gitignore
├── Dockerfile
├── LICENSE
├── Procfile
├── README.md
├── build.sh
├── go.mod
├── go.sum
├── handlers
├── assets.go
├── env.go
├── exit.go
├── handlers.go
├── hello.go
├── index.go
└── port.go
├── helpers
└── fetch_index.go
├── main.go
├── manifest.yml
├── routes
└── routes.go
└── vendor
├── github.com
├── bmizerany
│ └── pat
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── mux.go
├── pivotal-golang
│ └── lager
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── logger.go
│ │ ├── models.go
│ │ ├── reconfigurable_sink.go
│ │ └── writer_sink.go
└── tedsuo
│ ├── ifrit
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── doc.go
│ ├── http_server
│ │ └── http_server.go
│ ├── process.go
│ └── runner.go
│ └── rata
│ ├── LICENSE
│ ├── README.md
│ ├── VERSION
│ ├── docs.go
│ ├── requests.go
│ ├── router.go
│ └── routes.go
└── modules.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | lattice-app
3 | *.coverprofile
4 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM busybox:ubuntu-14.04
2 |
3 | ENTRYPOINT ["/test-app"]
4 |
5 | COPY test-app /test-app
6 | RUN chmod a+x /test-app
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: test-app
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Test App - a simple Go webapp
2 |
3 | ### Pushing the app to Cloud Foundry
4 |
5 | ```
6 | cf push test-app
7 | ```
8 |
9 | ### Endpoints
10 |
11 | - `/`: a simple landing page displaying the index and uptime
12 | - `/env`: displays environment variables
13 | - `/exit`: instructs the app to exit with status code 1
14 | - `/index`: returns the application index
15 | - `/port`: returns the local port the request was received on
16 |
17 | ### Configure the app to listen on multiple ports
18 |
19 | By providing a customer start command, you can configure the app to listen on multiple ports. The app responds the same way to each port.
20 | ```
21 | cf push test-app -c "test-app --ports=7777,8888"
22 | ```
23 |
24 | ### Pushing the app to CF as a Docker image
25 |
26 | Simple App is also packaged as a docker image at cloudfoundry/test-app
27 |
28 | ```bash
29 | cf push my-test-app -o cloudfoundry/test-app
30 | ```
31 |
32 | ### To rebuild the dockerimage:
33 |
34 | ```bash
35 | ./build.sh
36 | ```
37 |
38 | Assumes you have the go toolchain (with the ability to cross-compile to different platforms) and docker installed and pointing at your docker daemon.
39 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | echo "Compiling for linux..."
4 | GOOS=linux GOARCH=amd64 go build .
5 |
6 | echo "Constructing Dockerimage"
7 | docker build -t="cloudfoundry/test-app" .
8 | docker push cloudfoundry/test-app
9 |
10 | echo "Cleaning up..."
11 | rm test-app
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/cloudfoundry-samples/test-app
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/bmizerany/pat v0.0.0-20140625234703-b8a35001b773 // indirect
7 | github.com/pivotal-golang/lager v0.0.0-20150428205713-c88fa6d6c4d2
8 | github.com/tedsuo/ifrit v0.0.0-20150410161953-65ca48cd8a94
9 | github.com/tedsuo/rata v0.0.0-20141210191936-6197c97c67a0
10 | )
11 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/bmizerany/pat v0.0.0-20140625234703-b8a35001b773 h1:u92n4d/2otMkjVmGnxLPUkl/P+eOKqcgxREHWl/2/6Y=
2 | github.com/bmizerany/pat v0.0.0-20140625234703-b8a35001b773/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
3 | github.com/pivotal-golang/lager v0.0.0-20150428205713-c88fa6d6c4d2 h1:5mfs0yZ4ijQYZr5DDwhwbzjAEqdbvFOoePnDg3w+erU=
4 | github.com/pivotal-golang/lager v0.0.0-20150428205713-c88fa6d6c4d2/go.mod h1:EJZBAWMz/TvxVfLaBRwCv+gszrSByWbqQBRwfVbUhvM=
5 | github.com/tedsuo/ifrit v0.0.0-20150410161953-65ca48cd8a94 h1:cUXeV5OpVoN3ZAVGqz7oqFR0Bek2Hi5REtAtVeu7vOI=
6 | github.com/tedsuo/ifrit v0.0.0-20150410161953-65ca48cd8a94/go.mod h1:eyZnKCc955uh98WQvzOm0dgAeLnf2O0Rz0LPoC5ze+0=
7 | github.com/tedsuo/rata v0.0.0-20141210191936-6197c97c67a0 h1:mdzr5v22IVdKf9mYBo0ate/vDWAes0RhuL/G8NNussw=
8 | github.com/tedsuo/rata v0.0.0-20141210191936-6197c97c67a0/go.mod h1:X47ELzhOoLbfFIY0Cql9P6yo3Cdwf2CMX3FVZxRzJPc=
9 |
--------------------------------------------------------------------------------
/handlers/assets.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import "text/template"
4 |
5 | var styledTemplate = template.Must(template.New("experiment").Parse(`
6 |
7 |
8 |
114 |
115 |
116 | {{.Body}}
117 |
118 |
119 | `))
120 |
121 | type Body struct {
122 | Body string
123 | Class string
124 | }
125 |
--------------------------------------------------------------------------------
/handlers/env.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 | "os"
7 | "strings"
8 | )
9 |
10 | type Env struct {
11 | }
12 |
13 | func (p *Env) ServeHTTP(w http.ResponseWriter, r *http.Request) {
14 | formatJson := r.URL.Query().Get("json")
15 | if formatJson == "" {
16 | out := ""
17 | for _, env := range os.Environ() {
18 | kv := strings.Split(env, "=")
19 | out += "- " + kv[0] + "
"
20 | out += "- " + kv[1] + "
"
21 | }
22 | out += "
"
23 | styledTemplate.Execute(w, Body{Body: `` + out + `
`})
24 | } else {
25 | envs := [][]string{}
26 | for _, env := range os.Environ() {
27 | envs = append(envs, strings.Split(env, "="))
28 | }
29 | json.NewEncoder(w).Encode(envs)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/handlers/exit.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "os"
7 | "time"
8 |
9 | "github.com/cloudfoundry-samples/test-app/helpers"
10 | )
11 |
12 | type Exit struct {
13 | Time time.Time
14 | }
15 |
16 | func (p *Exit) ServeHTTP(w http.ResponseWriter, r *http.Request) {
17 | index, _ := helpers.FetchIndex()
18 |
19 | w.WriteHeader(http.StatusOK)
20 | styledTemplate.Execute(w, Body{
21 | Class: "goodbye",
22 | Body: fmt.Sprintf(`
23 |
24 | Shutting Down
25 |
26 |
27 | My Index Is
28 |
29 | %d
30 | Uptime: %s
31 |
32 | `, index, time.Since(p.Time)),
33 | })
34 |
35 | go func() {
36 | time.Sleep(100 * time.Millisecond)
37 | fmt.Println("Test App shutting down")
38 | os.Exit(1)
39 | }()
40 | }
41 |
--------------------------------------------------------------------------------
/handlers/handlers.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "net/http"
5 | "time"
6 |
7 | "github.com/cloudfoundry-samples/test-app/routes"
8 | "github.com/pivotal-golang/lager"
9 | "github.com/tedsuo/rata"
10 | )
11 |
12 | func New(logger lager.Logger, port string) rata.Handlers {
13 | t := time.Now()
14 | handlers := rata.Handlers{
15 | routes.Env: &Env{},
16 | routes.Hello: &Hello{Time: t},
17 | routes.Exit: &Exit{Time: t},
18 | routes.Index: &Index{},
19 | routes.Port: &Port{
20 | Port: port,
21 | },
22 | }
23 |
24 | for route, handler := range handlers {
25 | handlers[route] = &LoggingHandler{
26 | Route: route,
27 | Handler: handler,
28 | Logger: logger,
29 | }
30 | }
31 |
32 | return handlers
33 | }
34 |
35 | type LoggingHandler struct {
36 | Route string
37 | Handler http.Handler
38 | Logger lager.Logger
39 | }
40 |
41 | func (h *LoggingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
42 | session := h.Logger.Session(h.Route)
43 | session.Debug("request.begin")
44 | h.Handler.ServeHTTP(w, r)
45 | session.Debug("request.end")
46 | }
47 |
--------------------------------------------------------------------------------
/handlers/hello.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "time"
7 |
8 | "github.com/cloudfoundry-samples/test-app/helpers"
9 | )
10 |
11 | type Hello struct {
12 | Time time.Time
13 | }
14 |
15 | func (p *Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
16 | index, _ := helpers.FetchIndex()
17 |
18 | styledTemplate.Execute(w, Body{Body: fmt.Sprintf(`
19 |
20 | Test App
21 |
22 |
23 | My Index Is
24 |
25 | %d
26 | Uptime: %s
27 |
28 | `, index, time.Since(p.Time))})
29 | }
30 |
--------------------------------------------------------------------------------
/handlers/index.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 |
7 | "github.com/cloudfoundry-samples/test-app/helpers"
8 | )
9 |
10 | type Index struct {
11 | }
12 |
13 | func (_ *Index) ServeHTTP(w http.ResponseWriter, r *http.Request) {
14 | index, err := helpers.FetchIndex()
15 |
16 | if err != nil {
17 | w.Write([]byte(err.Error()))
18 | }
19 |
20 | w.Write([]byte(fmt.Sprintf("%d", index)))
21 | }
22 |
--------------------------------------------------------------------------------
/handlers/port.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | )
7 |
8 | type Port struct {
9 | Port string
10 | }
11 |
12 | func (p *Port) ServeHTTP(w http.ResponseWriter, r *http.Request) {
13 | w.Write([]byte(fmt.Sprintf("%s", p.Port)))
14 | }
15 |
--------------------------------------------------------------------------------
/helpers/fetch_index.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | "os"
5 | "strconv"
6 | )
7 |
8 | func FetchIndex() (int, error) {
9 | index := "-1"
10 |
11 | if os.Getenv("CF_INSTANCE_INDEX") != "" {
12 | index = os.Getenv("CF_INSTANCE_INDEX")
13 | } else if os.Getenv("INSTANCE_INDEX") != "" {
14 | index = os.Getenv("INSTANCE_INDEX")
15 | }
16 | return strconv.Atoi(index)
17 | }
18 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "os"
7 | "strings"
8 | "sync"
9 | "time"
10 |
11 | "github.com/cloudfoundry-samples/test-app/handlers"
12 | "github.com/cloudfoundry-samples/test-app/helpers"
13 | "github.com/cloudfoundry-samples/test-app/routes"
14 | "github.com/pivotal-golang/lager"
15 | "github.com/tedsuo/ifrit"
16 | "github.com/tedsuo/ifrit/http_server"
17 | "github.com/tedsuo/rata"
18 | )
19 |
20 | var message string
21 | var quiet bool
22 |
23 | var portsFlag = flag.String(
24 | "ports",
25 | "",
26 | "Comma delimited list of ports, where the app will be listening to",
27 | )
28 |
29 | func init() {
30 | flag.StringVar(&message, "message", "Hello", "The Message to Log and Display")
31 | flag.BoolVar(&quiet, "quiet", false, "Less Verbose Logging")
32 | flag.Parse()
33 | }
34 |
35 | func main() {
36 | flag.Parse()
37 |
38 | logger := lager.NewLogger("test-app")
39 | if quiet {
40 | logger.RegisterSink(lager.NewWriterSink(os.Stdout, lager.INFO))
41 | } else {
42 | logger.RegisterSink(lager.NewWriterSink(os.Stdout, lager.DEBUG))
43 | }
44 |
45 | ports := getServerPorts()
46 |
47 | logger.Info("test-app.starting", lager.Data{"ports": ports})
48 | index, err := helpers.FetchIndex()
49 | appName := fetchAppName()
50 | go func() {
51 | t := time.NewTicker(time.Second)
52 | for {
53 | <-t.C
54 | if err != nil {
55 | fmt.Fprintf(os.Stderr, "Failed to fetch index: %s\n", err.Error())
56 | } else {
57 | fmt.Println(fmt.Sprintf("%s. Says %s. on index: %d", appName, message, index))
58 | }
59 | }
60 | }()
61 |
62 | wg := sync.WaitGroup{}
63 | for _, port := range ports {
64 | wg.Add(1)
65 | go func(wg *sync.WaitGroup, port string) {
66 | defer wg.Done()
67 | handler, err := rata.NewRouter(routes.Routes, handlers.New(logger, port))
68 | if err != nil {
69 | logger.Fatal("router.creation.failed", err)
70 | }
71 |
72 | server := ifrit.Envoke(http_server.New(":"+port, handler))
73 | logger.Info("test-app.up", lager.Data{"port": port})
74 | err = <-server.Wait()
75 | if err != nil {
76 | logger.Error("shutting down server", err, lager.Data{"server port": port})
77 | }
78 | logger.Info("shutting down server", lager.Data{"server port": port})
79 | }(&wg, port)
80 | }
81 | wg.Wait()
82 | logger.Info("shutting latice app")
83 | }
84 |
85 | func fetchAppName() string {
86 | appName := os.Getenv("APP_NAME")
87 | if appName == "" {
88 | return "test-app"
89 | }
90 | return appName
91 | }
92 |
93 | func getServerPorts() []string {
94 | givenPorts := *portsFlag
95 | if givenPorts == "" {
96 | givenPorts = os.Getenv("PORT")
97 | }
98 | if givenPorts == "" {
99 | givenPorts = "8080"
100 | }
101 | ports := strings.Replace(givenPorts, " ", "", -1)
102 | return strings.Split(ports, ",")
103 | }
104 |
--------------------------------------------------------------------------------
/manifest.yml:
--------------------------------------------------------------------------------
1 | ---
2 | applications:
3 | - name: test-app
4 | memory: 256M
5 | instances: 1
6 | random-route: true
7 |
--------------------------------------------------------------------------------
/routes/routes.go:
--------------------------------------------------------------------------------
1 | package routes
2 |
3 | import "github.com/tedsuo/rata"
4 |
5 | const (
6 | Env = "ENV"
7 | Hello = "HELLO"
8 | Exit = "EXIT"
9 | Index = "INDEX"
10 | Port = "PORT"
11 | )
12 |
13 | var Routes = rata.Routes{
14 | {Path: "/", Method: "GET", Name: Hello},
15 | {Path: "/env", Method: "GET", Name: Env},
16 | {Path: "/exit", Method: "GET", Name: Exit},
17 | {Path: "/index", Method: "GET", Name: Index},
18 | {Path: "/port", Method: "GET", Name: Port},
19 | }
20 |
--------------------------------------------------------------------------------
/vendor/github.com/bmizerany/pat/.gitignore:
--------------------------------------------------------------------------------
1 | *.prof
2 | *.out
3 | example/example
4 |
--------------------------------------------------------------------------------
/vendor/github.com/bmizerany/pat/README.md:
--------------------------------------------------------------------------------
1 | # pat (formerly pat.go) - A Sinatra style pattern muxer for Go's net/http library
2 |
3 | ## INSTALL
4 |
5 | $ go get github.com/bmizerany/pat
6 |
7 | ## USE
8 |
9 | package main
10 |
11 | import (
12 | "io"
13 | "net/http"
14 | "github.com/bmizerany/pat"
15 | "log"
16 | )
17 |
18 | // hello world, the web server
19 | func HelloServer(w http.ResponseWriter, req *http.Request) {
20 | io.WriteString(w, "hello, "+req.URL.Query().Get(":name")+"!\n")
21 | }
22 |
23 | func main() {
24 | m := pat.New()
25 | m.Get("/hello/:name", http.HandlerFunc(HelloServer))
26 |
27 | // Register this pat with the default serve mux so that other packages
28 | // may also be exported. (i.e. /debug/pprof/*)
29 | http.Handle("/", m)
30 | err := http.ListenAndServe(":12345", nil)
31 | if err != nil {
32 | log.Fatal("ListenAndServe: ", err)
33 | }
34 | }
35 |
36 | It's that simple.
37 |
38 | For more information, see:
39 | http://godoc.org/github.com/bmizerany/pat
40 |
41 | ## CONTRIBUTORS
42 |
43 | * Keith Rarick (@krarick) - github.com/kr
44 | * Blake Mizerany (@bmizerany) - github.com/bmizerany
45 | * Evan Shaw
46 | * George Rogers
47 |
48 | ## LICENSE
49 |
50 | Copyright (C) 2012 by Keith Rarick, Blake Mizerany
51 |
52 | Permission is hereby granted, free of charge, to any person obtaining a copy
53 | of this software and associated documentation files (the "Software"), to deal
54 | in the Software without restriction, including without limitation the rights
55 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
56 | copies of the Software, and to permit persons to whom the Software is
57 | furnished to do so, subject to the following conditions:
58 |
59 | The above copyright notice and this permission notice shall be included in
60 | all copies or substantial portions of the Software.
61 |
62 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
63 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
64 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
65 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
66 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
67 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
68 | THE SOFTWARE.
69 |
--------------------------------------------------------------------------------
/vendor/github.com/bmizerany/pat/mux.go:
--------------------------------------------------------------------------------
1 | // Package pat implements a simple URL pattern muxer
2 | package pat
3 |
4 | import (
5 | "net/http"
6 | "net/url"
7 | "strings"
8 | )
9 |
10 | // PatternServeMux is an HTTP request multiplexer. It matches the URL of each
11 | // incoming request against a list of registered patterns with their associated
12 | // methods and calls the handler for the pattern that most closely matches the
13 | // URL.
14 | //
15 | // Pattern matching attempts each pattern in the order in which they were
16 | // registered.
17 | //
18 | // Patterns may contain literals or captures. Capture names start with a colon
19 | // and consist of letters A-Z, a-z, _, and 0-9. The rest of the pattern
20 | // matches literally. The portion of the URL matching each name ends with an
21 | // occurrence of the character in the pattern immediately following the name,
22 | // or a /, whichever comes first. It is possible for a name to match the empty
23 | // string.
24 | //
25 | // Example pattern with one capture:
26 | // /hello/:name
27 | // Will match:
28 | // /hello/blake
29 | // /hello/keith
30 | // Will not match:
31 | // /hello/blake/
32 | // /hello/blake/foo
33 | // /foo
34 | // /foo/bar
35 | //
36 | // Example 2:
37 | // /hello/:name/
38 | // Will match:
39 | // /hello/blake/
40 | // /hello/keith/foo
41 | // /hello/blake
42 | // /hello/keith
43 | // Will not match:
44 | // /foo
45 | // /foo/bar
46 | //
47 | // A pattern ending with a slash will get an implicit redirect to it's
48 | // non-slash version. For example: Get("/foo/", handler) will implicitly
49 | // register Get("/foo", handler). You may override it by registering
50 | // Get("/foo", anotherhandler) before the slash version.
51 | //
52 | // Retrieve the capture from the r.URL.Query().Get(":name") in a handler (note
53 | // the colon). If a capture name appears more than once, the additional values
54 | // are appended to the previous values (see
55 | // http://golang.org/pkg/net/url/#Values)
56 | //
57 | // A trivial example server is:
58 | //
59 | // package main
60 | //
61 | // import (
62 | // "io"
63 | // "net/http"
64 | // "github.com/bmizerany/pat"
65 | // "log"
66 | // )
67 | //
68 | // // hello world, the web server
69 | // func HelloServer(w http.ResponseWriter, req *http.Request) {
70 | // io.WriteString(w, "hello, "+req.URL.Query().Get(":name")+"!\n")
71 | // }
72 | //
73 | // func main() {
74 | // m := pat.New()
75 | // m.Get("/hello/:name", http.HandlerFunc(HelloServer))
76 | //
77 | // // Register this pat with the default serve mux so that other packages
78 | // // may also be exported. (i.e. /debug/pprof/*)
79 | // http.Handle("/", m)
80 | // err := http.ListenAndServe(":12345", nil)
81 | // if err != nil {
82 | // log.Fatal("ListenAndServe: ", err)
83 | // }
84 | // }
85 | //
86 | // When "Method Not Allowed":
87 | //
88 | // Pat knows what methods are allowed given a pattern and a URI. For
89 | // convenience, PatternServeMux will add the Allow header for requests that
90 | // match a pattern for a method other than the method requested and set the
91 | // Status to "405 Method Not Allowed".
92 | type PatternServeMux struct {
93 | handlers map[string][]*patHandler
94 | }
95 |
96 | // New returns a new PatternServeMux.
97 | func New() *PatternServeMux {
98 | return &PatternServeMux{make(map[string][]*patHandler)}
99 | }
100 |
101 | // ServeHTTP matches r.URL.Path against its routing table using the rules
102 | // described above.
103 | func (p *PatternServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
104 | for _, ph := range p.handlers[r.Method] {
105 | if params, ok := ph.try(r.URL.Path); ok {
106 | if len(params) > 0 {
107 | r.URL.RawQuery = url.Values(params).Encode() + "&" + r.URL.RawQuery
108 | }
109 | ph.ServeHTTP(w, r)
110 | return
111 | }
112 | }
113 |
114 | allowed := make([]string, 0, len(p.handlers))
115 | for meth, handlers := range p.handlers {
116 | if meth == r.Method {
117 | continue
118 | }
119 |
120 | for _, ph := range handlers {
121 | if _, ok := ph.try(r.URL.Path); ok {
122 | allowed = append(allowed, meth)
123 | }
124 | }
125 | }
126 |
127 | if len(allowed) == 0 {
128 | http.NotFound(w, r)
129 | return
130 | }
131 |
132 | w.Header().Add("Allow", strings.Join(allowed, ", "))
133 | http.Error(w, "Method Not Allowed", 405)
134 | }
135 |
136 | // Head will register a pattern with a handler for HEAD requests.
137 | func (p *PatternServeMux) Head(pat string, h http.Handler) {
138 | p.Add("HEAD", pat, h)
139 | }
140 |
141 | // Get will register a pattern with a handler for GET requests.
142 | // It also registers pat for HEAD requests. If this needs to be overridden, use
143 | // Head before Get with pat.
144 | func (p *PatternServeMux) Get(pat string, h http.Handler) {
145 | p.Add("HEAD", pat, h)
146 | p.Add("GET", pat, h)
147 | }
148 |
149 | // Post will register a pattern with a handler for POST requests.
150 | func (p *PatternServeMux) Post(pat string, h http.Handler) {
151 | p.Add("POST", pat, h)
152 | }
153 |
154 | // Put will register a pattern with a handler for PUT requests.
155 | func (p *PatternServeMux) Put(pat string, h http.Handler) {
156 | p.Add("PUT", pat, h)
157 | }
158 |
159 | // Del will register a pattern with a handler for DELETE requests.
160 | func (p *PatternServeMux) Del(pat string, h http.Handler) {
161 | p.Add("DELETE", pat, h)
162 | }
163 |
164 | // Options will register a pattern with a handler for OPTIONS requests.
165 | func (p *PatternServeMux) Options(pat string, h http.Handler) {
166 | p.Add("OPTIONS", pat, h)
167 | }
168 |
169 | // Add will register a pattern with a handler for meth requests.
170 | func (p *PatternServeMux) Add(meth, pat string, h http.Handler) {
171 | p.handlers[meth] = append(p.handlers[meth], &patHandler{pat, h})
172 |
173 | n := len(pat)
174 | if n > 0 && pat[n-1] == '/' {
175 | p.Add(meth, pat[:n-1], http.RedirectHandler(pat, http.StatusMovedPermanently))
176 | }
177 | }
178 |
179 | // Tail returns the trailing string in path after the final slash for a pat ending with a slash.
180 | //
181 | // Examples:
182 | //
183 | // Tail("/hello/:title/", "/hello/mr/mizerany") == "mizerany"
184 | // Tail("/:a/", "/x/y/z") == "y/z"
185 | //
186 | func Tail(pat, path string) string {
187 | var i, j int
188 | for i < len(path) {
189 | switch {
190 | case j >= len(pat):
191 | if pat[len(pat)-1] == '/' {
192 | return path[i:]
193 | }
194 | return ""
195 | case pat[j] == ':':
196 | var nextc byte
197 | _, nextc, j = match(pat, isAlnum, j+1)
198 | _, _, i = match(path, matchPart(nextc), i)
199 | case path[i] == pat[j]:
200 | i++
201 | j++
202 | default:
203 | return ""
204 | }
205 | }
206 | return ""
207 | }
208 |
209 | type patHandler struct {
210 | pat string
211 | http.Handler
212 | }
213 |
214 | func (ph *patHandler) try(path string) (url.Values, bool) {
215 | p := make(url.Values)
216 | var i, j int
217 | for i < len(path) {
218 | switch {
219 | case j >= len(ph.pat):
220 | if ph.pat != "/" && len(ph.pat) > 0 && ph.pat[len(ph.pat)-1] == '/' {
221 | return p, true
222 | }
223 | return nil, false
224 | case ph.pat[j] == ':':
225 | var name, val string
226 | var nextc byte
227 | name, nextc, j = match(ph.pat, isAlnum, j+1)
228 | val, _, i = match(path, matchPart(nextc), i)
229 | p.Add(":"+name, val)
230 | case path[i] == ph.pat[j]:
231 | i++
232 | j++
233 | default:
234 | return nil, false
235 | }
236 | }
237 | if j != len(ph.pat) {
238 | return nil, false
239 | }
240 | return p, true
241 | }
242 |
243 | func matchPart(b byte) func(byte) bool {
244 | return func(c byte) bool {
245 | return c != b && c != '/'
246 | }
247 | }
248 |
249 | func match(s string, f func(byte) bool, i int) (matched string, next byte, j int) {
250 | j = i
251 | for j < len(s) && f(s[j]) {
252 | j++
253 | }
254 | if j < len(s) {
255 | next = s[j]
256 | }
257 | return s[i:j], next, j
258 | }
259 |
260 | func isAlpha(ch byte) bool {
261 | return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
262 | }
263 |
264 | func isDigit(ch byte) bool {
265 | return '0' <= ch && ch <= '9'
266 | }
267 |
268 | func isAlnum(ch byte) bool {
269 | return isAlpha(ch) || isDigit(ch)
270 | }
271 |
--------------------------------------------------------------------------------
/vendor/github.com/pivotal-golang/lager/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/vendor/github.com/pivotal-golang/lager/README.md:
--------------------------------------------------------------------------------
1 | lager
2 | =====
3 |
4 | Lager is a logging library for go.
5 |
6 | ## Usage
7 |
8 | Instantiate a logger with the name of your component.
9 |
10 | ```go
11 | import (
12 | "github.com/pivotal-golang/lager"
13 | )
14 |
15 | logger := lager.NewLogger("my-app")
16 | ```
17 |
18 | ### Sinks
19 |
20 | Lager can write logs to a variety of destinations. You can specify the destinations
21 | using Lager sinks:
22 |
23 | To write to an arbitrary `Writer` object:
24 |
25 | ```go
26 | logger.RegisterSink(lager.NewWriterSink(myWriter, lager.INFO))
27 | ```
28 |
29 | ### Emitting logs
30 |
31 | Lager supports the usual level-based logging, with an optional argument for arbitrary key-value data.
32 |
33 | ```go
34 | logger.Info("doing-stuff", logger.Data{
35 | "informative": true,
36 | })
37 | ```
38 |
39 | output:
40 | ```json
41 | { "source": "my-app", "message": "doing-stuff", "data": { "informative": true }, "timestamp": 1232345, "log_level": 1 }
42 | ```
43 |
44 | Error messages also take an `Error` object:
45 |
46 | ```go
47 | logger.Error("failed-to-do-stuff", errors.New("Something went wrong"))
48 | ```
49 |
50 | output:
51 | ```json
52 | { "source": "my-app", "message": "failed-to-do-stuff", "data": { "error": "Something went wrong" }, "timestamp": 1232345, "log_level": 1 }
53 | ```
54 |
55 | ### Sessions
56 |
57 | You can avoid repetition of contextual data using 'Sessions':
58 |
59 | ```go
60 |
61 | contextualLogger := logger.Session("my-task", logger.Data{
62 | "request-id": 5,
63 | })
64 |
65 | contextualLogger.Info("my-action")
66 | ```
67 |
68 | output:
69 |
70 | ```json
71 | { "source": "my-app", "message": "my-task.my-action", "data": { "request-id": 5 }, "timestamp": 1232345, "log_level": 1 }
72 | ```
73 |
74 | ## License
75 |
76 | Lager is [Apache 2.0](https://github.com/pivotal-golang/lager/blob/master/LICENSE) licensed.
77 |
--------------------------------------------------------------------------------
/vendor/github.com/pivotal-golang/lager/logger.go:
--------------------------------------------------------------------------------
1 | package lager
2 |
3 | import (
4 | "fmt"
5 | "runtime"
6 | "sync/atomic"
7 | "time"
8 | )
9 |
10 | const STACK_TRACE_BUFFER_SIZE = 1024 * 100
11 |
12 | type Logger interface {
13 | RegisterSink(Sink)
14 | Session(task string, data ...Data) Logger
15 | SessionName() string
16 | Debug(action string, data ...Data)
17 | Info(action string, data ...Data)
18 | Error(action string, err error, data ...Data)
19 | Fatal(action string, err error, data ...Data)
20 | WithData(Data) Logger
21 | }
22 |
23 | type logger struct {
24 | component string
25 | task string
26 | sinks []Sink
27 | sessionID string
28 | nextSession uint64
29 | data Data
30 | }
31 |
32 | func NewLogger(component string) Logger {
33 | return &logger{
34 | component: component,
35 | task: component,
36 | sinks: []Sink{},
37 | data: Data{},
38 | }
39 | }
40 |
41 | func (l *logger) RegisterSink(sink Sink) {
42 | l.sinks = append(l.sinks, sink)
43 | }
44 |
45 | func (l *logger) SessionName() string {
46 | return l.task
47 | }
48 |
49 | func (l *logger) Session(task string, data ...Data) Logger {
50 | sid := atomic.AddUint64(&l.nextSession, 1)
51 |
52 | var sessionIDstr string
53 |
54 | if l.sessionID != "" {
55 | sessionIDstr = fmt.Sprintf("%s.%d", l.sessionID, sid)
56 | } else {
57 | sessionIDstr = fmt.Sprintf("%d", sid)
58 | }
59 |
60 | return &logger{
61 | component: l.component,
62 | task: fmt.Sprintf("%s.%s", l.task, task),
63 | sinks: l.sinks,
64 | sessionID: sessionIDstr,
65 | data: l.baseData(data...),
66 | }
67 | }
68 |
69 | func (l *logger) WithData(data Data) Logger {
70 | return &logger{
71 | component: l.component,
72 | task: l.task,
73 | sinks: l.sinks,
74 | sessionID: l.sessionID,
75 | data: l.baseData(data),
76 | }
77 | }
78 |
79 | func (l *logger) Debug(action string, data ...Data) {
80 | log := LogFormat{
81 | Timestamp: currentTimestamp(),
82 | Source: l.component,
83 | Message: fmt.Sprintf("%s.%s", l.task, action),
84 | LogLevel: DEBUG,
85 | Data: l.baseData(data...),
86 | }
87 |
88 | for _, sink := range l.sinks {
89 | sink.Log(DEBUG, log.ToJSON())
90 | }
91 | }
92 |
93 | func (l *logger) Info(action string, data ...Data) {
94 | log := LogFormat{
95 | Timestamp: currentTimestamp(),
96 | Source: l.component,
97 | Message: fmt.Sprintf("%s.%s", l.task, action),
98 | LogLevel: INFO,
99 | Data: l.baseData(data...),
100 | }
101 |
102 | for _, sink := range l.sinks {
103 | sink.Log(INFO, log.ToJSON())
104 | }
105 | }
106 |
107 | func (l *logger) Error(action string, err error, data ...Data) {
108 | logData := l.baseData(data...)
109 |
110 | if err != nil {
111 | logData["error"] = err.Error()
112 | }
113 |
114 | log := LogFormat{
115 | Timestamp: currentTimestamp(),
116 | Source: l.component,
117 | Message: fmt.Sprintf("%s.%s", l.task, action),
118 | LogLevel: ERROR,
119 | Data: logData,
120 | }
121 |
122 | for _, sink := range l.sinks {
123 | sink.Log(ERROR, log.ToJSON())
124 | }
125 | }
126 |
127 | func (l *logger) Fatal(action string, err error, data ...Data) {
128 | logData := l.baseData(data...)
129 |
130 | stackTrace := make([]byte, STACK_TRACE_BUFFER_SIZE)
131 | stackSize := runtime.Stack(stackTrace, false)
132 | stackTrace = stackTrace[:stackSize]
133 |
134 | if err != nil {
135 | logData["error"] = err.Error()
136 | }
137 |
138 | logData["trace"] = string(stackTrace)
139 |
140 | log := LogFormat{
141 | Timestamp: currentTimestamp(),
142 | Source: l.component,
143 | Message: fmt.Sprintf("%s.%s", l.task, action),
144 | LogLevel: FATAL,
145 | Data: logData,
146 | }
147 |
148 | for _, sink := range l.sinks {
149 | sink.Log(FATAL, log.ToJSON())
150 | }
151 |
152 | panic(err)
153 | }
154 |
155 | func (l *logger) baseData(givenData ...Data) Data {
156 | data := Data{}
157 |
158 | for k, v := range l.data {
159 | data[k] = v
160 | }
161 |
162 | if len(givenData) > 0 {
163 | for _, dataArg := range givenData {
164 | for key, val := range dataArg {
165 | data[key] = val
166 | }
167 | }
168 | }
169 |
170 | if l.sessionID != "" {
171 | data["session"] = l.sessionID
172 | }
173 |
174 | return data
175 | }
176 |
177 | func currentTimestamp() string {
178 | return fmt.Sprintf("%.9f", float64(time.Now().UnixNano())/1e9)
179 | }
180 |
--------------------------------------------------------------------------------
/vendor/github.com/pivotal-golang/lager/models.go:
--------------------------------------------------------------------------------
1 | package lager
2 |
3 | import "encoding/json"
4 |
5 | type LogLevel int
6 |
7 | const (
8 | DEBUG LogLevel = iota
9 | INFO
10 | ERROR
11 | FATAL
12 | )
13 |
14 | type Data map[string]interface{}
15 |
16 | type LogFormat struct {
17 | Timestamp string `json:"timestamp"`
18 | Source string `json:"source"`
19 | Message string `json:"message"`
20 | LogLevel LogLevel `json:"log_level"`
21 | Data Data `json:"data"`
22 | }
23 |
24 | func (log LogFormat) ToJSON() []byte {
25 | content, err := json.Marshal(log)
26 | if err != nil {
27 | panic(err)
28 | }
29 | return content
30 | }
31 |
--------------------------------------------------------------------------------
/vendor/github.com/pivotal-golang/lager/reconfigurable_sink.go:
--------------------------------------------------------------------------------
1 | package lager
2 |
3 | import "sync/atomic"
4 |
5 | type ReconfigurableSink struct {
6 | sink Sink
7 |
8 | minLogLevel int32
9 | }
10 |
11 | func NewReconfigurableSink(sink Sink, initialMinLogLevel LogLevel) *ReconfigurableSink {
12 | return &ReconfigurableSink{
13 | sink: sink,
14 |
15 | minLogLevel: int32(initialMinLogLevel),
16 | }
17 | }
18 |
19 | func (sink *ReconfigurableSink) Log(level LogLevel, log []byte) {
20 | minLogLevel := LogLevel(atomic.LoadInt32(&sink.minLogLevel))
21 |
22 | if level < minLogLevel {
23 | return
24 | }
25 |
26 | sink.sink.Log(level, log)
27 | }
28 |
29 | func (sink *ReconfigurableSink) SetMinLevel(level LogLevel) {
30 | atomic.StoreInt32(&sink.minLogLevel, int32(level))
31 | }
32 |
33 | func (sink *ReconfigurableSink) GetMinLevel() LogLevel {
34 | return LogLevel(atomic.LoadInt32(&sink.minLogLevel))
35 | }
36 |
--------------------------------------------------------------------------------
/vendor/github.com/pivotal-golang/lager/writer_sink.go:
--------------------------------------------------------------------------------
1 | package lager
2 |
3 | import (
4 | "io"
5 | "sync"
6 | )
7 |
8 | const logBufferSize = 1024
9 |
10 | // A Sink represents a write destination for a Logger. It provides
11 | // a thread-safe interface for writing logs
12 | type Sink interface {
13 | //Log to the sink. Best effort -- no need to worry about errors.
14 | Log(level LogLevel, payload []byte)
15 | }
16 |
17 | type writerSink struct {
18 | writer io.Writer
19 | minLogLevel LogLevel
20 | writeL *sync.Mutex
21 | }
22 |
23 | func NewWriterSink(writer io.Writer, minLogLevel LogLevel) Sink {
24 | return &writerSink{
25 | writer: writer,
26 | minLogLevel: minLogLevel,
27 | writeL: new(sync.Mutex),
28 | }
29 | }
30 |
31 | func (sink *writerSink) Log(level LogLevel, log []byte) {
32 | if level < sink.minLogLevel {
33 | return
34 | }
35 |
36 | sink.writeL.Lock()
37 | sink.writer.Write(log)
38 | sink.writer.Write([]byte("\n"))
39 | sink.writeL.Unlock()
40 | }
41 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/ifrit/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
4 | *.o
5 | *.a
6 | *.so
7 |
8 | # Folders
9 | _obj
10 | _test
11 |
12 | *.exe
13 |
14 | *.iml
15 | *.zpi
16 | *.zwi
17 |
18 | *.go-e
19 |
20 | # Log files
21 | *.log
22 |
23 | # IDE
24 | .idea/
25 |
26 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/ifrit/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Theodore Young
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all 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,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/ifrit/README.md:
--------------------------------------------------------------------------------
1 | # Ifrit - a process model for go.
2 |
3 | Ifrit is a small set of interfaces for composing single-purpose units of work
4 | into larger programs. Users divide their program into single purpose units of
5 | work, each of which implements the `Runner` interface Each `Runner` can be
6 | invoked to create a `Process` which can be monitored and signaled to stop.
7 |
8 | The name Ifrit comes from a type of daemon in arabic folklore. It's a play on
9 | the unix term 'daemon' to indicate a process that is managed by the init system.
10 |
11 | Ifrit ships with a standard library which contains packages for common
12 | processes - http servers, integration test helpers - alongside packages which
13 | model process supervision and orchestration. These packages can be combined to
14 | form complex servers which start and shutdown cleanly.
15 |
16 | The advantage of small, single-responsibility processes is that they are simple,
17 | and thus can be made reliable. Ifrit's interfaces are designed to be free
18 | of race conditions and edge cases, allowing larger orcestrated process to also
19 | be made reliable. The overall effect is less code and more reliability as your
20 | system grows with grace.
21 |
22 | The full documentation is written in godoc, and can be found at:
23 |
24 | http://godoc.org/github.com/tedsuo/ifrit
25 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/ifrit/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | A process model for go.
3 |
4 | Ifrit is a small set of interfaces for composing single-purpose units of work
5 | into larger programs. Users divide their program into single purpose units of
6 | work, each of which implements the `Runner` interface Each `Runner` can be
7 | invoked to create a `Process` which can be monitored and signaled to stop.
8 |
9 | The name Ifrit comes from a type of daemon in arabic folklore. It's a play on
10 | the unix term 'daemon' to indicate a process that is managed by the init system.
11 |
12 | Ifrit ships with a standard library which contains packages for common
13 | processes - http servers, integration test helpers - alongside packages which
14 | model process supervision and orchestration. These packages can be combined to
15 | form complex servers which start and shutdown cleanly.
16 |
17 | The advantage of small, single-responsibility processes is that they are simple,
18 | and thus can be made reliable. Ifrit's interfaces are designed to be free
19 | of race conditions and edge cases, allowing larger orcestrated process to also
20 | be made reliable. The overall effect is less code and more reliability as your
21 | system grows with grace.
22 | */
23 | package ifrit
24 |
25 |
26 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/ifrit/http_server/http_server.go:
--------------------------------------------------------------------------------
1 | package http_server
2 |
3 | import (
4 | "net"
5 | "net/http"
6 | "os"
7 | "sync"
8 |
9 | "github.com/tedsuo/ifrit"
10 | )
11 |
12 | type httpServer struct {
13 | address string
14 | handler http.Handler
15 |
16 | connectionWaitGroup *sync.WaitGroup
17 | inactiveConnections map[net.Conn]struct{}
18 | inactiveConnectionsMu *sync.Mutex
19 | stoppingChan chan struct{}
20 | }
21 |
22 | func New(address string, handler http.Handler) ifrit.Runner {
23 | return &httpServer{
24 | address: address,
25 | handler: handler,
26 | }
27 | }
28 |
29 | func (s *httpServer) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
30 | s.connectionWaitGroup = new(sync.WaitGroup)
31 | s.inactiveConnectionsMu = new(sync.Mutex)
32 | s.inactiveConnections = make(map[net.Conn]struct{})
33 | s.stoppingChan = make(chan struct{})
34 |
35 | server := http.Server{
36 | Handler: s.handler,
37 | ConnState: func(conn net.Conn, state http.ConnState) {
38 | switch state {
39 | case http.StateNew:
40 | s.connectionWaitGroup.Add(1)
41 | s.addInactiveConnection(conn)
42 |
43 | case http.StateIdle:
44 | s.addInactiveConnection(conn)
45 |
46 | case http.StateActive:
47 | s.removeInactiveConnection(conn)
48 |
49 | case http.StateHijacked, http.StateClosed:
50 | s.removeInactiveConnection(conn)
51 | s.connectionWaitGroup.Done()
52 | }
53 | },
54 | }
55 |
56 | listener, err := net.Listen("tcp", s.address)
57 | if err != nil {
58 | return err
59 | }
60 |
61 | serverErrChan := make(chan error, 1)
62 | go func() {
63 | serverErrChan <- server.Serve(listener)
64 | }()
65 |
66 | close(ready)
67 |
68 | for {
69 | select {
70 | case err = <-serverErrChan:
71 | return err
72 |
73 | case <-signals:
74 | close(s.stoppingChan)
75 |
76 | listener.Close()
77 |
78 | s.inactiveConnectionsMu.Lock()
79 | for c := range s.inactiveConnections {
80 | c.Close()
81 | }
82 | s.inactiveConnectionsMu.Unlock()
83 |
84 | s.connectionWaitGroup.Wait()
85 | return nil
86 | }
87 | }
88 | }
89 |
90 | func (s *httpServer) addInactiveConnection(conn net.Conn) {
91 | select {
92 | case <-s.stoppingChan:
93 | conn.Close()
94 | default:
95 | s.inactiveConnectionsMu.Lock()
96 | s.inactiveConnections[conn] = struct{}{}
97 | s.inactiveConnectionsMu.Unlock()
98 | }
99 | }
100 |
101 | func (s *httpServer) removeInactiveConnection(conn net.Conn) {
102 | s.inactiveConnectionsMu.Lock()
103 | delete(s.inactiveConnections, conn)
104 | s.inactiveConnectionsMu.Unlock()
105 | }
106 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/ifrit/process.go:
--------------------------------------------------------------------------------
1 | package ifrit
2 |
3 | import "os"
4 |
5 | /*
6 | A Process represents a Runner that has been started. It is safe to call any
7 | method on a Process even after the Process has exited.
8 | */
9 | type Process interface {
10 | // Ready returns a channel which will close once the runner is active
11 | Ready() <-chan struct{}
12 |
13 | // Wait returns a channel that will emit a single error once the Process exits.
14 | Wait() <-chan error
15 |
16 | // Signal sends a shutdown signal to the Process. It does not block.
17 | Signal(os.Signal)
18 | }
19 |
20 | /*
21 | Invoke executes a Runner and returns a Process once the Runner is ready. Waiting
22 | for ready allows program initializtion to be scripted in a procedural manner.
23 | To orcestrate the startup and monitoring of multiple Processes, please refer to
24 | the ifrit/grouper package.
25 | */
26 | func Invoke(r Runner) Process {
27 | p := Background(r)
28 |
29 | select {
30 | case <-p.Ready():
31 | case <-p.Wait():
32 | }
33 |
34 | return p
35 | }
36 |
37 | /*
38 | Envoke is deprecated in favor of Invoke, on account of it not being a real word.
39 | */
40 | func Envoke(r Runner) Process {
41 | return Invoke(r)
42 | }
43 |
44 | /*
45 | Background executes a Runner and returns a Process immediately, without waiting.
46 | */
47 | func Background(r Runner) Process {
48 | p := newProcess(r)
49 | go p.run()
50 | return p
51 | }
52 |
53 | type process struct {
54 | runner Runner
55 | signals chan os.Signal
56 | ready chan struct{}
57 | exited chan struct{}
58 | exitStatus error
59 | }
60 |
61 | func newProcess(runner Runner) *process {
62 | return &process{
63 | runner: runner,
64 | signals: make(chan os.Signal),
65 | ready: make(chan struct{}),
66 | exited: make(chan struct{}),
67 | }
68 | }
69 |
70 | func (p *process) run() {
71 | p.exitStatus = p.runner.Run(p.signals, p.ready)
72 | close(p.exited)
73 | }
74 |
75 | func (p *process) Ready() <-chan struct{} {
76 | return p.ready
77 | }
78 |
79 | func (p *process) Wait() <-chan error {
80 | exitChan := make(chan error, 1)
81 |
82 | go func() {
83 | <-p.exited
84 | exitChan <- p.exitStatus
85 | }()
86 |
87 | return exitChan
88 | }
89 |
90 | func (p *process) Signal(signal os.Signal) {
91 | go func() {
92 | select {
93 | case p.signals <- signal:
94 | case <-p.exited:
95 | }
96 | }()
97 | }
98 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/ifrit/runner.go:
--------------------------------------------------------------------------------
1 | package ifrit
2 |
3 | import "os"
4 |
5 | /*
6 | A Runner defines the contents of a Process. A Runner implementation performs an
7 | aribtrary unit of work, while waiting for a shutdown signal. The unit of work
8 | should avoid any orchestration. Instead, it should be broken down into simpler
9 | units of work in seperate Runners, which are then orcestrated by the ifrit
10 | standard library.
11 |
12 | An implementation of Runner has the following responibilities:
13 |
14 | - setup within a finite amount of time.
15 | - close the ready channel when setup is complete.
16 | - once ready, perform the unit of work, which may be infinite.
17 | - respond to shutdown signals by exiting within a finite amount of time.
18 | - return nil if shutdown is successful.
19 | - return an error if an exception has prevented a clean shutdown.
20 |
21 | By default, Runners are not considered restartable; Run will only be called once.
22 | See the ifrit/restart package for details on restartable Runners.
23 | */
24 | type Runner interface {
25 | Run(signals <-chan os.Signal, ready chan<- struct{}) error
26 | }
27 |
28 | /*
29 | The RunFunc type is an adapter to allow the use of ordinary functions as Runners.
30 | If f is a function that matches the Run method signature, RunFunc(f) is a Runner
31 | object that calls f.
32 | */
33 | type RunFunc func(signals <-chan os.Signal, ready chan<- struct{}) error
34 |
35 | func (r RunFunc) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
36 | return r(signals, ready)
37 | }
38 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/rata/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Ted Young
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all 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,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/rata/README.md:
--------------------------------------------------------------------------------
1 | # Rata: It's a smat rata. Wicked smat.
2 | Rata is a router with Pat-style path patterns, plus more.
3 |
4 | API Docs: https://godoc.org/github.com/tedsuo/rata
5 |
6 | Package rata provides three things: Routes, a Router, and a RequestGenerator.
7 |
8 | Routes are structs that define which Method and Path each associated http handler
9 | should respond to. Unlike many router implementations, the routes and the handlers
10 | are defined separately. This allows for the routes to be reused in multiple contexts.
11 | For example, a proxy server and a backend server can be created by having one set of
12 | Routes, but two sets of Handlers (one handler that proxies, another that serves the
13 | request). Likewise, your client code can use the routes with the RequestGenerator to
14 | create requests that use the same routes. Then, if the routes change, unit tests in
15 | the client and proxy service will warn you of the problem. This contract helps components
16 | stay in sync while relying less on integration tests.
17 |
18 | For example, let's imagine that you want to implement a "pet" resource that allows
19 | you to view, create, update, and delete which pets people own. Also, you would
20 | like to include the owner_id and pet_id as part of the URL path.
21 |
22 | First off, the routes might look like this:
23 | ```go
24 | petRoutes := rata.Routes{
25 | {Name: "get_pet", Method: "GET", Path: "/people/:owner_id/pets/:pet_id"},
26 | {Name: "create_pet", Method: "POST", Path: "/people/:owner_id/pets"},
27 | {Name: "update_pet", Method: "PUT", Path: "/people/:owner_id/pets/:pet_id"},
28 | {Name: "delete_pet", Method: "DELETE", Path: "/people/:owner_id/pets/:pet_id"},
29 | }
30 | ```
31 |
32 | On the server, create a matching set of http handlers, one for each route:
33 | ```go
34 | petHandlers := rata.Handlers{
35 | "get_pet": newGetPetHandler(),
36 | "create_pet": newCreatePetHandler(),
37 | "update_pet": newUpdatePetHandler(),
38 | "delete_pet": newDeletePetHandler()
39 | }
40 | ```
41 |
42 | You can create a router by mixing the routes and handlers together:
43 | ```go
44 | router, err := rata.NewRouter(petRoutes, petHandlers)
45 | if err != nil {
46 | panic(err)
47 | }
48 |
49 | // The router is just an http.Handler, so it can be used to create a server in the usual fashion:
50 | server := httptest.NewServer(router)
51 | ```
52 |
53 | Meanwhile, on the client side, you can create a request generator:
54 | ```go
55 | requestGenerator := rata.NewRequestGenerator(server.URL, petRoutes)
56 |
57 | // You can use the request generator to ensure you are creating a valid request:
58 | req, err := requestGenerator.CreateRequest("get_pet", rata.Params{"owner_id": "123", "pet_id": "5"}, nil)
59 |
60 | // The generated request can be used like any other http.Request object:
61 | res, err := http.DefaultClient.Do(req)
62 | ```
63 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/rata/VERSION:
--------------------------------------------------------------------------------
1 | 0.3.0
2 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/rata/docs.go:
--------------------------------------------------------------------------------
1 | /*
2 | Package rata provides three things: Routes, a Router, and a RequestGenerator.
3 |
4 | Routes are structs that define which Method and Path each associated http handler
5 | should respond to. Unlike many router implementations, the routes and the handlers
6 | are defined separately. This allows for the routes to be reused in multiple contexts.
7 | For example, a proxy server and a backend server can be created by having one set of
8 | Routes, but two sets of Handlers (one handler that proxies, another that serves the
9 | request). Likewise, your client code can use the routes with the RequestGenerator to
10 | create requests that use the same routes. Then, if the routes change, unit tests in
11 | the client and proxy service will warn you of the problem. This contract helps components
12 | stay in sync while relying less on integration tests.
13 |
14 | For example, let's imagine that you want to implement a "pet" resource that allows
15 | you to view, create, update, and delete which pets people own. Also, you would
16 | like to include the owner_id and pet_id as part of the URL path.
17 |
18 | First off, the routes might look like this:
19 | petRoutes := rata.Routes{
20 | {Name: "get_pet", Method: "GET", Path: "/people/:owner_id/pets/:pet_id"},
21 | {Name: "create_pet", Method: "POST", Path: "/people/:owner_id/pets"},
22 | {Name: "update_pet", Method: "PUT", Path: "/people/:owner_id/pets/:pet_id"},
23 | {Name: "delete_pet", Method: "DELETE", Path: "/people/:owner_id/pets/:pet_id"},
24 | }
25 |
26 |
27 | On the server, create a matching set of http handlers, one for each route:
28 | handlers := rata.Handlers{
29 | "get_pet": newGetPetHandler(),
30 | "create_pet": newCreatePetHandler(),
31 | "update_pet": newUpdatePetHandler(),
32 | "delete_pet": newDeletePetHandler()
33 | }
34 |
35 | You can create a router by mixing the routes and handlers together:
36 | router, err := rata.NewRouter(petRoutes, handlers)
37 | if err != nil {
38 | panic(err)
39 | }
40 |
41 | The router is just an http.Handler, so it can be used to create a server in the usual fashion:
42 | server := httptest.NewServer(router)
43 |
44 | Meanwhile, on the client side, you can create a request generator:
45 | requestGenerator := rata.NewRequestGenerator(server.URL, petRoutes)
46 |
47 | You can use the request generator to ensure you are creating a valid request:
48 | req, err := requestGenerator.CreateRequest("get_pet", rata.Params{"owner_id": "123", "pet_id": "5"}, nil)
49 |
50 | The generated request can be used like any other http.Request object:
51 | res, err := http.DefaultClient.Do(req)
52 | */
53 | package rata
54 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/rata/requests.go:
--------------------------------------------------------------------------------
1 | package rata
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "net/http"
7 | "strings"
8 | )
9 |
10 | // RequestGenerator creates http.Request objects with the correct path and method
11 | // pre-filled for the given route object. You can also set the the host and,
12 | // optionally, any headers you would like included with every request.
13 | type RequestGenerator struct {
14 | Header http.Header
15 | host string
16 | routes Routes
17 | }
18 |
19 | // NewRequestGenerator creates a RequestGenerator for a given host and route set.
20 | // Host is of the form "http://example.com".
21 | func NewRequestGenerator(host string, routes Routes) *RequestGenerator {
22 | return &RequestGenerator{
23 | Header: make(http.Header),
24 | host: host,
25 | routes: routes,
26 | }
27 | }
28 |
29 | // CreateRequest creates a new http Request for the matching handler. If the
30 | // request cannot be created, either because the handler does not exist or because
31 | // the given params do not match the params the route requires, then CreateRequest
32 | // returns an error.
33 | func (r *RequestGenerator) CreateRequest(
34 | name string,
35 | params Params,
36 | body io.Reader,
37 | ) (*http.Request, error) {
38 | route, ok := r.routes.FindRouteByName(name)
39 | if !ok {
40 | return &http.Request{}, fmt.Errorf("No route exists with the name %s", name)
41 | }
42 | path, err := route.CreatePath(params)
43 | if err != nil {
44 | return &http.Request{}, err
45 | }
46 |
47 | url := r.host + "/" + strings.TrimLeft(path, "/")
48 |
49 | req, err := http.NewRequest(route.Method, url, body)
50 | if err != nil {
51 | return &http.Request{}, err
52 | }
53 |
54 | for key, values := range r.Header {
55 | req.Header[key] = make([]string, len(values))
56 | copy(req.Header[key], values)
57 | }
58 |
59 | return req, nil
60 | }
61 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/rata/router.go:
--------------------------------------------------------------------------------
1 | package rata
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "strings"
7 |
8 | "github.com/bmizerany/pat"
9 | )
10 |
11 | // Handlers map route names to http.Handler objects. Each Handler key must
12 | // match a route Name in the Routes collection.
13 | type Handlers map[string]http.Handler
14 |
15 | // NewRouter combines a set of Routes with their corresponding Handlers to
16 | // produce a http request multiplexer (AKA a "router"). If any route does
17 | // not have a matching handler, an error occurs.
18 | func NewRouter(routes Routes, handlers Handlers) (http.Handler, error) {
19 | p := pat.New()
20 | for _, route := range routes {
21 | handler, ok := handlers[route.Name]
22 | if !ok {
23 | return nil, fmt.Errorf("missing handler %s", route.Name)
24 | }
25 | switch strings.ToUpper(route.Method) {
26 | case "GET":
27 | p.Get(route.Path, handler)
28 | case "POST":
29 | p.Post(route.Path, handler)
30 | case "PUT":
31 | p.Put(route.Path, handler)
32 | case "DELETE":
33 | p.Del(route.Path, handler)
34 | default:
35 | return nil, fmt.Errorf("invalid verb: %s", route.Method)
36 | }
37 | }
38 | return p, nil
39 | }
40 |
--------------------------------------------------------------------------------
/vendor/github.com/tedsuo/rata/routes.go:
--------------------------------------------------------------------------------
1 | package rata
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "net/url"
7 | "runtime"
8 | "strings"
9 | )
10 |
11 | // Params map path keys to values. For example, if your route has the path pattern:
12 | // /person/:person_id/pets/:pet_type
13 | // Then a correct Params map would lool like:
14 | // router.Params{
15 | // "person_id": "123",
16 | // "pet_type": "cats",
17 | // }
18 | type Params map[string]string
19 |
20 | // A Route defines properties of an HTTP endpoint. At runtime, the router will
21 | // associate each Route with a http.Handler object, and use the Route properties
22 | // to determine which Handler should be invoked.
23 | //
24 | // Currently, the properties used for matching are Method and Path.
25 | //
26 | // Method can be one of the following:
27 | // GET PUT POST DELETE
28 | //
29 | // Path conforms to Pat-style pattern matching. The following docs are taken from
30 | // http://godoc.org/github.com/bmizerany/pat#PatternServeMux
31 | //
32 | // Path Patterns may contain literals or captures. Capture names start with a colon
33 | // and consist of letters A-Z, a-z, _, and 0-9. The rest of the pattern
34 | // matches literally. The portion of the URL matching each name ends with an
35 | // occurrence of the character in the pattern immediately following the name,
36 | // or a /, whichever comes first. It is possible for a name to match the empty
37 | // string.
38 | //
39 | // Example pattern with one capture:
40 | // /hello/:name
41 | // Will match:
42 | // /hello/blake
43 | // /hello/keith
44 | // Will not match:
45 | // /hello/blake/
46 | // /hello/blake/foo
47 | // /foo
48 | // /foo/bar
49 | //
50 | // Example 2:
51 | // /hello/:name/
52 | // Will match:
53 | // /hello/blake/
54 | // /hello/keith/foo
55 | // /hello/blake
56 | // /hello/keith
57 | // Will not match:
58 | // /foo
59 | // /foo/bar
60 | type Route struct {
61 | // Name is a key specifying which HTTP handler the router
62 | // should associate with the endpoint at runtime.
63 | Name string
64 | // Method is one of the following: GET,PUT,POST,DELETE
65 | Method string
66 | // Path contains a path pattern
67 | Path string
68 | }
69 |
70 | // CreatePath combines the route's path pattern with a Params map
71 | // to produce a valid path.
72 | func (r Route) CreatePath(params Params) (string, error) {
73 | components := strings.Split(r.Path, "/")
74 | for i, c := range components {
75 | if len(c) == 0 {
76 | continue
77 | }
78 | if c[0] == ':' {
79 | val, ok := params[c[1:]]
80 | if !ok {
81 | return "", fmt.Errorf("missing param %s", c)
82 | }
83 | components[i] = val
84 | }
85 | }
86 |
87 | u, err := url.Parse(strings.Join(components, "/"))
88 | if err != nil {
89 | return "", err
90 | }
91 | return u.String(), nil
92 | }
93 |
94 | // Routes is a Route collection.
95 | type Routes []Route
96 |
97 | // Route looks up a Route by it's Handler key.
98 | func (r Routes) FindRouteByName(name string) (Route, bool) {
99 | for _, route := range r {
100 | if route.Name == name {
101 | return route, true
102 | }
103 | }
104 | return Route{}, false
105 | }
106 |
107 | // Path looks up a Route by it's Handler key and computes it's path
108 | // with a given Params map.
109 | func (r Routes) CreatePathForRoute(name string, params Params) (string, error) {
110 | route, ok := r.FindRouteByName(name)
111 | if !ok {
112 | return "", fmt.Errorf("No route exists with the name %", name)
113 | }
114 | return route.CreatePath(params)
115 | }
116 |
117 | // Router is deprecated, please use router.NewRouter() instead
118 | func (r Routes) Router(handlers Handlers) (http.Handler, error) {
119 | _, file, line, _ := runtime.Caller(1)
120 | fmt.Printf("\n\033[0;35m%s\033[0m%s:%d:%s\n", "WARNING:", file, line, " Routes.Router() is deprecated, please use router.NewRouter() instead")
121 | return NewRouter(r, handlers)
122 | }
123 |
--------------------------------------------------------------------------------
/vendor/modules.txt:
--------------------------------------------------------------------------------
1 | # github.com/bmizerany/pat v0.0.0-20140625234703-b8a35001b773
2 | github.com/bmizerany/pat
3 | # github.com/pivotal-golang/lager v0.0.0-20150428205713-c88fa6d6c4d2
4 | github.com/pivotal-golang/lager
5 | # github.com/tedsuo/ifrit v0.0.0-20150410161953-65ca48cd8a94
6 | github.com/tedsuo/ifrit
7 | github.com/tedsuo/ifrit/http_server
8 | # github.com/tedsuo/rata v0.0.0-20141210191936-6197c97c67a0
9 | github.com/tedsuo/rata
10 |
--------------------------------------------------------------------------------