diff --git a/tracing/README.md b/tracing/README.md new file mode 100644 index 000000000..536d1e32b --- /dev/null +++ b/tracing/README.md @@ -0,0 +1,71 @@ +# Tracing Example + +This example illustrates how to use the tracing and AWS X-Ray middleware in +Goa v3. + +## Server Tracing Setup + +The tracing middleware can be mounted on the `net/http` `Handler` for HTTP +transport or when initializing a gRPC server for gRPC transport. The ordering +of mounting the tracing and X-Ray middleware is important as shown below. + +``` + // HTTP + + var handler http.Handler = mux + { + xrayHndlr, err := xray.New("calc", daemon) + if err != nil { + logger.Printf("[WARN] cannot connect to xray daemon %s: %s", daemon, err) + } + // Wrap the Xray and the tracing handler. The order is very important. + handler = xrayHndlr(handler) + handler = httpmdlwr.Trace()(handler) + } + + // gRPC + + xm, err := xray.NewUnaryServer("calc", daemon) + if err != nil { + logger.Printf("[WARN] cannot connect to xray daemon %s: %s", daemon, err) + } + // Initialize gRPC server with the middleware. + srv := grpc.NewServer( + grpc.ChainUnaryInterceptor( + // Mount the trace and X-Ray middleware. Order is very important. + grpcmdlwr.UnaryServerTrace(), + xm, + ), + ) +``` + +## Client Tracing Setup + +The tracing middleware can be wrapped around a HTTP client for HTTP transport or +when initializing a gRPC client connection for gRPC transport. + +``` + // HTTP + + var ( + doer goahttp.Doer + ) + { + doer = &http.Client{Timeout: time.Duration(timeout) * time.Second} + // Wrap doer with X-Ray and trace client middleware. Order is very important. + doer = xray.WrapDoer(doer) + doer = middleware.WrapDoer(doer) + } + + // gRPC + + conn, err := grpc.Dial( + host, + grpc.WithInsecure(), + grpc.WithUnaryInterceptor(grpcmiddleware.ChainUnaryClient( + // Mount the X-Ray and trace client middleware. Order is very important. + xray.UnaryClient(host), + middleware.UnaryClientTrace(), + )), + ) +``` diff --git a/tracing/cmd/calc-cli/grpc.go b/tracing/cmd/calc-cli/grpc.go new file mode 100644 index 000000000..b0cb34e31 --- /dev/null +++ b/tracing/cmd/calc-cli/grpc.go @@ -0,0 +1,30 @@ +package main + +import ( + "fmt" + "os" + + grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware" + cli "goa.design/examples/basic/gen/grpc/cli/calc" + "goa.design/goa/v3/grpc/middleware" + "goa.design/goa/v3/grpc/middleware/xray" + goa "goa.design/goa/v3/pkg" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func doGRPC(scheme, host string, timeout int, debug bool) (goa.Endpoint, interface{}, error) { + conn, err := grpc.Dial( + host, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithUnaryInterceptor(grpcmiddleware.ChainUnaryClient( + // Mount the X-Ray and trace client middleware. Order is very important. + xray.UnaryClient(host), + middleware.UnaryClientTrace(), + )), + ) + if err != nil { + fmt.Fprintf(os.Stderr, "could not connect to gRPC server at %s: %v\n", host, err) + } + return cli.ParseEndpoint(conn) +} diff --git a/tracing/cmd/calc-cli/http.go b/tracing/cmd/calc-cli/http.go new file mode 100644 index 000000000..474bff494 --- /dev/null +++ b/tracing/cmd/calc-cli/http.go @@ -0,0 +1,43 @@ +package main + +import ( + "net/http" + "time" + + cli "goa.design/examples/basic/gen/http/cli/calc" + goahttp "goa.design/goa/v3/http" + "goa.design/goa/v3/http/middleware" + "goa.design/goa/v3/http/middleware/xray" + goa "goa.design/goa/v3/pkg" +) + +func doHTTP(scheme, host string, timeout int, debug bool) (goa.Endpoint, interface{}, error) { + var ( + doer goahttp.Doer + ) + { + doer = &http.Client{Timeout: time.Duration(timeout) * time.Second} + if debug { + doer = goahttp.NewDebugDoer(doer) + } + // Wrap doer with X-Ray and trace client middleware. Order is very important. + doer = xray.WrapDoer(doer) + doer = middleware.WrapDoer(doer) + } + + return cli.ParseEndpoint( + scheme, + host, + doer, + goahttp.RequestEncoder, + goahttp.ResponseDecoder, + debug, + ) +} +func httpUsageCommands() string { + return cli.UsageCommands() +} + +func httpUsageExamples() string { + return cli.UsageExamples() +} diff --git a/tracing/cmd/calc-cli/main.go b/tracing/cmd/calc-cli/main.go new file mode 100644 index 000000000..439d1d848 --- /dev/null +++ b/tracing/cmd/calc-cli/main.go @@ -0,0 +1,127 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "net/url" + "os" + "strings" + + goa "goa.design/goa/v3/pkg" +) + +func main() { + var ( + hostF = flag.String("host", "development", "Server host (valid values: development, production)") + addrF = flag.String("url", "", "URL to service host") + + versionF = flag.String("version", "v1", "API version") + verboseF = flag.Bool("verbose", false, "Print request and response details") + vF = flag.Bool("v", false, "Print request and response details") + timeoutF = flag.Int("timeout", 30, "Maximum number of seconds to wait for response") + ) + flag.Usage = usage + flag.Parse() + var ( + addr string + timeout int + debug bool + ) + { + addr = *addrF + if addr == "" { + switch *hostF { + case "development": + addr = "http://localhost:8000/calc" + case "production": + addr = "https://{version}.goa.design/calc" + addr = strings.Replace(addr, "{version}", *versionF, -1) + default: + fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: development|production)", *hostF) + os.Exit(1) + } + } + timeout = *timeoutF + debug = *verboseF || *vF + } + + var ( + scheme string + host string + ) + { + u, err := url.Parse(addr) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err) + os.Exit(1) + } + scheme = u.Scheme + host = u.Host + } + var ( + endpoint goa.Endpoint + payload interface{} + err error + ) + { + switch scheme { + case "http", "https": + endpoint, payload, err = doHTTP(scheme, host, timeout, debug) + case "grpc", "grpcs": + endpoint, payload, err = doGRPC(scheme, host, timeout, debug) + default: + fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: grpc|grpcs|http|https)", scheme) + os.Exit(1) + } + } + if err != nil { + if err == flag.ErrHelp { + os.Exit(0) + } + fmt.Fprintln(os.Stderr, err.Error()) + fmt.Fprintln(os.Stderr, "run '"+os.Args[0]+" --help' for detailed usage.") + os.Exit(1) + } + + data, err := endpoint(context.Background(), payload) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + if data != nil { + m, _ := json.MarshalIndent(data, "", " ") + fmt.Println(string(m)) + } +} + +func usage() { + fmt.Fprintf(os.Stderr, `%s is a command line client for the calc API. + +Usage: + %s [-host HOST][-url URL][-timeout SECONDS][-verbose|-v][-version VERSION] SERVICE ENDPOINT [flags] + + -host HOST: server host (development). valid values: development, production + -url URL: specify service URL overriding host URL (http://localhost:8080) + -timeout: maximum number of seconds to wait for response (30) + -verbose|-v: print request and response details (false) + -version: API version (v1) + +Commands: +%s +Additional help: + %s SERVICE [ENDPOINT] --help + +Example: +%s +`, os.Args[0], os.Args[0], indent(httpUsageCommands()), os.Args[0], indent(httpUsageExamples())) +} + +func indent(s string) string { + if s == "" { + return "" + } + return " " + strings.Replace(s, "\n", "\n ", -1) +} diff --git a/tracing/cmd/calc/grpc.go b/tracing/cmd/calc/grpc.go new file mode 100644 index 000000000..0cf9c680c --- /dev/null +++ b/tracing/cmd/calc/grpc.go @@ -0,0 +1,84 @@ +package main + +import ( + "context" + "log" + "net" + "net/url" + "sync" + + calcsvc "goa.design/examples/basic/gen/calc" + calcpb "goa.design/examples/basic/gen/grpc/calc/pb" + calcsvcsvr "goa.design/examples/basic/gen/grpc/calc/server" + grpcmdlwr "goa.design/goa/v3/grpc/middleware" + "goa.design/goa/v3/grpc/middleware/xray" + "goa.design/goa/v3/middleware" + "google.golang.org/grpc" +) + +// handleGRPCServer starts configures and starts a gRPC server on the given +// URL. It shuts down the server if any error is received in the error channel. +func handleGRPCServer(ctx context.Context, u *url.URL, calcEndpoints *calcsvc.Endpoints, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool, daemon string) { + + // Setup goa log adapter. + var ( + adapter middleware.Logger + ) + { + adapter = middleware.NewLogger(logger) + } + + // Wrap the endpoints with the transport specific layers. The generated + // server packages contains code generated from the design which maps + // the service input and output data structures to gRPC requests and + // responses. + var ( + calcServer *calcsvcsvr.Server + ) + { + calcServer = calcsvcsvr.New(calcEndpoints, nil) + } + + xm, err := xray.NewUnaryServer("calc", daemon) + if err != nil { + logger.Printf("[WARN] cannot connect to xray daemon %s: %s", daemon, err) + } + // Initialize gRPC server with the middleware. + srv := grpc.NewServer( + grpc.ChainUnaryInterceptor( + grpcmdlwr.UnaryRequestID(), + grpcmdlwr.UnaryServerLog(adapter), + // Mount the trace and X-Ray middleware. Order is very important. + grpcmdlwr.UnaryServerTrace(), + xm, + ), + ) + + // Register the servers. + calcpb.RegisterCalcServer(srv, calcServer) + + for svc, info := range srv.GetServiceInfo() { + for _, m := range info.Methods { + logger.Printf("serving gRPC method %s", svc+"/"+m.Name) + } + } + + (*wg).Add(1) + go func() { + defer (*wg).Done() + + // Start gRPC server in a separate goroutine. + go func() { + lis, err := net.Listen("tcp", u.Host) + if err != nil { + errc <- err + } + logger.Printf("gRPC server listening on %q", u.Host) + errc <- srv.Serve(lis) + }() + + <-ctx.Done() + logger.Printf("shutting down gRPC server at %q", u.Host) + srv.Stop() + }() +} diff --git a/tracing/cmd/calc/http.go b/tracing/cmd/calc/http.go new file mode 100644 index 000000000..664de8273 --- /dev/null +++ b/tracing/cmd/calc/http.go @@ -0,0 +1,117 @@ +package main + +import ( + "context" + "log" + "net/http" + "net/url" + "os" + "sync" + "time" + + calcsvc "goa.design/examples/basic/gen/calc" + calcsvcsvr "goa.design/examples/basic/gen/http/calc/server" + goahttp "goa.design/goa/v3/http" + httpmdlwr "goa.design/goa/v3/http/middleware" + "goa.design/goa/v3/http/middleware/xray" + "goa.design/goa/v3/middleware" +) + +// handleHTTPServer starts configures and starts a HTTP server on the given +// URL. It shuts down the server if any error is received in the error channel. +func handleHTTPServer(ctx context.Context, u *url.URL, calcEndpoints *calcsvc.Endpoints, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool, daemon string) { + + // Setup goa log adapter. + var ( + adapter middleware.Logger + ) + { + adapter = middleware.NewLogger(logger) + } + + // Provide the transport specific request decoder and response encoder. + // The goa http package has built-in support for JSON, XML and gob. + // Other encodings can be used by providing the corresponding functions, + // see goa.design/encoding. + var ( + dec = goahttp.RequestDecoder + enc = goahttp.ResponseEncoder + ) + + // Build the service HTTP request multiplexer and configure it to serve + // HTTP requests to the service endpoints. + var mux goahttp.Muxer + { + mux = goahttp.NewMuxer() + } + + // Wrap the endpoints with the transport specific layers. The generated + // server packages contains code generated from the design which maps + // the service input and output data structures to HTTP requests and + // responses. + var ( + calcServer *calcsvcsvr.Server + ) + { + eh := errorHandler(logger) + calcServer = calcsvcsvr.New(calcEndpoints, mux, dec, enc, eh, nil, nil) + } + // Configure the mux. + calcsvcsvr.Mount(mux, calcServer) + + // Wrap the multiplexer with additional middlewares. Middlewares mounted + // here apply to all the service endpoints. + var handler http.Handler = mux + { + if debug { + handler = httpmdlwr.Debug(mux, os.Stdout)(handler) + } + handler = httpmdlwr.Log(adapter)(handler) + handler = httpmdlwr.RequestID()(handler) + xrayHndlr, err := xray.New("calc", daemon) + if err != nil { + logger.Printf("[WARN] cannot connect to xray daemon %s: %s", daemon, err) + } + // Wrap the Xray and the tracing handler. The order is very important. + handler = xrayHndlr(handler) + handler = httpmdlwr.Trace()(handler) + } + + // Start HTTP server using default configuration, change the code to + // configure the server as required by your service. + srv := &http.Server{Addr: u.Host, Handler: handler} + for _, m := range calcServer.Mounts { + logger.Printf("HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) + } + + (*wg).Add(1) + go func() { + defer (*wg).Done() + + // Start HTTP server in a separate goroutine. + go func() { + logger.Printf("HTTP server listening on %q", u.Host) + errc <- srv.ListenAndServe() + }() + + ctx.Done() + logger.Printf("shutting down HTTP server at %q", u.Host) + + // Shutdown gracefully with a 30s timeout. + ctx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + + srv.Shutdown(ctx) + }() +} + +// errorHandler returns a function that writes and logs the given error. +// The function also writes and logs the error unique ID so that it's possible +// to correlate. +func errorHandler(logger *log.Logger) func(context.Context, http.ResponseWriter, error) { + return func(ctx context.Context, w http.ResponseWriter, err error) { + id := ctx.Value(middleware.RequestIDKey).(string) + w.Write([]byte("[" + id + "] encoding: " + err.Error())) + logger.Printf("[%s] ERROR: %s", id, err.Error()) + } +} diff --git a/tracing/cmd/calc/main.go b/tracing/cmd/calc/main.go new file mode 100644 index 000000000..1f872721f --- /dev/null +++ b/tracing/cmd/calc/main.go @@ -0,0 +1,177 @@ +package main + +import ( + "context" + "flag" + "fmt" + "log" + "net/url" + "os" + "os/signal" + "strings" + "sync" + + calc "goa.design/examples/basic" + calcsvc "goa.design/examples/basic/gen/calc" +) + +func main() { + // Define command line flags, add any other flag required to configure the + // service. + var ( + hostF = flag.String("host", "development", "Server host (valid values: development, production)") + domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") + httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") + grpcPortF = flag.String("grpc-port", "", "gRPC port (overrides host gRPC port specified in service design)") + versionF = flag.String("version", "v1", "API version") + secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") + dbgF = flag.Bool("debug", false, "Log request and response bodies") + daemonF = flag.String("daemon", "127.0.0.1:2000", "X-Ray daemon address") + ) + flag.Parse() + + // Setup logger. Replace logger with your own log package of choice. + var ( + logger *log.Logger + ) + { + logger = log.New(os.Stderr, "[calc] ", log.Ltime) + } + + // Initialize the services. + var ( + calcSvc calcsvc.Service + ) + { + calcSvc = calc.NewCalc(logger) + } + + // Wrap the services in endpoints that can be invoked from other services + // potentially running in different processes. + var ( + calcEndpoints *calcsvc.Endpoints + ) + { + calcEndpoints = calcsvc.NewEndpoints(calcSvc) + } + + // Create channel used by both the signal handler and server goroutines + // to notify the main goroutine when to stop the server. + errc := make(chan error) + + // Setup interrupt handler. This optional step configures the process so + // that SIGINT and SIGTERM signals cause the services to stop gracefully. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + errc <- fmt.Errorf("%s", <-c) + }() + + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(context.Background()) + // Start the servers and send errors (if any) to the error channel. + switch *hostF { + case "development": + { + addr := "http://localhost:8000/calc" + u, err := url.Parse(addr) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err) + os.Exit(1) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h := strings.Split(u.Host, ":")[0] + u.Host = h + ":" + *httpPortF + } else if u.Port() == "" { + u.Host += ":80" + } + handleHTTPServer(ctx, u, calcEndpoints, &wg, errc, logger, *dbgF, *daemonF) + } + + { + addr := "grpc://localhost:8080" + u, err := url.Parse(addr) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err) + os.Exit(1) + } + if *secureF { + u.Scheme = "grpcs" + } + if *domainF != "" { + u.Host = *domainF + } + if *grpcPortF != "" { + h := strings.Split(u.Host, ":")[0] + u.Host = h + ":" + *grpcPortF + } else if u.Port() == "" { + u.Host += ":8080" + } + handleGRPCServer(ctx, u, calcEndpoints, &wg, errc, logger, *dbgF, *daemonF) + } + + case "production": + { + addr := "https://{version}.goa.design/calc" + addr = strings.Replace(addr, "{version}", *versionF, -1) + u, err := url.Parse(addr) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err) + os.Exit(1) + } + if *secureF { + u.Scheme = "https" + } + if *domainF != "" { + u.Host = *domainF + } + if *httpPortF != "" { + h := strings.Split(u.Host, ":")[0] + u.Host = h + ":" + *httpPortF + } else if u.Port() == "" { + u.Host += ":443" + } + handleHTTPServer(ctx, u, calcEndpoints, &wg, errc, logger, *dbgF, *daemonF) + } + + { + addr := "grpcs://{version}.goa.design" + addr = strings.Replace(addr, "{version}", *versionF, -1) + u, err := url.Parse(addr) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err) + os.Exit(1) + } + if *secureF { + u.Scheme = "grpcs" + } + if *domainF != "" { + u.Host = *domainF + } + if *grpcPortF != "" { + h := strings.Split(u.Host, ":")[0] + u.Host = h + ":" + *grpcPortF + } else if u.Port() == "" { + u.Host += ":8443" + } + handleGRPCServer(ctx, u, calcEndpoints, &wg, errc, logger, *dbgF, *daemonF) + } + + default: + fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: development|production)", *hostF) + } + // Wait for signal. + logger.Printf("exiting (%v)", <-errc) + + // Send cancellation signal to the goroutines. + cancel() + + wg.Wait() + logger.Println("exited") +} diff --git a/tracing/go.mod b/tracing/go.mod new file mode 100644 index 000000000..0ded17a4d --- /dev/null +++ b/tracing/go.mod @@ -0,0 +1,23 @@ +module goa.design/examples/tracing + +go 1.21.3 + +require ( + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 + goa.design/examples/basic v0.0.0-00010101000000-000000000000 + goa.design/goa/v3 v3.13.2 + google.golang.org/grpc v1.58.3 +) + +require ( + github.com/go-chi/chi/v5 v5.0.10 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/tracing/go.sum b/tracing/go.sum new file mode 100644 index 000000000..b55432bc4 --- /dev/null +++ b/tracing/go.sum @@ -0,0 +1,148 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= +github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/ikawaha/calc v0.0.3 h1:lxw7qIXpYT329E4Fh+4rBeRiCZk3qZGnILNIrCXPm3Y= +github.com/ikawaha/calc v0.0.3/go.mod h1:VgNWbu1m4GXqe4EahlPZbDsIA0ynKoyP2t1cdUkSRHg= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +goa.design/goa/v3 v3.13.2 h1:RclNIpo7891ZqGRVO4fpBjT7Fs7LjBNm78i8J41KHrI= +goa.design/goa/v3 v3.13.2/go.mod h1:VvZsuC8CSIUQOHVqk6Ep3MFSFz21OjOv87UPqCHiB94= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=