diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..057e11a --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [peterbourgon] diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 0000000..fab4da8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,19 @@ +name: Bug report +description: Report a bug +labels: [bug] +body: +- type: textarea + attributes: + label: What did you do? + validations: + required: true +- type: textarea + attributes: + label: What did you expect? + validations: + required: true +- type: textarea + attributes: + label: What happened instead? + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..450da54 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,17 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/go-kit/kit/discussions/new?category=q-a + about: Questions and discussions with the Go kit community + + - name: Website + url: https://gokit.io/ + about: Project overview, examples, frequently asked questions, etc. + + - name: Reference + url: https://pkg.go.dev/github.com/go-kit/kit + about: Go kit package documentation + + - name: Slack channel + url: https://gophers.slack.com/messages/go-kit + about: Real-time discussions and Q&A diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml new file mode 100644 index 0000000..a28edd6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -0,0 +1,8 @@ +name: Feature request +description: Suggest new functionality or an enhancement +body: +- type: textarea + attributes: + label: What would you like? + validations: + required: true diff --git a/.github/workflows/.editorconfig b/.github/workflows/.editorconfig new file mode 100644 index 0000000..7bd3346 --- /dev/null +++ b/.github/workflows/.editorconfig @@ -0,0 +1,2 @@ +[*.yml] +indent_size = 2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9f5c2c7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,68 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + +jobs: + build: + name: Build + runs-on: ubuntu-latest + strategy: + matrix: # Support latest and one minor back + go: ["1.16", "1.17"] + env: + GOFLAGS: -mod=readonly + + services: + etcd: + image: gcr.io/etcd-development/etcd:v3.5.0 + ports: + - 2379 + env: + ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379 + ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379 + options: --health-cmd "ETCDCTL_API=3 etcdctl --endpoints http://localhost:2379 endpoint health" --health-interval 10s --health-timeout 5s --health-retries 5 + + consul: + image: consul:1.10 + ports: + - 8500 + + zk: + image: zookeeper:3.5 + ports: + - 2181 + + eureka: + image: springcloud/eureka + ports: + - 8761 + env: + eureka.server.responseCacheUpdateIntervalMs: 1000 + + steps: + - name: Set up Go + uses: actions/setup-go@v2.1.3 + with: + stable: "false" + go-version: ${{ matrix.go }} + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Run tests + env: + ETCD_ADDR: http://localhost:${{ job.services.etcd.ports[2379] }} + CONSUL_ADDR: localhost:${{ job.services.consul.ports[8500] }} + ZK_ADDR: localhost:${{ job.services.zk.ports[2181] }} + EUREKA_ADDR: http://localhost:${{ job.services.eureka.ports[8761] }}/eureka + run: go test -v -race -coverprofile=coverage.coverprofile -covermode=atomic -tags integration ./... + + - name: Upload coverage + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: coverage.coverprofile diff --git a/.gitignore b/.gitignore index 6062401..ff53c9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,3 @@ -examples/addsvc/addsvc -examples/addsvc/client/client -examples/apigateway/apigateway -examples/profilesvc/profilesvc -examples/stringsvc1/stringsvc1 -examples/stringsvc2/stringsvc2 -examples/stringsvc3/stringsvc3 *.coverprofile # Compiled Object files, Static and Dynamic libs (Shared Objects) diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 31624b2..0000000 --- a/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: go - -env: - - COVERALLS_TOKEN=MYSkSqcsWXd6DmP6TnSeiDhtvuL4u6ndp - -before_install: - - go get github.com/mattn/goveralls - - go get github.com/modocache/gover - -script: - - go test -race -v ./... - - ./coveralls.bash - -go: - - 1.9.x - - tip diff --git a/README.md b/README.md index 2d9a2b4..f2f5a4b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,23 @@ -# Go kit [![Circle CI](https://circleci.com/gh/go-kit/kit.svg?style=shield)](https://circleci.com/gh/go-kit/kit) [![Travis CI](https://travis-ci.org/go-kit/kit.svg?branch=master)](https://travis-ci.org/go-kit/kit) [![GoDoc](https://godoc.org/github.com/go-kit/kit?status.svg)](https://godoc.org/github.com/go-kit/kit) [![Coverage Status](https://coveralls.io/repos/go-kit/kit/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-kit/kit?branch=master) [![Go Report Card](https://goreportcard.com/badge/go-kit/kit)](https://goreportcard.com/report/go-kit/kit) [![Sourcegraph](https://sourcegraph.com/github.com/go-kit/kit/-/badge.svg)](https://sourcegraph.com/github.com/go-kit/kit?badge) +# Go kit -**Go kit** is a **programming toolkit** for building microservices -(or elegant monoliths) in Go. We solve common problems in distributed +![GitHub Workflow Status](https://github.com/go-kit/kit/workflows/CI/badge.svg) +[![GoDev](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/go-kit/kit?tab=doc) +[![codecov](https://codecov.io/gh/go-kit/kit/branch/master/graph/badge.svg)](https://codecov.io/gh/go-kit/kit) +[![Go Report Card](https://goreportcard.com/badge/go-kit/kit)](https://goreportcard.com/report/go-kit/kit) +[![Sourcegraph](https://sourcegraph.com/github.com/go-kit/kit/-/badge.svg)](https://sourcegraph.com/github.com/go-kit/kit?badge) + +**Go kit** is a **programming toolkit** for building microservices +(or elegant monoliths) in Go. We solve common problems in distributed systems and application architecture so you can focus on delivering business value. - Website: [gokit.io](https://gokit.io) - Mailing list: [go-kit](https://groups.google.com/forum/#!forum/go-kit) - Slack: [gophers.slack.com](https://gophers.slack.com) **#go-kit** ([invite](https://gophersinvite.herokuapp.com/)) + +## Sponsors + +Click on Sponsor, above, for more information on sponsorship. ## Motivation @@ -50,21 +60,22 @@ ## Dependency management -Go kit is a library, designed to be imported into a binary package. Vendoring -is currently the best way for binary package authors to ensure reliable, -reproducible builds. Therefore, we strongly recommend our users use vendoring -for all of their dependencies, including Go kit. To avoid compatibility and -availability issues, Go kit doesn't vendor its own dependencies, and -doesn't recommend use of third-party import proxies. +Go kit is [modules](https://github.com/golang/go/wiki/Modules) aware, and we +encourage users to use the standard modules tooling. But Go kit is at major +version 0, so it should be compatible with non-modules environments. -There are several tools which make vendoring easier, including - [dep](https://github.com/golang/dep), - [gb](http://getgb.io), - [glide](https://github.com/Masterminds/glide), - [gvt](https://github.com/FiloSottile/gvt), and - [govendor](https://github.com/kardianos/govendor). -In addition, Go kit uses a variety of continuous integration providers - to find and fix compatibility problems as soon as they occur. +## Code generators + +There are several third-party tools that can generate Go kit code based on +different starting assumptions. + +- [devimteam/microgen](https://github.com/devimteam/microgen) +- [GrantZheng/kit](https://github.com/GrantZheng/kit) +- [kujtimiihoxha/kit](https://github.com/kujtimiihoxha/kit) (unmaintained) +- [nytimes/marvin](https://github.com/nytimes/marvin) +- [sagikazarmark/mga](https://github.com/sagikazarmark/mga) +- [sagikazarmark/protoc-gen-kit](https://github.com/sagikazarmark/protoc-gen-kit) +- [tuneinc/truss](https://github.com/tuneinc/truss) ## Related projects @@ -73,7 +84,7 @@ ### Service frameworks - [gizmo](https://github.com/nytimes/gizmo), a microservice toolkit from The New York Times ★ -- [go-micro](https://github.com/myodc/go-micro), a microservices client/server library ★ +- [go-micro](https://github.com/micro/go-micro), a distributed systems development framework ★ - [gotalk](https://github.com/rsms/gotalk), async peer communication protocol & library - [Kite](https://github.com/koding/kite), a micro-service framework - [gocircuit](https://github.com/gocircuit/circuit), dynamic cloud orchestration @@ -91,7 +102,7 @@ - [mattheath/phosphor](https://github.com/mondough/phosphor), distributed system tracing - [pivotal-golang/lager](https://github.com/pivotal-golang/lager), an opinionated logging library - [rubyist/circuitbreaker](https://github.com/rubyist/circuitbreaker), circuit breaker library -- [Sirupsen/logrus](https://github.com/Sirupsen/logrus), structured, pluggable logging for Go ★ +- [sirupsen/logrus](https://github.com/sirupsen/logrus), structured, pluggable logging for Go ★ - [sourcegraph/appdash](https://github.com/sourcegraph/appdash), application tracing system based on Google's Dapper - [spacemonkeygo/monitor](https://github.com/spacemonkeygo/monitor), data collection, monitoring, instrumentation, and Zipkin client library - [streadway/handy](https://github.com/streadway/handy), net/http handler filters @@ -101,19 +112,16 @@ ### Web frameworks - [Gorilla](http://www.gorillatoolkit.org) -- [Gin](https://gin-gonic.github.io/gin/) +- [Gin](https://gin-gonic.com/) - [Negroni](https://github.com/codegangsta/negroni) - [Goji](https://github.com/zenazn/goji) - [Martini](https://github.com/go-martini/martini) - [Beego](http://beego.me/) - [Revel](https://revel.github.io/) (considered [harmful](https://github.com/go-kit/kit/issues/350)) +- [GoBuffalo](https://gobuffalo.io/) ## Additional reading -- [Architecting for the Cloud](http://fr.slideshare.net/stonse/architecting-for-the-cloud-using-netflixoss-codemash-workshop-29852233) — Netflix +- [Architecting for the Cloud](https://slideshare.net/stonse/architecting-for-the-cloud-using-netflixoss-codemash-workshop-29852233) — Netflix - [Dapper, a Large-Scale Distributed Systems Tracing Infrastructure](http://research.google.com/pubs/pub36356.html) — Google - [Your Server as a Function](http://monkey.org/~marius/funsrv.pdf) (PDF) — Twitter - ---- - -Development supported by [DigitalOcean](https://digitalocean.com). diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index 5c462aa..0000000 --- a/ROADMAP.md +++ /dev/null @@ -1,17 +0,0 @@ -# Roadmap - -This is a coarse-grained roadmap of Go kit development direction in the short -to mid-term future. It will be kept reasonably up-to-date by the project -maintainers. Suggest new ideas, enhancements, and features using the standard -[issues](https://github.com/go-kit/kit/issues) model. - -## Prioritized - -1. kitgen code generation (#308, #70) -1. package pubsub (#298, #295) - -## Unprioritized - -- package log/levels refactor (#250, #269, #252) -- package auth/jwt (#255) - diff --git a/auth/casbin/middleware.go b/auth/casbin/middleware.go new file mode 100644 index 0000000..6f459ad --- /dev/null +++ b/auth/casbin/middleware.go @@ -0,0 +1,71 @@ +package casbin + +import ( + "context" + "errors" + + stdcasbin "github.com/casbin/casbin/v2" + "github.com/go-kit/kit/endpoint" +) + +type contextKey string + +const ( + // CasbinModelContextKey holds the key to store the access control model + // in context, it can be a path to configuration file or a casbin/model + // Model. + CasbinModelContextKey contextKey = "CasbinModel" + + // CasbinPolicyContextKey holds the key to store the access control policy + // in context, it can be a path to policy file or an implementation of + // casbin/persist Adapter interface. + CasbinPolicyContextKey contextKey = "CasbinPolicy" + + // CasbinEnforcerContextKey holds the key to retrieve the active casbin + // Enforcer. + CasbinEnforcerContextKey contextKey = "CasbinEnforcer" +) + +var ( + // ErrModelContextMissing denotes a casbin model was not passed into + // the parsing of middleware's context. + ErrModelContextMissing = errors.New("CasbinModel is required in context") + + // ErrPolicyContextMissing denotes a casbin policy was not passed into + // the parsing of middleware's context. + ErrPolicyContextMissing = errors.New("CasbinPolicy is required in context") + + // ErrUnauthorized denotes the subject is not authorized to do the action + // intended on the given object, based on the context model and policy. + ErrUnauthorized = errors.New("Unauthorized Access") +) + +// NewEnforcer checks whether the subject is authorized to do the specified +// action on the given object. If a valid access control model and policy +// is given, then the generated casbin Enforcer is stored in the context +// with CasbinEnforcer as the key. +func NewEnforcer( + subject string, object interface{}, action string, +) endpoint.Middleware { + return func(next endpoint.Endpoint) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + casbinModel := ctx.Value(CasbinModelContextKey) + casbinPolicy := ctx.Value(CasbinPolicyContextKey) + enforcer, err := stdcasbin.NewEnforcer(casbinModel, casbinPolicy) + if err != nil { + return nil, err + } + + ctx = context.WithValue(ctx, CasbinEnforcerContextKey, enforcer) + ok, err := enforcer.Enforce(subject, object, action) + if err != nil { + return nil, err + } + if !ok { + return nil, ErrUnauthorized + } + + return next(ctx, request) + } + } +} diff --git a/auth/casbin/middleware_test.go b/auth/casbin/middleware_test.go new file mode 100644 index 0000000..6f418d5 --- /dev/null +++ b/auth/casbin/middleware_test.go @@ -0,0 +1,56 @@ +package casbin + +import ( + "context" + "testing" + + stdcasbin "github.com/casbin/casbin/v2" + "github.com/casbin/casbin/v2/model" + fileadapter "github.com/casbin/casbin/v2/persist/file-adapter" +) + +func TestStructBaseContext(t *testing.T) { + e := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil } + + m := model.NewModel() + m.AddDef("r", "r", "sub, obj, act") + m.AddDef("p", "p", "sub, obj, act") + m.AddDef("e", "e", "some(where (p.eft == allow))") + m.AddDef("m", "m", "r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)") + + a := fileadapter.NewAdapter("testdata/keymatch_policy.csv") + + ctx := context.WithValue(context.Background(), CasbinModelContextKey, m) + ctx = context.WithValue(ctx, CasbinPolicyContextKey, a) + + // positive case + middleware := NewEnforcer("alice", "/alice_data/resource1", "GET")(e) + ctx1, err := middleware(ctx, struct{}{}) + if err != nil { + t.Fatalf("Enforcer returned error: %s", err) + } + _, ok := ctx1.(context.Context).Value(CasbinEnforcerContextKey).(*stdcasbin.Enforcer) + if !ok { + t.Fatalf("context should contains the active enforcer") + } + + // negative case + middleware = NewEnforcer("alice", "/alice_data/resource2", "POST")(e) + _, err = middleware(ctx, struct{}{}) + if err == nil { + t.Fatalf("Enforcer should return error") + } +} + +func TestFileBaseContext(t *testing.T) { + e := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil } + ctx := context.WithValue(context.Background(), CasbinModelContextKey, "testdata/basic_model.conf") + ctx = context.WithValue(ctx, CasbinPolicyContextKey, "testdata/basic_policy.csv") + + // positive case + middleware := NewEnforcer("alice", "data1", "read")(e) + _, err := middleware(ctx, struct{}{}) + if err != nil { + t.Fatalf("Enforcer returned error: %s", err) + } +} diff --git a/auth/casbin/testdata/basic_model.conf b/auth/casbin/testdata/basic_model.conf new file mode 100644 index 0000000..dc6da81 --- /dev/null +++ b/auth/casbin/testdata/basic_model.conf @@ -0,0 +1,11 @@ +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = r.sub == p.sub && r.obj == p.obj && r.act == p.act \ No newline at end of file diff --git a/auth/casbin/testdata/basic_policy.csv b/auth/casbin/testdata/basic_policy.csv new file mode 100644 index 0000000..57aaa97 --- /dev/null +++ b/auth/casbin/testdata/basic_policy.csv @@ -0,0 +1,2 @@ +p, alice, data1, read +p, bob, data2, write \ No newline at end of file diff --git a/auth/casbin/testdata/keymatch_policy.csv b/auth/casbin/testdata/keymatch_policy.csv new file mode 100644 index 0000000..d6e9b7d --- /dev/null +++ b/auth/casbin/testdata/keymatch_policy.csv @@ -0,0 +1,7 @@ +p, alice, /alice_data/*, GET +p, alice, /alice_data/resource1, POST + +p, bob, /alice_data/resource2, GET +p, bob, /bob_data/*, POST + +p, cathy, /cathy_data, (GET)|(POST) \ No newline at end of file diff --git a/auth/jwt/README.md b/auth/jwt/README.md index d2430bd..58b7d91 100644 --- a/auth/jwt/README.md +++ b/auth/jwt/README.md @@ -7,12 +7,12 @@ NewParser takes a key function and an expected signing method and returns an `endpoint.Middleware`. The middleware will parse a token passed into the -context via the `jwt.JWTTokenContextKey`. If the token is valid, any claims +context via the `jwt.JWTContextKey`. If the token is valid, any claims will be added to the context via the `jwt.JWTClaimsContextKey`. ```go import ( - stdjwt "github.com/dgrijalva/jwt-go" + stdjwt "github.com/golang-jwt/jwt/v4" "github.com/go-kit/kit/auth/jwt" "github.com/go-kit/kit/endpoint" @@ -30,11 +30,11 @@ NewSigner takes a JWT key ID header, the signing key, signing method, and a claims object. It returns an `endpoint.Middleware`. The middleware will build -the token string and add it to the context via the `jwt.JWTTokenContextKey`. +the token string and add it to the context via the `jwt.JWTContextKey`. ```go import ( - stdjwt "github.com/dgrijalva/jwt-go" + stdjwt "github.com/golang-jwt/jwt/v4" "github.com/go-kit/kit/auth/jwt" "github.com/go-kit/kit/endpoint" @@ -55,8 +55,8 @@ ``` In order for the parser and the signer to work, the authorization headers need -to be passed between the request and the context. `ToHTTPContext()`, -`FromHTTPContext()`, `ToGRPCContext()`, and `FromGRPCContext()` are given as +to be passed between the request and the context. `HTTPToContext()`, +`ContextToHTTP()`, `GRPCToContext()`, and `ContextToGRPC()` are given as helpers to do this. These functions implement the correlating transport's RequestFunc interface and can be passed as ClientBefore or ServerBefore options. @@ -65,7 +65,7 @@ ```go import ( - stdjwt "github.com/dgrijalva/jwt-go" + stdjwt "github.com/golang-jwt/jwt/v4" grpctransport "github.com/go-kit/kit/transport/grpc" "github.com/go-kit/kit/auth/jwt" @@ -77,7 +77,7 @@ options := []httptransport.ClientOption{} var exampleEndpoint endpoint.Endpoint { - exampleEndpoint = grpctransport.NewClient(..., grpctransport.ClientBefore(jwt.FromGRPCContext())).Endpoint() + exampleEndpoint = grpctransport.NewClient(..., grpctransport.ClientBefore(jwt.ContextToGRPC())).Endpoint() exampleEndpoint = jwt.NewSigner( "kid-header", []byte("SigningString"), @@ -95,7 +95,7 @@ "context" "github.com/go-kit/kit/auth/jwt" - "github.com/go-kit/kit/log" + "github.com/go-kit/log" grpctransport "github.com/go-kit/kit/transport/grpc" ) @@ -108,7 +108,7 @@ endpoints.CreateUserEndpoint, DecodeGRPCCreateUserRequest, EncodeGRPCCreateUserResponse, - append(options, grpctransport.ServerBefore(jwt.ToGRPCContext()))..., + append(options, grpctransport.ServerBefore(jwt.GRPCToContext()))..., ), getUser: grpctransport.NewServer( ctx, diff --git a/auth/jwt/middleware.go b/auth/jwt/middleware.go index 0e29e6d..b7c89ce 100644 --- a/auth/jwt/middleware.go +++ b/auth/jwt/middleware.go @@ -4,17 +4,20 @@ "context" "errors" - jwt "github.com/dgrijalva/jwt-go" - "github.com/go-kit/kit/endpoint" + "github.com/golang-jwt/jwt/v4" ) type contextKey string const ( - // JWTTokenContextKey holds the key used to store a JWT Token in the - // context. - JWTTokenContextKey contextKey = "JWTToken" + // JWTContextKey holds the key used to store a JWT in the context. + JWTContextKey contextKey = "JWTToken" + + // JWTTokenContextKey is an alias for JWTContextKey. + // + // Deprecated: prefer JWTContextKey. + JWTTokenContextKey = JWTContextKey // JWTClaimsContextKey holds the key used to store the JWT Claims in the // context. @@ -27,13 +30,13 @@ ErrTokenContextMissing = errors.New("token up for parsing was not passed through the context") // ErrTokenInvalid denotes a token was not able to be validated. - ErrTokenInvalid = errors.New("JWT Token was invalid") + ErrTokenInvalid = errors.New("JWT was invalid") // ErrTokenExpired denotes a token's expire header (exp) has since passed. - ErrTokenExpired = errors.New("JWT Token is expired") + ErrTokenExpired = errors.New("JWT is expired") - // ErrTokenMalformed denotes a token was not formatted as a JWT token. - ErrTokenMalformed = errors.New("JWT Token is malformed") + // ErrTokenMalformed denotes a token was not formatted as a JWT. + ErrTokenMalformed = errors.New("JWT is malformed") // ErrTokenNotActive denotes a token's not before header (nbf) is in the // future. @@ -44,7 +47,7 @@ ErrUnexpectedSigningMethod = errors.New("unexpected signing method") ) -// NewSigner creates a new JWT token generating middleware, specifying key ID, +// NewSigner creates a new JWT generating middleware, specifying key ID, // signing string, signing method and the claims you would like it to contain. // Tokens are signed with a Key ID header (kid) which is useful for determining // the key to use for parsing. Particularly useful for clients. @@ -59,7 +62,7 @@ if err != nil { return nil, err } - ctx = context.WithValue(ctx, JWTTokenContextKey, tokenString) + ctx = context.WithValue(ctx, JWTContextKey, tokenString) return next(ctx, request) } @@ -82,7 +85,7 @@ return &jwt.StandardClaims{} } -// NewParser creates a new JWT token parsing middleware, specifying a +// NewParser creates a new JWT parsing middleware, specifying a // jwt.Keyfunc interface, the signing method and the claims type to be used. NewParser // adds the resulting claims to endpoint context or returns error on invalid token. // Particularly useful for servers. @@ -90,7 +93,7 @@ return func(next endpoint.Endpoint) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { // tokenString is stored in the context from the transport handlers. - tokenString, ok := ctx.Value(JWTTokenContextKey).(string) + tokenString, ok := ctx.Value(JWTContextKey).(string) if !ok { return nil, ErrTokenContextMissing } diff --git a/auth/jwt/middleware_test.go b/auth/jwt/middleware_test.go index 3278e13..32538c1 100644 --- a/auth/jwt/middleware_test.go +++ b/auth/jwt/middleware_test.go @@ -8,8 +8,8 @@ "crypto/subtle" - jwt "github.com/dgrijalva/jwt-go" "github.com/go-kit/kit/endpoint" + "github.com/golang-jwt/jwt/v4" ) type customClaims struct { @@ -44,13 +44,13 @@ t.Fatalf("Signer returned error: %s", err) } - token, ok := ctx.(context.Context).Value(JWTTokenContextKey).(string) + token, ok := ctx.(context.Context).Value(JWTContextKey).(string) if !ok { t.Fatal("Token did not exist in context") } if token != expectedKey { - t.Fatalf("JWT tokens did not match: expecting %s got %s", expectedKey, token) + t.Fatalf("JWTs did not match: expecting %s got %s", expectedKey, token) } } @@ -87,7 +87,7 @@ } // Invalid Token is passed into the parser - ctx := context.WithValue(context.Background(), JWTTokenContextKey, invalidKey) + ctx := context.WithValue(context.Background(), JWTContextKey, invalidKey) _, err = parser(ctx, struct{}{}) if err == nil { t.Error("Parser should have returned an error") @@ -95,7 +95,7 @@ // Invalid Method is used in the parser badParser := NewParser(keys, invalidMethod, MapClaimsFactory)(e) - ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey) + ctx = context.WithValue(context.Background(), JWTContextKey, signedKey) _, err = badParser(ctx, struct{}{}) if err == nil { t.Error("Parser should have returned an error") @@ -111,14 +111,14 @@ } badParser = NewParser(invalidKeys, method, MapClaimsFactory)(e) - ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey) + ctx = context.WithValue(context.Background(), JWTContextKey, signedKey) _, err = badParser(ctx, struct{}{}) if err == nil { t.Error("Parser should have returned an error") } // Correct token is passed into the parser - ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey) + ctx = context.WithValue(context.Background(), JWTContextKey, signedKey) ctx1, err := parser(ctx, struct{}{}) if err != nil { t.Fatalf("Parser returned error: %s", err) @@ -135,7 +135,7 @@ // Test for malformed token error response parser = NewParser(keys, method, StandardClaimsFactory)(e) - ctx = context.WithValue(context.Background(), JWTTokenContextKey, malformedKey) + ctx = context.WithValue(context.Background(), JWTContextKey, malformedKey) ctx1, err = parser(ctx, struct{}{}) if want, have := ErrTokenMalformed, err; want != have { t.Fatalf("Expected %+v, got %+v", want, have) @@ -148,7 +148,7 @@ if err != nil { t.Fatalf("Unable to Sign Token: %+v", err) } - ctx = context.WithValue(context.Background(), JWTTokenContextKey, token) + ctx = context.WithValue(context.Background(), JWTContextKey, token) ctx1, err = parser(ctx, struct{}{}) if want, have := ErrTokenExpired, err; want != have { t.Fatalf("Expected %+v, got %+v", want, have) @@ -161,7 +161,7 @@ if err != nil { t.Fatalf("Unable to Sign Token: %+v", err) } - ctx = context.WithValue(context.Background(), JWTTokenContextKey, token) + ctx = context.WithValue(context.Background(), JWTContextKey, token) ctx1, err = parser(ctx, struct{}{}) if want, have := ErrTokenNotActive, err; want != have { t.Fatalf("Expected %+v, got %+v", want, have) @@ -169,7 +169,7 @@ // test valid standard claims token parser = NewParser(keys, method, StandardClaimsFactory)(e) - ctx = context.WithValue(context.Background(), JWTTokenContextKey, standardSignedKey) + ctx = context.WithValue(context.Background(), JWTContextKey, standardSignedKey) ctx1, err = parser(ctx, struct{}{}) if err != nil { t.Fatalf("Parser returned error: %s", err) @@ -184,7 +184,7 @@ // test valid customized claims token parser = NewParser(keys, method, func() jwt.Claims { return &customClaims{} })(e) - ctx = context.WithValue(context.Background(), JWTTokenContextKey, customSignedKey) + ctx = context.WithValue(context.Background(), JWTContextKey, customSignedKey) ctx1, err = parser(ctx, struct{}{}) if err != nil { t.Fatalf("Parser returned error: %s", err) @@ -205,7 +205,7 @@ var ( kf = func(token *jwt.Token) (interface{}, error) { return []byte("secret"), nil } e = NewParser(kf, jwt.SigningMethodHS256, MapClaimsFactory)(endpoint.Nop) - key = JWTTokenContextKey + key = JWTContextKey val = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtpZCIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ28ta2l0In0.14M2VmYyApdSlV_LZ88ajjwuaLeIFplB8JpyNy0A19E" ctx = context.WithValue(context.Background(), key, val) ) diff --git a/auth/jwt/transport.go b/auth/jwt/transport.go index 57b3aae..e7d19c1 100644 --- a/auth/jwt/transport.go +++ b/auth/jwt/transport.go @@ -26,7 +26,7 @@ return ctx } - return context.WithValue(ctx, JWTTokenContextKey, token) + return context.WithValue(ctx, JWTContextKey, token) } } @@ -34,7 +34,7 @@ // useful for clients. func ContextToHTTP() http.RequestFunc { return func(ctx context.Context, r *stdhttp.Request) context.Context { - token, ok := ctx.Value(JWTTokenContextKey).(string) + token, ok := ctx.Value(JWTContextKey).(string) if ok { r.Header.Add("Authorization", generateAuthHeaderFromToken(token)) } @@ -54,7 +54,7 @@ token, ok := extractTokenFromAuthHeader(authHeader[0]) if ok { - ctx = context.WithValue(ctx, JWTTokenContextKey, token) + ctx = context.WithValue(ctx, JWTContextKey, token) } return ctx @@ -65,7 +65,7 @@ // useful for clients. func ContextToGRPC() grpc.ClientRequestFunc { return func(ctx context.Context, md *metadata.MD) context.Context { - token, ok := ctx.Value(JWTTokenContextKey).(string) + token, ok := ctx.Value(JWTContextKey).(string) if ok { // capital "Key" is illegal in HTTP/2. (*md)["authorization"] = []string{generateAuthHeaderFromToken(token)} @@ -77,7 +77,7 @@ func extractTokenFromAuthHeader(val string) (token string, ok bool) { authHeaderParts := strings.Split(val, " ") - if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != bearer { + if len(authHeaderParts) != 2 || !strings.EqualFold(authHeaderParts[0], bearer) { return "", false } diff --git a/auth/jwt/transport_test.go b/auth/jwt/transport_test.go index 83d0f17..91037ee 100644 --- a/auth/jwt/transport_test.go +++ b/auth/jwt/transport_test.go @@ -15,7 +15,7 @@ // When the header doesn't exist ctx := reqFunc(context.Background(), &http.Request{}) - if ctx.Value(JWTTokenContextKey) != nil { + if ctx.Value(JWTContextKey) != nil { t.Error("Context shouldn't contain the encoded JWT") } @@ -24,7 +24,7 @@ header.Set("Authorization", "no expected auth header format value") ctx = reqFunc(context.Background(), &http.Request{Header: header}) - if ctx.Value(JWTTokenContextKey) != nil { + if ctx.Value(JWTContextKey) != nil { t.Error("Context shouldn't contain the encoded JWT") } @@ -32,7 +32,7 @@ header.Set("Authorization", generateAuthHeaderFromToken(signedKey)) ctx = reqFunc(context.Background(), &http.Request{Header: header}) - token := ctx.Value(JWTTokenContextKey).(string) + token := ctx.Value(JWTContextKey).(string) if token != signedKey { t.Errorf("Context doesn't contain the expected encoded token value; expected: %s, got: %s", signedKey, token) } @@ -41,7 +41,7 @@ func TestContextToHTTP(t *testing.T) { reqFunc := ContextToHTTP() - // No JWT Token is passed in the context + // No JWT is passed in the context ctx := context.Background() r := http.Request{} reqFunc(ctx, &r) @@ -51,8 +51,8 @@ t.Error("authorization key should not exist in metadata") } - // Correct JWT Token is passed in the context - ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey) + // Correct JWT is passed in the context + ctx = context.WithValue(context.Background(), JWTContextKey, signedKey) r = http.Request{Header: http.Header{}} reqFunc(ctx, &r) @@ -60,7 +60,7 @@ expected := generateAuthHeaderFromToken(signedKey) if token != expected { - t.Errorf("Authorization header does not contain the expected JWT token; expected %s, got %s", expected, token) + t.Errorf("Authorization header does not contain the expected JWT; expected %s, got %s", expected, token) } } @@ -70,36 +70,36 @@ // No Authorization header is passed ctx := reqFunc(context.Background(), md) - token := ctx.Value(JWTTokenContextKey) + token := ctx.Value(JWTContextKey) if token != nil { - t.Error("Context should not contain a JWT Token") + t.Error("Context should not contain a JWT") } // Invalid Authorization header is passed - md["authorization"] = []string{fmt.Sprintf("%s", signedKey)} + md["authorization"] = []string{signedKey} ctx = reqFunc(context.Background(), md) - token = ctx.Value(JWTTokenContextKey) + token = ctx.Value(JWTContextKey) if token != nil { - t.Error("Context should not contain a JWT Token") + t.Error("Context should not contain a JWT") } // Authorization header is correct md["authorization"] = []string{fmt.Sprintf("Bearer %s", signedKey)} ctx = reqFunc(context.Background(), md) - token, ok := ctx.Value(JWTTokenContextKey).(string) + token, ok := ctx.Value(JWTContextKey).(string) if !ok { - t.Fatal("JWT Token not passed to context correctly") + t.Fatal("JWT not passed to context correctly") } if token != signedKey { - t.Errorf("JWT tokens did not match: expecting %s got %s", signedKey, token) + t.Errorf("JWTs did not match: expecting %s got %s", signedKey, token) } } func TestContextToGRPC(t *testing.T) { reqFunc := ContextToGRPC() - // No JWT Token is passed in the context + // No JWT is passed in the context ctx := context.Background() md := metadata.MD{} reqFunc(ctx, &md) @@ -109,17 +109,17 @@ t.Error("authorization key should not exist in metadata") } - // Correct JWT Token is passed in the context - ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey) + // Correct JWT is passed in the context + ctx = context.WithValue(context.Background(), JWTContextKey, signedKey) md = metadata.MD{} reqFunc(ctx, &md) token, ok := md["authorization"] if !ok { - t.Fatal("JWT Token not passed to metadata correctly") + t.Fatal("JWT not passed to metadata correctly") } if token[0] != generateAuthHeaderFromToken(signedKey) { - t.Errorf("JWT tokens did not match: expecting %s got %s", signedKey, token[0]) + t.Errorf("JWTs did not match: expecting %s got %s", signedKey, token[0]) } } diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 35ace2c..0000000 --- a/circle.yml +++ /dev/null @@ -1,27 +0,0 @@ -machine: - pre: - - curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.0 - - sudo rm -rf /usr/local/go - - curl -sSL https://storage.googleapis.com/golang/go1.9.linux-amd64.tar.gz | sudo tar xz -C /usr/local - services: - - docker - -dependencies: - pre: - - sudo curl -L "https://github.com/docker/compose/releases/download/1.10.0/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose - - sudo chmod +x /usr/local/bin/docker-compose - - docker-compose -f docker-compose-integration.yml up -d --force-recreate - -test: - pre: - - mkdir -p /home/ubuntu/.go_workspace/src/github.com/go-kit - - mv /home/ubuntu/kit /home/ubuntu/.go_workspace/src/github.com/go-kit - - ln -s /home/ubuntu/.go_workspace/src/github.com/go-kit/kit /home/ubuntu/kit - - go get -t github.com/go-kit/kit/... - override: - - go test -v -race -tags integration github.com/go-kit/kit/...: - environment: - ETCD_ADDR: http://localhost:2379 - CONSUL_ADDR: localhost:8500 - ZK_ADDR: localhost:2181 - EUREKA_ADDR: http://localhost:8761/eureka diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..69cb760 --- /dev/null +++ b/codecov.yml @@ -0,0 +1 @@ +comment: false diff --git a/coveralls.bash b/coveralls.bash deleted file mode 100755 index cf8fee9..0000000 --- a/coveralls.bash +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -if ! type -P gover -then - echo gover missing: go get github.com/modocache/gover - exit 1 -fi - -if ! type -P goveralls -then - echo goveralls missing: go get github.com/mattn/goveralls - exit 1 -fi - -if [[ "$COVERALLS_TOKEN" == "" ]] -then - echo COVERALLS_TOKEN not set - exit 1 -fi - -go list ./... | grep -v '/examples/' | cut -d'/' -f 4- | while read d -do - cd $d - go test -covermode count -coverprofile coverage.coverprofile - cd - -done - -gover -goveralls -coverprofile gover.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN -find . -name '*.coverprofile' -delete - diff --git a/docker-compose-integration.yml b/docker-compose-integration.yml index 287d97d..9632d0f 100644 --- a/docker-compose-integration.yml +++ b/docker-compose-integration.yml @@ -1,19 +1,23 @@ version: '2' services: etcd: - image: quay.io/coreos/etcd + image: gcr.io/etcd-development/etcd:v3.5.0 ports: - "2379:2379" - command: /usr/local/bin/etcd -advertise-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 -listen-client-urls "http://0.0.0.0:2379,http://0.0.0.0:4001" + environment: + ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379 + ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379 + consul: - image: progrium/consul + image: consul:1.7 ports: - "8500:8500" - command: -server -bootstrap + zk: - image: zookeeper + image: zookeeper:3.5 ports: - "2181:2181" + eureka: image: springcloud/eureka environment: diff --git a/endpoint/endpoint.go b/endpoint/endpoint.go index 1b64f50..6e9da36 100644 --- a/endpoint/endpoint.go +++ b/endpoint/endpoint.go @@ -26,3 +26,15 @@ return outer(next) } } + +// Failer may be implemented by Go kit response types that contain business +// logic error details. If Failed returns a non-nil error, the Go kit transport +// layer may interpret this as a business logic error, and may encode it +// differently than a regular, successful response. +// +// It's not necessary for your response types to implement Failer, but it may +// help for more sophisticated use cases. The addsvc example shows how Failer +// should be used by a complete application. +type Failer interface { + Failed() error +} diff --git a/examples/README.md b/examples/README.md index 2891d78..1148c18 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,5 +1,3 @@ # Examples -For more information about these examples, - including a walkthrough of the stringsvc example, - see [gokit.io/examples](https://gokit.io/examples). +Examples have been relocated to a separate repository: https://github.com/go-kit/examples diff --git a/examples/addsvc/README.md b/examples/addsvc/README.md deleted file mode 100644 index 0808006..0000000 --- a/examples/addsvc/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# addsvc - -addsvc is an example microservice which takes full advantage of most of Go -kit's features, including both service- and transport-level middlewares, -speaking multiple transports simultaneously, distributed tracing, and rich -error definitions. The server binary is available in cmd/addsvc. The client -binary is available in cmd/addcli. - -Finally, the addtransport package provides both server and clients for each -supported transport. The client structs bake-in certain middlewares, in order to -demonstrate the _client library pattern_. But beware: client libraries are -generally a bad idea, because they easily lead to the - [distributed monolith antipattern](https://www.microservices.com/talks/dont-build-a-distributed-monolith/). -If you don't _know_ you need to use one in your organization, it's probably best -avoided: prefer moving that logic to consumers, and relying on - [contract testing](https://docs.pact.io/best_practices/contract_tests_not_functional_tests.html) -to detect incompatibilities. diff --git a/examples/addsvc/cmd/addcli/addcli.go b/examples/addsvc/cmd/addcli/addcli.go deleted file mode 100644 index fe24fc2..0000000 --- a/examples/addsvc/cmd/addcli/addcli.go +++ /dev/null @@ -1,198 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "os" - "strconv" - "text/tabwriter" - "time" - - "google.golang.org/grpc" - - "github.com/apache/thrift/lib/go/thrift" - lightstep "github.com/lightstep/lightstep-tracer-go" - stdopentracing "github.com/opentracing/opentracing-go" - zipkin "github.com/openzipkin/zipkin-go-opentracing" - "sourcegraph.com/sourcegraph/appdash" - appdashot "sourcegraph.com/sourcegraph/appdash/opentracing" - - "github.com/go-kit/kit/log" - - "github.com/go-kit/kit/examples/addsvc/pkg/addservice" - "github.com/go-kit/kit/examples/addsvc/pkg/addtransport" - addthrift "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc" -) - -func main() { - // The addcli presumes no service discovery system, and expects users to - // provide the direct address of an addsvc. This presumption is reflected in - // the addcli binary and the client packages: the -transport.addr flags - // and various client constructors both expect host:port strings. For an - // example service with a client built on top of a service discovery system, - // see profilesvc. - fs := flag.NewFlagSet("addcli", flag.ExitOnError) - var ( - httpAddr = fs.String("http-addr", "", "HTTP address of addsvc") - grpcAddr = fs.String("grpc-addr", "", "gRPC address of addsvc") - thriftAddr = fs.String("thrift-addr", "", "Thrift address of addsvc") - thriftProtocol = fs.String("thrift-protocol", "binary", "binary, compact, json, simplejson") - thriftBuffer = fs.Int("thrift-buffer", 0, "0 for unbuffered") - thriftFramed = fs.Bool("thrift-framed", false, "true to enable framing") - zipkinURL = fs.String("zipkin-url", "", "Enable Zipkin tracing via a collector URL e.g. http://localhost:9411/api/v1/spans") - lightstepToken = flag.String("lightstep-token", "", "Enable LightStep tracing via a LightStep access token") - appdashAddr = flag.String("appdash-addr", "", "Enable Appdash tracing via an Appdash server host:port") - method = fs.String("method", "sum", "sum, concat") - ) - fs.Usage = usageFor(fs, os.Args[0]+" [flags] ") - fs.Parse(os.Args[1:]) - if len(fs.Args()) != 2 { - fs.Usage() - os.Exit(1) - } - - // This is a demonstration client, which supports multiple tracers. - // Your clients will probably just use one tracer. - var tracer stdopentracing.Tracer - { - if *zipkinURL != "" { - collector, err := zipkin.NewHTTPCollector(*zipkinURL) - if err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) - } - defer collector.Close() - var ( - debug = false - hostPort = "localhost:80" - serviceName = "addsvc" - ) - recorder := zipkin.NewRecorder(collector, debug, hostPort, serviceName) - tracer, err = zipkin.NewTracer(recorder) - if err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) - } - } else if *lightstepToken != "" { - tracer = lightstep.NewTracer(lightstep.Options{ - AccessToken: *lightstepToken, - }) - defer lightstep.FlushLightStepTracer(tracer) - } else if *appdashAddr != "" { - tracer = appdashot.NewTracer(appdash.NewRemoteCollector(*appdashAddr)) - } else { - tracer = stdopentracing.GlobalTracer() // no-op - } - } - - // This is a demonstration client, which supports multiple transports. - // Your clients will probably just define and stick with 1 transport. - var ( - svc addservice.Service - err error - ) - if *httpAddr != "" { - svc, err = addtransport.NewHTTPClient(*httpAddr, tracer, log.NewNopLogger()) - } else if *grpcAddr != "" { - conn, err := grpc.Dial(*grpcAddr, grpc.WithInsecure(), grpc.WithTimeout(time.Second)) - if err != nil { - fmt.Fprintf(os.Stderr, "error: %v", err) - os.Exit(1) - } - defer conn.Close() - svc = addtransport.NewGRPCClient(conn, tracer, log.NewNopLogger()) - } else if *thriftAddr != "" { - // It's necessary to do all of this construction in the func main, - // because (among other reasons) we need to control the lifecycle of the - // Thrift transport, i.e. close it eventually. - var protocolFactory thrift.TProtocolFactory - switch *thriftProtocol { - case "compact": - protocolFactory = thrift.NewTCompactProtocolFactory() - case "simplejson": - protocolFactory = thrift.NewTSimpleJSONProtocolFactory() - case "json": - protocolFactory = thrift.NewTJSONProtocolFactory() - case "binary", "": - protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() - default: - fmt.Fprintf(os.Stderr, "error: invalid protocol %q\n", *thriftProtocol) - os.Exit(1) - } - var transportFactory thrift.TTransportFactory - if *thriftBuffer > 0 { - transportFactory = thrift.NewTBufferedTransportFactory(*thriftBuffer) - } else { - transportFactory = thrift.NewTTransportFactory() - } - if *thriftFramed { - transportFactory = thrift.NewTFramedTransportFactory(transportFactory) - } - transportSocket, err := thrift.NewTSocket(*thriftAddr) - if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } - transport, err := transportFactory.GetTransport(transportSocket) - if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } - if err := transport.Open(); err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } - defer transport.Close() - client := addthrift.NewAddServiceClientFactory(transport, protocolFactory) - svc = addtransport.NewThriftClient(client) - } else { - fmt.Fprintf(os.Stderr, "error: no remote address specified\n") - os.Exit(1) - } - if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } - - switch *method { - case "sum": - a, _ := strconv.ParseInt(fs.Args()[0], 10, 64) - b, _ := strconv.ParseInt(fs.Args()[1], 10, 64) - v, err := svc.Sum(context.Background(), int(a), int(b)) - if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } - fmt.Fprintf(os.Stdout, "%d + %d = %d\n", a, b, v) - - case "concat": - a := fs.Args()[0] - b := fs.Args()[1] - v, err := svc.Concat(context.Background(), a, b) - if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } - fmt.Fprintf(os.Stdout, "%q + %q = %q\n", a, b, v) - - default: - fmt.Fprintf(os.Stderr, "error: invalid method %q\n", method) - os.Exit(1) - } -} - -func usageFor(fs *flag.FlagSet, short string) func() { - return func() { - fmt.Fprintf(os.Stderr, "USAGE\n") - fmt.Fprintf(os.Stderr, " %s\n", short) - fmt.Fprintf(os.Stderr, "\n") - fmt.Fprintf(os.Stderr, "FLAGS\n") - w := tabwriter.NewWriter(os.Stderr, 0, 2, 2, ' ', 0) - fs.VisitAll(func(f *flag.Flag) { - fmt.Fprintf(w, "\t-%s %s\t%s\n", f.Name, f.DefValue, f.Usage) - }) - w.Flush() - fmt.Fprintf(os.Stderr, "\n") - } -} diff --git a/examples/addsvc/cmd/addsvc/addsvc.go b/examples/addsvc/cmd/addsvc/addsvc.go deleted file mode 100644 index b1886e2..0000000 --- a/examples/addsvc/cmd/addsvc/addsvc.go +++ /dev/null @@ -1,279 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "net" - "net/http" - "os" - "os/signal" - "syscall" - "text/tabwriter" - - "github.com/apache/thrift/lib/go/thrift" - lightstep "github.com/lightstep/lightstep-tracer-go" - "github.com/oklog/oklog/pkg/group" - stdopentracing "github.com/opentracing/opentracing-go" - zipkin "github.com/openzipkin/zipkin-go-opentracing" - stdprometheus "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - "google.golang.org/grpc" - "sourcegraph.com/sourcegraph/appdash" - appdashot "sourcegraph.com/sourcegraph/appdash/opentracing" - - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/metrics" - "github.com/go-kit/kit/metrics/prometheus" - - addpb "github.com/go-kit/kit/examples/addsvc/pb" - "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint" - "github.com/go-kit/kit/examples/addsvc/pkg/addservice" - "github.com/go-kit/kit/examples/addsvc/pkg/addtransport" - addthrift "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc" -) - -func main() { - // Define our flags. Your service probably won't need to bind listeners for - // *all* supported transports, or support both Zipkin and LightStep, and so - // on, but we do it here for demonstration purposes. - fs := flag.NewFlagSet("addsvc", flag.ExitOnError) - var ( - debugAddr = fs.String("debug.addr", ":8080", "Debug and metrics listen address") - httpAddr = fs.String("http-addr", ":8081", "HTTP listen address") - grpcAddr = fs.String("grpc-addr", ":8082", "gRPC listen address") - thriftAddr = fs.String("thrift-addr", ":8083", "Thrift listen address") - thriftProtocol = fs.String("thrift-protocol", "binary", "binary, compact, json, simplejson") - thriftBuffer = fs.Int("thrift-buffer", 0, "0 for unbuffered") - thriftFramed = fs.Bool("thrift-framed", false, "true to enable framing") - zipkinURL = fs.String("zipkin-url", "", "Enable Zipkin tracing via a collector URL e.g. http://localhost:9411/api/v1/spans") - lightstepToken = flag.String("lightstep-token", "", "Enable LightStep tracing via a LightStep access token") - appdashAddr = flag.String("appdash-addr", "", "Enable Appdash tracing via an Appdash server host:port") - ) - fs.Usage = usageFor(fs, os.Args[0]+" [flags]") - fs.Parse(os.Args[1:]) - - // Create a single logger, which we'll use and give to other components. - var logger log.Logger - { - logger = log.NewLogfmtLogger(os.Stderr) - logger = log.With(logger, "ts", log.DefaultTimestampUTC) - logger = log.With(logger, "caller", log.DefaultCaller) - } - - // Determine which tracer to use. We'll pass the tracer to all the - // components that use it, as a dependency. - var tracer stdopentracing.Tracer - { - if *zipkinURL != "" { - logger.Log("tracer", "Zipkin", "URL", *zipkinURL) - collector, err := zipkin.NewHTTPCollector(*zipkinURL) - if err != nil { - logger.Log("err", err) - os.Exit(1) - } - defer collector.Close() - var ( - debug = false - hostPort = "localhost:80" - serviceName = "addsvc" - ) - recorder := zipkin.NewRecorder(collector, debug, hostPort, serviceName) - tracer, err = zipkin.NewTracer(recorder) - if err != nil { - logger.Log("err", err) - os.Exit(1) - } - } else if *lightstepToken != "" { - logger.Log("tracer", "LightStep") // probably don't want to print out the token :) - tracer = lightstep.NewTracer(lightstep.Options{ - AccessToken: *lightstepToken, - }) - defer lightstep.FlushLightStepTracer(tracer) - } else if *appdashAddr != "" { - logger.Log("tracer", "Appdash", "addr", *appdashAddr) - tracer = appdashot.NewTracer(appdash.NewRemoteCollector(*appdashAddr)) - } else { - logger.Log("tracer", "none") - tracer = stdopentracing.GlobalTracer() // no-op - } - } - - // Create the (sparse) metrics we'll use in the service. They, too, are - // dependencies that we pass to components that use them. - var ints, chars metrics.Counter - { - // Business-level metrics. - ints = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ - Namespace: "example", - Subsystem: "addsvc", - Name: "integers_summed", - Help: "Total count of integers summed via the Sum method.", - }, []string{}) - chars = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ - Namespace: "example", - Subsystem: "addsvc", - Name: "characters_concatenated", - Help: "Total count of characters concatenated via the Concat method.", - }, []string{}) - } - var duration metrics.Histogram - { - // Endpoint-level metrics. - duration = prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ - Namespace: "example", - Subsystem: "addsvc", - Name: "request_duration_seconds", - Help: "Request duration in seconds.", - }, []string{"method", "success"}) - } - http.DefaultServeMux.Handle("/metrics", promhttp.Handler()) - - // Build the layers of the service "onion" from the inside out. First, the - // business logic service; then, the set of endpoints that wrap the service; - // and finally, a series of concrete transport adapters. The adapters, like - // the HTTP handler or the gRPC server, are the bridge between Go kit and - // the interfaces that the transports expect. Note that we're not binding - // them to ports or anything yet; we'll do that next. - var ( - service = addservice.New(logger, ints, chars) - endpoints = addendpoint.New(service, logger, duration, tracer) - httpHandler = addtransport.NewHTTPHandler(endpoints, tracer, logger) - grpcServer = addtransport.NewGRPCServer(endpoints, tracer, logger) - thriftServer = addtransport.NewThriftServer(endpoints) - ) - - // Now we're to the part of the func main where we want to start actually - // running things, like servers bound to listeners to receive connections. - // - // The method is the same for each component: add a new actor to the group - // struct, which is a combination of 2 anonymous functions: the first - // function actually runs the component, and the second function should - // interrupt the first function and cause it to return. It's in these - // functions that we actually bind the Go kit server/handler structs to the - // concrete transports and run them. - // - // Putting each component into its own block is mostly for aesthetics: it - // clearly demarcates the scope in which each listener/socket may be used. - var g group.Group - { - // The debug listener mounts the http.DefaultServeMux, and serves up - // stuff like the Prometheus metrics route, the Go debug and profiling - // routes, and so on. - debugListener, err := net.Listen("tcp", *debugAddr) - if err != nil { - logger.Log("transport", "debug/HTTP", "during", "Listen", "err", err) - os.Exit(1) - } - g.Add(func() error { - logger.Log("transport", "debug/HTTP", "addr", *debugAddr) - return http.Serve(debugListener, http.DefaultServeMux) - }, func(error) { - debugListener.Close() - }) - } - { - // The HTTP listener mounts the Go kit HTTP handler we created. - httpListener, err := net.Listen("tcp", *httpAddr) - if err != nil { - logger.Log("transport", "HTTP", "during", "Listen", "err", err) - os.Exit(1) - } - g.Add(func() error { - logger.Log("transport", "HTTP", "addr", *httpAddr) - return http.Serve(httpListener, httpHandler) - }, func(error) { - httpListener.Close() - }) - } - { - // The gRPC listener mounts the Go kit gRPC server we created. - grpcListener, err := net.Listen("tcp", *grpcAddr) - if err != nil { - logger.Log("transport", "gRPC", "during", "Listen", "err", err) - os.Exit(1) - } - g.Add(func() error { - logger.Log("transport", "gRPC", "addr", *grpcAddr) - baseServer := grpc.NewServer() - addpb.RegisterAddServer(baseServer, grpcServer) - return baseServer.Serve(grpcListener) - }, func(error) { - grpcListener.Close() - }) - } - { - // The Thrift socket mounts the Go kit Thrift server we created earlier. - // There's a lot of boilerplate involved here, related to configuring - // the protocol and transport; blame Thrift. - thriftSocket, err := thrift.NewTServerSocket(*thriftAddr) - if err != nil { - logger.Log("transport", "Thrift", "during", "Listen", "err", err) - os.Exit(1) - } - g.Add(func() error { - logger.Log("transport", "Thrift", "addr", *thriftAddr) - var protocolFactory thrift.TProtocolFactory - switch *thriftProtocol { - case "binary": - protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() - case "compact": - protocolFactory = thrift.NewTCompactProtocolFactory() - case "json": - protocolFactory = thrift.NewTJSONProtocolFactory() - case "simplejson": - protocolFactory = thrift.NewTSimpleJSONProtocolFactory() - default: - return fmt.Errorf("invalid Thrift protocol %q", *thriftProtocol) - } - var transportFactory thrift.TTransportFactory - if *thriftBuffer > 0 { - transportFactory = thrift.NewTBufferedTransportFactory(*thriftBuffer) - } else { - transportFactory = thrift.NewTTransportFactory() - } - if *thriftFramed { - transportFactory = thrift.NewTFramedTransportFactory(transportFactory) - } - return thrift.NewTSimpleServer4( - addthrift.NewAddServiceProcessor(thriftServer), - thriftSocket, - transportFactory, - protocolFactory, - ).Serve() - }, func(error) { - thriftSocket.Close() - }) - } - { - // This function just sits and waits for ctrl-C. - cancelInterrupt := make(chan struct{}) - g.Add(func() error { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - select { - case sig := <-c: - return fmt.Errorf("received signal %s", sig) - case <-cancelInterrupt: - return nil - } - }, func(error) { - close(cancelInterrupt) - }) - } - logger.Log("exit", g.Run()) -} - -func usageFor(fs *flag.FlagSet, short string) func() { - return func() { - fmt.Fprintf(os.Stderr, "USAGE\n") - fmt.Fprintf(os.Stderr, " %s\n", short) - fmt.Fprintf(os.Stderr, "\n") - fmt.Fprintf(os.Stderr, "FLAGS\n") - w := tabwriter.NewWriter(os.Stderr, 0, 2, 2, ' ', 0) - fs.VisitAll(func(f *flag.Flag) { - fmt.Fprintf(w, "\t-%s %s\t%s\n", f.Name, f.DefValue, f.Usage) - }) - w.Flush() - fmt.Fprintf(os.Stderr, "\n") - } -} diff --git a/examples/addsvc/cmd/addsvc/pact_test.go b/examples/addsvc/cmd/addsvc/pact_test.go deleted file mode 100644 index 2c709b5..0000000 --- a/examples/addsvc/cmd/addsvc/pact_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "os" - "strings" - "testing" - - "github.com/pact-foundation/pact-go/dsl" -) - -func TestPactStringsvcUppercase(t *testing.T) { - if os.Getenv("WRITE_PACTS") == "" { - t.Skip("skipping Pact contracts; set WRITE_PACTS environment variable to enable") - } - - pact := dsl.Pact{ - Port: 6666, - Consumer: "addsvc", - Provider: "stringsvc", - } - defer pact.Teardown() - - pact.AddInteraction(). - UponReceiving("stringsvc uppercase"). - WithRequest(dsl.Request{ - Headers: map[string]string{"Content-Type": "application/json; charset=utf-8"}, - Method: "POST", - Path: "/uppercase", - Body: `{"s":"foo"}`, - }). - WillRespondWith(dsl.Response{ - Status: 200, - Headers: map[string]string{"Content-Type": "application/json; charset=utf-8"}, - Body: `{"v":"FOO"}`, - }) - - if err := pact.Verify(func() error { - u := fmt.Sprintf("http://localhost:%d/uppercase", pact.Server.Port) - req, err := http.NewRequest("POST", u, strings.NewReader(`{"s":"foo"}`)) - if err != nil { - return err - } - req.Header.Set("Content-Type", "application/json; charset=utf-8") - if _, err = http.DefaultClient.Do(req); err != nil { - return err - } - return nil - }); err != nil { - t.Fatal(err) - } - - pact.WritePact() -} diff --git a/examples/addsvc/cmd/addsvc/wiring_test.go b/examples/addsvc/cmd/addsvc/wiring_test.go deleted file mode 100644 index ca64bac..0000000 --- a/examples/addsvc/cmd/addsvc/wiring_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "io/ioutil" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/opentracing/opentracing-go" - - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/metrics/discard" - - "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint" - "github.com/go-kit/kit/examples/addsvc/pkg/addservice" - "github.com/go-kit/kit/examples/addsvc/pkg/addtransport" -) - -func TestHTTP(t *testing.T) { - svc := addservice.New(log.NewNopLogger(), discard.NewCounter(), discard.NewCounter()) - eps := addendpoint.New(svc, log.NewNopLogger(), discard.NewHistogram(), opentracing.GlobalTracer()) - mux := addtransport.NewHTTPHandler(eps, opentracing.GlobalTracer(), log.NewNopLogger()) - srv := httptest.NewServer(mux) - defer srv.Close() - - for _, testcase := range []struct { - method, url, body, want string - }{ - {"GET", srv.URL + "/concat", `{"a":"1","b":"2"}`, `{"v":"12"}`}, - {"GET", srv.URL + "/sum", `{"a":1,"b":2}`, `{"v":3}`}, - } { - req, _ := http.NewRequest(testcase.method, testcase.url, strings.NewReader(testcase.body)) - resp, _ := http.DefaultClient.Do(req) - body, _ := ioutil.ReadAll(resp.Body) - if want, have := testcase.want, strings.TrimSpace(string(body)); want != have { - t.Errorf("%s %s %s: want %q, have %q", testcase.method, testcase.url, testcase.body, want, have) - } - } -} diff --git a/examples/addsvc/pb/addsvc.pb.go b/examples/addsvc/pb/addsvc.pb.go deleted file mode 100644 index 781b50b..0000000 --- a/examples/addsvc/pb/addsvc.pb.go +++ /dev/null @@ -1,270 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: addsvc.proto - -/* -Package pb is a generated protocol buffer package. - -It is generated from these files: - addsvc.proto - -It has these top-level messages: - SumRequest - SumReply - ConcatRequest - ConcatReply -*/ -package pb - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -import ( - context "golang.org/x/net/context" - grpc "google.golang.org/grpc" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -// The sum request contains two parameters. -type SumRequest struct { - A int64 `protobuf:"varint,1,opt,name=a" json:"a,omitempty"` - B int64 `protobuf:"varint,2,opt,name=b" json:"b,omitempty"` -} - -func (m *SumRequest) Reset() { *m = SumRequest{} } -func (m *SumRequest) String() string { return proto.CompactTextString(m) } -func (*SumRequest) ProtoMessage() {} -func (*SumRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } - -func (m *SumRequest) GetA() int64 { - if m != nil { - return m.A - } - return 0 -} - -func (m *SumRequest) GetB() int64 { - if m != nil { - return m.B - } - return 0 -} - -// The sum response contains the result of the calculation. -type SumReply struct { - V int64 `protobuf:"varint,1,opt,name=v" json:"v,omitempty"` - Err string `protobuf:"bytes,2,opt,name=err" json:"err,omitempty"` -} - -func (m *SumReply) Reset() { *m = SumReply{} } -func (m *SumReply) String() string { return proto.CompactTextString(m) } -func (*SumReply) ProtoMessage() {} -func (*SumReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } - -func (m *SumReply) GetV() int64 { - if m != nil { - return m.V - } - return 0 -} - -func (m *SumReply) GetErr() string { - if m != nil { - return m.Err - } - return "" -} - -// The Concat request contains two parameters. -type ConcatRequest struct { - A string `protobuf:"bytes,1,opt,name=a" json:"a,omitempty"` - B string `protobuf:"bytes,2,opt,name=b" json:"b,omitempty"` -} - -func (m *ConcatRequest) Reset() { *m = ConcatRequest{} } -func (m *ConcatRequest) String() string { return proto.CompactTextString(m) } -func (*ConcatRequest) ProtoMessage() {} -func (*ConcatRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } - -func (m *ConcatRequest) GetA() string { - if m != nil { - return m.A - } - return "" -} - -func (m *ConcatRequest) GetB() string { - if m != nil { - return m.B - } - return "" -} - -// The Concat response contains the result of the concatenation. -type ConcatReply struct { - V string `protobuf:"bytes,1,opt,name=v" json:"v,omitempty"` - Err string `protobuf:"bytes,2,opt,name=err" json:"err,omitempty"` -} - -func (m *ConcatReply) Reset() { *m = ConcatReply{} } -func (m *ConcatReply) String() string { return proto.CompactTextString(m) } -func (*ConcatReply) ProtoMessage() {} -func (*ConcatReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } - -func (m *ConcatReply) GetV() string { - if m != nil { - return m.V - } - return "" -} - -func (m *ConcatReply) GetErr() string { - if m != nil { - return m.Err - } - return "" -} - -func init() { - proto.RegisterType((*SumRequest)(nil), "pb.SumRequest") - proto.RegisterType((*SumReply)(nil), "pb.SumReply") - proto.RegisterType((*ConcatRequest)(nil), "pb.ConcatRequest") - proto.RegisterType((*ConcatReply)(nil), "pb.ConcatReply") -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// Client API for Add service - -type AddClient interface { - // Sums two integers. - Sum(ctx context.Context, in *SumRequest, opts ...grpc.CallOption) (*SumReply, error) - // Concatenates two strings - Concat(ctx context.Context, in *ConcatRequest, opts ...grpc.CallOption) (*ConcatReply, error) -} - -type addClient struct { - cc *grpc.ClientConn -} - -func NewAddClient(cc *grpc.ClientConn) AddClient { - return &addClient{cc} -} - -func (c *addClient) Sum(ctx context.Context, in *SumRequest, opts ...grpc.CallOption) (*SumReply, error) { - out := new(SumReply) - err := grpc.Invoke(ctx, "/pb.Add/Sum", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *addClient) Concat(ctx context.Context, in *ConcatRequest, opts ...grpc.CallOption) (*ConcatReply, error) { - out := new(ConcatReply) - err := grpc.Invoke(ctx, "/pb.Add/Concat", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for Add service - -type AddServer interface { - // Sums two integers. - Sum(context.Context, *SumRequest) (*SumReply, error) - // Concatenates two strings - Concat(context.Context, *ConcatRequest) (*ConcatReply, error) -} - -func RegisterAddServer(s *grpc.Server, srv AddServer) { - s.RegisterService(&_Add_serviceDesc, srv) -} - -func _Add_Sum_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SumRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(AddServer).Sum(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/pb.Add/Sum", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(AddServer).Sum(ctx, req.(*SumRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Add_Concat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ConcatRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(AddServer).Concat(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/pb.Add/Concat", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(AddServer).Concat(ctx, req.(*ConcatRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _Add_serviceDesc = grpc.ServiceDesc{ - ServiceName: "pb.Add", - HandlerType: (*AddServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Sum", - Handler: _Add_Sum_Handler, - }, - { - MethodName: "Concat", - Handler: _Add_Concat_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "addsvc.proto", -} - -func init() { proto.RegisterFile("addsvc.proto", fileDescriptor0) } - -var fileDescriptor0 = []byte{ - // 189 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x49, 0x4c, 0x49, 0x29, - 0x2e, 0x4b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2a, 0x48, 0x52, 0xd2, 0xe0, 0xe2, - 0x0a, 0x2e, 0xcd, 0x0d, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0xe2, 0xe1, 0x62, 0x4c, 0x94, - 0x60, 0x54, 0x60, 0xd4, 0x60, 0x0e, 0x62, 0x4c, 0x04, 0xf1, 0x92, 0x24, 0x98, 0x20, 0xbc, 0x24, - 0x25, 0x2d, 0x2e, 0x0e, 0xb0, 0xca, 0x82, 0x9c, 0x4a, 0x90, 0x4c, 0x19, 0x4c, 0x5d, 0x99, 0x90, - 0x00, 0x17, 0x73, 0x6a, 0x51, 0x11, 0x58, 0x25, 0x67, 0x10, 0x88, 0xa9, 0xa4, 0xcd, 0xc5, 0xeb, - 0x9c, 0x9f, 0x97, 0x9c, 0x58, 0x82, 0x61, 0x30, 0x27, 0x8a, 0xc1, 0x9c, 0x20, 0x83, 0x75, 0xb9, - 0xb8, 0x61, 0x8a, 0x51, 0xcc, 0xe6, 0xc4, 0x6a, 0xb6, 0x51, 0x0c, 0x17, 0xb3, 0x63, 0x4a, 0x8a, - 0x90, 0x2a, 0x17, 0x73, 0x70, 0x69, 0xae, 0x10, 0x9f, 0x5e, 0x41, 0x92, 0x1e, 0xc2, 0x07, 0x52, - 0x3c, 0x70, 0x7e, 0x41, 0x4e, 0xa5, 0x12, 0x83, 0x90, 0x1e, 0x17, 0x1b, 0xc4, 0x70, 0x21, 0x41, - 0x90, 0x0c, 0x8a, 0xab, 0xa4, 0xf8, 0x91, 0x85, 0xc0, 0xea, 0x93, 0xd8, 0xc0, 0x41, 0x63, 0x0c, - 0x08, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x37, 0x81, 0x99, 0x2a, 0x01, 0x00, 0x00, -} diff --git a/examples/addsvc/pb/addsvc.proto b/examples/addsvc/pb/addsvc.proto deleted file mode 100644 index cf61532..0000000 --- a/examples/addsvc/pb/addsvc.proto +++ /dev/null @@ -1,36 +0,0 @@ -syntax = "proto3"; - -package pb; - -// The Add service definition. -service Add { - // Sums two integers. - rpc Sum (SumRequest) returns (SumReply) {} - - // Concatenates two strings - rpc Concat (ConcatRequest) returns (ConcatReply) {} -} - -// The sum request contains two parameters. -message SumRequest { - int64 a = 1; - int64 b = 2; -} - -// The sum response contains the result of the calculation. -message SumReply { - int64 v = 1; - string err = 2; -} - -// The Concat request contains two parameters. -message ConcatRequest { - string a = 1; - string b = 2; -} - -// The Concat response contains the result of the concatenation. -message ConcatReply { - string v = 1; - string err = 2; -} diff --git a/examples/addsvc/pb/compile.sh b/examples/addsvc/pb/compile.sh deleted file mode 100755 index c026844..0000000 --- a/examples/addsvc/pb/compile.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env sh - -# Install proto3 from source -# brew install autoconf automake libtool -# git clone https://github.com/google/protobuf -# ./autogen.sh ; ./configure ; make ; make install -# -# Update protoc Go bindings via -# go get -u github.com/golang/protobuf/{proto,protoc-gen-go} -# -# See also -# https://github.com/grpc/grpc-go/tree/master/examples - -protoc addsvc.proto --go_out=plugins=grpc:. diff --git a/examples/addsvc/pkg/addendpoint/middleware.go b/examples/addsvc/pkg/addendpoint/middleware.go deleted file mode 100644 index c83047b..0000000 --- a/examples/addsvc/pkg/addendpoint/middleware.go +++ /dev/null @@ -1,43 +0,0 @@ -package addendpoint - -import ( - "context" - "fmt" - "time" - - "github.com/go-kit/kit/endpoint" - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/metrics" -) - -// InstrumentingMiddleware returns an endpoint middleware that records -// the duration of each invocation to the passed histogram. The middleware adds -// a single field: "success", which is "true" if no error is returned, and -// "false" otherwise. -func InstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware { - return func(next endpoint.Endpoint) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - - defer func(begin time.Time) { - duration.With("success", fmt.Sprint(err == nil)).Observe(time.Since(begin).Seconds()) - }(time.Now()) - return next(ctx, request) - - } - } -} - -// LoggingMiddleware returns an endpoint middleware that logs the -// duration of each invocation, and the resulting error, if any. -func LoggingMiddleware(logger log.Logger) endpoint.Middleware { - return func(next endpoint.Endpoint) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - - defer func(begin time.Time) { - logger.Log("transport_error", err, "took", time.Since(begin)) - }(time.Now()) - return next(ctx, request) - - } - } -} diff --git a/examples/addsvc/pkg/addendpoint/set.go b/examples/addsvc/pkg/addendpoint/set.go deleted file mode 100644 index 3a65b08..0000000 --- a/examples/addsvc/pkg/addendpoint/set.go +++ /dev/null @@ -1,128 +0,0 @@ -package addendpoint - -import ( - "context" - - rl "github.com/juju/ratelimit" - stdopentracing "github.com/opentracing/opentracing-go" - "github.com/sony/gobreaker" - - "github.com/go-kit/kit/circuitbreaker" - "github.com/go-kit/kit/endpoint" - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/metrics" - "github.com/go-kit/kit/ratelimit" - "github.com/go-kit/kit/tracing/opentracing" - - "github.com/go-kit/kit/examples/addsvc/pkg/addservice" -) - -// Set collects all of the endpoints that compose an add service. It's meant to -// be used as a helper struct, to collect all of the endpoints into a single -// parameter. -type Set struct { - SumEndpoint endpoint.Endpoint - ConcatEndpoint endpoint.Endpoint -} - -// New returns a Set that wraps the provided server, and wires in all of the -// expected endpoint middlewares via the various parameters. -func New(svc addservice.Service, logger log.Logger, duration metrics.Histogram, trace stdopentracing.Tracer) Set { - var sumEndpoint endpoint.Endpoint - { - sumEndpoint = MakeSumEndpoint(svc) - sumEndpoint = ratelimit.NewTokenBucketLimiter(rl.NewBucketWithRate(1, 1))(sumEndpoint) - sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(sumEndpoint) - sumEndpoint = opentracing.TraceServer(trace, "Sum")(sumEndpoint) - sumEndpoint = LoggingMiddleware(log.With(logger, "method", "Sum"))(sumEndpoint) - sumEndpoint = InstrumentingMiddleware(duration.With("method", "Sum"))(sumEndpoint) - } - var concatEndpoint endpoint.Endpoint - { - concatEndpoint = MakeConcatEndpoint(svc) - concatEndpoint = ratelimit.NewTokenBucketLimiter(rl.NewBucketWithRate(100, 100))(concatEndpoint) - concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(concatEndpoint) - concatEndpoint = opentracing.TraceServer(trace, "Concat")(concatEndpoint) - concatEndpoint = LoggingMiddleware(log.With(logger, "method", "Concat"))(concatEndpoint) - concatEndpoint = InstrumentingMiddleware(duration.With("method", "Concat"))(concatEndpoint) - } - return Set{ - SumEndpoint: sumEndpoint, - ConcatEndpoint: concatEndpoint, - } -} - -// Sum implements the service interface, so Set may be used as a service. -// This is primarily useful in the context of a client library. -func (s Set) Sum(ctx context.Context, a, b int) (int, error) { - resp, err := s.SumEndpoint(ctx, SumRequest{A: a, B: b}) - if err != nil { - return 0, err - } - response := resp.(SumResponse) - return response.V, response.Err -} - -// Concat implements the service interface, so Set may be used as a -// service. This is primarily useful in the context of a client library. -func (s Set) Concat(ctx context.Context, a, b string) (string, error) { - resp, err := s.ConcatEndpoint(ctx, ConcatRequest{A: a, B: b}) - if err != nil { - return "", err - } - response := resp.(ConcatResponse) - return response.V, response.Err -} - -// MakeSumEndpoint constructs a Sum endpoint wrapping the service. -func MakeSumEndpoint(s addservice.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(SumRequest) - v, err := s.Sum(ctx, req.A, req.B) - return SumResponse{V: v, Err: err}, nil - } -} - -// MakeConcatEndpoint constructs a Concat endpoint wrapping the service. -func MakeConcatEndpoint(s addservice.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(ConcatRequest) - v, err := s.Concat(ctx, req.A, req.B) - return ConcatResponse{V: v, Err: err}, nil - } -} - -// Failer is an interface that should be implemented by response types. -// Response encoders can check if responses are Failer, and if so if they've -// failed, and if so encode them using a separate write path based on the error. -type Failer interface { - Failed() error -} - -// SumRequest collects the request parameters for the Sum method. -type SumRequest struct { - A, B int -} - -// SumResponse collects the response values for the Sum method. -type SumResponse struct { - V int `json:"v"` - Err error `json:"-"` // should be intercepted by Failed/errorEncoder -} - -// Failed implements Failer. -func (r SumResponse) Failed() error { return r.Err } - -// ConcatRequest collects the request parameters for the Concat method. -type ConcatRequest struct { - A, B string -} - -// ConcatResponse collects the response values for the Concat method. -type ConcatResponse struct { - V string `json:"v"` - Err error `json:"-"` -} - -// Failed implements Failer. -func (r ConcatResponse) Failed() error { return r.Err } diff --git a/examples/addsvc/pkg/addservice/middleware.go b/examples/addsvc/pkg/addservice/middleware.go deleted file mode 100644 index 5a1d6ee..0000000 --- a/examples/addsvc/pkg/addservice/middleware.go +++ /dev/null @@ -1,69 +0,0 @@ -package addservice - -import ( - "context" - - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/metrics" -) - -// Middleware describes a service (as opposed to endpoint) middleware. -type Middleware func(Service) Service - -// LoggingMiddleware takes a logger as a dependency -// and returns a ServiceMiddleware. -func LoggingMiddleware(logger log.Logger) Middleware { - return func(next Service) Service { - return loggingMiddleware{logger, next} - } -} - -type loggingMiddleware struct { - logger log.Logger - next Service -} - -func (mw loggingMiddleware) Sum(ctx context.Context, a, b int) (v int, err error) { - defer func() { - mw.logger.Log("method", "Sum", "a", a, "b", b, "v", v, "err", err) - }() - return mw.next.Sum(ctx, a, b) -} - -func (mw loggingMiddleware) Concat(ctx context.Context, a, b string) (v string, err error) { - defer func() { - mw.logger.Log("method", "Concat", "a", a, "b", b, "v", v, "err", err) - }() - return mw.next.Concat(ctx, a, b) -} - -// InstrumentingMiddleware returns a service middleware that instruments -// the number of integers summed and characters concatenated over the lifetime of -// the service. -func InstrumentingMiddleware(ints, chars metrics.Counter) Middleware { - return func(next Service) Service { - return instrumentingMiddleware{ - ints: ints, - chars: chars, - next: next, - } - } -} - -type instrumentingMiddleware struct { - ints metrics.Counter - chars metrics.Counter - next Service -} - -func (mw instrumentingMiddleware) Sum(ctx context.Context, a, b int) (int, error) { - v, err := mw.next.Sum(ctx, a, b) - mw.ints.Add(float64(v)) - return v, err -} - -func (mw instrumentingMiddleware) Concat(ctx context.Context, a, b string) (string, error) { - v, err := mw.next.Concat(ctx, a, b) - mw.chars.Add(float64(len(v))) - return v, err -} diff --git a/examples/addsvc/pkg/addservice/service.go b/examples/addsvc/pkg/addservice/service.go deleted file mode 100644 index d884373..0000000 --- a/examples/addsvc/pkg/addservice/service.go +++ /dev/null @@ -1,71 +0,0 @@ -package addservice - -import ( - "context" - "errors" - - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/metrics" -) - -// Service describes a service that adds things together. -type Service interface { - Sum(ctx context.Context, a, b int) (int, error) - Concat(ctx context.Context, a, b string) (string, error) -} - -// New returns a basic Service with all of the expected middlewares wired in. -func New(logger log.Logger, ints, chars metrics.Counter) Service { - var svc Service - { - svc = NewBasicService() - svc = LoggingMiddleware(logger)(svc) - svc = InstrumentingMiddleware(ints, chars)(svc) - } - return svc -} - -var ( - // ErrTwoZeroes is an arbitrary business rule for the Add method. - ErrTwoZeroes = errors.New("can't sum two zeroes") - - // ErrIntOverflow protects the Add method. We've decided that this error - // indicates a misbehaving service and should count against e.g. circuit - // breakers. So, we return it directly in endpoints, to illustrate the - // difference. In a real service, this probably wouldn't be the case. - ErrIntOverflow = errors.New("integer overflow") - - // ErrMaxSizeExceeded protects the Concat method. - ErrMaxSizeExceeded = errors.New("result exceeds maximum size") -) - -// NewBasicService returns a naïve, stateless implementation of Service. -func NewBasicService() Service { - return basicService{} -} - -type basicService struct{} - -const ( - intMax = 1<<31 - 1 - intMin = -(intMax + 1) - maxLen = 10 -) - -func (s basicService) Sum(_ context.Context, a, b int) (int, error) { - if a == 0 && b == 0 { - return 0, ErrTwoZeroes - } - if (b > 0 && a > (intMax-b)) || (b < 0 && a < (intMin-b)) { - return 0, ErrIntOverflow - } - return a + b, nil -} - -// Concat implements Service. -func (s basicService) Concat(_ context.Context, a, b string) (string, error) { - if len(a)+len(b) > maxLen { - return "", ErrMaxSizeExceeded - } - return a + b, nil -} diff --git a/examples/addsvc/pkg/addtransport/grpc.go b/examples/addsvc/pkg/addtransport/grpc.go deleted file mode 100644 index ec05baa..0000000 --- a/examples/addsvc/pkg/addtransport/grpc.go +++ /dev/null @@ -1,210 +0,0 @@ -package addtransport - -import ( - "context" - "errors" - "time" - - "google.golang.org/grpc" - - jujuratelimit "github.com/juju/ratelimit" - stdopentracing "github.com/opentracing/opentracing-go" - "github.com/sony/gobreaker" - oldcontext "golang.org/x/net/context" - - "github.com/go-kit/kit/circuitbreaker" - "github.com/go-kit/kit/endpoint" - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/ratelimit" - "github.com/go-kit/kit/tracing/opentracing" - grpctransport "github.com/go-kit/kit/transport/grpc" - - "github.com/go-kit/kit/examples/addsvc/pb" - "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint" - "github.com/go-kit/kit/examples/addsvc/pkg/addservice" -) - -type grpcServer struct { - sum grpctransport.Handler - concat grpctransport.Handler -} - -// NewGRPCServer makes a set of endpoints available as a gRPC AddServer. -func NewGRPCServer(endpoints addendpoint.Set, tracer stdopentracing.Tracer, logger log.Logger) pb.AddServer { - options := []grpctransport.ServerOption{ - grpctransport.ServerErrorLogger(logger), - } - return &grpcServer{ - sum: grpctransport.NewServer( - endpoints.SumEndpoint, - decodeGRPCSumRequest, - encodeGRPCSumResponse, - append(options, grpctransport.ServerBefore(opentracing.GRPCToContext(tracer, "Sum", logger)))..., - ), - concat: grpctransport.NewServer( - endpoints.ConcatEndpoint, - decodeGRPCConcatRequest, - encodeGRPCConcatResponse, - append(options, grpctransport.ServerBefore(opentracing.GRPCToContext(tracer, "Concat", logger)))..., - ), - } -} - -func (s *grpcServer) Sum(ctx oldcontext.Context, req *pb.SumRequest) (*pb.SumReply, error) { - _, rep, err := s.sum.ServeGRPC(ctx, req) - if err != nil { - return nil, err - } - return rep.(*pb.SumReply), nil -} - -func (s *grpcServer) Concat(ctx oldcontext.Context, req *pb.ConcatRequest) (*pb.ConcatReply, error) { - _, rep, err := s.concat.ServeGRPC(ctx, req) - if err != nil { - return nil, err - } - return rep.(*pb.ConcatReply), nil -} - -// NewGRPCClient returns an AddService backed by a gRPC server at the other end -// of the conn. The caller is responsible for constructing the conn, and -// eventually closing the underlying transport. We bake-in certain middlewares, -// implementing the client library pattern. -func NewGRPCClient(conn *grpc.ClientConn, tracer stdopentracing.Tracer, logger log.Logger) addservice.Service { - // We construct a single ratelimiter middleware, to limit the total outgoing - // QPS from this client to all methods on the remote instance. We also - // construct per-endpoint circuitbreaker middlewares to demonstrate how - // that's done, although they could easily be combined into a single breaker - // for the entire remote instance, too. - limiter := ratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(100, 100)) - - // Each individual endpoint is an http/transport.Client (which implements - // endpoint.Endpoint) that gets wrapped with various middlewares. If you - // made your own client library, you'd do this work there, so your server - // could rely on a consistent set of client behavior. - var sumEndpoint endpoint.Endpoint - { - sumEndpoint = grpctransport.NewClient( - conn, - "pb.Add", - "Sum", - encodeGRPCSumRequest, - decodeGRPCSumResponse, - pb.SumReply{}, - grpctransport.ClientBefore(opentracing.ContextToGRPC(tracer, logger)), - ).Endpoint() - sumEndpoint = opentracing.TraceClient(tracer, "Sum")(sumEndpoint) - sumEndpoint = limiter(sumEndpoint) - sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{ - Name: "Sum", - Timeout: 30 * time.Second, - }))(sumEndpoint) - } - - // The Concat endpoint is the same thing, with slightly different - // middlewares to demonstrate how to specialize per-endpoint. - var concatEndpoint endpoint.Endpoint - { - concatEndpoint = grpctransport.NewClient( - conn, - "pb.Add", - "Concat", - encodeGRPCConcatRequest, - decodeGRPCConcatResponse, - pb.ConcatReply{}, - grpctransport.ClientBefore(opentracing.ContextToGRPC(tracer, logger)), - ).Endpoint() - concatEndpoint = opentracing.TraceClient(tracer, "Concat")(concatEndpoint) - concatEndpoint = limiter(concatEndpoint) - concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{ - Name: "Concat", - Timeout: 10 * time.Second, - }))(concatEndpoint) - } - - // Returning the endpoint.Set as a service.Service relies on the - // endpoint.Set implementing the Service methods. That's just a simple bit - // of glue code. - return addendpoint.Set{ - SumEndpoint: sumEndpoint, - ConcatEndpoint: concatEndpoint, - } -} - -// decodeGRPCSumRequest is a transport/grpc.DecodeRequestFunc that converts a -// gRPC sum request to a user-domain sum request. Primarily useful in a server. -func decodeGRPCSumRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { - req := grpcReq.(*pb.SumRequest) - return addendpoint.SumRequest{A: int(req.A), B: int(req.B)}, nil -} - -// decodeGRPCConcatRequest is a transport/grpc.DecodeRequestFunc that converts a -// gRPC concat request to a user-domain concat request. Primarily useful in a -// server. -func decodeGRPCConcatRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { - req := grpcReq.(*pb.ConcatRequest) - return addendpoint.ConcatRequest{A: req.A, B: req.B}, nil -} - -// decodeGRPCSumResponse is a transport/grpc.DecodeResponseFunc that converts a -// gRPC sum reply to a user-domain sum response. Primarily useful in a client. -func decodeGRPCSumResponse(_ context.Context, grpcReply interface{}) (interface{}, error) { - reply := grpcReply.(*pb.SumReply) - return addendpoint.SumResponse{V: int(reply.V), Err: str2err(reply.Err)}, nil -} - -// decodeGRPCConcatResponse is a transport/grpc.DecodeResponseFunc that converts -// a gRPC concat reply to a user-domain concat response. Primarily useful in a -// client. -func decodeGRPCConcatResponse(_ context.Context, grpcReply interface{}) (interface{}, error) { - reply := grpcReply.(*pb.ConcatReply) - return addendpoint.ConcatResponse{V: reply.V, Err: str2err(reply.Err)}, nil -} - -// encodeGRPCSumResponse is a transport/grpc.EncodeResponseFunc that converts a -// user-domain sum response to a gRPC sum reply. Primarily useful in a server. -func encodeGRPCSumResponse(_ context.Context, response interface{}) (interface{}, error) { - resp := response.(addendpoint.SumResponse) - return &pb.SumReply{V: int64(resp.V), Err: err2str(resp.Err)}, nil -} - -// encodeGRPCConcatResponse is a transport/grpc.EncodeResponseFunc that converts -// a user-domain concat response to a gRPC concat reply. Primarily useful in a -// server. -func encodeGRPCConcatResponse(_ context.Context, response interface{}) (interface{}, error) { - resp := response.(addendpoint.ConcatResponse) - return &pb.ConcatReply{V: resp.V, Err: err2str(resp.Err)}, nil -} - -// encodeGRPCSumRequest is a transport/grpc.EncodeRequestFunc that converts a -// user-domain sum request to a gRPC sum request. Primarily useful in a client. -func encodeGRPCSumRequest(_ context.Context, request interface{}) (interface{}, error) { - req := request.(addendpoint.SumRequest) - return &pb.SumRequest{A: int64(req.A), B: int64(req.B)}, nil -} - -// encodeGRPCConcatRequest is a transport/grpc.EncodeRequestFunc that converts a -// user-domain concat request to a gRPC concat request. Primarily useful in a -// client. -func encodeGRPCConcatRequest(_ context.Context, request interface{}) (interface{}, error) { - req := request.(addendpoint.ConcatRequest) - return &pb.ConcatRequest{A: req.A, B: req.B}, nil -} - -// These annoying helper functions are required to translate Go error types to -// and from strings, which is the type we use in our IDLs to represent errors. -// There is special casing to treat empty strings as nil errors. - -func str2err(s string) error { - if s == "" { - return nil - } - return errors.New(s) -} - -func err2str(err error) string { - if err == nil { - return "" - } - return err.Error() -} diff --git a/examples/addsvc/pkg/addtransport/http.go b/examples/addsvc/pkg/addtransport/http.go deleted file mode 100644 index ecdee92..0000000 --- a/examples/addsvc/pkg/addtransport/http.go +++ /dev/null @@ -1,219 +0,0 @@ -package addtransport - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "io/ioutil" - "net/http" - "net/url" - "strings" - "time" - - jujuratelimit "github.com/juju/ratelimit" - stdopentracing "github.com/opentracing/opentracing-go" - "github.com/sony/gobreaker" - - "github.com/go-kit/kit/circuitbreaker" - "github.com/go-kit/kit/endpoint" - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/ratelimit" - "github.com/go-kit/kit/tracing/opentracing" - httptransport "github.com/go-kit/kit/transport/http" - - "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint" - "github.com/go-kit/kit/examples/addsvc/pkg/addservice" -) - -// NewHTTPHandler returns an HTTP handler that makes a set of endpoints -// available on predefined paths. -func NewHTTPHandler(endpoints addendpoint.Set, tracer stdopentracing.Tracer, logger log.Logger) http.Handler { - options := []httptransport.ServerOption{ - httptransport.ServerErrorEncoder(errorEncoder), - httptransport.ServerErrorLogger(logger), - } - m := http.NewServeMux() - m.Handle("/sum", httptransport.NewServer( - endpoints.SumEndpoint, - decodeHTTPSumRequest, - encodeHTTPGenericResponse, - append(options, httptransport.ServerBefore(opentracing.HTTPToContext(tracer, "Sum", logger)))..., - )) - m.Handle("/concat", httptransport.NewServer( - endpoints.ConcatEndpoint, - decodeHTTPConcatRequest, - encodeHTTPGenericResponse, - append(options, httptransport.ServerBefore(opentracing.HTTPToContext(tracer, "Concat", logger)))..., - )) - return m -} - -// NewHTTPClient returns an AddService backed by an HTTP server living at the -// remote instance. We expect instance to come from a service discovery system, -// so likely of the form "host:port". We bake-in certain middlewares, -// implementing the client library pattern. -func NewHTTPClient(instance string, tracer stdopentracing.Tracer, logger log.Logger) (addservice.Service, error) { - // Quickly sanitize the instance string. - if !strings.HasPrefix(instance, "http") { - instance = "http://" + instance - } - u, err := url.Parse(instance) - if err != nil { - return nil, err - } - - // We construct a single ratelimiter middleware, to limit the total outgoing - // QPS from this client to all methods on the remote instance. We also - // construct per-endpoint circuitbreaker middlewares to demonstrate how - // that's done, although they could easily be combined into a single breaker - // for the entire remote instance, too. - limiter := ratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(100, 100)) - - // Each individual endpoint is an http/transport.Client (which implements - // endpoint.Endpoint) that gets wrapped with various middlewares. If you - // made your own client library, you'd do this work there, so your server - // could rely on a consistent set of client behavior. - var sumEndpoint endpoint.Endpoint - { - sumEndpoint = httptransport.NewClient( - "POST", - copyURL(u, "/sum"), - encodeHTTPGenericRequest, - decodeHTTPSumResponse, - httptransport.ClientBefore(opentracing.ContextToHTTP(tracer, logger)), - ).Endpoint() - sumEndpoint = opentracing.TraceClient(tracer, "Sum")(sumEndpoint) - sumEndpoint = limiter(sumEndpoint) - sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{ - Name: "Sum", - Timeout: 30 * time.Second, - }))(sumEndpoint) - } - - // The Concat endpoint is the same thing, with slightly different - // middlewares to demonstrate how to specialize per-endpoint. - var concatEndpoint endpoint.Endpoint - { - concatEndpoint = httptransport.NewClient( - "POST", - copyURL(u, "/concat"), - encodeHTTPGenericRequest, - decodeHTTPConcatResponse, - httptransport.ClientBefore(opentracing.ContextToHTTP(tracer, logger)), - ).Endpoint() - concatEndpoint = opentracing.TraceClient(tracer, "Concat")(concatEndpoint) - concatEndpoint = limiter(concatEndpoint) - concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{ - Name: "Concat", - Timeout: 10 * time.Second, - }))(concatEndpoint) - } - - // Returning the endpoint.Set as a service.Service relies on the - // endpoint.Set implementing the Service methods. That's just a simple bit - // of glue code. - return addendpoint.Set{ - SumEndpoint: sumEndpoint, - ConcatEndpoint: concatEndpoint, - }, nil -} - -func copyURL(base *url.URL, path string) *url.URL { - next := *base - next.Path = path - return &next -} - -func errorEncoder(_ context.Context, err error, w http.ResponseWriter) { - w.WriteHeader(err2code(err)) - json.NewEncoder(w).Encode(errorWrapper{Error: err.Error()}) -} - -func err2code(err error) int { - switch err { - case addservice.ErrTwoZeroes, addservice.ErrMaxSizeExceeded, addservice.ErrIntOverflow: - return http.StatusBadRequest - } - return http.StatusInternalServerError -} - -func errorDecoder(r *http.Response) error { - var w errorWrapper - if err := json.NewDecoder(r.Body).Decode(&w); err != nil { - return err - } - return errors.New(w.Error) -} - -type errorWrapper struct { - Error string `json:"error"` -} - -// decodeHTTPSumRequest is a transport/http.DecodeRequestFunc that decodes a -// JSON-encoded sum request from the HTTP request body. Primarily useful in a -// server. -func decodeHTTPSumRequest(_ context.Context, r *http.Request) (interface{}, error) { - var req addendpoint.SumRequest - err := json.NewDecoder(r.Body).Decode(&req) - return req, err -} - -// decodeHTTPConcatRequest is a transport/http.DecodeRequestFunc that decodes a -// JSON-encoded concat request from the HTTP request body. Primarily useful in a -// server. -func decodeHTTPConcatRequest(_ context.Context, r *http.Request) (interface{}, error) { - var req addendpoint.ConcatRequest - err := json.NewDecoder(r.Body).Decode(&req) - return req, err -} - -// decodeHTTPSumResponse is a transport/http.DecodeResponseFunc that decodes a -// JSON-encoded sum response from the HTTP response body. If the response has a -// non-200 status code, we will interpret that as an error and attempt to decode -// the specific error message from the response body. Primarily useful in a -// client. -func decodeHTTPSumResponse(_ context.Context, r *http.Response) (interface{}, error) { - if r.StatusCode != http.StatusOK { - return nil, errors.New(r.Status) - } - var resp addendpoint.SumResponse - err := json.NewDecoder(r.Body).Decode(&resp) - return resp, err -} - -// decodeHTTPConcatResponse is a transport/http.DecodeResponseFunc that decodes -// a JSON-encoded concat response from the HTTP response body. If the response -// has a non-200 status code, we will interpret that as an error and attempt to -// decode the specific error message from the response body. Primarily useful in -// a client. -func decodeHTTPConcatResponse(_ context.Context, r *http.Response) (interface{}, error) { - if r.StatusCode != http.StatusOK { - return nil, errors.New(r.Status) - } - var resp addendpoint.ConcatResponse - err := json.NewDecoder(r.Body).Decode(&resp) - return resp, err -} - -// encodeHTTPGenericRequest is a transport/http.EncodeRequestFunc that -// JSON-encodes any request to the request body. Primarily useful in a client. -func encodeHTTPGenericRequest(_ context.Context, r *http.Request, request interface{}) error { - var buf bytes.Buffer - if err := json.NewEncoder(&buf).Encode(request); err != nil { - return err - } - r.Body = ioutil.NopCloser(&buf) - return nil -} - -// encodeHTTPGenericResponse is a transport/http.EncodeResponseFunc that encodes -// the response as JSON to the response writer. Primarily useful in a server. -func encodeHTTPGenericResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { - if f, ok := response.(addendpoint.Failer); ok && f.Failed() != nil { - errorEncoder(ctx, f.Failed(), w) - return nil - } - w.Header().Set("Content-Type", "application/json; charset=utf-8") - return json.NewEncoder(w).Encode(response) -} diff --git a/examples/addsvc/pkg/addtransport/thrift.go b/examples/addsvc/pkg/addtransport/thrift.go deleted file mode 100644 index c6797ec..0000000 --- a/examples/addsvc/pkg/addtransport/thrift.go +++ /dev/null @@ -1,119 +0,0 @@ -package addtransport - -import ( - "context" - "time" - - jujuratelimit "github.com/juju/ratelimit" - "github.com/sony/gobreaker" - - "github.com/go-kit/kit/circuitbreaker" - "github.com/go-kit/kit/endpoint" - "github.com/go-kit/kit/ratelimit" - - "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint" - "github.com/go-kit/kit/examples/addsvc/pkg/addservice" - addthrift "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc" -) - -type thriftServer struct { - ctx context.Context - endpoints addendpoint.Set -} - -// NewThriftServer makes a set of endpoints available as a Thrift service. -func NewThriftServer(endpoints addendpoint.Set) addthrift.AddService { - return &thriftServer{ - endpoints: endpoints, - } -} - -func (s *thriftServer) Sum(ctx context.Context, a int64, b int64) (*addthrift.SumReply, error) { - request := addendpoint.SumRequest{A: int(a), B: int(b)} - response, err := s.endpoints.SumEndpoint(ctx, request) - if err != nil { - return nil, err - } - resp := response.(addendpoint.SumResponse) - return &addthrift.SumReply{Value: int64(resp.V), Err: err2str(resp.Err)}, nil -} - -func (s *thriftServer) Concat(ctx context.Context, a string, b string) (*addthrift.ConcatReply, error) { - request := addendpoint.ConcatRequest{A: a, B: b} - response, err := s.endpoints.ConcatEndpoint(ctx, request) - if err != nil { - return nil, err - } - resp := response.(addendpoint.ConcatResponse) - return &addthrift.ConcatReply{Value: resp.V, Err: err2str(resp.Err)}, nil -} - -// NewThriftClient returns an AddService backed by a Thrift server described by -// the provided client. The caller is responsible for constructing the client, -// and eventually closing the underlying transport. We bake-in certain middlewares, -// implementing the client library pattern. -func NewThriftClient(client *addthrift.AddServiceClient) addservice.Service { - // We construct a single ratelimiter middleware, to limit the total outgoing - // QPS from this client to all methods on the remote instance. We also - // construct per-endpoint circuitbreaker middlewares to demonstrate how - // that's done, although they could easily be combined into a single breaker - // for the entire remote instance, too. - limiter := ratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(100, 100)) - - // Each individual endpoint is an http/transport.Client (which implements - // endpoint.Endpoint) that gets wrapped with various middlewares. If you - // could rely on a consistent set of client behavior. - var sumEndpoint endpoint.Endpoint - { - sumEndpoint = MakeThriftSumEndpoint(client) - sumEndpoint = limiter(sumEndpoint) - sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{ - Name: "Sum", - Timeout: 30 * time.Second, - }))(sumEndpoint) - } - - // The Concat endpoint is the same thing, with slightly different - // middlewares to demonstrate how to specialize per-endpoint. - var concatEndpoint endpoint.Endpoint - { - concatEndpoint = MakeThriftConcatEndpoint(client) - concatEndpoint = limiter(concatEndpoint) - concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{ - Name: "Concat", - Timeout: 10 * time.Second, - }))(concatEndpoint) - } - - // Returning the endpoint.Set as a service.Service relies on the - // endpoint.Set implementing the Service methods. That's just a simple bit - // of glue code. - return addendpoint.Set{ - SumEndpoint: sumEndpoint, - ConcatEndpoint: concatEndpoint, - } -} - -// MakeThriftSumEndpoint returns an endpoint that invokes the passed Thrift client. -// Useful only in clients, and only until a proper transport/thrift.Client exists. -func MakeThriftSumEndpoint(client *addthrift.AddServiceClient) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(addendpoint.SumRequest) - reply, err := client.Sum(ctx, int64(req.A), int64(req.B)) - if err == addservice.ErrIntOverflow { - return nil, err // special case; see comment on ErrIntOverflow - } - return addendpoint.SumResponse{V: int(reply.Value), Err: err}, nil - } -} - -// MakeThriftConcatEndpoint returns an endpoint that invokes the passed Thrift -// client. Useful only in clients, and only until a proper -// transport/thrift.Client exists. -func MakeThriftConcatEndpoint(client *addthrift.AddServiceClient) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(addendpoint.ConcatRequest) - reply, err := client.Concat(ctx, req.A, req.B) - return addendpoint.ConcatResponse{V: reply.Value, Err: err}, nil - } -} diff --git a/examples/addsvc/thrift/addsvc.thrift b/examples/addsvc/thrift/addsvc.thrift deleted file mode 100644 index e67ce1b..0000000 --- a/examples/addsvc/thrift/addsvc.thrift +++ /dev/null @@ -1,14 +0,0 @@ -struct SumReply { - 1: i64 value - 2: string err -} - -struct ConcatReply { - 1: string value - 2: string err -} - -service AddService { - SumReply Sum(1: i64 a, 2: i64 b) - ConcatReply Concat(1: string a, 2: string b) -} diff --git a/examples/addsvc/thrift/compile.sh b/examples/addsvc/thrift/compile.sh deleted file mode 100755 index 2ecce5b..0000000 --- a/examples/addsvc/thrift/compile.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh - -# See also https://thrift.apache.org/tutorial/go - -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 diff --git a/examples/addsvc/thrift/gen-go/addsvc/GoUnusedProtection__.go b/examples/addsvc/thrift/gen-go/addsvc/GoUnusedProtection__.go deleted file mode 100644 index 88387e1..0000000 --- a/examples/addsvc/thrift/gen-go/addsvc/GoUnusedProtection__.go +++ /dev/null @@ -1,7 +0,0 @@ -// Autogenerated by Thrift Compiler (1.0.0-dev) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package addsvc - -var GoUnusedProtection__ int; - diff --git a/examples/addsvc/thrift/gen-go/addsvc/add_service-remote/add_service-remote.go b/examples/addsvc/thrift/gen-go/addsvc/add_service-remote/add_service-remote.go deleted file mode 100755 index 2063763..0000000 --- a/examples/addsvc/thrift/gen-go/addsvc/add_service-remote/add_service-remote.go +++ /dev/null @@ -1,159 +0,0 @@ -// Autogenerated by Thrift Compiler (1.0.0-dev) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package main - -import ( - "context" - "flag" - "fmt" - "math" - "net" - "net/url" - "os" - "strconv" - "strings" - "github.com/apache/thrift/lib/go/thrift" - "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc" -) - - -func Usage() { - fmt.Fprintln(os.Stderr, "Usage of ", os.Args[0], " [-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:") - flag.PrintDefaults() - fmt.Fprintln(os.Stderr, "\nFunctions:") - fmt.Fprintln(os.Stderr, " SumReply Sum(i64 a, i64 b)") - fmt.Fprintln(os.Stderr, " ConcatReply Concat(string a, string b)") - fmt.Fprintln(os.Stderr) - os.Exit(0) -} - -func main() { - flag.Usage = Usage - var host string - var port int - var protocol string - var urlString string - var framed bool - var useHttp bool - var parsedUrl url.URL - var trans thrift.TTransport - _ = strconv.Atoi - _ = math.Abs - flag.Usage = Usage - flag.StringVar(&host, "h", "localhost", "Specify host and port") - flag.IntVar(&port, "p", 9090, "Specify port") - flag.StringVar(&protocol, "P", "binary", "Specify the protocol (binary, compact, simplejson, json)") - flag.StringVar(&urlString, "u", "", "Specify the url") - flag.BoolVar(&framed, "framed", false, "Use framed transport") - flag.BoolVar(&useHttp, "http", false, "Use http") - flag.Parse() - - if len(urlString) > 0 { - parsedUrl, err := url.Parse(urlString) - if err != nil { - fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) - flag.Usage() - } - host = parsedUrl.Host - useHttp = len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == "http" - } else if useHttp { - _, err := url.Parse(fmt.Sprint("http://", host, ":", port)) - if err != nil { - fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) - flag.Usage() - } - } - - cmd := flag.Arg(0) - var err error - if useHttp { - trans, err = thrift.NewTHttpClient(parsedUrl.String()) - } else { - portStr := fmt.Sprint(port) - if strings.Contains(host, ":") { - host, portStr, err = net.SplitHostPort(host) - if err != nil { - fmt.Fprintln(os.Stderr, "error with host:", err) - os.Exit(1) - } - } - trans, err = thrift.NewTSocket(net.JoinHostPort(host, portStr)) - if err != nil { - fmt.Fprintln(os.Stderr, "error resolving address:", err) - os.Exit(1) - } - if framed { - trans = thrift.NewTFramedTransport(trans) - } - } - if err != nil { - fmt.Fprintln(os.Stderr, "Error creating transport", err) - os.Exit(1) - } - defer trans.Close() - var protocolFactory thrift.TProtocolFactory - switch protocol { - case "compact": - protocolFactory = thrift.NewTCompactProtocolFactory() - break - case "simplejson": - protocolFactory = thrift.NewTSimpleJSONProtocolFactory() - break - case "json": - protocolFactory = thrift.NewTJSONProtocolFactory() - break - case "binary", "": - protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() - break - default: - fmt.Fprintln(os.Stderr, "Invalid protocol specified: ", protocol) - Usage() - os.Exit(1) - } - client := addsvc.NewAddServiceClientFactory(trans, protocolFactory) - if err := trans.Open(); err != nil { - fmt.Fprintln(os.Stderr, "Error opening socket to ", host, ":", port, " ", err) - os.Exit(1) - } - - switch cmd { - case "Sum": - if flag.NArg() - 1 != 2 { - fmt.Fprintln(os.Stderr, "Sum requires 2 args") - flag.Usage() - } - argvalue0, err6 := (strconv.ParseInt(flag.Arg(1), 10, 64)) - if err6 != nil { - Usage() - return - } - value0 := argvalue0 - argvalue1, err7 := (strconv.ParseInt(flag.Arg(2), 10, 64)) - if err7 != nil { - Usage() - return - } - value1 := argvalue1 - fmt.Print(client.Sum(context.Background(), value0, value1)) - fmt.Print("\n") - break - case "Concat": - if flag.NArg() - 1 != 2 { - fmt.Fprintln(os.Stderr, "Concat requires 2 args") - flag.Usage() - } - argvalue0 := flag.Arg(1) - value0 := argvalue0 - argvalue1 := flag.Arg(2) - value1 := argvalue1 - fmt.Print(client.Concat(context.Background(), value0, value1)) - fmt.Print("\n") - break - case "": - Usage() - break - default: - fmt.Fprintln(os.Stderr, "Invalid function ", cmd) - } -} diff --git a/examples/addsvc/thrift/gen-go/addsvc/addsvc-consts.go b/examples/addsvc/thrift/gen-go/addsvc/addsvc-consts.go deleted file mode 100644 index 3222f51..0000000 --- a/examples/addsvc/thrift/gen-go/addsvc/addsvc-consts.go +++ /dev/null @@ -1,24 +0,0 @@ -// Autogenerated by Thrift Compiler (1.0.0-dev) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package addsvc - -import ( - "bytes" - "reflect" - "context" - "fmt" - "github.com/apache/thrift/lib/go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = context.Background -var _ = reflect.DeepEqual -var _ = bytes.Equal - - -func init() { -} - diff --git a/examples/addsvc/thrift/gen-go/addsvc/addsvc.go b/examples/addsvc/thrift/gen-go/addsvc/addsvc.go deleted file mode 100644 index 4f695b9..0000000 --- a/examples/addsvc/thrift/gen-go/addsvc/addsvc.go +++ /dev/null @@ -1,1065 +0,0 @@ -// Autogenerated by Thrift Compiler (1.0.0-dev) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package addsvc - -import ( - "bytes" - "reflect" - "context" - "fmt" - "github.com/apache/thrift/lib/go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = context.Background -var _ = reflect.DeepEqual -var _ = bytes.Equal - -// Attributes: -// - Value -// - Err -type SumReply struct { - Value int64 `thrift:"value,1" db:"value" json:"value"` - Err string `thrift:"err,2" db:"err" json:"err"` -} - -func NewSumReply() *SumReply { - return &SumReply{} -} - - -func (p *SumReply) GetValue() int64 { - return p.Value -} - -func (p *SumReply) GetErr() string { - return p.Err -} -func (p *SumReply) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { break; } - switch fieldId { - case 1: - if fieldTypeId == thrift.I64 { - if err := p.ReadField1(iprot); err != nil { - return err - } - } else { - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - case 2: - if fieldTypeId == thrift.STRING { - if err := p.ReadField2(iprot); err != nil { - return err - } - } else { - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *SumReply) ReadField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 1: ", err) -} else { - p.Value = v -} - return nil -} - -func (p *SumReply) ReadField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 2: ", err) -} else { - p.Err = v -} - return nil -} - -func (p *SumReply) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("SumReply"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } - if p != nil { - if err := p.writeField1(oprot); err != nil { return err } - if err := p.writeField2(oprot); err != nil { return err } - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) } - return nil -} - -func (p *SumReply) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("value", thrift.I64, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:value: ", p), err) } - if err := oprot.WriteI64(int64(p.Value)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.value (1) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:value: ", p), err) } - return err -} - -func (p *SumReply) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("err", thrift.STRING, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:err: ", p), err) } - if err := oprot.WriteString(string(p.Err)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.err (2) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:err: ", p), err) } - return err -} - -func (p *SumReply) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("SumReply(%+v)", *p) -} - -// Attributes: -// - Value -// - Err -type ConcatReply struct { - Value string `thrift:"value,1" db:"value" json:"value"` - Err string `thrift:"err,2" db:"err" json:"err"` -} - -func NewConcatReply() *ConcatReply { - return &ConcatReply{} -} - - -func (p *ConcatReply) GetValue() string { - return p.Value -} - -func (p *ConcatReply) GetErr() string { - return p.Err -} -func (p *ConcatReply) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { break; } - switch fieldId { - case 1: - if fieldTypeId == thrift.STRING { - if err := p.ReadField1(iprot); err != nil { - return err - } - } else { - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - case 2: - if fieldTypeId == thrift.STRING { - if err := p.ReadField2(iprot); err != nil { - return err - } - } else { - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *ConcatReply) ReadField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 1: ", err) -} else { - p.Value = v -} - return nil -} - -func (p *ConcatReply) ReadField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 2: ", err) -} else { - p.Err = v -} - return nil -} - -func (p *ConcatReply) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("ConcatReply"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } - if p != nil { - if err := p.writeField1(oprot); err != nil { return err } - if err := p.writeField2(oprot); err != nil { return err } - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) } - return nil -} - -func (p *ConcatReply) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("value", thrift.STRING, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:value: ", p), err) } - if err := oprot.WriteString(string(p.Value)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.value (1) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:value: ", p), err) } - return err -} - -func (p *ConcatReply) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("err", thrift.STRING, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:err: ", p), err) } - if err := oprot.WriteString(string(p.Err)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.err (2) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:err: ", p), err) } - return err -} - -func (p *ConcatReply) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("ConcatReply(%+v)", *p) -} - -type AddService interface { - // Parameters: - // - A - // - B - Sum(ctx context.Context, a int64, b int64) (r *SumReply, err error) - // Parameters: - // - A - // - B - Concat(ctx context.Context, a string, b string) (r *ConcatReply, err error) -} - -type AddServiceClient struct { - Transport thrift.TTransport - ProtocolFactory thrift.TProtocolFactory - InputProtocol thrift.TProtocol - OutputProtocol thrift.TProtocol - SeqId int32 -} - -func NewAddServiceClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AddServiceClient { - return &AddServiceClient{Transport: t, - ProtocolFactory: f, - InputProtocol: f.GetProtocol(t), - OutputProtocol: f.GetProtocol(t), - SeqId: 0, - } -} - -func NewAddServiceClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AddServiceClient { - return &AddServiceClient{Transport: t, - ProtocolFactory: nil, - InputProtocol: iprot, - OutputProtocol: oprot, - SeqId: 0, - } -} - -// Parameters: -// - A -// - B -func (p *AddServiceClient) Sum(ctx context.Context, a int64, b int64) (r *SumReply, err error) { - if err = p.sendSum(a, b); err != nil { return } - return p.recvSum() -} - -func (p *AddServiceClient) sendSum(a int64, b int64)(err error) { - oprot := p.OutputProtocol - if oprot == nil { - oprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.OutputProtocol = oprot - } - p.SeqId++ - if err = oprot.WriteMessageBegin("Sum", thrift.CALL, p.SeqId); err != nil { - return - } - args := AddServiceSumArgs{ - A : a, - B : b, - } - if err = args.Write(oprot); err != nil { - return - } - if err = oprot.WriteMessageEnd(); err != nil { - return - } - return oprot.Flush() -} - - -func (p *AddServiceClient) recvSum() (value *SumReply, err error) { - iprot := p.InputProtocol - if iprot == nil { - iprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.InputProtocol = iprot - } - method, mTypeId, seqId, err := iprot.ReadMessageBegin() - if err != nil { - return - } - if method != "Sum" { - err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "Sum failed: wrong method name") - return - } - if p.SeqId != seqId { - err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "Sum failed: out of sequence response") - return - } - if mTypeId == thrift.EXCEPTION { - error0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") - var error1 error - error1, err = error0.Read(iprot) - if err != nil { - return - } - if err = iprot.ReadMessageEnd(); err != nil { - return - } - err = error1 - return - } - if mTypeId != thrift.REPLY { - err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "Sum failed: invalid message type") - return - } - result := AddServiceSumResult{} - if err = result.Read(iprot); err != nil { - return - } - if err = iprot.ReadMessageEnd(); err != nil { - return - } - value = result.GetSuccess() - return -} - -// Parameters: -// - A -// - B -func (p *AddServiceClient) Concat(ctx context.Context, a string, b string) (r *ConcatReply, err error) { - if err = p.sendConcat(a, b); err != nil { return } - return p.recvConcat() -} - -func (p *AddServiceClient) sendConcat(a string, b string)(err error) { - oprot := p.OutputProtocol - if oprot == nil { - oprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.OutputProtocol = oprot - } - p.SeqId++ - if err = oprot.WriteMessageBegin("Concat", thrift.CALL, p.SeqId); err != nil { - return - } - args := AddServiceConcatArgs{ - A : a, - B : b, - } - if err = args.Write(oprot); err != nil { - return - } - if err = oprot.WriteMessageEnd(); err != nil { - return - } - return oprot.Flush() -} - - -func (p *AddServiceClient) recvConcat() (value *ConcatReply, err error) { - iprot := p.InputProtocol - if iprot == nil { - iprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.InputProtocol = iprot - } - method, mTypeId, seqId, err := iprot.ReadMessageBegin() - if err != nil { - return - } - if method != "Concat" { - err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "Concat failed: wrong method name") - return - } - if p.SeqId != seqId { - err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "Concat failed: out of sequence response") - return - } - if mTypeId == thrift.EXCEPTION { - error2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") - var error3 error - error3, err = error2.Read(iprot) - if err != nil { - return - } - if err = iprot.ReadMessageEnd(); err != nil { - return - } - err = error3 - return - } - if mTypeId != thrift.REPLY { - err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "Concat failed: invalid message type") - return - } - result := AddServiceConcatResult{} - if err = result.Read(iprot); err != nil { - return - } - if err = iprot.ReadMessageEnd(); err != nil { - return - } - value = result.GetSuccess() - return -} - - -type AddServiceProcessor struct { - processorMap map[string]thrift.TProcessorFunction - handler AddService -} - -func (p *AddServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { - p.processorMap[key] = processor -} - -func (p *AddServiceProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { - processor, ok = p.processorMap[key] - return processor, ok -} - -func (p *AddServiceProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { - return p.processorMap -} - -func NewAddServiceProcessor(handler AddService) *AddServiceProcessor { - - self4 := &AddServiceProcessor{handler:handler, processorMap:make(map[string]thrift.TProcessorFunction)} - self4.processorMap["Sum"] = &addServiceProcessorSum{handler:handler} - self4.processorMap["Concat"] = &addServiceProcessorConcat{handler:handler} -return self4 -} - -func (p *AddServiceProcessor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - name, _, seqId, err := iprot.ReadMessageBegin() - if err != nil { return false, err } - if processor, ok := p.GetProcessorFunction(name); ok { - return processor.Process(ctx, seqId, iprot, oprot) - } - iprot.Skip(thrift.STRUCT) - iprot.ReadMessageEnd() - x5 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function " + name) - oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) - x5.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return false, x5 - -} - -type addServiceProcessorSum struct { - handler AddService -} - -func (p *addServiceProcessorSum) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - args := AddServiceSumArgs{} - if err = args.Read(iprot); err != nil { - iprot.ReadMessageEnd() - x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) - oprot.WriteMessageBegin("Sum", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return false, err - } - - iprot.ReadMessageEnd() - result := AddServiceSumResult{} -var retval *SumReply - var err2 error - if retval, err2 = p.handler.Sum(ctx, args.A, args.B); err2 != nil { - x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Sum: " + err2.Error()) - oprot.WriteMessageBegin("Sum", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return true, err2 - } else { - result.Success = retval -} - if err2 = oprot.WriteMessageBegin("Sum", thrift.REPLY, seqId); err2 != nil { - err = err2 - } - if err2 = result.Write(oprot); err == nil && err2 != nil { - err = err2 - } - if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { - err = err2 - } - if err2 = oprot.Flush(); err == nil && err2 != nil { - err = err2 - } - if err != nil { - return - } - return true, err -} - -type addServiceProcessorConcat struct { - handler AddService -} - -func (p *addServiceProcessorConcat) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - args := AddServiceConcatArgs{} - if err = args.Read(iprot); err != nil { - iprot.ReadMessageEnd() - x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) - oprot.WriteMessageBegin("Concat", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return false, err - } - - iprot.ReadMessageEnd() - result := AddServiceConcatResult{} -var retval *ConcatReply - var err2 error - if retval, err2 = p.handler.Concat(ctx, args.A, args.B); err2 != nil { - x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Concat: " + err2.Error()) - oprot.WriteMessageBegin("Concat", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return true, err2 - } else { - result.Success = retval -} - if err2 = oprot.WriteMessageBegin("Concat", thrift.REPLY, seqId); err2 != nil { - err = err2 - } - if err2 = result.Write(oprot); err == nil && err2 != nil { - err = err2 - } - if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { - err = err2 - } - if err2 = oprot.Flush(); err == nil && err2 != nil { - err = err2 - } - if err != nil { - return - } - return true, err -} - - -// HELPER FUNCTIONS AND STRUCTURES - -// Attributes: -// - A -// - B -type AddServiceSumArgs struct { - A int64 `thrift:"a,1" db:"a" json:"a"` - B int64 `thrift:"b,2" db:"b" json:"b"` -} - -func NewAddServiceSumArgs() *AddServiceSumArgs { - return &AddServiceSumArgs{} -} - - -func (p *AddServiceSumArgs) GetA() int64 { - return p.A -} - -func (p *AddServiceSumArgs) GetB() int64 { - return p.B -} -func (p *AddServiceSumArgs) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { break; } - switch fieldId { - case 1: - if fieldTypeId == thrift.I64 { - if err := p.ReadField1(iprot); err != nil { - return err - } - } else { - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - case 2: - if fieldTypeId == thrift.I64 { - if err := p.ReadField2(iprot); err != nil { - return err - } - } else { - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *AddServiceSumArgs) ReadField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 1: ", err) -} else { - p.A = v -} - return nil -} - -func (p *AddServiceSumArgs) ReadField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 2: ", err) -} else { - p.B = v -} - return nil -} - -func (p *AddServiceSumArgs) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Sum_args"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } - if p != nil { - if err := p.writeField1(oprot); err != nil { return err } - if err := p.writeField2(oprot); err != nil { return err } - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) } - return nil -} - -func (p *AddServiceSumArgs) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("a", thrift.I64, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:a: ", p), err) } - if err := oprot.WriteI64(int64(p.A)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.a (1) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:a: ", p), err) } - return err -} - -func (p *AddServiceSumArgs) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("b", thrift.I64, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:b: ", p), err) } - if err := oprot.WriteI64(int64(p.B)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.b (2) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:b: ", p), err) } - return err -} - -func (p *AddServiceSumArgs) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("AddServiceSumArgs(%+v)", *p) -} - -// Attributes: -// - Success -type AddServiceSumResult struct { - Success *SumReply `thrift:"success,0" db:"success" json:"success,omitempty"` -} - -func NewAddServiceSumResult() *AddServiceSumResult { - return &AddServiceSumResult{} -} - -var AddServiceSumResult_Success_DEFAULT *SumReply -func (p *AddServiceSumResult) GetSuccess() *SumReply { - if !p.IsSetSuccess() { - return AddServiceSumResult_Success_DEFAULT - } -return p.Success -} -func (p *AddServiceSumResult) IsSetSuccess() bool { - return p.Success != nil -} - -func (p *AddServiceSumResult) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { break; } - switch fieldId { - case 0: - if fieldTypeId == thrift.STRUCT { - if err := p.ReadField0(iprot); err != nil { - return err - } - } else { - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *AddServiceSumResult) ReadField0(iprot thrift.TProtocol) error { - p.Success = &SumReply{} - if err := p.Success.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) - } - return nil -} - -func (p *AddServiceSumResult) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Sum_result"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } - if p != nil { - if err := p.writeField0(oprot); err != nil { return err } - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) } - return nil -} - -func (p *AddServiceSumResult) writeField0(oprot thrift.TProtocol) (err error) { - if p.IsSetSuccess() { - if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) } - if err := p.Success.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) } - } - return err -} - -func (p *AddServiceSumResult) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("AddServiceSumResult(%+v)", *p) -} - -// Attributes: -// - A -// - B -type AddServiceConcatArgs struct { - A string `thrift:"a,1" db:"a" json:"a"` - B string `thrift:"b,2" db:"b" json:"b"` -} - -func NewAddServiceConcatArgs() *AddServiceConcatArgs { - return &AddServiceConcatArgs{} -} - - -func (p *AddServiceConcatArgs) GetA() string { - return p.A -} - -func (p *AddServiceConcatArgs) GetB() string { - return p.B -} -func (p *AddServiceConcatArgs) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { break; } - switch fieldId { - case 1: - if fieldTypeId == thrift.STRING { - if err := p.ReadField1(iprot); err != nil { - return err - } - } else { - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - case 2: - if fieldTypeId == thrift.STRING { - if err := p.ReadField2(iprot); err != nil { - return err - } - } else { - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *AddServiceConcatArgs) ReadField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 1: ", err) -} else { - p.A = v -} - return nil -} - -func (p *AddServiceConcatArgs) ReadField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 2: ", err) -} else { - p.B = v -} - return nil -} - -func (p *AddServiceConcatArgs) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Concat_args"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } - if p != nil { - if err := p.writeField1(oprot); err != nil { return err } - if err := p.writeField2(oprot); err != nil { return err } - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) } - return nil -} - -func (p *AddServiceConcatArgs) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("a", thrift.STRING, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:a: ", p), err) } - if err := oprot.WriteString(string(p.A)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.a (1) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:a: ", p), err) } - return err -} - -func (p *AddServiceConcatArgs) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("b", thrift.STRING, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:b: ", p), err) } - if err := oprot.WriteString(string(p.B)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.b (2) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:b: ", p), err) } - return err -} - -func (p *AddServiceConcatArgs) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("AddServiceConcatArgs(%+v)", *p) -} - -// Attributes: -// - Success -type AddServiceConcatResult struct { - Success *ConcatReply `thrift:"success,0" db:"success" json:"success,omitempty"` -} - -func NewAddServiceConcatResult() *AddServiceConcatResult { - return &AddServiceConcatResult{} -} - -var AddServiceConcatResult_Success_DEFAULT *ConcatReply -func (p *AddServiceConcatResult) GetSuccess() *ConcatReply { - if !p.IsSetSuccess() { - return AddServiceConcatResult_Success_DEFAULT - } -return p.Success -} -func (p *AddServiceConcatResult) IsSetSuccess() bool { - return p.Success != nil -} - -func (p *AddServiceConcatResult) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { break; } - switch fieldId { - case 0: - if fieldTypeId == thrift.STRUCT { - if err := p.ReadField0(iprot); err != nil { - return err - } - } else { - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *AddServiceConcatResult) ReadField0(iprot thrift.TProtocol) error { - p.Success = &ConcatReply{} - if err := p.Success.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) - } - return nil -} - -func (p *AddServiceConcatResult) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Concat_result"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } - if p != nil { - if err := p.writeField0(oprot); err != nil { return err } - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) } - return nil -} - -func (p *AddServiceConcatResult) writeField0(oprot thrift.TProtocol) (err error) { - if p.IsSetSuccess() { - if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) } - if err := p.Success.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) } - } - return err -} - -func (p *AddServiceConcatResult) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("AddServiceConcatResult(%+v)", *p) -} - - diff --git a/examples/apigateway/main.go b/examples/apigateway/main.go deleted file mode 100644 index 3b4cd67..0000000 --- a/examples/apigateway/main.go +++ /dev/null @@ -1,285 +0,0 @@ -package main - -import ( - "bytes" - "context" - "encoding/json" - "flag" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "os" - "os/signal" - "strings" - "syscall" - "time" - - consulsd "github.com/go-kit/kit/sd/consul" - "github.com/gorilla/mux" - "github.com/hashicorp/consul/api" - stdopentracing "github.com/opentracing/opentracing-go" - "google.golang.org/grpc" - - "github.com/go-kit/kit/endpoint" - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/sd" - "github.com/go-kit/kit/sd/lb" - httptransport "github.com/go-kit/kit/transport/http" - - "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint" - "github.com/go-kit/kit/examples/addsvc/pkg/addservice" - "github.com/go-kit/kit/examples/addsvc/pkg/addtransport" -) - -func main() { - var ( - httpAddr = flag.String("http.addr", ":8000", "Address for HTTP (JSON) server") - consulAddr = flag.String("consul.addr", "", "Consul agent address") - retryMax = flag.Int("retry.max", 3, "per-request retries to different instances") - retryTimeout = flag.Duration("retry.timeout", 500*time.Millisecond, "per-request timeout, including retries") - ) - flag.Parse() - - // Logging domain. - var logger log.Logger - { - logger = log.NewLogfmtLogger(os.Stderr) - logger = log.With(logger, "ts", log.DefaultTimestampUTC) - logger = log.With(logger, "caller", log.DefaultCaller) - } - - // Service discovery domain. In this example we use Consul. - var client consulsd.Client - { - consulConfig := api.DefaultConfig() - if len(*consulAddr) > 0 { - consulConfig.Address = *consulAddr - } - consulClient, err := api.NewClient(consulConfig) - if err != nil { - logger.Log("err", err) - os.Exit(1) - } - client = consulsd.NewClient(consulClient) - } - - // Transport domain. - tracer := stdopentracing.GlobalTracer() // no-op - ctx := context.Background() - r := mux.NewRouter() - - // Now we begin installing the routes. Each route corresponds to a single - // method: sum, concat, uppercase, and count. - - // addsvc routes. - { - // Each method gets constructed with a factory. Factories take an - // instance string, and return a specific endpoint. In the factory we - // dial the instance string we get from Consul, and then leverage an - // addsvc client package to construct a complete service. We can then - // leverage the addsvc.Make{Sum,Concat}Endpoint constructors to convert - // the complete service to specific endpoint. - var ( - tags = []string{} - passingOnly = true - endpoints = addendpoint.Set{} - instancer = consulsd.NewInstancer(client, logger, "addsvc", tags, passingOnly) - ) - { - factory := addsvcFactory(addendpoint.MakeSumEndpoint, tracer, logger) - endpointer := sd.NewEndpointer(instancer, factory, logger) - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(*retryMax, *retryTimeout, balancer) - endpoints.SumEndpoint = retry - } - { - factory := addsvcFactory(addendpoint.MakeConcatEndpoint, tracer, logger) - endpointer := sd.NewEndpointer(instancer, factory, logger) - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(*retryMax, *retryTimeout, balancer) - endpoints.ConcatEndpoint = retry - } - - // Here we leverage the fact that addsvc comes with a constructor for an - // HTTP handler, and just install it under a particular path prefix in - // our router. - - r.PathPrefix("/addsvc").Handler(http.StripPrefix("/addsvc", addtransport.NewHTTPHandler(endpoints, tracer, logger))) - } - - // stringsvc routes. - { - // addsvc had lots of nice importable Go packages we could leverage. - // With stringsvc we are not so fortunate, it just has some endpoints - // that we assume will exist. So we have to write that logic here. This - // is by design, so you can see two totally different methods of - // proxying to a remote service. - - var ( - tags = []string{} - passingOnly = true - uppercase endpoint.Endpoint - count endpoint.Endpoint - instancer = consulsd.NewInstancer(client, logger, "stringsvc", tags, passingOnly) - ) - { - factory := stringsvcFactory(ctx, "GET", "/uppercase") - endpointer := sd.NewEndpointer(instancer, factory, logger) - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(*retryMax, *retryTimeout, balancer) - uppercase = retry - } - { - factory := stringsvcFactory(ctx, "GET", "/count") - endpointer := sd.NewEndpointer(instancer, factory, logger) - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(*retryMax, *retryTimeout, balancer) - count = retry - } - - // We can use the transport/http.Server to act as our handler, all we - // have to do provide it with the encode and decode functions for our - // stringsvc methods. - - r.Handle("/stringsvc/uppercase", httptransport.NewServer(uppercase, decodeUppercaseRequest, encodeJSONResponse)) - r.Handle("/stringsvc/count", httptransport.NewServer(count, decodeCountRequest, encodeJSONResponse)) - } - - // Interrupt handler. - errc := make(chan error) - go func() { - c := make(chan os.Signal) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - // HTTP transport. - go func() { - logger.Log("transport", "HTTP", "addr", *httpAddr) - errc <- http.ListenAndServe(*httpAddr, r) - }() - - // Run! - logger.Log("exit", <-errc) -} - -func addsvcFactory(makeEndpoint func(addservice.Service) endpoint.Endpoint, tracer stdopentracing.Tracer, logger log.Logger) sd.Factory { - return func(instance string) (endpoint.Endpoint, io.Closer, error) { - // We could just as easily use the HTTP or Thrift client package to make - // the connection to addsvc. We've chosen gRPC arbitrarily. Note that - // the transport is an implementation detail: it doesn't leak out of - // this function. Nice! - - conn, err := grpc.Dial(instance, grpc.WithInsecure()) - if err != nil { - return nil, nil, err - } - service := addtransport.NewGRPCClient(conn, tracer, logger) - endpoint := makeEndpoint(service) - - // Notice that the addsvc gRPC client converts the connection to a - // complete addsvc, and we just throw away everything except the method - // we're interested in. A smarter factory would mux multiple methods - // over the same connection. But that would require more work to manage - // the returned io.Closer, e.g. reference counting. Since this is for - // the purposes of demonstration, we'll just keep it simple. - - return endpoint, conn, nil - } -} - -func stringsvcFactory(ctx context.Context, method, path string) sd.Factory { - return func(instance string) (endpoint.Endpoint, io.Closer, error) { - if !strings.HasPrefix(instance, "http") { - instance = "http://" + instance - } - tgt, err := url.Parse(instance) - if err != nil { - return nil, nil, err - } - tgt.Path = path - - // Since stringsvc doesn't have any kind of package we can import, or - // any formal spec, we are forced to just assert where the endpoints - // live, and write our own code to encode and decode requests and - // responses. Ideally, if you write the service, you will want to - // provide stronger guarantees to your clients. - - var ( - enc httptransport.EncodeRequestFunc - dec httptransport.DecodeResponseFunc - ) - switch path { - case "/uppercase": - enc, dec = encodeJSONRequest, decodeUppercaseResponse - case "/count": - enc, dec = encodeJSONRequest, decodeCountResponse - default: - return nil, nil, fmt.Errorf("unknown stringsvc path %q", path) - } - - return httptransport.NewClient(method, tgt, enc, dec).Endpoint(), nil, nil - } -} - -func encodeJSONRequest(_ context.Context, req *http.Request, request interface{}) error { - // Both uppercase and count requests are encoded in the same way: - // simple JSON serialization to the request body. - var buf bytes.Buffer - if err := json.NewEncoder(&buf).Encode(request); err != nil { - return err - } - req.Body = ioutil.NopCloser(&buf) - return nil -} - -func encodeJSONResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - return json.NewEncoder(w).Encode(response) -} - -// I've just copied these functions from stringsvc3/transport.go, inlining the -// struct definitions. - -func decodeUppercaseResponse(ctx context.Context, resp *http.Response) (interface{}, error) { - var response struct { - V string `json:"v"` - Err string `json:"err,omitempty"` - } - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return nil, err - } - return response, nil -} - -func decodeCountResponse(ctx context.Context, resp *http.Response) (interface{}, error) { - var response struct { - V int `json:"v"` - } - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return nil, err - } - return response, nil -} - -func decodeUppercaseRequest(ctx context.Context, req *http.Request) (interface{}, error) { - var request struct { - S string `json:"s"` - } - if err := json.NewDecoder(req.Body).Decode(&request); err != nil { - return nil, err - } - return request, nil -} - -func decodeCountRequest(ctx context.Context, req *http.Request) (interface{}, error) { - var request struct { - S string `json:"s"` - } - if err := json.NewDecoder(req.Body).Decode(&request); err != nil { - return nil, err - } - return request, nil -} diff --git a/examples/profilesvc/README.md b/examples/profilesvc/README.md deleted file mode 100644 index 68c4125..0000000 --- a/examples/profilesvc/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# profilesvc - -This example demonstrates how to use Go kit to implement a REST-y HTTP service. -It leverages the excellent [gorilla mux package](https://github.com/gorilla/mux) for routing. diff --git a/examples/profilesvc/client/client.go b/examples/profilesvc/client/client.go deleted file mode 100644 index 03c1dc7..0000000 --- a/examples/profilesvc/client/client.go +++ /dev/null @@ -1,121 +0,0 @@ -// Package client provides a profilesvc client based on a predefined Consul -// service name and relevant tags. Users must only provide the address of a -// Consul server. -package client - -import ( - "io" - "time" - - consulapi "github.com/hashicorp/consul/api" - - "github.com/go-kit/kit/endpoint" - "github.com/go-kit/kit/examples/profilesvc" - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/sd" - "github.com/go-kit/kit/sd/consul" - "github.com/go-kit/kit/sd/lb" -) - -// New returns a service that's load-balanced over instances of profilesvc found -// in the provided Consul server. The mechanism of looking up profilesvc -// instances in Consul is hard-coded into the client. -func New(consulAddr string, logger log.Logger) (profilesvc.Service, error) { - apiclient, err := consulapi.NewClient(&consulapi.Config{ - Address: consulAddr, - }) - if err != nil { - return nil, err - } - - // As the implementer of profilesvc, we declare and enforce these - // parameters for all of the profilesvc consumers. - var ( - consulService = "profilesvc" - consulTags = []string{"prod"} - passingOnly = true - retryMax = 3 - retryTimeout = 500 * time.Millisecond - ) - - var ( - sdclient = consul.NewClient(apiclient) - instancer = consul.NewInstancer(sdclient, logger, consulService, consulTags, passingOnly) - endpoints profilesvc.Endpoints - ) - { - factory := factoryFor(profilesvc.MakePostProfileEndpoint) - endpointer := sd.NewEndpointer(instancer, factory, logger) - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(retryMax, retryTimeout, balancer) - endpoints.PostProfileEndpoint = retry - } - { - factory := factoryFor(profilesvc.MakeGetProfileEndpoint) - endpointer := sd.NewEndpointer(instancer, factory, logger) - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(retryMax, retryTimeout, balancer) - endpoints.GetProfileEndpoint = retry - } - { - factory := factoryFor(profilesvc.MakePutProfileEndpoint) - endpointer := sd.NewEndpointer(instancer, factory, logger) - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(retryMax, retryTimeout, balancer) - endpoints.PutProfileEndpoint = retry - } - { - factory := factoryFor(profilesvc.MakePatchProfileEndpoint) - endpointer := sd.NewEndpointer(instancer, factory, logger) - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(retryMax, retryTimeout, balancer) - endpoints.PatchProfileEndpoint = retry - } - { - factory := factoryFor(profilesvc.MakeDeleteProfileEndpoint) - endpointer := sd.NewEndpointer(instancer, factory, logger) - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(retryMax, retryTimeout, balancer) - endpoints.DeleteProfileEndpoint = retry - } - { - factory := factoryFor(profilesvc.MakeGetAddressesEndpoint) - endpointer := sd.NewEndpointer(instancer, factory, logger) - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(retryMax, retryTimeout, balancer) - endpoints.GetAddressesEndpoint = retry - } - { - factory := factoryFor(profilesvc.MakeGetAddressEndpoint) - endpointer := sd.NewEndpointer(instancer, factory, logger) - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(retryMax, retryTimeout, balancer) - endpoints.GetAddressEndpoint = retry - } - { - factory := factoryFor(profilesvc.MakePostAddressEndpoint) - endpointer := sd.NewEndpointer(instancer, factory, logger) - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(retryMax, retryTimeout, balancer) - endpoints.PostAddressEndpoint = retry - } - { - factory := factoryFor(profilesvc.MakeDeleteAddressEndpoint) - endpointer := sd.NewEndpointer(instancer, factory, logger) - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(retryMax, retryTimeout, balancer) - endpoints.DeleteAddressEndpoint = retry - } - - return endpoints, nil -} - -func factoryFor(makeEndpoint func(profilesvc.Service) endpoint.Endpoint) sd.Factory { - return func(instance string) (endpoint.Endpoint, io.Closer, error) { - service, err := profilesvc.MakeClientEndpoints(instance) - if err != nil { - return nil, nil, err - } - return makeEndpoint(service), nil, nil - } -} diff --git a/examples/profilesvc/cmd/profilesvc/main.go b/examples/profilesvc/cmd/profilesvc/main.go deleted file mode 100644 index fda7529..0000000 --- a/examples/profilesvc/cmd/profilesvc/main.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "net/http" - "os" - "os/signal" - "syscall" - - "github.com/go-kit/kit/examples/profilesvc" - "github.com/go-kit/kit/log" -) - -func main() { - var ( - httpAddr = flag.String("http.addr", ":8080", "HTTP listen address") - ) - flag.Parse() - - var logger log.Logger - { - logger = log.NewLogfmtLogger(os.Stderr) - logger = log.With(logger, "ts", log.DefaultTimestampUTC) - logger = log.With(logger, "caller", log.DefaultCaller) - } - - var s profilesvc.Service - { - s = profilesvc.NewInmemService() - s = profilesvc.LoggingMiddleware(logger)(s) - } - - var h http.Handler - { - h = profilesvc.MakeHTTPHandler(s, log.With(logger, "component", "HTTP")) - } - - errs := make(chan error) - go func() { - c := make(chan os.Signal) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errs <- fmt.Errorf("%s", <-c) - }() - - go func() { - logger.Log("transport", "HTTP", "addr", *httpAddr) - errs <- http.ListenAndServe(*httpAddr, h) - }() - - logger.Log("exit", <-errs) -} diff --git a/examples/profilesvc/endpoints.go b/examples/profilesvc/endpoints.go deleted file mode 100644 index 3da29b4..0000000 --- a/examples/profilesvc/endpoints.go +++ /dev/null @@ -1,387 +0,0 @@ -package profilesvc - -import ( - "context" - "net/url" - "strings" - - "github.com/go-kit/kit/endpoint" - httptransport "github.com/go-kit/kit/transport/http" -) - -// Endpoints collects all of the endpoints that compose a profile service. It's -// meant to be used as a helper struct, to collect all of the endpoints into a -// single parameter. -// -// In a server, it's useful for functions that need to operate on a per-endpoint -// basis. For example, you might pass an Endpoints to a function that produces -// an http.Handler, with each method (endpoint) wired up to a specific path. (It -// is probably a mistake in design to invoke the Service methods on the -// Endpoints struct in a server.) -// -// In a client, it's useful to collect individually constructed endpoints into a -// single type that implements the Service interface. For example, you might -// construct individual endpoints using transport/http.NewClient, combine them -// into an Endpoints, and return it to the caller as a Service. -type Endpoints struct { - PostProfileEndpoint endpoint.Endpoint - GetProfileEndpoint endpoint.Endpoint - PutProfileEndpoint endpoint.Endpoint - PatchProfileEndpoint endpoint.Endpoint - DeleteProfileEndpoint endpoint.Endpoint - GetAddressesEndpoint endpoint.Endpoint - GetAddressEndpoint endpoint.Endpoint - PostAddressEndpoint endpoint.Endpoint - DeleteAddressEndpoint endpoint.Endpoint -} - -// MakeServerEndpoints returns an Endpoints struct where each endpoint invokes -// the corresponding method on the provided service. Useful in a profilesvc -// server. -func MakeServerEndpoints(s Service) Endpoints { - return Endpoints{ - PostProfileEndpoint: MakePostProfileEndpoint(s), - GetProfileEndpoint: MakeGetProfileEndpoint(s), - PutProfileEndpoint: MakePutProfileEndpoint(s), - PatchProfileEndpoint: MakePatchProfileEndpoint(s), - DeleteProfileEndpoint: MakeDeleteProfileEndpoint(s), - GetAddressesEndpoint: MakeGetAddressesEndpoint(s), - GetAddressEndpoint: MakeGetAddressEndpoint(s), - PostAddressEndpoint: MakePostAddressEndpoint(s), - DeleteAddressEndpoint: MakeDeleteAddressEndpoint(s), - } -} - -// MakeClientEndpoints returns an Endpoints struct where each endpoint invokes -// the corresponding method on the remote instance, via a transport/http.Client. -// Useful in a profilesvc client. -func MakeClientEndpoints(instance string) (Endpoints, error) { - if !strings.HasPrefix(instance, "http") { - instance = "http://" + instance - } - tgt, err := url.Parse(instance) - if err != nil { - return Endpoints{}, err - } - tgt.Path = "" - - options := []httptransport.ClientOption{} - - // Note that the request encoders need to modify the request URL, changing - // the path and method. That's fine: we simply need to provide specific - // encoders for each endpoint. - - return Endpoints{ - PostProfileEndpoint: httptransport.NewClient("POST", tgt, encodePostProfileRequest, decodePostProfileResponse, options...).Endpoint(), - GetProfileEndpoint: httptransport.NewClient("GET", tgt, encodeGetProfileRequest, decodeGetProfileResponse, options...).Endpoint(), - PutProfileEndpoint: httptransport.NewClient("PUT", tgt, encodePutProfileRequest, decodePutProfileResponse, options...).Endpoint(), - PatchProfileEndpoint: httptransport.NewClient("PATCH", tgt, encodePatchProfileRequest, decodePatchProfileResponse, options...).Endpoint(), - DeleteProfileEndpoint: httptransport.NewClient("DELETE", tgt, encodeDeleteProfileRequest, decodeDeleteProfileResponse, options...).Endpoint(), - GetAddressesEndpoint: httptransport.NewClient("GET", tgt, encodeGetAddressesRequest, decodeGetAddressesResponse, options...).Endpoint(), - GetAddressEndpoint: httptransport.NewClient("GET", tgt, encodeGetAddressRequest, decodeGetAddressResponse, options...).Endpoint(), - PostAddressEndpoint: httptransport.NewClient("POST", tgt, encodePostAddressRequest, decodePostAddressResponse, options...).Endpoint(), - DeleteAddressEndpoint: httptransport.NewClient("DELETE", tgt, encodeDeleteAddressRequest, decodeDeleteAddressResponse, options...).Endpoint(), - }, nil -} - -// PostProfile implements Service. Primarily useful in a client. -func (e Endpoints) PostProfile(ctx context.Context, p Profile) error { - request := postProfileRequest{Profile: p} - response, err := e.PostProfileEndpoint(ctx, request) - if err != nil { - return err - } - resp := response.(postProfileResponse) - return resp.Err -} - -// GetProfile implements Service. Primarily useful in a client. -func (e Endpoints) GetProfile(ctx context.Context, id string) (Profile, error) { - request := getProfileRequest{ID: id} - response, err := e.GetProfileEndpoint(ctx, request) - if err != nil { - return Profile{}, err - } - resp := response.(getProfileResponse) - return resp.Profile, resp.Err -} - -// PutProfile implements Service. Primarily useful in a client. -func (e Endpoints) PutProfile(ctx context.Context, id string, p Profile) error { - request := putProfileRequest{ID: id, Profile: p} - response, err := e.PutProfileEndpoint(ctx, request) - if err != nil { - return err - } - resp := response.(putProfileResponse) - return resp.Err -} - -// PatchProfile implements Service. Primarily useful in a client. -func (e Endpoints) PatchProfile(ctx context.Context, id string, p Profile) error { - request := patchProfileRequest{ID: id, Profile: p} - response, err := e.PatchProfileEndpoint(ctx, request) - if err != nil { - return err - } - resp := response.(patchProfileResponse) - return resp.Err -} - -// DeleteProfile implements Service. Primarily useful in a client. -func (e Endpoints) DeleteProfile(ctx context.Context, id string) error { - request := deleteProfileRequest{ID: id} - response, err := e.DeleteProfileEndpoint(ctx, request) - if err != nil { - return err - } - resp := response.(deleteProfileResponse) - return resp.Err -} - -// GetAddresses implements Service. Primarily useful in a client. -func (e Endpoints) GetAddresses(ctx context.Context, profileID string) ([]Address, error) { - request := getAddressesRequest{ProfileID: profileID} - response, err := e.GetAddressesEndpoint(ctx, request) - if err != nil { - return nil, err - } - resp := response.(getAddressesResponse) - return resp.Addresses, resp.Err -} - -// GetAddress implements Service. Primarily useful in a client. -func (e Endpoints) GetAddress(ctx context.Context, profileID string, addressID string) (Address, error) { - request := getAddressRequest{ProfileID: profileID, AddressID: addressID} - response, err := e.GetAddressEndpoint(ctx, request) - if err != nil { - return Address{}, err - } - resp := response.(getAddressResponse) - return resp.Address, resp.Err -} - -// PostAddress implements Service. Primarily useful in a client. -func (e Endpoints) PostAddress(ctx context.Context, profileID string, a Address) error { - request := postAddressRequest{ProfileID: profileID, Address: a} - response, err := e.PostAddressEndpoint(ctx, request) - if err != nil { - return err - } - resp := response.(postAddressResponse) - return resp.Err -} - -// DeleteAddress implements Service. Primarily useful in a client. -func (e Endpoints) DeleteAddress(ctx context.Context, profileID string, addressID string) error { - request := deleteAddressRequest{ProfileID: profileID, AddressID: addressID} - response, err := e.DeleteAddressEndpoint(ctx, request) - if err != nil { - return err - } - resp := response.(deleteAddressResponse) - return resp.Err -} - -// MakePostProfileEndpoint returns an endpoint via the passed service. -// Primarily useful in a server. -func MakePostProfileEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(postProfileRequest) - e := s.PostProfile(ctx, req.Profile) - return postProfileResponse{Err: e}, nil - } -} - -// MakeGetProfileEndpoint returns an endpoint via the passed service. -// Primarily useful in a server. -func MakeGetProfileEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(getProfileRequest) - p, e := s.GetProfile(ctx, req.ID) - return getProfileResponse{Profile: p, Err: e}, nil - } -} - -// MakePutProfileEndpoint returns an endpoint via the passed service. -// Primarily useful in a server. -func MakePutProfileEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(putProfileRequest) - e := s.PutProfile(ctx, req.ID, req.Profile) - return putProfileResponse{Err: e}, nil - } -} - -// MakePatchProfileEndpoint returns an endpoint via the passed service. -// Primarily useful in a server. -func MakePatchProfileEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(patchProfileRequest) - e := s.PatchProfile(ctx, req.ID, req.Profile) - return patchProfileResponse{Err: e}, nil - } -} - -// MakeDeleteProfileEndpoint returns an endpoint via the passed service. -// Primarily useful in a server. -func MakeDeleteProfileEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(deleteProfileRequest) - e := s.DeleteProfile(ctx, req.ID) - return deleteProfileResponse{Err: e}, nil - } -} - -// MakeGetAddressesEndpoint returns an endpoint via the passed service. -// Primarily useful in a server. -func MakeGetAddressesEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(getAddressesRequest) - a, e := s.GetAddresses(ctx, req.ProfileID) - return getAddressesResponse{Addresses: a, Err: e}, nil - } -} - -// MakeGetAddressEndpoint returns an endpoint via the passed service. -// Primarily useful in a server. -func MakeGetAddressEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(getAddressRequest) - a, e := s.GetAddress(ctx, req.ProfileID, req.AddressID) - return getAddressResponse{Address: a, Err: e}, nil - } -} - -// MakePostAddressEndpoint returns an endpoint via the passed service. -// Primarily useful in a server. -func MakePostAddressEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(postAddressRequest) - e := s.PostAddress(ctx, req.ProfileID, req.Address) - return postAddressResponse{Err: e}, nil - } -} - -// MakeDeleteAddressEndpoint returns an endpoint via the passed service. -// Primarily useful in a server. -func MakeDeleteAddressEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(deleteAddressRequest) - e := s.DeleteAddress(ctx, req.ProfileID, req.AddressID) - return deleteAddressResponse{Err: e}, nil - } -} - -// We have two options to return errors from the business logic. -// -// We could return the error via the endpoint itself. That makes certain things -// a little bit easier, like providing non-200 HTTP responses to the client. But -// Go kit assumes that endpoint errors are (or may be treated as) -// transport-domain errors. For example, an endpoint error will count against a -// circuit breaker error count. -// -// Therefore, it's often better to return service (business logic) errors in the -// response object. This means we have to do a bit more work in the HTTP -// response encoder to detect e.g. a not-found error and provide a proper HTTP -// status code. That work is done with the errorer interface, in transport.go. -// Response types that may contain business-logic errors implement that -// interface. - -type postProfileRequest struct { - Profile Profile -} - -type postProfileResponse struct { - Err error `json:"err,omitempty"` -} - -func (r postProfileResponse) error() error { return r.Err } - -type getProfileRequest struct { - ID string -} - -type getProfileResponse struct { - Profile Profile `json:"profile,omitempty"` - Err error `json:"err,omitempty"` -} - -func (r getProfileResponse) error() error { return r.Err } - -type putProfileRequest struct { - ID string - Profile Profile -} - -type putProfileResponse struct { - Err error `json:"err,omitempty"` -} - -func (r putProfileResponse) error() error { return nil } - -type patchProfileRequest struct { - ID string - Profile Profile -} - -type patchProfileResponse struct { - Err error `json:"err,omitempty"` -} - -func (r patchProfileResponse) error() error { return r.Err } - -type deleteProfileRequest struct { - ID string -} - -type deleteProfileResponse struct { - Err error `json:"err,omitempty"` -} - -func (r deleteProfileResponse) error() error { return r.Err } - -type getAddressesRequest struct { - ProfileID string -} - -type getAddressesResponse struct { - Addresses []Address `json:"addresses,omitempty"` - Err error `json:"err,omitempty"` -} - -func (r getAddressesResponse) error() error { return r.Err } - -type getAddressRequest struct { - ProfileID string - AddressID string -} - -type getAddressResponse struct { - Address Address `json:"address,omitempty"` - Err error `json:"err,omitempty"` -} - -func (r getAddressResponse) error() error { return r.Err } - -type postAddressRequest struct { - ProfileID string - Address Address -} - -type postAddressResponse struct { - Err error `json:"err,omitempty"` -} - -func (r postAddressResponse) error() error { return r.Err } - -type deleteAddressRequest struct { - ProfileID string - AddressID string -} - -type deleteAddressResponse struct { - Err error `json:"err,omitempty"` -} - -func (r deleteAddressResponse) error() error { return r.Err } diff --git a/examples/profilesvc/middlewares.go b/examples/profilesvc/middlewares.go deleted file mode 100644 index 1b738f5..0000000 --- a/examples/profilesvc/middlewares.go +++ /dev/null @@ -1,88 +0,0 @@ -package profilesvc - -import ( - "context" - "time" - - "github.com/go-kit/kit/log" -) - -// Middleware describes a service (as opposed to endpoint) middleware. -type Middleware func(Service) Service - -func LoggingMiddleware(logger log.Logger) Middleware { - return func(next Service) Service { - return &loggingMiddleware{ - next: next, - logger: logger, - } - } -} - -type loggingMiddleware struct { - next Service - logger log.Logger -} - -func (mw loggingMiddleware) PostProfile(ctx context.Context, p Profile) (err error) { - defer func(begin time.Time) { - mw.logger.Log("method", "PostProfile", "id", p.ID, "took", time.Since(begin), "err", err) - }(time.Now()) - return mw.next.PostProfile(ctx, p) -} - -func (mw loggingMiddleware) GetProfile(ctx context.Context, id string) (p Profile, err error) { - defer func(begin time.Time) { - mw.logger.Log("method", "GetProfile", "id", id, "took", time.Since(begin), "err", err) - }(time.Now()) - return mw.next.GetProfile(ctx, id) -} - -func (mw loggingMiddleware) PutProfile(ctx context.Context, id string, p Profile) (err error) { - defer func(begin time.Time) { - mw.logger.Log("method", "PutProfile", "id", id, "took", time.Since(begin), "err", err) - }(time.Now()) - return mw.next.PutProfile(ctx, id, p) -} - -func (mw loggingMiddleware) PatchProfile(ctx context.Context, id string, p Profile) (err error) { - defer func(begin time.Time) { - mw.logger.Log("method", "PatchProfile", "id", id, "took", time.Since(begin), "err", err) - }(time.Now()) - return mw.next.PatchProfile(ctx, id, p) -} - -func (mw loggingMiddleware) DeleteProfile(ctx context.Context, id string) (err error) { - defer func(begin time.Time) { - mw.logger.Log("method", "DeleteProfile", "id", id, "took", time.Since(begin), "err", err) - }(time.Now()) - return mw.next.DeleteProfile(ctx, id) -} - -func (mw loggingMiddleware) GetAddresses(ctx context.Context, profileID string) (addresses []Address, err error) { - defer func(begin time.Time) { - mw.logger.Log("method", "GetAddresses", "profileID", profileID, "took", time.Since(begin), "err", err) - }(time.Now()) - return mw.next.GetAddresses(ctx, profileID) -} - -func (mw loggingMiddleware) GetAddress(ctx context.Context, profileID string, addressID string) (a Address, err error) { - defer func(begin time.Time) { - mw.logger.Log("method", "GetAddress", "profileID", profileID, "addressID", addressID, "took", time.Since(begin), "err", err) - }(time.Now()) - return mw.next.GetAddress(ctx, profileID, addressID) -} - -func (mw loggingMiddleware) PostAddress(ctx context.Context, profileID string, a Address) (err error) { - defer func(begin time.Time) { - mw.logger.Log("method", "PostAddress", "profileID", profileID, "took", time.Since(begin), "err", err) - }(time.Now()) - return mw.next.PostAddress(ctx, profileID, a) -} - -func (mw loggingMiddleware) DeleteAddress(ctx context.Context, profileID string, addressID string) (err error) { - defer func(begin time.Time) { - mw.logger.Log("method", "DeleteAddress", "profileID", profileID, "addressID", addressID, "took", time.Since(begin), "err", err) - }(time.Now()) - return mw.next.DeleteAddress(ctx, profileID, addressID) -} diff --git a/examples/profilesvc/service.go b/examples/profilesvc/service.go deleted file mode 100644 index cd34ecd..0000000 --- a/examples/profilesvc/service.go +++ /dev/null @@ -1,185 +0,0 @@ -package profilesvc - -import ( - "context" - "errors" - "sync" -) - -// Service is a simple CRUD interface for user profiles. -type Service interface { - PostProfile(ctx context.Context, p Profile) error - GetProfile(ctx context.Context, id string) (Profile, error) - PutProfile(ctx context.Context, id string, p Profile) error - PatchProfile(ctx context.Context, id string, p Profile) error - DeleteProfile(ctx context.Context, id string) error - GetAddresses(ctx context.Context, profileID string) ([]Address, error) - GetAddress(ctx context.Context, profileID string, addressID string) (Address, error) - PostAddress(ctx context.Context, profileID string, a Address) error - DeleteAddress(ctx context.Context, profileID string, addressID string) error -} - -// Profile represents a single user profile. -// ID should be globally unique. -type Profile struct { - ID string `json:"id"` - Name string `json:"name,omitempty"` - Addresses []Address `json:"addresses,omitempty"` -} - -// Address is a field of a user profile. -// ID should be unique within the profile (at a minimum). -type Address struct { - ID string `json:"id"` - Location string `json:"location,omitempty"` -} - -var ( - ErrInconsistentIDs = errors.New("inconsistent IDs") - ErrAlreadyExists = errors.New("already exists") - ErrNotFound = errors.New("not found") -) - -type inmemService struct { - mtx sync.RWMutex - m map[string]Profile -} - -func NewInmemService() Service { - return &inmemService{ - m: map[string]Profile{}, - } -} - -func (s *inmemService) PostProfile(ctx context.Context, p Profile) error { - s.mtx.Lock() - defer s.mtx.Unlock() - if _, ok := s.m[p.ID]; ok { - return ErrAlreadyExists // POST = create, don't overwrite - } - s.m[p.ID] = p - return nil -} - -func (s *inmemService) GetProfile(ctx context.Context, id string) (Profile, error) { - s.mtx.RLock() - defer s.mtx.RUnlock() - p, ok := s.m[id] - if !ok { - return Profile{}, ErrNotFound - } - return p, nil -} - -func (s *inmemService) PutProfile(ctx context.Context, id string, p Profile) error { - if id != p.ID { - return ErrInconsistentIDs - } - s.mtx.Lock() - defer s.mtx.Unlock() - s.m[id] = p // PUT = create or update - return nil -} - -func (s *inmemService) PatchProfile(ctx context.Context, id string, p Profile) error { - if p.ID != "" && id != p.ID { - return ErrInconsistentIDs - } - - s.mtx.Lock() - defer s.mtx.Unlock() - - existing, ok := s.m[id] - if !ok { - return ErrNotFound // PATCH = update existing, don't create - } - - // We assume that it's not possible to PATCH the ID, and that it's not - // possible to PATCH any field to its zero value. That is, the zero value - // means not specified. The way around this is to use e.g. Name *string in - // the Profile definition. But since this is just a demonstrative example, - // I'm leaving that out. - - if p.Name != "" { - existing.Name = p.Name - } - if len(p.Addresses) > 0 { - existing.Addresses = p.Addresses - } - s.m[id] = existing - return nil -} - -func (s *inmemService) DeleteProfile(ctx context.Context, id string) error { - s.mtx.Lock() - defer s.mtx.Unlock() - if _, ok := s.m[id]; !ok { - return ErrNotFound - } - delete(s.m, id) - return nil -} - -func (s *inmemService) GetAddresses(ctx context.Context, profileID string) ([]Address, error) { - s.mtx.RLock() - defer s.mtx.RUnlock() - p, ok := s.m[profileID] - if !ok { - return []Address{}, ErrNotFound - } - return p.Addresses, nil -} - -func (s *inmemService) GetAddress(ctx context.Context, profileID string, addressID string) (Address, error) { - s.mtx.RLock() - defer s.mtx.RUnlock() - p, ok := s.m[profileID] - if !ok { - return Address{}, ErrNotFound - } - for _, address := range p.Addresses { - if address.ID == addressID { - return address, nil - } - } - return Address{}, ErrNotFound -} - -func (s *inmemService) PostAddress(ctx context.Context, profileID string, a Address) error { - s.mtx.Lock() - defer s.mtx.Unlock() - p, ok := s.m[profileID] - if !ok { - return ErrNotFound - } - for _, address := range p.Addresses { - if address.ID == a.ID { - return ErrAlreadyExists - } - } - p.Addresses = append(p.Addresses, a) - s.m[profileID] = p - return nil -} - -func (s *inmemService) DeleteAddress(ctx context.Context, profileID string, addressID string) error { - s.mtx.Lock() - defer s.mtx.Unlock() - p, ok := s.m[profileID] - if !ok { - return ErrNotFound - } - newAddresses := make([]Address, 0, len(p.Addresses)) - for _, address := range p.Addresses { - if address.ID == addressID { - continue // delete - } - newAddresses = append(newAddresses, address) - } - if len(newAddresses) == len(p.Addresses) { - return ErrNotFound - } - p.Addresses = newAddresses - s.m[profileID] = p - return nil -} diff --git a/examples/profilesvc/transport.go b/examples/profilesvc/transport.go deleted file mode 100644 index a0136ee..0000000 --- a/examples/profilesvc/transport.go +++ /dev/null @@ -1,400 +0,0 @@ -package profilesvc - -// The profilesvc is just over HTTP, so we just have a single transport.go. - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "io/ioutil" - "net/http" - "net/url" - - "github.com/gorilla/mux" - - "github.com/go-kit/kit/log" - httptransport "github.com/go-kit/kit/transport/http" -) - -var ( - // ErrBadRouting is returned when an expected path variable is missing. - // It always indicates programmer error. - ErrBadRouting = errors.New("inconsistent mapping between route and handler (programmer error)") -) - -// MakeHTTPHandler mounts all of the service endpoints into an http.Handler. -// Useful in a profilesvc server. -func MakeHTTPHandler(s Service, logger log.Logger) http.Handler { - r := mux.NewRouter() - e := MakeServerEndpoints(s) - options := []httptransport.ServerOption{ - httptransport.ServerErrorLogger(logger), - httptransport.ServerErrorEncoder(encodeError), - } - - // POST /profiles/ adds another profile - // GET /profiles/:id retrieves the given profile by id - // PUT /profiles/:id post updated profile information about the profile - // PATCH /profiles/:id partial updated profile information - // DELETE /profiles/:id remove the given profile - // GET /profiles/:id/addresses/ retrieve addresses associated with the profile - // GET /profiles/:id/addresses/:addressID retrieve a particular profile address - // POST /profiles/:id/addresses/ add a new address - // DELETE /profiles/:id/addresses/:addressID remove an address - - r.Methods("POST").Path("/profiles/").Handler(httptransport.NewServer( - e.PostProfileEndpoint, - decodePostProfileRequest, - encodeResponse, - options..., - )) - r.Methods("GET").Path("/profiles/{id}").Handler(httptransport.NewServer( - e.GetProfileEndpoint, - decodeGetProfileRequest, - encodeResponse, - options..., - )) - r.Methods("PUT").Path("/profiles/{id}").Handler(httptransport.NewServer( - e.PutProfileEndpoint, - decodePutProfileRequest, - encodeResponse, - options..., - )) - r.Methods("PATCH").Path("/profiles/{id}").Handler(httptransport.NewServer( - e.PatchProfileEndpoint, - decodePatchProfileRequest, - encodeResponse, - options..., - )) - r.Methods("DELETE").Path("/profiles/{id}").Handler(httptransport.NewServer( - e.DeleteProfileEndpoint, - decodeDeleteProfileRequest, - encodeResponse, - options..., - )) - r.Methods("GET").Path("/profiles/{id}/addresses/").Handler(httptransport.NewServer( - e.GetAddressesEndpoint, - decodeGetAddressesRequest, - encodeResponse, - options..., - )) - r.Methods("GET").Path("/profiles/{id}/addresses/{addressID}").Handler(httptransport.NewServer( - e.GetAddressEndpoint, - decodeGetAddressRequest, - encodeResponse, - options..., - )) - r.Methods("POST").Path("/profiles/{id}/addresses/").Handler(httptransport.NewServer( - e.PostAddressEndpoint, - decodePostAddressRequest, - encodeResponse, - options..., - )) - r.Methods("DELETE").Path("/profiles/{id}/addresses/{addressID}").Handler(httptransport.NewServer( - e.DeleteAddressEndpoint, - decodeDeleteAddressRequest, - encodeResponse, - options..., - )) - return r -} - -func decodePostProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) { - var req postProfileRequest - if e := json.NewDecoder(r.Body).Decode(&req.Profile); e != nil { - return nil, e - } - return req, nil -} - -func decodeGetProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) { - vars := mux.Vars(r) - id, ok := vars["id"] - if !ok { - return nil, ErrBadRouting - } - return getProfileRequest{ID: id}, nil -} - -func decodePutProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) { - vars := mux.Vars(r) - id, ok := vars["id"] - if !ok { - return nil, ErrBadRouting - } - var profile Profile - if err := json.NewDecoder(r.Body).Decode(&profile); err != nil { - return nil, err - } - return putProfileRequest{ - ID: id, - Profile: profile, - }, nil -} - -func decodePatchProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) { - vars := mux.Vars(r) - id, ok := vars["id"] - if !ok { - return nil, ErrBadRouting - } - var profile Profile - if err := json.NewDecoder(r.Body).Decode(&profile); err != nil { - return nil, err - } - return patchProfileRequest{ - ID: id, - Profile: profile, - }, nil -} - -func decodeDeleteProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) { - vars := mux.Vars(r) - id, ok := vars["id"] - if !ok { - return nil, ErrBadRouting - } - return deleteProfileRequest{ID: id}, nil -} - -func decodeGetAddressesRequest(_ context.Context, r *http.Request) (request interface{}, err error) { - vars := mux.Vars(r) - id, ok := vars["id"] - if !ok { - return nil, ErrBadRouting - } - return getAddressesRequest{ProfileID: id}, nil -} - -func decodeGetAddressRequest(_ context.Context, r *http.Request) (request interface{}, err error) { - vars := mux.Vars(r) - id, ok := vars["id"] - if !ok { - return nil, ErrBadRouting - } - addressID, ok := vars["addressID"] - if !ok { - return nil, ErrBadRouting - } - return getAddressRequest{ - ProfileID: id, - AddressID: addressID, - }, nil -} - -func decodePostAddressRequest(_ context.Context, r *http.Request) (request interface{}, err error) { - vars := mux.Vars(r) - id, ok := vars["id"] - if !ok { - return nil, ErrBadRouting - } - var address Address - if err := json.NewDecoder(r.Body).Decode(&address); err != nil { - return nil, err - } - return postAddressRequest{ - ProfileID: id, - Address: address, - }, nil -} - -func decodeDeleteAddressRequest(_ context.Context, r *http.Request) (request interface{}, err error) { - vars := mux.Vars(r) - id, ok := vars["id"] - if !ok { - return nil, ErrBadRouting - } - addressID, ok := vars["addressID"] - if !ok { - return nil, ErrBadRouting - } - return deleteAddressRequest{ - ProfileID: id, - AddressID: addressID, - }, nil -} - -func encodePostProfileRequest(ctx context.Context, req *http.Request, request interface{}) error { - // r.Methods("POST").Path("/profiles/") - req.Method, req.URL.Path = "POST", "/profiles/" - return encodeRequest(ctx, req, request) -} - -func encodeGetProfileRequest(ctx context.Context, req *http.Request, request interface{}) error { - // r.Methods("GET").Path("/profiles/{id}") - r := request.(getProfileRequest) - profileID := url.QueryEscape(r.ID) - req.Method, req.URL.Path = "GET", "/profiles/"+profileID - return encodeRequest(ctx, req, request) -} - -func encodePutProfileRequest(ctx context.Context, req *http.Request, request interface{}) error { - // r.Methods("PUT").Path("/profiles/{id}") - r := request.(putProfileRequest) - profileID := url.QueryEscape(r.ID) - req.Method, req.URL.Path = "PUT", "/profiles/"+profileID - return encodeRequest(ctx, req, request) -} - -func encodePatchProfileRequest(ctx context.Context, req *http.Request, request interface{}) error { - // r.Methods("PATCH").Path("/profiles/{id}") - r := request.(patchProfileRequest) - profileID := url.QueryEscape(r.ID) - req.Method, req.URL.Path = "PATCH", "/profiles/"+profileID - return encodeRequest(ctx, req, request) -} - -func encodeDeleteProfileRequest(ctx context.Context, req *http.Request, request interface{}) error { - // r.Methods("DELETE").Path("/profiles/{id}") - r := request.(deleteProfileRequest) - profileID := url.QueryEscape(r.ID) - req.Method, req.URL.Path = "DELETE", "/profiles/"+profileID - return encodeRequest(ctx, req, request) -} - -func encodeGetAddressesRequest(ctx context.Context, req *http.Request, request interface{}) error { - // r.Methods("GET").Path("/profiles/{id}/addresses/") - r := request.(getAddressesRequest) - profileID := url.QueryEscape(r.ProfileID) - req.Method, req.URL.Path = "GET", "/profiles/"+profileID+"/addresses/" - return encodeRequest(ctx, req, request) -} - -func encodeGetAddressRequest(ctx context.Context, req *http.Request, request interface{}) error { - // r.Methods("GET").Path("/profiles/{id}/addresses/{addressID}") - r := request.(getAddressRequest) - profileID := url.QueryEscape(r.ProfileID) - addressID := url.QueryEscape(r.AddressID) - req.Method, req.URL.Path = "GET", "/profiles/"+profileID+"/addresses/"+addressID - return encodeRequest(ctx, req, request) -} - -func encodePostAddressRequest(ctx context.Context, req *http.Request, request interface{}) error { - // r.Methods("POST").Path("/profiles/{id}/addresses/") - r := request.(postAddressRequest) - profileID := url.QueryEscape(r.ProfileID) - req.Method, req.URL.Path = "POST", "/profiles/"+profileID+"/addresses/" - return encodeRequest(ctx, req, request) -} - -func encodeDeleteAddressRequest(ctx context.Context, req *http.Request, request interface{}) error { - // r.Methods("DELETE").Path("/profiles/{id}/addresses/{addressID}") - r := request.(deleteAddressRequest) - profileID := url.QueryEscape(r.ProfileID) - addressID := url.QueryEscape(r.AddressID) - req.Method, req.URL.Path = "DELETE", "/profiles/"+profileID+"/addresses/"+addressID - return encodeRequest(ctx, req, request) -} - -func decodePostProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) { - var response postProfileResponse - err := json.NewDecoder(resp.Body).Decode(&response) - return response, err -} - -func decodeGetProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) { - var response getProfileResponse - err := json.NewDecoder(resp.Body).Decode(&response) - return response, err -} - -func decodePutProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) { - var response putProfileResponse - err := json.NewDecoder(resp.Body).Decode(&response) - return response, err -} - -func decodePatchProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) { - var response patchProfileResponse - err := json.NewDecoder(resp.Body).Decode(&response) - return response, err -} - -func decodeDeleteProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) { - var response deleteProfileResponse - err := json.NewDecoder(resp.Body).Decode(&response) - return response, err -} - -func decodeGetAddressesResponse(_ context.Context, resp *http.Response) (interface{}, error) { - var response getAddressesResponse - err := json.NewDecoder(resp.Body).Decode(&response) - return response, err -} - -func decodeGetAddressResponse(_ context.Context, resp *http.Response) (interface{}, error) { - var response getAddressResponse - err := json.NewDecoder(resp.Body).Decode(&response) - return response, err -} - -func decodePostAddressResponse(_ context.Context, resp *http.Response) (interface{}, error) { - var response postAddressResponse - err := json.NewDecoder(resp.Body).Decode(&response) - return response, err -} - -func decodeDeleteAddressResponse(_ context.Context, resp *http.Response) (interface{}, error) { - var response deleteAddressResponse - err := json.NewDecoder(resp.Body).Decode(&response) - return response, err -} - -// errorer is implemented by all concrete response types that may contain -// errors. It allows us to change the HTTP response code without needing to -// trigger an endpoint (transport-level) error. For more information, read the -// big comment in endpoints.go. -type errorer interface { - error() error -} - -// encodeResponse is the common method to encode all response types to the -// client. I chose to do it this way because, since we're using JSON, there's no -// reason to provide anything more specific. It's certainly possible to -// specialize on a per-response (per-method) basis. -func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { - if e, ok := response.(errorer); ok && e.error() != nil { - // Not a Go kit transport error, but a business-logic error. - // Provide those as HTTP errors. - encodeError(ctx, e.error(), w) - return nil - } - w.Header().Set("Content-Type", "application/json; charset=utf-8") - return json.NewEncoder(w).Encode(response) -} - -// encodeRequest likewise JSON-encodes the request to the HTTP request body. -// Don't use it directly as a transport/http.Client EncodeRequestFunc: -// profilesvc endpoints require mutating the HTTP method and request path. -func encodeRequest(_ context.Context, req *http.Request, request interface{}) error { - var buf bytes.Buffer - err := json.NewEncoder(&buf).Encode(request) - if err != nil { - return err - } - req.Body = ioutil.NopCloser(&buf) - return nil -} - -func encodeError(_ context.Context, err error, w http.ResponseWriter) { - if err == nil { - panic("encodeError with nil error") - } - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(codeFrom(err)) - json.NewEncoder(w).Encode(map[string]interface{}{ - "error": err.Error(), - }) -} - -func codeFrom(err error) int { - switch err { - case ErrNotFound: - return http.StatusNotFound - case ErrAlreadyExists, ErrInconsistentIDs: - return http.StatusBadRequest - default: - return http.StatusInternalServerError - } -} diff --git a/examples/shipping/README.md b/examples/shipping/README.md deleted file mode 100644 index 1a9a14e..0000000 --- a/examples/shipping/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# shipping - -This example demonstrates a more real-world application consisting of multiple services. - -## Description - -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). - -### Organization - -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. - -- __booking__ - used by the shipping company to book and route cargos. -- __handling__ - used by our staff around the world to register whenever the cargo has been received, loaded etc. -- __tracking__ - used by the customer to track the cargo along the route - -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. - -`inmem` contains in-memory implementations for the repositories found in the domain packages. - -The `routing` package provides a _domain service_ that is used to query an external application for possible routes. - -## Contributing - -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. diff --git a/examples/shipping/booking/endpoint.go b/examples/shipping/booking/endpoint.go deleted file mode 100644 index abe83f7..0000000 --- a/examples/shipping/booking/endpoint.go +++ /dev/null @@ -1,139 +0,0 @@ -package booking - -import ( - "context" - "time" - - "github.com/go-kit/kit/endpoint" - - "github.com/go-kit/kit/examples/shipping/cargo" - "github.com/go-kit/kit/examples/shipping/location" -) - -type bookCargoRequest struct { - Origin location.UNLocode - Destination location.UNLocode - ArrivalDeadline time.Time -} - -type bookCargoResponse struct { - ID cargo.TrackingID `json:"tracking_id,omitempty"` - Err error `json:"error,omitempty"` -} - -func (r bookCargoResponse) error() error { return r.Err } - -func makeBookCargoEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(bookCargoRequest) - id, err := s.BookNewCargo(req.Origin, req.Destination, req.ArrivalDeadline) - return bookCargoResponse{ID: id, Err: err}, nil - } -} - -type loadCargoRequest struct { - ID cargo.TrackingID -} - -type loadCargoResponse struct { - Cargo *Cargo `json:"cargo,omitempty"` - Err error `json:"error,omitempty"` -} - -func (r loadCargoResponse) error() error { return r.Err } - -func makeLoadCargoEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(loadCargoRequest) - c, err := s.LoadCargo(req.ID) - return loadCargoResponse{Cargo: &c, Err: err}, nil - } -} - -type requestRoutesRequest struct { - ID cargo.TrackingID -} - -type requestRoutesResponse struct { - Routes []cargo.Itinerary `json:"routes,omitempty"` - Err error `json:"error,omitempty"` -} - -func (r requestRoutesResponse) error() error { return r.Err } - -func makeRequestRoutesEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(requestRoutesRequest) - itin := s.RequestPossibleRoutesForCargo(req.ID) - return requestRoutesResponse{Routes: itin, Err: nil}, nil - } -} - -type assignToRouteRequest struct { - ID cargo.TrackingID - Itinerary cargo.Itinerary -} - -type assignToRouteResponse struct { - Err error `json:"error,omitempty"` -} - -func (r assignToRouteResponse) error() error { return r.Err } - -func makeAssignToRouteEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(assignToRouteRequest) - err := s.AssignCargoToRoute(req.ID, req.Itinerary) - return assignToRouteResponse{Err: err}, nil - } -} - -type changeDestinationRequest struct { - ID cargo.TrackingID - Destination location.UNLocode -} - -type changeDestinationResponse struct { - Err error `json:"error,omitempty"` -} - -func (r changeDestinationResponse) error() error { return r.Err } - -func makeChangeDestinationEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(changeDestinationRequest) - err := s.ChangeDestination(req.ID, req.Destination) - return changeDestinationResponse{Err: err}, nil - } -} - -type listCargosRequest struct{} - -type listCargosResponse struct { - Cargos []Cargo `json:"cargos,omitempty"` - Err error `json:"error,omitempty"` -} - -func (r listCargosResponse) error() error { return r.Err } - -func makeListCargosEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - _ = request.(listCargosRequest) - return listCargosResponse{Cargos: s.Cargos(), Err: nil}, nil - } -} - -type listLocationsRequest struct { -} - -type listLocationsResponse struct { - Locations []Location `json:"locations,omitempty"` - Err error `json:"error,omitempty"` -} - -func makeListLocationsEndpoint(s Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - _ = request.(listLocationsRequest) - return listLocationsResponse{Locations: s.Locations(), Err: nil}, nil - } -} diff --git a/examples/shipping/booking/instrumenting.go b/examples/shipping/booking/instrumenting.go deleted file mode 100644 index b9b03b9..0000000 --- a/examples/shipping/booking/instrumenting.go +++ /dev/null @@ -1,88 +0,0 @@ -package booking - -import ( - "time" - - "github.com/go-kit/kit/metrics" - - "github.com/go-kit/kit/examples/shipping/cargo" - "github.com/go-kit/kit/examples/shipping/location" -) - -type instrumentingService struct { - requestCount metrics.Counter - requestLatency metrics.Histogram - Service -} - -// NewInstrumentingService returns an instance of an instrumenting Service. -func NewInstrumentingService(counter metrics.Counter, latency metrics.Histogram, s Service) Service { - return &instrumentingService{ - requestCount: counter, - requestLatency: latency, - Service: s, - } -} - -func (s *instrumentingService) BookNewCargo(origin, destination location.UNLocode, deadline time.Time) (cargo.TrackingID, error) { - defer func(begin time.Time) { - s.requestCount.With("method", "book").Add(1) - s.requestLatency.With("method", "book").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return s.Service.BookNewCargo(origin, destination, deadline) -} - -func (s *instrumentingService) LoadCargo(id cargo.TrackingID) (c Cargo, err error) { - defer func(begin time.Time) { - s.requestCount.With("method", "load").Add(1) - s.requestLatency.With("method", "load").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return s.Service.LoadCargo(id) -} - -func (s *instrumentingService) RequestPossibleRoutesForCargo(id cargo.TrackingID) []cargo.Itinerary { - defer func(begin time.Time) { - s.requestCount.With("method", "request_routes").Add(1) - s.requestLatency.With("method", "request_routes").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return s.Service.RequestPossibleRoutesForCargo(id) -} - -func (s *instrumentingService) AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) (err error) { - defer func(begin time.Time) { - s.requestCount.With("method", "assign_to_route").Add(1) - s.requestLatency.With("method", "assign_to_route").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return s.Service.AssignCargoToRoute(id, itinerary) -} - -func (s *instrumentingService) ChangeDestination(id cargo.TrackingID, l location.UNLocode) (err error) { - defer func(begin time.Time) { - s.requestCount.With("method", "change_destination").Add(1) - s.requestLatency.With("method", "change_destination").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return s.Service.ChangeDestination(id, l) -} - -func (s *instrumentingService) Cargos() []Cargo { - defer func(begin time.Time) { - s.requestCount.With("method", "list_cargos").Add(1) - s.requestLatency.With("method", "list_cargos").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return s.Service.Cargos() -} - -func (s *instrumentingService) Locations() []Location { - defer func(begin time.Time) { - s.requestCount.With("method", "list_locations").Add(1) - s.requestLatency.With("method", "list_locations").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return s.Service.Locations() -} diff --git a/examples/shipping/booking/logging.go b/examples/shipping/booking/logging.go deleted file mode 100644 index 931d430..0000000 --- a/examples/shipping/booking/logging.go +++ /dev/null @@ -1,102 +0,0 @@ -package booking - -import ( - "time" - - "github.com/go-kit/kit/log" - - "github.com/go-kit/kit/examples/shipping/cargo" - "github.com/go-kit/kit/examples/shipping/location" -) - -type loggingService struct { - logger log.Logger - Service -} - -// NewLoggingService returns a new instance of a logging Service. -func NewLoggingService(logger log.Logger, s Service) Service { - return &loggingService{logger, s} -} - -func (s *loggingService) BookNewCargo(origin location.UNLocode, destination location.UNLocode, deadline time.Time) (id cargo.TrackingID, err error) { - defer func(begin time.Time) { - s.logger.Log( - "method", "book", - "origin", origin, - "destination", destination, - "arrival_deadline", deadline, - "took", time.Since(begin), - "err", err, - ) - }(time.Now()) - return s.Service.BookNewCargo(origin, destination, deadline) -} - -func (s *loggingService) LoadCargo(id cargo.TrackingID) (c Cargo, err error) { - defer func(begin time.Time) { - s.logger.Log( - "method", "load", - "tracking_id", id, - "took", time.Since(begin), - "err", err, - ) - }(time.Now()) - return s.Service.LoadCargo(id) -} - -func (s *loggingService) RequestPossibleRoutesForCargo(id cargo.TrackingID) []cargo.Itinerary { - defer func(begin time.Time) { - s.logger.Log( - "method", "request_routes", - "tracking_id", id, - "took", time.Since(begin), - ) - }(time.Now()) - return s.Service.RequestPossibleRoutesForCargo(id) -} - -func (s *loggingService) AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) (err error) { - defer func(begin time.Time) { - s.logger.Log( - "method", "assign_to_route", - "tracking_id", id, - "took", time.Since(begin), - "err", err, - ) - }(time.Now()) - return s.Service.AssignCargoToRoute(id, itinerary) -} - -func (s *loggingService) ChangeDestination(id cargo.TrackingID, l location.UNLocode) (err error) { - defer func(begin time.Time) { - s.logger.Log( - "method", "change_destination", - "tracking_id", id, - "destination", l, - "took", time.Since(begin), - "err", err, - ) - }(time.Now()) - return s.Service.ChangeDestination(id, l) -} - -func (s *loggingService) Cargos() []Cargo { - defer func(begin time.Time) { - s.logger.Log( - "method", "list_cargos", - "took", time.Since(begin), - ) - }(time.Now()) - return s.Service.Cargos() -} - -func (s *loggingService) Locations() []Location { - defer func(begin time.Time) { - s.logger.Log( - "method", "list_locations", - "took", time.Since(begin), - ) - }(time.Now()) - return s.Service.Locations() -} diff --git a/examples/shipping/booking/service.go b/examples/shipping/booking/service.go deleted file mode 100644 index 8689a5a..0000000 --- a/examples/shipping/booking/service.go +++ /dev/null @@ -1,197 +0,0 @@ -// Package booking provides the use-case of booking a cargo. Used by views -// facing an administrator. -package booking - -import ( - "errors" - "time" - - "github.com/go-kit/kit/examples/shipping/cargo" - "github.com/go-kit/kit/examples/shipping/location" - "github.com/go-kit/kit/examples/shipping/routing" -) - -// ErrInvalidArgument is returned when one or more arguments are invalid. -var ErrInvalidArgument = errors.New("invalid argument") - -// Service is the interface that provides booking methods. -type Service interface { - // BookNewCargo registers a new cargo in the tracking system, not yet - // routed. - BookNewCargo(origin location.UNLocode, destination location.UNLocode, deadline time.Time) (cargo.TrackingID, error) - - // LoadCargo returns a read model of a cargo. - LoadCargo(id cargo.TrackingID) (Cargo, error) - - // RequestPossibleRoutesForCargo requests a list of itineraries describing - // possible routes for this cargo. - RequestPossibleRoutesForCargo(id cargo.TrackingID) []cargo.Itinerary - - // AssignCargoToRoute assigns a cargo to the route specified by the - // itinerary. - AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) error - - // ChangeDestination changes the destination of a cargo. - ChangeDestination(id cargo.TrackingID, destination location.UNLocode) error - - // Cargos returns a list of all cargos that have been booked. - Cargos() []Cargo - - // Locations returns a list of registered locations. - Locations() []Location -} - -type service struct { - cargos cargo.Repository - locations location.Repository - handlingEvents cargo.HandlingEventRepository - routingService routing.Service -} - -func (s *service) AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) error { - if id == "" || len(itinerary.Legs) == 0 { - return ErrInvalidArgument - } - - c, err := s.cargos.Find(id) - if err != nil { - return err - } - - c.AssignToRoute(itinerary) - - return s.cargos.Store(c) -} - -func (s *service) BookNewCargo(origin, destination location.UNLocode, deadline time.Time) (cargo.TrackingID, error) { - if origin == "" || destination == "" || deadline.IsZero() { - return "", ErrInvalidArgument - } - - id := cargo.NextTrackingID() - rs := cargo.RouteSpecification{ - Origin: origin, - Destination: destination, - ArrivalDeadline: deadline, - } - - c := cargo.New(id, rs) - - if err := s.cargos.Store(c); err != nil { - return "", err - } - - return c.TrackingID, nil -} - -func (s *service) LoadCargo(id cargo.TrackingID) (Cargo, error) { - if id == "" { - return Cargo{}, ErrInvalidArgument - } - - c, err := s.cargos.Find(id) - if err != nil { - return Cargo{}, err - } - - return assemble(c, s.handlingEvents), nil -} - -func (s *service) ChangeDestination(id cargo.TrackingID, destination location.UNLocode) error { - if id == "" || destination == "" { - return ErrInvalidArgument - } - - c, err := s.cargos.Find(id) - if err != nil { - return err - } - - l, err := s.locations.Find(destination) - if err != nil { - return err - } - - c.SpecifyNewRoute(cargo.RouteSpecification{ - Origin: c.Origin, - Destination: l.UNLocode, - ArrivalDeadline: c.RouteSpecification.ArrivalDeadline, - }) - - if err := s.cargos.Store(c); err != nil { - return err - } - - return nil -} - -func (s *service) RequestPossibleRoutesForCargo(id cargo.TrackingID) []cargo.Itinerary { - if id == "" { - return nil - } - - c, err := s.cargos.Find(id) - if err != nil { - return []cargo.Itinerary{} - } - - return s.routingService.FetchRoutesForSpecification(c.RouteSpecification) -} - -func (s *service) Cargos() []Cargo { - var result []Cargo - for _, c := range s.cargos.FindAll() { - result = append(result, assemble(c, s.handlingEvents)) - } - return result -} - -func (s *service) Locations() []Location { - var result []Location - for _, v := range s.locations.FindAll() { - result = append(result, Location{ - UNLocode: string(v.UNLocode), - Name: v.Name, - }) - } - return result -} - -// NewService creates a booking service with necessary dependencies. -func NewService(cargos cargo.Repository, locations location.Repository, events cargo.HandlingEventRepository, rs routing.Service) Service { - return &service{ - cargos: cargos, - locations: locations, - handlingEvents: events, - routingService: rs, - } -} - -// Location is a read model for booking views. -type Location struct { - UNLocode string `json:"locode"` - Name string `json:"name"` -} - -// Cargo is a read model for booking views. -type Cargo struct { - ArrivalDeadline time.Time `json:"arrival_deadline"` - Destination string `json:"destination"` - Legs []cargo.Leg `json:"legs,omitempty"` - Misrouted bool `json:"misrouted"` - Origin string `json:"origin"` - Routed bool `json:"routed"` - TrackingID string `json:"tracking_id"` -} - -func assemble(c *cargo.Cargo, events cargo.HandlingEventRepository) Cargo { - return Cargo{ - TrackingID: string(c.TrackingID), - Origin: string(c.Origin), - Destination: string(c.RouteSpecification.Destination), - Misrouted: c.Delivery.RoutingStatus == cargo.Misrouted, - Routed: !c.Itinerary.IsEmpty(), - ArrivalDeadline: c.RouteSpecification.ArrivalDeadline, - Legs: c.Itinerary.Legs, - } -} diff --git a/examples/shipping/booking/transport.go b/examples/shipping/booking/transport.go deleted file mode 100644 index 8cf11a2..0000000 --- a/examples/shipping/booking/transport.go +++ /dev/null @@ -1,194 +0,0 @@ -package booking - -import ( - "context" - "encoding/json" - "errors" - "net/http" - "time" - - "github.com/gorilla/mux" - - kitlog "github.com/go-kit/kit/log" - kithttp "github.com/go-kit/kit/transport/http" - - "github.com/go-kit/kit/examples/shipping/cargo" - "github.com/go-kit/kit/examples/shipping/location" -) - -// MakeHandler returns a handler for the booking service. -func MakeHandler(bs Service, logger kitlog.Logger) http.Handler { - opts := []kithttp.ServerOption{ - kithttp.ServerErrorLogger(logger), - kithttp.ServerErrorEncoder(encodeError), - } - - bookCargoHandler := kithttp.NewServer( - makeBookCargoEndpoint(bs), - decodeBookCargoRequest, - encodeResponse, - opts..., - ) - loadCargoHandler := kithttp.NewServer( - makeLoadCargoEndpoint(bs), - decodeLoadCargoRequest, - encodeResponse, - opts..., - ) - requestRoutesHandler := kithttp.NewServer( - makeRequestRoutesEndpoint(bs), - decodeRequestRoutesRequest, - encodeResponse, - opts..., - ) - assignToRouteHandler := kithttp.NewServer( - makeAssignToRouteEndpoint(bs), - decodeAssignToRouteRequest, - encodeResponse, - opts..., - ) - changeDestinationHandler := kithttp.NewServer( - makeChangeDestinationEndpoint(bs), - decodeChangeDestinationRequest, - encodeResponse, - opts..., - ) - listCargosHandler := kithttp.NewServer( - makeListCargosEndpoint(bs), - decodeListCargosRequest, - encodeResponse, - opts..., - ) - listLocationsHandler := kithttp.NewServer( - makeListLocationsEndpoint(bs), - decodeListLocationsRequest, - encodeResponse, - opts..., - ) - - r := mux.NewRouter() - - r.Handle("/booking/v1/cargos", bookCargoHandler).Methods("POST") - r.Handle("/booking/v1/cargos", listCargosHandler).Methods("GET") - r.Handle("/booking/v1/cargos/{id}", loadCargoHandler).Methods("GET") - r.Handle("/booking/v1/cargos/{id}/request_routes", requestRoutesHandler).Methods("GET") - r.Handle("/booking/v1/cargos/{id}/assign_to_route", assignToRouteHandler).Methods("POST") - r.Handle("/booking/v1/cargos/{id}/change_destination", changeDestinationHandler).Methods("POST") - r.Handle("/booking/v1/locations", listLocationsHandler).Methods("GET") - - return r -} - -var errBadRoute = errors.New("bad route") - -func decodeBookCargoRequest(_ context.Context, r *http.Request) (interface{}, error) { - var body struct { - Origin string `json:"origin"` - Destination string `json:"destination"` - ArrivalDeadline time.Time `json:"arrival_deadline"` - } - - if err := json.NewDecoder(r.Body).Decode(&body); err != nil { - return nil, err - } - - return bookCargoRequest{ - Origin: location.UNLocode(body.Origin), - Destination: location.UNLocode(body.Destination), - ArrivalDeadline: body.ArrivalDeadline, - }, nil -} - -func decodeLoadCargoRequest(_ context.Context, r *http.Request) (interface{}, error) { - vars := mux.Vars(r) - id, ok := vars["id"] - if !ok { - return nil, errBadRoute - } - return loadCargoRequest{ID: cargo.TrackingID(id)}, nil -} - -func decodeRequestRoutesRequest(_ context.Context, r *http.Request) (interface{}, error) { - vars := mux.Vars(r) - id, ok := vars["id"] - if !ok { - return nil, errBadRoute - } - return requestRoutesRequest{ID: cargo.TrackingID(id)}, nil -} - -func decodeAssignToRouteRequest(_ context.Context, r *http.Request) (interface{}, error) { - vars := mux.Vars(r) - id, ok := vars["id"] - if !ok { - return nil, errBadRoute - } - - var itinerary cargo.Itinerary - if err := json.NewDecoder(r.Body).Decode(&itinerary); err != nil { - return nil, err - } - - return assignToRouteRequest{ - ID: cargo.TrackingID(id), - Itinerary: itinerary, - }, nil -} - -func decodeChangeDestinationRequest(_ context.Context, r *http.Request) (interface{}, error) { - vars := mux.Vars(r) - id, ok := vars["id"] - if !ok { - return nil, errBadRoute - } - - var body struct { - Destination string `json:"destination"` - } - - if err := json.NewDecoder(r.Body).Decode(&body); err != nil { - return nil, err - } - - return changeDestinationRequest{ - ID: cargo.TrackingID(id), - Destination: location.UNLocode(body.Destination), - }, nil -} - -func decodeListCargosRequest(_ context.Context, r *http.Request) (interface{}, error) { - return listCargosRequest{}, nil -} - -func decodeListLocationsRequest(_ context.Context, r *http.Request) (interface{}, error) { - return listLocationsRequest{}, nil -} - -func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { - if e, ok := response.(errorer); ok && e.error() != nil { - encodeError(ctx, e.error(), w) - return nil - } - w.Header().Set("Content-Type", "application/json; charset=utf-8") - return json.NewEncoder(w).Encode(response) -} - -type errorer interface { - error() error -} - -// encode errors from business-logic -func encodeError(_ context.Context, err error, w http.ResponseWriter) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - switch err { - case cargo.ErrUnknown: - w.WriteHeader(http.StatusNotFound) - case ErrInvalidArgument: - w.WriteHeader(http.StatusBadRequest) - default: - w.WriteHeader(http.StatusInternalServerError) - } - json.NewEncoder(w).Encode(map[string]interface{}{ - "error": err.Error(), - }) -} diff --git a/examples/shipping/cargo/cargo.go b/examples/shipping/cargo/cargo.go deleted file mode 100644 index a9440f5..0000000 --- a/examples/shipping/cargo/cargo.go +++ /dev/null @@ -1,137 +0,0 @@ -// Package cargo contains the heart of the domain model. -package cargo - -import ( - "errors" - "strings" - "time" - - "github.com/pborman/uuid" - - "github.com/go-kit/kit/examples/shipping/location" -) - -// TrackingID uniquely identifies a particular cargo. -type TrackingID string - -// Cargo is the central class in the domain model. -type Cargo struct { - TrackingID TrackingID - Origin location.UNLocode - RouteSpecification RouteSpecification - Itinerary Itinerary - Delivery Delivery -} - -// SpecifyNewRoute specifies a new route for this cargo. -func (c *Cargo) SpecifyNewRoute(rs RouteSpecification) { - c.RouteSpecification = rs - c.Delivery = c.Delivery.UpdateOnRouting(c.RouteSpecification, c.Itinerary) -} - -// AssignToRoute attaches a new itinerary to this cargo. -func (c *Cargo) AssignToRoute(itinerary Itinerary) { - c.Itinerary = itinerary - c.Delivery = c.Delivery.UpdateOnRouting(c.RouteSpecification, c.Itinerary) -} - -// DeriveDeliveryProgress updates all aspects of the cargo aggregate status -// based on the current route specification, itinerary and handling of the cargo. -func (c *Cargo) DeriveDeliveryProgress(history HandlingHistory) { - c.Delivery = DeriveDeliveryFrom(c.RouteSpecification, c.Itinerary, history) -} - -// New creates a new, unrouted cargo. -func New(id TrackingID, rs RouteSpecification) *Cargo { - itinerary := Itinerary{} - history := HandlingHistory{make([]HandlingEvent, 0)} - - return &Cargo{ - TrackingID: id, - Origin: rs.Origin, - RouteSpecification: rs, - Delivery: DeriveDeliveryFrom(rs, itinerary, history), - } -} - -// Repository provides access a cargo store. -type Repository interface { - Store(cargo *Cargo) error - Find(id TrackingID) (*Cargo, error) - FindAll() []*Cargo -} - -// ErrUnknown is used when a cargo could not be found. -var ErrUnknown = errors.New("unknown cargo") - -// NextTrackingID generates a new tracking ID. -// TODO: Move to infrastructure(?) -func NextTrackingID() TrackingID { - return TrackingID(strings.Split(strings.ToUpper(uuid.New()), "-")[0]) -} - -// RouteSpecification Contains information about a route: its origin, -// destination and arrival deadline. -type RouteSpecification struct { - Origin location.UNLocode - Destination location.UNLocode - ArrivalDeadline time.Time -} - -// IsSatisfiedBy checks whether provided itinerary satisfies this -// specification. -func (s RouteSpecification) IsSatisfiedBy(itinerary Itinerary) bool { - return itinerary.Legs != nil && - s.Origin == itinerary.InitialDepartureLocation() && - s.Destination == itinerary.FinalArrivalLocation() -} - -// RoutingStatus describes status of cargo routing. -type RoutingStatus int - -// Valid routing statuses. -const ( - NotRouted RoutingStatus = iota - Misrouted - Routed -) - -func (s RoutingStatus) String() string { - switch s { - case NotRouted: - return "Not routed" - case Misrouted: - return "Misrouted" - case Routed: - return "Routed" - } - return "" -} - -// TransportStatus describes status of cargo transportation. -type TransportStatus int - -// Valid transport statuses. -const ( - NotReceived TransportStatus = iota - InPort - OnboardCarrier - Claimed - Unknown -) - -func (s TransportStatus) String() string { - switch s { - case NotReceived: - return "Not received" - case InPort: - return "In port" - case OnboardCarrier: - return "Onboard carrier" - case Claimed: - return "Claimed" - case Unknown: - return "Unknown" - } - return "" -} diff --git a/examples/shipping/cargo/delivery.go b/examples/shipping/cargo/delivery.go deleted file mode 100644 index 34f079d..0000000 --- a/examples/shipping/cargo/delivery.go +++ /dev/null @@ -1,174 +0,0 @@ -package cargo - -import ( - "time" - - "github.com/go-kit/kit/examples/shipping/location" - "github.com/go-kit/kit/examples/shipping/voyage" -) - -// Delivery is the actual transportation of the cargo, as opposed to the -// customer requirement (RouteSpecification) and the plan (Itinerary). -type Delivery struct { - Itinerary Itinerary - RouteSpecification RouteSpecification - RoutingStatus RoutingStatus - TransportStatus TransportStatus - NextExpectedActivity HandlingActivity - LastEvent HandlingEvent - LastKnownLocation location.UNLocode - CurrentVoyage voyage.Number - ETA time.Time - IsMisdirected bool - IsUnloadedAtDestination bool -} - -// UpdateOnRouting creates a new delivery snapshot to reflect changes in -// routing, i.e. when the route specification or the itinerary has changed but -// no additional handling of the cargo has been performed. -func (d Delivery) UpdateOnRouting(rs RouteSpecification, itinerary Itinerary) Delivery { - return newDelivery(d.LastEvent, itinerary, rs) -} - -// IsOnTrack checks if the delivery is on track. -func (d Delivery) IsOnTrack() bool { - return d.RoutingStatus == Routed && !d.IsMisdirected -} - -// DeriveDeliveryFrom creates a new delivery snapshot based on the complete -// handling history of a cargo, as well as its route specification and -// itinerary. -func DeriveDeliveryFrom(rs RouteSpecification, itinerary Itinerary, history HandlingHistory) Delivery { - lastEvent, _ := history.MostRecentlyCompletedEvent() - return newDelivery(lastEvent, itinerary, rs) -} - -// newDelivery creates a up-to-date delivery based on an handling event, -// itinerary and a route specification. -func newDelivery(lastEvent HandlingEvent, itinerary Itinerary, rs RouteSpecification) Delivery { - var ( - routingStatus = calculateRoutingStatus(itinerary, rs) - transportStatus = calculateTransportStatus(lastEvent) - lastKnownLocation = calculateLastKnownLocation(lastEvent) - isMisdirected = calculateMisdirectedStatus(lastEvent, itinerary) - isUnloadedAtDestination = calculateUnloadedAtDestination(lastEvent, rs) - currentVoyage = calculateCurrentVoyage(transportStatus, lastEvent) - ) - - d := Delivery{ - LastEvent: lastEvent, - Itinerary: itinerary, - RouteSpecification: rs, - RoutingStatus: routingStatus, - TransportStatus: transportStatus, - LastKnownLocation: lastKnownLocation, - IsMisdirected: isMisdirected, - IsUnloadedAtDestination: isUnloadedAtDestination, - CurrentVoyage: currentVoyage, - } - - d.NextExpectedActivity = calculateNextExpectedActivity(d) - d.ETA = calculateETA(d) - - return d -} - -// Below are internal functions used when creating a new delivery. - -func calculateRoutingStatus(itinerary Itinerary, rs RouteSpecification) RoutingStatus { - if itinerary.Legs == nil { - return NotRouted - } - - if rs.IsSatisfiedBy(itinerary) { - return Routed - } - - return Misrouted -} - -func calculateMisdirectedStatus(event HandlingEvent, itinerary Itinerary) bool { - if event.Activity.Type == NotHandled { - return false - } - - return !itinerary.IsExpected(event) -} - -func calculateUnloadedAtDestination(event HandlingEvent, rs RouteSpecification) bool { - if event.Activity.Type == NotHandled { - return false - } - - return event.Activity.Type == Unload && rs.Destination == event.Activity.Location -} - -func calculateTransportStatus(event HandlingEvent) TransportStatus { - switch event.Activity.Type { - case NotHandled: - return NotReceived - case Load: - return OnboardCarrier - case Unload: - return InPort - case Receive: - return InPort - case Customs: - return InPort - case Claim: - return Claimed - } - return Unknown -} - -func calculateLastKnownLocation(event HandlingEvent) location.UNLocode { - return event.Activity.Location -} - -func calculateNextExpectedActivity(d Delivery) HandlingActivity { - if !d.IsOnTrack() { - return HandlingActivity{} - } - - switch d.LastEvent.Activity.Type { - case NotHandled: - return HandlingActivity{Type: Receive, Location: d.RouteSpecification.Origin} - case Receive: - l := d.Itinerary.Legs[0] - return HandlingActivity{Type: Load, Location: l.LoadLocation, VoyageNumber: l.VoyageNumber} - case Load: - for _, l := range d.Itinerary.Legs { - if l.LoadLocation == d.LastEvent.Activity.Location { - return HandlingActivity{Type: Unload, Location: l.UnloadLocation, VoyageNumber: l.VoyageNumber} - } - } - case Unload: - for i, l := range d.Itinerary.Legs { - if l.UnloadLocation == d.LastEvent.Activity.Location { - if i < len(d.Itinerary.Legs)-1 { - return HandlingActivity{Type: Load, Location: d.Itinerary.Legs[i+1].LoadLocation, VoyageNumber: d.Itinerary.Legs[i+1].VoyageNumber} - } - - return HandlingActivity{Type: Claim, Location: l.UnloadLocation} - } - } - } - - return HandlingActivity{} -} - -func calculateCurrentVoyage(transportStatus TransportStatus, event HandlingEvent) voyage.Number { - if transportStatus == OnboardCarrier && event.Activity.Type != NotHandled { - return event.Activity.VoyageNumber - } - - return voyage.Number("") -} - -func calculateETA(d Delivery) time.Time { - if !d.IsOnTrack() { - return time.Time{} - } - - return d.Itinerary.FinalArrivalTime() -} diff --git a/examples/shipping/cargo/handling.go b/examples/shipping/cargo/handling.go deleted file mode 100644 index bec8509..0000000 --- a/examples/shipping/cargo/handling.go +++ /dev/null @@ -1,121 +0,0 @@ -package cargo - -// TODO: It would make sense to have this in its own package. Unfortunately, -// then there would be a circular dependency between the cargo and handling -// packages since cargo.Delivery would use handling.HandlingEvent and -// handling.HandlingEvent would use cargo.TrackingID. Also, -// HandlingEventFactory depends on the cargo repository. -// -// It would make sense not having the cargo package depend on handling. - -import ( - "errors" - "time" - - "github.com/go-kit/kit/examples/shipping/location" - "github.com/go-kit/kit/examples/shipping/voyage" -) - -// HandlingActivity represents how and where a cargo can be handled, and can -// be used to express predictions about what is expected to happen to a cargo -// in the future. -type HandlingActivity struct { - Type HandlingEventType - Location location.UNLocode - VoyageNumber voyage.Number -} - -// HandlingEvent is used to register the event when, for instance, a cargo is -// unloaded from a carrier at a some location at a given time. -type HandlingEvent struct { - TrackingID TrackingID - Activity HandlingActivity -} - -// HandlingEventType describes type of a handling event. -type HandlingEventType int - -// Valid handling event types. -const ( - NotHandled HandlingEventType = iota - Load - Unload - Receive - Claim - Customs -) - -func (t HandlingEventType) String() string { - switch t { - case NotHandled: - return "Not Handled" - case Load: - return "Load" - case Unload: - return "Unload" - case Receive: - return "Receive" - case Claim: - return "Claim" - case Customs: - return "Customs" - } - - return "" -} - -// HandlingHistory is the handling history of a cargo. -type HandlingHistory struct { - HandlingEvents []HandlingEvent -} - -// MostRecentlyCompletedEvent returns most recently completed handling event. -func (h HandlingHistory) MostRecentlyCompletedEvent() (HandlingEvent, error) { - if len(h.HandlingEvents) == 0 { - return HandlingEvent{}, errors.New("delivery history is empty") - } - - return h.HandlingEvents[len(h.HandlingEvents)-1], nil -} - -// HandlingEventRepository provides access a handling event store. -type HandlingEventRepository interface { - Store(e HandlingEvent) - QueryHandlingHistory(TrackingID) HandlingHistory -} - -// HandlingEventFactory creates handling events. -type HandlingEventFactory struct { - CargoRepository Repository - VoyageRepository voyage.Repository - LocationRepository location.Repository -} - -// CreateHandlingEvent creates a validated handling event. -func (f *HandlingEventFactory) CreateHandlingEvent(registered time.Time, completed time.Time, id TrackingID, - voyageNumber voyage.Number, unLocode location.UNLocode, eventType HandlingEventType) (HandlingEvent, error) { - - if _, err := f.CargoRepository.Find(id); err != nil { - return HandlingEvent{}, err - } - - if _, err := f.VoyageRepository.Find(voyageNumber); err != nil { - // TODO: This is pretty ugly, but when creating a Receive event, the voyage number is not known. - if len(voyageNumber) > 0 { - return HandlingEvent{}, err - } - } - - if _, err := f.LocationRepository.Find(unLocode); err != nil { - return HandlingEvent{}, err - } - - return HandlingEvent{ - TrackingID: id, - Activity: HandlingActivity{ - Type: eventType, - Location: unLocode, - VoyageNumber: voyageNumber, - }, - }, nil -} diff --git a/examples/shipping/cargo/itinerary.go b/examples/shipping/cargo/itinerary.go deleted file mode 100644 index 6b5088e..0000000 --- a/examples/shipping/cargo/itinerary.go +++ /dev/null @@ -1,91 +0,0 @@ -package cargo - -import ( - "time" - - "github.com/go-kit/kit/examples/shipping/location" - "github.com/go-kit/kit/examples/shipping/voyage" -) - -// Leg describes the transportation between two locations on a voyage. -type Leg struct { - VoyageNumber voyage.Number `json:"voyage_number"` - LoadLocation location.UNLocode `json:"from"` - UnloadLocation location.UNLocode `json:"to"` - LoadTime time.Time `json:"load_time"` - UnloadTime time.Time `json:"unload_time"` -} - -// NewLeg creates a new itinerary leg. -func NewLeg(voyageNumber voyage.Number, loadLocation, unloadLocation location.UNLocode, loadTime, unloadTime time.Time) Leg { - return Leg{ - VoyageNumber: voyageNumber, - LoadLocation: loadLocation, - UnloadLocation: unloadLocation, - LoadTime: loadTime, - UnloadTime: unloadTime, - } -} - -// Itinerary specifies steps required to transport a cargo from its origin to -// destination. -type Itinerary struct { - Legs []Leg `json:"legs"` -} - -// InitialDepartureLocation returns the start of the itinerary. -func (i Itinerary) InitialDepartureLocation() location.UNLocode { - if i.IsEmpty() { - return location.UNLocode("") - } - return i.Legs[0].LoadLocation -} - -// FinalArrivalLocation returns the end of the itinerary. -func (i Itinerary) FinalArrivalLocation() location.UNLocode { - if i.IsEmpty() { - return location.UNLocode("") - } - return i.Legs[len(i.Legs)-1].UnloadLocation -} - -// FinalArrivalTime returns the expected arrival time at final destination. -func (i Itinerary) FinalArrivalTime() time.Time { - return i.Legs[len(i.Legs)-1].UnloadTime -} - -// IsEmpty checks if the itinerary contains at least one leg. -func (i Itinerary) IsEmpty() bool { - return i.Legs == nil || len(i.Legs) == 0 -} - -// IsExpected checks if the given handling event is expected when executing -// this itinerary. -func (i Itinerary) IsExpected(event HandlingEvent) bool { - if i.IsEmpty() { - return true - } - - switch event.Activity.Type { - case Receive: - return i.InitialDepartureLocation() == event.Activity.Location - case Load: - for _, l := range i.Legs { - if l.LoadLocation == event.Activity.Location && l.VoyageNumber == event.Activity.VoyageNumber { - return true - } - } - return false - case Unload: - for _, l := range i.Legs { - if l.UnloadLocation == event.Activity.Location && l.VoyageNumber == event.Activity.VoyageNumber { - return true - } - } - return false - case Claim: - return i.FinalArrivalLocation() == event.Activity.Location - } - - return true -} diff --git a/examples/shipping/handling/endpoint.go b/examples/shipping/handling/endpoint.go deleted file mode 100644 index 555d087..0000000 --- a/examples/shipping/handling/endpoint.go +++ /dev/null @@ -1,34 +0,0 @@ -package handling - -import ( - "context" - "time" - - "github.com/go-kit/kit/endpoint" - - "github.com/go-kit/kit/examples/shipping/cargo" - "github.com/go-kit/kit/examples/shipping/location" - "github.com/go-kit/kit/examples/shipping/voyage" -) - -type registerIncidentRequest struct { - ID cargo.TrackingID - Location location.UNLocode - Voyage voyage.Number - EventType cargo.HandlingEventType - CompletionTime time.Time -} - -type registerIncidentResponse struct { - Err error `json:"error,omitempty"` -} - -func (r registerIncidentResponse) error() error { return r.Err } - -func makeRegisterIncidentEndpoint(hs Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(registerIncidentRequest) - err := hs.RegisterHandlingEvent(req.CompletionTime, req.ID, req.Voyage, req.Location, req.EventType) - return registerIncidentResponse{Err: err}, nil - } -} diff --git a/examples/shipping/handling/instrumenting.go b/examples/shipping/handling/instrumenting.go deleted file mode 100644 index fecce04..0000000 --- a/examples/shipping/handling/instrumenting.go +++ /dev/null @@ -1,37 +0,0 @@ -package handling - -import ( - "time" - - "github.com/go-kit/kit/metrics" - - "github.com/go-kit/kit/examples/shipping/cargo" - "github.com/go-kit/kit/examples/shipping/location" - "github.com/go-kit/kit/examples/shipping/voyage" -) - -type instrumentingService struct { - requestCount metrics.Counter - requestLatency metrics.Histogram - Service -} - -// NewInstrumentingService returns an instance of an instrumenting Service. -func NewInstrumentingService(counter metrics.Counter, latency metrics.Histogram, s Service) Service { - return &instrumentingService{ - requestCount: counter, - requestLatency: latency, - Service: s, - } -} - -func (s *instrumentingService) RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number, - loc location.UNLocode, eventType cargo.HandlingEventType) error { - - defer func(begin time.Time) { - s.requestCount.With("method", "register_incident").Add(1) - s.requestLatency.With("method", "register_incident").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return s.Service.RegisterHandlingEvent(completed, id, voyageNumber, loc, eventType) -} diff --git a/examples/shipping/handling/logging.go b/examples/shipping/handling/logging.go deleted file mode 100644 index 84722fb..0000000 --- a/examples/shipping/handling/logging.go +++ /dev/null @@ -1,38 +0,0 @@ -package handling - -import ( - "time" - - "github.com/go-kit/kit/log" - - "github.com/go-kit/kit/examples/shipping/cargo" - "github.com/go-kit/kit/examples/shipping/location" - "github.com/go-kit/kit/examples/shipping/voyage" -) - -type loggingService struct { - logger log.Logger - Service -} - -// NewLoggingService returns a new instance of a logging Service. -func NewLoggingService(logger log.Logger, s Service) Service { - return &loggingService{logger, s} -} - -func (s *loggingService) RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number, - unLocode location.UNLocode, eventType cargo.HandlingEventType) (err error) { - defer func(begin time.Time) { - s.logger.Log( - "method", "register_incident", - "tracking_id", id, - "location", unLocode, - "voyage", voyageNumber, - "event_type", eventType, - "completion_time", completed, - "took", time.Since(begin), - "err", err, - ) - }(time.Now()) - return s.Service.RegisterHandlingEvent(completed, id, voyageNumber, unLocode, eventType) -} diff --git a/examples/shipping/handling/service.go b/examples/shipping/handling/service.go deleted file mode 100644 index 83d503a..0000000 --- a/examples/shipping/handling/service.go +++ /dev/null @@ -1,76 +0,0 @@ -// Package handling provides the use-case for registering incidents. Used by -// views facing the people handling the cargo along its route. -package handling - -import ( - "errors" - "time" - - "github.com/go-kit/kit/examples/shipping/cargo" - "github.com/go-kit/kit/examples/shipping/inspection" - "github.com/go-kit/kit/examples/shipping/location" - "github.com/go-kit/kit/examples/shipping/voyage" -) - -// ErrInvalidArgument is returned when one or more arguments are invalid. -var ErrInvalidArgument = errors.New("invalid argument") - -// EventHandler provides a means of subscribing to registered handling events. -type EventHandler interface { - CargoWasHandled(cargo.HandlingEvent) -} - -// Service provides handling operations. -type Service interface { - // RegisterHandlingEvent registers a handling event in the system, and - // notifies interested parties that a cargo has been handled. - RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number, - unLocode location.UNLocode, eventType cargo.HandlingEventType) error -} - -type service struct { - handlingEventRepository cargo.HandlingEventRepository - handlingEventFactory cargo.HandlingEventFactory - handlingEventHandler EventHandler -} - -func (s *service) RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number, - loc location.UNLocode, eventType cargo.HandlingEventType) error { - if completed.IsZero() || id == "" || loc == "" || eventType == cargo.NotHandled { - return ErrInvalidArgument - } - - e, err := s.handlingEventFactory.CreateHandlingEvent(time.Now(), completed, id, voyageNumber, loc, eventType) - if err != nil { - return err - } - - s.handlingEventRepository.Store(e) - s.handlingEventHandler.CargoWasHandled(e) - - return nil -} - -// NewService creates a handling event service with necessary dependencies. -func NewService(r cargo.HandlingEventRepository, f cargo.HandlingEventFactory, h EventHandler) Service { - return &service{ - handlingEventRepository: r, - handlingEventFactory: f, - handlingEventHandler: h, - } -} - -type handlingEventHandler struct { - InspectionService inspection.Service -} - -func (h *handlingEventHandler) CargoWasHandled(event cargo.HandlingEvent) { - h.InspectionService.InspectCargo(event.TrackingID) -} - -// NewEventHandler returns a new instance of a EventHandler. -func NewEventHandler(s inspection.Service) EventHandler { - return &handlingEventHandler{ - InspectionService: s, - } -} diff --git a/examples/shipping/handling/transport.go b/examples/shipping/handling/transport.go deleted file mode 100644 index 0e21365..0000000 --- a/examples/shipping/handling/transport.go +++ /dev/null @@ -1,100 +0,0 @@ -package handling - -import ( - "context" - "encoding/json" - "net/http" - "time" - - "github.com/gorilla/mux" - - kitlog "github.com/go-kit/kit/log" - kithttp "github.com/go-kit/kit/transport/http" - - "github.com/go-kit/kit/examples/shipping/cargo" - "github.com/go-kit/kit/examples/shipping/location" - "github.com/go-kit/kit/examples/shipping/voyage" -) - -// MakeHandler returns a handler for the handling service. -func MakeHandler(hs Service, logger kitlog.Logger) http.Handler { - r := mux.NewRouter() - - opts := []kithttp.ServerOption{ - kithttp.ServerErrorLogger(logger), - kithttp.ServerErrorEncoder(encodeError), - } - - registerIncidentHandler := kithttp.NewServer( - makeRegisterIncidentEndpoint(hs), - decodeRegisterIncidentRequest, - encodeResponse, - opts..., - ) - - r.Handle("/handling/v1/incidents", registerIncidentHandler).Methods("POST") - - return r -} - -func decodeRegisterIncidentRequest(_ context.Context, r *http.Request) (interface{}, error) { - var body struct { - CompletionTime time.Time `json:"completion_time"` - TrackingID string `json:"tracking_id"` - VoyageNumber string `json:"voyage"` - Location string `json:"location"` - EventType string `json:"event_type"` - } - - if err := json.NewDecoder(r.Body).Decode(&body); err != nil { - return nil, err - } - - return registerIncidentRequest{ - CompletionTime: body.CompletionTime, - ID: cargo.TrackingID(body.TrackingID), - Voyage: voyage.Number(body.VoyageNumber), - Location: location.UNLocode(body.Location), - EventType: stringToEventType(body.EventType), - }, nil -} - -func stringToEventType(s string) cargo.HandlingEventType { - types := map[string]cargo.HandlingEventType{ - cargo.Receive.String(): cargo.Receive, - cargo.Load.String(): cargo.Load, - cargo.Unload.String(): cargo.Unload, - cargo.Customs.String(): cargo.Customs, - cargo.Claim.String(): cargo.Claim, - } - return types[s] -} - -func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { - if e, ok := response.(errorer); ok && e.error() != nil { - encodeError(ctx, e.error(), w) - return nil - } - w.Header().Set("Content-Type", "application/json; charset=utf-8") - return json.NewEncoder(w).Encode(response) -} - -type errorer interface { - error() error -} - -// encode errors from business-logic -func encodeError(_ context.Context, err error, w http.ResponseWriter) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - switch err { - case cargo.ErrUnknown: - w.WriteHeader(http.StatusNotFound) - case ErrInvalidArgument: - w.WriteHeader(http.StatusBadRequest) - default: - w.WriteHeader(http.StatusInternalServerError) - } - json.NewEncoder(w).Encode(map[string]interface{}{ - "error": err.Error(), - }) -} diff --git a/examples/shipping/inmem/inmem.go b/examples/shipping/inmem/inmem.go deleted file mode 100644 index f941b7e..0000000 --- a/examples/shipping/inmem/inmem.go +++ /dev/null @@ -1,142 +0,0 @@ -// Package inmem provides in-memory implementations of all the domain repositories. -package inmem - -import ( - "sync" - - "github.com/go-kit/kit/examples/shipping/cargo" - "github.com/go-kit/kit/examples/shipping/location" - "github.com/go-kit/kit/examples/shipping/voyage" -) - -type cargoRepository struct { - mtx sync.RWMutex - cargos map[cargo.TrackingID]*cargo.Cargo -} - -func (r *cargoRepository) Store(c *cargo.Cargo) error { - r.mtx.Lock() - defer r.mtx.Unlock() - r.cargos[c.TrackingID] = c - return nil -} - -func (r *cargoRepository) Find(id cargo.TrackingID) (*cargo.Cargo, error) { - r.mtx.RLock() - defer r.mtx.RUnlock() - if val, ok := r.cargos[id]; ok { - return val, nil - } - return nil, cargo.ErrUnknown -} - -func (r *cargoRepository) FindAll() []*cargo.Cargo { - r.mtx.RLock() - defer r.mtx.RUnlock() - c := make([]*cargo.Cargo, 0, len(r.cargos)) - for _, val := range r.cargos { - c = append(c, val) - } - return c -} - -// NewCargoRepository returns a new instance of a in-memory cargo repository. -func NewCargoRepository() cargo.Repository { - return &cargoRepository{ - cargos: make(map[cargo.TrackingID]*cargo.Cargo), - } -} - -type locationRepository struct { - locations map[location.UNLocode]*location.Location -} - -func (r *locationRepository) Find(locode location.UNLocode) (*location.Location, error) { - if l, ok := r.locations[locode]; ok { - return l, nil - } - return nil, location.ErrUnknown -} - -func (r *locationRepository) FindAll() []*location.Location { - l := make([]*location.Location, 0, len(r.locations)) - for _, val := range r.locations { - l = append(l, val) - } - return l -} - -// NewLocationRepository returns a new instance of a in-memory location repository. -func NewLocationRepository() location.Repository { - r := &locationRepository{ - locations: make(map[location.UNLocode]*location.Location), - } - - r.locations[location.SESTO] = location.Stockholm - r.locations[location.AUMEL] = location.Melbourne - r.locations[location.CNHKG] = location.Hongkong - r.locations[location.JNTKO] = location.Tokyo - r.locations[location.NLRTM] = location.Rotterdam - r.locations[location.DEHAM] = location.Hamburg - - return r -} - -type voyageRepository struct { - voyages map[voyage.Number]*voyage.Voyage -} - -func (r *voyageRepository) Find(voyageNumber voyage.Number) (*voyage.Voyage, error) { - if v, ok := r.voyages[voyageNumber]; ok { - return v, nil - } - - return nil, voyage.ErrUnknown -} - -// NewVoyageRepository returns a new instance of a in-memory voyage repository. -func NewVoyageRepository() voyage.Repository { - r := &voyageRepository{ - voyages: make(map[voyage.Number]*voyage.Voyage), - } - - r.voyages[voyage.V100.Number] = voyage.V100 - r.voyages[voyage.V300.Number] = voyage.V300 - r.voyages[voyage.V400.Number] = voyage.V400 - - r.voyages[voyage.V0100S.Number] = voyage.V0100S - r.voyages[voyage.V0200T.Number] = voyage.V0200T - r.voyages[voyage.V0300A.Number] = voyage.V0300A - r.voyages[voyage.V0301S.Number] = voyage.V0301S - r.voyages[voyage.V0400S.Number] = voyage.V0400S - - return r -} - -type handlingEventRepository struct { - mtx sync.RWMutex - events map[cargo.TrackingID][]cargo.HandlingEvent -} - -func (r *handlingEventRepository) Store(e cargo.HandlingEvent) { - r.mtx.Lock() - defer r.mtx.Unlock() - // Make array if it's the first event with this tracking ID. - if _, ok := r.events[e.TrackingID]; !ok { - r.events[e.TrackingID] = make([]cargo.HandlingEvent, 0) - } - r.events[e.TrackingID] = append(r.events[e.TrackingID], e) -} - -func (r *handlingEventRepository) QueryHandlingHistory(id cargo.TrackingID) cargo.HandlingHistory { - r.mtx.RLock() - defer r.mtx.RUnlock() - return cargo.HandlingHistory{HandlingEvents: r.events[id]} -} - -// NewHandlingEventRepository returns a new instance of a in-memory handling event repository. -func NewHandlingEventRepository() cargo.HandlingEventRepository { - return &handlingEventRepository{ - events: make(map[cargo.TrackingID][]cargo.HandlingEvent), - } -} diff --git a/examples/shipping/inspection/inspection.go b/examples/shipping/inspection/inspection.go deleted file mode 100644 index 2ebf73d..0000000 --- a/examples/shipping/inspection/inspection.go +++ /dev/null @@ -1,53 +0,0 @@ -// Package inspection provides means to inspect cargos. -package inspection - -import ( - "github.com/go-kit/kit/examples/shipping/cargo" -) - -// EventHandler provides means of subscribing to inspection events. -type EventHandler interface { - CargoWasMisdirected(*cargo.Cargo) - CargoHasArrived(*cargo.Cargo) -} - -// Service provides cargo inspection operations. -type Service interface { - // InspectCargo inspects cargo and send relevant notifications to - // interested parties, for example if a cargo has been misdirected, or - // unloaded at the final destination. - InspectCargo(id cargo.TrackingID) -} - -type service struct { - cargos cargo.Repository - events cargo.HandlingEventRepository - handler EventHandler -} - -// TODO: Should be transactional -func (s *service) InspectCargo(id cargo.TrackingID) { - c, err := s.cargos.Find(id) - if err != nil { - return - } - - h := s.events.QueryHandlingHistory(id) - - c.DeriveDeliveryProgress(h) - - if c.Delivery.IsMisdirected { - s.handler.CargoWasMisdirected(c) - } - - if c.Delivery.IsUnloadedAtDestination { - s.handler.CargoHasArrived(c) - } - - s.cargos.Store(c) -} - -// NewService creates a inspection service with necessary dependencies. -func NewService(cargos cargo.Repository, events cargo.HandlingEventRepository, handler EventHandler) Service { - return &service{cargos, events, handler} -} diff --git a/examples/shipping/location/location.go b/examples/shipping/location/location.go deleted file mode 100644 index ee2e5d4..0000000 --- a/examples/shipping/location/location.go +++ /dev/null @@ -1,29 +0,0 @@ -// Package location provides the Location aggregate. -package location - -import ( - "errors" -) - -// UNLocode is the United Nations location code that uniquely identifies a -// particular location. -// -// http://www.unece.org/cefact/locode/ -// http://www.unece.org/cefact/locode/DocColumnDescription.htm#LOCODE -type UNLocode string - -// Location is a location is our model is stops on a journey, such as cargo -// origin or destination, or carrier movement endpoints. -type Location struct { - UNLocode UNLocode - Name string -} - -// ErrUnknown is used when a location could not be found. -var ErrUnknown = errors.New("unknown location") - -// Repository provides access a location store. -type Repository interface { - Find(locode UNLocode) (*Location, error) - FindAll() []*Location -} diff --git a/examples/shipping/location/sample_locations.go b/examples/shipping/location/sample_locations.go deleted file mode 100644 index 7fd34ef..0000000 --- a/examples/shipping/location/sample_locations.go +++ /dev/null @@ -1,27 +0,0 @@ -package location - -// Sample UN locodes. -var ( - SESTO UNLocode = "SESTO" - AUMEL UNLocode = "AUMEL" - CNHKG UNLocode = "CNHKG" - USNYC UNLocode = "USNYC" - USCHI UNLocode = "USCHI" - JNTKO UNLocode = "JNTKO" - DEHAM UNLocode = "DEHAM" - NLRTM UNLocode = "NLRTM" - FIHEL UNLocode = "FIHEL" -) - -// Sample locations. -var ( - Stockholm = &Location{SESTO, "Stockholm"} - Melbourne = &Location{AUMEL, "Melbourne"} - Hongkong = &Location{CNHKG, "Hongkong"} - NewYork = &Location{USNYC, "New York"} - Chicago = &Location{USCHI, "Chicago"} - Tokyo = &Location{JNTKO, "Tokyo"} - Hamburg = &Location{DEHAM, "Hamburg"} - Rotterdam = &Location{NLRTM, "Rotterdam"} - Helsinki = &Location{FIHEL, "Helsinki"} -) diff --git a/examples/shipping/main.go b/examples/shipping/main.go deleted file mode 100644 index 8337d61..0000000 --- a/examples/shipping/main.go +++ /dev/null @@ -1,200 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "net/http" - "os" - "os/signal" - "syscall" - "time" - - stdprometheus "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - - "github.com/go-kit/kit/log" - kitprometheus "github.com/go-kit/kit/metrics/prometheus" - - "github.com/go-kit/kit/examples/shipping/booking" - "github.com/go-kit/kit/examples/shipping/cargo" - "github.com/go-kit/kit/examples/shipping/handling" - "github.com/go-kit/kit/examples/shipping/inmem" - "github.com/go-kit/kit/examples/shipping/inspection" - "github.com/go-kit/kit/examples/shipping/location" - "github.com/go-kit/kit/examples/shipping/routing" - "github.com/go-kit/kit/examples/shipping/tracking" -) - -const ( - defaultPort = "8080" - defaultRoutingServiceURL = "http://localhost:7878" -) - -func main() { - var ( - addr = envString("PORT", defaultPort) - rsurl = envString("ROUTINGSERVICE_URL", defaultRoutingServiceURL) - - httpAddr = flag.String("http.addr", ":"+addr, "HTTP listen address") - routingServiceURL = flag.String("service.routing", rsurl, "routing service URL") - - ctx = context.Background() - ) - - flag.Parse() - - var logger log.Logger - logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) - logger = log.With(logger, "ts", log.DefaultTimestampUTC) - - var ( - cargos = inmem.NewCargoRepository() - locations = inmem.NewLocationRepository() - voyages = inmem.NewVoyageRepository() - handlingEvents = inmem.NewHandlingEventRepository() - ) - - // Configure some questionable dependencies. - var ( - handlingEventFactory = cargo.HandlingEventFactory{ - CargoRepository: cargos, - VoyageRepository: voyages, - LocationRepository: locations, - } - handlingEventHandler = handling.NewEventHandler( - inspection.NewService(cargos, handlingEvents, nil), - ) - ) - - // Facilitate testing by adding some cargos. - storeTestData(cargos) - - fieldKeys := []string{"method"} - - var rs routing.Service - rs = routing.NewProxyingMiddleware(ctx, *routingServiceURL)(rs) - - var bs booking.Service - bs = booking.NewService(cargos, locations, handlingEvents, rs) - bs = booking.NewLoggingService(log.With(logger, "component", "booking"), bs) - bs = booking.NewInstrumentingService( - kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ - Namespace: "api", - Subsystem: "booking_service", - Name: "request_count", - Help: "Number of requests received.", - }, fieldKeys), - kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ - Namespace: "api", - Subsystem: "booking_service", - Name: "request_latency_microseconds", - Help: "Total duration of requests in microseconds.", - }, fieldKeys), - bs, - ) - - var ts tracking.Service - ts = tracking.NewService(cargos, handlingEvents) - ts = tracking.NewLoggingService(log.With(logger, "component", "tracking"), ts) - ts = tracking.NewInstrumentingService( - kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ - Namespace: "api", - Subsystem: "tracking_service", - Name: "request_count", - Help: "Number of requests received.", - }, fieldKeys), - kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ - Namespace: "api", - Subsystem: "tracking_service", - Name: "request_latency_microseconds", - Help: "Total duration of requests in microseconds.", - }, fieldKeys), - ts, - ) - - var hs handling.Service - hs = handling.NewService(handlingEvents, handlingEventFactory, handlingEventHandler) - hs = handling.NewLoggingService(log.With(logger, "component", "handling"), hs) - hs = handling.NewInstrumentingService( - kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ - Namespace: "api", - Subsystem: "handling_service", - Name: "request_count", - Help: "Number of requests received.", - }, fieldKeys), - kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ - Namespace: "api", - Subsystem: "handling_service", - Name: "request_latency_microseconds", - Help: "Total duration of requests in microseconds.", - }, fieldKeys), - hs, - ) - - httpLogger := log.With(logger, "component", "http") - - mux := http.NewServeMux() - - mux.Handle("/booking/v1/", booking.MakeHandler(bs, httpLogger)) - mux.Handle("/tracking/v1/", tracking.MakeHandler(ts, httpLogger)) - mux.Handle("/handling/v1/", handling.MakeHandler(hs, httpLogger)) - - http.Handle("/", accessControl(mux)) - http.Handle("/metrics", promhttp.Handler()) - - errs := make(chan error, 2) - go func() { - logger.Log("transport", "http", "address", *httpAddr, "msg", "listening") - errs <- http.ListenAndServe(*httpAddr, nil) - }() - go func() { - c := make(chan os.Signal) - signal.Notify(c, syscall.SIGINT) - errs <- fmt.Errorf("%s", <-c) - }() - - logger.Log("terminated", <-errs) -} - -func accessControl(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS") - w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type") - - if r.Method == "OPTIONS" { - return - } - - h.ServeHTTP(w, r) - }) -} - -func envString(env, fallback string) string { - e := os.Getenv(env) - if e == "" { - return fallback - } - return e -} - -func storeTestData(r cargo.Repository) { - test1 := cargo.New("FTL456", cargo.RouteSpecification{ - Origin: location.AUMEL, - Destination: location.SESTO, - ArrivalDeadline: time.Now().AddDate(0, 0, 7), - }) - if err := r.Store(test1); err != nil { - panic(err) - } - - test2 := cargo.New("ABC123", cargo.RouteSpecification{ - Origin: location.SESTO, - Destination: location.CNHKG, - ArrivalDeadline: time.Now().AddDate(0, 0, 14), - }) - if err := r.Store(test2); err != nil { - panic(err) - } -} diff --git a/examples/shipping/routing/proxying.go b/examples/shipping/routing/proxying.go deleted file mode 100644 index 0c9150b..0000000 --- a/examples/shipping/routing/proxying.go +++ /dev/null @@ -1,117 +0,0 @@ -package routing - -import ( - "context" - "encoding/json" - "net/http" - "net/url" - "time" - - "github.com/go-kit/kit/circuitbreaker" - "github.com/go-kit/kit/endpoint" - kithttp "github.com/go-kit/kit/transport/http" - - "github.com/go-kit/kit/examples/shipping/cargo" - "github.com/go-kit/kit/examples/shipping/location" - "github.com/go-kit/kit/examples/shipping/voyage" -) - -type proxyService struct { - context.Context - FetchRoutesEndpoint endpoint.Endpoint - Service -} - -func (s proxyService) FetchRoutesForSpecification(rs cargo.RouteSpecification) []cargo.Itinerary { - response, err := s.FetchRoutesEndpoint(s.Context, fetchRoutesRequest{ - From: string(rs.Origin), - To: string(rs.Destination), - }) - if err != nil { - return []cargo.Itinerary{} - } - - resp := response.(fetchRoutesResponse) - - var itineraries []cargo.Itinerary - for _, r := range resp.Paths { - var legs []cargo.Leg - for _, e := range r.Edges { - legs = append(legs, cargo.Leg{ - VoyageNumber: voyage.Number(e.Voyage), - LoadLocation: location.UNLocode(e.Origin), - UnloadLocation: location.UNLocode(e.Destination), - LoadTime: e.Departure, - UnloadTime: e.Arrival, - }) - } - - itineraries = append(itineraries, cargo.Itinerary{Legs: legs}) - } - - return itineraries -} - -// ServiceMiddleware defines a middleware for a routing service. -type ServiceMiddleware func(Service) Service - -// NewProxyingMiddleware returns a new instance of a proxying middleware. -func NewProxyingMiddleware(ctx context.Context, proxyURL string) ServiceMiddleware { - return func(next Service) Service { - var e endpoint.Endpoint - e = makeFetchRoutesEndpoint(ctx, proxyURL) - e = circuitbreaker.Hystrix("fetch-routes")(e) - return proxyService{ctx, e, next} - } -} - -type fetchRoutesRequest struct { - From string - To string -} - -type fetchRoutesResponse struct { - Paths []struct { - Edges []struct { - Origin string `json:"origin"` - Destination string `json:"destination"` - Voyage string `json:"voyage"` - Departure time.Time `json:"departure"` - Arrival time.Time `json:"arrival"` - } `json:"edges"` - } `json:"paths"` -} - -func makeFetchRoutesEndpoint(ctx context.Context, instance string) endpoint.Endpoint { - u, err := url.Parse(instance) - if err != nil { - panic(err) - } - if u.Path == "" { - u.Path = "/paths" - } - return kithttp.NewClient( - "GET", u, - encodeFetchRoutesRequest, - decodeFetchRoutesResponse, - ).Endpoint() -} - -func decodeFetchRoutesResponse(_ context.Context, resp *http.Response) (interface{}, error) { - var response fetchRoutesResponse - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return nil, err - } - return response, nil -} - -func encodeFetchRoutesRequest(_ context.Context, r *http.Request, request interface{}) error { - req := request.(fetchRoutesRequest) - - vals := r.URL.Query() - vals.Add("from", req.From) - vals.Add("to", req.To) - r.URL.RawQuery = vals.Encode() - - return nil -} diff --git a/examples/shipping/routing/routing.go b/examples/shipping/routing/routing.go deleted file mode 100644 index 50496f2..0000000 --- a/examples/shipping/routing/routing.go +++ /dev/null @@ -1,15 +0,0 @@ -// Package routing provides the routing domain service. It does not actually -// implement the routing service but merely acts as a proxy for a separate -// bounded context. -package routing - -import ( - "github.com/go-kit/kit/examples/shipping/cargo" -) - -// Service provides access to an external routing service. -type Service interface { - // FetchRoutesForSpecification finds all possible routes that satisfy a - // given specification. - FetchRoutesForSpecification(rs cargo.RouteSpecification) []cargo.Itinerary -} diff --git a/examples/shipping/tracking/endpoint.go b/examples/shipping/tracking/endpoint.go deleted file mode 100644 index ddb1317..0000000 --- a/examples/shipping/tracking/endpoint.go +++ /dev/null @@ -1,26 +0,0 @@ -package tracking - -import ( - "context" - - "github.com/go-kit/kit/endpoint" -) - -type trackCargoRequest struct { - ID string -} - -type trackCargoResponse struct { - Cargo *Cargo `json:"cargo,omitempty"` - Err error `json:"error,omitempty"` -} - -func (r trackCargoResponse) error() error { return r.Err } - -func makeTrackCargoEndpoint(ts Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(trackCargoRequest) - c, err := ts.Track(req.ID) - return trackCargoResponse{Cargo: &c, Err: err}, nil - } -} diff --git a/examples/shipping/tracking/instrumenting.go b/examples/shipping/tracking/instrumenting.go deleted file mode 100644 index f5dc018..0000000 --- a/examples/shipping/tracking/instrumenting.go +++ /dev/null @@ -1,31 +0,0 @@ -package tracking - -import ( - "time" - - "github.com/go-kit/kit/metrics" -) - -type instrumentingService struct { - requestCount metrics.Counter - requestLatency metrics.Histogram - Service -} - -// NewInstrumentingService returns an instance of an instrumenting Service. -func NewInstrumentingService(counter metrics.Counter, latency metrics.Histogram, s Service) Service { - return &instrumentingService{ - requestCount: counter, - requestLatency: latency, - Service: s, - } -} - -func (s *instrumentingService) Track(id string) (Cargo, error) { - defer func(begin time.Time) { - s.requestCount.With("method", "track").Add(1) - s.requestLatency.With("method", "track").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return s.Service.Track(id) -} diff --git a/examples/shipping/tracking/logging.go b/examples/shipping/tracking/logging.go deleted file mode 100644 index 584aeaa..0000000 --- a/examples/shipping/tracking/logging.go +++ /dev/null @@ -1,24 +0,0 @@ -package tracking - -import ( - "time" - - "github.com/go-kit/kit/log" -) - -type loggingService struct { - logger log.Logger - Service -} - -// NewLoggingService returns a new instance of a logging Service. -func NewLoggingService(logger log.Logger, s Service) Service { - return &loggingService{logger, s} -} - -func (s *loggingService) Track(id string) (c Cargo, err error) { - defer func(begin time.Time) { - s.logger.Log("method", "track", "tracking_id", id, "took", time.Since(begin), "err", err) - }(time.Now()) - return s.Service.Track(id) -} diff --git a/examples/shipping/tracking/service.go b/examples/shipping/tracking/service.go deleted file mode 100644 index b0e360b..0000000 --- a/examples/shipping/tracking/service.go +++ /dev/null @@ -1,163 +0,0 @@ -// Package tracking provides the use-case of tracking a cargo. Used by views -// facing the end-user. -package tracking - -import ( - "errors" - "fmt" - "strings" - "time" - - "github.com/go-kit/kit/examples/shipping/cargo" -) - -// ErrInvalidArgument is returned when one or more arguments are invalid. -var ErrInvalidArgument = errors.New("invalid argument") - -// Service is the interface that provides the basic Track method. -type Service interface { - // Track returns a cargo matching a tracking ID. - Track(id string) (Cargo, error) -} - -type service struct { - cargos cargo.Repository - handlingEvents cargo.HandlingEventRepository -} - -func (s *service) Track(id string) (Cargo, error) { - if id == "" { - return Cargo{}, ErrInvalidArgument - } - c, err := s.cargos.Find(cargo.TrackingID(id)) - if err != nil { - return Cargo{}, err - } - return assemble(c, s.handlingEvents), nil -} - -// NewService returns a new instance of the default Service. -func NewService(cargos cargo.Repository, events cargo.HandlingEventRepository) Service { - return &service{ - cargos: cargos, - handlingEvents: events, - } -} - -// Cargo is a read model for tracking views. -type Cargo struct { - TrackingID string `json:"tracking_id"` - StatusText string `json:"status_text"` - Origin string `json:"origin"` - Destination string `json:"destination"` - ETA time.Time `json:"eta"` - NextExpectedActivity string `json:"next_expected_activity"` - ArrivalDeadline time.Time `json:"arrival_deadline"` - Events []Event `json:"events"` -} - -// Leg is a read model for booking views. -type Leg struct { - VoyageNumber string `json:"voyage_number"` - From string `json:"from"` - To string `json:"to"` - LoadTime time.Time `json:"load_time"` - UnloadTime time.Time `json:"unload_time"` -} - -// Event is a read model for tracking views. -type Event struct { - Description string `json:"description"` - Expected bool `json:"expected"` -} - -func assemble(c *cargo.Cargo, events cargo.HandlingEventRepository) Cargo { - return Cargo{ - TrackingID: string(c.TrackingID), - Origin: string(c.Origin), - Destination: string(c.RouteSpecification.Destination), - ETA: c.Delivery.ETA, - NextExpectedActivity: nextExpectedActivity(c), - ArrivalDeadline: c.RouteSpecification.ArrivalDeadline, - StatusText: assembleStatusText(c), - Events: assembleEvents(c, events), - } -} - -func assembleLegs(c cargo.Cargo) []Leg { - var legs []Leg - for _, l := range c.Itinerary.Legs { - legs = append(legs, Leg{ - VoyageNumber: string(l.VoyageNumber), - From: string(l.LoadLocation), - To: string(l.UnloadLocation), - LoadTime: l.LoadTime, - UnloadTime: l.UnloadTime, - }) - } - return legs -} - -func nextExpectedActivity(c *cargo.Cargo) string { - a := c.Delivery.NextExpectedActivity - prefix := "Next expected activity is to" - - switch a.Type { - case cargo.Load: - return fmt.Sprintf("%s %s cargo onto voyage %s in %s.", prefix, strings.ToLower(a.Type.String()), a.VoyageNumber, a.Location) - case cargo.Unload: - return fmt.Sprintf("%s %s cargo off of voyage %s in %s.", prefix, strings.ToLower(a.Type.String()), a.VoyageNumber, a.Location) - case cargo.NotHandled: - return "There are currently no expected activities for this cargo." - } - - return fmt.Sprintf("%s %s cargo in %s.", prefix, strings.ToLower(a.Type.String()), a.Location) -} - -func assembleStatusText(c *cargo.Cargo) string { - switch c.Delivery.TransportStatus { - case cargo.NotReceived: - return "Not received" - case cargo.InPort: - return fmt.Sprintf("In port %s", c.Delivery.LastKnownLocation) - case cargo.OnboardCarrier: - return fmt.Sprintf("Onboard voyage %s", c.Delivery.CurrentVoyage) - case cargo.Claimed: - return "Claimed" - default: - return "Unknown" - } -} - -func assembleEvents(c *cargo.Cargo, handlingEvents cargo.HandlingEventRepository) []Event { - h := handlingEvents.QueryHandlingHistory(c.TrackingID) - - var events []Event - for _, e := range h.HandlingEvents { - var description string - - switch e.Activity.Type { - case cargo.NotHandled: - description = "Cargo has not yet been received." - case cargo.Receive: - description = fmt.Sprintf("Received in %s, at %s", e.Activity.Location, time.Now().Format(time.RFC3339)) - case cargo.Load: - description = fmt.Sprintf("Loaded onto voyage %s in %s, at %s.", e.Activity.VoyageNumber, e.Activity.Location, time.Now().Format(time.RFC3339)) - case cargo.Unload: - description = fmt.Sprintf("Unloaded off voyage %s in %s, at %s.", e.Activity.VoyageNumber, e.Activity.Location, time.Now().Format(time.RFC3339)) - case cargo.Claim: - description = fmt.Sprintf("Claimed in %s, at %s.", e.Activity.Location, time.Now().Format(time.RFC3339)) - case cargo.Customs: - description = fmt.Sprintf("Cleared customs in %s, at %s.", e.Activity.Location, time.Now().Format(time.RFC3339)) - default: - description = "[Unknown status]" - } - - events = append(events, Event{ - Description: description, - Expected: c.Itinerary.IsExpected(e), - }) - } - - return events -} diff --git a/examples/shipping/tracking/transport.go b/examples/shipping/tracking/transport.go deleted file mode 100644 index 32db971..0000000 --- a/examples/shipping/tracking/transport.go +++ /dev/null @@ -1,74 +0,0 @@ -package tracking - -import ( - "context" - "encoding/json" - "errors" - "net/http" - - "github.com/gorilla/mux" - - kitlog "github.com/go-kit/kit/log" - kithttp "github.com/go-kit/kit/transport/http" - - "github.com/go-kit/kit/examples/shipping/cargo" -) - -// MakeHandler returns a handler for the tracking service. -func MakeHandler(ts Service, logger kitlog.Logger) http.Handler { - r := mux.NewRouter() - - opts := []kithttp.ServerOption{ - kithttp.ServerErrorLogger(logger), - kithttp.ServerErrorEncoder(encodeError), - } - - trackCargoHandler := kithttp.NewServer( - makeTrackCargoEndpoint(ts), - decodeTrackCargoRequest, - encodeResponse, - opts..., - ) - - r.Handle("/tracking/v1/cargos/{id}", trackCargoHandler).Methods("GET") - - return r -} - -func decodeTrackCargoRequest(_ context.Context, r *http.Request) (interface{}, error) { - vars := mux.Vars(r) - id, ok := vars["id"] - if !ok { - return nil, errors.New("bad route") - } - return trackCargoRequest{ID: id}, nil -} - -func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { - if e, ok := response.(errorer); ok && e.error() != nil { - encodeError(ctx, e.error(), w) - return nil - } - w.Header().Set("Content-Type", "application/json; charset=utf-8") - return json.NewEncoder(w).Encode(response) -} - -type errorer interface { - error() error -} - -// encode errors from business-logic -func encodeError(_ context.Context, err error, w http.ResponseWriter) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - switch err { - case cargo.ErrUnknown: - w.WriteHeader(http.StatusNotFound) - case ErrInvalidArgument: - w.WriteHeader(http.StatusBadRequest) - default: - w.WriteHeader(http.StatusInternalServerError) - } - json.NewEncoder(w).Encode(map[string]interface{}{ - "error": err.Error(), - }) -} diff --git a/examples/shipping/voyage/sample_voyages.go b/examples/shipping/voyage/sample_voyages.go deleted file mode 100644 index 751f588..0000000 --- a/examples/shipping/voyage/sample_voyages.go +++ /dev/null @@ -1,40 +0,0 @@ -package voyage - -import "github.com/go-kit/kit/examples/shipping/location" - -// A set of sample voyages. -var ( - V100 = New("V100", Schedule{ - []CarrierMovement{ - {DepartureLocation: location.CNHKG, ArrivalLocation: location.JNTKO}, - {DepartureLocation: location.JNTKO, ArrivalLocation: location.USNYC}, - }, - }) - - V300 = New("V300", Schedule{ - []CarrierMovement{ - {DepartureLocation: location.JNTKO, ArrivalLocation: location.NLRTM}, - {DepartureLocation: location.NLRTM, ArrivalLocation: location.DEHAM}, - {DepartureLocation: location.DEHAM, ArrivalLocation: location.AUMEL}, - {DepartureLocation: location.AUMEL, ArrivalLocation: location.JNTKO}, - }, - }) - - V400 = New("V400", Schedule{ - []CarrierMovement{ - {DepartureLocation: location.DEHAM, ArrivalLocation: location.SESTO}, - {DepartureLocation: location.SESTO, ArrivalLocation: location.FIHEL}, - {DepartureLocation: location.FIHEL, ArrivalLocation: location.DEHAM}, - }, - }) -) - -// These voyages are hard-coded into the current pathfinder. Make sure -// they exist. -var ( - V0100S = New("0100S", Schedule{[]CarrierMovement{}}) - V0200T = New("0200T", Schedule{[]CarrierMovement{}}) - V0300A = New("0300A", Schedule{[]CarrierMovement{}}) - V0301S = New("0301S", Schedule{[]CarrierMovement{}}) - V0400S = New("0400S", Schedule{[]CarrierMovement{}}) -) diff --git a/examples/shipping/voyage/voyage.go b/examples/shipping/voyage/voyage.go deleted file mode 100644 index 37366af..0000000 --- a/examples/shipping/voyage/voyage.go +++ /dev/null @@ -1,44 +0,0 @@ -// Package voyage provides the Voyage aggregate. -package voyage - -import ( - "errors" - "time" - - "github.com/go-kit/kit/examples/shipping/location" -) - -// Number uniquely identifies a particular Voyage. -type Number string - -// Voyage is a uniquely identifiable series of carrier movements. -type Voyage struct { - Number Number - Schedule Schedule -} - -// New creates a voyage with a voyage number and a provided schedule. -func New(n Number, s Schedule) *Voyage { - return &Voyage{Number: n, Schedule: s} -} - -// Schedule describes a voyage schedule. -type Schedule struct { - CarrierMovements []CarrierMovement -} - -// CarrierMovement is a vessel voyage from one location to another. -type CarrierMovement struct { - DepartureLocation location.UNLocode - ArrivalLocation location.UNLocode - DepartureTime time.Time - ArrivalTime time.Time -} - -// ErrUnknown is used when a voyage could not be found. -var ErrUnknown = errors.New("unknown voyage") - -// Repository provides access a voyage store. -type Repository interface { - Find(Number) (*Voyage, error) -} diff --git a/examples/stringsvc1/main.go b/examples/stringsvc1/main.go deleted file mode 100644 index 03fc793..0000000 --- a/examples/stringsvc1/main.go +++ /dev/null @@ -1,111 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "errors" - "log" - "net/http" - "strings" - - "github.com/go-kit/kit/endpoint" - httptransport "github.com/go-kit/kit/transport/http" -) - -// StringService provides operations on strings. -type StringService interface { - Uppercase(string) (string, error) - Count(string) int -} - -type stringService struct{} - -func (stringService) Uppercase(s string) (string, error) { - if s == "" { - return "", ErrEmpty - } - return strings.ToUpper(s), nil -} - -func (stringService) Count(s string) int { - return len(s) -} - -func main() { - svc := stringService{} - - uppercaseHandler := httptransport.NewServer( - makeUppercaseEndpoint(svc), - decodeUppercaseRequest, - encodeResponse, - ) - - countHandler := httptransport.NewServer( - makeCountEndpoint(svc), - decodeCountRequest, - encodeResponse, - ) - - http.Handle("/uppercase", uppercaseHandler) - http.Handle("/count", countHandler) - log.Fatal(http.ListenAndServe(":8080", nil)) -} - -func makeUppercaseEndpoint(svc StringService) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(uppercaseRequest) - v, err := svc.Uppercase(req.S) - if err != nil { - return uppercaseResponse{v, err.Error()}, nil - } - return uppercaseResponse{v, ""}, nil - } -} - -func makeCountEndpoint(svc StringService) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(countRequest) - v := svc.Count(req.S) - return countResponse{v}, nil - } -} - -func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) { - var request uppercaseRequest - if err := json.NewDecoder(r.Body).Decode(&request); err != nil { - return nil, err - } - return request, nil -} - -func decodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) { - var request countRequest - if err := json.NewDecoder(r.Body).Decode(&request); err != nil { - return nil, err - } - return request, nil -} - -func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { - return json.NewEncoder(w).Encode(response) -} - -type uppercaseRequest struct { - S string `json:"s"` -} - -type uppercaseResponse struct { - V string `json:"v"` - Err string `json:"err,omitempty"` // errors don't define JSON marshaling -} - -type countRequest struct { - S string `json:"s"` -} - -type countResponse struct { - V int `json:"v"` -} - -// ErrEmpty is returned when an input string is empty. -var ErrEmpty = errors.New("empty string") diff --git a/examples/stringsvc2/instrumenting.go b/examples/stringsvc2/instrumenting.go deleted file mode 100644 index 675617d..0000000 --- a/examples/stringsvc2/instrumenting.go +++ /dev/null @@ -1,38 +0,0 @@ -package main - -import ( - "fmt" - "time" - - "github.com/go-kit/kit/metrics" -) - -type instrumentingMiddleware struct { - requestCount metrics.Counter - requestLatency metrics.Histogram - countResult metrics.Histogram - next StringService -} - -func (mw instrumentingMiddleware) Uppercase(s string) (output string, err error) { - defer func(begin time.Time) { - lvs := []string{"method", "uppercase", "error", fmt.Sprint(err != nil)} - mw.requestCount.With(lvs...).Add(1) - mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) - }(time.Now()) - - output, err = mw.next.Uppercase(s) - return -} - -func (mw instrumentingMiddleware) Count(s string) (n int) { - defer func(begin time.Time) { - lvs := []string{"method", "count", "error", "false"} - mw.requestCount.With(lvs...).Add(1) - mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) - mw.countResult.Observe(float64(n)) - }(time.Now()) - - n = mw.next.Count(s) - return -} diff --git a/examples/stringsvc2/logging.go b/examples/stringsvc2/logging.go deleted file mode 100644 index b958f3b..0000000 --- a/examples/stringsvc2/logging.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "time" - - "github.com/go-kit/kit/log" -) - -type loggingMiddleware struct { - logger log.Logger - next StringService -} - -func (mw loggingMiddleware) Uppercase(s string) (output string, err error) { - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "uppercase", - "input", s, - "output", output, - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - output, err = mw.next.Uppercase(s) - return -} - -func (mw loggingMiddleware) Count(s string) (n int) { - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "count", - "input", s, - "n", n, - "took", time.Since(begin), - ) - }(time.Now()) - - n = mw.next.Count(s) - return -} diff --git a/examples/stringsvc2/main.go b/examples/stringsvc2/main.go deleted file mode 100644 index 60544f2..0000000 --- a/examples/stringsvc2/main.go +++ /dev/null @@ -1,60 +0,0 @@ -package main - -import ( - "net/http" - "os" - - stdprometheus "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - - "github.com/go-kit/kit/log" - kitprometheus "github.com/go-kit/kit/metrics/prometheus" - httptransport "github.com/go-kit/kit/transport/http" -) - -func main() { - logger := log.NewLogfmtLogger(os.Stderr) - - fieldKeys := []string{"method", "error"} - requestCount := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ - Namespace: "my_group", - Subsystem: "string_service", - Name: "request_count", - Help: "Number of requests received.", - }, fieldKeys) - requestLatency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ - Namespace: "my_group", - Subsystem: "string_service", - Name: "request_latency_microseconds", - Help: "Total duration of requests in microseconds.", - }, fieldKeys) - countResult := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ - Namespace: "my_group", - Subsystem: "string_service", - Name: "count_result", - Help: "The result of each count method.", - }, []string{}) // no fields here - - var svc StringService - svc = stringService{} - svc = loggingMiddleware{logger, svc} - svc = instrumentingMiddleware{requestCount, requestLatency, countResult, svc} - - uppercaseHandler := httptransport.NewServer( - makeUppercaseEndpoint(svc), - decodeUppercaseRequest, - encodeResponse, - ) - - countHandler := httptransport.NewServer( - makeCountEndpoint(svc), - decodeCountRequest, - encodeResponse, - ) - - http.Handle("/uppercase", uppercaseHandler) - http.Handle("/count", countHandler) - http.Handle("/metrics", promhttp.Handler()) - logger.Log("msg", "HTTP", "addr", ":8080") - logger.Log("err", http.ListenAndServe(":8080", nil)) -} diff --git a/examples/stringsvc2/service.go b/examples/stringsvc2/service.go deleted file mode 100644 index 1da2f3e..0000000 --- a/examples/stringsvc2/service.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "errors" - "strings" -) - -// StringService provides operations on strings. -type StringService interface { - Uppercase(string) (string, error) - Count(string) int -} - -type stringService struct{} - -func (stringService) Uppercase(s string) (string, error) { - if s == "" { - return "", ErrEmpty - } - return strings.ToUpper(s), nil -} - -func (stringService) Count(s string) int { - return len(s) -} - -// ErrEmpty is returned when an input string is empty. -var ErrEmpty = errors.New("empty string") diff --git a/examples/stringsvc2/transport.go b/examples/stringsvc2/transport.go deleted file mode 100644 index 297b3ff..0000000 --- a/examples/stringsvc2/transport.go +++ /dev/null @@ -1,65 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "net/http" - - "github.com/go-kit/kit/endpoint" -) - -func makeUppercaseEndpoint(svc StringService) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(uppercaseRequest) - v, err := svc.Uppercase(req.S) - if err != nil { - return uppercaseResponse{v, err.Error()}, nil - } - return uppercaseResponse{v, ""}, nil - } -} - -func makeCountEndpoint(svc StringService) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(countRequest) - v := svc.Count(req.S) - return countResponse{v}, nil - } -} - -func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) { - var request uppercaseRequest - if err := json.NewDecoder(r.Body).Decode(&request); err != nil { - return nil, err - } - return request, nil -} - -func decodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) { - var request countRequest - if err := json.NewDecoder(r.Body).Decode(&request); err != nil { - return nil, err - } - return request, nil -} - -func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { - return json.NewEncoder(w).Encode(response) -} - -type uppercaseRequest struct { - S string `json:"s"` -} - -type uppercaseResponse struct { - V string `json:"v"` - Err string `json:"err,omitempty"` -} - -type countRequest struct { - S string `json:"s"` -} - -type countResponse struct { - V int `json:"v"` -} diff --git a/examples/stringsvc3/instrumenting.go b/examples/stringsvc3/instrumenting.go deleted file mode 100644 index b21c016..0000000 --- a/examples/stringsvc3/instrumenting.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "fmt" - "time" - - "github.com/go-kit/kit/metrics" -) - -func instrumentingMiddleware( - requestCount metrics.Counter, - requestLatency metrics.Histogram, - countResult metrics.Histogram, -) ServiceMiddleware { - return func(next StringService) StringService { - return instrmw{requestCount, requestLatency, countResult, next} - } -} - -type instrmw struct { - requestCount metrics.Counter - requestLatency metrics.Histogram - countResult metrics.Histogram - StringService -} - -func (mw instrmw) Uppercase(s string) (output string, err error) { - defer func(begin time.Time) { - lvs := []string{"method", "uppercase", "error", fmt.Sprint(err != nil)} - mw.requestCount.With(lvs...).Add(1) - mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) - }(time.Now()) - - output, err = mw.StringService.Uppercase(s) - return -} - -func (mw instrmw) Count(s string) (n int) { - defer func(begin time.Time) { - lvs := []string{"method", "count", "error", "false"} - mw.requestCount.With(lvs...).Add(1) - mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) - mw.countResult.Observe(float64(n)) - }(time.Now()) - - n = mw.StringService.Count(s) - return -} diff --git a/examples/stringsvc3/logging.go b/examples/stringsvc3/logging.go deleted file mode 100644 index 72a2709..0000000 --- a/examples/stringsvc3/logging.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "time" - - "github.com/go-kit/kit/log" -) - -func loggingMiddleware(logger log.Logger) ServiceMiddleware { - return func(next StringService) StringService { - return logmw{logger, next} - } -} - -type logmw struct { - logger log.Logger - StringService -} - -func (mw logmw) Uppercase(s string) (output string, err error) { - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "uppercase", - "input", s, - "output", output, - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - output, err = mw.StringService.Uppercase(s) - return -} - -func (mw logmw) Count(s string) (n int) { - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "count", - "input", s, - "n", n, - "took", time.Since(begin), - ) - }(time.Now()) - - n = mw.StringService.Count(s) - return -} diff --git a/examples/stringsvc3/main.go b/examples/stringsvc3/main.go deleted file mode 100644 index 5cdb43b..0000000 --- a/examples/stringsvc3/main.go +++ /dev/null @@ -1,70 +0,0 @@ -package main - -import ( - "context" - "flag" - "net/http" - "os" - - stdprometheus "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - - "github.com/go-kit/kit/log" - kitprometheus "github.com/go-kit/kit/metrics/prometheus" - httptransport "github.com/go-kit/kit/transport/http" -) - -func main() { - var ( - listen = flag.String("listen", ":8080", "HTTP listen address") - proxy = flag.String("proxy", "", "Optional comma-separated list of URLs to proxy uppercase requests") - ) - flag.Parse() - - var logger log.Logger - logger = log.NewLogfmtLogger(os.Stderr) - logger = log.With(logger, "listen", *listen, "caller", log.DefaultCaller) - - fieldKeys := []string{"method", "error"} - requestCount := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ - Namespace: "my_group", - Subsystem: "string_service", - Name: "request_count", - Help: "Number of requests received.", - }, fieldKeys) - requestLatency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ - Namespace: "my_group", - Subsystem: "string_service", - Name: "request_latency_microseconds", - Help: "Total duration of requests in microseconds.", - }, fieldKeys) - countResult := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ - Namespace: "my_group", - Subsystem: "string_service", - Name: "count_result", - Help: "The result of each count method.", - }, []string{}) - - var svc StringService - svc = stringService{} - svc = proxyingMiddleware(context.Background(), *proxy, logger)(svc) - svc = loggingMiddleware(logger)(svc) - svc = instrumentingMiddleware(requestCount, requestLatency, countResult)(svc) - - uppercaseHandler := httptransport.NewServer( - makeUppercaseEndpoint(svc), - decodeUppercaseRequest, - encodeResponse, - ) - countHandler := httptransport.NewServer( - makeCountEndpoint(svc), - decodeCountRequest, - encodeResponse, - ) - - http.Handle("/uppercase", uppercaseHandler) - http.Handle("/count", countHandler) - http.Handle("/metrics", promhttp.Handler()) - logger.Log("msg", "HTTP", "addr", *listen) - logger.Log("err", http.ListenAndServe(*listen, nil)) -} diff --git a/examples/stringsvc3/proxying.go b/examples/stringsvc3/proxying.go deleted file mode 100644 index 8b1013f..0000000 --- a/examples/stringsvc3/proxying.go +++ /dev/null @@ -1,116 +0,0 @@ -package main - -import ( - "context" - "errors" - "fmt" - "net/url" - "strings" - "time" - - jujuratelimit "github.com/juju/ratelimit" - "github.com/sony/gobreaker" - - "github.com/go-kit/kit/circuitbreaker" - "github.com/go-kit/kit/endpoint" - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/ratelimit" - "github.com/go-kit/kit/sd" - "github.com/go-kit/kit/sd/lb" - httptransport "github.com/go-kit/kit/transport/http" -) - -func proxyingMiddleware(ctx context.Context, instances string, logger log.Logger) ServiceMiddleware { - // If instances is empty, don't proxy. - if instances == "" { - logger.Log("proxy_to", "none") - return func(next StringService) StringService { return next } - } - - // Set some parameters for our client. - var ( - qps = 100 // beyond which we will return an error - maxAttempts = 3 // per request, before giving up - maxTime = 250 * time.Millisecond // wallclock time, before giving up - ) - - // Otherwise, construct an endpoint for each instance in the list, and add - // it to a fixed set of endpoints. In a real service, rather than doing this - // by hand, you'd probably use package sd's support for your service - // discovery system. - var ( - instanceList = split(instances) - endpointer sd.FixedEndpointer - ) - logger.Log("proxy_to", fmt.Sprint(instanceList)) - for _, instance := range instanceList { - var e endpoint.Endpoint - e = makeUppercaseProxy(ctx, instance) - e = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(e) - e = ratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(float64(qps), int64(qps)))(e) - endpointer = append(endpointer, e) - } - - // Now, build a single, retrying, load-balancing endpoint out of all of - // those individual endpoints. - balancer := lb.NewRoundRobin(endpointer) - retry := lb.Retry(maxAttempts, maxTime, balancer) - - // And finally, return the ServiceMiddleware, implemented by proxymw. - return func(next StringService) StringService { - return proxymw{ctx, next, retry} - } -} - -// proxymw implements StringService, forwarding Uppercase requests to the -// provided endpoint, and serving all other (i.e. Count) requests via the -// next StringService. -type proxymw struct { - ctx context.Context - next StringService // Serve most requests via this service... - uppercase endpoint.Endpoint // ...except Uppercase, which gets served by this endpoint -} - -func (mw proxymw) Count(s string) int { - return mw.next.Count(s) -} - -func (mw proxymw) Uppercase(s string) (string, error) { - response, err := mw.uppercase(mw.ctx, uppercaseRequest{S: s}) - if err != nil { - return "", err - } - - resp := response.(uppercaseResponse) - if resp.Err != "" { - return resp.V, errors.New(resp.Err) - } - return resp.V, nil -} - -func makeUppercaseProxy(ctx context.Context, instance string) endpoint.Endpoint { - if !strings.HasPrefix(instance, "http") { - instance = "http://" + instance - } - u, err := url.Parse(instance) - if err != nil { - panic(err) - } - if u.Path == "" { - u.Path = "/uppercase" - } - return httptransport.NewClient( - "GET", - u, - encodeRequest, - decodeUppercaseResponse, - ).Endpoint() -} - -func split(s string) []string { - a := strings.Split(s, ",") - for i := range a { - a[i] = strings.TrimSpace(a[i]) - } - return a -} diff --git a/examples/stringsvc3/service.go b/examples/stringsvc3/service.go deleted file mode 100644 index 7e1773a..0000000 --- a/examples/stringsvc3/service.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "errors" - "strings" -) - -// StringService provides operations on strings. -type StringService interface { - Uppercase(string) (string, error) - Count(string) int -} - -type stringService struct{} - -func (stringService) Uppercase(s string) (string, error) { - if s == "" { - return "", ErrEmpty - } - return strings.ToUpper(s), nil -} - -func (stringService) Count(s string) int { - return len(s) -} - -// ErrEmpty is returned when an input string is empty. -var ErrEmpty = errors.New("empty string") - -// ServiceMiddleware is a chainable behavior modifier for StringService. -type ServiceMiddleware func(StringService) StringService diff --git a/examples/stringsvc3/transport.go b/examples/stringsvc3/transport.go deleted file mode 100644 index c17a055..0000000 --- a/examples/stringsvc3/transport.go +++ /dev/null @@ -1,84 +0,0 @@ -package main - -import ( - "bytes" - "context" - "encoding/json" - "io/ioutil" - "net/http" - - "github.com/go-kit/kit/endpoint" -) - -func makeUppercaseEndpoint(svc StringService) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(uppercaseRequest) - v, err := svc.Uppercase(req.S) - if err != nil { - return uppercaseResponse{v, err.Error()}, nil - } - return uppercaseResponse{v, ""}, nil - } -} - -func makeCountEndpoint(svc StringService) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(countRequest) - v := svc.Count(req.S) - return countResponse{v}, nil - } -} - -func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) { - var request uppercaseRequest - if err := json.NewDecoder(r.Body).Decode(&request); err != nil { - return nil, err - } - return request, nil -} - -func decodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) { - var request countRequest - if err := json.NewDecoder(r.Body).Decode(&request); err != nil { - return nil, err - } - return request, nil -} - -func decodeUppercaseResponse(_ context.Context, r *http.Response) (interface{}, error) { - var response uppercaseResponse - if err := json.NewDecoder(r.Body).Decode(&response); err != nil { - return nil, err - } - return response, nil -} - -func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { - return json.NewEncoder(w).Encode(response) -} - -func encodeRequest(_ context.Context, r *http.Request, request interface{}) error { - var buf bytes.Buffer - if err := json.NewEncoder(&buf).Encode(request); err != nil { - return err - } - r.Body = ioutil.NopCloser(&buf) - return nil -} - -type uppercaseRequest struct { - S string `json:"s"` -} - -type uppercaseResponse struct { - V string `json:"v"` - Err string `json:"err,omitempty"` -} - -type countRequest struct { - S string `json:"s"` -} - -type countResponse struct { - V int `json:"v"` -} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..bfd854d --- /dev/null +++ b/go.mod @@ -0,0 +1,93 @@ +module github.com/go-kit/kit + +go 1.17 + +require ( + github.com/VividCortex/gohistogram v1.0.0 + github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 + github.com/aws/aws-sdk-go v1.40.45 + github.com/aws/aws-sdk-go-v2 v1.9.1 + github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1 + github.com/casbin/casbin/v2 v2.37.0 + github.com/go-kit/log v0.2.0 + github.com/go-zookeeper/zk v1.0.2 + github.com/golang-jwt/jwt/v4 v4.0.0 + github.com/hashicorp/consul/api v1.10.1 + github.com/hudl/fargo v1.4.0 + github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab + github.com/nats-io/nats-server/v2 v2.5.0 + github.com/nats-io/nats.go v1.12.1 + github.com/opentracing/opentracing-go v1.2.0 + github.com/openzipkin/zipkin-go v0.2.5 + github.com/performancecopilot/speed/v4 v4.0.0 + github.com/prometheus/client_golang v1.11.0 + github.com/sirupsen/logrus v1.8.1 + github.com/sony/gobreaker v0.4.1 + github.com/streadway/amqp v1.0.0 + github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e + go.etcd.io/etcd/client/pkg/v3 v3.5.0 + go.etcd.io/etcd/client/v2 v2.305.0 + go.etcd.io/etcd/client/v3 v3.5.0 + go.opencensus.io v0.23.0 + go.uber.org/zap v1.19.1 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac + google.golang.org/grpc v1.40.0 + google.golang.org/protobuf v1.27.1 +) + +require ( + github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect + github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect + github.com/armon/go-metrics v0.3.9 // indirect + github.com/aws/smithy-go v1.8.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.1.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/clbanning/mxj v1.8.4 // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/edsrzf/mmap-go v1.0.0 // indirect + github.com/fatih/color v1.12.0 // indirect + github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 // indirect + github.com/go-logfmt/logfmt v0.5.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v0.16.2 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/serf v0.9.5 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.13.6 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/miekg/dns v1.1.43 // indirect + github.com/minio/highwayhash v1.0.2 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.4.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nats-io/jwt/v2 v2.0.3 // indirect + github.com/nats-io/nkeys v0.3.0 // indirect + github.com/nats-io/nuid v1.0.1 // indirect + github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.30.0 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + go.etcd.io/etcd/api/v3 v3.5.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.7.0 // indirect + golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 // indirect + golang.org/x/net v0.0.0-20210917221730-978cfadd31cf // indirect + golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4 // indirect + gopkg.in/gcfg.v1 v1.2.3 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ae52a68 --- /dev/null +++ b/go.sum @@ -0,0 +1,842 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.9 h1:O2sNqxBdvq8Eq5xmzljcYzAORli6RWCvEym4cJf9m18= +github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go v1.40.45 h1:QN1nsY27ssD/JmW4s83qmSb+uL6DG4GmCDzjmJB4xUI= +github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go-v2 v1.9.1 h1:ZbovGV/qo40nrOJ4q8G33AGICzaPI45FHQWJ9650pF4= +github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1 h1:w/fPGB0t5rWwA43mux4e9ozFSH5zF1moQemlA131PWc= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= +github.com/aws/smithy-go v1.8.0 h1:AEwwwXQZtUwP5Mz506FeXXrKBe0jA8gVM+1gEcSRooc= +github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/casbin/casbin/v2 v2.37.0 h1:/poEwPSovi4bTOcP752/CsTQiRz2xycyVKFG7GUhbDw= +github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= +github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= +github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= +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/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +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/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2 h1:cZqz+yOJ/R64LcKjNQOdARott/jP7BnUQ9Ah7KaZCvw= +github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 h1:a9ENSRDFBUPkJ5lCgVZh26+ZbGyoVJG7yb5SSzF5H54= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM= +github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/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.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.10.1 h1:MwZJp86nlnL+6+W1Zly4JUuVn9YHhMggBirMpHGD7kw= +github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= +github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= +github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hudl/fargo v1.4.0 h1:ZDDILMbB37UlAVLlWcJ2Iz1XuahZZTDZfdCKeclfq2s= +github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= +github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +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/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= +github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt v1.2.2 h1:w3GMTO969dFg+UOKTmmyuu7IGdusK+7Ytlt//OYH/uU= +github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q= +github.com/nats-io/jwt/v2 v2.0.3 h1:i/O6cmIsjpcQyWDYNcq2JyZ3/VTF8SJ4JWluI5OhpvI= +github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= +github.com/nats-io/nats-server/v2 v2.5.0 h1:wsnVaaXH9VRSg+A2MVg5Q727/CqxnmPLGFQ3YZYKTQg= +github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g= +github.com/nats-io/nats.go v1.12.1 h1:+0ndxwUPz3CmQ2vjbXdkC1fo3FdiOQDim4gl3Mge8Qo= +github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= +github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= +github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openzipkin/zipkin-go v0.2.5 h1:UwtQQx2pyPIgWYHRg+epgdx1/HnBQTgN3/oIYEJTQzU= +github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/performancecopilot/speed/v4 v4.0.0 h1:VxEDCmdkfbQYDlcr/GC9YoN9PQ6p8ulk9xVsepYy9ZY= +github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug= +github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= +github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e h1:mOtuXaRAbVZsxAHVdPR3IjfmN8T1h2iczJLynhLybf8= +github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= +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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0 h1:ftQ0nOOHMcbMS3KIaDQ0g5Qcd6bhaBrQT6b89DfwLTs= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.etcd.io/etcd/client/v3 v3.5.0 h1:62Eh0XOro+rDwkrypAGDfgmNh5Joq+z+W9HZdlXMzek= +go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 h1:3erb+vDS8lU1sxfDHF4/hhWyaXnhIaO+7RgL4fDZORA= +golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +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/mod v0.4.2/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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210917221730-978cfadd31cf h1:R150MpwJIv1MpS0N/pc+NhTM8ajzvlmxlY5OYsrevXQ= +golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 h1:J27LZFQBFoihqXoegpscI10HpjZ7B5WQLLKL2FZXQKw= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +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-20190206041539-40960b6deb8e/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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4 h1:ysnBoUyeL/H6RCvNRhWHjKoDEmguI+mPU+qHgK8qv/w= +google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +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.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +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/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/log/README.md b/log/README.md index 7222f80..5492dd9 100644 --- a/log/README.md +++ b/log/README.md @@ -1,4 +1,13 @@ # package log + +**Deprecation notice:** The core Go kit log packages (log, log/level, log/term, and +log/syslog) have been moved to their own repository at github.com/go-kit/log. +The corresponding packages in this directory remain for backwards compatibility. +Their types alias the types and their functions call the functions provided by +the new repository. Using either import path should be equivalent. Prefer the +new import path when practical. + +______ `package log` provides a minimal interface for structured logging in services. It may be wrapped to encode conventions, enforce type-safety, provide leveled @@ -103,6 +112,10 @@ // ts=2016-01-01T12:34:56Z caller=main.go:15 msg=hello ``` +## Levels + +Log levels are supported via the [level package](https://godoc.org/github.com/go-kit/kit/log/level). + ## Supported output formats - [Logfmt](https://brandur.org/logfmt) ([see also](https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write)) diff --git a/log/benchmark_test.go b/log/benchmark_test.go deleted file mode 100644 index 126bfa5..0000000 --- a/log/benchmark_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package log_test - -import ( - "testing" - - "github.com/go-kit/kit/log" -) - -func benchmarkRunner(b *testing.B, logger log.Logger, f func(log.Logger)) { - lc := log.With(logger, "common_key", "common_value") - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - f(lc) - } -} - -var ( - baseMessage = func(logger log.Logger) { logger.Log("foo_key", "foo_value") } - withMessage = func(logger log.Logger) { log.With(logger, "a", "b").Log("c", "d") } -) diff --git a/log/concurrency_test.go b/log/concurrency_test.go deleted file mode 100644 index 95a749e..0000000 --- a/log/concurrency_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package log_test - -import ( - "math" - "testing" - - "github.com/go-kit/kit/log" -) - -// These test are designed to be run with the race detector. - -func testConcurrency(t *testing.T, logger log.Logger, total int) { - n := int(math.Sqrt(float64(total))) - share := total / n - - errC := make(chan error, n) - - for i := 0; i < n; i++ { - go func() { - errC <- spam(logger, share) - }() - } - - for i := 0; i < n; i++ { - err := <-errC - if err != nil { - t.Fatalf("concurrent logging error: %v", err) - } - } -} - -func spam(logger log.Logger, count int) error { - for i := 0; i < count; i++ { - err := logger.Log("key", i) - if err != nil { - return err - } - } - return nil -} diff --git a/log/deprecated_levels/levels.go b/log/deprecated_levels/levels.go index a034212..2807cbd 100644 --- a/log/deprecated_levels/levels.go +++ b/log/deprecated_levels/levels.go @@ -1,6 +1,9 @@ +// Package levels implements leveled logging on top of Go kit's log package. +// +// Deprecated: Use github.com/go-kit/log/level instead. package levels -import "github.com/go-kit/kit/log" +import "github.com/go-kit/log" // Levels provides a leveled logging wrapper around a logger. It has five // levels: debug, info, warning (warn), error, and critical (crit). If you diff --git a/log/deprecated_levels/levels_test.go b/log/deprecated_levels/levels_test.go index 8d4a7f5..57854ea 100644 --- a/log/deprecated_levels/levels_test.go +++ b/log/deprecated_levels/levels_test.go @@ -5,8 +5,8 @@ "os" "testing" - "github.com/go-kit/kit/log" levels "github.com/go-kit/kit/log/deprecated_levels" + "github.com/go-kit/log" ) func TestDefaultLevels(t *testing.T) { diff --git a/log/doc.go b/log/doc.go index 918c0af..c9873f4 100644 --- a/log/doc.go +++ b/log/doc.go @@ -1,4 +1,6 @@ // Package log provides a structured logger. +// +// Deprecated: Use github.com/go-kit/log instead. // // Structured logging produces logs easily consumed later by humans or // machines. Humans might be interested in debugging errors, or tracing @@ -39,8 +41,8 @@ // // A contextual logger stores keyvals that it includes in all log events. // Building appropriate contextual loggers reduces repetition and aids -// consistency in the resulting log output. With and WithPrefix add context to -// a logger. We can use With to improve the RunTask example. +// consistency in the resulting log output. With, WithPrefix, and WithSuffix +// add context to a logger. We can use With to improve the RunTask example. // // func RunTask(task Task, logger log.Logger) string { // logger = log.With(logger, "taskID", task.ID) diff --git a/log/json_logger.go b/log/json_logger.go index 66094b4..edfde2f 100644 --- a/log/json_logger.go +++ b/log/json_logger.go @@ -1,89 +1,15 @@ package log import ( - "encoding" - "encoding/json" - "fmt" "io" - "reflect" + + "github.com/go-kit/log" ) - -type jsonLogger struct { - io.Writer -} // NewJSONLogger returns a Logger that encodes keyvals to the Writer as a // single JSON object. Each log event produces no more than one call to // w.Write. The passed Writer must be safe for concurrent use by multiple // goroutines if the returned Logger will be used concurrently. func NewJSONLogger(w io.Writer) Logger { - return &jsonLogger{w} + return log.NewJSONLogger(w) } - -func (l *jsonLogger) Log(keyvals ...interface{}) error { - n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd - m := make(map[string]interface{}, n) - for i := 0; i < len(keyvals); i += 2 { - k := keyvals[i] - var v interface{} = ErrMissingValue - if i+1 < len(keyvals) { - v = keyvals[i+1] - } - merge(m, k, v) - } - return json.NewEncoder(l.Writer).Encode(m) -} - -func merge(dst map[string]interface{}, k, v interface{}) { - var key string - switch x := k.(type) { - case string: - key = x - case fmt.Stringer: - key = safeString(x) - default: - key = fmt.Sprint(x) - } - - // We want json.Marshaler and encoding.TextMarshaller to take priority over - // err.Error() and v.String(). But json.Marshall (called later) does that by - // default so we force a no-op if it's one of those 2 case. - switch x := v.(type) { - case json.Marshaler: - case encoding.TextMarshaler: - case error: - v = safeError(x) - case fmt.Stringer: - v = safeString(x) - } - - dst[key] = v -} - -func safeString(str fmt.Stringer) (s string) { - defer func() { - if panicVal := recover(); panicVal != nil { - if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() { - s = "NULL" - } else { - panic(panicVal) - } - } - }() - s = str.String() - return -} - -func safeError(err error) (s interface{}) { - defer func() { - if panicVal := recover(); panicVal != nil { - if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() { - s = nil - } else { - panic(panicVal) - } - } - }() - s = err.Error() - return -} diff --git a/log/json_logger_test.go b/log/json_logger_test.go deleted file mode 100644 index e3e3090..0000000 --- a/log/json_logger_test.go +++ /dev/null @@ -1,162 +0,0 @@ -package log_test - -import ( - "bytes" - "errors" - "io/ioutil" - "testing" - - "github.com/go-kit/kit/log" -) - -func TestJSONLoggerCaller(t *testing.T) { - t.Parallel() - buf := &bytes.Buffer{} - logger := log.NewJSONLogger(buf) - logger = log.With(logger, "caller", log.DefaultCaller) - - if err := logger.Log(); err != nil { - t.Fatal(err) - } - if want, have := `{"caller":"json_logger_test.go:18"}`+"\n", buf.String(); want != have { - t.Errorf("\nwant %#v\nhave %#v", want, have) - } -} - -func TestJSONLogger(t *testing.T) { - t.Parallel() - buf := &bytes.Buffer{} - logger := log.NewJSONLogger(buf) - if err := logger.Log("err", errors.New("err"), "m", map[string]int{"0": 0}, "a", []int{1, 2, 3}); err != nil { - t.Fatal(err) - } - if want, have := `{"a":[1,2,3],"err":"err","m":{"0":0}}`+"\n", buf.String(); want != have { - t.Errorf("\nwant %#v\nhave %#v", want, have) - } -} - -func TestJSONLoggerMissingValue(t *testing.T) { - t.Parallel() - buf := &bytes.Buffer{} - logger := log.NewJSONLogger(buf) - if err := logger.Log("k"); err != nil { - t.Fatal(err) - } - if want, have := `{"k":"(MISSING)"}`+"\n", buf.String(); want != have { - t.Errorf("\nwant %#v\nhave %#v", want, have) - } -} - -func TestJSONLoggerNilStringerKey(t *testing.T) { - t.Parallel() - - buf := &bytes.Buffer{} - logger := log.NewJSONLogger(buf) - if err := logger.Log((*stringer)(nil), "v"); err != nil { - t.Fatal(err) - } - if want, have := `{"NULL":"v"}`+"\n", buf.String(); want != have { - t.Errorf("\nwant %#v\nhave %#v", want, have) - } -} - -func TestJSONLoggerNilErrorValue(t *testing.T) { - t.Parallel() - - buf := &bytes.Buffer{} - logger := log.NewJSONLogger(buf) - if err := logger.Log("err", (*stringError)(nil)); err != nil { - t.Fatal(err) - } - if want, have := `{"err":null}`+"\n", buf.String(); want != have { - t.Errorf("\nwant %#v\nhave %#v", want, have) - } -} - -// aller implements json.Marshaler, encoding.TextMarshaler, and fmt.Stringer. -type aller struct{} - -func (aller) MarshalJSON() ([]byte, error) { - return []byte("\"json\""), nil -} - -func (aller) MarshalText() ([]byte, error) { - return []byte("text"), nil -} - -func (aller) String() string { - return "string" -} - -func (aller) Error() string { - return "error" -} - -// textstringer implements encoding.TextMarshaler and fmt.Stringer. -type textstringer struct{} - -func (textstringer) MarshalText() ([]byte, error) { - return []byte("text"), nil -} - -func (textstringer) String() string { - return "string" -} - -func TestJSONLoggerStringValue(t *testing.T) { - t.Parallel() - tests := []struct { - v interface{} - expected string - }{ - { - v: aller{}, - expected: `{"v":"json"}`, - }, - { - v: textstringer{}, - expected: `{"v":"text"}`, - }, - { - v: stringer("string"), - expected: `{"v":"string"}`, - }, - } - - for _, test := range tests { - buf := &bytes.Buffer{} - logger := log.NewJSONLogger(buf) - if err := logger.Log("v", test.v); err != nil { - t.Fatal(err) - } - - if want, have := test.expected+"\n", buf.String(); want != have { - t.Errorf("\nwant %#v\nhave %#v", want, have) - } - } -} - -type stringer string - -func (s stringer) String() string { - return string(s) -} - -type stringError string - -func (s stringError) Error() string { - return string(s) -} - -func BenchmarkJSONLoggerSimple(b *testing.B) { - benchmarkRunner(b, log.NewJSONLogger(ioutil.Discard), baseMessage) -} - -func BenchmarkJSONLoggerContextual(b *testing.B) { - benchmarkRunner(b, log.NewJSONLogger(ioutil.Discard), withMessage) -} - -func TestJSONLoggerConcurrency(t *testing.T) { - t.Parallel() - testConcurrency(t, log.NewJSONLogger(ioutil.Discard), 10000) -} diff --git a/log/level/benchmark_test.go b/log/level/benchmark_test.go deleted file mode 100644 index 4fca6f0..0000000 --- a/log/level/benchmark_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package level_test - -import ( - "io/ioutil" - "testing" - - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/log/level" -) - -func Benchmark(b *testing.B) { - contexts := []struct { - name string - context func(log.Logger) log.Logger - }{ - {"NoContext", func(l log.Logger) log.Logger { - return l - }}, - {"TimeContext", func(l log.Logger) log.Logger { - return log.With(l, "time", log.DefaultTimestampUTC) - }}, - {"CallerContext", func(l log.Logger) log.Logger { - return log.With(l, "caller", log.DefaultCaller) - }}, - {"TimeCallerReqIDContext", func(l log.Logger) log.Logger { - return log.With(l, "time", log.DefaultTimestampUTC, "caller", log.DefaultCaller, "reqID", 29) - }}, - } - - loggers := []struct { - name string - logger log.Logger - }{ - {"Nop", log.NewNopLogger()}, - {"Logfmt", log.NewLogfmtLogger(ioutil.Discard)}, - {"JSON", log.NewJSONLogger(ioutil.Discard)}, - } - - filters := []struct { - name string - filter func(log.Logger) log.Logger - }{ - {"Baseline", func(l log.Logger) log.Logger { - return l - }}, - {"DisallowedLevel", func(l log.Logger) log.Logger { - return level.NewFilter(l, level.AllowInfo()) - }}, - {"AllowedLevel", func(l log.Logger) log.Logger { - return level.NewFilter(l, level.AllowAll()) - }}, - } - - for _, c := range contexts { - b.Run(c.name, func(b *testing.B) { - for _, f := range filters { - b.Run(f.name, func(b *testing.B) { - for _, l := range loggers { - b.Run(l.name, func(b *testing.B) { - logger := c.context(f.filter(l.logger)) - b.ResetTimer() - b.ReportAllocs() - for i := 0; i < b.N; i++ { - level.Debug(logger).Log("foo", "bar") - } - }) - } - }) - } - }) - } -} diff --git a/log/level/doc.go b/log/level/doc.go index 5e9df7f..7baf870 100644 --- a/log/level/doc.go +++ b/log/level/doc.go @@ -1,10 +1,13 @@ -// Package level implements leveled logging on top of package log. To use the -// level package, create a logger as per normal in your func main, and wrap it -// with level.NewFilter. +// Package level implements leveled logging on top of Go kit's log package. +// +// Deprecated: Use github.com/go-kit/log/level instead. +// +// To use the level package, create a logger as per normal in your func main, +// and wrap it with level.NewFilter. // // var logger log.Logger // logger = log.NewLogfmtLogger(os.Stderr) -// logger = level.NewFilter(logger, level.AllowInfoAndAbove()) // <-- +// logger = level.NewFilter(logger, level.AllowInfo()) // <-- // logger = log.With(logger, "ts", log.DefaultTimestampUTC) // // Then, at the callsites, use one of the level.Debug, Info, Warn, or Error diff --git a/log/level/example_test.go b/log/level/example_test.go index fed52e5..fb77770 100644 --- a/log/level/example_test.go +++ b/log/level/example_test.go @@ -9,17 +9,31 @@ ) func Example_basic() { - // setup logger with level filter + logger := log.NewLogfmtLogger(os.Stdout) + level.Debug(logger).Log("msg", "this message is at the debug level") + level.Info(logger).Log("msg", "this message is at the info level") + level.Warn(logger).Log("msg", "this message is at the warn level") + level.Error(logger).Log("msg", "this message is at the error level") + + // Output: + // level=debug msg="this message is at the debug level" + // level=info msg="this message is at the info level" + // level=warn msg="this message is at the warn level" + // level=error msg="this message is at the error level" +} + +func Example_filtered() { + // Set up logger with level filter. logger := log.NewLogfmtLogger(os.Stdout) logger = level.NewFilter(logger, level.AllowInfo()) logger = log.With(logger, "caller", log.DefaultCaller) - // use level helpers to log at different levels + // Use level helpers to log at different levels. level.Error(logger).Log("err", errors.New("bad data")) level.Info(logger).Log("event", "data saved") level.Debug(logger).Log("next item", 17) // filtered // Output: - // level=error caller=example_test.go:18 err="bad data" - // level=info caller=example_test.go:19 event="data saved" + // level=error caller=example_test.go:32 err="bad data" + // level=info caller=example_test.go:33 event="data saved" } diff --git a/log/level/level.go b/log/level/level.go index 6833b0d..803e8b9 100644 --- a/log/level/level.go +++ b/log/level/level.go @@ -1,25 +1,28 @@ package level -import "github.com/go-kit/kit/log" +import ( + "github.com/go-kit/log" + "github.com/go-kit/log/level" +) // Error returns a logger that includes a Key/ErrorValue pair. func Error(logger log.Logger) log.Logger { - return log.WithPrefix(logger, Key(), ErrorValue()) + return level.Error(logger) } // Warn returns a logger that includes a Key/WarnValue pair. func Warn(logger log.Logger) log.Logger { - return log.WithPrefix(logger, Key(), WarnValue()) + return level.Warn(logger) } // Info returns a logger that includes a Key/InfoValue pair. func Info(logger log.Logger) log.Logger { - return log.WithPrefix(logger, Key(), InfoValue()) + return level.Info(logger) } // Debug returns a logger that includes a Key/DebugValue pair. func Debug(logger log.Logger) log.Logger { - return log.WithPrefix(logger, Key(), DebugValue()) + return level.Debug(logger) } // NewFilter wraps next and implements level filtering. See the commentary on @@ -28,76 +31,40 @@ // Info, Warn or Error helper methods are squelched and non-leveled log // events are passed to next unmodified. func NewFilter(next log.Logger, options ...Option) log.Logger { - l := &logger{ - next: next, - } - for _, option := range options { - option(l) - } - return l -} - -type logger struct { - next log.Logger - allowed level - squelchNoLevel bool - errNotAllowed error - errNoLevel error -} - -func (l *logger) Log(keyvals ...interface{}) error { - var hasLevel, levelAllowed bool - for i := 1; i < len(keyvals); i += 2 { - if v, ok := keyvals[i].(*levelValue); ok { - hasLevel = true - levelAllowed = l.allowed&v.level != 0 - break - } - } - if !hasLevel && l.squelchNoLevel { - return l.errNoLevel - } - if hasLevel && !levelAllowed { - return l.errNotAllowed - } - return l.next.Log(keyvals...) + return level.NewFilter(next, options...) } // Option sets a parameter for the leveled logger. -type Option func(*logger) +type Option = level.Option // AllowAll is an alias for AllowDebug. func AllowAll() Option { - return AllowDebug() + return level.AllowAll() } // AllowDebug allows error, warn, info and debug level log events to pass. func AllowDebug() Option { - return allowed(levelError | levelWarn | levelInfo | levelDebug) + return level.AllowDebug() } // AllowInfo allows error, warn and info level log events to pass. func AllowInfo() Option { - return allowed(levelError | levelWarn | levelInfo) + return level.AllowInfo() } // AllowWarn allows error and warn level log events to pass. func AllowWarn() Option { - return allowed(levelError | levelWarn) + return level.AllowWarn() } // AllowError allows only error level log events to pass. func AllowError() Option { - return allowed(levelError) + return level.AllowError() } // AllowNone allows no leveled log events to pass. func AllowNone() Option { - return allowed(0) -} - -func allowed(allowed level) Option { - return func(l *logger) { l.allowed = allowed } + return level.AllowNone() } // ErrNotAllowed sets the error to return from Log when it squelches a log @@ -105,7 +72,7 @@ // ErrNotAllowed is nil; in this case the log event is squelched with no // error. func ErrNotAllowed(err error) Option { - return func(l *logger) { l.errNotAllowed = err } + return level.ErrNotAllowed(err) } // SquelchNoLevel instructs Log to squelch log events with no level, so that @@ -113,93 +80,41 @@ // to true and a log event is squelched in this way, the error value // configured with ErrNoLevel is returned to the caller. func SquelchNoLevel(squelch bool) Option { - return func(l *logger) { l.squelchNoLevel = squelch } + return level.SquelchNoLevel(squelch) } // ErrNoLevel sets the error to return from Log when it squelches a log event // with no level. By default, ErrNoLevel is nil; in this case the log event is // squelched with no error. func ErrNoLevel(err error) Option { - return func(l *logger) { l.errNoLevel = err } + return level.ErrNoLevel(err) } // NewInjector wraps next and returns a logger that adds a Key/level pair to // the beginning of log events that don't already contain a level. In effect, // this gives a default level to logs without a level. -func NewInjector(next log.Logger, level Value) log.Logger { - return &injector{ - next: next, - level: level, - } -} - -type injector struct { - next log.Logger - level interface{} -} - -func (l *injector) Log(keyvals ...interface{}) error { - for i := 1; i < len(keyvals); i += 2 { - if _, ok := keyvals[i].(*levelValue); ok { - return l.next.Log(keyvals...) - } - } - kvs := make([]interface{}, len(keyvals)+2) - kvs[0], kvs[1] = key, l.level - copy(kvs[2:], keyvals) - return l.next.Log(kvs...) +func NewInjector(next log.Logger, lvl Value) log.Logger { + return level.NewInjector(next, lvl) } // Value is the interface that each of the canonical level values implement. // It contains unexported methods that prevent types from other packages from // implementing it and guaranteeing that NewFilter can distinguish the levels // defined in this package from all other values. -type Value interface { - String() string - levelVal() -} +type Value = level.Value // Key returns the unique key added to log events by the loggers in this // package. -func Key() interface{} { return key } +func Key() interface{} { return level.Key() } // ErrorValue returns the unique value added to log events by Error. -func ErrorValue() Value { return errorValue } +func ErrorValue() Value { return level.ErrorValue() } // WarnValue returns the unique value added to log events by Warn. -func WarnValue() Value { return warnValue } +func WarnValue() Value { return level.WarnValue() } // InfoValue returns the unique value added to log events by Info. -func InfoValue() Value { return infoValue } +func InfoValue() Value { return level.InfoValue() } -// DebugValue returns the unique value added to log events by Warn. -func DebugValue() Value { return debugValue } - -var ( - // key is of type interfae{} so that it allocates once during package - // initialization and avoids allocating every type the value is added to a - // []interface{} later. - key interface{} = "level" - - errorValue = &levelValue{level: levelError, name: "error"} - warnValue = &levelValue{level: levelWarn, name: "warn"} - infoValue = &levelValue{level: levelInfo, name: "info"} - debugValue = &levelValue{level: levelDebug, name: "debug"} -) - -type level byte - -const ( - levelDebug level = 1 << iota - levelInfo - levelWarn - levelError -) - -type levelValue struct { - name string - level -} - -func (v *levelValue) String() string { return v.name } -func (v *levelValue) levelVal() {} +// DebugValue returns the unique value added to log events by Debug. +func DebugValue() Value { return level.DebugValue() } diff --git a/log/level/level_test.go b/log/level/level_test.go deleted file mode 100644 index e362eff..0000000 --- a/log/level/level_test.go +++ /dev/null @@ -1,235 +0,0 @@ -package level_test - -import ( - "bytes" - "errors" - "io" - "strings" - "testing" - - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/log/level" -) - -func TestVariousLevels(t *testing.T) { - testCases := []struct { - name string - allowed level.Option - want string - }{ - { - "AllowAll", - level.AllowAll(), - strings.Join([]string{ - `{"level":"debug","this is":"debug log"}`, - `{"level":"info","this is":"info log"}`, - `{"level":"warn","this is":"warn log"}`, - `{"level":"error","this is":"error log"}`, - }, "\n"), - }, - { - "AllowDebug", - level.AllowDebug(), - strings.Join([]string{ - `{"level":"debug","this is":"debug log"}`, - `{"level":"info","this is":"info log"}`, - `{"level":"warn","this is":"warn log"}`, - `{"level":"error","this is":"error log"}`, - }, "\n"), - }, - { - "AllowInfo", - level.AllowInfo(), - strings.Join([]string{ - `{"level":"info","this is":"info log"}`, - `{"level":"warn","this is":"warn log"}`, - `{"level":"error","this is":"error log"}`, - }, "\n"), - }, - { - "AllowWarn", - level.AllowWarn(), - strings.Join([]string{ - `{"level":"warn","this is":"warn log"}`, - `{"level":"error","this is":"error log"}`, - }, "\n"), - }, - { - "AllowError", - level.AllowError(), - strings.Join([]string{ - `{"level":"error","this is":"error log"}`, - }, "\n"), - }, - { - "AllowNone", - level.AllowNone(), - ``, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - var buf bytes.Buffer - logger := level.NewFilter(log.NewJSONLogger(&buf), tc.allowed) - - level.Debug(logger).Log("this is", "debug log") - level.Info(logger).Log("this is", "info log") - level.Warn(logger).Log("this is", "warn log") - level.Error(logger).Log("this is", "error log") - - if want, have := tc.want, strings.TrimSpace(buf.String()); want != have { - t.Errorf("\nwant:\n%s\nhave:\n%s", want, have) - } - }) - } -} - -func TestErrNotAllowed(t *testing.T) { - myError := errors.New("squelched!") - opts := []level.Option{ - level.AllowWarn(), - level.ErrNotAllowed(myError), - } - logger := level.NewFilter(log.NewNopLogger(), opts...) - - if want, have := myError, level.Info(logger).Log("foo", "bar"); want != have { - t.Errorf("want %#+v, have %#+v", want, have) - } - - if want, have := error(nil), level.Warn(logger).Log("foo", "bar"); want != have { - t.Errorf("want %#+v, have %#+v", want, have) - } -} - -func TestErrNoLevel(t *testing.T) { - myError := errors.New("no level specified") - - var buf bytes.Buffer - opts := []level.Option{ - level.SquelchNoLevel(true), - level.ErrNoLevel(myError), - } - logger := level.NewFilter(log.NewJSONLogger(&buf), opts...) - - if want, have := myError, logger.Log("foo", "bar"); want != have { - t.Errorf("want %v, have %v", want, have) - } - if want, have := ``, strings.TrimSpace(buf.String()); want != have { - t.Errorf("\nwant '%s'\nhave '%s'", want, have) - } -} - -func TestAllowNoLevel(t *testing.T) { - var buf bytes.Buffer - opts := []level.Option{ - level.SquelchNoLevel(false), - level.ErrNoLevel(errors.New("I should never be returned!")), - } - logger := level.NewFilter(log.NewJSONLogger(&buf), opts...) - - if want, have := error(nil), logger.Log("foo", "bar"); want != have { - t.Errorf("want %v, have %v", want, have) - } - if want, have := `{"foo":"bar"}`, strings.TrimSpace(buf.String()); want != have { - t.Errorf("\nwant '%s'\nhave '%s'", want, have) - } -} - -func TestLevelContext(t *testing.T) { - var buf bytes.Buffer - - // Wrapping the level logger with a context allows users to use - // log.DefaultCaller as per normal. - var logger log.Logger - logger = log.NewLogfmtLogger(&buf) - logger = level.NewFilter(logger, level.AllowAll()) - logger = log.With(logger, "caller", log.DefaultCaller) - - level.Info(logger).Log("foo", "bar") - if want, have := `level=info caller=level_test.go:149 foo=bar`, strings.TrimSpace(buf.String()); want != have { - t.Errorf("\nwant '%s'\nhave '%s'", want, have) - } -} - -func TestContextLevel(t *testing.T) { - var buf bytes.Buffer - - // Wrapping a context with the level logger still works, but requires users - // to specify a higher callstack depth value. - var logger log.Logger - logger = log.NewLogfmtLogger(&buf) - logger = log.With(logger, "caller", log.Caller(5)) - logger = level.NewFilter(logger, level.AllowAll()) - - level.Info(logger).Log("foo", "bar") - if want, have := `caller=level_test.go:165 level=info foo=bar`, strings.TrimSpace(buf.String()); want != have { - t.Errorf("\nwant '%s'\nhave '%s'", want, have) - } -} - -func TestLevelFormatting(t *testing.T) { - testCases := []struct { - name string - format func(io.Writer) log.Logger - output string - }{ - { - name: "logfmt", - format: log.NewLogfmtLogger, - output: `level=info foo=bar`, - }, - { - name: "JSON", - format: log.NewJSONLogger, - output: `{"foo":"bar","level":"info"}`, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - var buf bytes.Buffer - - logger := tc.format(&buf) - level.Info(logger).Log("foo", "bar") - if want, have := tc.output, strings.TrimSpace(buf.String()); want != have { - t.Errorf("\nwant: '%s'\nhave '%s'", want, have) - } - }) - } -} - -func TestInjector(t *testing.T) { - var ( - output []interface{} - logger log.Logger - ) - - logger = log.LoggerFunc(func(keyvals ...interface{}) error { - output = keyvals - return nil - }) - logger = level.NewInjector(logger, level.InfoValue()) - - logger.Log("foo", "bar") - if got, want := len(output), 4; got != want { - t.Errorf("missing level not injected: got len==%d, want len==%d", got, want) - } - if got, want := output[0], level.Key(); got != want { - t.Errorf("wrong level key: got %#v, want %#v", got, want) - } - if got, want := output[1], level.InfoValue(); got != want { - t.Errorf("wrong level value: got %#v, want %#v", got, want) - } - - level.Error(logger).Log("foo", "bar") - if got, want := len(output), 4; got != want { - t.Errorf("leveled record modified: got len==%d, want len==%d", got, want) - } - if got, want := output[0], level.Key(); got != want { - t.Errorf("wrong level key: got %#v, want %#v", got, want) - } - if got, want := output[1], level.ErrorValue(); got != want { - t.Errorf("wrong level value: got %#v, want %#v", got, want) - } -} diff --git a/log/log.go b/log/log.go index 66a9e2f..164a4f9 100644 --- a/log/log.go +++ b/log/log.go @@ -1,135 +1,51 @@ package log -import "errors" +import ( + "github.com/go-kit/log" +) // Logger is the fundamental interface for all log operations. Log creates a // log event from keyvals, a variadic sequence of alternating keys and values. // Implementations must be safe for concurrent use by multiple goroutines. In // particular, any implementation of Logger that appends to keyvals or // modifies or retains any of its elements must make a copy first. -type Logger interface { - Log(keyvals ...interface{}) error -} +type Logger = log.Logger // ErrMissingValue is appended to keyvals slices with odd length to substitute // the missing value. -var ErrMissingValue = errors.New("(MISSING)") +var ErrMissingValue = log.ErrMissingValue // With returns a new contextual logger with keyvals prepended to those passed -// to calls to Log. If logger is also a contextual logger created by With or -// WithPrefix, keyvals is appended to the existing context. +// to calls to Log. If logger is also a contextual logger created by With, +// WithPrefix, or WithSuffix, keyvals is appended to the existing context. // // The returned Logger replaces all value elements (odd indexes) containing a // Valuer with their generated value for each call to its Log method. func With(logger Logger, keyvals ...interface{}) Logger { - if len(keyvals) == 0 { - return logger - } - l := newContext(logger) - kvs := append(l.keyvals, keyvals...) - if len(kvs)%2 != 0 { - kvs = append(kvs, ErrMissingValue) - } - return &context{ - logger: l.logger, - // Limiting the capacity of the stored keyvals ensures that a new - // backing array is created if the slice must grow in Log or With. - // Using the extra capacity without copying risks a data race that - // would violate the Logger interface contract. - keyvals: kvs[:len(kvs):len(kvs)], - hasValuer: l.hasValuer || containsValuer(keyvals), - } + return log.With(logger, keyvals...) } // WithPrefix returns a new contextual logger with keyvals prepended to those // passed to calls to Log. If logger is also a contextual logger created by -// With or WithPrefix, keyvals is prepended to the existing context. +// With, WithPrefix, or WithSuffix, keyvals is prepended to the existing context. // // The returned Logger replaces all value elements (odd indexes) containing a // Valuer with their generated value for each call to its Log method. func WithPrefix(logger Logger, keyvals ...interface{}) Logger { - if len(keyvals) == 0 { - return logger - } - l := newContext(logger) - // Limiting the capacity of the stored keyvals ensures that a new - // backing array is created if the slice must grow in Log or With. - // Using the extra capacity without copying risks a data race that - // would violate the Logger interface contract. - n := len(l.keyvals) + len(keyvals) - if len(keyvals)%2 != 0 { - n++ - } - kvs := make([]interface{}, 0, n) - kvs = append(kvs, keyvals...) - if len(kvs)%2 != 0 { - kvs = append(kvs, ErrMissingValue) - } - kvs = append(kvs, l.keyvals...) - return &context{ - logger: l.logger, - keyvals: kvs, - hasValuer: l.hasValuer || containsValuer(keyvals), - } + return log.WithPrefix(logger, keyvals...) } -// context is the Logger implementation returned by With and WithPrefix. It -// wraps a Logger and holds keyvals that it includes in all log events. Its -// Log method calls bindValues to generate values for each Valuer in the -// context keyvals. +// WithSuffix returns a new contextual logger with keyvals appended to those +// passed to calls to Log. If logger is also a contextual logger created by +// With, WithPrefix, or WithSuffix, keyvals is appended to the existing context. // -// A context must always have the same number of stack frames between calls to -// its Log method and the eventual binding of Valuers to their value. This -// requirement comes from the functional requirement to allow a context to -// resolve application call site information for a Caller stored in the -// context. To do this we must be able to predict the number of logging -// functions on the stack when bindValues is called. -// -// Two implementation details provide the needed stack depth consistency. -// -// 1. newContext avoids introducing an additional layer when asked to -// wrap another context. -// 2. With and WithPrefix avoid introducing an additional layer by -// returning a newly constructed context with a merged keyvals rather -// than simply wrapping the existing context. -type context struct { - logger Logger - keyvals []interface{} - hasValuer bool -} - -func newContext(logger Logger) *context { - if c, ok := logger.(*context); ok { - return c - } - return &context{logger: logger} -} - -// Log replaces all value elements (odd indexes) containing a Valuer in the -// stored context with their generated value, appends keyvals, and passes the -// result to the wrapped Logger. -func (l *context) Log(keyvals ...interface{}) error { - kvs := append(l.keyvals, keyvals...) - if len(kvs)%2 != 0 { - kvs = append(kvs, ErrMissingValue) - } - if l.hasValuer { - // If no keyvals were appended above then we must copy l.keyvals so - // that future log events will reevaluate the stored Valuers. - if len(keyvals) == 0 { - kvs = append([]interface{}{}, l.keyvals...) - } - bindValues(kvs[:len(l.keyvals)]) - } - return l.logger.Log(kvs...) +// The returned Logger replaces all value elements (odd indexes) containing a +// Valuer with their generated value for each call to its Log method. +func WithSuffix(logger Logger, keyvals ...interface{}) Logger { + return log.WithSuffix(logger, keyvals...) } // LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If // f is a function with the appropriate signature, LoggerFunc(f) is a Logger // object that calls f. -type LoggerFunc func(...interface{}) error - -// Log implements Logger by calling f(keyvals...). -func (f LoggerFunc) Log(keyvals ...interface{}) error { - return f(keyvals...) -} +type LoggerFunc = log.LoggerFunc diff --git a/log/log_test.go b/log/log_test.go deleted file mode 100644 index 1bf2972..0000000 --- a/log/log_test.go +++ /dev/null @@ -1,191 +0,0 @@ -package log_test - -import ( - "bytes" - "fmt" - "sync" - "testing" - - "github.com/go-kit/kit/log" - "github.com/go-stack/stack" -) - -func TestContext(t *testing.T) { - t.Parallel() - buf := &bytes.Buffer{} - logger := log.NewLogfmtLogger(buf) - - kvs := []interface{}{"a", 123} - lc := log.With(logger, kvs...) - kvs[1] = 0 // With should copy its key values - - lc = log.With(lc, "b", "c") // With should stack - if err := lc.Log("msg", "message"); err != nil { - t.Fatal(err) - } - if want, have := "a=123 b=c msg=message\n", buf.String(); want != have { - t.Errorf("\nwant: %shave: %s", want, have) - } - - buf.Reset() - lc = log.WithPrefix(lc, "p", "first") - if err := lc.Log("msg", "message"); err != nil { - t.Fatal(err) - } - if want, have := "p=first a=123 b=c msg=message\n", buf.String(); want != have { - t.Errorf("\nwant: %shave: %s", want, have) - } -} - -func TestContextMissingValue(t *testing.T) { - t.Parallel() - var output []interface{} - logger := log.Logger(log.LoggerFunc(func(keyvals ...interface{}) error { - output = keyvals - return nil - })) - - log.WithPrefix(log.With(logger, "k1"), "k0").Log("k2") - if want, have := 6, len(output); want != have { - t.Errorf("want len(output) == %v, have %v", want, have) - } - for i := 1; i < 6; i += 2 { - if want, have := log.ErrMissingValue, output[i]; want != have { - t.Errorf("want output[%d] == %#v, have %#v", i, want, have) - } - } -} - -// Test that context.Log has a consistent function stack depth when binding -// Valuers, regardless of how many times With has been called. -func TestContextStackDepth(t *testing.T) { - t.Parallel() - fn := fmt.Sprintf("%n", stack.Caller(0)) - - var output []interface{} - - logger := log.Logger(log.LoggerFunc(func(keyvals ...interface{}) error { - output = keyvals - return nil - })) - - stackValuer := log.Valuer(func() interface{} { - for i, c := range stack.Trace() { - if fmt.Sprintf("%n", c) == fn { - return i - } - } - t.Fatal("Test function not found in stack trace.") - return nil - }) - - logger = log.With(logger, "stack", stackValuer) - - // Call through interface to get baseline. - logger.Log("k", "v") - want := output[1].(int) - - for len(output) < 10 { - logger.Log("k", "v") - if have := output[1]; have != want { - t.Errorf("%d Withs: have %v, want %v", len(output)/2-1, have, want) - } - - wrapped := log.With(logger) - wrapped.Log("k", "v") - if have := output[1]; have != want { - t.Errorf("%d Withs: have %v, want %v", len(output)/2-1, have, want) - } - - logger = log.With(logger, "k", "v") - } -} - -// Test that With returns a Logger safe for concurrent use. This test -// validates that the stored logging context does not get corrupted when -// multiple clients concurrently log additional keyvals. -// -// This test must be run with go test -cpu 2 (or more) to achieve its goal. -func TestWithConcurrent(t *testing.T) { - // Create some buckets to count how many events each goroutine logs. - const goroutines = 8 - counts := [goroutines]int{} - - // This logger extracts a goroutine id from the last value field and - // increments the referenced bucket. - logger := log.LoggerFunc(func(kv ...interface{}) error { - goroutine := kv[len(kv)-1].(int) - counts[goroutine]++ - return nil - }) - - // With must be careful about handling slices that can grow without - // copying the underlying array, so give it a challenge. - l := log.With(logger, make([]interface{}, 0, 2)...) - - // Start logging concurrently. Each goroutine logs its id so the logger - // can bucket the event counts. - var wg sync.WaitGroup - wg.Add(goroutines) - const n = 10000 - for i := 0; i < goroutines; i++ { - go func(idx int) { - defer wg.Done() - for j := 0; j < n; j++ { - l.Log("goroutineIdx", idx) - } - }(i) - } - wg.Wait() - - for bucket, have := range counts { - if want := n; want != have { - t.Errorf("bucket %d: want %d, have %d", bucket, want, have) // note Errorf - } - } -} - -func BenchmarkDiscard(b *testing.B) { - logger := log.NewNopLogger() - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - logger.Log("k", "v") - } -} - -func BenchmarkOneWith(b *testing.B) { - logger := log.NewNopLogger() - lc := log.With(logger, "k", "v") - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - lc.Log("k", "v") - } -} - -func BenchmarkTwoWith(b *testing.B) { - logger := log.NewNopLogger() - lc := log.With(logger, "k", "v") - for i := 1; i < 2; i++ { - lc = log.With(lc, "k", "v") - } - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - lc.Log("k", "v") - } -} - -func BenchmarkTenWith(b *testing.B) { - logger := log.NewNopLogger() - lc := log.With(logger, "k", "v") - for i := 1; i < 10; i++ { - lc = log.With(lc, "k", "v") - } - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - lc.Log("k", "v") - } -} diff --git a/log/logfmt_logger.go b/log/logfmt_logger.go index a003052..51cde2c 100644 --- a/log/logfmt_logger.go +++ b/log/logfmt_logger.go @@ -1,62 +1,15 @@ package log import ( - "bytes" "io" - "sync" - "github.com/go-logfmt/logfmt" + "github.com/go-kit/log" ) - -type logfmtEncoder struct { - *logfmt.Encoder - buf bytes.Buffer -} - -func (l *logfmtEncoder) Reset() { - l.Encoder.Reset() - l.buf.Reset() -} - -var logfmtEncoderPool = sync.Pool{ - New: func() interface{} { - var enc logfmtEncoder - enc.Encoder = logfmt.NewEncoder(&enc.buf) - return &enc - }, -} - -type logfmtLogger struct { - w io.Writer -} // NewLogfmtLogger returns a logger that encodes keyvals to the Writer in // logfmt format. Each log event produces no more than one call to w.Write. // The passed Writer must be safe for concurrent use by multiple goroutines if // the returned Logger will be used concurrently. func NewLogfmtLogger(w io.Writer) Logger { - return &logfmtLogger{w} + return log.NewLogfmtLogger(w) } - -func (l logfmtLogger) Log(keyvals ...interface{}) error { - enc := logfmtEncoderPool.Get().(*logfmtEncoder) - enc.Reset() - defer logfmtEncoderPool.Put(enc) - - if err := enc.EncodeKeyvals(keyvals...); err != nil { - return err - } - - // Add newline to the end of the buffer - if err := enc.EndRecord(); err != nil { - return err - } - - // The Logger interface requires implementations to be safe for concurrent - // use by multiple goroutines. For this implementation that means making - // only one call to l.w.Write() for each call to Log. - if _, err := l.w.Write(enc.buf.Bytes()); err != nil { - return err - } - return nil -} diff --git a/log/logfmt_logger_test.go b/log/logfmt_logger_test.go deleted file mode 100644 index 91bbca1..0000000 --- a/log/logfmt_logger_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package log_test - -import ( - "bytes" - "errors" - "io/ioutil" - "testing" - - "github.com/go-kit/kit/log" - "github.com/go-logfmt/logfmt" -) - -func TestLogfmtLogger(t *testing.T) { - t.Parallel() - buf := &bytes.Buffer{} - logger := log.NewLogfmtLogger(buf) - - if err := logger.Log("hello", "world"); err != nil { - t.Fatal(err) - } - if want, have := "hello=world\n", buf.String(); want != have { - t.Errorf("want %#v, have %#v", want, have) - } - - buf.Reset() - if err := logger.Log("a", 1, "err", errors.New("error")); err != nil { - t.Fatal(err) - } - if want, have := "a=1 err=error\n", buf.String(); want != have { - t.Errorf("want %#v, have %#v", want, have) - } - - buf.Reset() - if err := logger.Log("std_map", map[int]int{1: 2}, "my_map", mymap{0: 0}); err != nil { - t.Fatal(err) - } - if want, have := "std_map=\""+logfmt.ErrUnsupportedValueType.Error()+"\" my_map=special_behavior\n", buf.String(); want != have { - t.Errorf("want %#v, have %#v", want, have) - } -} - -func BenchmarkLogfmtLoggerSimple(b *testing.B) { - benchmarkRunner(b, log.NewLogfmtLogger(ioutil.Discard), baseMessage) -} - -func BenchmarkLogfmtLoggerContextual(b *testing.B) { - benchmarkRunner(b, log.NewLogfmtLogger(ioutil.Discard), withMessage) -} - -func TestLogfmtLoggerConcurrency(t *testing.T) { - t.Parallel() - testConcurrency(t, log.NewLogfmtLogger(ioutil.Discard), 10000) -} - -type mymap map[int]int - -func (m mymap) String() string { return "special_behavior" } diff --git a/log/logrus/logrus_logger.go b/log/logrus/logrus_logger.go new file mode 100644 index 0000000..dbee0f7 --- /dev/null +++ b/log/logrus/logrus_logger.go @@ -0,0 +1,69 @@ +// Package logrus provides an adapter to the +// go-kit log.Logger interface. +package logrus + +import ( + "errors" + "fmt" + + "github.com/go-kit/log" + "github.com/sirupsen/logrus" +) + +type Logger struct { + field logrus.FieldLogger + level logrus.Level +} + +type Option func(*Logger) + +var errMissingValue = errors.New("(MISSING)") + +// NewLogger returns a Go kit log.Logger that sends log events to a logrus.Logger. +func NewLogger(logger logrus.FieldLogger, options ...Option) log.Logger { + l := &Logger{ + field: logger, + level: logrus.InfoLevel, + } + + for _, optFunc := range options { + optFunc(l) + } + + return l +} + +// WithLevel configures a logrus logger to log at level for all events. +func WithLevel(level logrus.Level) Option { + return func(c *Logger) { + c.level = level + } +} + +func (l Logger) Log(keyvals ...interface{}) error { + fields := logrus.Fields{} + for i := 0; i < len(keyvals); i += 2 { + if i+1 < len(keyvals) { + fields[fmt.Sprint(keyvals[i])] = keyvals[i+1] + } else { + fields[fmt.Sprint(keyvals[i])] = errMissingValue + } + } + + switch l.level { + case logrus.InfoLevel: + l.field.WithFields(fields).Info() + case logrus.ErrorLevel: + l.field.WithFields(fields).Error() + case logrus.DebugLevel: + l.field.WithFields(fields).Debug() + case logrus.WarnLevel: + l.field.WithFields(fields).Warn() + case logrus.TraceLevel: + l.field.WithFields(fields).Trace() + default: + l.field.WithFields(fields).Print() + } + + return nil +} diff --git a/log/logrus/logrus_logger_test.go b/log/logrus/logrus_logger_test.go new file mode 100644 index 0000000..cc68184 --- /dev/null +++ b/log/logrus/logrus_logger_test.go @@ -0,0 +1,119 @@ +package logrus_test + +import ( + "bytes" + "encoding/json" + "errors" + "strings" + "testing" + + log "github.com/go-kit/kit/log/logrus" + "github.com/sirupsen/logrus" +) + +func TestLogrusLogger(t *testing.T) { + t.Parallel() + buf := &bytes.Buffer{} + logrusLogger := logrus.New() + logrusLogger.Out = buf + logrusLogger.Formatter = &logrus.TextFormatter{TimestampFormat: "02-01-2006 15:04:05", FullTimestamp: true} + logger := log.NewLogger(logrusLogger) + + if err := logger.Log("hello", "world"); err != nil { + t.Fatal(err) + } + if want, have := "hello=world\n", strings.Split(buf.String(), " ")[3]; want != have { + t.Errorf("want %#v, have %#v", want, have) + } + + buf.Reset() + if err := logger.Log("a", 1, "err", errors.New("error")); err != nil { + t.Fatal(err) + } + if want, have := "a=1 err=error", strings.TrimSpace(strings.SplitAfterN(buf.String(), " ", 4)[3]); want != have { + t.Errorf("want %#v, have %#v", want, have) + } + + buf.Reset() + if err := logger.Log("a", 1, "b"); err != nil { + t.Fatal(err) + } + if want, have := "a=1 b=\"(MISSING)\"", strings.TrimSpace(strings.SplitAfterN(buf.String(), " ", 4)[3]); want != have { + t.Errorf("want %#v, have %#v", want, have) + } + + buf.Reset() + if err := logger.Log("my_map", mymap{0: 0}); err != nil { + t.Fatal(err) + } + if want, have := "my_map=special_behavior", strings.TrimSpace(strings.Split(buf.String(), " ")[3]); want != have { + t.Errorf("want %#v, have %#v", want, have) + } +} + +type mymap map[int]int + +func (m mymap) String() string { return "special_behavior" } + +func TestWithLevel(t *testing.T) { + tests := []struct { + name string + level logrus.Level + expectedLevel logrus.Level + }{ + { + name: "Test Debug level", + level: logrus.DebugLevel, + expectedLevel: logrus.DebugLevel, + }, + { + name: "Test Error level", + level: logrus.ErrorLevel, + expectedLevel: logrus.ErrorLevel, + }, + { + name: "Test Warn level", + level: logrus.WarnLevel, + expectedLevel: logrus.WarnLevel, + }, + { + name: "Test Info level", + level: logrus.InfoLevel, + expectedLevel: logrus.InfoLevel, + }, + { + name: "Test Trace level", + level: logrus.TraceLevel, + expectedLevel: logrus.TraceLevel, + }, + { + name: "Test not existing level", + level: 999, + expectedLevel: logrus.InfoLevel, + }, + } + for _, tt := range tests { + buf := &bytes.Buffer{} + logrusLogger := logrus.New() + logrusLogger.Out = buf + logrusLogger.Level = tt.level + logrusLogger.Formatter = &logrus.JSONFormatter{} + logger := log.NewLogger(logrusLogger, log.WithLevel(tt.level)) + + t.Run(tt.name, func(t *testing.T) { + if err := logger.Log(); err != nil { + t.Fatal(err) + } + + l := map[string]interface{}{} + if err := json.Unmarshal(buf.Bytes(), &l); err != nil { + t.Fatal(err) + } + + if v, ok := l["level"].(string); !ok || v != tt.expectedLevel.String() { + t.Fatalf("Logging levels doesn't match. Expected: %s, got: %s", tt.level, v) + } + + }) + } +} diff --git a/log/nop_logger.go b/log/nop_logger.go index 1047d62..b02c686 100644 --- a/log/nop_logger.go +++ b/log/nop_logger.go @@ -1,8 +1,8 @@ package log -type nopLogger struct{} +import "github.com/go-kit/log" // NewNopLogger returns a logger that doesn't do anything. -func NewNopLogger() Logger { return nopLogger{} } - -func (nopLogger) Log(...interface{}) error { return nil } +func NewNopLogger() Logger { + return log.NewNopLogger() +} diff --git a/log/nop_logger_test.go b/log/nop_logger_test.go deleted file mode 100644 index 908ddd8..0000000 --- a/log/nop_logger_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package log_test - -import ( - "testing" - - "github.com/go-kit/kit/log" -) - -func TestNopLogger(t *testing.T) { - t.Parallel() - logger := log.NewNopLogger() - if err := logger.Log("abc", 123); err != nil { - t.Error(err) - } - if err := log.With(logger, "def", "ghi").Log(); err != nil { - t.Error(err) - } -} - -func BenchmarkNopLoggerSimple(b *testing.B) { - benchmarkRunner(b, log.NewNopLogger(), baseMessage) -} - -func BenchmarkNopLoggerContextual(b *testing.B) { - benchmarkRunner(b, log.NewNopLogger(), withMessage) -} diff --git a/log/stdlib.go b/log/stdlib.go index ff96b5d..cb604a7 100644 --- a/log/stdlib.go +++ b/log/stdlib.go @@ -2,9 +2,8 @@ import ( "io" - "log" - "regexp" - "strings" + + "github.com/go-kit/log" ) // StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's @@ -13,104 +12,43 @@ // // If you have any choice in the matter, you shouldn't use this. Prefer to // redirect the stdlib log to the Go kit logger via NewStdlibAdapter. -type StdlibWriter struct{} - -// Write implements io.Writer. -func (w StdlibWriter) Write(p []byte) (int, error) { - log.Print(strings.TrimSpace(string(p))) - return len(p), nil -} +type StdlibWriter = log.StdlibWriter // StdlibAdapter wraps a Logger and allows it to be passed to the stdlib // logger's SetOutput. It will extract date/timestamps, filenames, and // messages, and place them under relevant keys. -type StdlibAdapter struct { - Logger - timestampKey string - fileKey string - messageKey string -} +type StdlibAdapter = log.StdlibAdapter // StdlibAdapterOption sets a parameter for the StdlibAdapter. -type StdlibAdapterOption func(*StdlibAdapter) +type StdlibAdapterOption = log.StdlibAdapterOption // TimestampKey sets the key for the timestamp field. By default, it's "ts". func TimestampKey(key string) StdlibAdapterOption { - return func(a *StdlibAdapter) { a.timestampKey = key } + return log.TimestampKey(key) } // FileKey sets the key for the file and line field. By default, it's "caller". func FileKey(key string) StdlibAdapterOption { - return func(a *StdlibAdapter) { a.fileKey = key } + return log.FileKey(key) } // MessageKey sets the key for the actual log message. By default, it's "msg". func MessageKey(key string) StdlibAdapterOption { - return func(a *StdlibAdapter) { a.messageKey = key } + return log.MessageKey(key) +} + +// Prefix configures the adapter to parse a prefix from stdlib log events. If +// you provide a non-empty prefix to the stdlib logger, then your should provide +// that same prefix to the adapter via this option. +// +// By default, the prefix isn't included in the msg key. Set joinPrefixToMsg to +// true if you want to include the parsed prefix in the msg. +func Prefix(prefix string, joinPrefixToMsg bool) StdlibAdapterOption { + return log.Prefix(prefix, joinPrefixToMsg) } // NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed // logger. It's designed to be passed to log.SetOutput. func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer { - a := StdlibAdapter{ - Logger: logger, - timestampKey: "ts", - fileKey: "caller", - messageKey: "msg", - } - for _, option := range options { - option(&a) - } - return a + return log.NewStdlibAdapter(logger, options...) } - -func (a StdlibAdapter) Write(p []byte) (int, error) { - result := subexps(p) - keyvals := []interface{}{} - var timestamp string - if date, ok := result["date"]; ok && date != "" { - timestamp = date - } - if time, ok := result["time"]; ok && time != "" { - if timestamp != "" { - timestamp += " " - } - timestamp += time - } - if timestamp != "" { - keyvals = append(keyvals, a.timestampKey, timestamp) - } - if file, ok := result["file"]; ok && file != "" { - keyvals = append(keyvals, a.fileKey, file) - } - if msg, ok := result["msg"]; ok { - keyvals = append(keyvals, a.messageKey, msg) - } - if err := a.Logger.Log(keyvals...); err != nil { - return 0, err - } - return len(p), nil -} - -const ( - logRegexpDate = `(?P[0-9]{4}/[0-9]{2}/[0-9]{2})?[ ]?` - logRegexpTime = `(?P