Codebase list golang-github-go-kit-kit / 37dda2e
relocate examples to a separate repository Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com> Mark Sagi-Kazar 2 years ago
76 changed file(s) with 1 addition(s) and 9057 deletion(s). Raw diff Collapse all Expand all
6060 EUREKA_ADDR: http://localhost:${{ job.services.eureka.ports[8761] }}/eureka
6161 run: go test -v -race -coverprofile=coverage.coverprofile -covermode=atomic -tags integration ./...
6262
63 - name: Run example tests
64 run: |
65 cd examples
66 go test -v -race ./...
67
6863 - name: Upload coverage
6964 uses: codecov/codecov-action@v1
7065 with:
00 # Examples
11
2 For more information about these examples,
3 including a walkthrough of the stringsvc example,
4 see [gokit.io/examples](https://gokit.io/examples).
2 Examples have been relocated to a separate repository: https://github.com/go-kit/examples
+0
-17
examples/addsvc/README.md less more
0 # addsvc
1
2 addsvc is an example microservice which takes full advantage of most of Go
3 kit's features, including both service- and transport-level middlewares,
4 speaking multiple transports simultaneously, distributed tracing, and rich
5 error definitions. The server binary is available in cmd/addsvc. The client
6 binary is available in cmd/addcli.
7
8 Finally, the addtransport package provides both server and clients for each
9 supported transport. The client structs bake-in certain middlewares, in order to
10 demonstrate the _client library pattern_. But beware: client libraries are
11 generally a bad idea, because they easily lead to the
12 [distributed monolith antipattern](https://www.microservices.com/talks/dont-build-a-distributed-monolith/).
13 If you don't _know_ you need to use one in your organization, it's probably best
14 avoided: prefer moving that logic to consumers, and relying on
15 [contract testing](https://docs.pact.io/best_practices/contract_tests_not_functional_tests.html)
16 to detect incompatibilities.
+0
-210
examples/addsvc/cmd/addcli/addcli.go less more
0 package main
1
2 import (
3 "context"
4 "flag"
5 "fmt"
6 "os"
7 "strconv"
8 "text/tabwriter"
9 "time"
10
11 "google.golang.org/grpc"
12
13 "github.com/apache/thrift/lib/go/thrift"
14 lightstep "github.com/lightstep/lightstep-tracer-go"
15 stdopentracing "github.com/opentracing/opentracing-go"
16 zipkinot "github.com/openzipkin-contrib/zipkin-go-opentracing"
17 zipkin "github.com/openzipkin/zipkin-go"
18 zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"
19 "sourcegraph.com/sourcegraph/appdash"
20 appdashot "sourcegraph.com/sourcegraph/appdash/opentracing"
21
22 "github.com/go-kit/kit/log"
23
24 "github.com/go-kit/kit/examples/addsvc/pkg/addservice"
25 "github.com/go-kit/kit/examples/addsvc/pkg/addtransport"
26 addthrift "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc"
27 )
28
29 func main() {
30 // The addcli presumes no service discovery system, and expects users to
31 // provide the direct address of an addsvc. This presumption is reflected in
32 // the addcli binary and the client packages: the -transport.addr flags
33 // and various client constructors both expect host:port strings. For an
34 // example service with a client built on top of a service discovery system,
35 // see profilesvc.
36 fs := flag.NewFlagSet("addcli", flag.ExitOnError)
37 var (
38 httpAddr = fs.String("http-addr", "", "HTTP address of addsvc")
39 grpcAddr = fs.String("grpc-addr", "", "gRPC address of addsvc")
40 thriftAddr = fs.String("thrift-addr", "", "Thrift address of addsvc")
41 jsonRPCAddr = fs.String("jsonrpc-addr", "", "JSON RPC address of addsvc")
42 thriftProtocol = fs.String("thrift-protocol", "binary", "binary, compact, json, simplejson")
43 thriftBuffer = fs.Int("thrift-buffer", 0, "0 for unbuffered")
44 thriftFramed = fs.Bool("thrift-framed", false, "true to enable framing")
45 zipkinURL = fs.String("zipkin-url", "", "Enable Zipkin tracing via HTTP reporter URL e.g. http://localhost:9411/api/v2/spans")
46 zipkinBridge = fs.Bool("zipkin-ot-bridge", false, "Use Zipkin OpenTracing bridge instead of native implementation")
47 lightstepToken = fs.String("lightstep-token", "", "Enable LightStep tracing via a LightStep access token")
48 appdashAddr = fs.String("appdash-addr", "", "Enable Appdash tracing via an Appdash server host:port")
49 method = fs.String("method", "sum", "sum, concat")
50 )
51 fs.Usage = usageFor(fs, os.Args[0]+" [flags] <a> <b>")
52 fs.Parse(os.Args[1:])
53 if len(fs.Args()) != 2 {
54 fs.Usage()
55 os.Exit(1)
56 }
57
58 // This is a demonstration of the native Zipkin tracing client. If using
59 // Zipkin this is the more idiomatic client over OpenTracing.
60 var zipkinTracer *zipkin.Tracer
61 {
62 if *zipkinURL != "" {
63 var (
64 err error
65 hostPort = "" // if host:port is unknown we can keep this empty
66 serviceName = "addsvc-cli"
67 reporter = zipkinhttp.NewReporter(*zipkinURL)
68 )
69 defer reporter.Close()
70 zEP, _ := zipkin.NewEndpoint(serviceName, hostPort)
71 zipkinTracer, err = zipkin.NewTracer(reporter, zipkin.WithLocalEndpoint(zEP))
72 if err != nil {
73 fmt.Fprintf(os.Stderr, "unable to create zipkin tracer: %s\n", err.Error())
74 os.Exit(1)
75 }
76 }
77 }
78
79 // This is a demonstration client, which supports multiple tracers.
80 // Your clients will probably just use one tracer.
81 var otTracer stdopentracing.Tracer
82 {
83 if *zipkinBridge && zipkinTracer != nil {
84 otTracer = zipkinot.Wrap(zipkinTracer)
85 zipkinTracer = nil // do not instrument with both native and ot bridge
86 } else if *lightstepToken != "" {
87 otTracer = lightstep.NewTracer(lightstep.Options{
88 AccessToken: *lightstepToken,
89 })
90 defer lightstep.FlushLightStepTracer(otTracer)
91 } else if *appdashAddr != "" {
92 otTracer = appdashot.NewTracer(appdash.NewRemoteCollector(*appdashAddr))
93 } else {
94 otTracer = stdopentracing.GlobalTracer() // no-op
95 }
96 }
97
98 // This is a demonstration client, which supports multiple transports.
99 // Your clients will probably just define and stick with 1 transport.
100 var (
101 svc addservice.Service
102 err error
103 )
104 if *httpAddr != "" {
105 svc, err = addtransport.NewHTTPClient(*httpAddr, otTracer, zipkinTracer, log.NewNopLogger())
106 } else if *grpcAddr != "" {
107 conn, err := grpc.Dial(*grpcAddr, grpc.WithInsecure(), grpc.WithTimeout(time.Second))
108 if err != nil {
109 fmt.Fprintf(os.Stderr, "error: %v", err)
110 os.Exit(1)
111 }
112 defer conn.Close()
113 svc = addtransport.NewGRPCClient(conn, otTracer, zipkinTracer, log.NewNopLogger())
114 } else if *jsonRPCAddr != "" {
115 svc, err = addtransport.NewJSONRPCClient(*jsonRPCAddr, otTracer, log.NewNopLogger())
116 } else if *thriftAddr != "" {
117 // It's necessary to do all of this construction in the func main,
118 // because (among other reasons) we need to control the lifecycle of the
119 // Thrift transport, i.e. close it eventually.
120 var protocolFactory thrift.TProtocolFactory
121 switch *thriftProtocol {
122 case "compact":
123 protocolFactory = thrift.NewTCompactProtocolFactory()
124 case "simplejson":
125 protocolFactory = thrift.NewTSimpleJSONProtocolFactory()
126 case "json":
127 protocolFactory = thrift.NewTJSONProtocolFactory()
128 case "binary", "":
129 protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
130 default:
131 fmt.Fprintf(os.Stderr, "error: invalid protocol %q\n", *thriftProtocol)
132 os.Exit(1)
133 }
134 var transportFactory thrift.TTransportFactory
135 if *thriftBuffer > 0 {
136 transportFactory = thrift.NewTBufferedTransportFactory(*thriftBuffer)
137 } else {
138 transportFactory = thrift.NewTTransportFactory()
139 }
140 if *thriftFramed {
141 transportFactory = thrift.NewTFramedTransportFactory(transportFactory)
142 }
143 transportSocket, err := thrift.NewTSocket(*thriftAddr)
144 if err != nil {
145 fmt.Fprintf(os.Stderr, "error: %v\n", err)
146 os.Exit(1)
147 }
148 transport, err := transportFactory.GetTransport(transportSocket)
149 if err != nil {
150 fmt.Fprintf(os.Stderr, "error: %v\n", err)
151 os.Exit(1)
152 }
153 if err := transport.Open(); err != nil {
154 fmt.Fprintf(os.Stderr, "error: %v\n", err)
155 os.Exit(1)
156 }
157 defer transport.Close()
158 client := addthrift.NewAddServiceClientFactory(transport, protocolFactory)
159 svc = addtransport.NewThriftClient(client)
160 } else {
161 fmt.Fprintf(os.Stderr, "error: no remote address specified\n")
162 os.Exit(1)
163 }
164 if err != nil {
165 fmt.Fprintf(os.Stderr, "error: %v\n", err)
166 os.Exit(1)
167 }
168
169 switch *method {
170 case "sum":
171 a, _ := strconv.ParseInt(fs.Args()[0], 10, 64)
172 b, _ := strconv.ParseInt(fs.Args()[1], 10, 64)
173 v, err := svc.Sum(context.Background(), int(a), int(b))
174 if err != nil {
175 fmt.Fprintf(os.Stderr, "error: %v\n", err)
176 os.Exit(1)
177 }
178 fmt.Fprintf(os.Stdout, "%d + %d = %d\n", a, b, v)
179
180 case "concat":
181 a := fs.Args()[0]
182 b := fs.Args()[1]
183 v, err := svc.Concat(context.Background(), a, b)
184 if err != nil {
185 fmt.Fprintf(os.Stderr, "error: %v\n", err)
186 os.Exit(1)
187 }
188 fmt.Fprintf(os.Stdout, "%q + %q = %q\n", a, b, v)
189
190 default:
191 fmt.Fprintf(os.Stderr, "error: invalid method %q\n", *method)
192 os.Exit(1)
193 }
194 }
195
196 func usageFor(fs *flag.FlagSet, short string) func() {
197 return func() {
198 fmt.Fprintf(os.Stderr, "USAGE\n")
199 fmt.Fprintf(os.Stderr, " %s\n", short)
200 fmt.Fprintf(os.Stderr, "\n")
201 fmt.Fprintf(os.Stderr, "FLAGS\n")
202 w := tabwriter.NewWriter(os.Stderr, 0, 2, 2, ' ', 0)
203 fs.VisitAll(func(f *flag.Flag) {
204 fmt.Fprintf(w, "\t-%s %s\t%s\n", f.Name, f.DefValue, f.Usage)
205 })
206 w.Flush()
207 fmt.Fprintf(os.Stderr, "\n")
208 }
209 }
+0
-306
examples/addsvc/cmd/addsvc/addsvc.go less more
0 package main
1
2 import (
3 "flag"
4 "fmt"
5 "net"
6 "net/http"
7 "os"
8 "os/signal"
9 "syscall"
10 "text/tabwriter"
11
12 "github.com/apache/thrift/lib/go/thrift"
13 lightstep "github.com/lightstep/lightstep-tracer-go"
14 "github.com/oklog/oklog/pkg/group"
15 stdopentracing "github.com/opentracing/opentracing-go"
16 zipkinot "github.com/openzipkin-contrib/zipkin-go-opentracing"
17 zipkin "github.com/openzipkin/zipkin-go"
18 zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"
19 stdprometheus "github.com/prometheus/client_golang/prometheus"
20 "github.com/prometheus/client_golang/prometheus/promhttp"
21 "google.golang.org/grpc"
22 "sourcegraph.com/sourcegraph/appdash"
23 appdashot "sourcegraph.com/sourcegraph/appdash/opentracing"
24
25 "github.com/go-kit/kit/log"
26 "github.com/go-kit/kit/metrics"
27 "github.com/go-kit/kit/metrics/prometheus"
28 kitgrpc "github.com/go-kit/kit/transport/grpc"
29
30 addpb "github.com/go-kit/kit/examples/addsvc/pb"
31 "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint"
32 "github.com/go-kit/kit/examples/addsvc/pkg/addservice"
33 "github.com/go-kit/kit/examples/addsvc/pkg/addtransport"
34 addthrift "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc"
35 )
36
37 func main() {
38 // Define our flags. Your service probably won't need to bind listeners for
39 // *all* supported transports, or support both Zipkin and LightStep, and so
40 // on, but we do it here for demonstration purposes.
41 fs := flag.NewFlagSet("addsvc", flag.ExitOnError)
42 var (
43 debugAddr = fs.String("debug.addr", ":8080", "Debug and metrics listen address")
44 httpAddr = fs.String("http-addr", ":8081", "HTTP listen address")
45 grpcAddr = fs.String("grpc-addr", ":8082", "gRPC listen address")
46 thriftAddr = fs.String("thrift-addr", ":8083", "Thrift listen address")
47 jsonRPCAddr = fs.String("jsonrpc-addr", ":8084", "JSON RPC listen address")
48 thriftProtocol = fs.String("thrift-protocol", "binary", "binary, compact, json, simplejson")
49 thriftBuffer = fs.Int("thrift-buffer", 0, "0 for unbuffered")
50 thriftFramed = fs.Bool("thrift-framed", false, "true to enable framing")
51 zipkinURL = fs.String("zipkin-url", "", "Enable Zipkin tracing via HTTP reporter URL e.g. http://localhost:9411/api/v2/spans")
52 zipkinBridge = fs.Bool("zipkin-ot-bridge", false, "Use Zipkin OpenTracing bridge instead of native implementation")
53 lightstepToken = fs.String("lightstep-token", "", "Enable LightStep tracing via a LightStep access token")
54 appdashAddr = fs.String("appdash-addr", "", "Enable Appdash tracing via an Appdash server host:port")
55 )
56 fs.Usage = usageFor(fs, os.Args[0]+" [flags]")
57 fs.Parse(os.Args[1:])
58
59 // Create a single logger, which we'll use and give to other components.
60 var logger log.Logger
61 {
62 logger = log.NewLogfmtLogger(os.Stderr)
63 logger = log.With(logger, "ts", log.DefaultTimestampUTC)
64 logger = log.With(logger, "caller", log.DefaultCaller)
65 }
66
67 var zipkinTracer *zipkin.Tracer
68 {
69 if *zipkinURL != "" {
70 var (
71 err error
72 hostPort = "localhost:80"
73 serviceName = "addsvc"
74 reporter = zipkinhttp.NewReporter(*zipkinURL)
75 )
76 defer reporter.Close()
77 zEP, _ := zipkin.NewEndpoint(serviceName, hostPort)
78 zipkinTracer, err = zipkin.NewTracer(reporter, zipkin.WithLocalEndpoint(zEP))
79 if err != nil {
80 logger.Log("err", err)
81 os.Exit(1)
82 }
83 if !(*zipkinBridge) {
84 logger.Log("tracer", "Zipkin", "type", "Native", "URL", *zipkinURL)
85 }
86 }
87 }
88
89 // Determine which OpenTracing tracer to use. We'll pass the tracer to all the
90 // components that use it, as a dependency.
91 var tracer stdopentracing.Tracer
92 {
93 if *zipkinBridge && zipkinTracer != nil {
94 logger.Log("tracer", "Zipkin", "type", "OpenTracing", "URL", *zipkinURL)
95 tracer = zipkinot.Wrap(zipkinTracer)
96 zipkinTracer = nil // do not instrument with both native tracer and opentracing bridge
97 } else if *lightstepToken != "" {
98 logger.Log("tracer", "LightStep") // probably don't want to print out the token :)
99 tracer = lightstep.NewTracer(lightstep.Options{
100 AccessToken: *lightstepToken,
101 })
102 defer lightstep.FlushLightStepTracer(tracer)
103 } else if *appdashAddr != "" {
104 logger.Log("tracer", "Appdash", "addr", *appdashAddr)
105 tracer = appdashot.NewTracer(appdash.NewRemoteCollector(*appdashAddr))
106 } else {
107 tracer = stdopentracing.GlobalTracer() // no-op
108 }
109 }
110
111 // Create the (sparse) metrics we'll use in the service. They, too, are
112 // dependencies that we pass to components that use them.
113 var ints, chars metrics.Counter
114 {
115 // Business-level metrics.
116 ints = prometheus.NewCounterFrom(stdprometheus.CounterOpts{
117 Namespace: "example",
118 Subsystem: "addsvc",
119 Name: "integers_summed",
120 Help: "Total count of integers summed via the Sum method.",
121 }, []string{})
122 chars = prometheus.NewCounterFrom(stdprometheus.CounterOpts{
123 Namespace: "example",
124 Subsystem: "addsvc",
125 Name: "characters_concatenated",
126 Help: "Total count of characters concatenated via the Concat method.",
127 }, []string{})
128 }
129 var duration metrics.Histogram
130 {
131 // Endpoint-level metrics.
132 duration = prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
133 Namespace: "example",
134 Subsystem: "addsvc",
135 Name: "request_duration_seconds",
136 Help: "Request duration in seconds.",
137 }, []string{"method", "success"})
138 }
139 http.DefaultServeMux.Handle("/metrics", promhttp.Handler())
140
141 // Build the layers of the service "onion" from the inside out. First, the
142 // business logic service; then, the set of endpoints that wrap the service;
143 // and finally, a series of concrete transport adapters. The adapters, like
144 // the HTTP handler or the gRPC server, are the bridge between Go kit and
145 // the interfaces that the transports expect. Note that we're not binding
146 // them to ports or anything yet; we'll do that next.
147 var (
148 service = addservice.New(logger, ints, chars)
149 endpoints = addendpoint.New(service, logger, duration, tracer, zipkinTracer)
150 httpHandler = addtransport.NewHTTPHandler(endpoints, tracer, zipkinTracer, logger)
151 grpcServer = addtransport.NewGRPCServer(endpoints, tracer, zipkinTracer, logger)
152 thriftServer = addtransport.NewThriftServer(endpoints)
153 jsonrpcHandler = addtransport.NewJSONRPCHandler(endpoints, logger)
154 )
155
156 // Now we're to the part of the func main where we want to start actually
157 // running things, like servers bound to listeners to receive connections.
158 //
159 // The method is the same for each component: add a new actor to the group
160 // struct, which is a combination of 2 anonymous functions: the first
161 // function actually runs the component, and the second function should
162 // interrupt the first function and cause it to return. It's in these
163 // functions that we actually bind the Go kit server/handler structs to the
164 // concrete transports and run them.
165 //
166 // Putting each component into its own block is mostly for aesthetics: it
167 // clearly demarcates the scope in which each listener/socket may be used.
168 var g group.Group
169 {
170 // The debug listener mounts the http.DefaultServeMux, and serves up
171 // stuff like the Prometheus metrics route, the Go debug and profiling
172 // routes, and so on.
173 debugListener, err := net.Listen("tcp", *debugAddr)
174 if err != nil {
175 logger.Log("transport", "debug/HTTP", "during", "Listen", "err", err)
176 os.Exit(1)
177 }
178 g.Add(func() error {
179 logger.Log("transport", "debug/HTTP", "addr", *debugAddr)
180 return http.Serve(debugListener, http.DefaultServeMux)
181 }, func(error) {
182 debugListener.Close()
183 })
184 }
185 {
186 // The HTTP listener mounts the Go kit HTTP handler we created.
187 httpListener, err := net.Listen("tcp", *httpAddr)
188 if err != nil {
189 logger.Log("transport", "HTTP", "during", "Listen", "err", err)
190 os.Exit(1)
191 }
192 g.Add(func() error {
193 logger.Log("transport", "HTTP", "addr", *httpAddr)
194 return http.Serve(httpListener, httpHandler)
195 }, func(error) {
196 httpListener.Close()
197 })
198 }
199 {
200 // The gRPC listener mounts the Go kit gRPC server we created.
201 grpcListener, err := net.Listen("tcp", *grpcAddr)
202 if err != nil {
203 logger.Log("transport", "gRPC", "during", "Listen", "err", err)
204 os.Exit(1)
205 }
206 g.Add(func() error {
207 logger.Log("transport", "gRPC", "addr", *grpcAddr)
208 // we add the Go Kit gRPC Interceptor to our gRPC service as it is used by
209 // the here demonstrated zipkin tracing middleware.
210 baseServer := grpc.NewServer(grpc.UnaryInterceptor(kitgrpc.Interceptor))
211 addpb.RegisterAddServer(baseServer, grpcServer)
212 return baseServer.Serve(grpcListener)
213 }, func(error) {
214 grpcListener.Close()
215 })
216 }
217 {
218 // The Thrift socket mounts the Go kit Thrift server we created earlier.
219 // There's a lot of boilerplate involved here, related to configuring
220 // the protocol and transport; blame Thrift.
221 thriftSocket, err := thrift.NewTServerSocket(*thriftAddr)
222 if err != nil {
223 logger.Log("transport", "Thrift", "during", "Listen", "err", err)
224 os.Exit(1)
225 }
226 g.Add(func() error {
227 logger.Log("transport", "Thrift", "addr", *thriftAddr)
228 var protocolFactory thrift.TProtocolFactory
229 switch *thriftProtocol {
230 case "binary":
231 protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
232 case "compact":
233 protocolFactory = thrift.NewTCompactProtocolFactory()
234 case "json":
235 protocolFactory = thrift.NewTJSONProtocolFactory()
236 case "simplejson":
237 protocolFactory = thrift.NewTSimpleJSONProtocolFactory()
238 default:
239 return fmt.Errorf("invalid Thrift protocol %q", *thriftProtocol)
240 }
241 var transportFactory thrift.TTransportFactory
242 if *thriftBuffer > 0 {
243 transportFactory = thrift.NewTBufferedTransportFactory(*thriftBuffer)
244 } else {
245 transportFactory = thrift.NewTTransportFactory()
246 }
247 if *thriftFramed {
248 transportFactory = thrift.NewTFramedTransportFactory(transportFactory)
249 }
250 return thrift.NewTSimpleServer4(
251 addthrift.NewAddServiceProcessor(thriftServer),
252 thriftSocket,
253 transportFactory,
254 protocolFactory,
255 ).Serve()
256 }, func(error) {
257 thriftSocket.Close()
258 })
259 }
260 {
261 httpListener, err := net.Listen("tcp", *jsonRPCAddr)
262 if err != nil {
263 logger.Log("transport", "JSONRPC over HTTP", "during", "Listen", "err", err)
264 os.Exit(1)
265 }
266 g.Add(func() error {
267 logger.Log("transport", "JSONRPC over HTTP", "addr", *jsonRPCAddr)
268 return http.Serve(httpListener, jsonrpcHandler)
269 }, func(error) {
270 httpListener.Close()
271 })
272 }
273 {
274 // This function just sits and waits for ctrl-C.
275 cancelInterrupt := make(chan struct{})
276 g.Add(func() error {
277 c := make(chan os.Signal, 1)
278 signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
279 select {
280 case sig := <-c:
281 return fmt.Errorf("received signal %s", sig)
282 case <-cancelInterrupt:
283 return nil
284 }
285 }, func(error) {
286 close(cancelInterrupt)
287 })
288 }
289 logger.Log("exit", g.Run())
290 }
291
292 func usageFor(fs *flag.FlagSet, short string) func() {
293 return func() {
294 fmt.Fprintf(os.Stderr, "USAGE\n")
295 fmt.Fprintf(os.Stderr, " %s\n", short)
296 fmt.Fprintf(os.Stderr, "\n")
297 fmt.Fprintf(os.Stderr, "FLAGS\n")
298 w := tabwriter.NewWriter(os.Stderr, 0, 2, 2, ' ', 0)
299 fs.VisitAll(func(f *flag.Flag) {
300 fmt.Fprintf(w, "\t-%s %s\t%s\n", f.Name, f.DefValue, f.Usage)
301 })
302 w.Flush()
303 fmt.Fprintf(os.Stderr, "\n")
304 }
305 }
+0
-54
examples/addsvc/cmd/addsvc/pact_test.go less more
0 package main
1
2 import (
3 "fmt"
4 "net/http"
5 "os"
6 "strings"
7 "testing"
8
9 "github.com/pact-foundation/pact-go/dsl"
10 )
11
12 func TestPactStringsvcUppercase(t *testing.T) {
13 if os.Getenv("WRITE_PACTS") == "" {
14 t.Skip("skipping Pact contracts; set WRITE_PACTS environment variable to enable")
15 }
16
17 pact := dsl.Pact{
18 Consumer: "addsvc",
19 Provider: "stringsvc",
20 }
21 defer pact.Teardown()
22
23 pact.AddInteraction().
24 UponReceiving("stringsvc uppercase").
25 WithRequest(dsl.Request{
26 Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/json; charset=utf-8")},
27 Method: "POST",
28 Path: dsl.String("/uppercase"),
29 Body: `{"s":"foo"}`,
30 }).
31 WillRespondWith(dsl.Response{
32 Status: 200,
33 Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/json; charset=utf-8")},
34 Body: `{"v":"FOO"}`,
35 })
36
37 if err := pact.Verify(func() error {
38 u := fmt.Sprintf("http://localhost:%d/uppercase", pact.Server.Port)
39 req, err := http.NewRequest("POST", u, strings.NewReader(`{"s":"foo"}`))
40 if err != nil {
41 return err
42 }
43 req.Header.Set("Content-Type", "application/json; charset=utf-8")
44 if _, err = http.DefaultClient.Do(req); err != nil {
45 return err
46 }
47 return nil
48 }); err != nil {
49 t.Fatal(err)
50 }
51
52 pact.WritePact()
53 }
+0
-42
examples/addsvc/cmd/addsvc/wiring_test.go less more
0 package main
1
2 import (
3 "io/ioutil"
4 "net/http"
5 "net/http/httptest"
6 "strings"
7 "testing"
8
9 "github.com/opentracing/opentracing-go"
10 zipkin "github.com/openzipkin/zipkin-go"
11
12 "github.com/go-kit/kit/log"
13 "github.com/go-kit/kit/metrics/discard"
14
15 "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint"
16 "github.com/go-kit/kit/examples/addsvc/pkg/addservice"
17 "github.com/go-kit/kit/examples/addsvc/pkg/addtransport"
18 )
19
20 func TestHTTP(t *testing.T) {
21 zkt, _ := zipkin.NewTracer(nil, zipkin.WithNoopTracer(true))
22 svc := addservice.New(log.NewNopLogger(), discard.NewCounter(), discard.NewCounter())
23 eps := addendpoint.New(svc, log.NewNopLogger(), discard.NewHistogram(), opentracing.GlobalTracer(), zkt)
24 mux := addtransport.NewHTTPHandler(eps, opentracing.GlobalTracer(), zkt, log.NewNopLogger())
25 srv := httptest.NewServer(mux)
26 defer srv.Close()
27
28 for _, testcase := range []struct {
29 method, url, body, want string
30 }{
31 {"GET", srv.URL + "/concat", `{"a":"1","b":"2"}`, `{"v":"12"}`},
32 {"GET", srv.URL + "/sum", `{"a":1,"b":2}`, `{"v":3}`},
33 } {
34 req, _ := http.NewRequest(testcase.method, testcase.url, strings.NewReader(testcase.body))
35 resp, _ := http.DefaultClient.Do(req)
36 body, _ := ioutil.ReadAll(resp.Body)
37 if want, have := testcase.want, strings.TrimSpace(string(body)); want != have {
38 t.Errorf("%s %s %s: want %q, have %q", testcase.method, testcase.url, testcase.body, want, have)
39 }
40 }
41 }
+0
-349
examples/addsvc/pb/addsvc.pb.go less more
0 // Code generated by protoc-gen-go. DO NOT EDIT.
1 // source: addsvc.proto
2
3 package pb
4
5 import (
6 context "context"
7 fmt "fmt"
8 proto "github.com/golang/protobuf/proto"
9 grpc "google.golang.org/grpc"
10 math "math"
11 )
12
13 // Reference imports to suppress errors if they are not otherwise used.
14 var _ = proto.Marshal
15 var _ = fmt.Errorf
16 var _ = math.Inf
17
18 // This is a compile-time assertion to ensure that this generated file
19 // is compatible with the proto package it is being compiled against.
20 // A compilation error at this line likely means your copy of the
21 // proto package needs to be updated.
22 const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
23
24 // The sum request contains two parameters.
25 type SumRequest struct {
26 A int64 `protobuf:"varint,1,opt,name=a,proto3" json:"a,omitempty"`
27 B int64 `protobuf:"varint,2,opt,name=b,proto3" json:"b,omitempty"`
28 XXX_NoUnkeyedLiteral struct{} `json:"-"`
29 XXX_unrecognized []byte `json:"-"`
30 XXX_sizecache int32 `json:"-"`
31 }
32
33 func (m *SumRequest) Reset() { *m = SumRequest{} }
34 func (m *SumRequest) String() string { return proto.CompactTextString(m) }
35 func (*SumRequest) ProtoMessage() {}
36 func (*SumRequest) Descriptor() ([]byte, []int) {
37 return fileDescriptor_174367f558d60c26, []int{0}
38 }
39
40 func (m *SumRequest) XXX_Unmarshal(b []byte) error {
41 return xxx_messageInfo_SumRequest.Unmarshal(m, b)
42 }
43 func (m *SumRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
44 return xxx_messageInfo_SumRequest.Marshal(b, m, deterministic)
45 }
46 func (m *SumRequest) XXX_Merge(src proto.Message) {
47 xxx_messageInfo_SumRequest.Merge(m, src)
48 }
49 func (m *SumRequest) XXX_Size() int {
50 return xxx_messageInfo_SumRequest.Size(m)
51 }
52 func (m *SumRequest) XXX_DiscardUnknown() {
53 xxx_messageInfo_SumRequest.DiscardUnknown(m)
54 }
55
56 var xxx_messageInfo_SumRequest proto.InternalMessageInfo
57
58 func (m *SumRequest) GetA() int64 {
59 if m != nil {
60 return m.A
61 }
62 return 0
63 }
64
65 func (m *SumRequest) GetB() int64 {
66 if m != nil {
67 return m.B
68 }
69 return 0
70 }
71
72 // The sum response contains the result of the calculation.
73 type SumReply struct {
74 V int64 `protobuf:"varint,1,opt,name=v,proto3" json:"v,omitempty"`
75 Err string `protobuf:"bytes,2,opt,name=err,proto3" json:"err,omitempty"`
76 XXX_NoUnkeyedLiteral struct{} `json:"-"`
77 XXX_unrecognized []byte `json:"-"`
78 XXX_sizecache int32 `json:"-"`
79 }
80
81 func (m *SumReply) Reset() { *m = SumReply{} }
82 func (m *SumReply) String() string { return proto.CompactTextString(m) }
83 func (*SumReply) ProtoMessage() {}
84 func (*SumReply) Descriptor() ([]byte, []int) {
85 return fileDescriptor_174367f558d60c26, []int{1}
86 }
87
88 func (m *SumReply) XXX_Unmarshal(b []byte) error {
89 return xxx_messageInfo_SumReply.Unmarshal(m, b)
90 }
91 func (m *SumReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
92 return xxx_messageInfo_SumReply.Marshal(b, m, deterministic)
93 }
94 func (m *SumReply) XXX_Merge(src proto.Message) {
95 xxx_messageInfo_SumReply.Merge(m, src)
96 }
97 func (m *SumReply) XXX_Size() int {
98 return xxx_messageInfo_SumReply.Size(m)
99 }
100 func (m *SumReply) XXX_DiscardUnknown() {
101 xxx_messageInfo_SumReply.DiscardUnknown(m)
102 }
103
104 var xxx_messageInfo_SumReply proto.InternalMessageInfo
105
106 func (m *SumReply) GetV() int64 {
107 if m != nil {
108 return m.V
109 }
110 return 0
111 }
112
113 func (m *SumReply) GetErr() string {
114 if m != nil {
115 return m.Err
116 }
117 return ""
118 }
119
120 // The Concat request contains two parameters.
121 type ConcatRequest struct {
122 A string `protobuf:"bytes,1,opt,name=a,proto3" json:"a,omitempty"`
123 B string `protobuf:"bytes,2,opt,name=b,proto3" json:"b,omitempty"`
124 XXX_NoUnkeyedLiteral struct{} `json:"-"`
125 XXX_unrecognized []byte `json:"-"`
126 XXX_sizecache int32 `json:"-"`
127 }
128
129 func (m *ConcatRequest) Reset() { *m = ConcatRequest{} }
130 func (m *ConcatRequest) String() string { return proto.CompactTextString(m) }
131 func (*ConcatRequest) ProtoMessage() {}
132 func (*ConcatRequest) Descriptor() ([]byte, []int) {
133 return fileDescriptor_174367f558d60c26, []int{2}
134 }
135
136 func (m *ConcatRequest) XXX_Unmarshal(b []byte) error {
137 return xxx_messageInfo_ConcatRequest.Unmarshal(m, b)
138 }
139 func (m *ConcatRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
140 return xxx_messageInfo_ConcatRequest.Marshal(b, m, deterministic)
141 }
142 func (m *ConcatRequest) XXX_Merge(src proto.Message) {
143 xxx_messageInfo_ConcatRequest.Merge(m, src)
144 }
145 func (m *ConcatRequest) XXX_Size() int {
146 return xxx_messageInfo_ConcatRequest.Size(m)
147 }
148 func (m *ConcatRequest) XXX_DiscardUnknown() {
149 xxx_messageInfo_ConcatRequest.DiscardUnknown(m)
150 }
151
152 var xxx_messageInfo_ConcatRequest proto.InternalMessageInfo
153
154 func (m *ConcatRequest) GetA() string {
155 if m != nil {
156 return m.A
157 }
158 return ""
159 }
160
161 func (m *ConcatRequest) GetB() string {
162 if m != nil {
163 return m.B
164 }
165 return ""
166 }
167
168 // The Concat response contains the result of the concatenation.
169 type ConcatReply struct {
170 V string `protobuf:"bytes,1,opt,name=v,proto3" json:"v,omitempty"`
171 Err string `protobuf:"bytes,2,opt,name=err,proto3" json:"err,omitempty"`
172 XXX_NoUnkeyedLiteral struct{} `json:"-"`
173 XXX_unrecognized []byte `json:"-"`
174 XXX_sizecache int32 `json:"-"`
175 }
176
177 func (m *ConcatReply) Reset() { *m = ConcatReply{} }
178 func (m *ConcatReply) String() string { return proto.CompactTextString(m) }
179 func (*ConcatReply) ProtoMessage() {}
180 func (*ConcatReply) Descriptor() ([]byte, []int) {
181 return fileDescriptor_174367f558d60c26, []int{3}
182 }
183
184 func (m *ConcatReply) XXX_Unmarshal(b []byte) error {
185 return xxx_messageInfo_ConcatReply.Unmarshal(m, b)
186 }
187 func (m *ConcatReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
188 return xxx_messageInfo_ConcatReply.Marshal(b, m, deterministic)
189 }
190 func (m *ConcatReply) XXX_Merge(src proto.Message) {
191 xxx_messageInfo_ConcatReply.Merge(m, src)
192 }
193 func (m *ConcatReply) XXX_Size() int {
194 return xxx_messageInfo_ConcatReply.Size(m)
195 }
196 func (m *ConcatReply) XXX_DiscardUnknown() {
197 xxx_messageInfo_ConcatReply.DiscardUnknown(m)
198 }
199
200 var xxx_messageInfo_ConcatReply proto.InternalMessageInfo
201
202 func (m *ConcatReply) GetV() string {
203 if m != nil {
204 return m.V
205 }
206 return ""
207 }
208
209 func (m *ConcatReply) GetErr() string {
210 if m != nil {
211 return m.Err
212 }
213 return ""
214 }
215
216 func init() {
217 proto.RegisterType((*SumRequest)(nil), "pb.SumRequest")
218 proto.RegisterType((*SumReply)(nil), "pb.SumReply")
219 proto.RegisterType((*ConcatRequest)(nil), "pb.ConcatRequest")
220 proto.RegisterType((*ConcatReply)(nil), "pb.ConcatReply")
221 }
222
223 func init() { proto.RegisterFile("addsvc.proto", fileDescriptor_174367f558d60c26) }
224
225 var fileDescriptor_174367f558d60c26 = []byte{
226 // 189 bytes of a gzipped FileDescriptorProto
227 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x49, 0x4c, 0x49, 0x29,
228 0x2e, 0x4b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2a, 0x48, 0x52, 0xd2, 0xe0, 0xe2,
229 0x0a, 0x2e, 0xcd, 0x0d, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0xe2, 0xe1, 0x62, 0x4c, 0x94,
230 0x60, 0x54, 0x60, 0xd4, 0x60, 0x0e, 0x62, 0x4c, 0x04, 0xf1, 0x92, 0x24, 0x98, 0x20, 0xbc, 0x24,
231 0x25, 0x2d, 0x2e, 0x0e, 0xb0, 0xca, 0x82, 0x9c, 0x4a, 0x90, 0x4c, 0x19, 0x4c, 0x5d, 0x99, 0x90,
232 0x00, 0x17, 0x73, 0x6a, 0x51, 0x11, 0x58, 0x25, 0x67, 0x10, 0x88, 0xa9, 0xa4, 0xcd, 0xc5, 0xeb,
233 0x9c, 0x9f, 0x97, 0x9c, 0x58, 0x82, 0x61, 0x30, 0x27, 0x8a, 0xc1, 0x9c, 0x20, 0x83, 0x75, 0xb9,
234 0xb8, 0x61, 0x8a, 0x51, 0xcc, 0xe6, 0xc4, 0x6a, 0xb6, 0x51, 0x0c, 0x17, 0xb3, 0x63, 0x4a, 0x8a,
235 0x90, 0x2a, 0x17, 0x73, 0x70, 0x69, 0xae, 0x10, 0x9f, 0x5e, 0x41, 0x92, 0x1e, 0xc2, 0x07, 0x52,
236 0x3c, 0x70, 0x7e, 0x41, 0x4e, 0xa5, 0x12, 0x83, 0x90, 0x1e, 0x17, 0x1b, 0xc4, 0x70, 0x21, 0x41,
237 0x90, 0x0c, 0x8a, 0xab, 0xa4, 0xf8, 0x91, 0x85, 0xc0, 0xea, 0x93, 0xd8, 0xc0, 0x41, 0x63, 0x0c,
238 0x08, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x37, 0x81, 0x99, 0x2a, 0x01, 0x00, 0x00,
239 }
240
241 // Reference imports to suppress errors if they are not otherwise used.
242 var _ context.Context
243 var _ grpc.ClientConn
244
245 // This is a compile-time assertion to ensure that this generated file
246 // is compatible with the grpc package it is being compiled against.
247 const _ = grpc.SupportPackageIsVersion4
248
249 // AddClient is the client API for Add service.
250 //
251 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
252 type AddClient interface {
253 // Sums two integers.
254 Sum(ctx context.Context, in *SumRequest, opts ...grpc.CallOption) (*SumReply, error)
255 // Concatenates two strings
256 Concat(ctx context.Context, in *ConcatRequest, opts ...grpc.CallOption) (*ConcatReply, error)
257 }
258
259 type addClient struct {
260 cc *grpc.ClientConn
261 }
262
263 func NewAddClient(cc *grpc.ClientConn) AddClient {
264 return &addClient{cc}
265 }
266
267 func (c *addClient) Sum(ctx context.Context, in *SumRequest, opts ...grpc.CallOption) (*SumReply, error) {
268 out := new(SumReply)
269 err := c.cc.Invoke(ctx, "/pb.Add/Sum", in, out, opts...)
270 if err != nil {
271 return nil, err
272 }
273 return out, nil
274 }
275
276 func (c *addClient) Concat(ctx context.Context, in *ConcatRequest, opts ...grpc.CallOption) (*ConcatReply, error) {
277 out := new(ConcatReply)
278 err := c.cc.Invoke(ctx, "/pb.Add/Concat", in, out, opts...)
279 if err != nil {
280 return nil, err
281 }
282 return out, nil
283 }
284
285 // AddServer is the server API for Add service.
286 type AddServer interface {
287 // Sums two integers.
288 Sum(context.Context, *SumRequest) (*SumReply, error)
289 // Concatenates two strings
290 Concat(context.Context, *ConcatRequest) (*ConcatReply, error)
291 }
292
293 func RegisterAddServer(s *grpc.Server, srv AddServer) {
294 s.RegisterService(&_Add_serviceDesc, srv)
295 }
296
297 func _Add_Sum_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
298 in := new(SumRequest)
299 if err := dec(in); err != nil {
300 return nil, err
301 }
302 if interceptor == nil {
303 return srv.(AddServer).Sum(ctx, in)
304 }
305 info := &grpc.UnaryServerInfo{
306 Server: srv,
307 FullMethod: "/pb.Add/Sum",
308 }
309 handler := func(ctx context.Context, req interface{}) (interface{}, error) {
310 return srv.(AddServer).Sum(ctx, req.(*SumRequest))
311 }
312 return interceptor(ctx, in, info, handler)
313 }
314
315 func _Add_Concat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
316 in := new(ConcatRequest)
317 if err := dec(in); err != nil {
318 return nil, err
319 }
320 if interceptor == nil {
321 return srv.(AddServer).Concat(ctx, in)
322 }
323 info := &grpc.UnaryServerInfo{
324 Server: srv,
325 FullMethod: "/pb.Add/Concat",
326 }
327 handler := func(ctx context.Context, req interface{}) (interface{}, error) {
328 return srv.(AddServer).Concat(ctx, req.(*ConcatRequest))
329 }
330 return interceptor(ctx, in, info, handler)
331 }
332
333 var _Add_serviceDesc = grpc.ServiceDesc{
334 ServiceName: "pb.Add",
335 HandlerType: (*AddServer)(nil),
336 Methods: []grpc.MethodDesc{
337 {
338 MethodName: "Sum",
339 Handler: _Add_Sum_Handler,
340 },
341 {
342 MethodName: "Concat",
343 Handler: _Add_Concat_Handler,
344 },
345 },
346 Streams: []grpc.StreamDesc{},
347 Metadata: "addsvc.proto",
348 }
+0
-36
examples/addsvc/pb/addsvc.proto less more
0 syntax = "proto3";
1
2 package pb;
3
4 // The Add service definition.
5 service Add {
6 // Sums two integers.
7 rpc Sum (SumRequest) returns (SumReply) {}
8
9 // Concatenates two strings
10 rpc Concat (ConcatRequest) returns (ConcatReply) {}
11 }
12
13 // The sum request contains two parameters.
14 message SumRequest {
15 int64 a = 1;
16 int64 b = 2;
17 }
18
19 // The sum response contains the result of the calculation.
20 message SumReply {
21 int64 v = 1;
22 string err = 2;
23 }
24
25 // The Concat request contains two parameters.
26 message ConcatRequest {
27 string a = 1;
28 string b = 2;
29 }
30
31 // The Concat response contains the result of the concatenation.
32 message ConcatReply {
33 string v = 1;
34 string err = 2;
35 }
+0
-14
examples/addsvc/pb/compile.sh less more
0 #!/usr/bin/env sh
1
2 # Install proto3 from source
3 # brew install autoconf automake libtool
4 # git clone https://github.com/google/protobuf
5 # ./autogen.sh ; ./configure ; make ; make install
6 #
7 # Update protoc Go bindings via
8 # go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
9 #
10 # See also
11 # https://github.com/grpc/grpc-go/tree/master/examples
12
13 protoc addsvc.proto --go_out=plugins=grpc:.
+0
-43
examples/addsvc/pkg/addendpoint/middleware.go less more
0 package addendpoint
1
2 import (
3 "context"
4 "fmt"
5 "time"
6
7 "github.com/go-kit/kit/endpoint"
8 "github.com/go-kit/kit/log"
9 "github.com/go-kit/kit/metrics"
10 )
11
12 // InstrumentingMiddleware returns an endpoint middleware that records
13 // the duration of each invocation to the passed histogram. The middleware adds
14 // a single field: "success", which is "true" if no error is returned, and
15 // "false" otherwise.
16 func InstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware {
17 return func(next endpoint.Endpoint) endpoint.Endpoint {
18 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
19
20 defer func(begin time.Time) {
21 duration.With("success", fmt.Sprint(err == nil)).Observe(time.Since(begin).Seconds())
22 }(time.Now())
23 return next(ctx, request)
24
25 }
26 }
27 }
28
29 // LoggingMiddleware returns an endpoint middleware that logs the
30 // duration of each invocation, and the resulting error, if any.
31 func LoggingMiddleware(logger log.Logger) endpoint.Middleware {
32 return func(next endpoint.Endpoint) endpoint.Endpoint {
33 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
34
35 defer func(begin time.Time) {
36 logger.Log("transport_error", err, "took", time.Since(begin))
37 }(time.Now())
38 return next(ctx, request)
39
40 }
41 }
42 }
+0
-141
examples/addsvc/pkg/addendpoint/set.go less more
0 package addendpoint
1
2 import (
3 "context"
4 "time"
5
6 "golang.org/x/time/rate"
7
8 stdopentracing "github.com/opentracing/opentracing-go"
9 stdzipkin "github.com/openzipkin/zipkin-go"
10 "github.com/sony/gobreaker"
11
12 "github.com/go-kit/kit/circuitbreaker"
13 "github.com/go-kit/kit/endpoint"
14 "github.com/go-kit/kit/log"
15 "github.com/go-kit/kit/metrics"
16 "github.com/go-kit/kit/ratelimit"
17 "github.com/go-kit/kit/tracing/opentracing"
18 "github.com/go-kit/kit/tracing/zipkin"
19
20 "github.com/go-kit/kit/examples/addsvc/pkg/addservice"
21 )
22
23 // Set collects all of the endpoints that compose an add service. It's meant to
24 // be used as a helper struct, to collect all of the endpoints into a single
25 // parameter.
26 type Set struct {
27 SumEndpoint endpoint.Endpoint
28 ConcatEndpoint endpoint.Endpoint
29 }
30
31 // New returns a Set that wraps the provided server, and wires in all of the
32 // expected endpoint middlewares via the various parameters.
33 func New(svc addservice.Service, logger log.Logger, duration metrics.Histogram, otTracer stdopentracing.Tracer, zipkinTracer *stdzipkin.Tracer) Set {
34 var sumEndpoint endpoint.Endpoint
35 {
36 sumEndpoint = MakeSumEndpoint(svc)
37 // Sum is limited to 1 request per second with burst of 1 request.
38 // Note, rate is defined as a time interval between requests.
39 sumEndpoint = ratelimit.NewErroringLimiter(rate.NewLimiter(rate.Every(time.Second), 1))(sumEndpoint)
40 sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(sumEndpoint)
41 sumEndpoint = opentracing.TraceServer(otTracer, "Sum")(sumEndpoint)
42 if zipkinTracer != nil {
43 sumEndpoint = zipkin.TraceEndpoint(zipkinTracer, "Sum")(sumEndpoint)
44 }
45 sumEndpoint = LoggingMiddleware(log.With(logger, "method", "Sum"))(sumEndpoint)
46 sumEndpoint = InstrumentingMiddleware(duration.With("method", "Sum"))(sumEndpoint)
47 }
48 var concatEndpoint endpoint.Endpoint
49 {
50 concatEndpoint = MakeConcatEndpoint(svc)
51 // Concat is limited to 1 request per second with burst of 100 requests.
52 // Note, rate is defined as a number of requests per second.
53 concatEndpoint = ratelimit.NewErroringLimiter(rate.NewLimiter(rate.Limit(1), 100))(concatEndpoint)
54 concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(concatEndpoint)
55 concatEndpoint = opentracing.TraceServer(otTracer, "Concat")(concatEndpoint)
56 if zipkinTracer != nil {
57 concatEndpoint = zipkin.TraceEndpoint(zipkinTracer, "Concat")(concatEndpoint)
58 }
59 concatEndpoint = LoggingMiddleware(log.With(logger, "method", "Concat"))(concatEndpoint)
60 concatEndpoint = InstrumentingMiddleware(duration.With("method", "Concat"))(concatEndpoint)
61 }
62 return Set{
63 SumEndpoint: sumEndpoint,
64 ConcatEndpoint: concatEndpoint,
65 }
66 }
67
68 // Sum implements the service interface, so Set may be used as a service.
69 // This is primarily useful in the context of a client library.
70 func (s Set) Sum(ctx context.Context, a, b int) (int, error) {
71 resp, err := s.SumEndpoint(ctx, SumRequest{A: a, B: b})
72 if err != nil {
73 return 0, err
74 }
75 response := resp.(SumResponse)
76 return response.V, response.Err
77 }
78
79 // Concat implements the service interface, so Set may be used as a
80 // service. This is primarily useful in the context of a client library.
81 func (s Set) Concat(ctx context.Context, a, b string) (string, error) {
82 resp, err := s.ConcatEndpoint(ctx, ConcatRequest{A: a, B: b})
83 if err != nil {
84 return "", err
85 }
86 response := resp.(ConcatResponse)
87 return response.V, response.Err
88 }
89
90 // MakeSumEndpoint constructs a Sum endpoint wrapping the service.
91 func MakeSumEndpoint(s addservice.Service) endpoint.Endpoint {
92 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
93 req := request.(SumRequest)
94 v, err := s.Sum(ctx, req.A, req.B)
95 return SumResponse{V: v, Err: err}, nil
96 }
97 }
98
99 // MakeConcatEndpoint constructs a Concat endpoint wrapping the service.
100 func MakeConcatEndpoint(s addservice.Service) endpoint.Endpoint {
101 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
102 req := request.(ConcatRequest)
103 v, err := s.Concat(ctx, req.A, req.B)
104 return ConcatResponse{V: v, Err: err}, nil
105 }
106 }
107
108 // compile time assertions for our response types implementing endpoint.Failer.
109 var (
110 _ endpoint.Failer = SumResponse{}
111 _ endpoint.Failer = ConcatResponse{}
112 )
113
114 // SumRequest collects the request parameters for the Sum method.
115 type SumRequest struct {
116 A, B int
117 }
118
119 // SumResponse collects the response values for the Sum method.
120 type SumResponse struct {
121 V int `json:"v"`
122 Err error `json:"-"` // should be intercepted by Failed/errorEncoder
123 }
124
125 // Failed implements endpoint.Failer.
126 func (r SumResponse) Failed() error { return r.Err }
127
128 // ConcatRequest collects the request parameters for the Concat method.
129 type ConcatRequest struct {
130 A, B string
131 }
132
133 // ConcatResponse collects the response values for the Concat method.
134 type ConcatResponse struct {
135 V string `json:"v"`
136 Err error `json:"-"`
137 }
138
139 // Failed implements endpoint.Failer.
140 func (r ConcatResponse) Failed() error { return r.Err }
+0
-69
examples/addsvc/pkg/addservice/middleware.go less more
0 package addservice
1
2 import (
3 "context"
4
5 "github.com/go-kit/kit/log"
6 "github.com/go-kit/kit/metrics"
7 )
8
9 // Middleware describes a service (as opposed to endpoint) middleware.
10 type Middleware func(Service) Service
11
12 // LoggingMiddleware takes a logger as a dependency
13 // and returns a service Middleware.
14 func LoggingMiddleware(logger log.Logger) Middleware {
15 return func(next Service) Service {
16 return loggingMiddleware{logger, next}
17 }
18 }
19
20 type loggingMiddleware struct {
21 logger log.Logger
22 next Service
23 }
24
25 func (mw loggingMiddleware) Sum(ctx context.Context, a, b int) (v int, err error) {
26 defer func() {
27 mw.logger.Log("method", "Sum", "a", a, "b", b, "v", v, "err", err)
28 }()
29 return mw.next.Sum(ctx, a, b)
30 }
31
32 func (mw loggingMiddleware) Concat(ctx context.Context, a, b string) (v string, err error) {
33 defer func() {
34 mw.logger.Log("method", "Concat", "a", a, "b", b, "v", v, "err", err)
35 }()
36 return mw.next.Concat(ctx, a, b)
37 }
38
39 // InstrumentingMiddleware returns a service middleware that instruments
40 // the number of integers summed and characters concatenated over the lifetime of
41 // the service.
42 func InstrumentingMiddleware(ints, chars metrics.Counter) Middleware {
43 return func(next Service) Service {
44 return instrumentingMiddleware{
45 ints: ints,
46 chars: chars,
47 next: next,
48 }
49 }
50 }
51
52 type instrumentingMiddleware struct {
53 ints metrics.Counter
54 chars metrics.Counter
55 next Service
56 }
57
58 func (mw instrumentingMiddleware) Sum(ctx context.Context, a, b int) (int, error) {
59 v, err := mw.next.Sum(ctx, a, b)
60 mw.ints.Add(float64(v))
61 return v, err
62 }
63
64 func (mw instrumentingMiddleware) Concat(ctx context.Context, a, b string) (string, error) {
65 v, err := mw.next.Concat(ctx, a, b)
66 mw.chars.Add(float64(len(v)))
67 return v, err
68 }
+0
-71
examples/addsvc/pkg/addservice/service.go less more
0 package addservice
1
2 import (
3 "context"
4 "errors"
5
6 "github.com/go-kit/kit/log"
7 "github.com/go-kit/kit/metrics"
8 )
9
10 // Service describes a service that adds things together.
11 type Service interface {
12 Sum(ctx context.Context, a, b int) (int, error)
13 Concat(ctx context.Context, a, b string) (string, error)
14 }
15
16 // New returns a basic Service with all of the expected middlewares wired in.
17 func New(logger log.Logger, ints, chars metrics.Counter) Service {
18 var svc Service
19 {
20 svc = NewBasicService()
21 svc = LoggingMiddleware(logger)(svc)
22 svc = InstrumentingMiddleware(ints, chars)(svc)
23 }
24 return svc
25 }
26
27 var (
28 // ErrTwoZeroes is an arbitrary business rule for the Add method.
29 ErrTwoZeroes = errors.New("can't sum two zeroes")
30
31 // ErrIntOverflow protects the Add method. We've decided that this error
32 // indicates a misbehaving service and should count against e.g. circuit
33 // breakers. So, we return it directly in endpoints, to illustrate the
34 // difference. In a real service, this probably wouldn't be the case.
35 ErrIntOverflow = errors.New("integer overflow")
36
37 // ErrMaxSizeExceeded protects the Concat method.
38 ErrMaxSizeExceeded = errors.New("result exceeds maximum size")
39 )
40
41 // NewBasicService returns a naïve, stateless implementation of Service.
42 func NewBasicService() Service {
43 return basicService{}
44 }
45
46 type basicService struct{}
47
48 const (
49 intMax = 1<<31 - 1
50 intMin = -(intMax + 1)
51 maxLen = 10
52 )
53
54 func (s basicService) Sum(_ context.Context, a, b int) (int, error) {
55 if a == 0 && b == 0 {
56 return 0, ErrTwoZeroes
57 }
58 if (b > 0 && a > (intMax-b)) || (b < 0 && a < (intMin-b)) {
59 return 0, ErrIntOverflow
60 }
61 return a + b, nil
62 }
63
64 // Concat implements Service.
65 func (s basicService) Concat(_ context.Context, a, b string) (string, error) {
66 if len(a)+len(b) > maxLen {
67 return "", ErrMaxSizeExceeded
68 }
69 return a + b, nil
70 }
+0
-240
examples/addsvc/pkg/addtransport/grpc.go less more
0 package addtransport
1
2 import (
3 "context"
4 "errors"
5 "time"
6
7 "google.golang.org/grpc"
8
9 stdopentracing "github.com/opentracing/opentracing-go"
10 stdzipkin "github.com/openzipkin/zipkin-go"
11 "github.com/sony/gobreaker"
12 "golang.org/x/time/rate"
13
14 "github.com/go-kit/kit/circuitbreaker"
15 "github.com/go-kit/kit/endpoint"
16 "github.com/go-kit/kit/log"
17 "github.com/go-kit/kit/ratelimit"
18 "github.com/go-kit/kit/tracing/opentracing"
19 "github.com/go-kit/kit/tracing/zipkin"
20 "github.com/go-kit/kit/transport"
21 grpctransport "github.com/go-kit/kit/transport/grpc"
22
23 "github.com/go-kit/kit/examples/addsvc/pb"
24 "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint"
25 "github.com/go-kit/kit/examples/addsvc/pkg/addservice"
26 )
27
28 type grpcServer struct {
29 sum grpctransport.Handler
30 concat grpctransport.Handler
31 }
32
33 // NewGRPCServer makes a set of endpoints available as a gRPC AddServer.
34 func NewGRPCServer(endpoints addendpoint.Set, otTracer stdopentracing.Tracer, zipkinTracer *stdzipkin.Tracer, logger log.Logger) pb.AddServer {
35 options := []grpctransport.ServerOption{
36 grpctransport.ServerErrorHandler(transport.NewLogErrorHandler(logger)),
37 }
38
39 if zipkinTracer != nil {
40 // Zipkin GRPC Server Trace can either be instantiated per gRPC method with a
41 // provided operation name or a global tracing service can be instantiated
42 // without an operation name and fed to each Go kit gRPC server as a
43 // ServerOption.
44 // In the latter case, the operation name will be the endpoint's grpc method
45 // path if used in combination with the Go kit gRPC Interceptor.
46 //
47 // In this example, we demonstrate a global Zipkin tracing service with
48 // Go kit gRPC Interceptor.
49 options = append(options, zipkin.GRPCServerTrace(zipkinTracer))
50 }
51
52 return &grpcServer{
53 sum: grpctransport.NewServer(
54 endpoints.SumEndpoint,
55 decodeGRPCSumRequest,
56 encodeGRPCSumResponse,
57 append(options, grpctransport.ServerBefore(opentracing.GRPCToContext(otTracer, "Sum", logger)))...,
58 ),
59 concat: grpctransport.NewServer(
60 endpoints.ConcatEndpoint,
61 decodeGRPCConcatRequest,
62 encodeGRPCConcatResponse,
63 append(options, grpctransport.ServerBefore(opentracing.GRPCToContext(otTracer, "Concat", logger)))...,
64 ),
65 }
66 }
67
68 func (s *grpcServer) Sum(ctx context.Context, req *pb.SumRequest) (*pb.SumReply, error) {
69 _, rep, err := s.sum.ServeGRPC(ctx, req)
70 if err != nil {
71 return nil, err
72 }
73 return rep.(*pb.SumReply), nil
74 }
75
76 func (s *grpcServer) Concat(ctx context.Context, req *pb.ConcatRequest) (*pb.ConcatReply, error) {
77 _, rep, err := s.concat.ServeGRPC(ctx, req)
78 if err != nil {
79 return nil, err
80 }
81 return rep.(*pb.ConcatReply), nil
82 }
83
84 // NewGRPCClient returns an AddService backed by a gRPC server at the other end
85 // of the conn. The caller is responsible for constructing the conn, and
86 // eventually closing the underlying transport. We bake-in certain middlewares,
87 // implementing the client library pattern.
88 func NewGRPCClient(conn *grpc.ClientConn, otTracer stdopentracing.Tracer, zipkinTracer *stdzipkin.Tracer, logger log.Logger) addservice.Service {
89 // We construct a single ratelimiter middleware, to limit the total outgoing
90 // QPS from this client to all methods on the remote instance. We also
91 // construct per-endpoint circuitbreaker middlewares to demonstrate how
92 // that's done, although they could easily be combined into a single breaker
93 // for the entire remote instance, too.
94 limiter := ratelimit.NewErroringLimiter(rate.NewLimiter(rate.Every(time.Second), 100))
95
96 // global client middlewares
97 var options []grpctransport.ClientOption
98
99 if zipkinTracer != nil {
100 // Zipkin GRPC Client Trace can either be instantiated per gRPC method with a
101 // provided operation name or a global tracing client can be instantiated
102 // without an operation name and fed to each Go kit client as ClientOption.
103 // In the latter case, the operation name will be the endpoint's grpc method
104 // path.
105 //
106 // In this example, we demonstrace a global tracing client.
107 options = append(options, zipkin.GRPCClientTrace(zipkinTracer))
108
109 }
110 // Each individual endpoint is an grpc/transport.Client (which implements
111 // endpoint.Endpoint) that gets wrapped with various middlewares. If you
112 // made your own client library, you'd do this work there, so your server
113 // could rely on a consistent set of client behavior.
114 var sumEndpoint endpoint.Endpoint
115 {
116 sumEndpoint = grpctransport.NewClient(
117 conn,
118 "pb.Add",
119 "Sum",
120 encodeGRPCSumRequest,
121 decodeGRPCSumResponse,
122 pb.SumReply{},
123 append(options, grpctransport.ClientBefore(opentracing.ContextToGRPC(otTracer, logger)))...,
124 ).Endpoint()
125 sumEndpoint = opentracing.TraceClient(otTracer, "Sum")(sumEndpoint)
126 sumEndpoint = limiter(sumEndpoint)
127 sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
128 Name: "Sum",
129 Timeout: 30 * time.Second,
130 }))(sumEndpoint)
131 }
132
133 // The Concat endpoint is the same thing, with slightly different
134 // middlewares to demonstrate how to specialize per-endpoint.
135 var concatEndpoint endpoint.Endpoint
136 {
137 concatEndpoint = grpctransport.NewClient(
138 conn,
139 "pb.Add",
140 "Concat",
141 encodeGRPCConcatRequest,
142 decodeGRPCConcatResponse,
143 pb.ConcatReply{},
144 append(options, grpctransport.ClientBefore(opentracing.ContextToGRPC(otTracer, logger)))...,
145 ).Endpoint()
146 concatEndpoint = opentracing.TraceClient(otTracer, "Concat")(concatEndpoint)
147 concatEndpoint = limiter(concatEndpoint)
148 concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
149 Name: "Concat",
150 Timeout: 10 * time.Second,
151 }))(concatEndpoint)
152 }
153
154 // Returning the endpoint.Set as a service.Service relies on the
155 // endpoint.Set implementing the Service methods. That's just a simple bit
156 // of glue code.
157 return addendpoint.Set{
158 SumEndpoint: sumEndpoint,
159 ConcatEndpoint: concatEndpoint,
160 }
161 }
162
163 // decodeGRPCSumRequest is a transport/grpc.DecodeRequestFunc that converts a
164 // gRPC sum request to a user-domain sum request. Primarily useful in a server.
165 func decodeGRPCSumRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
166 req := grpcReq.(*pb.SumRequest)
167 return addendpoint.SumRequest{A: int(req.A), B: int(req.B)}, nil
168 }
169
170 // decodeGRPCConcatRequest is a transport/grpc.DecodeRequestFunc that converts a
171 // gRPC concat request to a user-domain concat request. Primarily useful in a
172 // server.
173 func decodeGRPCConcatRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
174 req := grpcReq.(*pb.ConcatRequest)
175 return addendpoint.ConcatRequest{A: req.A, B: req.B}, nil
176 }
177
178 // decodeGRPCSumResponse is a transport/grpc.DecodeResponseFunc that converts a
179 // gRPC sum reply to a user-domain sum response. Primarily useful in a client.
180 func decodeGRPCSumResponse(_ context.Context, grpcReply interface{}) (interface{}, error) {
181 reply := grpcReply.(*pb.SumReply)
182 return addendpoint.SumResponse{V: int(reply.V), Err: str2err(reply.Err)}, nil
183 }
184
185 // decodeGRPCConcatResponse is a transport/grpc.DecodeResponseFunc that converts
186 // a gRPC concat reply to a user-domain concat response. Primarily useful in a
187 // client.
188 func decodeGRPCConcatResponse(_ context.Context, grpcReply interface{}) (interface{}, error) {
189 reply := grpcReply.(*pb.ConcatReply)
190 return addendpoint.ConcatResponse{V: reply.V, Err: str2err(reply.Err)}, nil
191 }
192
193 // encodeGRPCSumResponse is a transport/grpc.EncodeResponseFunc that converts a
194 // user-domain sum response to a gRPC sum reply. Primarily useful in a server.
195 func encodeGRPCSumResponse(_ context.Context, response interface{}) (interface{}, error) {
196 resp := response.(addendpoint.SumResponse)
197 return &pb.SumReply{V: int64(resp.V), Err: err2str(resp.Err)}, nil
198 }
199
200 // encodeGRPCConcatResponse is a transport/grpc.EncodeResponseFunc that converts
201 // a user-domain concat response to a gRPC concat reply. Primarily useful in a
202 // server.
203 func encodeGRPCConcatResponse(_ context.Context, response interface{}) (interface{}, error) {
204 resp := response.(addendpoint.ConcatResponse)
205 return &pb.ConcatReply{V: resp.V, Err: err2str(resp.Err)}, nil
206 }
207
208 // encodeGRPCSumRequest is a transport/grpc.EncodeRequestFunc that converts a
209 // user-domain sum request to a gRPC sum request. Primarily useful in a client.
210 func encodeGRPCSumRequest(_ context.Context, request interface{}) (interface{}, error) {
211 req := request.(addendpoint.SumRequest)
212 return &pb.SumRequest{A: int64(req.A), B: int64(req.B)}, nil
213 }
214
215 // encodeGRPCConcatRequest is a transport/grpc.EncodeRequestFunc that converts a
216 // user-domain concat request to a gRPC concat request. Primarily useful in a
217 // client.
218 func encodeGRPCConcatRequest(_ context.Context, request interface{}) (interface{}, error) {
219 req := request.(addendpoint.ConcatRequest)
220 return &pb.ConcatRequest{A: req.A, B: req.B}, nil
221 }
222
223 // These annoying helper functions are required to translate Go error types to
224 // and from strings, which is the type we use in our IDLs to represent errors.
225 // There is special casing to treat empty strings as nil errors.
226
227 func str2err(s string) error {
228 if s == "" {
229 return nil
230 }
231 return errors.New(s)
232 }
233
234 func err2str(err error) string {
235 if err == nil {
236 return ""
237 }
238 return err.Error()
239 }
+0
-250
examples/addsvc/pkg/addtransport/http.go less more
0 package addtransport
1
2 import (
3 "bytes"
4 "context"
5 "encoding/json"
6 "errors"
7 "io/ioutil"
8 "net/http"
9 "net/url"
10 "strings"
11 "time"
12
13 "golang.org/x/time/rate"
14
15 stdopentracing "github.com/opentracing/opentracing-go"
16 stdzipkin "github.com/openzipkin/zipkin-go"
17 "github.com/sony/gobreaker"
18
19 "github.com/go-kit/kit/circuitbreaker"
20 "github.com/go-kit/kit/endpoint"
21 "github.com/go-kit/kit/log"
22 "github.com/go-kit/kit/ratelimit"
23 "github.com/go-kit/kit/tracing/opentracing"
24 "github.com/go-kit/kit/tracing/zipkin"
25 "github.com/go-kit/kit/transport"
26 httptransport "github.com/go-kit/kit/transport/http"
27
28 "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint"
29 "github.com/go-kit/kit/examples/addsvc/pkg/addservice"
30 )
31
32 // NewHTTPHandler returns an HTTP handler that makes a set of endpoints
33 // available on predefined paths.
34 func NewHTTPHandler(endpoints addendpoint.Set, otTracer stdopentracing.Tracer, zipkinTracer *stdzipkin.Tracer, logger log.Logger) http.Handler {
35 options := []httptransport.ServerOption{
36 httptransport.ServerErrorEncoder(errorEncoder),
37 httptransport.ServerErrorHandler(transport.NewLogErrorHandler(logger)),
38 }
39
40 if zipkinTracer != nil {
41 // Zipkin HTTP Server Trace can either be instantiated per endpoint with a
42 // provided operation name or a global tracing service can be instantiated
43 // without an operation name and fed to each Go kit endpoint as ServerOption.
44 // In the latter case, the operation name will be the endpoint's http method.
45 // We demonstrate a global tracing service here.
46 options = append(options, zipkin.HTTPServerTrace(zipkinTracer))
47 }
48
49 m := http.NewServeMux()
50 m.Handle("/sum", httptransport.NewServer(
51 endpoints.SumEndpoint,
52 decodeHTTPSumRequest,
53 encodeHTTPGenericResponse,
54 append(options, httptransport.ServerBefore(opentracing.HTTPToContext(otTracer, "Sum", logger)))...,
55 ))
56 m.Handle("/concat", httptransport.NewServer(
57 endpoints.ConcatEndpoint,
58 decodeHTTPConcatRequest,
59 encodeHTTPGenericResponse,
60 append(options, httptransport.ServerBefore(opentracing.HTTPToContext(otTracer, "Concat", logger)))...,
61 ))
62 return m
63 }
64
65 // NewHTTPClient returns an AddService backed by an HTTP server living at the
66 // remote instance. We expect instance to come from a service discovery system,
67 // so likely of the form "host:port". We bake-in certain middlewares,
68 // implementing the client library pattern.
69 func NewHTTPClient(instance string, otTracer stdopentracing.Tracer, zipkinTracer *stdzipkin.Tracer, logger log.Logger) (addservice.Service, error) {
70 // Quickly sanitize the instance string.
71 if !strings.HasPrefix(instance, "http") {
72 instance = "http://" + instance
73 }
74 u, err := url.Parse(instance)
75 if err != nil {
76 return nil, err
77 }
78
79 // We construct a single ratelimiter middleware, to limit the total outgoing
80 // QPS from this client to all methods on the remote instance. We also
81 // construct per-endpoint circuitbreaker middlewares to demonstrate how
82 // that's done, although they could easily be combined into a single breaker
83 // for the entire remote instance, too.
84 limiter := ratelimit.NewErroringLimiter(rate.NewLimiter(rate.Every(time.Second), 100))
85
86 // global client middlewares
87 var options []httptransport.ClientOption
88
89 if zipkinTracer != nil {
90 // Zipkin HTTP Client Trace can either be instantiated per endpoint with a
91 // provided operation name or a global tracing client can be instantiated
92 // without an operation name and fed to each Go kit endpoint as ClientOption.
93 // In the latter case, the operation name will be the endpoint's http method.
94 options = append(options, zipkin.HTTPClientTrace(zipkinTracer))
95 }
96
97 // Each individual endpoint is an http/transport.Client (which implements
98 // endpoint.Endpoint) that gets wrapped with various middlewares. If you
99 // made your own client library, you'd do this work there, so your server
100 // could rely on a consistent set of client behavior.
101 var sumEndpoint endpoint.Endpoint
102 {
103 sumEndpoint = httptransport.NewClient(
104 "POST",
105 copyURL(u, "/sum"),
106 encodeHTTPGenericRequest,
107 decodeHTTPSumResponse,
108 append(options, httptransport.ClientBefore(opentracing.ContextToHTTP(otTracer, logger)))...,
109 ).Endpoint()
110 sumEndpoint = opentracing.TraceClient(otTracer, "Sum")(sumEndpoint)
111 if zipkinTracer != nil {
112 sumEndpoint = zipkin.TraceEndpoint(zipkinTracer, "Sum")(sumEndpoint)
113 }
114 sumEndpoint = limiter(sumEndpoint)
115 sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
116 Name: "Sum",
117 Timeout: 30 * time.Second,
118 }))(sumEndpoint)
119 }
120
121 // The Concat endpoint is the same thing, with slightly different
122 // middlewares to demonstrate how to specialize per-endpoint.
123 var concatEndpoint endpoint.Endpoint
124 {
125 concatEndpoint = httptransport.NewClient(
126 "POST",
127 copyURL(u, "/concat"),
128 encodeHTTPGenericRequest,
129 decodeHTTPConcatResponse,
130 append(options, httptransport.ClientBefore(opentracing.ContextToHTTP(otTracer, logger)))...,
131 ).Endpoint()
132 concatEndpoint = opentracing.TraceClient(otTracer, "Concat")(concatEndpoint)
133 if zipkinTracer != nil {
134 concatEndpoint = zipkin.TraceEndpoint(zipkinTracer, "Concat")(concatEndpoint)
135 }
136 concatEndpoint = limiter(concatEndpoint)
137 concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
138 Name: "Concat",
139 Timeout: 10 * time.Second,
140 }))(concatEndpoint)
141 }
142
143 // Returning the endpoint.Set as a service.Service relies on the
144 // endpoint.Set implementing the Service methods. That's just a simple bit
145 // of glue code.
146 return addendpoint.Set{
147 SumEndpoint: sumEndpoint,
148 ConcatEndpoint: concatEndpoint,
149 }, nil
150 }
151
152 func copyURL(base *url.URL, path string) *url.URL {
153 next := *base
154 next.Path = path
155 return &next
156 }
157
158 func errorEncoder(_ context.Context, err error, w http.ResponseWriter) {
159 w.WriteHeader(err2code(err))
160 json.NewEncoder(w).Encode(errorWrapper{Error: err.Error()})
161 }
162
163 func err2code(err error) int {
164 switch err {
165 case addservice.ErrTwoZeroes, addservice.ErrMaxSizeExceeded, addservice.ErrIntOverflow:
166 return http.StatusBadRequest
167 }
168 return http.StatusInternalServerError
169 }
170
171 func errorDecoder(r *http.Response) error {
172 var w errorWrapper
173 if err := json.NewDecoder(r.Body).Decode(&w); err != nil {
174 return err
175 }
176 return errors.New(w.Error)
177 }
178
179 type errorWrapper struct {
180 Error string `json:"error"`
181 }
182
183 // decodeHTTPSumRequest is a transport/http.DecodeRequestFunc that decodes a
184 // JSON-encoded sum request from the HTTP request body. Primarily useful in a
185 // server.
186 func decodeHTTPSumRequest(_ context.Context, r *http.Request) (interface{}, error) {
187 var req addendpoint.SumRequest
188 err := json.NewDecoder(r.Body).Decode(&req)
189 return req, err
190 }
191
192 // decodeHTTPConcatRequest is a transport/http.DecodeRequestFunc that decodes a
193 // JSON-encoded concat request from the HTTP request body. Primarily useful in a
194 // server.
195 func decodeHTTPConcatRequest(_ context.Context, r *http.Request) (interface{}, error) {
196 var req addendpoint.ConcatRequest
197 err := json.NewDecoder(r.Body).Decode(&req)
198 return req, err
199 }
200
201 // decodeHTTPSumResponse is a transport/http.DecodeResponseFunc that decodes a
202 // JSON-encoded sum response from the HTTP response body. If the response has a
203 // non-200 status code, we will interpret that as an error and attempt to decode
204 // the specific error message from the response body. Primarily useful in a
205 // client.
206 func decodeHTTPSumResponse(_ context.Context, r *http.Response) (interface{}, error) {
207 if r.StatusCode != http.StatusOK {
208 return nil, errors.New(r.Status)
209 }
210 var resp addendpoint.SumResponse
211 err := json.NewDecoder(r.Body).Decode(&resp)
212 return resp, err
213 }
214
215 // decodeHTTPConcatResponse is a transport/http.DecodeResponseFunc that decodes
216 // a JSON-encoded concat response from the HTTP response body. If the response
217 // has a non-200 status code, we will interpret that as an error and attempt to
218 // decode the specific error message from the response body. Primarily useful in
219 // a client.
220 func decodeHTTPConcatResponse(_ context.Context, r *http.Response) (interface{}, error) {
221 if r.StatusCode != http.StatusOK {
222 return nil, errors.New(r.Status)
223 }
224 var resp addendpoint.ConcatResponse
225 err := json.NewDecoder(r.Body).Decode(&resp)
226 return resp, err
227 }
228
229 // encodeHTTPGenericRequest is a transport/http.EncodeRequestFunc that
230 // JSON-encodes any request to the request body. Primarily useful in a client.
231 func encodeHTTPGenericRequest(_ context.Context, r *http.Request, request interface{}) error {
232 var buf bytes.Buffer
233 if err := json.NewEncoder(&buf).Encode(request); err != nil {
234 return err
235 }
236 r.Body = ioutil.NopCloser(&buf)
237 return nil
238 }
239
240 // encodeHTTPGenericResponse is a transport/http.EncodeResponseFunc that encodes
241 // the response as JSON to the response writer. Primarily useful in a server.
242 func encodeHTTPGenericResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
243 if f, ok := response.(endpoint.Failer); ok && f.Failed() != nil {
244 errorEncoder(ctx, f.Failed(), w)
245 return nil
246 }
247 w.Header().Set("Content-Type", "application/json; charset=utf-8")
248 return json.NewEncoder(w).Encode(response)
249 }
+0
-213
examples/addsvc/pkg/addtransport/jsonrpc.go less more
0 package addtransport
1
2 import (
3 "context"
4 "encoding/json"
5 "fmt"
6 "net/url"
7 "strings"
8 "time"
9
10 "golang.org/x/time/rate"
11
12 "github.com/go-kit/kit/circuitbreaker"
13 "github.com/go-kit/kit/endpoint"
14 "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint"
15 "github.com/go-kit/kit/examples/addsvc/pkg/addservice"
16 "github.com/go-kit/kit/log"
17 "github.com/go-kit/kit/ratelimit"
18 "github.com/go-kit/kit/tracing/opentracing"
19 "github.com/go-kit/kit/transport/http/jsonrpc"
20 stdopentracing "github.com/opentracing/opentracing-go"
21 "github.com/sony/gobreaker"
22 )
23
24 // NewJSONRPCHandler returns a JSON RPC Server/Handler that can be passed to http.Handle()
25 func NewJSONRPCHandler(endpoints addendpoint.Set, logger log.Logger) *jsonrpc.Server {
26 handler := jsonrpc.NewServer(
27 makeEndpointCodecMap(endpoints),
28 jsonrpc.ServerErrorLogger(logger),
29 )
30 return handler
31 }
32
33 // NewJSONRPCClient returns an addservice backed by a JSON RPC over HTTP server
34 // living at the remote instance. We expect instance to come from a service
35 // discovery system, so likely of the form "host:port". We bake-in certain
36 // middlewares, implementing the client library pattern.
37 func NewJSONRPCClient(instance string, tracer stdopentracing.Tracer, logger log.Logger) (addservice.Service, error) {
38 // Quickly sanitize the instance string.
39 if !strings.HasPrefix(instance, "http") {
40 instance = "http://" + instance
41 }
42 u, err := url.Parse(instance)
43 if err != nil {
44 return nil, err
45 }
46
47 // We construct a single ratelimiter middleware, to limit the total outgoing
48 // QPS from this client to all methods on the remote instance. We also
49 // construct per-endpoint circuitbreaker middlewares to demonstrate how
50 // that's done, although they could easily be combined into a single breaker
51 // for the entire remote instance, too.
52 limiter := ratelimit.NewErroringLimiter(rate.NewLimiter(rate.Every(time.Second), 100))
53
54 var sumEndpoint endpoint.Endpoint
55 {
56 sumEndpoint = jsonrpc.NewClient(
57 u,
58 "sum",
59 jsonrpc.ClientRequestEncoder(encodeSumRequest),
60 jsonrpc.ClientResponseDecoder(decodeSumResponse),
61 ).Endpoint()
62 sumEndpoint = opentracing.TraceClient(tracer, "Sum")(sumEndpoint)
63 sumEndpoint = limiter(sumEndpoint)
64 sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
65 Name: "Sum",
66 Timeout: 30 * time.Second,
67 }))(sumEndpoint)
68 }
69
70 var concatEndpoint endpoint.Endpoint
71 {
72 concatEndpoint = jsonrpc.NewClient(
73 u,
74 "concat",
75 jsonrpc.ClientRequestEncoder(encodeConcatRequest),
76 jsonrpc.ClientResponseDecoder(decodeConcatResponse),
77 ).Endpoint()
78 concatEndpoint = opentracing.TraceClient(tracer, "Concat")(concatEndpoint)
79 concatEndpoint = limiter(concatEndpoint)
80 concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
81 Name: "Concat",
82 Timeout: 30 * time.Second,
83 }))(concatEndpoint)
84 }
85
86 // Returning the endpoint.Set as a service.Service relies on the
87 // endpoint.Set implementing the Service methods. That's just a simple bit
88 // of glue code.
89 return addendpoint.Set{
90 SumEndpoint: sumEndpoint,
91 ConcatEndpoint: concatEndpoint,
92 }, nil
93
94 }
95
96 // makeEndpointCodecMap returns a codec map configured for the addsvc.
97 func makeEndpointCodecMap(endpoints addendpoint.Set) jsonrpc.EndpointCodecMap {
98 return jsonrpc.EndpointCodecMap{
99 "sum": jsonrpc.EndpointCodec{
100 Endpoint: endpoints.SumEndpoint,
101 Decode: decodeSumRequest,
102 Encode: encodeSumResponse,
103 },
104 "concat": jsonrpc.EndpointCodec{
105 Endpoint: endpoints.ConcatEndpoint,
106 Decode: decodeConcatRequest,
107 Encode: encodeConcatResponse,
108 },
109 }
110 }
111
112 func decodeSumRequest(_ context.Context, msg json.RawMessage) (interface{}, error) {
113 var req addendpoint.SumRequest
114 err := json.Unmarshal(msg, &req)
115 if err != nil {
116 return nil, &jsonrpc.Error{
117 Code: -32000,
118 Message: fmt.Sprintf("couldn't unmarshal body to sum request: %s", err),
119 }
120 }
121 return req, nil
122 }
123
124 func encodeSumResponse(_ context.Context, obj interface{}) (json.RawMessage, error) {
125 res, ok := obj.(addendpoint.SumResponse)
126 if !ok {
127 return nil, &jsonrpc.Error{
128 Code: -32000,
129 Message: fmt.Sprintf("Asserting result to *SumResponse failed. Got %T, %+v", obj, obj),
130 }
131 }
132 b, err := json.Marshal(res)
133 if err != nil {
134 return nil, fmt.Errorf("couldn't marshal response: %s", err)
135 }
136 return b, nil
137 }
138
139 func decodeSumResponse(_ context.Context, res jsonrpc.Response) (interface{}, error) {
140 if res.Error != nil {
141 return nil, *res.Error
142 }
143 var sumres addendpoint.SumResponse
144 err := json.Unmarshal(res.Result, &sumres)
145 if err != nil {
146 return nil, fmt.Errorf("couldn't unmarshal body to SumResponse: %s", err)
147 }
148 return sumres, nil
149 }
150
151 func encodeSumRequest(_ context.Context, obj interface{}) (json.RawMessage, error) {
152 req, ok := obj.(addendpoint.SumRequest)
153 if !ok {
154 return nil, fmt.Errorf("couldn't assert request as SumRequest, got %T", obj)
155 }
156 b, err := json.Marshal(req)
157 if err != nil {
158 return nil, fmt.Errorf("couldn't marshal request: %s", err)
159 }
160 return b, nil
161 }
162
163 func decodeConcatRequest(_ context.Context, msg json.RawMessage) (interface{}, error) {
164 var req addendpoint.ConcatRequest
165 err := json.Unmarshal(msg, &req)
166 if err != nil {
167 return nil, &jsonrpc.Error{
168 Code: -32000,
169 Message: fmt.Sprintf("couldn't unmarshal body to concat request: %s", err),
170 }
171 }
172 return req, nil
173 }
174
175 func encodeConcatResponse(_ context.Context, obj interface{}) (json.RawMessage, error) {
176 res, ok := obj.(addendpoint.ConcatResponse)
177 if !ok {
178 return nil, &jsonrpc.Error{
179 Code: -32000,
180 Message: fmt.Sprintf("Asserting result to *ConcatResponse failed. Got %T, %+v", obj, obj),
181 }
182 }
183 b, err := json.Marshal(res)
184 if err != nil {
185 return nil, fmt.Errorf("couldn't marshal response: %s", err)
186 }
187 return b, nil
188 }
189
190 func decodeConcatResponse(_ context.Context, res jsonrpc.Response) (interface{}, error) {
191 if res.Error != nil {
192 return nil, *res.Error
193 }
194 var concatres addendpoint.ConcatResponse
195 err := json.Unmarshal(res.Result, &concatres)
196 if err != nil {
197 return nil, fmt.Errorf("couldn't unmarshal body to ConcatResponse: %s", err)
198 }
199 return concatres, nil
200 }
201
202 func encodeConcatRequest(_ context.Context, obj interface{}) (json.RawMessage, error) {
203 req, ok := obj.(addendpoint.ConcatRequest)
204 if !ok {
205 return nil, fmt.Errorf("couldn't assert request as ConcatRequest, got %T", obj)
206 }
207 b, err := json.Marshal(req)
208 if err != nil {
209 return nil, fmt.Errorf("couldn't marshal request: %s", err)
210 }
211 return b, nil
212 }
+0
-120
examples/addsvc/pkg/addtransport/thrift.go less more
0 package addtransport
1
2 import (
3 "context"
4 "time"
5
6 "golang.org/x/time/rate"
7
8 "github.com/sony/gobreaker"
9
10 "github.com/go-kit/kit/circuitbreaker"
11 "github.com/go-kit/kit/endpoint"
12 "github.com/go-kit/kit/ratelimit"
13
14 "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint"
15 "github.com/go-kit/kit/examples/addsvc/pkg/addservice"
16 addthrift "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc"
17 )
18
19 type thriftServer struct {
20 ctx context.Context
21 endpoints addendpoint.Set
22 }
23
24 // NewThriftServer makes a set of endpoints available as a Thrift service.
25 func NewThriftServer(endpoints addendpoint.Set) addthrift.AddService {
26 return &thriftServer{
27 endpoints: endpoints,
28 }
29 }
30
31 func (s *thriftServer) Sum(ctx context.Context, a int64, b int64) (*addthrift.SumReply, error) {
32 request := addendpoint.SumRequest{A: int(a), B: int(b)}
33 response, err := s.endpoints.SumEndpoint(ctx, request)
34 if err != nil {
35 return nil, err
36 }
37 resp := response.(addendpoint.SumResponse)
38 return &addthrift.SumReply{Value: int64(resp.V), Err: err2str(resp.Err)}, nil
39 }
40
41 func (s *thriftServer) Concat(ctx context.Context, a string, b string) (*addthrift.ConcatReply, error) {
42 request := addendpoint.ConcatRequest{A: a, B: b}
43 response, err := s.endpoints.ConcatEndpoint(ctx, request)
44 if err != nil {
45 return nil, err
46 }
47 resp := response.(addendpoint.ConcatResponse)
48 return &addthrift.ConcatReply{Value: resp.V, Err: err2str(resp.Err)}, nil
49 }
50
51 // NewThriftClient returns an AddService backed by a Thrift server described by
52 // the provided client. The caller is responsible for constructing the client,
53 // and eventually closing the underlying transport. We bake-in certain middlewares,
54 // implementing the client library pattern.
55 func NewThriftClient(client *addthrift.AddServiceClient) addservice.Service {
56 // We construct a single ratelimiter middleware, to limit the total outgoing
57 // QPS from this client to all methods on the remote instance. We also
58 // construct per-endpoint circuitbreaker middlewares to demonstrate how
59 // that's done, although they could easily be combined into a single breaker
60 // for the entire remote instance, too.
61 limiter := ratelimit.NewErroringLimiter(rate.NewLimiter(rate.Every(time.Second), 100))
62
63 // Each individual endpoint is an http/transport.Client (which implements
64 // endpoint.Endpoint) that gets wrapped with various middlewares. If you
65 // could rely on a consistent set of client behavior.
66 var sumEndpoint endpoint.Endpoint
67 {
68 sumEndpoint = MakeThriftSumEndpoint(client)
69 sumEndpoint = limiter(sumEndpoint)
70 sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
71 Name: "Sum",
72 Timeout: 30 * time.Second,
73 }))(sumEndpoint)
74 }
75
76 // The Concat endpoint is the same thing, with slightly different
77 // middlewares to demonstrate how to specialize per-endpoint.
78 var concatEndpoint endpoint.Endpoint
79 {
80 concatEndpoint = MakeThriftConcatEndpoint(client)
81 concatEndpoint = limiter(concatEndpoint)
82 concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{
83 Name: "Concat",
84 Timeout: 10 * time.Second,
85 }))(concatEndpoint)
86 }
87
88 // Returning the endpoint.Set as a service.Service relies on the
89 // endpoint.Set implementing the Service methods. That's just a simple bit
90 // of glue code.
91 return addendpoint.Set{
92 SumEndpoint: sumEndpoint,
93 ConcatEndpoint: concatEndpoint,
94 }
95 }
96
97 // MakeThriftSumEndpoint returns an endpoint that invokes the passed Thrift client.
98 // Useful only in clients, and only until a proper transport/thrift.Client exists.
99 func MakeThriftSumEndpoint(client *addthrift.AddServiceClient) endpoint.Endpoint {
100 return func(ctx context.Context, request interface{}) (interface{}, error) {
101 req := request.(addendpoint.SumRequest)
102 reply, err := client.Sum(ctx, int64(req.A), int64(req.B))
103 if err == addservice.ErrIntOverflow {
104 return nil, err // special case; see comment on ErrIntOverflow
105 }
106 return addendpoint.SumResponse{V: int(reply.Value), Err: err}, nil
107 }
108 }
109
110 // MakeThriftConcatEndpoint returns an endpoint that invokes the passed Thrift
111 // client. Useful only in clients, and only until a proper
112 // transport/thrift.Client exists.
113 func MakeThriftConcatEndpoint(client *addthrift.AddServiceClient) endpoint.Endpoint {
114 return func(ctx context.Context, request interface{}) (interface{}, error) {
115 req := request.(addendpoint.ConcatRequest)
116 reply, err := client.Concat(ctx, req.A, req.B)
117 return addendpoint.ConcatResponse{V: reply.Value, Err: err}, nil
118 }
119 }
+0
-14
examples/addsvc/thrift/addsvc.thrift less more
0 struct SumReply {
1 1: i64 value
2 2: string err
3 }
4
5 struct ConcatReply {
6 1: string value
7 2: string err
8 }
9
10 service AddService {
11 SumReply Sum(1: i64 a, 2: i64 b)
12 ConcatReply Concat(1: string a, 2: string b)
13 }
+0
-18
examples/addsvc/thrift/compile.sh less more
0 #!/usr/bin/env sh
1
2 # See also https://thrift.apache.org/tutorial/go.
3 #
4 # An old version can be obtained via `brew install thrift`.
5 # For the latest, here's the annoying dance:
6 #
7 # brew install automake bison pkg-config openssl
8 # ln -s /usr/local/opt/openssl/include/openssl /usr/local/include # if it isn't already
9 # git clone git@github.com:apache/thrift
10 # ./bootstrap.sh
11 # bash
12 # export PATH=/usr/local/Cellar/bison/*/bin:$PATH
13 # ./configure ./configure --without-qt4 --without-qt5 --without-c_glib --without-csharp --without-java --without-erlang --without-nodejs --without-lua --without-python --without-perl --without-php --without-php_extension --without-dart --without-ruby --without-haskell --without-rs --without-cl --without-haxe --without-dotnetcore --without-d
14 # make
15 # sudo make install
16
17 thrift -r --gen "go:package_prefix=github.com/go-kit/kit/examples/addsvc/thrift/gen-go/,thrift_import=github.com/apache/thrift/lib/go/thrift" addsvc.thrift
+0
-5
examples/addsvc/thrift/gen-go/addsvc/GoUnusedProtection__.go less more
0 // Code generated by Thrift Compiler (0.14.1). DO NOT EDIT.
1
2 package addsvc
3
4 var GoUnusedProtection__ int
+0
-186
examples/addsvc/thrift/gen-go/addsvc/add_service-remote/add_service-remote.go less more
0 // Code generated by Thrift Compiler (0.14.1). DO NOT EDIT.
1
2 package main
3
4 import (
5 "context"
6 "flag"
7 "fmt"
8 "github.com/apache/thrift/lib/go/thrift"
9 "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc"
10 "math"
11 "net"
12 "net/url"
13 "os"
14 "strconv"
15 "strings"
16 )
17
18 var _ = addsvc.GoUnusedProtection__
19
20 func Usage() {
21 fmt.Fprintln(os.Stderr, "Usage of ", os.Args[0], " [-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:")
22 flag.PrintDefaults()
23 fmt.Fprintln(os.Stderr, "\nFunctions:")
24 fmt.Fprintln(os.Stderr, " SumReply Sum(i64 a, i64 b)")
25 fmt.Fprintln(os.Stderr, " ConcatReply Concat(string a, string b)")
26 fmt.Fprintln(os.Stderr)
27 os.Exit(0)
28 }
29
30 type httpHeaders map[string]string
31
32 func (h httpHeaders) String() string {
33 var m map[string]string = h
34 return fmt.Sprintf("%s", m)
35 }
36
37 func (h httpHeaders) Set(value string) error {
38 parts := strings.Split(value, ": ")
39 if len(parts) != 2 {
40 return fmt.Errorf("header should be of format 'Key: Value'")
41 }
42 h[parts[0]] = parts[1]
43 return nil
44 }
45
46 func main() {
47 flag.Usage = Usage
48 var host string
49 var port int
50 var protocol string
51 var urlString string
52 var framed bool
53 var useHttp bool
54 headers := make(httpHeaders)
55 var parsedUrl *url.URL
56 var trans thrift.TTransport
57 _ = strconv.Atoi
58 _ = math.Abs
59 flag.Usage = Usage
60 flag.StringVar(&host, "h", "localhost", "Specify host and port")
61 flag.IntVar(&port, "p", 9090, "Specify port")
62 flag.StringVar(&protocol, "P", "binary", "Specify the protocol (binary, compact, simplejson, json)")
63 flag.StringVar(&urlString, "u", "", "Specify the url")
64 flag.BoolVar(&framed, "framed", false, "Use framed transport")
65 flag.BoolVar(&useHttp, "http", false, "Use http")
66 flag.Var(headers, "H", "Headers to set on the http(s) request (e.g. -H \"Key: Value\")")
67 flag.Parse()
68
69 if len(urlString) > 0 {
70 var err error
71 parsedUrl, err = url.Parse(urlString)
72 if err != nil {
73 fmt.Fprintln(os.Stderr, "Error parsing URL: ", err)
74 flag.Usage()
75 }
76 host = parsedUrl.Host
77 useHttp = len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == "http" || parsedUrl.Scheme == "https"
78 } else if useHttp {
79 _, err := url.Parse(fmt.Sprint("http://", host, ":", port))
80 if err != nil {
81 fmt.Fprintln(os.Stderr, "Error parsing URL: ", err)
82 flag.Usage()
83 }
84 }
85
86 cmd := flag.Arg(0)
87 var err error
88 if useHttp {
89 trans, err = thrift.NewTHttpClient(parsedUrl.String())
90 if len(headers) > 0 {
91 httptrans := trans.(*thrift.THttpClient)
92 for key, value := range headers {
93 httptrans.SetHeader(key, value)
94 }
95 }
96 } else {
97 portStr := fmt.Sprint(port)
98 if strings.Contains(host, ":") {
99 host, portStr, err = net.SplitHostPort(host)
100 if err != nil {
101 fmt.Fprintln(os.Stderr, "error with host:", err)
102 os.Exit(1)
103 }
104 }
105 trans, err = thrift.NewTSocket(net.JoinHostPort(host, portStr))
106 if err != nil {
107 fmt.Fprintln(os.Stderr, "error resolving address:", err)
108 os.Exit(1)
109 }
110 if framed {
111 trans = thrift.NewTFramedTransport(trans)
112 }
113 }
114 if err != nil {
115 fmt.Fprintln(os.Stderr, "Error creating transport", err)
116 os.Exit(1)
117 }
118 defer trans.Close()
119 var protocolFactory thrift.TProtocolFactory
120 switch protocol {
121 case "compact":
122 protocolFactory = thrift.NewTCompactProtocolFactory()
123 break
124 case "simplejson":
125 protocolFactory = thrift.NewTSimpleJSONProtocolFactory()
126 break
127 case "json":
128 protocolFactory = thrift.NewTJSONProtocolFactory()
129 break
130 case "binary", "":
131 protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
132 break
133 default:
134 fmt.Fprintln(os.Stderr, "Invalid protocol specified: ", protocol)
135 Usage()
136 os.Exit(1)
137 }
138 iprot := protocolFactory.GetProtocol(trans)
139 oprot := protocolFactory.GetProtocol(trans)
140 client := addsvc.NewAddServiceClient(thrift.NewTStandardClient(iprot, oprot))
141 if err := trans.Open(); err != nil {
142 fmt.Fprintln(os.Stderr, "Error opening socket to ", host, ":", port, " ", err)
143 os.Exit(1)
144 }
145
146 switch cmd {
147 case "Sum":
148 if flag.NArg()-1 != 2 {
149 fmt.Fprintln(os.Stderr, "Sum requires 2 args")
150 flag.Usage()
151 }
152 argvalue0, err8 := (strconv.ParseInt(flag.Arg(1), 10, 64))
153 if err8 != nil {
154 Usage()
155 return
156 }
157 value0 := argvalue0
158 argvalue1, err9 := (strconv.ParseInt(flag.Arg(2), 10, 64))
159 if err9 != nil {
160 Usage()
161 return
162 }
163 value1 := argvalue1
164 fmt.Print(client.Sum(context.Background(), value0, value1))
165 fmt.Print("\n")
166 break
167 case "Concat":
168 if flag.NArg()-1 != 2 {
169 fmt.Fprintln(os.Stderr, "Concat requires 2 args")
170 flag.Usage()
171 }
172 argvalue0 := flag.Arg(1)
173 value0 := argvalue0
174 argvalue1 := flag.Arg(2)
175 value1 := argvalue1
176 fmt.Print(client.Concat(context.Background(), value0, value1))
177 fmt.Print("\n")
178 break
179 case "":
180 Usage()
181 break
182 default:
183 fmt.Fprintln(os.Stderr, "Invalid function ", cmd)
184 }
185 }
+0
-21
examples/addsvc/thrift/gen-go/addsvc/addsvc-consts.go less more
0 // Code generated by Thrift Compiler (0.14.1). DO NOT EDIT.
1
2 package addsvc
3
4 import (
5 "bytes"
6 "context"
7 "fmt"
8 "github.com/apache/thrift/lib/go/thrift"
9 "time"
10 )
11
12 // (needed to ensure safety because of naive import list construction.)
13 var _ = thrift.ZERO
14 var _ = fmt.Printf
15 var _ = context.Background
16 var _ = time.Now
17 var _ = bytes.Equal
18
19 func init() {
20 }
+0
-1111
examples/addsvc/thrift/gen-go/addsvc/addsvc.go less more
0 // Code generated by Thrift Compiler (0.14.1). DO NOT EDIT.
1
2 package addsvc
3
4 import (
5 "bytes"
6 "context"
7 "fmt"
8 "github.com/apache/thrift/lib/go/thrift"
9 "time"
10 )
11
12 // (needed to ensure safety because of naive import list construction.)
13 var _ = thrift.ZERO
14 var _ = fmt.Printf
15 var _ = context.Background
16 var _ = time.Now
17 var _ = bytes.Equal
18
19 // Attributes:
20 // - Value
21 // - Err
22 type SumReply struct {
23 Value int64 `thrift:"value,1" db:"value" json:"value"`
24 Err string `thrift:"err,2" db:"err" json:"err"`
25 }
26
27 func NewSumReply() *SumReply {
28 return &SumReply{}
29 }
30
31 func (p *SumReply) GetValue() int64 {
32 return p.Value
33 }
34
35 func (p *SumReply) GetErr() string {
36 return p.Err
37 }
38 func (p *SumReply) Read(ctx context.Context, iprot thrift.TProtocol) error {
39 if _, err := iprot.ReadStructBegin(ctx); err != nil {
40 return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
41 }
42
43 for {
44 _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)
45 if err != nil {
46 return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err)
47 }
48 if fieldTypeId == thrift.STOP {
49 break
50 }
51 switch fieldId {
52 case 1:
53 if fieldTypeId == thrift.I64 {
54 if err := p.ReadField1(ctx, iprot); err != nil {
55 return err
56 }
57 } else {
58 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
59 return err
60 }
61 }
62 case 2:
63 if fieldTypeId == thrift.STRING {
64 if err := p.ReadField2(ctx, iprot); err != nil {
65 return err
66 }
67 } else {
68 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
69 return err
70 }
71 }
72 default:
73 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
74 return err
75 }
76 }
77 if err := iprot.ReadFieldEnd(ctx); err != nil {
78 return err
79 }
80 }
81 if err := iprot.ReadStructEnd(ctx); err != nil {
82 return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err)
83 }
84 return nil
85 }
86
87 func (p *SumReply) ReadField1(ctx context.Context, iprot thrift.TProtocol) error {
88 if v, err := iprot.ReadI64(ctx); err != nil {
89 return thrift.PrependError("error reading field 1: ", err)
90 } else {
91 p.Value = v
92 }
93 return nil
94 }
95
96 func (p *SumReply) ReadField2(ctx context.Context, iprot thrift.TProtocol) error {
97 if v, err := iprot.ReadString(ctx); err != nil {
98 return thrift.PrependError("error reading field 2: ", err)
99 } else {
100 p.Err = v
101 }
102 return nil
103 }
104
105 func (p *SumReply) Write(ctx context.Context, oprot thrift.TProtocol) error {
106 if err := oprot.WriteStructBegin(ctx, "SumReply"); err != nil {
107 return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
108 }
109 if p != nil {
110 if err := p.writeField1(ctx, oprot); err != nil {
111 return err
112 }
113 if err := p.writeField2(ctx, oprot); err != nil {
114 return err
115 }
116 }
117 if err := oprot.WriteFieldStop(ctx); err != nil {
118 return thrift.PrependError("write field stop error: ", err)
119 }
120 if err := oprot.WriteStructEnd(ctx); err != nil {
121 return thrift.PrependError("write struct stop error: ", err)
122 }
123 return nil
124 }
125
126 func (p *SumReply) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) {
127 if err := oprot.WriteFieldBegin(ctx, "value", thrift.I64, 1); err != nil {
128 return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:value: ", p), err)
129 }
130 if err := oprot.WriteI64(ctx, int64(p.Value)); err != nil {
131 return thrift.PrependError(fmt.Sprintf("%T.value (1) field write error: ", p), err)
132 }
133 if err := oprot.WriteFieldEnd(ctx); err != nil {
134 return thrift.PrependError(fmt.Sprintf("%T write field end error 1:value: ", p), err)
135 }
136 return err
137 }
138
139 func (p *SumReply) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) {
140 if err := oprot.WriteFieldBegin(ctx, "err", thrift.STRING, 2); err != nil {
141 return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:err: ", p), err)
142 }
143 if err := oprot.WriteString(ctx, string(p.Err)); err != nil {
144 return thrift.PrependError(fmt.Sprintf("%T.err (2) field write error: ", p), err)
145 }
146 if err := oprot.WriteFieldEnd(ctx); err != nil {
147 return thrift.PrependError(fmt.Sprintf("%T write field end error 2:err: ", p), err)
148 }
149 return err
150 }
151
152 func (p *SumReply) Equals(other *SumReply) bool {
153 if p == other {
154 return true
155 } else if p == nil || other == nil {
156 return false
157 }
158 if p.Value != other.Value {
159 return false
160 }
161 if p.Err != other.Err {
162 return false
163 }
164 return true
165 }
166
167 func (p *SumReply) String() string {
168 if p == nil {
169 return "<nil>"
170 }
171 return fmt.Sprintf("SumReply(%+v)", *p)
172 }
173
174 // Attributes:
175 // - Value
176 // - Err
177 type ConcatReply struct {
178 Value string `thrift:"value,1" db:"value" json:"value"`
179 Err string `thrift:"err,2" db:"err" json:"err"`
180 }
181
182 func NewConcatReply() *ConcatReply {
183 return &ConcatReply{}
184 }
185
186 func (p *ConcatReply) GetValue() string {
187 return p.Value
188 }
189
190 func (p *ConcatReply) GetErr() string {
191 return p.Err
192 }
193 func (p *ConcatReply) Read(ctx context.Context, iprot thrift.TProtocol) error {
194 if _, err := iprot.ReadStructBegin(ctx); err != nil {
195 return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
196 }
197
198 for {
199 _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)
200 if err != nil {
201 return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err)
202 }
203 if fieldTypeId == thrift.STOP {
204 break
205 }
206 switch fieldId {
207 case 1:
208 if fieldTypeId == thrift.STRING {
209 if err := p.ReadField1(ctx, iprot); err != nil {
210 return err
211 }
212 } else {
213 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
214 return err
215 }
216 }
217 case 2:
218 if fieldTypeId == thrift.STRING {
219 if err := p.ReadField2(ctx, iprot); err != nil {
220 return err
221 }
222 } else {
223 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
224 return err
225 }
226 }
227 default:
228 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
229 return err
230 }
231 }
232 if err := iprot.ReadFieldEnd(ctx); err != nil {
233 return err
234 }
235 }
236 if err := iprot.ReadStructEnd(ctx); err != nil {
237 return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err)
238 }
239 return nil
240 }
241
242 func (p *ConcatReply) ReadField1(ctx context.Context, iprot thrift.TProtocol) error {
243 if v, err := iprot.ReadString(ctx); err != nil {
244 return thrift.PrependError("error reading field 1: ", err)
245 } else {
246 p.Value = v
247 }
248 return nil
249 }
250
251 func (p *ConcatReply) ReadField2(ctx context.Context, iprot thrift.TProtocol) error {
252 if v, err := iprot.ReadString(ctx); err != nil {
253 return thrift.PrependError("error reading field 2: ", err)
254 } else {
255 p.Err = v
256 }
257 return nil
258 }
259
260 func (p *ConcatReply) Write(ctx context.Context, oprot thrift.TProtocol) error {
261 if err := oprot.WriteStructBegin(ctx, "ConcatReply"); err != nil {
262 return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
263 }
264 if p != nil {
265 if err := p.writeField1(ctx, oprot); err != nil {
266 return err
267 }
268 if err := p.writeField2(ctx, oprot); err != nil {
269 return err
270 }
271 }
272 if err := oprot.WriteFieldStop(ctx); err != nil {
273 return thrift.PrependError("write field stop error: ", err)
274 }
275 if err := oprot.WriteStructEnd(ctx); err != nil {
276 return thrift.PrependError("write struct stop error: ", err)
277 }
278 return nil
279 }
280
281 func (p *ConcatReply) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) {
282 if err := oprot.WriteFieldBegin(ctx, "value", thrift.STRING, 1); err != nil {
283 return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:value: ", p), err)
284 }
285 if err := oprot.WriteString(ctx, string(p.Value)); err != nil {
286 return thrift.PrependError(fmt.Sprintf("%T.value (1) field write error: ", p), err)
287 }
288 if err := oprot.WriteFieldEnd(ctx); err != nil {
289 return thrift.PrependError(fmt.Sprintf("%T write field end error 1:value: ", p), err)
290 }
291 return err
292 }
293
294 func (p *ConcatReply) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) {
295 if err := oprot.WriteFieldBegin(ctx, "err", thrift.STRING, 2); err != nil {
296 return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:err: ", p), err)
297 }
298 if err := oprot.WriteString(ctx, string(p.Err)); err != nil {
299 return thrift.PrependError(fmt.Sprintf("%T.err (2) field write error: ", p), err)
300 }
301 if err := oprot.WriteFieldEnd(ctx); err != nil {
302 return thrift.PrependError(fmt.Sprintf("%T write field end error 2:err: ", p), err)
303 }
304 return err
305 }
306
307 func (p *ConcatReply) Equals(other *ConcatReply) bool {
308 if p == other {
309 return true
310 } else if p == nil || other == nil {
311 return false
312 }
313 if p.Value != other.Value {
314 return false
315 }
316 if p.Err != other.Err {
317 return false
318 }
319 return true
320 }
321
322 func (p *ConcatReply) String() string {
323 if p == nil {
324 return "<nil>"
325 }
326 return fmt.Sprintf("ConcatReply(%+v)", *p)
327 }
328
329 type AddService interface {
330 // Parameters:
331 // - A
332 // - B
333 Sum(ctx context.Context, a int64, b int64) (_r *SumReply, _err error)
334 // Parameters:
335 // - A
336 // - B
337 Concat(ctx context.Context, a string, b string) (_r *ConcatReply, _err error)
338 }
339
340 type AddServiceClient struct {
341 c thrift.TClient
342 meta thrift.ResponseMeta
343 }
344
345 func NewAddServiceClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AddServiceClient {
346 return &AddServiceClient{
347 c: thrift.NewTStandardClient(f.GetProtocol(t), f.GetProtocol(t)),
348 }
349 }
350
351 func NewAddServiceClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AddServiceClient {
352 return &AddServiceClient{
353 c: thrift.NewTStandardClient(iprot, oprot),
354 }
355 }
356
357 func NewAddServiceClient(c thrift.TClient) *AddServiceClient {
358 return &AddServiceClient{
359 c: c,
360 }
361 }
362
363 func (p *AddServiceClient) Client_() thrift.TClient {
364 return p.c
365 }
366
367 func (p *AddServiceClient) LastResponseMeta_() thrift.ResponseMeta {
368 return p.meta
369 }
370
371 func (p *AddServiceClient) SetLastResponseMeta_(meta thrift.ResponseMeta) {
372 p.meta = meta
373 }
374
375 // Parameters:
376 // - A
377 // - B
378 func (p *AddServiceClient) Sum(ctx context.Context, a int64, b int64) (_r *SumReply, _err error) {
379 var _args0 AddServiceSumArgs
380 _args0.A = a
381 _args0.B = b
382 var _result2 AddServiceSumResult
383 var _meta1 thrift.ResponseMeta
384 _meta1, _err = p.Client_().Call(ctx, "Sum", &_args0, &_result2)
385 p.SetLastResponseMeta_(_meta1)
386 if _err != nil {
387 return
388 }
389 return _result2.GetSuccess(), nil
390 }
391
392 // Parameters:
393 // - A
394 // - B
395 func (p *AddServiceClient) Concat(ctx context.Context, a string, b string) (_r *ConcatReply, _err error) {
396 var _args3 AddServiceConcatArgs
397 _args3.A = a
398 _args3.B = b
399 var _result5 AddServiceConcatResult
400 var _meta4 thrift.ResponseMeta
401 _meta4, _err = p.Client_().Call(ctx, "Concat", &_args3, &_result5)
402 p.SetLastResponseMeta_(_meta4)
403 if _err != nil {
404 return
405 }
406 return _result5.GetSuccess(), nil
407 }
408
409 type AddServiceProcessor struct {
410 processorMap map[string]thrift.TProcessorFunction
411 handler AddService
412 }
413
414 func (p *AddServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {
415 p.processorMap[key] = processor
416 }
417
418 func (p *AddServiceProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) {
419 processor, ok = p.processorMap[key]
420 return processor, ok
421 }
422
423 func (p *AddServiceProcessor) ProcessorMap() map[string]thrift.TProcessorFunction {
424 return p.processorMap
425 }
426
427 func NewAddServiceProcessor(handler AddService) *AddServiceProcessor {
428
429 self6 := &AddServiceProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)}
430 self6.processorMap["Sum"] = &addServiceProcessorSum{handler: handler}
431 self6.processorMap["Concat"] = &addServiceProcessorConcat{handler: handler}
432 return self6
433 }
434
435 func (p *AddServiceProcessor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {
436 name, _, seqId, err2 := iprot.ReadMessageBegin(ctx)
437 if err2 != nil {
438 return false, thrift.WrapTException(err2)
439 }
440 if processor, ok := p.GetProcessorFunction(name); ok {
441 return processor.Process(ctx, seqId, iprot, oprot)
442 }
443 iprot.Skip(ctx, thrift.STRUCT)
444 iprot.ReadMessageEnd(ctx)
445 x7 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name)
446 oprot.WriteMessageBegin(ctx, name, thrift.EXCEPTION, seqId)
447 x7.Write(ctx, oprot)
448 oprot.WriteMessageEnd(ctx)
449 oprot.Flush(ctx)
450 return false, x7
451
452 }
453
454 type addServiceProcessorSum struct {
455 handler AddService
456 }
457
458 func (p *addServiceProcessorSum) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {
459 args := AddServiceSumArgs{}
460 var err2 error
461 if err2 = args.Read(ctx, iprot); err2 != nil {
462 iprot.ReadMessageEnd(ctx)
463 x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err2.Error())
464 oprot.WriteMessageBegin(ctx, "Sum", thrift.EXCEPTION, seqId)
465 x.Write(ctx, oprot)
466 oprot.WriteMessageEnd(ctx)
467 oprot.Flush(ctx)
468 return false, thrift.WrapTException(err2)
469 }
470 iprot.ReadMessageEnd(ctx)
471
472 tickerCancel := func() {}
473 // Start a goroutine to do server side connectivity check.
474 if thrift.ServerConnectivityCheckInterval > 0 {
475 var cancel context.CancelFunc
476 ctx, cancel = context.WithCancel(ctx)
477 defer cancel()
478 var tickerCtx context.Context
479 tickerCtx, tickerCancel = context.WithCancel(context.Background())
480 defer tickerCancel()
481 go func(ctx context.Context, cancel context.CancelFunc) {
482 ticker := time.NewTicker(thrift.ServerConnectivityCheckInterval)
483 defer ticker.Stop()
484 for {
485 select {
486 case <-ctx.Done():
487 return
488 case <-ticker.C:
489 if !iprot.Transport().IsOpen() {
490 cancel()
491 return
492 }
493 }
494 }
495 }(tickerCtx, cancel)
496 }
497
498 result := AddServiceSumResult{}
499 var retval *SumReply
500 if retval, err2 = p.handler.Sum(ctx, args.A, args.B); err2 != nil {
501 tickerCancel()
502 if err2 == thrift.ErrAbandonRequest {
503 return false, thrift.WrapTException(err2)
504 }
505 x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Sum: "+err2.Error())
506 oprot.WriteMessageBegin(ctx, "Sum", thrift.EXCEPTION, seqId)
507 x.Write(ctx, oprot)
508 oprot.WriteMessageEnd(ctx)
509 oprot.Flush(ctx)
510 return true, thrift.WrapTException(err2)
511 } else {
512 result.Success = retval
513 }
514 tickerCancel()
515 if err2 = oprot.WriteMessageBegin(ctx, "Sum", thrift.REPLY, seqId); err2 != nil {
516 err = thrift.WrapTException(err2)
517 }
518 if err2 = result.Write(ctx, oprot); err == nil && err2 != nil {
519 err = thrift.WrapTException(err2)
520 }
521 if err2 = oprot.WriteMessageEnd(ctx); err == nil && err2 != nil {
522 err = thrift.WrapTException(err2)
523 }
524 if err2 = oprot.Flush(ctx); err == nil && err2 != nil {
525 err = thrift.WrapTException(err2)
526 }
527 if err != nil {
528 return
529 }
530 return true, err
531 }
532
533 type addServiceProcessorConcat struct {
534 handler AddService
535 }
536
537 func (p *addServiceProcessorConcat) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {
538 args := AddServiceConcatArgs{}
539 var err2 error
540 if err2 = args.Read(ctx, iprot); err2 != nil {
541 iprot.ReadMessageEnd(ctx)
542 x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err2.Error())
543 oprot.WriteMessageBegin(ctx, "Concat", thrift.EXCEPTION, seqId)
544 x.Write(ctx, oprot)
545 oprot.WriteMessageEnd(ctx)
546 oprot.Flush(ctx)
547 return false, thrift.WrapTException(err2)
548 }
549 iprot.ReadMessageEnd(ctx)
550
551 tickerCancel := func() {}
552 // Start a goroutine to do server side connectivity check.
553 if thrift.ServerConnectivityCheckInterval > 0 {
554 var cancel context.CancelFunc
555 ctx, cancel = context.WithCancel(ctx)
556 defer cancel()
557 var tickerCtx context.Context
558 tickerCtx, tickerCancel = context.WithCancel(context.Background())
559 defer tickerCancel()
560 go func(ctx context.Context, cancel context.CancelFunc) {
561 ticker := time.NewTicker(thrift.ServerConnectivityCheckInterval)
562 defer ticker.Stop()
563 for {
564 select {
565 case <-ctx.Done():
566 return
567 case <-ticker.C:
568 if !iprot.Transport().IsOpen() {
569 cancel()
570 return
571 }
572 }
573 }
574 }(tickerCtx, cancel)
575 }
576
577 result := AddServiceConcatResult{}
578 var retval *ConcatReply
579 if retval, err2 = p.handler.Concat(ctx, args.A, args.B); err2 != nil {
580 tickerCancel()
581 if err2 == thrift.ErrAbandonRequest {
582 return false, thrift.WrapTException(err2)
583 }
584 x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Concat: "+err2.Error())
585 oprot.WriteMessageBegin(ctx, "Concat", thrift.EXCEPTION, seqId)
586 x.Write(ctx, oprot)
587 oprot.WriteMessageEnd(ctx)
588 oprot.Flush(ctx)
589 return true, thrift.WrapTException(err2)
590 } else {
591 result.Success = retval
592 }
593 tickerCancel()
594 if err2 = oprot.WriteMessageBegin(ctx, "Concat", thrift.REPLY, seqId); err2 != nil {
595 err = thrift.WrapTException(err2)
596 }
597 if err2 = result.Write(ctx, oprot); err == nil && err2 != nil {
598 err = thrift.WrapTException(err2)
599 }
600 if err2 = oprot.WriteMessageEnd(ctx); err == nil && err2 != nil {
601 err = thrift.WrapTException(err2)
602 }
603 if err2 = oprot.Flush(ctx); err == nil && err2 != nil {
604 err = thrift.WrapTException(err2)
605 }
606 if err != nil {
607 return
608 }
609 return true, err
610 }
611
612 // HELPER FUNCTIONS AND STRUCTURES
613
614 // Attributes:
615 // - A
616 // - B
617 type AddServiceSumArgs struct {
618 A int64 `thrift:"a,1" db:"a" json:"a"`
619 B int64 `thrift:"b,2" db:"b" json:"b"`
620 }
621
622 func NewAddServiceSumArgs() *AddServiceSumArgs {
623 return &AddServiceSumArgs{}
624 }
625
626 func (p *AddServiceSumArgs) GetA() int64 {
627 return p.A
628 }
629
630 func (p *AddServiceSumArgs) GetB() int64 {
631 return p.B
632 }
633 func (p *AddServiceSumArgs) Read(ctx context.Context, iprot thrift.TProtocol) error {
634 if _, err := iprot.ReadStructBegin(ctx); err != nil {
635 return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
636 }
637
638 for {
639 _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)
640 if err != nil {
641 return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err)
642 }
643 if fieldTypeId == thrift.STOP {
644 break
645 }
646 switch fieldId {
647 case 1:
648 if fieldTypeId == thrift.I64 {
649 if err := p.ReadField1(ctx, iprot); err != nil {
650 return err
651 }
652 } else {
653 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
654 return err
655 }
656 }
657 case 2:
658 if fieldTypeId == thrift.I64 {
659 if err := p.ReadField2(ctx, iprot); err != nil {
660 return err
661 }
662 } else {
663 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
664 return err
665 }
666 }
667 default:
668 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
669 return err
670 }
671 }
672 if err := iprot.ReadFieldEnd(ctx); err != nil {
673 return err
674 }
675 }
676 if err := iprot.ReadStructEnd(ctx); err != nil {
677 return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err)
678 }
679 return nil
680 }
681
682 func (p *AddServiceSumArgs) ReadField1(ctx context.Context, iprot thrift.TProtocol) error {
683 if v, err := iprot.ReadI64(ctx); err != nil {
684 return thrift.PrependError("error reading field 1: ", err)
685 } else {
686 p.A = v
687 }
688 return nil
689 }
690
691 func (p *AddServiceSumArgs) ReadField2(ctx context.Context, iprot thrift.TProtocol) error {
692 if v, err := iprot.ReadI64(ctx); err != nil {
693 return thrift.PrependError("error reading field 2: ", err)
694 } else {
695 p.B = v
696 }
697 return nil
698 }
699
700 func (p *AddServiceSumArgs) Write(ctx context.Context, oprot thrift.TProtocol) error {
701 if err := oprot.WriteStructBegin(ctx, "Sum_args"); err != nil {
702 return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
703 }
704 if p != nil {
705 if err := p.writeField1(ctx, oprot); err != nil {
706 return err
707 }
708 if err := p.writeField2(ctx, oprot); err != nil {
709 return err
710 }
711 }
712 if err := oprot.WriteFieldStop(ctx); err != nil {
713 return thrift.PrependError("write field stop error: ", err)
714 }
715 if err := oprot.WriteStructEnd(ctx); err != nil {
716 return thrift.PrependError("write struct stop error: ", err)
717 }
718 return nil
719 }
720
721 func (p *AddServiceSumArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) {
722 if err := oprot.WriteFieldBegin(ctx, "a", thrift.I64, 1); err != nil {
723 return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:a: ", p), err)
724 }
725 if err := oprot.WriteI64(ctx, int64(p.A)); err != nil {
726 return thrift.PrependError(fmt.Sprintf("%T.a (1) field write error: ", p), err)
727 }
728 if err := oprot.WriteFieldEnd(ctx); err != nil {
729 return thrift.PrependError(fmt.Sprintf("%T write field end error 1:a: ", p), err)
730 }
731 return err
732 }
733
734 func (p *AddServiceSumArgs) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) {
735 if err := oprot.WriteFieldBegin(ctx, "b", thrift.I64, 2); err != nil {
736 return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:b: ", p), err)
737 }
738 if err := oprot.WriteI64(ctx, int64(p.B)); err != nil {
739 return thrift.PrependError(fmt.Sprintf("%T.b (2) field write error: ", p), err)
740 }
741 if err := oprot.WriteFieldEnd(ctx); err != nil {
742 return thrift.PrependError(fmt.Sprintf("%T write field end error 2:b: ", p), err)
743 }
744 return err
745 }
746
747 func (p *AddServiceSumArgs) String() string {
748 if p == nil {
749 return "<nil>"
750 }
751 return fmt.Sprintf("AddServiceSumArgs(%+v)", *p)
752 }
753
754 // Attributes:
755 // - Success
756 type AddServiceSumResult struct {
757 Success *SumReply `thrift:"success,0" db:"success" json:"success,omitempty"`
758 }
759
760 func NewAddServiceSumResult() *AddServiceSumResult {
761 return &AddServiceSumResult{}
762 }
763
764 var AddServiceSumResult_Success_DEFAULT *SumReply
765
766 func (p *AddServiceSumResult) GetSuccess() *SumReply {
767 if !p.IsSetSuccess() {
768 return AddServiceSumResult_Success_DEFAULT
769 }
770 return p.Success
771 }
772 func (p *AddServiceSumResult) IsSetSuccess() bool {
773 return p.Success != nil
774 }
775
776 func (p *AddServiceSumResult) Read(ctx context.Context, iprot thrift.TProtocol) error {
777 if _, err := iprot.ReadStructBegin(ctx); err != nil {
778 return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
779 }
780
781 for {
782 _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)
783 if err != nil {
784 return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err)
785 }
786 if fieldTypeId == thrift.STOP {
787 break
788 }
789 switch fieldId {
790 case 0:
791 if fieldTypeId == thrift.STRUCT {
792 if err := p.ReadField0(ctx, iprot); err != nil {
793 return err
794 }
795 } else {
796 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
797 return err
798 }
799 }
800 default:
801 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
802 return err
803 }
804 }
805 if err := iprot.ReadFieldEnd(ctx); err != nil {
806 return err
807 }
808 }
809 if err := iprot.ReadStructEnd(ctx); err != nil {
810 return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err)
811 }
812 return nil
813 }
814
815 func (p *AddServiceSumResult) ReadField0(ctx context.Context, iprot thrift.TProtocol) error {
816 p.Success = &SumReply{}
817 if err := p.Success.Read(ctx, iprot); err != nil {
818 return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err)
819 }
820 return nil
821 }
822
823 func (p *AddServiceSumResult) Write(ctx context.Context, oprot thrift.TProtocol) error {
824 if err := oprot.WriteStructBegin(ctx, "Sum_result"); err != nil {
825 return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
826 }
827 if p != nil {
828 if err := p.writeField0(ctx, oprot); err != nil {
829 return err
830 }
831 }
832 if err := oprot.WriteFieldStop(ctx); err != nil {
833 return thrift.PrependError("write field stop error: ", err)
834 }
835 if err := oprot.WriteStructEnd(ctx); err != nil {
836 return thrift.PrependError("write struct stop error: ", err)
837 }
838 return nil
839 }
840
841 func (p *AddServiceSumResult) writeField0(ctx context.Context, oprot thrift.TProtocol) (err error) {
842 if p.IsSetSuccess() {
843 if err := oprot.WriteFieldBegin(ctx, "success", thrift.STRUCT, 0); err != nil {
844 return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err)
845 }
846 if err := p.Success.Write(ctx, oprot); err != nil {
847 return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err)
848 }
849 if err := oprot.WriteFieldEnd(ctx); err != nil {
850 return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err)
851 }
852 }
853 return err
854 }
855
856 func (p *AddServiceSumResult) String() string {
857 if p == nil {
858 return "<nil>"
859 }
860 return fmt.Sprintf("AddServiceSumResult(%+v)", *p)
861 }
862
863 // Attributes:
864 // - A
865 // - B
866 type AddServiceConcatArgs struct {
867 A string `thrift:"a,1" db:"a" json:"a"`
868 B string `thrift:"b,2" db:"b" json:"b"`
869 }
870
871 func NewAddServiceConcatArgs() *AddServiceConcatArgs {
872 return &AddServiceConcatArgs{}
873 }
874
875 func (p *AddServiceConcatArgs) GetA() string {
876 return p.A
877 }
878
879 func (p *AddServiceConcatArgs) GetB() string {
880 return p.B
881 }
882 func (p *AddServiceConcatArgs) Read(ctx context.Context, iprot thrift.TProtocol) error {
883 if _, err := iprot.ReadStructBegin(ctx); err != nil {
884 return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
885 }
886
887 for {
888 _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)
889 if err != nil {
890 return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err)
891 }
892 if fieldTypeId == thrift.STOP {
893 break
894 }
895 switch fieldId {
896 case 1:
897 if fieldTypeId == thrift.STRING {
898 if err := p.ReadField1(ctx, iprot); err != nil {
899 return err
900 }
901 } else {
902 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
903 return err
904 }
905 }
906 case 2:
907 if fieldTypeId == thrift.STRING {
908 if err := p.ReadField2(ctx, iprot); err != nil {
909 return err
910 }
911 } else {
912 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
913 return err
914 }
915 }
916 default:
917 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
918 return err
919 }
920 }
921 if err := iprot.ReadFieldEnd(ctx); err != nil {
922 return err
923 }
924 }
925 if err := iprot.ReadStructEnd(ctx); err != nil {
926 return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err)
927 }
928 return nil
929 }
930
931 func (p *AddServiceConcatArgs) ReadField1(ctx context.Context, iprot thrift.TProtocol) error {
932 if v, err := iprot.ReadString(ctx); err != nil {
933 return thrift.PrependError("error reading field 1: ", err)
934 } else {
935 p.A = v
936 }
937 return nil
938 }
939
940 func (p *AddServiceConcatArgs) ReadField2(ctx context.Context, iprot thrift.TProtocol) error {
941 if v, err := iprot.ReadString(ctx); err != nil {
942 return thrift.PrependError("error reading field 2: ", err)
943 } else {
944 p.B = v
945 }
946 return nil
947 }
948
949 func (p *AddServiceConcatArgs) Write(ctx context.Context, oprot thrift.TProtocol) error {
950 if err := oprot.WriteStructBegin(ctx, "Concat_args"); err != nil {
951 return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
952 }
953 if p != nil {
954 if err := p.writeField1(ctx, oprot); err != nil {
955 return err
956 }
957 if err := p.writeField2(ctx, oprot); err != nil {
958 return err
959 }
960 }
961 if err := oprot.WriteFieldStop(ctx); err != nil {
962 return thrift.PrependError("write field stop error: ", err)
963 }
964 if err := oprot.WriteStructEnd(ctx); err != nil {
965 return thrift.PrependError("write struct stop error: ", err)
966 }
967 return nil
968 }
969
970 func (p *AddServiceConcatArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) {
971 if err := oprot.WriteFieldBegin(ctx, "a", thrift.STRING, 1); err != nil {
972 return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:a: ", p), err)
973 }
974 if err := oprot.WriteString(ctx, string(p.A)); err != nil {
975 return thrift.PrependError(fmt.Sprintf("%T.a (1) field write error: ", p), err)
976 }
977 if err := oprot.WriteFieldEnd(ctx); err != nil {
978 return thrift.PrependError(fmt.Sprintf("%T write field end error 1:a: ", p), err)
979 }
980 return err
981 }
982
983 func (p *AddServiceConcatArgs) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) {
984 if err := oprot.WriteFieldBegin(ctx, "b", thrift.STRING, 2); err != nil {
985 return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:b: ", p), err)
986 }
987 if err := oprot.WriteString(ctx, string(p.B)); err != nil {
988 return thrift.PrependError(fmt.Sprintf("%T.b (2) field write error: ", p), err)
989 }
990 if err := oprot.WriteFieldEnd(ctx); err != nil {
991 return thrift.PrependError(fmt.Sprintf("%T write field end error 2:b: ", p), err)
992 }
993 return err
994 }
995
996 func (p *AddServiceConcatArgs) String() string {
997 if p == nil {
998 return "<nil>"
999 }
1000 return fmt.Sprintf("AddServiceConcatArgs(%+v)", *p)
1001 }
1002
1003 // Attributes:
1004 // - Success
1005 type AddServiceConcatResult struct {
1006 Success *ConcatReply `thrift:"success,0" db:"success" json:"success,omitempty"`
1007 }
1008
1009 func NewAddServiceConcatResult() *AddServiceConcatResult {
1010 return &AddServiceConcatResult{}
1011 }
1012
1013 var AddServiceConcatResult_Success_DEFAULT *ConcatReply
1014
1015 func (p *AddServiceConcatResult) GetSuccess() *ConcatReply {
1016 if !p.IsSetSuccess() {
1017 return AddServiceConcatResult_Success_DEFAULT
1018 }
1019 return p.Success
1020 }
1021 func (p *AddServiceConcatResult) IsSetSuccess() bool {
1022 return p.Success != nil
1023 }
1024
1025 func (p *AddServiceConcatResult) Read(ctx context.Context, iprot thrift.TProtocol) error {
1026 if _, err := iprot.ReadStructBegin(ctx); err != nil {
1027 return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
1028 }
1029
1030 for {
1031 _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)
1032 if err != nil {
1033 return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err)
1034 }
1035 if fieldTypeId == thrift.STOP {
1036 break
1037 }
1038 switch fieldId {
1039 case 0:
1040 if fieldTypeId == thrift.STRUCT {
1041 if err := p.ReadField0(ctx, iprot); err != nil {
1042 return err
1043 }
1044 } else {
1045 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
1046 return err
1047 }
1048 }
1049 default:
1050 if err := iprot.Skip(ctx, fieldTypeId); err != nil {
1051 return err
1052 }
1053 }
1054 if err := iprot.ReadFieldEnd(ctx); err != nil {
1055 return err
1056 }
1057 }
1058 if err := iprot.ReadStructEnd(ctx); err != nil {
1059 return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err)
1060 }
1061 return nil
1062 }
1063
1064 func (p *AddServiceConcatResult) ReadField0(ctx context.Context, iprot thrift.TProtocol) error {
1065 p.Success = &ConcatReply{}
1066 if err := p.Success.Read(ctx, iprot); err != nil {
1067 return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err)
1068 }
1069 return nil
1070 }
1071
1072 func (p *AddServiceConcatResult) Write(ctx context.Context, oprot thrift.TProtocol) error {
1073 if err := oprot.WriteStructBegin(ctx, "Concat_result"); err != nil {
1074 return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
1075 }
1076 if p != nil {
1077 if err := p.writeField0(ctx, oprot); err != nil {
1078 return err
1079 }
1080 }
1081 if err := oprot.WriteFieldStop(ctx); err != nil {
1082 return thrift.PrependError("write field stop error: ", err)
1083 }
1084 if err := oprot.WriteStructEnd(ctx); err != nil {
1085 return thrift.PrependError("write struct stop error: ", err)
1086 }
1087 return nil
1088 }
1089
1090 func (p *AddServiceConcatResult) writeField0(ctx context.Context, oprot thrift.TProtocol) (err error) {
1091 if p.IsSetSuccess() {
1092 if err := oprot.WriteFieldBegin(ctx, "success", thrift.STRUCT, 0); err != nil {
1093 return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err)
1094 }
1095 if err := p.Success.Write(ctx, oprot); err != nil {
1096 return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err)
1097 }
1098 if err := oprot.WriteFieldEnd(ctx); err != nil {
1099 return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err)
1100 }
1101 }
1102 return err
1103 }
1104
1105 func (p *AddServiceConcatResult) String() string {
1106 if p == nil {
1107 return "<nil>"
1108 }
1109 return fmt.Sprintf("AddServiceConcatResult(%+v)", *p)
1110 }
+0
-287
examples/apigateway/main.go less more
0 package main
1
2 import (
3 "bytes"
4 "context"
5 "encoding/json"
6 "flag"
7 "fmt"
8 "io"
9 "io/ioutil"
10 "net/http"
11 "net/url"
12 "os"
13 "os/signal"
14 "strings"
15 "syscall"
16 "time"
17
18 consulsd "github.com/go-kit/kit/sd/consul"
19 "github.com/gorilla/mux"
20 "github.com/hashicorp/consul/api"
21 stdopentracing "github.com/opentracing/opentracing-go"
22 stdzipkin "github.com/openzipkin/zipkin-go"
23 "google.golang.org/grpc"
24
25 "github.com/go-kit/kit/endpoint"
26 "github.com/go-kit/kit/log"
27 "github.com/go-kit/kit/sd"
28 "github.com/go-kit/kit/sd/lb"
29 httptransport "github.com/go-kit/kit/transport/http"
30
31 "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint"
32 "github.com/go-kit/kit/examples/addsvc/pkg/addservice"
33 "github.com/go-kit/kit/examples/addsvc/pkg/addtransport"
34 )
35
36 func main() {
37 var (
38 httpAddr = flag.String("http.addr", ":8000", "Address for HTTP (JSON) server")
39 consulAddr = flag.String("consul.addr", "", "Consul agent address")
40 retryMax = flag.Int("retry.max", 3, "per-request retries to different instances")
41 retryTimeout = flag.Duration("retry.timeout", 500*time.Millisecond, "per-request timeout, including retries")
42 )
43 flag.Parse()
44
45 // Logging domain.
46 var logger log.Logger
47 {
48 logger = log.NewLogfmtLogger(os.Stderr)
49 logger = log.With(logger, "ts", log.DefaultTimestampUTC)
50 logger = log.With(logger, "caller", log.DefaultCaller)
51 }
52
53 // Service discovery domain. In this example we use Consul.
54 var client consulsd.Client
55 {
56 consulConfig := api.DefaultConfig()
57 if len(*consulAddr) > 0 {
58 consulConfig.Address = *consulAddr
59 }
60 consulClient, err := api.NewClient(consulConfig)
61 if err != nil {
62 logger.Log("err", err)
63 os.Exit(1)
64 }
65 client = consulsd.NewClient(consulClient)
66 }
67
68 // Transport domain.
69 tracer := stdopentracing.GlobalTracer() // no-op
70 zipkinTracer, _ := stdzipkin.NewTracer(nil, stdzipkin.WithNoopTracer(true))
71 ctx := context.Background()
72 r := mux.NewRouter()
73
74 // Now we begin installing the routes. Each route corresponds to a single
75 // method: sum, concat, uppercase, and count.
76
77 // addsvc routes.
78 {
79 // Each method gets constructed with a factory. Factories take an
80 // instance string, and return a specific endpoint. In the factory we
81 // dial the instance string we get from Consul, and then leverage an
82 // addsvc client package to construct a complete service. We can then
83 // leverage the addsvc.Make{Sum,Concat}Endpoint constructors to convert
84 // the complete service to specific endpoint.
85 var (
86 tags = []string{}
87 passingOnly = true
88 endpoints = addendpoint.Set{}
89 instancer = consulsd.NewInstancer(client, logger, "addsvc", tags, passingOnly)
90 )
91 {
92 factory := addsvcFactory(addendpoint.MakeSumEndpoint, tracer, zipkinTracer, logger)
93 endpointer := sd.NewEndpointer(instancer, factory, logger)
94 balancer := lb.NewRoundRobin(endpointer)
95 retry := lb.Retry(*retryMax, *retryTimeout, balancer)
96 endpoints.SumEndpoint = retry
97 }
98 {
99 factory := addsvcFactory(addendpoint.MakeConcatEndpoint, tracer, zipkinTracer, logger)
100 endpointer := sd.NewEndpointer(instancer, factory, logger)
101 balancer := lb.NewRoundRobin(endpointer)
102 retry := lb.Retry(*retryMax, *retryTimeout, balancer)
103 endpoints.ConcatEndpoint = retry
104 }
105
106 // Here we leverage the fact that addsvc comes with a constructor for an
107 // HTTP handler, and just install it under a particular path prefix in
108 // our router.
109
110 r.PathPrefix("/addsvc").Handler(http.StripPrefix("/addsvc", addtransport.NewHTTPHandler(endpoints, tracer, zipkinTracer, logger)))
111 }
112
113 // stringsvc routes.
114 {
115 // addsvc had lots of nice importable Go packages we could leverage.
116 // With stringsvc we are not so fortunate, it just has some endpoints
117 // that we assume will exist. So we have to write that logic here. This
118 // is by design, so you can see two totally different methods of
119 // proxying to a remote service.
120
121 var (
122 tags = []string{}
123 passingOnly = true
124 uppercase endpoint.Endpoint
125 count endpoint.Endpoint
126 instancer = consulsd.NewInstancer(client, logger, "stringsvc", tags, passingOnly)
127 )
128 {
129 factory := stringsvcFactory(ctx, "GET", "/uppercase")
130 endpointer := sd.NewEndpointer(instancer, factory, logger)
131 balancer := lb.NewRoundRobin(endpointer)
132 retry := lb.Retry(*retryMax, *retryTimeout, balancer)
133 uppercase = retry
134 }
135 {
136 factory := stringsvcFactory(ctx, "GET", "/count")
137 endpointer := sd.NewEndpointer(instancer, factory, logger)
138 balancer := lb.NewRoundRobin(endpointer)
139 retry := lb.Retry(*retryMax, *retryTimeout, balancer)
140 count = retry
141 }
142
143 // We can use the transport/http.Server to act as our handler, all we
144 // have to do provide it with the encode and decode functions for our
145 // stringsvc methods.
146
147 r.Handle("/stringsvc/uppercase", httptransport.NewServer(uppercase, decodeUppercaseRequest, encodeJSONResponse))
148 r.Handle("/stringsvc/count", httptransport.NewServer(count, decodeCountRequest, encodeJSONResponse))
149 }
150
151 // Interrupt handler.
152 errc := make(chan error)
153 go func() {
154 c := make(chan os.Signal)
155 signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
156 errc <- fmt.Errorf("%s", <-c)
157 }()
158
159 // HTTP transport.
160 go func() {
161 logger.Log("transport", "HTTP", "addr", *httpAddr)
162 errc <- http.ListenAndServe(*httpAddr, r)
163 }()
164
165 // Run!
166 logger.Log("exit", <-errc)
167 }
168
169 func addsvcFactory(makeEndpoint func(addservice.Service) endpoint.Endpoint, tracer stdopentracing.Tracer, zipkinTracer *stdzipkin.Tracer, logger log.Logger) sd.Factory {
170 return func(instance string) (endpoint.Endpoint, io.Closer, error) {
171 // We could just as easily use the HTTP or Thrift client package to make
172 // the connection to addsvc. We've chosen gRPC arbitrarily. Note that
173 // the transport is an implementation detail: it doesn't leak out of
174 // this function. Nice!
175
176 conn, err := grpc.Dial(instance, grpc.WithInsecure())
177 if err != nil {
178 return nil, nil, err
179 }
180 service := addtransport.NewGRPCClient(conn, tracer, zipkinTracer, logger)
181 endpoint := makeEndpoint(service)
182
183 // Notice that the addsvc gRPC client converts the connection to a
184 // complete addsvc, and we just throw away everything except the method
185 // we're interested in. A smarter factory would mux multiple methods
186 // over the same connection. But that would require more work to manage
187 // the returned io.Closer, e.g. reference counting. Since this is for
188 // the purposes of demonstration, we'll just keep it simple.
189
190 return endpoint, conn, nil
191 }
192 }
193
194 func stringsvcFactory(ctx context.Context, method, path string) sd.Factory {
195 return func(instance string) (endpoint.Endpoint, io.Closer, error) {
196 if !strings.HasPrefix(instance, "http") {
197 instance = "http://" + instance
198 }
199 tgt, err := url.Parse(instance)
200 if err != nil {
201 return nil, nil, err
202 }
203 tgt.Path = path
204
205 // Since stringsvc doesn't have any kind of package we can import, or
206 // any formal spec, we are forced to just assert where the endpoints
207 // live, and write our own code to encode and decode requests and
208 // responses. Ideally, if you write the service, you will want to
209 // provide stronger guarantees to your clients.
210
211 var (
212 enc httptransport.EncodeRequestFunc
213 dec httptransport.DecodeResponseFunc
214 )
215 switch path {
216 case "/uppercase":
217 enc, dec = encodeJSONRequest, decodeUppercaseResponse
218 case "/count":
219 enc, dec = encodeJSONRequest, decodeCountResponse
220 default:
221 return nil, nil, fmt.Errorf("unknown stringsvc path %q", path)
222 }
223
224 return httptransport.NewClient(method, tgt, enc, dec).Endpoint(), nil, nil
225 }
226 }
227
228 func encodeJSONRequest(_ context.Context, req *http.Request, request interface{}) error {
229 // Both uppercase and count requests are encoded in the same way:
230 // simple JSON serialization to the request body.
231 var buf bytes.Buffer
232 if err := json.NewEncoder(&buf).Encode(request); err != nil {
233 return err
234 }
235 req.Body = ioutil.NopCloser(&buf)
236 return nil
237 }
238
239 func encodeJSONResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
240 w.Header().Set("Content-Type", "application/json; charset=utf-8")
241 return json.NewEncoder(w).Encode(response)
242 }
243
244 // I've just copied these functions from stringsvc3/transport.go, inlining the
245 // struct definitions.
246
247 func decodeUppercaseResponse(ctx context.Context, resp *http.Response) (interface{}, error) {
248 var response struct {
249 V string `json:"v"`
250 Err string `json:"err,omitempty"`
251 }
252 if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
253 return nil, err
254 }
255 return response, nil
256 }
257
258 func decodeCountResponse(ctx context.Context, resp *http.Response) (interface{}, error) {
259 var response struct {
260 V int `json:"v"`
261 }
262 if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
263 return nil, err
264 }
265 return response, nil
266 }
267
268 func decodeUppercaseRequest(ctx context.Context, req *http.Request) (interface{}, error) {
269 var request struct {
270 S string `json:"s"`
271 }
272 if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
273 return nil, err
274 }
275 return request, nil
276 }
277
278 func decodeCountRequest(ctx context.Context, req *http.Request) (interface{}, error) {
279 var request struct {
280 S string `json:"s"`
281 }
282 if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
283 return nil, err
284 }
285 return request, nil
286 }
+0
-29
examples/go.mod less more
0 module github.com/go-kit/kit/examples
1
2 go 1.16
3
4 require (
5 github.com/apache/thrift v0.14.1
6 github.com/go-kit/kit v0.10.0
7 github.com/golang/protobuf v1.5.2
8 github.com/gorilla/mux v1.7.3
9 github.com/hashicorp/consul/api v1.3.0
10 github.com/hashicorp/go-version v1.3.0 // indirect
11 github.com/lightstep/lightstep-tracer-go v0.22.0
12 github.com/nats-io/nats.go v1.11.0
13 github.com/oklog/oklog v0.3.2
14 github.com/oklog/run v1.1.0 // indirect
15 github.com/opentracing/basictracer-go v1.1.0 // indirect
16 github.com/opentracing/opentracing-go v1.2.0
17 github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5
18 github.com/openzipkin/zipkin-go v0.2.2
19 github.com/pact-foundation/pact-go v1.0.4
20 github.com/pborman/uuid v1.2.0
21 github.com/prometheus/client_golang v1.5.1
22 github.com/sony/gobreaker v0.4.1
23 golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
24 google.golang.org/grpc v1.37.0
25 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0
26 )
27
28 replace github.com/go-kit/kit => ../
+0
-453
examples/go.sum less more
0 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
1 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
3 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
4 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
5 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
6 github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
7 github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
8 github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
9 github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
10 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw=
11 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
12 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
13 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
14 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
15 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
16 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
17 github.com/apache/thrift v0.14.1 h1:Yh8v0hpCj63p5edXOLaqTJW0IJ1p+eMW6+YSOqw1d6s=
18 github.com/apache/thrift v0.14.1/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
19 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
20 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
21 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
22 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
23 github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
24 github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
25 github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
26 github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
27 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
28 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
29 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
30 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
31 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
32 github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
33 github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
34 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
35 github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
36 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
37 github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
38 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
39 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
40 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
41 github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
42 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
43 github.com/coreos/go-systemd/v22 v22.3.1/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
44 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
45 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
46 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
47 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
48 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
49 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
50 github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
51 github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
52 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
53 github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
54 github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
55 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
56 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
57 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
58 github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
59 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
60 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
61 github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
62 github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
63 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
64 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
65 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
66 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
67 github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
68 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
69 github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
70 github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
71 github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
72 github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
73 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
74 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
75 github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
76 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
77 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
78 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
79 github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
80 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
81 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
82 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
83 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
84 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
85 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
86 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
87 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
88 github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
89 github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
90 github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
91 github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
92 github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
93 github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
94 github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
95 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
96 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
97 github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
98 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
99 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
100 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
101 github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
102 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
103 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
104 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
105 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
106 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
107 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
108 github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
109 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
110 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
111 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
112 github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
113 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
114 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
115 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
116 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
117 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
118 github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
119 github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
120 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
121 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
122 github.com/hashicorp/consul/api v1.3.0 h1:HXNYlRkkM/t+Y/Yhxtwcy02dlYwIaoxzvxPnS+cqy78=
123 github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
124 github.com/hashicorp/consul/sdk v0.3.0 h1:UOxjlb4xVNF93jak1mzzoBatyFju9nrkxpVwIp/QqxQ=
125 github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
126 github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
127 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
128 github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
129 github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
130 github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
131 github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
132 github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
133 github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
134 github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
135 github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
136 github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
137 github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
138 github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
139 github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
140 github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
141 github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
142 github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
143 github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
144 github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw=
145 github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
146 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
147 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
148 github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
149 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
150 github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
151 github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
152 github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
153 github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
154 github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
155 github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
156 github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
157 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
158 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
159 github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
160 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
161 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
162 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
163 github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
164 github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
165 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
166 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
167 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
168 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
169 github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
170 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
171 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
172 github.com/klauspost/compress v1.11.12 h1:famVnQVu7QwryBN4jNseQdUKES71ZAOnB6UQQJPZvqk=
173 github.com/klauspost/compress v1.11.12/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
174 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
175 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
176 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
177 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
178 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
179 github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20200305213919-a88bf8de3718 h1:lrdADj7ifyBpqGJ+cT4vE5ztUoAF87uUf76+epwPViY=
180 github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20200305213919-a88bf8de3718/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
181 github.com/lightstep/lightstep-tracer-go v0.22.0 h1:Yy1G8UDT1Ayx010fji4IaTIsmijil5xijvwJ1OoypDk=
182 github.com/lightstep/lightstep-tracer-go v0.22.0/go.mod h1:RnONwHKg89zYPmF+Uig5PpHMUcYCFgml8+r4SS53y7A=
183 github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
184 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
185 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
186 github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
187 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
188 github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
189 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
190 github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0=
191 github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
192 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
193 github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
194 github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
195 github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
196 github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
197 github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
198 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
199 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
200 github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
201 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
202 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
203 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
204 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
205 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
206 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
207 github.com/nats-io/jwt v1.2.2 h1:w3GMTO969dFg+UOKTmmyuu7IGdusK+7Ytlt//OYH/uU=
208 github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q=
209 github.com/nats-io/jwt/v2 v2.0.2 h1:ejVCLO8gu6/4bOKIHQpmB5UhhUJfAQw55yvLWpfmKjI=
210 github.com/nats-io/jwt/v2 v2.0.2/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY=
211 github.com/nats-io/nats-server/v2 v2.2.6 h1:FPK9wWx9pagxcw14s8W9rlfzfyHm61uNLnJyybZbn48=
212 github.com/nats-io/nats-server/v2 v2.2.6/go.mod h1:sEnFaxqe09cDmfMgACxZbziXnhQFhwk+aKkZjBBRYrI=
213 github.com/nats-io/nats.go v1.11.0 h1:L263PZkrmkRJRJT2YHU8GwWWvEvmr9/LUKuJTXsF32k=
214 github.com/nats-io/nats.go v1.11.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
215 github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
216 github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
217 github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
218 github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
219 github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
220 github.com/oklog/oklog v0.3.2 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk=
221 github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
222 github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
223 github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
224 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
225 github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
226 github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
227 github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
228 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
229 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
230 github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU=
231 github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
232 github.com/opentracing/basictracer-go v1.1.0 h1:Oa1fTSBvAl8pa3U+IJYqrKm0NALwH9OsgwOqDv4xJW0=
233 github.com/opentracing/basictracer-go v1.1.0/go.mod h1:V2HZueSJEp879yv285Aap1BS69fQMD+MNP1mRs6mBQc=
234 github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
235 github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
236 github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
237 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
238 github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU=
239 github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
240 github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
241 github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb9NAWI=
242 github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
243 github.com/pact-foundation/pact-go v1.0.4 h1:OYkFijGHoZAYbOIb1LWXrwKQbMMRUv1oQ89blD2Mh2Q=
244 github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
245 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
246 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
247 github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
248 github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
249 github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
250 github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
251 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
252 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
253 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
254 github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
255 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
256 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
257 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
258 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
259 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
260 github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA=
261 github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
262 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
263 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
264 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
265 github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
266 github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
267 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
268 github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
269 github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
270 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
271 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
272 github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
273 github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
274 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
275 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
276 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
277 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
278 github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
279 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
280 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
281 github.com/shirou/gopsutil v2.20.1+incompatible h1:oIq9Cq4i84Hk8uQAUOG3eNdI/29hBawGrD5YRl6JRDY=
282 github.com/shirou/gopsutil v2.20.1+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
283 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
284 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
285 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
286 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
287 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
288 github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
289 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
290 github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ=
291 github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
292 github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
293 github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
294 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a h1:AhmOdSHeswKHBjhsLs/7+1voOxT+LLrSk/Nxvk35fug=
295 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
296 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
297 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
298 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
299 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
300 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
301 github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
302 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
303 github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
304 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
305 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
306 go.etcd.io/etcd/api/v3 v3.5.0-beta.4/go.mod h1:yF0YUmBghT48aC0/eTFrhULo+uKQAr5spQQ6sRhPauE=
307 go.etcd.io/etcd/client/pkg/v3 v3.5.0-beta.4/go.mod h1:a+pbz+UrcOpvve1Qxf6tGovi15PjgtRhi0QTO2Nlc4U=
308 go.etcd.io/etcd/client/v2 v2.305.0-beta.4/go.mod h1:PImE2qIZFEHFgOrFrvOX3bJvS2xc4Q9SRpCnuCZTNAE=
309 go.etcd.io/etcd/client/v3 v3.5.0-beta.4/go.mod h1:0L1RulN1QSXq6uKPMUSX+OTAYyFkapMK7iUHXXIH/1E=
310 go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
311 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
312 go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
313 go.uber.org/zap v1.16.1-0.20210329175301-c23abee72d19/go.mod h1:aMfIlz3TDBfB0BwTCKFU1XbEmj9zevr5S5LcBr85MXw=
314 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
315 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
316 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
317 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
318 golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
319 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
320 golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as=
321 golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
322 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
323 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
324 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
325 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
326 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
327 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
328 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
329 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
330 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
331 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
332 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
333 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
334 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
335 golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
336 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
337 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
338 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
339 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
340 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
341 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
342 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
343 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
344 golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
345 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
346 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
347 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
348 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
349 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
350 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
351 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
352 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
353 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
354 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
355 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
356 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
357 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
358 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
359 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
360 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
361 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
362 golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
363 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
364 golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
365 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
366 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
367 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
368 golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
369 golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
370 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
371 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
372 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
373 golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
374 golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
375 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
376 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
377 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
378 golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
379 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
380 golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
381 golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
382 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
383 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
384 golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
385 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
386 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
387 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
388 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
389 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
390 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
391 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
392 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
393 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
394 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
395 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
396 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
397 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
398 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
399 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
400 google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
401 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
402 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
403 google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
404 google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
405 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
406 google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
407 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
408 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
409 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
410 google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
411 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
412 google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
413 google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
414 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
415 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
416 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
417 google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
418 google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c=
419 google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
420 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
421 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
422 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
423 google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
424 google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
425 google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
426 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
427 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
428 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
429 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
430 google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
431 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
432 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
433 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
434 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
435 gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
436 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
437 gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
438 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
439 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
440 gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
441 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
442 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
443 gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
444 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
445 gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
446 gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
447 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
448 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
449 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
450 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
451 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0 h1:ucqkfpjg9WzSUubAO62csmucvxl4/JeW3F4I4909XkM=
452 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
+0
-25
examples/profilesvc/README.md less more
0 # profilesvc
1
2 This example demonstrates how to use Go kit to implement a REST-y HTTP service.
3 It leverages the excellent [gorilla mux package](https://github.com/gorilla/mux) for routing.
4
5 Run the example with the optional port address for the service:
6
7 ```bash
8 $ go run ./cmd/profilesvc/main.go -http.addr :8080
9 ts=2018-05-01T16:13:12.849086255Z caller=main.go:47 transport=HTTP addr=:8080
10 ```
11
12 Create a Profile:
13
14 ```bash
15 $ curl -d '{"id":"1234","Name":"Go Kit"}' -H "Content-Type: application/json" -X POST http://localhost:8080/profiles/
16 {}
17 ```
18
19 Get the profile you just created
20
21 ```bash
22 $ curl localhost:8080/profiles/1234
23 {"profile":{"id":"1234","name":"Go Kit"}}
24 ```
+0
-121
examples/profilesvc/client/client.go less more
0 // Package client provides a profilesvc client based on a predefined Consul
1 // service name and relevant tags. Users must only provide the address of a
2 // Consul server.
3 package client
4
5 import (
6 "io"
7 "time"
8
9 consulapi "github.com/hashicorp/consul/api"
10
11 "github.com/go-kit/kit/endpoint"
12 "github.com/go-kit/kit/examples/profilesvc"
13 "github.com/go-kit/kit/log"
14 "github.com/go-kit/kit/sd"
15 "github.com/go-kit/kit/sd/consul"
16 "github.com/go-kit/kit/sd/lb"
17 )
18
19 // New returns a service that's load-balanced over instances of profilesvc found
20 // in the provided Consul server. The mechanism of looking up profilesvc
21 // instances in Consul is hard-coded into the client.
22 func New(consulAddr string, logger log.Logger) (profilesvc.Service, error) {
23 apiclient, err := consulapi.NewClient(&consulapi.Config{
24 Address: consulAddr,
25 })
26 if err != nil {
27 return nil, err
28 }
29
30 // As the implementer of profilesvc, we declare and enforce these
31 // parameters for all of the profilesvc consumers.
32 var (
33 consulService = "profilesvc"
34 consulTags = []string{"prod"}
35 passingOnly = true
36 retryMax = 3
37 retryTimeout = 500 * time.Millisecond
38 )
39
40 var (
41 sdclient = consul.NewClient(apiclient)
42 instancer = consul.NewInstancer(sdclient, logger, consulService, consulTags, passingOnly)
43 endpoints profilesvc.Endpoints
44 )
45 {
46 factory := factoryFor(profilesvc.MakePostProfileEndpoint)
47 endpointer := sd.NewEndpointer(instancer, factory, logger)
48 balancer := lb.NewRoundRobin(endpointer)
49 retry := lb.Retry(retryMax, retryTimeout, balancer)
50 endpoints.PostProfileEndpoint = retry
51 }
52 {
53 factory := factoryFor(profilesvc.MakeGetProfileEndpoint)
54 endpointer := sd.NewEndpointer(instancer, factory, logger)
55 balancer := lb.NewRoundRobin(endpointer)
56 retry := lb.Retry(retryMax, retryTimeout, balancer)
57 endpoints.GetProfileEndpoint = retry
58 }
59 {
60 factory := factoryFor(profilesvc.MakePutProfileEndpoint)
61 endpointer := sd.NewEndpointer(instancer, factory, logger)
62 balancer := lb.NewRoundRobin(endpointer)
63 retry := lb.Retry(retryMax, retryTimeout, balancer)
64 endpoints.PutProfileEndpoint = retry
65 }
66 {
67 factory := factoryFor(profilesvc.MakePatchProfileEndpoint)
68 endpointer := sd.NewEndpointer(instancer, factory, logger)
69 balancer := lb.NewRoundRobin(endpointer)
70 retry := lb.Retry(retryMax, retryTimeout, balancer)
71 endpoints.PatchProfileEndpoint = retry
72 }
73 {
74 factory := factoryFor(profilesvc.MakeDeleteProfileEndpoint)
75 endpointer := sd.NewEndpointer(instancer, factory, logger)
76 balancer := lb.NewRoundRobin(endpointer)
77 retry := lb.Retry(retryMax, retryTimeout, balancer)
78 endpoints.DeleteProfileEndpoint = retry
79 }
80 {
81 factory := factoryFor(profilesvc.MakeGetAddressesEndpoint)
82 endpointer := sd.NewEndpointer(instancer, factory, logger)
83 balancer := lb.NewRoundRobin(endpointer)
84 retry := lb.Retry(retryMax, retryTimeout, balancer)
85 endpoints.GetAddressesEndpoint = retry
86 }
87 {
88 factory := factoryFor(profilesvc.MakeGetAddressEndpoint)
89 endpointer := sd.NewEndpointer(instancer, factory, logger)
90 balancer := lb.NewRoundRobin(endpointer)
91 retry := lb.Retry(retryMax, retryTimeout, balancer)
92 endpoints.GetAddressEndpoint = retry
93 }
94 {
95 factory := factoryFor(profilesvc.MakePostAddressEndpoint)
96 endpointer := sd.NewEndpointer(instancer, factory, logger)
97 balancer := lb.NewRoundRobin(endpointer)
98 retry := lb.Retry(retryMax, retryTimeout, balancer)
99 endpoints.PostAddressEndpoint = retry
100 }
101 {
102 factory := factoryFor(profilesvc.MakeDeleteAddressEndpoint)
103 endpointer := sd.NewEndpointer(instancer, factory, logger)
104 balancer := lb.NewRoundRobin(endpointer)
105 retry := lb.Retry(retryMax, retryTimeout, balancer)
106 endpoints.DeleteAddressEndpoint = retry
107 }
108
109 return endpoints, nil
110 }
111
112 func factoryFor(makeEndpoint func(profilesvc.Service) endpoint.Endpoint) sd.Factory {
113 return func(instance string) (endpoint.Endpoint, io.Closer, error) {
114 service, err := profilesvc.MakeClientEndpoints(instance)
115 if err != nil {
116 return nil, nil, err
117 }
118 return makeEndpoint(service), nil, nil
119 }
120 }
+0
-52
examples/profilesvc/cmd/profilesvc/main.go less more
0 package main
1
2 import (
3 "flag"
4 "fmt"
5 "net/http"
6 "os"
7 "os/signal"
8 "syscall"
9
10 "github.com/go-kit/kit/examples/profilesvc"
11 "github.com/go-kit/kit/log"
12 )
13
14 func main() {
15 var (
16 httpAddr = flag.String("http.addr", ":8080", "HTTP listen address")
17 )
18 flag.Parse()
19
20 var logger log.Logger
21 {
22 logger = log.NewLogfmtLogger(os.Stderr)
23 logger = log.With(logger, "ts", log.DefaultTimestampUTC)
24 logger = log.With(logger, "caller", log.DefaultCaller)
25 }
26
27 var s profilesvc.Service
28 {
29 s = profilesvc.NewInmemService()
30 s = profilesvc.LoggingMiddleware(logger)(s)
31 }
32
33 var h http.Handler
34 {
35 h = profilesvc.MakeHTTPHandler(s, log.With(logger, "component", "HTTP"))
36 }
37
38 errs := make(chan error)
39 go func() {
40 c := make(chan os.Signal)
41 signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
42 errs <- fmt.Errorf("%s", <-c)
43 }()
44
45 go func() {
46 logger.Log("transport", "HTTP", "addr", *httpAddr)
47 errs <- http.ListenAndServe(*httpAddr, h)
48 }()
49
50 logger.Log("exit", <-errs)
51 }
+0
-387
examples/profilesvc/endpoints.go less more
0 package profilesvc
1
2 import (
3 "context"
4 "net/url"
5 "strings"
6
7 "github.com/go-kit/kit/endpoint"
8 httptransport "github.com/go-kit/kit/transport/http"
9 )
10
11 // Endpoints collects all of the endpoints that compose a profile service. It's
12 // meant to be used as a helper struct, to collect all of the endpoints into a
13 // single parameter.
14 //
15 // In a server, it's useful for functions that need to operate on a per-endpoint
16 // basis. For example, you might pass an Endpoints to a function that produces
17 // an http.Handler, with each method (endpoint) wired up to a specific path. (It
18 // is probably a mistake in design to invoke the Service methods on the
19 // Endpoints struct in a server.)
20 //
21 // In a client, it's useful to collect individually constructed endpoints into a
22 // single type that implements the Service interface. For example, you might
23 // construct individual endpoints using transport/http.NewClient, combine them
24 // into an Endpoints, and return it to the caller as a Service.
25 type Endpoints struct {
26 PostProfileEndpoint endpoint.Endpoint
27 GetProfileEndpoint endpoint.Endpoint
28 PutProfileEndpoint endpoint.Endpoint
29 PatchProfileEndpoint endpoint.Endpoint
30 DeleteProfileEndpoint endpoint.Endpoint
31 GetAddressesEndpoint endpoint.Endpoint
32 GetAddressEndpoint endpoint.Endpoint
33 PostAddressEndpoint endpoint.Endpoint
34 DeleteAddressEndpoint endpoint.Endpoint
35 }
36
37 // MakeServerEndpoints returns an Endpoints struct where each endpoint invokes
38 // the corresponding method on the provided service. Useful in a profilesvc
39 // server.
40 func MakeServerEndpoints(s Service) Endpoints {
41 return Endpoints{
42 PostProfileEndpoint: MakePostProfileEndpoint(s),
43 GetProfileEndpoint: MakeGetProfileEndpoint(s),
44 PutProfileEndpoint: MakePutProfileEndpoint(s),
45 PatchProfileEndpoint: MakePatchProfileEndpoint(s),
46 DeleteProfileEndpoint: MakeDeleteProfileEndpoint(s),
47 GetAddressesEndpoint: MakeGetAddressesEndpoint(s),
48 GetAddressEndpoint: MakeGetAddressEndpoint(s),
49 PostAddressEndpoint: MakePostAddressEndpoint(s),
50 DeleteAddressEndpoint: MakeDeleteAddressEndpoint(s),
51 }
52 }
53
54 // MakeClientEndpoints returns an Endpoints struct where each endpoint invokes
55 // the corresponding method on the remote instance, via a transport/http.Client.
56 // Useful in a profilesvc client.
57 func MakeClientEndpoints(instance string) (Endpoints, error) {
58 if !strings.HasPrefix(instance, "http") {
59 instance = "http://" + instance
60 }
61 tgt, err := url.Parse(instance)
62 if err != nil {
63 return Endpoints{}, err
64 }
65 tgt.Path = ""
66
67 options := []httptransport.ClientOption{}
68
69 // Note that the request encoders need to modify the request URL, changing
70 // the path. That's fine: we simply need to provide specific encoders for
71 // each endpoint.
72
73 return Endpoints{
74 PostProfileEndpoint: httptransport.NewClient("POST", tgt, encodePostProfileRequest, decodePostProfileResponse, options...).Endpoint(),
75 GetProfileEndpoint: httptransport.NewClient("GET", tgt, encodeGetProfileRequest, decodeGetProfileResponse, options...).Endpoint(),
76 PutProfileEndpoint: httptransport.NewClient("PUT", tgt, encodePutProfileRequest, decodePutProfileResponse, options...).Endpoint(),
77 PatchProfileEndpoint: httptransport.NewClient("PATCH", tgt, encodePatchProfileRequest, decodePatchProfileResponse, options...).Endpoint(),
78 DeleteProfileEndpoint: httptransport.NewClient("DELETE", tgt, encodeDeleteProfileRequest, decodeDeleteProfileResponse, options...).Endpoint(),
79 GetAddressesEndpoint: httptransport.NewClient("GET", tgt, encodeGetAddressesRequest, decodeGetAddressesResponse, options...).Endpoint(),
80 GetAddressEndpoint: httptransport.NewClient("GET", tgt, encodeGetAddressRequest, decodeGetAddressResponse, options...).Endpoint(),
81 PostAddressEndpoint: httptransport.NewClient("POST", tgt, encodePostAddressRequest, decodePostAddressResponse, options...).Endpoint(),
82 DeleteAddressEndpoint: httptransport.NewClient("DELETE", tgt, encodeDeleteAddressRequest, decodeDeleteAddressResponse, options...).Endpoint(),
83 }, nil
84 }
85
86 // PostProfile implements Service. Primarily useful in a client.
87 func (e Endpoints) PostProfile(ctx context.Context, p Profile) error {
88 request := postProfileRequest{Profile: p}
89 response, err := e.PostProfileEndpoint(ctx, request)
90 if err != nil {
91 return err
92 }
93 resp := response.(postProfileResponse)
94 return resp.Err
95 }
96
97 // GetProfile implements Service. Primarily useful in a client.
98 func (e Endpoints) GetProfile(ctx context.Context, id string) (Profile, error) {
99 request := getProfileRequest{ID: id}
100 response, err := e.GetProfileEndpoint(ctx, request)
101 if err != nil {
102 return Profile{}, err
103 }
104 resp := response.(getProfileResponse)
105 return resp.Profile, resp.Err
106 }
107
108 // PutProfile implements Service. Primarily useful in a client.
109 func (e Endpoints) PutProfile(ctx context.Context, id string, p Profile) error {
110 request := putProfileRequest{ID: id, Profile: p}
111 response, err := e.PutProfileEndpoint(ctx, request)
112 if err != nil {
113 return err
114 }
115 resp := response.(putProfileResponse)
116 return resp.Err
117 }
118
119 // PatchProfile implements Service. Primarily useful in a client.
120 func (e Endpoints) PatchProfile(ctx context.Context, id string, p Profile) error {
121 request := patchProfileRequest{ID: id, Profile: p}
122 response, err := e.PatchProfileEndpoint(ctx, request)
123 if err != nil {
124 return err
125 }
126 resp := response.(patchProfileResponse)
127 return resp.Err
128 }
129
130 // DeleteProfile implements Service. Primarily useful in a client.
131 func (e Endpoints) DeleteProfile(ctx context.Context, id string) error {
132 request := deleteProfileRequest{ID: id}
133 response, err := e.DeleteProfileEndpoint(ctx, request)
134 if err != nil {
135 return err
136 }
137 resp := response.(deleteProfileResponse)
138 return resp.Err
139 }
140
141 // GetAddresses implements Service. Primarily useful in a client.
142 func (e Endpoints) GetAddresses(ctx context.Context, profileID string) ([]Address, error) {
143 request := getAddressesRequest{ProfileID: profileID}
144 response, err := e.GetAddressesEndpoint(ctx, request)
145 if err != nil {
146 return nil, err
147 }
148 resp := response.(getAddressesResponse)
149 return resp.Addresses, resp.Err
150 }
151
152 // GetAddress implements Service. Primarily useful in a client.
153 func (e Endpoints) GetAddress(ctx context.Context, profileID string, addressID string) (Address, error) {
154 request := getAddressRequest{ProfileID: profileID, AddressID: addressID}
155 response, err := e.GetAddressEndpoint(ctx, request)
156 if err != nil {
157 return Address{}, err
158 }
159 resp := response.(getAddressResponse)
160 return resp.Address, resp.Err
161 }
162
163 // PostAddress implements Service. Primarily useful in a client.
164 func (e Endpoints) PostAddress(ctx context.Context, profileID string, a Address) error {
165 request := postAddressRequest{ProfileID: profileID, Address: a}
166 response, err := e.PostAddressEndpoint(ctx, request)
167 if err != nil {
168 return err
169 }
170 resp := response.(postAddressResponse)
171 return resp.Err
172 }
173
174 // DeleteAddress implements Service. Primarily useful in a client.
175 func (e Endpoints) DeleteAddress(ctx context.Context, profileID string, addressID string) error {
176 request := deleteAddressRequest{ProfileID: profileID, AddressID: addressID}
177 response, err := e.DeleteAddressEndpoint(ctx, request)
178 if err != nil {
179 return err
180 }
181 resp := response.(deleteAddressResponse)
182 return resp.Err
183 }
184
185 // MakePostProfileEndpoint returns an endpoint via the passed service.
186 // Primarily useful in a server.
187 func MakePostProfileEndpoint(s Service) endpoint.Endpoint {
188 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
189 req := request.(postProfileRequest)
190 e := s.PostProfile(ctx, req.Profile)
191 return postProfileResponse{Err: e}, nil
192 }
193 }
194
195 // MakeGetProfileEndpoint returns an endpoint via the passed service.
196 // Primarily useful in a server.
197 func MakeGetProfileEndpoint(s Service) endpoint.Endpoint {
198 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
199 req := request.(getProfileRequest)
200 p, e := s.GetProfile(ctx, req.ID)
201 return getProfileResponse{Profile: p, Err: e}, nil
202 }
203 }
204
205 // MakePutProfileEndpoint returns an endpoint via the passed service.
206 // Primarily useful in a server.
207 func MakePutProfileEndpoint(s Service) endpoint.Endpoint {
208 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
209 req := request.(putProfileRequest)
210 e := s.PutProfile(ctx, req.ID, req.Profile)
211 return putProfileResponse{Err: e}, nil
212 }
213 }
214
215 // MakePatchProfileEndpoint returns an endpoint via the passed service.
216 // Primarily useful in a server.
217 func MakePatchProfileEndpoint(s Service) endpoint.Endpoint {
218 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
219 req := request.(patchProfileRequest)
220 e := s.PatchProfile(ctx, req.ID, req.Profile)
221 return patchProfileResponse{Err: e}, nil
222 }
223 }
224
225 // MakeDeleteProfileEndpoint returns an endpoint via the passed service.
226 // Primarily useful in a server.
227 func MakeDeleteProfileEndpoint(s Service) endpoint.Endpoint {
228 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
229 req := request.(deleteProfileRequest)
230 e := s.DeleteProfile(ctx, req.ID)
231 return deleteProfileResponse{Err: e}, nil
232 }
233 }
234
235 // MakeGetAddressesEndpoint returns an endpoint via the passed service.
236 // Primarily useful in a server.
237 func MakeGetAddressesEndpoint(s Service) endpoint.Endpoint {
238 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
239 req := request.(getAddressesRequest)
240 a, e := s.GetAddresses(ctx, req.ProfileID)
241 return getAddressesResponse{Addresses: a, Err: e}, nil
242 }
243 }
244
245 // MakeGetAddressEndpoint returns an endpoint via the passed service.
246 // Primarily useful in a server.
247 func MakeGetAddressEndpoint(s Service) endpoint.Endpoint {
248 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
249 req := request.(getAddressRequest)
250 a, e := s.GetAddress(ctx, req.ProfileID, req.AddressID)
251 return getAddressResponse{Address: a, Err: e}, nil
252 }
253 }
254
255 // MakePostAddressEndpoint returns an endpoint via the passed service.
256 // Primarily useful in a server.
257 func MakePostAddressEndpoint(s Service) endpoint.Endpoint {
258 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
259 req := request.(postAddressRequest)
260 e := s.PostAddress(ctx, req.ProfileID, req.Address)
261 return postAddressResponse{Err: e}, nil
262 }
263 }
264
265 // MakeDeleteAddressEndpoint returns an endpoint via the passed service.
266 // Primarily useful in a server.
267 func MakeDeleteAddressEndpoint(s Service) endpoint.Endpoint {
268 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
269 req := request.(deleteAddressRequest)
270 e := s.DeleteAddress(ctx, req.ProfileID, req.AddressID)
271 return deleteAddressResponse{Err: e}, nil
272 }
273 }
274
275 // We have two options to return errors from the business logic.
276 //
277 // We could return the error via the endpoint itself. That makes certain things
278 // a little bit easier, like providing non-200 HTTP responses to the client. But
279 // Go kit assumes that endpoint errors are (or may be treated as)
280 // transport-domain errors. For example, an endpoint error will count against a
281 // circuit breaker error count.
282 //
283 // Therefore, it's often better to return service (business logic) errors in the
284 // response object. This means we have to do a bit more work in the HTTP
285 // response encoder to detect e.g. a not-found error and provide a proper HTTP
286 // status code. That work is done with the errorer interface, in transport.go.
287 // Response types that may contain business-logic errors implement that
288 // interface.
289
290 type postProfileRequest struct {
291 Profile Profile
292 }
293
294 type postProfileResponse struct {
295 Err error `json:"err,omitempty"`
296 }
297
298 func (r postProfileResponse) error() error { return r.Err }
299
300 type getProfileRequest struct {
301 ID string
302 }
303
304 type getProfileResponse struct {
305 Profile Profile `json:"profile,omitempty"`
306 Err error `json:"err,omitempty"`
307 }
308
309 func (r getProfileResponse) error() error { return r.Err }
310
311 type putProfileRequest struct {
312 ID string
313 Profile Profile
314 }
315
316 type putProfileResponse struct {
317 Err error `json:"err,omitempty"`
318 }
319
320 func (r putProfileResponse) error() error { return nil }
321
322 type patchProfileRequest struct {
323 ID string
324 Profile Profile
325 }
326
327 type patchProfileResponse struct {
328 Err error `json:"err,omitempty"`
329 }
330
331 func (r patchProfileResponse) error() error { return r.Err }
332
333 type deleteProfileRequest struct {
334 ID string
335 }
336
337 type deleteProfileResponse struct {
338 Err error `json:"err,omitempty"`
339 }
340
341 func (r deleteProfileResponse) error() error { return r.Err }
342
343 type getAddressesRequest struct {
344 ProfileID string
345 }
346
347 type getAddressesResponse struct {
348 Addresses []Address `json:"addresses,omitempty"`
349 Err error `json:"err,omitempty"`
350 }
351
352 func (r getAddressesResponse) error() error { return r.Err }
353
354 type getAddressRequest struct {
355 ProfileID string
356 AddressID string
357 }
358
359 type getAddressResponse struct {
360 Address Address `json:"address,omitempty"`
361 Err error `json:"err,omitempty"`
362 }
363
364 func (r getAddressResponse) error() error { return r.Err }
365
366 type postAddressRequest struct {
367 ProfileID string
368 Address Address
369 }
370
371 type postAddressResponse struct {
372 Err error `json:"err,omitempty"`
373 }
374
375 func (r postAddressResponse) error() error { return r.Err }
376
377 type deleteAddressRequest struct {
378 ProfileID string
379 AddressID string
380 }
381
382 type deleteAddressResponse struct {
383 Err error `json:"err,omitempty"`
384 }
385
386 func (r deleteAddressResponse) error() error { return r.Err }
+0
-88
examples/profilesvc/middlewares.go less more
0 package profilesvc
1
2 import (
3 "context"
4 "time"
5
6 "github.com/go-kit/kit/log"
7 )
8
9 // Middleware describes a service (as opposed to endpoint) middleware.
10 type Middleware func(Service) Service
11
12 func LoggingMiddleware(logger log.Logger) Middleware {
13 return func(next Service) Service {
14 return &loggingMiddleware{
15 next: next,
16 logger: logger,
17 }
18 }
19 }
20
21 type loggingMiddleware struct {
22 next Service
23 logger log.Logger
24 }
25
26 func (mw loggingMiddleware) PostProfile(ctx context.Context, p Profile) (err error) {
27 defer func(begin time.Time) {
28 mw.logger.Log("method", "PostProfile", "id", p.ID, "took", time.Since(begin), "err", err)
29 }(time.Now())
30 return mw.next.PostProfile(ctx, p)
31 }
32
33 func (mw loggingMiddleware) GetProfile(ctx context.Context, id string) (p Profile, err error) {
34 defer func(begin time.Time) {
35 mw.logger.Log("method", "GetProfile", "id", id, "took", time.Since(begin), "err", err)
36 }(time.Now())
37 return mw.next.GetProfile(ctx, id)
38 }
39
40 func (mw loggingMiddleware) PutProfile(ctx context.Context, id string, p Profile) (err error) {
41 defer func(begin time.Time) {
42 mw.logger.Log("method", "PutProfile", "id", id, "took", time.Since(begin), "err", err)
43 }(time.Now())
44 return mw.next.PutProfile(ctx, id, p)
45 }
46
47 func (mw loggingMiddleware) PatchProfile(ctx context.Context, id string, p Profile) (err error) {
48 defer func(begin time.Time) {
49 mw.logger.Log("method", "PatchProfile", "id", id, "took", time.Since(begin), "err", err)
50 }(time.Now())
51 return mw.next.PatchProfile(ctx, id, p)
52 }
53
54 func (mw loggingMiddleware) DeleteProfile(ctx context.Context, id string) (err error) {
55 defer func(begin time.Time) {
56 mw.logger.Log("method", "DeleteProfile", "id", id, "took", time.Since(begin), "err", err)
57 }(time.Now())
58 return mw.next.DeleteProfile(ctx, id)
59 }
60
61 func (mw loggingMiddleware) GetAddresses(ctx context.Context, profileID string) (addresses []Address, err error) {
62 defer func(begin time.Time) {
63 mw.logger.Log("method", "GetAddresses", "profileID", profileID, "took", time.Since(begin), "err", err)
64 }(time.Now())
65 return mw.next.GetAddresses(ctx, profileID)
66 }
67
68 func (mw loggingMiddleware) GetAddress(ctx context.Context, profileID string, addressID string) (a Address, err error) {
69 defer func(begin time.Time) {
70 mw.logger.Log("method", "GetAddress", "profileID", profileID, "addressID", addressID, "took", time.Since(begin), "err", err)
71 }(time.Now())
72 return mw.next.GetAddress(ctx, profileID, addressID)
73 }
74
75 func (mw loggingMiddleware) PostAddress(ctx context.Context, profileID string, a Address) (err error) {
76 defer func(begin time.Time) {
77 mw.logger.Log("method", "PostAddress", "profileID", profileID, "took", time.Since(begin), "err", err)
78 }(time.Now())
79 return mw.next.PostAddress(ctx, profileID, a)
80 }
81
82 func (mw loggingMiddleware) DeleteAddress(ctx context.Context, profileID string, addressID string) (err error) {
83 defer func(begin time.Time) {
84 mw.logger.Log("method", "DeleteAddress", "profileID", profileID, "addressID", addressID, "took", time.Since(begin), "err", err)
85 }(time.Now())
86 return mw.next.DeleteAddress(ctx, profileID, addressID)
87 }
+0
-185
examples/profilesvc/service.go less more
0 package profilesvc
1
2 import (
3 "context"
4 "errors"
5 "sync"
6 )
7
8 // Service is a simple CRUD interface for user profiles.
9 type Service interface {
10 PostProfile(ctx context.Context, p Profile) error
11 GetProfile(ctx context.Context, id string) (Profile, error)
12 PutProfile(ctx context.Context, id string, p Profile) error
13 PatchProfile(ctx context.Context, id string, p Profile) error
14 DeleteProfile(ctx context.Context, id string) error
15 GetAddresses(ctx context.Context, profileID string) ([]Address, error)
16 GetAddress(ctx context.Context, profileID string, addressID string) (Address, error)
17 PostAddress(ctx context.Context, profileID string, a Address) error
18 DeleteAddress(ctx context.Context, profileID string, addressID string) error
19 }
20
21 // Profile represents a single user profile.
22 // ID should be globally unique.
23 type Profile struct {
24 ID string `json:"id"`
25 Name string `json:"name,omitempty"`
26 Addresses []Address `json:"addresses,omitempty"`
27 }
28
29 // Address is a field of a user profile.
30 // ID should be unique within the profile (at a minimum).
31 type Address struct {
32 ID string `json:"id"`
33 Location string `json:"location,omitempty"`
34 }
35
36 var (
37 ErrInconsistentIDs = errors.New("inconsistent IDs")
38 ErrAlreadyExists = errors.New("already exists")
39 ErrNotFound = errors.New("not found")
40 )
41
42 type inmemService struct {
43 mtx sync.RWMutex
44 m map[string]Profile
45 }
46
47 func NewInmemService() Service {
48 return &inmemService{
49 m: map[string]Profile{},
50 }
51 }
52
53 func (s *inmemService) PostProfile(ctx context.Context, p Profile) error {
54 s.mtx.Lock()
55 defer s.mtx.Unlock()
56 if _, ok := s.m[p.ID]; ok {
57 return ErrAlreadyExists // POST = create, don't overwrite
58 }
59 s.m[p.ID] = p
60 return nil
61 }
62
63 func (s *inmemService) GetProfile(ctx context.Context, id string) (Profile, error) {
64 s.mtx.RLock()
65 defer s.mtx.RUnlock()
66 p, ok := s.m[id]
67 if !ok {
68 return Profile{}, ErrNotFound
69 }
70 return p, nil
71 }
72
73 func (s *inmemService) PutProfile(ctx context.Context, id string, p Profile) error {
74 if id != p.ID {
75 return ErrInconsistentIDs
76 }
77 s.mtx.Lock()
78 defer s.mtx.Unlock()
79 s.m[id] = p // PUT = create or update
80 return nil
81 }
82
83 func (s *inmemService) PatchProfile(ctx context.Context, id string, p Profile) error {
84 if p.ID != "" && id != p.ID {
85 return ErrInconsistentIDs
86 }
87
88 s.mtx.Lock()
89 defer s.mtx.Unlock()
90
91 existing, ok := s.m[id]
92 if !ok {
93 return ErrNotFound // PATCH = update existing, don't create
94 }
95
96 // We assume that it's not possible to PATCH the ID, and that it's not
97 // possible to PATCH any field to its zero value. That is, the zero value
98 // means not specified. The way around this is to use e.g. Name *string in
99 // the Profile definition. But since this is just a demonstrative example,
100 // I'm leaving that out.
101
102 if p.Name != "" {
103 existing.Name = p.Name
104 }
105 if len(p.Addresses) > 0 {
106 existing.Addresses = p.Addresses
107 }
108 s.m[id] = existing
109 return nil
110 }
111
112 func (s *inmemService) DeleteProfile(ctx context.Context, id string) error {
113 s.mtx.Lock()
114 defer s.mtx.Unlock()
115 if _, ok := s.m[id]; !ok {
116 return ErrNotFound
117 }
118 delete(s.m, id)
119 return nil
120 }
121
122 func (s *inmemService) GetAddresses(ctx context.Context, profileID string) ([]Address, error) {
123 s.mtx.RLock()
124 defer s.mtx.RUnlock()
125 p, ok := s.m[profileID]
126 if !ok {
127 return []Address{}, ErrNotFound
128 }
129 return p.Addresses, nil
130 }
131
132 func (s *inmemService) GetAddress(ctx context.Context, profileID string, addressID string) (Address, error) {
133 s.mtx.RLock()
134 defer s.mtx.RUnlock()
135 p, ok := s.m[profileID]
136 if !ok {
137 return Address{}, ErrNotFound
138 }
139 for _, address := range p.Addresses {
140 if address.ID == addressID {
141 return address, nil
142 }
143 }
144 return Address{}, ErrNotFound
145 }
146
147 func (s *inmemService) PostAddress(ctx context.Context, profileID string, a Address) error {
148 s.mtx.Lock()
149 defer s.mtx.Unlock()
150 p, ok := s.m[profileID]
151 if !ok {
152 return ErrNotFound
153 }
154 for _, address := range p.Addresses {
155 if address.ID == a.ID {
156 return ErrAlreadyExists
157 }
158 }
159 p.Addresses = append(p.Addresses, a)
160 s.m[profileID] = p
161 return nil
162 }
163
164 func (s *inmemService) DeleteAddress(ctx context.Context, profileID string, addressID string) error {
165 s.mtx.Lock()
166 defer s.mtx.Unlock()
167 p, ok := s.m[profileID]
168 if !ok {
169 return ErrNotFound
170 }
171 newAddresses := make([]Address, 0, len(p.Addresses))
172 for _, address := range p.Addresses {
173 if address.ID == addressID {
174 continue // delete
175 }
176 newAddresses = append(newAddresses, address)
177 }
178 if len(newAddresses) == len(p.Addresses) {
179 return ErrNotFound
180 }
181 p.Addresses = newAddresses
182 s.m[profileID] = p
183 return nil
184 }
+0
-401
examples/profilesvc/transport.go less more
0 package profilesvc
1
2 // The profilesvc is just over HTTP, so we just have a single transport.go.
3
4 import (
5 "bytes"
6 "context"
7 "encoding/json"
8 "errors"
9 "io/ioutil"
10 "net/http"
11 "net/url"
12
13 "github.com/gorilla/mux"
14
15 "github.com/go-kit/kit/log"
16 "github.com/go-kit/kit/transport"
17 httptransport "github.com/go-kit/kit/transport/http"
18 )
19
20 var (
21 // ErrBadRouting is returned when an expected path variable is missing.
22 // It always indicates programmer error.
23 ErrBadRouting = errors.New("inconsistent mapping between route and handler (programmer error)")
24 )
25
26 // MakeHTTPHandler mounts all of the service endpoints into an http.Handler.
27 // Useful in a profilesvc server.
28 func MakeHTTPHandler(s Service, logger log.Logger) http.Handler {
29 r := mux.NewRouter()
30 e := MakeServerEndpoints(s)
31 options := []httptransport.ServerOption{
32 httptransport.ServerErrorHandler(transport.NewLogErrorHandler(logger)),
33 httptransport.ServerErrorEncoder(encodeError),
34 }
35
36 // POST /profiles/ adds another profile
37 // GET /profiles/:id retrieves the given profile by id
38 // PUT /profiles/:id post updated profile information about the profile
39 // PATCH /profiles/:id partial updated profile information
40 // DELETE /profiles/:id remove the given profile
41 // GET /profiles/:id/addresses/ retrieve addresses associated with the profile
42 // GET /profiles/:id/addresses/:addressID retrieve a particular profile address
43 // POST /profiles/:id/addresses/ add a new address
44 // DELETE /profiles/:id/addresses/:addressID remove an address
45
46 r.Methods("POST").Path("/profiles/").Handler(httptransport.NewServer(
47 e.PostProfileEndpoint,
48 decodePostProfileRequest,
49 encodeResponse,
50 options...,
51 ))
52 r.Methods("GET").Path("/profiles/{id}").Handler(httptransport.NewServer(
53 e.GetProfileEndpoint,
54 decodeGetProfileRequest,
55 encodeResponse,
56 options...,
57 ))
58 r.Methods("PUT").Path("/profiles/{id}").Handler(httptransport.NewServer(
59 e.PutProfileEndpoint,
60 decodePutProfileRequest,
61 encodeResponse,
62 options...,
63 ))
64 r.Methods("PATCH").Path("/profiles/{id}").Handler(httptransport.NewServer(
65 e.PatchProfileEndpoint,
66 decodePatchProfileRequest,
67 encodeResponse,
68 options...,
69 ))
70 r.Methods("DELETE").Path("/profiles/{id}").Handler(httptransport.NewServer(
71 e.DeleteProfileEndpoint,
72 decodeDeleteProfileRequest,
73 encodeResponse,
74 options...,
75 ))
76 r.Methods("GET").Path("/profiles/{id}/addresses/").Handler(httptransport.NewServer(
77 e.GetAddressesEndpoint,
78 decodeGetAddressesRequest,
79 encodeResponse,
80 options...,
81 ))
82 r.Methods("GET").Path("/profiles/{id}/addresses/{addressID}").Handler(httptransport.NewServer(
83 e.GetAddressEndpoint,
84 decodeGetAddressRequest,
85 encodeResponse,
86 options...,
87 ))
88 r.Methods("POST").Path("/profiles/{id}/addresses/").Handler(httptransport.NewServer(
89 e.PostAddressEndpoint,
90 decodePostAddressRequest,
91 encodeResponse,
92 options...,
93 ))
94 r.Methods("DELETE").Path("/profiles/{id}/addresses/{addressID}").Handler(httptransport.NewServer(
95 e.DeleteAddressEndpoint,
96 decodeDeleteAddressRequest,
97 encodeResponse,
98 options...,
99 ))
100 return r
101 }
102
103 func decodePostProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
104 var req postProfileRequest
105 if e := json.NewDecoder(r.Body).Decode(&req.Profile); e != nil {
106 return nil, e
107 }
108 return req, nil
109 }
110
111 func decodeGetProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
112 vars := mux.Vars(r)
113 id, ok := vars["id"]
114 if !ok {
115 return nil, ErrBadRouting
116 }
117 return getProfileRequest{ID: id}, nil
118 }
119
120 func decodePutProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
121 vars := mux.Vars(r)
122 id, ok := vars["id"]
123 if !ok {
124 return nil, ErrBadRouting
125 }
126 var profile Profile
127 if err := json.NewDecoder(r.Body).Decode(&profile); err != nil {
128 return nil, err
129 }
130 return putProfileRequest{
131 ID: id,
132 Profile: profile,
133 }, nil
134 }
135
136 func decodePatchProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
137 vars := mux.Vars(r)
138 id, ok := vars["id"]
139 if !ok {
140 return nil, ErrBadRouting
141 }
142 var profile Profile
143 if err := json.NewDecoder(r.Body).Decode(&profile); err != nil {
144 return nil, err
145 }
146 return patchProfileRequest{
147 ID: id,
148 Profile: profile,
149 }, nil
150 }
151
152 func decodeDeleteProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
153 vars := mux.Vars(r)
154 id, ok := vars["id"]
155 if !ok {
156 return nil, ErrBadRouting
157 }
158 return deleteProfileRequest{ID: id}, nil
159 }
160
161 func decodeGetAddressesRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
162 vars := mux.Vars(r)
163 id, ok := vars["id"]
164 if !ok {
165 return nil, ErrBadRouting
166 }
167 return getAddressesRequest{ProfileID: id}, nil
168 }
169
170 func decodeGetAddressRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
171 vars := mux.Vars(r)
172 id, ok := vars["id"]
173 if !ok {
174 return nil, ErrBadRouting
175 }
176 addressID, ok := vars["addressID"]
177 if !ok {
178 return nil, ErrBadRouting
179 }
180 return getAddressRequest{
181 ProfileID: id,
182 AddressID: addressID,
183 }, nil
184 }
185
186 func decodePostAddressRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
187 vars := mux.Vars(r)
188 id, ok := vars["id"]
189 if !ok {
190 return nil, ErrBadRouting
191 }
192 var address Address
193 if err := json.NewDecoder(r.Body).Decode(&address); err != nil {
194 return nil, err
195 }
196 return postAddressRequest{
197 ProfileID: id,
198 Address: address,
199 }, nil
200 }
201
202 func decodeDeleteAddressRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
203 vars := mux.Vars(r)
204 id, ok := vars["id"]
205 if !ok {
206 return nil, ErrBadRouting
207 }
208 addressID, ok := vars["addressID"]
209 if !ok {
210 return nil, ErrBadRouting
211 }
212 return deleteAddressRequest{
213 ProfileID: id,
214 AddressID: addressID,
215 }, nil
216 }
217
218 func encodePostProfileRequest(ctx context.Context, req *http.Request, request interface{}) error {
219 // r.Methods("POST").Path("/profiles/")
220 req.URL.Path = "/profiles/"
221 return encodeRequest(ctx, req, request)
222 }
223
224 func encodeGetProfileRequest(ctx context.Context, req *http.Request, request interface{}) error {
225 // r.Methods("GET").Path("/profiles/{id}")
226 r := request.(getProfileRequest)
227 profileID := url.QueryEscape(r.ID)
228 req.URL.Path = "/profiles/" + profileID
229 return encodeRequest(ctx, req, request)
230 }
231
232 func encodePutProfileRequest(ctx context.Context, req *http.Request, request interface{}) error {
233 // r.Methods("PUT").Path("/profiles/{id}")
234 r := request.(putProfileRequest)
235 profileID := url.QueryEscape(r.ID)
236 req.URL.Path = "/profiles/" + profileID
237 return encodeRequest(ctx, req, request)
238 }
239
240 func encodePatchProfileRequest(ctx context.Context, req *http.Request, request interface{}) error {
241 // r.Methods("PATCH").Path("/profiles/{id}")
242 r := request.(patchProfileRequest)
243 profileID := url.QueryEscape(r.ID)
244 req.URL.Path = "/profiles/" + profileID
245 return encodeRequest(ctx, req, request)
246 }
247
248 func encodeDeleteProfileRequest(ctx context.Context, req *http.Request, request interface{}) error {
249 // r.Methods("DELETE").Path("/profiles/{id}")
250 r := request.(deleteProfileRequest)
251 profileID := url.QueryEscape(r.ID)
252 req.URL.Path = "/profiles/" + profileID
253 return encodeRequest(ctx, req, request)
254 }
255
256 func encodeGetAddressesRequest(ctx context.Context, req *http.Request, request interface{}) error {
257 // r.Methods("GET").Path("/profiles/{id}/addresses/")
258 r := request.(getAddressesRequest)
259 profileID := url.QueryEscape(r.ProfileID)
260 req.URL.Path = "/profiles/" + profileID + "/addresses/"
261 return encodeRequest(ctx, req, request)
262 }
263
264 func encodeGetAddressRequest(ctx context.Context, req *http.Request, request interface{}) error {
265 // r.Methods("GET").Path("/profiles/{id}/addresses/{addressID}")
266 r := request.(getAddressRequest)
267 profileID := url.QueryEscape(r.ProfileID)
268 addressID := url.QueryEscape(r.AddressID)
269 req.URL.Path = "/profiles/" + profileID + "/addresses/" + addressID
270 return encodeRequest(ctx, req, request)
271 }
272
273 func encodePostAddressRequest(ctx context.Context, req *http.Request, request interface{}) error {
274 // r.Methods("POST").Path("/profiles/{id}/addresses/")
275 r := request.(postAddressRequest)
276 profileID := url.QueryEscape(r.ProfileID)
277 req.URL.Path = "/profiles/" + profileID + "/addresses/"
278 return encodeRequest(ctx, req, request)
279 }
280
281 func encodeDeleteAddressRequest(ctx context.Context, req *http.Request, request interface{}) error {
282 // r.Methods("DELETE").Path("/profiles/{id}/addresses/{addressID}")
283 r := request.(deleteAddressRequest)
284 profileID := url.QueryEscape(r.ProfileID)
285 addressID := url.QueryEscape(r.AddressID)
286 req.URL.Path = "/profiles/" + profileID + "/addresses/" + addressID
287 return encodeRequest(ctx, req, request)
288 }
289
290 func decodePostProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) {
291 var response postProfileResponse
292 err := json.NewDecoder(resp.Body).Decode(&response)
293 return response, err
294 }
295
296 func decodeGetProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) {
297 var response getProfileResponse
298 err := json.NewDecoder(resp.Body).Decode(&response)
299 return response, err
300 }
301
302 func decodePutProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) {
303 var response putProfileResponse
304 err := json.NewDecoder(resp.Body).Decode(&response)
305 return response, err
306 }
307
308 func decodePatchProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) {
309 var response patchProfileResponse
310 err := json.NewDecoder(resp.Body).Decode(&response)
311 return response, err
312 }
313
314 func decodeDeleteProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) {
315 var response deleteProfileResponse
316 err := json.NewDecoder(resp.Body).Decode(&response)
317 return response, err
318 }
319
320 func decodeGetAddressesResponse(_ context.Context, resp *http.Response) (interface{}, error) {
321 var response getAddressesResponse
322 err := json.NewDecoder(resp.Body).Decode(&response)
323 return response, err
324 }
325
326 func decodeGetAddressResponse(_ context.Context, resp *http.Response) (interface{}, error) {
327 var response getAddressResponse
328 err := json.NewDecoder(resp.Body).Decode(&response)
329 return response, err
330 }
331
332 func decodePostAddressResponse(_ context.Context, resp *http.Response) (interface{}, error) {
333 var response postAddressResponse
334 err := json.NewDecoder(resp.Body).Decode(&response)
335 return response, err
336 }
337
338 func decodeDeleteAddressResponse(_ context.Context, resp *http.Response) (interface{}, error) {
339 var response deleteAddressResponse
340 err := json.NewDecoder(resp.Body).Decode(&response)
341 return response, err
342 }
343
344 // errorer is implemented by all concrete response types that may contain
345 // errors. It allows us to change the HTTP response code without needing to
346 // trigger an endpoint (transport-level) error. For more information, read the
347 // big comment in endpoints.go.
348 type errorer interface {
349 error() error
350 }
351
352 // encodeResponse is the common method to encode all response types to the
353 // client. I chose to do it this way because, since we're using JSON, there's no
354 // reason to provide anything more specific. It's certainly possible to
355 // specialize on a per-response (per-method) basis.
356 func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
357 if e, ok := response.(errorer); ok && e.error() != nil {
358 // Not a Go kit transport error, but a business-logic error.
359 // Provide those as HTTP errors.
360 encodeError(ctx, e.error(), w)
361 return nil
362 }
363 w.Header().Set("Content-Type", "application/json; charset=utf-8")
364 return json.NewEncoder(w).Encode(response)
365 }
366
367 // encodeRequest likewise JSON-encodes the request to the HTTP request body.
368 // Don't use it directly as a transport/http.Client EncodeRequestFunc:
369 // profilesvc endpoints require mutating the HTTP method and request path.
370 func encodeRequest(_ context.Context, req *http.Request, request interface{}) error {
371 var buf bytes.Buffer
372 err := json.NewEncoder(&buf).Encode(request)
373 if err != nil {
374 return err
375 }
376 req.Body = ioutil.NopCloser(&buf)
377 return nil
378 }
379
380 func encodeError(_ context.Context, err error, w http.ResponseWriter) {
381 if err == nil {
382 panic("encodeError with nil error")
383 }
384 w.Header().Set("Content-Type", "application/json; charset=utf-8")
385 w.WriteHeader(codeFrom(err))
386 json.NewEncoder(w).Encode(map[string]interface{}{
387 "error": err.Error(),
388 })
389 }
390
391 func codeFrom(err error) int {
392 switch err {
393 case ErrNotFound:
394 return http.StatusNotFound
395 case ErrAlreadyExists, ErrInconsistentIDs:
396 return http.StatusBadRequest
397 default:
398 return http.StatusInternalServerError
399 }
400 }
+0
-25
examples/shipping/README.md less more
0 # shipping
1
2 This example demonstrates a more real-world application consisting of multiple services.
3
4 ## Description
5
6 The implementation is based on the container shipping domain from the [Domain Driven Design](http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215) book by Eric Evans, which was [originally](http://dddsample.sourceforge.net/) implemented in Java but has since been ported to Go. This example is a somewhat stripped down version to demonstrate the use of Go kit. The [original Go application](https://github.com/marcusolsson/goddd) is maintained separately and accompanied by an [AngularJS application](https://github.com/marcusolsson/dddelivery-angularjs) as well as a mock [routing service](https://github.com/marcusolsson/pathfinder).
7
8 ### Organization
9
10 The application consists of three application services, `booking`, `handling` and `tracking`. Each of these is an individual Go kit service as seen in previous examples.
11
12 - __booking__ - used by the shipping company to book and route cargos.
13 - __handling__ - used by our staff around the world to register whenever the cargo has been received, loaded etc.
14 - __tracking__ - used by the customer to track the cargo along the route
15
16 There are also a few pure domain packages that contain some intricate business-logic. They provide domain objects and services that are used by each application service to provide interesting use-cases for the user.
17
18 `inmem` contains in-memory implementations for the repositories found in the domain packages.
19
20 The `routing` package provides a _domain service_ that is used to query an external application for possible routes.
21
22 ## Contributing
23
24 As with all Go kit examples you are more than welcome to contribute. If you do however, please consider contributing back to the original project as well.
+0
-139
examples/shipping/booking/endpoint.go less more
0 package booking
1
2 import (
3 "context"
4 "time"
5
6 "github.com/go-kit/kit/endpoint"
7
8 "github.com/go-kit/kit/examples/shipping/cargo"
9 "github.com/go-kit/kit/examples/shipping/location"
10 )
11
12 type bookCargoRequest struct {
13 Origin location.UNLocode
14 Destination location.UNLocode
15 ArrivalDeadline time.Time
16 }
17
18 type bookCargoResponse struct {
19 ID cargo.TrackingID `json:"tracking_id,omitempty"`
20 Err error `json:"error,omitempty"`
21 }
22
23 func (r bookCargoResponse) error() error { return r.Err }
24
25 func makeBookCargoEndpoint(s Service) endpoint.Endpoint {
26 return func(ctx context.Context, request interface{}) (interface{}, error) {
27 req := request.(bookCargoRequest)
28 id, err := s.BookNewCargo(req.Origin, req.Destination, req.ArrivalDeadline)
29 return bookCargoResponse{ID: id, Err: err}, nil
30 }
31 }
32
33 type loadCargoRequest struct {
34 ID cargo.TrackingID
35 }
36
37 type loadCargoResponse struct {
38 Cargo *Cargo `json:"cargo,omitempty"`
39 Err error `json:"error,omitempty"`
40 }
41
42 func (r loadCargoResponse) error() error { return r.Err }
43
44 func makeLoadCargoEndpoint(s Service) endpoint.Endpoint {
45 return func(ctx context.Context, request interface{}) (interface{}, error) {
46 req := request.(loadCargoRequest)
47 c, err := s.LoadCargo(req.ID)
48 return loadCargoResponse{Cargo: &c, Err: err}, nil
49 }
50 }
51
52 type requestRoutesRequest struct {
53 ID cargo.TrackingID
54 }
55
56 type requestRoutesResponse struct {
57 Routes []cargo.Itinerary `json:"routes,omitempty"`
58 Err error `json:"error,omitempty"`
59 }
60
61 func (r requestRoutesResponse) error() error { return r.Err }
62
63 func makeRequestRoutesEndpoint(s Service) endpoint.Endpoint {
64 return func(ctx context.Context, request interface{}) (interface{}, error) {
65 req := request.(requestRoutesRequest)
66 itin := s.RequestPossibleRoutesForCargo(req.ID)
67 return requestRoutesResponse{Routes: itin, Err: nil}, nil
68 }
69 }
70
71 type assignToRouteRequest struct {
72 ID cargo.TrackingID
73 Itinerary cargo.Itinerary
74 }
75
76 type assignToRouteResponse struct {
77 Err error `json:"error,omitempty"`
78 }
79
80 func (r assignToRouteResponse) error() error { return r.Err }
81
82 func makeAssignToRouteEndpoint(s Service) endpoint.Endpoint {
83 return func(ctx context.Context, request interface{}) (interface{}, error) {
84 req := request.(assignToRouteRequest)
85 err := s.AssignCargoToRoute(req.ID, req.Itinerary)
86 return assignToRouteResponse{Err: err}, nil
87 }
88 }
89
90 type changeDestinationRequest struct {
91 ID cargo.TrackingID
92 Destination location.UNLocode
93 }
94
95 type changeDestinationResponse struct {
96 Err error `json:"error,omitempty"`
97 }
98
99 func (r changeDestinationResponse) error() error { return r.Err }
100
101 func makeChangeDestinationEndpoint(s Service) endpoint.Endpoint {
102 return func(ctx context.Context, request interface{}) (interface{}, error) {
103 req := request.(changeDestinationRequest)
104 err := s.ChangeDestination(req.ID, req.Destination)
105 return changeDestinationResponse{Err: err}, nil
106 }
107 }
108
109 type listCargosRequest struct{}
110
111 type listCargosResponse struct {
112 Cargos []Cargo `json:"cargos,omitempty"`
113 Err error `json:"error,omitempty"`
114 }
115
116 func (r listCargosResponse) error() error { return r.Err }
117
118 func makeListCargosEndpoint(s Service) endpoint.Endpoint {
119 return func(ctx context.Context, request interface{}) (interface{}, error) {
120 _ = request.(listCargosRequest)
121 return listCargosResponse{Cargos: s.Cargos(), Err: nil}, nil
122 }
123 }
124
125 type listLocationsRequest struct {
126 }
127
128 type listLocationsResponse struct {
129 Locations []Location `json:"locations,omitempty"`
130 Err error `json:"error,omitempty"`
131 }
132
133 func makeListLocationsEndpoint(s Service) endpoint.Endpoint {
134 return func(ctx context.Context, request interface{}) (interface{}, error) {
135 _ = request.(listLocationsRequest)
136 return listLocationsResponse{Locations: s.Locations(), Err: nil}, nil
137 }
138 }
+0
-88
examples/shipping/booking/instrumenting.go less more
0 package booking
1
2 import (
3 "time"
4
5 "github.com/go-kit/kit/metrics"
6
7 "github.com/go-kit/kit/examples/shipping/cargo"
8 "github.com/go-kit/kit/examples/shipping/location"
9 )
10
11 type instrumentingService struct {
12 requestCount metrics.Counter
13 requestLatency metrics.Histogram
14 Service
15 }
16
17 // NewInstrumentingService returns an instance of an instrumenting Service.
18 func NewInstrumentingService(counter metrics.Counter, latency metrics.Histogram, s Service) Service {
19 return &instrumentingService{
20 requestCount: counter,
21 requestLatency: latency,
22 Service: s,
23 }
24 }
25
26 func (s *instrumentingService) BookNewCargo(origin, destination location.UNLocode, deadline time.Time) (cargo.TrackingID, error) {
27 defer func(begin time.Time) {
28 s.requestCount.With("method", "book").Add(1)
29 s.requestLatency.With("method", "book").Observe(time.Since(begin).Seconds())
30 }(time.Now())
31
32 return s.Service.BookNewCargo(origin, destination, deadline)
33 }
34
35 func (s *instrumentingService) LoadCargo(id cargo.TrackingID) (c Cargo, err error) {
36 defer func(begin time.Time) {
37 s.requestCount.With("method", "load").Add(1)
38 s.requestLatency.With("method", "load").Observe(time.Since(begin).Seconds())
39 }(time.Now())
40
41 return s.Service.LoadCargo(id)
42 }
43
44 func (s *instrumentingService) RequestPossibleRoutesForCargo(id cargo.TrackingID) []cargo.Itinerary {
45 defer func(begin time.Time) {
46 s.requestCount.With("method", "request_routes").Add(1)
47 s.requestLatency.With("method", "request_routes").Observe(time.Since(begin).Seconds())
48 }(time.Now())
49
50 return s.Service.RequestPossibleRoutesForCargo(id)
51 }
52
53 func (s *instrumentingService) AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) (err error) {
54 defer func(begin time.Time) {
55 s.requestCount.With("method", "assign_to_route").Add(1)
56 s.requestLatency.With("method", "assign_to_route").Observe(time.Since(begin).Seconds())
57 }(time.Now())
58
59 return s.Service.AssignCargoToRoute(id, itinerary)
60 }
61
62 func (s *instrumentingService) ChangeDestination(id cargo.TrackingID, l location.UNLocode) (err error) {
63 defer func(begin time.Time) {
64 s.requestCount.With("method", "change_destination").Add(1)
65 s.requestLatency.With("method", "change_destination").Observe(time.Since(begin).Seconds())
66 }(time.Now())
67
68 return s.Service.ChangeDestination(id, l)
69 }
70
71 func (s *instrumentingService) Cargos() []Cargo {
72 defer func(begin time.Time) {
73 s.requestCount.With("method", "list_cargos").Add(1)
74 s.requestLatency.With("method", "list_cargos").Observe(time.Since(begin).Seconds())
75 }(time.Now())
76
77 return s.Service.Cargos()
78 }
79
80 func (s *instrumentingService) Locations() []Location {
81 defer func(begin time.Time) {
82 s.requestCount.With("method", "list_locations").Add(1)
83 s.requestLatency.With("method", "list_locations").Observe(time.Since(begin).Seconds())
84 }(time.Now())
85
86 return s.Service.Locations()
87 }
+0
-102
examples/shipping/booking/logging.go less more
0 package booking
1
2 import (
3 "time"
4
5 "github.com/go-kit/kit/log"
6
7 "github.com/go-kit/kit/examples/shipping/cargo"
8 "github.com/go-kit/kit/examples/shipping/location"
9 )
10
11 type loggingService struct {
12 logger log.Logger
13 Service
14 }
15
16 // NewLoggingService returns a new instance of a logging Service.
17 func NewLoggingService(logger log.Logger, s Service) Service {
18 return &loggingService{logger, s}
19 }
20
21 func (s *loggingService) BookNewCargo(origin location.UNLocode, destination location.UNLocode, deadline time.Time) (id cargo.TrackingID, err error) {
22 defer func(begin time.Time) {
23 s.logger.Log(
24 "method", "book",
25 "origin", origin,
26 "destination", destination,
27 "arrival_deadline", deadline,
28 "took", time.Since(begin),
29 "err", err,
30 )
31 }(time.Now())
32 return s.Service.BookNewCargo(origin, destination, deadline)
33 }
34
35 func (s *loggingService) LoadCargo(id cargo.TrackingID) (c Cargo, err error) {
36 defer func(begin time.Time) {
37 s.logger.Log(
38 "method", "load",
39 "tracking_id", id,
40 "took", time.Since(begin),
41 "err", err,
42 )
43 }(time.Now())
44 return s.Service.LoadCargo(id)
45 }
46
47 func (s *loggingService) RequestPossibleRoutesForCargo(id cargo.TrackingID) []cargo.Itinerary {
48 defer func(begin time.Time) {
49 s.logger.Log(
50 "method", "request_routes",
51 "tracking_id", id,
52 "took", time.Since(begin),
53 )
54 }(time.Now())
55 return s.Service.RequestPossibleRoutesForCargo(id)
56 }
57
58 func (s *loggingService) AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) (err error) {
59 defer func(begin time.Time) {
60 s.logger.Log(
61 "method", "assign_to_route",
62 "tracking_id", id,
63 "took", time.Since(begin),
64 "err", err,
65 )
66 }(time.Now())
67 return s.Service.AssignCargoToRoute(id, itinerary)
68 }
69
70 func (s *loggingService) ChangeDestination(id cargo.TrackingID, l location.UNLocode) (err error) {
71 defer func(begin time.Time) {
72 s.logger.Log(
73 "method", "change_destination",
74 "tracking_id", id,
75 "destination", l,
76 "took", time.Since(begin),
77 "err", err,
78 )
79 }(time.Now())
80 return s.Service.ChangeDestination(id, l)
81 }
82
83 func (s *loggingService) Cargos() []Cargo {
84 defer func(begin time.Time) {
85 s.logger.Log(
86 "method", "list_cargos",
87 "took", time.Since(begin),
88 )
89 }(time.Now())
90 return s.Service.Cargos()
91 }
92
93 func (s *loggingService) Locations() []Location {
94 defer func(begin time.Time) {
95 s.logger.Log(
96 "method", "list_locations",
97 "took", time.Since(begin),
98 )
99 }(time.Now())
100 return s.Service.Locations()
101 }
+0
-197
examples/shipping/booking/service.go less more
0 // Package booking provides the use-case of booking a cargo. Used by views
1 // facing an administrator.
2 package booking
3
4 import (
5 "errors"
6 "time"
7
8 "github.com/go-kit/kit/examples/shipping/cargo"
9 "github.com/go-kit/kit/examples/shipping/location"
10 "github.com/go-kit/kit/examples/shipping/routing"
11 )
12
13 // ErrInvalidArgument is returned when one or more arguments are invalid.
14 var ErrInvalidArgument = errors.New("invalid argument")
15
16 // Service is the interface that provides booking methods.
17 type Service interface {
18 // BookNewCargo registers a new cargo in the tracking system, not yet
19 // routed.
20 BookNewCargo(origin location.UNLocode, destination location.UNLocode, deadline time.Time) (cargo.TrackingID, error)
21
22 // LoadCargo returns a read model of a cargo.
23 LoadCargo(id cargo.TrackingID) (Cargo, error)
24
25 // RequestPossibleRoutesForCargo requests a list of itineraries describing
26 // possible routes for this cargo.
27 RequestPossibleRoutesForCargo(id cargo.TrackingID) []cargo.Itinerary
28
29 // AssignCargoToRoute assigns a cargo to the route specified by the
30 // itinerary.
31 AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) error
32
33 // ChangeDestination changes the destination of a cargo.
34 ChangeDestination(id cargo.TrackingID, destination location.UNLocode) error
35
36 // Cargos returns a list of all cargos that have been booked.
37 Cargos() []Cargo
38
39 // Locations returns a list of registered locations.
40 Locations() []Location
41 }
42
43 type service struct {
44 cargos cargo.Repository
45 locations location.Repository
46 handlingEvents cargo.HandlingEventRepository
47 routingService routing.Service
48 }
49
50 func (s *service) AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) error {
51 if id == "" || len(itinerary.Legs) == 0 {
52 return ErrInvalidArgument
53 }
54
55 c, err := s.cargos.Find(id)
56 if err != nil {
57 return err
58 }
59
60 c.AssignToRoute(itinerary)
61
62 return s.cargos.Store(c)
63 }
64
65 func (s *service) BookNewCargo(origin, destination location.UNLocode, deadline time.Time) (cargo.TrackingID, error) {
66 if origin == "" || destination == "" || deadline.IsZero() {
67 return "", ErrInvalidArgument
68 }
69
70 id := cargo.NextTrackingID()
71 rs := cargo.RouteSpecification{
72 Origin: origin,
73 Destination: destination,
74 ArrivalDeadline: deadline,
75 }
76
77 c := cargo.New(id, rs)
78
79 if err := s.cargos.Store(c); err != nil {
80 return "", err
81 }
82
83 return c.TrackingID, nil
84 }
85
86 func (s *service) LoadCargo(id cargo.TrackingID) (Cargo, error) {
87 if id == "" {
88 return Cargo{}, ErrInvalidArgument
89 }
90
91 c, err := s.cargos.Find(id)
92 if err != nil {
93 return Cargo{}, err
94 }
95
96 return assemble(c, s.handlingEvents), nil
97 }
98
99 func (s *service) ChangeDestination(id cargo.TrackingID, destination location.UNLocode) error {
100 if id == "" || destination == "" {
101 return ErrInvalidArgument
102 }
103
104 c, err := s.cargos.Find(id)
105 if err != nil {
106 return err
107 }
108
109 l, err := s.locations.Find(destination)
110 if err != nil {
111 return err
112 }
113
114 c.SpecifyNewRoute(cargo.RouteSpecification{
115 Origin: c.Origin,
116 Destination: l.UNLocode,
117 ArrivalDeadline: c.RouteSpecification.ArrivalDeadline,
118 })
119
120 if err := s.cargos.Store(c); err != nil {
121 return err
122 }
123
124 return nil
125 }
126
127 func (s *service) RequestPossibleRoutesForCargo(id cargo.TrackingID) []cargo.Itinerary {
128 if id == "" {
129 return nil
130 }
131
132 c, err := s.cargos.Find(id)
133 if err != nil {
134 return []cargo.Itinerary{}
135 }
136
137 return s.routingService.FetchRoutesForSpecification(c.RouteSpecification)
138 }
139
140 func (s *service) Cargos() []Cargo {
141 var result []Cargo
142 for _, c := range s.cargos.FindAll() {
143 result = append(result, assemble(c, s.handlingEvents))
144 }
145 return result
146 }
147
148 func (s *service) Locations() []Location {
149 var result []Location
150 for _, v := range s.locations.FindAll() {
151 result = append(result, Location{
152 UNLocode: string(v.UNLocode),
153 Name: v.Name,
154 })
155 }
156 return result
157 }
158
159 // NewService creates a booking service with necessary dependencies.
160 func NewService(cargos cargo.Repository, locations location.Repository, events cargo.HandlingEventRepository, rs routing.Service) Service {
161 return &service{
162 cargos: cargos,
163 locations: locations,
164 handlingEvents: events,
165 routingService: rs,
166 }
167 }
168
169 // Location is a read model for booking views.
170 type Location struct {
171 UNLocode string `json:"locode"`
172 Name string `json:"name"`
173 }
174
175 // Cargo is a read model for booking views.
176 type Cargo struct {
177 ArrivalDeadline time.Time `json:"arrival_deadline"`
178 Destination string `json:"destination"`
179 Legs []cargo.Leg `json:"legs,omitempty"`
180 Misrouted bool `json:"misrouted"`
181 Origin string `json:"origin"`
182 Routed bool `json:"routed"`
183 TrackingID string `json:"tracking_id"`
184 }
185
186 func assemble(c *cargo.Cargo, events cargo.HandlingEventRepository) Cargo {
187 return Cargo{
188 TrackingID: string(c.TrackingID),
189 Origin: string(c.Origin),
190 Destination: string(c.RouteSpecification.Destination),
191 Misrouted: c.Delivery.RoutingStatus == cargo.Misrouted,
192 Routed: !c.Itinerary.IsEmpty(),
193 ArrivalDeadline: c.RouteSpecification.ArrivalDeadline,
194 Legs: c.Itinerary.Legs,
195 }
196 }
+0
-195
examples/shipping/booking/transport.go less more
0 package booking
1
2 import (
3 "context"
4 "encoding/json"
5 "errors"
6 "net/http"
7 "time"
8
9 "github.com/gorilla/mux"
10
11 kitlog "github.com/go-kit/kit/log"
12 "github.com/go-kit/kit/transport"
13 kithttp "github.com/go-kit/kit/transport/http"
14
15 "github.com/go-kit/kit/examples/shipping/cargo"
16 "github.com/go-kit/kit/examples/shipping/location"
17 )
18
19 // MakeHandler returns a handler for the booking service.
20 func MakeHandler(bs Service, logger kitlog.Logger) http.Handler {
21 opts := []kithttp.ServerOption{
22 kithttp.ServerErrorHandler(transport.NewLogErrorHandler(logger)),
23 kithttp.ServerErrorEncoder(encodeError),
24 }
25
26 bookCargoHandler := kithttp.NewServer(
27 makeBookCargoEndpoint(bs),
28 decodeBookCargoRequest,
29 encodeResponse,
30 opts...,
31 )
32 loadCargoHandler := kithttp.NewServer(
33 makeLoadCargoEndpoint(bs),
34 decodeLoadCargoRequest,
35 encodeResponse,
36 opts...,
37 )
38 requestRoutesHandler := kithttp.NewServer(
39 makeRequestRoutesEndpoint(bs),
40 decodeRequestRoutesRequest,
41 encodeResponse,
42 opts...,
43 )
44 assignToRouteHandler := kithttp.NewServer(
45 makeAssignToRouteEndpoint(bs),
46 decodeAssignToRouteRequest,
47 encodeResponse,
48 opts...,
49 )
50 changeDestinationHandler := kithttp.NewServer(
51 makeChangeDestinationEndpoint(bs),
52 decodeChangeDestinationRequest,
53 encodeResponse,
54 opts...,
55 )
56 listCargosHandler := kithttp.NewServer(
57 makeListCargosEndpoint(bs),
58 decodeListCargosRequest,
59 encodeResponse,
60 opts...,
61 )
62 listLocationsHandler := kithttp.NewServer(
63 makeListLocationsEndpoint(bs),
64 decodeListLocationsRequest,
65 encodeResponse,
66 opts...,
67 )
68
69 r := mux.NewRouter()
70
71 r.Handle("/booking/v1/cargos", bookCargoHandler).Methods("POST")
72 r.Handle("/booking/v1/cargos", listCargosHandler).Methods("GET")
73 r.Handle("/booking/v1/cargos/{id}", loadCargoHandler).Methods("GET")
74 r.Handle("/booking/v1/cargos/{id}/request_routes", requestRoutesHandler).Methods("GET")
75 r.Handle("/booking/v1/cargos/{id}/assign_to_route", assignToRouteHandler).Methods("POST")
76 r.Handle("/booking/v1/cargos/{id}/change_destination", changeDestinationHandler).Methods("POST")
77 r.Handle("/booking/v1/locations", listLocationsHandler).Methods("GET")
78
79 return r
80 }
81
82 var errBadRoute = errors.New("bad route")
83
84 func decodeBookCargoRequest(_ context.Context, r *http.Request) (interface{}, error) {
85 var body struct {
86 Origin string `json:"origin"`
87 Destination string `json:"destination"`
88 ArrivalDeadline time.Time `json:"arrival_deadline"`
89 }
90
91 if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
92 return nil, err
93 }
94
95 return bookCargoRequest{
96 Origin: location.UNLocode(body.Origin),
97 Destination: location.UNLocode(body.Destination),
98 ArrivalDeadline: body.ArrivalDeadline,
99 }, nil
100 }
101
102 func decodeLoadCargoRequest(_ context.Context, r *http.Request) (interface{}, error) {
103 vars := mux.Vars(r)
104 id, ok := vars["id"]
105 if !ok {
106 return nil, errBadRoute
107 }
108 return loadCargoRequest{ID: cargo.TrackingID(id)}, nil
109 }
110
111 func decodeRequestRoutesRequest(_ context.Context, r *http.Request) (interface{}, error) {
112 vars := mux.Vars(r)
113 id, ok := vars["id"]
114 if !ok {
115 return nil, errBadRoute
116 }
117 return requestRoutesRequest{ID: cargo.TrackingID(id)}, nil
118 }
119
120 func decodeAssignToRouteRequest(_ context.Context, r *http.Request) (interface{}, error) {
121 vars := mux.Vars(r)
122 id, ok := vars["id"]
123 if !ok {
124 return nil, errBadRoute
125 }
126
127 var itinerary cargo.Itinerary
128 if err := json.NewDecoder(r.Body).Decode(&itinerary); err != nil {
129 return nil, err
130 }
131
132 return assignToRouteRequest{
133 ID: cargo.TrackingID(id),
134 Itinerary: itinerary,
135 }, nil
136 }
137
138 func decodeChangeDestinationRequest(_ context.Context, r *http.Request) (interface{}, error) {
139 vars := mux.Vars(r)
140 id, ok := vars["id"]
141 if !ok {
142 return nil, errBadRoute
143 }
144
145 var body struct {
146 Destination string `json:"destination"`
147 }
148
149 if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
150 return nil, err
151 }
152
153 return changeDestinationRequest{
154 ID: cargo.TrackingID(id),
155 Destination: location.UNLocode(body.Destination),
156 }, nil
157 }
158
159 func decodeListCargosRequest(_ context.Context, r *http.Request) (interface{}, error) {
160 return listCargosRequest{}, nil
161 }
162
163 func decodeListLocationsRequest(_ context.Context, r *http.Request) (interface{}, error) {
164 return listLocationsRequest{}, nil
165 }
166
167 func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
168 if e, ok := response.(errorer); ok && e.error() != nil {
169 encodeError(ctx, e.error(), w)
170 return nil
171 }
172 w.Header().Set("Content-Type", "application/json; charset=utf-8")
173 return json.NewEncoder(w).Encode(response)
174 }
175
176 type errorer interface {
177 error() error
178 }
179
180 // encode errors from business-logic
181 func encodeError(_ context.Context, err error, w http.ResponseWriter) {
182 w.Header().Set("Content-Type", "application/json; charset=utf-8")
183 switch err {
184 case cargo.ErrUnknown:
185 w.WriteHeader(http.StatusNotFound)
186 case ErrInvalidArgument:
187 w.WriteHeader(http.StatusBadRequest)
188 default:
189 w.WriteHeader(http.StatusInternalServerError)
190 }
191 json.NewEncoder(w).Encode(map[string]interface{}{
192 "error": err.Error(),
193 })
194 }
+0
-137
examples/shipping/cargo/cargo.go less more
0 // Package cargo contains the heart of the domain model.
1 package cargo
2
3 import (
4 "errors"
5 "strings"
6 "time"
7
8 "github.com/pborman/uuid"
9
10 "github.com/go-kit/kit/examples/shipping/location"
11 )
12
13 // TrackingID uniquely identifies a particular cargo.
14 type TrackingID string
15
16 // Cargo is the central class in the domain model.
17 type Cargo struct {
18 TrackingID TrackingID
19 Origin location.UNLocode
20 RouteSpecification RouteSpecification
21 Itinerary Itinerary
22 Delivery Delivery
23 }
24
25 // SpecifyNewRoute specifies a new route for this cargo.
26 func (c *Cargo) SpecifyNewRoute(rs RouteSpecification) {
27 c.RouteSpecification = rs
28 c.Delivery = c.Delivery.UpdateOnRouting(c.RouteSpecification, c.Itinerary)
29 }
30
31 // AssignToRoute attaches a new itinerary to this cargo.
32 func (c *Cargo) AssignToRoute(itinerary Itinerary) {
33 c.Itinerary = itinerary
34 c.Delivery = c.Delivery.UpdateOnRouting(c.RouteSpecification, c.Itinerary)
35 }
36
37 // DeriveDeliveryProgress updates all aspects of the cargo aggregate status
38 // based on the current route specification, itinerary and handling of the cargo.
39 func (c *Cargo) DeriveDeliveryProgress(history HandlingHistory) {
40 c.Delivery = DeriveDeliveryFrom(c.RouteSpecification, c.Itinerary, history)
41 }
42
43 // New creates a new, unrouted cargo.
44 func New(id TrackingID, rs RouteSpecification) *Cargo {
45 itinerary := Itinerary{}
46 history := HandlingHistory{make([]HandlingEvent, 0)}
47
48 return &Cargo{
49 TrackingID: id,
50 Origin: rs.Origin,
51 RouteSpecification: rs,
52 Delivery: DeriveDeliveryFrom(rs, itinerary, history),
53 }
54 }
55
56 // Repository provides access to a cargo store.
57 type Repository interface {
58 Store(cargo *Cargo) error
59 Find(id TrackingID) (*Cargo, error)
60 FindAll() []*Cargo
61 }
62
63 // ErrUnknown is used when a cargo could not be found.
64 var ErrUnknown = errors.New("unknown cargo")
65
66 // NextTrackingID generates a new tracking ID.
67 // TODO: Move to infrastructure(?)
68 func NextTrackingID() TrackingID {
69 return TrackingID(strings.Split(strings.ToUpper(uuid.New()), "-")[0])
70 }
71
72 // RouteSpecification Contains information about a route: its origin,
73 // destination and arrival deadline.
74 type RouteSpecification struct {
75 Origin location.UNLocode
76 Destination location.UNLocode
77 ArrivalDeadline time.Time
78 }
79
80 // IsSatisfiedBy checks whether provided itinerary satisfies this
81 // specification.
82 func (s RouteSpecification) IsSatisfiedBy(itinerary Itinerary) bool {
83 return itinerary.Legs != nil &&
84 s.Origin == itinerary.InitialDepartureLocation() &&
85 s.Destination == itinerary.FinalArrivalLocation()
86 }
87
88 // RoutingStatus describes status of cargo routing.
89 type RoutingStatus int
90
91 // Valid routing statuses.
92 const (
93 NotRouted RoutingStatus = iota
94 Misrouted
95 Routed
96 )
97
98 func (s RoutingStatus) String() string {
99 switch s {
100 case NotRouted:
101 return "Not routed"
102 case Misrouted:
103 return "Misrouted"
104 case Routed:
105 return "Routed"
106 }
107 return ""
108 }
109
110 // TransportStatus describes status of cargo transportation.
111 type TransportStatus int
112
113 // Valid transport statuses.
114 const (
115 NotReceived TransportStatus = iota
116 InPort
117 OnboardCarrier
118 Claimed
119 Unknown
120 )
121
122 func (s TransportStatus) String() string {
123 switch s {
124 case NotReceived:
125 return "Not received"
126 case InPort:
127 return "In port"
128 case OnboardCarrier:
129 return "Onboard carrier"
130 case Claimed:
131 return "Claimed"
132 case Unknown:
133 return "Unknown"
134 }
135 return ""
136 }
+0
-174
examples/shipping/cargo/delivery.go less more
0 package cargo
1
2 import (
3 "time"
4
5 "github.com/go-kit/kit/examples/shipping/location"
6 "github.com/go-kit/kit/examples/shipping/voyage"
7 )
8
9 // Delivery is the actual transportation of the cargo, as opposed to the
10 // customer requirement (RouteSpecification) and the plan (Itinerary).
11 type Delivery struct {
12 Itinerary Itinerary
13 RouteSpecification RouteSpecification
14 RoutingStatus RoutingStatus
15 TransportStatus TransportStatus
16 NextExpectedActivity HandlingActivity
17 LastEvent HandlingEvent
18 LastKnownLocation location.UNLocode
19 CurrentVoyage voyage.Number
20 ETA time.Time
21 IsMisdirected bool
22 IsUnloadedAtDestination bool
23 }
24
25 // UpdateOnRouting creates a new delivery snapshot to reflect changes in
26 // routing, i.e. when the route specification or the itinerary has changed but
27 // no additional handling of the cargo has been performed.
28 func (d Delivery) UpdateOnRouting(rs RouteSpecification, itinerary Itinerary) Delivery {
29 return newDelivery(d.LastEvent, itinerary, rs)
30 }
31
32 // IsOnTrack checks if the delivery is on track.
33 func (d Delivery) IsOnTrack() bool {
34 return d.RoutingStatus == Routed && !d.IsMisdirected
35 }
36
37 // DeriveDeliveryFrom creates a new delivery snapshot based on the complete
38 // handling history of a cargo, as well as its route specification and
39 // itinerary.
40 func DeriveDeliveryFrom(rs RouteSpecification, itinerary Itinerary, history HandlingHistory) Delivery {
41 lastEvent, _ := history.MostRecentlyCompletedEvent()
42 return newDelivery(lastEvent, itinerary, rs)
43 }
44
45 // newDelivery creates a up-to-date delivery based on an handling event,
46 // itinerary and a route specification.
47 func newDelivery(lastEvent HandlingEvent, itinerary Itinerary, rs RouteSpecification) Delivery {
48 var (
49 routingStatus = calculateRoutingStatus(itinerary, rs)
50 transportStatus = calculateTransportStatus(lastEvent)
51 lastKnownLocation = calculateLastKnownLocation(lastEvent)
52 isMisdirected = calculateMisdirectedStatus(lastEvent, itinerary)
53 isUnloadedAtDestination = calculateUnloadedAtDestination(lastEvent, rs)
54 currentVoyage = calculateCurrentVoyage(transportStatus, lastEvent)
55 )
56
57 d := Delivery{
58 LastEvent: lastEvent,
59 Itinerary: itinerary,
60 RouteSpecification: rs,
61 RoutingStatus: routingStatus,
62 TransportStatus: transportStatus,
63 LastKnownLocation: lastKnownLocation,
64 IsMisdirected: isMisdirected,
65 IsUnloadedAtDestination: isUnloadedAtDestination,
66 CurrentVoyage: currentVoyage,
67 }
68
69 d.NextExpectedActivity = calculateNextExpectedActivity(d)
70 d.ETA = calculateETA(d)
71
72 return d
73 }
74
75 // Below are internal functions used when creating a new delivery.
76
77 func calculateRoutingStatus(itinerary Itinerary, rs RouteSpecification) RoutingStatus {
78 if itinerary.Legs == nil {
79 return NotRouted
80 }
81
82 if rs.IsSatisfiedBy(itinerary) {
83 return Routed
84 }
85
86 return Misrouted
87 }
88
89 func calculateMisdirectedStatus(event HandlingEvent, itinerary Itinerary) bool {
90 if event.Activity.Type == NotHandled {
91 return false
92 }
93
94 return !itinerary.IsExpected(event)
95 }
96
97 func calculateUnloadedAtDestination(event HandlingEvent, rs RouteSpecification) bool {
98 if event.Activity.Type == NotHandled {
99 return false
100 }
101
102 return event.Activity.Type == Unload && rs.Destination == event.Activity.Location
103 }
104
105 func calculateTransportStatus(event HandlingEvent) TransportStatus {
106 switch event.Activity.Type {
107 case NotHandled:
108 return NotReceived
109 case Load:
110 return OnboardCarrier
111 case Unload:
112 return InPort
113 case Receive:
114 return InPort
115 case Customs:
116 return InPort
117 case Claim:
118 return Claimed
119 }
120 return Unknown
121 }
122
123 func calculateLastKnownLocation(event HandlingEvent) location.UNLocode {
124 return event.Activity.Location
125 }
126
127 func calculateNextExpectedActivity(d Delivery) HandlingActivity {
128 if !d.IsOnTrack() {
129 return HandlingActivity{}
130 }
131
132 switch d.LastEvent.Activity.Type {
133 case NotHandled:
134 return HandlingActivity{Type: Receive, Location: d.RouteSpecification.Origin}
135 case Receive:
136 l := d.Itinerary.Legs[0]
137 return HandlingActivity{Type: Load, Location: l.LoadLocation, VoyageNumber: l.VoyageNumber}
138 case Load:
139 for _, l := range d.Itinerary.Legs {
140 if l.LoadLocation == d.LastEvent.Activity.Location {
141 return HandlingActivity{Type: Unload, Location: l.UnloadLocation, VoyageNumber: l.VoyageNumber}
142 }
143 }
144 case Unload:
145 for i, l := range d.Itinerary.Legs {
146 if l.UnloadLocation == d.LastEvent.Activity.Location {
147 if i < len(d.Itinerary.Legs)-1 {
148 return HandlingActivity{Type: Load, Location: d.Itinerary.Legs[i+1].LoadLocation, VoyageNumber: d.Itinerary.Legs[i+1].VoyageNumber}
149 }
150
151 return HandlingActivity{Type: Claim, Location: l.UnloadLocation}
152 }
153 }
154 }
155
156 return HandlingActivity{}
157 }
158
159 func calculateCurrentVoyage(transportStatus TransportStatus, event HandlingEvent) voyage.Number {
160 if transportStatus == OnboardCarrier && event.Activity.Type != NotHandled {
161 return event.Activity.VoyageNumber
162 }
163
164 return voyage.Number("")
165 }
166
167 func calculateETA(d Delivery) time.Time {
168 if !d.IsOnTrack() {
169 return time.Time{}
170 }
171
172 return d.Itinerary.FinalArrivalTime()
173 }
+0
-121
examples/shipping/cargo/handling.go less more
0 package cargo
1
2 // TODO: It would make sense to have this in its own package. Unfortunately,
3 // then there would be a circular dependency between the cargo and handling
4 // packages since cargo.Delivery would use handling.HandlingEvent and
5 // handling.HandlingEvent would use cargo.TrackingID. Also,
6 // HandlingEventFactory depends on the cargo repository.
7 //
8 // It would make sense not having the cargo package depend on handling.
9
10 import (
11 "errors"
12 "time"
13
14 "github.com/go-kit/kit/examples/shipping/location"
15 "github.com/go-kit/kit/examples/shipping/voyage"
16 )
17
18 // HandlingActivity represents how and where a cargo can be handled, and can
19 // be used to express predictions about what is expected to happen to a cargo
20 // in the future.
21 type HandlingActivity struct {
22 Type HandlingEventType
23 Location location.UNLocode
24 VoyageNumber voyage.Number
25 }
26
27 // HandlingEvent is used to register the event when, for instance, a cargo is
28 // unloaded from a carrier at a some location at a given time.
29 type HandlingEvent struct {
30 TrackingID TrackingID
31 Activity HandlingActivity
32 }
33
34 // HandlingEventType describes type of a handling event.
35 type HandlingEventType int
36
37 // Valid handling event types.
38 const (
39 NotHandled HandlingEventType = iota
40 Load
41 Unload
42 Receive
43 Claim
44 Customs
45 )
46
47 func (t HandlingEventType) String() string {
48 switch t {
49 case NotHandled:
50 return "Not Handled"
51 case Load:
52 return "Load"
53 case Unload:
54 return "Unload"
55 case Receive:
56 return "Receive"
57 case Claim:
58 return "Claim"
59 case Customs:
60 return "Customs"
61 }
62
63 return ""
64 }
65
66 // HandlingHistory is the handling history of a cargo.
67 type HandlingHistory struct {
68 HandlingEvents []HandlingEvent
69 }
70
71 // MostRecentlyCompletedEvent returns most recently completed handling event.
72 func (h HandlingHistory) MostRecentlyCompletedEvent() (HandlingEvent, error) {
73 if len(h.HandlingEvents) == 0 {
74 return HandlingEvent{}, errors.New("delivery history is empty")
75 }
76
77 return h.HandlingEvents[len(h.HandlingEvents)-1], nil
78 }
79
80 // HandlingEventRepository provides access a handling event store.
81 type HandlingEventRepository interface {
82 Store(e HandlingEvent)
83 QueryHandlingHistory(TrackingID) HandlingHistory
84 }
85
86 // HandlingEventFactory creates handling events.
87 type HandlingEventFactory struct {
88 CargoRepository Repository
89 VoyageRepository voyage.Repository
90 LocationRepository location.Repository
91 }
92
93 // CreateHandlingEvent creates a validated handling event.
94 func (f *HandlingEventFactory) CreateHandlingEvent(registered time.Time, completed time.Time, id TrackingID,
95 voyageNumber voyage.Number, unLocode location.UNLocode, eventType HandlingEventType) (HandlingEvent, error) {
96
97 if _, err := f.CargoRepository.Find(id); err != nil {
98 return HandlingEvent{}, err
99 }
100
101 if _, err := f.VoyageRepository.Find(voyageNumber); err != nil {
102 // TODO: This is pretty ugly, but when creating a Receive event, the voyage number is not known.
103 if len(voyageNumber) > 0 {
104 return HandlingEvent{}, err
105 }
106 }
107
108 if _, err := f.LocationRepository.Find(unLocode); err != nil {
109 return HandlingEvent{}, err
110 }
111
112 return HandlingEvent{
113 TrackingID: id,
114 Activity: HandlingActivity{
115 Type: eventType,
116 Location: unLocode,
117 VoyageNumber: voyageNumber,
118 },
119 }, nil
120 }
+0
-91
examples/shipping/cargo/itinerary.go less more
0 package cargo
1
2 import (
3 "time"
4
5 "github.com/go-kit/kit/examples/shipping/location"
6 "github.com/go-kit/kit/examples/shipping/voyage"
7 )
8
9 // Leg describes the transportation between two locations on a voyage.
10 type Leg struct {
11 VoyageNumber voyage.Number `json:"voyage_number"`
12 LoadLocation location.UNLocode `json:"from"`
13 UnloadLocation location.UNLocode `json:"to"`
14 LoadTime time.Time `json:"load_time"`
15 UnloadTime time.Time `json:"unload_time"`
16 }
17
18 // NewLeg creates a new itinerary leg.
19 func NewLeg(voyageNumber voyage.Number, loadLocation, unloadLocation location.UNLocode, loadTime, unloadTime time.Time) Leg {
20 return Leg{
21 VoyageNumber: voyageNumber,
22 LoadLocation: loadLocation,
23 UnloadLocation: unloadLocation,
24 LoadTime: loadTime,
25 UnloadTime: unloadTime,
26 }
27 }
28
29 // Itinerary specifies steps required to transport a cargo from its origin to
30 // destination.
31 type Itinerary struct {
32 Legs []Leg `json:"legs"`
33 }
34
35 // InitialDepartureLocation returns the start of the itinerary.
36 func (i Itinerary) InitialDepartureLocation() location.UNLocode {
37 if i.IsEmpty() {
38 return location.UNLocode("")
39 }
40 return i.Legs[0].LoadLocation
41 }
42
43 // FinalArrivalLocation returns the end of the itinerary.
44 func (i Itinerary) FinalArrivalLocation() location.UNLocode {
45 if i.IsEmpty() {
46 return location.UNLocode("")
47 }
48 return i.Legs[len(i.Legs)-1].UnloadLocation
49 }
50
51 // FinalArrivalTime returns the expected arrival time at final destination.
52 func (i Itinerary) FinalArrivalTime() time.Time {
53 return i.Legs[len(i.Legs)-1].UnloadTime
54 }
55
56 // IsEmpty checks if the itinerary contains at least one leg.
57 func (i Itinerary) IsEmpty() bool {
58 return i.Legs == nil || len(i.Legs) == 0
59 }
60
61 // IsExpected checks if the given handling event is expected when executing
62 // this itinerary.
63 func (i Itinerary) IsExpected(event HandlingEvent) bool {
64 if i.IsEmpty() {
65 return true
66 }
67
68 switch event.Activity.Type {
69 case Receive:
70 return i.InitialDepartureLocation() == event.Activity.Location
71 case Load:
72 for _, l := range i.Legs {
73 if l.LoadLocation == event.Activity.Location && l.VoyageNumber == event.Activity.VoyageNumber {
74 return true
75 }
76 }
77 return false
78 case Unload:
79 for _, l := range i.Legs {
80 if l.UnloadLocation == event.Activity.Location && l.VoyageNumber == event.Activity.VoyageNumber {
81 return true
82 }
83 }
84 return false
85 case Claim:
86 return i.FinalArrivalLocation() == event.Activity.Location
87 }
88
89 return true
90 }
+0
-34
examples/shipping/handling/endpoint.go less more
0 package handling
1
2 import (
3 "context"
4 "time"
5
6 "github.com/go-kit/kit/endpoint"
7
8 "github.com/go-kit/kit/examples/shipping/cargo"
9 "github.com/go-kit/kit/examples/shipping/location"
10 "github.com/go-kit/kit/examples/shipping/voyage"
11 )
12
13 type registerIncidentRequest struct {
14 ID cargo.TrackingID
15 Location location.UNLocode
16 Voyage voyage.Number
17 EventType cargo.HandlingEventType
18 CompletionTime time.Time
19 }
20
21 type registerIncidentResponse struct {
22 Err error `json:"error,omitempty"`
23 }
24
25 func (r registerIncidentResponse) error() error { return r.Err }
26
27 func makeRegisterIncidentEndpoint(hs Service) endpoint.Endpoint {
28 return func(ctx context.Context, request interface{}) (interface{}, error) {
29 req := request.(registerIncidentRequest)
30 err := hs.RegisterHandlingEvent(req.CompletionTime, req.ID, req.Voyage, req.Location, req.EventType)
31 return registerIncidentResponse{Err: err}, nil
32 }
33 }
+0
-37
examples/shipping/handling/instrumenting.go less more
0 package handling
1
2 import (
3 "time"
4
5 "github.com/go-kit/kit/metrics"
6
7 "github.com/go-kit/kit/examples/shipping/cargo"
8 "github.com/go-kit/kit/examples/shipping/location"
9 "github.com/go-kit/kit/examples/shipping/voyage"
10 )
11
12 type instrumentingService struct {
13 requestCount metrics.Counter
14 requestLatency metrics.Histogram
15 Service
16 }
17
18 // NewInstrumentingService returns an instance of an instrumenting Service.
19 func NewInstrumentingService(counter metrics.Counter, latency metrics.Histogram, s Service) Service {
20 return &instrumentingService{
21 requestCount: counter,
22 requestLatency: latency,
23 Service: s,
24 }
25 }
26
27 func (s *instrumentingService) RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number,
28 loc location.UNLocode, eventType cargo.HandlingEventType) error {
29
30 defer func(begin time.Time) {
31 s.requestCount.With("method", "register_incident").Add(1)
32 s.requestLatency.With("method", "register_incident").Observe(time.Since(begin).Seconds())
33 }(time.Now())
34
35 return s.Service.RegisterHandlingEvent(completed, id, voyageNumber, loc, eventType)
36 }
+0
-38
examples/shipping/handling/logging.go less more
0 package handling
1
2 import (
3 "time"
4
5 "github.com/go-kit/kit/log"
6
7 "github.com/go-kit/kit/examples/shipping/cargo"
8 "github.com/go-kit/kit/examples/shipping/location"
9 "github.com/go-kit/kit/examples/shipping/voyage"
10 )
11
12 type loggingService struct {
13 logger log.Logger
14 Service
15 }
16
17 // NewLoggingService returns a new instance of a logging Service.
18 func NewLoggingService(logger log.Logger, s Service) Service {
19 return &loggingService{logger, s}
20 }
21
22 func (s *loggingService) RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number,
23 unLocode location.UNLocode, eventType cargo.HandlingEventType) (err error) {
24 defer func(begin time.Time) {
25 s.logger.Log(
26 "method", "register_incident",
27 "tracking_id", id,
28 "location", unLocode,
29 "voyage", voyageNumber,
30 "event_type", eventType,
31 "completion_time", completed,
32 "took", time.Since(begin),
33 "err", err,
34 )
35 }(time.Now())
36 return s.Service.RegisterHandlingEvent(completed, id, voyageNumber, unLocode, eventType)
37 }
+0
-76
examples/shipping/handling/service.go less more
0 // Package handling provides the use-case for registering incidents. Used by
1 // views facing the people handling the cargo along its route.
2 package handling
3
4 import (
5 "errors"
6 "time"
7
8 "github.com/go-kit/kit/examples/shipping/cargo"
9 "github.com/go-kit/kit/examples/shipping/inspection"
10 "github.com/go-kit/kit/examples/shipping/location"
11 "github.com/go-kit/kit/examples/shipping/voyage"
12 )
13
14 // ErrInvalidArgument is returned when one or more arguments are invalid.
15 var ErrInvalidArgument = errors.New("invalid argument")
16
17 // EventHandler provides a means of subscribing to registered handling events.
18 type EventHandler interface {
19 CargoWasHandled(cargo.HandlingEvent)
20 }
21
22 // Service provides handling operations.
23 type Service interface {
24 // RegisterHandlingEvent registers a handling event in the system, and
25 // notifies interested parties that a cargo has been handled.
26 RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number,
27 unLocode location.UNLocode, eventType cargo.HandlingEventType) error
28 }
29
30 type service struct {
31 handlingEventRepository cargo.HandlingEventRepository
32 handlingEventFactory cargo.HandlingEventFactory
33 handlingEventHandler EventHandler
34 }
35
36 func (s *service) RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number,
37 loc location.UNLocode, eventType cargo.HandlingEventType) error {
38 if completed.IsZero() || id == "" || loc == "" || eventType == cargo.NotHandled {
39 return ErrInvalidArgument
40 }
41
42 e, err := s.handlingEventFactory.CreateHandlingEvent(time.Now(), completed, id, voyageNumber, loc, eventType)
43 if err != nil {
44 return err
45 }
46
47 s.handlingEventRepository.Store(e)
48 s.handlingEventHandler.CargoWasHandled(e)
49
50 return nil
51 }
52
53 // NewService creates a handling event service with necessary dependencies.
54 func NewService(r cargo.HandlingEventRepository, f cargo.HandlingEventFactory, h EventHandler) Service {
55 return &service{
56 handlingEventRepository: r,
57 handlingEventFactory: f,
58 handlingEventHandler: h,
59 }
60 }
61
62 type handlingEventHandler struct {
63 InspectionService inspection.Service
64 }
65
66 func (h *handlingEventHandler) CargoWasHandled(event cargo.HandlingEvent) {
67 h.InspectionService.InspectCargo(event.TrackingID)
68 }
69
70 // NewEventHandler returns a new instance of a EventHandler.
71 func NewEventHandler(s inspection.Service) EventHandler {
72 return &handlingEventHandler{
73 InspectionService: s,
74 }
75 }
+0
-101
examples/shipping/handling/transport.go less more
0 package handling
1
2 import (
3 "context"
4 "encoding/json"
5 "net/http"
6 "time"
7
8 "github.com/gorilla/mux"
9
10 kitlog "github.com/go-kit/kit/log"
11 "github.com/go-kit/kit/transport"
12 kithttp "github.com/go-kit/kit/transport/http"
13
14 "github.com/go-kit/kit/examples/shipping/cargo"
15 "github.com/go-kit/kit/examples/shipping/location"
16 "github.com/go-kit/kit/examples/shipping/voyage"
17 )
18
19 // MakeHandler returns a handler for the handling service.
20 func MakeHandler(hs Service, logger kitlog.Logger) http.Handler {
21 r := mux.NewRouter()
22
23 opts := []kithttp.ServerOption{
24 kithttp.ServerErrorHandler(transport.NewLogErrorHandler(logger)),
25 kithttp.ServerErrorEncoder(encodeError),
26 }
27
28 registerIncidentHandler := kithttp.NewServer(
29 makeRegisterIncidentEndpoint(hs),
30 decodeRegisterIncidentRequest,
31 encodeResponse,
32 opts...,
33 )
34
35 r.Handle("/handling/v1/incidents", registerIncidentHandler).Methods("POST")
36
37 return r
38 }
39
40 func decodeRegisterIncidentRequest(_ context.Context, r *http.Request) (interface{}, error) {
41 var body struct {
42 CompletionTime time.Time `json:"completion_time"`
43 TrackingID string `json:"tracking_id"`
44 VoyageNumber string `json:"voyage"`
45 Location string `json:"location"`
46 EventType string `json:"event_type"`
47 }
48
49 if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
50 return nil, err
51 }
52
53 return registerIncidentRequest{
54 CompletionTime: body.CompletionTime,
55 ID: cargo.TrackingID(body.TrackingID),
56 Voyage: voyage.Number(body.VoyageNumber),
57 Location: location.UNLocode(body.Location),
58 EventType: stringToEventType(body.EventType),
59 }, nil
60 }
61
62 func stringToEventType(s string) cargo.HandlingEventType {
63 types := map[string]cargo.HandlingEventType{
64 cargo.Receive.String(): cargo.Receive,
65 cargo.Load.String(): cargo.Load,
66 cargo.Unload.String(): cargo.Unload,
67 cargo.Customs.String(): cargo.Customs,
68 cargo.Claim.String(): cargo.Claim,
69 }
70 return types[s]
71 }
72
73 func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
74 if e, ok := response.(errorer); ok && e.error() != nil {
75 encodeError(ctx, e.error(), w)
76 return nil
77 }
78 w.Header().Set("Content-Type", "application/json; charset=utf-8")
79 return json.NewEncoder(w).Encode(response)
80 }
81
82 type errorer interface {
83 error() error
84 }
85
86 // encode errors from business-logic
87 func encodeError(_ context.Context, err error, w http.ResponseWriter) {
88 w.Header().Set("Content-Type", "application/json; charset=utf-8")
89 switch err {
90 case cargo.ErrUnknown:
91 w.WriteHeader(http.StatusNotFound)
92 case ErrInvalidArgument:
93 w.WriteHeader(http.StatusBadRequest)
94 default:
95 w.WriteHeader(http.StatusInternalServerError)
96 }
97 json.NewEncoder(w).Encode(map[string]interface{}{
98 "error": err.Error(),
99 })
100 }
+0
-142
examples/shipping/inmem/inmem.go less more
0 // Package inmem provides in-memory implementations of all the domain repositories.
1 package inmem
2
3 import (
4 "sync"
5
6 "github.com/go-kit/kit/examples/shipping/cargo"
7 "github.com/go-kit/kit/examples/shipping/location"
8 "github.com/go-kit/kit/examples/shipping/voyage"
9 )
10
11 type cargoRepository struct {
12 mtx sync.RWMutex
13 cargos map[cargo.TrackingID]*cargo.Cargo
14 }
15
16 func (r *cargoRepository) Store(c *cargo.Cargo) error {
17 r.mtx.Lock()
18 defer r.mtx.Unlock()
19 r.cargos[c.TrackingID] = c
20 return nil
21 }
22
23 func (r *cargoRepository) Find(id cargo.TrackingID) (*cargo.Cargo, error) {
24 r.mtx.RLock()
25 defer r.mtx.RUnlock()
26 if val, ok := r.cargos[id]; ok {
27 return val, nil
28 }
29 return nil, cargo.ErrUnknown
30 }
31
32 func (r *cargoRepository) FindAll() []*cargo.Cargo {
33 r.mtx.RLock()
34 defer r.mtx.RUnlock()
35 c := make([]*cargo.Cargo, 0, len(r.cargos))
36 for _, val := range r.cargos {
37 c = append(c, val)
38 }
39 return c
40 }
41
42 // NewCargoRepository returns a new instance of a in-memory cargo repository.
43 func NewCargoRepository() cargo.Repository {
44 return &cargoRepository{
45 cargos: make(map[cargo.TrackingID]*cargo.Cargo),
46 }
47 }
48
49 type locationRepository struct {
50 locations map[location.UNLocode]*location.Location
51 }
52
53 func (r *locationRepository) Find(locode location.UNLocode) (*location.Location, error) {
54 if l, ok := r.locations[locode]; ok {
55 return l, nil
56 }
57 return nil, location.ErrUnknown
58 }
59
60 func (r *locationRepository) FindAll() []*location.Location {
61 l := make([]*location.Location, 0, len(r.locations))
62 for _, val := range r.locations {
63 l = append(l, val)
64 }
65 return l
66 }
67
68 // NewLocationRepository returns a new instance of a in-memory location repository.
69 func NewLocationRepository() location.Repository {
70 r := &locationRepository{
71 locations: make(map[location.UNLocode]*location.Location),
72 }
73
74 r.locations[location.SESTO] = location.Stockholm
75 r.locations[location.AUMEL] = location.Melbourne
76 r.locations[location.CNHKG] = location.Hongkong
77 r.locations[location.JNTKO] = location.Tokyo
78 r.locations[location.NLRTM] = location.Rotterdam
79 r.locations[location.DEHAM] = location.Hamburg
80
81 return r
82 }
83
84 type voyageRepository struct {
85 voyages map[voyage.Number]*voyage.Voyage
86 }
87
88 func (r *voyageRepository) Find(voyageNumber voyage.Number) (*voyage.Voyage, error) {
89 if v, ok := r.voyages[voyageNumber]; ok {
90 return v, nil
91 }
92
93 return nil, voyage.ErrUnknown
94 }
95
96 // NewVoyageRepository returns a new instance of a in-memory voyage repository.
97 func NewVoyageRepository() voyage.Repository {
98 r := &voyageRepository{
99 voyages: make(map[voyage.Number]*voyage.Voyage),
100 }
101
102 r.voyages[voyage.V100.Number] = voyage.V100
103 r.voyages[voyage.V300.Number] = voyage.V300
104 r.voyages[voyage.V400.Number] = voyage.V400
105
106 r.voyages[voyage.V0100S.Number] = voyage.V0100S
107 r.voyages[voyage.V0200T.Number] = voyage.V0200T
108 r.voyages[voyage.V0300A.Number] = voyage.V0300A
109 r.voyages[voyage.V0301S.Number] = voyage.V0301S
110 r.voyages[voyage.V0400S.Number] = voyage.V0400S
111
112 return r
113 }
114
115 type handlingEventRepository struct {
116 mtx sync.RWMutex
117 events map[cargo.TrackingID][]cargo.HandlingEvent
118 }
119
120 func (r *handlingEventRepository) Store(e cargo.HandlingEvent) {
121 r.mtx.Lock()
122 defer r.mtx.Unlock()
123 // Make array if it's the first event with this tracking ID.
124 if _, ok := r.events[e.TrackingID]; !ok {
125 r.events[e.TrackingID] = make([]cargo.HandlingEvent, 0)
126 }
127 r.events[e.TrackingID] = append(r.events[e.TrackingID], e)
128 }
129
130 func (r *handlingEventRepository) QueryHandlingHistory(id cargo.TrackingID) cargo.HandlingHistory {
131 r.mtx.RLock()
132 defer r.mtx.RUnlock()
133 return cargo.HandlingHistory{HandlingEvents: r.events[id]}
134 }
135
136 // NewHandlingEventRepository returns a new instance of a in-memory handling event repository.
137 func NewHandlingEventRepository() cargo.HandlingEventRepository {
138 return &handlingEventRepository{
139 events: make(map[cargo.TrackingID][]cargo.HandlingEvent),
140 }
141 }
+0
-53
examples/shipping/inspection/inspection.go less more
0 // Package inspection provides means to inspect cargos.
1 package inspection
2
3 import (
4 "github.com/go-kit/kit/examples/shipping/cargo"
5 )
6
7 // EventHandler provides means of subscribing to inspection events.
8 type EventHandler interface {
9 CargoWasMisdirected(*cargo.Cargo)
10 CargoHasArrived(*cargo.Cargo)
11 }
12
13 // Service provides cargo inspection operations.
14 type Service interface {
15 // InspectCargo inspects cargo and send relevant notifications to
16 // interested parties, for example if a cargo has been misdirected, or
17 // unloaded at the final destination.
18 InspectCargo(id cargo.TrackingID)
19 }
20
21 type service struct {
22 cargos cargo.Repository
23 events cargo.HandlingEventRepository
24 handler EventHandler
25 }
26
27 // TODO: Should be transactional
28 func (s *service) InspectCargo(id cargo.TrackingID) {
29 c, err := s.cargos.Find(id)
30 if err != nil {
31 return
32 }
33
34 h := s.events.QueryHandlingHistory(id)
35
36 c.DeriveDeliveryProgress(h)
37
38 if c.Delivery.IsMisdirected {
39 s.handler.CargoWasMisdirected(c)
40 }
41
42 if c.Delivery.IsUnloadedAtDestination {
43 s.handler.CargoHasArrived(c)
44 }
45
46 s.cargos.Store(c)
47 }
48
49 // NewService creates a inspection service with necessary dependencies.
50 func NewService(cargos cargo.Repository, events cargo.HandlingEventRepository, handler EventHandler) Service {
51 return &service{cargos, events, handler}
52 }
+0
-29
examples/shipping/location/location.go less more
0 // Package location provides the Location aggregate.
1 package location
2
3 import (
4 "errors"
5 )
6
7 // UNLocode is the United Nations location code that uniquely identifies a
8 // particular location.
9 //
10 // http://www.unece.org/cefact/locode/
11 // http://www.unece.org/cefact/locode/DocColumnDescription.htm#LOCODE
12 type UNLocode string
13
14 // Location is a location is our model is stops on a journey, such as cargo
15 // origin or destination, or carrier movement endpoints.
16 type Location struct {
17 UNLocode UNLocode
18 Name string
19 }
20
21 // ErrUnknown is used when a location could not be found.
22 var ErrUnknown = errors.New("unknown location")
23
24 // Repository provides access a location store.
25 type Repository interface {
26 Find(locode UNLocode) (*Location, error)
27 FindAll() []*Location
28 }
+0
-27
examples/shipping/location/sample_locations.go less more
0 package location
1
2 // Sample UN locodes.
3 var (
4 SESTO UNLocode = "SESTO"
5 AUMEL UNLocode = "AUMEL"
6 CNHKG UNLocode = "CNHKG"
7 USNYC UNLocode = "USNYC"
8 USCHI UNLocode = "USCHI"
9 JNTKO UNLocode = "JNTKO"
10 DEHAM UNLocode = "DEHAM"
11 NLRTM UNLocode = "NLRTM"
12 FIHEL UNLocode = "FIHEL"
13 )
14
15 // Sample locations.
16 var (
17 Stockholm = &Location{SESTO, "Stockholm"}
18 Melbourne = &Location{AUMEL, "Melbourne"}
19 Hongkong = &Location{CNHKG, "Hongkong"}
20 NewYork = &Location{USNYC, "New York"}
21 Chicago = &Location{USCHI, "Chicago"}
22 Tokyo = &Location{JNTKO, "Tokyo"}
23 Hamburg = &Location{DEHAM, "Hamburg"}
24 Rotterdam = &Location{NLRTM, "Rotterdam"}
25 Helsinki = &Location{FIHEL, "Helsinki"}
26 )
+0
-200
examples/shipping/main.go less more
0 package main
1
2 import (
3 "context"
4 "flag"
5 "fmt"
6 "net/http"
7 "os"
8 "os/signal"
9 "syscall"
10 "time"
11
12 stdprometheus "github.com/prometheus/client_golang/prometheus"
13 "github.com/prometheus/client_golang/prometheus/promhttp"
14
15 "github.com/go-kit/kit/log"
16 kitprometheus "github.com/go-kit/kit/metrics/prometheus"
17
18 "github.com/go-kit/kit/examples/shipping/booking"
19 "github.com/go-kit/kit/examples/shipping/cargo"
20 "github.com/go-kit/kit/examples/shipping/handling"
21 "github.com/go-kit/kit/examples/shipping/inmem"
22 "github.com/go-kit/kit/examples/shipping/inspection"
23 "github.com/go-kit/kit/examples/shipping/location"
24 "github.com/go-kit/kit/examples/shipping/routing"
25 "github.com/go-kit/kit/examples/shipping/tracking"
26 )
27
28 const (
29 defaultPort = "8080"
30 defaultRoutingServiceURL = "http://localhost:7878"
31 )
32
33 func main() {
34 var (
35 addr = envString("PORT", defaultPort)
36 rsurl = envString("ROUTINGSERVICE_URL", defaultRoutingServiceURL)
37
38 httpAddr = flag.String("http.addr", ":"+addr, "HTTP listen address")
39 routingServiceURL = flag.String("service.routing", rsurl, "routing service URL")
40
41 ctx = context.Background()
42 )
43
44 flag.Parse()
45
46 var logger log.Logger
47 logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
48 logger = log.With(logger, "ts", log.DefaultTimestampUTC)
49
50 var (
51 cargos = inmem.NewCargoRepository()
52 locations = inmem.NewLocationRepository()
53 voyages = inmem.NewVoyageRepository()
54 handlingEvents = inmem.NewHandlingEventRepository()
55 )
56
57 // Configure some questionable dependencies.
58 var (
59 handlingEventFactory = cargo.HandlingEventFactory{
60 CargoRepository: cargos,
61 VoyageRepository: voyages,
62 LocationRepository: locations,
63 }
64 handlingEventHandler = handling.NewEventHandler(
65 inspection.NewService(cargos, handlingEvents, nil),
66 )
67 )
68
69 // Facilitate testing by adding some cargos.
70 storeTestData(cargos)
71
72 fieldKeys := []string{"method"}
73
74 var rs routing.Service
75 rs = routing.NewProxyingMiddleware(ctx, *routingServiceURL)(rs)
76
77 var bs booking.Service
78 bs = booking.NewService(cargos, locations, handlingEvents, rs)
79 bs = booking.NewLoggingService(log.With(logger, "component", "booking"), bs)
80 bs = booking.NewInstrumentingService(
81 kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
82 Namespace: "api",
83 Subsystem: "booking_service",
84 Name: "request_count",
85 Help: "Number of requests received.",
86 }, fieldKeys),
87 kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
88 Namespace: "api",
89 Subsystem: "booking_service",
90 Name: "request_latency_microseconds",
91 Help: "Total duration of requests in microseconds.",
92 }, fieldKeys),
93 bs,
94 )
95
96 var ts tracking.Service
97 ts = tracking.NewService(cargos, handlingEvents)
98 ts = tracking.NewLoggingService(log.With(logger, "component", "tracking"), ts)
99 ts = tracking.NewInstrumentingService(
100 kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
101 Namespace: "api",
102 Subsystem: "tracking_service",
103 Name: "request_count",
104 Help: "Number of requests received.",
105 }, fieldKeys),
106 kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
107 Namespace: "api",
108 Subsystem: "tracking_service",
109 Name: "request_latency_microseconds",
110 Help: "Total duration of requests in microseconds.",
111 }, fieldKeys),
112 ts,
113 )
114
115 var hs handling.Service
116 hs = handling.NewService(handlingEvents, handlingEventFactory, handlingEventHandler)
117 hs = handling.NewLoggingService(log.With(logger, "component", "handling"), hs)
118 hs = handling.NewInstrumentingService(
119 kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
120 Namespace: "api",
121 Subsystem: "handling_service",
122 Name: "request_count",
123 Help: "Number of requests received.",
124 }, fieldKeys),
125 kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
126 Namespace: "api",
127 Subsystem: "handling_service",
128 Name: "request_latency_microseconds",
129 Help: "Total duration of requests in microseconds.",
130 }, fieldKeys),
131 hs,
132 )
133
134 httpLogger := log.With(logger, "component", "http")
135
136 mux := http.NewServeMux()
137
138 mux.Handle("/booking/v1/", booking.MakeHandler(bs, httpLogger))
139 mux.Handle("/tracking/v1/", tracking.MakeHandler(ts, httpLogger))
140 mux.Handle("/handling/v1/", handling.MakeHandler(hs, httpLogger))
141
142 http.Handle("/", accessControl(mux))
143 http.Handle("/metrics", promhttp.Handler())
144
145 errs := make(chan error, 2)
146 go func() {
147 logger.Log("transport", "http", "address", *httpAddr, "msg", "listening")
148 errs <- http.ListenAndServe(*httpAddr, nil)
149 }()
150 go func() {
151 c := make(chan os.Signal)
152 signal.Notify(c, syscall.SIGINT)
153 errs <- fmt.Errorf("%s", <-c)
154 }()
155
156 logger.Log("terminated", <-errs)
157 }
158
159 func accessControl(h http.Handler) http.Handler {
160 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
161 w.Header().Set("Access-Control-Allow-Origin", "*")
162 w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
163 w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type")
164
165 if r.Method == "OPTIONS" {
166 return
167 }
168
169 h.ServeHTTP(w, r)
170 })
171 }
172
173 func envString(env, fallback string) string {
174 e := os.Getenv(env)
175 if e == "" {
176 return fallback
177 }
178 return e
179 }
180
181 func storeTestData(r cargo.Repository) {
182 test1 := cargo.New("FTL456", cargo.RouteSpecification{
183 Origin: location.AUMEL,
184 Destination: location.SESTO,
185 ArrivalDeadline: time.Now().AddDate(0, 0, 7),
186 })
187 if err := r.Store(test1); err != nil {
188 panic(err)
189 }
190
191 test2 := cargo.New("ABC123", cargo.RouteSpecification{
192 Origin: location.SESTO,
193 Destination: location.CNHKG,
194 ArrivalDeadline: time.Now().AddDate(0, 0, 14),
195 })
196 if err := r.Store(test2); err != nil {
197 panic(err)
198 }
199 }
+0
-117
examples/shipping/routing/proxying.go less more
0 package routing
1
2 import (
3 "context"
4 "encoding/json"
5 "net/http"
6 "net/url"
7 "time"
8
9 "github.com/go-kit/kit/circuitbreaker"
10 "github.com/go-kit/kit/endpoint"
11 kithttp "github.com/go-kit/kit/transport/http"
12
13 "github.com/go-kit/kit/examples/shipping/cargo"
14 "github.com/go-kit/kit/examples/shipping/location"
15 "github.com/go-kit/kit/examples/shipping/voyage"
16 )
17
18 type proxyService struct {
19 context.Context
20 FetchRoutesEndpoint endpoint.Endpoint
21 Service
22 }
23
24 func (s proxyService) FetchRoutesForSpecification(rs cargo.RouteSpecification) []cargo.Itinerary {
25 response, err := s.FetchRoutesEndpoint(s.Context, fetchRoutesRequest{
26 From: string(rs.Origin),
27 To: string(rs.Destination),
28 })
29 if err != nil {
30 return []cargo.Itinerary{}
31 }
32
33 resp := response.(fetchRoutesResponse)
34
35 var itineraries []cargo.Itinerary
36 for _, r := range resp.Paths {
37 var legs []cargo.Leg
38 for _, e := range r.Edges {
39 legs = append(legs, cargo.Leg{
40 VoyageNumber: voyage.Number(e.Voyage),
41 LoadLocation: location.UNLocode(e.Origin),
42 UnloadLocation: location.UNLocode(e.Destination),
43 LoadTime: e.Departure,
44 UnloadTime: e.Arrival,
45 })
46 }
47
48 itineraries = append(itineraries, cargo.Itinerary{Legs: legs})
49 }
50
51 return itineraries
52 }
53
54 // ServiceMiddleware defines a middleware for a routing service.
55 type ServiceMiddleware func(Service) Service
56
57 // NewProxyingMiddleware returns a new instance of a proxying middleware.
58 func NewProxyingMiddleware(ctx context.Context, proxyURL string) ServiceMiddleware {
59 return func(next Service) Service {
60 var e endpoint.Endpoint
61 e = makeFetchRoutesEndpoint(ctx, proxyURL)
62 e = circuitbreaker.Hystrix("fetch-routes")(e)
63 return proxyService{ctx, e, next}
64 }
65 }
66
67 type fetchRoutesRequest struct {
68 From string
69 To string
70 }
71
72 type fetchRoutesResponse struct {
73 Paths []struct {
74 Edges []struct {
75 Origin string `json:"origin"`
76 Destination string `json:"destination"`
77 Voyage string `json:"voyage"`
78 Departure time.Time `json:"departure"`
79 Arrival time.Time `json:"arrival"`
80 } `json:"edges"`
81 } `json:"paths"`
82 }
83
84 func makeFetchRoutesEndpoint(ctx context.Context, instance string) endpoint.Endpoint {
85 u, err := url.Parse(instance)
86 if err != nil {
87 panic(err)
88 }
89 if u.Path == "" {
90 u.Path = "/paths"
91 }
92 return kithttp.NewClient(
93 "GET", u,
94 encodeFetchRoutesRequest,
95 decodeFetchRoutesResponse,
96 ).Endpoint()
97 }
98
99 func decodeFetchRoutesResponse(_ context.Context, resp *http.Response) (interface{}, error) {
100 var response fetchRoutesResponse
101 if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
102 return nil, err
103 }
104 return response, nil
105 }
106
107 func encodeFetchRoutesRequest(_ context.Context, r *http.Request, request interface{}) error {
108 req := request.(fetchRoutesRequest)
109
110 vals := r.URL.Query()
111 vals.Add("from", req.From)
112 vals.Add("to", req.To)
113 r.URL.RawQuery = vals.Encode()
114
115 return nil
116 }
+0
-15
examples/shipping/routing/routing.go less more
0 // Package routing provides the routing domain service. It does not actually
1 // implement the routing service but merely acts as a proxy for a separate
2 // bounded context.
3 package routing
4
5 import (
6 "github.com/go-kit/kit/examples/shipping/cargo"
7 )
8
9 // Service provides access to an external routing service.
10 type Service interface {
11 // FetchRoutesForSpecification finds all possible routes that satisfy a
12 // given specification.
13 FetchRoutesForSpecification(rs cargo.RouteSpecification) []cargo.Itinerary
14 }
+0
-26
examples/shipping/tracking/endpoint.go less more
0 package tracking
1
2 import (
3 "context"
4
5 "github.com/go-kit/kit/endpoint"
6 )
7
8 type trackCargoRequest struct {
9 ID string
10 }
11
12 type trackCargoResponse struct {
13 Cargo *Cargo `json:"cargo,omitempty"`
14 Err error `json:"error,omitempty"`
15 }
16
17 func (r trackCargoResponse) error() error { return r.Err }
18
19 func makeTrackCargoEndpoint(ts Service) endpoint.Endpoint {
20 return func(ctx context.Context, request interface{}) (interface{}, error) {
21 req := request.(trackCargoRequest)
22 c, err := ts.Track(req.ID)
23 return trackCargoResponse{Cargo: &c, Err: err}, nil
24 }
25 }
+0
-31
examples/shipping/tracking/instrumenting.go less more
0 package tracking
1
2 import (
3 "time"
4
5 "github.com/go-kit/kit/metrics"
6 )
7
8 type instrumentingService struct {
9 requestCount metrics.Counter
10 requestLatency metrics.Histogram
11 Service
12 }
13
14 // NewInstrumentingService returns an instance of an instrumenting Service.
15 func NewInstrumentingService(counter metrics.Counter, latency metrics.Histogram, s Service) Service {
16 return &instrumentingService{
17 requestCount: counter,
18 requestLatency: latency,
19 Service: s,
20 }
21 }
22
23 func (s *instrumentingService) Track(id string) (Cargo, error) {
24 defer func(begin time.Time) {
25 s.requestCount.With("method", "track").Add(1)
26 s.requestLatency.With("method", "track").Observe(time.Since(begin).Seconds())
27 }(time.Now())
28
29 return s.Service.Track(id)
30 }
+0
-24
examples/shipping/tracking/logging.go less more
0 package tracking
1
2 import (
3 "time"
4
5 "github.com/go-kit/kit/log"
6 )
7
8 type loggingService struct {
9 logger log.Logger
10 Service
11 }
12
13 // NewLoggingService returns a new instance of a logging Service.
14 func NewLoggingService(logger log.Logger, s Service) Service {
15 return &loggingService{logger, s}
16 }
17
18 func (s *loggingService) Track(id string) (c Cargo, err error) {
19 defer func(begin time.Time) {
20 s.logger.Log("method", "track", "tracking_id", id, "took", time.Since(begin), "err", err)
21 }(time.Now())
22 return s.Service.Track(id)
23 }
+0
-163
examples/shipping/tracking/service.go less more
0 // Package tracking provides the use-case of tracking a cargo. Used by views
1 // facing the end-user.
2 package tracking
3
4 import (
5 "errors"
6 "fmt"
7 "strings"
8 "time"
9
10 "github.com/go-kit/kit/examples/shipping/cargo"
11 )
12
13 // ErrInvalidArgument is returned when one or more arguments are invalid.
14 var ErrInvalidArgument = errors.New("invalid argument")
15
16 // Service is the interface that provides the basic Track method.
17 type Service interface {
18 // Track returns a cargo matching a tracking ID.
19 Track(id string) (Cargo, error)
20 }
21
22 type service struct {
23 cargos cargo.Repository
24 handlingEvents cargo.HandlingEventRepository
25 }
26
27 func (s *service) Track(id string) (Cargo, error) {
28 if id == "" {
29 return Cargo{}, ErrInvalidArgument
30 }
31 c, err := s.cargos.Find(cargo.TrackingID(id))
32 if err != nil {
33 return Cargo{}, err
34 }
35 return assemble(c, s.handlingEvents), nil
36 }
37
38 // NewService returns a new instance of the default Service.
39 func NewService(cargos cargo.Repository, events cargo.HandlingEventRepository) Service {
40 return &service{
41 cargos: cargos,
42 handlingEvents: events,
43 }
44 }
45
46 // Cargo is a read model for tracking views.
47 type Cargo struct {
48 TrackingID string `json:"tracking_id"`
49 StatusText string `json:"status_text"`
50 Origin string `json:"origin"`
51 Destination string `json:"destination"`
52 ETA time.Time `json:"eta"`
53 NextExpectedActivity string `json:"next_expected_activity"`
54 ArrivalDeadline time.Time `json:"arrival_deadline"`
55 Events []Event `json:"events"`
56 }
57
58 // Leg is a read model for booking views.
59 type Leg struct {
60 VoyageNumber string `json:"voyage_number"`
61 From string `json:"from"`
62 To string `json:"to"`
63 LoadTime time.Time `json:"load_time"`
64 UnloadTime time.Time `json:"unload_time"`
65 }
66
67 // Event is a read model for tracking views.
68 type Event struct {
69 Description string `json:"description"`
70 Expected bool `json:"expected"`
71 }
72
73 func assemble(c *cargo.Cargo, events cargo.HandlingEventRepository) Cargo {
74 return Cargo{
75 TrackingID: string(c.TrackingID),
76 Origin: string(c.Origin),
77 Destination: string(c.RouteSpecification.Destination),
78 ETA: c.Delivery.ETA,
79 NextExpectedActivity: nextExpectedActivity(c),
80 ArrivalDeadline: c.RouteSpecification.ArrivalDeadline,
81 StatusText: assembleStatusText(c),
82 Events: assembleEvents(c, events),
83 }
84 }
85
86 func assembleLegs(c cargo.Cargo) []Leg {
87 var legs []Leg
88 for _, l := range c.Itinerary.Legs {
89 legs = append(legs, Leg{
90 VoyageNumber: string(l.VoyageNumber),
91 From: string(l.LoadLocation),
92 To: string(l.UnloadLocation),
93 LoadTime: l.LoadTime,
94 UnloadTime: l.UnloadTime,
95 })
96 }
97 return legs
98 }
99
100 func nextExpectedActivity(c *cargo.Cargo) string {
101 a := c.Delivery.NextExpectedActivity
102 prefix := "Next expected activity is to"
103
104 switch a.Type {
105 case cargo.Load:
106 return fmt.Sprintf("%s %s cargo onto voyage %s in %s.", prefix, strings.ToLower(a.Type.String()), a.VoyageNumber, a.Location)
107 case cargo.Unload:
108 return fmt.Sprintf("%s %s cargo off of voyage %s in %s.", prefix, strings.ToLower(a.Type.String()), a.VoyageNumber, a.Location)
109 case cargo.NotHandled:
110 return "There are currently no expected activities for this cargo."
111 }
112
113 return fmt.Sprintf("%s %s cargo in %s.", prefix, strings.ToLower(a.Type.String()), a.Location)
114 }
115
116 func assembleStatusText(c *cargo.Cargo) string {
117 switch c.Delivery.TransportStatus {
118 case cargo.NotReceived:
119 return "Not received"
120 case cargo.InPort:
121 return fmt.Sprintf("In port %s", c.Delivery.LastKnownLocation)
122 case cargo.OnboardCarrier:
123 return fmt.Sprintf("Onboard voyage %s", c.Delivery.CurrentVoyage)
124 case cargo.Claimed:
125 return "Claimed"
126 default:
127 return "Unknown"
128 }
129 }
130
131 func assembleEvents(c *cargo.Cargo, handlingEvents cargo.HandlingEventRepository) []Event {
132 h := handlingEvents.QueryHandlingHistory(c.TrackingID)
133
134 var events []Event
135 for _, e := range h.HandlingEvents {
136 var description string
137
138 switch e.Activity.Type {
139 case cargo.NotHandled:
140 description = "Cargo has not yet been received."
141 case cargo.Receive:
142 description = fmt.Sprintf("Received in %s, at %s", e.Activity.Location, time.Now().Format(time.RFC3339))
143 case cargo.Load:
144 description = fmt.Sprintf("Loaded onto voyage %s in %s, at %s.", e.Activity.VoyageNumber, e.Activity.Location, time.Now().Format(time.RFC3339))
145 case cargo.Unload:
146 description = fmt.Sprintf("Unloaded off voyage %s in %s, at %s.", e.Activity.VoyageNumber, e.Activity.Location, time.Now().Format(time.RFC3339))
147 case cargo.Claim:
148 description = fmt.Sprintf("Claimed in %s, at %s.", e.Activity.Location, time.Now().Format(time.RFC3339))
149 case cargo.Customs:
150 description = fmt.Sprintf("Cleared customs in %s, at %s.", e.Activity.Location, time.Now().Format(time.RFC3339))
151 default:
152 description = "[Unknown status]"
153 }
154
155 events = append(events, Event{
156 Description: description,
157 Expected: c.Itinerary.IsExpected(e),
158 })
159 }
160
161 return events
162 }
+0
-75
examples/shipping/tracking/transport.go less more
0 package tracking
1
2 import (
3 "context"
4 "encoding/json"
5 "errors"
6 "net/http"
7
8 "github.com/gorilla/mux"
9
10 kitlog "github.com/go-kit/kit/log"
11 kittransport "github.com/go-kit/kit/transport"
12 kithttp "github.com/go-kit/kit/transport/http"
13
14 "github.com/go-kit/kit/examples/shipping/cargo"
15 )
16
17 // MakeHandler returns a handler for the tracking service.
18 func MakeHandler(ts Service, logger kitlog.Logger) http.Handler {
19 r := mux.NewRouter()
20
21 opts := []kithttp.ServerOption{
22 kithttp.ServerErrorHandler(kittransport.NewLogErrorHandler(logger)),
23 kithttp.ServerErrorEncoder(encodeError),
24 }
25
26 trackCargoHandler := kithttp.NewServer(
27 makeTrackCargoEndpoint(ts),
28 decodeTrackCargoRequest,
29 encodeResponse,
30 opts...,
31 )
32
33 r.Handle("/tracking/v1/cargos/{id}", trackCargoHandler).Methods("GET")
34
35 return r
36 }
37
38 func decodeTrackCargoRequest(_ context.Context, r *http.Request) (interface{}, error) {
39 vars := mux.Vars(r)
40 id, ok := vars["id"]
41 if !ok {
42 return nil, errors.New("bad route")
43 }
44 return trackCargoRequest{ID: id}, nil
45 }
46
47 func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
48 if e, ok := response.(errorer); ok && e.error() != nil {
49 encodeError(ctx, e.error(), w)
50 return nil
51 }
52 w.Header().Set("Content-Type", "application/json; charset=utf-8")
53 return json.NewEncoder(w).Encode(response)
54 }
55
56 type errorer interface {
57 error() error
58 }
59
60 // encode errors from business-logic
61 func encodeError(_ context.Context, err error, w http.ResponseWriter) {
62 w.Header().Set("Content-Type", "application/json; charset=utf-8")
63 switch err {
64 case cargo.ErrUnknown:
65 w.WriteHeader(http.StatusNotFound)
66 case ErrInvalidArgument:
67 w.WriteHeader(http.StatusBadRequest)
68 default:
69 w.WriteHeader(http.StatusInternalServerError)
70 }
71 json.NewEncoder(w).Encode(map[string]interface{}{
72 "error": err.Error(),
73 })
74 }
+0
-40
examples/shipping/voyage/sample_voyages.go less more
0 package voyage
1
2 import "github.com/go-kit/kit/examples/shipping/location"
3
4 // A set of sample voyages.
5 var (
6 V100 = New("V100", Schedule{
7 []CarrierMovement{
8 {DepartureLocation: location.CNHKG, ArrivalLocation: location.JNTKO},
9 {DepartureLocation: location.JNTKO, ArrivalLocation: location.USNYC},
10 },
11 })
12
13 V300 = New("V300", Schedule{
14 []CarrierMovement{
15 {DepartureLocation: location.JNTKO, ArrivalLocation: location.NLRTM},
16 {DepartureLocation: location.NLRTM, ArrivalLocation: location.DEHAM},
17 {DepartureLocation: location.DEHAM, ArrivalLocation: location.AUMEL},
18 {DepartureLocation: location.AUMEL, ArrivalLocation: location.JNTKO},
19 },
20 })
21
22 V400 = New("V400", Schedule{
23 []CarrierMovement{
24 {DepartureLocation: location.DEHAM, ArrivalLocation: location.SESTO},
25 {DepartureLocation: location.SESTO, ArrivalLocation: location.FIHEL},
26 {DepartureLocation: location.FIHEL, ArrivalLocation: location.DEHAM},
27 },
28 })
29 )
30
31 // These voyages are hard-coded into the current pathfinder. Make sure
32 // they exist.
33 var (
34 V0100S = New("0100S", Schedule{[]CarrierMovement{}})
35 V0200T = New("0200T", Schedule{[]CarrierMovement{}})
36 V0300A = New("0300A", Schedule{[]CarrierMovement{}})
37 V0301S = New("0301S", Schedule{[]CarrierMovement{}})
38 V0400S = New("0400S", Schedule{[]CarrierMovement{}})
39 )
+0
-44
examples/shipping/voyage/voyage.go less more
0 // Package voyage provides the Voyage aggregate.
1 package voyage
2
3 import (
4 "errors"
5 "time"
6
7 "github.com/go-kit/kit/examples/shipping/location"
8 )
9
10 // Number uniquely identifies a particular Voyage.
11 type Number string
12
13 // Voyage is a uniquely identifiable series of carrier movements.
14 type Voyage struct {
15 Number Number
16 Schedule Schedule
17 }
18
19 // New creates a voyage with a voyage number and a provided schedule.
20 func New(n Number, s Schedule) *Voyage {
21 return &Voyage{Number: n, Schedule: s}
22 }
23
24 // Schedule describes a voyage schedule.
25 type Schedule struct {
26 CarrierMovements []CarrierMovement
27 }
28
29 // CarrierMovement is a vessel voyage from one location to another.
30 type CarrierMovement struct {
31 DepartureLocation location.UNLocode
32 ArrivalLocation location.UNLocode
33 DepartureTime time.Time
34 ArrivalTime time.Time
35 }
36
37 // ErrUnknown is used when a voyage could not be found.
38 var ErrUnknown = errors.New("unknown voyage")
39
40 // Repository provides access a voyage store.
41 type Repository interface {
42 Find(Number) (*Voyage, error)
43 }
+0
-115
examples/stringsvc1/main.go less more
0 package main
1
2 import (
3 "context"
4 "encoding/json"
5 "errors"
6 "log"
7 "net/http"
8 "strings"
9
10 "github.com/go-kit/kit/endpoint"
11 httptransport "github.com/go-kit/kit/transport/http"
12 )
13
14 // StringService provides operations on strings.
15 type StringService interface {
16 Uppercase(string) (string, error)
17 Count(string) int
18 }
19
20 // stringService is a concrete implementation of StringService
21 type stringService struct{}
22
23 func (stringService) Uppercase(s string) (string, error) {
24 if s == "" {
25 return "", ErrEmpty
26 }
27 return strings.ToUpper(s), nil
28 }
29
30 func (stringService) Count(s string) int {
31 return len(s)
32 }
33
34 // ErrEmpty is returned when an input string is empty.
35 var ErrEmpty = errors.New("empty string")
36
37 // For each method, we define request and response structs
38 type uppercaseRequest struct {
39 S string `json:"s"`
40 }
41
42 type uppercaseResponse struct {
43 V string `json:"v"`
44 Err string `json:"err,omitempty"` // errors don't define JSON marshaling
45 }
46
47 type countRequest struct {
48 S string `json:"s"`
49 }
50
51 type countResponse struct {
52 V int `json:"v"`
53 }
54
55 // Endpoints are a primary abstraction in go-kit. An endpoint represents a single RPC (method in our service interface)
56 func makeUppercaseEndpoint(svc StringService) endpoint.Endpoint {
57 return func(_ context.Context, request interface{}) (interface{}, error) {
58 req := request.(uppercaseRequest)
59 v, err := svc.Uppercase(req.S)
60 if err != nil {
61 return uppercaseResponse{v, err.Error()}, nil
62 }
63 return uppercaseResponse{v, ""}, nil
64 }
65 }
66
67 func makeCountEndpoint(svc StringService) endpoint.Endpoint {
68 return func(_ context.Context, request interface{}) (interface{}, error) {
69 req := request.(countRequest)
70 v := svc.Count(req.S)
71 return countResponse{v}, nil
72 }
73 }
74
75 // Transports expose the service to the network. In this first example we utilize JSON over HTTP.
76 func main() {
77 svc := stringService{}
78
79 uppercaseHandler := httptransport.NewServer(
80 makeUppercaseEndpoint(svc),
81 decodeUppercaseRequest,
82 encodeResponse,
83 )
84
85 countHandler := httptransport.NewServer(
86 makeCountEndpoint(svc),
87 decodeCountRequest,
88 encodeResponse,
89 )
90
91 http.Handle("/uppercase", uppercaseHandler)
92 http.Handle("/count", countHandler)
93 log.Fatal(http.ListenAndServe(":8080", nil))
94 }
95
96 func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) {
97 var request uppercaseRequest
98 if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
99 return nil, err
100 }
101 return request, nil
102 }
103
104 func decodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) {
105 var request countRequest
106 if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
107 return nil, err
108 }
109 return request, nil
110 }
111
112 func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
113 return json.NewEncoder(w).Encode(response)
114 }
+0
-38
examples/stringsvc2/instrumenting.go less more
0 package main
1
2 import (
3 "fmt"
4 "time"
5
6 "github.com/go-kit/kit/metrics"
7 )
8
9 type instrumentingMiddleware struct {
10 requestCount metrics.Counter
11 requestLatency metrics.Histogram
12 countResult metrics.Histogram
13 next StringService
14 }
15
16 func (mw instrumentingMiddleware) Uppercase(s string) (output string, err error) {
17 defer func(begin time.Time) {
18 lvs := []string{"method", "uppercase", "error", fmt.Sprint(err != nil)}
19 mw.requestCount.With(lvs...).Add(1)
20 mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds())
21 }(time.Now())
22
23 output, err = mw.next.Uppercase(s)
24 return
25 }
26
27 func (mw instrumentingMiddleware) Count(s string) (n int) {
28 defer func(begin time.Time) {
29 lvs := []string{"method", "count", "error", "false"}
30 mw.requestCount.With(lvs...).Add(1)
31 mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds())
32 mw.countResult.Observe(float64(n))
33 }(time.Now())
34
35 n = mw.next.Count(s)
36 return
37 }
+0
-41
examples/stringsvc2/logging.go less more
0 package main
1
2 import (
3 "time"
4
5 "github.com/go-kit/kit/log"
6 )
7
8 type loggingMiddleware struct {
9 logger log.Logger
10 next StringService
11 }
12
13 func (mw loggingMiddleware) Uppercase(s string) (output string, err error) {
14 defer func(begin time.Time) {
15 _ = mw.logger.Log(
16 "method", "uppercase",
17 "input", s,
18 "output", output,
19 "err", err,
20 "took", time.Since(begin),
21 )
22 }(time.Now())
23
24 output, err = mw.next.Uppercase(s)
25 return
26 }
27
28 func (mw loggingMiddleware) Count(s string) (n int) {
29 defer func(begin time.Time) {
30 _ = mw.logger.Log(
31 "method", "count",
32 "input", s,
33 "n", n,
34 "took", time.Since(begin),
35 )
36 }(time.Now())
37
38 n = mw.next.Count(s)
39 return
40 }
+0
-60
examples/stringsvc2/main.go less more
0 package main
1
2 import (
3 "net/http"
4 "os"
5
6 stdprometheus "github.com/prometheus/client_golang/prometheus"
7 "github.com/prometheus/client_golang/prometheus/promhttp"
8
9 "github.com/go-kit/kit/log"
10 kitprometheus "github.com/go-kit/kit/metrics/prometheus"
11 httptransport "github.com/go-kit/kit/transport/http"
12 )
13
14 func main() {
15 logger := log.NewLogfmtLogger(os.Stderr)
16
17 fieldKeys := []string{"method", "error"}
18 requestCount := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
19 Namespace: "my_group",
20 Subsystem: "string_service",
21 Name: "request_count",
22 Help: "Number of requests received.",
23 }, fieldKeys)
24 requestLatency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
25 Namespace: "my_group",
26 Subsystem: "string_service",
27 Name: "request_latency_microseconds",
28 Help: "Total duration of requests in microseconds.",
29 }, fieldKeys)
30 countResult := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
31 Namespace: "my_group",
32 Subsystem: "string_service",
33 Name: "count_result",
34 Help: "The result of each count method.",
35 }, []string{}) // no fields here
36
37 var svc StringService
38 svc = stringService{}
39 svc = loggingMiddleware{logger, svc}
40 svc = instrumentingMiddleware{requestCount, requestLatency, countResult, svc}
41
42 uppercaseHandler := httptransport.NewServer(
43 makeUppercaseEndpoint(svc),
44 decodeUppercaseRequest,
45 encodeResponse,
46 )
47
48 countHandler := httptransport.NewServer(
49 makeCountEndpoint(svc),
50 decodeCountRequest,
51 encodeResponse,
52 )
53
54 http.Handle("/uppercase", uppercaseHandler)
55 http.Handle("/count", countHandler)
56 http.Handle("/metrics", promhttp.Handler())
57 logger.Log("msg", "HTTP", "addr", ":8080")
58 logger.Log("err", http.ListenAndServe(":8080", nil))
59 }
+0
-28
examples/stringsvc2/service.go less more
0 package main
1
2 import (
3 "errors"
4 "strings"
5 )
6
7 // StringService provides operations on strings.
8 type StringService interface {
9 Uppercase(string) (string, error)
10 Count(string) int
11 }
12
13 type stringService struct{}
14
15 func (stringService) Uppercase(s string) (string, error) {
16 if s == "" {
17 return "", ErrEmpty
18 }
19 return strings.ToUpper(s), nil
20 }
21
22 func (stringService) Count(s string) int {
23 return len(s)
24 }
25
26 // ErrEmpty is returned when an input string is empty.
27 var ErrEmpty = errors.New("empty string")
+0
-65
examples/stringsvc2/transport.go less more
0 package main
1
2 import (
3 "context"
4 "encoding/json"
5 "net/http"
6
7 "github.com/go-kit/kit/endpoint"
8 )
9
10 func makeUppercaseEndpoint(svc StringService) endpoint.Endpoint {
11 return func(_ context.Context, request interface{}) (interface{}, error) {
12 req := request.(uppercaseRequest)
13 v, err := svc.Uppercase(req.S)
14 if err != nil {
15 return uppercaseResponse{v, err.Error()}, nil
16 }
17 return uppercaseResponse{v, ""}, nil
18 }
19 }
20
21 func makeCountEndpoint(svc StringService) endpoint.Endpoint {
22 return func(_ context.Context, request interface{}) (interface{}, error) {
23 req := request.(countRequest)
24 v := svc.Count(req.S)
25 return countResponse{v}, nil
26 }
27 }
28
29 func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) {
30 var request uppercaseRequest
31 if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
32 return nil, err
33 }
34 return request, nil
35 }
36
37 func decodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) {
38 var request countRequest
39 if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
40 return nil, err
41 }
42 return request, nil
43 }
44
45 func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
46 return json.NewEncoder(w).Encode(response)
47 }
48
49 type uppercaseRequest struct {
50 S string `json:"s"`
51 }
52
53 type uppercaseResponse struct {
54 V string `json:"v"`
55 Err string `json:"err,omitempty"`
56 }
57
58 type countRequest struct {
59 S string `json:"s"`
60 }
61
62 type countResponse struct {
63 V int `json:"v"`
64 }
+0
-48
examples/stringsvc3/instrumenting.go less more
0 package main
1
2 import (
3 "fmt"
4 "time"
5
6 "github.com/go-kit/kit/metrics"
7 )
8
9 func instrumentingMiddleware(
10 requestCount metrics.Counter,
11 requestLatency metrics.Histogram,
12 countResult metrics.Histogram,
13 ) ServiceMiddleware {
14 return func(next StringService) StringService {
15 return instrmw{requestCount, requestLatency, countResult, next}
16 }
17 }
18
19 type instrmw struct {
20 requestCount metrics.Counter
21 requestLatency metrics.Histogram
22 countResult metrics.Histogram
23 StringService
24 }
25
26 func (mw instrmw) Uppercase(s string) (output string, err error) {
27 defer func(begin time.Time) {
28 lvs := []string{"method", "uppercase", "error", fmt.Sprint(err != nil)}
29 mw.requestCount.With(lvs...).Add(1)
30 mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds())
31 }(time.Now())
32
33 output, err = mw.StringService.Uppercase(s)
34 return
35 }
36
37 func (mw instrmw) Count(s string) (n int) {
38 defer func(begin time.Time) {
39 lvs := []string{"method", "count", "error", "false"}
40 mw.requestCount.With(lvs...).Add(1)
41 mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds())
42 mw.countResult.Observe(float64(n))
43 }(time.Now())
44
45 n = mw.StringService.Count(s)
46 return
47 }
+0
-47
examples/stringsvc3/logging.go less more
0 package main
1
2 import (
3 "time"
4
5 "github.com/go-kit/kit/log"
6 )
7
8 func loggingMiddleware(logger log.Logger) ServiceMiddleware {
9 return func(next StringService) StringService {
10 return logmw{logger, next}
11 }
12 }
13
14 type logmw struct {
15 logger log.Logger
16 StringService
17 }
18
19 func (mw logmw) Uppercase(s string) (output string, err error) {
20 defer func(begin time.Time) {
21 _ = mw.logger.Log(
22 "method", "uppercase",
23 "input", s,
24 "output", output,
25 "err", err,
26 "took", time.Since(begin),
27 )
28 }(time.Now())
29
30 output, err = mw.StringService.Uppercase(s)
31 return
32 }
33
34 func (mw logmw) Count(s string) (n int) {
35 defer func(begin time.Time) {
36 _ = mw.logger.Log(
37 "method", "count",
38 "input", s,
39 "n", n,
40 "took", time.Since(begin),
41 )
42 }(time.Now())
43
44 n = mw.StringService.Count(s)
45 return
46 }
+0
-70
examples/stringsvc3/main.go less more
0 package main
1
2 import (
3 "context"
4 "flag"
5 "net/http"
6 "os"
7
8 stdprometheus "github.com/prometheus/client_golang/prometheus"
9 "github.com/prometheus/client_golang/prometheus/promhttp"
10
11 "github.com/go-kit/kit/log"
12 kitprometheus "github.com/go-kit/kit/metrics/prometheus"
13 httptransport "github.com/go-kit/kit/transport/http"
14 )
15
16 func main() {
17 var (
18 listen = flag.String("listen", ":8080", "HTTP listen address")
19 proxy = flag.String("proxy", "", "Optional comma-separated list of URLs to proxy uppercase requests")
20 )
21 flag.Parse()
22
23 var logger log.Logger
24 logger = log.NewLogfmtLogger(os.Stderr)
25 logger = log.With(logger, "listen", *listen, "caller", log.DefaultCaller)
26
27 fieldKeys := []string{"method", "error"}
28 requestCount := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
29 Namespace: "my_group",
30 Subsystem: "string_service",
31 Name: "request_count",
32 Help: "Number of requests received.",
33 }, fieldKeys)
34 requestLatency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
35 Namespace: "my_group",
36 Subsystem: "string_service",
37 Name: "request_latency_microseconds",
38 Help: "Total duration of requests in microseconds.",
39 }, fieldKeys)
40 countResult := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
41 Namespace: "my_group",
42 Subsystem: "string_service",
43 Name: "count_result",
44 Help: "The result of each count method.",
45 }, []string{})
46
47 var svc StringService
48 svc = stringService{}
49 svc = proxyingMiddleware(context.Background(), *proxy, logger)(svc)
50 svc = loggingMiddleware(logger)(svc)
51 svc = instrumentingMiddleware(requestCount, requestLatency, countResult)(svc)
52
53 uppercaseHandler := httptransport.NewServer(
54 makeUppercaseEndpoint(svc),
55 decodeUppercaseRequest,
56 encodeResponse,
57 )
58 countHandler := httptransport.NewServer(
59 makeCountEndpoint(svc),
60 decodeCountRequest,
61 encodeResponse,
62 )
63
64 http.Handle("/uppercase", uppercaseHandler)
65 http.Handle("/count", countHandler)
66 http.Handle("/metrics", promhttp.Handler())
67 logger.Log("msg", "HTTP", "addr", *listen)
68 logger.Log("err", http.ListenAndServe(*listen, nil))
69 }
+0
-117
examples/stringsvc3/proxying.go less more
0 package main
1
2 import (
3 "context"
4 "errors"
5 "fmt"
6 "net/url"
7 "strings"
8 "time"
9
10 "golang.org/x/time/rate"
11
12 "github.com/sony/gobreaker"
13
14 "github.com/go-kit/kit/circuitbreaker"
15 "github.com/go-kit/kit/endpoint"
16 "github.com/go-kit/kit/log"
17 "github.com/go-kit/kit/ratelimit"
18 "github.com/go-kit/kit/sd"
19 "github.com/go-kit/kit/sd/lb"
20 httptransport "github.com/go-kit/kit/transport/http"
21 )
22
23 func proxyingMiddleware(ctx context.Context, instances string, logger log.Logger) ServiceMiddleware {
24 // If instances is empty, don't proxy.
25 if instances == "" {
26 logger.Log("proxy_to", "none")
27 return func(next StringService) StringService { return next }
28 }
29
30 // Set some parameters for our client.
31 var (
32 qps = 100 // beyond which we will return an error
33 maxAttempts = 3 // per request, before giving up
34 maxTime = 250 * time.Millisecond // wallclock time, before giving up
35 )
36
37 // Otherwise, construct an endpoint for each instance in the list, and add
38 // it to a fixed set of endpoints. In a real service, rather than doing this
39 // by hand, you'd probably use package sd's support for your service
40 // discovery system.
41 var (
42 instanceList = split(instances)
43 endpointer sd.FixedEndpointer
44 )
45 logger.Log("proxy_to", fmt.Sprint(instanceList))
46 for _, instance := range instanceList {
47 var e endpoint.Endpoint
48 e = makeUppercaseProxy(ctx, instance)
49 e = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(e)
50 e = ratelimit.NewErroringLimiter(rate.NewLimiter(rate.Every(time.Second), qps))(e)
51 endpointer = append(endpointer, e)
52 }
53
54 // Now, build a single, retrying, load-balancing endpoint out of all of
55 // those individual endpoints.
56 balancer := lb.NewRoundRobin(endpointer)
57 retry := lb.Retry(maxAttempts, maxTime, balancer)
58
59 // And finally, return the ServiceMiddleware, implemented by proxymw.
60 return func(next StringService) StringService {
61 return proxymw{ctx, next, retry}
62 }
63 }
64
65 // proxymw implements StringService, forwarding Uppercase requests to the
66 // provided endpoint, and serving all other (i.e. Count) requests via the
67 // next StringService.
68 type proxymw struct {
69 ctx context.Context
70 next StringService // Serve most requests via this service...
71 uppercase endpoint.Endpoint // ...except Uppercase, which gets served by this endpoint
72 }
73
74 func (mw proxymw) Count(s string) int {
75 return mw.next.Count(s)
76 }
77
78 func (mw proxymw) Uppercase(s string) (string, error) {
79 response, err := mw.uppercase(mw.ctx, uppercaseRequest{S: s})
80 if err != nil {
81 return "", err
82 }
83
84 resp := response.(uppercaseResponse)
85 if resp.Err != "" {
86 return resp.V, errors.New(resp.Err)
87 }
88 return resp.V, nil
89 }
90
91 func makeUppercaseProxy(ctx context.Context, instance string) endpoint.Endpoint {
92 if !strings.HasPrefix(instance, "http") {
93 instance = "http://" + instance
94 }
95 u, err := url.Parse(instance)
96 if err != nil {
97 panic(err)
98 }
99 if u.Path == "" {
100 u.Path = "/uppercase"
101 }
102 return httptransport.NewClient(
103 "GET",
104 u,
105 encodeRequest,
106 decodeUppercaseResponse,
107 ).Endpoint()
108 }
109
110 func split(s string) []string {
111 a := strings.Split(s, ",")
112 for i := range a {
113 a[i] = strings.TrimSpace(a[i])
114 }
115 return a
116 }
+0
-31
examples/stringsvc3/service.go less more
0 package main
1
2 import (
3 "errors"
4 "strings"
5 )
6
7 // StringService provides operations on strings.
8 type StringService interface {
9 Uppercase(string) (string, error)
10 Count(string) int
11 }
12
13 type stringService struct{}
14
15 func (stringService) Uppercase(s string) (string, error) {
16 if s == "" {
17 return "", ErrEmpty
18 }
19 return strings.ToUpper(s), nil
20 }
21
22 func (stringService) Count(s string) int {
23 return len(s)
24 }
25
26 // ErrEmpty is returned when an input string is empty.
27 var ErrEmpty = errors.New("empty string")
28
29 // ServiceMiddleware is a chainable behavior modifier for StringService.
30 type ServiceMiddleware func(StringService) StringService
+0
-84
examples/stringsvc3/transport.go less more
0 package main
1
2 import (
3 "bytes"
4 "context"
5 "encoding/json"
6 "io/ioutil"
7 "net/http"
8
9 "github.com/go-kit/kit/endpoint"
10 )
11
12 func makeUppercaseEndpoint(svc StringService) endpoint.Endpoint {
13 return func(ctx context.Context, request interface{}) (interface{}, error) {
14 req := request.(uppercaseRequest)
15 v, err := svc.Uppercase(req.S)
16 if err != nil {
17 return uppercaseResponse{v, err.Error()}, nil
18 }
19 return uppercaseResponse{v, ""}, nil
20 }
21 }
22
23 func makeCountEndpoint(svc StringService) endpoint.Endpoint {
24 return func(ctx context.Context, request interface{}) (interface{}, error) {
25 req := request.(countRequest)
26 v := svc.Count(req.S)
27 return countResponse{v}, nil
28 }
29 }
30
31 func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) {
32 var request uppercaseRequest
33 if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
34 return nil, err
35 }
36 return request, nil
37 }
38
39 func decodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) {
40 var request countRequest
41 if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
42 return nil, err
43 }
44 return request, nil
45 }
46
47 func decodeUppercaseResponse(_ context.Context, r *http.Response) (interface{}, error) {
48 var response uppercaseResponse
49 if err := json.NewDecoder(r.Body).Decode(&response); err != nil {
50 return nil, err
51 }
52 return response, nil
53 }
54
55 func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
56 return json.NewEncoder(w).Encode(response)
57 }
58
59 func encodeRequest(_ context.Context, r *http.Request, request interface{}) error {
60 var buf bytes.Buffer
61 if err := json.NewEncoder(&buf).Encode(request); err != nil {
62 return err
63 }
64 r.Body = ioutil.NopCloser(&buf)
65 return nil
66 }
67
68 type uppercaseRequest struct {
69 S string `json:"s"`
70 }
71
72 type uppercaseResponse struct {
73 V string `json:"v"`
74 Err string `json:"err,omitempty"`
75 }
76
77 type countRequest struct {
78 S string `json:"s"`
79 }
80
81 type countResponse struct {
82 V int `json:"v"`
83 }
+0
-206
examples/stringsvc4/main.go less more
0 package main
1
2 import (
3 "context"
4 "encoding/json"
5 "errors"
6 "log"
7 "strings"
8 "flag"
9 "net/http"
10
11 "github.com/go-kit/kit/endpoint"
12 natstransport "github.com/go-kit/kit/transport/nats"
13 httptransport "github.com/go-kit/kit/transport/http"
14
15 "github.com/nats-io/nats.go"
16 )
17
18 // StringService provides operations on strings.
19 type StringService interface {
20 Uppercase(context.Context, string) (string, error)
21 Count(context.Context, string) int
22 }
23
24 // stringService is a concrete implementation of StringService
25 type stringService struct{}
26
27 func (stringService) Uppercase(_ context.Context, s string) (string, error) {
28 if s == "" {
29 return "", ErrEmpty
30 }
31 return strings.ToUpper(s), nil
32 }
33
34 func (stringService) Count(_ context.Context, s string) int {
35 return len(s)
36 }
37
38 // ErrEmpty is returned when an input string is empty.
39 var ErrEmpty = errors.New("empty string")
40
41 // For each method, we define request and response structs
42 type uppercaseRequest struct {
43 S string `json:"s"`
44 }
45
46 type uppercaseResponse struct {
47 V string `json:"v"`
48 Err string `json:"err,omitempty"` // errors don't define JSON marshaling
49 }
50
51 type countRequest struct {
52 S string `json:"s"`
53 }
54
55 type countResponse struct {
56 V int `json:"v"`
57 }
58
59 // Endpoints are a primary abstraction in go-kit. An endpoint represents a single RPC (method in our service interface)
60 func makeUppercaseHTTPEndpoint(nc *nats.Conn) endpoint.Endpoint {
61 return natstransport.NewPublisher(
62 nc,
63 "stringsvc.uppercase",
64 natstransport.EncodeJSONRequest,
65 decodeUppercaseResponse,
66 ).Endpoint()
67 }
68
69 func makeCountHTTPEndpoint(nc *nats.Conn) endpoint.Endpoint {
70 return natstransport.NewPublisher(
71 nc,
72 "stringsvc.count",
73 natstransport.EncodeJSONRequest,
74 decodeCountResponse,
75 ).Endpoint()
76 }
77
78 func makeUppercaseEndpoint(svc StringService) endpoint.Endpoint {
79 return func(ctx context.Context, request interface{}) (interface{}, error) {
80 req := request.(uppercaseRequest)
81 v, err := svc.Uppercase(ctx, req.S)
82 if err != nil {
83 return uppercaseResponse{v, err.Error()}, nil
84 }
85 return uppercaseResponse{v, ""}, nil
86 }
87 }
88
89 func makeCountEndpoint(svc StringService) endpoint.Endpoint {
90 return func(ctx context.Context, request interface{}) (interface{}, error) {
91 req := request.(countRequest)
92 v := svc.Count(ctx, req.S)
93 return countResponse{v}, nil
94 }
95 }
96
97 // Transports expose the service to the network. In this fourth example we utilize JSON over NATS and HTTP.
98 func main() {
99 svc := stringService{}
100
101 natsURL := flag.String("nats-url", nats.DefaultURL, "URL for connection to NATS")
102 flag.Parse()
103
104 nc, err := nats.Connect(*natsURL)
105 if err != nil {
106 log.Fatal(err)
107 }
108 defer nc.Close()
109
110 uppercaseHTTPHandler := httptransport.NewServer(
111 makeUppercaseHTTPEndpoint(nc),
112 decodeUppercaseHTTPRequest,
113 httptransport.EncodeJSONResponse,
114 )
115
116 countHTTPHandler := httptransport.NewServer(
117 makeCountHTTPEndpoint(nc),
118 decodeCountHTTPRequest,
119 httptransport.EncodeJSONResponse,
120 )
121
122 uppercaseHandler := natstransport.NewSubscriber(
123 makeUppercaseEndpoint(svc),
124 decodeUppercaseRequest,
125 natstransport.EncodeJSONResponse,
126 )
127
128 countHandler := natstransport.NewSubscriber(
129 makeCountEndpoint(svc),
130 decodeCountRequest,
131 natstransport.EncodeJSONResponse,
132 )
133
134 uSub, err := nc.QueueSubscribe("stringsvc.uppercase", "stringsvc", uppercaseHandler.ServeMsg(nc))
135 if err != nil {
136 log.Fatal(err)
137 }
138 defer uSub.Unsubscribe()
139
140 cSub, err := nc.QueueSubscribe("stringsvc.count", "stringsvc", countHandler.ServeMsg(nc))
141 if err != nil {
142 log.Fatal(err)
143 }
144 defer cSub.Unsubscribe()
145
146 http.Handle("/uppercase", uppercaseHTTPHandler)
147 http.Handle("/count", countHTTPHandler)
148 log.Fatal(http.ListenAndServe(":8080", nil))
149
150 }
151
152 func decodeUppercaseHTTPRequest(_ context.Context, r *http.Request) (interface{}, error) {
153 var request uppercaseRequest
154 if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
155 return nil, err
156 }
157 return request, nil
158 }
159
160 func decodeCountHTTPRequest(_ context.Context, r *http.Request) (interface{}, error) {
161 var request countRequest
162 if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
163 return nil, err
164 }
165 return request, nil
166 }
167
168 func decodeUppercaseResponse(_ context.Context, msg *nats.Msg) (interface{}, error) {
169 var response uppercaseResponse
170
171 if err := json.Unmarshal(msg.Data, &response); err != nil {
172 return nil, err
173 }
174
175 return response, nil
176 }
177
178 func decodeCountResponse(_ context.Context, msg *nats.Msg) (interface{}, error) {
179 var response countResponse
180
181 if err := json.Unmarshal(msg.Data, &response); err != nil {
182 return nil, err
183 }
184
185 return response, nil
186 }
187
188 func decodeUppercaseRequest(_ context.Context, msg *nats.Msg) (interface{}, error) {
189 var request uppercaseRequest
190
191 if err := json.Unmarshal(msg.Data, &request); err != nil {
192 return nil, err
193 }
194 return request, nil
195 }
196
197 func decodeCountRequest(_ context.Context, msg *nats.Msg) (interface{}, error) {
198 var request countRequest
199
200 if err := json.Unmarshal(msg.Data, &request); err != nil {
201 return nil, err
202 }
203 return request, nil
204 }
205